-
-
Notifications
You must be signed in to change notification settings - Fork 32.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Chip] Migrate component [Avatar] Support children, change default colors #5852
Changes from all commits
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 |
---|---|---|
@@ -0,0 +1,23 @@ | ||
Chip | ||
==== | ||
|
||
Chips represent complex entities in small blocks, such as a contact. | ||
|
||
```jsx | ||
<Chip avatar={<Avatar>} label="Label text" /> | ||
``` | ||
|
||
Props | ||
----- | ||
|
||
| Name | Type | Default | Description | | ||
|:-----|:-----|:--------|:------------| | ||
| avatar | node | | Avatar element. | | ||
| className | string | | CSS `className` of the root element. | | ||
| deleteIconClassName | string | | CSS `className` of the delete icon element. | | ||
| label | string | | Label | | ||
| labelClassName | string | | CSS `className` of the label. | | ||
| onClick | function | | Callback function fired when the `Chip` element is clicked. If set, the chip will by styled for hover focus and active states.<br><br>**Signature:**<br>`function(event: object) => void`<br>*event:* `onClick` event targeting the root element. | | ||
| onRequestDelete | function | | Callback function fired when the delete icon is clicked. If set, the delete icon will be shown.<br><br>**Signature:**<br>`function(event: object) => void`<br>*event:* `onClick` event targeting the delete icon element. | | ||
|
||
Any other properties supplied will be spread to the root element. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
// @flow weak | ||
|
||
import React, { PropTypes } from 'react'; | ||
import { createStyleSheet } from 'jss-theme-reactor'; | ||
import Avatar from 'material-ui/Avatar'; | ||
import Chip from 'material-ui/Chip'; | ||
import Face from 'material-ui/svg-icons/face'; | ||
import { grey } from 'material-ui/styles/colors'; | ||
import avatarImage from 'docs/site/assets/images/uxceo-128.jpg'; | ||
|
||
const styleSheet = createStyleSheet('Chips', () => ({ | ||
chip: { | ||
margin: '0 8px', | ||
}, | ||
svgIcon: { | ||
color: grey[800], | ||
}, | ||
row: { | ||
display: 'flex', | ||
justifyContent: 'center', | ||
}, | ||
})); | ||
|
||
function handleRequestDelete() { | ||
alert('You clicked the delete icon.'); // eslint-ignore-line no-alert | ||
} | ||
|
||
function handleClick() { | ||
alert('You clicked the Chip.'); // eslint-ignore-line no-alert | ||
} | ||
|
||
export default function Chips(props, context) { | ||
const classes = context.styleManager.render(styleSheet); | ||
return ( | ||
<div className={classes.row}> | ||
<Chip | ||
label="Basic Chip" | ||
className={classes.chip} | ||
/> | ||
<Chip | ||
avatar={<Avatar>MB</Avatar>} | ||
label="Clickable Chip" | ||
onClick={handleClick} | ||
className={classes.chip} | ||
/> | ||
<Chip | ||
avatar={<Avatar src={avatarImage} />} | ||
label="Deletable Chip" | ||
onRequestDelete={handleRequestDelete} | ||
className={classes.chip} | ||
/> | ||
<Chip | ||
avatar={<Avatar><Face className={classes.svgIcon} /></Avatar>} | ||
label="Clickable Deletable Chip" | ||
onClick={handleClick} | ||
onRequestDelete={handleRequestDelete} | ||
className={classes.chip} | ||
/> | ||
</div> | ||
); | ||
} | ||
|
||
Chips.contextTypes = { | ||
styleManager: PropTypes.object.isRequired, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
// @flow weak | ||
|
||
import React, { Component, PropTypes } from 'react'; | ||
import { createStyleSheet } from 'jss-theme-reactor'; | ||
import Chip from 'material-ui/Chip'; | ||
|
||
|
||
const styleSheet = createStyleSheet('ChipsArray', () => ({ | ||
chip: { | ||
margin: '0 4px', | ||
}, | ||
row: { | ||
display: 'flex', | ||
justifyContent: 'center', | ||
}, | ||
})); | ||
|
||
export default class ChipsArray extends Component { | ||
static contextTypes = { | ||
styleManager: PropTypes.object.isRequired, | ||
}; | ||
|
||
state = { chipData: [ | ||
{ key: 0, label: 'Angular' }, | ||
{ key: 1, label: 'JQuery' }, | ||
{ key: 2, label: 'Polymer' }, | ||
{ key: 3, label: 'ReactJS' }, | ||
{ key: 4, label: 'Vue.js' }, | ||
] }; | ||
|
||
styles = { | ||
chip: { | ||
margin: 4, | ||
}, | ||
wrapper: { | ||
display: 'flex', | ||
flexWrap: 'wrap', | ||
}, | ||
}; | ||
|
||
handleRequestDelete = (key) => { | ||
if (key === 3) { | ||
alert('Why would you want to delete React?! :)'); // eslint-ignore-line no-alert | ||
return; | ||
} | ||
|
||
const chipData = [...this.state.chipData]; | ||
const chipToDelete = chipData.indexOf(chipData.find((chip) => chip.key === key)); | ||
chipData.splice(chipToDelete, 1); | ||
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. I'm not sure it's a great example. const chipData = [...this.state.chipData]; 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. Line 47? (Or am I misunderstanding?) 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. Yes, indeed 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. Right, right. I'm surprised React doesn't scream about that! |
||
this.setState({ chipData }); | ||
}; | ||
|
||
render() { | ||
const classes = this.context.styleManager.render(styleSheet); | ||
|
||
const renderChip = (data) => { | ||
return ( | ||
<Chip | ||
label={data.label} | ||
key={data.key} | ||
onRequestDelete={() => this.handleRequestDelete(data.key)} | ||
className={classes.chip} | ||
/> | ||
); | ||
}; | ||
|
||
return ( | ||
<div className={classes.row}> | ||
{this.state.chipData.map(renderChip, this)} | ||
</div> | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# Chips | ||
|
||
[Chips](https://www.google.com/design/spec/components/chips.html) | ||
represent complex entities in small blocks, such as a contact. | ||
|
||
While included here as a standalone component, the most common use will | ||
be in some form of input, so some of the behaviour demonstrated here is | ||
not shown in context. | ||
|
||
## Chip | ||
|
||
Examples of Chips, using an image Avatar, SVG Icon Avatar, "Letter" | ||
and (string) Avatar. | ||
Chips with the `onClick` property defined change appearance on focus, | ||
hover, and click. | ||
|
||
Chips with the `onRequestDelete` property defined will display a delete | ||
icon which changes appearance on hover. | ||
|
||
{{demo='demos/chips/Chips.js'}} | ||
|
||
## Chip array | ||
An example of rendering multiple Chips from an array of values. | ||
Deleting a chip removes it from the array. Note that since no | ||
`onClick` property is defined, the Chip can be focused, but does not | ||
gain depth while clicked or touched. | ||
|
||
{{demo='demos/chips/ChipsArray.js'}} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ | |
import React, { PropTypes } from 'react'; | ||
import { createStyleSheet } from 'jss-theme-reactor'; | ||
import classNames from 'classnames'; | ||
import { emphasize } from '../styles/colorManipulator'; | ||
|
||
export const styleSheet = createStyleSheet('Avatar', (theme) => { | ||
const { palette } = theme; | ||
|
@@ -14,13 +15,14 @@ export const styleSheet = createStyleSheet('Avatar', (theme) => { | |
justifyContent: 'center', | ||
width: 40, | ||
height: 40, | ||
fontSize: 24, | ||
fontSize: 20, | ||
borderRadius: '50%', | ||
overflow: 'hidden', | ||
userSelect: 'none', | ||
}, | ||
defaultColor: { | ||
color: palette.getContrastText(palette.text.disabled), | ||
background: palette.text.disabled, | ||
color: palette.background.default, | ||
backgroundColor: emphasize(palette.background.default, 0.26), | ||
}, | ||
img: { | ||
maxWidth: '100%', | ||
|
@@ -34,8 +36,9 @@ export default function Avatar(props, context) { | |
const { | ||
alt, | ||
className: classNameProp, | ||
children: childrenProp, | ||
childrenClassName: childrenClassNameProp, | ||
component, | ||
icon, | ||
sizes, | ||
src, | ||
srcSet, | ||
|
@@ -44,18 +47,22 @@ export default function Avatar(props, context) { | |
|
||
const classes = context.styleManager.render(styleSheet); | ||
const className = classNames(classes.root, { | ||
[classes.defaultColor]: icon && !src && !srcSet, | ||
[classes.defaultColor]: childrenProp && !src && !srcSet, | ||
}, classNameProp); | ||
|
||
const containerProps = { | ||
className, | ||
...other, | ||
}; | ||
|
||
let children = null; | ||
|
||
if (icon) { | ||
children = icon; | ||
if (childrenProp) { | ||
if (childrenClassNameProp && React.isValidElement(childrenProp)) { | ||
const childrenClassName = classNames(childrenClassNameProp, childrenProp.props.className); | ||
children = React.cloneElement(childrenProp, { className: childrenClassName }); | ||
} else { | ||
children = childrenProp; | ||
} | ||
} else if (src || srcSet) { | ||
const imgProps = { | ||
alt, | ||
|
@@ -76,18 +83,28 @@ Avatar.propTypes = { | |
* provide an alt attribute for the rendered `img` element. | ||
*/ | ||
alt: PropTypes.string, | ||
/** | ||
* Used to render icon or text elements inside the Avatar. | ||
* `src` and `alt` props will not be used and no `img` will | ||
* be rendered by default. | ||
* | ||
* This can be an element, or just a string. | ||
*/ | ||
children: PropTypes.node, | ||
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. What was the issue with the icon property? I mean, we could have simply renamed it children. Why keeping both or do we need a different logic? 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 keeping both, I just didn't want to barge in and change the API without discussing it first, so it left both options in place until everyone was agreed. I'll strip it back to just |
||
/** | ||
* @ignore | ||
* The className of the child element. | ||
* Used by Chip to style the Avatar icon. | ||
*/ | ||
childrenClassName: PropTypes.string, | ||
/** | ||
* The CSS class name of the root element. | ||
*/ | ||
className: PropTypes.string, | ||
component: PropTypes.string, | ||
/** | ||
* Supply a custom icon. `src` and `alt` props will | ||
* not be used and no `img` will be renderd by default. | ||
* | ||
* This can be a custom element, or even just a string. | ||
* The component type of the root element. | ||
*/ | ||
icon: PropTypes.node, | ||
component: PropTypes.string, | ||
/** | ||
* sizes desc | ||
*/ | ||
|
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.
This element is focusable but don't have
:focus
state.Is that expected?