diff --git a/docs/data/joy/getting-started/templates/TemplateCollection.js b/docs/data/joy/getting-started/templates/TemplateCollection.js index 0063c48ab182d9..cb303e7839aa39 100644 --- a/docs/data/joy/getting-started/templates/TemplateCollection.js +++ b/docs/data/joy/getting-started/templates/TemplateCollection.js @@ -59,6 +59,10 @@ const AUTHORS = { name: 'MUI', link: 'https://twitter.com/MUI_hq', }, + 'rental-dashboard': { + name: 'Steve Ernstberger', + link: 'https://twitter.com/steveeberger', + }, }; const DESIGNS = { 'order-dashboard': { @@ -73,13 +77,17 @@ const DESIGNS = { name: 'Frames X', link: 'https://framesxfigma.buninux.com/', }, + 'rental-dashboard': { + name: 'Untitled UI', + link: 'https://www.figma.com/community/file/1020079203222518115/%E2%9D%96-Untitled-UI-%E2%80%93-FREE-Figma-UI-kit-and-design-system', + }, }; export default function TemplateCollection() { const newTemplates = [ + 'rental-dashboard', 'framesx-web-blocks', 'profile-dashboard', - 'order-dashboard', ]; // Stay at the top of the page with `new` badge const { names: templateNames, map: templateMap } = sourceJoyTemplates(); const names = [ @@ -256,7 +264,7 @@ export default function TemplateCollection() { - + {author && ( Built by{' '} diff --git a/docs/data/joy/getting-started/templates/rental-dashboard/App.tsx b/docs/data/joy/getting-started/templates/rental-dashboard/App.tsx new file mode 100644 index 00000000000000..e1350e85e01e61 --- /dev/null +++ b/docs/data/joy/getting-started/templates/rental-dashboard/App.tsx @@ -0,0 +1,147 @@ +import * as React from 'react'; +import { CssVarsProvider } from '@mui/joy/styles'; +import GlobalStyles from '@mui/joy/GlobalStyles'; +import CssBaseline from '@mui/joy/CssBaseline'; +import Box from '@mui/joy/Box'; +import Divider from '@mui/joy/Divider'; +import Grid from '@mui/joy/Grid'; +import Stack from '@mui/joy/Stack'; +import useScript from './useScript'; +import FirstSidebar from './components/FirstSidebar'; +import Header from './components/Header'; +import customTheme from './theme'; +import RentalCard from './components/RentalCard'; +import Main from './components/Main'; +import HeaderSection from './components/HeaderSection'; +import Search from './components/Search'; +import Filters from './components/Filters'; +import Toggles from './components/Toggles'; +import Pagination from './components/Pagination'; + +const useEnhancedEffect = + typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect; + +export default function RentalDashboard() { + const status = useScript(`https://unpkg.com/feather-icons`); + + useEnhancedEffect(() => { + // Feather icon setup: https://github.com/feathericons/feather#4-replace + // @ts-ignore + if (typeof feather !== 'undefined') { + // @ts-ignore + feather.replace(); + } + }, [status]); + + return ( + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + ); +} diff --git a/docs/data/joy/getting-started/templates/rental-dashboard/components/ColorSchemeToggle.tsx b/docs/data/joy/getting-started/templates/rental-dashboard/components/ColorSchemeToggle.tsx new file mode 100644 index 00000000000000..7804bd030b07e5 --- /dev/null +++ b/docs/data/joy/getting-started/templates/rental-dashboard/components/ColorSchemeToggle.tsx @@ -0,0 +1,48 @@ +import * as React from 'react'; +import { useColorScheme } from '@mui/joy/styles'; +import { ListItem, ListItemButton, ListItemButtonProps } from '@mui/joy'; + +export default function ColorSchemeToggle({ + onClick, + sx, + ...props +}: ListItemButtonProps) { + const { mode, setMode } = useColorScheme(); + const [mounted, setMounted] = React.useState(false); + React.useEffect(() => { + setMounted(true); + }, []); + if (!mounted) { + return ; + } + + return ( + + ) => { + if (mode === 'light') { + setMode('dark'); + } else { + setMode('light'); + } + onClick?.(event); + }} + sx={[ + { + '& > *:first-child': { + display: mode === 'dark' ? 'none' : 'initial', + }, + '& > *:last-child': { + display: mode === 'light' ? 'none' : 'initial', + }, + }, + ...(Array.isArray(sx) ? sx : [sx]), + ]} + > + + + + + ); +} diff --git a/docs/data/joy/getting-started/templates/rental-dashboard/components/CountrySelector.tsx b/docs/data/joy/getting-started/templates/rental-dashboard/components/CountrySelector.tsx new file mode 100644 index 00000000000000..76ae8b5083151e --- /dev/null +++ b/docs/data/joy/getting-started/templates/rental-dashboard/components/CountrySelector.tsx @@ -0,0 +1,481 @@ +import * as React from 'react'; +import Autocomplete from '@mui/joy/Autocomplete'; +import AutocompleteOption from '@mui/joy/AutocompleteOption'; +import AspectRatio from '@mui/joy/AspectRatio'; +import FormControl, { FormControlProps } from '@mui/joy/FormControl'; +import ListItemDecorator from '@mui/joy/ListItemDecorator'; +import Typography from '@mui/joy/Typography'; + +export default function ContrySelector({ sx, ...props }: FormControlProps) { + return ( + + option.code === value.code} + defaultValue={{ code: 'US', label: 'United States', phone: '1' }} + options={countries} + renderOption={(optionProps, option) => ( + + + + + + + {option.label} + + (+{option.phone}) + + + )} + slotProps={{ + input: { + autoComplete: 'new-password', // disable autocomplete and autofill + }, + }} + /> + + ); +} + +interface CountryType { + code: string; + label: string; + phone: string; + suggested?: boolean; +} + +// From https://bitbucket.org/atlassian/atlaskit-mk-2/raw/4ad0e56649c3e6c973e226b7efaeb28cb240ccb0/packages/core/select/src/data/countries.js +const countries: readonly CountryType[] = [ + { code: 'AD', label: 'Andorra', phone: '376' }, + { + code: 'AE', + label: 'United Arab Emirates', + phone: '971', + }, + { code: 'AF', label: 'Afghanistan', phone: '93' }, + { + code: 'AG', + label: 'Antigua and Barbuda', + phone: '1-268', + }, + { code: 'AI', label: 'Anguilla', phone: '1-264' }, + { code: 'AL', label: 'Albania', phone: '355' }, + { code: 'AM', label: 'Armenia', phone: '374' }, + { code: 'AO', label: 'Angola', phone: '244' }, + { code: 'AQ', label: 'Antarctica', phone: '672' }, + { code: 'AR', label: 'Argentina', phone: '54' }, + { code: 'AS', label: 'American Samoa', phone: '1-684' }, + { code: 'AT', label: 'Austria', phone: '43' }, + { + code: 'AU', + label: 'Australia', + phone: '61', + suggested: true, + }, + { code: 'AW', label: 'Aruba', phone: '297' }, + { code: 'AX', label: 'Alland Islands', phone: '358' }, + { code: 'AZ', label: 'Azerbaijan', phone: '994' }, + { + code: 'BA', + label: 'Bosnia and Herzegovina', + phone: '387', + }, + { code: 'BB', label: 'Barbados', phone: '1-246' }, + { code: 'BD', label: 'Bangladesh', phone: '880' }, + { code: 'BE', label: 'Belgium', phone: '32' }, + { code: 'BF', label: 'Burkina Faso', phone: '226' }, + { code: 'BG', label: 'Bulgaria', phone: '359' }, + { code: 'BH', label: 'Bahrain', phone: '973' }, + { code: 'BI', label: 'Burundi', phone: '257' }, + { code: 'BJ', label: 'Benin', phone: '229' }, + { code: 'BL', label: 'Saint Barthelemy', phone: '590' }, + { code: 'BM', label: 'Bermuda', phone: '1-441' }, + { code: 'BN', label: 'Brunei Darussalam', phone: '673' }, + { code: 'BO', label: 'Bolivia', phone: '591' }, + { code: 'BR', label: 'Brazil', phone: '55' }, + { code: 'BS', label: 'Bahamas', phone: '1-242' }, + { code: 'BT', label: 'Bhutan', phone: '975' }, + { code: 'BV', label: 'Bouvet Island', phone: '47' }, + { code: 'BW', label: 'Botswana', phone: '267' }, + { code: 'BY', label: 'Belarus', phone: '375' }, + { code: 'BZ', label: 'Belize', phone: '501' }, + { + code: 'CA', + label: 'Canada', + phone: '1', + suggested: true, + }, + { + code: 'CC', + label: 'Cocos (Keeling) Islands', + phone: '61', + }, + { + code: 'CD', + label: 'Congo, Democratic Republic of the', + phone: '243', + }, + { + code: 'CF', + label: 'Central African Republic', + phone: '236', + }, + { + code: 'CG', + label: 'Congo, Republic of the', + phone: '242', + }, + { code: 'CH', label: 'Switzerland', phone: '41' }, + { code: 'CI', label: "Cote d'Ivoire", phone: '225' }, + { code: 'CK', label: 'Cook Islands', phone: '682' }, + { code: 'CL', label: 'Chile', phone: '56' }, + { code: 'CM', label: 'Cameroon', phone: '237' }, + { code: 'CN', label: 'China', phone: '86' }, + { code: 'CO', label: 'Colombia', phone: '57' }, + { code: 'CR', label: 'Costa Rica', phone: '506' }, + { code: 'CU', label: 'Cuba', phone: '53' }, + { code: 'CV', label: 'Cape Verde', phone: '238' }, + { code: 'CW', label: 'Curacao', phone: '599' }, + { code: 'CX', label: 'Christmas Island', phone: '61' }, + { code: 'CY', label: 'Cyprus', phone: '357' }, + { code: 'CZ', label: 'Czech Republic', phone: '420' }, + { + code: 'DE', + label: 'Germany', + phone: '49', + suggested: true, + }, + { code: 'DJ', label: 'Djibouti', phone: '253' }, + { code: 'DK', label: 'Denmark', phone: '45' }, + { code: 'DM', label: 'Dominica', phone: '1-767' }, + { + code: 'DO', + label: 'Dominican Republic', + phone: '1-809', + }, + { code: 'DZ', label: 'Algeria', phone: '213' }, + { code: 'EC', label: 'Ecuador', phone: '593' }, + { code: 'EE', label: 'Estonia', phone: '372' }, + { code: 'EG', label: 'Egypt', phone: '20' }, + { code: 'EH', label: 'Western Sahara', phone: '212' }, + { code: 'ER', label: 'Eritrea', phone: '291' }, + { code: 'ES', label: 'Spain', phone: '34' }, + { code: 'ET', label: 'Ethiopia', phone: '251' }, + { code: 'FI', label: 'Finland', phone: '358' }, + { code: 'FJ', label: 'Fiji', phone: '679' }, + { + code: 'FK', + label: 'Falkland Islands (Malvinas)', + phone: '500', + }, + { + code: 'FM', + label: 'Micronesia, Federated States of', + phone: '691', + }, + { code: 'FO', label: 'Faroe Islands', phone: '298' }, + { + code: 'FR', + label: 'France', + phone: '33', + suggested: true, + }, + { code: 'GA', label: 'Gabon', phone: '241' }, + { code: 'GB', label: 'United Kingdom', phone: '44' }, + { code: 'GD', label: 'Grenada', phone: '1-473' }, + { code: 'GE', label: 'Georgia', phone: '995' }, + { code: 'GF', label: 'French Guiana', phone: '594' }, + { code: 'GG', label: 'Guernsey', phone: '44' }, + { code: 'GH', label: 'Ghana', phone: '233' }, + { code: 'GI', label: 'Gibraltar', phone: '350' }, + { code: 'GL', label: 'Greenland', phone: '299' }, + { code: 'GM', label: 'Gambia', phone: '220' }, + { code: 'GN', label: 'Guinea', phone: '224' }, + { code: 'GP', label: 'Guadeloupe', phone: '590' }, + { code: 'GQ', label: 'Equatorial Guinea', phone: '240' }, + { code: 'GR', label: 'Greece', phone: '30' }, + { + code: 'GS', + label: 'South Georgia and the South Sandwich Islands', + phone: '500', + }, + { code: 'GT', label: 'Guatemala', phone: '502' }, + { code: 'GU', label: 'Guam', phone: '1-671' }, + { code: 'GW', label: 'Guinea-Bissau', phone: '245' }, + { code: 'GY', label: 'Guyana', phone: '592' }, + { code: 'HK', label: 'Hong Kong', phone: '852' }, + { + code: 'HM', + label: 'Heard Island and McDonald Islands', + phone: '672', + }, + { code: 'HN', label: 'Honduras', phone: '504' }, + { code: 'HR', label: 'Croatia', phone: '385' }, + { code: 'HT', label: 'Haiti', phone: '509' }, + { code: 'HU', label: 'Hungary', phone: '36' }, + { code: 'ID', label: 'Indonesia', phone: '62' }, + { code: 'IE', label: 'Ireland', phone: '353' }, + { code: 'IL', label: 'Israel', phone: '972' }, + { code: 'IM', label: 'Isle of Man', phone: '44' }, + { code: 'IN', label: 'India', phone: '91' }, + { + code: 'IO', + label: 'British Indian Ocean Territory', + phone: '246', + }, + { code: 'IQ', label: 'Iraq', phone: '964' }, + { + code: 'IR', + label: 'Iran, Islamic Republic of', + phone: '98', + }, + { code: 'IS', label: 'Iceland', phone: '354' }, + { code: 'IT', label: 'Italy', phone: '39' }, + { code: 'JE', label: 'Jersey', phone: '44' }, + { code: 'JM', label: 'Jamaica', phone: '1-876' }, + { code: 'JO', label: 'Jordan', phone: '962' }, + { + code: 'JP', + label: 'Japan', + phone: '81', + suggested: true, + }, + { code: 'KE', label: 'Kenya', phone: '254' }, + { code: 'KG', label: 'Kyrgyzstan', phone: '996' }, + { code: 'KH', label: 'Cambodia', phone: '855' }, + { code: 'KI', label: 'Kiribati', phone: '686' }, + { code: 'KM', label: 'Comoros', phone: '269' }, + { + code: 'KN', + label: 'Saint Kitts and Nevis', + phone: '1-869', + }, + { + code: 'KP', + label: "Korea, Democratic People's Republic of", + phone: '850', + }, + { code: 'KR', label: 'Korea, Republic of', phone: '82' }, + { code: 'KW', label: 'Kuwait', phone: '965' }, + { code: 'KY', label: 'Cayman Islands', phone: '1-345' }, + { code: 'KZ', label: 'Kazakhstan', phone: '7' }, + { + code: 'LA', + label: "Lao People's Democratic Republic", + phone: '856', + }, + { code: 'LB', label: 'Lebanon', phone: '961' }, + { code: 'LC', label: 'Saint Lucia', phone: '1-758' }, + { code: 'LI', label: 'Liechtenstein', phone: '423' }, + { code: 'LK', label: 'Sri Lanka', phone: '94' }, + { code: 'LR', label: 'Liberia', phone: '231' }, + { code: 'LS', label: 'Lesotho', phone: '266' }, + { code: 'LT', label: 'Lithuania', phone: '370' }, + { code: 'LU', label: 'Luxembourg', phone: '352' }, + { code: 'LV', label: 'Latvia', phone: '371' }, + { code: 'LY', label: 'Libya', phone: '218' }, + { code: 'MA', label: 'Morocco', phone: '212' }, + { code: 'MC', label: 'Monaco', phone: '377' }, + { + code: 'MD', + label: 'Moldova, Republic of', + phone: '373', + }, + { code: 'ME', label: 'Montenegro', phone: '382' }, + { + code: 'MF', + label: 'Saint Martin (French part)', + phone: '590', + }, + { code: 'MG', label: 'Madagascar', phone: '261' }, + { code: 'MH', label: 'Marshall Islands', phone: '692' }, + { + code: 'MK', + label: 'Macedonia, the Former Yugoslav Republic of', + phone: '389', + }, + { code: 'ML', label: 'Mali', phone: '223' }, + { code: 'MM', label: 'Myanmar', phone: '95' }, + { code: 'MN', label: 'Mongolia', phone: '976' }, + { code: 'MO', label: 'Macao', phone: '853' }, + { + code: 'MP', + label: 'Northern Mariana Islands', + phone: '1-670', + }, + { code: 'MQ', label: 'Martinique', phone: '596' }, + { code: 'MR', label: 'Mauritania', phone: '222' }, + { code: 'MS', label: 'Montserrat', phone: '1-664' }, + { code: 'MT', label: 'Malta', phone: '356' }, + { code: 'MU', label: 'Mauritius', phone: '230' }, + { code: 'MV', label: 'Maldives', phone: '960' }, + { code: 'MW', label: 'Malawi', phone: '265' }, + { code: 'MX', label: 'Mexico', phone: '52' }, + { code: 'MY', label: 'Malaysia', phone: '60' }, + { code: 'MZ', label: 'Mozambique', phone: '258' }, + { code: 'NA', label: 'Namibia', phone: '264' }, + { code: 'NC', label: 'New Caledonia', phone: '687' }, + { code: 'NE', label: 'Niger', phone: '227' }, + { code: 'NF', label: 'Norfolk Island', phone: '672' }, + { code: 'NG', label: 'Nigeria', phone: '234' }, + { code: 'NI', label: 'Nicaragua', phone: '505' }, + { code: 'NL', label: 'Netherlands', phone: '31' }, + { code: 'NO', label: 'Norway', phone: '47' }, + { code: 'NP', label: 'Nepal', phone: '977' }, + { code: 'NR', label: 'Nauru', phone: '674' }, + { code: 'NU', label: 'Niue', phone: '683' }, + { code: 'NZ', label: 'New Zealand', phone: '64' }, + { code: 'OM', label: 'Oman', phone: '968' }, + { code: 'PA', label: 'Panama', phone: '507' }, + { code: 'PE', label: 'Peru', phone: '51' }, + { code: 'PF', label: 'French Polynesia', phone: '689' }, + { code: 'PG', label: 'Papua New Guinea', phone: '675' }, + { code: 'PH', label: 'Philippines', phone: '63' }, + { code: 'PK', label: 'Pakistan', phone: '92' }, + { code: 'PL', label: 'Poland', phone: '48' }, + { + code: 'PM', + label: 'Saint Pierre and Miquelon', + phone: '508', + }, + { code: 'PN', label: 'Pitcairn', phone: '870' }, + { code: 'PR', label: 'Puerto Rico', phone: '1' }, + { + code: 'PS', + label: 'Palestine, State of', + phone: '970', + }, + { code: 'PT', label: 'Portugal', phone: '351' }, + { code: 'PW', label: 'Palau', phone: '680' }, + { code: 'PY', label: 'Paraguay', phone: '595' }, + { code: 'QA', label: 'Qatar', phone: '974' }, + { code: 'RE', label: 'Reunion', phone: '262' }, + { code: 'RO', label: 'Romania', phone: '40' }, + { code: 'RS', label: 'Serbia', phone: '381' }, + { code: 'RU', label: 'Russian Federation', phone: '7' }, + { code: 'RW', label: 'Rwanda', phone: '250' }, + { code: 'SA', label: 'Saudi Arabia', phone: '966' }, + { code: 'SB', label: 'Solomon Islands', phone: '677' }, + { code: 'SC', label: 'Seychelles', phone: '248' }, + { code: 'SD', label: 'Sudan', phone: '249' }, + { code: 'SE', label: 'Sweden', phone: '46' }, + { code: 'SG', label: 'Singapore', phone: '65' }, + { code: 'SH', label: 'Saint Helena', phone: '290' }, + { code: 'SI', label: 'Slovenia', phone: '386' }, + { + code: 'SJ', + label: 'Svalbard and Jan Mayen', + phone: '47', + }, + { code: 'SK', label: 'Slovakia', phone: '421' }, + { code: 'SL', label: 'Sierra Leone', phone: '232' }, + { code: 'SM', label: 'San Marino', phone: '378' }, + { code: 'SN', label: 'Senegal', phone: '221' }, + { code: 'SO', label: 'Somalia', phone: '252' }, + { code: 'SR', label: 'Suriname', phone: '597' }, + { code: 'SS', label: 'South Sudan', phone: '211' }, + { + code: 'ST', + label: 'Sao Tome and Principe', + phone: '239', + }, + { code: 'SV', label: 'El Salvador', phone: '503' }, + { + code: 'SX', + label: 'Sint Maarten (Dutch part)', + phone: '1-721', + }, + { + code: 'SY', + label: 'Syrian Arab Republic', + phone: '963', + }, + { code: 'SZ', label: 'Swaziland', phone: '268' }, + { + code: 'TC', + label: 'Turks and Caicos Islands', + phone: '1-649', + }, + { code: 'TD', label: 'Chad', phone: '235' }, + { + code: 'TF', + label: 'French Southern Territories', + phone: '262', + }, + { code: 'TG', label: 'Togo', phone: '228' }, + { code: 'TH', label: 'Thailand', phone: '66' }, + { code: 'TJ', label: 'Tajikistan', phone: '992' }, + { code: 'TK', label: 'Tokelau', phone: '690' }, + { code: 'TL', label: 'Timor-Leste', phone: '670' }, + { code: 'TM', label: 'Turkmenistan', phone: '993' }, + { code: 'TN', label: 'Tunisia', phone: '216' }, + { code: 'TO', label: 'Tonga', phone: '676' }, + { code: 'TR', label: 'Turkey', phone: '90' }, + { + code: 'TT', + label: 'Trinidad and Tobago', + phone: '1-868', + }, + { code: 'TV', label: 'Tuvalu', phone: '688' }, + { + code: 'TW', + label: 'Taiwan, Republic of China', + phone: '886', + }, + { + code: 'TZ', + label: 'United Republic of Tanzania', + phone: '255', + }, + { code: 'UA', label: 'Ukraine', phone: '380' }, + { code: 'UG', label: 'Uganda', phone: '256' }, + { + code: 'US', + label: 'United States', + phone: '1', + suggested: true, + }, + { code: 'UY', label: 'Uruguay', phone: '598' }, + { code: 'UZ', label: 'Uzbekistan', phone: '998' }, + { + code: 'VA', + label: 'Holy See (Vatican City State)', + phone: '379', + }, + { + code: 'VC', + label: 'Saint Vincent and the Grenadines', + phone: '1-784', + }, + { code: 'VE', label: 'Venezuela', phone: '58' }, + { + code: 'VG', + label: 'British Virgin Islands', + phone: '1-284', + }, + { + code: 'VI', + label: 'US Virgin Islands', + phone: '1-340', + }, + { code: 'VN', label: 'Vietnam', phone: '84' }, + { code: 'VU', label: 'Vanuatu', phone: '678' }, + { code: 'WF', label: 'Wallis and Futuna', phone: '681' }, + { code: 'WS', label: 'Samoa', phone: '685' }, + { code: 'XK', label: 'Kosovo', phone: '383' }, + { code: 'YE', label: 'Yemen', phone: '967' }, + { code: 'YT', label: 'Mayotte', phone: '262' }, + { code: 'ZA', label: 'South Africa', phone: '27' }, + { code: 'ZM', label: 'Zambia', phone: '260' }, + { code: 'ZW', label: 'Zimbabwe', phone: '263' }, +]; diff --git a/docs/data/joy/getting-started/templates/rental-dashboard/components/Filters.tsx b/docs/data/joy/getting-started/templates/rental-dashboard/components/Filters.tsx new file mode 100644 index 00000000000000..baa0a42dc63a08 --- /dev/null +++ b/docs/data/joy/getting-started/templates/rental-dashboard/components/Filters.tsx @@ -0,0 +1,49 @@ +import * as React from 'react'; +import Box from '@mui/joy/Box'; +import Button from '@mui/joy/Button'; +import FormControl from '@mui/joy/FormControl'; +import Input from '@mui/joy/Input'; +import Grid from '@mui/joy/Grid'; +import CountrySelector from './CountrySelector'; + +export default function Filters() { + return ( + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/docs/data/joy/getting-started/templates/rental-dashboard/components/FirstSidebar.tsx b/docs/data/joy/getting-started/templates/rental-dashboard/components/FirstSidebar.tsx new file mode 100644 index 00000000000000..1e1a40a9189245 --- /dev/null +++ b/docs/data/joy/getting-started/templates/rental-dashboard/components/FirstSidebar.tsx @@ -0,0 +1,120 @@ +import * as React from 'react'; +import GlobalStyles from '@mui/joy/GlobalStyles'; +import Avatar from '@mui/joy/Avatar'; +import List from '@mui/joy/List'; +import ListItem from '@mui/joy/ListItem'; +import ListItemButton from '@mui/joy/ListItemButton'; +import IconButton from '@mui/joy/IconButton'; +import Sheet from '@mui/joy/Sheet'; +import MuiLogo from './MuiLogo'; +import { openSidebar, closeSidebar } from '../utils'; +import ColorSchemeToggle from './ColorSchemeToggle'; + +export default function FirstSidebar() { + return ( + + + closeSidebar()} + sx={{ display: { md: 'none' }, mt: -2, borderRadius: '50%' }} + > + + + + + + + + + + + openSidebar()}> + + + + + openSidebar()}> + + + + + openSidebar()}> + + + + + openSidebar()} + > + + + + + openSidebar()}> + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/docs/data/joy/getting-started/templates/rental-dashboard/components/Header.tsx b/docs/data/joy/getting-started/templates/rental-dashboard/components/Header.tsx new file mode 100644 index 00000000000000..746e72e993fe86 --- /dev/null +++ b/docs/data/joy/getting-started/templates/rental-dashboard/components/Header.tsx @@ -0,0 +1,48 @@ +import * as React from 'react'; +import GlobalStyles from '@mui/joy/GlobalStyles'; +import IconButton from '@mui/joy/IconButton'; +import Sheet from '@mui/joy/Sheet'; +import MuiLogo from './MuiLogo'; +import ColorSchemeToggle from './ColorSchemeToggle'; +import { toggleSidebar } from '../utils'; + +export default function Header() { + return ( + + ({ + ':root': { + '--Header-height': '52px', + [theme.breakpoints.up('md')]: { + '--Header-height': '0px', + }, + }, + })} + /> + toggleSidebar()} + variant="outlined" + color="neutral" + size="sm" + > + + + + + + ); +} diff --git a/docs/data/joy/getting-started/templates/rental-dashboard/components/HeaderSection.tsx b/docs/data/joy/getting-started/templates/rental-dashboard/components/HeaderSection.tsx new file mode 100644 index 00000000000000..73affc456eff10 --- /dev/null +++ b/docs/data/joy/getting-started/templates/rental-dashboard/components/HeaderSection.tsx @@ -0,0 +1,46 @@ +import * as React from 'react'; +import Button from '@mui/joy/Button'; +import Stack from '@mui/joy/Stack'; +import Typography from '@mui/joy/Typography'; + +export default function HeaderSection() { + return ( + +
+ + 232 stays in Melbourne + + + Book your next stay at one of our properties. + +
+ + + + + +
+ ); +} diff --git a/docs/data/joy/getting-started/templates/rental-dashboard/components/Main.tsx b/docs/data/joy/getting-started/templates/rental-dashboard/components/Main.tsx new file mode 100644 index 00000000000000..912e63a5103aa7 --- /dev/null +++ b/docs/data/joy/getting-started/templates/rental-dashboard/components/Main.tsx @@ -0,0 +1,20 @@ +import * as React from 'react'; +import Box from '@mui/joy/Box'; + +type MainProps = { + children: React.ReactNode; +}; + +export default function Main({ children }: MainProps) { + return ( + + {children} + + ); +} diff --git a/docs/data/joy/getting-started/templates/rental-dashboard/components/MuiLogo.tsx b/docs/data/joy/getting-started/templates/rental-dashboard/components/MuiLogo.tsx new file mode 100644 index 00000000000000..fb03654416ad45 --- /dev/null +++ b/docs/data/joy/getting-started/templates/rental-dashboard/components/MuiLogo.tsx @@ -0,0 +1,34 @@ +import * as React from 'react'; +import AspectRatio, { AspectRatioProps } from '@mui/joy/AspectRatio'; + +export default function MuiLogo({ sx, ...props }: AspectRatioProps) { + return ( + +
+ + + +
+
+ ); +} diff --git a/docs/data/joy/getting-started/templates/rental-dashboard/components/Pagination.tsx b/docs/data/joy/getting-started/templates/rental-dashboard/components/Pagination.tsx new file mode 100644 index 00000000000000..9e1d436454d210 --- /dev/null +++ b/docs/data/joy/getting-started/templates/rental-dashboard/components/Pagination.tsx @@ -0,0 +1,79 @@ +import * as React from 'react'; +import Box from '@mui/joy/Box'; +import Button from '@mui/joy/Button'; +import IconButton, { iconButtonClasses } from '@mui/joy/IconButton'; +import Typography from '@mui/joy/Typography'; + +export default function Pagination() { + return ( +
+ + + + + + Page 1 of 10 + + + + + + + + + + {['1', '2', '3', '…', '8', '9', '10'].map((page) => ( + + {page} + + ))} + + + + +
+ ); +} diff --git a/docs/data/joy/getting-started/templates/rental-dashboard/components/Rating.tsx b/docs/data/joy/getting-started/templates/rental-dashboard/components/Rating.tsx new file mode 100644 index 00000000000000..e5bde46ecc813a --- /dev/null +++ b/docs/data/joy/getting-started/templates/rental-dashboard/components/Rating.tsx @@ -0,0 +1,22 @@ +import * as React from 'react'; +import Typography from '@mui/joy/Typography'; +import Star from '@mui/icons-material/Star'; + +export default function Rating() { + return ( + + + + + + + + } + > + 4.9 + + ); +} diff --git a/docs/data/joy/getting-started/templates/rental-dashboard/components/RentalCard.tsx b/docs/data/joy/getting-started/templates/rental-dashboard/components/RentalCard.tsx new file mode 100644 index 00000000000000..ed68d8038d0764 --- /dev/null +++ b/docs/data/joy/getting-started/templates/rental-dashboard/components/RentalCard.tsx @@ -0,0 +1,175 @@ +import * as React from 'react'; +import AspectRatio from '@mui/joy/AspectRatio'; +import Box from '@mui/joy/Box'; +import Card from '@mui/joy/Card'; +import Chip from '@mui/joy/Chip'; +import IconButton from '@mui/joy/IconButton'; +import Link from '@mui/joy/Link'; +import Stack from '@mui/joy/Stack'; +import Typography from '@mui/joy/Typography'; +import Rating from './Rating'; + +type RentalCardProps = { + category: React.ReactNode; + image: string; + liked?: boolean; + rareFind?: boolean; + title: React.ReactNode; +}; + +export default function RentalCard({ + category, + title, + rareFind = false, + liked = false, + image, +}: RentalCardProps) { + const [isLiked, setIsLiked] = React.useState(liked); + return ( + + + + ({ + borderRadius: 'xs', + [theme.breakpoints.down('sm')]: { + borderBottomLeftRadius: 0, + borderBottomRightRadius: 0, + }, + })} + > + + {rareFind && ( + } + size="sm" + sx={{ position: 'absolute', bottom: 8, left: 8 }} + > + Rare find + + )} + setIsLiked((prev) => !prev)} + sx={{ + position: 'absolute', + bottom: 8, + right: 8, + display: { xs: 'flex', sm: 'none' }, + }} + > + + + + + + +
+ + {category} + + + + {title} + + +
+ setIsLiked((prev) => !prev)} + sx={{ + display: { xs: 'none', sm: 'flex' }, + }} + > + + +
+ + + + 202 reviews + + + + }> + Collingwood VIC + + } + display={{ + xs: 'none', + md: 'flex', + }} + > + 1 bed + + } + display={{ + xs: 'none', + md: 'flex', + }} + > + Wi-Fi + + + $540 total + + +
+
+
+ ); +} diff --git a/docs/data/joy/getting-started/templates/rental-dashboard/components/Search.tsx b/docs/data/joy/getting-started/templates/rental-dashboard/components/Search.tsx new file mode 100644 index 00000000000000..4955ae87554645 --- /dev/null +++ b/docs/data/joy/getting-started/templates/rental-dashboard/components/Search.tsx @@ -0,0 +1,30 @@ +import * as React from 'react'; +import Box from '@mui/joy/Box'; +import Button from '@mui/joy/Button'; +import FormControl from '@mui/joy/FormControl'; +import Input from '@mui/joy/Input'; +import Stack from '@mui/joy/Stack'; + +export default function Search() { + return ( + + + } + aria-label="Search" + /> + + + + + + + + + ); +} diff --git a/docs/data/joy/getting-started/templates/rental-dashboard/components/ToggleGroup.tsx b/docs/data/joy/getting-started/templates/rental-dashboard/components/ToggleGroup.tsx new file mode 100644 index 00000000000000..c0c606f9b8a96b --- /dev/null +++ b/docs/data/joy/getting-started/templates/rental-dashboard/components/ToggleGroup.tsx @@ -0,0 +1,74 @@ +import * as React from 'react'; +import Box from '@mui/joy/Box'; +import Radio, { radioClasses } from '@mui/joy/Radio'; +import RadioGroup from '@mui/joy/RadioGroup'; + +type Option = { + label: React.ReactNode; + value: string; +}; + +type ToggleGroupProps = { + options: Option[]; +}; + +export default function ToggleGroup({ options }: ToggleGroupProps) { + const [selectedOption, setSelectedOption] = React.useState(options[0].value); + + return ( + ) => + setSelectedOption(event.target.value) + } + > + {options.map((option, index) => ( + ({ + position: 'relative', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + height: 40, + paddingX: 2, + '&:not([data-first-child])': { + borderLeft: '1px solid', + borderColor: 'divider', + }, + [`&[data-first-child] .${radioClasses.action}`]: { + borderTopLeftRadius: `calc(${theme.vars.radius.sm} - 1px)`, + borderBottomLeftRadius: `calc(${theme.vars.radius.sm} - 1px)`, + }, + [`&[data-last-child] .${radioClasses.action}`]: { + borderTopRightRadius: `calc(${theme.vars.radius.sm} - 1px)`, + borderBottomRightRadius: `calc(${theme.vars.radius.sm} - 1px)`, + }, + })} + data-first-child={index === 0 ? true : undefined} + data-last-child={index === options.length - 1 ? true : undefined} + > + + + ))} + + ); +} diff --git a/docs/data/joy/getting-started/templates/rental-dashboard/components/Toggles.tsx b/docs/data/joy/getting-started/templates/rental-dashboard/components/Toggles.tsx new file mode 100644 index 00000000000000..3a5a079825260f --- /dev/null +++ b/docs/data/joy/getting-started/templates/rental-dashboard/components/Toggles.tsx @@ -0,0 +1,22 @@ +import * as React from 'react'; +import { Stack } from '@mui/joy'; +import ToggleGroup from './ToggleGroup'; + +export default function Toggles() { + return ( + + + , value: 'list' }, + { label: , value: 'map' }, + ]} + /> + + ); +} diff --git a/docs/data/joy/getting-started/templates/rental-dashboard/theme.ts b/docs/data/joy/getting-started/templates/rental-dashboard/theme.ts new file mode 100644 index 00000000000000..4742b97f2f8147 --- /dev/null +++ b/docs/data/joy/getting-started/templates/rental-dashboard/theme.ts @@ -0,0 +1,8 @@ +import { extendTheme } from '@mui/joy/styles'; + +export default extendTheme({ + fontFamily: { + display: "'Inter', var(--joy-fontFamily-fallback)", + body: "'Inter', var(--joy-fontFamily-fallback)", + }, +}); diff --git a/docs/data/joy/getting-started/templates/rental-dashboard/useScript.ts b/docs/data/joy/getting-started/templates/rental-dashboard/useScript.ts new file mode 100644 index 00000000000000..205f011215caa6 --- /dev/null +++ b/docs/data/joy/getting-started/templates/rental-dashboard/useScript.ts @@ -0,0 +1,101 @@ +import * as React from 'react'; + +export type UseScriptStatus = 'idle' | 'loading' | 'ready' | 'error'; + +// Cached script statuses +const cachedScriptStatuses: Record = {}; + +/** + * Simplified version of https://usehooks-ts.com/react-hook/use-script + */ +function getScriptNode(src: string) { + const node: HTMLScriptElement | null = document.querySelector(`script[src="${src}"]`); + const status = node?.getAttribute('data-status') as UseScriptStatus | undefined; + + return { + node, + status, + }; +} + +function useScript(src: string): UseScriptStatus { + const [status, setStatus] = React.useState(() => { + if (typeof window === 'undefined') { + // SSR Handling - always return 'loading' + return 'loading'; + } + + return cachedScriptStatuses[src] ?? 'loading'; + }); + + React.useEffect(() => { + const cachedScriptStatus = cachedScriptStatuses[src]; + if (cachedScriptStatus === 'ready' || cachedScriptStatus === 'error') { + // If the script is already cached, set its status immediately + setStatus(cachedScriptStatus); + return; + } + + // Fetch existing script element by src + // It may have been added by another instance of this hook + const script = getScriptNode(src); + let scriptNode = script.node; + + if (!scriptNode) { + // Create script element and add it to document body + scriptNode = document.createElement('script'); + scriptNode.src = src; + scriptNode.async = true; + scriptNode.setAttribute('data-status', 'loading'); + document.body.appendChild(scriptNode); + + // Store status in attribute on script + // This can be read by other instances of this hook + const setAttributeFromEvent = (event: Event) => { + const scriptStatus: UseScriptStatus = event.type === 'load' ? 'ready' : 'error'; + + scriptNode?.setAttribute('data-status', scriptStatus); + }; + + scriptNode.addEventListener('load', setAttributeFromEvent); + scriptNode.addEventListener('error', setAttributeFromEvent); + } else { + // Grab existing script status from attribute and set to state. + setStatus(script.status ?? cachedScriptStatus ?? 'loading'); + } + + // Script event handler to update status in state + // Note: Even if the script already exists we still need to add + // event handlers to update the state for *this* hook instance. + const setStateFromEvent = (event: Event) => { + const newStatus = event.type === 'load' ? 'ready' : 'error'; + setStatus(newStatus); + cachedScriptStatuses[src] = newStatus; + }; + + // Add event listeners + scriptNode.addEventListener('load', setStateFromEvent); + scriptNode.addEventListener('error', setStateFromEvent); + + // Remove event listeners on cleanup + // eslint-disable-next-line consistent-return + return () => { + if (scriptNode) { + scriptNode.removeEventListener('load', setStateFromEvent); + scriptNode.removeEventListener('error', setStateFromEvent); + } + + if (scriptNode) { + try { + scriptNode.remove(); + } catch (error) { + // ignore error + } + } + }; + }, [src]); + + return status; +} + +export default useScript; diff --git a/docs/data/joy/getting-started/templates/rental-dashboard/utils.ts b/docs/data/joy/getting-started/templates/rental-dashboard/utils.ts new file mode 100644 index 00000000000000..83220658582ce6 --- /dev/null +++ b/docs/data/joy/getting-started/templates/rental-dashboard/utils.ts @@ -0,0 +1,26 @@ +export const openSidebar = () => { + if (typeof document !== 'undefined') { + document.body.style.overflow = 'hidden'; + document.documentElement.style.setProperty('--SideNavigation-slideIn', '1'); + } +}; + +export const closeSidebar = () => { + if (typeof document !== 'undefined') { + document.documentElement.style.removeProperty('--SideNavigation-slideIn'); + document.body.style.removeProperty('overflow'); + } +}; + +export const toggleSidebar = () => { + if (typeof window !== 'undefined' && typeof document !== 'undefined') { + const slideIn = window + .getComputedStyle(document.documentElement) + .getPropertyValue('--SideNavigation-slideIn'); + if (slideIn) { + closeSidebar(); + } else { + openSidebar(); + } + } +}; diff --git a/docs/pages/joy-ui/getting-started/templates/rental-dashboard.js b/docs/pages/joy-ui/getting-started/templates/rental-dashboard.js new file mode 100644 index 00000000000000..5c5452b8cc0780 --- /dev/null +++ b/docs/pages/joy-ui/getting-started/templates/rental-dashboard.js @@ -0,0 +1,22 @@ +import * as React from 'react'; +import Head from 'docs/src/modules/components/Head'; +import RentalDashboardTemplate from 'docs/data/joy/getting-started/templates/rental-dashboard/App'; +import { NextNProgressBar } from 'docs/src/modules/components/AppFrame'; + +export default function RentalDashboard() { + return ( + + + + + + + + + + + ); +} diff --git a/docs/public/static/screenshots/joy-ui/getting-started/templates/rental-dashboard-dark.jpg b/docs/public/static/screenshots/joy-ui/getting-started/templates/rental-dashboard-dark.jpg new file mode 100644 index 00000000000000..d1d55cb855b7ed Binary files /dev/null and b/docs/public/static/screenshots/joy-ui/getting-started/templates/rental-dashboard-dark.jpg differ diff --git a/docs/public/static/screenshots/joy-ui/getting-started/templates/rental-dashboard.jpg b/docs/public/static/screenshots/joy-ui/getting-started/templates/rental-dashboard.jpg new file mode 100644 index 00000000000000..8f7a6b1f135e0b Binary files /dev/null and b/docs/public/static/screenshots/joy-ui/getting-started/templates/rental-dashboard.jpg differ