diff --git a/docs/data/material/components/autocomplete/GitHubLabel.js b/docs/data/material/components/autocomplete/GitHubLabel.js index 23baefb03e2edc..41e56b35af5b43 100644 --- a/docs/data/material/components/autocomplete/GitHubLabel.js +++ b/docs/data/material/components/autocomplete/GitHubLabel.js @@ -19,13 +19,19 @@ const StyledAutocompletePopper = styled('div')(({ theme }) => ({ fontSize: 13, }, [`& .${autocompleteClasses.listbox}`]: { - backgroundColor: '#fff', padding: 0, + backgroundColor: '#fff', + ...theme.applyStyles('dark', { + backgroundColor: '#1c2128', + }), [`& .${autocompleteClasses.option}`]: { minHeight: 'auto', alignItems: 'flex-start', padding: 8, - borderBottom: `1px solid ${' #eaecef'}`, + borderBottom: '1px solid #eaecef', + ...theme.applyStyles('dark', { + borderBottom: '1px solid #30363d', + }), '&[aria-selected="true"]': { backgroundColor: 'transparent', }, @@ -33,13 +39,7 @@ const StyledAutocompletePopper = styled('div')(({ theme }) => ({ { backgroundColor: theme.palette.action.hover, }, - ...theme.applyStyles('dark', { - borderBottom: `1px solid ${'#30363d'}`, - }), }, - ...theme.applyStyles('dark', { - backgroundColor: '#1c2128', - }), }, [`&.${autocompleteClasses.popperDisablePortal}`]: { position: 'relative', @@ -58,7 +58,7 @@ PopperComponent.propTypes = { }; const StyledPopper = styled(Popper)(({ theme }) => ({ - border: `1px solid ${'#e1e4e8'}`, + border: '1px solid #e1e4e8', boxShadow: `0 8px 24px ${'rgba(149, 157, 165, 0.2)'}`, color: '#24292e', backgroundColor: '#fff', @@ -67,8 +67,8 @@ const StyledPopper = styled(Popper)(({ theme }) => ({ zIndex: theme.zIndex.modal, fontSize: 13, ...theme.applyStyles('dark', { - border: `1px solid ${'#30363d'}`, - boxShadow: `0 8px 24px ${'rgb(1, 4, 9)'}`, + border: '1px solid #30363d', + boxShadow: '0 8px 24px rgb(1, 4, 9)', color: '#c9d1d9', backgroundColor: '#1c2128', }), @@ -77,30 +77,30 @@ const StyledPopper = styled(Popper)(({ theme }) => ({ const StyledInput = styled(InputBase)(({ theme }) => ({ padding: 10, width: '100%', - borderBottom: `1px solid ${'#30363d'}`, + borderBottom: '1px solid #eaecef', + ...theme.applyStyles('dark', { + borderBottom: '1px solid #30363d', + }), '& input': { borderRadius: 4, - backgroundColor: '#fff', - border: `1px solid ${'#30363d'}`, padding: 8, transition: theme.transitions.create(['border-color', 'box-shadow']), fontSize: 14, + backgroundColor: '#fff', + border: '1px solid #30363d', + ...theme.applyStyles('dark', { + backgroundColor: '#0d1117', + border: '1px solid #eaecef', + }), '&:focus': { - boxShadow: `0px 0px 0px 3px ${'rgba(3, 102, 214, 0.3)'}`, + boxShadow: '0px 0px 0px 3px rgba(3, 102, 214, 0.3)', borderColor: '#0366d6', ...theme.applyStyles('dark', { - boxShadow: `0px 0px 0px 3px ${'rgb(12, 45, 107)'}`, + boxShadow: '0px 0px 0px 3px rgb(12, 45, 107)', borderColor: '#388bfd', }), }, - ...theme.applyStyles('dark', { - backgroundColor: '#0d1117', - border: `1px solid ${'#eaecef'}`, - }), }, - ...theme.applyStyles('dark', { - borderBottom: `1px solid ${'#eaecef'}`, - }), })); const Button = styled(ButtonBase)(({ theme }) => ({ @@ -108,8 +108,11 @@ const Button = styled(ButtonBase)(({ theme }) => ({ width: '100%', textAlign: 'left', paddingBottom: 8, - color: '#586069', fontWeight: 600, + color: '#586069', + ...theme.applyStyles('dark', { + color: '#8b949e', + }), '&:hover,&:focus': { color: '#0366d6', ...theme.applyStyles('dark', { @@ -123,9 +126,6 @@ const Button = styled(ButtonBase)(({ theme }) => ({ width: 16, height: 16, }, - ...theme.applyStyles('dark', { - color: '#8b949e', - }), })); export default function GitHubLabel() { @@ -182,11 +182,11 @@ export default function GitHubLabel() {
({ - borderBottom: `1px solid ${'#30363d'}`, + borderBottom: '1px solid #30363d', padding: '8px 10px', fontWeight: 600, ...t.applyStyles('light', { - borderBottom: `1px solid ${'#eaecef'}`, + borderBottom: '1px solid #eaecef', }), })} > diff --git a/docs/data/material/components/autocomplete/GitHubLabel.tsx b/docs/data/material/components/autocomplete/GitHubLabel.tsx index d454737b623a12..f95d7fe1d1d396 100644 --- a/docs/data/material/components/autocomplete/GitHubLabel.tsx +++ b/docs/data/material/components/autocomplete/GitHubLabel.tsx @@ -27,15 +27,19 @@ const StyledAutocompletePopper = styled('div')(({ theme }) => ({ fontSize: 13, }, [`& .${autocompleteClasses.listbox}`]: { - backgroundColor: '#fff', - padding: 0, + backgroundColor: '#fff', + ...theme.applyStyles('dark', { + backgroundColor: '#1c2128', + }), [`& .${autocompleteClasses.option}`]: { minHeight: 'auto', alignItems: 'flex-start', padding: 8, - borderBottom: `1px solid ${' #eaecef'}`, - + borderBottom: '1px solid #eaecef', + ...theme.applyStyles('dark', { + borderBottom: '1px solid #30363d', + }), '&[aria-selected="true"]': { backgroundColor: 'transparent', }, @@ -43,13 +47,7 @@ const StyledAutocompletePopper = styled('div')(({ theme }) => ({ { backgroundColor: theme.palette.action.hover, }, - ...theme.applyStyles('dark', { - borderBottom: `1px solid ${'#30363d'}`, - }), }, - ...theme.applyStyles('dark', { - backgroundColor: '#1c2128', - }), }, [`&.${autocompleteClasses.popperDisablePortal}`]: { position: 'relative', @@ -62,7 +60,7 @@ function PopperComponent(props: PopperComponentProps) { } const StyledPopper = styled(Popper)(({ theme }) => ({ - border: `1px solid ${'#e1e4e8'}`, + border: '1px solid #e1e4e8', boxShadow: `0 8px 24px ${'rgba(149, 157, 165, 0.2)'}`, color: '#24292e', backgroundColor: '#fff', @@ -71,8 +69,8 @@ const StyledPopper = styled(Popper)(({ theme }) => ({ zIndex: theme.zIndex.modal, fontSize: 13, ...theme.applyStyles('dark', { - border: `1px solid ${'#30363d'}`, - boxShadow: `0 8px 24px ${'rgb(1, 4, 9)'}`, + border: '1px solid #30363d', + boxShadow: '0 8px 24px rgb(1, 4, 9)', color: '#c9d1d9', backgroundColor: '#1c2128', }), @@ -81,30 +79,30 @@ const StyledPopper = styled(Popper)(({ theme }) => ({ const StyledInput = styled(InputBase)(({ theme }) => ({ padding: 10, width: '100%', - borderBottom: `1px solid ${'#30363d'}`, + borderBottom: '1px solid #eaecef', + ...theme.applyStyles('dark', { + borderBottom: '1px solid #30363d', + }), '& input': { borderRadius: 4, - backgroundColor: '#fff', - border: `1px solid ${'#30363d'}`, padding: 8, transition: theme.transitions.create(['border-color', 'box-shadow']), fontSize: 14, + backgroundColor: '#fff', + border: '1px solid #30363d', + ...theme.applyStyles('dark', { + backgroundColor: '#0d1117', + border: '1px solid #eaecef', + }), '&:focus': { - boxShadow: `0px 0px 0px 3px ${'rgba(3, 102, 214, 0.3)'}`, + boxShadow: '0px 0px 0px 3px rgba(3, 102, 214, 0.3)', borderColor: '#0366d6', ...theme.applyStyles('dark', { - boxShadow: `0px 0px 0px 3px ${'rgb(12, 45, 107)'}`, + boxShadow: '0px 0px 0px 3px rgb(12, 45, 107)', borderColor: '#388bfd', }), }, - ...theme.applyStyles('dark', { - backgroundColor: '#0d1117', - border: `1px solid ${'#eaecef'}`, - }), }, - ...theme.applyStyles('dark', { - borderBottom: `1px solid ${'#eaecef'}`, - }), })); const Button = styled(ButtonBase)(({ theme }) => ({ @@ -112,8 +110,11 @@ const Button = styled(ButtonBase)(({ theme }) => ({ width: '100%', textAlign: 'left', paddingBottom: 8, - color: '#586069', fontWeight: 600, + color: '#586069', + ...theme.applyStyles('dark', { + color: '#8b949e', + }), '&:hover,&:focus': { color: '#0366d6', ...theme.applyStyles('dark', { @@ -127,9 +128,6 @@ const Button = styled(ButtonBase)(({ theme }) => ({ width: 16, height: 16, }, - ...theme.applyStyles('dark', { - color: '#8b949e', - }), })); export default function GitHubLabel() { @@ -186,11 +184,11 @@ export default function GitHubLabel() {
({ - borderBottom: `1px solid ${'#30363d'}`, + borderBottom: '1px solid #30363d', padding: '8px 10px', fontWeight: 600, ...t.applyStyles('light', { - borderBottom: `1px solid ${'#eaecef'}`, + borderBottom: '1px solid #eaecef', }), })} > diff --git a/docs/data/material/components/autocomplete/GoogleMaps.js b/docs/data/material/components/autocomplete/GoogleMaps.js index 7dd26015ac2c89..944fe71bda7f93 100644 --- a/docs/data/material/components/autocomplete/GoogleMaps.js +++ b/docs/data/material/components/autocomplete/GoogleMaps.js @@ -1,12 +1,18 @@ import * as React from 'react'; +import PropTypes from 'prop-types'; import Box from '@mui/material/Box'; import TextField from '@mui/material/TextField'; import Autocomplete from '@mui/material/Autocomplete'; +import Paper from '@mui/material/Paper'; import LocationOnIcon from '@mui/icons-material/LocationOn'; import Grid2 from '@mui/material/Grid2'; import Typography from '@mui/material/Typography'; +import { useTheme } from '@mui/material/styles'; import parse from 'autosuggest-highlight/parse'; -import throttle from 'lodash/throttle'; +// For the sake of this demo, we have to use debounce to reduce Google Maps Places API quote use +// But prefer to use throttle in practice +// import throttle from 'lodash/throttle'; +import { debounce } from '@mui/material/utils'; // This key was created specifically for the demo in mui.com. // You need to create a new one for your application. @@ -23,31 +29,79 @@ function loadScript(src, position) { return script; } -const fetch = throttle(async (request, callback) => { - const { suggestions } = - await window.google.maps.places.AutocompleteSuggestion.fetchAutocompleteSuggestions( - request, - ); +function CustomPaper(props) { + const theme = useTheme(); - callback( - suggestions.map((suggestion) => { - const place = suggestion.placePrediction; - // Map to the old AutocompleteService.getPlacePredictions format - // https://developers.google.com/maps/documentation/javascript/places-migration-autocomplete - return { - description: place.text.text, - structured_formatting: { - main_text: place.mainText.text, - main_text_matched_substrings: place.mainText.matches.map((match) => ({ - offset: match.startOffset, - length: match.endOffset - match.startOffset, - })), - secondary_text: place.secondaryText?.text, - }, - }; - }), + return ( + + {props.children} + {/* Legal requirment https://developers.google.com/maps/documentation/javascript/policies#logo */} + ({ + display: 'flex', + justifyContent: 'flex-end', + p: 1, + pt: '1px', + ...staticTheme.applyStyles('dark', { + opacity: 0.8, + }), + })} + > + + + ); -}, 300); +} + +CustomPaper.propTypes = { + /** + * The content of the component. + */ + children: PropTypes.node, +}; + +const fetch = debounce(async (request, callback) => { + try { + const { suggestions } = + await window.google.maps.places.AutocompleteSuggestion.fetchAutocompleteSuggestions( + request, + ); + + callback( + suggestions.map((suggestion) => { + const place = suggestion.placePrediction; + // Map to the old AutocompleteService.getPlacePredictions format + // https://developers.google.com/maps/documentation/javascript/places-migration-autocomplete + return { + description: place.text.text, + structured_formatting: { + main_text: place.mainText.text, + main_text_matched_substrings: place.mainText.matches.map((match) => ({ + offset: match.startOffset, + length: match.endOffset - match.startOffset, + })), + secondary_text: place.secondaryText?.text, + }, + }; + }), + ); + } catch (err) { + if (err.message === 'Quota exceeded for quota') { + callback(request.input.length === 1 ? fakeAnswer.p : fakeAnswer.paris); + } + + throw err; + } +}, 400); const emptyOptions = []; let sessionToken; @@ -129,6 +183,9 @@ export default function GoogleMaps() { typeof option === 'string' ? option : option.description } filterOptions={(x) => x} + slots={{ + paper: CustomPaper, + }} options={options} autoComplete includeInputInList @@ -186,3 +243,87 @@ export default function GoogleMaps() { /> ); } + +// Fake data in case Google Map Places API returns a rate limit. +const fakeAnswer = { + p: [ + { + description: 'Portugal', + structured_formatting: { + main_text: 'Portugal', + main_text_matched_substrings: [{ offset: 0, length: 1 }], + }, + }, + { + description: 'Puerto Rico', + structured_formatting: { + main_text: 'Puerto Rico', + main_text_matched_substrings: [{ offset: 0, length: 1 }], + }, + }, + { + description: 'Pakistan', + structured_formatting: { + main_text: 'Pakistan', + main_text_matched_substrings: [{ offset: 0, length: 1 }], + }, + }, + { + description: 'Philippines', + structured_formatting: { + main_text: 'Philippines', + main_text_matched_substrings: [{ offset: 0, length: 1 }], + }, + }, + { + description: 'Paris, France', + structured_formatting: { + main_text: 'Paris', + main_text_matched_substrings: [{ offset: 0, length: 1 }], + secondary_text: 'France', + }, + }, + ], + paris: [ + { + description: 'Paris, France', + structured_formatting: { + main_text: 'Paris', + main_text_matched_substrings: [{ offset: 0, length: 5 }], + secondary_text: 'France', + }, + }, + { + description: 'Paris, TX, USA', + structured_formatting: { + main_text: 'Paris', + main_text_matched_substrings: [{ offset: 0, length: 5 }], + secondary_text: 'TX, USA', + }, + }, + { + description: "Paris Beauvais Airport, Route de l'Aéroport, Tillé, France", + structured_formatting: { + main_text: 'Paris Beauvais Airport', + main_text_matched_substrings: [{ offset: 0, length: 5 }], + secondary_text: "Route de l'Aéroport, Tillé, France", + }, + }, + { + description: 'Paris Las Vegas, South Las Vegas Boulevard, Las Vegas, NV, USA', + structured_formatting: { + main_text: 'Paris Las Vegas', + main_text_matched_substrings: [{ offset: 0, length: 5 }], + secondary_text: 'South Las Vegas Boulevard, Las Vegas, NV, USA', + }, + }, + { + description: "Paris La Défense Arena, Jardin de l'Arche, Nanterre, France", + structured_formatting: { + main_text: 'Paris La Défense Arena', + main_text_matched_substrings: [{ offset: 0, length: 5 }], + secondary_text: "Jardin de l'Arche, Nanterre, France", + }, + }, + ], +}; diff --git a/docs/data/material/components/autocomplete/GoogleMaps.tsx b/docs/data/material/components/autocomplete/GoogleMaps.tsx index 2d94e09bff7b0c..f0f5bd0d2e2829 100644 --- a/docs/data/material/components/autocomplete/GoogleMaps.tsx +++ b/docs/data/material/components/autocomplete/GoogleMaps.tsx @@ -2,11 +2,16 @@ import * as React from 'react'; import Box from '@mui/material/Box'; import TextField from '@mui/material/TextField'; import Autocomplete from '@mui/material/Autocomplete'; +import Paper, { PaperProps } from '@mui/material/Paper'; import LocationOnIcon from '@mui/icons-material/LocationOn'; import Grid2 from '@mui/material/Grid2'; import Typography from '@mui/material/Typography'; +import { useTheme } from '@mui/material/styles'; import parse from 'autosuggest-highlight/parse'; -import throttle from 'lodash/throttle'; +// For the sake of this demo, we have to use debounce to reduce Google Maps Places API quote use +// But prefer to use throttle in practice +// import throttle from 'lodash/throttle'; +import { debounce } from '@mui/material/utils'; // This key was created specifically for the demo in mui.com. // You need to create a new one for your application. @@ -37,39 +42,80 @@ interface PlaceType { structured_formatting: StructuredFormatting; } -const fetch = throttle( +function CustomPaper(props: PaperProps) { + const theme = useTheme(); + + return ( + + {props.children} + {/* Legal requirment https://developers.google.com/maps/documentation/javascript/policies#logo */} + ({ + display: 'flex', + justifyContent: 'flex-end', + p: 1, + pt: '1px', + ...staticTheme.applyStyles('dark', { + opacity: 0.8, + }), + })} + > + + + + ); +} + +const fetch = debounce( async ( request: { input: string; sessionToken: any }, callback: (results?: readonly PlaceType[]) => void, ) => { - const { suggestions } = await ( - window as any - ).google.maps.places.AutocompleteSuggestion.fetchAutocompleteSuggestions( - request, - ); - - callback( - suggestions.map((suggestion: any) => { - const place = suggestion.placePrediction; - // Map to the old AutocompleteService.getPlacePredictions format - // https://developers.google.com/maps/documentation/javascript/places-migration-autocomplete - return { - description: place.text.text, - structured_formatting: { - main_text: place.mainText.text, - main_text_matched_substrings: place.mainText.matches.map( - (match: any) => ({ - offset: match.startOffset, - length: match.endOffset - match.startOffset, - }), - ), - secondary_text: place.secondaryText?.text, - }, - }; - }), - ); + try { + const { suggestions } = await ( + window as any + ).google.maps.places.AutocompleteSuggestion.fetchAutocompleteSuggestions( + request, + ); + + callback( + suggestions.map((suggestion: any) => { + const place = suggestion.placePrediction; + // Map to the old AutocompleteService.getPlacePredictions format + // https://developers.google.com/maps/documentation/javascript/places-migration-autocomplete + return { + description: place.text.text, + structured_formatting: { + main_text: place.mainText.text, + main_text_matched_substrings: place.mainText.matches.map( + (match: any) => ({ + offset: match.startOffset, + length: match.endOffset - match.startOffset, + }), + ), + secondary_text: place.secondaryText?.text, + }, + }; + }), + ); + } catch (err: any) { + if (err.message === 'Quota exceeded for quota') { + callback(request.input.length === 1 ? fakeAnswer.p : fakeAnswer.paris); + } + + throw err; + } }, - 300, + 400, ); const emptyOptions = [] as any; @@ -155,6 +201,9 @@ export default function GoogleMaps() { typeof option === 'string' ? option : option.description } filterOptions={(x) => x} + slots={{ + paper: CustomPaper, + }} options={options} autoComplete includeInputInList @@ -212,3 +261,87 @@ export default function GoogleMaps() { /> ); } + +// Fake data in case Google Map Places API returns a rate limit. +const fakeAnswer = { + p: [ + { + description: 'Portugal', + structured_formatting: { + main_text: 'Portugal', + main_text_matched_substrings: [{ offset: 0, length: 1 }], + }, + }, + { + description: 'Puerto Rico', + structured_formatting: { + main_text: 'Puerto Rico', + main_text_matched_substrings: [{ offset: 0, length: 1 }], + }, + }, + { + description: 'Pakistan', + structured_formatting: { + main_text: 'Pakistan', + main_text_matched_substrings: [{ offset: 0, length: 1 }], + }, + }, + { + description: 'Philippines', + structured_formatting: { + main_text: 'Philippines', + main_text_matched_substrings: [{ offset: 0, length: 1 }], + }, + }, + { + description: 'Paris, France', + structured_formatting: { + main_text: 'Paris', + main_text_matched_substrings: [{ offset: 0, length: 1 }], + secondary_text: 'France', + }, + }, + ], + paris: [ + { + description: 'Paris, France', + structured_formatting: { + main_text: 'Paris', + main_text_matched_substrings: [{ offset: 0, length: 5 }], + secondary_text: 'France', + }, + }, + { + description: 'Paris, TX, USA', + structured_formatting: { + main_text: 'Paris', + main_text_matched_substrings: [{ offset: 0, length: 5 }], + secondary_text: 'TX, USA', + }, + }, + { + description: "Paris Beauvais Airport, Route de l'Aéroport, Tillé, France", + structured_formatting: { + main_text: 'Paris Beauvais Airport', + main_text_matched_substrings: [{ offset: 0, length: 5 }], + secondary_text: "Route de l'Aéroport, Tillé, France", + }, + }, + { + description: 'Paris Las Vegas, South Las Vegas Boulevard, Las Vegas, NV, USA', + structured_formatting: { + main_text: 'Paris Las Vegas', + main_text_matched_substrings: [{ offset: 0, length: 5 }], + secondary_text: 'South Las Vegas Boulevard, Las Vegas, NV, USA', + }, + }, + { + description: "Paris La Défense Arena, Jardin de l'Arche, Nanterre, France", + structured_formatting: { + main_text: 'Paris La Défense Arena', + main_text_matched_substrings: [{ offset: 0, length: 5 }], + secondary_text: "Jardin de l'Arche, Nanterre, France", + }, + }, + ], +}; diff --git a/docs/data/material/components/autocomplete/autocomplete.md b/docs/data/material/components/autocomplete/autocomplete.md index 09ec3ffbf059bc..553ba3c910d09d 100644 --- a/docs/data/material/components/autocomplete/autocomplete.md +++ b/docs/data/material/components/autocomplete/autocomplete.md @@ -221,6 +221,8 @@ The demo relies on [autosuggest-highlight](https://github.com/moroshko/autosugge :::error Before you can start using the Google Maps JavaScript API and Places API, you need to get your own [API key](https://developers.google.com/maps/documentation/javascript/get-api-key). + +This demo has limited quotas to make API requests. When your quota exceeds, you will see the response for "Paris". ::: ## Multiple values