Skip to content

Commit

Permalink
feat(motion): Collapse orientation param & horizontal implementation (
Browse files Browse the repository at this point in the history
  • Loading branch information
robertpenner authored Oct 14, 2024
1 parent 04dd27b commit 46cdc69
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "feat: add Collapse orientation parameter & horizontal implementation",
"packageName": "@fluentui/react-motion-components-preview",
"email": "robertpenner@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { motionTokens, createPresenceComponent } from '@fluentui/react-motion';
import type { PresenceMotionFnCreator } from '../../types';

type CollapseOrientation = 'horizontal' | 'vertical';

type CollapseVariantParams = {
/** Time (ms) for the enter transition (expand). Defaults to the `durationNormal` value (200 ms). */
enterDuration?: number;
Expand All @@ -18,6 +20,9 @@ type CollapseVariantParams = {
type CollapseRuntimeParams = {
/** Whether to animate the opacity. Defaults to `true`. */
animateOpacity?: boolean;

/** The orientation of the size animation. Defaults to `'vertical'` to expand/collapse the height. */
orientation?: CollapseOrientation;
};

/** Define a presence motion for collapse/expand */
Expand All @@ -28,25 +33,29 @@ export const createCollapsePresence: PresenceMotionFnCreator<CollapseVariantPara
exitDuration = enterDuration,
exitEasing = enterEasing,
} = {}) =>
({ element, animateOpacity = true }) => {
({ element, animateOpacity = true, orientation = 'vertical' }) => {
// TODO: don't change opacity at all if animateOpacity is false
const fromOpacity = animateOpacity ? 0 : 1;
const toOpacity = 1;
const fromHeight = '0'; // Could be a custom param in the future to start partially expanded
const toHeight = `${element.scrollHeight}px`;
const overflow = 'hidden';
const fromSize = '0'; // Could be a custom param in the future to start with partially expanded width or height
const measuredSize = orientation === 'horizontal' ? element.scrollWidth : element.scrollHeight;
const toSize = `${measuredSize}px`;
// use generic names for size and overflow, handling vertical or horizontal orientation
const sizeName = orientation === 'horizontal' ? 'maxWidth' : 'maxHeight';
const overflowName = orientation === 'horizontal' ? 'overflowX' : 'overflowY';

const enterKeyframes = [
{ opacity: fromOpacity, maxHeight: fromHeight, overflow },
{ opacity: fromOpacity, [sizeName]: fromSize, [overflowName]: 'hidden' },
// Transition to the height of the content, at 99.99% of the duration.
{ opacity: toOpacity, maxHeight: toHeight, offset: 0.9999, overflow },
{ opacity: toOpacity, [sizeName]: toSize, offset: 0.9999, [overflowName]: 'hidden' },
// On completion, remove the maxHeight because the content might need to expand later.
// This extra keyframe is simpler than firing a callback on completion.
{ opacity: toOpacity, maxHeight: 'unset', overflow },
{ opacity: toOpacity, [sizeName]: 'unset', [overflowName]: 'hidden' },
];

const exitKeyframes = [
{ opacity: toOpacity, maxHeight: toHeight, overflow },
{ opacity: fromOpacity, maxHeight: fromHeight, overflow },
{ opacity: toOpacity, [sizeName]: toSize, [overflowName]: 'hidden' },
{ opacity: fromOpacity, [sizeName]: fromSize, [overflowName]: 'hidden' },
];

return {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
The `Collapse` component manages content presence, using a height expand/collapse motion.
The `Collapse` component manages content presence, using a height or width expand/collapse motion.

> **⚠️ Preview components are considered unstable**
Expand All @@ -8,7 +8,7 @@ import { Collapse } from '@fluentui/react-motion-components-preview';
function Component({ visible }) {
return (
<Collapse visible={visible}>
<div style={{ background: 'lightblue' }}>Content</div>
<div>Content</div>
</Collapse>
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
For a horizontal `Collapse`, set the `orientation` parameter:

```tsx
<Collapse orientation="horizontal" ...>
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { Field, makeStyles, tokens, Switch } from '@fluentui/react-components';
import { Collapse } from '@fluentui/react-motion-components-preview';
import * as React from 'react';

import description from './CollapseHorizontal.stories.md';

const useClasses = makeStyles({
container: {},
sideContent: {
background: 'lightgrey',
padding: '50px',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
},
card: {
gridArea: 'card',
padding: '20px',
width: '300px',
},
controls: {
display: 'grid',
gridTemplateColumns: '1fr 3fr',
justifyContent: 'start',
gridArea: 'controls',
border: `${tokens.strokeWidthThicker} solid ${tokens.colorNeutralForeground3}`,
borderRadius: tokens.borderRadiusMedium,
boxShadow: tokens.shadow16,
padding: '10px',
},
field: {
flex: 1,
},
});

const LoremIpsum = () => (
<>
{'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. '.repeat(
3,
)}
</>
);

export const Horizontal = () => {
const classes = useClasses();
const [visible, setVisible] = React.useState<boolean>(false);

return (
<div className={classes.container}>
<div className={classes.controls}>
<Field className={classes.field}>
<Switch label="Visible" checked={visible} onChange={() => setVisible(v => !v)} />
</Field>
</div>

<div style={{ display: 'flex' }}>
<Collapse visible={visible} orientation="horizontal">
{/* Wrapper div to make the collapse crop the card without reflowing the text. */}
<div>
<div className={classes.card}>
<LoremIpsum />
</div>
</div>
</Collapse>
<div className={classes.sideContent}>[side content]</div>
</div>
</div>
);
};

Horizontal.parameters = {
docs: {
description: {
story: description,
},
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Collapse } from '@fluentui/react-motion-components-preview';
import CollapseDescription from './CollapseDescription.md';

export { Default } from './CollapseDefault.stories';
export { Horizontal } from './CollapseHorizontal.stories';
export { Snappy } from './CollapseSnappy.stories';
export { Exaggerated } from './CollapseExaggerated.stories';
export { Customization } from './CollapseCustomization.stories';
Expand Down

0 comments on commit 46cdc69

Please sign in to comment.