From c1037bd5cb80d898d5208db5614db5bb12e9aaa9 Mon Sep 17 00:00:00 2001 From: "@itelofilho" Date: Tue, 11 Feb 2020 21:54:01 -0300 Subject: [PATCH 1/7] [CreatableAutocomplete]: add a new component to create option in autocomplete [wip] --- .../autocomplete/CreatableAutocomplete.js | 122 ++++++ .../CreatableAutocompleteDialog.js | 166 ++++++++ .../components/autocomplete/autocomplete.md | 8 + .../CreatableAutocomplete.d.ts | 45 +++ .../CreatableAutocomplete.js | 357 ++++++++++++++++++ .../src/CreatableAutocomplete/index.d.ts | 3 + .../src/CreatableAutocomplete/index.js | 1 + 7 files changed, 702 insertions(+) create mode 100644 docs/src/pages/components/autocomplete/CreatableAutocomplete.js create mode 100644 docs/src/pages/components/autocomplete/CreatableAutocompleteDialog.js create mode 100644 packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.d.ts create mode 100644 packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.js create mode 100644 packages/material-ui-lab/src/CreatableAutocomplete/index.d.ts create mode 100644 packages/material-ui-lab/src/CreatableAutocomplete/index.js diff --git a/docs/src/pages/components/autocomplete/CreatableAutocomplete.js b/docs/src/pages/components/autocomplete/CreatableAutocomplete.js new file mode 100644 index 00000000000000..79c82bf0618c52 --- /dev/null +++ b/docs/src/pages/components/autocomplete/CreatableAutocomplete.js @@ -0,0 +1,122 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import TextField from '@material-ui/core/TextField'; +import CreatableAutocomplete from '@material-ui/lab/CreatableAutocomplete'; + +export default function CreatableAutocompleteDialog() { + return ( + option.label} + style={{ width: 300 }} + renderInput={params => ( + + )} + /> + ); +} + +// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top +const top100Films = [ + { label: 'The Shawshank Redemption', value: 1994 }, + { label: 'The Godfather', value: 1972 }, + { label: 'The Godfather: Part II', value: 1974 }, + { label: 'The Dark Knight', value: 2008 }, + { label: '12 Angry Men', value: 1957 }, + { label: "Schindler's List", value: 1993 }, + { label: 'Pulp Fiction', value: 1994 }, + { label: 'The Lord of the Rings: The Return of the King', value: 2003 }, + { label: 'The Good, the Bad and the Ugly', value: 1966 }, + { label: 'Fight Club', value: 1999 }, + { label: 'The Lord of the Rings: The Fellowship of the Ring', value: 2001 }, + { label: 'Star Wars: Episode V - The Empire Strikes Back', value: 1980 }, + { label: 'Forrest Gump', value: 1994 }, + { label: 'Inception', value: 2010 }, + { label: 'The Lord of the Rings: The Two Towers', value: 2002 }, + { label: "One Flew Over the Cuckoo's Nest", value: 1975 }, + { label: 'Goodfellas', value: 1990 }, + { label: 'The Matrix', value: 1999 }, + { label: 'Seven Samurai', value: 1954 }, + { label: 'Star Wars: Episode IV - A New Hope', value: 1977 }, + { label: 'City of God', value: 2002 }, + { label: 'Se7en', value: 1995 }, + { label: 'The Silence of the Lambs', value: 1991 }, + { label: "It's a Wonderful Life", value: 1946 }, + { label: 'Life Is Beautiful', value: 1997 }, + { label: 'The Usual Suspects', value: 1995 }, + { label: 'Léon: The Professional', value: 1994 }, + { label: 'Spirited Away', value: 2001 }, + { label: 'Saving Private Ryan', value: 1998 }, + { label: 'Once Upon a Time in the West', value: 1968 }, + { label: 'American History X', value: 1998 }, + { label: 'Interstellar', value: 2014 }, + { label: 'Casablanca', value: 1942 }, + { label: 'City Lights', value: 1931 }, + { label: 'Psycho', value: 1960 }, + { label: 'The Green Mile', value: 1999 }, + { label: 'The Intouchables', value: 2011 }, + { label: 'Modern Times', value: 1936 }, + { label: 'Raiders of the Lost Ark', value: 1981 }, + { label: 'Rear Window', value: 1954 }, + { label: 'The Pianist', value: 2002 }, + { label: 'The Departed', value: 2006 }, + { label: 'Terminator 2: Judgment Day', value: 1991 }, + { label: 'Back to the Future', value: 1985 }, + { label: 'Whiplash', value: 2014 }, + { label: 'Gladiator', value: 2000 }, + { label: 'Memento', value: 2000 }, + { label: 'The Prestige', value: 2006 }, + { label: 'The Lion King', value: 1994 }, + { label: 'Apocalypse Now', value: 1979 }, + { label: 'Alien', value: 1979 }, + { label: 'Sunset Boulevard', value: 1950 }, + { label: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', value: 1964 }, + { label: 'The Great Dictator', value: 1940 }, + { label: 'Cinema Paradiso', value: 1988 }, + { label: 'The Lives of Others', value: 2006 }, + { label: 'Grave of the Fireflies', value: 1988 }, + { label: 'Paths of Glory', value: 1957 }, + { label: 'Django Unchained', value: 2012 }, + { label: 'The Shining', value: 1980 }, + { label: 'WALL·E', value: 2008 }, + { label: 'American Beauty', value: 1999 }, + { label: 'The Dark Knight Rises', value: 2012 }, + { label: 'Princess Mononoke', value: 1997 }, + { label: 'Aliens', value: 1986 }, + { label: 'Oldboy', value: 2003 }, + { label: 'Once Upon a Time in America', value: 1984 }, + { label: 'Witness for the Prosecution', value: 1957 }, + { label: 'Das Boot', value: 1981 }, + { label: 'Citizen Kane', value: 1941 }, + { label: 'North by Northwest', value: 1959 }, + { label: 'Vertigo', value: 1958 }, + { label: 'Star Wars: Episode VI - Return of the Jedi', value: 1983 }, + { label: 'Reservoir Dogs', value: 1992 }, + { label: 'Braveheart', value: 1995 }, + { label: 'M', value: 1931 }, + { label: 'Requiem for a Dream', value: 2000 }, + { label: 'Amélie', value: 2001 }, + { label: 'A Clockwork Orange', value: 1971 }, + { label: 'Like Stars on Earth', value: 2007 }, + { label: 'Taxi Driver', value: 1976 }, + { label: 'Lawrence of Arabia', value: 1962 }, + { label: 'Double Indemnity', value: 1944 }, + { label: 'Eternal Sunshine of the Spotless Mind', value: 2004 }, + { label: 'Amadeus', value: 1984 }, + { label: 'To Kill a Mockingbird', value: 1962 }, + { label: 'Toy Story 3', value: 2010 }, + { label: 'Logan', value: 2017 }, + { label: 'Full Metal Jacket', value: 1987 }, + { label: 'Dangal', value: 2016 }, + { label: 'The Sting', value: 1973 }, + { label: '2001: A Space Odyssey', value: 1968 }, + { label: "Singin' in the Rain", value: 1952 }, + { label: 'Toy Story', value: 1995 }, + { label: 'Bicycle Thieves', value: 1948 }, + { label: 'The Kid', value: 1921 }, + { label: 'Inglourious Basterds', value: 2009 }, + { label: 'Snatch', value: 2000 }, + { label: '3 Idiots', value: 2009 }, + { label: 'Monty Python and the Holy Grail', value: 1975 }, +]; diff --git a/docs/src/pages/components/autocomplete/CreatableAutocompleteDialog.js b/docs/src/pages/components/autocomplete/CreatableAutocompleteDialog.js new file mode 100644 index 00000000000000..81e5205c00af25 --- /dev/null +++ b/docs/src/pages/components/autocomplete/CreatableAutocompleteDialog.js @@ -0,0 +1,166 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import TextField from '@material-ui/core/TextField'; +import Dialog from '@material-ui/core/Dialog'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogActions from '@material-ui/core/DialogActions'; +import Button from '@material-ui/core/Button'; +import CreatableAutocomplete from '@material-ui/lab/CreatableAutocomplete'; + +export default function CreatableAutocompleteExample() { + const [open, toggleOpen] = React.useState(false); + + const handleClose = () => toggleOpen(false); + + return ( + <> + + Create a new film + + + missing a film, please insert it in. + + + + + + + + + + option.label} + style={{ width: 300 }} + onCreateNewOption={() => toggleOpen(true)} + renderInput={params => ( + + )} + /> + + ); +} + +// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top +const top100Films = [ + { label: 'The Shawshank Redemption', value: 1994 }, + { label: 'The Godfather', value: 1972 }, + { label: 'The Godfather: Part II', value: 1974 }, + { label: 'The Dark Knight', value: 2008 }, + { label: '12 Angry Men', value: 1957 }, + { label: "Schindler's List", value: 1993 }, + { label: 'Pulp Fiction', value: 1994 }, + { label: 'The Lord of the Rings: The Return of the King', value: 2003 }, + { label: 'The Good, the Bad and the Ugly', value: 1966 }, + { label: 'Fight Club', value: 1999 }, + { label: 'The Lord of the Rings: The Fellowship of the Ring', value: 2001 }, + { label: 'Star Wars: Episode V - The Empire Strikes Back', value: 1980 }, + { label: 'Forrest Gump', value: 1994 }, + { label: 'Inception', value: 2010 }, + { label: 'The Lord of the Rings: The Two Towers', value: 2002 }, + { label: "One Flew Over the Cuckoo's Nest", value: 1975 }, + { label: 'Goodfellas', value: 1990 }, + { label: 'The Matrix', value: 1999 }, + { label: 'Seven Samurai', value: 1954 }, + { label: 'Star Wars: Episode IV - A New Hope', value: 1977 }, + { label: 'City of God', value: 2002 }, + { label: 'Se7en', value: 1995 }, + { label: 'The Silence of the Lambs', value: 1991 }, + { label: "It's a Wonderful Life", value: 1946 }, + { label: 'Life Is Beautiful', value: 1997 }, + { label: 'The Usual Suspects', value: 1995 }, + { label: 'Léon: The Professional', value: 1994 }, + { label: 'Spirited Away', value: 2001 }, + { label: 'Saving Private Ryan', value: 1998 }, + { label: 'Once Upon a Time in the West', value: 1968 }, + { label: 'American History X', value: 1998 }, + { label: 'Interstellar', value: 2014 }, + { label: 'Casablanca', value: 1942 }, + { label: 'City Lights', value: 1931 }, + { label: 'Psycho', value: 1960 }, + { label: 'The Green Mile', value: 1999 }, + { label: 'The Intouchables', value: 2011 }, + { label: 'Modern Times', value: 1936 }, + { label: 'Raiders of the Lost Ark', value: 1981 }, + { label: 'Rear Window', value: 1954 }, + { label: 'The Pianist', value: 2002 }, + { label: 'The Departed', value: 2006 }, + { label: 'Terminator 2: Judgment Day', value: 1991 }, + { label: 'Back to the Future', value: 1985 }, + { label: 'Whiplash', value: 2014 }, + { label: 'Gladiator', value: 2000 }, + { label: 'Memento', value: 2000 }, + { label: 'The Prestige', value: 2006 }, + { label: 'The Lion King', value: 1994 }, + { label: 'Apocalypse Now', value: 1979 }, + { label: 'Alien', value: 1979 }, + { label: 'Sunset Boulevard', value: 1950 }, + { label: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', value: 1964 }, + { label: 'The Great Dictator', value: 1940 }, + { label: 'Cinema Paradiso', value: 1988 }, + { label: 'The Lives of Others', value: 2006 }, + { label: 'Grave of the Fireflies', value: 1988 }, + { label: 'Paths of Glory', value: 1957 }, + { label: 'Django Unchained', value: 2012 }, + { label: 'The Shining', value: 1980 }, + { label: 'WALL·E', value: 2008 }, + { label: 'American Beauty', value: 1999 }, + { label: 'The Dark Knight Rises', value: 2012 }, + { label: 'Princess Mononoke', value: 1997 }, + { label: 'Aliens', value: 1986 }, + { label: 'Oldboy', value: 2003 }, + { label: 'Once Upon a Time in America', value: 1984 }, + { label: 'Witness for the Prosecution', value: 1957 }, + { label: 'Das Boot', value: 1981 }, + { label: 'Citizen Kane', value: 1941 }, + { label: 'North by Northwest', value: 1959 }, + { label: 'Vertigo', value: 1958 }, + { label: 'Star Wars: Episode VI - Return of the Jedi', value: 1983 }, + { label: 'Reservoir Dogs', value: 1992 }, + { label: 'Braveheart', value: 1995 }, + { label: 'M', value: 1931 }, + { label: 'Requiem for a Dream', value: 2000 }, + { label: 'Amélie', value: 2001 }, + { label: 'A Clockwork Orange', value: 1971 }, + { label: 'Like Stars on Earth', value: 2007 }, + { label: 'Taxi Driver', value: 1976 }, + { label: 'Lawrence of Arabia', value: 1962 }, + { label: 'Double Indemnity', value: 1944 }, + { label: 'Eternal Sunshine of the Spotless Mind', value: 2004 }, + { label: 'Amadeus', value: 1984 }, + { label: 'To Kill a Mockingbird', value: 1962 }, + { label: 'Toy Story 3', value: 2010 }, + { label: 'Logan', value: 2017 }, + { label: 'Full Metal Jacket', value: 1987 }, + { label: 'Dangal', value: 2016 }, + { label: 'The Sting', value: 1973 }, + { label: '2001: A Space Odyssey', value: 1968 }, + { label: "Singin' in the Rain", value: 1952 }, + { label: 'Toy Story', value: 1995 }, + { label: 'Bicycle Thieves', value: 1948 }, + { label: 'The Kid', value: 1921 }, + { label: 'Inglourious Basterds', value: 2009 }, + { label: 'Snatch', value: 2000 }, + { label: '3 Idiots', value: 2009 }, + { label: 'Monty Python and the Holy Grail', value: 1975 }, +]; diff --git a/docs/src/pages/components/autocomplete/autocomplete.md b/docs/src/pages/components/autocomplete/autocomplete.md index 907e13b4985028..3078ec2ae2284e 100644 --- a/docs/src/pages/components/autocomplete/autocomplete.md +++ b/docs/src/pages/components/autocomplete/autocomplete.md @@ -12,6 +12,14 @@ The widget is useful for setting the value of a single-line textbox in one of tw 1. The value for the textbox must be chosen from a predefined set of allowed values, e.g., a location field must contain a valid location name: [combo box](#combo-box). 2. The textbox may contain any arbitrary value, but it is advantageous to suggest possible values to the user, e.g., a search field may suggest similar or previous searches to save the user time: [free solo](#free-solo). +## Creatable Autocomplete + +{{"demo": "pages/components/autocomplete/CreatableAutocomplete.js"}} + +## Creatable Autocomplete Dialog + +{{"demo": "pages/components/autocomplete/CreatableAutocompleteDialog.js"}} + ## Combo box The value must be chosen from a predefined set of allowed values. diff --git a/packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.d.ts b/packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.d.ts new file mode 100644 index 00000000000000..caeaf9de68870b --- /dev/null +++ b/packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.d.ts @@ -0,0 +1,45 @@ +import * as React from 'react'; +import { StandardProps } from '@material-ui/core'; +import { PopperProps } from '@material-ui/core/Popper'; +import { + UseAutocompleteCommonProps, + createFilterOptions, + UseAutocompleteProps, +} from '../useAutocomplete'; +import { AutocompleteProps } from '../Autocomplete'; + +export interface CreatableAutocomplete extends UseAutocompleteCommonProps, +StandardProps< + React.HTMLAttributes, + AutocompleteClassKey, + 'defaultValue' | 'onChange' | 'children' +> { + onCreateNewOption: UseAutocompleteProps['onChange']; +} + +export type AutocompleteClassKey = + | 'root' + | 'focused' + | 'tag' + | 'tagSizeSmall' + | 'inputRoot' + | 'input' + | 'inputFocused' + | 'endAdornment' + | 'clearIndicator' + | 'clearIndicatorDirty' + | 'popupIndicator' + | 'popupIndicatorOpen' + | 'popper' + | 'popperDisablePortal' + | 'paper' + | 'listbox' + | 'loading' + | 'noOptions' + | 'option' + | 'groupLabel' + | 'groupUl'; + +export default function CreatableAutocomplete( + props: CreatableAutocomplete, +): JSX.Element; diff --git a/packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.js b/packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.js new file mode 100644 index 00000000000000..a68e92266b6fa9 --- /dev/null +++ b/packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.js @@ -0,0 +1,357 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +/* eslint-disable react/prop-types */ +/* eslint-disable no-console */ +/* eslint-disable-next-line react/prop-types */ + +import React from 'react'; +import PropTypes from 'prop-types'; +import { withStyles } from '@material-ui/core/styles'; +import Autocomplete from '@material-ui/lab/Autocomplete'; + +export const styles = () => ({}); + +const CreatableAutocomplete = (props) => { + const { + options: optionsProps, + inputValue, + createOptionPositionInLast = true, + onCreateNewOption, + getNewOptionLabel = (value) => `Create "${value}"`, + getNewOptionData = (value, optionLabel) => ({ + label: optionLabel, + value, + isNew: true, + }), + onChange, + ...otherProps + } = props; + const [_inputValue, setInputValue] = React.useState(inputValue); + const [options, setOptions] = React.useState(optionsProps); + + React.useEffect(() => { + const newOptionData = getNewOptionData(_inputValue, getNewOptionLabel(_inputValue)); + const newOptions = createOptionPositionInLast ? [...optionsProps, newOptionData] : [newOptionData, ...optionsProps]; + + setOptions(newOptions); + }, [optionsProps, _inputValue]); + + const handleChange = + (e, option) => { + console.log('if (option.isNew) {'); + console.log({props}); + if (option.isNew) { + console.log('if (onCreateNewOption) {'); + console.log({onCreateNewOption}); + if (onCreateNewOption) { + onCreateNewOption(e, option); + return; + } + console.log('if (onChange) {'); + console.log({onChange}); + if (onChange) { + onChange(e, option); + } + } + } + + return ( + setInputValue(value)} + /> + ); +} + +Autocomplete.propTypes = { + // ----------------------------- Warning -------------------------------- + // | These PropTypes are generated from the TypeScript type definitions | + // | To update them edit the d.ts file and run "yarn proptypes" | + // ---------------------------------------------------------------------- + /** + * If `true`, the portion of the selected suggestion that has not been typed by the user, + * known as the completion string, appears inline after the input cursor in the textbox. + * The inline completion string is visually highlighted and has a selected state. + */ + autoComplete: PropTypes.bool, + /** + * If `true`, the first option is automatically highlighted. + */ + autoHighlight: PropTypes.bool, + /** + * If `true`, the selected option becomes the value of the input + * when the Autocomplete loses focus unless the user chooses + * a different option or changes the character string in the input. + */ + autoSelect: PropTypes.bool, + /** + * Control if the input should be blurred when an option is selected: + * + * - `false` the input is not blurred. + * - `true` the input is always blurred. + * - `touch` the input is blurred after a touch event. + * - `mouse` the input is blurred after a mouse event. + */ + blurOnSelect: PropTypes.oneOfType([PropTypes.oneOf(['mouse', 'touch']), PropTypes.bool]), + /** + * Props applied to the [`Chip`](/api/chip/) element. + */ + ChipProps: PropTypes.object, + /** + * Override or extend the styles applied to the component. + * See [CSS API](#css) below for more details. + */ + classes: PropTypes.object, + /** + * @ignore + */ + className: PropTypes.string, + /** + * If `true`, clear all values when the user presses escape and the popup is closed. + */ + clearOnEscape: PropTypes.bool, + /** + * Override the default text for the *clear* icon button. + * + * For localization purposes, you can use the provided [translations](/guides/localization/). + */ + clearText: PropTypes.string, + /** + * The icon to display in place of the default close icon. + */ + closeIcon: PropTypes.node, + /** + * Override the default text for the *close popup* icon button. + * + * For localization purposes, you can use the provided [translations](/guides/localization/). + */ + closeText: PropTypes.string, + /** + * If `true`, the popup will ignore the blur event if the input if filled. + * You can inspect the popup markup with your browser tools. + * Consider this option when you need to customize the component. + */ + debug: PropTypes.bool, + /** + * The default input value. Use when the component is not controlled. + */ + defaultValue: PropTypes.oneOfType([PropTypes.any, PropTypes.array]), + /** + * If `true`, the input can't be cleared. + */ + disableClearable: PropTypes.bool, + /** + * If `true`, the popup won't close when a value is selected. + */ + disableCloseOnSelect: PropTypes.bool, + /** + * If `true`, the input will be disabled. + */ + disabled: PropTypes.bool, + /** + * If `true`, the list box in the popup will not wrap focus. + */ + disableListWrap: PropTypes.bool, + /** + * If `true`, the popup won't open on input focus. + */ + disableOpenOnFocus: PropTypes.bool, + /** + * Disable the portal behavior. + * The children stay within it's parent DOM hierarchy. + */ + disablePortal: PropTypes.bool, + /** + * A filter function that determines the options that are eligible. + * + * @param {T[]} options The options to render. + * @param {object} state The state of the component. + * @returns {T[]} + */ + filterOptions: PropTypes.func, + /** + * If `true`, hide the selected options from the list box. + */ + filterSelectedOptions: PropTypes.bool, + /** + * Force the visibility display of the popup icon. + */ + forcePopupIcon: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.bool]), + /** + * If `true`, the Autocomplete is free solo, meaning that the user input is not bound to provided options. + */ + freeSolo: PropTypes.bool, + /** + * Used to determine the disabled state for a given option. + */ + getOptionDisabled: PropTypes.func, + /** + * 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). + */ + getOptionLabel: PropTypes.func, + /** + * Used to determine if an option is selected. + * Uses strict equality by default. + */ + getOptionSelected: PropTypes.func, + /** + * 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. + * + * @param {T} options The option to group. + * @returns {string} + */ + groupBy: PropTypes.func, + /** + * 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. + */ + id: PropTypes.string, + /** + * If `true`, the highlight can move to the input. + */ + includeInputInList: PropTypes.bool, + /** + * The input value. + */ + inputValue: PropTypes.string, + /** + * The component used to render the listbox. + */ + ListboxComponent: PropTypes.elementType, + /** + * Props applied to the Listbox element. + */ + ListboxProps: PropTypes.object, + /** + * If `true`, the component is in a loading state. + */ + loading: PropTypes.bool, + /** + * Text to display when in a loading state. + * + * For localization purposes, you can use the provided [translations](/guides/localization/). + */ + loadingText: PropTypes.node, + /** + * If `true`, `value` must be an array and the menu will support multiple selections. + */ + multiple: PropTypes.bool, + /** + * Text to display when there are no options. + * + * For localization purposes, you can use the provided [translations](/guides/localization/). + */ + noOptionsText: PropTypes.node, + /** + * Callback fired when the value changes. + * + * @param {object} event The event source of the callback. + * @param {T} value + */ + onChange: PropTypes.func, + /** + * Callback fired when the popup requests to be closed. + * Use in controlled mode (see open). + * + * @param {object} event The event source of the callback. + */ + onClose: PropTypes.func, + /** + * Callback fired when the input value changes. + * + * @param {object} event The event source of the callback. + * @param {string} value The new value of the text input. + * @param {string} reason Can be: `"input"` (user input), `"reset"` (programmatic change), `"clear"`. + */ + onInputChange: PropTypes.func, + /** + * Text to display when there are no options. + * + * For localization purposes, you can use the provided [translations](/guides/localization/). + */ + onNoOptionsSelected: PropTypes.elementType, + /** + * Callback fired when the popup requests to be opened. + * Use in controlled mode (see open). + * + * @param {object} event The event source of the callback. + */ + onOpen: PropTypes.func, + /** + * Control the popup` open state. + */ + open: PropTypes.bool, + /** + * Override the default text for the *open popup* icon button. + * + * For localization purposes, you can use the provided [translations](/guides/localization/). + */ + openText: PropTypes.string, + /** + * Array of options. + */ + options: PropTypes.array.isRequired, + /** + * The component used to render the body of the popup. + */ + PaperComponent: PropTypes.elementType, + /** + * The component used to position the popup. + */ + PopperComponent: PropTypes.elementType, + /** + * The icon to display in place of the default popup icon. + */ + popupIcon: PropTypes.node, + /** + * Render the group. + * + * @param {any} option The group to render. + * @returns {ReactNode} + */ + renderGroup: PropTypes.func, + /** + * Render the input. + * + * @param {object} params + * @returns {ReactNode} + */ + renderInput: PropTypes.func.isRequired, + /** + * Render the option, use `getOptionLabel` by default. + * + * @param {T} option The option to render. + * @param {object} state The state of the component. + * @returns {ReactNode} + */ + renderOption: PropTypes.func, + /** + * Render the selected value. + * + * @param {T[]} value The `value` provided to the component. + * @param {function} getTagProps A tag props getter. + * @returns {ReactNode} + */ + renderTags: PropTypes.func, + /** + * If `true`, the input's text will be selected on focus. + */ + selectOnFocus: PropTypes.bool, + /** + * The size of the autocomplete. + */ + size: PropTypes.oneOf(['medium', 'small']), + /** + * The value of the autocomplete. + * + * The value must have reference equality with the option in order to be selected. + * You can customize the equality behavior with the `getOptionSelected` prop. + */ + value: PropTypes.oneOfType([PropTypes.any, PropTypes.array]), +}; + +export default withStyles(styles, { name: 'MuiCreatableAutocomplete' })(CreatableAutocomplete); diff --git a/packages/material-ui-lab/src/CreatableAutocomplete/index.d.ts b/packages/material-ui-lab/src/CreatableAutocomplete/index.d.ts new file mode 100644 index 00000000000000..8bf1f571d4b4d4 --- /dev/null +++ b/packages/material-ui-lab/src/CreatableAutocomplete/index.d.ts @@ -0,0 +1,3 @@ +export { default } from './CreatableAutocomplete'; + +export * from './CreatableAutocomplete'; diff --git a/packages/material-ui-lab/src/CreatableAutocomplete/index.js b/packages/material-ui-lab/src/CreatableAutocomplete/index.js new file mode 100644 index 00000000000000..773606f973961f --- /dev/null +++ b/packages/material-ui-lab/src/CreatableAutocomplete/index.js @@ -0,0 +1 @@ +export { default } from './CreatableAutocomplete'; From c1f4af573f27d879841a3e2c53e33fbbe699f26a Mon Sep 17 00:00:00 2001 From: "@itelofilho" Date: Wed, 12 Feb 2020 21:44:19 -0300 Subject: [PATCH 2/7] remove CreatableAucomplete component and improve docs --- .../autocomplete/CreatableAutocomplete.js | 122 ------ .../CreatableAutocompleteDialog.js | 166 -------- .../autocomplete/FreeSoloCreateOption.js | 154 ++++++++ .../FreeSoloCreateOptionDialog.js | 218 +++++++++++ .../components/autocomplete/autocomplete.md | 20 +- .../CreatableAutocomplete.d.ts | 45 --- .../CreatableAutocomplete.js | 357 ------------------ .../src/CreatableAutocomplete/index.d.ts | 3 - .../src/CreatableAutocomplete/index.js | 1 - 9 files changed, 384 insertions(+), 702 deletions(-) delete mode 100644 docs/src/pages/components/autocomplete/CreatableAutocomplete.js delete mode 100644 docs/src/pages/components/autocomplete/CreatableAutocompleteDialog.js create mode 100644 docs/src/pages/components/autocomplete/FreeSoloCreateOption.js create mode 100644 docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.js delete mode 100644 packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.d.ts delete mode 100644 packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.js delete mode 100644 packages/material-ui-lab/src/CreatableAutocomplete/index.d.ts delete mode 100644 packages/material-ui-lab/src/CreatableAutocomplete/index.js diff --git a/docs/src/pages/components/autocomplete/CreatableAutocomplete.js b/docs/src/pages/components/autocomplete/CreatableAutocomplete.js deleted file mode 100644 index 79c82bf0618c52..00000000000000 --- a/docs/src/pages/components/autocomplete/CreatableAutocomplete.js +++ /dev/null @@ -1,122 +0,0 @@ -/* eslint-disable no-use-before-define */ -import React from 'react'; -import TextField from '@material-ui/core/TextField'; -import CreatableAutocomplete from '@material-ui/lab/CreatableAutocomplete'; - -export default function CreatableAutocompleteDialog() { - return ( - option.label} - style={{ width: 300 }} - renderInput={params => ( - - )} - /> - ); -} - -// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top -const top100Films = [ - { label: 'The Shawshank Redemption', value: 1994 }, - { label: 'The Godfather', value: 1972 }, - { label: 'The Godfather: Part II', value: 1974 }, - { label: 'The Dark Knight', value: 2008 }, - { label: '12 Angry Men', value: 1957 }, - { label: "Schindler's List", value: 1993 }, - { label: 'Pulp Fiction', value: 1994 }, - { label: 'The Lord of the Rings: The Return of the King', value: 2003 }, - { label: 'The Good, the Bad and the Ugly', value: 1966 }, - { label: 'Fight Club', value: 1999 }, - { label: 'The Lord of the Rings: The Fellowship of the Ring', value: 2001 }, - { label: 'Star Wars: Episode V - The Empire Strikes Back', value: 1980 }, - { label: 'Forrest Gump', value: 1994 }, - { label: 'Inception', value: 2010 }, - { label: 'The Lord of the Rings: The Two Towers', value: 2002 }, - { label: "One Flew Over the Cuckoo's Nest", value: 1975 }, - { label: 'Goodfellas', value: 1990 }, - { label: 'The Matrix', value: 1999 }, - { label: 'Seven Samurai', value: 1954 }, - { label: 'Star Wars: Episode IV - A New Hope', value: 1977 }, - { label: 'City of God', value: 2002 }, - { label: 'Se7en', value: 1995 }, - { label: 'The Silence of the Lambs', value: 1991 }, - { label: "It's a Wonderful Life", value: 1946 }, - { label: 'Life Is Beautiful', value: 1997 }, - { label: 'The Usual Suspects', value: 1995 }, - { label: 'Léon: The Professional', value: 1994 }, - { label: 'Spirited Away', value: 2001 }, - { label: 'Saving Private Ryan', value: 1998 }, - { label: 'Once Upon a Time in the West', value: 1968 }, - { label: 'American History X', value: 1998 }, - { label: 'Interstellar', value: 2014 }, - { label: 'Casablanca', value: 1942 }, - { label: 'City Lights', value: 1931 }, - { label: 'Psycho', value: 1960 }, - { label: 'The Green Mile', value: 1999 }, - { label: 'The Intouchables', value: 2011 }, - { label: 'Modern Times', value: 1936 }, - { label: 'Raiders of the Lost Ark', value: 1981 }, - { label: 'Rear Window', value: 1954 }, - { label: 'The Pianist', value: 2002 }, - { label: 'The Departed', value: 2006 }, - { label: 'Terminator 2: Judgment Day', value: 1991 }, - { label: 'Back to the Future', value: 1985 }, - { label: 'Whiplash', value: 2014 }, - { label: 'Gladiator', value: 2000 }, - { label: 'Memento', value: 2000 }, - { label: 'The Prestige', value: 2006 }, - { label: 'The Lion King', value: 1994 }, - { label: 'Apocalypse Now', value: 1979 }, - { label: 'Alien', value: 1979 }, - { label: 'Sunset Boulevard', value: 1950 }, - { label: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', value: 1964 }, - { label: 'The Great Dictator', value: 1940 }, - { label: 'Cinema Paradiso', value: 1988 }, - { label: 'The Lives of Others', value: 2006 }, - { label: 'Grave of the Fireflies', value: 1988 }, - { label: 'Paths of Glory', value: 1957 }, - { label: 'Django Unchained', value: 2012 }, - { label: 'The Shining', value: 1980 }, - { label: 'WALL·E', value: 2008 }, - { label: 'American Beauty', value: 1999 }, - { label: 'The Dark Knight Rises', value: 2012 }, - { label: 'Princess Mononoke', value: 1997 }, - { label: 'Aliens', value: 1986 }, - { label: 'Oldboy', value: 2003 }, - { label: 'Once Upon a Time in America', value: 1984 }, - { label: 'Witness for the Prosecution', value: 1957 }, - { label: 'Das Boot', value: 1981 }, - { label: 'Citizen Kane', value: 1941 }, - { label: 'North by Northwest', value: 1959 }, - { label: 'Vertigo', value: 1958 }, - { label: 'Star Wars: Episode VI - Return of the Jedi', value: 1983 }, - { label: 'Reservoir Dogs', value: 1992 }, - { label: 'Braveheart', value: 1995 }, - { label: 'M', value: 1931 }, - { label: 'Requiem for a Dream', value: 2000 }, - { label: 'Amélie', value: 2001 }, - { label: 'A Clockwork Orange', value: 1971 }, - { label: 'Like Stars on Earth', value: 2007 }, - { label: 'Taxi Driver', value: 1976 }, - { label: 'Lawrence of Arabia', value: 1962 }, - { label: 'Double Indemnity', value: 1944 }, - { label: 'Eternal Sunshine of the Spotless Mind', value: 2004 }, - { label: 'Amadeus', value: 1984 }, - { label: 'To Kill a Mockingbird', value: 1962 }, - { label: 'Toy Story 3', value: 2010 }, - { label: 'Logan', value: 2017 }, - { label: 'Full Metal Jacket', value: 1987 }, - { label: 'Dangal', value: 2016 }, - { label: 'The Sting', value: 1973 }, - { label: '2001: A Space Odyssey', value: 1968 }, - { label: "Singin' in the Rain", value: 1952 }, - { label: 'Toy Story', value: 1995 }, - { label: 'Bicycle Thieves', value: 1948 }, - { label: 'The Kid', value: 1921 }, - { label: 'Inglourious Basterds', value: 2009 }, - { label: 'Snatch', value: 2000 }, - { label: '3 Idiots', value: 2009 }, - { label: 'Monty Python and the Holy Grail', value: 1975 }, -]; diff --git a/docs/src/pages/components/autocomplete/CreatableAutocompleteDialog.js b/docs/src/pages/components/autocomplete/CreatableAutocompleteDialog.js deleted file mode 100644 index 81e5205c00af25..00000000000000 --- a/docs/src/pages/components/autocomplete/CreatableAutocompleteDialog.js +++ /dev/null @@ -1,166 +0,0 @@ -/* eslint-disable no-use-before-define */ -import React from 'react'; -import TextField from '@material-ui/core/TextField'; -import Dialog from '@material-ui/core/Dialog'; -import DialogTitle from '@material-ui/core/DialogTitle'; -import DialogContent from '@material-ui/core/DialogContent'; -import DialogContentText from '@material-ui/core/DialogContentText'; -import DialogActions from '@material-ui/core/DialogActions'; -import Button from '@material-ui/core/Button'; -import CreatableAutocomplete from '@material-ui/lab/CreatableAutocomplete'; - -export default function CreatableAutocompleteExample() { - const [open, toggleOpen] = React.useState(false); - - const handleClose = () => toggleOpen(false); - - return ( - <> - - Create a new film - - - missing a film, please insert it in. - - - - - - - - - - option.label} - style={{ width: 300 }} - onCreateNewOption={() => toggleOpen(true)} - renderInput={params => ( - - )} - /> - - ); -} - -// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top -const top100Films = [ - { label: 'The Shawshank Redemption', value: 1994 }, - { label: 'The Godfather', value: 1972 }, - { label: 'The Godfather: Part II', value: 1974 }, - { label: 'The Dark Knight', value: 2008 }, - { label: '12 Angry Men', value: 1957 }, - { label: "Schindler's List", value: 1993 }, - { label: 'Pulp Fiction', value: 1994 }, - { label: 'The Lord of the Rings: The Return of the King', value: 2003 }, - { label: 'The Good, the Bad and the Ugly', value: 1966 }, - { label: 'Fight Club', value: 1999 }, - { label: 'The Lord of the Rings: The Fellowship of the Ring', value: 2001 }, - { label: 'Star Wars: Episode V - The Empire Strikes Back', value: 1980 }, - { label: 'Forrest Gump', value: 1994 }, - { label: 'Inception', value: 2010 }, - { label: 'The Lord of the Rings: The Two Towers', value: 2002 }, - { label: "One Flew Over the Cuckoo's Nest", value: 1975 }, - { label: 'Goodfellas', value: 1990 }, - { label: 'The Matrix', value: 1999 }, - { label: 'Seven Samurai', value: 1954 }, - { label: 'Star Wars: Episode IV - A New Hope', value: 1977 }, - { label: 'City of God', value: 2002 }, - { label: 'Se7en', value: 1995 }, - { label: 'The Silence of the Lambs', value: 1991 }, - { label: "It's a Wonderful Life", value: 1946 }, - { label: 'Life Is Beautiful', value: 1997 }, - { label: 'The Usual Suspects', value: 1995 }, - { label: 'Léon: The Professional', value: 1994 }, - { label: 'Spirited Away', value: 2001 }, - { label: 'Saving Private Ryan', value: 1998 }, - { label: 'Once Upon a Time in the West', value: 1968 }, - { label: 'American History X', value: 1998 }, - { label: 'Interstellar', value: 2014 }, - { label: 'Casablanca', value: 1942 }, - { label: 'City Lights', value: 1931 }, - { label: 'Psycho', value: 1960 }, - { label: 'The Green Mile', value: 1999 }, - { label: 'The Intouchables', value: 2011 }, - { label: 'Modern Times', value: 1936 }, - { label: 'Raiders of the Lost Ark', value: 1981 }, - { label: 'Rear Window', value: 1954 }, - { label: 'The Pianist', value: 2002 }, - { label: 'The Departed', value: 2006 }, - { label: 'Terminator 2: Judgment Day', value: 1991 }, - { label: 'Back to the Future', value: 1985 }, - { label: 'Whiplash', value: 2014 }, - { label: 'Gladiator', value: 2000 }, - { label: 'Memento', value: 2000 }, - { label: 'The Prestige', value: 2006 }, - { label: 'The Lion King', value: 1994 }, - { label: 'Apocalypse Now', value: 1979 }, - { label: 'Alien', value: 1979 }, - { label: 'Sunset Boulevard', value: 1950 }, - { label: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', value: 1964 }, - { label: 'The Great Dictator', value: 1940 }, - { label: 'Cinema Paradiso', value: 1988 }, - { label: 'The Lives of Others', value: 2006 }, - { label: 'Grave of the Fireflies', value: 1988 }, - { label: 'Paths of Glory', value: 1957 }, - { label: 'Django Unchained', value: 2012 }, - { label: 'The Shining', value: 1980 }, - { label: 'WALL·E', value: 2008 }, - { label: 'American Beauty', value: 1999 }, - { label: 'The Dark Knight Rises', value: 2012 }, - { label: 'Princess Mononoke', value: 1997 }, - { label: 'Aliens', value: 1986 }, - { label: 'Oldboy', value: 2003 }, - { label: 'Once Upon a Time in America', value: 1984 }, - { label: 'Witness for the Prosecution', value: 1957 }, - { label: 'Das Boot', value: 1981 }, - { label: 'Citizen Kane', value: 1941 }, - { label: 'North by Northwest', value: 1959 }, - { label: 'Vertigo', value: 1958 }, - { label: 'Star Wars: Episode VI - Return of the Jedi', value: 1983 }, - { label: 'Reservoir Dogs', value: 1992 }, - { label: 'Braveheart', value: 1995 }, - { label: 'M', value: 1931 }, - { label: 'Requiem for a Dream', value: 2000 }, - { label: 'Amélie', value: 2001 }, - { label: 'A Clockwork Orange', value: 1971 }, - { label: 'Like Stars on Earth', value: 2007 }, - { label: 'Taxi Driver', value: 1976 }, - { label: 'Lawrence of Arabia', value: 1962 }, - { label: 'Double Indemnity', value: 1944 }, - { label: 'Eternal Sunshine of the Spotless Mind', value: 2004 }, - { label: 'Amadeus', value: 1984 }, - { label: 'To Kill a Mockingbird', value: 1962 }, - { label: 'Toy Story 3', value: 2010 }, - { label: 'Logan', value: 2017 }, - { label: 'Full Metal Jacket', value: 1987 }, - { label: 'Dangal', value: 2016 }, - { label: 'The Sting', value: 1973 }, - { label: '2001: A Space Odyssey', value: 1968 }, - { label: "Singin' in the Rain", value: 1952 }, - { label: 'Toy Story', value: 1995 }, - { label: 'Bicycle Thieves', value: 1948 }, - { label: 'The Kid', value: 1921 }, - { label: 'Inglourious Basterds', value: 2009 }, - { label: 'Snatch', value: 2000 }, - { label: '3 Idiots', value: 2009 }, - { label: 'Monty Python and the Holy Grail', value: 1975 }, -]; diff --git a/docs/src/pages/components/autocomplete/FreeSoloCreateOption.js b/docs/src/pages/components/autocomplete/FreeSoloCreateOption.js new file mode 100644 index 00000000000000..81f3c753d7ea45 --- /dev/null +++ b/docs/src/pages/components/autocomplete/FreeSoloCreateOption.js @@ -0,0 +1,154 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import TextField from '@material-ui/core/TextField'; +import Autocomplete, { createFilterOptions } from '@material-ui/lab/Autocomplete'; + +const filter = createFilterOptions(); + +export default function ComboBox() { + const [value, setValue] = React.useState(''); + + return ( + { + if (newValue && newValue.freeSolo) { + setValue({ + title: newValue.inputValue, + }); + return; + } + + setValue(newValue); + }} + filterOptions={(options, params) => { + const filtered = filter(options, params); + + if (params.inputValue !== '') { + filtered.push({ + freeSolo: true, + inputValue: params.inputValue, + title: `Add "${params.inputValue}"`, + }); + } + + return filtered; + }} + id="free-solo-with-text-demo" + options={top100Films} + getOptionLabel={option => option.title} + style={{ width: 300 }} + freeSolo + renderInput={params => ( + + )} + /> + ); +} + +// 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/FreeSoloCreateOptionDialog.js b/docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.js new file mode 100644 index 00000000000000..b6801b3334ea29 --- /dev/null +++ b/docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.js @@ -0,0 +1,218 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import TextField from '@material-ui/core/TextField'; +import Dialog from '@material-ui/core/Dialog'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogActions from '@material-ui/core/DialogActions'; +import Button from '@material-ui/core/Button'; +import Autocomplete, { createFilterOptions } from '@material-ui/lab/Autocomplete'; + +const filter = createFilterOptions(); + +export default function ComboBox() { + const [value, setValue] = React.useState(''); + + const [open, toggleOpen] = React.useState(false); + + const handleClose = () => { + setDialogValue({ + title: '', + year: null, + }); + toggleOpen(false); + }; + + const [dialogValue, setDialogValue] = React.useState({ + title: '', + year: null, + }); + + const handleAdd = () => { + setValue(dialogValue); + handleClose(); + }; + + return ( + <> + { + if (newValue && newValue.freeSolo) { + toggleOpen(true); + setDialogValue({ + title: newValue.inputValue, + year: null, + }); + return; + } + + setValue(newValue); + }} + filterOptions={(options, params) => { + console.log(params); + const filtered = filter(options, params); + + if (params.inputValue !== '') { + filtered.push({ + freeSolo: true, + inputValue: params.inputValue, + title: `Add "${params.inputValue}"`, + }); + } + + return filtered; + }} + id="free-solo-dialog-demo" + options={top100Films} + getOptionLabel={option => option.title} + style={{ width: 300 }} + freeSolo + renderInput={params => ( + + )} + /> + + Add a new film + + Did you miss any film in our list? Please, add it! + setDialogValue({ ...dialogValue, title: event.target.value })} + label="title" + type="text" + fullWidth + /> + setDialogValue({ ...dialogValue, year: event.target.value })} + label="year" + type="number" + fullWidth + /> + + + + + + + + ); +} + +// 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/autocomplete.md b/docs/src/pages/components/autocomplete/autocomplete.md index 3078ec2ae2284e..08bcd4d7982308 100644 --- a/docs/src/pages/components/autocomplete/autocomplete.md +++ b/docs/src/pages/components/autocomplete/autocomplete.md @@ -12,14 +12,6 @@ The widget is useful for setting the value of a single-line textbox in one of tw 1. The value for the textbox must be chosen from a predefined set of allowed values, e.g., a location field must contain a valid location name: [combo box](#combo-box). 2. The textbox may contain any arbitrary value, but it is advantageous to suggest possible values to the user, e.g., a search field may suggest similar or previous searches to save the user time: [free solo](#free-solo). -## Creatable Autocomplete - -{{"demo": "pages/components/autocomplete/CreatableAutocomplete.js"}} - -## Creatable Autocomplete Dialog - -{{"demo": "pages/components/autocomplete/CreatableAutocompleteDialog.js"}} - ## Combo box The value must be chosen from a predefined set of allowed values. @@ -46,6 +38,18 @@ However, if you intend to use it for a [combo box](#combo-box) like experience ( {{"demo": "pages/components/autocomplete/FreeSolo.js"}} +### Free solo with explicit text + +Sometimes you want to make explicit to the user that he/she can add whatever value he/she wants. + +{{"demo": "pages/components/autocomplete/FreeSoloCreateOption.js"}} + +### Free solo to open a Dialog + +You could also create dialog when the user wants to add a new value. + +{{"demo": "pages/components/autocomplete/FreeSoloCreateOptionDialog.js"}} + ## Grouped {{"demo": "pages/components/autocomplete/Grouped.js"}} diff --git a/packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.d.ts b/packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.d.ts deleted file mode 100644 index caeaf9de68870b..00000000000000 --- a/packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.d.ts +++ /dev/null @@ -1,45 +0,0 @@ -import * as React from 'react'; -import { StandardProps } from '@material-ui/core'; -import { PopperProps } from '@material-ui/core/Popper'; -import { - UseAutocompleteCommonProps, - createFilterOptions, - UseAutocompleteProps, -} from '../useAutocomplete'; -import { AutocompleteProps } from '../Autocomplete'; - -export interface CreatableAutocomplete extends UseAutocompleteCommonProps, -StandardProps< - React.HTMLAttributes, - AutocompleteClassKey, - 'defaultValue' | 'onChange' | 'children' -> { - onCreateNewOption: UseAutocompleteProps['onChange']; -} - -export type AutocompleteClassKey = - | 'root' - | 'focused' - | 'tag' - | 'tagSizeSmall' - | 'inputRoot' - | 'input' - | 'inputFocused' - | 'endAdornment' - | 'clearIndicator' - | 'clearIndicatorDirty' - | 'popupIndicator' - | 'popupIndicatorOpen' - | 'popper' - | 'popperDisablePortal' - | 'paper' - | 'listbox' - | 'loading' - | 'noOptions' - | 'option' - | 'groupLabel' - | 'groupUl'; - -export default function CreatableAutocomplete( - props: CreatableAutocomplete, -): JSX.Element; diff --git a/packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.js b/packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.js deleted file mode 100644 index a68e92266b6fa9..00000000000000 --- a/packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.js +++ /dev/null @@ -1,357 +0,0 @@ -/* eslint-disable react-hooks/exhaustive-deps */ -/* eslint-disable react/prop-types */ -/* eslint-disable no-console */ -/* eslint-disable-next-line react/prop-types */ - -import React from 'react'; -import PropTypes from 'prop-types'; -import { withStyles } from '@material-ui/core/styles'; -import Autocomplete from '@material-ui/lab/Autocomplete'; - -export const styles = () => ({}); - -const CreatableAutocomplete = (props) => { - const { - options: optionsProps, - inputValue, - createOptionPositionInLast = true, - onCreateNewOption, - getNewOptionLabel = (value) => `Create "${value}"`, - getNewOptionData = (value, optionLabel) => ({ - label: optionLabel, - value, - isNew: true, - }), - onChange, - ...otherProps - } = props; - const [_inputValue, setInputValue] = React.useState(inputValue); - const [options, setOptions] = React.useState(optionsProps); - - React.useEffect(() => { - const newOptionData = getNewOptionData(_inputValue, getNewOptionLabel(_inputValue)); - const newOptions = createOptionPositionInLast ? [...optionsProps, newOptionData] : [newOptionData, ...optionsProps]; - - setOptions(newOptions); - }, [optionsProps, _inputValue]); - - const handleChange = - (e, option) => { - console.log('if (option.isNew) {'); - console.log({props}); - if (option.isNew) { - console.log('if (onCreateNewOption) {'); - console.log({onCreateNewOption}); - if (onCreateNewOption) { - onCreateNewOption(e, option); - return; - } - console.log('if (onChange) {'); - console.log({onChange}); - if (onChange) { - onChange(e, option); - } - } - } - - return ( - setInputValue(value)} - /> - ); -} - -Autocomplete.propTypes = { - // ----------------------------- Warning -------------------------------- - // | These PropTypes are generated from the TypeScript type definitions | - // | To update them edit the d.ts file and run "yarn proptypes" | - // ---------------------------------------------------------------------- - /** - * If `true`, the portion of the selected suggestion that has not been typed by the user, - * known as the completion string, appears inline after the input cursor in the textbox. - * The inline completion string is visually highlighted and has a selected state. - */ - autoComplete: PropTypes.bool, - /** - * If `true`, the first option is automatically highlighted. - */ - autoHighlight: PropTypes.bool, - /** - * If `true`, the selected option becomes the value of the input - * when the Autocomplete loses focus unless the user chooses - * a different option or changes the character string in the input. - */ - autoSelect: PropTypes.bool, - /** - * Control if the input should be blurred when an option is selected: - * - * - `false` the input is not blurred. - * - `true` the input is always blurred. - * - `touch` the input is blurred after a touch event. - * - `mouse` the input is blurred after a mouse event. - */ - blurOnSelect: PropTypes.oneOfType([PropTypes.oneOf(['mouse', 'touch']), PropTypes.bool]), - /** - * Props applied to the [`Chip`](/api/chip/) element. - */ - ChipProps: PropTypes.object, - /** - * Override or extend the styles applied to the component. - * See [CSS API](#css) below for more details. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * If `true`, clear all values when the user presses escape and the popup is closed. - */ - clearOnEscape: PropTypes.bool, - /** - * Override the default text for the *clear* icon button. - * - * For localization purposes, you can use the provided [translations](/guides/localization/). - */ - clearText: PropTypes.string, - /** - * The icon to display in place of the default close icon. - */ - closeIcon: PropTypes.node, - /** - * Override the default text for the *close popup* icon button. - * - * For localization purposes, you can use the provided [translations](/guides/localization/). - */ - closeText: PropTypes.string, - /** - * If `true`, the popup will ignore the blur event if the input if filled. - * You can inspect the popup markup with your browser tools. - * Consider this option when you need to customize the component. - */ - debug: PropTypes.bool, - /** - * The default input value. Use when the component is not controlled. - */ - defaultValue: PropTypes.oneOfType([PropTypes.any, PropTypes.array]), - /** - * If `true`, the input can't be cleared. - */ - disableClearable: PropTypes.bool, - /** - * If `true`, the popup won't close when a value is selected. - */ - disableCloseOnSelect: PropTypes.bool, - /** - * If `true`, the input will be disabled. - */ - disabled: PropTypes.bool, - /** - * If `true`, the list box in the popup will not wrap focus. - */ - disableListWrap: PropTypes.bool, - /** - * If `true`, the popup won't open on input focus. - */ - disableOpenOnFocus: PropTypes.bool, - /** - * Disable the portal behavior. - * The children stay within it's parent DOM hierarchy. - */ - disablePortal: PropTypes.bool, - /** - * A filter function that determines the options that are eligible. - * - * @param {T[]} options The options to render. - * @param {object} state The state of the component. - * @returns {T[]} - */ - filterOptions: PropTypes.func, - /** - * If `true`, hide the selected options from the list box. - */ - filterSelectedOptions: PropTypes.bool, - /** - * Force the visibility display of the popup icon. - */ - forcePopupIcon: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.bool]), - /** - * If `true`, the Autocomplete is free solo, meaning that the user input is not bound to provided options. - */ - freeSolo: PropTypes.bool, - /** - * Used to determine the disabled state for a given option. - */ - getOptionDisabled: PropTypes.func, - /** - * 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). - */ - getOptionLabel: PropTypes.func, - /** - * Used to determine if an option is selected. - * Uses strict equality by default. - */ - getOptionSelected: PropTypes.func, - /** - * 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. - * - * @param {T} options The option to group. - * @returns {string} - */ - groupBy: PropTypes.func, - /** - * 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. - */ - id: PropTypes.string, - /** - * If `true`, the highlight can move to the input. - */ - includeInputInList: PropTypes.bool, - /** - * The input value. - */ - inputValue: PropTypes.string, - /** - * The component used to render the listbox. - */ - ListboxComponent: PropTypes.elementType, - /** - * Props applied to the Listbox element. - */ - ListboxProps: PropTypes.object, - /** - * If `true`, the component is in a loading state. - */ - loading: PropTypes.bool, - /** - * Text to display when in a loading state. - * - * For localization purposes, you can use the provided [translations](/guides/localization/). - */ - loadingText: PropTypes.node, - /** - * If `true`, `value` must be an array and the menu will support multiple selections. - */ - multiple: PropTypes.bool, - /** - * Text to display when there are no options. - * - * For localization purposes, you can use the provided [translations](/guides/localization/). - */ - noOptionsText: PropTypes.node, - /** - * Callback fired when the value changes. - * - * @param {object} event The event source of the callback. - * @param {T} value - */ - onChange: PropTypes.func, - /** - * Callback fired when the popup requests to be closed. - * Use in controlled mode (see open). - * - * @param {object} event The event source of the callback. - */ - onClose: PropTypes.func, - /** - * Callback fired when the input value changes. - * - * @param {object} event The event source of the callback. - * @param {string} value The new value of the text input. - * @param {string} reason Can be: `"input"` (user input), `"reset"` (programmatic change), `"clear"`. - */ - onInputChange: PropTypes.func, - /** - * Text to display when there are no options. - * - * For localization purposes, you can use the provided [translations](/guides/localization/). - */ - onNoOptionsSelected: PropTypes.elementType, - /** - * Callback fired when the popup requests to be opened. - * Use in controlled mode (see open). - * - * @param {object} event The event source of the callback. - */ - onOpen: PropTypes.func, - /** - * Control the popup` open state. - */ - open: PropTypes.bool, - /** - * Override the default text for the *open popup* icon button. - * - * For localization purposes, you can use the provided [translations](/guides/localization/). - */ - openText: PropTypes.string, - /** - * Array of options. - */ - options: PropTypes.array.isRequired, - /** - * The component used to render the body of the popup. - */ - PaperComponent: PropTypes.elementType, - /** - * The component used to position the popup. - */ - PopperComponent: PropTypes.elementType, - /** - * The icon to display in place of the default popup icon. - */ - popupIcon: PropTypes.node, - /** - * Render the group. - * - * @param {any} option The group to render. - * @returns {ReactNode} - */ - renderGroup: PropTypes.func, - /** - * Render the input. - * - * @param {object} params - * @returns {ReactNode} - */ - renderInput: PropTypes.func.isRequired, - /** - * Render the option, use `getOptionLabel` by default. - * - * @param {T} option The option to render. - * @param {object} state The state of the component. - * @returns {ReactNode} - */ - renderOption: PropTypes.func, - /** - * Render the selected value. - * - * @param {T[]} value The `value` provided to the component. - * @param {function} getTagProps A tag props getter. - * @returns {ReactNode} - */ - renderTags: PropTypes.func, - /** - * If `true`, the input's text will be selected on focus. - */ - selectOnFocus: PropTypes.bool, - /** - * The size of the autocomplete. - */ - size: PropTypes.oneOf(['medium', 'small']), - /** - * The value of the autocomplete. - * - * The value must have reference equality with the option in order to be selected. - * You can customize the equality behavior with the `getOptionSelected` prop. - */ - value: PropTypes.oneOfType([PropTypes.any, PropTypes.array]), -}; - -export default withStyles(styles, { name: 'MuiCreatableAutocomplete' })(CreatableAutocomplete); diff --git a/packages/material-ui-lab/src/CreatableAutocomplete/index.d.ts b/packages/material-ui-lab/src/CreatableAutocomplete/index.d.ts deleted file mode 100644 index 8bf1f571d4b4d4..00000000000000 --- a/packages/material-ui-lab/src/CreatableAutocomplete/index.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { default } from './CreatableAutocomplete'; - -export * from './CreatableAutocomplete'; diff --git a/packages/material-ui-lab/src/CreatableAutocomplete/index.js b/packages/material-ui-lab/src/CreatableAutocomplete/index.js deleted file mode 100644 index 773606f973961f..00000000000000 --- a/packages/material-ui-lab/src/CreatableAutocomplete/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './CreatableAutocomplete'; From c5203ad2e408561199dd50dd860ddade6a9ed506 Mon Sep 17 00:00:00 2001 From: "@itelofilho" Date: Tue, 11 Feb 2020 21:54:01 -0300 Subject: [PATCH 3/7] [CreatableAutocomplete]: add a new component to create option in autocomplete [wip] --- .../autocomplete/CreatableAutocomplete.js | 122 ++++++ .../CreatableAutocompleteDialog.js | 166 ++++++++ .../components/autocomplete/autocomplete.md | 8 + .../CreatableAutocomplete.d.ts | 45 +++ .../CreatableAutocomplete.js | 357 ++++++++++++++++++ .../src/CreatableAutocomplete/index.d.ts | 3 + .../src/CreatableAutocomplete/index.js | 1 + 7 files changed, 702 insertions(+) create mode 100644 docs/src/pages/components/autocomplete/CreatableAutocomplete.js create mode 100644 docs/src/pages/components/autocomplete/CreatableAutocompleteDialog.js create mode 100644 packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.d.ts create mode 100644 packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.js create mode 100644 packages/material-ui-lab/src/CreatableAutocomplete/index.d.ts create mode 100644 packages/material-ui-lab/src/CreatableAutocomplete/index.js diff --git a/docs/src/pages/components/autocomplete/CreatableAutocomplete.js b/docs/src/pages/components/autocomplete/CreatableAutocomplete.js new file mode 100644 index 00000000000000..79c82bf0618c52 --- /dev/null +++ b/docs/src/pages/components/autocomplete/CreatableAutocomplete.js @@ -0,0 +1,122 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import TextField from '@material-ui/core/TextField'; +import CreatableAutocomplete from '@material-ui/lab/CreatableAutocomplete'; + +export default function CreatableAutocompleteDialog() { + return ( + option.label} + style={{ width: 300 }} + renderInput={params => ( + + )} + /> + ); +} + +// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top +const top100Films = [ + { label: 'The Shawshank Redemption', value: 1994 }, + { label: 'The Godfather', value: 1972 }, + { label: 'The Godfather: Part II', value: 1974 }, + { label: 'The Dark Knight', value: 2008 }, + { label: '12 Angry Men', value: 1957 }, + { label: "Schindler's List", value: 1993 }, + { label: 'Pulp Fiction', value: 1994 }, + { label: 'The Lord of the Rings: The Return of the King', value: 2003 }, + { label: 'The Good, the Bad and the Ugly', value: 1966 }, + { label: 'Fight Club', value: 1999 }, + { label: 'The Lord of the Rings: The Fellowship of the Ring', value: 2001 }, + { label: 'Star Wars: Episode V - The Empire Strikes Back', value: 1980 }, + { label: 'Forrest Gump', value: 1994 }, + { label: 'Inception', value: 2010 }, + { label: 'The Lord of the Rings: The Two Towers', value: 2002 }, + { label: "One Flew Over the Cuckoo's Nest", value: 1975 }, + { label: 'Goodfellas', value: 1990 }, + { label: 'The Matrix', value: 1999 }, + { label: 'Seven Samurai', value: 1954 }, + { label: 'Star Wars: Episode IV - A New Hope', value: 1977 }, + { label: 'City of God', value: 2002 }, + { label: 'Se7en', value: 1995 }, + { label: 'The Silence of the Lambs', value: 1991 }, + { label: "It's a Wonderful Life", value: 1946 }, + { label: 'Life Is Beautiful', value: 1997 }, + { label: 'The Usual Suspects', value: 1995 }, + { label: 'Léon: The Professional', value: 1994 }, + { label: 'Spirited Away', value: 2001 }, + { label: 'Saving Private Ryan', value: 1998 }, + { label: 'Once Upon a Time in the West', value: 1968 }, + { label: 'American History X', value: 1998 }, + { label: 'Interstellar', value: 2014 }, + { label: 'Casablanca', value: 1942 }, + { label: 'City Lights', value: 1931 }, + { label: 'Psycho', value: 1960 }, + { label: 'The Green Mile', value: 1999 }, + { label: 'The Intouchables', value: 2011 }, + { label: 'Modern Times', value: 1936 }, + { label: 'Raiders of the Lost Ark', value: 1981 }, + { label: 'Rear Window', value: 1954 }, + { label: 'The Pianist', value: 2002 }, + { label: 'The Departed', value: 2006 }, + { label: 'Terminator 2: Judgment Day', value: 1991 }, + { label: 'Back to the Future', value: 1985 }, + { label: 'Whiplash', value: 2014 }, + { label: 'Gladiator', value: 2000 }, + { label: 'Memento', value: 2000 }, + { label: 'The Prestige', value: 2006 }, + { label: 'The Lion King', value: 1994 }, + { label: 'Apocalypse Now', value: 1979 }, + { label: 'Alien', value: 1979 }, + { label: 'Sunset Boulevard', value: 1950 }, + { label: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', value: 1964 }, + { label: 'The Great Dictator', value: 1940 }, + { label: 'Cinema Paradiso', value: 1988 }, + { label: 'The Lives of Others', value: 2006 }, + { label: 'Grave of the Fireflies', value: 1988 }, + { label: 'Paths of Glory', value: 1957 }, + { label: 'Django Unchained', value: 2012 }, + { label: 'The Shining', value: 1980 }, + { label: 'WALL·E', value: 2008 }, + { label: 'American Beauty', value: 1999 }, + { label: 'The Dark Knight Rises', value: 2012 }, + { label: 'Princess Mononoke', value: 1997 }, + { label: 'Aliens', value: 1986 }, + { label: 'Oldboy', value: 2003 }, + { label: 'Once Upon a Time in America', value: 1984 }, + { label: 'Witness for the Prosecution', value: 1957 }, + { label: 'Das Boot', value: 1981 }, + { label: 'Citizen Kane', value: 1941 }, + { label: 'North by Northwest', value: 1959 }, + { label: 'Vertigo', value: 1958 }, + { label: 'Star Wars: Episode VI - Return of the Jedi', value: 1983 }, + { label: 'Reservoir Dogs', value: 1992 }, + { label: 'Braveheart', value: 1995 }, + { label: 'M', value: 1931 }, + { label: 'Requiem for a Dream', value: 2000 }, + { label: 'Amélie', value: 2001 }, + { label: 'A Clockwork Orange', value: 1971 }, + { label: 'Like Stars on Earth', value: 2007 }, + { label: 'Taxi Driver', value: 1976 }, + { label: 'Lawrence of Arabia', value: 1962 }, + { label: 'Double Indemnity', value: 1944 }, + { label: 'Eternal Sunshine of the Spotless Mind', value: 2004 }, + { label: 'Amadeus', value: 1984 }, + { label: 'To Kill a Mockingbird', value: 1962 }, + { label: 'Toy Story 3', value: 2010 }, + { label: 'Logan', value: 2017 }, + { label: 'Full Metal Jacket', value: 1987 }, + { label: 'Dangal', value: 2016 }, + { label: 'The Sting', value: 1973 }, + { label: '2001: A Space Odyssey', value: 1968 }, + { label: "Singin' in the Rain", value: 1952 }, + { label: 'Toy Story', value: 1995 }, + { label: 'Bicycle Thieves', value: 1948 }, + { label: 'The Kid', value: 1921 }, + { label: 'Inglourious Basterds', value: 2009 }, + { label: 'Snatch', value: 2000 }, + { label: '3 Idiots', value: 2009 }, + { label: 'Monty Python and the Holy Grail', value: 1975 }, +]; diff --git a/docs/src/pages/components/autocomplete/CreatableAutocompleteDialog.js b/docs/src/pages/components/autocomplete/CreatableAutocompleteDialog.js new file mode 100644 index 00000000000000..81e5205c00af25 --- /dev/null +++ b/docs/src/pages/components/autocomplete/CreatableAutocompleteDialog.js @@ -0,0 +1,166 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import TextField from '@material-ui/core/TextField'; +import Dialog from '@material-ui/core/Dialog'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogActions from '@material-ui/core/DialogActions'; +import Button from '@material-ui/core/Button'; +import CreatableAutocomplete from '@material-ui/lab/CreatableAutocomplete'; + +export default function CreatableAutocompleteExample() { + const [open, toggleOpen] = React.useState(false); + + const handleClose = () => toggleOpen(false); + + return ( + <> + + Create a new film + + + missing a film, please insert it in. + + + + + + + + + + option.label} + style={{ width: 300 }} + onCreateNewOption={() => toggleOpen(true)} + renderInput={params => ( + + )} + /> + + ); +} + +// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top +const top100Films = [ + { label: 'The Shawshank Redemption', value: 1994 }, + { label: 'The Godfather', value: 1972 }, + { label: 'The Godfather: Part II', value: 1974 }, + { label: 'The Dark Knight', value: 2008 }, + { label: '12 Angry Men', value: 1957 }, + { label: "Schindler's List", value: 1993 }, + { label: 'Pulp Fiction', value: 1994 }, + { label: 'The Lord of the Rings: The Return of the King', value: 2003 }, + { label: 'The Good, the Bad and the Ugly', value: 1966 }, + { label: 'Fight Club', value: 1999 }, + { label: 'The Lord of the Rings: The Fellowship of the Ring', value: 2001 }, + { label: 'Star Wars: Episode V - The Empire Strikes Back', value: 1980 }, + { label: 'Forrest Gump', value: 1994 }, + { label: 'Inception', value: 2010 }, + { label: 'The Lord of the Rings: The Two Towers', value: 2002 }, + { label: "One Flew Over the Cuckoo's Nest", value: 1975 }, + { label: 'Goodfellas', value: 1990 }, + { label: 'The Matrix', value: 1999 }, + { label: 'Seven Samurai', value: 1954 }, + { label: 'Star Wars: Episode IV - A New Hope', value: 1977 }, + { label: 'City of God', value: 2002 }, + { label: 'Se7en', value: 1995 }, + { label: 'The Silence of the Lambs', value: 1991 }, + { label: "It's a Wonderful Life", value: 1946 }, + { label: 'Life Is Beautiful', value: 1997 }, + { label: 'The Usual Suspects', value: 1995 }, + { label: 'Léon: The Professional', value: 1994 }, + { label: 'Spirited Away', value: 2001 }, + { label: 'Saving Private Ryan', value: 1998 }, + { label: 'Once Upon a Time in the West', value: 1968 }, + { label: 'American History X', value: 1998 }, + { label: 'Interstellar', value: 2014 }, + { label: 'Casablanca', value: 1942 }, + { label: 'City Lights', value: 1931 }, + { label: 'Psycho', value: 1960 }, + { label: 'The Green Mile', value: 1999 }, + { label: 'The Intouchables', value: 2011 }, + { label: 'Modern Times', value: 1936 }, + { label: 'Raiders of the Lost Ark', value: 1981 }, + { label: 'Rear Window', value: 1954 }, + { label: 'The Pianist', value: 2002 }, + { label: 'The Departed', value: 2006 }, + { label: 'Terminator 2: Judgment Day', value: 1991 }, + { label: 'Back to the Future', value: 1985 }, + { label: 'Whiplash', value: 2014 }, + { label: 'Gladiator', value: 2000 }, + { label: 'Memento', value: 2000 }, + { label: 'The Prestige', value: 2006 }, + { label: 'The Lion King', value: 1994 }, + { label: 'Apocalypse Now', value: 1979 }, + { label: 'Alien', value: 1979 }, + { label: 'Sunset Boulevard', value: 1950 }, + { label: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', value: 1964 }, + { label: 'The Great Dictator', value: 1940 }, + { label: 'Cinema Paradiso', value: 1988 }, + { label: 'The Lives of Others', value: 2006 }, + { label: 'Grave of the Fireflies', value: 1988 }, + { label: 'Paths of Glory', value: 1957 }, + { label: 'Django Unchained', value: 2012 }, + { label: 'The Shining', value: 1980 }, + { label: 'WALL·E', value: 2008 }, + { label: 'American Beauty', value: 1999 }, + { label: 'The Dark Knight Rises', value: 2012 }, + { label: 'Princess Mononoke', value: 1997 }, + { label: 'Aliens', value: 1986 }, + { label: 'Oldboy', value: 2003 }, + { label: 'Once Upon a Time in America', value: 1984 }, + { label: 'Witness for the Prosecution', value: 1957 }, + { label: 'Das Boot', value: 1981 }, + { label: 'Citizen Kane', value: 1941 }, + { label: 'North by Northwest', value: 1959 }, + { label: 'Vertigo', value: 1958 }, + { label: 'Star Wars: Episode VI - Return of the Jedi', value: 1983 }, + { label: 'Reservoir Dogs', value: 1992 }, + { label: 'Braveheart', value: 1995 }, + { label: 'M', value: 1931 }, + { label: 'Requiem for a Dream', value: 2000 }, + { label: 'Amélie', value: 2001 }, + { label: 'A Clockwork Orange', value: 1971 }, + { label: 'Like Stars on Earth', value: 2007 }, + { label: 'Taxi Driver', value: 1976 }, + { label: 'Lawrence of Arabia', value: 1962 }, + { label: 'Double Indemnity', value: 1944 }, + { label: 'Eternal Sunshine of the Spotless Mind', value: 2004 }, + { label: 'Amadeus', value: 1984 }, + { label: 'To Kill a Mockingbird', value: 1962 }, + { label: 'Toy Story 3', value: 2010 }, + { label: 'Logan', value: 2017 }, + { label: 'Full Metal Jacket', value: 1987 }, + { label: 'Dangal', value: 2016 }, + { label: 'The Sting', value: 1973 }, + { label: '2001: A Space Odyssey', value: 1968 }, + { label: "Singin' in the Rain", value: 1952 }, + { label: 'Toy Story', value: 1995 }, + { label: 'Bicycle Thieves', value: 1948 }, + { label: 'The Kid', value: 1921 }, + { label: 'Inglourious Basterds', value: 2009 }, + { label: 'Snatch', value: 2000 }, + { label: '3 Idiots', value: 2009 }, + { label: 'Monty Python and the Holy Grail', value: 1975 }, +]; diff --git a/docs/src/pages/components/autocomplete/autocomplete.md b/docs/src/pages/components/autocomplete/autocomplete.md index 907e13b4985028..3078ec2ae2284e 100644 --- a/docs/src/pages/components/autocomplete/autocomplete.md +++ b/docs/src/pages/components/autocomplete/autocomplete.md @@ -12,6 +12,14 @@ The widget is useful for setting the value of a single-line textbox in one of tw 1. The value for the textbox must be chosen from a predefined set of allowed values, e.g., a location field must contain a valid location name: [combo box](#combo-box). 2. The textbox may contain any arbitrary value, but it is advantageous to suggest possible values to the user, e.g., a search field may suggest similar or previous searches to save the user time: [free solo](#free-solo). +## Creatable Autocomplete + +{{"demo": "pages/components/autocomplete/CreatableAutocomplete.js"}} + +## Creatable Autocomplete Dialog + +{{"demo": "pages/components/autocomplete/CreatableAutocompleteDialog.js"}} + ## Combo box The value must be chosen from a predefined set of allowed values. diff --git a/packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.d.ts b/packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.d.ts new file mode 100644 index 00000000000000..caeaf9de68870b --- /dev/null +++ b/packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.d.ts @@ -0,0 +1,45 @@ +import * as React from 'react'; +import { StandardProps } from '@material-ui/core'; +import { PopperProps } from '@material-ui/core/Popper'; +import { + UseAutocompleteCommonProps, + createFilterOptions, + UseAutocompleteProps, +} from '../useAutocomplete'; +import { AutocompleteProps } from '../Autocomplete'; + +export interface CreatableAutocomplete extends UseAutocompleteCommonProps, +StandardProps< + React.HTMLAttributes, + AutocompleteClassKey, + 'defaultValue' | 'onChange' | 'children' +> { + onCreateNewOption: UseAutocompleteProps['onChange']; +} + +export type AutocompleteClassKey = + | 'root' + | 'focused' + | 'tag' + | 'tagSizeSmall' + | 'inputRoot' + | 'input' + | 'inputFocused' + | 'endAdornment' + | 'clearIndicator' + | 'clearIndicatorDirty' + | 'popupIndicator' + | 'popupIndicatorOpen' + | 'popper' + | 'popperDisablePortal' + | 'paper' + | 'listbox' + | 'loading' + | 'noOptions' + | 'option' + | 'groupLabel' + | 'groupUl'; + +export default function CreatableAutocomplete( + props: CreatableAutocomplete, +): JSX.Element; diff --git a/packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.js b/packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.js new file mode 100644 index 00000000000000..a68e92266b6fa9 --- /dev/null +++ b/packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.js @@ -0,0 +1,357 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +/* eslint-disable react/prop-types */ +/* eslint-disable no-console */ +/* eslint-disable-next-line react/prop-types */ + +import React from 'react'; +import PropTypes from 'prop-types'; +import { withStyles } from '@material-ui/core/styles'; +import Autocomplete from '@material-ui/lab/Autocomplete'; + +export const styles = () => ({}); + +const CreatableAutocomplete = (props) => { + const { + options: optionsProps, + inputValue, + createOptionPositionInLast = true, + onCreateNewOption, + getNewOptionLabel = (value) => `Create "${value}"`, + getNewOptionData = (value, optionLabel) => ({ + label: optionLabel, + value, + isNew: true, + }), + onChange, + ...otherProps + } = props; + const [_inputValue, setInputValue] = React.useState(inputValue); + const [options, setOptions] = React.useState(optionsProps); + + React.useEffect(() => { + const newOptionData = getNewOptionData(_inputValue, getNewOptionLabel(_inputValue)); + const newOptions = createOptionPositionInLast ? [...optionsProps, newOptionData] : [newOptionData, ...optionsProps]; + + setOptions(newOptions); + }, [optionsProps, _inputValue]); + + const handleChange = + (e, option) => { + console.log('if (option.isNew) {'); + console.log({props}); + if (option.isNew) { + console.log('if (onCreateNewOption) {'); + console.log({onCreateNewOption}); + if (onCreateNewOption) { + onCreateNewOption(e, option); + return; + } + console.log('if (onChange) {'); + console.log({onChange}); + if (onChange) { + onChange(e, option); + } + } + } + + return ( + setInputValue(value)} + /> + ); +} + +Autocomplete.propTypes = { + // ----------------------------- Warning -------------------------------- + // | These PropTypes are generated from the TypeScript type definitions | + // | To update them edit the d.ts file and run "yarn proptypes" | + // ---------------------------------------------------------------------- + /** + * If `true`, the portion of the selected suggestion that has not been typed by the user, + * known as the completion string, appears inline after the input cursor in the textbox. + * The inline completion string is visually highlighted and has a selected state. + */ + autoComplete: PropTypes.bool, + /** + * If `true`, the first option is automatically highlighted. + */ + autoHighlight: PropTypes.bool, + /** + * If `true`, the selected option becomes the value of the input + * when the Autocomplete loses focus unless the user chooses + * a different option or changes the character string in the input. + */ + autoSelect: PropTypes.bool, + /** + * Control if the input should be blurred when an option is selected: + * + * - `false` the input is not blurred. + * - `true` the input is always blurred. + * - `touch` the input is blurred after a touch event. + * - `mouse` the input is blurred after a mouse event. + */ + blurOnSelect: PropTypes.oneOfType([PropTypes.oneOf(['mouse', 'touch']), PropTypes.bool]), + /** + * Props applied to the [`Chip`](/api/chip/) element. + */ + ChipProps: PropTypes.object, + /** + * Override or extend the styles applied to the component. + * See [CSS API](#css) below for more details. + */ + classes: PropTypes.object, + /** + * @ignore + */ + className: PropTypes.string, + /** + * If `true`, clear all values when the user presses escape and the popup is closed. + */ + clearOnEscape: PropTypes.bool, + /** + * Override the default text for the *clear* icon button. + * + * For localization purposes, you can use the provided [translations](/guides/localization/). + */ + clearText: PropTypes.string, + /** + * The icon to display in place of the default close icon. + */ + closeIcon: PropTypes.node, + /** + * Override the default text for the *close popup* icon button. + * + * For localization purposes, you can use the provided [translations](/guides/localization/). + */ + closeText: PropTypes.string, + /** + * If `true`, the popup will ignore the blur event if the input if filled. + * You can inspect the popup markup with your browser tools. + * Consider this option when you need to customize the component. + */ + debug: PropTypes.bool, + /** + * The default input value. Use when the component is not controlled. + */ + defaultValue: PropTypes.oneOfType([PropTypes.any, PropTypes.array]), + /** + * If `true`, the input can't be cleared. + */ + disableClearable: PropTypes.bool, + /** + * If `true`, the popup won't close when a value is selected. + */ + disableCloseOnSelect: PropTypes.bool, + /** + * If `true`, the input will be disabled. + */ + disabled: PropTypes.bool, + /** + * If `true`, the list box in the popup will not wrap focus. + */ + disableListWrap: PropTypes.bool, + /** + * If `true`, the popup won't open on input focus. + */ + disableOpenOnFocus: PropTypes.bool, + /** + * Disable the portal behavior. + * The children stay within it's parent DOM hierarchy. + */ + disablePortal: PropTypes.bool, + /** + * A filter function that determines the options that are eligible. + * + * @param {T[]} options The options to render. + * @param {object} state The state of the component. + * @returns {T[]} + */ + filterOptions: PropTypes.func, + /** + * If `true`, hide the selected options from the list box. + */ + filterSelectedOptions: PropTypes.bool, + /** + * Force the visibility display of the popup icon. + */ + forcePopupIcon: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.bool]), + /** + * If `true`, the Autocomplete is free solo, meaning that the user input is not bound to provided options. + */ + freeSolo: PropTypes.bool, + /** + * Used to determine the disabled state for a given option. + */ + getOptionDisabled: PropTypes.func, + /** + * 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). + */ + getOptionLabel: PropTypes.func, + /** + * Used to determine if an option is selected. + * Uses strict equality by default. + */ + getOptionSelected: PropTypes.func, + /** + * 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. + * + * @param {T} options The option to group. + * @returns {string} + */ + groupBy: PropTypes.func, + /** + * 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. + */ + id: PropTypes.string, + /** + * If `true`, the highlight can move to the input. + */ + includeInputInList: PropTypes.bool, + /** + * The input value. + */ + inputValue: PropTypes.string, + /** + * The component used to render the listbox. + */ + ListboxComponent: PropTypes.elementType, + /** + * Props applied to the Listbox element. + */ + ListboxProps: PropTypes.object, + /** + * If `true`, the component is in a loading state. + */ + loading: PropTypes.bool, + /** + * Text to display when in a loading state. + * + * For localization purposes, you can use the provided [translations](/guides/localization/). + */ + loadingText: PropTypes.node, + /** + * If `true`, `value` must be an array and the menu will support multiple selections. + */ + multiple: PropTypes.bool, + /** + * Text to display when there are no options. + * + * For localization purposes, you can use the provided [translations](/guides/localization/). + */ + noOptionsText: PropTypes.node, + /** + * Callback fired when the value changes. + * + * @param {object} event The event source of the callback. + * @param {T} value + */ + onChange: PropTypes.func, + /** + * Callback fired when the popup requests to be closed. + * Use in controlled mode (see open). + * + * @param {object} event The event source of the callback. + */ + onClose: PropTypes.func, + /** + * Callback fired when the input value changes. + * + * @param {object} event The event source of the callback. + * @param {string} value The new value of the text input. + * @param {string} reason Can be: `"input"` (user input), `"reset"` (programmatic change), `"clear"`. + */ + onInputChange: PropTypes.func, + /** + * Text to display when there are no options. + * + * For localization purposes, you can use the provided [translations](/guides/localization/). + */ + onNoOptionsSelected: PropTypes.elementType, + /** + * Callback fired when the popup requests to be opened. + * Use in controlled mode (see open). + * + * @param {object} event The event source of the callback. + */ + onOpen: PropTypes.func, + /** + * Control the popup` open state. + */ + open: PropTypes.bool, + /** + * Override the default text for the *open popup* icon button. + * + * For localization purposes, you can use the provided [translations](/guides/localization/). + */ + openText: PropTypes.string, + /** + * Array of options. + */ + options: PropTypes.array.isRequired, + /** + * The component used to render the body of the popup. + */ + PaperComponent: PropTypes.elementType, + /** + * The component used to position the popup. + */ + PopperComponent: PropTypes.elementType, + /** + * The icon to display in place of the default popup icon. + */ + popupIcon: PropTypes.node, + /** + * Render the group. + * + * @param {any} option The group to render. + * @returns {ReactNode} + */ + renderGroup: PropTypes.func, + /** + * Render the input. + * + * @param {object} params + * @returns {ReactNode} + */ + renderInput: PropTypes.func.isRequired, + /** + * Render the option, use `getOptionLabel` by default. + * + * @param {T} option The option to render. + * @param {object} state The state of the component. + * @returns {ReactNode} + */ + renderOption: PropTypes.func, + /** + * Render the selected value. + * + * @param {T[]} value The `value` provided to the component. + * @param {function} getTagProps A tag props getter. + * @returns {ReactNode} + */ + renderTags: PropTypes.func, + /** + * If `true`, the input's text will be selected on focus. + */ + selectOnFocus: PropTypes.bool, + /** + * The size of the autocomplete. + */ + size: PropTypes.oneOf(['medium', 'small']), + /** + * The value of the autocomplete. + * + * The value must have reference equality with the option in order to be selected. + * You can customize the equality behavior with the `getOptionSelected` prop. + */ + value: PropTypes.oneOfType([PropTypes.any, PropTypes.array]), +}; + +export default withStyles(styles, { name: 'MuiCreatableAutocomplete' })(CreatableAutocomplete); diff --git a/packages/material-ui-lab/src/CreatableAutocomplete/index.d.ts b/packages/material-ui-lab/src/CreatableAutocomplete/index.d.ts new file mode 100644 index 00000000000000..8bf1f571d4b4d4 --- /dev/null +++ b/packages/material-ui-lab/src/CreatableAutocomplete/index.d.ts @@ -0,0 +1,3 @@ +export { default } from './CreatableAutocomplete'; + +export * from './CreatableAutocomplete'; diff --git a/packages/material-ui-lab/src/CreatableAutocomplete/index.js b/packages/material-ui-lab/src/CreatableAutocomplete/index.js new file mode 100644 index 00000000000000..773606f973961f --- /dev/null +++ b/packages/material-ui-lab/src/CreatableAutocomplete/index.js @@ -0,0 +1 @@ +export { default } from './CreatableAutocomplete'; From 04dca9db8510904d8bc4cfc92fc23487cf9ab0a4 Mon Sep 17 00:00:00 2001 From: "@itelofilho" Date: Wed, 12 Feb 2020 21:44:19 -0300 Subject: [PATCH 4/7] remove CreatableAucomplete component and improve docs --- .../autocomplete/CreatableAutocomplete.js | 122 ------ .../CreatableAutocompleteDialog.js | 166 -------- .../autocomplete/FreeSoloCreateOption.js | 154 ++++++++ .../FreeSoloCreateOptionDialog.js | 218 +++++++++++ .../components/autocomplete/autocomplete.md | 20 +- .../CreatableAutocomplete.d.ts | 45 --- .../CreatableAutocomplete.js | 357 ------------------ .../src/CreatableAutocomplete/index.d.ts | 3 - .../src/CreatableAutocomplete/index.js | 1 - 9 files changed, 384 insertions(+), 702 deletions(-) delete mode 100644 docs/src/pages/components/autocomplete/CreatableAutocomplete.js delete mode 100644 docs/src/pages/components/autocomplete/CreatableAutocompleteDialog.js create mode 100644 docs/src/pages/components/autocomplete/FreeSoloCreateOption.js create mode 100644 docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.js delete mode 100644 packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.d.ts delete mode 100644 packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.js delete mode 100644 packages/material-ui-lab/src/CreatableAutocomplete/index.d.ts delete mode 100644 packages/material-ui-lab/src/CreatableAutocomplete/index.js diff --git a/docs/src/pages/components/autocomplete/CreatableAutocomplete.js b/docs/src/pages/components/autocomplete/CreatableAutocomplete.js deleted file mode 100644 index 79c82bf0618c52..00000000000000 --- a/docs/src/pages/components/autocomplete/CreatableAutocomplete.js +++ /dev/null @@ -1,122 +0,0 @@ -/* eslint-disable no-use-before-define */ -import React from 'react'; -import TextField from '@material-ui/core/TextField'; -import CreatableAutocomplete from '@material-ui/lab/CreatableAutocomplete'; - -export default function CreatableAutocompleteDialog() { - return ( - option.label} - style={{ width: 300 }} - renderInput={params => ( - - )} - /> - ); -} - -// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top -const top100Films = [ - { label: 'The Shawshank Redemption', value: 1994 }, - { label: 'The Godfather', value: 1972 }, - { label: 'The Godfather: Part II', value: 1974 }, - { label: 'The Dark Knight', value: 2008 }, - { label: '12 Angry Men', value: 1957 }, - { label: "Schindler's List", value: 1993 }, - { label: 'Pulp Fiction', value: 1994 }, - { label: 'The Lord of the Rings: The Return of the King', value: 2003 }, - { label: 'The Good, the Bad and the Ugly', value: 1966 }, - { label: 'Fight Club', value: 1999 }, - { label: 'The Lord of the Rings: The Fellowship of the Ring', value: 2001 }, - { label: 'Star Wars: Episode V - The Empire Strikes Back', value: 1980 }, - { label: 'Forrest Gump', value: 1994 }, - { label: 'Inception', value: 2010 }, - { label: 'The Lord of the Rings: The Two Towers', value: 2002 }, - { label: "One Flew Over the Cuckoo's Nest", value: 1975 }, - { label: 'Goodfellas', value: 1990 }, - { label: 'The Matrix', value: 1999 }, - { label: 'Seven Samurai', value: 1954 }, - { label: 'Star Wars: Episode IV - A New Hope', value: 1977 }, - { label: 'City of God', value: 2002 }, - { label: 'Se7en', value: 1995 }, - { label: 'The Silence of the Lambs', value: 1991 }, - { label: "It's a Wonderful Life", value: 1946 }, - { label: 'Life Is Beautiful', value: 1997 }, - { label: 'The Usual Suspects', value: 1995 }, - { label: 'Léon: The Professional', value: 1994 }, - { label: 'Spirited Away', value: 2001 }, - { label: 'Saving Private Ryan', value: 1998 }, - { label: 'Once Upon a Time in the West', value: 1968 }, - { label: 'American History X', value: 1998 }, - { label: 'Interstellar', value: 2014 }, - { label: 'Casablanca', value: 1942 }, - { label: 'City Lights', value: 1931 }, - { label: 'Psycho', value: 1960 }, - { label: 'The Green Mile', value: 1999 }, - { label: 'The Intouchables', value: 2011 }, - { label: 'Modern Times', value: 1936 }, - { label: 'Raiders of the Lost Ark', value: 1981 }, - { label: 'Rear Window', value: 1954 }, - { label: 'The Pianist', value: 2002 }, - { label: 'The Departed', value: 2006 }, - { label: 'Terminator 2: Judgment Day', value: 1991 }, - { label: 'Back to the Future', value: 1985 }, - { label: 'Whiplash', value: 2014 }, - { label: 'Gladiator', value: 2000 }, - { label: 'Memento', value: 2000 }, - { label: 'The Prestige', value: 2006 }, - { label: 'The Lion King', value: 1994 }, - { label: 'Apocalypse Now', value: 1979 }, - { label: 'Alien', value: 1979 }, - { label: 'Sunset Boulevard', value: 1950 }, - { label: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', value: 1964 }, - { label: 'The Great Dictator', value: 1940 }, - { label: 'Cinema Paradiso', value: 1988 }, - { label: 'The Lives of Others', value: 2006 }, - { label: 'Grave of the Fireflies', value: 1988 }, - { label: 'Paths of Glory', value: 1957 }, - { label: 'Django Unchained', value: 2012 }, - { label: 'The Shining', value: 1980 }, - { label: 'WALL·E', value: 2008 }, - { label: 'American Beauty', value: 1999 }, - { label: 'The Dark Knight Rises', value: 2012 }, - { label: 'Princess Mononoke', value: 1997 }, - { label: 'Aliens', value: 1986 }, - { label: 'Oldboy', value: 2003 }, - { label: 'Once Upon a Time in America', value: 1984 }, - { label: 'Witness for the Prosecution', value: 1957 }, - { label: 'Das Boot', value: 1981 }, - { label: 'Citizen Kane', value: 1941 }, - { label: 'North by Northwest', value: 1959 }, - { label: 'Vertigo', value: 1958 }, - { label: 'Star Wars: Episode VI - Return of the Jedi', value: 1983 }, - { label: 'Reservoir Dogs', value: 1992 }, - { label: 'Braveheart', value: 1995 }, - { label: 'M', value: 1931 }, - { label: 'Requiem for a Dream', value: 2000 }, - { label: 'Amélie', value: 2001 }, - { label: 'A Clockwork Orange', value: 1971 }, - { label: 'Like Stars on Earth', value: 2007 }, - { label: 'Taxi Driver', value: 1976 }, - { label: 'Lawrence of Arabia', value: 1962 }, - { label: 'Double Indemnity', value: 1944 }, - { label: 'Eternal Sunshine of the Spotless Mind', value: 2004 }, - { label: 'Amadeus', value: 1984 }, - { label: 'To Kill a Mockingbird', value: 1962 }, - { label: 'Toy Story 3', value: 2010 }, - { label: 'Logan', value: 2017 }, - { label: 'Full Metal Jacket', value: 1987 }, - { label: 'Dangal', value: 2016 }, - { label: 'The Sting', value: 1973 }, - { label: '2001: A Space Odyssey', value: 1968 }, - { label: "Singin' in the Rain", value: 1952 }, - { label: 'Toy Story', value: 1995 }, - { label: 'Bicycle Thieves', value: 1948 }, - { label: 'The Kid', value: 1921 }, - { label: 'Inglourious Basterds', value: 2009 }, - { label: 'Snatch', value: 2000 }, - { label: '3 Idiots', value: 2009 }, - { label: 'Monty Python and the Holy Grail', value: 1975 }, -]; diff --git a/docs/src/pages/components/autocomplete/CreatableAutocompleteDialog.js b/docs/src/pages/components/autocomplete/CreatableAutocompleteDialog.js deleted file mode 100644 index 81e5205c00af25..00000000000000 --- a/docs/src/pages/components/autocomplete/CreatableAutocompleteDialog.js +++ /dev/null @@ -1,166 +0,0 @@ -/* eslint-disable no-use-before-define */ -import React from 'react'; -import TextField from '@material-ui/core/TextField'; -import Dialog from '@material-ui/core/Dialog'; -import DialogTitle from '@material-ui/core/DialogTitle'; -import DialogContent from '@material-ui/core/DialogContent'; -import DialogContentText from '@material-ui/core/DialogContentText'; -import DialogActions from '@material-ui/core/DialogActions'; -import Button from '@material-ui/core/Button'; -import CreatableAutocomplete from '@material-ui/lab/CreatableAutocomplete'; - -export default function CreatableAutocompleteExample() { - const [open, toggleOpen] = React.useState(false); - - const handleClose = () => toggleOpen(false); - - return ( - <> - - Create a new film - - - missing a film, please insert it in. - - - - - - - - - - option.label} - style={{ width: 300 }} - onCreateNewOption={() => toggleOpen(true)} - renderInput={params => ( - - )} - /> - - ); -} - -// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top -const top100Films = [ - { label: 'The Shawshank Redemption', value: 1994 }, - { label: 'The Godfather', value: 1972 }, - { label: 'The Godfather: Part II', value: 1974 }, - { label: 'The Dark Knight', value: 2008 }, - { label: '12 Angry Men', value: 1957 }, - { label: "Schindler's List", value: 1993 }, - { label: 'Pulp Fiction', value: 1994 }, - { label: 'The Lord of the Rings: The Return of the King', value: 2003 }, - { label: 'The Good, the Bad and the Ugly', value: 1966 }, - { label: 'Fight Club', value: 1999 }, - { label: 'The Lord of the Rings: The Fellowship of the Ring', value: 2001 }, - { label: 'Star Wars: Episode V - The Empire Strikes Back', value: 1980 }, - { label: 'Forrest Gump', value: 1994 }, - { label: 'Inception', value: 2010 }, - { label: 'The Lord of the Rings: The Two Towers', value: 2002 }, - { label: "One Flew Over the Cuckoo's Nest", value: 1975 }, - { label: 'Goodfellas', value: 1990 }, - { label: 'The Matrix', value: 1999 }, - { label: 'Seven Samurai', value: 1954 }, - { label: 'Star Wars: Episode IV - A New Hope', value: 1977 }, - { label: 'City of God', value: 2002 }, - { label: 'Se7en', value: 1995 }, - { label: 'The Silence of the Lambs', value: 1991 }, - { label: "It's a Wonderful Life", value: 1946 }, - { label: 'Life Is Beautiful', value: 1997 }, - { label: 'The Usual Suspects', value: 1995 }, - { label: 'Léon: The Professional', value: 1994 }, - { label: 'Spirited Away', value: 2001 }, - { label: 'Saving Private Ryan', value: 1998 }, - { label: 'Once Upon a Time in the West', value: 1968 }, - { label: 'American History X', value: 1998 }, - { label: 'Interstellar', value: 2014 }, - { label: 'Casablanca', value: 1942 }, - { label: 'City Lights', value: 1931 }, - { label: 'Psycho', value: 1960 }, - { label: 'The Green Mile', value: 1999 }, - { label: 'The Intouchables', value: 2011 }, - { label: 'Modern Times', value: 1936 }, - { label: 'Raiders of the Lost Ark', value: 1981 }, - { label: 'Rear Window', value: 1954 }, - { label: 'The Pianist', value: 2002 }, - { label: 'The Departed', value: 2006 }, - { label: 'Terminator 2: Judgment Day', value: 1991 }, - { label: 'Back to the Future', value: 1985 }, - { label: 'Whiplash', value: 2014 }, - { label: 'Gladiator', value: 2000 }, - { label: 'Memento', value: 2000 }, - { label: 'The Prestige', value: 2006 }, - { label: 'The Lion King', value: 1994 }, - { label: 'Apocalypse Now', value: 1979 }, - { label: 'Alien', value: 1979 }, - { label: 'Sunset Boulevard', value: 1950 }, - { label: 'Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb', value: 1964 }, - { label: 'The Great Dictator', value: 1940 }, - { label: 'Cinema Paradiso', value: 1988 }, - { label: 'The Lives of Others', value: 2006 }, - { label: 'Grave of the Fireflies', value: 1988 }, - { label: 'Paths of Glory', value: 1957 }, - { label: 'Django Unchained', value: 2012 }, - { label: 'The Shining', value: 1980 }, - { label: 'WALL·E', value: 2008 }, - { label: 'American Beauty', value: 1999 }, - { label: 'The Dark Knight Rises', value: 2012 }, - { label: 'Princess Mononoke', value: 1997 }, - { label: 'Aliens', value: 1986 }, - { label: 'Oldboy', value: 2003 }, - { label: 'Once Upon a Time in America', value: 1984 }, - { label: 'Witness for the Prosecution', value: 1957 }, - { label: 'Das Boot', value: 1981 }, - { label: 'Citizen Kane', value: 1941 }, - { label: 'North by Northwest', value: 1959 }, - { label: 'Vertigo', value: 1958 }, - { label: 'Star Wars: Episode VI - Return of the Jedi', value: 1983 }, - { label: 'Reservoir Dogs', value: 1992 }, - { label: 'Braveheart', value: 1995 }, - { label: 'M', value: 1931 }, - { label: 'Requiem for a Dream', value: 2000 }, - { label: 'Amélie', value: 2001 }, - { label: 'A Clockwork Orange', value: 1971 }, - { label: 'Like Stars on Earth', value: 2007 }, - { label: 'Taxi Driver', value: 1976 }, - { label: 'Lawrence of Arabia', value: 1962 }, - { label: 'Double Indemnity', value: 1944 }, - { label: 'Eternal Sunshine of the Spotless Mind', value: 2004 }, - { label: 'Amadeus', value: 1984 }, - { label: 'To Kill a Mockingbird', value: 1962 }, - { label: 'Toy Story 3', value: 2010 }, - { label: 'Logan', value: 2017 }, - { label: 'Full Metal Jacket', value: 1987 }, - { label: 'Dangal', value: 2016 }, - { label: 'The Sting', value: 1973 }, - { label: '2001: A Space Odyssey', value: 1968 }, - { label: "Singin' in the Rain", value: 1952 }, - { label: 'Toy Story', value: 1995 }, - { label: 'Bicycle Thieves', value: 1948 }, - { label: 'The Kid', value: 1921 }, - { label: 'Inglourious Basterds', value: 2009 }, - { label: 'Snatch', value: 2000 }, - { label: '3 Idiots', value: 2009 }, - { label: 'Monty Python and the Holy Grail', value: 1975 }, -]; diff --git a/docs/src/pages/components/autocomplete/FreeSoloCreateOption.js b/docs/src/pages/components/autocomplete/FreeSoloCreateOption.js new file mode 100644 index 00000000000000..81f3c753d7ea45 --- /dev/null +++ b/docs/src/pages/components/autocomplete/FreeSoloCreateOption.js @@ -0,0 +1,154 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import TextField from '@material-ui/core/TextField'; +import Autocomplete, { createFilterOptions } from '@material-ui/lab/Autocomplete'; + +const filter = createFilterOptions(); + +export default function ComboBox() { + const [value, setValue] = React.useState(''); + + return ( + { + if (newValue && newValue.freeSolo) { + setValue({ + title: newValue.inputValue, + }); + return; + } + + setValue(newValue); + }} + filterOptions={(options, params) => { + const filtered = filter(options, params); + + if (params.inputValue !== '') { + filtered.push({ + freeSolo: true, + inputValue: params.inputValue, + title: `Add "${params.inputValue}"`, + }); + } + + return filtered; + }} + id="free-solo-with-text-demo" + options={top100Films} + getOptionLabel={option => option.title} + style={{ width: 300 }} + freeSolo + renderInput={params => ( + + )} + /> + ); +} + +// 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/FreeSoloCreateOptionDialog.js b/docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.js new file mode 100644 index 00000000000000..b6801b3334ea29 --- /dev/null +++ b/docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.js @@ -0,0 +1,218 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import TextField from '@material-ui/core/TextField'; +import Dialog from '@material-ui/core/Dialog'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogActions from '@material-ui/core/DialogActions'; +import Button from '@material-ui/core/Button'; +import Autocomplete, { createFilterOptions } from '@material-ui/lab/Autocomplete'; + +const filter = createFilterOptions(); + +export default function ComboBox() { + const [value, setValue] = React.useState(''); + + const [open, toggleOpen] = React.useState(false); + + const handleClose = () => { + setDialogValue({ + title: '', + year: null, + }); + toggleOpen(false); + }; + + const [dialogValue, setDialogValue] = React.useState({ + title: '', + year: null, + }); + + const handleAdd = () => { + setValue(dialogValue); + handleClose(); + }; + + return ( + <> + { + if (newValue && newValue.freeSolo) { + toggleOpen(true); + setDialogValue({ + title: newValue.inputValue, + year: null, + }); + return; + } + + setValue(newValue); + }} + filterOptions={(options, params) => { + console.log(params); + const filtered = filter(options, params); + + if (params.inputValue !== '') { + filtered.push({ + freeSolo: true, + inputValue: params.inputValue, + title: `Add "${params.inputValue}"`, + }); + } + + return filtered; + }} + id="free-solo-dialog-demo" + options={top100Films} + getOptionLabel={option => option.title} + style={{ width: 300 }} + freeSolo + renderInput={params => ( + + )} + /> + + Add a new film + + Did you miss any film in our list? Please, add it! + setDialogValue({ ...dialogValue, title: event.target.value })} + label="title" + type="text" + fullWidth + /> + setDialogValue({ ...dialogValue, year: event.target.value })} + label="year" + type="number" + fullWidth + /> + + + + + + + + ); +} + +// 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/autocomplete.md b/docs/src/pages/components/autocomplete/autocomplete.md index 3078ec2ae2284e..08bcd4d7982308 100644 --- a/docs/src/pages/components/autocomplete/autocomplete.md +++ b/docs/src/pages/components/autocomplete/autocomplete.md @@ -12,14 +12,6 @@ The widget is useful for setting the value of a single-line textbox in one of tw 1. The value for the textbox must be chosen from a predefined set of allowed values, e.g., a location field must contain a valid location name: [combo box](#combo-box). 2. The textbox may contain any arbitrary value, but it is advantageous to suggest possible values to the user, e.g., a search field may suggest similar or previous searches to save the user time: [free solo](#free-solo). -## Creatable Autocomplete - -{{"demo": "pages/components/autocomplete/CreatableAutocomplete.js"}} - -## Creatable Autocomplete Dialog - -{{"demo": "pages/components/autocomplete/CreatableAutocompleteDialog.js"}} - ## Combo box The value must be chosen from a predefined set of allowed values. @@ -46,6 +38,18 @@ However, if you intend to use it for a [combo box](#combo-box) like experience ( {{"demo": "pages/components/autocomplete/FreeSolo.js"}} +### Free solo with explicit text + +Sometimes you want to make explicit to the user that he/she can add whatever value he/she wants. + +{{"demo": "pages/components/autocomplete/FreeSoloCreateOption.js"}} + +### Free solo to open a Dialog + +You could also create dialog when the user wants to add a new value. + +{{"demo": "pages/components/autocomplete/FreeSoloCreateOptionDialog.js"}} + ## Grouped {{"demo": "pages/components/autocomplete/Grouped.js"}} diff --git a/packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.d.ts b/packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.d.ts deleted file mode 100644 index caeaf9de68870b..00000000000000 --- a/packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.d.ts +++ /dev/null @@ -1,45 +0,0 @@ -import * as React from 'react'; -import { StandardProps } from '@material-ui/core'; -import { PopperProps } from '@material-ui/core/Popper'; -import { - UseAutocompleteCommonProps, - createFilterOptions, - UseAutocompleteProps, -} from '../useAutocomplete'; -import { AutocompleteProps } from '../Autocomplete'; - -export interface CreatableAutocomplete extends UseAutocompleteCommonProps, -StandardProps< - React.HTMLAttributes, - AutocompleteClassKey, - 'defaultValue' | 'onChange' | 'children' -> { - onCreateNewOption: UseAutocompleteProps['onChange']; -} - -export type AutocompleteClassKey = - | 'root' - | 'focused' - | 'tag' - | 'tagSizeSmall' - | 'inputRoot' - | 'input' - | 'inputFocused' - | 'endAdornment' - | 'clearIndicator' - | 'clearIndicatorDirty' - | 'popupIndicator' - | 'popupIndicatorOpen' - | 'popper' - | 'popperDisablePortal' - | 'paper' - | 'listbox' - | 'loading' - | 'noOptions' - | 'option' - | 'groupLabel' - | 'groupUl'; - -export default function CreatableAutocomplete( - props: CreatableAutocomplete, -): JSX.Element; diff --git a/packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.js b/packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.js deleted file mode 100644 index a68e92266b6fa9..00000000000000 --- a/packages/material-ui-lab/src/CreatableAutocomplete/CreatableAutocomplete.js +++ /dev/null @@ -1,357 +0,0 @@ -/* eslint-disable react-hooks/exhaustive-deps */ -/* eslint-disable react/prop-types */ -/* eslint-disable no-console */ -/* eslint-disable-next-line react/prop-types */ - -import React from 'react'; -import PropTypes from 'prop-types'; -import { withStyles } from '@material-ui/core/styles'; -import Autocomplete from '@material-ui/lab/Autocomplete'; - -export const styles = () => ({}); - -const CreatableAutocomplete = (props) => { - const { - options: optionsProps, - inputValue, - createOptionPositionInLast = true, - onCreateNewOption, - getNewOptionLabel = (value) => `Create "${value}"`, - getNewOptionData = (value, optionLabel) => ({ - label: optionLabel, - value, - isNew: true, - }), - onChange, - ...otherProps - } = props; - const [_inputValue, setInputValue] = React.useState(inputValue); - const [options, setOptions] = React.useState(optionsProps); - - React.useEffect(() => { - const newOptionData = getNewOptionData(_inputValue, getNewOptionLabel(_inputValue)); - const newOptions = createOptionPositionInLast ? [...optionsProps, newOptionData] : [newOptionData, ...optionsProps]; - - setOptions(newOptions); - }, [optionsProps, _inputValue]); - - const handleChange = - (e, option) => { - console.log('if (option.isNew) {'); - console.log({props}); - if (option.isNew) { - console.log('if (onCreateNewOption) {'); - console.log({onCreateNewOption}); - if (onCreateNewOption) { - onCreateNewOption(e, option); - return; - } - console.log('if (onChange) {'); - console.log({onChange}); - if (onChange) { - onChange(e, option); - } - } - } - - return ( - setInputValue(value)} - /> - ); -} - -Autocomplete.propTypes = { - // ----------------------------- Warning -------------------------------- - // | These PropTypes are generated from the TypeScript type definitions | - // | To update them edit the d.ts file and run "yarn proptypes" | - // ---------------------------------------------------------------------- - /** - * If `true`, the portion of the selected suggestion that has not been typed by the user, - * known as the completion string, appears inline after the input cursor in the textbox. - * The inline completion string is visually highlighted and has a selected state. - */ - autoComplete: PropTypes.bool, - /** - * If `true`, the first option is automatically highlighted. - */ - autoHighlight: PropTypes.bool, - /** - * If `true`, the selected option becomes the value of the input - * when the Autocomplete loses focus unless the user chooses - * a different option or changes the character string in the input. - */ - autoSelect: PropTypes.bool, - /** - * Control if the input should be blurred when an option is selected: - * - * - `false` the input is not blurred. - * - `true` the input is always blurred. - * - `touch` the input is blurred after a touch event. - * - `mouse` the input is blurred after a mouse event. - */ - blurOnSelect: PropTypes.oneOfType([PropTypes.oneOf(['mouse', 'touch']), PropTypes.bool]), - /** - * Props applied to the [`Chip`](/api/chip/) element. - */ - ChipProps: PropTypes.object, - /** - * Override or extend the styles applied to the component. - * See [CSS API](#css) below for more details. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * If `true`, clear all values when the user presses escape and the popup is closed. - */ - clearOnEscape: PropTypes.bool, - /** - * Override the default text for the *clear* icon button. - * - * For localization purposes, you can use the provided [translations](/guides/localization/). - */ - clearText: PropTypes.string, - /** - * The icon to display in place of the default close icon. - */ - closeIcon: PropTypes.node, - /** - * Override the default text for the *close popup* icon button. - * - * For localization purposes, you can use the provided [translations](/guides/localization/). - */ - closeText: PropTypes.string, - /** - * If `true`, the popup will ignore the blur event if the input if filled. - * You can inspect the popup markup with your browser tools. - * Consider this option when you need to customize the component. - */ - debug: PropTypes.bool, - /** - * The default input value. Use when the component is not controlled. - */ - defaultValue: PropTypes.oneOfType([PropTypes.any, PropTypes.array]), - /** - * If `true`, the input can't be cleared. - */ - disableClearable: PropTypes.bool, - /** - * If `true`, the popup won't close when a value is selected. - */ - disableCloseOnSelect: PropTypes.bool, - /** - * If `true`, the input will be disabled. - */ - disabled: PropTypes.bool, - /** - * If `true`, the list box in the popup will not wrap focus. - */ - disableListWrap: PropTypes.bool, - /** - * If `true`, the popup won't open on input focus. - */ - disableOpenOnFocus: PropTypes.bool, - /** - * Disable the portal behavior. - * The children stay within it's parent DOM hierarchy. - */ - disablePortal: PropTypes.bool, - /** - * A filter function that determines the options that are eligible. - * - * @param {T[]} options The options to render. - * @param {object} state The state of the component. - * @returns {T[]} - */ - filterOptions: PropTypes.func, - /** - * If `true`, hide the selected options from the list box. - */ - filterSelectedOptions: PropTypes.bool, - /** - * Force the visibility display of the popup icon. - */ - forcePopupIcon: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.bool]), - /** - * If `true`, the Autocomplete is free solo, meaning that the user input is not bound to provided options. - */ - freeSolo: PropTypes.bool, - /** - * Used to determine the disabled state for a given option. - */ - getOptionDisabled: PropTypes.func, - /** - * 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). - */ - getOptionLabel: PropTypes.func, - /** - * Used to determine if an option is selected. - * Uses strict equality by default. - */ - getOptionSelected: PropTypes.func, - /** - * 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. - * - * @param {T} options The option to group. - * @returns {string} - */ - groupBy: PropTypes.func, - /** - * 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. - */ - id: PropTypes.string, - /** - * If `true`, the highlight can move to the input. - */ - includeInputInList: PropTypes.bool, - /** - * The input value. - */ - inputValue: PropTypes.string, - /** - * The component used to render the listbox. - */ - ListboxComponent: PropTypes.elementType, - /** - * Props applied to the Listbox element. - */ - ListboxProps: PropTypes.object, - /** - * If `true`, the component is in a loading state. - */ - loading: PropTypes.bool, - /** - * Text to display when in a loading state. - * - * For localization purposes, you can use the provided [translations](/guides/localization/). - */ - loadingText: PropTypes.node, - /** - * If `true`, `value` must be an array and the menu will support multiple selections. - */ - multiple: PropTypes.bool, - /** - * Text to display when there are no options. - * - * For localization purposes, you can use the provided [translations](/guides/localization/). - */ - noOptionsText: PropTypes.node, - /** - * Callback fired when the value changes. - * - * @param {object} event The event source of the callback. - * @param {T} value - */ - onChange: PropTypes.func, - /** - * Callback fired when the popup requests to be closed. - * Use in controlled mode (see open). - * - * @param {object} event The event source of the callback. - */ - onClose: PropTypes.func, - /** - * Callback fired when the input value changes. - * - * @param {object} event The event source of the callback. - * @param {string} value The new value of the text input. - * @param {string} reason Can be: `"input"` (user input), `"reset"` (programmatic change), `"clear"`. - */ - onInputChange: PropTypes.func, - /** - * Text to display when there are no options. - * - * For localization purposes, you can use the provided [translations](/guides/localization/). - */ - onNoOptionsSelected: PropTypes.elementType, - /** - * Callback fired when the popup requests to be opened. - * Use in controlled mode (see open). - * - * @param {object} event The event source of the callback. - */ - onOpen: PropTypes.func, - /** - * Control the popup` open state. - */ - open: PropTypes.bool, - /** - * Override the default text for the *open popup* icon button. - * - * For localization purposes, you can use the provided [translations](/guides/localization/). - */ - openText: PropTypes.string, - /** - * Array of options. - */ - options: PropTypes.array.isRequired, - /** - * The component used to render the body of the popup. - */ - PaperComponent: PropTypes.elementType, - /** - * The component used to position the popup. - */ - PopperComponent: PropTypes.elementType, - /** - * The icon to display in place of the default popup icon. - */ - popupIcon: PropTypes.node, - /** - * Render the group. - * - * @param {any} option The group to render. - * @returns {ReactNode} - */ - renderGroup: PropTypes.func, - /** - * Render the input. - * - * @param {object} params - * @returns {ReactNode} - */ - renderInput: PropTypes.func.isRequired, - /** - * Render the option, use `getOptionLabel` by default. - * - * @param {T} option The option to render. - * @param {object} state The state of the component. - * @returns {ReactNode} - */ - renderOption: PropTypes.func, - /** - * Render the selected value. - * - * @param {T[]} value The `value` provided to the component. - * @param {function} getTagProps A tag props getter. - * @returns {ReactNode} - */ - renderTags: PropTypes.func, - /** - * If `true`, the input's text will be selected on focus. - */ - selectOnFocus: PropTypes.bool, - /** - * The size of the autocomplete. - */ - size: PropTypes.oneOf(['medium', 'small']), - /** - * The value of the autocomplete. - * - * The value must have reference equality with the option in order to be selected. - * You can customize the equality behavior with the `getOptionSelected` prop. - */ - value: PropTypes.oneOfType([PropTypes.any, PropTypes.array]), -}; - -export default withStyles(styles, { name: 'MuiCreatableAutocomplete' })(CreatableAutocomplete); diff --git a/packages/material-ui-lab/src/CreatableAutocomplete/index.d.ts b/packages/material-ui-lab/src/CreatableAutocomplete/index.d.ts deleted file mode 100644 index 8bf1f571d4b4d4..00000000000000 --- a/packages/material-ui-lab/src/CreatableAutocomplete/index.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { default } from './CreatableAutocomplete'; - -export * from './CreatableAutocomplete'; diff --git a/packages/material-ui-lab/src/CreatableAutocomplete/index.js b/packages/material-ui-lab/src/CreatableAutocomplete/index.js deleted file mode 100644 index 773606f973961f..00000000000000 --- a/packages/material-ui-lab/src/CreatableAutocomplete/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './CreatableAutocomplete'; From fb9f4e99cfd187d07e8d50c172dd6ea41da9c404 Mon Sep 17 00:00:00 2001 From: Olivier Tassinari Date: Thu, 13 Feb 2020 12:53:28 +0100 Subject: [PATCH 5/7] fix issues reported --- docs/pages/api/autocomplete.md | 2 +- .../components/autocomplete/CustomizedHook.js | 43 ++-- .../autocomplete/CustomizedHook.tsx | 43 ++-- .../autocomplete/FreeSoloCreateOption.js | 20 +- .../autocomplete/FreeSoloCreateOption.tsx | 169 +++++++++++++ .../FreeSoloCreateOptionDialog.js | 39 ++- .../FreeSoloCreateOptionDialog.tsx | 234 ++++++++++++++++++ .../components/autocomplete/autocomplete.md | 5 +- .../src/Autocomplete/Autocomplete.js | 1 + .../src/useAutocomplete/useAutocomplete.d.ts | 1 + 10 files changed, 496 insertions(+), 61 deletions(-) create mode 100644 docs/src/pages/components/autocomplete/FreeSoloCreateOption.tsx create mode 100644 docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.tsx diff --git a/docs/pages/api/autocomplete.md b/docs/pages/api/autocomplete.md index 5084808b95ec06..1ffa03c5ef1e6a 100644 --- a/docs/pages/api/autocomplete.md +++ b/docs/pages/api/autocomplete.md @@ -73,7 +73,7 @@ You can learn more about the difference by [reading this guide](/guides/minimizi | renderInput * | func | | Render the input.

**Signature:**
`function(params: object) => ReactNode`
*params:* null | | renderOption | func | | Render the option, use `getOptionLabel` by default.

**Signature:**
`function(option: T, state: object) => ReactNode`
*option:* The option to render.
*state:* The state of the component. | | renderTags | func | | Render the selected value.

**Signature:**
`function(value: undefined, getTagProps: function) => ReactNode`
*value:* The `value` provided to the component.
*getTagProps:* A tag props getter. | -| selectOnFocus | bool | !props.freeSolo | If `true`, the input's text will be selected on focus. | +| selectOnFocus | bool | !props.freeSolo | If `true`, the input's text will be selected on focus. It helps the user clearning the selected value. | | size | 'medium'
| 'small'
| 'medium' | The size of the autocomplete. | | value | any
| array
| | The value of the autocomplete.
The value must have reference equality with the option in order to be selected. You can customize the equality behavior with the `getOptionSelected` prop. | diff --git a/docs/src/pages/components/autocomplete/CustomizedHook.js b/docs/src/pages/components/autocomplete/CustomizedHook.js index 219b9456a328dd..bd5dbaf1da4043 100644 --- a/docs/src/pages/components/autocomplete/CustomizedHook.js +++ b/docs/src/pages/components/autocomplete/CustomizedHook.js @@ -1,6 +1,7 @@ /* eslint-disable no-use-before-define */ import React from 'react'; import useAutocomplete from '@material-ui/lab/useAutocomplete'; +import NoSsr from '@material-ui/core/NoSsr'; import CheckIcon from '@material-ui/icons/Check'; import CloseIcon from '@material-ui/icons/Close'; import styled from 'styled-components'; @@ -146,28 +147,30 @@ export default function CustomizedHook() { }); return ( -
-
- - - {value.map((option, index) => ( - - ))} + +
+
+ + + {value.map((option, index) => ( + + ))} - - + + +
+ {groupedOptions.length > 0 ? ( + + {groupedOptions.map((option, index) => ( +
  • + {option.title} + +
  • + ))} +
    + ) : null}
    - {groupedOptions.length > 0 ? ( - - {groupedOptions.map((option, index) => ( -
  • - {option.title} - -
  • - ))} -
    - ) : null} -
    + ); } diff --git a/docs/src/pages/components/autocomplete/CustomizedHook.tsx b/docs/src/pages/components/autocomplete/CustomizedHook.tsx index c2d4db38691799..c19c9d8411dbae 100644 --- a/docs/src/pages/components/autocomplete/CustomizedHook.tsx +++ b/docs/src/pages/components/autocomplete/CustomizedHook.tsx @@ -1,6 +1,7 @@ /* eslint-disable no-use-before-define */ import React from 'react'; import useAutocomplete from '@material-ui/lab/useAutocomplete'; +import NoSsr from '@material-ui/core/NoSsr'; import CheckIcon from '@material-ui/icons/Check'; import CloseIcon from '@material-ui/icons/Close'; import styled from 'styled-components'; @@ -146,27 +147,29 @@ export default function CustomizedHook() { }); return ( -
    -
    - - - {value.map((option: FilmOptionType, index: number) => ( - - ))} - - + +
    +
    + + + {value.map((option: FilmOptionType, index: number) => ( + + ))} + + +
    + {groupedOptions.length > 0 ? ( + + {groupedOptions.map((option, index) => ( +
  • + {option.title} + +
  • + ))} +
    + ) : null}
    - {groupedOptions.length > 0 ? ( - - {groupedOptions.map((option, index) => ( -
  • - {option.title} - -
  • - ))} -
    - ) : null} -
    + ); } diff --git a/docs/src/pages/components/autocomplete/FreeSoloCreateOption.js b/docs/src/pages/components/autocomplete/FreeSoloCreateOption.js index 81f3c753d7ea45..3aa9a6b2bfa1c1 100644 --- a/docs/src/pages/components/autocomplete/FreeSoloCreateOption.js +++ b/docs/src/pages/components/autocomplete/FreeSoloCreateOption.js @@ -5,17 +5,18 @@ import Autocomplete, { createFilterOptions } from '@material-ui/lab/Autocomplete const filter = createFilterOptions(); -export default function ComboBox() { - const [value, setValue] = React.useState(''); +export default function FreeSoloCreateOption() { + const [value, setValue] = React.useState(null); return ( { - if (newValue && newValue.freeSolo) { + if (newValue && newValue.inputValue) { setValue({ title: newValue.inputValue, }); + return; } @@ -26,7 +27,6 @@ export default function ComboBox() { if (params.inputValue !== '') { filtered.push({ - freeSolo: true, inputValue: params.inputValue, title: `Add "${params.inputValue}"`, }); @@ -36,7 +36,17 @@ export default function ComboBox() { }} id="free-solo-with-text-demo" options={top100Films} - getOptionLabel={option => option.title} + getOptionLabel={option => { + // e.g value selected with enter, right from the input + if (typeof option === 'string') { + return option; + } + if (option.inputValue) { + return option.inputValue; + } + return option.title; + }} + renderOption={option => option.title} style={{ width: 300 }} freeSolo renderInput={params => ( diff --git a/docs/src/pages/components/autocomplete/FreeSoloCreateOption.tsx b/docs/src/pages/components/autocomplete/FreeSoloCreateOption.tsx new file mode 100644 index 00000000000000..0323feffd3559d --- /dev/null +++ b/docs/src/pages/components/autocomplete/FreeSoloCreateOption.tsx @@ -0,0 +1,169 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import TextField from '@material-ui/core/TextField'; +import Autocomplete, { createFilterOptions } from '@material-ui/lab/Autocomplete'; + +const filter = createFilterOptions(); + +export default function FreeSoloCreateOption() { + const [value, setValue] = React.useState(null); + + return ( + { + if (newValue && newValue.inputValue) { + setValue({ + title: newValue.inputValue, + }); + return; + } + + setValue(newValue); + }} + filterOptions={(options, params) => { + const filtered = filter(options, params) as FilmOptionType[]; + + if (params.inputValue !== '') { + filtered.push({ + inputValue: params.inputValue, + title: `Add "${params.inputValue}"`, + }); + } + + return filtered; + }} + id="free-solo-with-text-demo" + options={top100Films as FilmOptionType[]} + getOptionLabel={option => { + // e.g value selected with enter, right from the input + if (typeof option === 'string') { + return option; + } + if (option.inputValue) { + return option.inputValue; + } + return option.title; + }} + renderOption={option => option.title} + style={{ width: 300 }} + freeSolo + renderInput={params => ( + + )} + /> + ); +} + +interface FilmOptionType { + inputValue?: string; + title: string; + year?: number; +} + +// 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/FreeSoloCreateOptionDialog.js b/docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.js index b6801b3334ea29..bcc296f63537a9 100644 --- a/docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.js +++ b/docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.js @@ -11,52 +11,55 @@ import Autocomplete, { createFilterOptions } from '@material-ui/lab/Autocomplete const filter = createFilterOptions(); -export default function ComboBox() { - const [value, setValue] = React.useState(''); - +export default function FreeSoloCreateOptionDialog() { + const [value, setValue] = React.useState(null); const [open, toggleOpen] = React.useState(false); const handleClose = () => { setDialogValue({ title: '', - year: null, + year: '', }); + toggleOpen(false); }; const [dialogValue, setDialogValue] = React.useState({ title: '', - year: null, + year: '', }); const handleAdd = () => { - setValue(dialogValue); + setValue({ + title: dialogValue.title, + year: parseInt(dialogValue.title, 10), + }); + handleClose(); }; return ( - <> + { - if (newValue && newValue.freeSolo) { + if (newValue && newValue.inputValue) { toggleOpen(true); setDialogValue({ title: newValue.inputValue, - year: null, + year: '', }); + return; } setValue(newValue); }} filterOptions={(options, params) => { - console.log(params); const filtered = filter(options, params); if (params.inputValue !== '') { filtered.push({ - freeSolo: true, inputValue: params.inputValue, title: `Add "${params.inputValue}"`, }); @@ -66,7 +69,17 @@ export default function ComboBox() { }} id="free-solo-dialog-demo" options={top100Films} - getOptionLabel={option => option.title} + getOptionLabel={option => { + // e.g value selected with enter, right from the input + if (typeof option === 'string') { + return option; + } + if (option.inputValue) { + return option.inputValue; + } + return option.title; + }} + renderOption={option => option.title} style={{ width: 300 }} freeSolo renderInput={params => ( @@ -106,7 +119,7 @@ export default function ComboBox() { - + ); } diff --git a/docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.tsx b/docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.tsx new file mode 100644 index 00000000000000..2bdb29b3ee33b4 --- /dev/null +++ b/docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.tsx @@ -0,0 +1,234 @@ +/* eslint-disable no-use-before-define */ +import React from 'react'; +import TextField from '@material-ui/core/TextField'; +import Dialog from '@material-ui/core/Dialog'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogActions from '@material-ui/core/DialogActions'; +import Button from '@material-ui/core/Button'; +import Autocomplete, { createFilterOptions } from '@material-ui/lab/Autocomplete'; + +const filter = createFilterOptions(); + +export default function FreeSoloCreateOptionDialog() { + const [value, setValue] = React.useState(null); + const [open, toggleOpen] = React.useState(false); + + const handleClose = () => { + setDialogValue({ + title: '', + year: '', + }); + toggleOpen(false); + }; + + const [dialogValue, setDialogValue] = React.useState({ + title: '', + year: '', + }); + + const handleAdd = () => { + setValue({ + title: dialogValue.title, + year: parseInt(dialogValue.title, 10), + }); + handleClose(); + }; + + return ( + + { + if (newValue && newValue.inputValue) { + toggleOpen(true); + setDialogValue({ + title: newValue.inputValue, + year: '', + }); + return; + } + + setValue(newValue); + }} + filterOptions={(options, params) => { + const filtered = filter(options, params) as FilmOptionType[]; + + if (params.inputValue !== '') { + filtered.push({ + inputValue: params.inputValue, + title: `Add "${params.inputValue}"`, + }); + } + + return filtered; + }} + id="free-solo-dialog-demo" + options={top100Films} + getOptionLabel={option => { + // e.g value selected with enter, right from the input + if (typeof option === 'string') { + return option; + } + if (option.inputValue) { + return option.inputValue; + } + return option.title; + }} + renderOption={option => option.title} + style={{ width: 300 }} + freeSolo + renderInput={params => ( + + )} + /> + + Add a new film + + Did you miss any film in our list? Please, add it! + setDialogValue({ ...dialogValue, title: event.target.value })} + label="title" + type="text" + fullWidth + /> + setDialogValue({ ...dialogValue, year: event.target.value })} + label="year" + type="number" + fullWidth + /> + + + + + + + + ); +} + +interface FilmOptionType { + inputValue?: string; + title: string; + year?: number; +} + +// 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/autocomplete.md b/docs/src/pages/components/autocomplete/autocomplete.md index 08bcd4d7982308..094252e30e6d25 100644 --- a/docs/src/pages/components/autocomplete/autocomplete.md +++ b/docs/src/pages/components/autocomplete/autocomplete.md @@ -34,17 +34,18 @@ Choose one country between 248. Set `freeSolo` to true so the textbox can contain any arbitrary value. The prop is designed to cover the primary use case of a search box with suggestions, e.g. Google search. -However, if you intend to use it for a [combo box](#combo-box) like experience (an enhanced version of a select element) we recommend setting `selectOnFocus`. +However, if you intend to use it for a [combo box](#combo-box) like experience (an enhanced version of a select element) we recommend setting `selectOnFocus` (it helps the user clearning the selected value). {{"demo": "pages/components/autocomplete/FreeSolo.js"}} ### Free solo with explicit text Sometimes you want to make explicit to the user that he/she can add whatever value he/she wants. +The following demo adds a last option: `Add "YOUR SEARCH"` . {{"demo": "pages/components/autocomplete/FreeSoloCreateOption.js"}} -### Free solo to open a Dialog +### Free solo to open a dialog You could also create dialog when the user wants to add a new value. diff --git a/packages/material-ui-lab/src/Autocomplete/Autocomplete.js b/packages/material-ui-lab/src/Autocomplete/Autocomplete.js index 5e7cb5d9accdb9..ebfbd5226fffca 100644 --- a/packages/material-ui-lab/src/Autocomplete/Autocomplete.js +++ b/packages/material-ui-lab/src/Autocomplete/Autocomplete.js @@ -745,6 +745,7 @@ Autocomplete.propTypes = { renderTags: PropTypes.func, /** * If `true`, the input's text will be selected on focus. + * It helps the user clearning the selected value. */ selectOnFocus: PropTypes.bool, /** diff --git a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.d.ts b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.d.ts index 86de086256f888..d25398ed1ba4d2 100644 --- a/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.d.ts +++ b/packages/material-ui-lab/src/useAutocomplete/useAutocomplete.d.ts @@ -160,6 +160,7 @@ export interface UseAutocompleteCommonProps { options: T[]; /** * If `true`, the input's text will be selected on focus. + * It helps the user clearning the selected value. */ selectOnFocus?: boolean; } From 79741c08105973047b106340240279b07cf88bcf Mon Sep 17 00:00:00 2001 From: Olivier Tassinari Date: Thu, 13 Feb 2020 14:12:51 +0100 Subject: [PATCH 6/7] use a form element --- .../FreeSoloCreateOptionDialog.js | 81 +++++++++++-------- .../FreeSoloCreateOptionDialog.tsx | 81 +++++++++++-------- .../components/autocomplete/autocomplete.md | 8 +- 3 files changed, 101 insertions(+), 69 deletions(-) diff --git a/docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.js b/docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.js index bcc296f63537a9..e3dbd73285bfe4 100644 --- a/docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.js +++ b/docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.js @@ -29,7 +29,8 @@ export default function FreeSoloCreateOptionDialog() { year: '', }); - const handleAdd = () => { + const handleSubmit = event => { + event.preventDefault(); setValue({ title: dialogValue.title, year: parseInt(dialogValue.title, 10), @@ -43,6 +44,18 @@ export default function FreeSoloCreateOptionDialog() { { + if (typeof newValue === 'string') { + // timeout to avoid instant validation of the dialog's form. + setTimeout(() => { + toggleOpen(true); + setDialogValue({ + title: newValue, + year: '', + }); + }); + return; + } + if (newValue && newValue.inputValue) { toggleOpen(true); setDialogValue({ @@ -87,37 +100,41 @@ export default function FreeSoloCreateOptionDialog() { )} /> - Add a new film - - Did you miss any film in our list? Please, add it! - setDialogValue({ ...dialogValue, title: event.target.value })} - label="title" - type="text" - fullWidth - /> - setDialogValue({ ...dialogValue, year: event.target.value })} - label="year" - type="number" - fullWidth - /> - - - - - +
    + Add a new film + + + Did you miss any film in our list? Please, add it! + + setDialogValue({ ...dialogValue, title: event.target.value })} + label="title" + type="text" + fullWidth + /> + setDialogValue({ ...dialogValue, year: event.target.value })} + label="year" + type="number" + fullWidth + /> + + + + + +
    ); diff --git a/docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.tsx b/docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.tsx index 2bdb29b3ee33b4..9112a939adbf0a 100644 --- a/docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.tsx +++ b/docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.tsx @@ -28,7 +28,8 @@ export default function FreeSoloCreateOptionDialog() { year: '', }); - const handleAdd = () => { + const handleSubmit = (event: React.FormEvent) => { + event.preventDefault(); setValue({ title: dialogValue.title, year: parseInt(dialogValue.title, 10), @@ -41,6 +42,18 @@ export default function FreeSoloCreateOptionDialog() { { + if (typeof newValue === 'string') { + // timeout to avoid instant validation of the dialog's form. + setTimeout(() => { + toggleOpen(true); + setDialogValue({ + title: newValue, + year: '', + }); + }); + return; + } + if (newValue && newValue.inputValue) { toggleOpen(true); setDialogValue({ @@ -84,37 +97,41 @@ export default function FreeSoloCreateOptionDialog() { )} /> - Add a new film - - Did you miss any film in our list? Please, add it! - setDialogValue({ ...dialogValue, title: event.target.value })} - label="title" - type="text" - fullWidth - /> - setDialogValue({ ...dialogValue, year: event.target.value })} - label="year" - type="number" - fullWidth - /> - - - - - +
    + Add a new film + + + Did you miss any film in our list? Please, add it! + + setDialogValue({ ...dialogValue, title: event.target.value })} + label="title" + type="text" + fullWidth + /> + setDialogValue({ ...dialogValue, year: event.target.value })} + label="year" + type="number" + fullWidth + /> + + + + + +
    ); diff --git a/docs/src/pages/components/autocomplete/autocomplete.md b/docs/src/pages/components/autocomplete/autocomplete.md index 094252e30e6d25..05a790f5dfabf0 100644 --- a/docs/src/pages/components/autocomplete/autocomplete.md +++ b/docs/src/pages/components/autocomplete/autocomplete.md @@ -38,16 +38,14 @@ However, if you intend to use it for a [combo box](#combo-box) like experience ( {{"demo": "pages/components/autocomplete/FreeSolo.js"}} -### Free solo with explicit text +### Helper message Sometimes you want to make explicit to the user that he/she can add whatever value he/she wants. -The following demo adds a last option: `Add "YOUR SEARCH"` . +The following demo adds a last option: `Add "YOUR SEARCH"`. {{"demo": "pages/components/autocomplete/FreeSoloCreateOption.js"}} -### Free solo to open a dialog - -You could also create dialog when the user wants to add a new value. +You could also display a dialog when the user wants to add a new value. {{"demo": "pages/components/autocomplete/FreeSoloCreateOptionDialog.js"}} From 48cb975ed8c875b321c5b8fd533703ac7623bdcd Mon Sep 17 00:00:00 2001 From: "@itelofilho" Date: Thu, 13 Feb 2020 20:03:00 -0300 Subject: [PATCH 7/7] fix typescript dialog example --- .../components/autocomplete/FreeSoloCreateOptionDialog.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.tsx b/docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.tsx index 9112a939adbf0a..caa7b3f415097a 100644 --- a/docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.tsx +++ b/docs/src/pages/components/autocomplete/FreeSoloCreateOptionDialog.tsx @@ -32,7 +32,7 @@ export default function FreeSoloCreateOptionDialog() { event.preventDefault(); setValue({ title: dialogValue.title, - year: parseInt(dialogValue.title, 10), + year: parseInt(dialogValue.year, 10), }); handleClose(); };