diff --git a/docs/src/pages/components/autocomplete/CustomizedHook.js b/docs/src/pages/components/autocomplete/CustomizedHook.js new file mode 100644 index 00000000000000..eb0e08e8e71f81 --- /dev/null +++ b/docs/src/pages/components/autocomplete/CustomizedHook.js @@ -0,0 +1,275 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import useAutocomplete from '@material-ui/lab/useAutocomplete'; +import CheckIcon from '@material-ui/icons/Check'; +import CloseIcon from '@material-ui/icons/Close'; +import styled from 'styled-components'; + +const Label = styled('label')` + padding: 0 0 4px; + line-height: 1.5; + display: block; +`; + +const InputWrapper = styled('div')` + width: 300px; + border: 1px solid #d9d9d9; + background-color: #fff; + border-radius: 4px; + padding: 1px; + display: flex; + flex-wrap: wrap; + + &:hover { + border-color: #40a9ff; + } + + &.focused { + border-color: #40a9ff; + box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2); + } + + & input { + font-size: 14px; + line-height: 26px; + padding: 2px 6px; + flex-grow: 1; + border: 0; + outline: 0; + } +`; + +const Tag = styled(({ label, onDelete, ...props }) => ( +
+ {label} + +
+))` + display: flex; + align-items: center; + height: 24px; + margin: 2px; + line-height: 22px; + background-color: #fafafa; + border: 1px solid #e8e8e8; + border-radius: 2px; + box-sizing: content-box; + padding: 0 4px 0 10px; + outline: 0; + overflow: hidden; + + &:focus { + border-color: #40a9ff; + background-color: #e6f7ff; + } + + & span { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + & svg { + font-size: 12px; + cursor: pointer; + padding: 4px; + } +`; + +const Listbox = styled('ul')` + width: 300px; + margin: 0; + margin-top: 2px; + padding: 0; + position: absolute; + list-style: none; + background-color: #fff; + overflow: auto; + max-height: 250px; + border-radius: 4px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + z-index: 1; + + & li { + padding: 5px 12px; + display: flex; + & span { + flex-grow: 1; + } + & svg { + color: transparent; + } + } + + & li[aria-selected='true'] { + background-color: #fafafa; + font-weight: 600; + & svg { + color: #1890ff; + } + } + + & li[data-focus='true'] { + background-color: #e6f7ff; + cursor: pointer; + & svg { + color: #000; + } + } +`; + +export default function CustomizedHook() { + const { + getComboboxProps, + getInputLabelProps, + getInputProps, + getTagProps, + getListboxProps, + getOptionProps, + groupedOptions, + value, + focused, + focusedTag, + setAnchorEl, + } = useAutocomplete({ + multiple: true, + debug: true, + options: top100Films, + getOptionLabel: option => option.title, + }); + + return ( +
+
+ + + {value.map((option, index) => ( + + ))} + + + +
+ {groupedOptions.length > 0 ? ( + + {groupedOptions.map((option, index) => ( +
  • + {option.title} + +
  • + ))} +
    + ) : null} +
    + ); +} + +// 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 }, +]; diff --git a/docs/src/pages/components/autocomplete/CustomizedHook.tsx b/docs/src/pages/components/autocomplete/CustomizedHook.tsx new file mode 100644 index 00000000000000..71a1702e18b2ed --- /dev/null +++ b/docs/src/pages/components/autocomplete/CustomizedHook.tsx @@ -0,0 +1,278 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import useAutocomplete from '@material-ui/lab/useAutocomplete'; +import CheckIcon from '@material-ui/icons/Check'; +import CloseIcon from '@material-ui/icons/Close'; +import styled from 'styled-components'; + +const Label = styled('label')` + padding: 0 0 4px; + line-height: 1.5; + display: block; +`; + +const InputWrapper = styled('div')` + width: 300px; + border: 1px solid #d9d9d9; + background-color: #fff; + border-radius: 4px; + padding: 1px; + display: flex; + flex-wrap: wrap; + + &:hover { + border-color: #40a9ff; + } + + &.focused { + border-color: #40a9ff; + box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2); + } + + & input { + font-size: 14px; + line-height: 26px; + padding: 2px 6px; + flex-grow: 1; + border: 0; + outline: 0; + } +`; + +const Tag = styled(({ label, onDelete, ...props }) => ( +
    + {label} + +
    +))` + display: flex; + align-items: center; + height: 24px; + margin: 2px; + line-height: 22px; + background-color: #fafafa; + border: 1px solid #e8e8e8; + border-radius: 2px; + box-sizing: content-box; + padding: 0 4px 0 10px; + outline: 0; + overflow: hidden; + + &:focus { + border-color: #40a9ff; + background-color: #e6f7ff; + } + + & span { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + & svg { + font-size: 12px; + cursor: pointer; + padding: 4px; + } +`; + +const Listbox = styled('ul')` + width: 300px; + margin: 0; + margin-top: 2px; + padding: 0; + position: absolute; + list-style: none; + background-color: #fff; + overflow: auto; + max-height: 250px; + border-radius: 4px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + z-index: 1; + + & li { + padding: 5px 12px; + display: flex; + + & span { + flex-grow: 1; + } + + & svg { + color: transparent; + } + } + + & li[aria-selected='true'] { + background-color: #fafafa; + font-weight: 600; + + & svg { + color: #1890ff; + } + } + + & li[data-focus='true'] { + background-color: #e6f7ff; + cursor: pointer; + + & svg { + color: #000; + } + } +`; + +export default function CustomizedHook() { + const { + getComboboxProps, + getInputLabelProps, + getInputProps, + getTagProps, + getListboxProps, + getOptionProps, + groupedOptions, + value, + focused, + focusedTag, + setAnchorEl, + } = useAutocomplete({ + multiple: true, + debug: true, + options: top100Films, + getOptionLabel: option => option.title, + }); + + return ( +
    +
    + + + {value.map((option, index) => ( + + ))} + + +
    + {groupedOptions.length > 0 ? ( + + {groupedOptions.map((option, index) => ( +
  • + {option.title} + +
  • + ))} +
    + ) : null} +
    + ); +} + +// 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 }, +]; diff --git a/docs/src/pages/components/autocomplete/GitHubLabel.js b/docs/src/pages/components/autocomplete/GitHubLabel.js index 3c1544201fe50b..2c4c9518ad60ef 100644 --- a/docs/src/pages/components/autocomplete/GitHubLabel.js +++ b/docs/src/pages/components/autocomplete/GitHubLabel.js @@ -41,7 +41,6 @@ const useStyles = makeStyles(theme => ({ borderRadius: 2, }, popper: { - backgroundColor: theme.palette.background.paper, border: '1px solid rgba(27,31,35,.15)', boxShadow: '0 3px 12px rgba(27,31,35,.15)', borderRadius: 3, @@ -49,15 +48,14 @@ const useStyles = makeStyles(theme => ({ zIndex: 1, fontSize: 13, color: '#586069', + backgroundColor: '#f6f8fa', }, header: { - backgroundColor: '#f6f8fa', borderBottom: '1px solid #e1e4e8', padding: '8px 10px', fontWeight: 600, }, inputBase: { - backgroundColor: '#f6f8fa', padding: 10, width: '100%', borderBottom: '1px solid #dfe2e5', @@ -90,6 +88,9 @@ const useStyles = makeStyles(theme => ({ backgroundColor: theme.palette.action.hover, }, }, + popperDisablePortal: { + position: 'relative', + }, iconSelected: { width: 17, height: 17, @@ -174,9 +175,11 @@ export default function GitHubLabel() { open onClose={handleClose} multiple + debug classes={{ paper: classes.paper, option: classes.option, + popperDisablePortal: classes.popperDisablePortal, }} value={pendingValue} onChange={(event, newValue) => { diff --git a/docs/src/pages/components/autocomplete/GitHubLabel.tsx b/docs/src/pages/components/autocomplete/GitHubLabel.tsx index 240c4d70c7a6c7..d082cc0fe1d18f 100644 --- a/docs/src/pages/components/autocomplete/GitHubLabel.tsx +++ b/docs/src/pages/components/autocomplete/GitHubLabel.tsx @@ -42,7 +42,6 @@ const useStyles = makeStyles((theme: Theme) => borderRadius: 2, }, popper: { - backgroundColor: theme.palette.background.paper, border: '1px solid rgba(27,31,35,.15)', boxShadow: '0 3px 12px rgba(27,31,35,.15)', borderRadius: 3, @@ -50,15 +49,14 @@ const useStyles = makeStyles((theme: Theme) => zIndex: 1, fontSize: 13, color: '#586069', + backgroundColor: '#f6f8fa', }, header: { - backgroundColor: '#f6f8fa', borderBottom: '1px solid #e1e4e8', padding: '8px 10px', fontWeight: 600, }, inputBase: { - backgroundColor: '#f6f8fa', padding: 10, width: '100%', borderBottom: '1px solid #dfe2e5', @@ -91,6 +89,9 @@ const useStyles = makeStyles((theme: Theme) => backgroundColor: theme.palette.action.hover, }, }, + popperDisablePortal: { + position: 'relative', + }, iconSelected: { width: 17, height: 17, @@ -176,9 +177,11 @@ export default function GitHubLabel() { open onClose={handleClose} multiple + debug classes={{ paper: classes.paper, option: classes.option, + popperDisablePortal: classes.popperDisablePortal, }} value={pendingValue} onChange={(event, newValue) => { diff --git a/docs/src/pages/components/autocomplete/UseAutocomplete.js b/docs/src/pages/components/autocomplete/UseAutocomplete.js index f58a7c51ee85b3..aef20ed6d42172 100644 --- a/docs/src/pages/components/autocomplete/UseAutocomplete.js +++ b/docs/src/pages/components/autocomplete/UseAutocomplete.js @@ -4,9 +4,6 @@ import useAutocomplete from '@material-ui/lab/useAutocomplete'; import { makeStyles } from '@material-ui/core/styles'; const useStyles = makeStyles(theme => ({ - root: { - position: 'relative', - }, input: { width: 200, }, @@ -14,6 +11,7 @@ const useStyles = makeStyles(theme => ({ width: 200, margin: 0, padding: 0, + zIndex: 1, position: 'absolute', listStyle: 'none', backgroundColor: theme.palette.background.paper, @@ -36,8 +34,8 @@ export default function UseAutocomplete() { const classes = useStyles(); const { getComboboxProps, - getInputProps, getInputLabelProps, + getInputProps, getListboxProps, getOptionProps, groupedOptions, @@ -47,7 +45,7 @@ export default function UseAutocomplete() { }); return ( -
    +

    diff --git a/docs/src/pages/components/autocomplete/UseAutocomplete.tsx b/docs/src/pages/components/autocomplete/UseAutocomplete.tsx index 13fc3ec5045c17..09edf68f6986bf 100644 --- a/docs/src/pages/components/autocomplete/UseAutocomplete.tsx +++ b/docs/src/pages/components/autocomplete/UseAutocomplete.tsx @@ -5,9 +5,6 @@ import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; const useStyles = makeStyles((theme: Theme) => createStyles({ - root: { - position: 'relative', - }, input: { width: 200, }, @@ -15,6 +12,7 @@ const useStyles = makeStyles((theme: Theme) => width: 200, margin: 0, padding: 0, + zIndex: 1, position: 'absolute', listStyle: 'none', backgroundColor: theme.palette.background.paper, @@ -38,8 +36,8 @@ export default function UseAutocomplete() { const classes = useStyles(); const { getComboboxProps, - getInputProps, getInputLabelProps, + getInputProps, getListboxProps, getOptionProps, groupedOptions, @@ -49,7 +47,7 @@ export default function UseAutocomplete() { }); return ( -
    +

    diff --git a/docs/src/pages/components/autocomplete/autocomplete.md b/docs/src/pages/components/autocomplete/autocomplete.md index 86ff7e297d70b1..e618b32b43b88f 100644 --- a/docs/src/pages/components/autocomplete/autocomplete.md +++ b/docs/src/pages/components/autocomplete/autocomplete.md @@ -59,9 +59,9 @@ import useAutocomplete from '@material-ui/lab/useAutocomplete'; {{"demo": "pages/components/autocomplete/UseAutocomplete.js", "defaultCodeOpen": false}} -### Customized useAutocomplete +### Customized hook -WIP: to implement [this design](https://www.behance.net/gallery/27997595/Multi-select-dropdown-tags-field-with-search). +{{"demo": "pages/components/autocomplete/CustomizedHook.js"}} Head to [Customized Autocomplete](#customized-autocomplete) for a customization example with the Autocomplete component instead of the hook.