diff --git a/docs/data/joy/getting-started/templates/TemplateCollection.js b/docs/data/joy/getting-started/templates/TemplateCollection.js index 0d683a7dcf856f..1af3229709be0e 100644 --- a/docs/data/joy/getting-started/templates/TemplateCollection.js +++ b/docs/data/joy/getting-started/templates/TemplateCollection.js @@ -6,10 +6,10 @@ import { useTheme } from '@mui/joy/styles'; 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 Link from '@mui/joy/Link'; import List from '@mui/joy/List'; -import ListDivider from '@mui/joy/ListDivider'; -import Button from '@mui/joy/Button'; +import IconButton from '@mui/joy/IconButton'; import Typography from '@mui/joy/Typography'; import SvgIcon from '@mui/joy/SvgIcon'; import Visibility from '@mui/icons-material/Visibility'; @@ -17,7 +17,7 @@ import codeSandbox from 'docs/src/modules/sandbox/CodeSandbox'; import extractTemplates from 'docs/src/modules/utils/extractTemplates'; const cache = {}; -const req = require.context('./?raw', true, /^\.\/[^/]+\/.*\.(js|tsx)$/); +const req = require.context('./?raw', true, /^\.\/[^/]+\/.*\.(js|tsx|ts)$/); req.keys().forEach((key) => { cache[key] = req(key); }); @@ -44,135 +44,190 @@ function addHiddenInput(form, name, value) { * - The name of the folder will be used as the url and title */ +/** + * @typedef {Object} Author + * @property {string} name name of the author + * @property {string} github github username + */ + +/** + * @type {Object.} + */ +const AUTHORS = { + team: { + name: 'MUI', + link: 'https://twitter.com/MUI_hq', + }, + files: { + name: 'MUI', + link: 'https://twitter.com/MUI_hq', + }, + email: { + name: 'MUI', + link: 'https://twitter.com/MUI_hq', + }, + 'sign-in-side': { + name: 'MUI', + link: 'https://twitter.com/MUI_hq', + }, +}; + export default function TemplateCollection() { + const newTemplates = ['sign-in-side']; // Stay at the top of the page with `new` badge const templates = extractTemplates(cache); const theme = useTheme(); + const names = [ + ...newTemplates, + ...Object.keys(templates).filter((name) => !newTemplates.includes(name)), + ]; return ( - {Object.keys(templates).map((name, index) => { + {names.map((name) => { const item = templates[name]; + const author = AUTHORS[name]; return ( - - {index !== 0 && } - + + New + + ) : null + } + slotProps={{ endDecorator: { sx: { ml: 'auto' } } }} + sx={{ mb: 1 }} > + {startCase(name)} + + + *': { - minWidth: `clamp(0px, (400px - 100%) * 999 , 100%)`, - }, + background: `center/cover no-repeat url(/static/screenshots/joy-ui/getting-started/templates/${name}${ + theme.palette.mode === 'dark' ? '-dark' : '' + }.jpg)`, + transition: '0.3s', }} + /> + - - {startCase(name)} - - - - - - - - - - {/* eslint-disable-next-line jsx-a11y/anchor-is-valid */} - - - - - + + + + { + const { files } = codeSandbox.createJoyTemplate({ + ...item, + title: `${startCase(name)} Template - Joy UI`, + githubLocation: `${process.env.SOURCE_CODE_REPO}/blob/v${ + process.env.LIB_VERSION + }/docs/data/joy/templates/${name}/App.${ + item.codeVariant === 'TS' ? 'tsx' : 'js' + }`, + }); + const parameters = compress({ files }); + + // ref: https://codesandbox.io/docs/api/#define-api + const form = document.createElement('form'); + form.method = 'POST'; + form.target = '_blank'; + form.action = 'https://codesandbox.io/api/v1/sandboxes/define'; + addHiddenInput(form, 'parameters', parameters); + addHiddenInput( + form, + 'query', + item.codeVariant === 'TS' ? 'file=/App.tsx' : 'file=/App.js', + ); + document.body.appendChild(form); + form.submit(); + document.body.removeChild(form); + }} + > + + + + + + {author && ( + + Created by{' '} + + {author.name} + + + )} + + ); })} diff --git a/docs/data/joy/getting-started/templates/sign-in-side/App.tsx b/docs/data/joy/getting-started/templates/sign-in-side/App.tsx new file mode 100644 index 00000000000000..2d0d709f00b116 --- /dev/null +++ b/docs/data/joy/getting-started/templates/sign-in-side/App.tsx @@ -0,0 +1,246 @@ +import * as React from 'react'; +import { CssVarsProvider, useColorScheme } from '@mui/joy/styles'; +import GlobalStyles from '@mui/joy/GlobalStyles'; +import CssBaseline from '@mui/joy/CssBaseline'; +import Box from '@mui/joy/Box'; +import Button from '@mui/joy/Button'; +import Checkbox from '@mui/joy/Checkbox'; +import FormControl from '@mui/joy/FormControl'; +import FormLabel, { formLabelClasses } from '@mui/joy/FormLabel'; +import IconButton, { IconButtonProps } from '@mui/joy/IconButton'; +import Link from '@mui/joy/Link'; +import Input from '@mui/joy/Input'; +import Typography from '@mui/joy/Typography'; +import DarkModeRoundedIcon from '@mui/icons-material/DarkModeRounded'; +import LightModeRoundedIcon from '@mui/icons-material/LightModeRounded'; +import customTheme from './theme'; +import GoogleIcon from './GoogleIcon'; + +interface FormElements extends HTMLFormControlsCollection { + email: HTMLInputElement; + password: HTMLInputElement; + persistent: HTMLInputElement; +} +interface SignInFormElement extends HTMLFormElement { + readonly elements: FormElements; +} + +function ColorSchemeToggle({ onClick, ...props }: IconButtonProps) { + 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); + }} + > + {mode === 'light' ? : } + + ); +} + +/** + * This template uses [`Inter`](https://fonts.google.com/specimen/Inter?query=inter) font. + */ +export default function JoySignInSideTemplate() { + return ( + + + + ({ + width: + 'clamp(100vw - var(--Cover-width), (var(--Collapsed-breakpoint) - 100vw) * 999, 100vw)', + transition: 'width var(--Transition-duration)', + transitionDelay: 'calc(var(--Transition-duration) + 0.1s)', + position: 'relative', + zIndex: 1, + display: 'flex', + justifyContent: 'flex-end', + backdropFilter: 'blur(4px)', + backgroundColor: 'rgba(255 255 255 / 0.6)', + [theme.getColorSchemeSelector('dark')]: { + backgroundColor: 'rgba(19 19 24 / 0.4)', + }, + })} + > + + + + `linear-gradient(45deg, ${theme.vars.palette.primary.solidBg}, ${theme.vars.palette.primary.solidBg} 30%, ${theme.vars.palette.primary.softBg})`, + borderRadius: '50%', + boxShadow: (theme) => theme.shadow.md, + '--joy-shadowChannel': (theme) => + theme.vars.palette.primary.mainChannel, + }} + /> + } + > + Logo + + + + +
+ + Welcome back + + + Let's get started! Please enter your details. + +
+
) => { + event.preventDefault(); + const formElements = event.currentTarget.elements; + const data = { + email: formElements.email.value, + password: formElements.password.value, + persistent: formElements.persistent.checked, + }; + alert(JSON.stringify(data, null, 2)); + }} + > + + Email + + + + Password + + + + + + Forgot password + + + +
+ +
+ + + © Your company {new Date().getFullYear()} + + +
+
+ ({ + height: '100%', + position: 'fixed', + right: 0, + top: 0, + bottom: 0, + left: 'clamp(0px, (100vw - var(--Collapsed-breakpoint)) * 999, 100vw - var(--Cover-width))', + transition: + 'background-image var(--Transition-duration), left var(--Transition-duration) !important', + transitionDelay: 'calc(var(--Transition-duration) + 0.1s)', + backgroundColor: 'background.level1', + backgroundSize: 'cover', + backgroundPosition: 'center', + backgroundRepeat: 'no-repeat', + backgroundImage: + 'url(https://images.unsplash.com/photo-1527181152855-fc03fc7949c8)', + [theme.getColorSchemeSelector('dark')]: { + backgroundImage: + 'url(https://images.unsplash.com/photo-1572072393749-3ca9c8ea0831)', + }, + })} + /> +
+ ); +} diff --git a/docs/data/joy/getting-started/templates/sign-in-side/GoogleIcon.tsx b/docs/data/joy/getting-started/templates/sign-in-side/GoogleIcon.tsx new file mode 100644 index 00000000000000..87f81ee3491382 --- /dev/null +++ b/docs/data/joy/getting-started/templates/sign-in-side/GoogleIcon.tsx @@ -0,0 +1,27 @@ +import * as React from 'react'; +import SvgIcon from '@mui/joy/SvgIcon'; + +export default function GoogleIcon() { + return ( + + + + + + + + + ); +} diff --git a/docs/data/joy/getting-started/templates/sign-in-side/theme.ts b/docs/data/joy/getting-started/templates/sign-in-side/theme.ts new file mode 100644 index 00000000000000..4742b97f2f8147 --- /dev/null +++ b/docs/data/joy/getting-started/templates/sign-in-side/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/pages/_document.js b/docs/pages/_document.js index 718c9a2e882fec..5c2c1676853c4d 100644 --- a/docs/pages/_document.js +++ b/docs/pages/_document.js @@ -8,6 +8,7 @@ import createEmotionCache from 'docs/src/createEmotionCache'; import { getMetaThemeColor } from 'docs/src/modules/brandingTheme'; import GlobalStyles from '@mui/material/GlobalStyles'; import { getInitColorSchemeScript as getMuiInitColorSchemeScript } from '@mui/material/styles'; +import { getInitColorSchemeScript as getJoyInitColorSchemeScript } from '@mui/joy/styles'; // You can find a benchmark of the available CSS minifiers under // https://github.com/GoalSmashers/css-minification-benchmark @@ -146,6 +147,7 @@ export default class MyDocument extends Document { {getMuiInitColorSchemeScript({ defaultMode: 'system' })} + {getJoyInitColorSchemeScript({ defaultMode: 'system' })}