diff --git a/apps/zero-runtime-next-app/src/app/accordion/page.tsx b/apps/zero-runtime-next-app/src/app/accordion/page.tsx new file mode 100644 index 00000000000000..ad3990c6112c7d --- /dev/null +++ b/apps/zero-runtime-next-app/src/app/accordion/page.tsx @@ -0,0 +1,33 @@ +import Typography from '@mui/material/Typography'; +import AccordionDetails from '@mui/material/AccordionDetails'; +import Accordion from '@/components/Accordion/Accordion'; +import AccordionSummary from '@/components/Accordion/AccordionSummary'; + +export default function AccordionExpandDefault() { + return ( +
+ + + Expanded by default + + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, + sit amet blandit leo lobortis eget. + + + + + + Header + + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada lacus ex, + sit amet blandit leo lobortis eget. + + + +
+ ); +} diff --git a/apps/zero-runtime-next-app/src/components/Accordion/Accordion.js b/apps/zero-runtime-next-app/src/components/Accordion/Accordion.js new file mode 100644 index 00000000000000..ed939a5cef6ce9 --- /dev/null +++ b/apps/zero-runtime-next-app/src/components/Accordion/Accordion.js @@ -0,0 +1,315 @@ +'use client'; +import * as React from 'react'; +import { isFragment } from 'react-is'; +import PropTypes from 'prop-types'; +import clsx from 'clsx'; +import chainPropTypes from '@mui/utils/chainPropTypes'; +import composeClasses from '@mui/utils/composeClasses'; +import { styled } from '@mui/zero-runtime'; +import { useThemeProps } from '@mui/material/styles'; +import Collapse from '@mui/material/Collapse'; +import Paper from '@mui/material/Paper'; +/* eslint-disable-next-line no-restricted-imports */ +import AccordionContext from '@mui/material/Accordion/AccordionContext'; +import { useControlled } from '@mui/material/utils'; +/* eslint-disable-next-line no-restricted-imports */ +import useSlot from '@mui/material/utils/useSlot'; +import { accordionClasses, getAccordionUtilityClass } from '@mui/material/Accordion'; + +const useUtilityClasses = (ownerState) => { + const { classes, square, expanded, disabled, disableGutters } = ownerState; + + const slots = { + root: [ + 'root', + !square && 'rounded', + expanded && 'expanded', + disabled && 'disabled', + !disableGutters && 'gutters', + ], + region: ['region'], + }; + + return composeClasses(slots, getAccordionUtilityClass, classes); +}; + +const AccordionRoot = styled(Paper, { + name: 'MuiAccordion', + slot: 'Root', + overridesResolver: (props, styles) => { + const { ownerState } = props; + + return [ + { [`& .${accordionClasses.region}`]: styles.region }, + styles.root, + !ownerState.square && styles.rounded, + !ownerState.disableGutters && styles.gutters, + ]; + }, +})( + ({ theme }) => { + const transition = { + duration: theme.transitions.duration.shortest, + }; + + return { + position: 'relative', + transition: theme.transitions.create(['margin'], transition), + overflowAnchor: 'none', // Keep the same scrolling position + '&::before': { + position: 'absolute', + left: 0, + top: -1, + right: 0, + height: 1, + content: '""', + opacity: 1, + backgroundColor: (theme.vars || theme).palette.divider, + transition: theme.transitions.create(['opacity', 'background-color'], transition), + }, + '&:first-of-type': { + '&::before': { + display: 'none', + }, + }, + [`&.${accordionClasses.expanded}`]: { + '&::before': { + opacity: 0, + }, + '&:first-of-type': { + marginTop: 0, + }, + '&:last-of-type': { + marginBottom: 0, + }, + '& + &': { + '&::before': { + display: 'none', + }, + }, + }, + [`&.${accordionClasses.disabled}`]: { + backgroundColor: (theme.vars || theme).palette.action.disabledBackground, + }, + }; + }, + ({ theme }) => ({ + variants: [ + { + props: (props) => !props.square, + style: { + borderRadius: 0, + '&:first-of-type': { + borderTopLeftRadius: (theme.vars || theme).shape.borderRadius, + borderTopRightRadius: (theme.vars || theme).shape.borderRadius, + }, + '&:last-of-type': { + borderBottomLeftRadius: (theme.vars || theme).shape.borderRadius, + borderBottomRightRadius: (theme.vars || theme).shape.borderRadius, + // Fix a rendering issue on Edge + '@supports (-ms-ime-align: auto)': { + borderBottomLeftRadius: 0, + borderBottomRightRadius: 0, + }, + }, + }, + }, + { + props: (props) => !props.disableGutters, + style: { + [`&.${accordionClasses.expanded}`]: { + margin: '16px 0', + }, + }, + }, + ], + }), +); + +const Accordion = React.forwardRef(function Accordion(inProps, ref) { + const props = useThemeProps({ props: inProps, name: 'MuiAccordion' }); + const { + children: childrenProp, + className, + defaultExpanded = false, + disabled = false, + disableGutters = false, + expanded: expandedProp, + onChange, + square = false, + slots = {}, + slotProps = {}, + TransitionComponent: TransitionComponentProp, + TransitionProps: TransitionPropsProp, + ...other + } = props; + + const [expanded, setExpandedState] = useControlled({ + controlled: expandedProp, + default: defaultExpanded, + name: 'Accordion', + state: 'expanded', + }); + + const handleChange = React.useCallback( + (event) => { + setExpandedState(!expanded); + + if (onChange) { + onChange(event, !expanded); + } + }, + [expanded, onChange, setExpandedState], + ); + + const [summary, ...children] = React.Children.toArray(childrenProp); + const contextValue = React.useMemo( + () => ({ expanded, disabled, disableGutters, toggle: handleChange }), + [expanded, disabled, disableGutters, handleChange], + ); + + const ownerState = { + ...props, + square, + disabled, + disableGutters, + expanded, + }; + + const classes = useUtilityClasses(ownerState); + + const backwardCompatibleSlots = { transition: TransitionComponentProp, ...slots }; + const backwardCompatibleSlotProps = { transition: TransitionPropsProp, ...slotProps }; + + const [TransitionSlot, transitionProps] = useSlot('transition', { + elementType: Collapse, + externalForwardedProps: { + slots: backwardCompatibleSlots, + slotProps: backwardCompatibleSlotProps, + }, + ownerState, + }); + + return ( + + {summary} + +
+ {children} +
+
+
+ ); +}); + +Accordion.propTypes /* remove-proptypes */ = { + // ┌────────────────────────────── Warning ──────────────────────────────┐ + // │ These PropTypes are generated from the TypeScript type definitions. │ + // │ To update them, edit the d.ts file and run `pnpm proptypes`. │ + // └─────────────────────────────────────────────────────────────────────┘ + /** + * The content of the component. + */ + children: chainPropTypes(PropTypes.node.isRequired, (props) => { + const summary = React.Children.toArray(props.children)[0]; + if (isFragment(summary)) { + return new Error( + "MUI: The Accordion doesn't accept a Fragment as a child. " + + 'Consider providing an array instead.', + ); + } + + if (!React.isValidElement(summary)) { + return new Error('MUI: Expected the first child of Accordion to be a valid element.'); + } + + return null; + }), + /** + * Override or extend the styles applied to the component. + */ + classes: PropTypes.object, + /** + * @ignore + */ + className: PropTypes.string, + /** + * If `true`, expands the accordion by default. + * @default false + */ + defaultExpanded: PropTypes.bool, + /** + * If `true`, the component is disabled. + * @default false + */ + disabled: PropTypes.bool, + /** + * If `true`, it removes the margin between two expanded accordion items and the increase of height. + * @default false + */ + disableGutters: PropTypes.bool, + /** + * If `true`, expands the accordion, otherwise collapse it. + * Setting this prop enables control over the accordion. + */ + expanded: PropTypes.bool, + /** + * Callback fired when the expand/collapse state is changed. + * + * @param {React.SyntheticEvent} event The event source of the callback. **Warning**: This is a generic event not a change event. + * @param {boolean} expanded The `expanded` state of the accordion. + */ + onChange: PropTypes.func, + /** + * The props used for each slot inside. + * @default {} + */ + slotProps: PropTypes.shape({ + transition: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + }), + /** + * The components used for each slot inside. + * @default {} + */ + slots: PropTypes.shape({ + transition: PropTypes.elementType, + }), + /** + * If `true`, rounded corners are disabled. + * @default false + */ + square: PropTypes.bool, + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), + PropTypes.func, + PropTypes.object, + ]), + /** + * The component used for the transition. + * [Follow this guide](/material-ui/transitions/#transitioncomponent-prop) to learn more about the requirements for this component. + * @deprecated Use `slots.transition` instead. This prop will be removed in v7. [How to migrate](/material-ui/migration/migrating-from-deprecated-apis/). + */ + TransitionComponent: PropTypes.elementType, + /** + * Props applied to the transition element. + * By default, the element is based on this [`Transition`](https://reactcommunity.org/react-transition-group/transition/) component. + * @deprecated Use `slotProps.transition` instead. This prop will be removed in v7. [How to migrate](/material-ui/migration/migrating-from-deprecated-apis/). + */ + TransitionProps: PropTypes.object, +}; + +export default Accordion; diff --git a/apps/zero-runtime-next-app/src/components/Accordion/AccordionActions.js b/apps/zero-runtime-next-app/src/components/Accordion/AccordionActions.js new file mode 100644 index 00000000000000..01380e00bb63ab --- /dev/null +++ b/apps/zero-runtime-next-app/src/components/Accordion/AccordionActions.js @@ -0,0 +1,94 @@ +'use client'; +import * as React from 'react'; +import PropTypes from 'prop-types'; +import clsx from 'clsx'; +import composeClasses from '@mui/utils/composeClasses'; +import { styled } from '@mui/zero-runtime'; +import { useThemeProps } from '@mui/material/styles'; +import { getAccordionActionsUtilityClass } from '@mui/material/AccordionActions'; + +const useUtilityClasses = (ownerState) => { + const { classes, disableSpacing } = ownerState; + + const slots = { + root: ['root', !disableSpacing && 'spacing'], + }; + + return composeClasses(slots, getAccordionActionsUtilityClass, classes); +}; + +const AccordionActionsRoot = styled('div', { + name: 'MuiAccordionActions', + slot: 'Root', + overridesResolver: (props, styles) => { + const { ownerState } = props; + + return [styles.root, !ownerState.disableSpacing && styles.spacing]; + }, +})({ + display: 'flex', + alignItems: 'center', + padding: 8, + justifyContent: 'flex-end', + variants: [ + { + props: (props) => !props.disableSpacing, + style: { + '& > :not(style) ~ :not(style)': { + marginLeft: 8, + }, + }, + }, + ], +}); + +const AccordionActions = React.forwardRef(function AccordionActions(inProps, ref) { + const props = useThemeProps({ props: inProps, name: 'MuiAccordionActions' }); + const { className, disableSpacing = false, ...other } = props; + const ownerState = { ...props, disableSpacing }; + + const classes = useUtilityClasses(ownerState); + + return ( + + ); +}); + +AccordionActions.propTypes /* remove-proptypes */ = { + // ┌────────────────────────────── Warning ──────────────────────────────┐ + // │ These PropTypes are generated from the TypeScript type definitions. │ + // │ To update them, edit the d.ts file and run `pnpm proptypes`. │ + // └─────────────────────────────────────────────────────────────────────┘ + /** + * The content of the component. + */ + children: PropTypes.node, + /** + * Override or extend the styles applied to the component. + */ + classes: PropTypes.object, + /** + * @ignore + */ + className: PropTypes.string, + /** + * If `true`, the actions do not have additional margin. + * @default false + */ + disableSpacing: PropTypes.bool, + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), + PropTypes.func, + PropTypes.object, + ]), +}; + +export default AccordionActions; diff --git a/apps/zero-runtime-next-app/src/components/Accordion/AccordionSummary.js b/apps/zero-runtime-next-app/src/components/Accordion/AccordionSummary.js new file mode 100644 index 00000000000000..3cbb1874a147fd --- /dev/null +++ b/apps/zero-runtime-next-app/src/components/Accordion/AccordionSummary.js @@ -0,0 +1,200 @@ +'use client'; +import * as React from 'react'; +import PropTypes from 'prop-types'; +import clsx from 'clsx'; +import composeClasses from '@mui/utils/composeClasses'; +import { styled } from '@mui/zero-runtime'; +import { useThemeProps } from '@mui/material/styles'; +import ButtonBase from '@mui/material/ButtonBase'; +/* eslint-disable-next-line no-restricted-imports */ +import AccordionContext from '@mui/material/Accordion/AccordionContext'; +import { + accordionSummaryClasses, + getAccordionSummaryUtilityClass, +} from '@mui/material/AccordionSummary'; + +const useUtilityClasses = (ownerState) => { + const { classes, expanded, disabled, disableGutters } = ownerState; + + const slots = { + root: ['root', expanded && 'expanded', disabled && 'disabled', !disableGutters && 'gutters'], + focusVisible: ['focusVisible'], + content: ['content', expanded && 'expanded', !disableGutters && 'contentGutters'], + expandIconWrapper: ['expandIconWrapper', expanded && 'expanded'], + }; + + return composeClasses(slots, getAccordionSummaryUtilityClass, classes); +}; + +const AccordionSummaryRoot = styled(ButtonBase, { + name: 'MuiAccordionSummary', + slot: 'Root', + overridesResolver: (props, styles) => styles.root, +})(({ theme }) => { + const transition = { + duration: theme.transitions.duration.shortest, + }; + + return { + display: 'flex', + minHeight: 48, + padding: theme.spacing(0, 2), + transition: theme.transitions.create(['min-height', 'background-color'], transition), + [`&.${accordionSummaryClasses.focusVisible}`]: { + backgroundColor: (theme.vars || theme).palette.action.focus, + }, + [`&.${accordionSummaryClasses.disabled}`]: { + opacity: (theme.vars || theme).palette.action.disabledOpacity, + }, + [`&:hover:not(.${accordionSummaryClasses.disabled})`]: { + cursor: 'pointer', + }, + variants: [ + { + props: (props) => !props.disableGutters, + style: { + [`&.${accordionSummaryClasses.expanded}`]: { + minHeight: 64, + }, + }, + }, + ], + }; +}); + +const AccordionSummaryContent = styled('div', { + name: 'MuiAccordionSummary', + slot: 'Content', + overridesResolver: (props, styles) => styles.content, +})(({ theme }) => ({ + display: 'flex', + flexGrow: 1, + margin: '12px 0', + variants: [ + { + props: (props) => !props.disableGutters, + style: { + transition: theme.transitions.create(['margin'], { + duration: theme.transitions.duration.shortest, + }), + [`&.${accordionSummaryClasses.expanded}`]: { + margin: '20px 0', + }, + }, + }, + ], +})); + +const AccordionSummaryExpandIconWrapper = styled('div', { + name: 'MuiAccordionSummary', + slot: 'ExpandIconWrapper', + overridesResolver: (props, styles) => styles.expandIconWrapper, +})(({ theme }) => ({ + display: 'flex', + color: (theme.vars || theme).palette.action.active, + transform: 'rotate(0deg)', + transition: theme.transitions.create('transform', { + duration: theme.transitions.duration.shortest, + }), + [`&.${accordionSummaryClasses.expanded}`]: { + transform: 'rotate(180deg)', + }, +})); + +const AccordionSummary = React.forwardRef(function AccordionSummary(inProps, ref) { + const props = useThemeProps({ props: inProps, name: 'MuiAccordionSummary' }); + const { children, className, expandIcon, focusVisibleClassName, onClick, ...other } = props; + + const { disabled = false, disableGutters, expanded, toggle } = React.useContext(AccordionContext); + const handleChange = (event) => { + if (toggle) { + toggle(event); + } + if (onClick) { + onClick(event); + } + }; + + const ownerState = { + ...props, + expanded, + disabled, + disableGutters, + }; + + const classes = useUtilityClasses(ownerState); + + return ( + + + {children} + + {expandIcon && ( + + {expandIcon} + + )} + + ); +}); + +AccordionSummary.propTypes /* remove-proptypes */ = { + // ┌────────────────────────────── Warning ──────────────────────────────┐ + // │ These PropTypes are generated from the TypeScript type definitions. │ + // │ To update them, edit the d.ts file and run `pnpm proptypes`. │ + // └─────────────────────────────────────────────────────────────────────┘ + /** + * The content of the component. + */ + children: PropTypes.node, + /** + * Override or extend the styles applied to the component. + */ + classes: PropTypes.object, + /** + * @ignore + */ + className: PropTypes.string, + /** + * The icon to display as the expand indicator. + */ + expandIcon: PropTypes.node, + /** + * This prop can help identify which element has keyboard focus. + * The class name will be applied when the element gains the focus through keyboard interaction. + * It's a polyfill for the [CSS :focus-visible selector](https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo). + * The rationale for using this feature [is explained here](/~https://github.com/WICG/focus-visible/blob/HEAD/explainer.md). + * A [polyfill can be used](/~https://github.com/WICG/focus-visible) to apply a `focus-visible` class to other components + * if needed. + */ + focusVisibleClassName: PropTypes.string, + /** + * @ignore + */ + onClick: PropTypes.func, + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), + PropTypes.func, + PropTypes.object, + ]), +}; + +export default AccordionSummary; diff --git a/packages/mui-material/src/Accordion/Accordion.js b/packages/mui-material/src/Accordion/Accordion.js index 8fa55bb490d58d..3997d62c75be3a 100644 --- a/packages/mui-material/src/Accordion/Accordion.js +++ b/packages/mui-material/src/Accordion/Accordion.js @@ -92,32 +92,35 @@ const AccordionRoot = styled(Paper, { }; }, ({ theme }) => ({ - variants: [{ - props: props => !props.square, - style: { - borderRadius: 0, - '&:first-of-type': { - borderTopLeftRadius: (theme.vars || theme).shape.borderRadius, - borderTopRightRadius: (theme.vars || theme).shape.borderRadius, - }, - '&:last-of-type': { - borderBottomLeftRadius: (theme.vars || theme).shape.borderRadius, - borderBottomRightRadius: (theme.vars || theme).shape.borderRadius, - // Fix a rendering issue on Edge - '@supports (-ms-ime-align: auto)': { - borderBottomLeftRadius: 0, - borderBottomRightRadius: 0, + variants: [ + { + props: (props) => !props.square, + style: { + borderRadius: 0, + '&:first-of-type': { + borderTopLeftRadius: (theme.vars || theme).shape.borderRadius, + borderTopRightRadius: (theme.vars || theme).shape.borderRadius, + }, + '&:last-of-type': { + borderBottomLeftRadius: (theme.vars || theme).shape.borderRadius, + borderBottomRightRadius: (theme.vars || theme).shape.borderRadius, + // Fix a rendering issue on Edge + '@supports (-ms-ime-align: auto)': { + borderBottomLeftRadius: 0, + borderBottomRightRadius: 0, + }, }, }, - } - }, { - props: props => !props.disableGutters, - style: { - [`&.${accordionClasses.expanded}`]: { - margin: '16px 0', + }, + { + props: (props) => !props.disableGutters, + style: { + [`&.${accordionClasses.expanded}`]: { + margin: '16px 0', + }, }, - } - }] + }, + ], }), ); diff --git a/packages/mui-material/src/AccordionActions/AccordionActions.js b/packages/mui-material/src/AccordionActions/AccordionActions.js index 89591473d9b53e..5bd5980b21d680 100644 --- a/packages/mui-material/src/AccordionActions/AccordionActions.js +++ b/packages/mui-material/src/AccordionActions/AccordionActions.js @@ -30,14 +30,16 @@ const AccordionActionsRoot = styled('div', { alignItems: 'center', padding: 8, justifyContent: 'flex-end', - variants: [{ - props: props => !props.disableSpacing, - style: { - '& > :not(style) ~ :not(style)': { - marginLeft: 8, + variants: [ + { + props: (props) => !props.disableSpacing, + style: { + '& > :not(style) ~ :not(style)': { + marginLeft: 8, + }, }, - } - }] + }, + ], }); const AccordionActions = React.forwardRef(function AccordionActions(inProps, ref) { diff --git a/packages/mui-material/src/AccordionSummary/AccordionSummary.js b/packages/mui-material/src/AccordionSummary/AccordionSummary.js index c9d93685929528..f4cfc07b1cd4b1 100644 --- a/packages/mui-material/src/AccordionSummary/AccordionSummary.js +++ b/packages/mui-material/src/AccordionSummary/AccordionSummary.js @@ -47,14 +47,16 @@ const AccordionSummaryRoot = styled(ButtonBase, { [`&:hover:not(.${accordionSummaryClasses.disabled})`]: { cursor: 'pointer', }, - variants: [{ - props: props => !props.disableGutters, - style: { - [`&.${accordionSummaryClasses.expanded}`]: { - minHeight: 64, + variants: [ + { + props: (props) => !props.disableGutters, + style: { + [`&.${accordionSummaryClasses.expanded}`]: { + minHeight: 64, + }, }, - } - }] + }, + ], }; }); @@ -66,17 +68,19 @@ const AccordionSummaryContent = styled('div', { display: 'flex', flexGrow: 1, margin: '12px 0', - variants: [{ - props: props => !props.disableGutters, - style: { - transition: theme.transitions.create(['margin'], { - duration: theme.transitions.duration.shortest, - }), - [`&.${accordionSummaryClasses.expanded}`]: { - margin: '20px 0', + variants: [ + { + props: (props) => !props.disableGutters, + style: { + transition: theme.transitions.create(['margin'], { + duration: theme.transitions.duration.shortest, + }), + [`&.${accordionSummaryClasses.expanded}`]: { + margin: '20px 0', + }, }, - } - }] + }, + ], })); const AccordionSummaryExpandIconWrapper = styled('div', {