Skip to content
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] Add limitTags prop #20209

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/pages/api-docs/autocomplete.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,15 @@ You can learn more about the difference by [reading this guide](/guides/minimizi
| <span class="prop-name">filterSelectedOptions</span> | <span class="prop-type">bool</span> | <span class="prop-default">false</span> | If `true`, hide the selected options from the list box. |
| <span class="prop-name">forcePopupIcon</span> | <span class="prop-type">'auto'<br>&#124;&nbsp;bool</span> | <span class="prop-default">'auto'</span> | Force the visibility display of the popup icon. |
| <span class="prop-name">freeSolo</span> | <span class="prop-type">bool</span> | <span class="prop-default">false</span> | If `true`, the Autocomplete is free solo, meaning that the user input is not bound to provided options. |
| <span class="prop-name">getLimitTagsText</span> | <span class="prop-type">func</span> | <span class="prop-default">(more) => `+${more}`</span> | The label to display when the tags are truncated (`limitTags`).<br><br>**Signature:**<br>`function(more: number) => ReactNode`<br>*more:* The number of truncated tags. |
| <span class="prop-name">getOptionDisabled</span> | <span class="prop-type">func</span> | | Used to determine the disabled state for a given option. |
| <span class="prop-name">getOptionLabel</span> | <span class="prop-type">func</span> | <span class="prop-default">(x) => x</span> | Used to determine the string value for a given option. It's used to fill the input (and the list box options if `renderOption` is not provided). |
| <span class="prop-name">getOptionSelected</span> | <span class="prop-type">func</span> | | Used to determine if an option is selected. Uses strict equality by default. |
| <span class="prop-name">groupBy</span> | <span class="prop-type">func</span> | | If provided, the options will be grouped under the returned string. The groupBy value is also used as the text for group headings when `renderGroup` is not provided.<br><br>**Signature:**<br>`function(options: T) => string`<br>*options:* The option to group. |
| <span class="prop-name">id</span> | <span class="prop-type">string</span> | | This prop is used to help implement the accessibility logic. If you don't provide this prop. It falls back to a randomly generated id. |
| <span class="prop-name">includeInputInList</span> | <span class="prop-type">bool</span> | <span class="prop-default">false</span> | If `true`, the highlight can move to the input. |
| <span class="prop-name">inputValue</span> | <span class="prop-type">string</span> | | The input value. |
| <span class="prop-name">limitTags</span> | <span class="prop-type">number</span> | <span class="prop-default">-1</span> | The maximum number of tags that will be visible when not focused. Set `-1` to disable the limit. |
| <span class="prop-name">ListboxComponent</span> | <span class="prop-type">elementType</span> | <span class="prop-default">'ul'</span> | The component used to render the listbox. |
| <span class="prop-name">ListboxProps</span> | <span class="prop-type">object</span> | | Props applied to the Listbox element. |
| <span class="prop-name">loading</span> | <span class="prop-type">bool</span> | <span class="prop-default">false</span> | If `true`, the component is in a loading state. |
Expand Down
138 changes: 138 additions & 0 deletions docs/src/pages/components/autocomplete/LimitTags.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/* eslint-disable no-use-before-define */
import React from 'react';
import Autocomplete from '@material-ui/lab/Autocomplete';
import { makeStyles } from '@material-ui/core/styles';
import TextField from '@material-ui/core/TextField';

const useStyles = makeStyles((theme) => ({
root: {
width: 500,
'& > * + *': {
marginTop: theme.spacing(3),
},
},
}));

export default function LimitTags() {
const classes = useStyles();

return (
<div className={classes.root}>
<Autocomplete
multiple
limitTags={2}
id="tags-standard"
options={top100Films}
getOptionLabel={(option) => option.title}
defaultValue={[top100Films[13], top100Films[12], top100Films[11]]}
renderInput={(params) => (
<TextField {...params} variant="outlined" label="limitTags" placeholder="Favorites" />
)}
/>
</div>
);
}

// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top
const top100Films = [
{ title: 'The Shawshank Redemption', year: 1994 },
{ title: 'The Godfather', year: 1972 },
{ title: 'The Godfather: Part II', year: 1974 },
{ title: 'The Dark Knight', year: 2008 },
{ title: '12 Angry Men', year: 1957 },
{ title: "Schindler's List", year: 1993 },
{ title: 'Pulp Fiction', year: 1994 },
{ title: 'The Lord of the Rings: The Return of the King', year: 2003 },
{ title: 'The Good, the Bad and the Ugly', year: 1966 },
{ title: 'Fight Club', year: 1999 },
{ title: 'The Lord of the Rings: The Fellowship of the Ring', year: 2001 },
{ title: 'Star Wars: Episode V - The Empire Strikes Back', year: 1980 },
{ title: 'Forrest Gump', year: 1994 },
{ title: 'Inception', year: 2010 },
{ title: 'The Lord of the Rings: The Two Towers', year: 2002 },
{ title: "One Flew Over the Cuckoo's Nest", year: 1975 },
{ title: 'Goodfellas', year: 1990 },
{ title: 'The Matrix', year: 1999 },
{ title: 'Seven Samurai', year: 1954 },
{ title: 'Star Wars: Episode IV - A New Hope', year: 1977 },
{ title: 'City of God', year: 2002 },
{ title: 'Se7en', year: 1995 },
{ title: 'The Silence of the Lambs', year: 1991 },
{ title: "It's a Wonderful Life", year: 1946 },
{ title: 'Life Is Beautiful', year: 1997 },
{ title: 'The Usual Suspects', year: 1995 },
{ title: 'Léon: The Professional', year: 1994 },
{ title: 'Spirited Away', year: 2001 },
{ title: 'Saving Private Ryan', year: 1998 },
{ title: 'Once Upon a Time in the West', year: 1968 },
{ title: 'American History X', year: 1998 },
{ title: 'Interstellar', year: 2014 },
{ title: 'Casablanca', year: 1942 },
{ title: 'City Lights', year: 1931 },
{ title: 'Psycho', year: 1960 },
{ title: 'The Green Mile', year: 1999 },
{ title: 'The Intouchables', year: 2011 },
{ title: 'Modern Times', year: 1936 },
{ title: 'Raiders of the Lost Ark', year: 1981 },
{ title: 'Rear Window', year: 1954 },
{ title: 'The Pianist', year: 2002 },
{ title: 'The Departed', year: 2006 },
{ title: 'Terminator 2: Judgment Day', year: 1991 },
{ title: 'Back to the Future', year: 1985 },
{ title: 'Whiplash', year: 2014 },
{ title: 'Gladiator', year: 2000 },
{ title: 'Memento', year: 2000 },
{ title: 'The Prestige', year: 2006 },
{ title: 'The Lion King', year: 1994 },
{ title: 'Apocalypse Now', year: 1979 },
{ title: 'Alien', year: 1979 },
{ title: 'Sunset Boulevard', year: 1950 },
{ title: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', year: 1964 },
{ title: 'The Great Dictator', year: 1940 },
{ title: 'Cinema Paradiso', year: 1988 },
{ title: 'The Lives of Others', year: 2006 },
{ title: 'Grave of the Fireflies', year: 1988 },
{ title: 'Paths of Glory', year: 1957 },
{ title: 'Django Unchained', year: 2012 },
{ title: 'The Shining', year: 1980 },
{ title: 'WALL·E', year: 2008 },
{ title: 'American Beauty', year: 1999 },
{ title: 'The Dark Knight Rises', year: 2012 },
{ title: 'Princess Mononoke', year: 1997 },
{ title: 'Aliens', year: 1986 },
{ title: 'Oldboy', year: 2003 },
{ title: 'Once Upon a Time in America', year: 1984 },
{ title: 'Witness for the Prosecution', year: 1957 },
{ title: 'Das Boot', year: 1981 },
{ title: 'Citizen Kane', year: 1941 },
{ title: 'North by Northwest', year: 1959 },
{ title: 'Vertigo', year: 1958 },
{ title: 'Star Wars: Episode VI - Return of the Jedi', year: 1983 },
{ title: 'Reservoir Dogs', year: 1992 },
{ title: 'Braveheart', year: 1995 },
{ title: 'M', year: 1931 },
{ title: 'Requiem for a Dream', year: 2000 },
{ title: 'Amélie', year: 2001 },
{ title: 'A Clockwork Orange', year: 1971 },
{ title: 'Like Stars on Earth', year: 2007 },
{ title: 'Taxi Driver', year: 1976 },
{ title: 'Lawrence of Arabia', year: 1962 },
{ title: 'Double Indemnity', year: 1944 },
{ title: 'Eternal Sunshine of the Spotless Mind', year: 2004 },
{ title: 'Amadeus', year: 1984 },
{ title: 'To Kill a Mockingbird', year: 1962 },
{ title: 'Toy Story 3', year: 2010 },
{ title: 'Logan', year: 2017 },
{ title: 'Full Metal Jacket', year: 1987 },
{ title: 'Dangal', year: 2016 },
{ title: 'The Sting', year: 1973 },
{ title: '2001: A Space Odyssey', year: 1968 },
{ title: "Singin' in the Rain", year: 1952 },
{ title: 'Toy Story', year: 1995 },
{ title: 'Bicycle Thieves', year: 1948 },
{ title: 'The Kid', year: 1921 },
{ title: 'Inglourious Basterds', year: 2009 },
{ title: 'Snatch', year: 2000 },
{ title: '3 Idiots', year: 2009 },
{ title: 'Monty Python and the Holy Grail', year: 1975 },
];
140 changes: 140 additions & 0 deletions docs/src/pages/components/autocomplete/LimitTags.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/* eslint-disable no-use-before-define */
import React from 'react';
import Autocomplete from '@material-ui/lab/Autocomplete';
import { makeStyles, createStyles, Theme } from '@material-ui/core/styles';
import TextField from '@material-ui/core/TextField';

const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
width: 500,
'& > * + *': {
marginTop: theme.spacing(3),
},
},
}),
);

export default function LimitTags() {
const classes = useStyles();

return (
<div className={classes.root}>
<Autocomplete
multiple
limitTags={2}
id="tags-standard"
options={top100Films}
getOptionLabel={(option) => option.title}
defaultValue={[top100Films[13], top100Films[12], top100Films[11]]}
renderInput={(params) => (
<TextField {...params} variant="outlined" label="limitTags" placeholder="Favorites" />
)}
/>
</div>
);
}

// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top
const top100Films = [
{ title: 'The Shawshank Redemption', year: 1994 },
{ title: 'The Godfather', year: 1972 },
{ title: 'The Godfather: Part II', year: 1974 },
{ title: 'The Dark Knight', year: 2008 },
{ title: '12 Angry Men', year: 1957 },
{ title: "Schindler's List", year: 1993 },
{ title: 'Pulp Fiction', year: 1994 },
{ title: 'The Lord of the Rings: The Return of the King', year: 2003 },
{ title: 'The Good, the Bad and the Ugly', year: 1966 },
{ title: 'Fight Club', year: 1999 },
{ title: 'The Lord of the Rings: The Fellowship of the Ring', year: 2001 },
{ title: 'Star Wars: Episode V - The Empire Strikes Back', year: 1980 },
{ title: 'Forrest Gump', year: 1994 },
{ title: 'Inception', year: 2010 },
{ title: 'The Lord of the Rings: The Two Towers', year: 2002 },
{ title: "One Flew Over the Cuckoo's Nest", year: 1975 },
{ title: 'Goodfellas', year: 1990 },
{ title: 'The Matrix', year: 1999 },
{ title: 'Seven Samurai', year: 1954 },
{ title: 'Star Wars: Episode IV - A New Hope', year: 1977 },
{ title: 'City of God', year: 2002 },
{ title: 'Se7en', year: 1995 },
{ title: 'The Silence of the Lambs', year: 1991 },
{ title: "It's a Wonderful Life", year: 1946 },
{ title: 'Life Is Beautiful', year: 1997 },
{ title: 'The Usual Suspects', year: 1995 },
{ title: 'Léon: The Professional', year: 1994 },
{ title: 'Spirited Away', year: 2001 },
{ title: 'Saving Private Ryan', year: 1998 },
{ title: 'Once Upon a Time in the West', year: 1968 },
{ title: 'American History X', year: 1998 },
{ title: 'Interstellar', year: 2014 },
{ title: 'Casablanca', year: 1942 },
{ title: 'City Lights', year: 1931 },
{ title: 'Psycho', year: 1960 },
{ title: 'The Green Mile', year: 1999 },
{ title: 'The Intouchables', year: 2011 },
{ title: 'Modern Times', year: 1936 },
{ title: 'Raiders of the Lost Ark', year: 1981 },
{ title: 'Rear Window', year: 1954 },
{ title: 'The Pianist', year: 2002 },
{ title: 'The Departed', year: 2006 },
{ title: 'Terminator 2: Judgment Day', year: 1991 },
{ title: 'Back to the Future', year: 1985 },
{ title: 'Whiplash', year: 2014 },
{ title: 'Gladiator', year: 2000 },
{ title: 'Memento', year: 2000 },
{ title: 'The Prestige', year: 2006 },
{ title: 'The Lion King', year: 1994 },
{ title: 'Apocalypse Now', year: 1979 },
{ title: 'Alien', year: 1979 },
{ title: 'Sunset Boulevard', year: 1950 },
{ title: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', year: 1964 },
{ title: 'The Great Dictator', year: 1940 },
{ title: 'Cinema Paradiso', year: 1988 },
{ title: 'The Lives of Others', year: 2006 },
{ title: 'Grave of the Fireflies', year: 1988 },
{ title: 'Paths of Glory', year: 1957 },
{ title: 'Django Unchained', year: 2012 },
{ title: 'The Shining', year: 1980 },
{ title: 'WALL·E', year: 2008 },
{ title: 'American Beauty', year: 1999 },
{ title: 'The Dark Knight Rises', year: 2012 },
{ title: 'Princess Mononoke', year: 1997 },
{ title: 'Aliens', year: 1986 },
{ title: 'Oldboy', year: 2003 },
{ title: 'Once Upon a Time in America', year: 1984 },
{ title: 'Witness for the Prosecution', year: 1957 },
{ title: 'Das Boot', year: 1981 },
{ title: 'Citizen Kane', year: 1941 },
{ title: 'North by Northwest', year: 1959 },
{ title: 'Vertigo', year: 1958 },
{ title: 'Star Wars: Episode VI - Return of the Jedi', year: 1983 },
{ title: 'Reservoir Dogs', year: 1992 },
{ title: 'Braveheart', year: 1995 },
{ title: 'M', year: 1931 },
{ title: 'Requiem for a Dream', year: 2000 },
{ title: 'Amélie', year: 2001 },
{ title: 'A Clockwork Orange', year: 1971 },
{ title: 'Like Stars on Earth', year: 2007 },
{ title: 'Taxi Driver', year: 1976 },
{ title: 'Lawrence of Arabia', year: 1962 },
{ title: 'Double Indemnity', year: 1944 },
{ title: 'Eternal Sunshine of the Spotless Mind', year: 2004 },
{ title: 'Amadeus', year: 1984 },
{ title: 'To Kill a Mockingbird', year: 1962 },
{ title: 'Toy Story 3', year: 2010 },
{ title: 'Logan', year: 2017 },
{ title: 'Full Metal Jacket', year: 1987 },
{ title: 'Dangal', year: 2016 },
{ title: 'The Sting', year: 1973 },
{ title: '2001: A Space Odyssey', year: 1968 },
{ title: "Singin' in the Rain", year: 1952 },
{ title: 'Toy Story', year: 1995 },
{ title: 'Bicycle Thieves', year: 1948 },
{ title: 'The Kid', year: 1921 },
{ title: 'Inglourious Basterds', year: 2009 },
{ title: 'Snatch', year: 2000 },
{ title: '3 Idiots', year: 2009 },
{ title: 'Monty Python and the Holy Grail', year: 1975 },
];
6 changes: 6 additions & 0 deletions docs/src/pages/components/autocomplete/autocomplete.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,12 @@ In the event that you need to lock certain tag so that they can't be removed in

{{"demo": "pages/components/autocomplete/CheckboxesTags.js"}}

### Limit tags

You can use the `limitTags` prop to limit the number of displayed options when not focused.

{{"demo": "pages/components/autocomplete/LimitTags.js"}}

## Sizes

Fancy smaller inputs? Use the `size` prop.
Expand Down
12 changes: 12 additions & 0 deletions packages/material-ui-lab/src/Autocomplete/Autocomplete.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,13 @@ export interface AutocompleteProps<T>
* Force the visibility display of the popup icon.
*/
forcePopupIcon?: true | false | 'auto';
/**
* The label to display when the tags are truncated (`limitTags`).
*
* @param {number} more The number of truncated tags.
* @returns {ReactNode}
*/
getLimitTagsText?: (more: number) => React.ReactNode;
/**
* The component used to render the listbox.
*/
Expand All @@ -101,6 +108,11 @@ export interface AutocompleteProps<T>
* For localization purposes, you can use the provided [translations](/guides/localization/).
*/
loadingText?: React.ReactNode;
/**
* The maximum number of tags that will be visible when not focused.
* Set `-1` to disable the limit.
*/
limitTags?: number;
/**
* Text to display when there are no options.
*
Expand Down
26 changes: 26 additions & 0 deletions packages/material-ui-lab/src/Autocomplete/Autocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,13 +257,15 @@ const Autocomplete = React.forwardRef(function Autocomplete(props, ref) {
filterSelectedOptions = false,
forcePopupIcon = 'auto',
freeSolo = false,
getLimitTagsText = (more) => `+${more}`,
getOptionDisabled,
getOptionLabel = (x) => x,
getOptionSelected,
groupBy,
id: idProp,
includeInputInList = false,
inputValue: inputValueProp,
limitTags = -1,
ListboxComponent = 'ul',
ListboxProps,
loading = false,
Expand Down Expand Up @@ -340,6 +342,18 @@ const Autocomplete = React.forwardRef(function Autocomplete(props, ref) {
}
}

if (limitTags > -1 && Array.isArray(startAdornment)) {
const more = startAdornment.length - limitTags;
if (limitTags && !focused && more > 0) {
startAdornment = startAdornment.splice(0, limitTags);
startAdornment.push(
<span className={classes.tag} key={startAdornment.length}>
{getLimitTagsText(more)}
</span>,
);
}
}

const defaultRenderGroup = (params) => (
<li key={params.key}>
<ListSubheader className={classes.groupLabel} component="div">
Expand Down Expand Up @@ -592,6 +606,13 @@ Autocomplete.propTypes = {
* If `true`, the Autocomplete is free solo, meaning that the user input is not bound to provided options.
*/
freeSolo: PropTypes.bool,
/**
* The label to display when the tags are truncated (`limitTags`).
*
* @param {number} more The number of truncated tags.
* @returns {ReactNode}
*/
getLimitTagsText: PropTypes.func,
/**
* Used to determine the disabled state for a given option.
*/
Expand Down Expand Up @@ -627,6 +648,11 @@ Autocomplete.propTypes = {
* The input value.
*/
inputValue: PropTypes.string,
/**
* The maximum number of tags that will be visible when not focused.
* Set `-1` to disable the limit.
*/
limitTags: PropTypes.number,
/**
* The component used to render the listbox.
*/
Expand Down
Loading