From 46cdc69c21ecfe816511bec739f2fd11d4892e59 Mon Sep 17 00:00:00 2001 From: Robert Penner Date: Mon, 14 Oct 2024 14:29:31 -0400 Subject: [PATCH] feat(motion): Collapse `orientation` param & horizontal implementation (#32998) --- ...-e49e73e0-27c3-4285-baf8-2ca1d7fc8efd.json | 7 ++ .../src/components/Collapse/Collapse.ts | 27 ++++--- .../src/Collapse/CollapseDescription.md | 4 +- .../Collapse/CollapseHorizontal.stories.md | 5 ++ .../Collapse/CollapseHorizontal.stories.tsx | 77 +++++++++++++++++++ .../stories/src/Collapse/index.stories.ts | 1 + 6 files changed, 110 insertions(+), 11 deletions(-) create mode 100644 change/@fluentui-react-motion-components-preview-e49e73e0-27c3-4285-baf8-2ca1d7fc8efd.json create mode 100644 packages/react-components/react-motion-components-preview/stories/src/Collapse/CollapseHorizontal.stories.md create mode 100644 packages/react-components/react-motion-components-preview/stories/src/Collapse/CollapseHorizontal.stories.tsx diff --git a/change/@fluentui-react-motion-components-preview-e49e73e0-27c3-4285-baf8-2ca1d7fc8efd.json b/change/@fluentui-react-motion-components-preview-e49e73e0-27c3-4285-baf8-2ca1d7fc8efd.json new file mode 100644 index 00000000000000..6ce790a236bdb1 --- /dev/null +++ b/change/@fluentui-react-motion-components-preview-e49e73e0-27c3-4285-baf8-2ca1d7fc8efd.json @@ -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" +} diff --git a/packages/react-components/react-motion-components-preview/library/src/components/Collapse/Collapse.ts b/packages/react-components/react-motion-components-preview/library/src/components/Collapse/Collapse.ts index 03dafaf0a61377..e71dce8c8ca707 100644 --- a/packages/react-components/react-motion-components-preview/library/src/components/Collapse/Collapse.ts +++ b/packages/react-components/react-motion-components-preview/library/src/components/Collapse/Collapse.ts @@ -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; @@ -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 */ @@ -28,25 +33,29 @@ export const createCollapsePresence: PresenceMotionFnCreator - ({ 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 { diff --git a/packages/react-components/react-motion-components-preview/stories/src/Collapse/CollapseDescription.md b/packages/react-components/react-motion-components-preview/stories/src/Collapse/CollapseDescription.md index f7b09dd2516556..c369204684d3ca 100644 --- a/packages/react-components/react-motion-components-preview/stories/src/Collapse/CollapseDescription.md +++ b/packages/react-components/react-motion-components-preview/stories/src/Collapse/CollapseDescription.md @@ -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** @@ -8,7 +8,7 @@ import { Collapse } from '@fluentui/react-motion-components-preview'; function Component({ visible }) { return ( -
Content
+
Content
); } diff --git a/packages/react-components/react-motion-components-preview/stories/src/Collapse/CollapseHorizontal.stories.md b/packages/react-components/react-motion-components-preview/stories/src/Collapse/CollapseHorizontal.stories.md new file mode 100644 index 00000000000000..b2d9ce43d27661 --- /dev/null +++ b/packages/react-components/react-motion-components-preview/stories/src/Collapse/CollapseHorizontal.stories.md @@ -0,0 +1,5 @@ +For a horizontal `Collapse`, set the `orientation` parameter: + +```tsx + +``` diff --git a/packages/react-components/react-motion-components-preview/stories/src/Collapse/CollapseHorizontal.stories.tsx b/packages/react-components/react-motion-components-preview/stories/src/Collapse/CollapseHorizontal.stories.tsx new file mode 100644 index 00000000000000..cf596e1140c7f8 --- /dev/null +++ b/packages/react-components/react-motion-components-preview/stories/src/Collapse/CollapseHorizontal.stories.tsx @@ -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(false); + + return ( +
+
+ + setVisible(v => !v)} /> + +
+ +
+ + {/* Wrapper div to make the collapse crop the card without reflowing the text. */} +
+
+ +
+
+
+
[side content]
+
+
+ ); +}; + +Horizontal.parameters = { + docs: { + description: { + story: description, + }, + }, +}; diff --git a/packages/react-components/react-motion-components-preview/stories/src/Collapse/index.stories.ts b/packages/react-components/react-motion-components-preview/stories/src/Collapse/index.stories.ts index ec8df42be470d4..1837c53175eb12 100644 --- a/packages/react-components/react-motion-components-preview/stories/src/Collapse/index.stories.ts +++ b/packages/react-components/react-motion-components-preview/stories/src/Collapse/index.stories.ts @@ -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';