-
-
Notifications
You must be signed in to change notification settings - Fork 32.4k
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
[Autocomplete] Multiselection with Chips #4952
Comments
i have some spare time at the moment... trying to implement "multiple" prop on the Autocomplete component, following my analysis @ #1956 It currently returns an array of objects [{text: "selectedOption1", value: "id1"}, {text: "selectedOption2", value: "id2"}, ... ] but i'm having trouble with state.focusTextField preventing a lot of behaviors i wish for the extended component... if someone could tell me where to post my WIP to help me with this, things might go faster. |
You could fork the repo, create a new branch and push to it and reference it here so anybody could have a look at. |
Yes me too i expect to use Chips to display selected options, but main difference is Chips won't be stacked directly inside the main input of the Autocomplete, but instead the dev will have to reuse the returned array of results to display them as Chips in an external component (a multiline textarea for example) Why? Currently i'm using a fork of react-select-popover: the chips stacking up in the input makes the surrounding UI stretch/wrap... This does not give the feel of a controlled, clean Material UX :s EDIT: here for an initial fork |
Looks like we have something very close made by @leMaik https://github.com/TeamWertarbyte/material-ui-chip-input ✨ . |
@oliviertassinari Do you think it makes sense to integrate the component into material-ui? |
@davincho Integrating this component was also suggested here by a user. |
|
@mbrookes Is right, we need to rewrite the @davincho @leMaik I don't really mind about integrating material-ui-chip-input or not inside Material-UI. Either way is great from my point of view. As long as, material-ui-chip-input is discoverable enough. Building a community around the low-level Material-UI components is a good things to aim at. What I really care about, is the simple fact that material-ui-chip-input project was made possible by the composition approach. |
@davincho @oliviertassinari I'd keep the seperate project then. Having small projects that depend on mui and build a community around material-ui also seems more useful to me than to make material-ui bigger and bigger by adding more and more components. It is already a good base for creating new components by composition. Edit: Sorry for hijacking this thread. 😬 |
@leMaik Great 👍.
True, that's the minimum we can do. |
I'd actually prefer that this be included with material-ui rather than be another dependency I have to add to my project. I think we should try to be on par with the Angular Material library. There's images of this sort of thing in the material.io chip component docs, so all the more reason to include it here in my opinion. Let's make it as easy as possible for developers to build whatever it is they want to build with as little work and as few dependencies as possible. |
@MarkMurphy What's the problem with dependencies? You could either have one very big, everything-dependency (and I don't think that Material-UI should aim to be this library) or you could "outsource" things into seperate projects. I don't really see a difference here, as long as both, the everything-dependency and the small dependencies are well documented. Also, just look at how many issues and PRs I got for the chip input. That is a single, small component. It's much easier to handle that on a per-component basis than it is for one project that does everything. Disclaimer: I implemented material-ui-chip-input, and I like libraries that do one thing and do it well. Regarding the "as little work as possible" part: |
imho, i think Murphy refers to the hassle of searching into multiple different documentations in the case of a project with tons of tierce components/dependencies. But in practice, i think it's better to start those new components in a per-component basis like leMaik explained, then MaterialUI authors can consider making a PR to discuss integration and/or include the component in their lib. Like leMaik, i also implemented my own component merging DropdownMenu, SelectField, and Autocomplete with chips. It's still missing integration of react-virtualized for heavy datalists. Now it's up to MaterialUI authors to contat me or leMaik if they want to integrate our components into materialui@next |
@Sharlaan I'd prefer to merge the documentation instead of the code. Wouldn't it be cool to keep the code separate but have a single source of documentation for Material-UI and 3rd-party components? That could be the best of both worlds: Less searching for documentation and components while still maintaining every component on its own. |
@Sharlaan Yes, valid point. @leMaik Nothing wrong with dependencies but the more you have the harder it is to maintain when you need to update them and later on reason about when you ask yourself why you installed them in the first place. Second, when I found material-ui, and read through material.io I was disappointed that the component you built (but existed in the Angular version) wasn't available. After some googling around I found this issue and then your component (nicely done 👍 ). You're right, it's not a big deal but it's more than just If it was bundled with material-ui none of those things are an issue anymore. The size of material-ui doesn't bother me at all. Even if I'm only using a single component, that's all that gets bundled come build and deploy time. Also, being in the same repo means existing contributors have access. That's another plus in my opinion. |
I would say that if it's included or shown in material.io documentation, it should be included in material-ui |
@MarkMurphy The point is: When it's inside of Material-UI, I actually don't have access to the component anymore. I'd have to PR every change and filter out the issues of Material-UI, making maintenance a little harder. I agree that it should be easier to find components, see my previous comment. |
True it's annoying to PR every change and waiting for author thoughts/approval .... Then how about applying to MaterialUI maintainer team XD ? |
How do other projects handle this, is there a way to do both? I think Ruby on Rails is kind of a combination of both. But obviously that's a bit different |
@Sharlaan I still don't see a point in merging every component inside this project. The bigger it gets, the harder it is to maintain.
I would say that if it's included or shown in material.io documentation, it should be implemented in Material-UI or at least be linked in the documentation of Material-UI. |
@leMaik In my opinion it would be even harder to maintain having what I consider core components distributed elsewhere. |
@MarkMurphy For me (a maintainer), it would be harder to maintain if it wasn't my project. Above, you were on the user's side, and I totally agree that it's harder to use many dependencies. I don't really regard the chip input as a core component. The chip and the text field are core components, that can be used to build other stuff (e.g. a chip input). Edit: I think that we should wait for the Material-UI maintainers to say what they think. 👍 |
er.... little correction leMaik: this topic is about Autocomplete with multiselect feature (chips or not is secondary ?) I agree this topic (Autocomplete with multiselect) should be a core feature., and thus should be integrated into material-ui. But as a maintainer, it's much easier and faster to fork it (than PR'ing every change), and then simply ask MaterialUI to add a link in their main site. For instance, MarkMurphy if you are here, i bet you are probably searching for an autocomplete component with multiselect ? |
@Sharlaan Yep, but then @oliviertassinari said that there was material-ui-chip input and @MarkMurphy wanted it to be included in Material-UI itself (if I got it right). The thing is (imho): "Autocomplete with multiselect" is a chip input. Or how would you implement it? |
I'm considering Angular Material the gold standard by which to compare. @Sharlaan I'm actually looking for an autocomplete multiselect contact chip component like the one shown in material.io docs:
|
@Sharlaan using Material ui should seriously take care of this component! |
i know the rerender issue, but i were refering to class properties (check method4). It's currently Stage-2. Regarding DatePicker, how about this one ? which feature(s) is it missing ? (i never used yet) |
I did not know this syntax, thanks for the discovery @Sharlaan. |
@GuillaumeCisco You seem to have created a great chip input component and a DateTime component and think that Material UI should take care of them? 👍 Why don't you submit a PR? |
Btw, material-ui@next switched from inlined styles to JSS. |
Hey @leMaik, I did not created a chip input component in the material ui way, just used another library : react-select. And absolutely yes! Material UI should take care of them! It is really important, the need is here! For my own needs, I needed a Clearable DateTime Picker, so I ended up creating several files. Clearable component (HOC) import React, {PropTypes} from 'react';
import IconButton from 'material-ui/IconButton';
import Clear from 'material-ui/svg-icons/content/clear';
const Clearable = (ComposedComponent, clearStyle) => {
class Component extends React.Component {
constructor(props) {
super(props);
this.clear = this.clear.bind(this);
this.clearStyle = {
display: 'inline-block',
verticalAlign: 'middle',
marginLeft: '4px',
padding: '0',
width: '24px',
height: '24px',
...clearStyle,
};
}
clear(event) {
event.preventDefault();
this.component.clear();
}
render() {
const {value, style} = this.props;
return (
<div style={{position: 'relative', ...style}}>
<ComposedComponent
ref={(c) => {
this.component = c;
}} {...this.props}
style={{width: 'calc(100% - 28px)'}}
/>
{value &&
<IconButton
onClick={this.clear}
style={this.clearStyle}
>
<Clear />
</IconButton>
}
</div>
);
}
}
Component.propTypes = {
value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.shape({}),
]),
style: PropTypes.shape({}),
};
return Component;
};
export default Clearable; Dumb Date component import React, {PropTypes} from 'react';
import DatePicker from 'material-ui/DatePicker';
const Date = (props) => {
const {value, onChange, label, style} = props;
return (
<DatePicker
autoOk
container="inline"
hintText={label}
floatingLabelText={label}
onChange={onChange}
value={value || null}
style={{width: 100, display: 'inline-block', ...style}}
textFieldStyle={{width: 100}}
/>);
};
Date.propTypes = {
value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.shape({}),
]),
label: PropTypes.string,
onChange: PropTypes.func,
style: PropTypes.shape({}),
};
export default Date; dumb Time component import React, {PropTypes} from 'react';
import TimePicker from 'material-ui/TimePicker';
import {isValid, parse, setHours, setMinutes} from 'date-fns';
const Time = (props) => {
const {value, label, onChange} = props;
return (
<TimePicker
autoOk
format="24hr"
hintText={label}
floatingLabelText={label}
onChange={onChange}
value={value === '' || !value ?
null :
(!isValid(parse(value)) ?
setHours(setMinutes(new Date(), value.split(':')[1]), value.split(':')[0]) :
parse(value))
}
style={{width: 50, display: 'inline-block'}}
textFieldStyle={{width: 50}}
/>
);
};
Time.propTypes = {
value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.shape({}),
]),
label: PropTypes.string,
onChange: PropTypes.func,
};
export default Time; Clearable Date Picker: import React, {PropTypes} from 'react';
import Clearable from './Clearable';
import Date from '../../presentation/filter/date';
class ClearableDatePickerFilter extends React.Component {
constructor(props) {
super(props);
this.clear = this.clear.bind(this);
}
clear() {
this.props.onChange(null, null);
}
render() {
return <Date {...this.props} />;
}
}
ClearableDatePickerFilter.propTypes = {
onChange: PropTypes.func,
};
export default Clearable(ClearableDatePickerFilter); Clearable Time Picker: import React, {PropTypes} from 'react';
import Clearable from './Clearable';
import Time from '../../presentation/filter/time';
class ClearableTimePickerFilter extends React.Component {
constructor(props) {
super(props);
this.clear = this.clear.bind(this);
}
clear() {
this.props.onChange(null, null);
}
render() {
return <Time {...this.props} />;
}
}
ClearableTimePickerFilter.propTypes = {
onChange: PropTypes.func,
};
export default Clearable(ClearableTimePickerFilter); Clearable DateTime Picker: import React, {PropTypes} from 'react';
import {setHours, setMinutes, getMinutes, getHours} from 'date-fns';
import Clearable from './Clearable';
import Date from '../../presentation/filter/date';
import Time from '../../presentation/filter/time';
const containerStyle = {
display: 'inline-block',
verticalAlign: 'middle',
};
class ClearableDateTime extends React.Component {
constructor(props) {
super(props);
this.clear = this.clear.bind(this);
this.onDateChange = this.onDateChange.bind(this);
}
onDateChange(evt, value) {
// we need to keep the time
const v = setHours(setMinutes(value, getMinutes(this.props.value) || 0), getHours(this.props.value) || 0);
return this.props.onChange(evt, v);
}
clear() {
this.props.onChange(null, null);
}
render() {
return (<div style={{...containerStyle, ...this.props.style}}>
<Date {...this.props} onChange={this.onDateChange} style={{}} />
<Time {...this.props} style={{}} />
</div>);
}
}
ClearableDateTime.propTypes = {
value: PropTypes.oneOfType([
PropTypes.instanceOf(Date),
PropTypes.shape({}),
]),
onChange: PropTypes.func,
style: PropTypes.shape({}),
};
export default Clearable(ClearableDateTime, {verticalAlign: -20}); You can even create a Clearable Select with this method, it easier in an UX point of view than putting a null item in the top of the list: Dumb Select import React, {PropTypes} from 'react';
import MenuItem from 'material-ui/MenuItem';
import SelectField from 'material-ui/SelectField';
import LoaderSmall from '../loaders/small';
const Select = (props) => {
const {model, onChange, label, style} = props;
return (
<div style={{display: 'inline-block', ...style}}>
{(model.loading || model.next) && <LoaderSmall />}
{!(model.loading || model.next) &&
<SelectField
hintText={label}
floatingLabelText={label}
value={model.current}
onChange={onChange}
style={{width: '100%', minWidth: 256}}
>
{model.results.map((o, i) =>
<MenuItem key={i} value={o.value} primaryText={o.label} />)
}
</SelectField>
}
</div>);
};
Select.propTypes = {
model: PropTypes.oneOfType([
PropTypes.shape({}),
PropTypes.arrayOf(PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
])),
]),
label: PropTypes.string,
style: PropTypes.shape({}),
onChange: PropTypes.func,
};
export default Select; import React, {PropTypes} from 'react';
import Clearable from './Clearable';
import Select from '../../presentation/filter/select';
class ClearableSelectFilter extends React.Component {
constructor(props) {
super(props);
this.clear = this.clear.bind(this);
}
clear() {
this.setState({
value: null,
}, () => {
this.props.onChange(null, null);
});
}
render() {
return <Select {...this.props} />;
}
}
ClearableSelectFilter.propTypes = {
onChange: PropTypes.func,
};
export default Clearable(ClearableSelectFilter); Please be aware that this code satisfy my needs. It needs more test, refactoring etc. |
@GuillaumeCisco Well, Material-UI sure would take care of those components if somebody would just send a PR to add them! 😄 |
@leMaik i'm wondering how to add
|
@GuillaumeCisco I'm really trying hard (breaking my head over this for several days now) to get what you've suggested to work with https://github.com/reactGo/reactGo |
@slavab89 what are you trying to achieve? |
@GuillaumeCisco Sorry for the late response. In any case, the only way i was able to make it work was to manually copy the whole CSS file of react-select to my project and then put your css over it. That works but its kinda hacky. Any other suggestion? |
@slavab89 Please take this discussion of the Material-UI repo. Thanks! |
For whoever reaches here, i've recently been trying to create this exact behavior and so far i've managed to do that with the great library https://github.com/paypal/downshift I will update the code in the example shown there and hopefully it will be added to the examples of the project. The components in the example are simple HTML ones but they can easily be swapped with |
@slavab89 I am looking for something like your solution. How far with the implementation are you :-)? If you aren't started working on it yet i could give it a try :-) |
@thupi Go ahead 😄 . It will probably be a while before i'll be able to do it with material-ui v1. |
For anyone who's looking for implementation inspiration, Microsoft's office-ui-fabric-react project has a Demo: |
I'm closing the issue as it's basically what the react-select demo provides. I wish the demo was simpler. Let's hope someone from the community will be build something even better. |
Well done @oliviertassinari ! Next step should be to rewrite it in material-ui directly in order not to be dependent of |
@oliviertassinari the demo is is awfully long it should be simplify or just make a components that takes an array of suggestions |
https://github.com/gregchamberlain/react-chips multi section, etc.. |
Description
It would be cool to extend the functionality of the autocomplete component to enable users to select multiple values. An example would be
Contact Chips
on [1].Essentially it would look something like:
Instead of a single value the
AutoComplete
would return an object like{1: {...option1}, 2: {...option2}}
or something similar. Any thoughts/ideas?Images & references
[1] https://material.angularjs.org/latest/demo/chips
The text was updated successfully, but these errors were encountered: