From a259627e85837f3e9545cef1b3a306f6c16a349a Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Sat, 3 Jun 2023 00:42:51 +0200 Subject: [PATCH 001/111] feat: implement motion for Drawer --- .../react-drawer/etc/react-drawer.api.md | 16 +-- .../DrawerInline/DrawerInline.types.ts | 7 +- .../DrawerInline/renderDrawerInline.tsx | 2 +- .../DrawerInline/useDrawerInline.ts | 12 +- .../useDrawerInlineStyles.styles.ts | 27 ++++- .../DrawerOverlay/DrawerOverlay.types.ts | 7 +- .../DrawerOverlay/useDrawerOverlay.ts | 13 ++- .../useDrawerOverlayStyles.styles.ts | 22 +++- .../react-drawer/src/util/DrawerBase.types.ts | 24 +++- .../src/util/getDefaultDrawerProps.ts | 4 +- .../src/util/useDrawerBaseStyles.styles.ts | 41 ++++++- .../react-drawer/src/util/useDrawerRef.ts | 8 ++ .../react-drawer/src/util/usePresenceState.ts | 110 ++++++++++++++++++ 13 files changed, 256 insertions(+), 37 deletions(-) create mode 100644 packages/react-components/react-drawer/src/util/useDrawerRef.ts create mode 100644 packages/react-components/react-drawer/src/util/usePresenceState.ts diff --git a/packages/react-components/react-drawer/etc/react-drawer.api.md b/packages/react-components/react-drawer/etc/react-drawer.api.md index 44c1d47ba968b4..4371f4a2ae5f70 100644 --- a/packages/react-components/react-drawer/etc/react-drawer.api.md +++ b/packages/react-components/react-drawer/etc/react-drawer.api.md @@ -6,15 +6,7 @@ /// -import type { ComponentProps } from '@fluentui/react-utilities'; -import type { ComponentState } from '@fluentui/react-utilities'; -import { DialogProps } from '@fluentui/react-dialog'; -import { DialogSurfaceProps } from '@fluentui/react-dialog'; -import { DialogTitleSlots } from '@fluentui/react-dialog'; -import type { ForwardRefComponent } from '@fluentui/react-utilities'; import * as React_2 from 'react'; -import type { Slot } from '@fluentui/react-utilities'; -import type { SlotClassNames } from '@fluentui/react-utilities'; // @public export const Drawer: ForwardRefComponent; @@ -115,7 +107,7 @@ export const DrawerInline: ForwardRefComponent; export const drawerInlineClassNames: SlotClassNames; // @public -export type DrawerInlineProps = ComponentProps & DrawerBaseTypes & { +export type DrawerInlineProps = ComponentProps & DrawerBaseProps & { separator?: boolean; }; @@ -125,7 +117,7 @@ export type DrawerInlineSlots = { }; // @public -export type DrawerInlineState = ComponentState & DrawerBaseTypes & Pick; +export type DrawerInlineState = ComponentState & DrawerBaseProps & DrawerBaseState & Pick; // @public export const DrawerOverlay: ForwardRefComponent; @@ -134,7 +126,7 @@ export const DrawerOverlay: ForwardRefComponent; export const drawerOverlayClassNames: SlotClassNames; // @public -export type DrawerOverlayProps = ComponentProps & DrawerBaseTypes & Pick; +export type DrawerOverlayProps = ComponentProps & DrawerBaseProps & Pick; // @public (undocumented) export type DrawerOverlaySlots = { @@ -142,7 +134,7 @@ export type DrawerOverlaySlots = { }; // @public -export type DrawerOverlayState = ComponentState & DrawerBaseTypes & { +export type DrawerOverlayState = ComponentState & DrawerBaseProps & DrawerBaseState & { dialog: DialogProps; }; diff --git a/packages/react-components/react-drawer/src/components/DrawerInline/DrawerInline.types.ts b/packages/react-components/react-drawer/src/components/DrawerInline/DrawerInline.types.ts index 38457435b5fda4..230d7c109d0dfc 100644 --- a/packages/react-components/react-drawer/src/components/DrawerInline/DrawerInline.types.ts +++ b/packages/react-components/react-drawer/src/components/DrawerInline/DrawerInline.types.ts @@ -1,5 +1,5 @@ import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities'; -import { DrawerBaseTypes } from '../../util/DrawerBase.types'; +import { DrawerBaseProps, DrawerBaseState } from '../../util/DrawerBase.types'; export type DrawerInlineSlots = { root: Slot<'div'>; @@ -9,7 +9,7 @@ export type DrawerInlineSlots = { * DrawerInline Props */ export type DrawerInlineProps = ComponentProps & - DrawerBaseTypes & { + DrawerBaseProps & { /** * Whether the drawer has a separator line. * @@ -22,5 +22,6 @@ export type DrawerInlineProps = ComponentProps & * State used in rendering DrawerInline */ export type DrawerInlineState = ComponentState & - DrawerBaseTypes & + DrawerBaseProps & + DrawerBaseState & Pick; diff --git a/packages/react-components/react-drawer/src/components/DrawerInline/renderDrawerInline.tsx b/packages/react-components/react-drawer/src/components/DrawerInline/renderDrawerInline.tsx index 22dc8d0a3c5099..b36f034fac0c70 100644 --- a/packages/react-components/react-drawer/src/components/DrawerInline/renderDrawerInline.tsx +++ b/packages/react-components/react-drawer/src/components/DrawerInline/renderDrawerInline.tsx @@ -8,7 +8,7 @@ import type { DrawerInlineState, DrawerInlineSlots } from './DrawerInline.types' export const renderDrawerInline_unstable = (state: DrawerInlineState) => { const { slots, slotProps } = getSlots(state); - if (!state.open) { + if (!state.rendered) { return null; } diff --git a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts index 34ea024fe5fdd2..7e16928361c6e4 100644 --- a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts +++ b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts @@ -2,6 +2,8 @@ import * as React from 'react'; import { getNativeElementProps, useControllableState } from '@fluentui/react-utilities'; import type { DrawerInlineProps, DrawerInlineState } from './DrawerInline.types'; import { getDefaultDrawerProps } from '../../util/getDefaultDrawerProps'; +import usePresenceState from '../../util/usePresenceState'; +import useDrawerRef from '../../util/useDrawerRef'; /** * Create the state required to render DrawerInline. @@ -22,13 +24,16 @@ export const useDrawerInline_unstable = (props: DrawerInlineProps, ref: React.Re initialState: false, }); + const drawerRef = useDrawerRef(ref); + const { rendered, mounted, entering, exiting } = usePresenceState(open, drawerRef); + return { components: { root: 'div', }, root: getNativeElementProps('div', { - ref, + ref: drawerRef, ...props, }), @@ -36,5 +41,10 @@ export const useDrawerInline_unstable = (props: DrawerInlineProps, ref: React.Re position, open, separator, + + rendered, + mounted, + entering, + exiting, }; }; diff --git a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts index a2ed418f27d865..2ad5cf7fe92b9e 100644 --- a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts +++ b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts @@ -2,7 +2,7 @@ import * as React from 'react'; import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; import type { DrawerInlineSlots, DrawerInlineState } from './DrawerInline.types'; import type { SlotClassNames } from '@fluentui/react-utilities'; -import { useDrawerBaseStyles } from '../../util/useDrawerBaseStyles.styles'; +import { getDrawerBaseClassNames, useDrawerBaseStyles } from '../../util/useDrawerBaseStyles.styles'; import { tokens } from '@fluentui/react-theme'; export const drawerInlineClassNames: SlotClassNames = { @@ -24,6 +24,26 @@ const useStyles = makeStyles({ separatorRight: { ...shorthands.borderLeft('1px', 'solid', tokens.colorNeutralBackground3), }, + + /* Positioning */ + left: { + marginLeft: 'calc(var(--fui-Drawer--size) * -1)', + transitionProperty: 'margin-left', + willChange: 'margin-left', + }, + right: { + marginRight: 'calc(var(--fui-Drawer--size) * -1)', + transitionProperty: 'margin-right', + willChange: 'margin-right', + }, + + /* Mounted */ + mountedLeft: { + marginLeft: 0, + }, + mountedRight: { + marginRight: 0, + }, }); /** @@ -45,8 +65,9 @@ export const useDrawerInlineStyles_unstable = (state: DrawerInlineState): Drawer drawerInlineClassNames.root, baseStyles.root, styles.root, - state.size && baseStyles[state.size], - state.position && baseStyles[state.position], + getDrawerBaseClassNames(state, baseStyles), + state.position && styles[state.position], + state.mounted && (state.position === 'left' ? styles.mountedLeft : styles.mountedRight), separatorClass, state.root.className, ); diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/DrawerOverlay.types.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/DrawerOverlay.types.ts index 863a8cd8776d0f..3390e2eac2e212 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/DrawerOverlay.types.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/DrawerOverlay.types.ts @@ -1,6 +1,6 @@ import { DialogProps, DialogSurfaceProps } from '@fluentui/react-dialog'; import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities'; -import { DrawerBaseTypes } from '../../util/DrawerBase.types'; +import { DrawerBaseProps, DrawerBaseState } from '../../util/DrawerBase.types'; export type DrawerOverlaySlots = { root: Slot; @@ -10,13 +10,14 @@ export type DrawerOverlaySlots = { * DrawerOverlay Props */ export type DrawerOverlayProps = ComponentProps & - DrawerBaseTypes & + DrawerBaseProps & Pick; /** * State used in rendering DrawerOverlay */ export type DrawerOverlayState = ComponentState & - DrawerBaseTypes & { + DrawerBaseProps & + DrawerBaseState & { dialog: DialogProps; }; diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts index 8f13d0447a4e5a..8aab3ad96bda8f 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts @@ -3,6 +3,8 @@ import { getNativeElementProps } from '@fluentui/react-utilities'; import type { DrawerOverlayProps, DrawerOverlayState } from './DrawerOverlay.types'; import { DialogProps, DialogSurface } from '@fluentui/react-dialog'; import { getDefaultDrawerProps } from '../../util/getDefaultDrawerProps'; +import usePresenceState from '../../util/usePresenceState'; +import useDrawerRef from '../../util/useDrawerRef'; /** * Create the state required to render DrawerOverlay. @@ -20,17 +22,20 @@ export const useDrawerOverlay_unstable = ( const { open, defaultOpen, size, position } = getDefaultDrawerProps(props); const { modalType = 'modal', inertTrapFocus, onOpenChange } = props; + const drawerRef = useDrawerRef(ref); + const { rendered, mounted, entering, exiting } = usePresenceState(open, drawerRef); + return { components: { root: DialogSurface, }, root: getNativeElementProps('div', { - ref, + ref: drawerRef, ...props, }), dialog: { - open, + open: rendered, defaultOpen, onOpenChange, inertTrapFocus, @@ -39,5 +44,9 @@ export const useDrawerOverlay_unstable = ( size, position, + rendered, + mounted, + entering, + exiting, }; }; diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts index ca1366036dac56..c11b3205f3c6f5 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts @@ -1,7 +1,7 @@ import { makeStyles, mergeClasses } from '@griffel/react'; import type { DrawerOverlaySlots, DrawerOverlayState } from './DrawerOverlay.types'; import type { SlotClassNames } from '@fluentui/react-utilities'; -import { useDrawerBaseStyles } from '../../util/useDrawerBaseStyles.styles'; +import { getDrawerBaseClassNames, useDrawerBaseStyles } from '../../util/useDrawerBaseStyles.styles'; export const drawerOverlayClassNames: SlotClassNames = { root: 'fui-DrawerOverlay', @@ -15,6 +15,21 @@ const useStyles = makeStyles({ position: 'fixed', top: 0, bottom: 0, + transitionProperty: 'transform', + willChange: 'transform', + }, + + /* Positioning */ + left: { + transform: 'translate3D(calc(var(--fui-Drawer--size) * -1), 0, 0)', + }, + right: { + transform: 'translate3D(calc(var(--fui-Drawer--size) * 1), 0, 0)', + }, + + /* Mounted */ + mounted: { + transform: 'translate3D(0, 0, 0)', }, }); @@ -29,8 +44,9 @@ export const useDrawerOverlayStyles_unstable = (state: DrawerOverlayState): Draw drawerOverlayClassNames.root, baseStyles.root, styles.root, - state.size && baseStyles[state.size], - state.position && baseStyles[state.position], + getDrawerBaseClassNames(state, baseStyles), + state.position && styles[state.position], + state.mounted && styles.mounted, state.root.className, ); diff --git a/packages/react-components/react-drawer/src/util/DrawerBase.types.ts b/packages/react-components/react-drawer/src/util/DrawerBase.types.ts index fbfc6246092a99..673db5fa8bd7f6 100644 --- a/packages/react-components/react-drawer/src/util/DrawerBase.types.ts +++ b/packages/react-components/react-drawer/src/util/DrawerBase.types.ts @@ -1,4 +1,4 @@ -export type DrawerBaseTypes = { +export type DrawerBaseProps = { /** * Position of the drawer. * @@ -32,3 +32,25 @@ export type DrawerBaseTypes = { */ defaultOpen?: boolean; }; + +export type DrawerBaseState = { + /** + * Whether the drawer is rendered but not yet mounted. + * */ + rendered: boolean; + + /** + * Whether the drawer is rendered and already mounted. + * */ + mounted: boolean; + + /** + * Whether the drawer is entering the screen. + * */ + entering: boolean; + + /** + * Whether the drawer is exiting the screen. + * */ + exiting: boolean; +}; diff --git a/packages/react-components/react-drawer/src/util/getDefaultDrawerProps.ts b/packages/react-components/react-drawer/src/util/getDefaultDrawerProps.ts index be958c60d828a5..af835f0465ff97 100644 --- a/packages/react-components/react-drawer/src/util/getDefaultDrawerProps.ts +++ b/packages/react-components/react-drawer/src/util/getDefaultDrawerProps.ts @@ -1,6 +1,6 @@ -import { DrawerBaseTypes } from './DrawerBase.types'; +import { DrawerBaseProps } from './DrawerBase.types'; -export function getDefaultDrawerProps(props: DrawerBaseTypes) { +export function getDefaultDrawerProps(props: DrawerBaseProps) { const { open = false, defaultOpen = false, size = 'small', position = 'left' } = props; return { diff --git a/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts b/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts index 61dcb0da817c05..e8bc0934abb668 100644 --- a/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts +++ b/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts @@ -1,5 +1,13 @@ -import { makeStyles, shorthands } from '@griffel/react'; +import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; import { tokens } from '@fluentui/react-theme'; +import { DrawerBaseProps, DrawerBaseState } from './DrawerBase.types'; + +/** + * CSS variable names used internally for uniform styling in Drawer. + */ +export const drawerCSSVars = { + drawerSizeVar: '--fui-Drawer--size', +}; /** * Styles for the root slot @@ -11,6 +19,7 @@ export const useDrawerBaseStyles = makeStyles({ ...shorthands.borderRadius(0), ...shorthands.border(0), + width: `var(${drawerCSSVars.drawerSizeVar})`, maxWidth: '100vw', height: 'auto', boxSizing: 'border-box', @@ -19,6 +28,7 @@ export const useDrawerBaseStyles = makeStyles({ alignItems: 'flex-start', justifyContent: 'flex-start', backgroundColor: tokens.colorNeutralBackground1, + transitionDuration: tokens.durationNormal, }, /* Positioning */ @@ -31,18 +41,37 @@ export const useDrawerBaseStyles = makeStyles({ left: 'auto', }, + /* Motion */ + entering: { + transitionTimingFunction: tokens.curveDecelerateMid, + }, + exiting: { + transitionTimingFunction: tokens.curveAccelerateMin, + }, + /* Sizes */ small: { - width: '320px', + [drawerCSSVars.drawerSizeVar]: '320px', }, medium: { - width: '592px', + [drawerCSSVars.drawerSizeVar]: '592px', }, large: { - width: '940px', + [drawerCSSVars.drawerSizeVar]: '940px', }, full: { - width: '100vw', - maxWidth: '100vw', + [drawerCSSVars.drawerSizeVar]: '100vw', }, }); + +export const getDrawerBaseClassNames = ( + { position, size, entering, exiting }: Partial, + baseStyles: ReturnType, +) => { + return mergeClasses( + position && baseStyles[position], + size && baseStyles[size], + entering && baseStyles.entering, + exiting && baseStyles.exiting, + ); +}; diff --git a/packages/react-components/react-drawer/src/util/useDrawerRef.ts b/packages/react-components/react-drawer/src/util/useDrawerRef.ts new file mode 100644 index 00000000000000..ebca4488a2c4f2 --- /dev/null +++ b/packages/react-components/react-drawer/src/util/useDrawerRef.ts @@ -0,0 +1,8 @@ +import * as React from 'react'; +import { useMergedRefs } from '@fluentui/react-utilities'; + +const useDrawerRef = (ref: React.Ref) => { + return useMergedRefs(ref, React.useRef(null)); +}; + +export default useDrawerRef; diff --git a/packages/react-components/react-drawer/src/util/usePresenceState.ts b/packages/react-components/react-drawer/src/util/usePresenceState.ts new file mode 100644 index 00000000000000..9f12a3cca3790c --- /dev/null +++ b/packages/react-components/react-drawer/src/util/usePresenceState.ts @@ -0,0 +1,110 @@ +import * as React from 'react'; +import { useIsomorphicLayoutEffect } from '@fluentui/react-utilities'; + +type UsePresenceStateStore = { + rendered: boolean; + mounted: boolean; + entering: boolean; + exiting: boolean; +}; + +type UsePresenceState = UsePresenceStateStore & { + animating: boolean; +}; + +const usePresenceState = (open: boolean, ref: React.RefObject): UsePresenceState => { + const [hasFinishedStart, setHasFinishedStart] = React.useState(false); + + const [presenceState, setPresenceState] = React.useState({ + rendered: open, + mounted: false, + entering: false, + exiting: false, + }); + const animating = React.useMemo(() => presenceState.entering || presenceState.exiting, [presenceState]); + + const onStart = React.useCallback( + event => { + if (event.target !== ref.current) { + return; + } + + setPresenceState(prevState => ({ + ...prevState, + entering: open, + exiting: !open, + })); + setHasFinishedStart(true); + }, + [open, ref], + ); + + const onEnd = React.useCallback( + event => { + if (event.target !== ref.current) { + return; + } + + setPresenceState({ + entering: false, + exiting: false, + rendered: open, + mounted: open, + }); + }, + [open, ref], + ); + + useIsomorphicLayoutEffect(() => { + let currentRef: HTMLElement; + + if (open) { + setPresenceState(prevState => ({ + ...prevState, + rendered: true, + })); + } else if (!hasFinishedStart) { + setPresenceState(prevState => ({ + ...prevState, + rendered: false, + })); + setHasFinishedStart(false); + } + + const animationFrame = requestAnimationFrame(() => { + if (ref && ref.current) { + currentRef = ref.current; + + ref.current.addEventListener('transitionstart', onStart); + ref.current.addEventListener('animationstart', onStart); + ref.current.addEventListener('transitionend', onEnd); + ref.current.addEventListener('animationend', onEnd); + } + + setPresenceState(prevState => ({ + ...prevState, + mounted: open, + })); + }); + + return () => { + cancelAnimationFrame(animationFrame); + + if (!currentRef) { + return; + } + + currentRef.removeEventListener('transitionstart', onStart); + currentRef.removeEventListener('animationstart', onStart); + currentRef.removeEventListener('transitionend', onEnd); + currentRef.removeEventListener('animationend', onEnd); + }; + }, [hasFinishedStart, onEnd, onStart, open, ref]); + + return { + ...presenceState, + animating, + }; +}; + +export default usePresenceState; From a536462dd30dac1911606071bee9168c7022ba38 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Sun, 4 Jun 2023 14:48:34 +0200 Subject: [PATCH 002/111] feat: add events based on presence state --- .../DrawerInline/useDrawerInline.ts | 2 +- .../DrawerOverlay/useDrawerOverlay.ts | 2 +- .../react-drawer/src/util/usePresenceState.ts | 33 +++++++++++++++++-- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts index 7e16928361c6e4..9d5cdcd5d215bd 100644 --- a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts +++ b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts @@ -25,7 +25,7 @@ export const useDrawerInline_unstable = (props: DrawerInlineProps, ref: React.Re }); const drawerRef = useDrawerRef(ref); - const { rendered, mounted, entering, exiting } = usePresenceState(open, drawerRef); + const { rendered, mounted, entering, exiting } = usePresenceState(drawerRef, open); return { components: { diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts index 8aab3ad96bda8f..1d534fa948520f 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts @@ -23,7 +23,7 @@ export const useDrawerOverlay_unstable = ( const { modalType = 'modal', inertTrapFocus, onOpenChange } = props; const drawerRef = useDrawerRef(ref); - const { rendered, mounted, entering, exiting } = usePresenceState(open, drawerRef); + const { rendered, mounted, entering, exiting } = usePresenceState(drawerRef, open); return { components: { diff --git a/packages/react-components/react-drawer/src/util/usePresenceState.ts b/packages/react-components/react-drawer/src/util/usePresenceState.ts index 9f12a3cca3790c..5659c49ab28ab5 100644 --- a/packages/react-components/react-drawer/src/util/usePresenceState.ts +++ b/packages/react-components/react-drawer/src/util/usePresenceState.ts @@ -12,7 +12,22 @@ type UsePresenceState = UsePresenceStateStore & { animating: boolean; }; -const usePresenceState = (open: boolean, ref: React.RefObject): UsePresenceState => { +type UsePresenceStateOptions = { + onEnter?: () => void; + onEntered?: () => void; + onExit?: () => void; + onExited?: () => void; +}; + +const noop = () => ({}); + +const usePresenceState = ( + ref: React.RefObject, + open: boolean, + options?: UsePresenceStateOptions, +): UsePresenceState => { + const { onEnter = noop, onEntered = noop, onExit = noop, onExited = noop } = options || {}; + const [hasFinishedStart, setHasFinishedStart] = React.useState(false); const [presenceState, setPresenceState] = React.useState({ @@ -35,8 +50,14 @@ const usePresenceState = (open: boolean, ref: React.RefObject): Use exiting: !open, })); setHasFinishedStart(true); + + if (open) { + onEnter(); + } else { + onExit(); + } }, - [open, ref], + [onEnter, onExit, open, ref], ); const onEnd = React.useCallback( @@ -51,8 +72,14 @@ const usePresenceState = (open: boolean, ref: React.RefObject): Use rendered: open, mounted: open, }); + + if (open) { + onEntered(); + } else { + onExited(); + } }, - [open, ref], + [onEntered, onExited, open, ref], ); useIsomorphicLayoutEffect(() => { From 77ca96d98e53f8821d4842e8d98d6d1c88aea538 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Sun, 4 Jun 2023 15:14:34 +0200 Subject: [PATCH 003/111] docs: create motion documentation --- .../Drawer/DrawerCustomTransition.stories.tsx | 45 +++++++++++++++++++ .../DrawerDisabledTransition.stories.tsx | 45 +++++++++++++++++++ .../stories/Drawer/index.stories.tsx | 6 ++- 3 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx create mode 100644 packages/react-components/react-drawer/stories/Drawer/DrawerDisabledTransition.stories.tsx diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx new file mode 100644 index 00000000000000..a723391026095f --- /dev/null +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx @@ -0,0 +1,45 @@ +import * as React from 'react'; +import { Drawer, DrawerBody, DrawerHeader, DrawerHeaderTitle } from '@fluentui/react-drawer'; +import { Button, makeStyles } from '@fluentui/react-components'; +import { Dismiss24Regular } from '@fluentui/react-icons'; + +const useStyles = makeStyles({ + drawer: { + transitionDuration: '1000ms', + }, +}); + +export const CustomTransition = () => { + const styles = useStyles(); + + const [isOpen, setIsOpen] = React.useState(false); + + return ( +
+ setIsOpen(open)}> + + } + onClick={() => setIsOpen(false)} + /> + } + > + Drawer with custom transition + + + + +

Drawer content

+
+
+ + +
+ ); +}; diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerDisabledTransition.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerDisabledTransition.stories.tsx new file mode 100644 index 00000000000000..7b3e3c0187b672 --- /dev/null +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerDisabledTransition.stories.tsx @@ -0,0 +1,45 @@ +import * as React from 'react'; +import { Drawer, DrawerBody, DrawerHeader, DrawerHeaderTitle } from '@fluentui/react-drawer'; +import { Button, makeStyles } from '@fluentui/react-components'; +import { Dismiss24Regular } from '@fluentui/react-icons'; + +const useStyles = makeStyles({ + drawer: { + transitionDuration: '0ms', + }, +}); + +export const DisabledTransition = () => { + const styles = useStyles(); + + const [isOpen, setIsOpen] = React.useState(false); + + return ( +
+ setIsOpen(open)}> + + } + onClick={() => setIsOpen(false)} + /> + } + > + Drawer with no transition + + + + +

Drawer content

+
+
+ + +
+ ); +}; diff --git a/packages/react-components/react-drawer/stories/Drawer/index.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/index.stories.tsx index af8ffa40657142..f2fe5c915e0df8 100644 --- a/packages/react-components/react-drawer/stories/Drawer/index.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/index.stories.tsx @@ -18,10 +18,12 @@ export { Position } from './DrawerPosition.stories'; export { Size } from './DrawerSize.stories'; export { CustomSize } from './DrawerCustomSize.stories'; export { Separator } from './DrawerSeparator.stories'; -export { AlwaysOpen } from './DrawerAlwaysOpen.stories'; -export { PreventClose } from './DrawerPreventClose.stories'; export { WithNavigation } from './DrawerWithNavigation.stories'; export { WithScroll } from './DrawerWithScroll.stories'; +export { DisabledTransition } from './DrawerDisabledTransition.stories'; +export { CustomTransition } from './DrawerCustomTransition.stories'; +export { AlwaysOpen } from './DrawerAlwaysOpen.stories'; +export { PreventClose } from './DrawerPreventClose.stories'; export { Responsive } from './DrawerResponsive.stories'; export { Resizable } from './DrawerResizable.stories'; From 8a885d17205b8be84edaf3feb688a601d6bdb1d2 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Sun, 4 Jun 2023 15:27:25 +0200 Subject: [PATCH 004/111] docs: add custom motion docs --- .../react-drawer/src/index.ts | 2 ++ .../Drawer/DrawerCustomTransition.stories.tsx | 28 ++++++++++++++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/packages/react-components/react-drawer/src/index.ts b/packages/react-components/react-drawer/src/index.ts index d7ea7456d8d59a..24a4b54e9438fa 100644 --- a/packages/react-components/react-drawer/src/index.ts +++ b/packages/react-components/react-drawer/src/index.ts @@ -67,3 +67,5 @@ export { useDrawerFooter_unstable, } from './DrawerFooter'; export type { DrawerFooterProps, DrawerFooterSlots, DrawerFooterState } from './DrawerFooter'; + +export { default as usePresenceState } from './util/usePresenceState'; diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx index a723391026095f..5d36e5010a1454 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx @@ -1,11 +1,24 @@ import * as React from 'react'; -import { Drawer, DrawerBody, DrawerHeader, DrawerHeaderTitle } from '@fluentui/react-drawer'; -import { Button, makeStyles } from '@fluentui/react-components'; +import { Drawer, DrawerBody, DrawerHeader, DrawerHeaderTitle, usePresenceState } from '@fluentui/react-drawer'; +import { Button, makeStyles, mergeClasses, shorthands, tokens } from '@fluentui/react-components'; import { Dismiss24Regular } from '@fluentui/react-icons'; const useStyles = makeStyles({ drawer: { - transitionDuration: '1000ms', + ...shorthands.borderRadius('36px'), + + opacity: 0, + transform: 'translate3D(-100%, 0, 0) scale(0.9)', + transitionDuration: tokens.durationSlower, + transitionProperty: 'opacity, transform, border-radius', + willChange: 'opacity, transform, border-radius', + }, + + drawerOpen: { + ...shorthands.borderRadius(0), + + opacity: 1, + transform: 'translate3D(0, 0, 0) scale(1)', }, }); @@ -13,10 +26,17 @@ export const CustomTransition = () => { const styles = useStyles(); const [isOpen, setIsOpen] = React.useState(false); + const ref = React.useRef(null); + const { mounted } = usePresenceState(ref, isOpen); return (
- setIsOpen(open)}> + setIsOpen(open)} + > Date: Sun, 4 Jun 2023 15:37:51 +0200 Subject: [PATCH 005/111] feat: make transition duration based on drawer size --- .../react-drawer/src/util/useDrawerBaseStyles.styles.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts b/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts index e8bc0934abb668..bb9f4d4e156a1f 100644 --- a/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts +++ b/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts @@ -55,12 +55,15 @@ export const useDrawerBaseStyles = makeStyles({ }, medium: { [drawerCSSVars.drawerSizeVar]: '592px', + transitionDuration: tokens.durationSlow, }, large: { [drawerCSSVars.drawerSizeVar]: '940px', + transitionDuration: tokens.durationSlower, }, full: { [drawerCSSVars.drawerSizeVar]: '100vw', + transitionDuration: tokens.durationUltraSlow, }, }); From 010b9fd9896593c69f59c91ee3721f158ce87629 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Sun, 4 Jun 2023 15:38:08 +0200 Subject: [PATCH 006/111] dcos: add missing non-modal example --- .../Drawer/DrawerOverlayNoModal.stories.tsx | 37 +++++++++++++++++++ .../stories/Drawer/index.stories.tsx | 1 + 2 files changed, 38 insertions(+) create mode 100644 packages/react-components/react-drawer/stories/Drawer/DrawerOverlayNoModal.stories.tsx diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerOverlayNoModal.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerOverlayNoModal.stories.tsx new file mode 100644 index 00000000000000..837da6e6ada887 --- /dev/null +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerOverlayNoModal.stories.tsx @@ -0,0 +1,37 @@ +import * as React from 'react'; +import { DrawerBody, DrawerHeader, DrawerHeaderTitle, DrawerOverlay } from '@fluentui/react-drawer'; +import { Button } from '@fluentui/react-components'; +import { Dismiss24Regular } from '@fluentui/react-icons'; + +export const OverlayNoModal = () => { + const [isOpen, setIsOpen] = React.useState(false); + + return ( +
+ setIsOpen(open)}> + + } + onClick={() => setIsOpen(false)} + /> + } + > + Overlay Drawer + + + + +

Drawer content

+
+
+ + +
+ ); +}; diff --git a/packages/react-components/react-drawer/stories/Drawer/index.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/index.stories.tsx index f2fe5c915e0df8..58779057b61d7d 100644 --- a/packages/react-components/react-drawer/stories/Drawer/index.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/index.stories.tsx @@ -13,6 +13,7 @@ import previewMd from './DrawerPreview.md'; export { Default } from './DrawerDefault.stories'; export { Overlay } from './DrawerOverlay.stories'; +export { OverlayNoModal } from './DrawerOverlayNoModal.stories'; export { Inline } from './DrawerInline.stories'; export { Position } from './DrawerPosition.stories'; export { Size } from './DrawerSize.stories'; From a282740e54e691c5f385243d17606e23e82f814e Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Sun, 4 Jun 2023 15:56:28 +0200 Subject: [PATCH 007/111] docs: improve documentation examples to align with motion --- .../Drawer/DrawerCustomSize.stories.tsx | 2 +- .../Drawer/DrawerCustomTransition.stories.tsx | 4 +- .../DrawerDisabledTransition.stories.tsx | 4 +- .../stories/Drawer/DrawerInline.stories.tsx | 3 +- .../stories/Drawer/DrawerOverlay.stories.tsx | 2 +- .../stories/Drawer/DrawerPosition.stories.tsx | 70 ++++++++++--------- .../Drawer/DrawerPreventClose.stories.tsx | 2 +- .../Drawer/DrawerResizable.stories.tsx | 1 + .../Drawer/DrawerResponsive.stories.tsx | 8 ++- .../Drawer/DrawerSeparator.stories.tsx | 5 +- .../stories/Drawer/DrawerSize.stories.tsx | 2 +- .../Drawer/DrawerWithNavigation.stories.tsx | 2 +- .../Drawer/DrawerWithScroll.stories.tsx | 6 +- 13 files changed, 60 insertions(+), 51 deletions(-) diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomSize.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomSize.stories.tsx index 768179d12a3994..89bf8ad7e63181 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomSize.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomSize.stories.tsx @@ -54,7 +54,7 @@ export const CustomSize = () => {
diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx index 5d36e5010a1454..ee804e62731b31 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx @@ -57,8 +57,8 @@ export const CustomTransition = () => { -
); diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerDisabledTransition.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerDisabledTransition.stories.tsx index 7b3e3c0187b672..5bc934f9ed2c94 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerDisabledTransition.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerDisabledTransition.stories.tsx @@ -37,8 +37,8 @@ export const DisabledTransition = () => { -
); diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerInline.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerInline.stories.tsx index d373f672abd20e..5670a0054b041e 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerInline.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerInline.stories.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { DrawerBody, DrawerHeader, DrawerHeaderTitle, DrawerInline } from '@fluentui/react-drawer'; -import { Button, makeStyles, shorthands } from '@fluentui/react-components'; +import { Button, makeStyles, shorthands, tokens } from '@fluentui/react-components'; import { Dismiss24Regular } from '@fluentui/react-icons'; const useStyles = makeStyles({ @@ -18,6 +18,7 @@ const useStyles = makeStyles({ display: 'flex', justifyContent: 'center', alignItems: 'flex-start', + columnGap: tokens.spacingHorizontalXS, }, }); diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerOverlay.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerOverlay.stories.tsx index 378561c7e844ad..507ac3190d5bc0 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerOverlay.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerOverlay.stories.tsx @@ -30,7 +30,7 @@ export const Overlay = () => {
); diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerPosition.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerPosition.stories.tsx index f03b9ab987e04a..2b05d2b2f00825 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerPosition.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerPosition.stories.tsx @@ -1,15 +1,36 @@ import * as React from 'react'; -import { DrawerBody, DrawerHeader, DrawerHeaderTitle, DrawerOverlay } from '@fluentui/react-drawer'; -import { Button } from '@fluentui/react-components'; +import { DrawerBody, DrawerHeader, DrawerHeaderTitle, DrawerOverlay, DrawerProps } from '@fluentui/react-drawer'; +import { Button, makeStyles, tokens } from '@fluentui/react-components'; import { Dismiss24Regular } from '@fluentui/react-icons'; +const useStyles = makeStyles({ + content: { + display: 'flex', + justifyContent: 'center', + alignItems: 'flex-start', + columnGap: tokens.spacingHorizontalXS, + }, +}); + export const Position = () => { - const [leftOpen, setLeftOpen] = React.useState(false); - const [rightOpen, setRightOpen] = React.useState(false); + const styles = useStyles(); + + const [isOpen, setIsOpen] = React.useState(false); + const [position, setPosition] = React.useState('left'); + + const onClickLeftButton = React.useCallback(() => { + setPosition('left'); + setIsOpen(true); + }, []); + + const onClickRightButton = React.useCallback(() => { + setPosition('right'); + setIsOpen(true); + }, []); return (
- setLeftOpen(open)}> + setIsOpen(open)}> { appearance="subtle" aria-label="Close" icon={} - onClick={() => setLeftOpen(false)} + onClick={() => setIsOpen(false)} /> } > - Left Drawer + {position === 'left' ? 'Left' : 'Right'} Drawer @@ -30,34 +51,15 @@ export const Position = () => { - - - - - setRightOpen(open)}> - - } - onClick={() => setRightOpen(false)} - /> - } - > - Right Drawer - - +
+ - -

Drawer content

-
- + +
); }; diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerPreventClose.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerPreventClose.stories.tsx index 58a476fe141f79..e2b9ef332c8475 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerPreventClose.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerPreventClose.stories.tsx @@ -30,7 +30,7 @@ export const PreventClose = () => { ); diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerResizable.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerResizable.stories.tsx index 9b7dc34768369e..a6a6fe9aa67b96 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerResizable.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerResizable.stories.tsx @@ -63,6 +63,7 @@ export const Resizable = () => { React.useEffect(() => { window.addEventListener('mousemove', resize); window.addEventListener('mouseup', stopResizing); + return () => { window.removeEventListener('mousemove', resize); window.removeEventListener('mouseup', stopResizing); diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerResponsive.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerResponsive.stories.tsx index 505d012042fcbf..0ce7100b1fd0d6 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerResponsive.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerResponsive.stories.tsx @@ -26,6 +26,8 @@ export const Responsive = () => { const [isOpen, setIsOpen] = React.useState(true); const [type, setType] = React.useState('inline'); + const onMediaQueryChange = React.useCallback(({ matches }) => setType(matches ? 'overlay' : 'inline'), [setType]); + React.useEffect(() => { const match = window.matchMedia('(max-width: 720px)'); @@ -33,8 +35,10 @@ export const Responsive = () => { setType('overlay'); } - match.addEventListener('change', ({ matches }) => setType(matches ? 'overlay' : 'inline')); - }, []); + match.addEventListener('change', onMediaQueryChange); + + return () => match.removeEventListener('change', onMediaQueryChange); + }, [onMediaQueryChange]); return (
diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerSeparator.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerSeparator.stories.tsx index 76a1fa294bdef8..85f5974e61eecd 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerSeparator.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerSeparator.stories.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { DrawerBody, DrawerHeader, DrawerHeaderTitle, DrawerInline } from '@fluentui/react-drawer'; -import { Button, makeStyles, shorthands } from '@fluentui/react-components'; +import { Button, makeStyles, shorthands, tokens } from '@fluentui/react-components'; import { Dismiss24Regular } from '@fluentui/react-icons'; const useStyles = makeStyles({ @@ -18,6 +18,7 @@ const useStyles = makeStyles({ display: 'flex', justifyContent: 'center', alignItems: 'flex-start', + columnGap: tokens.spacingHorizontalXS, }, }); @@ -29,7 +30,7 @@ export const Separator = () => { return (
- + {
diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerWithNavigation.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerWithNavigation.stories.tsx index 1c65e72df0d652..f9ef1d9cd4a47d 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerWithNavigation.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerWithNavigation.stories.tsx @@ -53,7 +53,7 @@ export const WithNavigation = () => {
); diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerWithScroll.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerWithScroll.stories.tsx index 133fdf48dd5e01..fb87d4153073ec 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerWithScroll.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerWithScroll.stories.tsx @@ -1,12 +1,12 @@ import * as React from 'react'; import { DrawerInline, DrawerBody, DrawerHeader, DrawerHeaderTitle, DrawerFooter } from '@fluentui/react-drawer'; -import { Button, makeStyles } from '@fluentui/react-components'; +import { Button, makeStyles, tokens } from '@fluentui/react-components'; const useStyles = makeStyles({ root: { display: 'flex', - rowGap: '24px', - columnGap: '24px', + rowGap: tokens.spacingHorizontalXXL, + columnGap: tokens.spacingHorizontalXXL, }, drawer: { From 7cd0c8bee7b2cd8a780f1d6919ca4f2d8ed3160e Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Sun, 4 Jun 2023 15:58:11 +0200 Subject: [PATCH 008/111] fix: generate api --- packages/react-components/react-drawer/etc/react-drawer.api.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/react-components/react-drawer/etc/react-drawer.api.md b/packages/react-components/react-drawer/etc/react-drawer.api.md index 4371f4a2ae5f70..1a65430dda5202 100644 --- a/packages/react-components/react-drawer/etc/react-drawer.api.md +++ b/packages/react-components/react-drawer/etc/react-drawer.api.md @@ -220,6 +220,9 @@ export const useDrawerOverlay_unstable: (props: DrawerOverlayProps, ref: React_2 // @public export const useDrawerOverlayStyles_unstable: (state: DrawerOverlayState) => DrawerOverlayState; +// @public (undocumented) +export const usePresenceState: (ref: React_2.RefObject, open: boolean, options?: UsePresenceStateOptions | undefined) => UsePresenceState; + // (No @packageDocumentation comment for this package) ``` From 2e31660c0fdc433c0b1e3080a5209f463a877baa Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Sun, 4 Jun 2023 16:00:29 +0200 Subject: [PATCH 009/111] fix: improve imports/exports --- .../src/components/DrawerInline/useDrawerInline.ts | 2 +- .../src/components/DrawerOverlay/useDrawerOverlay.ts | 2 +- .../react-drawer/src/util/usePresenceState.ts | 8 +++----- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts index 9d5cdcd5d215bd..10c952961995a9 100644 --- a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts +++ b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts @@ -2,7 +2,7 @@ import * as React from 'react'; import { getNativeElementProps, useControllableState } from '@fluentui/react-utilities'; import type { DrawerInlineProps, DrawerInlineState } from './DrawerInline.types'; import { getDefaultDrawerProps } from '../../util/getDefaultDrawerProps'; -import usePresenceState from '../../util/usePresenceState'; +import { usePresenceState } from '../../util/usePresenceState'; import useDrawerRef from '../../util/useDrawerRef'; /** diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts index 1d534fa948520f..ca5030d0b6df4f 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts @@ -3,7 +3,7 @@ import { getNativeElementProps } from '@fluentui/react-utilities'; import type { DrawerOverlayProps, DrawerOverlayState } from './DrawerOverlay.types'; import { DialogProps, DialogSurface } from '@fluentui/react-dialog'; import { getDefaultDrawerProps } from '../../util/getDefaultDrawerProps'; -import usePresenceState from '../../util/usePresenceState'; +import { usePresenceState } from '../../util/usePresenceState'; import useDrawerRef from '../../util/useDrawerRef'; /** diff --git a/packages/react-components/react-drawer/src/util/usePresenceState.ts b/packages/react-components/react-drawer/src/util/usePresenceState.ts index 5659c49ab28ab5..2de3637dcc33ab 100644 --- a/packages/react-components/react-drawer/src/util/usePresenceState.ts +++ b/packages/react-components/react-drawer/src/util/usePresenceState.ts @@ -8,11 +8,11 @@ type UsePresenceStateStore = { exiting: boolean; }; -type UsePresenceState = UsePresenceStateStore & { +export type UsePresenceState = UsePresenceStateStore & { animating: boolean; }; -type UsePresenceStateOptions = { +export type UsePresenceStateOptions = { onEnter?: () => void; onEntered?: () => void; onExit?: () => void; @@ -21,7 +21,7 @@ type UsePresenceStateOptions = { const noop = () => ({}); -const usePresenceState = ( +export const usePresenceState = ( ref: React.RefObject, open: boolean, options?: UsePresenceStateOptions, @@ -133,5 +133,3 @@ const usePresenceState = ( animating, }; }; - -export default usePresenceState; From 68eab218b39ec50cd3c4750b4d50638a7fc994b1 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Sun, 4 Jun 2023 16:02:02 +0200 Subject: [PATCH 010/111] fix: improve imports/exports --- .../react-drawer/etc/react-drawer.api.md | 13 +++++++++++++ packages/react-components/react-drawer/src/index.ts | 3 ++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/react-components/react-drawer/etc/react-drawer.api.md b/packages/react-components/react-drawer/etc/react-drawer.api.md index 1a65430dda5202..c3641fd28d847d 100644 --- a/packages/react-components/react-drawer/etc/react-drawer.api.md +++ b/packages/react-components/react-drawer/etc/react-drawer.api.md @@ -220,9 +220,22 @@ export const useDrawerOverlay_unstable: (props: DrawerOverlayProps, ref: React_2 // @public export const useDrawerOverlayStyles_unstable: (state: DrawerOverlayState) => DrawerOverlayState; +// @public (undocumented) +export type UsePresenceState = UsePresenceStateStore & { + animating: boolean; +}; + // @public (undocumented) export const usePresenceState: (ref: React_2.RefObject, open: boolean, options?: UsePresenceStateOptions | undefined) => UsePresenceState; +// @public (undocumented) +export type UsePresenceStateOptions = { + onEnter?: () => void; + onEntered?: () => void; + onExit?: () => void; + onExited?: () => void; +}; + // (No @packageDocumentation comment for this package) ``` diff --git a/packages/react-components/react-drawer/src/index.ts b/packages/react-components/react-drawer/src/index.ts index 24a4b54e9438fa..38817d6a64c9dc 100644 --- a/packages/react-components/react-drawer/src/index.ts +++ b/packages/react-components/react-drawer/src/index.ts @@ -68,4 +68,5 @@ export { } from './DrawerFooter'; export type { DrawerFooterProps, DrawerFooterSlots, DrawerFooterState } from './DrawerFooter'; -export { default as usePresenceState } from './util/usePresenceState'; +export { usePresenceState } from './util/usePresenceState'; +export type { UsePresenceState, UsePresenceStateOptions } from './util/usePresenceState'; From 251dbd660306d4f46d62844a519aa6f5a5b40b79 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Sun, 4 Jun 2023 16:04:44 +0200 Subject: [PATCH 011/111] fix: add missing changefiles --- ...-react-drawer-906d4815-2dd6-43b5-af6e-4ef4571ed915.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/@fluentui-react-drawer-906d4815-2dd6-43b5-af6e-4ef4571ed915.json diff --git a/change/@fluentui-react-drawer-906d4815-2dd6-43b5-af6e-4ef4571ed915.json b/change/@fluentui-react-drawer-906d4815-2dd6-43b5-af6e-4ef4571ed915.json new file mode 100644 index 00000000000000..328e8990f91a12 --- /dev/null +++ b/change/@fluentui-react-drawer-906d4815-2dd6-43b5-af6e-4ef4571ed915.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "feat: add transition on open/close", + "packageName": "@fluentui/react-drawer", + "email": "marcosvmmoura@gmail.com", + "dependentChangeType": "patch" +} From f3a79d2a5e132eb7c238166082667827e43ac627 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Mon, 5 Jun 2023 04:59:23 +0200 Subject: [PATCH 012/111] refactor: improve overall performance of usePresenceState --- .../DrawerInline/renderDrawerInline.tsx | 2 +- .../DrawerInline/useDrawerInline.ts | 10 +- .../DrawerOverlay/useDrawerOverlay.ts | 12 +- .../react-drawer/src/util/DrawerBase.types.ts | 4 +- .../react-drawer/src/util/useDrawerRef.ts | 8 - .../react-drawer/src/util/usePresenceState.ts | 192 ++++++++++-------- .../Drawer/DrawerCustomTransition.stories.tsx | 5 +- 7 files changed, 118 insertions(+), 115 deletions(-) delete mode 100644 packages/react-components/react-drawer/src/util/useDrawerRef.ts diff --git a/packages/react-components/react-drawer/src/components/DrawerInline/renderDrawerInline.tsx b/packages/react-components/react-drawer/src/components/DrawerInline/renderDrawerInline.tsx index b36f034fac0c70..9b51fd26e17970 100644 --- a/packages/react-components/react-drawer/src/components/DrawerInline/renderDrawerInline.tsx +++ b/packages/react-components/react-drawer/src/components/DrawerInline/renderDrawerInline.tsx @@ -8,7 +8,7 @@ import type { DrawerInlineState, DrawerInlineSlots } from './DrawerInline.types' export const renderDrawerInline_unstable = (state: DrawerInlineState) => { const { slots, slotProps } = getSlots(state); - if (!state.rendered) { + if (!state.shouldRender) { return null; } diff --git a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts index 10c952961995a9..0e9ec7d006b5a7 100644 --- a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts +++ b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts @@ -1,9 +1,8 @@ import * as React from 'react'; -import { getNativeElementProps, useControllableState } from '@fluentui/react-utilities'; +import { getNativeElementProps, useControllableState, useMergedRefs } from '@fluentui/react-utilities'; import type { DrawerInlineProps, DrawerInlineState } from './DrawerInline.types'; import { getDefaultDrawerProps } from '../../util/getDefaultDrawerProps'; import { usePresenceState } from '../../util/usePresenceState'; -import useDrawerRef from '../../util/useDrawerRef'; /** * Create the state required to render DrawerInline. @@ -24,8 +23,7 @@ export const useDrawerInline_unstable = (props: DrawerInlineProps, ref: React.Re initialState: false, }); - const drawerRef = useDrawerRef(ref); - const { rendered, mounted, entering, exiting } = usePresenceState(drawerRef, open); + const { ref: drawerRef, shouldRender, mounted, entering, exiting } = usePresenceState(open); return { components: { @@ -33,7 +31,7 @@ export const useDrawerInline_unstable = (props: DrawerInlineProps, ref: React.Re }, root: getNativeElementProps('div', { - ref: drawerRef, + ref: useMergedRefs(ref, drawerRef), ...props, }), @@ -42,7 +40,7 @@ export const useDrawerInline_unstable = (props: DrawerInlineProps, ref: React.Re open, separator, - rendered, + shouldRender, mounted, entering, exiting, diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts index ca5030d0b6df4f..1a241100ca7075 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts @@ -1,10 +1,9 @@ import * as React from 'react'; -import { getNativeElementProps } from '@fluentui/react-utilities'; +import { getNativeElementProps, useMergedRefs } from '@fluentui/react-utilities'; import type { DrawerOverlayProps, DrawerOverlayState } from './DrawerOverlay.types'; import { DialogProps, DialogSurface } from '@fluentui/react-dialog'; import { getDefaultDrawerProps } from '../../util/getDefaultDrawerProps'; import { usePresenceState } from '../../util/usePresenceState'; -import useDrawerRef from '../../util/useDrawerRef'; /** * Create the state required to render DrawerOverlay. @@ -22,8 +21,7 @@ export const useDrawerOverlay_unstable = ( const { open, defaultOpen, size, position } = getDefaultDrawerProps(props); const { modalType = 'modal', inertTrapFocus, onOpenChange } = props; - const drawerRef = useDrawerRef(ref); - const { rendered, mounted, entering, exiting } = usePresenceState(drawerRef, open); + const { ref: drawerRef, shouldRender, mounted, entering, exiting } = usePresenceState(open); return { components: { @@ -31,11 +29,11 @@ export const useDrawerOverlay_unstable = ( }, root: getNativeElementProps('div', { - ref: drawerRef, + ref: useMergedRefs(ref, drawerRef), ...props, }), dialog: { - open: rendered, + open: shouldRender, defaultOpen, onOpenChange, inertTrapFocus, @@ -44,7 +42,7 @@ export const useDrawerOverlay_unstable = ( size, position, - rendered, + shouldRender, mounted, entering, exiting, diff --git a/packages/react-components/react-drawer/src/util/DrawerBase.types.ts b/packages/react-components/react-drawer/src/util/DrawerBase.types.ts index 673db5fa8bd7f6..fdf0be4a0e4f8b 100644 --- a/packages/react-components/react-drawer/src/util/DrawerBase.types.ts +++ b/packages/react-components/react-drawer/src/util/DrawerBase.types.ts @@ -35,9 +35,9 @@ export type DrawerBaseProps = { export type DrawerBaseState = { /** - * Whether the drawer is rendered but not yet mounted. + * Whether the drawer should be rendered. * */ - rendered: boolean; + shouldRender: boolean; /** * Whether the drawer is rendered and already mounted. diff --git a/packages/react-components/react-drawer/src/util/useDrawerRef.ts b/packages/react-components/react-drawer/src/util/useDrawerRef.ts deleted file mode 100644 index ebca4488a2c4f2..00000000000000 --- a/packages/react-components/react-drawer/src/util/useDrawerRef.ts +++ /dev/null @@ -1,8 +0,0 @@ -import * as React from 'react'; -import { useMergedRefs } from '@fluentui/react-utilities'; - -const useDrawerRef = (ref: React.Ref) => { - return useMergedRefs(ref, React.useRef(null)); -}; - -export default useDrawerRef; diff --git a/packages/react-components/react-drawer/src/util/usePresenceState.ts b/packages/react-components/react-drawer/src/util/usePresenceState.ts index 2de3637dcc33ab..08ceb32cabf723 100644 --- a/packages/react-components/react-drawer/src/util/usePresenceState.ts +++ b/packages/react-components/react-drawer/src/util/usePresenceState.ts @@ -1,135 +1,151 @@ import * as React from 'react'; import { useIsomorphicLayoutEffect } from '@fluentui/react-utilities'; -type UsePresenceStateStore = { - rendered: boolean; - mounted: boolean; - entering: boolean; - exiting: boolean; -}; - -export type UsePresenceState = UsePresenceStateStore & { - animating: boolean; -}; +const noop = () => ({}); -export type UsePresenceStateOptions = { +export type UsePresenceStateEvents = { onEnter?: () => void; - onEntered?: () => void; onExit?: () => void; - onExited?: () => void; }; -const noop = () => ({}); +function getMaxTransitionDuration(transitionDuration: string) { + if (!transitionDuration) { + return 0; + } -export const usePresenceState = ( - ref: React.RefObject, - open: boolean, - options?: UsePresenceStateOptions, -): UsePresenceState => { - const { onEnter = noop, onEntered = noop, onExit = noop, onExited = noop } = options || {}; + const durations = transitionDuration.split(',').map(duration => { + const trimmed = duration.trim(); + const parsed = parseFloat(trimmed); - const [hasFinishedStart, setHasFinishedStart] = React.useState(false); + if (duration.includes('ms')) { + return parsed; + } - const [presenceState, setPresenceState] = React.useState({ - rendered: open, - mounted: false, - entering: false, - exiting: false, + return parsed * 1000; }); - const animating = React.useMemo(() => presenceState.entering || presenceState.exiting, [presenceState]); + + return Math.max(...durations); +} + +/** + * Hook to manage the presence of an element in the DOM. + * + * @param open - Whether the element should be present in the DOM + * @param events - Callbacks for when the element enters or exits the DOM + */ +export const usePresenceState = (open: boolean, events?: UsePresenceStateEvents) => { + const { onEnter = noop, onExit = noop } = events || {}; + + const [shouldRender, setShouldRender] = React.useState(open); + const [mounted, setMounted] = React.useState(false); + const [entering, setEntering] = React.useState(false); + const [exiting, setExiting] = React.useState(false); + const animating = entering || exiting; + + const [currentElement, setCurrentElement] = React.useState(null); + + const ref = React.useCallback(node => { + if (!node) { + return; + } + + setCurrentElement(node); + }, []); + + const notCurrentElement = React.useCallback((target: HTMLElement) => target !== currentElement, [currentElement]); const onStart = React.useCallback( - event => { - if (event.target !== ref.current) { + ({ target }) => { + if (notCurrentElement(target)) { return; } - setPresenceState(prevState => ({ - ...prevState, - entering: open, - exiting: !open, - })); - setHasFinishedStart(true); - if (open) { - onEnter(); + setEntering(true); } else { - onExit(); + setExiting(true); } }, - [onEnter, onExit, open, ref], + [notCurrentElement, open], ); const onEnd = React.useCallback( - event => { - if (event.target !== ref.current) { + ({ target }) => { + if (notCurrentElement(target)) { return; } - setPresenceState({ - entering: false, - exiting: false, - rendered: open, - mounted: open, - }); - if (open) { - onEntered(); + setEntering(false); + onEnter(); } else { - onExited(); + setExiting(false); + setShouldRender(false); + onExit(); } }, - [onEntered, onExited, open, ref], + [notCurrentElement, onEnter, onExit, open], ); - useIsomorphicLayoutEffect(() => { - let currentRef: HTMLElement; - - if (open) { - setPresenceState(prevState => ({ - ...prevState, - rendered: true, - })); - } else if (!hasFinishedStart) { - setPresenceState(prevState => ({ - ...prevState, - rendered: false, - })); - setHasFinishedStart(false); - } + const onCanceled = React.useCallback( + ({ target }) => { + if (notCurrentElement(target)) { + return; + } - const animationFrame = requestAnimationFrame(() => { - if (ref && ref.current) { - currentRef = ref.current; + setEntering(false); + setExiting(false); + setShouldRender(open); + setMounted(open); + }, + [notCurrentElement, open], + ); - ref.current.addEventListener('transitionstart', onStart); - ref.current.addEventListener('animationstart', onStart); - ref.current.addEventListener('transitionend', onEnd); - ref.current.addEventListener('animationend', onEnd); - } + useIsomorphicLayoutEffect(() => { + if (!currentElement) { + return; + } - setPresenceState(prevState => ({ - ...prevState, - mounted: open, - })); - }); + currentElement.addEventListener('transitionstart', onStart); + currentElement.addEventListener('transitionend', onEnd); + currentElement.addEventListener('transitioncancel', onCanceled); return () => { - cancelAnimationFrame(animationFrame); - - if (!currentRef) { + if (!currentElement) { return; } - currentRef.removeEventListener('transitionstart', onStart); - currentRef.removeEventListener('animationstart', onStart); - currentRef.removeEventListener('transitionend', onEnd); - currentRef.removeEventListener('animationend', onEnd); + currentElement.removeEventListener('transitionstart', onStart); + currentElement.removeEventListener('transitionend', onEnd); + currentElement.removeEventListener('transitioncancel', onCanceled); }; - }, [hasFinishedStart, onEnd, onStart, open, ref]); + }, [currentElement, onCanceled, onEnd, onStart]); + + React.useEffect(() => { + if (open) { + setShouldRender(true); + } else { + if (currentElement) { + const { transitionDuration } = window?.getComputedStyle(currentElement); + const hasTransition = getMaxTransitionDuration(transitionDuration) > 0; + + if (!hasTransition) { + setShouldRender(false); + } + } else { + setShouldRender(false); + } + } + + requestAnimationFrame(() => setMounted(open)); + }, [currentElement, open]); return { - ...presenceState, + ref, + shouldRender, + mounted, + entering, + exiting, animating, }; }; diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx index ee804e62731b31..dbda1fd2fc8ee8 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx @@ -9,7 +9,7 @@ const useStyles = makeStyles({ opacity: 0, transform: 'translate3D(-100%, 0, 0) scale(0.9)', - transitionDuration: tokens.durationSlower, + transitionDuration: `${tokens.durationSlower}`, transitionProperty: 'opacity, transform, border-radius', willChange: 'opacity, transform, border-radius', }, @@ -26,8 +26,7 @@ export const CustomTransition = () => { const styles = useStyles(); const [isOpen, setIsOpen] = React.useState(false); - const ref = React.useRef(null); - const { mounted } = usePresenceState(ref, isOpen); + const { ref, mounted } = usePresenceState(isOpen); return (
From 182ea1c1b54508e288daf46c87298811e234d92d Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Mon, 5 Jun 2023 05:00:03 +0200 Subject: [PATCH 013/111] fix: restore styles --- .../stories/Drawer/DrawerCustomTransition.stories.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx index dbda1fd2fc8ee8..c41c08696ca977 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx @@ -9,7 +9,7 @@ const useStyles = makeStyles({ opacity: 0, transform: 'translate3D(-100%, 0, 0) scale(0.9)', - transitionDuration: `${tokens.durationSlower}`, + transitionDuration: tokens.durationSlower, transitionProperty: 'opacity, transform, border-radius', willChange: 'opacity, transform, border-radius', }, From 318e9be2b9b69212255b2b7dd5d0acbe6f4db496 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Mon, 5 Jun 2023 05:10:15 +0200 Subject: [PATCH 014/111] fix: improve typings of usePresenceState --- .../components/DrawerInline/useDrawerInline.ts | 8 +++++--- .../DrawerOverlay/useDrawerOverlay.ts | 6 +++--- .../react-drawer/src/util/usePresenceState.ts | 18 +++++++++++++++--- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts index 0e9ec7d006b5a7..f1ba2d9d26e842 100644 --- a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts +++ b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts @@ -13,7 +13,10 @@ import { usePresenceState } from '../../util/usePresenceState'; * @param props - props from this instance of DrawerInline * @param ref - reference to root HTMLElement of DrawerInline */ -export const useDrawerInline_unstable = (props: DrawerInlineProps, ref: React.Ref): DrawerInlineState => { +export const useDrawerInline_unstable = ( + props: DrawerInlineProps, + ref: React.Ref, +): DrawerInlineState => { const { open: initialOpen, defaultOpen, size, position } = getDefaultDrawerProps(props); const { separator = false } = props; @@ -23,7 +26,7 @@ export const useDrawerInline_unstable = (props: DrawerInlineProps, ref: React.Re initialState: false, }); - const { ref: drawerRef, shouldRender, mounted, entering, exiting } = usePresenceState(open); + const { ref: drawerRef, shouldRender, mounted, entering, exiting } = usePresenceState(open); return { components: { @@ -39,7 +42,6 @@ export const useDrawerInline_unstable = (props: DrawerInlineProps, ref: React.Re position, open, separator, - shouldRender, mounted, entering, diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts index 1a241100ca7075..28eba124ac99a5 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts @@ -12,16 +12,16 @@ import { usePresenceState } from '../../util/usePresenceState'; * before being passed to renderDrawerOverlay_unstable. * * @param props - props from this instance of DrawerOverlay - * @param ref - reference to root HTMLElement of DrawerOverlay + * @param ref - reference to root HTMLDivElement of DrawerOverlay */ export const useDrawerOverlay_unstable = ( props: DrawerOverlayProps, - ref: React.Ref, + ref: React.Ref, ): DrawerOverlayState => { const { open, defaultOpen, size, position } = getDefaultDrawerProps(props); const { modalType = 'modal', inertTrapFocus, onOpenChange } = props; - const { ref: drawerRef, shouldRender, mounted, entering, exiting } = usePresenceState(open); + const { ref: drawerRef, shouldRender, mounted, entering, exiting } = usePresenceState(open); return { components: { diff --git a/packages/react-components/react-drawer/src/util/usePresenceState.ts b/packages/react-components/react-drawer/src/util/usePresenceState.ts index 08ceb32cabf723..7c9819d75cb5c0 100644 --- a/packages/react-components/react-drawer/src/util/usePresenceState.ts +++ b/packages/react-components/react-drawer/src/util/usePresenceState.ts @@ -3,6 +3,15 @@ import { useIsomorphicLayoutEffect } from '@fluentui/react-utilities'; const noop = () => ({}); +export type UsePresenceStateResult = { + ref: React.RefCallback; + shouldRender: boolean; + mounted: boolean; + entering: boolean; + exiting: boolean; + animating: boolean; +}; + export type UsePresenceStateEvents = { onEnter?: () => void; onExit?: () => void; @@ -33,7 +42,10 @@ function getMaxTransitionDuration(transitionDuration: string) { * @param open - Whether the element should be present in the DOM * @param events - Callbacks for when the element enters or exits the DOM */ -export const usePresenceState = (open: boolean, events?: UsePresenceStateEvents) => { +export const usePresenceState = ( + open: boolean, + events?: UsePresenceStateEvents, +): UsePresenceStateResult => { const { onEnter = noop, onExit = noop } = events || {}; const [shouldRender, setShouldRender] = React.useState(open); @@ -42,7 +54,7 @@ export const usePresenceState = (open: boolean, events?: UsePresenceStateEvents) const [exiting, setExiting] = React.useState(false); const animating = entering || exiting; - const [currentElement, setCurrentElement] = React.useState(null); + const [currentElement, setCurrentElement] = React.useState(null); const ref = React.useCallback(node => { if (!node) { @@ -52,7 +64,7 @@ export const usePresenceState = (open: boolean, events?: UsePresenceStateEvents) setCurrentElement(node); }, []); - const notCurrentElement = React.useCallback((target: HTMLElement) => target !== currentElement, [currentElement]); + const notCurrentElement = React.useCallback((target: T) => target !== currentElement, [currentElement]); const onStart = React.useCallback( ({ target }) => { From e91ea3e5009309632bd58b0099e0d71632d7ad29 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Mon, 5 Jun 2023 16:32:34 +0200 Subject: [PATCH 015/111] feat: refactor motion hooks to not fire multiple transition events --- .../react-drawer/etc/react-drawer.api.md | 27 +-- .../DrawerInline/useDrawerInline.ts | 6 +- .../useDrawerInlineStyles.styles.ts | 8 +- .../DrawerOverlay/useDrawerOverlay.ts | 6 +- .../useDrawerOverlayStyles.styles.ts | 6 +- .../react-drawer/src/index.ts | 4 +- .../react-drawer/src/util/DrawerBase.types.ts | 4 +- .../react-drawer/src/util/usePresenceState.ts | 163 --------------- .../src/util/useTransitionPresence.ts | 185 ++++++++++++++++++ .../Drawer/DrawerCustomTransition.stories.tsx | 4 +- 10 files changed, 219 insertions(+), 194 deletions(-) delete mode 100644 packages/react-components/react-drawer/src/util/usePresenceState.ts create mode 100644 packages/react-components/react-drawer/src/util/useTransitionPresence.ts diff --git a/packages/react-components/react-drawer/etc/react-drawer.api.md b/packages/react-components/react-drawer/etc/react-drawer.api.md index c3641fd28d847d..f0ccdd180bac22 100644 --- a/packages/react-components/react-drawer/etc/react-drawer.api.md +++ b/packages/react-components/react-drawer/etc/react-drawer.api.md @@ -209,33 +209,36 @@ export const useDrawerHeaderTitle_unstable: (props: DrawerHeaderTitleProps, ref: export const useDrawerHeaderTitleStyles_unstable: (state: DrawerHeaderTitleState) => DrawerHeaderTitleState; // @public -export const useDrawerInline_unstable: (props: DrawerInlineProps, ref: React_2.Ref) => DrawerInlineState; +export const useDrawerInline_unstable: (props: DrawerInlineProps, ref: React_2.Ref) => DrawerInlineState; // @public export const useDrawerInlineStyles_unstable: (state: DrawerInlineState) => DrawerInlineState; // @public -export const useDrawerOverlay_unstable: (props: DrawerOverlayProps, ref: React_2.Ref) => DrawerOverlayState; +export const useDrawerOverlay_unstable: (props: DrawerOverlayProps, ref: React_2.Ref) => DrawerOverlayState; // @public export const useDrawerOverlayStyles_unstable: (state: DrawerOverlayState) => DrawerOverlayState; -// @public (undocumented) -export type UsePresenceState = UsePresenceStateStore & { - animating: boolean; -}; - -// @public (undocumented) -export const usePresenceState: (ref: React_2.RefObject, open: boolean, options?: UsePresenceStateOptions | undefined) => UsePresenceState; +// @public +export const useTransitionPresence: (present: boolean, events?: UseTransitionPresenceEvents | undefined) => UseTransitionPresenceState; // @public (undocumented) -export type UsePresenceStateOptions = { - onEnter?: () => void; +export type UseTransitionPresenceEvents = { onEntered?: () => void; - onExit?: () => void; onExited?: () => void; }; +// @public (undocumented) +export type UseTransitionPresenceState = { + ref: React_2.RefCallback; + shouldRender: boolean; + visible: boolean; + entering: boolean; + exiting: boolean; + animating: boolean; +}; + // (No @packageDocumentation comment for this package) ``` diff --git a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts index f1ba2d9d26e842..a22ff94ba8cfd3 100644 --- a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts +++ b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts @@ -2,7 +2,7 @@ import * as React from 'react'; import { getNativeElementProps, useControllableState, useMergedRefs } from '@fluentui/react-utilities'; import type { DrawerInlineProps, DrawerInlineState } from './DrawerInline.types'; import { getDefaultDrawerProps } from '../../util/getDefaultDrawerProps'; -import { usePresenceState } from '../../util/usePresenceState'; +import { useTransitionPresence } from '../../util/useTransitionPresence'; /** * Create the state required to render DrawerInline. @@ -26,7 +26,7 @@ export const useDrawerInline_unstable = ( initialState: false, }); - const { ref: drawerRef, shouldRender, mounted, entering, exiting } = usePresenceState(open); + const { ref: drawerRef, shouldRender, visible, entering, exiting } = useTransitionPresence(open); return { components: { @@ -43,7 +43,7 @@ export const useDrawerInline_unstable = ( open, separator, shouldRender, - mounted, + visible, entering, exiting, }; diff --git a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts index 2ad5cf7fe92b9e..3a358f9e3fcccb 100644 --- a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts +++ b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts @@ -37,11 +37,11 @@ const useStyles = makeStyles({ willChange: 'margin-right', }, - /* Mounted */ - mountedLeft: { + /* Visible */ + visibleLeft: { marginLeft: 0, }, - mountedRight: { + visibleRight: { marginRight: 0, }, }); @@ -67,7 +67,7 @@ export const useDrawerInlineStyles_unstable = (state: DrawerInlineState): Drawer styles.root, getDrawerBaseClassNames(state, baseStyles), state.position && styles[state.position], - state.mounted && (state.position === 'left' ? styles.mountedLeft : styles.mountedRight), + state.visible && (state.position === 'left' ? styles.visibleLeft : styles.visibleRight), separatorClass, state.root.className, ); diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts index 28eba124ac99a5..61f4b1e5194139 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts @@ -3,7 +3,7 @@ import { getNativeElementProps, useMergedRefs } from '@fluentui/react-utilities' import type { DrawerOverlayProps, DrawerOverlayState } from './DrawerOverlay.types'; import { DialogProps, DialogSurface } from '@fluentui/react-dialog'; import { getDefaultDrawerProps } from '../../util/getDefaultDrawerProps'; -import { usePresenceState } from '../../util/usePresenceState'; +import { useTransitionPresence } from '../../util/useTransitionPresence'; /** * Create the state required to render DrawerOverlay. @@ -21,7 +21,7 @@ export const useDrawerOverlay_unstable = ( const { open, defaultOpen, size, position } = getDefaultDrawerProps(props); const { modalType = 'modal', inertTrapFocus, onOpenChange } = props; - const { ref: drawerRef, shouldRender, mounted, entering, exiting } = usePresenceState(open); + const { ref: drawerRef, shouldRender, visible, entering, exiting } = useTransitionPresence(open); return { components: { @@ -43,7 +43,7 @@ export const useDrawerOverlay_unstable = ( size, position, shouldRender, - mounted, + visible, entering, exiting, }; diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts index c11b3205f3c6f5..aee81d5113ea20 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts @@ -27,8 +27,8 @@ const useStyles = makeStyles({ transform: 'translate3D(calc(var(--fui-Drawer--size) * 1), 0, 0)', }, - /* Mounted */ - mounted: { + /* Visible */ + visible: { transform: 'translate3D(0, 0, 0)', }, }); @@ -46,7 +46,7 @@ export const useDrawerOverlayStyles_unstable = (state: DrawerOverlayState): Draw styles.root, getDrawerBaseClassNames(state, baseStyles), state.position && styles[state.position], - state.mounted && styles.mounted, + state.visible && styles.visible, state.root.className, ); diff --git a/packages/react-components/react-drawer/src/index.ts b/packages/react-components/react-drawer/src/index.ts index 38817d6a64c9dc..a64bfe6eef1496 100644 --- a/packages/react-components/react-drawer/src/index.ts +++ b/packages/react-components/react-drawer/src/index.ts @@ -68,5 +68,5 @@ export { } from './DrawerFooter'; export type { DrawerFooterProps, DrawerFooterSlots, DrawerFooterState } from './DrawerFooter'; -export { usePresenceState } from './util/usePresenceState'; -export type { UsePresenceState, UsePresenceStateOptions } from './util/usePresenceState'; +export { useTransitionPresence } from './util/useTransitionPresence'; +export type { UseTransitionPresenceEvents, UseTransitionPresenceState } from './util/useTransitionPresence'; diff --git a/packages/react-components/react-drawer/src/util/DrawerBase.types.ts b/packages/react-components/react-drawer/src/util/DrawerBase.types.ts index fdf0be4a0e4f8b..2cabafae76e49c 100644 --- a/packages/react-components/react-drawer/src/util/DrawerBase.types.ts +++ b/packages/react-components/react-drawer/src/util/DrawerBase.types.ts @@ -40,9 +40,9 @@ export type DrawerBaseState = { shouldRender: boolean; /** - * Whether the drawer is rendered and already mounted. + * Whether the drawer is rendered and already visible. * */ - mounted: boolean; + visible: boolean; /** * Whether the drawer is entering the screen. diff --git a/packages/react-components/react-drawer/src/util/usePresenceState.ts b/packages/react-components/react-drawer/src/util/usePresenceState.ts deleted file mode 100644 index 7c9819d75cb5c0..00000000000000 --- a/packages/react-components/react-drawer/src/util/usePresenceState.ts +++ /dev/null @@ -1,163 +0,0 @@ -import * as React from 'react'; -import { useIsomorphicLayoutEffect } from '@fluentui/react-utilities'; - -const noop = () => ({}); - -export type UsePresenceStateResult = { - ref: React.RefCallback; - shouldRender: boolean; - mounted: boolean; - entering: boolean; - exiting: boolean; - animating: boolean; -}; - -export type UsePresenceStateEvents = { - onEnter?: () => void; - onExit?: () => void; -}; - -function getMaxTransitionDuration(transitionDuration: string) { - if (!transitionDuration) { - return 0; - } - - const durations = transitionDuration.split(',').map(duration => { - const trimmed = duration.trim(); - const parsed = parseFloat(trimmed); - - if (duration.includes('ms')) { - return parsed; - } - - return parsed * 1000; - }); - - return Math.max(...durations); -} - -/** - * Hook to manage the presence of an element in the DOM. - * - * @param open - Whether the element should be present in the DOM - * @param events - Callbacks for when the element enters or exits the DOM - */ -export const usePresenceState = ( - open: boolean, - events?: UsePresenceStateEvents, -): UsePresenceStateResult => { - const { onEnter = noop, onExit = noop } = events || {}; - - const [shouldRender, setShouldRender] = React.useState(open); - const [mounted, setMounted] = React.useState(false); - const [entering, setEntering] = React.useState(false); - const [exiting, setExiting] = React.useState(false); - const animating = entering || exiting; - - const [currentElement, setCurrentElement] = React.useState(null); - - const ref = React.useCallback(node => { - if (!node) { - return; - } - - setCurrentElement(node); - }, []); - - const notCurrentElement = React.useCallback((target: T) => target !== currentElement, [currentElement]); - - const onStart = React.useCallback( - ({ target }) => { - if (notCurrentElement(target)) { - return; - } - - if (open) { - setEntering(true); - } else { - setExiting(true); - } - }, - [notCurrentElement, open], - ); - - const onEnd = React.useCallback( - ({ target }) => { - if (notCurrentElement(target)) { - return; - } - - if (open) { - setEntering(false); - onEnter(); - } else { - setExiting(false); - setShouldRender(false); - onExit(); - } - }, - [notCurrentElement, onEnter, onExit, open], - ); - - const onCanceled = React.useCallback( - ({ target }) => { - if (notCurrentElement(target)) { - return; - } - - setEntering(false); - setExiting(false); - setShouldRender(open); - setMounted(open); - }, - [notCurrentElement, open], - ); - - useIsomorphicLayoutEffect(() => { - if (!currentElement) { - return; - } - - currentElement.addEventListener('transitionstart', onStart); - currentElement.addEventListener('transitionend', onEnd); - currentElement.addEventListener('transitioncancel', onCanceled); - - return () => { - if (!currentElement) { - return; - } - - currentElement.removeEventListener('transitionstart', onStart); - currentElement.removeEventListener('transitionend', onEnd); - currentElement.removeEventListener('transitioncancel', onCanceled); - }; - }, [currentElement, onCanceled, onEnd, onStart]); - - React.useEffect(() => { - if (open) { - setShouldRender(true); - } else { - if (currentElement) { - const { transitionDuration } = window?.getComputedStyle(currentElement); - const hasTransition = getMaxTransitionDuration(transitionDuration) > 0; - - if (!hasTransition) { - setShouldRender(false); - } - } else { - setShouldRender(false); - } - } - - requestAnimationFrame(() => setMounted(open)); - }, [currentElement, open]); - - return { - ref, - shouldRender, - mounted, - entering, - exiting, - animating, - }; -}; diff --git a/packages/react-components/react-drawer/src/util/useTransitionPresence.ts b/packages/react-components/react-drawer/src/util/useTransitionPresence.ts new file mode 100644 index 00000000000000..0524640034d65f --- /dev/null +++ b/packages/react-components/react-drawer/src/util/useTransitionPresence.ts @@ -0,0 +1,185 @@ +import * as React from 'react'; + +const noop = () => ({}); + +export type UseTransitionPresenceState = { + ref: React.RefCallback; + shouldRender: boolean; + visible: boolean; + entering: boolean; + exiting: boolean; + animating: boolean; +}; + +export type UseTransitionPresenceEvents = { + onEntered?: () => void; + onExited?: () => void; +}; + +function toMs(s: string): number { + return Number(s.slice(0, -1).replace(',', '.')) * 1000; +} + +const getMaxCssDuration = (durations: string[]) => { + return Math.max( + ...durations.map(d => { + const trimmed = d.trim(); + + if (d.includes('ms')) { + return parseFloat(trimmed); + } + + return toMs(trimmed); + }), + ); +}; + +const getTransitionInfo = (computedStyle: CSSStyleDeclaration) => { + const getProp = (prop: string) => (computedStyle.getPropertyValue(prop) || '').split(','); + + const durations = getProp('transition-duration'); + const delays = getProp('transition-delay'); + + const totalDuration = getMaxCssDuration(durations) + getMaxCssDuration(delays); + + return { + count: durations.length, + duration: totalDuration, + hasTransition: totalDuration > 0, + }; +}; + +/** + * Hook to manage the presence of an element in the DOM based on its CSS transition state. + * + * @param present - Whether the element should be present in the DOM + * @param events - Callbacks for when the element enters or exits the DOM - Only called when + * the element has a transition + */ +export const useTransitionPresence = ( + present: boolean, + events?: UseTransitionPresenceEvents, +): UseTransitionPresenceState => { + const { onEntered = noop, onExited = noop } = events || {}; + + const [shouldRender, setShouldRender] = React.useState(present); + const [visible, setVisible] = React.useState(false); + const [entering, setEntering] = React.useState(false); + const [exiting, setExiting] = React.useState(false); + const animating = entering || exiting; + + const [currentElement, setCurrentElement] = React.useState(null); + + const ref: React.RefCallback = React.useCallback(node => { + if (!node) { + return; + } + + setCurrentElement(node); + }, []); + + const notCurrentElement = React.useCallback((target: TElement) => target !== currentElement, [currentElement]); + + const onEntering = React.useCallback(() => { + setEntering(true); + setVisible(true); + }, []); + + const onExiting = React.useCallback(() => { + setExiting(true); + }, []); + + const onFinishedAnimating = React.useCallback(() => { + setEntering(false); + setExiting(false); + }, []); + + const onEnterAnimationEnd = React.useCallback(() => { + onFinishedAnimating(); + onEntered(); + }, [onEntered, onFinishedAnimating]); + + const onExitAnimationEnd = React.useCallback(() => { + onFinishedAnimating(); + setVisible(false); + setShouldRender(false); + onExited(); + }, [onExited, onFinishedAnimating]); + + const onCanceled = React.useCallback( + ({ target }) => { + if (notCurrentElement(target)) { + return; + } + + onFinishedAnimating(); + setVisible(present); + setShouldRender(present); + }, + [notCurrentElement, onFinishedAnimating, present], + ); + + React.useEffect(() => { + if (present) { + setShouldRender(true); + } + }, [present]); + + React.useEffect(() => { + currentElement?.addEventListener('transitioncancel', onCanceled); + + return () => currentElement?.removeEventListener('transitioncancel', onCanceled); + }, [currentElement, onCanceled]); + + React.useEffect(() => { + if (!currentElement) { + return; + } + + let timeout: number | undefined; + + const styles = window?.getComputedStyle(currentElement); + const { duration, hasTransition } = getTransitionInfo(styles); + + const animationFrame = requestAnimationFrame(() => { + setVisible(present); + + if (!hasTransition) { + setShouldRender(present); + } else { + if (present) { + onEntering(); + } else { + onExiting(); + } + + /** + * Use CSS transition duration + 1ms to ensure the animation has finished on both enter and exit states. + * This is an alternative to using the `transitionend` event which can be unreliable as it fires multiple times + * if the transition has multiple properties. + */ + timeout = setTimeout(() => { + if (present) { + onEnterAnimationEnd(); + } else { + onExitAnimationEnd(); + } + }, duration + 1); + } + }); + + return () => { + cancelAnimationFrame(animationFrame); + clearTimeout(timeout); + }; + }, [currentElement, onExitAnimationEnd, onEnterAnimationEnd, present, onEntering, onExiting]); + + return { + ref, + shouldRender, + visible, + entering, + exiting, + animating, + }; +}; diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx index c41c08696ca977..45947f98d0a4a9 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx @@ -26,13 +26,13 @@ export const CustomTransition = () => { const styles = useStyles(); const [isOpen, setIsOpen] = React.useState(false); - const { ref, mounted } = usePresenceState(isOpen); + const { ref, visible } = usePresenceState(isOpen); return (
setIsOpen(open)} > From d7061f974a540005333cfa7978854ef3bbbb571b Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Mon, 5 Jun 2023 16:36:35 +0200 Subject: [PATCH 016/111] fix: wrong imports inside stories --- .../stories/Drawer/DrawerCustomTransition.stories.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx index 45947f98d0a4a9..a59e7d17b59837 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Drawer, DrawerBody, DrawerHeader, DrawerHeaderTitle, usePresenceState } from '@fluentui/react-drawer'; +import { Drawer, DrawerBody, DrawerHeader, DrawerHeaderTitle, useTransitionPresence } from '@fluentui/react-drawer'; import { Button, makeStyles, mergeClasses, shorthands, tokens } from '@fluentui/react-components'; import { Dismiss24Regular } from '@fluentui/react-icons'; @@ -26,7 +26,7 @@ export const CustomTransition = () => { const styles = useStyles(); const [isOpen, setIsOpen] = React.useState(false); - const { ref, visible } = usePresenceState(isOpen); + const { ref, visible } = useTransitionPresence(isOpen); return (
From 892c4ffc8fdad52e7a44c90f169cba5c7d96472f Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Mon, 5 Jun 2023 17:47:11 +0200 Subject: [PATCH 017/111] fix: use safe timeout --- .../src/util/useTransitionPresence.ts | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/packages/react-components/react-drawer/src/util/useTransitionPresence.ts b/packages/react-components/react-drawer/src/util/useTransitionPresence.ts index 0524640034d65f..70fe7c600b5ed5 100644 --- a/packages/react-components/react-drawer/src/util/useTransitionPresence.ts +++ b/packages/react-components/react-drawer/src/util/useTransitionPresence.ts @@ -1,6 +1,7 @@ import * as React from 'react'; +import { useTimeout } from '@fluentui/react-utilities'; -const noop = () => ({}); +const noop = () => null; export type UseTransitionPresenceState = { ref: React.RefCallback; @@ -70,6 +71,8 @@ export const useTransitionPresence = ( const [currentElement, setCurrentElement] = React.useState(null); + const [setAnimationTimeout, clearAnimationTimeout] = useTimeout(); + const ref: React.RefCallback = React.useCallback(node => { if (!node) { return; @@ -136,8 +139,6 @@ export const useTransitionPresence = ( return; } - let timeout: number | undefined; - const styles = window?.getComputedStyle(currentElement); const { duration, hasTransition } = getTransitionInfo(styles); @@ -158,7 +159,7 @@ export const useTransitionPresence = ( * This is an alternative to using the `transitionend` event which can be unreliable as it fires multiple times * if the transition has multiple properties. */ - timeout = setTimeout(() => { + setAnimationTimeout(() => { if (present) { onEnterAnimationEnd(); } else { @@ -170,9 +171,18 @@ export const useTransitionPresence = ( return () => { cancelAnimationFrame(animationFrame); - clearTimeout(timeout); + clearAnimationTimeout(); }; - }, [currentElement, onExitAnimationEnd, onEnterAnimationEnd, present, onEntering, onExiting]); + }, [ + currentElement, + onExitAnimationEnd, + onEnterAnimationEnd, + present, + onEntering, + onExiting, + setAnimationTimeout, + clearAnimationTimeout, + ]); return { ref, From 580c8959b9307d7c3f559a1abe0774c27166edf0 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Mon, 5 Jun 2023 20:30:32 +0200 Subject: [PATCH 018/111] fix: move hook to a dedicated folder --- .../src/components/DrawerInline/useDrawerInline.ts | 2 +- .../src/components/DrawerOverlay/useDrawerOverlay.ts | 2 +- .../src/{util => hooks}/useTransitionPresence.ts | 0 packages/react-components/react-drawer/src/index.ts | 7 +++++-- 4 files changed, 7 insertions(+), 4 deletions(-) rename packages/react-components/react-drawer/src/{util => hooks}/useTransitionPresence.ts (100%) diff --git a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts index a22ff94ba8cfd3..75cc2742f8704f 100644 --- a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts +++ b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts @@ -2,7 +2,7 @@ import * as React from 'react'; import { getNativeElementProps, useControllableState, useMergedRefs } from '@fluentui/react-utilities'; import type { DrawerInlineProps, DrawerInlineState } from './DrawerInline.types'; import { getDefaultDrawerProps } from '../../util/getDefaultDrawerProps'; -import { useTransitionPresence } from '../../util/useTransitionPresence'; +import { useTransitionPresence } from '../../hooks/useTransitionPresence'; /** * Create the state required to render DrawerInline. diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts index 61f4b1e5194139..33599b90c1d789 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts @@ -3,7 +3,7 @@ import { getNativeElementProps, useMergedRefs } from '@fluentui/react-utilities' import type { DrawerOverlayProps, DrawerOverlayState } from './DrawerOverlay.types'; import { DialogProps, DialogSurface } from '@fluentui/react-dialog'; import { getDefaultDrawerProps } from '../../util/getDefaultDrawerProps'; -import { useTransitionPresence } from '../../util/useTransitionPresence'; +import { useTransitionPresence } from '../../hooks/useTransitionPresence'; /** * Create the state required to render DrawerOverlay. diff --git a/packages/react-components/react-drawer/src/util/useTransitionPresence.ts b/packages/react-components/react-drawer/src/hooks/useTransitionPresence.ts similarity index 100% rename from packages/react-components/react-drawer/src/util/useTransitionPresence.ts rename to packages/react-components/react-drawer/src/hooks/useTransitionPresence.ts diff --git a/packages/react-components/react-drawer/src/index.ts b/packages/react-components/react-drawer/src/index.ts index a64bfe6eef1496..b74945a346cdce 100644 --- a/packages/react-components/react-drawer/src/index.ts +++ b/packages/react-components/react-drawer/src/index.ts @@ -68,5 +68,8 @@ export { } from './DrawerFooter'; export type { DrawerFooterProps, DrawerFooterSlots, DrawerFooterState } from './DrawerFooter'; -export { useTransitionPresence } from './util/useTransitionPresence'; -export type { UseTransitionPresenceEvents, UseTransitionPresenceState } from './util/useTransitionPresence'; +export { useDrawerAnimationState, useDrawerStore } from './hooks/useDrawerAnimationState'; +export type { DrawerAnimationState } from './hooks/useDrawerAnimationState'; + +export { useTransitionPresence } from './hooks/useTransitionPresence'; +export type { UseTransitionPresenceEvents, UseTransitionPresenceState } from './hooks/useTransitionPresence'; From dcd604372bdfdd8b31f4b44d663c545a1f9a1df9 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Tue, 6 Jun 2023 15:33:37 +0200 Subject: [PATCH 019/111] feat: cache styles to only read it when ref changes --- .../src/hooks/useTransitionPresence.ts | 35 ++++++++++++++++--- .../react-drawer/src/index.ts | 3 -- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/packages/react-components/react-drawer/src/hooks/useTransitionPresence.ts b/packages/react-components/react-drawer/src/hooks/useTransitionPresence.ts index 70fe7c600b5ed5..37cc565073b9e4 100644 --- a/packages/react-components/react-drawer/src/hooks/useTransitionPresence.ts +++ b/packages/react-components/react-drawer/src/hooks/useTransitionPresence.ts @@ -3,7 +3,7 @@ import { useTimeout } from '@fluentui/react-utilities'; const noop = () => null; -export type UseTransitionPresenceState = { +export type UseTransitionPresenceState = { ref: React.RefCallback; shouldRender: boolean; visible: boolean; @@ -21,6 +21,12 @@ function toMs(s: string): number { return Number(s.slice(0, -1).replace(',', '.')) * 1000; } +/** + * Gets the maximum duration from a list of CSS durations. + * + * @param durations - List of CSS durations + * @returns Maximum duration + */ const getMaxCssDuration = (durations: string[]) => { return Math.max( ...durations.map(d => { @@ -35,6 +41,13 @@ const getMaxCssDuration = (durations: string[]) => { ); }; +/** + * Gets the transition information for a given element. + * + * @param computedStyle - Computed style of the element + * @returns Transition information + */ + const getTransitionInfo = (computedStyle: CSSStyleDeclaration) => { const getProp = (prop: string) => (computedStyle.getPropertyValue(prop) || '').split(','); @@ -44,12 +57,25 @@ const getTransitionInfo = (computedStyle: CSSStyleDeclaration) => { const totalDuration = getMaxCssDuration(durations) + getMaxCssDuration(delays); return { - count: durations.length, duration: totalDuration, hasTransition: totalDuration > 0, }; }; +export function mergeRefs( + refs: Array | React.MutableRefObject | React.LegacyRef>, +): React.RefCallback { + return value => { + refs.forEach(ref => { + if (typeof ref === 'function') { + ref(value); + } else if (ref !== null) { + (ref as React.MutableRefObject).current = value; + } + }); + }; +} + /** * Hook to manage the presence of an element in the DOM based on its CSS transition state. * @@ -73,11 +99,13 @@ export const useTransitionPresence = ( const [setAnimationTimeout, clearAnimationTimeout] = useTimeout(); + const computedStylesRef = React.useRef({} as CSSStyleDeclaration); const ref: React.RefCallback = React.useCallback(node => { if (!node) { return; } + computedStylesRef.current = window?.getComputedStyle(node); setCurrentElement(node); }, []); @@ -139,8 +167,7 @@ export const useTransitionPresence = ( return; } - const styles = window?.getComputedStyle(currentElement); - const { duration, hasTransition } = getTransitionInfo(styles); + const { duration, hasTransition } = getTransitionInfo(computedStylesRef.current); const animationFrame = requestAnimationFrame(() => { setVisible(present); diff --git a/packages/react-components/react-drawer/src/index.ts b/packages/react-components/react-drawer/src/index.ts index b74945a346cdce..77bf548eac10de 100644 --- a/packages/react-components/react-drawer/src/index.ts +++ b/packages/react-components/react-drawer/src/index.ts @@ -68,8 +68,5 @@ export { } from './DrawerFooter'; export type { DrawerFooterProps, DrawerFooterSlots, DrawerFooterState } from './DrawerFooter'; -export { useDrawerAnimationState, useDrawerStore } from './hooks/useDrawerAnimationState'; -export type { DrawerAnimationState } from './hooks/useDrawerAnimationState'; - export { useTransitionPresence } from './hooks/useTransitionPresence'; export type { UseTransitionPresenceEvents, UseTransitionPresenceState } from './hooks/useTransitionPresence'; From d20cecec4a4c46547ffbd109478cf609e9ecb635 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Tue, 6 Jun 2023 15:37:42 +0200 Subject: [PATCH 020/111] feat: improve documentation --- .../src/hooks/useTransitionPresence.ts | 40 +++++++------------ 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/packages/react-components/react-drawer/src/hooks/useTransitionPresence.ts b/packages/react-components/react-drawer/src/hooks/useTransitionPresence.ts index 37cc565073b9e4..a8d387edee7790 100644 --- a/packages/react-components/react-drawer/src/hooks/useTransitionPresence.ts +++ b/packages/react-components/react-drawer/src/hooks/useTransitionPresence.ts @@ -17,37 +17,39 @@ export type UseTransitionPresenceEvents = { onExited?: () => void; }; +/** + * @internal + * Converts a CSS duration string to milliseconds. + * + * @param s - CSS duration string + * @returns Duration in milliseconds + */ function toMs(s: string): number { + if (s.includes('ms')) { + return parseFloat(s); + } + return Number(s.slice(0, -1).replace(',', '.')) * 1000; } /** + * @internal * Gets the maximum duration from a list of CSS durations. * * @param durations - List of CSS durations * @returns Maximum duration */ const getMaxCssDuration = (durations: string[]) => { - return Math.max( - ...durations.map(d => { - const trimmed = d.trim(); - - if (d.includes('ms')) { - return parseFloat(trimmed); - } - - return toMs(trimmed); - }), - ); + return Math.max(...durations.map(d => toMs(d.trim()))); }; /** + * @internal * Gets the transition information for a given element. * * @param computedStyle - Computed style of the element * @returns Transition information */ - const getTransitionInfo = (computedStyle: CSSStyleDeclaration) => { const getProp = (prop: string) => (computedStyle.getPropertyValue(prop) || '').split(','); @@ -62,20 +64,6 @@ const getTransitionInfo = (computedStyle: CSSStyleDeclaration) => { }; }; -export function mergeRefs( - refs: Array | React.MutableRefObject | React.LegacyRef>, -): React.RefCallback { - return value => { - refs.forEach(ref => { - if (typeof ref === 'function') { - ref(value); - } else if (ref !== null) { - (ref as React.MutableRefObject).current = value; - } - }); - }; -} - /** * Hook to manage the presence of an element in the DOM based on its CSS transition state. * From 8978acf1a48c7c66ccc9422d65a260798ad5a3bd Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Tue, 6 Jun 2023 15:38:54 +0200 Subject: [PATCH 021/111] feat: generate API --- packages/react-components/react-drawer/etc/react-drawer.api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-components/react-drawer/etc/react-drawer.api.md b/packages/react-components/react-drawer/etc/react-drawer.api.md index f0ccdd180bac22..35bf9ff25fa069 100644 --- a/packages/react-components/react-drawer/etc/react-drawer.api.md +++ b/packages/react-components/react-drawer/etc/react-drawer.api.md @@ -230,7 +230,7 @@ export type UseTransitionPresenceEvents = { }; // @public (undocumented) -export type UseTransitionPresenceState = { +export type UseTransitionPresenceState = { ref: React_2.RefCallback; shouldRender: boolean; visible: boolean; From 0cb7d136967ad7ab2483debf32408e931ccaa65c Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Tue, 6 Jun 2023 15:44:01 +0200 Subject: [PATCH 022/111] docs: add better documentation for the hook --- .../react-drawer/etc/react-drawer.api.md | 4 +- .../src/hooks/useTransitionPresence.ts | 41 +++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/packages/react-components/react-drawer/etc/react-drawer.api.md b/packages/react-components/react-drawer/etc/react-drawer.api.md index 35bf9ff25fa069..4ccfedbbbfe861 100644 --- a/packages/react-components/react-drawer/etc/react-drawer.api.md +++ b/packages/react-components/react-drawer/etc/react-drawer.api.md @@ -223,13 +223,13 @@ export const useDrawerOverlayStyles_unstable: (state: DrawerOverlayState) => Dra // @public export const useTransitionPresence: (present: boolean, events?: UseTransitionPresenceEvents | undefined) => UseTransitionPresenceState; -// @public (undocumented) +// @public export type UseTransitionPresenceEvents = { onEntered?: () => void; onExited?: () => void; }; -// @public (undocumented) +// @public export type UseTransitionPresenceState = { ref: React_2.RefCallback; shouldRender: boolean; diff --git a/packages/react-components/react-drawer/src/hooks/useTransitionPresence.ts b/packages/react-components/react-drawer/src/hooks/useTransitionPresence.ts index a8d387edee7790..abf18257221917 100644 --- a/packages/react-components/react-drawer/src/hooks/useTransitionPresence.ts +++ b/packages/react-components/react-drawer/src/hooks/useTransitionPresence.ts @@ -3,17 +3,58 @@ import { useTimeout } from '@fluentui/react-utilities'; const noop = () => null; +/** + * State for useTransitionPresence hook. + */ export type UseTransitionPresenceState = { + /** + * Ref to the element that is being transitioned. + */ ref: React.RefCallback; + + /** + * Whether the element should be rendered in the DOM. + * This should be used to conditionally render the element. + */ shouldRender: boolean; + + /** + * Whether the element is visible in the DOM. + * This is true when the element is already rendered and transitioning from being hidden to visible. + */ visible: boolean; + + /** + * Whether the element is entering the DOM. + * This is true when the element is transitioning from not being rendered to being rendered. + */ entering: boolean; + + /** + * Whether the element is exiting the DOM. + * This is true when the element is transitioning from being rendered to not being rendered. + */ exiting: boolean; + + /** + * Whether the element is animating. + * This is true when the element is entering or exiting the DOM. + */ animating: boolean; }; +/** + * Events for useTransitionPresence hook. + */ export type UseTransitionPresenceEvents = { + /** + * Callback for after the element enters the DOM. + */ onEntered?: () => void; + + /** + * Callback for after the element exits the DOM. + */ onExited?: () => void; }; From 2fafeaeff6f0d7f59a9cf15d81fdf462da16614c Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Tue, 6 Jun 2023 15:56:24 +0200 Subject: [PATCH 023/111] fix: rename variables for better naming --- .../src/hooks/useTransitionPresence.ts | 75 +++++++++++-------- 1 file changed, 45 insertions(+), 30 deletions(-) diff --git a/packages/react-components/react-drawer/src/hooks/useTransitionPresence.ts b/packages/react-components/react-drawer/src/hooks/useTransitionPresence.ts index abf18257221917..557a7fd6c22f47 100644 --- a/packages/react-components/react-drawer/src/hooks/useTransitionPresence.ts +++ b/packages/react-components/react-drawer/src/hooks/useTransitionPresence.ts @@ -58,6 +58,21 @@ export type UseTransitionPresenceEvents = { onExited?: () => void; }; +/** + * Returns CSS styles of the given node. + * @param node - DOM node. + * @returns - CSS styles. + */ +const getStyleComputedProperty = (node: HTMLElement): Partial => { + if (node.nodeType !== 1) { + return {}; + } + + const window = node.ownerDocument?.defaultView; + + return window!.getComputedStyle(node, null); +}; + /** * @internal * Converts a CSS duration string to milliseconds. @@ -134,49 +149,49 @@ export const useTransitionPresence = ( return; } - computedStylesRef.current = window?.getComputedStyle(node); + computedStylesRef.current = getStyleComputedProperty(node) as CSSStyleDeclaration; setCurrentElement(node); }, []); const notCurrentElement = React.useCallback((target: TElement) => target !== currentElement, [currentElement]); - const onEntering = React.useCallback(() => { + const onFinishedTransition = React.useCallback(() => { + setEntering(false); + setExiting(false); + }, []); + + const onStartEntering = React.useCallback(() => { setEntering(true); setVisible(true); }, []); - const onExiting = React.useCallback(() => { - setExiting(true); - }, []); + const onFinishedEntering = React.useCallback(() => { + onFinishedTransition(); + onEntered(); + }, [onEntered, onFinishedTransition]); - const onFinishedAnimating = React.useCallback(() => { - setEntering(false); - setExiting(false); + const onStartExiting = React.useCallback(() => { + setExiting(true); }, []); - const onEnterAnimationEnd = React.useCallback(() => { - onFinishedAnimating(); - onEntered(); - }, [onEntered, onFinishedAnimating]); - - const onExitAnimationEnd = React.useCallback(() => { - onFinishedAnimating(); + const onFinishedExiting = React.useCallback(() => { + onFinishedTransition(); setVisible(false); setShouldRender(false); onExited(); - }, [onExited, onFinishedAnimating]); + }, [onExited, onFinishedTransition]); - const onCanceled = React.useCallback( + const onTransitionCanceled = React.useCallback( ({ target }) => { if (notCurrentElement(target)) { return; } - onFinishedAnimating(); + onFinishedTransition(); setVisible(present); setShouldRender(present); }, - [notCurrentElement, onFinishedAnimating, present], + [notCurrentElement, onFinishedTransition, present], ); React.useEffect(() => { @@ -186,10 +201,10 @@ export const useTransitionPresence = ( }, [present]); React.useEffect(() => { - currentElement?.addEventListener('transitioncancel', onCanceled); + currentElement?.addEventListener('transitioncancel', onTransitionCanceled); - return () => currentElement?.removeEventListener('transitioncancel', onCanceled); - }, [currentElement, onCanceled]); + return () => currentElement?.removeEventListener('transitioncancel', onTransitionCanceled); + }, [currentElement, onTransitionCanceled]); React.useEffect(() => { if (!currentElement) { @@ -205,9 +220,9 @@ export const useTransitionPresence = ( setShouldRender(present); } else { if (present) { - onEntering(); + onStartEntering(); } else { - onExiting(); + onStartExiting(); } /** @@ -217,9 +232,9 @@ export const useTransitionPresence = ( */ setAnimationTimeout(() => { if (present) { - onEnterAnimationEnd(); + onFinishedEntering(); } else { - onExitAnimationEnd(); + onFinishedExiting(); } }, duration + 1); } @@ -231,11 +246,11 @@ export const useTransitionPresence = ( }; }, [ currentElement, - onExitAnimationEnd, - onEnterAnimationEnd, + onFinishedExiting, + onFinishedEntering, present, - onEntering, - onExiting, + onStartEntering, + onStartExiting, setAnimationTimeout, clearAnimationTimeout, ]); From 0b5f2f672d1612c26cd27eacad9dc94ac0219b3c Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Tue, 6 Jun 2023 17:49:08 +0200 Subject: [PATCH 024/111] feat: make transitionPresence hook available in react-utilities and add animation to drawer backdrop --- .../etc/react-components.api.md | 6678 ++++++++++------- .../etc/react-components.unstable.api.md | 1131 +-- .../react-components/src/index.ts | 3 + .../react-drawer/etc/react-drawer.api.md | 19 - .../DrawerInline/useDrawerInline.ts | 8 +- .../DrawerOverlay/DrawerOverlay.types.ts | 3 +- .../DrawerOverlay/useDrawerOverlay.ts | 32 +- .../useDrawerOverlayStyles.styles.ts | 27 + .../react-drawer/src/index.ts | 3 - .../src/util/useDrawerBaseStyles.styles.ts | 3 + .../Drawer/DrawerCustomTransition.stories.tsx | 11 +- .../etc/react-utilities.api.md | 19 + .../react-utilities/src/hooks/index.ts | 1 + .../src/hooks/useTransitionPresence.ts | 0 .../react-utilities/src/index.ts | 9 +- 15 files changed, 4880 insertions(+), 3067 deletions(-) rename packages/react-components/{react-drawer => react-utilities}/src/hooks/useTransitionPresence.ts (100%) diff --git a/packages/react-components/react-components/etc/react-components.api.md b/packages/react-components/react-components/etc/react-components.api.md index 966d033f0de62f..2d445bf19c9525 100644 --- a/packages/react-components/react-components/etc/react-components.api.md +++ b/packages/react-components/react-components/etc/react-components.api.md @@ -4,1024 +4,32 @@ ```ts +/// + import { __css } from '@griffel/react'; import { __resetCSS } from '@griffel/react'; import { __resetStyles } from '@griffel/react'; import { __styles } from '@griffel/react'; -import { Accordion } from '@fluentui/react-accordion'; -import { accordionClassNames } from '@fluentui/react-accordion'; -import { AccordionContextValue } from '@fluentui/react-accordion'; -import { AccordionContextValues } from '@fluentui/react-accordion'; -import { AccordionHeader } from '@fluentui/react-accordion'; -import { accordionHeaderClassNames } from '@fluentui/react-accordion'; -import { AccordionHeaderContextValue } from '@fluentui/react-accordion'; -import { AccordionHeaderContextValues } from '@fluentui/react-accordion'; -import { AccordionHeaderExpandIconPosition } from '@fluentui/react-accordion'; -import { AccordionHeaderProps } from '@fluentui/react-accordion'; -import { AccordionHeaderSize } from '@fluentui/react-accordion'; -import { AccordionHeaderSlots } from '@fluentui/react-accordion'; -import { AccordionHeaderState } from '@fluentui/react-accordion'; -import { AccordionIndex } from '@fluentui/react-accordion'; -import { AccordionItem } from '@fluentui/react-accordion'; -import { accordionItemClassNames } from '@fluentui/react-accordion'; -import { AccordionItemContextValue } from '@fluentui/react-accordion'; -import { AccordionItemContextValues } from '@fluentui/react-accordion'; -import { AccordionItemProps } from '@fluentui/react-accordion'; -import { AccordionItemProvider } from '@fluentui/react-accordion'; -import { AccordionItemSlots } from '@fluentui/react-accordion'; -import { AccordionItemState } from '@fluentui/react-accordion'; -import { AccordionItemValue } from '@fluentui/react-accordion'; -import { AccordionPanel } from '@fluentui/react-accordion'; -import { accordionPanelClassNames } from '@fluentui/react-accordion'; -import { AccordionPanelProps } from '@fluentui/react-accordion'; -import { AccordionPanelSlots } from '@fluentui/react-accordion'; -import { AccordionPanelState } from '@fluentui/react-accordion'; -import { AccordionProps } from '@fluentui/react-accordion'; -import { AccordionProvider } from '@fluentui/react-accordion'; -import { AccordionSlots } from '@fluentui/react-accordion'; -import { AccordionState } from '@fluentui/react-accordion'; -import { AccordionToggleData } from '@fluentui/react-accordion'; -import { AccordionToggleEvent } from '@fluentui/react-accordion'; -import { AccordionToggleEventHandler } from '@fluentui/react-accordion'; -import { arrowHeights } from '@fluentui/react-popover'; -import { Avatar } from '@fluentui/react-avatar'; -import { avatarClassNames } from '@fluentui/react-avatar'; -import { AvatarGroup } from '@fluentui/react-avatar'; -import { avatarGroupClassNames } from '@fluentui/react-avatar'; -import { AvatarGroupContextValue } from '@fluentui/react-avatar'; -import { AvatarGroupContextValues } from '@fluentui/react-avatar'; -import { AvatarGroupItem } from '@fluentui/react-avatar'; -import { avatarGroupItemClassNames } from '@fluentui/react-avatar'; -import { AvatarGroupItemProps } from '@fluentui/react-avatar'; -import { AvatarGroupItemSlots } from '@fluentui/react-avatar'; -import { AvatarGroupItemState } from '@fluentui/react-avatar'; -import { AvatarGroupPopover } from '@fluentui/react-avatar'; -import { avatarGroupPopoverClassNames } from '@fluentui/react-avatar'; -import { AvatarGroupPopoverProps } from '@fluentui/react-avatar'; -import { AvatarGroupPopoverSlots } from '@fluentui/react-avatar'; -import { AvatarGroupPopoverState } from '@fluentui/react-avatar'; -import { AvatarGroupProps } from '@fluentui/react-avatar'; -import { AvatarGroupProvider } from '@fluentui/react-avatar'; -import { AvatarGroupSlots } from '@fluentui/react-avatar'; -import { AvatarGroupState } from '@fluentui/react-avatar'; -import { AvatarNamedColor } from '@fluentui/react-avatar'; -import { AvatarProps } from '@fluentui/react-avatar'; -import { AvatarSize } from '@fluentui/react-avatar'; -import { AvatarSizes } from '@fluentui/react-avatar'; -import { AvatarSlots } from '@fluentui/react-avatar'; -import { AvatarState } from '@fluentui/react-avatar'; -import { Badge } from '@fluentui/react-badge'; -import { badgeClassNames } from '@fluentui/react-badge'; -import { BadgeProps } from '@fluentui/react-badge'; -import { BadgeSlots } from '@fluentui/react-badge'; -import { BadgeState } from '@fluentui/react-badge'; -import { Body1 } from '@fluentui/react-text'; -import { body1ClassNames } from '@fluentui/react-text'; -import { Body1Strong } from '@fluentui/react-text'; -import { body1StrongClassNames } from '@fluentui/react-text'; -import { Body1Stronger } from '@fluentui/react-text'; -import { body1StrongerClassNames } from '@fluentui/react-text'; -import { Body2 } from '@fluentui/react-text'; -import { body2ClassNames } from '@fluentui/react-text'; -import { BorderRadiusTokens } from '@fluentui/react-theme'; -import { BrandVariants } from '@fluentui/react-theme'; -import { Button } from '@fluentui/react-button'; -import { buttonClassNames } from '@fluentui/react-button'; -import { ButtonProps } from '@fluentui/react-button'; -import { ButtonSlots } from '@fluentui/react-button'; -import { ButtonState } from '@fluentui/react-button'; -import { Caption1 } from '@fluentui/react-text'; -import { caption1ClassNames } from '@fluentui/react-text'; -import { Caption1Strong } from '@fluentui/react-text'; -import { caption1StrongClassNames } from '@fluentui/react-text'; -import { Caption1Stronger } from '@fluentui/react-text'; -import { caption1StrongerClassNames } from '@fluentui/react-text'; -import { Caption2 } from '@fluentui/react-text'; -import { caption2ClassNames } from '@fluentui/react-text'; -import { Caption2Strong } from '@fluentui/react-text'; -import { caption2StrongClassNames } from '@fluentui/react-text'; -import { Card } from '@fluentui/react-card'; -import { cardClassNames } from '@fluentui/react-card'; -import { cardCSSVars } from '@fluentui/react-card'; -import { CardFooter } from '@fluentui/react-card'; -import { cardFooterClassNames } from '@fluentui/react-card'; -import { CardFooterProps } from '@fluentui/react-card'; -import { CardFooterSlots } from '@fluentui/react-card'; -import { CardFooterState } from '@fluentui/react-card'; -import { CardHeader } from '@fluentui/react-card'; -import { cardHeaderClassNames } from '@fluentui/react-card'; -import { cardHeaderCSSVars } from '@fluentui/react-card'; -import { CardHeaderProps } from '@fluentui/react-card'; -import { CardHeaderSlots } from '@fluentui/react-card'; -import { CardHeaderState } from '@fluentui/react-card'; -import { CardPreview } from '@fluentui/react-card'; -import { cardPreviewClassNames } from '@fluentui/react-card'; -import { CardPreviewProps } from '@fluentui/react-card'; -import { CardPreviewSlots } from '@fluentui/react-card'; -import { CardPreviewState } from '@fluentui/react-card'; -import { CardProps } from '@fluentui/react-card'; -import { CardSlots } from '@fluentui/react-card'; -import { CardState } from '@fluentui/react-card'; -import { Checkbox } from '@fluentui/react-checkbox'; -import { checkboxClassNames } from '@fluentui/react-checkbox'; -import { CheckboxOnChangeData } from '@fluentui/react-checkbox'; -import { CheckboxProps } from '@fluentui/react-checkbox'; -import { CheckboxSlots } from '@fluentui/react-checkbox'; -import { CheckboxState } from '@fluentui/react-checkbox'; -import { ColorPaletteTokens } from '@fluentui/react-theme'; -import { ColorTokens } from '@fluentui/react-theme'; -import { Combobox } from '@fluentui/react-combobox'; -import { comboboxClassNames } from '@fluentui/react-combobox'; -import { ComboboxContextValue } from '@fluentui/react-combobox'; -import { ComboboxContextValues } from '@fluentui/react-combobox'; -import { ComboboxOpenChangeData } from '@fluentui/react-combobox'; -import { ComboboxOpenEvents } from '@fluentui/react-combobox'; -import { ComboboxProps } from '@fluentui/react-combobox'; -import { ComboboxProvider } from '@fluentui/react-combobox'; -import { ComboboxSlots } from '@fluentui/react-combobox'; -import { ComboboxState } from '@fluentui/react-combobox'; -import { ComponentProps } from '@fluentui/react-utilities'; -import { ComponentState } from '@fluentui/react-utilities'; -import { CompoundButton } from '@fluentui/react-button'; -import { compoundButtonClassNames } from '@fluentui/react-button'; -import { CompoundButtonProps } from '@fluentui/react-button'; -import { CompoundButtonSlots } from '@fluentui/react-button'; -import { CompoundButtonState } from '@fluentui/react-button'; -import { CounterBadge } from '@fluentui/react-badge'; -import { counterBadgeClassNames } from '@fluentui/react-badge'; -import { CounterBadgeProps } from '@fluentui/react-badge'; -import { CounterBadgeState } from '@fluentui/react-badge'; -import { createCustomFocusIndicatorStyle } from '@fluentui/react-tabster'; -import { CreateCustomFocusIndicatorStyleOptions } from '@fluentui/react-tabster'; -import { createDarkTheme } from '@fluentui/react-theme'; import { createDOMRenderer } from '@griffel/react'; -import { createFocusOutlineStyle } from '@fluentui/react-tabster'; -import { CreateFocusOutlineStyleOptions } from '@fluentui/react-tabster'; -import { createHighContrastTheme } from '@fluentui/react-theme'; -import { createLightTheme } from '@fluentui/react-theme'; -import { createTableColumn } from '@fluentui/react-table'; -import { CreateTableColumnOptions } from '@fluentui/react-table'; -import { createTeamsDarkTheme } from '@fluentui/react-theme'; -import { CurveTokens } from '@fluentui/react-theme'; -import { DATA_OVERFLOW_ITEM } from '@fluentui/react-overflow'; -import { DATA_OVERFLOW_MENU } from '@fluentui/react-overflow'; -import { DATA_OVERFLOWING } from '@fluentui/react-overflow'; -import { DataGrid } from '@fluentui/react-table'; -import { DataGridBody } from '@fluentui/react-table'; -import { dataGridBodyClassNames } from '@fluentui/react-table'; -import { DataGridBodyProps } from '@fluentui/react-table'; -import { DataGridBodySlots } from '@fluentui/react-table'; -import { DataGridBodyState } from '@fluentui/react-table'; -import { DataGridCell } from '@fluentui/react-table'; -import { dataGridCellClassNames } from '@fluentui/react-table'; -import { DataGridCellProps } from '@fluentui/react-table'; -import { DataGridCellSlots } from '@fluentui/react-table'; -import { DataGridCellState } from '@fluentui/react-table'; -import { dataGridClassNames } from '@fluentui/react-table'; -import { DataGridContextValue } from '@fluentui/react-table'; -import { DataGridContextValues } from '@fluentui/react-table'; -import { DataGridHeader } from '@fluentui/react-table'; -import { DataGridHeaderCell } from '@fluentui/react-table'; -import { dataGridHeaderCellClassNames } from '@fluentui/react-table'; -import { DataGridHeaderCellProps } from '@fluentui/react-table'; -import { DataGridHeaderCellSlots } from '@fluentui/react-table'; -import { DataGridHeaderCellState } from '@fluentui/react-table'; -import { dataGridHeaderClassNames } from '@fluentui/react-table'; -import { DataGridHeaderProps } from '@fluentui/react-table'; -import { DataGridHeaderSlots } from '@fluentui/react-table'; -import { DataGridHeaderState } from '@fluentui/react-table'; -import { DataGridProps } from '@fluentui/react-table'; -import { DataGridRow } from '@fluentui/react-table'; -import { dataGridRowClassNames } from '@fluentui/react-table'; -import { DataGridRowProps } from '@fluentui/react-table'; -import { DataGridRowSlots } from '@fluentui/react-table'; -import { DataGridRowState } from '@fluentui/react-table'; -import { DataGridSelectionCell } from '@fluentui/react-table'; -import { dataGridSelectionCellClassNames } from '@fluentui/react-table'; -import { DataGridSelectionCellProps } from '@fluentui/react-table'; -import { DataGridSelectionCellSlots } from '@fluentui/react-table'; -import { DataGridSelectionCellState } from '@fluentui/react-table'; -import { DataGridSlots } from '@fluentui/react-table'; -import { DataGridState } from '@fluentui/react-table'; -import { Dialog } from '@fluentui/react-dialog'; -import { DialogActions } from '@fluentui/react-dialog'; -import { dialogActionsClassNames } from '@fluentui/react-dialog'; -import { DialogActionsPosition } from '@fluentui/react-dialog'; -import { DialogActionsProps } from '@fluentui/react-dialog'; -import { DialogActionsSlots } from '@fluentui/react-dialog'; -import { DialogActionsState } from '@fluentui/react-dialog'; -import { DialogBody } from '@fluentui/react-dialog'; -import { dialogBodyClassNames } from '@fluentui/react-dialog'; -import { DialogBodyProps } from '@fluentui/react-dialog'; -import { DialogBodySlots } from '@fluentui/react-dialog'; -import { DialogBodyState } from '@fluentui/react-dialog'; -import { DialogContent } from '@fluentui/react-dialog'; -import { dialogContentClassNames } from '@fluentui/react-dialog'; -import { DialogContentProps } from '@fluentui/react-dialog'; -import { DialogContentSlots } from '@fluentui/react-dialog'; -import { DialogContentState } from '@fluentui/react-dialog'; -import { DialogOpenChangeData } from '@fluentui/react-dialog'; -import { DialogOpenChangeEvent } from '@fluentui/react-dialog'; -import { DialogProps } from '@fluentui/react-dialog'; -import { DialogSlots } from '@fluentui/react-dialog'; -import { DialogState } from '@fluentui/react-dialog'; -import { DialogSurface } from '@fluentui/react-dialog'; -import { dialogSurfaceClassNames } from '@fluentui/react-dialog'; -import { DialogSurfaceProps } from '@fluentui/react-dialog'; -import { DialogSurfaceSlots } from '@fluentui/react-dialog'; -import { DialogSurfaceState } from '@fluentui/react-dialog'; -import { DialogTitle } from '@fluentui/react-dialog'; -import { dialogTitleClassNames } from '@fluentui/react-dialog'; -import { DialogTitleProps } from '@fluentui/react-dialog'; -import { DialogTitleSlots } from '@fluentui/react-dialog'; -import { DialogTitleState } from '@fluentui/react-dialog'; -import { DialogTrigger } from '@fluentui/react-dialog'; -import { DialogTriggerAction } from '@fluentui/react-dialog'; -import { DialogTriggerChildProps } from '@fluentui/react-dialog'; -import { DialogTriggerProps } from '@fluentui/react-dialog'; -import { DialogTriggerState } from '@fluentui/react-dialog'; -import { Display } from '@fluentui/react-text'; -import { displayClassNames } from '@fluentui/react-text'; -import { Divider } from '@fluentui/react-divider'; -import { dividerClassNames } from '@fluentui/react-divider'; -import { DividerProps } from '@fluentui/react-divider'; -import { DividerSlots } from '@fluentui/react-divider'; -import { DividerState } from '@fluentui/react-divider'; -import { Dropdown } from '@fluentui/react-combobox'; -import { dropdownClassNames } from '@fluentui/react-combobox'; -import { DropdownContextValues } from '@fluentui/react-combobox'; -import { DropdownOpenChangeData } from '@fluentui/react-combobox'; -import { DropdownOpenEvents } from '@fluentui/react-combobox'; -import { DropdownProps } from '@fluentui/react-combobox'; -import { DropdownSlots } from '@fluentui/react-combobox'; -import { DropdownState } from '@fluentui/react-combobox'; -import { DurationTokens } from '@fluentui/react-theme'; -import { Field } from '@fluentui/react-field'; -import { fieldClassNames } from '@fluentui/react-field'; -import { FieldContextProvider } from '@fluentui/react-field'; -import { FieldContextValue } from '@fluentui/react-field'; -import { FieldContextValues } from '@fluentui/react-field'; -import { FieldControlProps } from '@fluentui/react-field'; -import { FieldControlPropsOptions } from '@fluentui/react-field'; -import { FieldProps } from '@fluentui/react-field'; -import { FieldSlots } from '@fluentui/react-field'; -import { FieldState } from '@fluentui/react-field'; -import { FluentProvider } from '@fluentui/react-provider'; -import { fluentProviderClassNames } from '@fluentui/react-provider'; -import { FluentProviderContextValues } from '@fluentui/react-provider'; -import { FluentProviderCustomStyleHooks } from '@fluentui/react-provider'; -import { FluentProviderProps } from '@fluentui/react-provider'; -import { FluentProviderSlots } from '@fluentui/react-provider'; -import { FluentProviderState } from '@fluentui/react-provider'; -import { FontFamilyTokens } from '@fluentui/react-theme'; -import { FontSizeTokens } from '@fluentui/react-theme'; -import { FontWeightTokens } from '@fluentui/react-theme'; -import { ForwardRefComponent } from '@fluentui/react-utilities'; -import { getNativeElementProps } from '@fluentui/react-utilities'; -import { getPartitionedNativeProps } from '@fluentui/react-utilities'; -import { getSlots } from '@fluentui/react-utilities'; +import { FC } from 'react'; +import type { FunctionComponent } from 'react'; import { GriffelRenderer } from '@griffel/react'; import { GriffelStyle } from '@griffel/react'; -import { HorizontalSpacingTokens } from '@fluentui/react-theme'; -import { IdPrefixProvider } from '@fluentui/react-utilities'; -import { Image as Image_2 } from '@fluentui/react-image'; -import { imageClassNames } from '@fluentui/react-image'; -import { ImageProps } from '@fluentui/react-image'; -import { ImageSlots } from '@fluentui/react-image'; -import { ImageState } from '@fluentui/react-image'; -import { Input } from '@fluentui/react-input'; -import { inputClassNames } from '@fluentui/react-input'; -import { InputOnChangeData } from '@fluentui/react-input'; -import { InputProps } from '@fluentui/react-input'; -import { InputSlots } from '@fluentui/react-input'; -import { InputState } from '@fluentui/react-input'; -import { Label } from '@fluentui/react-label'; -import { labelClassNames } from '@fluentui/react-label'; -import { LabelProps } from '@fluentui/react-label'; -import { LabelSlots } from '@fluentui/react-label'; -import { LabelState } from '@fluentui/react-label'; -import { LargeTitle } from '@fluentui/react-text'; -import { largeTitleClassNames } from '@fluentui/react-text'; -import { LineHeightTokens } from '@fluentui/react-theme'; -import { Link } from '@fluentui/react-link'; -import { linkClassNames } from '@fluentui/react-link'; -import { LinkProps } from '@fluentui/react-link'; -import { LinkSlots } from '@fluentui/react-link'; -import { LinkState } from '@fluentui/react-link'; -import { Listbox } from '@fluentui/react-combobox'; -import { listboxClassNames } from '@fluentui/react-combobox'; -import { ListboxContextValue } from '@fluentui/react-combobox'; -import { ListboxContextValues } from '@fluentui/react-combobox'; -import { ListboxProps } from '@fluentui/react-combobox'; -import { ListboxProvider } from '@fluentui/react-combobox'; -import { ListboxSlots } from '@fluentui/react-combobox'; -import { ListboxState } from '@fluentui/react-combobox'; +import { JSXElementConstructor } from 'react'; import { makeResetStyles } from '@griffel/react'; import { makeStaticStyles } from '@griffel/react'; import { makeStyles } from '@griffel/react'; -import { Menu } from '@fluentui/react-menu'; -import { MenuButton } from '@fluentui/react-button'; -import { menuButtonClassNames } from '@fluentui/react-button'; -import { MenuButtonProps } from '@fluentui/react-button'; -import { MenuButtonSlots } from '@fluentui/react-button'; -import { MenuButtonState } from '@fluentui/react-button'; -import { MenuCheckedValueChangeData } from '@fluentui/react-menu'; -import { MenuCheckedValueChangeEvent } from '@fluentui/react-menu'; -import { MenuContextValue } from '@fluentui/react-menu'; -import { MenuContextValues } from '@fluentui/react-menu'; -import { MenuDivider } from '@fluentui/react-menu'; -import { menuDividerClassNames } from '@fluentui/react-menu'; -import { MenuDividerProps } from '@fluentui/react-menu'; -import { MenuDividerSlots } from '@fluentui/react-menu'; -import { MenuDividerState } from '@fluentui/react-menu'; -import { MenuGroup } from '@fluentui/react-menu'; -import { menuGroupClassNames } from '@fluentui/react-menu'; -import { MenuGroupContextProvider } from '@fluentui/react-menu'; -import { MenuGroupContextValue } from '@fluentui/react-menu'; -import { MenuGroupContextValues } from '@fluentui/react-menu'; -import { MenuGroupHeader } from '@fluentui/react-menu'; -import { menuGroupHeaderClassNames } from '@fluentui/react-menu'; -import { MenuGroupHeaderProps } from '@fluentui/react-menu'; -import { MenuGroupHeaderSlots } from '@fluentui/react-menu'; -import { MenuGroupHeaderState } from '@fluentui/react-menu'; -import { MenuGroupProps } from '@fluentui/react-menu'; -import { MenuGroupSlots } from '@fluentui/react-menu'; -import { MenuGroupState } from '@fluentui/react-menu'; -import { MenuItem } from '@fluentui/react-menu'; -import { MenuItemCheckbox } from '@fluentui/react-menu'; -import { menuItemCheckboxClassNames } from '@fluentui/react-menu'; -import { MenuItemCheckboxProps } from '@fluentui/react-menu'; -import { MenuItemCheckboxState } from '@fluentui/react-menu'; -import { menuItemClassNames } from '@fluentui/react-menu'; -import { MenuItemProps } from '@fluentui/react-menu'; -import { MenuItemRadio } from '@fluentui/react-menu'; -import { menuItemRadioClassNames } from '@fluentui/react-menu'; -import { MenuItemRadioProps } from '@fluentui/react-menu'; -import { MenuItemRadioState } from '@fluentui/react-menu'; -import { MenuItemSelectableProps } from '@fluentui/react-menu'; -import { MenuItemSelectableState } from '@fluentui/react-menu'; -import { MenuItemSlots } from '@fluentui/react-menu'; -import { MenuItemState } from '@fluentui/react-menu'; -import { MenuList } from '@fluentui/react-menu'; -import { menuListClassNames } from '@fluentui/react-menu'; -import { MenuListContextValue } from '@fluentui/react-menu'; -import { MenuListContextValues } from '@fluentui/react-menu'; -import { MenuListProps } from '@fluentui/react-menu'; -import { MenuListProvider } from '@fluentui/react-menu'; -import { MenuListSlots } from '@fluentui/react-menu'; -import { MenuListState } from '@fluentui/react-menu'; -import { MenuOpenChangeData } from '@fluentui/react-menu'; -import { MenuOpenEvent } from '@fluentui/react-menu'; -import { MenuOpenEvents } from '@fluentui/react-menu'; -import { MenuPopover } from '@fluentui/react-menu'; -import { menuPopoverClassNames } from '@fluentui/react-menu'; -import { MenuPopoverProps } from '@fluentui/react-menu'; -import { MenuPopoverSlots } from '@fluentui/react-menu'; -import { MenuPopoverState } from '@fluentui/react-menu'; -import { MenuProps } from '@fluentui/react-menu'; -import { MenuProvider } from '@fluentui/react-menu'; -import { MenuSlots } from '@fluentui/react-menu'; -import { MenuSplitGroup } from '@fluentui/react-menu'; -import { menuSplitGroupClassNames } from '@fluentui/react-menu'; -import { MenuSplitGroupProps } from '@fluentui/react-menu'; -import { MenuSplitGroupSlots } from '@fluentui/react-menu'; -import { MenuSplitGroupState } from '@fluentui/react-menu'; -import { MenuState } from '@fluentui/react-menu'; -import { MenuTrigger } from '@fluentui/react-menu'; -import { MenuTriggerChildProps } from '@fluentui/react-menu'; -import { MenuTriggerContextProvider } from '@fluentui/react-menu'; -import { MenuTriggerProps } from '@fluentui/react-menu'; -import { MenuTriggerState } from '@fluentui/react-menu'; import { mergeClasses } from '@griffel/react'; -import { OnOpenChangeData } from '@fluentui/react-popover'; -import { OnVisibleChangeData } from '@fluentui/react-tooltip'; -import { OpenPopoverEvents } from '@fluentui/react-popover'; -import { Option as Option_2 } from '@fluentui/react-combobox'; -import { optionClassNames } from '@fluentui/react-combobox'; -import { OptionGroup } from '@fluentui/react-combobox'; -import { optionGroupClassNames } from '@fluentui/react-combobox'; -import { OptionGroupProps } from '@fluentui/react-combobox'; -import { OptionGroupSlots } from '@fluentui/react-combobox'; -import { OptionGroupState } from '@fluentui/react-combobox'; -import { OptionProps } from '@fluentui/react-combobox'; -import { OptionSlots } from '@fluentui/react-combobox'; -import { OptionState } from '@fluentui/react-combobox'; -import { Overflow } from '@fluentui/react-overflow'; -import { OverflowItem } from '@fluentui/react-overflow'; -import { OverflowItemProps } from '@fluentui/react-overflow'; -import { OverflowProps } from '@fluentui/react-overflow'; -import { PartialTheme } from '@fluentui/react-theme'; -import { PartitionAvatarGroupItems } from '@fluentui/react-avatar'; -import { partitionAvatarGroupItems } from '@fluentui/react-avatar'; -import { PartitionAvatarGroupItemsOptions } from '@fluentui/react-avatar'; -import { Persona } from '@fluentui/react-persona'; -import { personaClassNames } from '@fluentui/react-persona'; -import { PersonaProps } from '@fluentui/react-persona'; -import { PersonaSlots } from '@fluentui/react-persona'; -import { PersonaState } from '@fluentui/react-persona'; -import { Popover } from '@fluentui/react-popover'; -import { PopoverContextValue } from '@fluentui/react-popover'; -import { PopoverProps } from '@fluentui/react-popover'; -import { PopoverProvider } from '@fluentui/react-popover'; -import { PopoverSize } from '@fluentui/react-popover'; -import { PopoverState } from '@fluentui/react-popover'; -import { PopoverSurface } from '@fluentui/react-popover'; -import { popoverSurfaceClassNames } from '@fluentui/react-popover'; -import { PopoverSurfaceProps } from '@fluentui/react-popover'; -import { PopoverSurfaceSlots } from '@fluentui/react-popover'; -import { PopoverSurfaceState } from '@fluentui/react-popover'; -import { PopoverTrigger } from '@fluentui/react-popover'; -import { PopoverTriggerChildProps } from '@fluentui/react-popover'; -import { PopoverTriggerProps } from '@fluentui/react-popover'; -import { PopoverTriggerState } from '@fluentui/react-popover'; -import { Portal } from '@fluentui/react-portal'; -import { PortalProps } from '@fluentui/react-portal'; -import { PortalState } from '@fluentui/react-portal'; -import { PositioningImperativeRef } from '@fluentui/react-positioning'; -import { PositioningProps } from '@fluentui/react-positioning'; -import { PositioningShorthand } from '@fluentui/react-positioning'; -import { PositioningShorthandValue } from '@fluentui/react-positioning'; -import { PositioningVirtualElement } from '@fluentui/react-positioning'; -import { PresenceBadge } from '@fluentui/react-badge'; -import { presenceBadgeClassNames } from '@fluentui/react-badge'; -import { PresenceBadgeProps } from '@fluentui/react-badge'; -import { PresenceBadgeState } from '@fluentui/react-badge'; -import { PresenceBadgeStatus } from '@fluentui/react-badge'; -import { ProgressBar } from '@fluentui/react-progress'; -import { progressBarClassNames } from '@fluentui/react-progress'; -import { ProgressBarProps } from '@fluentui/react-progress'; -import { ProgressBarSlots } from '@fluentui/react-progress'; -import { ProgressBarState } from '@fluentui/react-progress'; -import { Radio } from '@fluentui/react-radio'; -import { radioClassNames } from '@fluentui/react-radio'; -import { RadioGroup } from '@fluentui/react-radio'; -import { radioGroupClassNames } from '@fluentui/react-radio'; -import { RadioGroupContextValue } from '@fluentui/react-radio'; -import { RadioGroupContextValues } from '@fluentui/react-radio'; -import { RadioGroupOnChangeData } from '@fluentui/react-radio'; -import { RadioGroupProps } from '@fluentui/react-radio'; -import { RadioGroupProvider } from '@fluentui/react-radio'; -import { RadioGroupSlots } from '@fluentui/react-radio'; -import { RadioGroupState } from '@fluentui/react-radio'; -import { RadioOnChangeData } from '@fluentui/react-radio'; -import { RadioProps } from '@fluentui/react-radio'; -import { RadioSlots } from '@fluentui/react-radio'; -import { RadioState } from '@fluentui/react-radio'; -import { RegisterTabEventHandler } from '@fluentui/react-tabs'; -import { renderAccordion_unstable } from '@fluentui/react-accordion'; -import { renderAccordionHeader_unstable } from '@fluentui/react-accordion'; -import { renderAccordionItem_unstable } from '@fluentui/react-accordion'; -import { renderAccordionPanel_unstable } from '@fluentui/react-accordion'; -import { renderAvatar_unstable } from '@fluentui/react-avatar'; -import { renderAvatarGroup_unstable } from '@fluentui/react-avatar'; -import { renderAvatarGroupItem_unstable } from '@fluentui/react-avatar'; -import { renderAvatarGroupPopover_unstable } from '@fluentui/react-avatar'; -import { renderBadge_unstable } from '@fluentui/react-badge'; -import { renderButton_unstable } from '@fluentui/react-button'; -import { renderCard_unstable } from '@fluentui/react-card'; -import { renderCardFooter_unstable } from '@fluentui/react-card'; -import { renderCardHeader_unstable } from '@fluentui/react-card'; -import { renderCardPreview_unstable } from '@fluentui/react-card'; -import { renderCheckbox_unstable } from '@fluentui/react-checkbox'; -import { renderCombobox_unstable } from '@fluentui/react-combobox'; -import { renderCompoundButton_unstable } from '@fluentui/react-button'; -import { renderDataGrid_unstable } from '@fluentui/react-table'; -import { renderDataGridBody_unstable } from '@fluentui/react-table'; -import { renderDataGridCell_unstable } from '@fluentui/react-table'; -import { renderDataGridHeader_unstable } from '@fluentui/react-table'; -import { renderDataGridHeaderCell_unstable } from '@fluentui/react-table'; -import { renderDataGridRow_unstable } from '@fluentui/react-table'; -import { renderDataGridSelectionCell_unstable } from '@fluentui/react-table'; -import { renderDialog_unstable } from '@fluentui/react-dialog'; -import { renderDialogActions_unstable } from '@fluentui/react-dialog'; -import { renderDialogBody_unstable } from '@fluentui/react-dialog'; -import { renderDialogContent_unstable } from '@fluentui/react-dialog'; -import { renderDialogSurface_unstable } from '@fluentui/react-dialog'; -import { renderDialogTitle_unstable } from '@fluentui/react-dialog'; -import { renderDialogTrigger_unstable } from '@fluentui/react-dialog'; -import { renderDivider_unstable } from '@fluentui/react-divider'; -import { renderDropdown_unstable } from '@fluentui/react-combobox'; +import { Provider } from 'react'; +import { ProviderProps } from 'react'; +import * as React_2 from 'react'; +import { ReactElement } from 'react'; +import { ReactNode } from 'react'; +import type { RefObject } from 'react'; import { RendererProvider } from '@griffel/react'; -import { renderField_unstable } from '@fluentui/react-field'; -import { renderFluentProvider_unstable } from '@fluentui/react-provider'; -import { renderImage_unstable } from '@fluentui/react-image'; -import { renderInput_unstable } from '@fluentui/react-input'; -import { renderLabel_unstable } from '@fluentui/react-label'; -import { renderLink_unstable } from '@fluentui/react-link'; -import { renderListbox_unstable } from '@fluentui/react-combobox'; -import { renderMenu_unstable } from '@fluentui/react-menu'; -import { renderMenuButton_unstable } from '@fluentui/react-button'; -import { renderMenuDivider_unstable } from '@fluentui/react-menu'; -import { renderMenuGroup_unstable } from '@fluentui/react-menu'; -import { renderMenuGroupHeader_unstable } from '@fluentui/react-menu'; -import { renderMenuItem_unstable } from '@fluentui/react-menu'; -import { renderMenuItemCheckbox_unstable } from '@fluentui/react-menu'; -import { renderMenuItemRadio_unstable } from '@fluentui/react-menu'; -import { renderMenuList_unstable } from '@fluentui/react-menu'; -import { renderMenuPopover_unstable } from '@fluentui/react-menu'; -import { renderMenuSplitGroup_unstable } from '@fluentui/react-menu'; -import { renderMenuTrigger_unstable } from '@fluentui/react-menu'; -import { renderOption_unstable } from '@fluentui/react-combobox'; -import { renderOptionGroup_unstable } from '@fluentui/react-combobox'; -import { renderPersona_unstable } from '@fluentui/react-persona'; -import { renderPopover_unstable } from '@fluentui/react-popover'; -import { renderPopoverSurface_unstable } from '@fluentui/react-popover'; -import { renderPopoverTrigger_unstable } from '@fluentui/react-popover'; -import { renderPortal_unstable } from '@fluentui/react-portal'; -import { renderProgressBar_unstable } from '@fluentui/react-progress'; -import { renderRadio_unstable } from '@fluentui/react-radio'; -import { renderRadioGroup_unstable } from '@fluentui/react-radio'; -import { renderSelect_unstable } from '@fluentui/react-select'; -import { renderSkeleton_unstable } from '@fluentui/react-skeleton'; -import { renderSkeletonItem_unstable } from '@fluentui/react-skeleton'; -import { renderSlider_unstable } from '@fluentui/react-slider'; -import { renderSpinButton_unstable } from '@fluentui/react-spinbutton'; -import { renderSpinner_unstable } from '@fluentui/react-spinner'; -import { renderSplitButton_unstable } from '@fluentui/react-button'; -import { renderSwitch_unstable } from '@fluentui/react-switch'; -import { renderTab_unstable } from '@fluentui/react-tabs'; -import { renderTable_unstable } from '@fluentui/react-table'; -import { renderTableBody_unstable } from '@fluentui/react-table'; -import { renderTableCell_unstable } from '@fluentui/react-table'; -import { renderTableCellActions_unstable } from '@fluentui/react-table'; -import { renderTableCellLayout_unstable } from '@fluentui/react-table'; -import { renderTableHeader_unstable } from '@fluentui/react-table'; -import { renderTableHeaderCell_unstable } from '@fluentui/react-table'; -import { renderTableResizeHandle_unstable } from '@fluentui/react-table'; -import { renderTableRow_unstable } from '@fluentui/react-table'; -import { renderTableSelectionCell_unstable } from '@fluentui/react-table'; -import { renderTabList_unstable } from '@fluentui/react-tabs'; -import { renderText_unstable } from '@fluentui/react-text'; -import { renderTextarea_unstable } from '@fluentui/react-textarea'; -import { renderToggleButton_unstable } from '@fluentui/react-button'; -import { renderToolbar_unstable } from '@fluentui/react-toolbar'; -import { renderToolbarGroup_unstable } from '@fluentui/react-toolbar'; -import { renderTooltip_unstable } from '@fluentui/react-tooltip'; import { renderToStyleElements } from '@griffel/react'; -import { resetIdsForTests } from '@fluentui/react-utilities'; -import { resolveShorthand } from '@fluentui/react-utilities'; -import { ResolveShorthandFunction } from '@fluentui/react-utilities'; -import { ResolveShorthandOptions } from '@fluentui/react-utilities'; -import { Select } from '@fluentui/react-select'; -import { SelectableHandler } from '@fluentui/react-menu'; -import { selectClassNames } from '@fluentui/react-select'; -import { SelectOnChangeData } from '@fluentui/react-select'; -import { SelectProps } from '@fluentui/react-select'; -import { SelectSlots } from '@fluentui/react-select'; -import { SelectState } from '@fluentui/react-select'; -import { SelectTabData } from '@fluentui/react-tabs'; -import { SelectTabEvent } from '@fluentui/react-tabs'; -import { SelectTabEventHandler } from '@fluentui/react-tabs'; -import { ShadowBrandTokens } from '@fluentui/react-theme'; -import { ShadowTokens } from '@fluentui/react-theme'; import { shorthands } from '@griffel/react'; -import { Skeleton } from '@fluentui/react-skeleton'; -import { skeletonClassNames } from '@fluentui/react-skeleton'; -import { SkeletonContextProvider } from '@fluentui/react-skeleton'; -import { SkeletonContextValue } from '@fluentui/react-skeleton'; -import { SkeletonItem } from '@fluentui/react-skeleton'; -import { skeletonItemClassNames } from '@fluentui/react-skeleton'; -import { SkeletonItemProps } from '@fluentui/react-skeleton'; -import { SkeletonItemSlots } from '@fluentui/react-skeleton'; -import { SkeletonItemState } from '@fluentui/react-skeleton'; -import { SkeletonProps } from '@fluentui/react-skeleton'; -import { SkeletonSlots } from '@fluentui/react-skeleton'; -import { SkeletonState } from '@fluentui/react-skeleton'; -import { Slider } from '@fluentui/react-slider'; -import { sliderClassNames } from '@fluentui/react-slider'; -import { sliderCSSVars } from '@fluentui/react-slider'; -import { SliderOnChangeData } from '@fluentui/react-slider'; -import { SliderProps } from '@fluentui/react-slider'; -import { SliderSlots } from '@fluentui/react-slider'; -import { SliderState } from '@fluentui/react-slider'; -import { Slot } from '@fluentui/react-utilities'; -import { SlotClassNames } from '@fluentui/react-utilities'; -import { SlotPropsRecord } from '@fluentui/react-utilities'; -import { SlotRenderFunction } from '@fluentui/react-utilities'; -import { SortDirection } from '@fluentui/react-table'; -import { SpacingTokens } from '@fluentui/react-theme'; -import { SpinButton } from '@fluentui/react-spinbutton'; -import { SpinButtonBounds } from '@fluentui/react-spinbutton'; -import { SpinButtonChangeEvent } from '@fluentui/react-spinbutton'; -import { spinButtonClassNames } from '@fluentui/react-spinbutton'; -import { SpinButtonOnChangeData } from '@fluentui/react-spinbutton'; -import { SpinButtonProps } from '@fluentui/react-spinbutton'; -import { SpinButtonSlots } from '@fluentui/react-spinbutton'; -import { SpinButtonSpinState } from '@fluentui/react-spinbutton'; -import { SpinButtonState } from '@fluentui/react-spinbutton'; -import { Spinner } from '@fluentui/react-spinner'; -import { spinnerClassNames } from '@fluentui/react-spinner'; -import { SpinnerProps } from '@fluentui/react-spinner'; -import { SpinnerSlots } from '@fluentui/react-spinner'; -import { SpinnerState } from '@fluentui/react-spinner'; -import { SplitButton } from '@fluentui/react-button'; -import { splitButtonClassNames } from '@fluentui/react-button'; -import { SplitButtonProps } from '@fluentui/react-button'; -import { SplitButtonSlots } from '@fluentui/react-button'; -import { SplitButtonState } from '@fluentui/react-button'; -import { SSRProvider } from '@fluentui/react-utilities'; -import { StrokeWidthTokens } from '@fluentui/react-theme'; -import { Subtitle1 } from '@fluentui/react-text'; -import { subtitle1ClassNames } from '@fluentui/react-text'; -import { Subtitle2 } from '@fluentui/react-text'; -import { subtitle2ClassNames } from '@fluentui/react-text'; -import { Subtitle2Stronger } from '@fluentui/react-text'; -import { subtitle2StrongerClassNames } from '@fluentui/react-text'; -import { Switch } from '@fluentui/react-switch'; -import { switchClassNames } from '@fluentui/react-switch'; -import { SwitchOnChangeData } from '@fluentui/react-switch'; -import { SwitchProps } from '@fluentui/react-switch'; -import { SwitchSlots } from '@fluentui/react-switch'; -import { SwitchState } from '@fluentui/react-switch'; -import { Tab } from '@fluentui/react-tabs'; -import { tabClassNames } from '@fluentui/react-tabs'; -import { Table } from '@fluentui/react-table'; -import { TableBody } from '@fluentui/react-table'; -import { tableBodyClassName } from '@fluentui/react-table'; -import { tableBodyClassNames } from '@fluentui/react-table'; -import { TableBodyProps } from '@fluentui/react-table'; -import { TableBodySlots } from '@fluentui/react-table'; -import { TableBodyState } from '@fluentui/react-table'; -import { TableCell } from '@fluentui/react-table'; -import { TableCellActions } from '@fluentui/react-table'; -import { tableCellActionsClassNames } from '@fluentui/react-table'; -import { TableCellActionsProps } from '@fluentui/react-table'; -import { TableCellActionsSlots } from '@fluentui/react-table'; -import { TableCellActionsState } from '@fluentui/react-table'; -import { tableCellClassName } from '@fluentui/react-table'; -import { tableCellClassNames } from '@fluentui/react-table'; -import { TableCellLayout } from '@fluentui/react-table'; -import { tableCellLayoutClassNames } from '@fluentui/react-table'; -import { TableCellLayoutProps } from '@fluentui/react-table'; -import { TableCellLayoutSlots } from '@fluentui/react-table'; -import { TableCellLayoutState } from '@fluentui/react-table'; -import { TableCellProps } from '@fluentui/react-table'; -import { TableCellSlots } from '@fluentui/react-table'; -import { TableCellState } from '@fluentui/react-table'; -import { tableClassName } from '@fluentui/react-table'; -import { tableClassNames } from '@fluentui/react-table'; -import { TableColumnDefinition } from '@fluentui/react-table'; -import { TableColumnId } from '@fluentui/react-table'; -import { TableColumnSizingOptions } from '@fluentui/react-table'; -import { TableContextProvider } from '@fluentui/react-table'; -import { TableContextValue } from '@fluentui/react-table'; -import { TableContextValues } from '@fluentui/react-table'; -import { TableFeaturePlugin } from '@fluentui/react-table'; -import { TableFeaturesState } from '@fluentui/react-table'; -import { TableHeader } from '@fluentui/react-table'; -import { TableHeaderCell } from '@fluentui/react-table'; -import { tableHeaderCellClassName } from '@fluentui/react-table'; -import { tableHeaderCellClassNames } from '@fluentui/react-table'; -import { TableHeaderCellProps } from '@fluentui/react-table'; -import { TableHeaderCellSlots } from '@fluentui/react-table'; -import { TableHeaderCellState } from '@fluentui/react-table'; -import { tableHeaderClassName } from '@fluentui/react-table'; -import { tableHeaderClassNames } from '@fluentui/react-table'; -import { TableHeaderProps } from '@fluentui/react-table'; -import { TableHeaderSlots } from '@fluentui/react-table'; -import { TableHeaderState } from '@fluentui/react-table'; -import { TableProps } from '@fluentui/react-table'; -import { TableResizeHandle } from '@fluentui/react-table'; -import { tableResizeHandleClassNames } from '@fluentui/react-table'; -import { TableRow } from '@fluentui/react-table'; -import { tableRowClassName } from '@fluentui/react-table'; -import { tableRowClassNames } from '@fluentui/react-table'; -import { TableRowData } from '@fluentui/react-table'; -import { TableRowId } from '@fluentui/react-table'; -import { TableRowIdContextProvider } from '@fluentui/react-table'; -import { TableRowProps } from '@fluentui/react-table'; -import { TableRowSlots } from '@fluentui/react-table'; -import { TableRowState } from '@fluentui/react-table'; -import { TableSelectionCell } from '@fluentui/react-table'; -import { tableSelectionCellClassNames } from '@fluentui/react-table'; -import { TableSelectionCellProps } from '@fluentui/react-table'; -import { TableSelectionCellSlots } from '@fluentui/react-table'; -import { TableSelectionCellState } from '@fluentui/react-table'; -import { TableSelectionState } from '@fluentui/react-table'; -import { TableSlots } from '@fluentui/react-table'; -import { TableSortState } from '@fluentui/react-table'; -import { TableState } from '@fluentui/react-table'; -import { TabList } from '@fluentui/react-tabs'; -import { tabListClassNames } from '@fluentui/react-tabs'; -import { TabListContextValue } from '@fluentui/react-tabs'; -import { TabListContextValues } from '@fluentui/react-tabs'; -import { TabListProps } from '@fluentui/react-tabs'; -import { TabListProvider } from '@fluentui/react-tabs'; -import { TabListSlots } from '@fluentui/react-tabs'; -import { TabListState } from '@fluentui/react-tabs'; -import { TabProps } from '@fluentui/react-tabs'; -import { TabRegisterData } from '@fluentui/react-tabs'; -import { TabSlots } from '@fluentui/react-tabs'; -import { TabState } from '@fluentui/react-tabs'; -import { TabValue } from '@fluentui/react-tabs'; -import { teamsDarkTheme } from '@fluentui/react-theme'; -import { teamsHighContrastTheme } from '@fluentui/react-theme'; -import { teamsLightTheme } from '@fluentui/react-theme'; -import { Text as Text_2 } from '@fluentui/react-text'; -import { Textarea } from '@fluentui/react-textarea'; -import { textareaClassNames } from '@fluentui/react-textarea'; -import { TextareaOnChangeData } from '@fluentui/react-textarea'; -import { TextareaProps } from '@fluentui/react-textarea'; -import { TextareaSlots } from '@fluentui/react-textarea'; -import { TextareaState } from '@fluentui/react-textarea'; -import { textClassNames } from '@fluentui/react-text'; -import { TextPresetProps } from '@fluentui/react-text'; -import { TextProps } from '@fluentui/react-text'; -import { TextSlots } from '@fluentui/react-text'; -import { TextState } from '@fluentui/react-text'; -import { Theme } from '@fluentui/react-theme'; -import { themeToTokensObject } from '@fluentui/react-theme'; -import { Title1 } from '@fluentui/react-text'; -import { title1ClassNames } from '@fluentui/react-text'; -import { Title2 } from '@fluentui/react-text'; -import { title2ClassNames } from '@fluentui/react-text'; -import { Title3 } from '@fluentui/react-text'; -import { title3ClassNames } from '@fluentui/react-text'; -import { ToggleButton } from '@fluentui/react-button'; -import { toggleButtonClassNames } from '@fluentui/react-button'; -import { ToggleButtonProps } from '@fluentui/react-button'; -import { ToggleButtonState } from '@fluentui/react-button'; -import { tokens } from '@fluentui/react-theme'; -import { Toolbar } from '@fluentui/react-toolbar'; -import { ToolbarButton } from '@fluentui/react-toolbar'; -import { ToolbarButtonProps } from '@fluentui/react-toolbar'; -import { ToolbarButtonState } from '@fluentui/react-toolbar'; -import { toolbarClassNames } from '@fluentui/react-toolbar'; -import { ToolbarContextValue } from '@fluentui/react-toolbar'; -import { ToolbarContextValues } from '@fluentui/react-toolbar'; -import { ToolbarDivider } from '@fluentui/react-toolbar'; -import { ToolbarDividerProps } from '@fluentui/react-toolbar'; -import { ToolbarDividerState } from '@fluentui/react-toolbar'; -import { ToolbarGroup } from '@fluentui/react-toolbar'; -import { toolbarGroupClassNames } from '@fluentui/react-toolbar'; -import { ToolbarGroupProps } from '@fluentui/react-toolbar'; -import { ToolbarGroupState } from '@fluentui/react-toolbar'; -import { ToolbarProps } from '@fluentui/react-toolbar'; -import { ToolbarRadioButton } from '@fluentui/react-toolbar'; -import { ToolbarRadioButtonProps } from '@fluentui/react-toolbar'; -import { ToolbarRadioButtonState } from '@fluentui/react-toolbar'; -import { ToolbarRadioGroup } from '@fluentui/react-toolbar'; -import { ToolbarRadioGroupProps } from '@fluentui/react-toolbar'; -import { ToolbarRadioGroupState } from '@fluentui/react-toolbar'; -import { ToolbarSlots } from '@fluentui/react-toolbar'; -import { ToolbarState } from '@fluentui/react-toolbar'; -import { ToolbarToggleButton } from '@fluentui/react-toolbar'; -import { ToolbarToggleButtonProps } from '@fluentui/react-toolbar'; -import { ToolbarToggleButtonState } from '@fluentui/react-toolbar'; -import { Tooltip } from '@fluentui/react-tooltip'; -import { tooltipClassNames } from '@fluentui/react-tooltip'; -import { TooltipProps } from '@fluentui/react-tooltip'; -import { TooltipSlots } from '@fluentui/react-tooltip'; -import { TooltipState } from '@fluentui/react-tooltip'; -import { TooltipTriggerProps } from '@fluentui/react-tooltip'; -import { TypographyStyle } from '@fluentui/react-theme'; -import { TypographyStyles } from '@fluentui/react-theme'; -import { typographyStyles } from '@fluentui/react-theme'; -import { UninitializedMenuListState } from '@fluentui/react-menu'; -import { useAccordion_unstable } from '@fluentui/react-accordion'; -import { useAccordionContext_unstable } from '@fluentui/react-accordion'; -import { useAccordionContextValues_unstable } from '@fluentui/react-accordion'; -import { useAccordionHeader_unstable } from '@fluentui/react-accordion'; -import { useAccordionHeaderContextValues_unstable } from '@fluentui/react-accordion'; -import { useAccordionHeaderStyles_unstable } from '@fluentui/react-accordion'; -import { useAccordionItem_unstable } from '@fluentui/react-accordion'; -import { useAccordionItemContext_unstable } from '@fluentui/react-accordion'; -import { useAccordionItemContextValues_unstable } from '@fluentui/react-accordion'; -import { useAccordionItemStyles_unstable } from '@fluentui/react-accordion'; -import { useAccordionPanel_unstable } from '@fluentui/react-accordion'; -import { useAccordionPanelStyles_unstable } from '@fluentui/react-accordion'; -import { useAccordionStyles_unstable } from '@fluentui/react-accordion'; -import { useArrowNavigationGroup } from '@fluentui/react-tabster'; -import { UseArrowNavigationGroupOptions } from '@fluentui/react-tabster'; -import { useAvatar_unstable } from '@fluentui/react-avatar'; -import { useAvatarGroup_unstable } from '@fluentui/react-avatar'; -import { useAvatarGroupContext_unstable } from '@fluentui/react-avatar'; -import { useAvatarGroupContextValues } from '@fluentui/react-avatar'; -import { useAvatarGroupItem_unstable } from '@fluentui/react-avatar'; -import { useAvatarGroupItemStyles_unstable } from '@fluentui/react-avatar'; -import { useAvatarGroupPopover_unstable } from '@fluentui/react-avatar'; -import { useAvatarGroupPopoverStyles_unstable } from '@fluentui/react-avatar'; -import { useAvatarGroupStyles_unstable } from '@fluentui/react-avatar'; -import { useAvatarStyles_unstable } from '@fluentui/react-avatar'; -import { useBadge_unstable } from '@fluentui/react-badge'; -import { useBadgeStyles_unstable } from '@fluentui/react-badge'; -import { useButton_unstable } from '@fluentui/react-button'; -import { useButtonStyles_unstable } from '@fluentui/react-button'; -import { useCard_unstable } from '@fluentui/react-card'; -import { useCardFooter_unstable } from '@fluentui/react-card'; -import { useCardFooterStyles_unstable } from '@fluentui/react-card'; -import { useCardHeader_unstable } from '@fluentui/react-card'; -import { useCardHeaderStyles_unstable } from '@fluentui/react-card'; -import { useCardPreview_unstable } from '@fluentui/react-card'; -import { useCardPreviewStyles_unstable } from '@fluentui/react-card'; -import { useCardStyles_unstable } from '@fluentui/react-card'; -import { useCheckbox_unstable } from '@fluentui/react-checkbox'; -import { useCheckboxStyles_unstable } from '@fluentui/react-checkbox'; -import { useCheckmarkStyles_unstable } from '@fluentui/react-menu'; -import { useCombobox_unstable } from '@fluentui/react-combobox'; -import { useComboboxContextValues } from '@fluentui/react-combobox'; -import { useComboboxStyles_unstable } from '@fluentui/react-combobox'; -import { useCompoundButton_unstable } from '@fluentui/react-button'; -import { useCompoundButtonStyles_unstable } from '@fluentui/react-button'; -import { useCounterBadge_unstable } from '@fluentui/react-badge'; -import { useCounterBadgeStyles_unstable } from '@fluentui/react-badge'; -import { useDataGrid_unstable } from '@fluentui/react-table'; -import { useDataGridBody_unstable } from '@fluentui/react-table'; -import { useDataGridBodyStyles_unstable } from '@fluentui/react-table'; -import { useDataGridCell_unstable } from '@fluentui/react-table'; -import { useDataGridCellStyles_unstable } from '@fluentui/react-table'; -import { useDataGridContextValues_unstable } from '@fluentui/react-table'; -import { useDataGridHeader_unstable } from '@fluentui/react-table'; -import { useDataGridHeaderCell_unstable } from '@fluentui/react-table'; -import { useDataGridHeaderCellStyles_unstable } from '@fluentui/react-table'; -import { useDataGridHeaderStyles_unstable } from '@fluentui/react-table'; -import { useDataGridRow_unstable } from '@fluentui/react-table'; -import { useDataGridRowStyles_unstable } from '@fluentui/react-table'; -import { useDataGridSelectionCell_unstable } from '@fluentui/react-table'; -import { useDataGridSelectionCellStyles_unstable } from '@fluentui/react-table'; -import { useDataGridStyles_unstable } from '@fluentui/react-table'; -import { useDialog_unstable } from '@fluentui/react-dialog'; -import { useDialogActions_unstable } from '@fluentui/react-dialog'; -import { useDialogActionsStyles_unstable } from '@fluentui/react-dialog'; -import { useDialogBody_unstable } from '@fluentui/react-dialog'; -import { useDialogBodyStyles_unstable } from '@fluentui/react-dialog'; -import { useDialogContent_unstable } from '@fluentui/react-dialog'; -import { useDialogContentStyles_unstable } from '@fluentui/react-dialog'; -import { useDialogSurface_unstable } from '@fluentui/react-dialog'; -import { useDialogSurfaceStyles_unstable } from '@fluentui/react-dialog'; -import { useDialogTitle_unstable } from '@fluentui/react-dialog'; -import { useDialogTitleStyles_unstable } from '@fluentui/react-dialog'; -import { useDialogTrigger_unstable } from '@fluentui/react-dialog'; -import { useDivider_unstable } from '@fluentui/react-divider'; -import { useDividerStyles_unstable } from '@fluentui/react-divider'; -import { useDropdown_unstable } from '@fluentui/react-combobox'; -import { useDropdownStyles_unstable } from '@fluentui/react-combobox'; -import { useField_unstable } from '@fluentui/react-field'; -import { useFieldContext_unstable } from '@fluentui/react-field'; -import { useFieldContextValues_unstable } from '@fluentui/react-field'; -import { useFieldControlProps_unstable } from '@fluentui/react-field'; -import { useFieldStyles_unstable } from '@fluentui/react-field'; -import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts'; -import { useFluentProvider_unstable } from '@fluentui/react-provider'; -import { useFluentProviderContextValues_unstable } from '@fluentui/react-provider'; -import { useFluentProviderStyles_unstable } from '@fluentui/react-provider'; -import { useFocusableGroup } from '@fluentui/react-tabster'; -import { UseFocusableGroupOptions } from '@fluentui/react-tabster'; -import { useFocusFinders } from '@fluentui/react-tabster'; -import { useFocusWithin } from '@fluentui/react-tabster'; -import { useId } from '@fluentui/react-utilities'; -import { useImage_unstable } from '@fluentui/react-image'; -import { useImageStyles_unstable } from '@fluentui/react-image'; -import { useInput_unstable } from '@fluentui/react-input'; -import { useInputStyles_unstable } from '@fluentui/react-input'; -import { useIsomorphicLayoutEffect } from '@fluentui/react-utilities'; -import { useIsOverflowGroupVisible } from '@fluentui/react-overflow'; -import { useIsOverflowItemVisible } from '@fluentui/react-overflow'; -import { useIsSSR } from '@fluentui/react-utilities'; -import { useKeyboardNavAttribute } from '@fluentui/react-tabster'; -import { useLabel_unstable } from '@fluentui/react-label'; -import { useLabelStyles_unstable } from '@fluentui/react-label'; -import { useLink_unstable } from '@fluentui/react-link'; -import { useLinkState_unstable } from '@fluentui/react-link'; -import { useLinkStyles_unstable } from '@fluentui/react-link'; -import { useListbox_unstable } from '@fluentui/react-combobox'; -import { useListboxContextValues } from '@fluentui/react-combobox'; -import { useListboxStyles_unstable } from '@fluentui/react-combobox'; -import { useMenu_unstable } from '@fluentui/react-menu'; -import { useMenuButton_unstable } from '@fluentui/react-button'; -import { useMenuButtonStyles_unstable } from '@fluentui/react-button'; -import { useMenuContext_unstable } from '@fluentui/react-menu'; -import { useMenuContextValues_unstable } from '@fluentui/react-menu'; -import { useMenuDivider_unstable } from '@fluentui/react-menu'; -import { useMenuDividerStyles_unstable } from '@fluentui/react-menu'; -import { useMenuGroup_unstable } from '@fluentui/react-menu'; -import { useMenuGroupContext_unstable } from '@fluentui/react-menu'; -import { useMenuGroupContextValues_unstable } from '@fluentui/react-menu'; -import { useMenuGroupHeader_unstable } from '@fluentui/react-menu'; -import { useMenuGroupHeaderStyles_unstable } from '@fluentui/react-menu'; -import { useMenuGroupStyles_unstable } from '@fluentui/react-menu'; -import { useMenuItem_unstable } from '@fluentui/react-menu'; -import { useMenuItemCheckbox_unstable } from '@fluentui/react-menu'; -import { useMenuItemCheckboxStyles_unstable } from '@fluentui/react-menu'; -import { useMenuItemRadio_unstable } from '@fluentui/react-menu'; -import { useMenuItemRadioStyles_unstable } from '@fluentui/react-menu'; -import { useMenuItemStyles_unstable } from '@fluentui/react-menu'; -import { useMenuList_unstable } from '@fluentui/react-menu'; -import { useMenuListContext_unstable } from '@fluentui/react-menu'; -import { useMenuListContextValues_unstable } from '@fluentui/react-menu'; -import { useMenuListStyles_unstable } from '@fluentui/react-menu'; -import { useMenuPopover_unstable } from '@fluentui/react-menu'; -import { useMenuPopoverStyles_unstable } from '@fluentui/react-menu'; -import { useMenuSplitGroup_unstable } from '@fluentui/react-menu'; -import { useMenuSplitGroupStyles_unstable } from '@fluentui/react-menu'; -import { useMenuTrigger_unstable } from '@fluentui/react-menu'; -import { useMenuTriggerContext_unstable } from '@fluentui/react-menu'; -import { useMergedRefs } from '@fluentui/react-utilities'; -import { useModalAttributes } from '@fluentui/react-tabster'; -import { UseModalAttributesOptions } from '@fluentui/react-tabster'; -import { useOption_unstable } from '@fluentui/react-combobox'; -import { useOptionGroup_unstable } from '@fluentui/react-combobox'; -import { useOptionGroupStyles_unstable } from '@fluentui/react-combobox'; -import { useOptionStyles_unstable } from '@fluentui/react-combobox'; -import { useOverflowCount } from '@fluentui/react-overflow'; -import { useOverflowMenu } from '@fluentui/react-overflow'; -import { usePersona_unstable } from '@fluentui/react-persona'; -import { usePersonaStyles_unstable } from '@fluentui/react-persona'; -import { usePopover_unstable } from '@fluentui/react-popover'; -import { usePopoverContext_unstable } from '@fluentui/react-popover'; -import { usePopoverSurface_unstable } from '@fluentui/react-popover'; -import { usePopoverSurfaceStyles_unstable } from '@fluentui/react-popover'; -import { usePopoverTrigger_unstable } from '@fluentui/react-popover'; -import { usePortal_unstable } from '@fluentui/react-portal'; -import { usePresenceBadge_unstable } from '@fluentui/react-badge'; -import { usePresenceBadgeStyles_unstable } from '@fluentui/react-badge'; -import { useProgressBar_unstable } from '@fluentui/react-progress'; -import { useProgressBarStyles_unstable } from '@fluentui/react-progress'; -import { useRadio_unstable } from '@fluentui/react-radio'; -import { useRadioGroup_unstable } from '@fluentui/react-radio'; -import { useRadioGroupContext_unstable } from '@fluentui/react-radio'; -import { useRadioGroupContextValue_unstable } from '@fluentui/react-radio'; -import { useRadioGroupContextValues } from '@fluentui/react-radio'; -import { useRadioGroupStyles_unstable } from '@fluentui/react-radio'; -import { useRadioStyles_unstable } from '@fluentui/react-radio'; -import { useScrollbarWidth } from '@fluentui/react-utilities'; -import { useSelect_unstable } from '@fluentui/react-select'; -import { useSelectStyles_unstable } from '@fluentui/react-select'; -import { useSkeleton_unstable } from '@fluentui/react-skeleton'; -import { useSkeletonContext } from '@fluentui/react-skeleton'; -import { useSkeletonItem_unstable } from '@fluentui/react-skeleton'; -import { useSkeletonItemStyles_unstable } from '@fluentui/react-skeleton'; -import { useSkeletonStyles_unstable } from '@fluentui/react-skeleton'; -import { useSlider_unstable } from '@fluentui/react-slider'; -import { useSliderState_unstable } from '@fluentui/react-slider'; -import { useSliderStyles_unstable } from '@fluentui/react-slider'; -import { useSpinButton_unstable } from '@fluentui/react-spinbutton'; -import { useSpinButtonStyles_unstable } from '@fluentui/react-spinbutton'; -import { useSpinner_unstable } from '@fluentui/react-spinner'; -import { useSpinnerStyles_unstable } from '@fluentui/react-spinner'; -import { useSplitButton_unstable } from '@fluentui/react-button'; -import { useSplitButtonStyles_unstable } from '@fluentui/react-button'; -import { useSwitch_unstable } from '@fluentui/react-switch'; -import { useSwitchStyles_unstable } from '@fluentui/react-switch'; -import { useTab_unstable } from '@fluentui/react-tabs'; -import { useTable_unstable } from '@fluentui/react-table'; -import { useTableBody_unstable } from '@fluentui/react-table'; -import { useTableBodyStyles_unstable } from '@fluentui/react-table'; -import { useTableCell_unstable } from '@fluentui/react-table'; -import { useTableCellActions_unstable } from '@fluentui/react-table'; -import { useTableCellActionsStyles_unstable } from '@fluentui/react-table'; -import { useTableCellLayout_unstable } from '@fluentui/react-table'; -import { useTableCellLayoutStyles_unstable } from '@fluentui/react-table'; -import { useTableCellStyles_unstable } from '@fluentui/react-table'; -import { useTableColumnSizing_unstable } from '@fluentui/react-table'; -import { useTableContext } from '@fluentui/react-table'; -import { useTableFeatures } from '@fluentui/react-table'; -import { UseTableFeaturesOptions } from '@fluentui/react-table'; -import { useTableHeader_unstable } from '@fluentui/react-table'; -import { useTableHeaderCell_unstable } from '@fluentui/react-table'; -import { useTableHeaderCellStyles_unstable } from '@fluentui/react-table'; -import { useTableHeaderStyles_unstable } from '@fluentui/react-table'; -import { useTableResizeHandle_unstable } from '@fluentui/react-table'; -import { useTableResizeHandleStyles_unstable } from '@fluentui/react-table'; -import { useTableRow_unstable } from '@fluentui/react-table'; -import { useTableRowIdContext } from '@fluentui/react-table'; -import { useTableRowStyles_unstable } from '@fluentui/react-table'; -import { useTableSelection } from '@fluentui/react-table'; -import { useTableSelectionCell_unstable } from '@fluentui/react-table'; -import { useTableSelectionCellStyles_unstable } from '@fluentui/react-table'; -import { useTableSort } from '@fluentui/react-table'; -import { useTableStyles_unstable } from '@fluentui/react-table'; -import { useTabList_unstable } from '@fluentui/react-tabs'; -import { useTabListContext_unstable } from '@fluentui/react-tabs'; -import { useTabListContextValues_unstable } from '@fluentui/react-tabs'; -import { useTabListStyles_unstable } from '@fluentui/react-tabs'; -import { useTabStyles_unstable } from '@fluentui/react-tabs'; -import { useText_unstable } from '@fluentui/react-text'; -import { useTextarea_unstable } from '@fluentui/react-textarea'; -import { useTextareaStyles_unstable } from '@fluentui/react-textarea'; -import { useTextStyles_unstable } from '@fluentui/react-text'; -import { useThemeClassName_unstable as useThemeClassName } from '@fluentui/react-shared-contexts'; -import { useToggleButton_unstable } from '@fluentui/react-button'; -import { useToggleButtonStyles_unstable } from '@fluentui/react-button'; -import { useToggleState } from '@fluentui/react-button'; -import { useToolbar_unstable } from '@fluentui/react-toolbar'; -import { useToolbarButton_unstable } from '@fluentui/react-toolbar'; -import { useToolbarButtonStyles_unstable } from '@fluentui/react-toolbar'; -import { useToolbarDivider_unstable } from '@fluentui/react-toolbar'; -import { useToolbarDividerStyles_unstable } from '@fluentui/react-toolbar'; -import { useToolbarGroup_unstable } from '@fluentui/react-toolbar'; -import { useToolbarGroupStyles_unstable } from '@fluentui/react-toolbar'; -import { useToolbarRadioButton_unstable } from '@fluentui/react-toolbar'; -import { useToolbarRadioButtonStyles_unstable } from '@fluentui/react-toolbar'; -import { useToolbarStyles_unstable } from '@fluentui/react-toolbar'; -import { useToolbarToggleButton_unstable } from '@fluentui/react-toolbar'; -import { useToolbarToggleButtonStyles_unstable } from '@fluentui/react-toolbar'; -import { useTooltip_unstable } from '@fluentui/react-tooltip'; -import { useTooltipStyles_unstable } from '@fluentui/react-tooltip'; -import { useTooltipVisibility_unstable as useTooltipVisibility } from '@fluentui/react-shared-contexts'; -import { VerticalSpacingTokens } from '@fluentui/react-theme'; -import { webDarkTheme } from '@fluentui/react-theme'; -import { webLightTheme } from '@fluentui/react-theme'; +import { Types } from 'tabster'; export { __css } @@ -1031,2033 +39,4521 @@ export { __resetStyles } export { __styles } -export { Accordion } - -export { accordionClassNames } - -export { AccordionContextValue } - -export { AccordionContextValues } - -export { AccordionHeader } - -export { accordionHeaderClassNames } - -export { AccordionHeaderContextValue } - -export { AccordionHeaderContextValues } - -export { AccordionHeaderExpandIconPosition } - -export { AccordionHeaderProps } - -export { AccordionHeaderSize } - -export { AccordionHeaderSlots } - -export { AccordionHeaderState } - -export { AccordionIndex } - -export { AccordionItem } - -export { accordionItemClassNames } - -export { AccordionItemContextValue } - -export { AccordionItemContextValues } - -export { AccordionItemProps } - -export { AccordionItemProvider } - -export { AccordionItemSlots } - -export { AccordionItemState } - -export { AccordionItemValue } - -export { AccordionPanel } - -export { accordionPanelClassNames } - -export { AccordionPanelProps } - -export { AccordionPanelSlots } - -export { AccordionPanelState } - -export { AccordionProps } - -export { AccordionProvider } - -export { AccordionSlots } - -export { AccordionState } - -export { AccordionToggleData } - -export { AccordionToggleEvent } - -export { AccordionToggleEventHandler } - -export { arrowHeights } - -export { Avatar } - -export { avatarClassNames } - -export { AvatarGroup } - -export { avatarGroupClassNames } - -export { AvatarGroupContextValue } - -export { AvatarGroupContextValues } - -export { AvatarGroupItem } - -export { avatarGroupItemClassNames } - -export { AvatarGroupItemProps } - -export { AvatarGroupItemSlots } - -export { AvatarGroupItemState } - -export { AvatarGroupPopover } - -export { avatarGroupPopoverClassNames } - -export { AvatarGroupPopoverProps } - -export { AvatarGroupPopoverSlots } - -export { AvatarGroupPopoverState } - -export { AvatarGroupProps } - -export { AvatarGroupProvider } - -export { AvatarGroupSlots } - -export { AvatarGroupState } - -export { AvatarNamedColor } - -export { AvatarProps } - -export { AvatarSize } - -export { AvatarSizes } - -export { AvatarSlots } - -export { AvatarState } - -export { Badge } - -export { badgeClassNames } - -export { BadgeProps } - -export { BadgeSlots } - -export { BadgeState } - -export { Body1 } - -export { body1ClassNames } - -export { Body1Strong } - -export { body1StrongClassNames } - -export { Body1Stronger } - -export { body1StrongerClassNames } - -export { Body2 } - -export { body2ClassNames } - -export { BorderRadiusTokens } - -export { BrandVariants } - -export { Button } - -export { buttonClassNames } - -export { ButtonProps } - -export { ButtonSlots } - -export { ButtonState } - -export { Caption1 } - -export { caption1ClassNames } - -export { Caption1Strong } - -export { caption1StrongClassNames } - -export { Caption1Stronger } - -export { caption1StrongerClassNames } - -export { Caption2 } - -export { caption2ClassNames } - -export { Caption2Strong } - -export { caption2StrongClassNames } - -export { Card } - -export { cardClassNames } - -export { cardCSSVars } - -export { CardFooter } - -export { cardFooterClassNames } - -export { CardFooterProps } - -export { CardFooterSlots } - -export { CardFooterState } - -export { CardHeader } - -export { cardHeaderClassNames } - -export { cardHeaderCSSVars } - -export { CardHeaderProps } - -export { CardHeaderSlots } - -export { CardHeaderState } - -export { CardPreview } - -export { cardPreviewClassNames } - -export { CardPreviewProps } - -export { CardPreviewSlots } - -export { CardPreviewState } - -export { CardProps } - -export { CardSlots } - -export { CardState } - -export { Checkbox } - -export { checkboxClassNames } - -export { CheckboxOnChangeData } - -export { CheckboxProps } - -export { CheckboxSlots } - -export { CheckboxState } - -export { ColorPaletteTokens } - -export { ColorTokens } - -export { Combobox } - -export { comboboxClassNames } - -export { ComboboxContextValue } - -export { ComboboxContextValues } - -export { ComboboxOpenChangeData } - -export { ComboboxOpenEvents } +// @public +export const Accordion: ForwardRefComponent; + +// @public (undocumented) +export const accordionClassNames: SlotClassNames; -export { ComboboxProps } +// @public (undocumented) +export type AccordionContextValue = Required> & Pick & { + openItems: AccordionItemValue[]; + requestToggle: (event: AccordionToggleEvent, data: AccordionToggleData) => void; +}; -export { ComboboxProvider } +// @public (undocumented) +export type AccordionContextValues = { + accordion: AccordionContextValue; +}; -export { ComboboxSlots } +// @public +export const AccordionHeader: ForwardRefComponent; + +// @public (undocumented) +export const accordionHeaderClassNames: SlotClassNames; + +// @public (undocumented) +export type AccordionHeaderContextValue = Required> & { + disabled: boolean; + open: boolean; +}; + +// @public (undocumented) +export type AccordionHeaderContextValues = { + accordionHeader: AccordionHeaderContextValue; +}; + +// @public (undocumented) +export type AccordionHeaderExpandIconPosition = 'start' | 'end'; + +// @public (undocumented) +export type AccordionHeaderProps = ComponentProps> & { + expandIconPosition?: AccordionHeaderExpandIconPosition; + inline?: boolean; + size?: AccordionHeaderSize; +}; + +// @public (undocumented) +export type AccordionHeaderSize = 'small' | 'medium' | 'large' | 'extra-large'; + +// @public (undocumented) +export type AccordionHeaderSlots = { + root: NonNullable>; + button: NonNullable>>; + expandIcon?: Slot<'span'>; + icon?: Slot<'div'>; +}; + +// @public (undocumented) +export type AccordionHeaderState = ComponentState & Required> & AccordionHeaderContextValue; -export { ComboboxState } +// @public (undocumented) +export type AccordionIndex = number | number[]; -export { ComponentProps } +// @public +export const AccordionItem: ForwardRefComponent; -export { ComponentState } +// @public (undocumented) +export const accordionItemClassNames: SlotClassNames; + +// @public (undocumented) +export type AccordionItemContextValue = Required> & { + onHeaderClick(ev: React_2.MouseEvent | React_2.KeyboardEvent): void; + open: boolean; +}; + +// @public (undocumented) +export type AccordionItemContextValues = { + accordionItem: AccordionItemContextValue; +}; -export { CompoundButton } +// @public (undocumented) +export type AccordionItemProps = ComponentProps & { + disabled?: boolean; + value: AccordionItemValue; +}; -export { compoundButtonClassNames } +// @public (undocumented) +export const AccordionItemProvider: React_2.Provider; + +// @public (undocumented) +export type AccordionItemSlots = { + root: NonNullable>; +}; + +// @public (undocumented) +export type AccordionItemState = ComponentState & AccordionItemContextValue; + +// @public (undocumented) +export type AccordionItemValue = unknown; + +// @public +export const AccordionPanel: ForwardRefComponent; + +// @public (undocumented) +export const accordionPanelClassNames: SlotClassNames; + +// @public (undocumented) +export type AccordionPanelProps = ComponentProps; + +// @public (undocumented) +export type AccordionPanelSlots = { + root: NonNullable>; +}; + +// @public (undocumented) +export type AccordionPanelState = ComponentState & { + open: boolean; +}; + +// @public (undocumented) +export type AccordionProps = ComponentProps & { + defaultOpenItems?: AccordionItemValue | AccordionItemValue[]; + collapsible?: boolean; + multiple?: boolean; + navigation?: 'linear' | 'circular'; + onToggle?: AccordionToggleEventHandler; + openItems?: AccordionItemValue | AccordionItemValue[]; +}; + +// @public (undocumented) +export const AccordionProvider: Provider & FC>; + +// @public (undocumented) +export type AccordionSlots = { + root: NonNullable>; +}; + +// @public (undocumented) +export type AccordionState = ComponentState & AccordionContextValue; + +// @public (undocumented) +export type AccordionToggleData = { + value: AccordionItemValue; +}; + +// @public (undocumented) +export type AccordionToggleEvent = React_2.MouseEvent | React_2.KeyboardEvent; + +// @public (undocumented) +export type AccordionToggleEventHandler = (event: AccordionToggleEvent, data: AccordionToggleData) => void; + +// @public (undocumented) +export const arrowHeights: Record; + +// @public (undocumented) +export const Avatar: ForwardRefComponent; + +// @public (undocumented) +export const avatarClassNames: SlotClassNames; + +// @public +export const AvatarGroup: ForwardRefComponent; + +// @public (undocumented) +export const avatarGroupClassNames: SlotClassNames; + +// @public (undocumented) +export type AvatarGroupContextValue = Pick & { + isOverflow?: boolean; +}; + +// @public (undocumented) +export type AvatarGroupContextValues = { + avatarGroup: AvatarGroupContextValue; +}; + +// @public +export const AvatarGroupItem: ForwardRefComponent; + +// @public (undocumented) +export const avatarGroupItemClassNames: SlotClassNames; + +// @public +export type AvatarGroupItemProps = Omit, 'avatar'>, 'size' | 'shape'>; + +// @public (undocumented) +export type AvatarGroupItemSlots = { + root: NonNullable>; + avatar: NonNullable>; + overflowLabel: NonNullable>; +}; + +// @public +export type AvatarGroupItemState = ComponentState & { + isOverflowItem?: boolean; + layout: AvatarGroupProps['layout']; + size: AvatarSize; +}; + +// @public +export const AvatarGroupPopover: React_2.FC; + +// @public (undocumented) +export const avatarGroupPopoverClassNames: SlotClassNames; + +// @public +export type AvatarGroupPopoverProps = Omit>, 'children'> & { + indicator?: 'count' | 'icon'; + count?: number; + children: React_2.ReactNode; +}; + +// @public (undocumented) +export type AvatarGroupPopoverSlots = { + root: NonNullable>; + triggerButton: NonNullable>; + content: NonNullable>; + popoverSurface: NonNullable>; + tooltip: NonNullable>; +}; + +// @public +export type AvatarGroupPopoverState = ComponentState & Required> & { + popoverOpen: boolean; + layout: AvatarGroupProps['layout']; + size: AvatarSize; +}; + +// @public +export type AvatarGroupProps = ComponentProps & { + layout?: 'spread' | 'stack' | 'pie'; + size?: AvatarSize; +}; + +// @public (undocumented) +export const AvatarGroupProvider: Provider & FC>; + +// @public (undocumented) +export type AvatarGroupSlots = { + root: NonNullable>; +}; + +// @public +export type AvatarGroupState = ComponentState & Required>; + +// @public +export type AvatarNamedColor = 'dark-red' | 'cranberry' | 'red' | 'pumpkin' | 'peach' | 'marigold' | 'gold' | 'brass' | 'brown' | 'forest' | 'seafoam' | 'dark-green' | 'light-teal' | 'teal' | 'steel' | 'blue' | 'royal-blue' | 'cornflower' | 'navy' | 'lavender' | 'purple' | 'grape' | 'lilac' | 'pink' | 'magenta' | 'plum' | 'beige' | 'mink' | 'platinum' | 'anchor'; + +// @public +export type AvatarProps = Omit, 'color'> & { + active?: 'active' | 'inactive' | 'unset'; + activeAppearance?: 'ring' | 'shadow' | 'ring-shadow'; + color?: 'neutral' | 'brand' | 'colorful' | AvatarNamedColor; + idForColor?: string | undefined; + name?: string; + shape?: AvatarShape; + size?: AvatarSize; +}; + +// @public +export type AvatarSize = 16 | 20 | 24 | 28 | 32 | 36 | 40 | 48 | 56 | 64 | 72 | 96 | 120 | 128; + +// @public @deprecated +export type AvatarSizes = AvatarSize; + +// @public (undocumented) +export type AvatarSlots = { + root: Slot<'span'>; + image?: Slot<'img'>; + initials?: Slot<'span'>; + icon?: Slot<'span'>; + badge?: Slot; +}; + +// @public +export type AvatarState = ComponentState & Required> & { + color: NonNullable>; + activeAriaLabelElement?: JSX.Element; +}; + +// @public +export const Badge: ForwardRefComponent; + +// @public (undocumented) +export const badgeClassNames: SlotClassNames; + +// @public (undocumented) +export type BadgeProps = Omit, 'color'> & { + appearance?: 'filled' | 'ghost' | 'outline' | 'tint'; + color?: 'brand' | 'danger' | 'important' | 'informative' | 'severe' | 'subtle' | 'success' | 'warning'; + iconPosition?: 'before' | 'after'; + shape?: 'circular' | 'rounded' | 'square'; + size?: 'tiny' | 'extra-small' | 'small' | 'medium' | 'large' | 'extra-large'; +}; + +// @public (undocumented) +export type BadgeSlots = { + root: Slot<'div'>; + icon?: Slot<'span'>; +}; + +// @public (undocumented) +export type BadgeState = ComponentState & Required>; + +// @public +export const Body1: FunctionComponent; + +// @public (undocumented) +export const body1ClassNames: SlotClassNames; + +// @public +export const Body1Strong: FunctionComponent; + +// @public (undocumented) +export const body1StrongClassNames: SlotClassNames; + +// @public +export const Body1Stronger: FunctionComponent; + +// @public (undocumented) +export const body1StrongerClassNames: SlotClassNames; + +// @public +export const Body2: FunctionComponent; + +// @public (undocumented) +export const body2ClassNames: SlotClassNames; + +// @public (undocumented) +export type BorderRadiusTokens = { + borderRadiusNone: string; + borderRadiusSmall: string; + borderRadiusMedium: string; + borderRadiusLarge: string; + borderRadiusXLarge: string; + borderRadiusCircular: string; +}; + +// @public (undocumented) +export type BrandVariants = Record; + +// @public +export const Button: ForwardRefComponent; + +// @public (undocumented) +export const buttonClassNames: SlotClassNames; + +// @public (undocumented) +export type ButtonProps = ComponentProps & { + appearance?: 'secondary' | 'primary' | 'outline' | 'subtle' | 'transparent'; + disabledFocusable?: boolean; + disabled?: boolean; + iconPosition?: 'before' | 'after'; + shape?: 'rounded' | 'circular' | 'square'; + size?: ButtonSize; +}; -export { CompoundButtonProps } +// @public (undocumented) +export type ButtonSlots = { + root: NonNullable>>; + icon?: Slot<'span'>; +}; -export { CompoundButtonSlots } +// @public (undocumented) +export type ButtonState = ComponentState & Required> & { + iconOnly: boolean; +}; + +// @public +export const Caption1: FunctionComponent; -export { CompoundButtonState } +// @public (undocumented) +export const caption1ClassNames: SlotClassNames; -export { CounterBadge } +// @public +export const Caption1Strong: FunctionComponent; -export { counterBadgeClassNames } +// @public (undocumented) +export const caption1StrongClassNames: SlotClassNames; -export { CounterBadgeProps } +// @public +export const Caption1Stronger: FunctionComponent; -export { CounterBadgeState } +// @public (undocumented) +export const caption1StrongerClassNames: SlotClassNames; + +// @public +export const Caption2: FunctionComponent; + +// @public (undocumented) +export const caption2ClassNames: SlotClassNames; + +// @public +export const Caption2Strong: FunctionComponent; + +// @public (undocumented) +export const caption2StrongClassNames: SlotClassNames; + +// @public +export const Card: ForwardRefComponent; -export { createCustomFocusIndicatorStyle } +// @public +export const cardClassNames: SlotClassNames; -export { CreateCustomFocusIndicatorStyleOptions } - -export { createDarkTheme } +// @public +export const cardCSSVars: { + cardSizeVar: string; + cardBorderRadiusVar: string; +}; + +// @public +export const CardFooter: ForwardRefComponent; + +// @public +export const cardFooterClassNames: SlotClassNames; + +// @public +export type CardFooterProps = ComponentProps; + +// @public +export type CardFooterSlots = { + root: Slot<'div'>; + action?: Slot<'div'>; +}; + +// @public +export type CardFooterState = ComponentState; + +// @public +export const CardHeader: ForwardRefComponent; + +// @public +export const cardHeaderClassNames: SlotClassNames; + +// @public +export const cardHeaderCSSVars: { + cardHeaderGapVar: string; +}; + +// @public +export type CardHeaderProps = ComponentProps>; + +// @public +export type CardHeaderSlots = { + root: Slot<'div'>; + image: Slot<'div', 'img'>; + header: Slot<'div'>; + description: Slot<'div'>; + action?: Slot<'div'>; +}; + +// @public +export type CardHeaderState = ComponentState; + +// @public +export const CardPreview: ForwardRefComponent; + +// @public +export const cardPreviewClassNames: SlotClassNames; + +// @public +export type CardPreviewProps = ComponentProps; + +// @public +export type CardPreviewSlots = { + root: Slot<'div'>; + logo?: Slot<'div', 'img'>; +}; + +// @public +export type CardPreviewState = ComponentState; + +// @public +export type CardProps = ComponentProps & { + appearance?: 'filled' | 'filled-alternative' | 'outline' | 'subtle'; + focusMode?: 'off' | 'no-tab' | 'tab-exit' | 'tab-only'; + orientation?: 'horizontal' | 'vertical'; + size?: 'small' | 'medium' | 'large'; + selected?: boolean; + defaultSelected?: boolean; + onSelectionChange?: (event: CardOnSelectionChangeEvent, data: CardOnSelectData) => void; +}; + +// @public +export type CardSlots = { + root: Slot<'div'>; + floatingAction?: Slot<'div'>; + checkbox?: Slot<'input'>; +}; + +// @public +export type CardState = ComponentState & CardContextValue & Required & { + interactive: boolean; + selectable: boolean; + selected: boolean; + selectFocused: boolean; +}>; + +// @public +export const Checkbox: ForwardRefComponent; + +// @public (undocumented) +export const checkboxClassNames: SlotClassNames; + +// @public +export interface CheckboxOnChangeData { + // (undocumented) + checked: 'mixed' | boolean; +} + +// @public +export type CheckboxProps = Omit, 'input'>, 'checked' | 'defaultChecked' | 'onChange' | 'size'> & { + checked?: 'mixed' | boolean; + children?: never; + defaultChecked?: 'mixed' | boolean; + labelPosition?: 'before' | 'after'; + onChange?: (ev: React_2.ChangeEvent, data: CheckboxOnChangeData) => void; + shape?: 'square' | 'circular'; + size?: 'medium' | 'large'; +}; + +// @public (undocumented) +export type CheckboxSlots = { + root: NonNullable>; + label?: Slot; + input: NonNullable>; + indicator: Slot<'div'>; +}; + +// @public +export type CheckboxState = ComponentState & Required>; + +// @public (undocumented) +export type ColorPaletteTokens = StatusColorPaletteTokens & PersonaColorPaletteTokens; + +// @public +export type ColorTokens = { + colorNeutralForeground1: string; + colorNeutralForeground1Hover: string; + colorNeutralForeground1Pressed: string; + colorNeutralForeground1Selected: string; + colorNeutralForeground2: string; + colorNeutralForeground2Hover: string; + colorNeutralForeground2Pressed: string; + colorNeutralForeground2Selected: string; + colorNeutralForeground2BrandHover: string; + colorNeutralForeground2BrandPressed: string; + colorNeutralForeground2BrandSelected: string; + colorNeutralForeground3: string; + colorNeutralForeground3Hover: string; + colorNeutralForeground3Pressed: string; + colorNeutralForeground3Selected: string; + colorNeutralForeground3BrandHover: string; + colorNeutralForeground3BrandPressed: string; + colorNeutralForeground3BrandSelected: string; + colorNeutralForeground4: string; + colorNeutralForegroundDisabled: string; + colorNeutralForegroundInvertedDisabled: string; + colorBrandForegroundLink: string; + colorBrandForegroundLinkHover: string; + colorBrandForegroundLinkPressed: string; + colorBrandForegroundLinkSelected: string; + colorNeutralForeground2Link: string; + colorNeutralForeground2LinkHover: string; + colorNeutralForeground2LinkPressed: string; + colorNeutralForeground2LinkSelected: string; + colorCompoundBrandForeground1: string; + colorCompoundBrandForeground1Hover: string; + colorCompoundBrandForeground1Pressed: string; + colorBrandForeground1: string; + colorBrandForeground2: string; + colorNeutralForeground1Static: string; + colorNeutralForegroundInverted: string; + colorNeutralForegroundInvertedHover: string; + colorNeutralForegroundInvertedPressed: string; + colorNeutralForegroundInvertedSelected: string; + colorNeutralForegroundInverted2: string; + colorNeutralForegroundOnBrand: string; + colorNeutralForegroundStaticInverted: string; + colorNeutralForegroundInvertedLink: string; + colorNeutralForegroundInvertedLinkHover: string; + colorNeutralForegroundInvertedLinkPressed: string; + colorNeutralForegroundInvertedLinkSelected: string; + colorBrandForegroundInverted: string; + colorBrandForegroundInvertedHover: string; + colorBrandForegroundInvertedPressed: string; + colorBrandForegroundOnLight: string; + colorBrandForegroundOnLightHover: string; + colorBrandForegroundOnLightPressed: string; + colorBrandForegroundOnLightSelected: string; + colorNeutralBackground1: string; + colorNeutralBackground1Hover: string; + colorNeutralBackground1Pressed: string; + colorNeutralBackground1Selected: string; + colorNeutralBackground2: string; + colorNeutralBackground2Hover: string; + colorNeutralBackground2Pressed: string; + colorNeutralBackground2Selected: string; + colorNeutralBackground3: string; + colorNeutralBackground3Hover: string; + colorNeutralBackground3Pressed: string; + colorNeutralBackground3Selected: string; + colorNeutralBackground4: string; + colorNeutralBackground4Hover: string; + colorNeutralBackground4Pressed: string; + colorNeutralBackground4Selected: string; + colorNeutralBackground5: string; + colorNeutralBackground5Hover: string; + colorNeutralBackground5Pressed: string; + colorNeutralBackground5Selected: string; + colorNeutralBackground6: string; + colorNeutralBackgroundInverted: string; + colorNeutralBackgroundStatic: string; + colorNeutralBackgroundAlpha: string; + colorNeutralBackgroundAlpha2: string; + colorSubtleBackground: string; + colorSubtleBackgroundHover: string; + colorSubtleBackgroundPressed: string; + colorSubtleBackgroundSelected: string; + colorSubtleBackgroundLightAlphaHover: string; + colorSubtleBackgroundLightAlphaPressed: string; + colorSubtleBackgroundLightAlphaSelected: string; + colorSubtleBackgroundInverted: string; + colorSubtleBackgroundInvertedHover: string; + colorSubtleBackgroundInvertedPressed: string; + colorSubtleBackgroundInvertedSelected: string; + colorTransparentBackground: string; + colorTransparentBackgroundHover: string; + colorTransparentBackgroundPressed: string; + colorTransparentBackgroundSelected: string; + colorNeutralBackgroundDisabled: string; + colorNeutralBackgroundInvertedDisabled: string; + colorNeutralStencil1: string; + colorNeutralStencil2: string; + colorNeutralStencil1Alpha: string; + colorNeutralStencil2Alpha: string; + colorBackgroundOverlay: string; + colorScrollbarOverlay: string; + colorBrandBackground: string; + colorBrandBackgroundHover: string; + colorBrandBackgroundPressed: string; + colorBrandBackgroundSelected: string; + colorCompoundBrandBackground: string; + colorCompoundBrandBackgroundHover: string; + colorCompoundBrandBackgroundPressed: string; + colorBrandBackgroundStatic: string; + colorBrandBackground2: string; + colorBrandBackgroundInverted: string; + colorBrandBackgroundInvertedHover: string; + colorBrandBackgroundInvertedPressed: string; + colorBrandBackgroundInvertedSelected: string; + colorNeutralStrokeAccessible: string; + colorNeutralStrokeAccessibleHover: string; + colorNeutralStrokeAccessiblePressed: string; + colorNeutralStrokeAccessibleSelected: string; + colorNeutralStroke1: string; + colorNeutralStroke1Hover: string; + colorNeutralStroke1Pressed: string; + colorNeutralStroke1Selected: string; + colorNeutralStroke2: string; + colorNeutralStroke3: string; + colorNeutralStrokeOnBrand: string; + colorNeutralStrokeOnBrand2: string; + colorNeutralStrokeOnBrand2Hover: string; + colorNeutralStrokeOnBrand2Pressed: string; + colorNeutralStrokeOnBrand2Selected: string; + colorBrandStroke1: string; + colorBrandStroke2: string; + colorCompoundBrandStroke: string; + colorCompoundBrandStrokeHover: string; + colorCompoundBrandStrokePressed: string; + colorNeutralStrokeDisabled: string; + colorNeutralStrokeInvertedDisabled: string; + colorTransparentStroke: string; + colorTransparentStrokeInteractive: string; + colorTransparentStrokeDisabled: string; + colorNeutralStrokeAlpha: string; + colorStrokeFocus1: string; + colorStrokeFocus2: string; + colorNeutralShadowAmbient: string; + colorNeutralShadowKey: string; + colorNeutralShadowAmbientLighter: string; + colorNeutralShadowKeyLighter: string; + colorNeutralShadowAmbientDarker: string; + colorNeutralShadowKeyDarker: string; + colorBrandShadowAmbient: string; + colorBrandShadowKey: string; +}; + +// @public +export const Combobox: ForwardRefComponent; + +// @public (undocumented) +export const comboboxClassNames: SlotClassNames; + +// @public +export type ComboboxContextValue = Pick; + +// @public (undocumented) +export type ComboboxContextValues = ComboboxBaseContextValues; + +// @public (undocumented) +export type ComboboxOpenChangeData = ComboboxBaseOpenChangeData; + +// @public (undocumented) +export type ComboboxOpenEvents = ComboboxBaseOpenEvents; + +// @public +export type ComboboxProps = Omit, 'input'>, 'children' | 'size'> & ComboboxBaseProps & { + freeform?: boolean; + children?: React_2.ReactNode; +}; + +// @public (undocumented) +export const ComboboxProvider: Provider & FC>; + +// @public (undocumented) +export type ComboboxSlots = { + root: NonNullable>; + expandIcon: Slot<'span'>; + input: NonNullable>; + listbox?: Slot; +}; + +// @public +export type ComboboxState = ComponentState & ComboboxBaseState; + +// @public +export type ComponentProps = Omit & PropsWithoutRef>; + +// @public +export type ComponentState = { + components: { + [Key in keyof Slots]-?: React_2.ComponentType> | (ExtractSlotProps extends AsIntrinsicElement ? As : keyof JSX.IntrinsicElements); + }; +} & { + [Key in keyof Slots]: ReplaceNullWithUndefined>; +}; + +// @public +export const CompoundButton: ForwardRefComponent; + +// @public (undocumented) +export const compoundButtonClassNames: SlotClassNames; + +// @public (undocumented) +export type CompoundButtonProps = ComponentProps> & Pick; + +// @public (undocumented) +export type CompoundButtonSlots = ButtonSlots & { + secondaryContent?: Slot<'span'>; + contentContainer: NonNullable>; +}; + +// @public (undocumented) +export type CompoundButtonState = ComponentState & Omit; + +// @public +export const CounterBadge: ForwardRefComponent; + +// @public (undocumented) +export const counterBadgeClassNames: SlotClassNames; + +// @public (undocumented) +export type CounterBadgeProps = Omit & { + appearance?: 'filled' | 'ghost'; + color?: Extract; + count?: number; + dot?: boolean; + overflowCount?: number; + shape?: 'circular' | 'rounded'; + showZero?: boolean; +}; + +// @public (undocumented) +export type CounterBadgeState = Omit & Required>; + +// @public +export function createCustomFocusIndicatorStyle(style: TStyle, { selector }?: CreateCustomFocusIndicatorStyleOptions): TStyle extends GriffelStyle ? GriffelStyle : GriffelResetStyle; + +// @public (undocumented) +export interface CreateCustomFocusIndicatorStyleOptions { + // @deprecated + enableOutline?: boolean; + selector?: 'focus' | 'focus-within'; +} + +// @public (undocumented) +export const createDarkTheme: (brand: BrandVariants) => Theme; export { createDOMRenderer } -export { createFocusOutlineStyle } - -export { CreateFocusOutlineStyleOptions } - -export { createHighContrastTheme } - -export { createLightTheme } - -export { createTableColumn } - -export { CreateTableColumnOptions } - -export { createTeamsDarkTheme } - -export { CurveTokens } - -export { DATA_OVERFLOW_ITEM } - -export { DATA_OVERFLOW_MENU } - -export { DATA_OVERFLOWING } - -export { DataGrid } - -export { DataGridBody } - -export { dataGridBodyClassNames } - -export { DataGridBodyProps } - -export { DataGridBodySlots } - -export { DataGridBodyState } - -export { DataGridCell } - -export { dataGridCellClassNames } - -export { DataGridCellProps } - -export { DataGridCellSlots } - -export { DataGridCellState } - -export { dataGridClassNames } - -export { DataGridContextValue } - -export { DataGridContextValues } - -export { DataGridHeader } - -export { DataGridHeaderCell } - -export { dataGridHeaderCellClassNames } - -export { DataGridHeaderCellProps } - -export { DataGridHeaderCellSlots } - -export { DataGridHeaderCellState } - -export { dataGridHeaderClassNames } - -export { DataGridHeaderProps } - -export { DataGridHeaderSlots } - -export { DataGridHeaderState } - -export { DataGridProps } - -export { DataGridRow } - -export { dataGridRowClassNames } - -export { DataGridRowProps } - -export { DataGridRowSlots } - -export { DataGridRowState } - -export { DataGridSelectionCell } - -export { dataGridSelectionCellClassNames } - -export { DataGridSelectionCellProps } - -export { DataGridSelectionCellSlots } - -export { DataGridSelectionCellState } - -export { DataGridSlots } - -export { DataGridState } - -export { Dialog } - -export { DialogActions } - -export { dialogActionsClassNames } - -export { DialogActionsPosition } - -export { DialogActionsProps } - -export { DialogActionsSlots } - -export { DialogActionsState } - -export { DialogBody } - -export { dialogBodyClassNames } - -export { DialogBodyProps } - -export { DialogBodySlots } - -export { DialogBodyState } - -export { DialogContent } - -export { dialogContentClassNames } - -export { DialogContentProps } - -export { DialogContentSlots } +// @public +export const createFocusOutlineStyle: ({ enableOutline, selector, style, }?: CreateFocusOutlineStyleOptions) => GriffelStyle; + +// @public (undocumented) +export interface CreateFocusOutlineStyleOptions extends Omit { + enableOutline?: boolean; + // (undocumented) + style?: Partial; +} + +// @public (undocumented) +export const createHighContrastTheme: () => Theme; + +// @public (undocumented) +export const createLightTheme: (brand: BrandVariants) => Theme; + +// @public +export function createTableColumn(options: CreateTableColumnOptions): { + columnId: TableColumnId; + renderCell: (item: TItem) => ReactNode; + renderHeaderCell: () => ReactNode; + compare: (a: TItem, b: TItem) => number; +}; + +// @public (undocumented) +export interface CreateTableColumnOptions extends Partial> { + // (undocumented) + columnId: TableColumnId; +} -export { DialogContentState } +// @public (undocumented) +export const createTeamsDarkTheme: (brand: BrandVariants) => Theme; -export { DialogOpenChangeData } +// @public (undocumented) +export type CurveTokens = { + curveAccelerateMax: string; + curveAccelerateMid: string; + curveAccelerateMin: string; + curveDecelerateMax: string; + curveDecelerateMid: string; + curveDecelerateMin: string; + curveEasyEaseMax: string; + curveEasyEase: string; + curveLinear: string; +}; -export { DialogOpenChangeEvent } +// @public (undocumented) +export const DATA_OVERFLOW_ITEM = "data-overflow-item"; + +// @public (undocumented) +export const DATA_OVERFLOW_MENU = "data-overflow-menu"; + +// @public (undocumented) +export const DATA_OVERFLOWING = "data-overflowing"; + +// @public +export const DataGrid: ForwardRefComponent; -export { DialogProps } +// @public +export const DataGridBody: ForwardRefComponent & ((props: DataGridBodyProps) => JSX.Element); + +// @public (undocumented) +export const dataGridBodyClassNames: SlotClassNames; + +// @public +export type DataGridBodyProps = Omit & { + children: RowRenderFunction; +}; + +// @public (undocumented) +export type DataGridBodySlots = TableBodySlots; + +// @public +export type DataGridBodyState = TableBodyState & { + rows: TableRowData[]; + renderRow: RowRenderFunction; +}; + +// @public +export const DataGridCell: ForwardRefComponent; + +// @public (undocumented) +export const dataGridCellClassNames: SlotClassNames; + +// @public +export type DataGridCellProps = TableCellProps; + +// @public (undocumented) +export type DataGridCellSlots = TableCellSlots; -export { DialogSlots } +// @public +export type DataGridCellState = TableCellState; -export { DialogState } +// @public (undocumented) +export const dataGridClassNames: SlotClassNames; + +// @public (undocumented) +export type DataGridContextValue = TableFeaturesState & { + focusMode: DataGridFocusMode; + selectableRows: boolean; + subtleSelection: boolean; + selectionAppearance: TableRowProps['appearance']; + resizableColumns?: boolean; +}; + +// @public (undocumented) +export type DataGridContextValues = TableContextValues & { + dataGrid: DataGridContextValue; +}; -export { DialogSurface } +// @public +export const DataGridHeader: ForwardRefComponent; -export { dialogSurfaceClassNames } +// @public +export const DataGridHeaderCell: ForwardRefComponent; -export { DialogSurfaceProps } +// @public (undocumented) +export const dataGridHeaderCellClassNames: SlotClassNames; + +// @public +export type DataGridHeaderCellProps = TableHeaderCellProps; -export { DialogSurfaceSlots } +// @public (undocumented) +export type DataGridHeaderCellSlots = TableHeaderCellSlots; -export { DialogSurfaceState } +// @public +export type DataGridHeaderCellState = TableHeaderCellState; -export { DialogTitle } +// @public (undocumented) +export const dataGridHeaderClassNames: SlotClassNames; -export { dialogTitleClassNames } +// @public +export type DataGridHeaderProps = TableHeaderProps; -export { DialogTitleProps } +// @public (undocumented) +export type DataGridHeaderSlots = TableHeaderSlots; -export { DialogTitleSlots } +// @public +export type DataGridHeaderState = TableHeaderState; -export { DialogTitleState } +// @public +export type DataGridProps = TableProps & Pick & Pick, 'focusMode' | 'subtleSelection' | 'selectionAppearance' | 'resizableColumns'> & Pick & Pick & { + onSortChange?: (e: React_2.MouseEvent, sortState: SortState) => void; + onSelectionChange?: (e: React_2.MouseEvent | React_2.KeyboardEvent, data: OnSelectionChangeData) => void; + selectionMode?: SelectionMode_2; + columnSizingOptions?: TableColumnSizingOptions; + onColumnResize?: (e: KeyboardEvent | TouchEvent | MouseEvent | undefined, data: { + columnId: TableColumnId; + width: number; + }) => void; +}; -export { DialogTrigger } +// @public +export const DataGridRow: ForwardRefComponent & ((props: DataGridRowProps) => JSX.Element); -export { DialogTriggerAction } +// @public (undocumented) +export const dataGridRowClassNames: SlotClassNames; -export { DialogTriggerChildProps } +// @public +export type DataGridRowProps = Omit & Omit, 'children'> & { + children: CellRenderFunction; +}; -export { DialogTriggerProps } +// @public (undocumented) +export type DataGridRowSlots = TableRowSlots & { + selectionCell?: Slot; +}; -export { DialogTriggerState } +// @public +export type DataGridRowState = TableRowState & ComponentState & { + renderCell: CellRenderFunction; + columnDefs: TableColumnDefinition[]; + dataGridContextValue: DataGridContextValue; +}; -export { Display } +// @public +export const DataGridSelectionCell: ForwardRefComponent; -export { displayClassNames } +// @public (undocumented) +export const dataGridSelectionCellClassNames: SlotClassNames; -export { Divider } +// @public +export type DataGridSelectionCellProps = TableSelectionCellProps; + +// @public (undocumented) +export type DataGridSelectionCellSlots = TableSelectionCellSlots; + +// @public +export type DataGridSelectionCellState = TableSelectionCellState; + +// @public (undocumented) +export type DataGridSlots = TableSlots; + +// @public +export type DataGridState = TableState & { + tableState: TableFeaturesState; +} & Pick; + +// @public +export const Dialog: React_2.FC; + +// @public +export const DialogActions: ForwardRefComponent; -export { dividerClassNames } +// @public (undocumented) +export const dialogActionsClassNames: SlotClassNames; + +// @public (undocumented) +export type DialogActionsPosition = 'start' | 'end'; -export { DividerProps } +// @public +export type DialogActionsProps = ComponentProps & { + position?: DialogActionsPosition; + fluid?: boolean; +}; -export { DividerSlots } +// @public (undocumented) +export type DialogActionsSlots = { + root: Slot<'div'>; +}; + +// @public +export type DialogActionsState = ComponentState & Pick, 'position' | 'fluid'>; + +// @public +export const DialogBody: ForwardRefComponent; + +// @public (undocumented) +export const dialogBodyClassNames: SlotClassNames; + +// @public +export type DialogBodyProps = ComponentProps & {}; + +// @public (undocumented) +export type DialogBodySlots = { + root: Slot<'div'>; +}; + +// @public +export type DialogBodyState = ComponentState; + +// @public +export const DialogContent: ForwardRefComponent; + +// @public (undocumented) +export const dialogContentClassNames: SlotClassNames; + +// @public +export type DialogContentProps = ComponentProps; + +// @public (undocumented) +export type DialogContentSlots = { + root: Slot<'div'>; +}; + +// @public +export type DialogContentState = ComponentState; + +// @public (undocumented) +export type DialogOpenChangeData = { + type: 'escapeKeyDown'; + open: boolean; + event: React_2.KeyboardEvent; +} | { + type: 'backdropClick'; + open: boolean; + event: React_2.MouseEvent; +} | { + type: 'triggerClick'; + open: boolean; + event: React_2.MouseEvent; +}; -export { DividerState } +// @public (undocumented) +export type DialogOpenChangeEvent = DialogOpenChangeData['event']; + +// @public (undocumented) +export type DialogProps = ComponentProps> & { + modalType?: DialogModalType; + open?: boolean; + defaultOpen?: boolean; + onOpenChange?: DialogOpenChangeEventHandler; + children: [JSX.Element, JSX.Element] | JSX.Element; + inertTrapFocus?: boolean; +}; + +// @public (undocumented) +export type DialogSlots = {}; + +// @public (undocumented) +export type DialogState = ComponentState & DialogContextValue & { + content: React_2.ReactNode; + trigger: React_2.ReactNode; +}; + +// @public +export const DialogSurface: ForwardRefComponent; -export { Dropdown } +// @public (undocumented) +export const dialogSurfaceClassNames: SlotClassNames; -export { dropdownClassNames } - -export { DropdownContextValues } - -export { DropdownOpenChangeData } - -export { DropdownOpenEvents } - -export { DropdownProps } - -export { DropdownSlots } - -export { DropdownState } - -export { DurationTokens } - -export { Field } - -export { fieldClassNames } - -export { FieldContextProvider } - -export { FieldContextValue } - -export { FieldContextValues } - -export { FieldControlProps } - -export { FieldControlPropsOptions } - -export { FieldProps } - -export { FieldSlots } - -export { FieldState } - -export { FluentProvider } - -export { fluentProviderClassNames } - -export { FluentProviderContextValues } - -export { FluentProviderCustomStyleHooks } - -export { FluentProviderProps } - -export { FluentProviderSlots } - -export { FluentProviderState } - -export { FontFamilyTokens } - -export { FontSizeTokens } - -export { FontWeightTokens } - -export { ForwardRefComponent } - -export { getNativeElementProps } - -export { getPartitionedNativeProps } - -export { getSlots } +// @public +export type DialogSurfaceProps = ComponentProps; + +// @public (undocumented) +export type DialogSurfaceSlots = { + backdrop?: Slot<'div'>; + root: Slot<'div'>; +}; + +// @public +export type DialogSurfaceState = ComponentState; + +// @public +export const DialogTitle: ForwardRefComponent; + +// @public (undocumented) +export const dialogTitleClassNames: SlotClassNames; + +// @public +export type DialogTitleProps = ComponentProps; + +// @public (undocumented) +export type DialogTitleSlots = { + root: Slot<'h2', 'h1' | 'h3' | 'h4' | 'h5' | 'h6' | 'div'>; + action?: Slot<'div'>; +}; + +// @public +export type DialogTitleState = ComponentState; + +// @public +export const DialogTrigger: React_2.FC; + +// @public (undocumented) +export type DialogTriggerAction = 'open' | 'close'; + +// @public +export type DialogTriggerChildProps = ARIAButtonResultProps; + +// @public (undocumented) +export type DialogTriggerProps = TriggerProps & { + action?: DialogTriggerAction; + disableButtonEnhancement?: boolean; +}; + +// @public (undocumented) +export type DialogTriggerState = { + children: React_2.ReactElement | null; +}; + +// @public +export const Display: FunctionComponent; + +// @public (undocumented) +export const displayClassNames: SlotClassNames; + +// @public +export const Divider: ForwardRefComponent; + +// @public (undocumented) +export const dividerClassNames: SlotClassNames; + +// @public (undocumented) +export type DividerProps = ComponentProps> & { + alignContent?: 'start' | 'center' | 'end'; + appearance?: 'brand' | 'default' | 'strong' | 'subtle'; + inset?: boolean; + vertical?: boolean; +}; + +// @public (undocumented) +export type DividerSlots = { + root: NonNullable>; + wrapper: NonNullable>; +}; + +// @public (undocumented) +export type DividerState = ComponentState & Required>; + +// @public +export const Dropdown: ForwardRefComponent; + +// @public (undocumented) +export const dropdownClassNames: SlotClassNames; + +// @public (undocumented) +export type DropdownContextValues = ComboboxBaseContextValues; + +// @public (undocumented) +export type DropdownOpenChangeData = ComboboxBaseOpenChangeData; + +// @public (undocumented) +export type DropdownOpenEvents = ComboboxBaseOpenEvents; + +// @public +export type DropdownProps = ComponentProps, 'button'> & ComboboxBaseProps; + +// @public (undocumented) +export type DropdownSlots = { + root: NonNullable>; + expandIcon: Slot<'span'>; + button: NonNullable>; + listbox?: Slot; +}; + +// @public +export type DropdownState = ComponentState & ComboboxBaseState & { + placeholderVisible: boolean; +}; + +// @public (undocumented) +export type DurationTokens = { + durationUltraFast: string; + durationFaster: string; + durationFast: string; + durationNormal: string; + durationSlow: string; + durationSlower: string; + durationUltraSlow: string; +}; + +// @public (undocumented) +export const Field: ForwardRefComponent; + +// @public (undocumented) +export const fieldClassNames: SlotClassNames; + +// @public (undocumented) +export const FieldContextProvider: React_2.Provider & { + labelFor?: string | undefined; + labelId?: string | undefined; + validationMessageId?: string | undefined; + hintId?: string | undefined; +}> | undefined>; + +// @public (undocumented) +export type FieldContextValue = Readonly & { + labelFor?: string; + labelId?: string; + validationMessageId?: string; + hintId?: string; +}>; + +// @public (undocumented) +export type FieldContextValues = { + field: FieldContextValue; +}; + +// @public +export type FieldControlProps = Pick, 'id' | 'aria-labelledby' | 'aria-describedby' | 'aria-invalid' | 'aria-required'>; + +// @public +export type FieldControlPropsOptions = { + supportsLabelFor?: boolean; + supportsRequired?: boolean; + supportsSize?: boolean; +}; + +// @public +export type FieldProps = Omit, 'children'> & { + children?: React_2.ReactNode | ((props: FieldControlProps) => React_2.ReactNode); + orientation?: 'vertical' | 'horizontal'; + validationState?: 'error' | 'warning' | 'success' | 'none'; + required?: boolean; + size?: 'small' | 'medium' | 'large'; +}; + +// @public +export type FieldSlots = { + root: NonNullable>; + label?: Slot; + validationMessage?: Slot<'div'>; + validationMessageIcon?: Slot<'span'>; + hint?: Slot<'div'>; +}; + +// @public +export type FieldState = ComponentState> & Required> & Pick & { + generatedControlId: string; +}; + +// @public (undocumented) +export const FluentProvider: React_2.ForwardRefExoticComponent, "dir"> & { + applyStylesToPortals?: boolean | undefined; + customStyleHooks_unstable?: Partial<{ + useAccordionHeaderStyles_unstable: (state: unknown) => void; + useAccordionItemStyles_unstable: (state: unknown) => void; + useAccordionPanelStyles_unstable: (state: unknown) => void; + useAccordionStyles_unstable: (state: unknown) => void; + useAvatarStyles_unstable: (state: unknown) => void; + useAvatarGroupStyles_unstable: (state: unknown) => void; + useAvatarGroupItemStyles_unstable: (state: unknown) => void; + useAvatarGroupPopoverStyles_unstable: (state: unknown) => void; + useBadgeStyles_unstable: (state: unknown) => void; + useCounterBadgeStyles_unstable: (state: unknown) => void; + useCardHeaderStyles_unstable: (state: unknown) => void; + useCardStyles_unstable: (state: unknown) => void; + useCardFooterStyles_unstable: (state: unknown) => void; + useCardPreviewStyles_unstable: (state: unknown) => void; + usePresenceBadgeStyles_unstable: (state: unknown) => void; + useButtonStyles_unstable: (state: unknown) => void; + useCompoundButtonStyles_unstable: (state: unknown) => void; + useMenuButtonStyles_unstable: (state: unknown) => void; + useSplitButtonStyles_unstable: (state: unknown) => void; + useToggleButtonStyles_unstable: (state: unknown) => void; + useCheckboxStyles_unstable: (state: unknown) => void; + useComboboxStyles_unstable: (state: unknown) => void; + useDropdownStyles_unstable: (state: unknown) => void; + useListboxStyles_unstable: (state: unknown) => void; + useOptionStyles_unstable: (state: unknown) => void; + useOptionGroupStyles_unstable: (state: unknown) => void; + useDividerStyles_unstable: (state: unknown) => void; + useInputStyles_unstable: (state: unknown) => void; + useImageStyles_unstable: (state: unknown) => void; + useLabelStyles_unstable: (state: unknown) => void; + useLinkStyles_unstable: (state: unknown) => void; + useMenuDividerStyles_unstable: (state: unknown) => void; + useMenuGroupHeaderStyles_unstable: (state: unknown) => void; + useMenuGroupStyles_unstable: (state: unknown) => void; + useMenuItemCheckboxStyles_unstable: (state: unknown) => void; + useMenuItemRadioStyles_unstable: (state: unknown) => void; + useMenuItemStyles_unstable: (state: unknown) => void; + useMenuListStyles_unstable: (state: unknown) => void; + useMenuPopoverStyles_unstable: (state: unknown) => void; + useMenuSplitGroupStyles_unstable: (state: unknown) => void; + usePersonaStyles_unstable: (state: unknown) => void; + usePopoverSurfaceStyles_unstable: (state: unknown) => void; + useRadioGroupStyles_unstable: (state: unknown) => void; + useRadioStyles_unstable: (state: unknown) => void; + useSelectStyles_unstable: (state: unknown) => void; + useSliderStyles_unstable: (state: unknown) => void; + useSpinButtonStyles_unstable: (state: unknown) => void; + useSpinnerStyles_unstable: (state: unknown) => void; + useSwitchStyles_unstable: (state: unknown) => void; + useTabStyles_unstable: (state: unknown) => void; + useTabListStyles_unstable: (state: unknown) => void; + useTextStyles_unstable: (state: unknown) => void; + useTextareaStyles_unstable: (state: unknown) => void; + useTooltipStyles_unstable: (state: unknown) => void; + useDialogTitleStyles_unstable: (state: unknown) => void; + useDialogBodyStyles_unstable: (state: unknown) => void; + useDialogActionsStyles_unstable: (state: unknown) => void; + useDialogSurfaceStyles_unstable: (state: unknown) => void; + useDialogContentStyles_unstable: (state: unknown) => void; + useProgressBarStyles_unstable: (state: unknown) => void; + useToolbarButtonStyles_unstable: (state: unknown) => void; + useToolbarRadioButtonStyles_unstable: (state: unknown) => void; + useToolbarGroupStyles_unstable: (state: unknown) => void; + useToolbarToggleButtonStyles_unstable: (state: unknown) => void; + useToolbarDividerStyles_unstable: (state: unknown) => void; + useToolbarStyles_unstable: (state: unknown) => void; + useTableCellStyles_unstable: (state: unknown) => void; + useTableRowStyles_unstable: (state: unknown) => void; + useTableBodyStyles_unstable: (state: unknown) => void; + useTableStyles_unstable: (state: unknown) => void; + useTableHeaderStyles_unstable: (state: unknown) => void; + useTableHeaderCellStyles_unstable: (state: unknown) => void; + useTableResizeHandleStyles_unstable: (state: unknown) => void; + useTableSelectionCellStyles_unstable: (state: unknown) => void; + useTableCellActionsStyles_unstable: (state: unknown) => void; + useTableCellLayoutStyles_unstable: (state: unknown) => void; + useDataGridCellStyles_unstable: (state: unknown) => void; + useDataGridRowStyles_unstable: (state: unknown) => void; + useDataGridBodyStyles_unstable: (state: unknown) => void; + useDataGridStyles_unstable: (state: unknown) => void; + useDataGridHeaderStyles_unstable: (state: unknown) => void; + useDataGridHeaderCellStyles_unstable: (state: unknown) => void; + useDataGridSelectionCellStyles_unstable: (state: unknown) => void; + useDrawerStyles_unstable: (state: unknown) => void; + useDrawerBodyStyles_unstable: (state: unknown) => void; + useDrawerHeaderStyles_unstable: (state: unknown) => void; + useDrawerHeaderTitleStyles_unstable: (state: unknown) => void; + useDrawerHeaderNavigationStyles_unstable: (state: unknown) => void; + }> | undefined; + dir?: "ltr" | "rtl" | undefined; + targetDocument?: Document | undefined; + theme?: Partial | undefined; + overrides_unstable?: OverridesContextValue | undefined; +} & React_2.RefAttributes>; + +// @public (undocumented) +export const fluentProviderClassNames: SlotClassNames; + +// @public (undocumented) +export type FluentProviderContextValues = Pick & { + provider: ProviderContextValue; + themeClassName: ThemeClassNameContextValue; + textDirection: 'ltr' | 'rtl'; + tooltip: TooltipVisibilityContextValue; +}; + +// @public (undocumented) +export type FluentProviderCustomStyleHooks = CustomStyleHooksContextValue; + +// @public (undocumented) +export type FluentProviderProps = Omit, 'dir'> & { + applyStylesToPortals?: boolean; + customStyleHooks_unstable?: FluentProviderCustomStyleHooks; + dir?: 'ltr' | 'rtl'; + targetDocument?: Document; + theme?: PartialTheme; + overrides_unstable?: OverridesContextValue; +}; + +// @public (undocumented) +export type FluentProviderSlots = { + root: Slot<'div'>; +}; + +// @public (undocumented) +export type FluentProviderState = ComponentState & Pick & Required> & { + theme: ThemeContextValue; + themeClassName: string; + serverStyleProps: { + cssRule: string; + attributes: Record; + }; +}; + +// @public (undocumented) +export type FontFamilyTokens = { + fontFamilyBase: string; + fontFamilyMonospace: string; + fontFamilyNumeric: string; +}; + +// @public (undocumented) +export type FontSizeTokens = { + fontSizeBase100: string; + fontSizeBase200: string; + fontSizeBase300: string; + fontSizeBase400: string; + fontSizeBase500: string; + fontSizeBase600: string; + fontSizeHero700: string; + fontSizeHero800: string; + fontSizeHero900: string; + fontSizeHero1000: string; +}; + +// @public (undocumented) +export type FontWeightTokens = { + fontWeightRegular: number; + fontWeightMedium: number; + fontWeightSemibold: number; + fontWeightBold: number; +}; + +// @public +export type ForwardRefComponent = ObscureEventName extends keyof Props ? Required[ObscureEventName] extends React_2.PointerEventHandler ? React_2.ForwardRefExoticComponent> : never : never; + +// @public +export function getNativeElementProps>(tagName: string, props: {}, excludedPropNames?: string[]): TAttributes; + +// @public +export const getPartitionedNativeProps: , "style" | "className">, ExcludedPropKeys extends Extract = never>({ primarySlotTagName, props, excludedPropNames, }: { + primarySlotTagName: keyof JSX.IntrinsicElements; + props: Props; + excludedPropNames?: ExcludedPropKeys[] | undefined; +}) => { + root: { + style: React_2.CSSProperties | undefined; + className: string | undefined; + }; + primary: Omit; +}; + +// @public +export function getSlots(state: ComponentState): { + slots: Slots; + slotProps: ObjectSlotProps; +}; export { GriffelRenderer } export { GriffelStyle } -export { HorizontalSpacingTokens } - -export { IdPrefixProvider } - +// @public (undocumented) +export type HorizontalSpacingTokens = { + spacingHorizontalNone: string; + spacingHorizontalXXS: string; + spacingHorizontalXS: string; + spacingHorizontalSNudge: string; + spacingHorizontalS: string; + spacingHorizontalMNudge: string; + spacingHorizontalM: string; + spacingHorizontalL: string; + spacingHorizontalXL: string; + spacingHorizontalXXL: string; + spacingHorizontalXXXL: string; +}; + +// @public +export const IdPrefixProvider: React_2.Provider; + +// @public +const Image_2: ForwardRefComponent; export { Image_2 as Image } -export { imageClassNames } - -export { ImageProps } - -export { ImageSlots } - -export { ImageState } - -export { Input } - -export { inputClassNames } - -export { InputOnChangeData } - -export { InputProps } - -export { InputSlots } - -export { InputState } - -export { Label } - -export { labelClassNames } - -export { LabelProps } - -export { LabelSlots } - -export { LabelState } - -export { LargeTitle } +// @public (undocumented) +export const imageClassNames: SlotClassNames; + +// @public (undocumented) +export type ImageProps = ComponentProps & { + block?: boolean; + bordered?: boolean; + fit?: 'none' | 'center' | 'contain' | 'cover' | 'default'; + shadow?: boolean; + shape?: 'square' | 'circular' | 'rounded'; +}; + +// @public (undocumented) +export type ImageSlots = { + root: Slot<'img'>; +}; + +// @public (undocumented) +export type ImageState = ComponentState & Required>; + +// @public +export const Input: ForwardRefComponent; + +// @public (undocumented) +export const inputClassNames: SlotClassNames; + +// @public +export type InputOnChangeData = { + value: string; +}; + +// @public (undocumented) +export type InputProps = Omit, 'input'>, 'children' | 'defaultValue' | 'onChange' | 'size' | 'type' | 'value'> & { + children?: never; + size?: 'small' | 'medium' | 'large'; + appearance?: 'outline' | 'underline' | 'filled-darker' | 'filled-lighter' | 'filled-darker-shadow' | 'filled-lighter-shadow'; + defaultValue?: string; + value?: string; + onChange?: (ev: React_2.ChangeEvent, data: InputOnChangeData) => void; + type?: 'text' | 'email' | 'password' | 'search' | 'tel' | 'url' | 'date' | 'datetime-local' | 'month' | 'number' | 'time' | 'week'; +}; + +// @public (undocumented) +export type InputSlots = { + root: NonNullable>; + input: NonNullable>; + contentBefore?: Slot<'span'>; + contentAfter?: Slot<'span'>; +}; + +// @public +export type InputState = Required> & ComponentState; + +// @public +export const Label: ForwardRefComponent; + +// @public (undocumented) +export const labelClassNames: SlotClassNames; + +// @public +export type LabelProps = Omit, 'required'> & { + disabled?: boolean; + required?: boolean | Slot<'span'>; + size?: 'small' | 'medium' | 'large'; + weight?: 'regular' | 'semibold'; +}; + +// @public (undocumented) +export type LabelSlots = { + root: Slot<'label'>; + required?: Slot<'span'>; +}; + +// @public +export type LabelState = ComponentState & Required>; + +// @public +export const LargeTitle: FunctionComponent; + +// @public (undocumented) +export const largeTitleClassNames: SlotClassNames; + +// @public (undocumented) +export type LineHeightTokens = { + lineHeightBase100: string; + lineHeightBase200: string; + lineHeightBase300: string; + lineHeightBase400: string; + lineHeightBase500: string; + lineHeightBase600: string; + lineHeightHero700: string; + lineHeightHero800: string; + lineHeightHero900: string; + lineHeightHero1000: string; +}; + +// @public +export const Link: ForwardRefComponent; + +// @public (undocumented) +export const linkClassNames: SlotClassNames; + +// @public (undocumented) +export type LinkProps = ComponentProps & { + appearance?: 'default' | 'subtle'; + disabled?: boolean; + disabledFocusable?: boolean; + inline?: boolean; +}; + +// @public (undocumented) +export type LinkSlots = { + root: Slot<'a', 'button'>; +}; + +// @public (undocumented) +export type LinkState = ComponentState & Required>; + +// @public +export const Listbox: ForwardRefComponent; + +// @public (undocumented) +export const listboxClassNames: SlotClassNames; + +// @public +export type ListboxContextValue = Pick; + +// @public (undocumented) +export type ListboxContextValues = { + listbox: ListboxContextValue; +}; + +// @public +export type ListboxProps = ComponentProps & SelectionProps; + +// @public (undocumented) +export const ListboxProvider: Provider & FC>; + +// @public (undocumented) +export type ListboxSlots = { + root: Slot<'div'>; +}; + +// @public +export type ListboxState = ComponentState & OptionCollectionState & Pick & SelectionState & { + activeOption?: OptionValue; + focusVisible: boolean; + selectOption(event: SelectionEvents, option: OptionValue): void; + setActiveOption(option?: OptionValue): void; +}; -export { largeTitleClassNames } - -export { LineHeightTokens } - -export { Link } +export { makeResetStyles } -export { linkClassNames } +export { makeStaticStyles } -export { LinkProps } +export { makeStyles } -export { LinkSlots } +// @public +export const Menu: React_2.FC; -export { LinkState } +// @public +export const MenuButton: ForwardRefComponent; -export { Listbox } +// @public (undocumented) +export const menuButtonClassNames: SlotClassNames; -export { listboxClassNames } +// @public (undocumented) +export type MenuButtonProps = ComponentProps & Pick; -export { ListboxContextValue } +// @public (undocumented) +export type MenuButtonSlots = ButtonSlots & { + menuIcon?: Slot<'span'>; +}; -export { ListboxContextValues } +// @public (undocumented) +export type MenuButtonState = ComponentState & Omit; -export { ListboxProps } +// @public (undocumented) +export type MenuCheckedValueChangeData = { + checkedItems: string[]; + name: string; +}; -export { ListboxProvider } +// @public (undocumented) +export type MenuCheckedValueChangeEvent = React_2.MouseEvent | React_2.KeyboardEvent; -export { ListboxSlots } +// @public +export type MenuContextValue = Pick & { + open: boolean; + triggerId: string; + defaultCheckedValues?: Record; +}; -export { ListboxState } +// @public (undocumented) +export type MenuContextValues = { + menu: MenuContextValue; +}; -export { makeResetStyles } +// @public +export const MenuDivider: ForwardRefComponent; -export { makeStaticStyles } +// @public (undocumented) +export const menuDividerClassNames: SlotClassNames; -export { makeStyles } +// @public (undocumented) +export type MenuDividerProps = ComponentProps; -export { Menu } +// @public (undocumented) +export type MenuDividerSlots = { + root: Slot<'div'>; +}; -export { MenuButton } +// @public (undocumented) +export type MenuDividerState = ComponentState; -export { menuButtonClassNames } +// @public +export const MenuGroup: ForwardRefComponent; -export { MenuButtonProps } +// @public (undocumented) +export const menuGroupClassNames: SlotClassNames; -export { MenuButtonSlots } +// @public (undocumented) +export const MenuGroupContextProvider: React_2.Provider; -export { MenuButtonState } +// @public +export type MenuGroupContextValue = { + headerId: string; +}; -export { MenuCheckedValueChangeData } +// @public (undocumented) +export type MenuGroupContextValues = { + menuGroup: MenuGroupContextValue; +}; -export { MenuCheckedValueChangeEvent } +// @public +export const MenuGroupHeader: ForwardRefComponent; -export { MenuContextValue } +// @public (undocumented) +export const menuGroupHeaderClassNames: SlotClassNames; -export { MenuContextValues } +// @public (undocumented) +export type MenuGroupHeaderProps = ComponentProps; -export { MenuDivider } +// @public (undocumented) +export type MenuGroupHeaderSlots = { + root: Slot<'div'>; +}; -export { menuDividerClassNames } +// @public (undocumented) +export type MenuGroupHeaderState = ComponentState; -export { MenuDividerProps } +// @public (undocumented) +export type MenuGroupProps = ComponentProps; -export { MenuDividerSlots } +// @public (undocumented) +export type MenuGroupSlots = { + root: Slot<'div'>; +}; + +// @public (undocumented) +export type MenuGroupState = ComponentState & { + headerId: string; +}; + +// @public +export const MenuItem: ForwardRefComponent; + +// @public +export const MenuItemCheckbox: ForwardRefComponent; + +// @public (undocumented) +export const menuItemCheckboxClassNames: SlotClassNames>; + +// @public (undocumented) +export type MenuItemCheckboxProps = MenuItemProps & MenuItemSelectableProps; + +// @public (undocumented) +export type MenuItemCheckboxState = MenuItemState & MenuItemSelectableState; + +// @public (undocumented) +export const menuItemClassNames: SlotClassNames; + +// @public (undocumented) +export type MenuItemProps = ComponentProps> & { + hasSubmenu?: boolean; + persistOnClick?: boolean; + disabled?: boolean; + disabledFocusable?: boolean; +}; + +// @public +export const MenuItemRadio: ForwardRefComponent; + +// @public (undocumented) +export const menuItemRadioClassNames: SlotClassNames>; + +// @public (undocumented) +export type MenuItemRadioProps = MenuItemProps & MenuItemSelectableProps; + +// @public (undocumented) +export type MenuItemRadioState = MenuItemState & MenuItemSelectableState; + +// @public +export type MenuItemSelectableProps = { + name: string; + value: string; +}; + +// @public +export type MenuItemSelectableState = MenuItemSelectableProps & { + checked: boolean; +}; + +// @public (undocumented) +export type MenuItemSlots = { + root: Slot<'div'>; + icon?: Slot<'span'>; + checkmark?: Slot<'span'>; + submenuIndicator?: Slot<'span'>; + content?: Slot<'span'>; + secondaryContent?: Slot<'span'>; +}; + +// @public (undocumented) +export type MenuItemState = ComponentState & Required>; + +// @public +export const MenuList: ForwardRefComponent; + +// @public (undocumented) +export const menuListClassNames: SlotClassNames; + +// @public +export type MenuListContextValue = Pick & { + setFocusByFirstCharacter?: (e: React_2.KeyboardEvent, itemEl: HTMLElement) => void; + toggleCheckbox?: SelectableHandler; + selectRadio?: SelectableHandler; + onCheckedValueChange?: (e: MenuCheckedValueChangeEvent, data: MenuCheckedValueChangeData) => void; +}; + +// @public (undocumented) +export type MenuListContextValues = { + menuList: MenuListContextValue; +}; + +// @public (undocumented) +export type MenuListProps = ComponentProps & { + checkedValues?: Record; + defaultCheckedValues?: Record; + hasCheckmarks?: boolean; + hasIcons?: boolean; + onCheckedValueChange?: (e: MenuCheckedValueChangeEvent, data: MenuCheckedValueChangeData) => void; +}; + +// @public (undocumented) +export const MenuListProvider: React_2.Provider & React_2.FC>; + +// @public (undocumented) +export type MenuListSlots = { + root: Slot<'div'>; +}; + +// @public (undocumented) +export type MenuListState = ComponentState & Required> & Pick & { + selectRadio: SelectableHandler; + setFocusByFirstCharacter: NonNullable; + toggleCheckbox: SelectableHandler; +}; + +// @public +export type MenuOpenChangeData = { + bubble?: boolean; + keyboard?: boolean; + open: boolean; +} & ({ + type: 'menuTriggerContextMenu'; + event: React_2.MouseEvent; +} | { + type: 'menuTriggerClick'; + event: React_2.MouseEvent; +} | { + type: 'menuTriggerMouseEnter'; + event: React_2.MouseEvent; +} | { + type: 'menuTriggerMouseLeave'; + event: React_2.MouseEvent; +} | { + type: 'menuTriggerMouseMove'; + event: React_2.MouseEvent; +} | { + type: 'menuTriggerKeyDown'; + event: React_2.KeyboardEvent; +} | { + type: 'menuItemClick'; + event: React_2.MouseEvent; +} | { + type: 'menuPopoverMouseEnter'; + event: React_2.MouseEvent; +} | { + type: 'menuPopoverKeyDown'; + event: React_2.KeyboardEvent; +} | { + type: 'clickOutside'; + event: MouseEvent | TouchEvent; +} | { + type: 'scrollOutside'; + event: MouseEvent | TouchEvent; +} | { + type: 'menuMouseEnter'; + event: MouseEvent | TouchEvent; +}); + +// @public +export type MenuOpenEvent = MenuOpenChangeData['event']; + +// @public @deprecated (undocumented) +export type MenuOpenEvents = MenuOpenEvent; + +// @public +export const MenuPopover: ForwardRefComponent; + +// @public (undocumented) +export const menuPopoverClassNames: SlotClassNames; + +// @public +export type MenuPopoverProps = ComponentProps; + +// @public (undocumented) +export type MenuPopoverSlots = { + root: Slot<'div'>; +}; + +// @public +export type MenuPopoverState = ComponentState & Pick & { + inline: boolean; +}; + +// @public +export type MenuProps = ComponentProps & Pick & Pick & { + children: [JSX.Element, JSX.Element] | JSX.Element; + hoverDelay?: number; + inline?: boolean; + onOpenChange?: (e: MenuOpenEvent, data: MenuOpenChangeData) => void; + open?: boolean; + defaultOpen?: boolean; + openOnContext?: boolean; + openOnHover?: boolean; + persistOnItemClick?: boolean; + positioning?: PositioningShorthand; + closeOnScroll?: boolean; +}; + +// @public (undocumented) +export const MenuProvider: React_2.Provider & React_2.FC>; + +// @public (undocumented) +export type MenuSlots = {}; + +// @public +export const MenuSplitGroup: ForwardRefComponent; + +// @public (undocumented) +export const menuSplitGroupClassNames: SlotClassNames; + +// @public +export type MenuSplitGroupProps = ComponentProps; + +// @public (undocumented) +export type MenuSplitGroupSlots = { + root: Slot<'div'>; +}; + +// @public +export type MenuSplitGroupState = ComponentState; + +// @public (undocumented) +export type MenuState = ComponentState & Required> & { + contextTarget?: PositioningVirtualElement; + isSubmenu: boolean; + menuPopover: React_2.ReactNode; + menuPopoverRef: React_2.MutableRefObject; + menuTrigger: React_2.ReactNode; + setContextTarget: SetVirtualMouseTarget; + setOpen: (e: MenuOpenEvent, data: MenuOpenChangeData) => void; + triggerId: string; + triggerRef: React_2.MutableRefObject; + onOpenChange?: (e: MenuOpenEvent, data: MenuOpenChangeData) => void; + defaultCheckedValues?: Record; +}; + +// @public +export const MenuTrigger: React_2.FC; + +// @public +export type MenuTriggerChildProps = ARIAButtonResultProps; + onMouseEnter: React_2.MouseEventHandler; + onMouseLeave: React_2.MouseEventHandler; + onMouseMove: React_2.MouseEventHandler; + onContextMenu: React_2.MouseEventHandler; +}>; + +// @public (undocumented) +export const MenuTriggerContextProvider: React_2.Provider; + +// @public (undocumented) +export type MenuTriggerProps = TriggerProps & { + disableButtonEnhancement?: boolean; +}; + +// @public (undocumented) +export type MenuTriggerState = { + children: React_2.ReactElement | null; + isSubmenu: boolean; +}; -export { MenuDividerState } +export { mergeClasses } -export { MenuGroup } +// @public +export type OnOpenChangeData = { + open: boolean; +}; -export { menuGroupClassNames } +// @public +export type OnVisibleChangeData = { + visible: boolean; +}; -export { MenuGroupContextProvider } +// @public +export type OpenPopoverEvents = MouseEvent | TouchEvent | React_2.FocusEvent | React_2.KeyboardEvent | React_2.MouseEvent; -export { MenuGroupContextValue } +// @public +const Option_2: ForwardRefComponent; +export { Option_2 as Option } -export { MenuGroupContextValues } +// @public (undocumented) +export const optionClassNames: SlotClassNames; + +// @public +export const OptionGroup: ForwardRefComponent; + +// @public (undocumented) +export const optionGroupClassNames: SlotClassNames; + +// @public +export type OptionGroupProps = ComponentProps>; + +// @public (undocumented) +export type OptionGroupSlots = { + root: NonNullable>; + label?: Slot<'span'>; +}; + +// @public +export type OptionGroupState = ComponentState; + +// @public +export type OptionProps = ComponentProps> & { + disabled?: boolean; + value?: string; +} & ({ + text?: string; + children: string; +} | { + text: string; + children?: React_2.ReactNode; +}); + +// @public (undocumented) +export type OptionSlots = { + root: NonNullable>; + checkIcon: Slot<'span'>; +}; + +// @public +export type OptionState = ComponentState & Pick & { + active: boolean; + focusVisible: boolean; + multiselect?: boolean; + selected: boolean; +}; + +// @public +export const Overflow: React_2.ForwardRefExoticComponent> & { + children: React_2.ReactElement; +} & React_2.RefAttributes>; + +// @public +export const OverflowItem: React_2.ForwardRefExoticComponent>; + +// @public +export type OverflowItemProps = { + id: string; + groupId?: string; + priority?: number; + children: React_2.ReactElement; +}; + +// @public +export type OverflowProps = Partial> & { + children: React_2.ReactElement; +}; + +// @public (undocumented) +export type PartialTheme = Partial; + +// @public (undocumented) +export type PartitionAvatarGroupItems = { + inlineItems: readonly T[]; + overflowItems?: readonly T[]; +}; + +// @public +export const partitionAvatarGroupItems: (options: PartitionAvatarGroupItemsOptions) => PartitionAvatarGroupItems; + +// @public (undocumented) +export type PartitionAvatarGroupItemsOptions = { + items: readonly T[]; + layout?: 'spread' | 'stack' | 'pie'; + maxInlineItems?: number; +}; + +// @public +export const Persona: ForwardRefComponent; + +// @public (undocumented) +export const personaClassNames: SlotClassNames; + +// @public +export type PersonaProps = ComponentProps & { + name?: string; + presenceOnly?: boolean; + size?: 'extra-small' | 'small' | 'medium' | 'large' | 'extra-large' | 'huge'; + textPosition?: 'after' | 'before' | 'below'; + textAlignment?: 'center' | 'start'; +}; + +// @public (undocumented) +export type PersonaSlots = { + root: NonNullable>; + avatar?: Slot; + presence?: Slot; + primaryText?: Slot<'span'>; + secondaryText?: Slot<'span'>; + tertiaryText?: Slot<'span'>; + quaternaryText?: Slot<'span'>; +}; + +// @public +export type PersonaState = ComponentState & Required> & { + numTextLines: number; +}; + +// @public +export const Popover: React_2.FC; + +// @public +export type PopoverContextValue = Pick; + +// @public +export type PopoverProps = Pick & { + appearance?: 'brand' | 'inverted'; + children: [JSX.Element, JSX.Element] | JSX.Element; + closeOnScroll?: boolean; + defaultOpen?: boolean; + inline?: boolean; + mouseLeaveDelay?: number; + withArrow?: boolean; + onOpenChange?: (e: OpenPopoverEvents, data: OnOpenChangeData) => void; + open?: boolean; + openOnContext?: boolean; + openOnHover?: boolean; + positioning?: PositioningShorthand; + size?: PopoverSize; + trapFocus?: boolean; + legacyTrapFocus?: boolean; + inertTrapFocus?: boolean; + unstable_disableAutoFocus?: boolean; +}; + +// @public (undocumented) +export const PopoverProvider: Provider & FC>; + +// @public +export type PopoverSize = 'small' | 'medium' | 'large'; + +// @public +export type PopoverState = Pick & Required> & Pick & { + arrowRef: React_2.MutableRefObject; + contentRef: React_2.MutableRefObject; + contextTarget: PositioningVirtualElement | undefined; + popoverSurface: React_2.ReactElement | undefined; + popoverTrigger: React_2.ReactElement | undefined; + setContextTarget: SetVirtualMouseTarget; + setOpen: (e: OpenPopoverEvents, open: boolean) => void; + size: NonNullable; + toggleOpen: (e: OpenPopoverEvents) => void; + triggerRef: React_2.MutableRefObject; +}; + +// @public +export const PopoverSurface: ForwardRefComponent; + +// @public (undocumented) +export const popoverSurfaceClassNames: SlotClassNames; + +// @public +export type PopoverSurfaceProps = ComponentProps; + +// @public +export type PopoverSurfaceSlots = { + root: Slot<'div'>; +}; + +// @public +export type PopoverSurfaceState = ComponentState & Pick & { + arrowClassName?: string; +}; + +// @public +export const PopoverTrigger: React_2.FC; + +// @public +export type PopoverTriggerChildProps = ARIAButtonResultProps; + onMouseEnter: React_2.MouseEventHandler; + onMouseLeave: React_2.MouseEventHandler; + onContextMenu: React_2.MouseEventHandler; +}>; + +// @public +export type PopoverTriggerProps = TriggerProps & { + disableButtonEnhancement?: boolean; +}; + +// @public +export type PopoverTriggerState = { + children: React_2.ReactElement | null; +}; + +// @public +export const Portal: React_2.FC; + +// @public (undocumented) +export type PortalProps = { + children?: React_2.ReactNode; + mountNode?: HTMLElement | null | { + element?: HTMLElement | null; + className?: string; + }; +}; + +// @public (undocumented) +export type PortalState = Pick & { + mountNode: HTMLElement | null | undefined; + virtualParentRootRef: React_2.MutableRefObject; +}; + +// @public (undocumented) +export type PositioningImperativeRef = { + updatePosition: () => void; + setTarget: (target: TargetElement) => void; +}; + +// @public (undocumented) +export interface PositioningProps extends Pick { + positioningRef?: React_2.Ref; + target?: TargetElement | null; +} + +// @public (undocumented) +export type PositioningShorthand = PositioningProps | PositioningShorthandValue; + +// @public (undocumented) +export type PositioningShorthandValue = 'above' | 'above-start' | 'above-end' | 'below' | 'below-start' | 'below-end' | 'before' | 'before-top' | 'before-bottom' | 'after' | 'after-top' | 'after-bottom'; + +// @public (undocumented) +export type PositioningVirtualElement = { + getBoundingClientRect: () => { + x: number; + y: number; + top: number; + left: number; + bottom: number; + right: number; + width: number; + height: number; + }; + contextElement?: Element; +}; + +// @public +export const PresenceBadge: ForwardRefComponent; + +// @public (undocumented) +export const presenceBadgeClassNames: SlotClassNames; + +// @public (undocumented) +export type PresenceBadgeProps = Omit>, 'color'> & Pick & { + status?: PresenceBadgeStatus; + outOfOffice?: boolean; +}; + +// @public (undocumented) +export type PresenceBadgeState = ComponentState & BadgeState & Required>; + +// @public (undocumented) +export type PresenceBadgeStatus = 'busy' | 'out-of-office' | 'away' | 'available' | 'offline' | 'do-not-disturb' | 'unknown' | 'blocked'; + +// @public +export const ProgressBar: ForwardRefComponent; + +// @public (undocumented) +export const progressBarClassNames: SlotClassNames; + +// @public +export type ProgressBarProps = Omit, 'size'> & { + shape?: 'rounded' | 'square'; + value?: number; + max?: number; + thickness?: 'medium' | 'large'; + color?: 'brand' | 'success' | 'warning' | 'error'; +}; + +// @public (undocumented) +export type ProgressBarSlots = { + root: NonNullable>; + bar?: NonNullable>; +}; + +// @public +export type ProgressBarState = ComponentState & Required> & Pick; + +// @public +export const Radio: ForwardRefComponent; + +// @public (undocumented) +export const radioClassNames: SlotClassNames; + +// @public +export const RadioGroup: ForwardRefComponent; + +// @public (undocumented) +export const radioGroupClassNames: SlotClassNames; + +// @public (undocumented) +export type RadioGroupContextValue = Pick; + +// @public (undocumented) +export type RadioGroupContextValues = { + radioGroup: RadioGroupContextValue; +}; + +// @public +export type RadioGroupOnChangeData = { + value: string; +}; + +// @public (undocumented) +export type RadioGroupProps = Omit>, 'onChange'> & { + name?: string; + value?: string; + defaultValue?: string; + onChange?: (ev: React_2.FormEvent, data: RadioGroupOnChangeData) => void; + layout?: 'vertical' | 'horizontal' | 'horizontal-stacked'; + disabled?: boolean; + required?: boolean; +}; + +// @public (undocumented) +export const RadioGroupProvider: React_2.Provider; + +// @public (undocumented) +export type RadioGroupSlots = { + root: NonNullable>; +}; + +// @public +export type RadioGroupState = ComponentState & Required> & Pick; + +// @public +export type RadioOnChangeData = { + value: string; +}; + +// @public +export type RadioProps = Omit, 'input'>, 'onChange' | 'size'> & { + value?: string; + labelPosition?: 'after' | 'below'; + disabled?: boolean; + onChange?: (ev: React_2.ChangeEvent, data: RadioOnChangeData) => void; +}; + +// @public (undocumented) +export type RadioSlots = { + root: NonNullable>; + label: Slot; + input: NonNullable>; + indicator: NonNullable>; +}; + +// @public +export type RadioState = ComponentState & Required>; + +// @public (undocumented) +export type RegisterTabEventHandler = (data: TabRegisterData) => void; + +// @public +export const renderAccordion_unstable: (state: AccordionState, contextValues: AccordionContextValues) => JSX.Element; + +// @public +export const renderAccordionHeader_unstable: (state: AccordionHeaderState, contextValues: AccordionHeaderContextValues) => JSX.Element; + +// @public +export const renderAccordionItem_unstable: (state: AccordionItemState, contextValues: AccordionItemContextValues) => JSX.Element; + +// @public +export const renderAccordionPanel_unstable: (state: AccordionPanelState) => JSX.Element | null; + +// @public (undocumented) +export const renderAvatar_unstable: (state: AvatarState) => JSX.Element; -export { MenuGroupHeader } +// @public +export const renderAvatarGroup_unstable: (state: AvatarGroupState, contextValues: AvatarGroupContextValues) => JSX.Element; + +// @public +export const renderAvatarGroupItem_unstable: (state: AvatarGroupItemState) => JSX.Element; -export { menuGroupHeaderClassNames } +// @public +export const renderAvatarGroupPopover_unstable: (state: AvatarGroupPopoverState, contextValues: AvatarGroupContextValues) => JSX.Element; -export { MenuGroupHeaderProps } +// @public (undocumented) +export const renderBadge_unstable: (state: BadgeState) => JSX.Element; -export { MenuGroupHeaderSlots } +// @public +const renderButton_unstable: (state: ButtonState) => JSX.Element; +export { renderButton_unstable } +export { renderButton_unstable as renderToggleButton_unstable } -export { MenuGroupHeaderState } +// @public +export const renderCard_unstable: (state: CardState, cardContextValue: CardContextValue) => JSX.Element; -export { MenuGroupProps } +// @public +export const renderCardFooter_unstable: (state: CardFooterState) => JSX.Element; -export { MenuGroupSlots } +// @public +export const renderCardHeader_unstable: (state: CardHeaderState) => JSX.Element; -export { MenuGroupState } +// @public +export const renderCardPreview_unstable: (state: CardPreviewState) => JSX.Element; -export { MenuItem } +// @public (undocumented) +export const renderCheckbox_unstable: (state: CheckboxState) => JSX.Element; -export { MenuItemCheckbox } +// @public +export const renderCombobox_unstable: (state: ComboboxState, contextValues: ComboboxContextValues) => JSX.Element; -export { menuItemCheckboxClassNames } +// @public +export const renderCompoundButton_unstable: (state: CompoundButtonState) => JSX.Element; -export { MenuItemCheckboxProps } +// @public +export const renderDataGrid_unstable: (state: DataGridState, contextValues: DataGridContextValues) => JSX.Element; -export { MenuItemCheckboxState } +// @public +export const renderDataGridBody_unstable: (state: DataGridBodyState) => JSX.Element; -export { menuItemClassNames } +// @public +export const renderDataGridCell_unstable: (state: DataGridCellState) => JSX.Element; -export { MenuItemProps } +// @public +export const renderDataGridHeader_unstable: (state: DataGridHeaderState) => JSX.Element; -export { MenuItemRadio } +// @public +export const renderDataGridHeaderCell_unstable: (state: DataGridHeaderCellState) => JSX.Element; -export { menuItemRadioClassNames } +// @public +export const renderDataGridRow_unstable: (state: DataGridRowState) => JSX.Element; -export { MenuItemRadioProps } +// @public +export const renderDataGridSelectionCell_unstable: (state: DataGridSelectionCellState) => JSX.Element; -export { MenuItemRadioState } +// @public +export const renderDialog_unstable: (state: DialogState, contextValues: DialogContextValues) => JSX.Element; -export { MenuItemSelectableProps } +// @public +export const renderDialogActions_unstable: (state: DialogActionsState) => JSX.Element; -export { MenuItemSelectableState } +// @public +export const renderDialogBody_unstable: (state: DialogBodyState) => JSX.Element; -export { MenuItemSlots } +// @public +export const renderDialogContent_unstable: (state: DialogContentState) => JSX.Element; -export { MenuItemState } +// @public +export const renderDialogSurface_unstable: (state: DialogSurfaceState, contextValues: DialogSurfaceContextValues) => JSX.Element; -export { MenuList } +// @public +export const renderDialogTitle_unstable: (state: DialogTitleState) => JSX.Element; -export { menuListClassNames } +// @public +export const renderDialogTrigger_unstable: (state: DialogTriggerState) => ReactElement> | null; -export { MenuListContextValue } +// @public +export const renderDivider_unstable: (state: DividerState) => JSX.Element; -export { MenuListContextValues } +// @public +export const renderDropdown_unstable: (state: DropdownState, contextValues: DropdownContextValues) => JSX.Element; -export { MenuListProps } +export { RendererProvider } -export { MenuListProvider } +// @public +export const renderField_unstable: (state: FieldState, contextValues: FieldContextValues) => JSX.Element; -export { MenuListSlots } +// @public +export const renderFluentProvider_unstable: (state: FluentProviderState, contextValues: FluentProviderContextValues) => JSX.Element; -export { MenuListState } +// @public +export const renderImage_unstable: (state: ImageState) => JSX.Element; -export { MenuOpenChangeData } +// @public +export const renderInput_unstable: (state: InputState) => JSX.Element; -export { MenuOpenEvent } +// @public +export const renderLabel_unstable: (state: LabelState) => JSX.Element; -export { MenuOpenEvents } +// @public +export const renderLink_unstable: (state: LinkState) => JSX.Element; -export { MenuPopover } +// @public +export const renderListbox_unstable: (state: ListboxState, contextValues: ListboxContextValues) => JSX.Element; -export { menuPopoverClassNames } +// @public +export const renderMenu_unstable: (state: MenuState, contextValues: MenuContextValues) => JSX.Element; -export { MenuPopoverProps } +// @public +export const renderMenuButton_unstable: (state: MenuButtonState) => JSX.Element; -export { MenuPopoverSlots } +// @public +export const renderMenuDivider_unstable: (state: MenuDividerState) => JSX.Element; -export { MenuPopoverState } +// @public +export const renderMenuGroup_unstable: (state: MenuGroupState, contextValues: MenuGroupContextValues) => JSX.Element; -export { MenuProps } +// @public +export const renderMenuGroupHeader_unstable: (state: MenuGroupHeaderState) => JSX.Element; -export { MenuProvider } +// @public +export const renderMenuItem_unstable: (state: MenuItemState) => JSX.Element; -export { MenuSlots } +// @public +export const renderMenuItemCheckbox_unstable: (state: MenuItemCheckboxState) => JSX.Element; -export { MenuSplitGroup } +// @public +export const renderMenuItemRadio_unstable: (state: MenuItemRadioState) => JSX.Element; -export { menuSplitGroupClassNames } +// @public +export const renderMenuList_unstable: (state: MenuListState, contextValues: MenuListContextValues) => JSX.Element; -export { MenuSplitGroupProps } +// @public +export const renderMenuPopover_unstable: (state: MenuPopoverState) => JSX.Element; -export { MenuSplitGroupSlots } +// @public +export const renderMenuSplitGroup_unstable: (state: MenuSplitGroupState) => JSX.Element; -export { MenuSplitGroupState } +// @public +export const renderMenuTrigger_unstable: (state: MenuTriggerState) => JSX.Element; -export { MenuState } +// @public +export const renderOption_unstable: (state: OptionState) => JSX.Element; -export { MenuTrigger } +// @public +export const renderOptionGroup_unstable: (state: OptionGroupState) => JSX.Element; -export { MenuTriggerChildProps } +// @public +export const renderPersona_unstable: (state: PersonaState) => JSX.Element; -export { MenuTriggerContextProvider } +// @public +export const renderPopover_unstable: (state: PopoverState) => JSX.Element; -export { MenuTriggerProps } +// @public +export const renderPopoverSurface_unstable: (state: PopoverSurfaceState) => JSX.Element; -export { MenuTriggerState } +// @public +export const renderPopoverTrigger_unstable: (state: PopoverTriggerState) => ReactElement> | null; -export { mergeClasses } +// @public +export const renderPortal_unstable: (state: PortalState) => React_2.ReactElement; -export { OnOpenChangeData } +// @public +export const renderProgressBar_unstable: (state: ProgressBarState) => JSX.Element; -export { OnVisibleChangeData } +// @public +export const renderRadio_unstable: (state: RadioState) => JSX.Element; -export { OpenPopoverEvents } +// @public +export const renderRadioGroup_unstable: (state: RadioGroupState, contextValues: RadioGroupContextValues) => JSX.Element; -export { Option_2 as Option } +// @public +export const renderSelect_unstable: (state: SelectState) => JSX.Element; -export { optionClassNames } +// @public +export const renderSkeleton_unstable: (state: SkeletonState, contextValues: SkeletonContextValues) => JSX.Element; -export { OptionGroup } +// @public +export const renderSkeletonItem_unstable: (state: SkeletonItemState) => JSX.Element; -export { optionGroupClassNames } +// @public +export const renderSlider_unstable: (state: SliderState) => JSX.Element; -export { OptionGroupProps } +// @public +export const renderSpinButton_unstable: (state: SpinButtonState) => JSX.Element; -export { OptionGroupSlots } +// @public +export const renderSpinner_unstable: (state: SpinnerState) => JSX.Element; -export { OptionGroupState } +// @public +export const renderSplitButton_unstable: (state: SplitButtonState) => JSX.Element; -export { OptionProps } +// @public +export const renderSwitch_unstable: (state: SwitchState) => JSX.Element; -export { OptionSlots } +// @public +export const renderTab_unstable: (state: TabState) => JSX.Element; -export { OptionState } +// @public +export const renderTable_unstable: (state: TableState, contextValues: TableContextValues) => JSX.Element; -export { Overflow } +// @public +export const renderTableBody_unstable: (state: TableBodyState) => JSX.Element; -export { OverflowItem } +// @public +export const renderTableCell_unstable: (state: TableCellState) => JSX.Element; -export { OverflowItemProps } +// @public +export const renderTableCellActions_unstable: (state: TableCellActionsState) => JSX.Element; -export { OverflowProps } +// @public +export const renderTableCellLayout_unstable: (state: TableCellLayoutState, contextValues: TableCellLayoutContextValues) => JSX.Element; -export { PartialTheme } +// @public +export const renderTableHeader_unstable: (state: TableHeaderState) => JSX.Element; -export { PartitionAvatarGroupItems } +// @public +export const renderTableHeaderCell_unstable: (state: TableHeaderCellState) => JSX.Element; -export { partitionAvatarGroupItems } +// @public +export const renderTableResizeHandle_unstable: (state: TableResizeHandleState) => JSX.Element; -export { PartitionAvatarGroupItemsOptions } +// @public +export const renderTableRow_unstable: (state: TableRowState) => JSX.Element; -export { Persona } +// @public +export const renderTableSelectionCell_unstable: (state: TableSelectionCellState) => JSX.Element; -export { personaClassNames } +// @public +export const renderTabList_unstable: (state: TabListState, contextValues: TabListContextValues) => JSX.Element; -export { PersonaProps } +// @public +export const renderText_unstable: (state: TextState) => JSX.Element; -export { PersonaSlots } +// @public +export const renderTextarea_unstable: (state: TextareaState) => JSX.Element; -export { PersonaState } +// @public +export const renderToolbar_unstable: (state: ToolbarState, contextValues: ToolbarContextValues) => JSX.Element; -export { Popover } +// @public +export const renderToolbarGroup_unstable: (state: ToolbarGroupState) => JSX.Element; -export { PopoverContextValue } +// @public +export const renderTooltip_unstable: (state: TooltipState) => JSX.Element; -export { PopoverProps } +export { renderToStyleElements } -export { PopoverProvider } +// @public +export function resetIdsForTests(): void; + +// @public +export const resolveShorthand: ResolveShorthandFunction; + +// @public (undocumented) +export type ResolveShorthandFunction = { +

(value: P | SlotShorthandValue | undefined, options: ResolveShorthandOptions): P; +

(value: P | SlotShorthandValue | null | undefined, options?: ResolveShorthandOptions): P | undefined; +}; + +// @public (undocumented) +export type ResolveShorthandOptions = Required extends true ? { + required: true; + defaultProps?: Props; +} : { + required?: Required; + defaultProps?: Props; +}; + +// @public +export const Select: ForwardRefComponent; + +// @public (undocumented) +export type SelectableHandler = (e: React_2.MouseEvent | React_2.KeyboardEvent, name: string, value: string, checked: boolean) => void; + +// @public (undocumented) +export const selectClassNames: SlotClassNames; + +// @public +export type SelectOnChangeData = { + value: string; +}; + +// @public (undocumented) +export type SelectProps = Omit, 'select'>, 'size' | 'onChange'> & { + appearance?: 'outline' | 'underline' | 'filled-darker' | 'filled-lighter'; + onChange?: (ev: React_2.ChangeEvent, data: SelectOnChangeData) => void; + size?: 'small' | 'medium' | 'large'; +}; + +// @public (undocumented) +export type SelectSlots = { + root: NonNullable>; + select: NonNullable>; + icon: Slot<'span'>; +}; + +// @public (undocumented) +export type SelectState = ComponentState & Required>; + +// @public (undocumented) +export type SelectTabData = { + value: TabValue; +}; + +// @public (undocumented) +export type SelectTabEvent = React_2.MouseEvent | React_2.KeyboardEvent; + +// @public (undocumented) +export type SelectTabEventHandler = (event: SelectTabEvent, data: SelectTabData) => void; + +// @public (undocumented) +export type ShadowBrandTokens = { + shadow2Brand: string; + shadow4Brand: string; + shadow8Brand: string; + shadow16Brand: string; + shadow28Brand: string; + shadow64Brand: string; +}; + +// @public +export type ShadowTokens = { + shadow2: string; + shadow4: string; + shadow8: string; + shadow16: string; + shadow28: string; + shadow64: string; +}; -export { PopoverSize } +export { shorthands } -export { PopoverState } +// @public +export const Skeleton: ForwardRefComponent; + +// @public (undocumented) +export const skeletonClassNames: SlotClassNames; + +// @public (undocumented) +export const SkeletonContextProvider: React_2.Provider; + +// @public (undocumented) +export interface SkeletonContextValue { + // (undocumented) + animation?: 'wave' | 'pulse'; + // (undocumented) + appearance?: 'opaque' | 'translucent'; +} + +// @public (undocumented) +export const SkeletonItem: ForwardRefComponent; + +// @public (undocumented) +export const skeletonItemClassNames: SlotClassNames; + +// @public +export type SkeletonItemProps = ComponentProps & { + animation?: 'wave' | 'pulse'; + appearance?: 'opaque' | 'translucent'; + size?: SkeletonItemSize; + shape?: 'circle' | 'square' | 'rectangle'; +}; + +// @public (undocumented) +export type SkeletonItemSlots = { + root: Slot<'div'>; +}; + +// @public +export type SkeletonItemState = ComponentState & Required>; + +// @public +export type SkeletonProps = Omit>, 'width'> & { + animation?: 'wave' | 'pulse'; + appearance?: 'opaque' | 'translucent'; + width?: number | string; +}; + +// @public (undocumented) +export type SkeletonSlots = { + root: NonNullable>; +}; + +// @public +export type SkeletonState = ComponentState & Required>; + +// @public +export const Slider: ForwardRefComponent; + +// @public (undocumented) +export const sliderClassNames: SlotClassNames; + +// @public (undocumented) +export const sliderCSSVars: { + sliderDirectionVar: string; + sliderProgressVar: string; + sliderStepsPercentVar: string; +}; + +// @public (undocumented) +export type SliderOnChangeData = { + value: number; +}; + +// @public (undocumented) +export type SliderProps = Omit, 'input'>, 'defaultValue' | 'onChange' | 'size' | 'value'> & { + defaultValue?: number; + disabled?: boolean; + max?: number; + min?: number; + size?: 'small' | 'medium'; + step?: number; + value?: number; + vertical?: boolean; + onChange?: (ev: React_2.ChangeEvent, data: SliderOnChangeData) => void; +}; + +// @public (undocumented) +export type SliderSlots = { + root: NonNullable>; + rail: NonNullable>; + thumb: NonNullable>; + input: NonNullable> & { + orient?: 'horizontal' | 'vertical'; + }; +}; + +// @public (undocumented) +export type SliderState = ComponentState & Pick; + +// @public +export type Slot = IsSingleton> extends true ? WithSlotShorthandValue> : Type extends React_2.ComponentType ? WithSlotRenderFunction : Type> | { + [As in AlternateAs]: { + as: As; + } & WithSlotRenderFunction>; +}[AlternateAs] | null : 'Error: First parameter to Slot must not be not a union of types. See documentation of Slot type.'; + +// @public +export type SlotClassNames = { + [SlotName in keyof Slots]-?: string; +}; + +// @public +export type SlotPropsRecord = Record; + +// @public (undocumented) +export type SlotRenderFunction = (Component: React_2.ElementType, props: Omit) => React_2.ReactNode; + +// @public (undocumented) +export type SortDirection = 'ascending' | 'descending'; + +// @public (undocumented) +export type SpacingTokens = { + none: string; + xxs: string; + xs: string; + sNudge: string; + s: string; + mNudge: string; + m: string; + l: string; + xl: string; + xxl: string; + xxxl: string; +}; + +// @public +export const SpinButton: ForwardRefComponent; + +// @public (undocumented) +export type SpinButtonBounds = 'none' | 'min' | 'max' | 'both'; + +// @public (undocumented) +export type SpinButtonChangeEvent = React_2.MouseEvent | React_2.ChangeEvent | React_2.FocusEvent | React_2.KeyboardEvent; + +// @public (undocumented) +export const spinButtonClassNames: SlotClassNames; + +// @public (undocumented) +export type SpinButtonOnChangeData = { + value?: number | null; + displayValue?: string; +}; + +// @public +export type SpinButtonProps = Omit, 'input'>, 'defaultValue' | 'onChange' | 'size' | 'value'> & { + appearance?: 'outline' | 'underline' | 'filled-darker' | 'filled-lighter'; + defaultValue?: number | null; + displayValue?: string; + max?: number; + min?: number; + onChange?: (event: SpinButtonChangeEvent, data: SpinButtonOnChangeData) => void; + precision?: number; + size?: 'small' | 'medium'; + step?: number; + stepPage?: number; + value?: number | null; +}; + +// @public (undocumented) +export type SpinButtonSlots = { + root: NonNullable>; + input: NonNullable>; + incrementButton: NonNullable>; + decrementButton: NonNullable>; +}; + +// @public (undocumented) +export type SpinButtonSpinState = 'rest' | 'up' | 'down'; + +// @public +export type SpinButtonState = ComponentState & Required> & { + spinState: SpinButtonSpinState; + atBound: SpinButtonBounds; +}; + +// @public +export const Spinner: ForwardRefComponent; + +// @public (undocumented) +export const spinnerClassNames: SlotClassNames; + +// @public +export type SpinnerProps = Omit, 'size'> & { + appearance?: 'primary' | 'inverted'; + delay?: number; + labelPosition?: 'above' | 'below' | 'before' | 'after'; + size?: 'tiny' | 'extra-small' | 'small' | 'medium' | 'large' | 'extra-large' | 'huge'; +}; + +// @public (undocumented) +export type SpinnerSlots = { + root: NonNullable>; + spinner?: Slot<'span'>; + label?: Slot; +}; + +// @public +export type SpinnerState = ComponentState & Required> & { + shouldRenderSpinner: boolean; +}; + +// @public +export const SplitButton: ForwardRefComponent; + +// @public (undocumented) +export const splitButtonClassNames: SlotClassNames; + +// @public (undocumented) +export type SplitButtonProps = ComponentProps & Omit & Omit; + +// @public (undocumented) +export type SplitButtonSlots = { + root: NonNullable>; + menuButton?: Slot; + primaryActionButton?: Slot; +}; + +// @public (undocumented) +export type SplitButtonState = ComponentState & Omit & Omit; + +// @public +export const SSRProvider: React_2.FC<{ + children: React_2.ReactNode; +}>; + +// @public (undocumented) +export type StrokeWidthTokens = { + strokeWidthThin: string; + strokeWidthThick: string; + strokeWidthThicker: string; + strokeWidthThickest: string; +}; + +// @public +export const Subtitle1: FunctionComponent; + +// @public (undocumented) +export const subtitle1ClassNames: SlotClassNames; + +// @public +export const Subtitle2: FunctionComponent; + +// @public (undocumented) +export const subtitle2ClassNames: SlotClassNames; + +// @public +export const Subtitle2Stronger: FunctionComponent; + +// @public (undocumented) +export const subtitle2StrongerClassNames: SlotClassNames; + +// @public +export const Switch: ForwardRefComponent; + +// @public (undocumented) +export const switchClassNames: SlotClassNames; + +// @public (undocumented) +export type SwitchOnChangeData = { + checked: boolean; +}; + +// @public +export type SwitchProps = Omit, 'input'>, 'checked' | 'defaultChecked' | 'onChange'> & { + checked?: boolean; + defaultChecked?: boolean; + labelPosition?: 'above' | 'after' | 'before'; + onChange?: (ev: React_2.ChangeEvent, data: SwitchOnChangeData) => void; +}; + +// @public (undocumented) +export type SwitchSlots = { + root: NonNullable>; + indicator: NonNullable>; + input: NonNullable>; + label?: Slot; +}; -export { PopoverSurface } +// @public +export type SwitchState = ComponentState & Required>; -export { popoverSurfaceClassNames } +// @public +export const Tab: ForwardRefComponent; + +// @public (undocumented) +export const tabClassNames: SlotClassNames; + +// @public +export const Table: ForwardRefComponent; + +// @public +export const TableBody: ForwardRefComponent; + +// @public (undocumented) +export const tableBodyClassName = "fui-TableBody"; -export { PopoverSurfaceProps } +// @public (undocumented) +export const tableBodyClassNames: SlotClassNames; + +// @public +export type TableBodyProps = ComponentProps; + +// @public (undocumented) +export type TableBodySlots = { + root: Slot<'tbody', 'div'>; +}; + +// @public +export type TableBodyState = ComponentState & Pick; + +// @public +export const TableCell: ForwardRefComponent; + +// @public +export const TableCellActions: ForwardRefComponent; + +// @public (undocumented) +export const tableCellActionsClassNames: SlotClassNames; + +// @public +export type TableCellActionsProps = ComponentProps & { + visible?: boolean; +}; + +// @public (undocumented) +export type TableCellActionsSlots = { + root: Slot<'div'>; +}; + +// @public +export type TableCellActionsState = ComponentState & Pick, 'visible'>; + +// @public (undocumented) +export const tableCellClassName = "fui-TableCell"; + +// @public (undocumented) +export const tableCellClassNames: SlotClassNames; + +// @public +export const TableCellLayout: ForwardRefComponent; + +// @public (undocumented) +export const tableCellLayoutClassNames: SlotClassNames; + +// @public +export type TableCellLayoutProps = ComponentProps> & { + appearance?: 'primary'; + truncate?: boolean; +}; + +// @public (undocumented) +export type TableCellLayoutSlots = { + root: Slot<'div'>; + media: Slot<'span'>; + main: Slot<'span'>; + description: Slot<'span'>; + content: Slot<'div'>; +}; + +// @public +export type TableCellLayoutState = ComponentState & Pick & { + avatarSize: AvatarSize | undefined; +} & Pick; + +// @public +export type TableCellProps = ComponentProps & {}; + +// @public (undocumented) +export type TableCellSlots = { + root: Slot<'td', 'div'>; +}; + +// @public +export type TableCellState = ComponentState & Pick; + +// @public (undocumented) +export const tableClassName = "fui-Table"; -export { PopoverSurfaceSlots } +// @public (undocumented) +export const tableClassNames: SlotClassNames; -export { PopoverSurfaceState } - -export { PopoverTrigger } - -export { PopoverTriggerChildProps } - -export { PopoverTriggerProps } - -export { PopoverTriggerState } - -export { Portal } - -export { PortalProps } - -export { PortalState } - -export { PositioningImperativeRef } - -export { PositioningProps } - -export { PositioningShorthand } - -export { PositioningShorthandValue } - -export { PositioningVirtualElement } - -export { PresenceBadge } - -export { presenceBadgeClassNames } - -export { PresenceBadgeProps } - -export { PresenceBadgeState } - -export { PresenceBadgeStatus } - -export { ProgressBar } - -export { progressBarClassNames } - -export { ProgressBarProps } - -export { ProgressBarSlots } - -export { ProgressBarState } - -export { Radio } - -export { radioClassNames } - -export { RadioGroup } - -export { radioGroupClassNames } - -export { RadioGroupContextValue } - -export { RadioGroupContextValues } - -export { RadioGroupOnChangeData } - -export { RadioGroupProps } - -export { RadioGroupProvider } - -export { RadioGroupSlots } - -export { RadioGroupState } - -export { RadioOnChangeData } - -export { RadioProps } - -export { RadioSlots } - -export { RadioState } - -export { RegisterTabEventHandler } - -export { renderAccordion_unstable } - -export { renderAccordionHeader_unstable } - -export { renderAccordionItem_unstable } - -export { renderAccordionPanel_unstable } - -export { renderAvatar_unstable } - -export { renderAvatarGroup_unstable } - -export { renderAvatarGroupItem_unstable } - -export { renderAvatarGroupPopover_unstable } - -export { renderBadge_unstable } - -export { renderButton_unstable } - -export { renderCard_unstable } - -export { renderCardFooter_unstable } - -export { renderCardHeader_unstable } - -export { renderCardPreview_unstable } - -export { renderCheckbox_unstable } - -export { renderCombobox_unstable } - -export { renderCompoundButton_unstable } - -export { renderDataGrid_unstable } - -export { renderDataGridBody_unstable } - -export { renderDataGridCell_unstable } - -export { renderDataGridHeader_unstable } - -export { renderDataGridHeaderCell_unstable } - -export { renderDataGridRow_unstable } - -export { renderDataGridSelectionCell_unstable } - -export { renderDialog_unstable } - -export { renderDialogActions_unstable } - -export { renderDialogBody_unstable } - -export { renderDialogContent_unstable } - -export { renderDialogSurface_unstable } - -export { renderDialogTitle_unstable } - -export { renderDialogTrigger_unstable } - -export { renderDivider_unstable } - -export { renderDropdown_unstable } - -export { RendererProvider } - -export { renderField_unstable } - -export { renderFluentProvider_unstable } - -export { renderImage_unstable } - -export { renderInput_unstable } - -export { renderLabel_unstable } - -export { renderLink_unstable } - -export { renderListbox_unstable } - -export { renderMenu_unstable } - -export { renderMenuButton_unstable } - -export { renderMenuDivider_unstable } - -export { renderMenuGroup_unstable } - -export { renderMenuGroupHeader_unstable } - -export { renderMenuItem_unstable } - -export { renderMenuItemCheckbox_unstable } - -export { renderMenuItemRadio_unstable } - -export { renderMenuList_unstable } - -export { renderMenuPopover_unstable } - -export { renderMenuSplitGroup_unstable } - -export { renderMenuTrigger_unstable } - -export { renderOption_unstable } - -export { renderOptionGroup_unstable } - -export { renderPersona_unstable } - -export { renderPopover_unstable } - -export { renderPopoverSurface_unstable } - -export { renderPopoverTrigger_unstable } - -export { renderPortal_unstable } - -export { renderProgressBar_unstable } - -export { renderRadio_unstable } - -export { renderRadioGroup_unstable } - -export { renderSelect_unstable } - -export { renderSkeleton_unstable } - -export { renderSkeletonItem_unstable } - -export { renderSlider_unstable } - -export { renderSpinButton_unstable } - -export { renderSpinner_unstable } - -export { renderSplitButton_unstable } - -export { renderSwitch_unstable } - -export { renderTab_unstable } - -export { renderTable_unstable } - -export { renderTableBody_unstable } - -export { renderTableCell_unstable } - -export { renderTableCellActions_unstable } - -export { renderTableCellLayout_unstable } - -export { renderTableHeader_unstable } - -export { renderTableHeaderCell_unstable } - -export { renderTableResizeHandle_unstable } - -export { renderTableRow_unstable } - -export { renderTableSelectionCell_unstable } - -export { renderTabList_unstable } - -export { renderText_unstable } - -export { renderTextarea_unstable } - -export { renderToggleButton_unstable } - -export { renderToolbar_unstable } - -export { renderToolbarGroup_unstable } - -export { renderTooltip_unstable } - -export { renderToStyleElements } - -export { resetIdsForTests } - -export { resolveShorthand } - -export { ResolveShorthandFunction } - -export { ResolveShorthandOptions } - -export { Select } - -export { SelectableHandler } - -export { selectClassNames } - -export { SelectOnChangeData } - -export { SelectProps } - -export { SelectSlots } - -export { SelectState } - -export { SelectTabData } - -export { SelectTabEvent } - -export { SelectTabEventHandler } - -export { ShadowBrandTokens } - -export { ShadowTokens } - -export { shorthands } - -export { Skeleton } - -export { skeletonClassNames } - -export { SkeletonContextProvider } - -export { SkeletonContextValue } - -export { SkeletonItem } - -export { skeletonItemClassNames } - -export { SkeletonItemProps } - -export { SkeletonItemSlots } - -export { SkeletonItemState } - -export { SkeletonProps } - -export { SkeletonSlots } - -export { SkeletonState } - -export { Slider } - -export { sliderClassNames } - -export { sliderCSSVars } - -export { SliderOnChangeData } - -export { SliderProps } - -export { SliderSlots } - -export { SliderState } - -export { Slot } - -export { SlotClassNames } - -export { SlotPropsRecord } - -export { SlotRenderFunction } - -export { SortDirection } - -export { SpacingTokens } - -export { SpinButton } - -export { SpinButtonBounds } - -export { SpinButtonChangeEvent } - -export { spinButtonClassNames } - -export { SpinButtonOnChangeData } - -export { SpinButtonProps } - -export { SpinButtonSlots } - -export { SpinButtonSpinState } - -export { SpinButtonState } - -export { Spinner } - -export { spinnerClassNames } - -export { SpinnerProps } - -export { SpinnerSlots } - -export { SpinnerState } - -export { SplitButton } - -export { splitButtonClassNames } - -export { SplitButtonProps } - -export { SplitButtonSlots } - -export { SplitButtonState } - -export { SSRProvider } - -export { StrokeWidthTokens } - -export { Subtitle1 } - -export { subtitle1ClassNames } - -export { Subtitle2 } - -export { subtitle2ClassNames } - -export { Subtitle2Stronger } - -export { subtitle2StrongerClassNames } - -export { Switch } - -export { switchClassNames } - -export { SwitchOnChangeData } - -export { SwitchProps } - -export { SwitchSlots } - -export { SwitchState } - -export { Tab } - -export { tabClassNames } - -export { Table } - -export { TableBody } - -export { tableBodyClassName } - -export { tableBodyClassNames } - -export { TableBodyProps } - -export { TableBodySlots } - -export { TableBodyState } - -export { TableCell } - -export { TableCellActions } - -export { tableCellActionsClassNames } - -export { TableCellActionsProps } - -export { TableCellActionsSlots } - -export { TableCellActionsState } - -export { tableCellClassName } - -export { tableCellClassNames } - -export { TableCellLayout } - -export { tableCellLayoutClassNames } - -export { TableCellLayoutProps } - -export { TableCellLayoutSlots } - -export { TableCellLayoutState } - -export { TableCellProps } - -export { TableCellSlots } - -export { TableCellState } - -export { tableClassName } - -export { tableClassNames } - -export { TableColumnDefinition } - -export { TableColumnId } - -export { TableColumnSizingOptions } - -export { TableContextProvider } - -export { TableContextValue } - -export { TableContextValues } - -export { TableFeaturePlugin } - -export { TableFeaturesState } - -export { TableHeader } - -export { TableHeaderCell } - -export { tableHeaderCellClassName } - -export { tableHeaderCellClassNames } - -export { TableHeaderCellProps } - -export { TableHeaderCellSlots } - -export { TableHeaderCellState } - -export { tableHeaderClassName } - -export { tableHeaderClassNames } - -export { TableHeaderProps } - -export { TableHeaderSlots } - -export { TableHeaderState } - -export { TableProps } - -export { TableResizeHandle } - -export { tableResizeHandleClassNames } - -export { TableRow } - -export { tableRowClassName } - -export { tableRowClassNames } - -export { TableRowData } - -export { TableRowId } - -export { TableRowIdContextProvider } - -export { TableRowProps } - -export { TableRowSlots } - -export { TableRowState } - -export { TableSelectionCell } - -export { tableSelectionCellClassNames } - -export { TableSelectionCellProps } - -export { TableSelectionCellSlots } - -export { TableSelectionCellState } - -export { TableSelectionState } - -export { TableSlots } - -export { TableSortState } - -export { TableState } - -export { TabList } - -export { tabListClassNames } - -export { TabListContextValue } - -export { TabListContextValues } - -export { TabListProps } - -export { TabListProvider } - -export { TabListSlots } - -export { TabListState } - -export { TabProps } - -export { TabRegisterData } - -export { TabSlots } - -export { TabState } - -export { TabValue } - -export { teamsDarkTheme } - -export { teamsHighContrastTheme } - -export { teamsLightTheme } +// @public (undocumented) +export interface TableColumnDefinition { + // (undocumented) + columnId: TableColumnId; + // (undocumented) + compare: (a: TItem, b: TItem) => number; + // (undocumented) + renderCell: (item: TItem) => React_2.ReactNode; + // (undocumented) + renderHeaderCell: () => React_2.ReactNode; +} +// @public (undocumented) +export type TableColumnId = string | number; + +// @public (undocumented) +export type TableColumnSizingOptions = Record> & { + defaultWidth?: number; +}>; + +// @public (undocumented) +export const TableContextProvider: React_2.Provider; + +// @public (undocumented) +export type TableContextValue = { + size: 'extra-small' | 'small' | 'medium'; + noNativeElements: boolean; + sortable: boolean; +}; + +// @public (undocumented) +export type TableContextValues = { + table: TableContextValue; +}; + +// @public (undocumented) +export type TableFeaturePlugin = (tableState: TableFeaturesState) => TableFeaturesState; + +// @public (undocumented) +export interface TableFeaturesState extends Pick, 'items' | 'getRowId'> { + columns: TableColumnDefinition[]; + columnSizing_unstable: TableColumnSizingState; + getRows: = TableRowData>(rowEnhancer?: RowEnhancer) => TRowState[]; + selection: TableSelectionState; + sort: TableSortState; + tableRef: React_2.Ref; +} + +// @public +export const TableHeader: ForwardRefComponent; + +// @public +export const TableHeaderCell: ForwardRefComponent; + +// @public (undocumented) +export const tableHeaderCellClassName = "fui-TableHeaderCell"; + +// @public (undocumented) +export const tableHeaderCellClassNames: SlotClassNames; + +// @public +export type TableHeaderCellProps = ComponentProps> & { + sortDirection?: SortDirection; +}; + +// @public (undocumented) +export type TableHeaderCellSlots = { + root: Slot<'th', 'div'>; + sortIcon: Slot<'span'>; + button: NonNullable>; + aside: Slot<'span'>; +}; + +// @public +export type TableHeaderCellState = ComponentState & Pick; + +// @public (undocumented) +export const tableHeaderClassName = "fui-TableHeader"; + +// @public (undocumented) +export const tableHeaderClassNames: SlotClassNames; + +// @public +export type TableHeaderProps = ComponentProps & {}; + +// @public (undocumented) +export type TableHeaderSlots = { + root: Slot<'thead', 'div'>; +}; + +// @public +export type TableHeaderState = ComponentState & Pick; + +// @public +export type TableProps = ComponentProps & Partial; + +// @public +export const TableResizeHandle: ForwardRefComponent; + +// @public (undocumented) +export const tableResizeHandleClassNames: SlotClassNames; + +// @public +export const TableRow: ForwardRefComponent; + +// @public (undocumented) +export const tableRowClassName = "fui-TableRow"; + +// @public (undocumented) +export const tableRowClassNames: SlotClassNames; + +// @public (undocumented) +export interface TableRowData { + item: TItem; + rowId: TableRowId; +} + +// @public (undocumented) +export type TableRowId = string | number; + +// @public (undocumented) +export const TableRowIdContextProvider: React_2.Provider; + +// @public +export type TableRowProps = ComponentProps & { + appearance?: 'brand' | 'neutral' | 'none'; +}; + +// @public (undocumented) +export type TableRowSlots = { + root: Slot<'tr', 'div'>; +}; + +// @public +export type TableRowState = ComponentState & Pick & Pick, 'appearance'> & { + isHeaderRow: boolean; +}; + +// @public +export const TableSelectionCell: ForwardRefComponent; + +// @public (undocumented) +export const tableSelectionCellClassNames: SlotClassNames; + +// @public +export type TableSelectionCellProps = ComponentProps> & { + type?: 'checkbox' | 'radio'; + checked?: CheckboxProps['checked']; + subtle?: boolean; + hidden?: boolean; +}; + +// @public (undocumented) +export type TableSelectionCellSlots = { + checkboxIndicator: Slot; + radioIndicator: Slot; +} & Pick; + +// @public +export type TableSelectionCellState = ComponentState & Pick, 'type' | 'checked' | 'subtle' | 'hidden'> & Pick; + +// @public (undocumented) +export interface TableSelectionState { + allRowsSelected: boolean; + clearRows: (e: React_2.SyntheticEvent) => void; + deselectRow: (e: React_2.SyntheticEvent, rowId: TableRowId) => void; + isRowSelected: (rowId: TableRowId) => boolean; + selectedRows: Set; + // (undocumented) + selectionMode: SelectionMode_2; + selectRow: (e: React_2.SyntheticEvent, rowId: TableRowId) => void; + someRowsSelected: boolean; + toggleAllRows: (e: React_2.SyntheticEvent) => void; + toggleRow: (e: React_2.SyntheticEvent, rowId: TableRowId) => void; +} + +// @public (undocumented) +export type TableSlots = { + root: Slot<'table', 'div'>; +}; + +// @public (undocumented) +export interface TableSortState { + getSortDirection: (columnId: TableColumnId) => SortDirection | undefined; + setColumnSort: (event: React_2.SyntheticEvent, columnId: TableColumnId, sortDirection: SortDirection) => void; + sort: >(rows: TRowState[]) => TRowState[]; + sortColumn: TableColumnId | undefined; + sortDirection: SortDirection; + toggleColumnSort: (event: React_2.SyntheticEvent, columnId: TableColumnId) => void; +} + +// @public +export type TableState = ComponentState & Pick, 'size' | 'noNativeElements'> & TableContextValue; + +// @public +export const TabList: ForwardRefComponent; + +// @public (undocumented) +export const tabListClassNames: SlotClassNames; + +// @public (undocumented) +export type TabListContextValue = Pick & Required> & { + onRegister: RegisterTabEventHandler; + onUnregister: RegisterTabEventHandler; + onSelect: SelectTabEventHandler; + getRegisteredTabs: () => { + selectedValue?: TabValue; + previousSelectedValue?: TabValue; + registeredTabs: Record; + }; +}; + +// @public +export type TabListContextValues = { + tabList: TabListContextValue; +}; + +// @public +export type TabListProps = ComponentProps & { + appearance?: 'transparent' | 'subtle'; + reserveSelectedTabSpace?: boolean; + defaultSelectedValue?: TabValue; + disabled?: boolean; + onTabSelect?: SelectTabEventHandler; + selectedValue?: TabValue; + size?: 'small' | 'medium' | 'large'; + vertical?: boolean; +}; + +// @public (undocumented) +export const TabListProvider: Provider & FC>; + +// @public (undocumented) +export type TabListSlots = { + root: Slot<'div'>; +}; + +// @public +export type TabListState = ComponentState> & TabListContextValue; + +// @public +export type TabProps = ComponentProps> & { + disabled?: boolean; + value: TabValue; +}; + +// @public (undocumented) +export type TabRegisterData = { + value: TabValue; + ref: React_2.RefObject; +}; + +// @public (undocumented) +export type TabSlots = { + root: Slot<'button'>; + icon?: Slot<'span'>; + content: NonNullable>; +}; + +// @public +export type TabState = ComponentState & Pick & Required> & { + appearance?: 'transparent' | 'subtle'; + iconOnly: boolean; + selected: boolean; + contentReservedSpaceClassName?: string; + size: 'small' | 'medium' | 'large'; + vertical: boolean; +}; + +// @public +export type TabValue = unknown; + +// @public (undocumented) +export const teamsDarkTheme: Theme; + +// @public (undocumented) +export const teamsHighContrastTheme: Theme; + +// @public (undocumented) +export const teamsLightTheme: Theme; + +// @public +const Text_2: ForwardRefComponent; export { Text_2 as Text } -export { Textarea } - -export { textareaClassNames } - -export { TextareaOnChangeData } - -export { TextareaProps } - -export { TextareaSlots } - -export { TextareaState } - -export { textClassNames } - -export { TextPresetProps } - -export { TextProps } - -export { TextSlots } - -export { TextState } - -export { Theme } - -export { themeToTokensObject } - -export { Title1 } - -export { title1ClassNames } - -export { Title2 } - -export { title2ClassNames } - -export { Title3 } - -export { title3ClassNames } - -export { ToggleButton } - -export { toggleButtonClassNames } - -export { ToggleButtonProps } - -export { ToggleButtonState } - -export { tokens } - -export { Toolbar } - -export { ToolbarButton } - -export { ToolbarButtonProps } - -export { ToolbarButtonState } - -export { toolbarClassNames } - -export { ToolbarContextValue } - -export { ToolbarContextValues } - -export { ToolbarDivider } - -export { ToolbarDividerProps } - -export { ToolbarDividerState } - -export { ToolbarGroup } - -export { toolbarGroupClassNames } - -export { ToolbarGroupProps } - -export { ToolbarGroupState } - -export { ToolbarProps } - -export { ToolbarRadioButton } - -export { ToolbarRadioButtonProps } - -export { ToolbarRadioButtonState } - -export { ToolbarRadioGroup } - -export { ToolbarRadioGroupProps } - -export { ToolbarRadioGroupState } - -export { ToolbarSlots } - -export { ToolbarState } - -export { ToolbarToggleButton } - -export { ToolbarToggleButtonProps } - -export { ToolbarToggleButtonState } - -export { Tooltip } +// @public +export const Textarea: ForwardRefComponent; -export { tooltipClassNames } +// @public (undocumented) +export const textareaClassNames: SlotClassNames; -export { TooltipProps } +// @public +export type TextareaOnChangeData = { + value: string; +}; + +// @public +export type TextareaProps = Omit, 'textarea'>, 'defaultValue' | 'onChange' | 'size' | 'value'> & { + appearance?: 'outline' | 'filled-darker' | 'filled-lighter' | 'filled-darker-shadow' | 'filled-lighter-shadow'; + defaultValue?: string; + onChange?: (ev: React_2.ChangeEvent, data: TextareaOnChangeData) => void; + resize?: 'none' | 'horizontal' | 'vertical' | 'both'; + size?: 'small' | 'medium' | 'large'; + value?: string; +}; -export { TooltipSlots } +// @public (undocumented) +export type TextareaSlots = { + root: NonNullable>; + textarea: NonNullable>; +}; + +// @public +export type TextareaState = ComponentState & Required>; -export { TooltipState } +// @public (undocumented) +export const textClassNames: SlotClassNames; -export { TooltipTriggerProps } +// @public +export type TextPresetProps = Omit; + +// @public +export type TextProps = ComponentProps & { + align?: 'start' | 'center' | 'end' | 'justify'; + block?: boolean; + font?: 'base' | 'monospace' | 'numeric'; + italic?: boolean; + size?: 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 1000; + strikethrough?: boolean; + truncate?: boolean; + underline?: boolean; + weight?: 'regular' | 'medium' | 'semibold' | 'bold'; + wrap?: boolean; +}; -export { TypographyStyle } +// @public +export type TextSlots = { + root: Slot<'span', 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'pre'>; +}; -export { TypographyStyles } +// @public +export type TextState = ComponentState & Required>; -export { typographyStyles } +// @public (undocumented) +export type Theme = FontSizeTokens & LineHeightTokens & BorderRadiusTokens & StrokeWidthTokens & HorizontalSpacingTokens & VerticalSpacingTokens & DurationTokens & CurveTokens & ShadowTokens & ShadowBrandTokens & FontFamilyTokens & FontWeightTokens & ColorPaletteTokens & ColorTokens; + +// @public +export function themeToTokensObject(theme: TTheme): Record; + +// @public +export const Title1: FunctionComponent; + +// @public (undocumented) +export const title1ClassNames: SlotClassNames; + +// @public +export const Title2: FunctionComponent; + +// @public (undocumented) +export const title2ClassNames: SlotClassNames; + +// @public +export const Title3: FunctionComponent; + +// @public (undocumented) +export const title3ClassNames: SlotClassNames; -export { UninitializedMenuListState } +// @public +export const ToggleButton: ForwardRefComponent; -export { useAccordion_unstable } +// @public (undocumented) +export const toggleButtonClassNames: SlotClassNames; + +// @public (undocumented) +export type ToggleButtonProps = ButtonProps & { + defaultChecked?: boolean; + checked?: boolean; +}; + +// @public (undocumented) +export type ToggleButtonState = ButtonState & Required>; + +// @public (undocumented) +export const tokens: Record; + +// @public +export const Toolbar: ForwardRefComponent; + +// @public +export const ToolbarButton: ForwardRefComponent; + +// @public +export type ToolbarButtonProps = ComponentProps & Partial> & { + appearance?: 'primary' | 'subtle'; +} & { + vertical?: boolean; +}; + +// @public +export type ToolbarButtonState = ComponentState> & ButtonState & Required>; + +// @public (undocumented) +export const toolbarClassNames: SlotClassNames; + +// @public (undocumented) +export type ToolbarContextValue = Pick & { + handleToggleButton?: ToggableHandler; + handleRadio?: ToggableHandler; +}; + +// @public (undocumented) +export type ToolbarContextValues = { + toolbar: ToolbarContextValue; +}; + +// @public +export const ToolbarDivider: ForwardRefComponent; + +// @public +export type ToolbarDividerProps = ComponentProps> & { + vertical?: boolean; +}; + +// @public +export type ToolbarDividerState = ComponentState> & DividerState; + +// @public +export const ToolbarGroup: ForwardRefComponent; + +// @public (undocumented) +export const toolbarGroupClassNames: SlotClassNames; + +// @public +export type ToolbarGroupProps = ComponentProps; + +// @public +export type ToolbarGroupState = ComponentState; + +// @public +export type ToolbarProps = ComponentProps & { + size?: 'small' | 'medium' | 'large'; + vertical?: boolean; + checkedValues?: Record; + defaultCheckedValues?: Record; + onCheckedValueChange?: (e: ToolbarCheckedValueChangeEvent, data: ToolbarCheckedValueChangeData) => void; +}; + +// @public +export const ToolbarRadioButton: ForwardRefComponent; + +// @public +export type ToolbarRadioButtonProps = ComponentProps & Partial> & { + appearance?: 'primary' | 'subtle'; + name: string; + value: string; +}; + +// @public +export type ToolbarRadioButtonState = ComponentState> & ToggleButtonState & Required> & Pick; + +// @public +export const ToolbarRadioGroup: ForwardRefComponent; + +// @public +export type ToolbarRadioGroupProps = ComponentProps; + +// @public +export type ToolbarRadioGroupState = ComponentState; + +// @public (undocumented) +export type ToolbarSlots = { + root: Slot<'div'>; +}; + +// @public +export type ToolbarState = ComponentState & Required> & Pick & { + handleToggleButton: ToggableHandler; + handleRadio: ToggableHandler; +}; + +// @public +export const ToolbarToggleButton: ForwardRefComponent; + +// @public +export type ToolbarToggleButtonProps = ComponentProps & Partial> & { + appearance?: 'primary' | 'subtle'; + name: string; + value: string; +}; + +// @public +export type ToolbarToggleButtonState = ComponentState> & ToggleButtonState & Required> & Pick; + +// @public +export const Tooltip: React_2.FC; + +// @public (undocumented) +export const tooltipClassNames: SlotClassNames; + +// @public +export type TooltipProps = ComponentProps & TriggerProps & Pick & { + appearance?: 'normal' | 'inverted'; + hideDelay?: number; + onVisibleChange?: (event: React_2.PointerEvent | React_2.FocusEvent | undefined, data: OnVisibleChangeData) => void; + positioning?: PositioningShorthand; + relationship: 'label' | 'description' | 'inaccessible'; + showDelay?: number; + visible?: boolean; + withArrow?: boolean; +}; + +// @public +export type TooltipSlots = { + content: NonNullable>; +}; + +// @public +export type TooltipState = ComponentState & Pick & Required> & { + children?: React_2.ReactElement | null; + shouldRenderTooltip?: boolean; + arrowRef?: React_2.Ref; + arrowClassName?: string; +}; + +// @public +export type TooltipTriggerProps = { + ref?: React_2.Ref; +} & Pick, 'aria-describedby' | 'aria-label' | 'aria-labelledby' | 'onBlur' | 'onFocus' | 'onPointerEnter' | 'onPointerLeave'>; + +// @public (undocumented) +export type TypographyStyle = { + fontFamily: string; + fontSize: string; + fontWeight: string; + lineHeight: string; +}; + +// @public (undocumented) +export type TypographyStyles = { + body1: TypographyStyle; + body1Strong: TypographyStyle; + body1Stronger: TypographyStyle; + body2: TypographyStyle; + caption1: TypographyStyle; + caption1Strong: TypographyStyle; + caption1Stronger: TypographyStyle; + caption2: TypographyStyle; + caption2Strong: TypographyStyle; + subtitle1: TypographyStyle; + subtitle2: TypographyStyle; + subtitle2Stronger: TypographyStyle; + title1: TypographyStyle; + title2: TypographyStyle; + title3: TypographyStyle; + largeTitle: TypographyStyle; + display: TypographyStyle; +}; + +// @public +export const typographyStyles: TypographyStyles; + +// @public @deprecated (undocumented) +export type UninitializedMenuListState = Omit & Partial>; + +// @public +export const useAccordion_unstable: (props: AccordionProps, ref: React_2.Ref) => AccordionState; + +// @public (undocumented) +export const useAccordionContext_unstable: (selector: ContextSelector) => T; + +// @public (undocumented) +export function useAccordionContextValues_unstable(state: AccordionState): AccordionContextValues; + +// @public +export const useAccordionHeader_unstable: (props: AccordionHeaderProps, ref: React_2.Ref) => AccordionHeaderState; + +// @public (undocumented) +export function useAccordionHeaderContextValues_unstable(state: AccordionHeaderState): AccordionHeaderContextValues; -export { useAccordionContext_unstable } +// @public +export const useAccordionHeaderStyles_unstable: (state: AccordionHeaderState) => AccordionHeaderState; -export { useAccordionContextValues_unstable } +// @public +export const useAccordionItem_unstable: (props: AccordionItemProps, ref: React_2.Ref) => AccordionItemState; -export { useAccordionHeader_unstable } +// @public (undocumented) +export const useAccordionItemContext_unstable: () => AccordionItemContextValue; -export { useAccordionHeaderContextValues_unstable } +// @public (undocumented) +export function useAccordionItemContextValues_unstable(state: AccordionItemState): AccordionItemContextValues; -export { useAccordionHeaderStyles_unstable } +// @public (undocumented) +export const useAccordionItemStyles_unstable: (state: AccordionItemState) => AccordionItemState; -export { useAccordionItem_unstable } +// @public +export const useAccordionPanel_unstable: (props: AccordionPanelProps, ref: React_2.Ref) => AccordionPanelState; -export { useAccordionItemContext_unstable } +// @public +export const useAccordionPanelStyles_unstable: (state: AccordionPanelState) => AccordionPanelState; -export { useAccordionItemContextValues_unstable } +// @public (undocumented) +export const useAccordionStyles_unstable: (state: AccordionState) => AccordionState; -export { useAccordionItemStyles_unstable } +// @public +export const useArrowNavigationGroup: (options?: UseArrowNavigationGroupOptions) => Types.TabsterDOMAttribute; -export { useAccordionPanel_unstable } +// @public (undocumented) +export interface UseArrowNavigationGroupOptions { + axis?: 'vertical' | 'horizontal' | 'grid' | 'both'; + circular?: boolean; + ignoreDefaultKeydown?: Types.FocusableProps['ignoreKeydown']; + memorizeCurrent?: boolean; + tabbable?: boolean; + unstable_hasDefault?: boolean; +} -export { useAccordionPanelStyles_unstable } +// @public (undocumented) +export const useAvatar_unstable: (props: AvatarProps, ref: React_2.Ref) => AvatarState; -export { useAccordionStyles_unstable } +// @public +export const useAvatarGroup_unstable: (props: AvatarGroupProps, ref: React_2.Ref) => AvatarGroupState; -export { useArrowNavigationGroup } +// @public (undocumented) +export const useAvatarGroupContext_unstable: (selector: ContextSelector) => T; -export { UseArrowNavigationGroupOptions } +// @public (undocumented) +export const useAvatarGroupContextValues: (state: AvatarGroupState) => AvatarGroupContextValues; -export { useAvatar_unstable } +// @public +export const useAvatarGroupItem_unstable: (props: AvatarGroupItemProps, ref: React_2.Ref) => AvatarGroupItemState; -export { useAvatarGroup_unstable } +// @public +export const useAvatarGroupItemStyles_unstable: (state: AvatarGroupItemState) => AvatarGroupItemState; -export { useAvatarGroupContext_unstable } +// @public +export const useAvatarGroupPopover_unstable: (props: AvatarGroupPopoverProps) => AvatarGroupPopoverState; -export { useAvatarGroupContextValues } +// @public +export const useAvatarGroupPopoverStyles_unstable: (state: AvatarGroupPopoverState) => AvatarGroupPopoverState; -export { useAvatarGroupItem_unstable } +// @public +export const useAvatarGroupStyles_unstable: (state: AvatarGroupState) => AvatarGroupState; -export { useAvatarGroupItemStyles_unstable } +// @public (undocumented) +export const useAvatarStyles_unstable: (state: AvatarState) => AvatarState; -export { useAvatarGroupPopover_unstable } +// @public +export const useBadge_unstable: (props: BadgeProps, ref: React_2.Ref) => BadgeState; -export { useAvatarGroupPopoverStyles_unstable } +// @public +export const useBadgeStyles_unstable: (state: BadgeState) => BadgeState; -export { useAvatarGroupStyles_unstable } +// @public +export const useButton_unstable: (props: ButtonProps, ref: React_2.Ref) => ButtonState; -export { useAvatarStyles_unstable } +// @public (undocumented) +export const useButtonStyles_unstable: (state: ButtonState) => ButtonState; -export { useBadge_unstable } +// @public +export const useCard_unstable: (props: CardProps, ref: React_2.Ref) => CardState; -export { useBadgeStyles_unstable } +// @public +export const useCardFooter_unstable: (props: CardFooterProps, ref: React_2.Ref) => CardFooterState; -export { useButton_unstable } +// @public +export const useCardFooterStyles_unstable: (state: CardFooterState) => CardFooterState; -export { useButtonStyles_unstable } +// @public +export const useCardHeader_unstable: (props: CardHeaderProps, ref: React_2.Ref) => CardHeaderState; -export { useCard_unstable } +// @public +export const useCardHeaderStyles_unstable: (state: CardHeaderState) => CardHeaderState; -export { useCardFooter_unstable } +// @public +export const useCardPreview_unstable: (props: CardPreviewProps, ref: React_2.Ref) => CardPreviewState; -export { useCardFooterStyles_unstable } +// @public +export const useCardPreviewStyles_unstable: (state: CardPreviewState) => CardPreviewState; -export { useCardHeader_unstable } +// @public +export const useCardStyles_unstable: (state: CardState) => CardState; -export { useCardHeaderStyles_unstable } +// @public +export const useCheckbox_unstable: (props: CheckboxProps, ref: React_2.Ref) => CheckboxState; -export { useCardPreview_unstable } +// @public +export const useCheckboxStyles_unstable: (state: CheckboxState) => CheckboxState; -export { useCardPreviewStyles_unstable } +// @public +export const useCheckmarkStyles_unstable: (state: MenuItemSelectableState & Pick) => void; -export { useCardStyles_unstable } +// @public +export const useCombobox_unstable: (props: ComboboxProps, ref: React_2.Ref) => ComboboxState; -export { useCheckbox_unstable } +// @public (undocumented) +export function useComboboxContextValues(state: ComboboxBaseState): ComboboxBaseContextValues; -export { useCheckboxStyles_unstable } +// @public +export const useComboboxStyles_unstable: (state: ComboboxState) => ComboboxState; -export { useCheckmarkStyles_unstable } +// @public +export const useCompoundButton_unstable: ({ contentContainer, secondaryContent, ...props }: CompoundButtonProps, ref: React_2.Ref) => CompoundButtonState; -export { useCombobox_unstable } +// @public (undocumented) +export const useCompoundButtonStyles_unstable: (state: CompoundButtonState) => CompoundButtonState; -export { useComboboxContextValues } +// @public +export const useCounterBadge_unstable: (props: CounterBadgeProps, ref: React_2.Ref) => CounterBadgeState; -export { useComboboxStyles_unstable } +// @public +export const useCounterBadgeStyles_unstable: (state: CounterBadgeState) => CounterBadgeState; -export { useCompoundButton_unstable } +// @public +export const useDataGrid_unstable: (props: DataGridProps, ref: React_2.Ref) => DataGridState; -export { useCompoundButtonStyles_unstable } +// @public +export const useDataGridBody_unstable: (props: DataGridBodyProps, ref: React_2.Ref) => DataGridBodyState; -export { useCounterBadge_unstable } +// @public +export const useDataGridBodyStyles_unstable: (state: DataGridBodyState) => DataGridBodyState; -export { useCounterBadgeStyles_unstable } +// @public +export const useDataGridCell_unstable: (props: DataGridCellProps, ref: React_2.Ref) => DataGridCellState; -export { useDataGrid_unstable } +// @public +export const useDataGridCellStyles_unstable: (state: DataGridCellState) => DataGridCellState; -export { useDataGridBody_unstable } +// @public (undocumented) +export function useDataGridContextValues_unstable(state: DataGridState): DataGridContextValues; -export { useDataGridBodyStyles_unstable } +// @public +export const useDataGridHeader_unstable: (props: DataGridHeaderProps, ref: React_2.Ref) => DataGridHeaderState; -export { useDataGridCell_unstable } +// @public +export const useDataGridHeaderCell_unstable: (props: DataGridHeaderCellProps, ref: React_2.Ref) => DataGridHeaderCellState; -export { useDataGridCellStyles_unstable } +// @public +export const useDataGridHeaderCellStyles_unstable: (state: DataGridHeaderCellState) => DataGridHeaderCellState; -export { useDataGridContextValues_unstable } +// @public +export const useDataGridHeaderStyles_unstable: (state: DataGridHeaderState) => DataGridHeaderState; -export { useDataGridHeader_unstable } +// @public +export const useDataGridRow_unstable: (props: DataGridRowProps, ref: React_2.Ref) => DataGridRowState; -export { useDataGridHeaderCell_unstable } +// @public +export const useDataGridRowStyles_unstable: (state: DataGridRowState) => DataGridRowState; -export { useDataGridHeaderCellStyles_unstable } +// @public +export const useDataGridSelectionCell_unstable: (props: DataGridSelectionCellProps, ref: React_2.Ref) => DataGridSelectionCellState; -export { useDataGridHeaderStyles_unstable } +// @public +export const useDataGridSelectionCellStyles_unstable: (state: DataGridSelectionCellState) => DataGridSelectionCellState; -export { useDataGridRow_unstable } +// @public +export const useDataGridStyles_unstable: (state: DataGridState) => DataGridState; -export { useDataGridRowStyles_unstable } +// @public +export const useDialog_unstable: (props: DialogProps) => DialogState; -export { useDataGridSelectionCell_unstable } +// @public +export const useDialogActions_unstable: (props: DialogActionsProps, ref: React_2.Ref) => DialogActionsState; -export { useDataGridSelectionCellStyles_unstable } +// @public +export const useDialogActionsStyles_unstable: (state: DialogActionsState) => DialogActionsState; -export { useDataGridStyles_unstable } +// @public +export const useDialogBody_unstable: (props: DialogBodyProps, ref: React_2.Ref) => DialogBodyState; -export { useDialog_unstable } +// @public +export const useDialogBodyStyles_unstable: (state: DialogBodyState) => DialogBodyState; -export { useDialogActions_unstable } +// @public +export const useDialogContent_unstable: (props: DialogContentProps, ref: React_2.Ref) => DialogContentState; -export { useDialogActionsStyles_unstable } +// @public +export const useDialogContentStyles_unstable: (state: DialogContentState) => DialogContentState; -export { useDialogBody_unstable } +// @public +export const useDialogSurface_unstable: (props: DialogSurfaceProps, ref: React_2.Ref) => DialogSurfaceState; -export { useDialogBodyStyles_unstable } +// @public +export const useDialogSurfaceStyles_unstable: (state: DialogSurfaceState) => DialogSurfaceState; -export { useDialogContent_unstable } +// @public +export const useDialogTitle_unstable: (props: DialogTitleProps, ref: React_2.Ref) => DialogTitleState; -export { useDialogContentStyles_unstable } +// @public +export const useDialogTitleStyles_unstable: (state: DialogTitleState) => DialogTitleState; -export { useDialogSurface_unstable } +// @public +export const useDialogTrigger_unstable: (props: DialogTriggerProps) => DialogTriggerState; -export { useDialogSurfaceStyles_unstable } +// @public +export const useDivider_unstable: (props: DividerProps, ref: React_2.Ref) => DividerState; -export { useDialogTitle_unstable } +// @public (undocumented) +export const useDividerStyles_unstable: (state: DividerState) => DividerState; -export { useDialogTitleStyles_unstable } +// @public +export const useDropdown_unstable: (props: DropdownProps, ref: React_2.Ref) => DropdownState; -export { useDialogTrigger_unstable } +// @public +export const useDropdownStyles_unstable: (state: DropdownState) => DropdownState; -export { useDivider_unstable } +// @public +export const useField_unstable: (props: FieldProps, ref: React_2.Ref) => FieldState; -export { useDividerStyles_unstable } +// @public (undocumented) +export const useFieldContext_unstable: () => Readonly & { + labelFor?: string | undefined; + labelId?: string | undefined; + validationMessageId?: string | undefined; + hintId?: string | undefined; +}> | undefined; -export { useDropdown_unstable } +// @public +export const useFieldContextValues_unstable: (state: FieldState) => FieldContextValues; -export { useDropdownStyles_unstable } +// @public +export function useFieldControlProps_unstable(): FieldControlProps | undefined; -export { useField_unstable } +// @public +export function useFieldControlProps_unstable(props: Props, options?: FieldControlPropsOptions): Props; -export { useFieldContext_unstable } +// @public +export const useFieldStyles_unstable: (state: FieldState) => void; -export { useFieldContextValues_unstable } +// @public (undocumented) +export function useFluent(): ProviderContextValue; -export { useFieldControlProps_unstable } +// @public +export const useFluentProvider_unstable: (props: FluentProviderProps, ref: React_2.Ref) => FluentProviderState; -export { useFieldStyles_unstable } +// @public (undocumented) +export function useFluentProviderContextValues_unstable(state: FluentProviderState): FluentProviderContextValues; -export { useFluent } +// @public +export const useFluentProviderStyles_unstable: (state: FluentProviderState) => FluentProviderState; -export { useFluentProvider_unstable } +// @public +export const useFocusableGroup: (options?: UseFocusableGroupOptions | undefined) => Types.TabsterDOMAttribute; -export { useFluentProviderContextValues_unstable } +// @public (undocumented) +export interface UseFocusableGroupOptions { + tabBehavior?: 'unlimited' | 'limited' | 'limited-trap-focus'; +} -export { useFluentProviderStyles_unstable } +// @public +export const useFocusFinders: () => { + findAllFocusable: (container: HTMLElement, acceptCondition?: ((el: HTMLElement) => boolean) | undefined) => HTMLElement[]; + findFirstFocusable: (container: HTMLElement) => HTMLElement | null | undefined; + findLastFocusable: (container: HTMLElement) => HTMLElement | null | undefined; + findNextFocusable: (currentElement: HTMLElement, options?: Pick, 'container'>) => HTMLElement | null | undefined; + findPrevFocusable: (currentElement: HTMLElement, options?: Pick, 'container'>) => HTMLElement | null | undefined; +}; -export { useFocusableGroup } +// @public +export function useFocusWithin(): React_2.RefObject; -export { UseFocusableGroupOptions } +// @public +export function useId(prefix?: string, providedId?: string): string; -export { useFocusFinders } +// @public +export const useImage_unstable: (props: ImageProps, ref: React_2.Ref) => ImageState; -export { useFocusWithin } +// @public (undocumented) +export const useImageStyles_unstable: (state: ImageState) => void; -export { useId } +// @public +export const useInput_unstable: (props: InputProps, ref: React_2.Ref) => InputState; -export { useImage_unstable } +// @public +export const useInputStyles_unstable: (state: InputState) => InputState; -export { useImageStyles_unstable } +// @public +export const useIsomorphicLayoutEffect: typeof React_2.useEffect; -export { useInput_unstable } +// @public (undocumented) +export function useIsOverflowGroupVisible(id: string): OverflowGroupState; -export { useInputStyles_unstable } +// @public (undocumented) +export function useIsOverflowItemVisible(id: string): boolean; -export { useIsomorphicLayoutEffect } +// @public +export function useIsSSR(): boolean; -export { useIsOverflowGroupVisible } +// @public +export function useKeyboardNavAttribute(): RefObject; -export { useIsOverflowItemVisible } +// @public +export const useLabel_unstable: (props: LabelProps, ref: React_2.Ref) => LabelState; -export { useIsSSR } +// @public +export const useLabelStyles_unstable: (state: LabelState) => LabelState; -export { useKeyboardNavAttribute } +// @public +export const useLink_unstable: (props: LinkProps, ref: React_2.Ref) => LinkState; -export { useLabel_unstable } +// @public +export const useLinkState_unstable: (state: LinkState) => LinkState; -export { useLabelStyles_unstable } +// @public (undocumented) +export const useLinkStyles_unstable: (state: LinkState) => LinkState; -export { useLink_unstable } +// @public +export const useListbox_unstable: (props: ListboxProps, ref: React_2.Ref) => ListboxState; -export { useLinkState_unstable } +// @public (undocumented) +export function useListboxContextValues(state: ListboxState): ListboxContextValues; -export { useLinkStyles_unstable } +// @public +export const useListboxStyles_unstable: (state: ListboxState) => ListboxState; -export { useListbox_unstable } +// @public +export const useMenu_unstable: (props: MenuProps) => MenuState; -export { useListboxContextValues } +// @public +export const useMenuButton_unstable: ({ menuIcon, ...props }: MenuButtonProps, ref: React_2.Ref) => MenuButtonState; -export { useListboxStyles_unstable } +// @public (undocumented) +export const useMenuButtonStyles_unstable: (state: MenuButtonState) => MenuButtonState; -export { useMenu_unstable } +// @public (undocumented) +export const useMenuContext_unstable: (selector: ContextSelector) => T; -export { useMenuButton_unstable } +// @public (undocumented) +export function useMenuContextValues_unstable(state: MenuState): MenuContextValues; -export { useMenuButtonStyles_unstable } +// @public +export const useMenuDivider_unstable: (props: MenuDividerProps, ref: React_2.Ref) => MenuDividerState; -export { useMenuContext_unstable } +// @public (undocumented) +export const useMenuDividerStyles_unstable: (state: MenuDividerState) => MenuDividerState; -export { useMenuContextValues_unstable } +// @public +export function useMenuGroup_unstable(props: MenuGroupProps, ref: React_2.Ref): MenuGroupState; -export { useMenuDivider_unstable } +// @public (undocumented) +export const useMenuGroupContext_unstable: () => MenuGroupContextValue; -export { useMenuDividerStyles_unstable } +// @public (undocumented) +export function useMenuGroupContextValues_unstable(state: MenuGroupState): MenuGroupContextValues; -export { useMenuGroup_unstable } +// @public +export function useMenuGroupHeader_unstable(props: MenuGroupHeaderProps, ref: React_2.Ref): MenuGroupHeaderState; -export { useMenuGroupContext_unstable } +// @public (undocumented) +export const useMenuGroupHeaderStyles_unstable: (state: MenuGroupHeaderState) => MenuGroupHeaderState; -export { useMenuGroupContextValues_unstable } +// @public (undocumented) +export const useMenuGroupStyles_unstable: (state: MenuGroupState) => MenuGroupState; -export { useMenuGroupHeader_unstable } +// @public +export const useMenuItem_unstable: (props: MenuItemProps, ref: React_2.Ref>) => MenuItemState; -export { useMenuGroupHeaderStyles_unstable } +// @public +export const useMenuItemCheckbox_unstable: (props: MenuItemCheckboxProps, ref: React_2.Ref>) => MenuItemCheckboxState; -export { useMenuGroupStyles_unstable } +// @public (undocumented) +export const useMenuItemCheckboxStyles_unstable: (state: MenuItemCheckboxState) => void; -export { useMenuItem_unstable } +// @public +export const useMenuItemRadio_unstable: (props: MenuItemRadioProps, ref: React_2.Ref>) => MenuItemRadioState; -export { useMenuItemCheckbox_unstable } +// @public (undocumented) +export const useMenuItemRadioStyles_unstable: (state: MenuItemRadioState) => void; -export { useMenuItemCheckboxStyles_unstable } +// @public +export const useMenuItemStyles_unstable: (state: MenuItemState) => void; -export { useMenuItemRadio_unstable } +// @public +export const useMenuList_unstable: (props: MenuListProps, ref: React_2.Ref) => MenuListState; -export { useMenuItemRadioStyles_unstable } +// @public (undocumented) +export const useMenuListContext_unstable: (selector: ContextSelector) => T; -export { useMenuItemStyles_unstable } +// @public (undocumented) +export function useMenuListContextValues_unstable(state: MenuListState): MenuListContextValues; -export { useMenuList_unstable } +// @public +export const useMenuListStyles_unstable: (state: MenuListState) => MenuListState; -export { useMenuListContext_unstable } +// @public +export const useMenuPopover_unstable: (props: MenuPopoverProps, ref: React_2.Ref) => MenuPopoverState; -export { useMenuListContextValues_unstable } +// @public +export const useMenuPopoverStyles_unstable: (state: MenuPopoverState) => MenuPopoverState; -export { useMenuListStyles_unstable } +// @public +export const useMenuSplitGroup_unstable: (props: MenuSplitGroupProps, ref: React_2.Ref) => MenuSplitGroupState; -export { useMenuPopover_unstable } +// @public +export const useMenuSplitGroupStyles_unstable: (state: MenuSplitGroupState) => MenuSplitGroupState; -export { useMenuPopoverStyles_unstable } +// @public +export const useMenuTrigger_unstable: (props: MenuTriggerProps) => MenuTriggerState; -export { useMenuSplitGroup_unstable } +// @public (undocumented) +export const useMenuTriggerContext_unstable: () => boolean; -export { useMenuSplitGroupStyles_unstable } +// @public +export function useMergedRefs(...refs: (React_2.Ref | undefined)[]): RefObjectFunction; -export { useMenuTrigger_unstable } +// @public +export const useModalAttributes: (options?: UseModalAttributesOptions) => { + modalAttributes: Types.TabsterDOMAttribute; + triggerAttributes: Types.TabsterDOMAttribute; +}; -export { useMenuTriggerContext_unstable } +// @public (undocumented) +export interface UseModalAttributesOptions { + alwaysFocusable?: boolean; + id?: string; + legacyTrapFocus?: boolean; + trapFocus?: boolean; +} -export { useMergedRefs } +// @public +export const useOption_unstable: (props: OptionProps, ref: React_2.Ref) => OptionState; -export { useModalAttributes } +// @public +export const useOptionGroup_unstable: (props: OptionGroupProps, ref: React_2.Ref) => OptionGroupState; -export { UseModalAttributesOptions } +// @public +export const useOptionGroupStyles_unstable: (state: OptionGroupState) => OptionGroupState; -export { useOption_unstable } +// @public +export const useOptionStyles_unstable: (state: OptionState) => OptionState; -export { useOptionGroup_unstable } +// @public (undocumented) +export const useOverflowCount: () => number; -export { useOptionGroupStyles_unstable } +// @public (undocumented) +export function useOverflowMenu(id?: string): { + ref: React_2.RefObject; + overflowCount: number; + isOverflowing: boolean; +}; -export { useOptionStyles_unstable } +// @public +export const usePersona_unstable: (props: PersonaProps, ref: React_2.Ref) => PersonaState; -export { useOverflowCount } +// @public +export const usePersonaStyles_unstable: (state: PersonaState) => PersonaState; -export { useOverflowMenu } +// @public +export const usePopover_unstable: (props: PopoverProps) => PopoverState; -export { usePersona_unstable } +// @public (undocumented) +export const usePopoverContext_unstable: (selector: ContextSelector) => T; -export { usePersonaStyles_unstable } +// @public +export const usePopoverSurface_unstable: (props: PopoverSurfaceProps, ref: React_2.Ref) => PopoverSurfaceState; -export { usePopover_unstable } +// @public +export const usePopoverSurfaceStyles_unstable: (state: PopoverSurfaceState) => PopoverSurfaceState; -export { usePopoverContext_unstable } +// @public +export const usePopoverTrigger_unstable: (props: PopoverTriggerProps) => PopoverTriggerState; -export { usePopoverSurface_unstable } +// @public +export const usePortal_unstable: (props: PortalProps) => PortalState; -export { usePopoverSurfaceStyles_unstable } +// @public +export const usePresenceBadge_unstable: (props: PresenceBadgeProps, ref: React_2.Ref) => PresenceBadgeState; -export { usePopoverTrigger_unstable } +// @public +export const usePresenceBadgeStyles_unstable: (state: PresenceBadgeState) => PresenceBadgeState; -export { usePortal_unstable } +// @public +export const useProgressBar_unstable: (props: ProgressBarProps, ref: React_2.Ref) => ProgressBarState; -export { usePresenceBadge_unstable } +// @public +export const useProgressBarStyles_unstable: (state: ProgressBarState) => ProgressBarState; -export { usePresenceBadgeStyles_unstable } +// @public +export const useRadio_unstable: (props: RadioProps, ref: React_2.Ref) => RadioState; -export { useProgressBar_unstable } +// @public +export const useRadioGroup_unstable: (props: RadioGroupProps, ref: React_2.Ref) => RadioGroupState; -export { useProgressBarStyles_unstable } +// @public @deprecated (undocumented) +export const useRadioGroupContext_unstable: (selector: (ctx: RadioGroupContextValue) => T) => T; -export { useRadio_unstable } +// @public +export const useRadioGroupContextValue_unstable: () => RadioGroupContextValue; -export { useRadioGroup_unstable } +// @public (undocumented) +export const useRadioGroupContextValues: (state: RadioGroupState) => RadioGroupContextValues; -export { useRadioGroupContext_unstable } +// @public +export const useRadioGroupStyles_unstable: (state: RadioGroupState) => void; -export { useRadioGroupContextValue_unstable } +// @public +export const useRadioStyles_unstable: (state: RadioState) => void; -export { useRadioGroupContextValues } +// @public (undocumented) +export function useScrollbarWidth(options: UseScrollbarWidthOptions): number | undefined; -export { useRadioGroupStyles_unstable } +// @public +export const useSelect_unstable: (props: SelectProps, ref: React_2.Ref) => SelectState; -export { useRadioStyles_unstable } +// @public +export const useSelectStyles_unstable: (state: SelectState) => SelectState; -export { useScrollbarWidth } +// @public +export const useSkeleton_unstable: (props: SkeletonProps, ref: React_2.Ref) => SkeletonState; -export { useSelect_unstable } +// @public (undocumented) +export const useSkeletonContext: () => SkeletonContextValue; -export { useSelectStyles_unstable } +// @public +export const useSkeletonItem_unstable: (props: SkeletonItemProps, ref: React_2.Ref) => SkeletonItemState; -export { useSkeleton_unstable } +// @public +export const useSkeletonItemStyles_unstable: (state: SkeletonItemState) => SkeletonItemState; -export { useSkeletonContext } +// @public +export const useSkeletonStyles_unstable: (state: SkeletonState) => SkeletonState; -export { useSkeletonItem_unstable } +// @public (undocumented) +export const useSlider_unstable: (props: SliderProps, ref: React_2.Ref) => SliderState; -export { useSkeletonItemStyles_unstable } +// @public (undocumented) +export const useSliderState_unstable: (state: SliderState, props: SliderProps) => SliderState; -export { useSkeletonStyles_unstable } +// @public +export const useSliderStyles_unstable: (state: SliderState) => SliderState; -export { useSlider_unstable } +// @public +export const useSpinButton_unstable: (props: SpinButtonProps, ref: React_2.Ref) => SpinButtonState; -export { useSliderState_unstable } +// @public +export const useSpinButtonStyles_unstable: (state: SpinButtonState) => SpinButtonState; -export { useSliderStyles_unstable } +// @public +export const useSpinner_unstable: (props: SpinnerProps, ref: React_2.Ref) => SpinnerState; -export { useSpinButton_unstable } +// @public +export const useSpinnerStyles_unstable: (state: SpinnerState) => SpinnerState; -export { useSpinButtonStyles_unstable } +// @public +export const useSplitButton_unstable: (props: SplitButtonProps, ref: React_2.Ref) => SplitButtonState; -export { useSpinner_unstable } +// @public (undocumented) +export const useSplitButtonStyles_unstable: (state: SplitButtonState) => SplitButtonState; -export { useSpinnerStyles_unstable } +// @public +export const useSwitch_unstable: (props: SwitchProps, ref: React_2.Ref) => SwitchState; -export { useSplitButton_unstable } +// @public +export const useSwitchStyles_unstable: (state: SwitchState) => SwitchState; -export { useSplitButtonStyles_unstable } +// @public +export const useTab_unstable: (props: TabProps, ref: React_2.Ref) => TabState; -export { useSwitch_unstable } +// @public +export const useTable_unstable: (props: TableProps, ref: React_2.Ref) => TableState; -export { useSwitchStyles_unstable } +// @public +export const useTableBody_unstable: (props: TableBodyProps, ref: React_2.Ref) => TableBodyState; -export { useTab_unstable } +// @public +export const useTableBodyStyles_unstable: (state: TableBodyState) => TableBodyState; -export { useTable_unstable } +// @public +export const useTableCell_unstable: (props: TableCellProps, ref: React_2.Ref) => TableCellState; -export { useTableBody_unstable } +// @public +export const useTableCellActions_unstable: (props: TableCellActionsProps, ref: React_2.Ref) => TableCellActionsState; -export { useTableBodyStyles_unstable } +// @public +export const useTableCellActionsStyles_unstable: (state: TableCellActionsState) => TableCellActionsState; -export { useTableCell_unstable } +// @public +export const useTableCellLayout_unstable: (props: TableCellLayoutProps, ref: React_2.Ref) => TableCellLayoutState; -export { useTableCellActions_unstable } +// @public +export const useTableCellLayoutStyles_unstable: (state: TableCellLayoutState) => TableCellLayoutState; -export { useTableCellActionsStyles_unstable } +// @public +export const useTableCellStyles_unstable: (state: TableCellState) => TableCellState; -export { useTableCellLayout_unstable } +// @public (undocumented) +export function useTableColumnSizing_unstable(params?: UseTableColumnSizingParams): (tableState: TableFeaturesState) => TableFeaturesState; -export { useTableCellLayoutStyles_unstable } +// @public (undocumented) +export const useTableContext: () => TableContextValue; -export { useTableCellStyles_unstable } +// @public (undocumented) +export function useTableFeatures(options: UseTableFeaturesOptions, plugins?: TableFeaturePlugin[]): TableFeaturesState; -export { useTableColumnSizing_unstable } +// @public (undocumented) +export interface UseTableFeaturesOptions { + // (undocumented) + columns: TableColumnDefinition[]; + // (undocumented) + getRowId?: (item: TItem) => TableRowId; + // (undocumented) + items: TItem[]; +} -export { useTableContext } +// @public +export const useTableHeader_unstable: (props: TableHeaderProps, ref: React_2.Ref) => TableHeaderState; -export { useTableFeatures } +// @public +export const useTableHeaderCell_unstable: (props: TableHeaderCellProps, ref: React_2.Ref) => TableHeaderCellState; -export { UseTableFeaturesOptions } +// @public +export const useTableHeaderCellStyles_unstable: (state: TableHeaderCellState) => TableHeaderCellState; -export { useTableHeader_unstable } +// @public +export const useTableHeaderStyles_unstable: (state: TableHeaderState) => TableHeaderState; -export { useTableHeaderCell_unstable } +// @public +export const useTableResizeHandle_unstable: (props: TableResizeHandleProps, ref: React_2.Ref) => TableResizeHandleState; -export { useTableHeaderCellStyles_unstable } +// @public +export const useTableResizeHandleStyles_unstable: (state: TableResizeHandleState) => TableResizeHandleState; -export { useTableHeaderStyles_unstable } +// @public +export const useTableRow_unstable: (props: TableRowProps, ref: React_2.Ref) => TableRowState; -export { useTableResizeHandle_unstable } +// @public (undocumented) +export const useTableRowIdContext: () => TableRowId; -export { useTableResizeHandleStyles_unstable } +// @public +export const useTableRowStyles_unstable: (state: TableRowState) => TableRowState; -export { useTableRow_unstable } +// @public (undocumented) +export function useTableSelection(options: UseTableSelectionOptions): (tableState: TableFeaturesState) => TableFeaturesState; -export { useTableRowIdContext } +// @public +export const useTableSelectionCell_unstable: (props: TableSelectionCellProps, ref: React_2.Ref) => TableSelectionCellState; -export { useTableRowStyles_unstable } +// @public +export const useTableSelectionCellStyles_unstable: (state: TableSelectionCellState) => TableSelectionCellState; -export { useTableSelection } +// @public (undocumented) +export function useTableSort(options: UseTableSortOptions): (tableState: TableFeaturesState) => TableFeaturesState; -export { useTableSelectionCell_unstable } +// @public +export const useTableStyles_unstable: (state: TableState) => TableState; -export { useTableSelectionCellStyles_unstable } +// @public +export const useTabList_unstable: (props: TabListProps, ref: React_2.Ref) => TabListState; -export { useTableSort } +// @public (undocumented) +export const useTabListContext_unstable: (selector: ContextSelector) => T; -export { useTableStyles_unstable } +// @public (undocumented) +export function useTabListContextValues_unstable(state: TabListState): TabListContextValues; -export { useTabList_unstable } +// @public +export const useTabListStyles_unstable: (state: TabListState) => TabListState; -export { useTabListContext_unstable } +// @public +export const useTabStyles_unstable: (state: TabState) => TabState; -export { useTabListContextValues_unstable } +// @public +export const useText_unstable: (props: TextProps, ref: React_2.Ref) => TextState; -export { useTabListStyles_unstable } +// @public +export const useTextarea_unstable: (props: TextareaProps, ref: React_2.Ref) => TextareaState; -export { useTabStyles_unstable } +// @public +export const useTextareaStyles_unstable: (state: TextareaState) => TextareaState; -export { useText_unstable } +// @public +export const useTextStyles_unstable: (state: TextState) => TextState; -export { useTextarea_unstable } +// @public (undocumented) +export function useThemeClassName(): ThemeClassNameContextValue; -export { useTextareaStyles_unstable } +// @public +export const useToggleButton_unstable: (props: ToggleButtonProps, ref: React_2.Ref) => ToggleButtonState; -export { useTextStyles_unstable } +// @public (undocumented) +export const useToggleButtonStyles_unstable: (state: ToggleButtonState) => ToggleButtonState; -export { useThemeClassName } +// @public (undocumented) +export function useToggleState, TButtonState extends Pick, TToggleButtonState extends Pick>(props: TToggleButtonProps, state: TButtonState): TToggleButtonState; -export { useToggleButton_unstable } +// @public +export const useToolbar_unstable: (props: ToolbarProps, ref: React_2.Ref) => ToolbarState; -export { useToggleButtonStyles_unstable } +// @public +export const useToolbarButton_unstable: (props: ToolbarButtonProps, ref: React_2.Ref) => ToolbarButtonState; -export { useToggleState } +// @public +export const useToolbarButtonStyles_unstable: (state: ToolbarButtonState) => void; -export { useToolbar_unstable } +// @public +export const useToolbarDivider_unstable: (props: ToolbarDividerProps, ref: React_2.Ref) => ToolbarDividerState; -export { useToolbarButton_unstable } +// @public +export const useToolbarDividerStyles_unstable: (state: ToolbarDividerState) => ToolbarDividerState; -export { useToolbarButtonStyles_unstable } +// @public +export const useToolbarGroup_unstable: (props: ToolbarGroupProps, ref: React_2.Ref) => ToolbarGroupState; -export { useToolbarDivider_unstable } +// @public +export const useToolbarGroupStyles_unstable: (state: ToolbarGroupState) => ToolbarGroupState; -export { useToolbarDividerStyles_unstable } +// @public +export const useToolbarRadioButton_unstable: (props: ToolbarRadioButtonProps, ref: React_2.Ref) => ToolbarRadioButtonState; -export { useToolbarGroup_unstable } +// @public +export const useToolbarRadioButtonStyles_unstable: (state: ToolbarRadioButtonState) => void; -export { useToolbarGroupStyles_unstable } +// @public +export const useToolbarStyles_unstable: (state: ToolbarState) => ToolbarState; -export { useToolbarRadioButton_unstable } +// @public +export const useToolbarToggleButton_unstable: (props: ToolbarToggleButtonProps, ref: React_2.Ref) => ToolbarToggleButtonState; -export { useToolbarRadioButtonStyles_unstable } +// @public +export const useToolbarToggleButtonStyles_unstable: (state: ToolbarToggleButtonState) => void; -export { useToolbarStyles_unstable } +// @public +export const useTooltip_unstable: (props: TooltipProps) => TooltipState; -export { useToolbarToggleButton_unstable } +// @public +export const useTooltipStyles_unstable: (state: TooltipState) => TooltipState; -export { useToolbarToggleButtonStyles_unstable } +// @public (undocumented) +export function useTooltipVisibility(): TooltipVisibilityContextValue; -export { useTooltip_unstable } +// @public +export const useTransitionPresence: (present: boolean, events?: UseTransitionPresenceEvents | undefined) => UseTransitionPresenceState; -export { useTooltipStyles_unstable } +// @public +export type UseTransitionPresenceEvents = { + onEntered?: () => void; + onExited?: () => void; +}; -export { useTooltipVisibility } +// @public +export type UseTransitionPresenceState = { + ref: React_2.RefCallback; + shouldRender: boolean; + visible: boolean; + entering: boolean; + exiting: boolean; + animating: boolean; +}; -export { VerticalSpacingTokens } +// @public (undocumented) +export type VerticalSpacingTokens = { + spacingVerticalNone: string; + spacingVerticalXXS: string; + spacingVerticalXS: string; + spacingVerticalSNudge: string; + spacingVerticalS: string; + spacingVerticalMNudge: string; + spacingVerticalM: string; + spacingVerticalL: string; + spacingVerticalXL: string; + spacingVerticalXXL: string; + spacingVerticalXXXL: string; +}; -export { webDarkTheme } +// @public (undocumented) +export const webDarkTheme: Theme; -export { webLightTheme } +// @public (undocumented) +export const webLightTheme: Theme; // (No @packageDocumentation comment for this package) diff --git a/packages/react-components/react-components/etc/react-components.unstable.api.md b/packages/react-components/react-components/etc/react-components.unstable.api.md index e39140d00fd301..2079e97b4c3830 100644 --- a/packages/react-components/react-components/etc/react-components.unstable.api.md +++ b/packages/react-components/react-components/etc/react-components.unstable.api.md @@ -4,528 +4,775 @@ ```ts -import { Alert } from '@fluentui/react-alert'; -import { alertClassNames } from '@fluentui/react-alert'; -import { AlertProps } from '@fluentui/react-alert'; -import { AlertSlots } from '@fluentui/react-alert'; -import { AlertState } from '@fluentui/react-alert'; -import { Drawer } from '@fluentui/react-drawer'; -import { DrawerBody } from '@fluentui/react-drawer'; -import { drawerBodyClassNames } from '@fluentui/react-drawer'; -import { DrawerBodySlots } from '@fluentui/react-drawer'; -import { DrawerBodyState } from '@fluentui/react-drawer'; -import { DrawerFooter } from '@fluentui/react-drawer'; -import { drawerFooterClassNames } from '@fluentui/react-drawer'; -import { DrawerFooterSlots } from '@fluentui/react-drawer'; -import { DrawerFooterState } from '@fluentui/react-drawer'; -import { DrawerHeader } from '@fluentui/react-drawer'; -import { drawerHeaderClassNames } from '@fluentui/react-drawer'; -import { DrawerHeaderNavigation } from '@fluentui/react-drawer'; -import { drawerHeaderNavigationClassNames } from '@fluentui/react-drawer'; -import { DrawerHeaderNavigationProps } from '@fluentui/react-drawer'; -import { DrawerHeaderNavigationSlots } from '@fluentui/react-drawer'; -import { DrawerHeaderNavigationState } from '@fluentui/react-drawer'; -import { DrawerHeaderSlots } from '@fluentui/react-drawer'; -import { DrawerHeaderState } from '@fluentui/react-drawer'; -import { DrawerHeaderTitle } from '@fluentui/react-drawer'; -import { drawerHeaderTitleClassNames } from '@fluentui/react-drawer'; -import { DrawerHeaderTitleSlots } from '@fluentui/react-drawer'; -import { DrawerHeaderTitleState } from '@fluentui/react-drawer'; -import { DrawerInline } from '@fluentui/react-drawer'; -import { drawerInlineClassNames } from '@fluentui/react-drawer'; -import { DrawerInlineProps } from '@fluentui/react-drawer'; -import { DrawerInlineSlots } from '@fluentui/react-drawer'; -import { DrawerInlineState } from '@fluentui/react-drawer'; -import { DrawerOverlay } from '@fluentui/react-drawer'; -import { drawerOverlayClassNames } from '@fluentui/react-drawer'; -import { DrawerOverlayProps } from '@fluentui/react-drawer'; -import { DrawerOverlaySlots } from '@fluentui/react-drawer'; -import { DrawerOverlayState } from '@fluentui/react-drawer'; -import { DrawerProps } from '@fluentui/react-drawer'; -import { DrawerSlots } from '@fluentui/react-drawer'; -import { DrawerState } from '@fluentui/react-drawer'; -import { flattenTree_unstable } from '@fluentui/react-tree'; -import { FlatTree } from '@fluentui/react-tree'; -import { FlatTreeItem } from '@fluentui/react-tree'; -import { FlatTreeItemProps } from '@fluentui/react-tree'; -import { FlatTreeProps } from '@fluentui/react-tree'; -import { InfoButton } from '@fluentui/react-infobutton'; -import { infoButtonClassNames } from '@fluentui/react-infobutton'; -import { InfoButtonProps } from '@fluentui/react-infobutton'; -import { InfoButtonSlots } from '@fluentui/react-infobutton'; -import { InfoButtonState } from '@fluentui/react-infobutton'; -import { InfoLabel } from '@fluentui/react-infobutton'; -import { infoLabelClassNames } from '@fluentui/react-infobutton'; -import { InfoLabelProps } from '@fluentui/react-infobutton'; -import { InfoLabelSlots } from '@fluentui/react-infobutton'; -import { InfoLabelState } from '@fluentui/react-infobutton'; -import { NestedTreeItem } from '@fluentui/react-tree'; -import { renderAlert_unstable } from '@fluentui/react-alert'; -import { renderDrawer_unstable } from '@fluentui/react-drawer'; -import { renderDrawerBody_unstable } from '@fluentui/react-drawer'; -import { renderDrawerFooter_unstable } from '@fluentui/react-drawer'; -import { renderDrawerHeader_unstable } from '@fluentui/react-drawer'; -import { renderDrawerHeaderNavigation_unstable } from '@fluentui/react-drawer'; -import { renderDrawerHeaderTitle_unstable } from '@fluentui/react-drawer'; -import { renderDrawerInline_unstable } from '@fluentui/react-drawer'; -import { renderDrawerOverlay_unstable } from '@fluentui/react-drawer'; -import { renderInfoButton_unstable } from '@fluentui/react-infobutton'; -import { renderInfoLabel_unstable } from '@fluentui/react-infobutton'; -import { renderTree_unstable } from '@fluentui/react-tree'; -import { renderTreeItem_unstable } from '@fluentui/react-tree'; -import { renderTreeItemAside_unstable } from '@fluentui/react-tree'; -import { renderTreeItemLayout_unstable } from '@fluentui/react-tree'; -import { renderTreeItemPersonaLayout_unstable } from '@fluentui/react-tree'; -import { renderVirtualizer_unstable } from '@fluentui/react-virtualizer'; -import { renderVirtualizerScrollView_unstable } from '@fluentui/react-virtualizer'; -import { renderVirtualizerScrollViewDynamic_unstable } from '@fluentui/react-virtualizer'; -import { ResizeCallbackWithRef } from '@fluentui/react-virtualizer'; -import { Tree } from '@fluentui/react-tree'; -import { treeClassNames } from '@fluentui/react-tree'; -import { TreeContextValue } from '@fluentui/react-tree'; -import { TreeItem } from '@fluentui/react-tree'; -import { TreeItemAside } from '@fluentui/react-tree'; -import { treeItemAsideClassNames } from '@fluentui/react-tree'; -import { TreeItemAsideProps } from '@fluentui/react-tree'; -import { TreeItemAsideSlots } from '@fluentui/react-tree'; -import { TreeItemAsideState } from '@fluentui/react-tree'; -import { treeItemClassNames } from '@fluentui/react-tree'; -import { TreeItemLayout } from '@fluentui/react-tree'; -import { treeItemLayoutClassNames } from '@fluentui/react-tree'; -import { TreeItemLayoutProps } from '@fluentui/react-tree'; -import { TreeItemLayoutSlots } from '@fluentui/react-tree'; -import { TreeItemLayoutState } from '@fluentui/react-tree'; -import { treeItemLevelToken } from '@fluentui/react-tree'; -import { TreeItemPersonaLayout } from '@fluentui/react-tree'; -import { treeItemPersonaLayoutClassNames } from '@fluentui/react-tree'; -import { TreeItemPersonaLayoutProps } from '@fluentui/react-tree'; -import { TreeItemPersonaLayoutSlots } from '@fluentui/react-tree'; -import { TreeItemPersonaLayoutState } from '@fluentui/react-tree'; -import { TreeItemProps } from '@fluentui/react-tree'; -import { TreeItemProvider } from '@fluentui/react-tree'; -import { TreeItemSlots } from '@fluentui/react-tree'; -import { TreeItemState } from '@fluentui/react-tree'; -import { TreeNavigationData_unstable } from '@fluentui/react-tree'; -import { TreeNavigationEvent_unstable } from '@fluentui/react-tree'; -import { TreeOpenChangeData } from '@fluentui/react-tree'; -import { TreeOpenChangeEvent } from '@fluentui/react-tree'; -import { TreeProps } from '@fluentui/react-tree'; -import { TreeProvider } from '@fluentui/react-tree'; -import { TreeSlots } from '@fluentui/react-tree'; -import { TreeState } from '@fluentui/react-tree'; -import { useAlert_unstable } from '@fluentui/react-alert'; -import { useAlertStyles_unstable } from '@fluentui/react-alert'; -import { useDrawer_unstable } from '@fluentui/react-drawer'; -import { useDrawerBody_unstable } from '@fluentui/react-drawer'; -import { useDrawerBodyStyles_unstable } from '@fluentui/react-drawer'; -import { useDrawerFooter_unstable } from '@fluentui/react-drawer'; -import { useDrawerFooterStyles_unstable } from '@fluentui/react-drawer'; -import { useDrawerHeader_unstable } from '@fluentui/react-drawer'; -import { useDrawerHeaderNavigation_unstable } from '@fluentui/react-drawer'; -import { useDrawerHeaderNavigationStyles_unstable } from '@fluentui/react-drawer'; -import { useDrawerHeaderStyles_unstable } from '@fluentui/react-drawer'; -import { useDrawerHeaderTitle_unstable } from '@fluentui/react-drawer'; -import { useDrawerHeaderTitleStyles_unstable } from '@fluentui/react-drawer'; -import { useDrawerInline_unstable } from '@fluentui/react-drawer'; -import { useDrawerInlineStyles_unstable } from '@fluentui/react-drawer'; -import { useDrawerOverlay_unstable } from '@fluentui/react-drawer'; -import { useDrawerOverlayStyles_unstable } from '@fluentui/react-drawer'; -import { useDynamicVirtualizerMeasure } from '@fluentui/react-virtualizer'; -import { useFlatTree_unstable } from '@fluentui/react-tree'; -import { useInfoButton_unstable } from '@fluentui/react-infobutton'; -import { useInfoButtonStyles_unstable } from '@fluentui/react-infobutton'; -import { useInfoLabel_unstable } from '@fluentui/react-infobutton'; -import { useInfoLabelStyles_unstable } from '@fluentui/react-infobutton'; -import { useIntersectionObserver } from '@fluentui/react-virtualizer'; -import { useResizeObserverRef_unstable } from '@fluentui/react-virtualizer'; -import { useStaticVirtualizerMeasure } from '@fluentui/react-virtualizer'; -import { useTree_unstable } from '@fluentui/react-tree'; -import { useTreeContext_unstable } from '@fluentui/react-tree'; -import { useTreeItem_unstable } from '@fluentui/react-tree'; -import { useTreeItemAside_unstable } from '@fluentui/react-tree'; -import { useTreeItemAsideStyles_unstable } from '@fluentui/react-tree'; -import { useTreeItemContext_unstable } from '@fluentui/react-tree'; -import { useTreeItemLayout_unstable } from '@fluentui/react-tree'; -import { useTreeItemLayoutStyles_unstable } from '@fluentui/react-tree'; -import { useTreeItemPersonaLayout_unstable } from '@fluentui/react-tree'; -import { useTreeItemPersonaLayoutStyles_unstable } from '@fluentui/react-tree'; -import { useTreeItemStyles_unstable } from '@fluentui/react-tree'; -import { useTreeStyles_unstable } from '@fluentui/react-tree'; -import { useVirtualizer_unstable } from '@fluentui/react-virtualizer'; -import { useVirtualizerContext_unstable } from '@fluentui/react-virtualizer'; -import { useVirtualizerScrollView_unstable } from '@fluentui/react-virtualizer'; -import { useVirtualizerScrollViewDynamic_unstable } from '@fluentui/react-virtualizer'; -import { useVirtualizerScrollViewDynamicStyles_unstable } from '@fluentui/react-virtualizer'; -import { useVirtualizerScrollViewStyles_unstable } from '@fluentui/react-virtualizer'; -import { useVirtualizerStyles_unstable } from '@fluentui/react-virtualizer'; -import { Virtualizer } from '@fluentui/react-virtualizer'; -import { VirtualizerChildRenderFunction } from '@fluentui/react-virtualizer'; -import { virtualizerClassNames } from '@fluentui/react-virtualizer'; -import { VirtualizerContextProps } from '@fluentui/react-virtualizer'; -import { VirtualizerContextProvider } from '@fluentui/react-virtualizer'; -import { VirtualizerMeasureDynamicProps } from '@fluentui/react-virtualizer'; -import { VirtualizerMeasureProps } from '@fluentui/react-virtualizer'; -import { VirtualizerProps } from '@fluentui/react-virtualizer'; -import { VirtualizerScrollView } from '@fluentui/react-virtualizer'; -import { virtualizerScrollViewClassNames } from '@fluentui/react-virtualizer'; -import { VirtualizerScrollViewDynamic } from '@fluentui/react-virtualizer'; -import { virtualizerScrollViewDynamicClassNames } from '@fluentui/react-virtualizer'; -import { VirtualizerScrollViewDynamicProps } from '@fluentui/react-virtualizer'; -import { VirtualizerScrollViewDynamicSlots } from '@fluentui/react-virtualizer'; -import { VirtualizerScrollViewDynamicState } from '@fluentui/react-virtualizer'; -import { VirtualizerScrollViewProps } from '@fluentui/react-virtualizer'; -import { VirtualizerScrollViewSlots } from '@fluentui/react-virtualizer'; -import { VirtualizerScrollViewState } from '@fluentui/react-virtualizer'; -import { VirtualizerSlots } from '@fluentui/react-virtualizer'; -import { VirtualizerState } from '@fluentui/react-virtualizer'; +/// -export { Alert } +import type { Dispatch } from 'react'; +import { FC } from 'react'; +import { MutableRefObject } from 'react'; +import { Provider } from 'react'; +import { ProviderProps } from 'react'; +import * as React_2 from 'react'; +import type { SetStateAction } from 'react'; -export { alertClassNames } +// @public +export const Alert: ForwardRefComponent; -export { AlertProps } +// @public (undocumented) +export const alertClassNames: SlotClassNames; -export { AlertSlots } +// @public +export type AlertProps = ComponentProps & { + intent?: 'info' | 'success' | 'error' | 'warning'; + appearance?: 'primary' | 'inverted'; +}; -export { AlertState } +// @public (undocumented) +export type AlertSlots = { + root: NonNullable>; + icon?: Slot<'span'>; + action?: Slot; + avatar?: Slot; +}; -export { Drawer } +// @public +export type AlertState = ComponentState & Pick & Required>; -export { DrawerBody } +// @public +export const Drawer: ForwardRefComponent; -export { drawerBodyClassNames } +// @public +export const DrawerBody: ForwardRefComponent; -export { DrawerBodySlots } +// @public (undocumented) +export const drawerBodyClassNames: SlotClassNames; -export { DrawerBodyState } +// @public (undocumented) +export type DrawerBodySlots = { + root: Slot<'div'>; +}; -export { DrawerFooter } +// @public +export type DrawerBodyState = ComponentState; -export { drawerFooterClassNames } +// @public +export const DrawerFooter: ForwardRefComponent; -export { DrawerFooterSlots } +// @public (undocumented) +export const drawerFooterClassNames: SlotClassNames; -export { DrawerFooterState } +// @public (undocumented) +export type DrawerFooterSlots = { + root: Slot<'footer'>; +}; -export { DrawerHeader } +// @public +export type DrawerFooterState = ComponentState; -export { drawerHeaderClassNames } +// @public +export const DrawerHeader: ForwardRefComponent; -export { DrawerHeaderNavigation } +// @public (undocumented) +export const drawerHeaderClassNames: SlotClassNames; -export { drawerHeaderNavigationClassNames } +// @public +export const DrawerHeaderNavigation: ForwardRefComponent; -export { DrawerHeaderNavigationProps } +// @public (undocumented) +export const drawerHeaderNavigationClassNames: SlotClassNames; -export { DrawerHeaderNavigationSlots } +// @public +export type DrawerHeaderNavigationProps = ComponentProps; -export { DrawerHeaderNavigationState } +// @public (undocumented) +export type DrawerHeaderNavigationSlots = { + root: Slot<'nav'>; +}; + +// @public +export type DrawerHeaderNavigationState = ComponentState; + +// @public (undocumented) +export type DrawerHeaderSlots = { + root: Slot<'header'>; +}; + +// @public +export type DrawerHeaderState = ComponentState; + +// @public +export const DrawerHeaderTitle: ForwardRefComponent; + +// @public (undocumented) +export const drawerHeaderTitleClassNames: SlotClassNames; + +// @public (undocumented) +export type DrawerHeaderTitleSlots = { + root: Slot<'div'>; + heading?: DialogTitleSlots['root']; + action?: DialogTitleSlots['action']; +}; + +// @public +export type DrawerHeaderTitleState = ComponentState; + +// @public +export const DrawerInline: ForwardRefComponent; + +// @public (undocumented) +export const drawerInlineClassNames: SlotClassNames; + +// @public +export type DrawerInlineProps = ComponentProps & DrawerBaseProps & { + separator?: boolean; +}; + +// @public (undocumented) +export type DrawerInlineSlots = { + root: Slot<'div'>; +}; + +// @public +export type DrawerInlineState = ComponentState & DrawerBaseProps & DrawerBaseState & Pick; -export { DrawerHeaderSlots } +// @public +export const DrawerOverlay: ForwardRefComponent; + +// @public (undocumented) +export const drawerOverlayClassNames: SlotClassNames; + +// @public +export type DrawerOverlayProps = ComponentProps & DrawerBaseProps & Pick; + +// @public (undocumented) +export type DrawerOverlaySlots = { + root: Slot; +}; + +// @public +export type DrawerOverlayState = ComponentState & DrawerBaseProps & DrawerBaseState & { + dialog: DialogProps; +}; + +// @public +export type DrawerProps = ComponentProps> & DrawerOverlayProps & DrawerInlineProps & { + type?: 'inline' | 'overlay'; +}; + +// @public (undocumented) +export type DrawerSlots = { + root: Slot; +}; + +// @public +export type DrawerState = ComponentState; + +// @public +export const flattenTree_unstable: >(items: NestedTreeItem[]) => FlattenedTreeItem[]; + +// @public +export type FlatTree = FlatTreeItemProps> = { + getTreeProps(): FlatTreeProps; + navigate(data: TreeNavigationData_unstable): void; + getNextNavigableItem(visibleItems: FlatTreeItem[], data: TreeNavigationData_unstable): FlatTreeItem | undefined; + items(): IterableIterator>; +}; -export { DrawerHeaderState } +// @public +export type FlatTreeItem = FlatTreeItemProps> = { + index: number; + level: number; + childrenSize: number; + value: Props['value']; + parentValue: Props['parentValue']; + ref: React_2.RefObject; + getTreeItemProps(): Required> & Omit; +}; -export { DrawerHeaderTitle } +// @public (undocumented) +export type FlatTreeItemProps = Omit, 'itemType'> & Partial, 'itemType'>> & { + value: Value; + parentValue?: Value; +}; -export { drawerHeaderTitleClassNames } +// @public (undocumented) +export type FlatTreeProps = Required, 'openItems' | 'onOpenChange' | 'onNavigation_unstable'>> & { + ref: React_2.Ref; + openItems: ImmutableSet; +}; -export { DrawerHeaderTitleSlots } +// @public +export const InfoButton: ForwardRefComponent; -export { DrawerHeaderTitleState } +// @public (undocumented) +export const infoButtonClassNames: SlotClassNames; -export { DrawerInline } +// @public +export type InfoButtonProps = Omit>, 'disabled'> & { + size?: 'small' | 'medium' | 'large'; +}; -export { drawerInlineClassNames } +// @public (undocumented) +export type InfoButtonSlots = { + root: NonNullable>; + popover: NonNullable>>; + info: NonNullable>; +}; + +// @public +export type InfoButtonState = ComponentState & Required>; + +// @public +export const InfoLabel: ForwardRefComponent; + +// @public (undocumented) +export const infoLabelClassNames: SlotClassNames; + +// @public +export type InfoLabelProps = ComponentProps, 'label'> & { + info?: InfoButtonProps['info']; +}; + +// @public (undocumented) +export type InfoLabelSlots = { + root: NonNullable>; + label: NonNullable>; + infoButton: Slot; +}; + +// @public +export type InfoLabelState = ComponentState & Pick; + +// @public (undocumented) +export type NestedTreeItem> = Omit & { + subtree?: NestedTreeItem[]; +}; + +// @public (undocumented) +export const renderAlert_unstable: (state: AlertState) => JSX.Element; + +// @public +export const renderDrawer_unstable: (state: DrawerState) => JSX.Element; + +// @public +export const renderDrawerBody_unstable: (state: DrawerBodyState) => JSX.Element; + +// @public +export const renderDrawerFooter_unstable: (state: DrawerFooterState) => JSX.Element; + +// @public +export const renderDrawerHeader_unstable: (state: DrawerHeaderState) => JSX.Element; + +// @public +export const renderDrawerHeaderNavigation_unstable: (state: DrawerHeaderNavigationState) => JSX.Element; + +// @public +export const renderDrawerHeaderTitle_unstable: (state: DrawerHeaderTitleState) => JSX.Element; + +// @public +export const renderDrawerInline_unstable: (state: DrawerInlineState) => JSX.Element | null; + +// @public +export const renderDrawerOverlay_unstable: (state: DrawerOverlayState) => JSX.Element; + +// @public +export const renderInfoButton_unstable: (state: InfoButtonState) => JSX.Element; + +// @public +export const renderInfoLabel_unstable: (state: InfoLabelState) => JSX.Element; + +// @public (undocumented) +export const renderTree_unstable: (state: TreeState, contextValues: TreeContextValues) => JSX.Element; + +// @public +export const renderTreeItem_unstable: (state: TreeItemState, contextValues: TreeItemContextValues) => JSX.Element; + +// @public +export const renderTreeItemAside_unstable: (state: TreeItemAsideState) => JSX.Element | null; + +// @public +export const renderTreeItemLayout_unstable: (state: TreeItemLayoutState) => JSX.Element; + +// @public +export const renderTreeItemPersonaLayout_unstable: (state: TreeItemPersonaLayoutState, contextValues: TreeItemPersonaLayoutContextValues) => JSX.Element; + +// @public (undocumented) +export const renderVirtualizer_unstable: (state: VirtualizerState) => JSX.Element; + +// @public (undocumented) +export const renderVirtualizerScrollView_unstable: (state: VirtualizerScrollViewState) => JSX.Element; + +// @public (undocumented) +export const renderVirtualizerScrollViewDynamic_unstable: (state: VirtualizerScrollViewDynamicState) => JSX.Element; + +// @public +export interface ResizeCallbackWithRef { + // (undocumented) + (entries: ResizeObserverEntry[], observer: ResizeObserver, scrollRef?: MutableRefObject): void; +} + +// @public +export const Tree: React_2.ForwardRefExoticComponent & Omit<{ + as?: "div" | undefined; +} & Pick, HTMLDivElement>, "key" | keyof React_2.HTMLAttributes> & { + ref?: ((instance: HTMLDivElement | null) => void) | React_2.RefObject | null | undefined; +} & { + children?: React_2.ReactNode | SlotRenderFunction, HTMLDivElement>, "key" | keyof React_2.HTMLAttributes> & { + ref?: ((instance: HTMLDivElement | null) => void) | React_2.RefObject | null | undefined; + }>; +}, "ref"> & { + appearance?: "transparent" | "subtle" | "subtle-alpha" | undefined; + size?: "small" | "medium" | undefined; + openItems?: Iterable | undefined; + defaultOpenItems?: Iterable | undefined; + onOpenChange?(event: React_2.KeyboardEvent | React_2.MouseEvent, data: TreeOpenChangeData): void; + onNavigation_unstable?(event: React_2.KeyboardEvent | React_2.MouseEvent, data: TreeNavigationData_unstable): void; +} & React_2.RefAttributes> & ((props: TreeProps) => JSX.Element); + +// @public (undocumented) +export const treeClassNames: SlotClassNames; + +// @public (undocumented) +export type TreeContextValue = { + level: number; + appearance: 'subtle' | 'subtle-alpha' | 'transparent'; + size: 'small' | 'medium'; + openItems: ImmutableSet; + requestTreeResponse(request: TreeItemRequest): void; +}; + +// @public +export const TreeItem: React_2.ForwardRefExoticComponent, "root"> & Omit<{ + as?: "div" | undefined; +} & Pick, HTMLDivElement>, "key" | keyof React_2.HTMLAttributes> & { + ref?: ((instance: HTMLDivElement | null) => void) | React_2.RefObject | null | undefined; +} & { + children?: React_2.ReactNode | SlotRenderFunction, HTMLDivElement>, "key" | keyof React_2.HTMLAttributes> & { + ref?: ((instance: HTMLDivElement | null) => void) | React_2.RefObject | null | undefined; + }>; +} & { + style?: TreeItemCSSProperties | undefined; +}, "ref"> & { + value?: string | undefined; + itemType: TreeItemType; +} & React_2.RefAttributes> & ((props: TreeItemProps) => JSX.Element); + +// @public +export const TreeItemAside: ForwardRefComponent; + +// @public (undocumented) +export const treeItemAsideClassNames: SlotClassNames; + +// @public +export type TreeItemAsideProps = ComponentProps & { + actions?: boolean; + visible?: true; +}; + +// @public (undocumented) +export type TreeItemAsideSlots = { + root: Slot<'div'>; +}; + +// @public +export type TreeItemAsideState = ComponentState & { + actions: boolean; + visible: boolean; + buttonContextValue: ButtonContextValue; +}; + +// @public (undocumented) +export const treeItemClassNames: SlotClassNames; + +// @public +export const TreeItemLayout: ForwardRefComponent; + +// @public (undocumented) +export const treeItemLayoutClassNames: SlotClassNames; + +// @public +export type TreeItemLayoutProps = ComponentProps>; + +// @public (undocumented) +export type TreeItemLayoutSlots = { + root: Slot<'div'>; + expandIcon?: Slot<'div'>; + iconBefore?: Slot<'div'>; + iconAfter?: Slot<'div'>; +}; + +// @public +export type TreeItemLayoutState = ComponentState; + +// @public (undocumented) +export const treeItemLevelToken: "--fluent-TreeItem--level"; + +// @public +export const TreeItemPersonaLayout: ForwardRefComponent; + +// @public (undocumented) +export const treeItemPersonaLayoutClassNames: SlotClassNames; + +// @public +export type TreeItemPersonaLayoutProps = ComponentProps>; + +// @public (undocumented) +export type TreeItemPersonaLayoutSlots = { + root: NonNullable>; + expandIcon?: Slot<'div'>; + media: NonNullable>; + main: NonNullable>; + description?: Slot<'div'>; + content: NonNullable>; +}; + +// @public +export type TreeItemPersonaLayoutState = ComponentState & { + avatarSize: AvatarSize; +}; + +// @public +export type TreeItemProps = ComponentProps> & { + value?: Value; + itemType: TreeItemType; +}; + +// @public (undocumented) +export const TreeItemProvider: React_2.Provider & React_2.FC>; + +// @public (undocumented) +export type TreeItemSlots = { + root: Slot & { + style?: TreeItemCSSProperties; + }>>; +}; + +// @public +export type TreeItemState = ComponentState & TreeItemContextValue & { + level: number; + itemType: TreeItemType; +}; + +// @public (undocumented) +export type TreeNavigationData_unstable = { + value: Value; + target: HTMLElement; +} & ({ + event: React_2.MouseEvent; + type: 'Click'; +} | { + event: React_2.KeyboardEvent; + type: 'TypeAhead'; +} | { + event: React_2.KeyboardEvent; + type: typeof ArrowRight; +} | { + event: React_2.KeyboardEvent; + type: typeof ArrowLeft; +} | { + event: React_2.KeyboardEvent; + type: typeof ArrowUp; +} | { + event: React_2.KeyboardEvent; + type: typeof ArrowDown; +} | { + event: React_2.KeyboardEvent; + type: typeof Home; +} | { + event: React_2.KeyboardEvent; + type: typeof End; +}); + +// @public (undocumented) +export type TreeNavigationEvent_unstable = TreeNavigationData_unstable['event']; + +// @public (undocumented) +export type TreeOpenChangeData = { + open: boolean; + value: Value; +} & ({ + event: React_2.MouseEvent; + target: HTMLElement; + type: 'ExpandIconClick'; +} | { + event: React_2.MouseEvent; + target: HTMLElement; + type: 'Click'; +} | { + event: React_2.KeyboardEvent; + target: HTMLElement; + type: typeof Enter; +} | { + event: React_2.KeyboardEvent; + target: HTMLElement; + type: typeof ArrowRight; +} | { + event: React_2.KeyboardEvent; + target: HTMLElement; + type: typeof ArrowLeft; +}); + +// @public (undocumented) +export type TreeOpenChangeEvent = TreeOpenChangeData['event']; + +// @public (undocumented) +export type TreeProps = ComponentProps & { + appearance?: 'subtle' | 'subtle-alpha' | 'transparent'; + size?: 'small' | 'medium'; + openItems?: Iterable; + defaultOpenItems?: Iterable; + onOpenChange?(event: TreeOpenChangeEvent, data: TreeOpenChangeData): void; + onNavigation_unstable?(event: TreeNavigationEvent_unstable, data: TreeNavigationData_unstable): void; +}; + +// @public (undocumented) +export const TreeProvider: Provider & FC>; + +// @public (undocumented) +export type TreeSlots = { + root: Slot<'div'>; +}; + +// @public +export type TreeState = ComponentState & TreeContextValue & { + open: boolean; +}; + +// @public +export const useAlert_unstable: (props: AlertProps, ref: React_2.Ref) => AlertState; + +// @public +export const useAlertStyles_unstable: (state: AlertState) => AlertState; + +// @public +export const useDrawer_unstable: (props: DrawerProps, ref: React_2.Ref) => DrawerState; + +// @public +export const useDrawerBody_unstable: (props: DrawerBodyProps, ref: React_2.Ref) => DrawerBodyState; + +// @public +export const useDrawerBodyStyles_unstable: (state: DrawerBodyState) => DrawerBodyState; + +// @public +export const useDrawerFooter_unstable: (props: DrawerFooterProps, ref: React_2.Ref) => DrawerFooterState; + +// @public +export const useDrawerFooterStyles_unstable: (state: DrawerFooterState) => DrawerFooterState; + +// @public +export const useDrawerHeader_unstable: (props: DrawerHeaderProps, ref: React_2.Ref) => DrawerHeaderState; + +// @public +export const useDrawerHeaderNavigation_unstable: (props: DrawerHeaderNavigationProps, ref: React_2.Ref) => DrawerHeaderNavigationState; + +// @public +export const useDrawerHeaderNavigationStyles_unstable: (state: DrawerHeaderNavigationState) => DrawerHeaderNavigationState; + +// @public +export const useDrawerHeaderStyles_unstable: (state: DrawerHeaderState) => DrawerHeaderState; + +// @public +export const useDrawerHeaderTitle_unstable: (props: DrawerHeaderTitleProps, ref: React_2.Ref) => DrawerHeaderTitleState; + +// @public +export const useDrawerHeaderTitleStyles_unstable: (state: DrawerHeaderTitleState) => DrawerHeaderTitleState; + +// @public +export const useDrawerInline_unstable: (props: DrawerInlineProps, ref: React_2.Ref) => DrawerInlineState; -export { DrawerInlineProps } +// @public +export const useDrawerInlineStyles_unstable: (state: DrawerInlineState) => DrawerInlineState; -export { DrawerInlineSlots } +// @public +export const useDrawerOverlay_unstable: (props: DrawerOverlayProps, ref: React_2.Ref) => DrawerOverlayState; -export { DrawerInlineState } +// @public +export const useDrawerOverlayStyles_unstable: (state: DrawerOverlayState) => DrawerOverlayState; -export { DrawerOverlay } +// @public +export const useDynamicVirtualizerMeasure: (virtualizerProps: VirtualizerMeasureDynamicProps) => { + virtualizerLength: number; + bufferItems: number; + bufferSize: number; + scrollRef: (instance: TElement | null) => void; +}; -export { drawerOverlayClassNames } +// @public +export function useFlatTree_unstable = FlatTreeItemProps>(flatTreeItemProps: Props[], options?: FlatTreeOptions): FlatTree; -export { DrawerOverlayProps } +// @public +export const useInfoButton_unstable: (props: InfoButtonProps, ref: React_2.Ref) => InfoButtonState; -export { DrawerOverlaySlots } +// @public +export const useInfoButtonStyles_unstable: (state: InfoButtonState) => InfoButtonState; -export { DrawerOverlayState } +// @public +export const useInfoLabel_unstable: (props: InfoLabelProps, ref: React_2.Ref) => InfoLabelState; -export { DrawerProps } +// @public +export const useInfoLabelStyles_unstable: (state: InfoLabelState) => InfoLabelState; -export { DrawerSlots } +// @public +export const useIntersectionObserver: (callback: IntersectionObserverCallback, options?: IntersectionObserverInit | undefined) => { + setObserverList: Dispatch>; + setObserverInit: Dispatch>; + observer: MutableRefObject; +}; -export { DrawerState } +// @public +export const useResizeObserverRef_unstable: (resizeCallback: ResizeCallbackWithRef) => (instance: HTMLElement | HTMLDivElement | null) => void; -export { flattenTree_unstable } +// @public +export const useStaticVirtualizerMeasure: (virtualizerProps: VirtualizerMeasureProps) => { + virtualizerLength: number; + bufferItems: number; + bufferSize: number; + scrollRef: (instance: TElement | null) => void; +}; -export { FlatTree } +// @public +export const useTree_unstable: (props: TreeProps, ref: React_2.Ref) => TreeState; -export { FlatTreeItem } +// @public (undocumented) +export const useTreeContext_unstable: (selector: ContextSelector) => T; -export { FlatTreeItemProps } +// @public +export function useTreeItem_unstable(props: TreeItemProps, ref: React_2.Ref): TreeItemState; -export { FlatTreeProps } +// @public +export const useTreeItemAside_unstable: (props: TreeItemAsideProps, ref: React_2.Ref) => TreeItemAsideState; -export { InfoButton } +// @public +export const useTreeItemAsideStyles_unstable: (state: TreeItemAsideState) => TreeItemAsideState; -export { infoButtonClassNames } +// @public (undocumented) +export const useTreeItemContext_unstable: (selector: ContextSelector) => T; -export { InfoButtonProps } +// @public +export const useTreeItemLayout_unstable: (props: TreeItemLayoutProps, ref: React_2.Ref) => TreeItemLayoutState; -export { InfoButtonSlots } +// @public +export const useTreeItemLayoutStyles_unstable: (state: TreeItemLayoutState) => TreeItemLayoutState; -export { InfoButtonState } +// @public +export const useTreeItemPersonaLayout_unstable: (props: TreeItemPersonaLayoutProps, ref: React_2.Ref) => TreeItemPersonaLayoutState; -export { InfoLabel } +// @public +export const useTreeItemPersonaLayoutStyles_unstable: (state: TreeItemPersonaLayoutState) => TreeItemPersonaLayoutState; -export { infoLabelClassNames } +// @public +export const useTreeItemStyles_unstable: (state: TreeItemState) => TreeItemState; -export { InfoLabelProps } +// @public (undocumented) +export const useTreeStyles_unstable: (state: TreeState) => TreeState; -export { InfoLabelSlots } +// @public (undocumented) +export function useVirtualizer_unstable(props: VirtualizerProps): VirtualizerState; -export { InfoLabelState } +// @public (undocumented) +export const useVirtualizerContext_unstable: () => VirtualizerContextProps; -export { NestedTreeItem } +// @public (undocumented) +export function useVirtualizerScrollView_unstable(props: VirtualizerScrollViewProps): VirtualizerScrollViewState; -export { renderAlert_unstable } +// @public (undocumented) +export function useVirtualizerScrollViewDynamic_unstable(props: VirtualizerScrollViewDynamicProps): VirtualizerScrollViewDynamicState; -export { renderDrawer_unstable } +// @public +export const useVirtualizerScrollViewDynamicStyles_unstable: (state: VirtualizerScrollViewDynamicState) => VirtualizerScrollViewDynamicState; -export { renderDrawerBody_unstable } +// @public +export const useVirtualizerScrollViewStyles_unstable: (state: VirtualizerScrollViewState) => VirtualizerScrollViewState; -export { renderDrawerFooter_unstable } +// @public +export const useVirtualizerStyles_unstable: (state: VirtualizerState) => VirtualizerState; -export { renderDrawerHeader_unstable } +// @public +export const Virtualizer: FC; -export { renderDrawerHeaderNavigation_unstable } +// @public (undocumented) +export type VirtualizerChildRenderFunction = (index: number) => React_2.ReactNode; -export { renderDrawerHeaderTitle_unstable } +// @public (undocumented) +export const virtualizerClassNames: SlotClassNames; -export { renderDrawerInline_unstable } +// @public (undocumented) +export type VirtualizerContextProps = { + contextIndex: number; + setContextIndex: (index: number) => void; +}; -export { renderDrawerOverlay_unstable } +// @public (undocumented) +export const VirtualizerContextProvider: React_2.Provider; -export { renderInfoButton_unstable } +// @public (undocumented) +export type VirtualizerMeasureDynamicProps = { + defaultItemSize: number; + currentIndex: number; + numItems: number; + getItemSize: (index: number) => number; + direction?: 'vertical' | 'horizontal'; +}; -export { renderInfoLabel_unstable } +// @public (undocumented) +export type VirtualizerMeasureProps = { + defaultItemSize: number; + direction?: 'vertical' | 'horizontal'; +}; -export { renderTree_unstable } +// @public (undocumented) +export type VirtualizerProps = ComponentProps> & VirtualizerConfigProps; -export { renderTreeItem_unstable } +// @public +export const VirtualizerScrollView: React_2.FC; -export { renderTreeItemAside_unstable } +// @public (undocumented) +export const virtualizerScrollViewClassNames: SlotClassNames; -export { renderTreeItemLayout_unstable } +// @public +export const VirtualizerScrollViewDynamic: React_2.FC; -export { renderTreeItemPersonaLayout_unstable } +// @public (undocumented) +export const virtualizerScrollViewDynamicClassNames: SlotClassNames; -export { renderVirtualizer_unstable } +// @public (undocumented) +export type VirtualizerScrollViewDynamicProps = ComponentProps> & Partial> & { + itemSize: number; + getItemSize: (index: number) => number; + numItems: number; + children: VirtualizerChildRenderFunction; +}; -export { renderVirtualizerScrollView_unstable } +// @public (undocumented) +export type VirtualizerScrollViewDynamicSlots = VirtualizerScrollViewSlots; -export { renderVirtualizerScrollViewDynamic_unstable } +// @public (undocumented) +export type VirtualizerScrollViewDynamicState = ComponentState & VirtualizerConfigState; -export { ResizeCallbackWithRef } +// @public (undocumented) +export type VirtualizerScrollViewProps = ComponentProps> & Partial> & { + itemSize: number; + numItems: number; + children: VirtualizerChildRenderFunction; +}; -export { Tree } +// @public (undocumented) +export type VirtualizerScrollViewSlots = VirtualizerSlots & { + container: NonNullable>; +}; -export { treeClassNames } +// @public (undocumented) +export type VirtualizerScrollViewState = ComponentState & VirtualizerConfigState; -export { TreeContextValue } +// @public (undocumented) +export type VirtualizerSlots = { + before: NonNullable>; + beforeContainer: NonNullable>; + after: NonNullable>; + afterContainer: NonNullable>; +}; -export { TreeItem } - -export { TreeItemAside } - -export { treeItemAsideClassNames } - -export { TreeItemAsideProps } - -export { TreeItemAsideSlots } - -export { TreeItemAsideState } - -export { treeItemClassNames } - -export { TreeItemLayout } - -export { treeItemLayoutClassNames } - -export { TreeItemLayoutProps } - -export { TreeItemLayoutSlots } - -export { TreeItemLayoutState } - -export { treeItemLevelToken } - -export { TreeItemPersonaLayout } - -export { treeItemPersonaLayoutClassNames } - -export { TreeItemPersonaLayoutProps } - -export { TreeItemPersonaLayoutSlots } - -export { TreeItemPersonaLayoutState } - -export { TreeItemProps } - -export { TreeItemProvider } - -export { TreeItemSlots } - -export { TreeItemState } - -export { TreeNavigationData_unstable } - -export { TreeNavigationEvent_unstable } - -export { TreeOpenChangeData } - -export { TreeOpenChangeEvent } - -export { TreeProps } - -export { TreeProvider } - -export { TreeSlots } - -export { TreeState } - -export { useAlert_unstable } - -export { useAlertStyles_unstable } - -export { useDrawer_unstable } - -export { useDrawerBody_unstable } - -export { useDrawerBodyStyles_unstable } - -export { useDrawerFooter_unstable } - -export { useDrawerFooterStyles_unstable } - -export { useDrawerHeader_unstable } - -export { useDrawerHeaderNavigation_unstable } - -export { useDrawerHeaderNavigationStyles_unstable } - -export { useDrawerHeaderStyles_unstable } - -export { useDrawerHeaderTitle_unstable } - -export { useDrawerHeaderTitleStyles_unstable } - -export { useDrawerInline_unstable } - -export { useDrawerInlineStyles_unstable } - -export { useDrawerOverlay_unstable } - -export { useDrawerOverlayStyles_unstable } - -export { useDynamicVirtualizerMeasure } - -export { useFlatTree_unstable } - -export { useInfoButton_unstable } - -export { useInfoButtonStyles_unstable } - -export { useInfoLabel_unstable } - -export { useInfoLabelStyles_unstable } - -export { useIntersectionObserver } - -export { useResizeObserverRef_unstable } - -export { useStaticVirtualizerMeasure } - -export { useTree_unstable } - -export { useTreeContext_unstable } - -export { useTreeItem_unstable } - -export { useTreeItemAside_unstable } - -export { useTreeItemAsideStyles_unstable } - -export { useTreeItemContext_unstable } - -export { useTreeItemLayout_unstable } - -export { useTreeItemLayoutStyles_unstable } - -export { useTreeItemPersonaLayout_unstable } - -export { useTreeItemPersonaLayoutStyles_unstable } - -export { useTreeItemStyles_unstable } - -export { useTreeStyles_unstable } - -export { useVirtualizer_unstable } - -export { useVirtualizerContext_unstable } - -export { useVirtualizerScrollView_unstable } - -export { useVirtualizerScrollViewDynamic_unstable } - -export { useVirtualizerScrollViewDynamicStyles_unstable } - -export { useVirtualizerScrollViewStyles_unstable } - -export { useVirtualizerStyles_unstable } - -export { Virtualizer } - -export { VirtualizerChildRenderFunction } - -export { virtualizerClassNames } - -export { VirtualizerContextProps } - -export { VirtualizerContextProvider } - -export { VirtualizerMeasureDynamicProps } - -export { VirtualizerMeasureProps } - -export { VirtualizerProps } - -export { VirtualizerScrollView } - -export { virtualizerScrollViewClassNames } - -export { VirtualizerScrollViewDynamic } - -export { virtualizerScrollViewDynamicClassNames } - -export { VirtualizerScrollViewDynamicProps } - -export { VirtualizerScrollViewDynamicSlots } - -export { VirtualizerScrollViewDynamicState } - -export { VirtualizerScrollViewProps } - -export { VirtualizerScrollViewSlots } - -export { VirtualizerScrollViewState } - -export { VirtualizerSlots } - -export { VirtualizerState } +// @public (undocumented) +export type VirtualizerState = ComponentState & VirtualizerConfigState; // (No @packageDocumentation comment for this package) diff --git a/packages/react-components/react-components/src/index.ts b/packages/react-components/react-components/src/index.ts index e1a462d7e1d6f2..cf2f5572c37142 100644 --- a/packages/react-components/react-components/src/index.ts +++ b/packages/react-components/react-components/src/index.ts @@ -101,6 +101,7 @@ export { useIsSSR, useMergedRefs, useScrollbarWidth, + useTransitionPresence, } from '@fluentui/react-utilities'; export type { ComponentProps, @@ -112,6 +113,8 @@ export type { SlotClassNames, SlotPropsRecord, SlotRenderFunction, + UseTransitionPresenceEvents, + UseTransitionPresenceState, } from '@fluentui/react-utilities'; // Components diff --git a/packages/react-components/react-drawer/etc/react-drawer.api.md b/packages/react-components/react-drawer/etc/react-drawer.api.md index 4ccfedbbbfe861..5c82af50b4933f 100644 --- a/packages/react-components/react-drawer/etc/react-drawer.api.md +++ b/packages/react-components/react-drawer/etc/react-drawer.api.md @@ -220,25 +220,6 @@ export const useDrawerOverlay_unstable: (props: DrawerOverlayProps, ref: React_2 // @public export const useDrawerOverlayStyles_unstable: (state: DrawerOverlayState) => DrawerOverlayState; -// @public -export const useTransitionPresence: (present: boolean, events?: UseTransitionPresenceEvents | undefined) => UseTransitionPresenceState; - -// @public -export type UseTransitionPresenceEvents = { - onEntered?: () => void; - onExited?: () => void; -}; - -// @public -export type UseTransitionPresenceState = { - ref: React_2.RefCallback; - shouldRender: boolean; - visible: boolean; - entering: boolean; - exiting: boolean; - animating: boolean; -}; - // (No @packageDocumentation comment for this package) ``` diff --git a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts index 75cc2742f8704f..df619bd69495e1 100644 --- a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts +++ b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts @@ -1,8 +1,12 @@ import * as React from 'react'; -import { getNativeElementProps, useControllableState, useMergedRefs } from '@fluentui/react-utilities'; +import { + getNativeElementProps, + useControllableState, + useMergedRefs, + useTransitionPresence, +} from '@fluentui/react-utilities'; import type { DrawerInlineProps, DrawerInlineState } from './DrawerInline.types'; import { getDefaultDrawerProps } from '../../util/getDefaultDrawerProps'; -import { useTransitionPresence } from '../../hooks/useTransitionPresence'; /** * Create the state required to render DrawerInline. diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/DrawerOverlay.types.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/DrawerOverlay.types.ts index 3390e2eac2e212..ede828f725d2ed 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/DrawerOverlay.types.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/DrawerOverlay.types.ts @@ -1,5 +1,5 @@ import { DialogProps, DialogSurfaceProps } from '@fluentui/react-dialog'; -import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities'; +import type { ComponentProps, ComponentState, Slot, UseTransitionPresenceState } from '@fluentui/react-utilities'; import { DrawerBaseProps, DrawerBaseState } from '../../util/DrawerBase.types'; export type DrawerOverlaySlots = { @@ -20,4 +20,5 @@ export type DrawerOverlayState = ComponentState & DrawerBaseProps & DrawerBaseState & { dialog: DialogProps; + backdropPresence: UseTransitionPresenceState; }; diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts index 33599b90c1d789..ca8499b87b0440 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts @@ -1,9 +1,13 @@ import * as React from 'react'; -import { getNativeElementProps, useMergedRefs } from '@fluentui/react-utilities'; +import { + getNativeElementProps, + resolveShorthand, + useMergedRefs, + useTransitionPresence, +} from '@fluentui/react-utilities'; import type { DrawerOverlayProps, DrawerOverlayState } from './DrawerOverlay.types'; -import { DialogProps, DialogSurface } from '@fluentui/react-dialog'; +import { DialogProps, DialogSurface, DialogSurfaceProps } from '@fluentui/react-dialog'; import { getDefaultDrawerProps } from '../../util/getDefaultDrawerProps'; -import { useTransitionPresence } from '../../hooks/useTransitionPresence'; /** * Create the state required to render DrawerOverlay. @@ -22,15 +26,30 @@ export const useDrawerOverlay_unstable = ( const { modalType = 'modal', inertTrapFocus, onOpenChange } = props; const { ref: drawerRef, shouldRender, visible, entering, exiting } = useTransitionPresence(open); + const backdropPresence = useTransitionPresence(open); + + const backdropProps = React.useMemo(() => { + if (backdropPresence.shouldRender) { + return { + ref: backdropPresence.ref, + }; + } + + return null; + }, [backdropPresence.ref, backdropPresence.shouldRender]); return { components: { root: DialogSurface, }, - root: getNativeElementProps('div', { - ref: useMergedRefs(ref, drawerRef), - ...props, + root: resolveShorthand(getNativeElementProps('div', {}), { + required: true, + defaultProps: { + ...props, + ref: useMergedRefs(ref, drawerRef), + backdrop: backdropProps, + } as DialogSurfaceProps, }), dialog: { open: shouldRender, @@ -46,5 +65,6 @@ export const useDrawerOverlay_unstable = ( visible, entering, exiting, + backdropPresence, }; }; diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts index aee81d5113ea20..81155522f516b9 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts @@ -2,6 +2,8 @@ import { makeStyles, mergeClasses } from '@griffel/react'; import type { DrawerOverlaySlots, DrawerOverlayState } from './DrawerOverlay.types'; import type { SlotClassNames } from '@fluentui/react-utilities'; import { getDrawerBaseClassNames, useDrawerBaseStyles } from '../../util/useDrawerBaseStyles.styles'; +import { tokens } from '@fluentui/react-theme'; +import { HTMLAttributes } from 'react'; export const drawerOverlayClassNames: SlotClassNames = { root: 'fui-DrawerOverlay', @@ -31,6 +33,21 @@ const useStyles = makeStyles({ visible: { transform: 'translate3D(0, 0, 0)', }, + + /* Backdrop */ + backdrop: { + opacity: 0, + transitionProperty: 'opacity', + transitionDuration: tokens.durationNormal, + transitionTimingFunction: tokens.curveEasyEase, + willChange: 'opacity', + '@media screen and (prefers-reduced-motion: reduce)': { + transitionDuration: '0.01ms', + }, + }, + backdropVisible: { + opacity: 1, + }, }); /** @@ -50,5 +67,15 @@ export const useDrawerOverlayStyles_unstable = (state: DrawerOverlayState): Draw state.root.className, ); + const backdrop = state.root.backdrop as HTMLAttributes; + + if (backdrop) { + backdrop.className = mergeClasses( + backdrop.className, + styles.backdrop, + state.backdropPresence.visible && styles.backdropVisible, + ); + } + return state; }; diff --git a/packages/react-components/react-drawer/src/index.ts b/packages/react-components/react-drawer/src/index.ts index 77bf548eac10de..d7ea7456d8d59a 100644 --- a/packages/react-components/react-drawer/src/index.ts +++ b/packages/react-components/react-drawer/src/index.ts @@ -67,6 +67,3 @@ export { useDrawerFooter_unstable, } from './DrawerFooter'; export type { DrawerFooterProps, DrawerFooterSlots, DrawerFooterState } from './DrawerFooter'; - -export { useTransitionPresence } from './hooks/useTransitionPresence'; -export type { UseTransitionPresenceEvents, UseTransitionPresenceState } from './hooks/useTransitionPresence'; diff --git a/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts b/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts index bb9f4d4e156a1f..a9970f9fc3013d 100644 --- a/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts +++ b/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts @@ -29,6 +29,9 @@ export const useDrawerBaseStyles = makeStyles({ justifyContent: 'flex-start', backgroundColor: tokens.colorNeutralBackground1, transitionDuration: tokens.durationNormal, + '@media screen and (prefers-reduced-motion: reduce)': { + transitionDuration: '0.01ms', + }, }, /* Positioning */ diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx index a59e7d17b59837..eb9b3c3288a3d4 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx @@ -1,6 +1,13 @@ import * as React from 'react'; -import { Drawer, DrawerBody, DrawerHeader, DrawerHeaderTitle, useTransitionPresence } from '@fluentui/react-drawer'; -import { Button, makeStyles, mergeClasses, shorthands, tokens } from '@fluentui/react-components'; +import { Drawer, DrawerBody, DrawerHeader, DrawerHeaderTitle } from '@fluentui/react-drawer'; +import { + Button, + makeStyles, + mergeClasses, + shorthands, + tokens, + useTransitionPresence, +} from '@fluentui/react-components'; import { Dismiss24Regular } from '@fluentui/react-icons'; const useStyles = makeStyles({ diff --git a/packages/react-components/react-utilities/etc/react-utilities.api.md b/packages/react-components/react-utilities/etc/react-utilities.api.md index 1c29e73b0cddcd..f72f88280df99f 100644 --- a/packages/react-components/react-utilities/etc/react-utilities.api.md +++ b/packages/react-components/react-utilities/etc/react-utilities.api.md @@ -267,6 +267,25 @@ export function useScrollbarWidth(options: UseScrollbarWidthOptions): number | u // @internal export function useTimeout(): readonly [(fn: () => void, delay: number) => void, () => void]; +// @public +export const useTransitionPresence: (present: boolean, events?: UseTransitionPresenceEvents | undefined) => UseTransitionPresenceState; + +// @public +export type UseTransitionPresenceEvents = { + onEntered?: () => void; + onExited?: () => void; +}; + +// @public +export type UseTransitionPresenceState = { + ref: React_2.RefCallback; + shouldRender: boolean; + visible: boolean; + entering: boolean; + exiting: boolean; + animating: boolean; +}; + // (No @packageDocumentation comment for this package) ``` diff --git a/packages/react-components/react-utilities/src/hooks/index.ts b/packages/react-components/react-utilities/src/hooks/index.ts index e5928f400aa78f..3626c493e41dc3 100644 --- a/packages/react-components/react-utilities/src/hooks/index.ts +++ b/packages/react-components/react-utilities/src/hooks/index.ts @@ -10,3 +10,4 @@ export * from './useOnScrollOutside'; export * from './usePrevious'; export * from './useScrollbarWidth'; export * from './useTimeout'; +export * from './useTransitionPresence'; diff --git a/packages/react-components/react-drawer/src/hooks/useTransitionPresence.ts b/packages/react-components/react-utilities/src/hooks/useTransitionPresence.ts similarity index 100% rename from packages/react-components/react-drawer/src/hooks/useTransitionPresence.ts rename to packages/react-components/react-utilities/src/hooks/useTransitionPresence.ts diff --git a/packages/react-components/react-utilities/src/index.ts b/packages/react-components/react-utilities/src/index.ts index a9a94d94460dbb..26b288f021a3bd 100644 --- a/packages/react-components/react-utilities/src/index.ts +++ b/packages/react-components/react-utilities/src/index.ts @@ -36,8 +36,15 @@ export { usePrevious, useScrollbarWidth, useTimeout, + useTransitionPresence, +} from './hooks/index'; +export type { + RefObjectFunction, + UseControllableStateOptions, + UseOnClickOrScrollOutsideOptions, + UseTransitionPresenceEvents, + UseTransitionPresenceState, } from './hooks/index'; -export type { RefObjectFunction, UseControllableStateOptions, UseOnClickOrScrollOutsideOptions } from './hooks/index'; export { canUseDOM, useIsSSR, SSRProvider } from './ssr/index'; From 893747557539549ba9d9a0107853ada5a326df57 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Tue, 6 Jun 2023 17:51:31 +0200 Subject: [PATCH 025/111] fix: add missing changefiles --- ...ct-components-05ea9e2c-59c4-4b95-8d7a-e6fc6ee1bb5c.json | 7 +++++++ ...act-utilities-02937573-2ff4-49a6-8fec-9dbc38b1644a.json | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 change/@fluentui-react-components-05ea9e2c-59c4-4b95-8d7a-e6fc6ee1bb5c.json create mode 100644 change/@fluentui-react-utilities-02937573-2ff4-49a6-8fec-9dbc38b1644a.json diff --git a/change/@fluentui-react-components-05ea9e2c-59c4-4b95-8d7a-e6fc6ee1bb5c.json b/change/@fluentui-react-components-05ea9e2c-59c4-4b95-8d7a-e6fc6ee1bb5c.json new file mode 100644 index 00000000000000..b02aef9624bb03 --- /dev/null +++ b/change/@fluentui-react-components-05ea9e2c-59c4-4b95-8d7a-e6fc6ee1bb5c.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "feat: make useTransitionPresence hook", + "packageName": "@fluentui/react-components", + "email": "marcosvmmoura@gmail.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-utilities-02937573-2ff4-49a6-8fec-9dbc38b1644a.json b/change/@fluentui-react-utilities-02937573-2ff4-49a6-8fec-9dbc38b1644a.json new file mode 100644 index 00000000000000..e6efe516a9c2e0 --- /dev/null +++ b/change/@fluentui-react-utilities-02937573-2ff4-49a6-8fec-9dbc38b1644a.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "feat: make useTransitionPresence hook", + "packageName": "@fluentui/react-utilities", + "email": "marcosvmmoura@gmail.com", + "dependentChangeType": "patch" +} From b9db84b5d1a28c6367949140af07fca25b8f4f31 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Tue, 6 Jun 2023 20:13:09 +0200 Subject: [PATCH 026/111] fix: expose fewer data to state --- .../react-components/react-drawer/etc/react-drawer.api.md | 1 + .../src/components/DrawerOverlay/DrawerOverlay.types.ts | 4 ++-- .../src/components/DrawerOverlay/useDrawerOverlay.ts | 2 +- .../components/DrawerOverlay/useDrawerOverlayStyles.styles.ts | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/react-components/react-drawer/etc/react-drawer.api.md b/packages/react-components/react-drawer/etc/react-drawer.api.md index 5c82af50b4933f..21fe3f7f6a4140 100644 --- a/packages/react-components/react-drawer/etc/react-drawer.api.md +++ b/packages/react-components/react-drawer/etc/react-drawer.api.md @@ -136,6 +136,7 @@ export type DrawerOverlaySlots = { // @public export type DrawerOverlayState = ComponentState & DrawerBaseProps & DrawerBaseState & { dialog: DialogProps; + backdropVisible: boolean; }; // @public diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/DrawerOverlay.types.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/DrawerOverlay.types.ts index ede828f725d2ed..9411e332ee8142 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/DrawerOverlay.types.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/DrawerOverlay.types.ts @@ -1,5 +1,5 @@ import { DialogProps, DialogSurfaceProps } from '@fluentui/react-dialog'; -import type { ComponentProps, ComponentState, Slot, UseTransitionPresenceState } from '@fluentui/react-utilities'; +import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities'; import { DrawerBaseProps, DrawerBaseState } from '../../util/DrawerBase.types'; export type DrawerOverlaySlots = { @@ -20,5 +20,5 @@ export type DrawerOverlayState = ComponentState & DrawerBaseProps & DrawerBaseState & { dialog: DialogProps; - backdropPresence: UseTransitionPresenceState; + backdropVisible: boolean; }; diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts index ca8499b87b0440..04042ca57cbda2 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts @@ -65,6 +65,6 @@ export const useDrawerOverlay_unstable = ( visible, entering, exiting, - backdropPresence, + backdropVisible: backdropPresence.visible, }; }; diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts index 81155522f516b9..1315c76afd83e6 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts @@ -73,7 +73,7 @@ export const useDrawerOverlayStyles_unstable = (state: DrawerOverlayState): Draw backdrop.className = mergeClasses( backdrop.className, styles.backdrop, - state.backdropPresence.visible && styles.backdropVisible, + state.backdropVisible && styles.backdropVisible, ); } From 89441026ae1b44d0c0ec42d4de2dcdffe1fdd40d Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Wed, 7 Jun 2023 11:30:57 +0200 Subject: [PATCH 027/111] fix: rename variable --- .../react-utilities/src/hooks/useTransitionPresence.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-components/react-utilities/src/hooks/useTransitionPresence.ts b/packages/react-components/react-utilities/src/hooks/useTransitionPresence.ts index 557a7fd6c22f47..c2f530550fe7bc 100644 --- a/packages/react-components/react-utilities/src/hooks/useTransitionPresence.ts +++ b/packages/react-components/react-utilities/src/hooks/useTransitionPresence.ts @@ -95,7 +95,7 @@ function toMs(s: string): number { * @param durations - List of CSS durations * @returns Maximum duration */ -const getMaxCssDuration = (durations: string[]) => { +const getMaxCSSDuration = (durations: string[]) => { return Math.max(...durations.map(d => toMs(d.trim()))); }; @@ -112,7 +112,7 @@ const getTransitionInfo = (computedStyle: CSSStyleDeclaration) => { const durations = getProp('transition-duration'); const delays = getProp('transition-delay'); - const totalDuration = getMaxCssDuration(durations) + getMaxCssDuration(delays); + const totalDuration = getMaxCSSDuration(durations) + getMaxCSSDuration(delays); return { duration: totalDuration, From 1be28cef6e0f9b93d4aae5770977a7980315dceb Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Wed, 7 Jun 2023 13:57:25 +0200 Subject: [PATCH 028/111] feat: support css animations as well --- .../useDrawerOverlayStyles.styles.ts | 4 +- .../src/util/useDrawerBaseStyles.styles.ts | 7 +- .../Drawer/DrawerCustomAnimation.stories.tsx | 91 +++++++++++++++++++ .../Drawer/DrawerCustomTransition.stories.tsx | 38 ++++---- .../stories/Drawer/index.stories.tsx | 1 + .../src/hooks/useTransitionPresence.ts | 36 +++++--- 6 files changed, 140 insertions(+), 37 deletions(-) create mode 100644 packages/react-components/react-drawer/stories/Drawer/DrawerCustomAnimation.stories.tsx diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts index 1315c76afd83e6..7d5a7dff335e4a 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts @@ -41,9 +41,6 @@ const useStyles = makeStyles({ transitionDuration: tokens.durationNormal, transitionTimingFunction: tokens.curveEasyEase, willChange: 'opacity', - '@media screen and (prefers-reduced-motion: reduce)': { - transitionDuration: '0.01ms', - }, }, backdropVisible: { opacity: 1, @@ -73,6 +70,7 @@ export const useDrawerOverlayStyles_unstable = (state: DrawerOverlayState): Draw backdrop.className = mergeClasses( backdrop.className, styles.backdrop, + baseStyles.reducedMotion, state.backdropVisible && styles.backdropVisible, ); } diff --git a/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts b/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts index a9970f9fc3013d..2275c90d316846 100644 --- a/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts +++ b/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts @@ -29,8 +29,12 @@ export const useDrawerBaseStyles = makeStyles({ justifyContent: 'flex-start', backgroundColor: tokens.colorNeutralBackground1, transitionDuration: tokens.durationNormal, + }, + + /* Reduced motion */ + reducedMotion: { '@media screen and (prefers-reduced-motion: reduce)': { - transitionDuration: '0.01ms', + transitionDuration: '0.001ms', }, }, @@ -75,6 +79,7 @@ export const getDrawerBaseClassNames = ( baseStyles: ReturnType, ) => { return mergeClasses( + baseStyles.reducedMotion, position && baseStyles[position], size && baseStyles[size], entering && baseStyles.entering, diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomAnimation.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomAnimation.stories.tsx new file mode 100644 index 00000000000000..7cdbe59fae3d27 --- /dev/null +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomAnimation.stories.tsx @@ -0,0 +1,91 @@ +import * as React from 'react'; +import { DrawerOverlay, DrawerBody, DrawerHeader, DrawerHeaderTitle } from '@fluentui/react-drawer'; +import { + Button, + makeStyles, + mergeClasses, + shorthands, + tokens, + useTransitionPresence, +} from '@fluentui/react-components'; +import { Dismiss24Regular } from '@fluentui/react-icons'; + +const visibleKeyframe = { + ...shorthands.borderRadius(0), + opacity: 1, + transform: 'translate3D(0, 0, 0) scale(1)', +}; + +const hiddenKeyframe = { + ...shorthands.borderRadius('36px'), + opacity: 0, + transform: 'translate3D(-100%, 0, 0) scale(0.9)', +}; + +const useStyles = makeStyles({ + drawer: { + animationDuration: tokens.durationUltraSlow, + willChange: 'opacity, transform, border-radius', + }, + + drawerEntering: { + animationTimingFunction: tokens.curveDecelerateMid, + animationName: { + '0%': hiddenKeyframe, + to: visibleKeyframe, + }, + }, + + drawerExiting: { + animationTimingFunction: tokens.curveAccelerateMin, + animationName: { + '0%': visibleKeyframe, + '100%': hiddenKeyframe, + }, + }, +}); + +export const CustomAnimation = () => { + const styles = useStyles(); + + const [isOpen, setIsOpen] = React.useState(false); + const { ref, entering, exiting } = useTransitionPresence(isOpen); + + React.useEffect(() => { + console.log({ entering, exiting }); + }, [entering, exiting]); + + return ( +

+ setIsOpen(open)} + > + + } + onClick={() => setIsOpen(false)} + /> + } + > + Drawer with custom transition + + + + +

Drawer content

+
+
+ + +
+ ); +}; diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx index eb9b3c3288a3d4..e48daa77c6c378 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx @@ -1,45 +1,39 @@ import * as React from 'react'; -import { Drawer, DrawerBody, DrawerHeader, DrawerHeaderTitle } from '@fluentui/react-drawer'; -import { - Button, - makeStyles, - mergeClasses, - shorthands, - tokens, - useTransitionPresence, -} from '@fluentui/react-components'; +import { DrawerOverlay, DrawerBody, DrawerHeader, DrawerHeaderTitle } from '@fluentui/react-drawer'; +import { Button, makeStyles, mergeClasses, tokens, useTransitionPresence } from '@fluentui/react-components'; import { Dismiss24Regular } from '@fluentui/react-icons'; const useStyles = makeStyles({ drawer: { - ...shorthands.borderRadius('36px'), - opacity: 0, - transform: 'translate3D(-100%, 0, 0) scale(0.9)', - transitionDuration: tokens.durationSlower, - transitionProperty: 'opacity, transform, border-radius', - willChange: 'opacity, transform, border-radius', + transform: 'translate3D(0, 0, 0) scale(0)', + transitionDuration: tokens.durationUltraSlow, + transitionProperty: 'transform, opacity', + willChange: 'transform, opacity', }, - drawerOpen: { - ...shorthands.borderRadius(0), - + drawerVisible: { opacity: 1, transform: 'translate3D(0, 0, 0) scale(1)', }, + + drawerExiting: { + opacity: 0, + transform: 'translate3D(0, 0, 0) scale(2)', + }, }); export const CustomTransition = () => { const styles = useStyles(); const [isOpen, setIsOpen] = React.useState(false); - const { ref, visible } = useTransitionPresence(isOpen); + const { ref, visible, exiting } = useTransitionPresence(isOpen); return (
- setIsOpen(open)} > @@ -61,7 +55,7 @@ export const CustomTransition = () => {

Drawer content

-
+ + +

Resize the window to see the change

+
); }; From 3e9cb0ee3036977d4781006f903a89e09d998aa0 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Tue, 13 Jun 2023 18:33:04 +0200 Subject: [PATCH 035/111] fix: remove redundant types --- .../react-components/react-drawer/etc/react-drawer.api.md | 2 +- .../src/components/DrawerInline/DrawerInline.types.ts | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/react-components/react-drawer/etc/react-drawer.api.md b/packages/react-components/react-drawer/etc/react-drawer.api.md index 52f3f34607876b..fbfa04d4732cf1 100644 --- a/packages/react-components/react-drawer/etc/react-drawer.api.md +++ b/packages/react-components/react-drawer/etc/react-drawer.api.md @@ -117,7 +117,7 @@ export type DrawerInlineSlots = { }; // @public -export type DrawerInlineState = ComponentState & DrawerBaseProps & DrawerBaseState & Pick; +export type DrawerInlineState = ComponentState & DrawerInlineProps & DrawerBaseState; // @public export const DrawerOverlay: ForwardRefComponent; diff --git a/packages/react-components/react-drawer/src/components/DrawerInline/DrawerInline.types.ts b/packages/react-components/react-drawer/src/components/DrawerInline/DrawerInline.types.ts index 230d7c109d0dfc..193cc28df728b3 100644 --- a/packages/react-components/react-drawer/src/components/DrawerInline/DrawerInline.types.ts +++ b/packages/react-components/react-drawer/src/components/DrawerInline/DrawerInline.types.ts @@ -21,7 +21,4 @@ export type DrawerInlineProps = ComponentProps & /** * State used in rendering DrawerInline */ -export type DrawerInlineState = ComponentState & - DrawerBaseProps & - DrawerBaseState & - Pick; +export type DrawerInlineState = ComponentState & DrawerInlineProps & DrawerBaseState; From 38e7bc644d3d16b428e614486eaa145f3226ef57 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Tue, 20 Jun 2023 18:14:16 +0200 Subject: [PATCH 036/111] fix: regenerate API files --- .../etc/react-components.api.md | 6710 +++++++---------- .../etc/react-components.unstable.api.md | 1189 ++- .../react-drawer/etc/react-drawer.api.md | 9 + .../etc/react-utilities.api.md | 2 +- 4 files changed, 3084 insertions(+), 4826 deletions(-) diff --git a/packages/react-components/react-components/etc/react-components.api.md b/packages/react-components/react-components/etc/react-components.api.md index 56314ba883dc69..e2321581694a58 100644 --- a/packages/react-components/react-components/etc/react-components.api.md +++ b/packages/react-components/react-components/etc/react-components.api.md @@ -4,32 +4,1029 @@ ```ts -/// - import { __css } from '@griffel/react'; import { __resetCSS } from '@griffel/react'; import { __resetStyles } from '@griffel/react'; import { __styles } from '@griffel/react'; +import { Accordion } from '@fluentui/react-accordion'; +import { accordionClassNames } from '@fluentui/react-accordion'; +import { AccordionContextValue } from '@fluentui/react-accordion'; +import { AccordionContextValues } from '@fluentui/react-accordion'; +import { AccordionHeader } from '@fluentui/react-accordion'; +import { accordionHeaderClassNames } from '@fluentui/react-accordion'; +import { AccordionHeaderContextValue } from '@fluentui/react-accordion'; +import { AccordionHeaderContextValues } from '@fluentui/react-accordion'; +import { AccordionHeaderExpandIconPosition } from '@fluentui/react-accordion'; +import { AccordionHeaderProps } from '@fluentui/react-accordion'; +import { AccordionHeaderSize } from '@fluentui/react-accordion'; +import { AccordionHeaderSlots } from '@fluentui/react-accordion'; +import { AccordionHeaderState } from '@fluentui/react-accordion'; +import { AccordionIndex } from '@fluentui/react-accordion'; +import { AccordionItem } from '@fluentui/react-accordion'; +import { accordionItemClassNames } from '@fluentui/react-accordion'; +import { AccordionItemContextValue } from '@fluentui/react-accordion'; +import { AccordionItemContextValues } from '@fluentui/react-accordion'; +import { AccordionItemProps } from '@fluentui/react-accordion'; +import { AccordionItemProvider } from '@fluentui/react-accordion'; +import { AccordionItemSlots } from '@fluentui/react-accordion'; +import { AccordionItemState } from '@fluentui/react-accordion'; +import { AccordionItemValue } from '@fluentui/react-accordion'; +import { AccordionPanel } from '@fluentui/react-accordion'; +import { accordionPanelClassNames } from '@fluentui/react-accordion'; +import { AccordionPanelProps } from '@fluentui/react-accordion'; +import { AccordionPanelSlots } from '@fluentui/react-accordion'; +import { AccordionPanelState } from '@fluentui/react-accordion'; +import { AccordionProps } from '@fluentui/react-accordion'; +import { AccordionProvider } from '@fluentui/react-accordion'; +import { AccordionSlots } from '@fluentui/react-accordion'; +import { AccordionState } from '@fluentui/react-accordion'; +import { AccordionToggleData } from '@fluentui/react-accordion'; +import { AccordionToggleEvent } from '@fluentui/react-accordion'; +import { AccordionToggleEventHandler } from '@fluentui/react-accordion'; +import { arrowHeights } from '@fluentui/react-popover'; +import { Avatar } from '@fluentui/react-avatar'; +import { avatarClassNames } from '@fluentui/react-avatar'; +import { AvatarGroup } from '@fluentui/react-avatar'; +import { avatarGroupClassNames } from '@fluentui/react-avatar'; +import { AvatarGroupContextValue } from '@fluentui/react-avatar'; +import { AvatarGroupContextValues } from '@fluentui/react-avatar'; +import { AvatarGroupItem } from '@fluentui/react-avatar'; +import { avatarGroupItemClassNames } from '@fluentui/react-avatar'; +import { AvatarGroupItemProps } from '@fluentui/react-avatar'; +import { AvatarGroupItemSlots } from '@fluentui/react-avatar'; +import { AvatarGroupItemState } from '@fluentui/react-avatar'; +import { AvatarGroupPopover } from '@fluentui/react-avatar'; +import { avatarGroupPopoverClassNames } from '@fluentui/react-avatar'; +import { AvatarGroupPopoverProps } from '@fluentui/react-avatar'; +import { AvatarGroupPopoverSlots } from '@fluentui/react-avatar'; +import { AvatarGroupPopoverState } from '@fluentui/react-avatar'; +import { AvatarGroupProps } from '@fluentui/react-avatar'; +import { AvatarGroupProvider } from '@fluentui/react-avatar'; +import { AvatarGroupSlots } from '@fluentui/react-avatar'; +import { AvatarGroupState } from '@fluentui/react-avatar'; +import { AvatarNamedColor } from '@fluentui/react-avatar'; +import { AvatarProps } from '@fluentui/react-avatar'; +import { AvatarSize } from '@fluentui/react-avatar'; +import { AvatarSizes } from '@fluentui/react-avatar'; +import { AvatarSlots } from '@fluentui/react-avatar'; +import { AvatarState } from '@fluentui/react-avatar'; +import { Badge } from '@fluentui/react-badge'; +import { badgeClassNames } from '@fluentui/react-badge'; +import { BadgeProps } from '@fluentui/react-badge'; +import { BadgeSlots } from '@fluentui/react-badge'; +import { BadgeState } from '@fluentui/react-badge'; +import { Body1 } from '@fluentui/react-text'; +import { body1ClassNames } from '@fluentui/react-text'; +import { Body1Strong } from '@fluentui/react-text'; +import { body1StrongClassNames } from '@fluentui/react-text'; +import { Body1Stronger } from '@fluentui/react-text'; +import { body1StrongerClassNames } from '@fluentui/react-text'; +import { Body2 } from '@fluentui/react-text'; +import { body2ClassNames } from '@fluentui/react-text'; +import { BorderRadiusTokens } from '@fluentui/react-theme'; +import { BrandVariants } from '@fluentui/react-theme'; +import { Button } from '@fluentui/react-button'; +import { buttonClassNames } from '@fluentui/react-button'; +import { ButtonProps } from '@fluentui/react-button'; +import { ButtonSlots } from '@fluentui/react-button'; +import { ButtonState } from '@fluentui/react-button'; +import { Caption1 } from '@fluentui/react-text'; +import { caption1ClassNames } from '@fluentui/react-text'; +import { Caption1Strong } from '@fluentui/react-text'; +import { caption1StrongClassNames } from '@fluentui/react-text'; +import { Caption1Stronger } from '@fluentui/react-text'; +import { caption1StrongerClassNames } from '@fluentui/react-text'; +import { Caption2 } from '@fluentui/react-text'; +import { caption2ClassNames } from '@fluentui/react-text'; +import { Caption2Strong } from '@fluentui/react-text'; +import { caption2StrongClassNames } from '@fluentui/react-text'; +import { Card } from '@fluentui/react-card'; +import { cardClassNames } from '@fluentui/react-card'; +import { cardCSSVars } from '@fluentui/react-card'; +import { CardFooter } from '@fluentui/react-card'; +import { cardFooterClassNames } from '@fluentui/react-card'; +import { CardFooterProps } from '@fluentui/react-card'; +import { CardFooterSlots } from '@fluentui/react-card'; +import { CardFooterState } from '@fluentui/react-card'; +import { CardHeader } from '@fluentui/react-card'; +import { cardHeaderClassNames } from '@fluentui/react-card'; +import { cardHeaderCSSVars } from '@fluentui/react-card'; +import { CardHeaderProps } from '@fluentui/react-card'; +import { CardHeaderSlots } from '@fluentui/react-card'; +import { CardHeaderState } from '@fluentui/react-card'; +import { CardPreview } from '@fluentui/react-card'; +import { cardPreviewClassNames } from '@fluentui/react-card'; +import { CardPreviewProps } from '@fluentui/react-card'; +import { CardPreviewSlots } from '@fluentui/react-card'; +import { CardPreviewState } from '@fluentui/react-card'; +import { CardProps } from '@fluentui/react-card'; +import { CardSlots } from '@fluentui/react-card'; +import { CardState } from '@fluentui/react-card'; +import { Checkbox } from '@fluentui/react-checkbox'; +import { checkboxClassNames } from '@fluentui/react-checkbox'; +import { CheckboxOnChangeData } from '@fluentui/react-checkbox'; +import { CheckboxProps } from '@fluentui/react-checkbox'; +import { CheckboxSlots } from '@fluentui/react-checkbox'; +import { CheckboxState } from '@fluentui/react-checkbox'; +import { ColorPaletteTokens } from '@fluentui/react-theme'; +import { ColorTokens } from '@fluentui/react-theme'; +import { Combobox } from '@fluentui/react-combobox'; +import { comboboxClassNames } from '@fluentui/react-combobox'; +import { ComboboxContextValue } from '@fluentui/react-combobox'; +import { ComboboxContextValues } from '@fluentui/react-combobox'; +import { ComboboxOpenChangeData } from '@fluentui/react-combobox'; +import { ComboboxOpenEvents } from '@fluentui/react-combobox'; +import { ComboboxProps } from '@fluentui/react-combobox'; +import { ComboboxProvider } from '@fluentui/react-combobox'; +import { ComboboxSlots } from '@fluentui/react-combobox'; +import { ComboboxState } from '@fluentui/react-combobox'; +import { ComponentProps } from '@fluentui/react-utilities'; +import { ComponentState } from '@fluentui/react-utilities'; +import { CompoundButton } from '@fluentui/react-button'; +import { compoundButtonClassNames } from '@fluentui/react-button'; +import { CompoundButtonProps } from '@fluentui/react-button'; +import { CompoundButtonSlots } from '@fluentui/react-button'; +import { CompoundButtonState } from '@fluentui/react-button'; +import { CounterBadge } from '@fluentui/react-badge'; +import { counterBadgeClassNames } from '@fluentui/react-badge'; +import { CounterBadgeProps } from '@fluentui/react-badge'; +import { CounterBadgeState } from '@fluentui/react-badge'; +import { createCustomFocusIndicatorStyle } from '@fluentui/react-tabster'; +import { CreateCustomFocusIndicatorStyleOptions } from '@fluentui/react-tabster'; +import { createDarkTheme } from '@fluentui/react-theme'; import { createDOMRenderer } from '@griffel/react'; -import { FC } from 'react'; -import type { FunctionComponent } from 'react'; +import { createFocusOutlineStyle } from '@fluentui/react-tabster'; +import { CreateFocusOutlineStyleOptions } from '@fluentui/react-tabster'; +import { createHighContrastTheme } from '@fluentui/react-theme'; +import { createLightTheme } from '@fluentui/react-theme'; +import { createTableColumn } from '@fluentui/react-table'; +import { CreateTableColumnOptions } from '@fluentui/react-table'; +import { createTeamsDarkTheme } from '@fluentui/react-theme'; +import { CurveTokens } from '@fluentui/react-theme'; +import { DATA_OVERFLOW_DIVIDER } from '@fluentui/react-overflow'; +import { DATA_OVERFLOW_ITEM } from '@fluentui/react-overflow'; +import { DATA_OVERFLOW_MENU } from '@fluentui/react-overflow'; +import { DATA_OVERFLOWING } from '@fluentui/react-overflow'; +import { DataGrid } from '@fluentui/react-table'; +import { DataGridBody } from '@fluentui/react-table'; +import { dataGridBodyClassNames } from '@fluentui/react-table'; +import { DataGridBodyProps } from '@fluentui/react-table'; +import { DataGridBodySlots } from '@fluentui/react-table'; +import { DataGridBodyState } from '@fluentui/react-table'; +import { DataGridCell } from '@fluentui/react-table'; +import { dataGridCellClassNames } from '@fluentui/react-table'; +import { DataGridCellProps } from '@fluentui/react-table'; +import { DataGridCellSlots } from '@fluentui/react-table'; +import { DataGridCellState } from '@fluentui/react-table'; +import { dataGridClassNames } from '@fluentui/react-table'; +import { DataGridContextValue } from '@fluentui/react-table'; +import { DataGridContextValues } from '@fluentui/react-table'; +import { DataGridHeader } from '@fluentui/react-table'; +import { DataGridHeaderCell } from '@fluentui/react-table'; +import { dataGridHeaderCellClassNames } from '@fluentui/react-table'; +import { DataGridHeaderCellProps } from '@fluentui/react-table'; +import { DataGridHeaderCellSlots } from '@fluentui/react-table'; +import { DataGridHeaderCellState } from '@fluentui/react-table'; +import { dataGridHeaderClassNames } from '@fluentui/react-table'; +import { DataGridHeaderProps } from '@fluentui/react-table'; +import { DataGridHeaderSlots } from '@fluentui/react-table'; +import { DataGridHeaderState } from '@fluentui/react-table'; +import { DataGridProps } from '@fluentui/react-table'; +import { DataGridRow } from '@fluentui/react-table'; +import { dataGridRowClassNames } from '@fluentui/react-table'; +import { DataGridRowProps } from '@fluentui/react-table'; +import { DataGridRowSlots } from '@fluentui/react-table'; +import { DataGridRowState } from '@fluentui/react-table'; +import { DataGridSelectionCell } from '@fluentui/react-table'; +import { dataGridSelectionCellClassNames } from '@fluentui/react-table'; +import { DataGridSelectionCellProps } from '@fluentui/react-table'; +import { DataGridSelectionCellSlots } from '@fluentui/react-table'; +import { DataGridSelectionCellState } from '@fluentui/react-table'; +import { DataGridSlots } from '@fluentui/react-table'; +import { DataGridState } from '@fluentui/react-table'; +import { Dialog } from '@fluentui/react-dialog'; +import { DialogActions } from '@fluentui/react-dialog'; +import { dialogActionsClassNames } from '@fluentui/react-dialog'; +import { DialogActionsPosition } from '@fluentui/react-dialog'; +import { DialogActionsProps } from '@fluentui/react-dialog'; +import { DialogActionsSlots } from '@fluentui/react-dialog'; +import { DialogActionsState } from '@fluentui/react-dialog'; +import { DialogBody } from '@fluentui/react-dialog'; +import { dialogBodyClassNames } from '@fluentui/react-dialog'; +import { DialogBodyProps } from '@fluentui/react-dialog'; +import { DialogBodySlots } from '@fluentui/react-dialog'; +import { DialogBodyState } from '@fluentui/react-dialog'; +import { DialogContent } from '@fluentui/react-dialog'; +import { dialogContentClassNames } from '@fluentui/react-dialog'; +import { DialogContentProps } from '@fluentui/react-dialog'; +import { DialogContentSlots } from '@fluentui/react-dialog'; +import { DialogContentState } from '@fluentui/react-dialog'; +import { DialogOpenChangeData } from '@fluentui/react-dialog'; +import { DialogOpenChangeEvent } from '@fluentui/react-dialog'; +import { DialogProps } from '@fluentui/react-dialog'; +import { DialogSlots } from '@fluentui/react-dialog'; +import { DialogState } from '@fluentui/react-dialog'; +import { DialogSurface } from '@fluentui/react-dialog'; +import { dialogSurfaceClassNames } from '@fluentui/react-dialog'; +import { DialogSurfaceProps } from '@fluentui/react-dialog'; +import { DialogSurfaceSlots } from '@fluentui/react-dialog'; +import { DialogSurfaceState } from '@fluentui/react-dialog'; +import { DialogTitle } from '@fluentui/react-dialog'; +import { dialogTitleClassNames } from '@fluentui/react-dialog'; +import { DialogTitleProps } from '@fluentui/react-dialog'; +import { DialogTitleSlots } from '@fluentui/react-dialog'; +import { DialogTitleState } from '@fluentui/react-dialog'; +import { DialogTrigger } from '@fluentui/react-dialog'; +import { DialogTriggerAction } from '@fluentui/react-dialog'; +import { DialogTriggerChildProps } from '@fluentui/react-dialog'; +import { DialogTriggerProps } from '@fluentui/react-dialog'; +import { DialogTriggerState } from '@fluentui/react-dialog'; +import { Display } from '@fluentui/react-text'; +import { displayClassNames } from '@fluentui/react-text'; +import { Divider } from '@fluentui/react-divider'; +import { dividerClassNames } from '@fluentui/react-divider'; +import { DividerProps } from '@fluentui/react-divider'; +import { DividerSlots } from '@fluentui/react-divider'; +import { DividerState } from '@fluentui/react-divider'; +import { Dropdown } from '@fluentui/react-combobox'; +import { dropdownClassNames } from '@fluentui/react-combobox'; +import { DropdownContextValues } from '@fluentui/react-combobox'; +import { DropdownOpenChangeData } from '@fluentui/react-combobox'; +import { DropdownOpenEvents } from '@fluentui/react-combobox'; +import { DropdownProps } from '@fluentui/react-combobox'; +import { DropdownSlots } from '@fluentui/react-combobox'; +import { DropdownState } from '@fluentui/react-combobox'; +import { DurationTokens } from '@fluentui/react-theme'; +import { Field } from '@fluentui/react-field'; +import { fieldClassNames } from '@fluentui/react-field'; +import { FieldContextProvider } from '@fluentui/react-field'; +import { FieldContextValue } from '@fluentui/react-field'; +import { FieldContextValues } from '@fluentui/react-field'; +import { FieldControlProps } from '@fluentui/react-field'; +import { FieldControlPropsOptions } from '@fluentui/react-field'; +import { FieldProps } from '@fluentui/react-field'; +import { FieldSlots } from '@fluentui/react-field'; +import { FieldState } from '@fluentui/react-field'; +import { FluentProvider } from '@fluentui/react-provider'; +import { fluentProviderClassNames } from '@fluentui/react-provider'; +import { FluentProviderContextValues } from '@fluentui/react-provider'; +import { FluentProviderCustomStyleHooks } from '@fluentui/react-provider'; +import { FluentProviderProps } from '@fluentui/react-provider'; +import { FluentProviderSlots } from '@fluentui/react-provider'; +import { FluentProviderState } from '@fluentui/react-provider'; +import { FontFamilyTokens } from '@fluentui/react-theme'; +import { FontSizeTokens } from '@fluentui/react-theme'; +import { FontWeightTokens } from '@fluentui/react-theme'; +import { ForwardRefComponent } from '@fluentui/react-utilities'; +import { getNativeElementProps } from '@fluentui/react-utilities'; +import { getPartitionedNativeProps } from '@fluentui/react-utilities'; +import { getSlots } from '@fluentui/react-utilities'; import { GriffelRenderer } from '@griffel/react'; import { GriffelStyle } from '@griffel/react'; -import { JSXElementConstructor } from 'react'; +import { HorizontalSpacingTokens } from '@fluentui/react-theme'; +import { IdPrefixProvider } from '@fluentui/react-utilities'; +import { Image as Image_2 } from '@fluentui/react-image'; +import { imageClassNames } from '@fluentui/react-image'; +import { ImageProps } from '@fluentui/react-image'; +import { ImageSlots } from '@fluentui/react-image'; +import { ImageState } from '@fluentui/react-image'; +import { Input } from '@fluentui/react-input'; +import { inputClassNames } from '@fluentui/react-input'; +import { InputOnChangeData } from '@fluentui/react-input'; +import { InputProps } from '@fluentui/react-input'; +import { InputSlots } from '@fluentui/react-input'; +import { InputState } from '@fluentui/react-input'; +import { Label } from '@fluentui/react-label'; +import { labelClassNames } from '@fluentui/react-label'; +import { LabelProps } from '@fluentui/react-label'; +import { LabelSlots } from '@fluentui/react-label'; +import { LabelState } from '@fluentui/react-label'; +import { LargeTitle } from '@fluentui/react-text'; +import { largeTitleClassNames } from '@fluentui/react-text'; +import { LineHeightTokens } from '@fluentui/react-theme'; +import { Link } from '@fluentui/react-link'; +import { linkClassNames } from '@fluentui/react-link'; +import { LinkProps } from '@fluentui/react-link'; +import { LinkSlots } from '@fluentui/react-link'; +import { LinkState } from '@fluentui/react-link'; +import { Listbox } from '@fluentui/react-combobox'; +import { listboxClassNames } from '@fluentui/react-combobox'; +import { ListboxContextValue } from '@fluentui/react-combobox'; +import { ListboxContextValues } from '@fluentui/react-combobox'; +import { ListboxProps } from '@fluentui/react-combobox'; +import { ListboxProvider } from '@fluentui/react-combobox'; +import { ListboxSlots } from '@fluentui/react-combobox'; +import { ListboxState } from '@fluentui/react-combobox'; import { makeResetStyles } from '@griffel/react'; import { makeStaticStyles } from '@griffel/react'; import { makeStyles } from '@griffel/react'; +import { Menu } from '@fluentui/react-menu'; +import { MenuButton } from '@fluentui/react-button'; +import { menuButtonClassNames } from '@fluentui/react-button'; +import { MenuButtonProps } from '@fluentui/react-button'; +import { MenuButtonSlots } from '@fluentui/react-button'; +import { MenuButtonState } from '@fluentui/react-button'; +import { MenuCheckedValueChangeData } from '@fluentui/react-menu'; +import { MenuCheckedValueChangeEvent } from '@fluentui/react-menu'; +import { MenuContextValue } from '@fluentui/react-menu'; +import { MenuContextValues } from '@fluentui/react-menu'; +import { MenuDivider } from '@fluentui/react-menu'; +import { menuDividerClassNames } from '@fluentui/react-menu'; +import { MenuDividerProps } from '@fluentui/react-menu'; +import { MenuDividerSlots } from '@fluentui/react-menu'; +import { MenuDividerState } from '@fluentui/react-menu'; +import { MenuGroup } from '@fluentui/react-menu'; +import { menuGroupClassNames } from '@fluentui/react-menu'; +import { MenuGroupContextProvider } from '@fluentui/react-menu'; +import { MenuGroupContextValue } from '@fluentui/react-menu'; +import { MenuGroupContextValues } from '@fluentui/react-menu'; +import { MenuGroupHeader } from '@fluentui/react-menu'; +import { menuGroupHeaderClassNames } from '@fluentui/react-menu'; +import { MenuGroupHeaderProps } from '@fluentui/react-menu'; +import { MenuGroupHeaderSlots } from '@fluentui/react-menu'; +import { MenuGroupHeaderState } from '@fluentui/react-menu'; +import { MenuGroupProps } from '@fluentui/react-menu'; +import { MenuGroupSlots } from '@fluentui/react-menu'; +import { MenuGroupState } from '@fluentui/react-menu'; +import { MenuItem } from '@fluentui/react-menu'; +import { MenuItemCheckbox } from '@fluentui/react-menu'; +import { menuItemCheckboxClassNames } from '@fluentui/react-menu'; +import { MenuItemCheckboxProps } from '@fluentui/react-menu'; +import { MenuItemCheckboxState } from '@fluentui/react-menu'; +import { menuItemClassNames } from '@fluentui/react-menu'; +import { MenuItemProps } from '@fluentui/react-menu'; +import { MenuItemRadio } from '@fluentui/react-menu'; +import { menuItemRadioClassNames } from '@fluentui/react-menu'; +import { MenuItemRadioProps } from '@fluentui/react-menu'; +import { MenuItemRadioState } from '@fluentui/react-menu'; +import { MenuItemSelectableProps } from '@fluentui/react-menu'; +import { MenuItemSelectableState } from '@fluentui/react-menu'; +import { MenuItemSlots } from '@fluentui/react-menu'; +import { MenuItemState } from '@fluentui/react-menu'; +import { MenuList } from '@fluentui/react-menu'; +import { menuListClassNames } from '@fluentui/react-menu'; +import { MenuListContextValue } from '@fluentui/react-menu'; +import { MenuListContextValues } from '@fluentui/react-menu'; +import { MenuListProps } from '@fluentui/react-menu'; +import { MenuListProvider } from '@fluentui/react-menu'; +import { MenuListSlots } from '@fluentui/react-menu'; +import { MenuListState } from '@fluentui/react-menu'; +import { MenuOpenChangeData } from '@fluentui/react-menu'; +import { MenuOpenEvent } from '@fluentui/react-menu'; +import { MenuOpenEvents } from '@fluentui/react-menu'; +import { MenuPopover } from '@fluentui/react-menu'; +import { menuPopoverClassNames } from '@fluentui/react-menu'; +import { MenuPopoverProps } from '@fluentui/react-menu'; +import { MenuPopoverSlots } from '@fluentui/react-menu'; +import { MenuPopoverState } from '@fluentui/react-menu'; +import { MenuProps } from '@fluentui/react-menu'; +import { MenuProvider } from '@fluentui/react-menu'; +import { MenuSlots } from '@fluentui/react-menu'; +import { MenuSplitGroup } from '@fluentui/react-menu'; +import { menuSplitGroupClassNames } from '@fluentui/react-menu'; +import { MenuSplitGroupProps } from '@fluentui/react-menu'; +import { MenuSplitGroupSlots } from '@fluentui/react-menu'; +import { MenuSplitGroupState } from '@fluentui/react-menu'; +import { MenuState } from '@fluentui/react-menu'; +import { MenuTrigger } from '@fluentui/react-menu'; +import { MenuTriggerChildProps } from '@fluentui/react-menu'; +import { MenuTriggerContextProvider } from '@fluentui/react-menu'; +import { MenuTriggerProps } from '@fluentui/react-menu'; +import { MenuTriggerState } from '@fluentui/react-menu'; import { mergeClasses } from '@griffel/react'; -import { Provider } from 'react'; -import { ProviderProps } from 'react'; -import * as React_2 from 'react'; -import { ReactElement } from 'react'; -import { ReactNode } from 'react'; -import type { RefObject } from 'react'; +import { OnOpenChangeData } from '@fluentui/react-popover'; +import { OnVisibleChangeData } from '@fluentui/react-tooltip'; +import { OpenPopoverEvents } from '@fluentui/react-popover'; +import { Option as Option_2 } from '@fluentui/react-combobox'; +import { optionClassNames } from '@fluentui/react-combobox'; +import { OptionGroup } from '@fluentui/react-combobox'; +import { optionGroupClassNames } from '@fluentui/react-combobox'; +import { OptionGroupProps } from '@fluentui/react-combobox'; +import { OptionGroupSlots } from '@fluentui/react-combobox'; +import { OptionGroupState } from '@fluentui/react-combobox'; +import { OptionProps } from '@fluentui/react-combobox'; +import { OptionSlots } from '@fluentui/react-combobox'; +import { OptionState } from '@fluentui/react-combobox'; +import { Overflow } from '@fluentui/react-overflow'; +import { OverflowDivider } from '@fluentui/react-overflow'; +import { OverflowItem } from '@fluentui/react-overflow'; +import { OverflowItemProps } from '@fluentui/react-overflow'; +import { OverflowProps } from '@fluentui/react-overflow'; +import { PartialTheme } from '@fluentui/react-theme'; +import { PartitionAvatarGroupItems } from '@fluentui/react-avatar'; +import { partitionAvatarGroupItems } from '@fluentui/react-avatar'; +import { PartitionAvatarGroupItemsOptions } from '@fluentui/react-avatar'; +import { Persona } from '@fluentui/react-persona'; +import { personaClassNames } from '@fluentui/react-persona'; +import { PersonaProps } from '@fluentui/react-persona'; +import { PersonaSlots } from '@fluentui/react-persona'; +import { PersonaState } from '@fluentui/react-persona'; +import { Popover } from '@fluentui/react-popover'; +import { PopoverContextValue } from '@fluentui/react-popover'; +import { PopoverProps } from '@fluentui/react-popover'; +import { PopoverProvider } from '@fluentui/react-popover'; +import { PopoverSize } from '@fluentui/react-popover'; +import { PopoverState } from '@fluentui/react-popover'; +import { PopoverSurface } from '@fluentui/react-popover'; +import { popoverSurfaceClassNames } from '@fluentui/react-popover'; +import { PopoverSurfaceProps } from '@fluentui/react-popover'; +import { PopoverSurfaceSlots } from '@fluentui/react-popover'; +import { PopoverSurfaceState } from '@fluentui/react-popover'; +import { PopoverTrigger } from '@fluentui/react-popover'; +import { PopoverTriggerChildProps } from '@fluentui/react-popover'; +import { PopoverTriggerProps } from '@fluentui/react-popover'; +import { PopoverTriggerState } from '@fluentui/react-popover'; +import { Portal } from '@fluentui/react-portal'; +import { PortalProps } from '@fluentui/react-portal'; +import { PortalState } from '@fluentui/react-portal'; +import { PositioningImperativeRef } from '@fluentui/react-positioning'; +import { PositioningProps } from '@fluentui/react-positioning'; +import { PositioningShorthand } from '@fluentui/react-positioning'; +import { PositioningShorthandValue } from '@fluentui/react-positioning'; +import { PositioningVirtualElement } from '@fluentui/react-positioning'; +import { PresenceBadge } from '@fluentui/react-badge'; +import { presenceBadgeClassNames } from '@fluentui/react-badge'; +import { PresenceBadgeProps } from '@fluentui/react-badge'; +import { PresenceBadgeState } from '@fluentui/react-badge'; +import { PresenceBadgeStatus } from '@fluentui/react-badge'; +import { ProgressBar } from '@fluentui/react-progress'; +import { progressBarClassNames } from '@fluentui/react-progress'; +import { ProgressBarProps } from '@fluentui/react-progress'; +import { ProgressBarSlots } from '@fluentui/react-progress'; +import { ProgressBarState } from '@fluentui/react-progress'; +import { Radio } from '@fluentui/react-radio'; +import { radioClassNames } from '@fluentui/react-radio'; +import { RadioGroup } from '@fluentui/react-radio'; +import { radioGroupClassNames } from '@fluentui/react-radio'; +import { RadioGroupContextValue } from '@fluentui/react-radio'; +import { RadioGroupContextValues } from '@fluentui/react-radio'; +import { RadioGroupOnChangeData } from '@fluentui/react-radio'; +import { RadioGroupProps } from '@fluentui/react-radio'; +import { RadioGroupProvider } from '@fluentui/react-radio'; +import { RadioGroupSlots } from '@fluentui/react-radio'; +import { RadioGroupState } from '@fluentui/react-radio'; +import { RadioOnChangeData } from '@fluentui/react-radio'; +import { RadioProps } from '@fluentui/react-radio'; +import { RadioSlots } from '@fluentui/react-radio'; +import { RadioState } from '@fluentui/react-radio'; +import { RegisterTabEventHandler } from '@fluentui/react-tabs'; +import { renderAccordion_unstable } from '@fluentui/react-accordion'; +import { renderAccordionHeader_unstable } from '@fluentui/react-accordion'; +import { renderAccordionItem_unstable } from '@fluentui/react-accordion'; +import { renderAccordionPanel_unstable } from '@fluentui/react-accordion'; +import { renderAvatar_unstable } from '@fluentui/react-avatar'; +import { renderAvatarGroup_unstable } from '@fluentui/react-avatar'; +import { renderAvatarGroupItem_unstable } from '@fluentui/react-avatar'; +import { renderAvatarGroupPopover_unstable } from '@fluentui/react-avatar'; +import { renderBadge_unstable } from '@fluentui/react-badge'; +import { renderButton_unstable } from '@fluentui/react-button'; +import { renderCard_unstable } from '@fluentui/react-card'; +import { renderCardFooter_unstable } from '@fluentui/react-card'; +import { renderCardHeader_unstable } from '@fluentui/react-card'; +import { renderCardPreview_unstable } from '@fluentui/react-card'; +import { renderCheckbox_unstable } from '@fluentui/react-checkbox'; +import { renderCombobox_unstable } from '@fluentui/react-combobox'; +import { renderCompoundButton_unstable } from '@fluentui/react-button'; +import { renderDataGrid_unstable } from '@fluentui/react-table'; +import { renderDataGridBody_unstable } from '@fluentui/react-table'; +import { renderDataGridCell_unstable } from '@fluentui/react-table'; +import { renderDataGridHeader_unstable } from '@fluentui/react-table'; +import { renderDataGridHeaderCell_unstable } from '@fluentui/react-table'; +import { renderDataGridRow_unstable } from '@fluentui/react-table'; +import { renderDataGridSelectionCell_unstable } from '@fluentui/react-table'; +import { renderDialog_unstable } from '@fluentui/react-dialog'; +import { renderDialogActions_unstable } from '@fluentui/react-dialog'; +import { renderDialogBody_unstable } from '@fluentui/react-dialog'; +import { renderDialogContent_unstable } from '@fluentui/react-dialog'; +import { renderDialogSurface_unstable } from '@fluentui/react-dialog'; +import { renderDialogTitle_unstable } from '@fluentui/react-dialog'; +import { renderDialogTrigger_unstable } from '@fluentui/react-dialog'; +import { renderDivider_unstable } from '@fluentui/react-divider'; +import { renderDropdown_unstable } from '@fluentui/react-combobox'; import { RendererProvider } from '@griffel/react'; +import { renderField_unstable } from '@fluentui/react-field'; +import { renderFluentProvider_unstable } from '@fluentui/react-provider'; +import { renderImage_unstable } from '@fluentui/react-image'; +import { renderInput_unstable } from '@fluentui/react-input'; +import { renderLabel_unstable } from '@fluentui/react-label'; +import { renderLink_unstable } from '@fluentui/react-link'; +import { renderListbox_unstable } from '@fluentui/react-combobox'; +import { renderMenu_unstable } from '@fluentui/react-menu'; +import { renderMenuButton_unstable } from '@fluentui/react-button'; +import { renderMenuDivider_unstable } from '@fluentui/react-menu'; +import { renderMenuGroup_unstable } from '@fluentui/react-menu'; +import { renderMenuGroupHeader_unstable } from '@fluentui/react-menu'; +import { renderMenuItem_unstable } from '@fluentui/react-menu'; +import { renderMenuItemCheckbox_unstable } from '@fluentui/react-menu'; +import { renderMenuItemRadio_unstable } from '@fluentui/react-menu'; +import { renderMenuList_unstable } from '@fluentui/react-menu'; +import { renderMenuPopover_unstable } from '@fluentui/react-menu'; +import { renderMenuSplitGroup_unstable } from '@fluentui/react-menu'; +import { renderMenuTrigger_unstable } from '@fluentui/react-menu'; +import { renderOption_unstable } from '@fluentui/react-combobox'; +import { renderOptionGroup_unstable } from '@fluentui/react-combobox'; +import { renderPersona_unstable } from '@fluentui/react-persona'; +import { renderPopover_unstable } from '@fluentui/react-popover'; +import { renderPopoverSurface_unstable } from '@fluentui/react-popover'; +import { renderPopoverTrigger_unstable } from '@fluentui/react-popover'; +import { renderPortal_unstable } from '@fluentui/react-portal'; +import { renderProgressBar_unstable } from '@fluentui/react-progress'; +import { renderRadio_unstable } from '@fluentui/react-radio'; +import { renderRadioGroup_unstable } from '@fluentui/react-radio'; +import { renderSelect_unstable } from '@fluentui/react-select'; +import { renderSkeleton_unstable } from '@fluentui/react-skeleton'; +import { renderSkeletonItem_unstable } from '@fluentui/react-skeleton'; +import { renderSlider_unstable } from '@fluentui/react-slider'; +import { renderSpinButton_unstable } from '@fluentui/react-spinbutton'; +import { renderSpinner_unstable } from '@fluentui/react-spinner'; +import { renderSplitButton_unstable } from '@fluentui/react-button'; +import { renderSwitch_unstable } from '@fluentui/react-switch'; +import { renderTab_unstable } from '@fluentui/react-tabs'; +import { renderTable_unstable } from '@fluentui/react-table'; +import { renderTableBody_unstable } from '@fluentui/react-table'; +import { renderTableCell_unstable } from '@fluentui/react-table'; +import { renderTableCellActions_unstable } from '@fluentui/react-table'; +import { renderTableCellLayout_unstable } from '@fluentui/react-table'; +import { renderTableHeader_unstable } from '@fluentui/react-table'; +import { renderTableHeaderCell_unstable } from '@fluentui/react-table'; +import { renderTableResizeHandle_unstable } from '@fluentui/react-table'; +import { renderTableRow_unstable } from '@fluentui/react-table'; +import { renderTableSelectionCell_unstable } from '@fluentui/react-table'; +import { renderTabList_unstable } from '@fluentui/react-tabs'; +import { renderText_unstable } from '@fluentui/react-text'; +import { renderTextarea_unstable } from '@fluentui/react-textarea'; +import { renderToggleButton_unstable } from '@fluentui/react-button'; +import { renderToolbar_unstable } from '@fluentui/react-toolbar'; +import { renderToolbarGroup_unstable } from '@fluentui/react-toolbar'; +import { renderTooltip_unstable } from '@fluentui/react-tooltip'; import { renderToStyleElements } from '@griffel/react'; +import { resetIdsForTests } from '@fluentui/react-utilities'; +import { resolveShorthand } from '@fluentui/react-utilities'; +import { ResolveShorthandFunction } from '@fluentui/react-utilities'; +import { ResolveShorthandOptions } from '@fluentui/react-utilities'; +import { Select } from '@fluentui/react-select'; +import { SelectableHandler } from '@fluentui/react-menu'; +import { selectClassNames } from '@fluentui/react-select'; +import { SelectOnChangeData } from '@fluentui/react-select'; +import { SelectProps } from '@fluentui/react-select'; +import { SelectSlots } from '@fluentui/react-select'; +import { SelectState } from '@fluentui/react-select'; +import { SelectTabData } from '@fluentui/react-tabs'; +import { SelectTabEvent } from '@fluentui/react-tabs'; +import { SelectTabEventHandler } from '@fluentui/react-tabs'; +import { ShadowBrandTokens } from '@fluentui/react-theme'; +import { ShadowTokens } from '@fluentui/react-theme'; import { shorthands } from '@griffel/react'; -import { Types } from 'tabster'; +import { Skeleton } from '@fluentui/react-skeleton'; +import { skeletonClassNames } from '@fluentui/react-skeleton'; +import { SkeletonContextProvider } from '@fluentui/react-skeleton'; +import { SkeletonContextValue } from '@fluentui/react-skeleton'; +import { SkeletonItem } from '@fluentui/react-skeleton'; +import { skeletonItemClassNames } from '@fluentui/react-skeleton'; +import { SkeletonItemProps } from '@fluentui/react-skeleton'; +import { SkeletonItemSlots } from '@fluentui/react-skeleton'; +import { SkeletonItemState } from '@fluentui/react-skeleton'; +import { SkeletonProps } from '@fluentui/react-skeleton'; +import { SkeletonSlots } from '@fluentui/react-skeleton'; +import { SkeletonState } from '@fluentui/react-skeleton'; +import { Slider } from '@fluentui/react-slider'; +import { sliderClassNames } from '@fluentui/react-slider'; +import { sliderCSSVars } from '@fluentui/react-slider'; +import { SliderOnChangeData } from '@fluentui/react-slider'; +import { SliderProps } from '@fluentui/react-slider'; +import { SliderSlots } from '@fluentui/react-slider'; +import { SliderState } from '@fluentui/react-slider'; +import { Slot } from '@fluentui/react-utilities'; +import { SlotClassNames } from '@fluentui/react-utilities'; +import { SlotPropsRecord } from '@fluentui/react-utilities'; +import { SlotRenderFunction } from '@fluentui/react-utilities'; +import { SortDirection } from '@fluentui/react-table'; +import { SpacingTokens } from '@fluentui/react-theme'; +import { SpinButton } from '@fluentui/react-spinbutton'; +import { SpinButtonBounds } from '@fluentui/react-spinbutton'; +import { SpinButtonChangeEvent } from '@fluentui/react-spinbutton'; +import { spinButtonClassNames } from '@fluentui/react-spinbutton'; +import { SpinButtonOnChangeData } from '@fluentui/react-spinbutton'; +import { SpinButtonProps } from '@fluentui/react-spinbutton'; +import { SpinButtonSlots } from '@fluentui/react-spinbutton'; +import { SpinButtonSpinState } from '@fluentui/react-spinbutton'; +import { SpinButtonState } from '@fluentui/react-spinbutton'; +import { Spinner } from '@fluentui/react-spinner'; +import { spinnerClassNames } from '@fluentui/react-spinner'; +import { SpinnerProps } from '@fluentui/react-spinner'; +import { SpinnerSlots } from '@fluentui/react-spinner'; +import { SpinnerState } from '@fluentui/react-spinner'; +import { SplitButton } from '@fluentui/react-button'; +import { splitButtonClassNames } from '@fluentui/react-button'; +import { SplitButtonProps } from '@fluentui/react-button'; +import { SplitButtonSlots } from '@fluentui/react-button'; +import { SplitButtonState } from '@fluentui/react-button'; +import { SSRProvider } from '@fluentui/react-utilities'; +import { StrokeWidthTokens } from '@fluentui/react-theme'; +import { Subtitle1 } from '@fluentui/react-text'; +import { subtitle1ClassNames } from '@fluentui/react-text'; +import { Subtitle2 } from '@fluentui/react-text'; +import { subtitle2ClassNames } from '@fluentui/react-text'; +import { Subtitle2Stronger } from '@fluentui/react-text'; +import { subtitle2StrongerClassNames } from '@fluentui/react-text'; +import { Switch } from '@fluentui/react-switch'; +import { switchClassNames } from '@fluentui/react-switch'; +import { SwitchOnChangeData } from '@fluentui/react-switch'; +import { SwitchProps } from '@fluentui/react-switch'; +import { SwitchSlots } from '@fluentui/react-switch'; +import { SwitchState } from '@fluentui/react-switch'; +import { Tab } from '@fluentui/react-tabs'; +import { tabClassNames } from '@fluentui/react-tabs'; +import { Table } from '@fluentui/react-table'; +import { TableBody } from '@fluentui/react-table'; +import { tableBodyClassName } from '@fluentui/react-table'; +import { tableBodyClassNames } from '@fluentui/react-table'; +import { TableBodyProps } from '@fluentui/react-table'; +import { TableBodySlots } from '@fluentui/react-table'; +import { TableBodyState } from '@fluentui/react-table'; +import { TableCell } from '@fluentui/react-table'; +import { TableCellActions } from '@fluentui/react-table'; +import { tableCellActionsClassNames } from '@fluentui/react-table'; +import { TableCellActionsProps } from '@fluentui/react-table'; +import { TableCellActionsSlots } from '@fluentui/react-table'; +import { TableCellActionsState } from '@fluentui/react-table'; +import { tableCellClassName } from '@fluentui/react-table'; +import { tableCellClassNames } from '@fluentui/react-table'; +import { TableCellLayout } from '@fluentui/react-table'; +import { tableCellLayoutClassNames } from '@fluentui/react-table'; +import { TableCellLayoutProps } from '@fluentui/react-table'; +import { TableCellLayoutSlots } from '@fluentui/react-table'; +import { TableCellLayoutState } from '@fluentui/react-table'; +import { TableCellProps } from '@fluentui/react-table'; +import { TableCellSlots } from '@fluentui/react-table'; +import { TableCellState } from '@fluentui/react-table'; +import { tableClassName } from '@fluentui/react-table'; +import { tableClassNames } from '@fluentui/react-table'; +import { TableColumnDefinition } from '@fluentui/react-table'; +import { TableColumnId } from '@fluentui/react-table'; +import { TableColumnSizingOptions } from '@fluentui/react-table'; +import { TableContextProvider } from '@fluentui/react-table'; +import { TableContextValue } from '@fluentui/react-table'; +import { TableContextValues } from '@fluentui/react-table'; +import { TableFeaturePlugin } from '@fluentui/react-table'; +import { TableFeaturesState } from '@fluentui/react-table'; +import { TableHeader } from '@fluentui/react-table'; +import { TableHeaderCell } from '@fluentui/react-table'; +import { tableHeaderCellClassName } from '@fluentui/react-table'; +import { tableHeaderCellClassNames } from '@fluentui/react-table'; +import { TableHeaderCellProps } from '@fluentui/react-table'; +import { TableHeaderCellSlots } from '@fluentui/react-table'; +import { TableHeaderCellState } from '@fluentui/react-table'; +import { tableHeaderClassName } from '@fluentui/react-table'; +import { tableHeaderClassNames } from '@fluentui/react-table'; +import { TableHeaderProps } from '@fluentui/react-table'; +import { TableHeaderSlots } from '@fluentui/react-table'; +import { TableHeaderState } from '@fluentui/react-table'; +import { TableProps } from '@fluentui/react-table'; +import { TableResizeHandle } from '@fluentui/react-table'; +import { tableResizeHandleClassNames } from '@fluentui/react-table'; +import { TableRow } from '@fluentui/react-table'; +import { tableRowClassName } from '@fluentui/react-table'; +import { tableRowClassNames } from '@fluentui/react-table'; +import { TableRowData } from '@fluentui/react-table'; +import { TableRowId } from '@fluentui/react-table'; +import { TableRowIdContextProvider } from '@fluentui/react-table'; +import { TableRowProps } from '@fluentui/react-table'; +import { TableRowSlots } from '@fluentui/react-table'; +import { TableRowState } from '@fluentui/react-table'; +import { TableSelectionCell } from '@fluentui/react-table'; +import { tableSelectionCellClassNames } from '@fluentui/react-table'; +import { TableSelectionCellProps } from '@fluentui/react-table'; +import { TableSelectionCellSlots } from '@fluentui/react-table'; +import { TableSelectionCellState } from '@fluentui/react-table'; +import { TableSelectionState } from '@fluentui/react-table'; +import { TableSlots } from '@fluentui/react-table'; +import { TableSortState } from '@fluentui/react-table'; +import { TableState } from '@fluentui/react-table'; +import { TabList } from '@fluentui/react-tabs'; +import { tabListClassNames } from '@fluentui/react-tabs'; +import { TabListContextValue } from '@fluentui/react-tabs'; +import { TabListContextValues } from '@fluentui/react-tabs'; +import { TabListProps } from '@fluentui/react-tabs'; +import { TabListProvider } from '@fluentui/react-tabs'; +import { TabListSlots } from '@fluentui/react-tabs'; +import { TabListState } from '@fluentui/react-tabs'; +import { TabProps } from '@fluentui/react-tabs'; +import { TabRegisterData } from '@fluentui/react-tabs'; +import { TabSlots } from '@fluentui/react-tabs'; +import { TabState } from '@fluentui/react-tabs'; +import { TabValue } from '@fluentui/react-tabs'; +import { teamsDarkTheme } from '@fluentui/react-theme'; +import { teamsHighContrastTheme } from '@fluentui/react-theme'; +import { teamsLightTheme } from '@fluentui/react-theme'; +import { Text as Text_2 } from '@fluentui/react-text'; +import { Textarea } from '@fluentui/react-textarea'; +import { textareaClassNames } from '@fluentui/react-textarea'; +import { TextareaOnChangeData } from '@fluentui/react-textarea'; +import { TextareaProps } from '@fluentui/react-textarea'; +import { TextareaSlots } from '@fluentui/react-textarea'; +import { TextareaState } from '@fluentui/react-textarea'; +import { textClassNames } from '@fluentui/react-text'; +import { TextPresetProps } from '@fluentui/react-text'; +import { TextProps } from '@fluentui/react-text'; +import { TextSlots } from '@fluentui/react-text'; +import { TextState } from '@fluentui/react-text'; +import { Theme } from '@fluentui/react-theme'; +import { themeToTokensObject } from '@fluentui/react-theme'; +import { Title1 } from '@fluentui/react-text'; +import { title1ClassNames } from '@fluentui/react-text'; +import { Title2 } from '@fluentui/react-text'; +import { title2ClassNames } from '@fluentui/react-text'; +import { Title3 } from '@fluentui/react-text'; +import { title3ClassNames } from '@fluentui/react-text'; +import { ToggleButton } from '@fluentui/react-button'; +import { toggleButtonClassNames } from '@fluentui/react-button'; +import { ToggleButtonProps } from '@fluentui/react-button'; +import { ToggleButtonState } from '@fluentui/react-button'; +import { tokens } from '@fluentui/react-theme'; +import { Toolbar } from '@fluentui/react-toolbar'; +import { ToolbarButton } from '@fluentui/react-toolbar'; +import { ToolbarButtonProps } from '@fluentui/react-toolbar'; +import { ToolbarButtonState } from '@fluentui/react-toolbar'; +import { toolbarClassNames } from '@fluentui/react-toolbar'; +import { ToolbarContextValue } from '@fluentui/react-toolbar'; +import { ToolbarContextValues } from '@fluentui/react-toolbar'; +import { ToolbarDivider } from '@fluentui/react-toolbar'; +import { ToolbarDividerProps } from '@fluentui/react-toolbar'; +import { ToolbarDividerState } from '@fluentui/react-toolbar'; +import { ToolbarGroup } from '@fluentui/react-toolbar'; +import { toolbarGroupClassNames } from '@fluentui/react-toolbar'; +import { ToolbarGroupProps } from '@fluentui/react-toolbar'; +import { ToolbarGroupState } from '@fluentui/react-toolbar'; +import { ToolbarProps } from '@fluentui/react-toolbar'; +import { ToolbarRadioButton } from '@fluentui/react-toolbar'; +import { ToolbarRadioButtonProps } from '@fluentui/react-toolbar'; +import { ToolbarRadioButtonState } from '@fluentui/react-toolbar'; +import { ToolbarRadioGroup } from '@fluentui/react-toolbar'; +import { ToolbarRadioGroupProps } from '@fluentui/react-toolbar'; +import { ToolbarRadioGroupState } from '@fluentui/react-toolbar'; +import { ToolbarSlots } from '@fluentui/react-toolbar'; +import { ToolbarState } from '@fluentui/react-toolbar'; +import { ToolbarToggleButton } from '@fluentui/react-toolbar'; +import { ToolbarToggleButtonProps } from '@fluentui/react-toolbar'; +import { ToolbarToggleButtonState } from '@fluentui/react-toolbar'; +import { Tooltip } from '@fluentui/react-tooltip'; +import { tooltipClassNames } from '@fluentui/react-tooltip'; +import { TooltipProps } from '@fluentui/react-tooltip'; +import { TooltipSlots } from '@fluentui/react-tooltip'; +import { TooltipState } from '@fluentui/react-tooltip'; +import { TooltipTriggerProps } from '@fluentui/react-tooltip'; +import { TypographyStyle } from '@fluentui/react-theme'; +import { TypographyStyles } from '@fluentui/react-theme'; +import { typographyStyles } from '@fluentui/react-theme'; +import { UninitializedMenuListState } from '@fluentui/react-menu'; +import { useAccordion_unstable } from '@fluentui/react-accordion'; +import { useAccordionContext_unstable } from '@fluentui/react-accordion'; +import { useAccordionContextValues_unstable } from '@fluentui/react-accordion'; +import { useAccordionHeader_unstable } from '@fluentui/react-accordion'; +import { useAccordionHeaderContextValues_unstable } from '@fluentui/react-accordion'; +import { useAccordionHeaderStyles_unstable } from '@fluentui/react-accordion'; +import { useAccordionItem_unstable } from '@fluentui/react-accordion'; +import { useAccordionItemContext_unstable } from '@fluentui/react-accordion'; +import { useAccordionItemContextValues_unstable } from '@fluentui/react-accordion'; +import { useAccordionItemStyles_unstable } from '@fluentui/react-accordion'; +import { useAccordionPanel_unstable } from '@fluentui/react-accordion'; +import { useAccordionPanelStyles_unstable } from '@fluentui/react-accordion'; +import { useAccordionStyles_unstable } from '@fluentui/react-accordion'; +import { useArrowNavigationGroup } from '@fluentui/react-tabster'; +import { UseArrowNavigationGroupOptions } from '@fluentui/react-tabster'; +import { useAvatar_unstable } from '@fluentui/react-avatar'; +import { useAvatarGroup_unstable } from '@fluentui/react-avatar'; +import { useAvatarGroupContext_unstable } from '@fluentui/react-avatar'; +import { useAvatarGroupContextValues } from '@fluentui/react-avatar'; +import { useAvatarGroupItem_unstable } from '@fluentui/react-avatar'; +import { useAvatarGroupItemStyles_unstable } from '@fluentui/react-avatar'; +import { useAvatarGroupPopover_unstable } from '@fluentui/react-avatar'; +import { useAvatarGroupPopoverStyles_unstable } from '@fluentui/react-avatar'; +import { useAvatarGroupStyles_unstable } from '@fluentui/react-avatar'; +import { useAvatarStyles_unstable } from '@fluentui/react-avatar'; +import { useBadge_unstable } from '@fluentui/react-badge'; +import { useBadgeStyles_unstable } from '@fluentui/react-badge'; +import { useButton_unstable } from '@fluentui/react-button'; +import { useButtonStyles_unstable } from '@fluentui/react-button'; +import { useCard_unstable } from '@fluentui/react-card'; +import { useCardFooter_unstable } from '@fluentui/react-card'; +import { useCardFooterStyles_unstable } from '@fluentui/react-card'; +import { useCardHeader_unstable } from '@fluentui/react-card'; +import { useCardHeaderStyles_unstable } from '@fluentui/react-card'; +import { useCardPreview_unstable } from '@fluentui/react-card'; +import { useCardPreviewStyles_unstable } from '@fluentui/react-card'; +import { useCardStyles_unstable } from '@fluentui/react-card'; +import { useCheckbox_unstable } from '@fluentui/react-checkbox'; +import { useCheckboxStyles_unstable } from '@fluentui/react-checkbox'; +import { useCheckmarkStyles_unstable } from '@fluentui/react-menu'; +import { useCombobox_unstable } from '@fluentui/react-combobox'; +import { useComboboxContextValues } from '@fluentui/react-combobox'; +import { useComboboxStyles_unstable } from '@fluentui/react-combobox'; +import { useCompoundButton_unstable } from '@fluentui/react-button'; +import { useCompoundButtonStyles_unstable } from '@fluentui/react-button'; +import { useCounterBadge_unstable } from '@fluentui/react-badge'; +import { useCounterBadgeStyles_unstable } from '@fluentui/react-badge'; +import { useDataGrid_unstable } from '@fluentui/react-table'; +import { useDataGridBody_unstable } from '@fluentui/react-table'; +import { useDataGridBodyStyles_unstable } from '@fluentui/react-table'; +import { useDataGridCell_unstable } from '@fluentui/react-table'; +import { useDataGridCellStyles_unstable } from '@fluentui/react-table'; +import { useDataGridContextValues_unstable } from '@fluentui/react-table'; +import { useDataGridHeader_unstable } from '@fluentui/react-table'; +import { useDataGridHeaderCell_unstable } from '@fluentui/react-table'; +import { useDataGridHeaderCellStyles_unstable } from '@fluentui/react-table'; +import { useDataGridHeaderStyles_unstable } from '@fluentui/react-table'; +import { useDataGridRow_unstable } from '@fluentui/react-table'; +import { useDataGridRowStyles_unstable } from '@fluentui/react-table'; +import { useDataGridSelectionCell_unstable } from '@fluentui/react-table'; +import { useDataGridSelectionCellStyles_unstable } from '@fluentui/react-table'; +import { useDataGridStyles_unstable } from '@fluentui/react-table'; +import { useDialog_unstable } from '@fluentui/react-dialog'; +import { useDialogActions_unstable } from '@fluentui/react-dialog'; +import { useDialogActionsStyles_unstable } from '@fluentui/react-dialog'; +import { useDialogBody_unstable } from '@fluentui/react-dialog'; +import { useDialogBodyStyles_unstable } from '@fluentui/react-dialog'; +import { useDialogContent_unstable } from '@fluentui/react-dialog'; +import { useDialogContentStyles_unstable } from '@fluentui/react-dialog'; +import { useDialogSurface_unstable } from '@fluentui/react-dialog'; +import { useDialogSurfaceStyles_unstable } from '@fluentui/react-dialog'; +import { useDialogTitle_unstable } from '@fluentui/react-dialog'; +import { useDialogTitleStyles_unstable } from '@fluentui/react-dialog'; +import { useDialogTrigger_unstable } from '@fluentui/react-dialog'; +import { useDivider_unstable } from '@fluentui/react-divider'; +import { useDividerStyles_unstable } from '@fluentui/react-divider'; +import { useDropdown_unstable } from '@fluentui/react-combobox'; +import { useDropdownStyles_unstable } from '@fluentui/react-combobox'; +import { useField_unstable } from '@fluentui/react-field'; +import { useFieldContext_unstable } from '@fluentui/react-field'; +import { useFieldContextValues_unstable } from '@fluentui/react-field'; +import { useFieldControlProps_unstable } from '@fluentui/react-field'; +import { useFieldStyles_unstable } from '@fluentui/react-field'; +import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts'; +import { useFluentProvider_unstable } from '@fluentui/react-provider'; +import { useFluentProviderContextValues_unstable } from '@fluentui/react-provider'; +import { useFluentProviderStyles_unstable } from '@fluentui/react-provider'; +import { useFocusableGroup } from '@fluentui/react-tabster'; +import { UseFocusableGroupOptions } from '@fluentui/react-tabster'; +import { useFocusFinders } from '@fluentui/react-tabster'; +import { useFocusWithin } from '@fluentui/react-tabster'; +import { useId } from '@fluentui/react-utilities'; +import { useImage_unstable } from '@fluentui/react-image'; +import { useImageStyles_unstable } from '@fluentui/react-image'; +import { useInput_unstable } from '@fluentui/react-input'; +import { useInputStyles_unstable } from '@fluentui/react-input'; +import { useIsomorphicLayoutEffect } from '@fluentui/react-utilities'; +import { useIsOverflowGroupVisible } from '@fluentui/react-overflow'; +import { useIsOverflowItemVisible } from '@fluentui/react-overflow'; +import { useIsSSR } from '@fluentui/react-utilities'; +import { useKeyboardNavAttribute } from '@fluentui/react-tabster'; +import { useLabel_unstable } from '@fluentui/react-label'; +import { useLabelStyles_unstable } from '@fluentui/react-label'; +import { useLink_unstable } from '@fluentui/react-link'; +import { useLinkState_unstable } from '@fluentui/react-link'; +import { useLinkStyles_unstable } from '@fluentui/react-link'; +import { useListbox_unstable } from '@fluentui/react-combobox'; +import { useListboxContextValues } from '@fluentui/react-combobox'; +import { useListboxStyles_unstable } from '@fluentui/react-combobox'; +import { useMenu_unstable } from '@fluentui/react-menu'; +import { useMenuButton_unstable } from '@fluentui/react-button'; +import { useMenuButtonStyles_unstable } from '@fluentui/react-button'; +import { useMenuContext_unstable } from '@fluentui/react-menu'; +import { useMenuContextValues_unstable } from '@fluentui/react-menu'; +import { useMenuDivider_unstable } from '@fluentui/react-menu'; +import { useMenuDividerStyles_unstable } from '@fluentui/react-menu'; +import { useMenuGroup_unstable } from '@fluentui/react-menu'; +import { useMenuGroupContext_unstable } from '@fluentui/react-menu'; +import { useMenuGroupContextValues_unstable } from '@fluentui/react-menu'; +import { useMenuGroupHeader_unstable } from '@fluentui/react-menu'; +import { useMenuGroupHeaderStyles_unstable } from '@fluentui/react-menu'; +import { useMenuGroupStyles_unstable } from '@fluentui/react-menu'; +import { useMenuItem_unstable } from '@fluentui/react-menu'; +import { useMenuItemCheckbox_unstable } from '@fluentui/react-menu'; +import { useMenuItemCheckboxStyles_unstable } from '@fluentui/react-menu'; +import { useMenuItemRadio_unstable } from '@fluentui/react-menu'; +import { useMenuItemRadioStyles_unstable } from '@fluentui/react-menu'; +import { useMenuItemStyles_unstable } from '@fluentui/react-menu'; +import { useMenuList_unstable } from '@fluentui/react-menu'; +import { useMenuListContext_unstable } from '@fluentui/react-menu'; +import { useMenuListContextValues_unstable } from '@fluentui/react-menu'; +import { useMenuListStyles_unstable } from '@fluentui/react-menu'; +import { useMenuPopover_unstable } from '@fluentui/react-menu'; +import { useMenuPopoverStyles_unstable } from '@fluentui/react-menu'; +import { useMenuSplitGroup_unstable } from '@fluentui/react-menu'; +import { useMenuSplitGroupStyles_unstable } from '@fluentui/react-menu'; +import { useMenuTrigger_unstable } from '@fluentui/react-menu'; +import { useMenuTriggerContext_unstable } from '@fluentui/react-menu'; +import { useMergedRefs } from '@fluentui/react-utilities'; +import { useModalAttributes } from '@fluentui/react-tabster'; +import { UseModalAttributesOptions } from '@fluentui/react-tabster'; +import { useMotionPresence } from '@fluentui/react-utilities'; +import { UseMotionPresenceEvents } from '@fluentui/react-utilities'; +import { UseMotionPresenceState } from '@fluentui/react-utilities'; +import { useOption_unstable } from '@fluentui/react-combobox'; +import { useOptionGroup_unstable } from '@fluentui/react-combobox'; +import { useOptionGroupStyles_unstable } from '@fluentui/react-combobox'; +import { useOptionStyles_unstable } from '@fluentui/react-combobox'; +import { useOverflowCount } from '@fluentui/react-overflow'; +import { useOverflowMenu } from '@fluentui/react-overflow'; +import { usePersona_unstable } from '@fluentui/react-persona'; +import { usePersonaStyles_unstable } from '@fluentui/react-persona'; +import { usePopover_unstable } from '@fluentui/react-popover'; +import { usePopoverContext_unstable } from '@fluentui/react-popover'; +import { usePopoverSurface_unstable } from '@fluentui/react-popover'; +import { usePopoverSurfaceStyles_unstable } from '@fluentui/react-popover'; +import { usePopoverTrigger_unstable } from '@fluentui/react-popover'; +import { usePortal_unstable } from '@fluentui/react-portal'; +import { usePresenceBadge_unstable } from '@fluentui/react-badge'; +import { usePresenceBadgeStyles_unstable } from '@fluentui/react-badge'; +import { useProgressBar_unstable } from '@fluentui/react-progress'; +import { useProgressBarStyles_unstable } from '@fluentui/react-progress'; +import { useRadio_unstable } from '@fluentui/react-radio'; +import { useRadioGroup_unstable } from '@fluentui/react-radio'; +import { useRadioGroupContext_unstable } from '@fluentui/react-radio'; +import { useRadioGroupContextValue_unstable } from '@fluentui/react-radio'; +import { useRadioGroupContextValues } from '@fluentui/react-radio'; +import { useRadioGroupStyles_unstable } from '@fluentui/react-radio'; +import { useRadioStyles_unstable } from '@fluentui/react-radio'; +import { useScrollbarWidth } from '@fluentui/react-utilities'; +import { useSelect_unstable } from '@fluentui/react-select'; +import { useSelectStyles_unstable } from '@fluentui/react-select'; +import { useSkeleton_unstable } from '@fluentui/react-skeleton'; +import { useSkeletonContext } from '@fluentui/react-skeleton'; +import { useSkeletonItem_unstable } from '@fluentui/react-skeleton'; +import { useSkeletonItemStyles_unstable } from '@fluentui/react-skeleton'; +import { useSkeletonStyles_unstable } from '@fluentui/react-skeleton'; +import { useSlider_unstable } from '@fluentui/react-slider'; +import { useSliderState_unstable } from '@fluentui/react-slider'; +import { useSliderStyles_unstable } from '@fluentui/react-slider'; +import { useSpinButton_unstable } from '@fluentui/react-spinbutton'; +import { useSpinButtonStyles_unstable } from '@fluentui/react-spinbutton'; +import { useSpinner_unstable } from '@fluentui/react-spinner'; +import { useSpinnerStyles_unstable } from '@fluentui/react-spinner'; +import { useSplitButton_unstable } from '@fluentui/react-button'; +import { useSplitButtonStyles_unstable } from '@fluentui/react-button'; +import { useSwitch_unstable } from '@fluentui/react-switch'; +import { useSwitchStyles_unstable } from '@fluentui/react-switch'; +import { useTab_unstable } from '@fluentui/react-tabs'; +import { useTable_unstable } from '@fluentui/react-table'; +import { useTableBody_unstable } from '@fluentui/react-table'; +import { useTableBodyStyles_unstable } from '@fluentui/react-table'; +import { useTableCell_unstable } from '@fluentui/react-table'; +import { useTableCellActions_unstable } from '@fluentui/react-table'; +import { useTableCellActionsStyles_unstable } from '@fluentui/react-table'; +import { useTableCellLayout_unstable } from '@fluentui/react-table'; +import { useTableCellLayoutStyles_unstable } from '@fluentui/react-table'; +import { useTableCellStyles_unstable } from '@fluentui/react-table'; +import { useTableColumnSizing_unstable } from '@fluentui/react-table'; +import { useTableContext } from '@fluentui/react-table'; +import { useTableFeatures } from '@fluentui/react-table'; +import { UseTableFeaturesOptions } from '@fluentui/react-table'; +import { useTableHeader_unstable } from '@fluentui/react-table'; +import { useTableHeaderCell_unstable } from '@fluentui/react-table'; +import { useTableHeaderCellStyles_unstable } from '@fluentui/react-table'; +import { useTableHeaderStyles_unstable } from '@fluentui/react-table'; +import { useTableResizeHandle_unstable } from '@fluentui/react-table'; +import { useTableResizeHandleStyles_unstable } from '@fluentui/react-table'; +import { useTableRow_unstable } from '@fluentui/react-table'; +import { useTableRowIdContext } from '@fluentui/react-table'; +import { useTableRowStyles_unstable } from '@fluentui/react-table'; +import { useTableSelection } from '@fluentui/react-table'; +import { useTableSelectionCell_unstable } from '@fluentui/react-table'; +import { useTableSelectionCellStyles_unstable } from '@fluentui/react-table'; +import { useTableSort } from '@fluentui/react-table'; +import { useTableStyles_unstable } from '@fluentui/react-table'; +import { useTabList_unstable } from '@fluentui/react-tabs'; +import { useTabListContext_unstable } from '@fluentui/react-tabs'; +import { useTabListContextValues_unstable } from '@fluentui/react-tabs'; +import { useTabListStyles_unstable } from '@fluentui/react-tabs'; +import { useTabStyles_unstable } from '@fluentui/react-tabs'; +import { useText_unstable } from '@fluentui/react-text'; +import { useTextarea_unstable } from '@fluentui/react-textarea'; +import { useTextareaStyles_unstable } from '@fluentui/react-textarea'; +import { useTextStyles_unstable } from '@fluentui/react-text'; +import { useThemeClassName_unstable as useThemeClassName } from '@fluentui/react-shared-contexts'; +import { useToggleButton_unstable } from '@fluentui/react-button'; +import { useToggleButtonStyles_unstable } from '@fluentui/react-button'; +import { useToggleState } from '@fluentui/react-button'; +import { useToolbar_unstable } from '@fluentui/react-toolbar'; +import { useToolbarButton_unstable } from '@fluentui/react-toolbar'; +import { useToolbarButtonStyles_unstable } from '@fluentui/react-toolbar'; +import { useToolbarDivider_unstable } from '@fluentui/react-toolbar'; +import { useToolbarDividerStyles_unstable } from '@fluentui/react-toolbar'; +import { useToolbarGroup_unstable } from '@fluentui/react-toolbar'; +import { useToolbarGroupStyles_unstable } from '@fluentui/react-toolbar'; +import { useToolbarRadioButton_unstable } from '@fluentui/react-toolbar'; +import { useToolbarRadioButtonStyles_unstable } from '@fluentui/react-toolbar'; +import { useToolbarStyles_unstable } from '@fluentui/react-toolbar'; +import { useToolbarToggleButton_unstable } from '@fluentui/react-toolbar'; +import { useToolbarToggleButtonStyles_unstable } from '@fluentui/react-toolbar'; +import { useTooltip_unstable } from '@fluentui/react-tooltip'; +import { useTooltipStyles_unstable } from '@fluentui/react-tooltip'; +import { useTooltipVisibility_unstable as useTooltipVisibility } from '@fluentui/react-shared-contexts'; +import { VerticalSpacingTokens } from '@fluentui/react-theme'; +import { webDarkTheme } from '@fluentui/react-theme'; +import { webLightTheme } from '@fluentui/react-theme'; export { __css } @@ -39,4522 +1036,2043 @@ export { __resetStyles } export { __styles } -// @public -export const Accordion: ForwardRefComponent; - -// @public (undocumented) -export const accordionClassNames: SlotClassNames; +export { Accordion } + +export { accordionClassNames } + +export { AccordionContextValue } + +export { AccordionContextValues } + +export { AccordionHeader } + +export { accordionHeaderClassNames } + +export { AccordionHeaderContextValue } + +export { AccordionHeaderContextValues } + +export { AccordionHeaderExpandIconPosition } + +export { AccordionHeaderProps } + +export { AccordionHeaderSize } + +export { AccordionHeaderSlots } + +export { AccordionHeaderState } + +export { AccordionIndex } + +export { AccordionItem } + +export { accordionItemClassNames } + +export { AccordionItemContextValue } + +export { AccordionItemContextValues } + +export { AccordionItemProps } + +export { AccordionItemProvider } + +export { AccordionItemSlots } + +export { AccordionItemState } + +export { AccordionItemValue } + +export { AccordionPanel } + +export { accordionPanelClassNames } + +export { AccordionPanelProps } + +export { AccordionPanelSlots } + +export { AccordionPanelState } + +export { AccordionProps } + +export { AccordionProvider } + +export { AccordionSlots } + +export { AccordionState } + +export { AccordionToggleData } + +export { AccordionToggleEvent } + +export { AccordionToggleEventHandler } + +export { arrowHeights } + +export { Avatar } + +export { avatarClassNames } + +export { AvatarGroup } + +export { avatarGroupClassNames } + +export { AvatarGroupContextValue } + +export { AvatarGroupContextValues } + +export { AvatarGroupItem } + +export { avatarGroupItemClassNames } + +export { AvatarGroupItemProps } + +export { AvatarGroupItemSlots } + +export { AvatarGroupItemState } + +export { AvatarGroupPopover } + +export { avatarGroupPopoverClassNames } + +export { AvatarGroupPopoverProps } + +export { AvatarGroupPopoverSlots } + +export { AvatarGroupPopoverState } + +export { AvatarGroupProps } + +export { AvatarGroupProvider } + +export { AvatarGroupSlots } + +export { AvatarGroupState } + +export { AvatarNamedColor } + +export { AvatarProps } + +export { AvatarSize } + +export { AvatarSizes } + +export { AvatarSlots } + +export { AvatarState } + +export { Badge } + +export { badgeClassNames } + +export { BadgeProps } + +export { BadgeSlots } + +export { BadgeState } + +export { Body1 } + +export { body1ClassNames } + +export { Body1Strong } + +export { body1StrongClassNames } + +export { Body1Stronger } + +export { body1StrongerClassNames } + +export { Body2 } + +export { body2ClassNames } + +export { BorderRadiusTokens } + +export { BrandVariants } + +export { Button } + +export { buttonClassNames } + +export { ButtonProps } + +export { ButtonSlots } + +export { ButtonState } + +export { Caption1 } + +export { caption1ClassNames } + +export { Caption1Strong } + +export { caption1StrongClassNames } + +export { Caption1Stronger } + +export { caption1StrongerClassNames } + +export { Caption2 } + +export { caption2ClassNames } + +export { Caption2Strong } + +export { caption2StrongClassNames } + +export { Card } + +export { cardClassNames } + +export { cardCSSVars } + +export { CardFooter } + +export { cardFooterClassNames } + +export { CardFooterProps } + +export { CardFooterSlots } + +export { CardFooterState } + +export { CardHeader } + +export { cardHeaderClassNames } + +export { cardHeaderCSSVars } + +export { CardHeaderProps } + +export { CardHeaderSlots } + +export { CardHeaderState } + +export { CardPreview } + +export { cardPreviewClassNames } + +export { CardPreviewProps } + +export { CardPreviewSlots } + +export { CardPreviewState } + +export { CardProps } + +export { CardSlots } + +export { CardState } + +export { Checkbox } + +export { checkboxClassNames } + +export { CheckboxOnChangeData } + +export { CheckboxProps } + +export { CheckboxSlots } + +export { CheckboxState } + +export { ColorPaletteTokens } + +export { ColorTokens } + +export { Combobox } + +export { comboboxClassNames } + +export { ComboboxContextValue } + +export { ComboboxContextValues } + +export { ComboboxOpenChangeData } + +export { ComboboxOpenEvents } -// @public (undocumented) -export type AccordionContextValue = Required> & Pick & { - openItems: AccordionItemValue[]; - requestToggle: (event: AccordionToggleEvent, data: AccordionToggleData) => void; -}; +export { ComboboxProps } -// @public (undocumented) -export type AccordionContextValues = { - accordion: AccordionContextValue; -}; +export { ComboboxProvider } -// @public -export const AccordionHeader: ForwardRefComponent; - -// @public (undocumented) -export const accordionHeaderClassNames: SlotClassNames; - -// @public (undocumented) -export type AccordionHeaderContextValue = Required> & { - disabled: boolean; - open: boolean; -}; - -// @public (undocumented) -export type AccordionHeaderContextValues = { - accordionHeader: AccordionHeaderContextValue; -}; - -// @public (undocumented) -export type AccordionHeaderExpandIconPosition = 'start' | 'end'; - -// @public (undocumented) -export type AccordionHeaderProps = ComponentProps> & { - expandIconPosition?: AccordionHeaderExpandIconPosition; - inline?: boolean; - size?: AccordionHeaderSize; -}; - -// @public (undocumented) -export type AccordionHeaderSize = 'small' | 'medium' | 'large' | 'extra-large'; - -// @public (undocumented) -export type AccordionHeaderSlots = { - root: NonNullable>; - button: NonNullable>>; - expandIcon?: Slot<'span'>; - icon?: Slot<'div'>; -}; - -// @public (undocumented) -export type AccordionHeaderState = ComponentState & Required> & AccordionHeaderContextValue; +export { ComboboxSlots } -// @public (undocumented) -export type AccordionIndex = number | number[]; +export { ComboboxState } -// @public -export const AccordionItem: ForwardRefComponent; +export { ComponentProps } -// @public (undocumented) -export const accordionItemClassNames: SlotClassNames; - -// @public (undocumented) -export type AccordionItemContextValue = Required> & { - onHeaderClick(ev: React_2.MouseEvent | React_2.KeyboardEvent): void; - open: boolean; -}; - -// @public (undocumented) -export type AccordionItemContextValues = { - accordionItem: AccordionItemContextValue; -}; +export { ComponentState } -// @public (undocumented) -export type AccordionItemProps = ComponentProps & { - disabled?: boolean; - value: AccordionItemValue; -}; +export { CompoundButton } -// @public (undocumented) -export const AccordionItemProvider: React_2.Provider; - -// @public (undocumented) -export type AccordionItemSlots = { - root: NonNullable>; -}; - -// @public (undocumented) -export type AccordionItemState = ComponentState & AccordionItemContextValue; - -// @public (undocumented) -export type AccordionItemValue = unknown; - -// @public -export const AccordionPanel: ForwardRefComponent; - -// @public (undocumented) -export const accordionPanelClassNames: SlotClassNames; - -// @public (undocumented) -export type AccordionPanelProps = ComponentProps; - -// @public (undocumented) -export type AccordionPanelSlots = { - root: NonNullable>; -}; - -// @public (undocumented) -export type AccordionPanelState = ComponentState & { - open: boolean; -}; - -// @public (undocumented) -export type AccordionProps = ComponentProps & { - defaultOpenItems?: AccordionItemValue | AccordionItemValue[]; - collapsible?: boolean; - multiple?: boolean; - navigation?: 'linear' | 'circular'; - onToggle?: AccordionToggleEventHandler; - openItems?: AccordionItemValue | AccordionItemValue[]; -}; - -// @public (undocumented) -export const AccordionProvider: Provider & FC>; - -// @public (undocumented) -export type AccordionSlots = { - root: NonNullable>; -}; - -// @public (undocumented) -export type AccordionState = ComponentState & AccordionContextValue; - -// @public (undocumented) -export type AccordionToggleData = { - value: AccordionItemValue; -}; - -// @public (undocumented) -export type AccordionToggleEvent = React_2.MouseEvent | React_2.KeyboardEvent; - -// @public (undocumented) -export type AccordionToggleEventHandler = (event: AccordionToggleEvent, data: AccordionToggleData) => void; - -// @public (undocumented) -export const arrowHeights: Record; - -// @public (undocumented) -export const Avatar: ForwardRefComponent; - -// @public (undocumented) -export const avatarClassNames: SlotClassNames; - -// @public -export const AvatarGroup: ForwardRefComponent; - -// @public (undocumented) -export const avatarGroupClassNames: SlotClassNames; - -// @public (undocumented) -export type AvatarGroupContextValue = Pick & { - isOverflow?: boolean; -}; - -// @public (undocumented) -export type AvatarGroupContextValues = { - avatarGroup: AvatarGroupContextValue; -}; - -// @public -export const AvatarGroupItem: ForwardRefComponent; - -// @public (undocumented) -export const avatarGroupItemClassNames: SlotClassNames; - -// @public -export type AvatarGroupItemProps = Omit, 'avatar'>, 'size' | 'shape'>; - -// @public (undocumented) -export type AvatarGroupItemSlots = { - root: NonNullable>; - avatar: NonNullable>; - overflowLabel: NonNullable>; -}; - -// @public -export type AvatarGroupItemState = ComponentState & { - isOverflowItem?: boolean; - layout: AvatarGroupProps['layout']; - size: AvatarSize; -}; - -// @public -export const AvatarGroupPopover: React_2.FC; - -// @public (undocumented) -export const avatarGroupPopoverClassNames: SlotClassNames; - -// @public -export type AvatarGroupPopoverProps = Omit>, 'children'> & { - indicator?: 'count' | 'icon'; - count?: number; - children: React_2.ReactNode; -}; - -// @public (undocumented) -export type AvatarGroupPopoverSlots = { - root: NonNullable>; - triggerButton: NonNullable>; - content: NonNullable>; - popoverSurface: NonNullable>; - tooltip: NonNullable>; -}; - -// @public -export type AvatarGroupPopoverState = ComponentState & Required> & { - popoverOpen: boolean; - layout: AvatarGroupProps['layout']; - size: AvatarSize; -}; - -// @public -export type AvatarGroupProps = ComponentProps & { - layout?: 'spread' | 'stack' | 'pie'; - size?: AvatarSize; -}; - -// @public (undocumented) -export const AvatarGroupProvider: Provider & FC>; - -// @public (undocumented) -export type AvatarGroupSlots = { - root: NonNullable>; -}; - -// @public -export type AvatarGroupState = ComponentState & Required>; - -// @public -export type AvatarNamedColor = 'dark-red' | 'cranberry' | 'red' | 'pumpkin' | 'peach' | 'marigold' | 'gold' | 'brass' | 'brown' | 'forest' | 'seafoam' | 'dark-green' | 'light-teal' | 'teal' | 'steel' | 'blue' | 'royal-blue' | 'cornflower' | 'navy' | 'lavender' | 'purple' | 'grape' | 'lilac' | 'pink' | 'magenta' | 'plum' | 'beige' | 'mink' | 'platinum' | 'anchor'; - -// @public -export type AvatarProps = Omit, 'color'> & { - active?: 'active' | 'inactive' | 'unset'; - activeAppearance?: 'ring' | 'shadow' | 'ring-shadow'; - color?: 'neutral' | 'brand' | 'colorful' | AvatarNamedColor; - idForColor?: string | undefined; - name?: string; - shape?: AvatarShape; - size?: AvatarSize; -}; - -// @public -export type AvatarSize = 16 | 20 | 24 | 28 | 32 | 36 | 40 | 48 | 56 | 64 | 72 | 96 | 120 | 128; - -// @public @deprecated -export type AvatarSizes = AvatarSize; - -// @public (undocumented) -export type AvatarSlots = { - root: Slot<'span'>; - image?: Slot<'img'>; - initials?: Slot<'span'>; - icon?: Slot<'span'>; - badge?: Slot; -}; - -// @public -export type AvatarState = ComponentState & Required> & { - color: NonNullable>; - activeAriaLabelElement?: JSX.Element; -}; - -// @public -export const Badge: ForwardRefComponent; - -// @public (undocumented) -export const badgeClassNames: SlotClassNames; - -// @public (undocumented) -export type BadgeProps = Omit, 'color'> & { - appearance?: 'filled' | 'ghost' | 'outline' | 'tint'; - color?: 'brand' | 'danger' | 'important' | 'informative' | 'severe' | 'subtle' | 'success' | 'warning'; - iconPosition?: 'before' | 'after'; - shape?: 'circular' | 'rounded' | 'square'; - size?: 'tiny' | 'extra-small' | 'small' | 'medium' | 'large' | 'extra-large'; -}; - -// @public (undocumented) -export type BadgeSlots = { - root: Slot<'div'>; - icon?: Slot<'span'>; -}; - -// @public (undocumented) -export type BadgeState = ComponentState & Required>; - -// @public -export const Body1: FunctionComponent; - -// @public (undocumented) -export const body1ClassNames: SlotClassNames; - -// @public -export const Body1Strong: FunctionComponent; - -// @public (undocumented) -export const body1StrongClassNames: SlotClassNames; - -// @public -export const Body1Stronger: FunctionComponent; - -// @public (undocumented) -export const body1StrongerClassNames: SlotClassNames; - -// @public -export const Body2: FunctionComponent; - -// @public (undocumented) -export const body2ClassNames: SlotClassNames; - -// @public (undocumented) -export type BorderRadiusTokens = { - borderRadiusNone: string; - borderRadiusSmall: string; - borderRadiusMedium: string; - borderRadiusLarge: string; - borderRadiusXLarge: string; - borderRadiusCircular: string; -}; - -// @public (undocumented) -export type BrandVariants = Record; - -// @public -export const Button: ForwardRefComponent; - -// @public (undocumented) -export const buttonClassNames: SlotClassNames; - -// @public (undocumented) -export type ButtonProps = ComponentProps & { - appearance?: 'secondary' | 'primary' | 'outline' | 'subtle' | 'transparent'; - disabledFocusable?: boolean; - disabled?: boolean; - iconPosition?: 'before' | 'after'; - shape?: 'rounded' | 'circular' | 'square'; - size?: ButtonSize; -}; +export { compoundButtonClassNames } -// @public (undocumented) -export type ButtonSlots = { - root: NonNullable>>; - icon?: Slot<'span'>; -}; +export { CompoundButtonProps } -// @public (undocumented) -export type ButtonState = ComponentState & Required> & { - iconOnly: boolean; -}; - -// @public -export const Caption1: FunctionComponent; +export { CompoundButtonSlots } -// @public (undocumented) -export const caption1ClassNames: SlotClassNames; +export { CompoundButtonState } -// @public -export const Caption1Strong: FunctionComponent; +export { CounterBadge } -// @public (undocumented) -export const caption1StrongClassNames: SlotClassNames; +export { counterBadgeClassNames } -// @public -export const Caption1Stronger: FunctionComponent; +export { CounterBadgeProps } -// @public (undocumented) -export const caption1StrongerClassNames: SlotClassNames; - -// @public -export const Caption2: FunctionComponent; - -// @public (undocumented) -export const caption2ClassNames: SlotClassNames; - -// @public -export const Caption2Strong: FunctionComponent; - -// @public (undocumented) -export const caption2StrongClassNames: SlotClassNames; - -// @public -export const Card: ForwardRefComponent; +export { CounterBadgeState } -// @public -export const cardClassNames: SlotClassNames; +export { createCustomFocusIndicatorStyle } -// @public -export const cardCSSVars: { - cardSizeVar: string; - cardBorderRadiusVar: string; -}; - -// @public -export const CardFooter: ForwardRefComponent; - -// @public -export const cardFooterClassNames: SlotClassNames; - -// @public -export type CardFooterProps = ComponentProps; - -// @public -export type CardFooterSlots = { - root: Slot<'div'>; - action?: Slot<'div'>; -}; - -// @public -export type CardFooterState = ComponentState; - -// @public -export const CardHeader: ForwardRefComponent; - -// @public -export const cardHeaderClassNames: SlotClassNames; - -// @public -export const cardHeaderCSSVars: { - cardHeaderGapVar: string; -}; - -// @public -export type CardHeaderProps = ComponentProps>; - -// @public -export type CardHeaderSlots = { - root: Slot<'div'>; - image: Slot<'div', 'img'>; - header: Slot<'div'>; - description: Slot<'div'>; - action?: Slot<'div'>; -}; - -// @public -export type CardHeaderState = ComponentState; - -// @public -export const CardPreview: ForwardRefComponent; - -// @public -export const cardPreviewClassNames: SlotClassNames; - -// @public -export type CardPreviewProps = ComponentProps; - -// @public -export type CardPreviewSlots = { - root: Slot<'div'>; - logo?: Slot<'div', 'img'>; -}; - -// @public -export type CardPreviewState = ComponentState; - -// @public -export type CardProps = ComponentProps & { - appearance?: 'filled' | 'filled-alternative' | 'outline' | 'subtle'; - focusMode?: 'off' | 'no-tab' | 'tab-exit' | 'tab-only'; - orientation?: 'horizontal' | 'vertical'; - size?: 'small' | 'medium' | 'large'; - selected?: boolean; - defaultSelected?: boolean; - onSelectionChange?: (event: CardOnSelectionChangeEvent, data: CardOnSelectData) => void; -}; - -// @public -export type CardSlots = { - root: Slot<'div'>; - floatingAction?: Slot<'div'>; - checkbox?: Slot<'input'>; -}; - -// @public -export type CardState = ComponentState & CardContextValue & Required & { - interactive: boolean; - selectable: boolean; - selected: boolean; - selectFocused: boolean; -}>; - -// @public -export const Checkbox: ForwardRefComponent; - -// @public (undocumented) -export const checkboxClassNames: SlotClassNames; - -// @public -export interface CheckboxOnChangeData { - // (undocumented) - checked: 'mixed' | boolean; -} - -// @public -export type CheckboxProps = Omit, 'input'>, 'checked' | 'defaultChecked' | 'onChange' | 'size'> & { - checked?: 'mixed' | boolean; - children?: never; - defaultChecked?: 'mixed' | boolean; - labelPosition?: 'before' | 'after'; - onChange?: (ev: React_2.ChangeEvent, data: CheckboxOnChangeData) => void; - shape?: 'square' | 'circular'; - size?: 'medium' | 'large'; -}; - -// @public (undocumented) -export type CheckboxSlots = { - root: NonNullable>; - label?: Slot; - input: NonNullable>; - indicator: Slot<'div'>; -}; - -// @public -export type CheckboxState = ComponentState & Required>; - -// @public (undocumented) -export type ColorPaletteTokens = StatusColorPaletteTokens & PersonaColorPaletteTokens; - -// @public -export type ColorTokens = { - colorNeutralForeground1: string; - colorNeutralForeground1Hover: string; - colorNeutralForeground1Pressed: string; - colorNeutralForeground1Selected: string; - colorNeutralForeground2: string; - colorNeutralForeground2Hover: string; - colorNeutralForeground2Pressed: string; - colorNeutralForeground2Selected: string; - colorNeutralForeground2BrandHover: string; - colorNeutralForeground2BrandPressed: string; - colorNeutralForeground2BrandSelected: string; - colorNeutralForeground3: string; - colorNeutralForeground3Hover: string; - colorNeutralForeground3Pressed: string; - colorNeutralForeground3Selected: string; - colorNeutralForeground3BrandHover: string; - colorNeutralForeground3BrandPressed: string; - colorNeutralForeground3BrandSelected: string; - colorNeutralForeground4: string; - colorNeutralForegroundDisabled: string; - colorNeutralForegroundInvertedDisabled: string; - colorBrandForegroundLink: string; - colorBrandForegroundLinkHover: string; - colorBrandForegroundLinkPressed: string; - colorBrandForegroundLinkSelected: string; - colorNeutralForeground2Link: string; - colorNeutralForeground2LinkHover: string; - colorNeutralForeground2LinkPressed: string; - colorNeutralForeground2LinkSelected: string; - colorCompoundBrandForeground1: string; - colorCompoundBrandForeground1Hover: string; - colorCompoundBrandForeground1Pressed: string; - colorBrandForeground1: string; - colorBrandForeground2: string; - colorNeutralForeground1Static: string; - colorNeutralForegroundInverted: string; - colorNeutralForegroundInvertedHover: string; - colorNeutralForegroundInvertedPressed: string; - colorNeutralForegroundInvertedSelected: string; - colorNeutralForegroundInverted2: string; - colorNeutralForegroundOnBrand: string; - colorNeutralForegroundStaticInverted: string; - colorNeutralForegroundInvertedLink: string; - colorNeutralForegroundInvertedLinkHover: string; - colorNeutralForegroundInvertedLinkPressed: string; - colorNeutralForegroundInvertedLinkSelected: string; - colorBrandForegroundInverted: string; - colorBrandForegroundInvertedHover: string; - colorBrandForegroundInvertedPressed: string; - colorBrandForegroundOnLight: string; - colorBrandForegroundOnLightHover: string; - colorBrandForegroundOnLightPressed: string; - colorBrandForegroundOnLightSelected: string; - colorNeutralBackground1: string; - colorNeutralBackground1Hover: string; - colorNeutralBackground1Pressed: string; - colorNeutralBackground1Selected: string; - colorNeutralBackground2: string; - colorNeutralBackground2Hover: string; - colorNeutralBackground2Pressed: string; - colorNeutralBackground2Selected: string; - colorNeutralBackground3: string; - colorNeutralBackground3Hover: string; - colorNeutralBackground3Pressed: string; - colorNeutralBackground3Selected: string; - colorNeutralBackground4: string; - colorNeutralBackground4Hover: string; - colorNeutralBackground4Pressed: string; - colorNeutralBackground4Selected: string; - colorNeutralBackground5: string; - colorNeutralBackground5Hover: string; - colorNeutralBackground5Pressed: string; - colorNeutralBackground5Selected: string; - colorNeutralBackground6: string; - colorNeutralBackgroundInverted: string; - colorNeutralBackgroundStatic: string; - colorNeutralBackgroundAlpha: string; - colorNeutralBackgroundAlpha2: string; - colorSubtleBackground: string; - colorSubtleBackgroundHover: string; - colorSubtleBackgroundPressed: string; - colorSubtleBackgroundSelected: string; - colorSubtleBackgroundLightAlphaHover: string; - colorSubtleBackgroundLightAlphaPressed: string; - colorSubtleBackgroundLightAlphaSelected: string; - colorSubtleBackgroundInverted: string; - colorSubtleBackgroundInvertedHover: string; - colorSubtleBackgroundInvertedPressed: string; - colorSubtleBackgroundInvertedSelected: string; - colorTransparentBackground: string; - colorTransparentBackgroundHover: string; - colorTransparentBackgroundPressed: string; - colorTransparentBackgroundSelected: string; - colorNeutralBackgroundDisabled: string; - colorNeutralBackgroundInvertedDisabled: string; - colorNeutralStencil1: string; - colorNeutralStencil2: string; - colorNeutralStencil1Alpha: string; - colorNeutralStencil2Alpha: string; - colorBackgroundOverlay: string; - colorScrollbarOverlay: string; - colorBrandBackground: string; - colorBrandBackgroundHover: string; - colorBrandBackgroundPressed: string; - colorBrandBackgroundSelected: string; - colorCompoundBrandBackground: string; - colorCompoundBrandBackgroundHover: string; - colorCompoundBrandBackgroundPressed: string; - colorBrandBackgroundStatic: string; - colorBrandBackground2: string; - colorBrandBackgroundInverted: string; - colorBrandBackgroundInvertedHover: string; - colorBrandBackgroundInvertedPressed: string; - colorBrandBackgroundInvertedSelected: string; - colorNeutralStrokeAccessible: string; - colorNeutralStrokeAccessibleHover: string; - colorNeutralStrokeAccessiblePressed: string; - colorNeutralStrokeAccessibleSelected: string; - colorNeutralStroke1: string; - colorNeutralStroke1Hover: string; - colorNeutralStroke1Pressed: string; - colorNeutralStroke1Selected: string; - colorNeutralStroke2: string; - colorNeutralStroke3: string; - colorNeutralStrokeOnBrand: string; - colorNeutralStrokeOnBrand2: string; - colorNeutralStrokeOnBrand2Hover: string; - colorNeutralStrokeOnBrand2Pressed: string; - colorNeutralStrokeOnBrand2Selected: string; - colorBrandStroke1: string; - colorBrandStroke2: string; - colorCompoundBrandStroke: string; - colorCompoundBrandStrokeHover: string; - colorCompoundBrandStrokePressed: string; - colorNeutralStrokeDisabled: string; - colorNeutralStrokeInvertedDisabled: string; - colorTransparentStroke: string; - colorTransparentStrokeInteractive: string; - colorTransparentStrokeDisabled: string; - colorNeutralStrokeAlpha: string; - colorStrokeFocus1: string; - colorStrokeFocus2: string; - colorNeutralShadowAmbient: string; - colorNeutralShadowKey: string; - colorNeutralShadowAmbientLighter: string; - colorNeutralShadowKeyLighter: string; - colorNeutralShadowAmbientDarker: string; - colorNeutralShadowKeyDarker: string; - colorBrandShadowAmbient: string; - colorBrandShadowKey: string; -}; - -// @public -export const Combobox: ForwardRefComponent; - -// @public (undocumented) -export const comboboxClassNames: SlotClassNames; - -// @public -export type ComboboxContextValue = Pick; - -// @public (undocumented) -export type ComboboxContextValues = ComboboxBaseContextValues; - -// @public (undocumented) -export type ComboboxOpenChangeData = ComboboxBaseOpenChangeData; - -// @public (undocumented) -export type ComboboxOpenEvents = ComboboxBaseOpenEvents; - -// @public -export type ComboboxProps = Omit, 'input'>, 'children' | 'size'> & ComboboxBaseProps & { - freeform?: boolean; - children?: React_2.ReactNode; -}; - -// @public (undocumented) -export const ComboboxProvider: Provider & FC>; - -// @public (undocumented) -export type ComboboxSlots = { - root: NonNullable>; - expandIcon: Slot<'span'>; - input: NonNullable>; - listbox?: Slot; -}; - -// @public -export type ComboboxState = ComponentState & ComboboxBaseState; - -// @public -export type ComponentProps = Omit & PropsWithoutRef>; - -// @public -export type ComponentState = { - components: { - [Key in keyof Slots]-?: React_2.ComponentType> | (ExtractSlotProps extends AsIntrinsicElement ? As : keyof JSX.IntrinsicElements); - }; -} & { - [Key in keyof Slots]: ReplaceNullWithUndefined>; -}; - -// @public -export const CompoundButton: ForwardRefComponent; - -// @public (undocumented) -export const compoundButtonClassNames: SlotClassNames; - -// @public (undocumented) -export type CompoundButtonProps = ComponentProps> & Pick; - -// @public (undocumented) -export type CompoundButtonSlots = ButtonSlots & { - secondaryContent?: Slot<'span'>; - contentContainer: NonNullable>; -}; - -// @public (undocumented) -export type CompoundButtonState = ComponentState & Omit; - -// @public -export const CounterBadge: ForwardRefComponent; - -// @public (undocumented) -export const counterBadgeClassNames: SlotClassNames; - -// @public (undocumented) -export type CounterBadgeProps = Omit & { - appearance?: 'filled' | 'ghost'; - color?: Extract; - count?: number; - dot?: boolean; - overflowCount?: number; - shape?: 'circular' | 'rounded'; - showZero?: boolean; -}; - -// @public (undocumented) -export type CounterBadgeState = Omit & Required>; - -// @public -export function createCustomFocusIndicatorStyle(style: TStyle, { selector }?: CreateCustomFocusIndicatorStyleOptions): TStyle extends GriffelStyle ? GriffelStyle : GriffelResetStyle; - -// @public (undocumented) -export interface CreateCustomFocusIndicatorStyleOptions { - // @deprecated - enableOutline?: boolean; - selector?: 'focus' | 'focus-within'; -} - -// @public (undocumented) -export const createDarkTheme: (brand: BrandVariants) => Theme; +export { CreateCustomFocusIndicatorStyleOptions } + +export { createDarkTheme } export { createDOMRenderer } -// @public -export const createFocusOutlineStyle: ({ enableOutline, selector, style, }?: CreateFocusOutlineStyleOptions) => GriffelStyle; - -// @public (undocumented) -export interface CreateFocusOutlineStyleOptions extends Omit { - enableOutline?: boolean; - // (undocumented) - style?: Partial; -} - -// @public (undocumented) -export const createHighContrastTheme: () => Theme; - -// @public (undocumented) -export const createLightTheme: (brand: BrandVariants) => Theme; - -// @public -export function createTableColumn(options: CreateTableColumnOptions): { - columnId: TableColumnId; - renderCell: (item: TItem) => ReactNode; - renderHeaderCell: () => ReactNode; - compare: (a: TItem, b: TItem) => number; -}; - -// @public (undocumented) -export interface CreateTableColumnOptions extends Partial> { - // (undocumented) - columnId: TableColumnId; -} +export { createFocusOutlineStyle } + +export { CreateFocusOutlineStyleOptions } + +export { createHighContrastTheme } + +export { createLightTheme } + +export { createTableColumn } + +export { CreateTableColumnOptions } + +export { createTeamsDarkTheme } + +export { CurveTokens } + +export { DATA_OVERFLOW_DIVIDER } + +export { DATA_OVERFLOW_ITEM } + +export { DATA_OVERFLOW_MENU } + +export { DATA_OVERFLOWING } + +export { DataGrid } + +export { DataGridBody } + +export { dataGridBodyClassNames } + +export { DataGridBodyProps } + +export { DataGridBodySlots } + +export { DataGridBodyState } + +export { DataGridCell } + +export { dataGridCellClassNames } + +export { DataGridCellProps } + +export { DataGridCellSlots } + +export { DataGridCellState } + +export { dataGridClassNames } + +export { DataGridContextValue } + +export { DataGridContextValues } + +export { DataGridHeader } + +export { DataGridHeaderCell } + +export { dataGridHeaderCellClassNames } + +export { DataGridHeaderCellProps } + +export { DataGridHeaderCellSlots } + +export { DataGridHeaderCellState } + +export { dataGridHeaderClassNames } + +export { DataGridHeaderProps } + +export { DataGridHeaderSlots } + +export { DataGridHeaderState } + +export { DataGridProps } + +export { DataGridRow } + +export { dataGridRowClassNames } + +export { DataGridRowProps } + +export { DataGridRowSlots } + +export { DataGridRowState } + +export { DataGridSelectionCell } + +export { dataGridSelectionCellClassNames } + +export { DataGridSelectionCellProps } + +export { DataGridSelectionCellSlots } + +export { DataGridSelectionCellState } + +export { DataGridSlots } + +export { DataGridState } + +export { Dialog } + +export { DialogActions } + +export { dialogActionsClassNames } + +export { DialogActionsPosition } + +export { DialogActionsProps } + +export { DialogActionsSlots } + +export { DialogActionsState } + +export { DialogBody } + +export { dialogBodyClassNames } + +export { DialogBodyProps } + +export { DialogBodySlots } + +export { DialogBodyState } + +export { DialogContent } + +export { dialogContentClassNames } + +export { DialogContentProps } -// @public (undocumented) -export const createTeamsDarkTheme: (brand: BrandVariants) => Theme; +export { DialogContentSlots } -// @public (undocumented) -export type CurveTokens = { - curveAccelerateMax: string; - curveAccelerateMid: string; - curveAccelerateMin: string; - curveDecelerateMax: string; - curveDecelerateMid: string; - curveDecelerateMin: string; - curveEasyEaseMax: string; - curveEasyEase: string; - curveLinear: string; -}; +export { DialogContentState } -// @public (undocumented) -export const DATA_OVERFLOW_ITEM = "data-overflow-item"; - -// @public (undocumented) -export const DATA_OVERFLOW_MENU = "data-overflow-menu"; - -// @public (undocumented) -export const DATA_OVERFLOWING = "data-overflowing"; - -// @public -export const DataGrid: ForwardRefComponent; - -// @public -export const DataGridBody: ForwardRefComponent & ((props: DataGridBodyProps) => JSX.Element); - -// @public (undocumented) -export const dataGridBodyClassNames: SlotClassNames; - -// @public -export type DataGridBodyProps = Omit & { - children: RowRenderFunction; -}; - -// @public (undocumented) -export type DataGridBodySlots = TableBodySlots; - -// @public -export type DataGridBodyState = TableBodyState & { - rows: TableRowData[]; - renderRow: RowRenderFunction; -}; - -// @public -export const DataGridCell: ForwardRefComponent; - -// @public (undocumented) -export const dataGridCellClassNames: SlotClassNames; - -// @public -export type DataGridCellProps = TableCellProps; - -// @public (undocumented) -export type DataGridCellSlots = TableCellSlots; - -// @public -export type DataGridCellState = TableCellState; - -// @public (undocumented) -export const dataGridClassNames: SlotClassNames; - -// @public (undocumented) -export type DataGridContextValue = TableFeaturesState & { - focusMode: DataGridFocusMode; - selectableRows: boolean; - subtleSelection: boolean; - selectionAppearance: TableRowProps['appearance']; - resizableColumns?: boolean; -}; - -// @public (undocumented) -export type DataGridContextValues = TableContextValues & { - dataGrid: DataGridContextValue; -}; +export { DialogOpenChangeData } -// @public -export const DataGridHeader: ForwardRefComponent; +export { DialogOpenChangeEvent } -// @public -export const DataGridHeaderCell: ForwardRefComponent; - -// @public (undocumented) -export const dataGridHeaderCellClassNames: SlotClassNames; - -// @public -export type DataGridHeaderCellProps = TableHeaderCellProps; +export { DialogProps } -// @public (undocumented) -export type DataGridHeaderCellSlots = TableHeaderCellSlots; +export { DialogSlots } -// @public -export type DataGridHeaderCellState = TableHeaderCellState; +export { DialogState } -// @public (undocumented) -export const dataGridHeaderClassNames: SlotClassNames; +export { DialogSurface } -// @public -export type DataGridHeaderProps = TableHeaderProps; +export { dialogSurfaceClassNames } -// @public (undocumented) -export type DataGridHeaderSlots = TableHeaderSlots; +export { DialogSurfaceProps } -// @public -export type DataGridHeaderState = TableHeaderState; +export { DialogSurfaceSlots } -// @public -export type DataGridProps = TableProps & Pick & Pick, 'focusMode' | 'subtleSelection' | 'selectionAppearance' | 'resizableColumns'> & Pick & Pick & { - onSortChange?: (e: React_2.MouseEvent, sortState: SortState) => void; - onSelectionChange?: (e: React_2.MouseEvent | React_2.KeyboardEvent, data: OnSelectionChangeData) => void; - selectionMode?: SelectionMode_2; - columnSizingOptions?: TableColumnSizingOptions; - onColumnResize?: (e: KeyboardEvent | TouchEvent | MouseEvent | undefined, data: { - columnId: TableColumnId; - width: number; - }) => void; - containerWidthOffset?: number; -}; +export { DialogSurfaceState } -// @public -export const DataGridRow: ForwardRefComponent & ((props: DataGridRowProps) => JSX.Element); +export { DialogTitle } -// @public (undocumented) -export const dataGridRowClassNames: SlotClassNames; +export { dialogTitleClassNames } -// @public -export type DataGridRowProps = Omit & Omit, 'children'> & { - children: CellRenderFunction; -}; +export { DialogTitleProps } -// @public (undocumented) -export type DataGridRowSlots = TableRowSlots & { - selectionCell?: Slot; -}; +export { DialogTitleSlots } -// @public -export type DataGridRowState = TableRowState & ComponentState & { - renderCell: CellRenderFunction; - columnDefs: TableColumnDefinition[]; - dataGridContextValue: DataGridContextValue; -}; +export { DialogTitleState } -// @public -export const DataGridSelectionCell: ForwardRefComponent; +export { DialogTrigger } -// @public (undocumented) -export const dataGridSelectionCellClassNames: SlotClassNames; - -// @public -export type DataGridSelectionCellProps = TableSelectionCellProps; - -// @public (undocumented) -export type DataGridSelectionCellSlots = TableSelectionCellSlots; - -// @public -export type DataGridSelectionCellState = TableSelectionCellState; - -// @public (undocumented) -export type DataGridSlots = TableSlots; - -// @public -export type DataGridState = TableState & { - tableState: TableFeaturesState; -} & Pick; - -// @public -export const Dialog: React_2.FC; - -// @public -export const DialogActions: ForwardRefComponent; +export { DialogTriggerAction } -// @public (undocumented) -export const dialogActionsClassNames: SlotClassNames; - -// @public (undocumented) -export type DialogActionsPosition = 'start' | 'end'; - -// @public -export type DialogActionsProps = ComponentProps & { - position?: DialogActionsPosition; - fluid?: boolean; -}; - -// @public (undocumented) -export type DialogActionsSlots = { - root: Slot<'div'>; -}; - -// @public -export type DialogActionsState = ComponentState & Pick, 'position' | 'fluid'>; - -// @public -export const DialogBody: ForwardRefComponent; - -// @public (undocumented) -export const dialogBodyClassNames: SlotClassNames; - -// @public -export type DialogBodyProps = ComponentProps & {}; - -// @public (undocumented) -export type DialogBodySlots = { - root: Slot<'div'>; -}; - -// @public -export type DialogBodyState = ComponentState; - -// @public -export const DialogContent: ForwardRefComponent; - -// @public (undocumented) -export const dialogContentClassNames: SlotClassNames; - -// @public -export type DialogContentProps = ComponentProps; - -// @public (undocumented) -export type DialogContentSlots = { - root: Slot<'div'>; -}; - -// @public -export type DialogContentState = ComponentState; - -// @public (undocumented) -export type DialogOpenChangeData = { - type: 'escapeKeyDown'; - open: boolean; - event: React_2.KeyboardEvent; -} | { - type: 'backdropClick'; - open: boolean; - event: React_2.MouseEvent; -} | { - type: 'triggerClick'; - open: boolean; - event: React_2.MouseEvent; -}; +export { DialogTriggerChildProps } -// @public (undocumented) -export type DialogOpenChangeEvent = DialogOpenChangeData['event']; - -// @public (undocumented) -export type DialogProps = ComponentProps> & { - modalType?: DialogModalType; - open?: boolean; - defaultOpen?: boolean; - onOpenChange?: DialogOpenChangeEventHandler; - children: [JSX.Element, JSX.Element] | JSX.Element; - inertTrapFocus?: boolean; -}; - -// @public (undocumented) -export type DialogSlots = {}; - -// @public (undocumented) -export type DialogState = ComponentState & DialogContextValue & { - content: React_2.ReactNode; - trigger: React_2.ReactNode; -}; - -// @public -export const DialogSurface: ForwardRefComponent; +export { DialogTriggerProps } -// @public (undocumented) -export const dialogSurfaceClassNames: SlotClassNames; +export { DialogTriggerState } -// @public -export type DialogSurfaceProps = ComponentProps; - -// @public (undocumented) -export type DialogSurfaceSlots = { - backdrop?: Slot<'div'>; - root: Slot<'div'>; -}; - -// @public -export type DialogSurfaceState = ComponentState; - -// @public -export const DialogTitle: ForwardRefComponent; - -// @public (undocumented) -export const dialogTitleClassNames: SlotClassNames; - -// @public -export type DialogTitleProps = ComponentProps; - -// @public (undocumented) -export type DialogTitleSlots = { - root: Slot<'h2', 'h1' | 'h3' | 'h4' | 'h5' | 'h6' | 'div'>; - action?: Slot<'div'>; -}; - -// @public -export type DialogTitleState = ComponentState; - -// @public -export const DialogTrigger: React_2.FC; - -// @public (undocumented) -export type DialogTriggerAction = 'open' | 'close'; - -// @public -export type DialogTriggerChildProps = ARIAButtonResultProps; - -// @public (undocumented) -export type DialogTriggerProps = TriggerProps & { - action?: DialogTriggerAction; - disableButtonEnhancement?: boolean; -}; - -// @public (undocumented) -export type DialogTriggerState = { - children: React_2.ReactElement | null; -}; - -// @public -export const Display: FunctionComponent; - -// @public (undocumented) -export const displayClassNames: SlotClassNames; - -// @public -export const Divider: ForwardRefComponent; - -// @public (undocumented) -export const dividerClassNames: SlotClassNames; - -// @public (undocumented) -export type DividerProps = ComponentProps> & { - alignContent?: 'start' | 'center' | 'end'; - appearance?: 'brand' | 'default' | 'strong' | 'subtle'; - inset?: boolean; - vertical?: boolean; -}; - -// @public (undocumented) -export type DividerSlots = { - root: NonNullable>; - wrapper: NonNullable>; -}; - -// @public (undocumented) -export type DividerState = ComponentState & Required>; - -// @public -export const Dropdown: ForwardRefComponent; - -// @public (undocumented) -export const dropdownClassNames: SlotClassNames; - -// @public (undocumented) -export type DropdownContextValues = ComboboxBaseContextValues; - -// @public (undocumented) -export type DropdownOpenChangeData = ComboboxBaseOpenChangeData; - -// @public (undocumented) -export type DropdownOpenEvents = ComboboxBaseOpenEvents; - -// @public -export type DropdownProps = ComponentProps, 'button'> & ComboboxBaseProps; - -// @public (undocumented) -export type DropdownSlots = { - root: NonNullable>; - expandIcon: Slot<'span'>; - button: NonNullable>; - listbox?: Slot; -}; - -// @public -export type DropdownState = ComponentState & ComboboxBaseState & { - placeholderVisible: boolean; -}; - -// @public (undocumented) -export type DurationTokens = { - durationUltraFast: string; - durationFaster: string; - durationFast: string; - durationNormal: string; - durationSlow: string; - durationSlower: string; - durationUltraSlow: string; -}; - -// @public (undocumented) -export const Field: ForwardRefComponent; - -// @public (undocumented) -export const fieldClassNames: SlotClassNames; - -// @public (undocumented) -export const FieldContextProvider: React_2.Provider & { - labelFor?: string | undefined; - labelId?: string | undefined; - validationMessageId?: string | undefined; - hintId?: string | undefined; -}> | undefined>; - -// @public (undocumented) -export type FieldContextValue = Readonly & { - labelFor?: string; - labelId?: string; - validationMessageId?: string; - hintId?: string; -}>; - -// @public (undocumented) -export type FieldContextValues = { - field: FieldContextValue; -}; - -// @public -export type FieldControlProps = Pick, 'id' | 'aria-labelledby' | 'aria-describedby' | 'aria-invalid' | 'aria-required'>; - -// @public -export type FieldControlPropsOptions = { - supportsLabelFor?: boolean; - supportsRequired?: boolean; - supportsSize?: boolean; -}; - -// @public -export type FieldProps = Omit, 'children'> & { - children?: React_2.ReactNode | ((props: FieldControlProps) => React_2.ReactNode); - orientation?: 'vertical' | 'horizontal'; - validationState?: 'error' | 'warning' | 'success' | 'none'; - required?: boolean; - size?: 'small' | 'medium' | 'large'; -}; - -// @public -export type FieldSlots = { - root: NonNullable>; - label?: Slot; - validationMessage?: Slot<'div'>; - validationMessageIcon?: Slot<'span'>; - hint?: Slot<'div'>; -}; - -// @public -export type FieldState = ComponentState> & Required> & Pick & { - generatedControlId: string; -}; - -// @public (undocumented) -export const FluentProvider: React_2.ForwardRefExoticComponent, "dir"> & { - applyStylesToPortals?: boolean | undefined; - customStyleHooks_unstable?: Partial<{ - useAccordionHeaderStyles_unstable: (state: unknown) => void; - useAccordionItemStyles_unstable: (state: unknown) => void; - useAccordionPanelStyles_unstable: (state: unknown) => void; - useAccordionStyles_unstable: (state: unknown) => void; - useAvatarStyles_unstable: (state: unknown) => void; - useAvatarGroupStyles_unstable: (state: unknown) => void; - useAvatarGroupItemStyles_unstable: (state: unknown) => void; - useAvatarGroupPopoverStyles_unstable: (state: unknown) => void; - useBadgeStyles_unstable: (state: unknown) => void; - useCounterBadgeStyles_unstable: (state: unknown) => void; - useCardHeaderStyles_unstable: (state: unknown) => void; - useCardStyles_unstable: (state: unknown) => void; - useCardFooterStyles_unstable: (state: unknown) => void; - useCardPreviewStyles_unstable: (state: unknown) => void; - usePresenceBadgeStyles_unstable: (state: unknown) => void; - useButtonStyles_unstable: (state: unknown) => void; - useCompoundButtonStyles_unstable: (state: unknown) => void; - useMenuButtonStyles_unstable: (state: unknown) => void; - useSplitButtonStyles_unstable: (state: unknown) => void; - useToggleButtonStyles_unstable: (state: unknown) => void; - useCheckboxStyles_unstable: (state: unknown) => void; - useComboboxStyles_unstable: (state: unknown) => void; - useDropdownStyles_unstable: (state: unknown) => void; - useListboxStyles_unstable: (state: unknown) => void; - useOptionStyles_unstable: (state: unknown) => void; - useOptionGroupStyles_unstable: (state: unknown) => void; - useDividerStyles_unstable: (state: unknown) => void; - useInputStyles_unstable: (state: unknown) => void; - useImageStyles_unstable: (state: unknown) => void; - useLabelStyles_unstable: (state: unknown) => void; - useLinkStyles_unstable: (state: unknown) => void; - useMenuDividerStyles_unstable: (state: unknown) => void; - useMenuGroupHeaderStyles_unstable: (state: unknown) => void; - useMenuGroupStyles_unstable: (state: unknown) => void; - useMenuItemCheckboxStyles_unstable: (state: unknown) => void; - useMenuItemRadioStyles_unstable: (state: unknown) => void; - useMenuItemStyles_unstable: (state: unknown) => void; - useMenuListStyles_unstable: (state: unknown) => void; - useMenuPopoverStyles_unstable: (state: unknown) => void; - useMenuSplitGroupStyles_unstable: (state: unknown) => void; - usePersonaStyles_unstable: (state: unknown) => void; - usePopoverSurfaceStyles_unstable: (state: unknown) => void; - useRadioGroupStyles_unstable: (state: unknown) => void; - useRadioStyles_unstable: (state: unknown) => void; - useSelectStyles_unstable: (state: unknown) => void; - useSliderStyles_unstable: (state: unknown) => void; - useSpinButtonStyles_unstable: (state: unknown) => void; - useSpinnerStyles_unstable: (state: unknown) => void; - useSwitchStyles_unstable: (state: unknown) => void; - useTabStyles_unstable: (state: unknown) => void; - useTabListStyles_unstable: (state: unknown) => void; - useTextStyles_unstable: (state: unknown) => void; - useTextareaStyles_unstable: (state: unknown) => void; - useTooltipStyles_unstable: (state: unknown) => void; - useDialogTitleStyles_unstable: (state: unknown) => void; - useDialogBodyStyles_unstable: (state: unknown) => void; - useDialogActionsStyles_unstable: (state: unknown) => void; - useDialogSurfaceStyles_unstable: (state: unknown) => void; - useDialogContentStyles_unstable: (state: unknown) => void; - useProgressBarStyles_unstable: (state: unknown) => void; - useToolbarButtonStyles_unstable: (state: unknown) => void; - useToolbarRadioButtonStyles_unstable: (state: unknown) => void; - useToolbarGroupStyles_unstable: (state: unknown) => void; - useToolbarToggleButtonStyles_unstable: (state: unknown) => void; - useToolbarDividerStyles_unstable: (state: unknown) => void; - useToolbarStyles_unstable: (state: unknown) => void; - useTableCellStyles_unstable: (state: unknown) => void; - useTableRowStyles_unstable: (state: unknown) => void; - useTableBodyStyles_unstable: (state: unknown) => void; - useTableStyles_unstable: (state: unknown) => void; - useTableHeaderStyles_unstable: (state: unknown) => void; - useTableHeaderCellStyles_unstable: (state: unknown) => void; - useTableResizeHandleStyles_unstable: (state: unknown) => void; - useTableSelectionCellStyles_unstable: (state: unknown) => void; - useTableCellActionsStyles_unstable: (state: unknown) => void; - useTableCellLayoutStyles_unstable: (state: unknown) => void; - useDataGridCellStyles_unstable: (state: unknown) => void; - useDataGridRowStyles_unstable: (state: unknown) => void; - useDataGridBodyStyles_unstable: (state: unknown) => void; - useDataGridStyles_unstable: (state: unknown) => void; - useDataGridHeaderStyles_unstable: (state: unknown) => void; - useDataGridHeaderCellStyles_unstable: (state: unknown) => void; - useDataGridSelectionCellStyles_unstable: (state: unknown) => void; - useDrawerStyles_unstable: (state: unknown) => void; - useDrawerBodyStyles_unstable: (state: unknown) => void; - useDrawerHeaderStyles_unstable: (state: unknown) => void; - useDrawerHeaderTitleStyles_unstable: (state: unknown) => void; - useDrawerHeaderNavigationStyles_unstable: (state: unknown) => void; - }> | undefined; - dir?: "ltr" | "rtl" | undefined; - targetDocument?: Document | undefined; - theme?: Partial | undefined; - overrides_unstable?: OverridesContextValue | undefined; -} & React_2.RefAttributes>; - -// @public (undocumented) -export const fluentProviderClassNames: SlotClassNames; - -// @public (undocumented) -export type FluentProviderContextValues = Pick & { - provider: ProviderContextValue; - themeClassName: ThemeClassNameContextValue; - textDirection: 'ltr' | 'rtl'; - tooltip: TooltipVisibilityContextValue; -}; - -// @public (undocumented) -export type FluentProviderCustomStyleHooks = CustomStyleHooksContextValue; - -// @public (undocumented) -export type FluentProviderProps = Omit, 'dir'> & { - applyStylesToPortals?: boolean; - customStyleHooks_unstable?: FluentProviderCustomStyleHooks; - dir?: 'ltr' | 'rtl'; - targetDocument?: Document; - theme?: PartialTheme; - overrides_unstable?: OverridesContextValue; -}; - -// @public (undocumented) -export type FluentProviderSlots = { - root: Slot<'div'>; -}; - -// @public (undocumented) -export type FluentProviderState = ComponentState & Pick & Required> & { - theme: ThemeContextValue; - themeClassName: string; - serverStyleProps: { - cssRule: string; - attributes: Record; - }; -}; - -// @public (undocumented) -export type FontFamilyTokens = { - fontFamilyBase: string; - fontFamilyMonospace: string; - fontFamilyNumeric: string; -}; - -// @public (undocumented) -export type FontSizeTokens = { - fontSizeBase100: string; - fontSizeBase200: string; - fontSizeBase300: string; - fontSizeBase400: string; - fontSizeBase500: string; - fontSizeBase600: string; - fontSizeHero700: string; - fontSizeHero800: string; - fontSizeHero900: string; - fontSizeHero1000: string; -}; - -// @public (undocumented) -export type FontWeightTokens = { - fontWeightRegular: number; - fontWeightMedium: number; - fontWeightSemibold: number; - fontWeightBold: number; -}; - -// @public -export type ForwardRefComponent = ObscureEventName extends keyof Props ? Required[ObscureEventName] extends React_2.PointerEventHandler ? React_2.ForwardRefExoticComponent> : never : never; - -// @public -export function getNativeElementProps>(tagName: string, props: {}, excludedPropNames?: string[]): TAttributes; - -// @public -export const getPartitionedNativeProps: , "style" | "className">, ExcludedPropKeys extends Extract = never>({ primarySlotTagName, props, excludedPropNames, }: { - primarySlotTagName: keyof JSX.IntrinsicElements; - props: Props; - excludedPropNames?: ExcludedPropKeys[] | undefined; -}) => { - root: { - style: React_2.CSSProperties | undefined; - className: string | undefined; - }; - primary: Omit; -}; - -// @public -export function getSlots(state: ComponentState): { - slots: Slots; - slotProps: ObjectSlotProps; -}; +export { Display } + +export { displayClassNames } + +export { Divider } + +export { dividerClassNames } + +export { DividerProps } + +export { DividerSlots } + +export { DividerState } + +export { Dropdown } + +export { dropdownClassNames } + +export { DropdownContextValues } + +export { DropdownOpenChangeData } + +export { DropdownOpenEvents } + +export { DropdownProps } + +export { DropdownSlots } + +export { DropdownState } + +export { DurationTokens } + +export { Field } + +export { fieldClassNames } + +export { FieldContextProvider } + +export { FieldContextValue } + +export { FieldContextValues } + +export { FieldControlProps } + +export { FieldControlPropsOptions } + +export { FieldProps } + +export { FieldSlots } + +export { FieldState } + +export { FluentProvider } + +export { fluentProviderClassNames } + +export { FluentProviderContextValues } + +export { FluentProviderCustomStyleHooks } + +export { FluentProviderProps } + +export { FluentProviderSlots } + +export { FluentProviderState } + +export { FontFamilyTokens } + +export { FontSizeTokens } + +export { FontWeightTokens } + +export { ForwardRefComponent } + +export { getNativeElementProps } + +export { getPartitionedNativeProps } + +export { getSlots } export { GriffelRenderer } export { GriffelStyle } -// @public (undocumented) -export type HorizontalSpacingTokens = { - spacingHorizontalNone: string; - spacingHorizontalXXS: string; - spacingHorizontalXS: string; - spacingHorizontalSNudge: string; - spacingHorizontalS: string; - spacingHorizontalMNudge: string; - spacingHorizontalM: string; - spacingHorizontalL: string; - spacingHorizontalXL: string; - spacingHorizontalXXL: string; - spacingHorizontalXXXL: string; -}; - -// @public -export const IdPrefixProvider: React_2.Provider; - -// @public -const Image_2: ForwardRefComponent; +export { HorizontalSpacingTokens } + +export { IdPrefixProvider } + export { Image_2 as Image } -// @public (undocumented) -export const imageClassNames: SlotClassNames; - -// @public (undocumented) -export type ImageProps = ComponentProps & { - block?: boolean; - bordered?: boolean; - fit?: 'none' | 'center' | 'contain' | 'cover' | 'default'; - shadow?: boolean; - shape?: 'square' | 'circular' | 'rounded'; -}; - -// @public (undocumented) -export type ImageSlots = { - root: Slot<'img'>; -}; - -// @public (undocumented) -export type ImageState = ComponentState & Required>; - -// @public -export const Input: ForwardRefComponent; - -// @public (undocumented) -export const inputClassNames: SlotClassNames; - -// @public -export type InputOnChangeData = { - value: string; -}; - -// @public (undocumented) -export type InputProps = Omit, 'input'>, 'children' | 'defaultValue' | 'onChange' | 'size' | 'type' | 'value'> & { - children?: never; - size?: 'small' | 'medium' | 'large'; - appearance?: 'outline' | 'underline' | 'filled-darker' | 'filled-lighter' | 'filled-darker-shadow' | 'filled-lighter-shadow'; - defaultValue?: string; - value?: string; - onChange?: (ev: React_2.ChangeEvent, data: InputOnChangeData) => void; - type?: 'text' | 'email' | 'password' | 'search' | 'tel' | 'url' | 'date' | 'datetime-local' | 'month' | 'number' | 'time' | 'week'; -}; - -// @public (undocumented) -export type InputSlots = { - root: NonNullable>; - input: NonNullable>; - contentBefore?: Slot<'span'>; - contentAfter?: Slot<'span'>; -}; - -// @public -export type InputState = Required> & ComponentState; - -// @public -export const Label: ForwardRefComponent; - -// @public (undocumented) -export const labelClassNames: SlotClassNames; - -// @public -export type LabelProps = Omit, 'required'> & { - disabled?: boolean; - required?: boolean | Slot<'span'>; - size?: 'small' | 'medium' | 'large'; - weight?: 'regular' | 'semibold'; -}; - -// @public (undocumented) -export type LabelSlots = { - root: Slot<'label'>; - required?: Slot<'span'>; -}; - -// @public -export type LabelState = ComponentState & Required>; - -// @public -export const LargeTitle: FunctionComponent; - -// @public (undocumented) -export const largeTitleClassNames: SlotClassNames; - -// @public (undocumented) -export type LineHeightTokens = { - lineHeightBase100: string; - lineHeightBase200: string; - lineHeightBase300: string; - lineHeightBase400: string; - lineHeightBase500: string; - lineHeightBase600: string; - lineHeightHero700: string; - lineHeightHero800: string; - lineHeightHero900: string; - lineHeightHero1000: string; -}; - -// @public -export const Link: ForwardRefComponent; - -// @public (undocumented) -export const linkClassNames: SlotClassNames; - -// @public (undocumented) -export type LinkProps = ComponentProps & { - appearance?: 'default' | 'subtle'; - disabled?: boolean; - disabledFocusable?: boolean; - inline?: boolean; -}; - -// @public (undocumented) -export type LinkSlots = { - root: Slot<'a', 'button'>; -}; - -// @public (undocumented) -export type LinkState = ComponentState & Required>; - -// @public -export const Listbox: ForwardRefComponent; - -// @public (undocumented) -export const listboxClassNames: SlotClassNames; - -// @public -export type ListboxContextValue = Pick; - -// @public (undocumented) -export type ListboxContextValues = { - listbox: ListboxContextValue; -}; - -// @public -export type ListboxProps = ComponentProps & SelectionProps; - -// @public (undocumented) -export const ListboxProvider: Provider & FC>; - -// @public (undocumented) -export type ListboxSlots = { - root: Slot<'div'>; -}; - -// @public -export type ListboxState = ComponentState & OptionCollectionState & Pick & SelectionState & { - activeOption?: OptionValue; - focusVisible: boolean; - selectOption(event: SelectionEvents, option: OptionValue): void; - setActiveOption(option?: OptionValue): void; -}; +export { imageClassNames } -export { makeResetStyles } +export { ImageProps } -export { makeStaticStyles } +export { ImageSlots } -export { makeStyles } +export { ImageState } -// @public -export const Menu: React_2.FC; +export { Input } -// @public -export const MenuButton: ForwardRefComponent; +export { inputClassNames } -// @public (undocumented) -export const menuButtonClassNames: SlotClassNames; +export { InputOnChangeData } -// @public (undocumented) -export type MenuButtonProps = ComponentProps & Pick; +export { InputProps } -// @public (undocumented) -export type MenuButtonSlots = ButtonSlots & { - menuIcon?: Slot<'span'>; -}; +export { InputSlots } -// @public (undocumented) -export type MenuButtonState = ComponentState & Omit; +export { InputState } -// @public (undocumented) -export type MenuCheckedValueChangeData = { - checkedItems: string[]; - name: string; -}; +export { Label } -// @public (undocumented) -export type MenuCheckedValueChangeEvent = React_2.MouseEvent | React_2.KeyboardEvent; +export { labelClassNames } -// @public -export type MenuContextValue = Pick & { - open: boolean; - triggerId: string; - defaultCheckedValues?: Record; -}; +export { LabelProps } -// @public (undocumented) -export type MenuContextValues = { - menu: MenuContextValue; -}; +export { LabelSlots } -// @public -export const MenuDivider: ForwardRefComponent; +export { LabelState } -// @public (undocumented) -export const menuDividerClassNames: SlotClassNames; +export { LargeTitle } -// @public (undocumented) -export type MenuDividerProps = ComponentProps; +export { largeTitleClassNames } -// @public (undocumented) -export type MenuDividerSlots = { - root: Slot<'div'>; -}; +export { LineHeightTokens } -// @public (undocumented) -export type MenuDividerState = ComponentState; +export { Link } -// @public -export const MenuGroup: ForwardRefComponent; +export { linkClassNames } -// @public (undocumented) -export const menuGroupClassNames: SlotClassNames; +export { LinkProps } -// @public (undocumented) -export const MenuGroupContextProvider: React_2.Provider; +export { LinkSlots } -// @public -export type MenuGroupContextValue = { - headerId: string; -}; +export { LinkState } -// @public (undocumented) -export type MenuGroupContextValues = { - menuGroup: MenuGroupContextValue; -}; +export { Listbox } -// @public -export const MenuGroupHeader: ForwardRefComponent; +export { listboxClassNames } -// @public (undocumented) -export const menuGroupHeaderClassNames: SlotClassNames; +export { ListboxContextValue } -// @public (undocumented) -export type MenuGroupHeaderProps = ComponentProps; +export { ListboxContextValues } -// @public (undocumented) -export type MenuGroupHeaderSlots = { - root: Slot<'div'>; -}; +export { ListboxProps } -// @public (undocumented) -export type MenuGroupHeaderState = ComponentState; +export { ListboxProvider } -// @public (undocumented) -export type MenuGroupProps = ComponentProps; +export { ListboxSlots } -// @public (undocumented) -export type MenuGroupSlots = { - root: Slot<'div'>; -}; - -// @public (undocumented) -export type MenuGroupState = ComponentState & { - headerId: string; -}; - -// @public -export const MenuItem: ForwardRefComponent; - -// @public -export const MenuItemCheckbox: ForwardRefComponent; - -// @public (undocumented) -export const menuItemCheckboxClassNames: SlotClassNames>; - -// @public (undocumented) -export type MenuItemCheckboxProps = MenuItemProps & MenuItemSelectableProps; - -// @public (undocumented) -export type MenuItemCheckboxState = MenuItemState & MenuItemSelectableState; - -// @public (undocumented) -export const menuItemClassNames: SlotClassNames; - -// @public (undocumented) -export type MenuItemProps = ComponentProps> & { - hasSubmenu?: boolean; - persistOnClick?: boolean; - disabled?: boolean; - disabledFocusable?: boolean; -}; - -// @public -export const MenuItemRadio: ForwardRefComponent; - -// @public (undocumented) -export const menuItemRadioClassNames: SlotClassNames>; - -// @public (undocumented) -export type MenuItemRadioProps = MenuItemProps & MenuItemSelectableProps; - -// @public (undocumented) -export type MenuItemRadioState = MenuItemState & MenuItemSelectableState; - -// @public -export type MenuItemSelectableProps = { - name: string; - value: string; -}; - -// @public -export type MenuItemSelectableState = MenuItemSelectableProps & { - checked: boolean; -}; - -// @public (undocumented) -export type MenuItemSlots = { - root: Slot<'div'>; - icon?: Slot<'span'>; - checkmark?: Slot<'span'>; - submenuIndicator?: Slot<'span'>; - content?: Slot<'span'>; - secondaryContent?: Slot<'span'>; -}; - -// @public (undocumented) -export type MenuItemState = ComponentState & Required>; - -// @public -export const MenuList: ForwardRefComponent; - -// @public (undocumented) -export const menuListClassNames: SlotClassNames; - -// @public -export type MenuListContextValue = Pick & { - setFocusByFirstCharacter?: (e: React_2.KeyboardEvent, itemEl: HTMLElement) => void; - toggleCheckbox?: SelectableHandler; - selectRadio?: SelectableHandler; - onCheckedValueChange?: (e: MenuCheckedValueChangeEvent, data: MenuCheckedValueChangeData) => void; -}; - -// @public (undocumented) -export type MenuListContextValues = { - menuList: MenuListContextValue; -}; - -// @public (undocumented) -export type MenuListProps = ComponentProps & { - checkedValues?: Record; - defaultCheckedValues?: Record; - hasCheckmarks?: boolean; - hasIcons?: boolean; - onCheckedValueChange?: (e: MenuCheckedValueChangeEvent, data: MenuCheckedValueChangeData) => void; -}; - -// @public (undocumented) -export const MenuListProvider: React_2.Provider & React_2.FC>; - -// @public (undocumented) -export type MenuListSlots = { - root: Slot<'div'>; -}; - -// @public (undocumented) -export type MenuListState = ComponentState & Required> & Pick & { - selectRadio: SelectableHandler; - setFocusByFirstCharacter: NonNullable; - toggleCheckbox: SelectableHandler; -}; - -// @public -export type MenuOpenChangeData = { - bubble?: boolean; - keyboard?: boolean; - open: boolean; -} & ({ - type: 'menuTriggerContextMenu'; - event: React_2.MouseEvent; -} | { - type: 'menuTriggerClick'; - event: React_2.MouseEvent; -} | { - type: 'menuTriggerMouseEnter'; - event: React_2.MouseEvent; -} | { - type: 'menuTriggerMouseLeave'; - event: React_2.MouseEvent; -} | { - type: 'menuTriggerMouseMove'; - event: React_2.MouseEvent; -} | { - type: 'menuTriggerKeyDown'; - event: React_2.KeyboardEvent; -} | { - type: 'menuItemClick'; - event: React_2.MouseEvent; -} | { - type: 'menuPopoverMouseEnter'; - event: React_2.MouseEvent; -} | { - type: 'menuPopoverKeyDown'; - event: React_2.KeyboardEvent; -} | { - type: 'clickOutside'; - event: MouseEvent | TouchEvent; -} | { - type: 'scrollOutside'; - event: MouseEvent | TouchEvent; -} | { - type: 'menuMouseEnter'; - event: MouseEvent | TouchEvent; -}); - -// @public -export type MenuOpenEvent = MenuOpenChangeData['event']; - -// @public @deprecated (undocumented) -export type MenuOpenEvents = MenuOpenEvent; - -// @public -export const MenuPopover: ForwardRefComponent; - -// @public (undocumented) -export const menuPopoverClassNames: SlotClassNames; - -// @public -export type MenuPopoverProps = ComponentProps; - -// @public (undocumented) -export type MenuPopoverSlots = { - root: Slot<'div'>; -}; - -// @public -export type MenuPopoverState = ComponentState & Pick & { - inline: boolean; -}; - -// @public -export type MenuProps = ComponentProps & Pick & Pick & { - children: [JSX.Element, JSX.Element] | JSX.Element; - hoverDelay?: number; - inline?: boolean; - onOpenChange?: (e: MenuOpenEvent, data: MenuOpenChangeData) => void; - open?: boolean; - defaultOpen?: boolean; - openOnContext?: boolean; - openOnHover?: boolean; - persistOnItemClick?: boolean; - positioning?: PositioningShorthand; - closeOnScroll?: boolean; -}; - -// @public (undocumented) -export const MenuProvider: React_2.Provider & React_2.FC>; - -// @public (undocumented) -export type MenuSlots = {}; - -// @public -export const MenuSplitGroup: ForwardRefComponent; - -// @public (undocumented) -export const menuSplitGroupClassNames: SlotClassNames; - -// @public -export type MenuSplitGroupProps = ComponentProps; - -// @public (undocumented) -export type MenuSplitGroupSlots = { - root: Slot<'div'>; -}; - -// @public -export type MenuSplitGroupState = ComponentState; - -// @public (undocumented) -export type MenuState = ComponentState & Required> & { - contextTarget?: PositioningVirtualElement; - isSubmenu: boolean; - menuPopover: React_2.ReactNode; - menuPopoverRef: React_2.MutableRefObject; - menuTrigger: React_2.ReactNode; - setContextTarget: SetVirtualMouseTarget; - setOpen: (e: MenuOpenEvent, data: MenuOpenChangeData) => void; - triggerId: string; - triggerRef: React_2.MutableRefObject; - onOpenChange?: (e: MenuOpenEvent, data: MenuOpenChangeData) => void; - defaultCheckedValues?: Record; -}; - -// @public -export const MenuTrigger: React_2.FC; - -// @public -export type MenuTriggerChildProps = ARIAButtonResultProps; - onMouseEnter: React_2.MouseEventHandler; - onMouseLeave: React_2.MouseEventHandler; - onMouseMove: React_2.MouseEventHandler; - onContextMenu: React_2.MouseEventHandler; -}>; - -// @public (undocumented) -export const MenuTriggerContextProvider: React_2.Provider; - -// @public (undocumented) -export type MenuTriggerProps = TriggerProps & { - disableButtonEnhancement?: boolean; -}; - -// @public (undocumented) -export type MenuTriggerState = { - children: React_2.ReactElement | null; - isSubmenu: boolean; -}; +export { ListboxState } -export { mergeClasses } +export { makeResetStyles } + +export { makeStaticStyles } -// @public -export type OnOpenChangeData = { - open: boolean; -}; +export { makeStyles } -// @public -export type OnVisibleChangeData = { - visible: boolean; -}; +export { Menu } -// @public -export type OpenPopoverEvents = MouseEvent | TouchEvent | React_2.FocusEvent | React_2.KeyboardEvent | React_2.MouseEvent; +export { MenuButton } -// @public -const Option_2: ForwardRefComponent; -export { Option_2 as Option } +export { menuButtonClassNames } -// @public (undocumented) -export const optionClassNames: SlotClassNames; - -// @public -export const OptionGroup: ForwardRefComponent; - -// @public (undocumented) -export const optionGroupClassNames: SlotClassNames; - -// @public -export type OptionGroupProps = ComponentProps>; - -// @public (undocumented) -export type OptionGroupSlots = { - root: NonNullable>; - label?: Slot<'span'>; -}; - -// @public -export type OptionGroupState = ComponentState; - -// @public -export type OptionProps = ComponentProps> & { - disabled?: boolean; - value?: string; -} & ({ - text?: string; - children: string; -} | { - text: string; - children?: React_2.ReactNode; -}); - -// @public (undocumented) -export type OptionSlots = { - root: NonNullable>; - checkIcon: Slot<'span'>; -}; - -// @public -export type OptionState = ComponentState & Pick & { - active: boolean; - focusVisible: boolean; - multiselect?: boolean; - selected: boolean; -}; - -// @public -export const Overflow: React_2.ForwardRefExoticComponent> & { - children: React_2.ReactElement; -} & React_2.RefAttributes>; - -// @public -export const OverflowItem: React_2.ForwardRefExoticComponent>; - -// @public -export type OverflowItemProps = { - id: string; - groupId?: string; - priority?: number; - children: React_2.ReactElement; -}; - -// @public -export type OverflowProps = Partial> & { - children: React_2.ReactElement; -}; - -// @public (undocumented) -export type PartialTheme = Partial; - -// @public (undocumented) -export type PartitionAvatarGroupItems = { - inlineItems: readonly T[]; - overflowItems?: readonly T[]; -}; - -// @public -export const partitionAvatarGroupItems: (options: PartitionAvatarGroupItemsOptions) => PartitionAvatarGroupItems; - -// @public (undocumented) -export type PartitionAvatarGroupItemsOptions = { - items: readonly T[]; - layout?: 'spread' | 'stack' | 'pie'; - maxInlineItems?: number; -}; - -// @public -export const Persona: ForwardRefComponent; - -// @public (undocumented) -export const personaClassNames: SlotClassNames; - -// @public -export type PersonaProps = ComponentProps & { - name?: string; - presenceOnly?: boolean; - size?: 'extra-small' | 'small' | 'medium' | 'large' | 'extra-large' | 'huge'; - textPosition?: 'after' | 'before' | 'below'; - textAlignment?: 'center' | 'start'; -}; - -// @public (undocumented) -export type PersonaSlots = { - root: NonNullable>; - avatar?: Slot; - presence?: Slot; - primaryText?: Slot<'span'>; - secondaryText?: Slot<'span'>; - tertiaryText?: Slot<'span'>; - quaternaryText?: Slot<'span'>; -}; - -// @public -export type PersonaState = ComponentState & Required> & { - numTextLines: number; -}; - -// @public -export const Popover: React_2.FC; - -// @public -export type PopoverContextValue = Pick; - -// @public -export type PopoverProps = Pick & { - appearance?: 'brand' | 'inverted'; - children: [JSX.Element, JSX.Element] | JSX.Element; - closeOnScroll?: boolean; - defaultOpen?: boolean; - inline?: boolean; - mouseLeaveDelay?: number; - withArrow?: boolean; - onOpenChange?: (e: OpenPopoverEvents, data: OnOpenChangeData) => void; - open?: boolean; - openOnContext?: boolean; - openOnHover?: boolean; - positioning?: PositioningShorthand; - size?: PopoverSize; - trapFocus?: boolean; - legacyTrapFocus?: boolean; - inertTrapFocus?: boolean; - unstable_disableAutoFocus?: boolean; -}; - -// @public (undocumented) -export const PopoverProvider: Provider & FC>; - -// @public -export type PopoverSize = 'small' | 'medium' | 'large'; - -// @public -export type PopoverState = Pick & Required> & Pick & { - arrowRef: React_2.MutableRefObject; - contentRef: React_2.MutableRefObject; - contextTarget: PositioningVirtualElement | undefined; - popoverSurface: React_2.ReactElement | undefined; - popoverTrigger: React_2.ReactElement | undefined; - setContextTarget: SetVirtualMouseTarget; - setOpen: (e: OpenPopoverEvents, open: boolean) => void; - size: NonNullable; - toggleOpen: (e: OpenPopoverEvents) => void; - triggerRef: React_2.MutableRefObject; -}; - -// @public -export const PopoverSurface: ForwardRefComponent; - -// @public (undocumented) -export const popoverSurfaceClassNames: SlotClassNames; - -// @public -export type PopoverSurfaceProps = ComponentProps; - -// @public -export type PopoverSurfaceSlots = { - root: Slot<'div'>; -}; - -// @public -export type PopoverSurfaceState = ComponentState & Pick & { - arrowClassName?: string; -}; - -// @public -export const PopoverTrigger: React_2.FC; - -// @public -export type PopoverTriggerChildProps = ARIAButtonResultProps; - onMouseEnter: React_2.MouseEventHandler; - onMouseLeave: React_2.MouseEventHandler; - onContextMenu: React_2.MouseEventHandler; -}>; - -// @public -export type PopoverTriggerProps = TriggerProps & { - disableButtonEnhancement?: boolean; -}; - -// @public -export type PopoverTriggerState = { - children: React_2.ReactElement | null; -}; - -// @public -export const Portal: React_2.FC; - -// @public (undocumented) -export type PortalProps = { - children?: React_2.ReactNode; - mountNode?: HTMLElement | null | { - element?: HTMLElement | null; - className?: string; - }; -}; - -// @public (undocumented) -export type PortalState = Pick & { - mountNode: HTMLElement | null | undefined; - virtualParentRootRef: React_2.MutableRefObject; -}; - -// @public (undocumented) -export type PositioningImperativeRef = { - updatePosition: () => void; - setTarget: (target: TargetElement) => void; -}; - -// @public (undocumented) -export interface PositioningProps extends Pick { - positioningRef?: React_2.Ref; - target?: TargetElement | null; -} - -// @public (undocumented) -export type PositioningShorthand = PositioningProps | PositioningShorthandValue; - -// @public (undocumented) -export type PositioningShorthandValue = 'above' | 'above-start' | 'above-end' | 'below' | 'below-start' | 'below-end' | 'before' | 'before-top' | 'before-bottom' | 'after' | 'after-top' | 'after-bottom'; - -// @public (undocumented) -export type PositioningVirtualElement = { - getBoundingClientRect: () => { - x: number; - y: number; - top: number; - left: number; - bottom: number; - right: number; - width: number; - height: number; - }; - contextElement?: Element; -}; - -// @public -export const PresenceBadge: ForwardRefComponent; - -// @public (undocumented) -export const presenceBadgeClassNames: SlotClassNames; - -// @public (undocumented) -export type PresenceBadgeProps = Omit>, 'color'> & Pick & { - status?: PresenceBadgeStatus; - outOfOffice?: boolean; -}; - -// @public (undocumented) -export type PresenceBadgeState = ComponentState & BadgeState & Required>; - -// @public (undocumented) -export type PresenceBadgeStatus = 'busy' | 'out-of-office' | 'away' | 'available' | 'offline' | 'do-not-disturb' | 'unknown' | 'blocked'; - -// @public -export const ProgressBar: ForwardRefComponent; - -// @public (undocumented) -export const progressBarClassNames: SlotClassNames; - -// @public -export type ProgressBarProps = Omit, 'size'> & { - shape?: 'rounded' | 'square'; - value?: number; - max?: number; - thickness?: 'medium' | 'large'; - color?: 'brand' | 'success' | 'warning' | 'error'; -}; - -// @public (undocumented) -export type ProgressBarSlots = { - root: NonNullable>; - bar?: NonNullable>; -}; - -// @public -export type ProgressBarState = ComponentState & Required> & Pick; - -// @public -export const Radio: ForwardRefComponent; - -// @public (undocumented) -export const radioClassNames: SlotClassNames; - -// @public -export const RadioGroup: ForwardRefComponent; - -// @public (undocumented) -export const radioGroupClassNames: SlotClassNames; - -// @public (undocumented) -export type RadioGroupContextValue = Pick; - -// @public (undocumented) -export type RadioGroupContextValues = { - radioGroup: RadioGroupContextValue; -}; - -// @public -export type RadioGroupOnChangeData = { - value: string; -}; - -// @public (undocumented) -export type RadioGroupProps = Omit>, 'onChange'> & { - name?: string; - value?: string; - defaultValue?: string; - onChange?: (ev: React_2.FormEvent, data: RadioGroupOnChangeData) => void; - layout?: 'vertical' | 'horizontal' | 'horizontal-stacked'; - disabled?: boolean; - required?: boolean; -}; - -// @public (undocumented) -export const RadioGroupProvider: React_2.Provider; - -// @public (undocumented) -export type RadioGroupSlots = { - root: NonNullable>; -}; - -// @public -export type RadioGroupState = ComponentState & Required> & Pick; - -// @public -export type RadioOnChangeData = { - value: string; -}; - -// @public -export type RadioProps = Omit, 'input'>, 'onChange' | 'size'> & { - value?: string; - labelPosition?: 'after' | 'below'; - disabled?: boolean; - onChange?: (ev: React_2.ChangeEvent, data: RadioOnChangeData) => void; -}; - -// @public (undocumented) -export type RadioSlots = { - root: NonNullable>; - label: Slot; - input: NonNullable>; - indicator: NonNullable>; -}; - -// @public -export type RadioState = ComponentState & Required>; - -// @public (undocumented) -export type RegisterTabEventHandler = (data: TabRegisterData) => void; - -// @public -export const renderAccordion_unstable: (state: AccordionState, contextValues: AccordionContextValues) => JSX.Element; - -// @public -export const renderAccordionHeader_unstable: (state: AccordionHeaderState, contextValues: AccordionHeaderContextValues) => JSX.Element; - -// @public -export const renderAccordionItem_unstable: (state: AccordionItemState, contextValues: AccordionItemContextValues) => JSX.Element; - -// @public -export const renderAccordionPanel_unstable: (state: AccordionPanelState) => JSX.Element | null; - -// @public (undocumented) -export const renderAvatar_unstable: (state: AvatarState) => JSX.Element; +export { MenuButtonProps } -// @public -export const renderAvatarGroup_unstable: (state: AvatarGroupState, contextValues: AvatarGroupContextValues) => JSX.Element; - -// @public -export const renderAvatarGroupItem_unstable: (state: AvatarGroupItemState) => JSX.Element; +export { MenuButtonSlots } -// @public -export const renderAvatarGroupPopover_unstable: (state: AvatarGroupPopoverState, contextValues: AvatarGroupContextValues) => JSX.Element; +export { MenuButtonState } -// @public (undocumented) -export const renderBadge_unstable: (state: BadgeState) => JSX.Element; +export { MenuCheckedValueChangeData } -// @public -const renderButton_unstable: (state: ButtonState) => JSX.Element; -export { renderButton_unstable } -export { renderButton_unstable as renderToggleButton_unstable } +export { MenuCheckedValueChangeEvent } -// @public -export const renderCard_unstable: (state: CardState, cardContextValue: CardContextValue) => JSX.Element; +export { MenuContextValue } -// @public -export const renderCardFooter_unstable: (state: CardFooterState) => JSX.Element; +export { MenuContextValues } -// @public -export const renderCardHeader_unstable: (state: CardHeaderState) => JSX.Element; +export { MenuDivider } -// @public -export const renderCardPreview_unstable: (state: CardPreviewState) => JSX.Element; +export { menuDividerClassNames } -// @public (undocumented) -export const renderCheckbox_unstable: (state: CheckboxState) => JSX.Element; +export { MenuDividerProps } -// @public -export const renderCombobox_unstable: (state: ComboboxState, contextValues: ComboboxContextValues) => JSX.Element; +export { MenuDividerSlots } -// @public -export const renderCompoundButton_unstable: (state: CompoundButtonState) => JSX.Element; +export { MenuDividerState } -// @public -export const renderDataGrid_unstable: (state: DataGridState, contextValues: DataGridContextValues) => JSX.Element; +export { MenuGroup } -// @public -export const renderDataGridBody_unstable: (state: DataGridBodyState) => JSX.Element; +export { menuGroupClassNames } -// @public -export const renderDataGridCell_unstable: (state: DataGridCellState) => JSX.Element; +export { MenuGroupContextProvider } -// @public -export const renderDataGridHeader_unstable: (state: DataGridHeaderState) => JSX.Element; +export { MenuGroupContextValue } -// @public -export const renderDataGridHeaderCell_unstable: (state: DataGridHeaderCellState) => JSX.Element; +export { MenuGroupContextValues } -// @public -export const renderDataGridRow_unstable: (state: DataGridRowState) => JSX.Element; +export { MenuGroupHeader } -// @public -export const renderDataGridSelectionCell_unstable: (state: DataGridSelectionCellState) => JSX.Element; +export { menuGroupHeaderClassNames } -// @public -export const renderDialog_unstable: (state: DialogState, contextValues: DialogContextValues) => JSX.Element; +export { MenuGroupHeaderProps } -// @public -export const renderDialogActions_unstable: (state: DialogActionsState) => JSX.Element; +export { MenuGroupHeaderSlots } -// @public -export const renderDialogBody_unstable: (state: DialogBodyState) => JSX.Element; +export { MenuGroupHeaderState } -// @public -export const renderDialogContent_unstable: (state: DialogContentState) => JSX.Element; +export { MenuGroupProps } -// @public -export const renderDialogSurface_unstable: (state: DialogSurfaceState, contextValues: DialogSurfaceContextValues) => JSX.Element; +export { MenuGroupSlots } -// @public -export const renderDialogTitle_unstable: (state: DialogTitleState) => JSX.Element; +export { MenuGroupState } -// @public -export const renderDialogTrigger_unstable: (state: DialogTriggerState) => ReactElement> | null; +export { MenuItem } -// @public -export const renderDivider_unstable: (state: DividerState) => JSX.Element; +export { MenuItemCheckbox } -// @public -export const renderDropdown_unstable: (state: DropdownState, contextValues: DropdownContextValues) => JSX.Element; +export { menuItemCheckboxClassNames } -export { RendererProvider } +export { MenuItemCheckboxProps } -// @public -export const renderField_unstable: (state: FieldState, contextValues: FieldContextValues) => JSX.Element; +export { MenuItemCheckboxState } -// @public -export const renderFluentProvider_unstable: (state: FluentProviderState, contextValues: FluentProviderContextValues) => JSX.Element; +export { menuItemClassNames } -// @public -export const renderImage_unstable: (state: ImageState) => JSX.Element; +export { MenuItemProps } -// @public -export const renderInput_unstable: (state: InputState) => JSX.Element; +export { MenuItemRadio } -// @public -export const renderLabel_unstable: (state: LabelState) => JSX.Element; +export { menuItemRadioClassNames } -// @public -export const renderLink_unstable: (state: LinkState) => JSX.Element; +export { MenuItemRadioProps } -// @public -export const renderListbox_unstable: (state: ListboxState, contextValues: ListboxContextValues) => JSX.Element; +export { MenuItemRadioState } -// @public -export const renderMenu_unstable: (state: MenuState, contextValues: MenuContextValues) => JSX.Element; +export { MenuItemSelectableProps } -// @public -export const renderMenuButton_unstable: (state: MenuButtonState) => JSX.Element; +export { MenuItemSelectableState } -// @public -export const renderMenuDivider_unstable: (state: MenuDividerState) => JSX.Element; +export { MenuItemSlots } -// @public -export const renderMenuGroup_unstable: (state: MenuGroupState, contextValues: MenuGroupContextValues) => JSX.Element; +export { MenuItemState } -// @public -export const renderMenuGroupHeader_unstable: (state: MenuGroupHeaderState) => JSX.Element; +export { MenuList } -// @public -export const renderMenuItem_unstable: (state: MenuItemState) => JSX.Element; +export { menuListClassNames } -// @public -export const renderMenuItemCheckbox_unstable: (state: MenuItemCheckboxState) => JSX.Element; +export { MenuListContextValue } -// @public -export const renderMenuItemRadio_unstable: (state: MenuItemRadioState) => JSX.Element; +export { MenuListContextValues } -// @public -export const renderMenuList_unstable: (state: MenuListState, contextValues: MenuListContextValues) => JSX.Element; +export { MenuListProps } -// @public -export const renderMenuPopover_unstable: (state: MenuPopoverState) => JSX.Element; +export { MenuListProvider } -// @public -export const renderMenuSplitGroup_unstable: (state: MenuSplitGroupState) => JSX.Element; +export { MenuListSlots } -// @public -export const renderMenuTrigger_unstable: (state: MenuTriggerState) => JSX.Element; +export { MenuListState } -// @public -export const renderOption_unstable: (state: OptionState) => JSX.Element; +export { MenuOpenChangeData } -// @public -export const renderOptionGroup_unstable: (state: OptionGroupState) => JSX.Element; +export { MenuOpenEvent } -// @public -export const renderPersona_unstable: (state: PersonaState) => JSX.Element; +export { MenuOpenEvents } -// @public -export const renderPopover_unstable: (state: PopoverState) => JSX.Element; +export { MenuPopover } -// @public -export const renderPopoverSurface_unstable: (state: PopoverSurfaceState) => JSX.Element; +export { menuPopoverClassNames } -// @public -export const renderPopoverTrigger_unstable: (state: PopoverTriggerState) => ReactElement> | null; +export { MenuPopoverProps } -// @public -export const renderPortal_unstable: (state: PortalState) => React_2.ReactElement; +export { MenuPopoverSlots } -// @public -export const renderProgressBar_unstable: (state: ProgressBarState) => JSX.Element; +export { MenuPopoverState } -// @public -export const renderRadio_unstable: (state: RadioState) => JSX.Element; +export { MenuProps } -// @public -export const renderRadioGroup_unstable: (state: RadioGroupState, contextValues: RadioGroupContextValues) => JSX.Element; +export { MenuProvider } -// @public -export const renderSelect_unstable: (state: SelectState) => JSX.Element; +export { MenuSlots } -// @public -export const renderSkeleton_unstable: (state: SkeletonState, contextValues: SkeletonContextValues) => JSX.Element; +export { MenuSplitGroup } -// @public -export const renderSkeletonItem_unstable: (state: SkeletonItemState) => JSX.Element; +export { menuSplitGroupClassNames } -// @public -export const renderSlider_unstable: (state: SliderState) => JSX.Element; +export { MenuSplitGroupProps } -// @public -export const renderSpinButton_unstable: (state: SpinButtonState) => JSX.Element; +export { MenuSplitGroupSlots } -// @public -export const renderSpinner_unstable: (state: SpinnerState) => JSX.Element; +export { MenuSplitGroupState } -// @public -export const renderSplitButton_unstable: (state: SplitButtonState) => JSX.Element; +export { MenuState } -// @public -export const renderSwitch_unstable: (state: SwitchState) => JSX.Element; +export { MenuTrigger } -// @public -export const renderTab_unstable: (state: TabState) => JSX.Element; +export { MenuTriggerChildProps } -// @public -export const renderTable_unstable: (state: TableState, contextValues: TableContextValues) => JSX.Element; +export { MenuTriggerContextProvider } -// @public -export const renderTableBody_unstable: (state: TableBodyState) => JSX.Element; +export { MenuTriggerProps } -// @public -export const renderTableCell_unstable: (state: TableCellState) => JSX.Element; +export { MenuTriggerState } -// @public -export const renderTableCellActions_unstable: (state: TableCellActionsState) => JSX.Element; +export { mergeClasses } -// @public -export const renderTableCellLayout_unstable: (state: TableCellLayoutState, contextValues: TableCellLayoutContextValues) => JSX.Element; +export { OnOpenChangeData } -// @public -export const renderTableHeader_unstable: (state: TableHeaderState) => JSX.Element; +export { OnVisibleChangeData } -// @public -export const renderTableHeaderCell_unstable: (state: TableHeaderCellState) => JSX.Element; +export { OpenPopoverEvents } -// @public -export const renderTableResizeHandle_unstable: (state: TableResizeHandleState) => JSX.Element; +export { Option_2 as Option } -// @public -export const renderTableRow_unstable: (state: TableRowState) => JSX.Element; +export { optionClassNames } -// @public -export const renderTableSelectionCell_unstable: (state: TableSelectionCellState) => JSX.Element; +export { OptionGroup } -// @public -export const renderTabList_unstable: (state: TabListState, contextValues: TabListContextValues) => JSX.Element; +export { optionGroupClassNames } -// @public -export const renderText_unstable: (state: TextState) => JSX.Element; +export { OptionGroupProps } -// @public -export const renderTextarea_unstable: (state: TextareaState) => JSX.Element; +export { OptionGroupSlots } -// @public -export const renderToolbar_unstable: (state: ToolbarState, contextValues: ToolbarContextValues) => JSX.Element; +export { OptionGroupState } -// @public -export const renderToolbarGroup_unstable: (state: ToolbarGroupState) => JSX.Element; +export { OptionProps } -// @public -export const renderTooltip_unstable: (state: TooltipState) => JSX.Element; +export { OptionSlots } -export { renderToStyleElements } +export { OptionState } -// @public -export function resetIdsForTests(): void; - -// @public -export const resolveShorthand: ResolveShorthandFunction; - -// @public (undocumented) -export type ResolveShorthandFunction = { -

(value: P | SlotShorthandValue | undefined, options: ResolveShorthandOptions): P; -

(value: P | SlotShorthandValue | null | undefined, options?: ResolveShorthandOptions): P | undefined; -}; - -// @public (undocumented) -export type ResolveShorthandOptions = Required extends true ? { - required: true; - defaultProps?: Props; -} : { - required?: Required; - defaultProps?: Props; -}; - -// @public -export const Select: ForwardRefComponent; - -// @public (undocumented) -export type SelectableHandler = (e: React_2.MouseEvent | React_2.KeyboardEvent, name: string, value: string, checked: boolean) => void; - -// @public (undocumented) -export const selectClassNames: SlotClassNames; - -// @public -export type SelectOnChangeData = { - value: string; -}; - -// @public (undocumented) -export type SelectProps = Omit, 'select'>, 'size' | 'onChange'> & { - appearance?: 'outline' | 'underline' | 'filled-darker' | 'filled-lighter'; - onChange?: (ev: React_2.ChangeEvent, data: SelectOnChangeData) => void; - size?: 'small' | 'medium' | 'large'; -}; - -// @public (undocumented) -export type SelectSlots = { - root: NonNullable>; - select: NonNullable>; - icon: Slot<'span'>; -}; - -// @public (undocumented) -export type SelectState = ComponentState & Required>; - -// @public (undocumented) -export type SelectTabData = { - value: TabValue; -}; - -// @public (undocumented) -export type SelectTabEvent = React_2.MouseEvent | React_2.KeyboardEvent; - -// @public (undocumented) -export type SelectTabEventHandler = (event: SelectTabEvent, data: SelectTabData) => void; - -// @public (undocumented) -export type ShadowBrandTokens = { - shadow2Brand: string; - shadow4Brand: string; - shadow8Brand: string; - shadow16Brand: string; - shadow28Brand: string; - shadow64Brand: string; -}; - -// @public -export type ShadowTokens = { - shadow2: string; - shadow4: string; - shadow8: string; - shadow16: string; - shadow28: string; - shadow64: string; -}; +export { Overflow } -export { shorthands } +export { OverflowDivider } -// @public -export const Skeleton: ForwardRefComponent; - -// @public (undocumented) -export const skeletonClassNames: SlotClassNames; - -// @public (undocumented) -export const SkeletonContextProvider: React_2.Provider; - -// @public (undocumented) -export interface SkeletonContextValue { - // (undocumented) - animation?: 'wave' | 'pulse'; - // (undocumented) - appearance?: 'opaque' | 'translucent'; -} - -// @public (undocumented) -export const SkeletonItem: ForwardRefComponent; - -// @public (undocumented) -export const skeletonItemClassNames: SlotClassNames; - -// @public -export type SkeletonItemProps = ComponentProps & { - animation?: 'wave' | 'pulse'; - appearance?: 'opaque' | 'translucent'; - size?: SkeletonItemSize; - shape?: 'circle' | 'square' | 'rectangle'; -}; - -// @public (undocumented) -export type SkeletonItemSlots = { - root: Slot<'div'>; -}; - -// @public -export type SkeletonItemState = ComponentState & Required>; - -// @public -export type SkeletonProps = Omit>, 'width'> & { - animation?: 'wave' | 'pulse'; - appearance?: 'opaque' | 'translucent'; - width?: number | string; -}; - -// @public (undocumented) -export type SkeletonSlots = { - root: NonNullable>; -}; - -// @public -export type SkeletonState = ComponentState & Required>; - -// @public -export const Slider: ForwardRefComponent; - -// @public (undocumented) -export const sliderClassNames: SlotClassNames; - -// @public (undocumented) -export const sliderCSSVars: { - sliderDirectionVar: string; - sliderProgressVar: string; - sliderStepsPercentVar: string; -}; - -// @public (undocumented) -export type SliderOnChangeData = { - value: number; -}; - -// @public (undocumented) -export type SliderProps = Omit, 'input'>, 'defaultValue' | 'onChange' | 'size' | 'value'> & { - defaultValue?: number; - disabled?: boolean; - max?: number; - min?: number; - size?: 'small' | 'medium'; - step?: number; - value?: number; - vertical?: boolean; - onChange?: (ev: React_2.ChangeEvent, data: SliderOnChangeData) => void; -}; - -// @public (undocumented) -export type SliderSlots = { - root: NonNullable>; - rail: NonNullable>; - thumb: NonNullable>; - input: NonNullable> & { - orient?: 'horizontal' | 'vertical'; - }; -}; - -// @public (undocumented) -export type SliderState = ComponentState & Pick; - -// @public -export type Slot = IsSingleton> extends true ? WithSlotShorthandValue> : Type extends React_2.ComponentType ? WithSlotRenderFunction : Type> | { - [As in AlternateAs]: { - as: As; - } & WithSlotRenderFunction>; -}[AlternateAs] | null : 'Error: First parameter to Slot must not be not a union of types. See documentation of Slot type.'; - -// @public -export type SlotClassNames = { - [SlotName in keyof Slots]-?: string; -}; - -// @public -export type SlotPropsRecord = Record; - -// @public (undocumented) -export type SlotRenderFunction = (Component: React_2.ElementType, props: Omit) => React_2.ReactNode; - -// @public (undocumented) -export type SortDirection = 'ascending' | 'descending'; - -// @public (undocumented) -export type SpacingTokens = { - none: string; - xxs: string; - xs: string; - sNudge: string; - s: string; - mNudge: string; - m: string; - l: string; - xl: string; - xxl: string; - xxxl: string; -}; - -// @public -export const SpinButton: ForwardRefComponent; - -// @public (undocumented) -export type SpinButtonBounds = 'none' | 'min' | 'max' | 'both'; - -// @public (undocumented) -export type SpinButtonChangeEvent = React_2.MouseEvent | React_2.ChangeEvent | React_2.FocusEvent | React_2.KeyboardEvent; - -// @public (undocumented) -export const spinButtonClassNames: SlotClassNames; - -// @public (undocumented) -export type SpinButtonOnChangeData = { - value?: number | null; - displayValue?: string; -}; - -// @public -export type SpinButtonProps = Omit, 'input'>, 'defaultValue' | 'onChange' | 'size' | 'value'> & { - appearance?: 'outline' | 'underline' | 'filled-darker' | 'filled-lighter'; - defaultValue?: number | null; - displayValue?: string; - max?: number; - min?: number; - onChange?: (event: SpinButtonChangeEvent, data: SpinButtonOnChangeData) => void; - precision?: number; - size?: 'small' | 'medium'; - step?: number; - stepPage?: number; - value?: number | null; -}; - -// @public (undocumented) -export type SpinButtonSlots = { - root: NonNullable>; - input: NonNullable>; - incrementButton: NonNullable>; - decrementButton: NonNullable>; -}; - -// @public (undocumented) -export type SpinButtonSpinState = 'rest' | 'up' | 'down'; - -// @public -export type SpinButtonState = ComponentState & Required> & { - spinState: SpinButtonSpinState; - atBound: SpinButtonBounds; -}; - -// @public -export const Spinner: ForwardRefComponent; - -// @public (undocumented) -export const spinnerClassNames: SlotClassNames; - -// @public -export type SpinnerProps = Omit, 'size'> & { - appearance?: 'primary' | 'inverted'; - delay?: number; - labelPosition?: 'above' | 'below' | 'before' | 'after'; - size?: 'tiny' | 'extra-small' | 'small' | 'medium' | 'large' | 'extra-large' | 'huge'; -}; - -// @public (undocumented) -export type SpinnerSlots = { - root: NonNullable>; - spinner?: Slot<'span'>; - label?: Slot; -}; - -// @public -export type SpinnerState = ComponentState & Required> & { - shouldRenderSpinner: boolean; -}; - -// @public -export const SplitButton: ForwardRefComponent; - -// @public (undocumented) -export const splitButtonClassNames: SlotClassNames; - -// @public (undocumented) -export type SplitButtonProps = ComponentProps & Omit & Omit; - -// @public (undocumented) -export type SplitButtonSlots = { - root: NonNullable>; - menuButton?: Slot; - primaryActionButton?: Slot; -}; - -// @public (undocumented) -export type SplitButtonState = ComponentState & Omit & Omit; - -// @public -export const SSRProvider: React_2.FC<{ - children: React_2.ReactNode; -}>; - -// @public (undocumented) -export type StrokeWidthTokens = { - strokeWidthThin: string; - strokeWidthThick: string; - strokeWidthThicker: string; - strokeWidthThickest: string; -}; - -// @public -export const Subtitle1: FunctionComponent; - -// @public (undocumented) -export const subtitle1ClassNames: SlotClassNames; - -// @public -export const Subtitle2: FunctionComponent; - -// @public (undocumented) -export const subtitle2ClassNames: SlotClassNames; - -// @public -export const Subtitle2Stronger: FunctionComponent; - -// @public (undocumented) -export const subtitle2StrongerClassNames: SlotClassNames; - -// @public -export const Switch: ForwardRefComponent; - -// @public (undocumented) -export const switchClassNames: SlotClassNames; - -// @public (undocumented) -export type SwitchOnChangeData = { - checked: boolean; -}; - -// @public -export type SwitchProps = Omit, 'input'>, 'checked' | 'defaultChecked' | 'onChange'> & { - checked?: boolean; - defaultChecked?: boolean; - labelPosition?: 'above' | 'after' | 'before'; - onChange?: (ev: React_2.ChangeEvent, data: SwitchOnChangeData) => void; -}; - -// @public (undocumented) -export type SwitchSlots = { - root: NonNullable>; - indicator: NonNullable>; - input: NonNullable>; - label?: Slot; -}; +export { OverflowItem } -// @public -export type SwitchState = ComponentState & Required>; +export { OverflowItemProps } -// @public -export const Tab: ForwardRefComponent; - -// @public (undocumented) -export const tabClassNames: SlotClassNames; - -// @public -export const Table: ForwardRefComponent; - -// @public -export const TableBody: ForwardRefComponent; - -// @public (undocumented) -export const tableBodyClassName = "fui-TableBody"; +export { OverflowProps } -// @public (undocumented) -export const tableBodyClassNames: SlotClassNames; - -// @public -export type TableBodyProps = ComponentProps; - -// @public (undocumented) -export type TableBodySlots = { - root: Slot<'tbody', 'div'>; -}; - -// @public -export type TableBodyState = ComponentState & Pick; - -// @public -export const TableCell: ForwardRefComponent; - -// @public -export const TableCellActions: ForwardRefComponent; - -// @public (undocumented) -export const tableCellActionsClassNames: SlotClassNames; - -// @public -export type TableCellActionsProps = ComponentProps & { - visible?: boolean; -}; - -// @public (undocumented) -export type TableCellActionsSlots = { - root: Slot<'div'>; -}; - -// @public -export type TableCellActionsState = ComponentState & Pick, 'visible'>; - -// @public (undocumented) -export const tableCellClassName = "fui-TableCell"; - -// @public (undocumented) -export const tableCellClassNames: SlotClassNames; - -// @public -export const TableCellLayout: ForwardRefComponent; - -// @public (undocumented) -export const tableCellLayoutClassNames: SlotClassNames; - -// @public -export type TableCellLayoutProps = ComponentProps> & { - appearance?: 'primary'; - truncate?: boolean; -}; - -// @public (undocumented) -export type TableCellLayoutSlots = { - root: Slot<'div'>; - media: Slot<'span'>; - main: Slot<'span'>; - description: Slot<'span'>; - content: Slot<'div'>; -}; - -// @public -export type TableCellLayoutState = ComponentState & Pick & { - avatarSize: AvatarSize | undefined; -} & Pick; - -// @public -export type TableCellProps = ComponentProps & {}; - -// @public (undocumented) -export type TableCellSlots = { - root: Slot<'td', 'div'>; -}; - -// @public -export type TableCellState = ComponentState & Pick; - -// @public (undocumented) -export const tableClassName = "fui-Table"; +export { PartialTheme } -// @public (undocumented) -export const tableClassNames: SlotClassNames; +export { PartitionAvatarGroupItems } -// @public (undocumented) -export interface TableColumnDefinition { - // (undocumented) - columnId: TableColumnId; - // (undocumented) - compare: (a: TItem, b: TItem) => number; - // (undocumented) - renderCell: (item: TItem) => React_2.ReactNode; - // (undocumented) - renderHeaderCell: () => React_2.ReactNode; -} +export { partitionAvatarGroupItems } -// @public (undocumented) -export type TableColumnId = string | number; - -// @public (undocumented) -export type TableColumnSizingOptions = Record> & { - defaultWidth?: number; -}>; - -// @public (undocumented) -export const TableContextProvider: React_2.Provider; - -// @public (undocumented) -export type TableContextValue = { - size: 'extra-small' | 'small' | 'medium'; - noNativeElements: boolean; - sortable: boolean; -}; - -// @public (undocumented) -export type TableContextValues = { - table: TableContextValue; -}; - -// @public (undocumented) -export type TableFeaturePlugin = (tableState: TableFeaturesState) => TableFeaturesState; - -// @public (undocumented) -export interface TableFeaturesState extends Pick, 'items' | 'getRowId'> { - columns: TableColumnDefinition[]; - columnSizing_unstable: TableColumnSizingState; - getRows: = TableRowData>(rowEnhancer?: RowEnhancer) => TRowState[]; - selection: TableSelectionState; - sort: TableSortState; - tableRef: React_2.Ref; -} - -// @public -export const TableHeader: ForwardRefComponent; - -// @public -export const TableHeaderCell: ForwardRefComponent; - -// @public (undocumented) -export const tableHeaderCellClassName = "fui-TableHeaderCell"; - -// @public (undocumented) -export const tableHeaderCellClassNames: SlotClassNames; - -// @public -export type TableHeaderCellProps = ComponentProps> & { - sortDirection?: SortDirection; -}; - -// @public (undocumented) -export type TableHeaderCellSlots = { - root: Slot<'th', 'div'>; - sortIcon: Slot<'span'>; - button: NonNullable>; - aside: Slot<'span'>; -}; - -// @public -export type TableHeaderCellState = ComponentState & Pick; - -// @public (undocumented) -export const tableHeaderClassName = "fui-TableHeader"; - -// @public (undocumented) -export const tableHeaderClassNames: SlotClassNames; - -// @public -export type TableHeaderProps = ComponentProps & {}; - -// @public (undocumented) -export type TableHeaderSlots = { - root: Slot<'thead', 'div'>; -}; - -// @public -export type TableHeaderState = ComponentState & Pick; - -// @public -export type TableProps = ComponentProps & Partial; - -// @public -export const TableResizeHandle: ForwardRefComponent; - -// @public (undocumented) -export const tableResizeHandleClassNames: SlotClassNames; - -// @public -export const TableRow: ForwardRefComponent; - -// @public (undocumented) -export const tableRowClassName = "fui-TableRow"; - -// @public (undocumented) -export const tableRowClassNames: SlotClassNames; - -// @public (undocumented) -export interface TableRowData { - item: TItem; - rowId: TableRowId; -} - -// @public (undocumented) -export type TableRowId = string | number; - -// @public (undocumented) -export const TableRowIdContextProvider: React_2.Provider; - -// @public -export type TableRowProps = ComponentProps & { - appearance?: 'brand' | 'neutral' | 'none'; -}; - -// @public (undocumented) -export type TableRowSlots = { - root: Slot<'tr', 'div'>; -}; - -// @public -export type TableRowState = ComponentState & Pick & Pick, 'appearance'> & { - isHeaderRow: boolean; -}; - -// @public -export const TableSelectionCell: ForwardRefComponent; - -// @public (undocumented) -export const tableSelectionCellClassNames: SlotClassNames; - -// @public -export type TableSelectionCellProps = ComponentProps> & { - type?: 'checkbox' | 'radio'; - checked?: CheckboxProps['checked']; - subtle?: boolean; - hidden?: boolean; -}; - -// @public (undocumented) -export type TableSelectionCellSlots = { - checkboxIndicator: Slot; - radioIndicator: Slot; -} & Pick; - -// @public -export type TableSelectionCellState = ComponentState & Pick, 'type' | 'checked' | 'subtle' | 'hidden'> & Pick; - -// @public (undocumented) -export interface TableSelectionState { - allRowsSelected: boolean; - clearRows: (e: React_2.SyntheticEvent) => void; - deselectRow: (e: React_2.SyntheticEvent, rowId: TableRowId) => void; - isRowSelected: (rowId: TableRowId) => boolean; - selectedRows: Set; - // (undocumented) - selectionMode: SelectionMode_2; - selectRow: (e: React_2.SyntheticEvent, rowId: TableRowId) => void; - someRowsSelected: boolean; - toggleAllRows: (e: React_2.SyntheticEvent) => void; - toggleRow: (e: React_2.SyntheticEvent, rowId: TableRowId) => void; -} - -// @public (undocumented) -export type TableSlots = { - root: Slot<'table', 'div'>; -}; - -// @public (undocumented) -export interface TableSortState { - getSortDirection: (columnId: TableColumnId) => SortDirection | undefined; - setColumnSort: (event: React_2.SyntheticEvent, columnId: TableColumnId, sortDirection: SortDirection) => void; - sort: >(rows: TRowState[]) => TRowState[]; - sortColumn: TableColumnId | undefined; - sortDirection: SortDirection; - toggleColumnSort: (event: React_2.SyntheticEvent, columnId: TableColumnId) => void; -} - -// @public -export type TableState = ComponentState & Pick, 'size' | 'noNativeElements'> & TableContextValue; - -// @public -export const TabList: ForwardRefComponent; - -// @public (undocumented) -export const tabListClassNames: SlotClassNames; - -// @public (undocumented) -export type TabListContextValue = Pick & Required> & { - onRegister: RegisterTabEventHandler; - onUnregister: RegisterTabEventHandler; - onSelect: SelectTabEventHandler; - getRegisteredTabs: () => { - selectedValue?: TabValue; - previousSelectedValue?: TabValue; - registeredTabs: Record; - }; -}; - -// @public -export type TabListContextValues = { - tabList: TabListContextValue; -}; - -// @public -export type TabListProps = ComponentProps & { - appearance?: 'transparent' | 'subtle'; - reserveSelectedTabSpace?: boolean; - defaultSelectedValue?: TabValue; - disabled?: boolean; - onTabSelect?: SelectTabEventHandler; - selectedValue?: TabValue; - size?: 'small' | 'medium' | 'large'; - vertical?: boolean; -}; - -// @public (undocumented) -export const TabListProvider: Provider & FC>; - -// @public (undocumented) -export type TabListSlots = { - root: Slot<'div'>; -}; - -// @public -export type TabListState = ComponentState> & TabListContextValue; - -// @public -export type TabProps = ComponentProps> & { - disabled?: boolean; - value: TabValue; -}; - -// @public (undocumented) -export type TabRegisterData = { - value: TabValue; - ref: React_2.RefObject; -}; - -// @public (undocumented) -export type TabSlots = { - root: Slot<'button'>; - icon?: Slot<'span'>; - content: NonNullable>; -}; - -// @public -export type TabState = ComponentState & Pick & Required> & { - appearance?: 'transparent' | 'subtle'; - iconOnly: boolean; - selected: boolean; - contentReservedSpaceClassName?: string; - size: 'small' | 'medium' | 'large'; - vertical: boolean; -}; - -// @public -export type TabValue = unknown; - -// @public (undocumented) -export const teamsDarkTheme: Theme; - -// @public (undocumented) -export const teamsHighContrastTheme: Theme; - -// @public (undocumented) -export const teamsLightTheme: Theme; - -// @public -const Text_2: ForwardRefComponent; -export { Text_2 as Text } +export { PartitionAvatarGroupItemsOptions } + +export { Persona } + +export { personaClassNames } + +export { PersonaProps } + +export { PersonaSlots } + +export { PersonaState } + +export { Popover } + +export { PopoverContextValue } + +export { PopoverProps } + +export { PopoverProvider } + +export { PopoverSize } + +export { PopoverState } + +export { PopoverSurface } + +export { popoverSurfaceClassNames } + +export { PopoverSurfaceProps } + +export { PopoverSurfaceSlots } + +export { PopoverSurfaceState } + +export { PopoverTrigger } + +export { PopoverTriggerChildProps } + +export { PopoverTriggerProps } + +export { PopoverTriggerState } + +export { Portal } + +export { PortalProps } + +export { PortalState } + +export { PositioningImperativeRef } + +export { PositioningProps } + +export { PositioningShorthand } + +export { PositioningShorthandValue } + +export { PositioningVirtualElement } + +export { PresenceBadge } + +export { presenceBadgeClassNames } + +export { PresenceBadgeProps } + +export { PresenceBadgeState } + +export { PresenceBadgeStatus } + +export { ProgressBar } + +export { progressBarClassNames } + +export { ProgressBarProps } + +export { ProgressBarSlots } + +export { ProgressBarState } + +export { Radio } + +export { radioClassNames } + +export { RadioGroup } + +export { radioGroupClassNames } + +export { RadioGroupContextValue } + +export { RadioGroupContextValues } + +export { RadioGroupOnChangeData } + +export { RadioGroupProps } + +export { RadioGroupProvider } + +export { RadioGroupSlots } + +export { RadioGroupState } + +export { RadioOnChangeData } + +export { RadioProps } + +export { RadioSlots } + +export { RadioState } + +export { RegisterTabEventHandler } + +export { renderAccordion_unstable } + +export { renderAccordionHeader_unstable } + +export { renderAccordionItem_unstable } + +export { renderAccordionPanel_unstable } + +export { renderAvatar_unstable } + +export { renderAvatarGroup_unstable } + +export { renderAvatarGroupItem_unstable } + +export { renderAvatarGroupPopover_unstable } + +export { renderBadge_unstable } + +export { renderButton_unstable } + +export { renderCard_unstable } + +export { renderCardFooter_unstable } + +export { renderCardHeader_unstable } + +export { renderCardPreview_unstable } + +export { renderCheckbox_unstable } + +export { renderCombobox_unstable } + +export { renderCompoundButton_unstable } + +export { renderDataGrid_unstable } + +export { renderDataGridBody_unstable } + +export { renderDataGridCell_unstable } + +export { renderDataGridHeader_unstable } + +export { renderDataGridHeaderCell_unstable } + +export { renderDataGridRow_unstable } + +export { renderDataGridSelectionCell_unstable } + +export { renderDialog_unstable } + +export { renderDialogActions_unstable } + +export { renderDialogBody_unstable } + +export { renderDialogContent_unstable } + +export { renderDialogSurface_unstable } + +export { renderDialogTitle_unstable } + +export { renderDialogTrigger_unstable } + +export { renderDivider_unstable } + +export { renderDropdown_unstable } + +export { RendererProvider } + +export { renderField_unstable } + +export { renderFluentProvider_unstable } + +export { renderImage_unstable } + +export { renderInput_unstable } + +export { renderLabel_unstable } + +export { renderLink_unstable } + +export { renderListbox_unstable } + +export { renderMenu_unstable } + +export { renderMenuButton_unstable } + +export { renderMenuDivider_unstable } + +export { renderMenuGroup_unstable } + +export { renderMenuGroupHeader_unstable } + +export { renderMenuItem_unstable } + +export { renderMenuItemCheckbox_unstable } + +export { renderMenuItemRadio_unstable } + +export { renderMenuList_unstable } + +export { renderMenuPopover_unstable } + +export { renderMenuSplitGroup_unstable } + +export { renderMenuTrigger_unstable } + +export { renderOption_unstable } + +export { renderOptionGroup_unstable } + +export { renderPersona_unstable } + +export { renderPopover_unstable } + +export { renderPopoverSurface_unstable } + +export { renderPopoverTrigger_unstable } + +export { renderPortal_unstable } + +export { renderProgressBar_unstable } + +export { renderRadio_unstable } + +export { renderRadioGroup_unstable } + +export { renderSelect_unstable } + +export { renderSkeleton_unstable } + +export { renderSkeletonItem_unstable } + +export { renderSlider_unstable } + +export { renderSpinButton_unstable } + +export { renderSpinner_unstable } + +export { renderSplitButton_unstable } + +export { renderSwitch_unstable } + +export { renderTab_unstable } + +export { renderTable_unstable } + +export { renderTableBody_unstable } + +export { renderTableCell_unstable } + +export { renderTableCellActions_unstable } + +export { renderTableCellLayout_unstable } + +export { renderTableHeader_unstable } + +export { renderTableHeaderCell_unstable } + +export { renderTableResizeHandle_unstable } + +export { renderTableRow_unstable } + +export { renderTableSelectionCell_unstable } + +export { renderTabList_unstable } + +export { renderText_unstable } + +export { renderTextarea_unstable } + +export { renderToggleButton_unstable } + +export { renderToolbar_unstable } + +export { renderToolbarGroup_unstable } + +export { renderTooltip_unstable } + +export { renderToStyleElements } + +export { resetIdsForTests } + +export { resolveShorthand } + +export { ResolveShorthandFunction } + +export { ResolveShorthandOptions } + +export { Select } + +export { SelectableHandler } + +export { selectClassNames } + +export { SelectOnChangeData } + +export { SelectProps } + +export { SelectSlots } + +export { SelectState } + +export { SelectTabData } + +export { SelectTabEvent } + +export { SelectTabEventHandler } + +export { ShadowBrandTokens } + +export { ShadowTokens } + +export { shorthands } + +export { Skeleton } + +export { skeletonClassNames } + +export { SkeletonContextProvider } + +export { SkeletonContextValue } + +export { SkeletonItem } + +export { skeletonItemClassNames } + +export { SkeletonItemProps } + +export { SkeletonItemSlots } + +export { SkeletonItemState } + +export { SkeletonProps } + +export { SkeletonSlots } + +export { SkeletonState } + +export { Slider } + +export { sliderClassNames } + +export { sliderCSSVars } + +export { SliderOnChangeData } + +export { SliderProps } + +export { SliderSlots } + +export { SliderState } + +export { Slot } + +export { SlotClassNames } + +export { SlotPropsRecord } + +export { SlotRenderFunction } + +export { SortDirection } + +export { SpacingTokens } + +export { SpinButton } + +export { SpinButtonBounds } + +export { SpinButtonChangeEvent } + +export { spinButtonClassNames } + +export { SpinButtonOnChangeData } + +export { SpinButtonProps } + +export { SpinButtonSlots } + +export { SpinButtonSpinState } + +export { SpinButtonState } + +export { Spinner } + +export { spinnerClassNames } + +export { SpinnerProps } + +export { SpinnerSlots } + +export { SpinnerState } + +export { SplitButton } + +export { splitButtonClassNames } + +export { SplitButtonProps } + +export { SplitButtonSlots } + +export { SplitButtonState } + +export { SSRProvider } + +export { StrokeWidthTokens } + +export { Subtitle1 } + +export { subtitle1ClassNames } + +export { Subtitle2 } + +export { subtitle2ClassNames } + +export { Subtitle2Stronger } + +export { subtitle2StrongerClassNames } + +export { Switch } + +export { switchClassNames } + +export { SwitchOnChangeData } + +export { SwitchProps } + +export { SwitchSlots } + +export { SwitchState } + +export { Tab } + +export { tabClassNames } + +export { Table } + +export { TableBody } + +export { tableBodyClassName } + +export { tableBodyClassNames } + +export { TableBodyProps } + +export { TableBodySlots } + +export { TableBodyState } + +export { TableCell } + +export { TableCellActions } + +export { tableCellActionsClassNames } + +export { TableCellActionsProps } + +export { TableCellActionsSlots } + +export { TableCellActionsState } + +export { tableCellClassName } + +export { tableCellClassNames } + +export { TableCellLayout } + +export { tableCellLayoutClassNames } + +export { TableCellLayoutProps } + +export { TableCellLayoutSlots } + +export { TableCellLayoutState } + +export { TableCellProps } + +export { TableCellSlots } + +export { TableCellState } + +export { tableClassName } + +export { tableClassNames } + +export { TableColumnDefinition } + +export { TableColumnId } + +export { TableColumnSizingOptions } + +export { TableContextProvider } + +export { TableContextValue } + +export { TableContextValues } + +export { TableFeaturePlugin } + +export { TableFeaturesState } + +export { TableHeader } + +export { TableHeaderCell } + +export { tableHeaderCellClassName } + +export { tableHeaderCellClassNames } + +export { TableHeaderCellProps } + +export { TableHeaderCellSlots } + +export { TableHeaderCellState } + +export { tableHeaderClassName } + +export { tableHeaderClassNames } + +export { TableHeaderProps } + +export { TableHeaderSlots } + +export { TableHeaderState } + +export { TableProps } + +export { TableResizeHandle } + +export { tableResizeHandleClassNames } + +export { TableRow } + +export { tableRowClassName } + +export { tableRowClassNames } + +export { TableRowData } + +export { TableRowId } + +export { TableRowIdContextProvider } + +export { TableRowProps } + +export { TableRowSlots } + +export { TableRowState } + +export { TableSelectionCell } + +export { tableSelectionCellClassNames } + +export { TableSelectionCellProps } + +export { TableSelectionCellSlots } + +export { TableSelectionCellState } + +export { TableSelectionState } + +export { TableSlots } + +export { TableSortState } + +export { TableState } + +export { TabList } + +export { tabListClassNames } + +export { TabListContextValue } + +export { TabListContextValues } + +export { TabListProps } + +export { TabListProvider } + +export { TabListSlots } + +export { TabListState } + +export { TabProps } + +export { TabRegisterData } + +export { TabSlots } + +export { TabState } + +export { TabValue } + +export { teamsDarkTheme } + +export { teamsHighContrastTheme } + +export { teamsLightTheme } + +export { Text_2 as Text } + +export { Textarea } + +export { textareaClassNames } + +export { TextareaOnChangeData } + +export { TextareaProps } + +export { TextareaSlots } + +export { TextareaState } + +export { textClassNames } + +export { TextPresetProps } + +export { TextProps } + +export { TextSlots } + +export { TextState } + +export { Theme } + +export { themeToTokensObject } + +export { Title1 } + +export { title1ClassNames } + +export { Title2 } + +export { title2ClassNames } + +export { Title3 } + +export { title3ClassNames } + +export { ToggleButton } + +export { toggleButtonClassNames } + +export { ToggleButtonProps } + +export { ToggleButtonState } + +export { tokens } + +export { Toolbar } + +export { ToolbarButton } + +export { ToolbarButtonProps } + +export { ToolbarButtonState } + +export { toolbarClassNames } + +export { ToolbarContextValue } + +export { ToolbarContextValues } + +export { ToolbarDivider } + +export { ToolbarDividerProps } + +export { ToolbarDividerState } + +export { ToolbarGroup } + +export { toolbarGroupClassNames } + +export { ToolbarGroupProps } + +export { ToolbarGroupState } + +export { ToolbarProps } + +export { ToolbarRadioButton } + +export { ToolbarRadioButtonProps } + +export { ToolbarRadioButtonState } + +export { ToolbarRadioGroup } + +export { ToolbarRadioGroupProps } + +export { ToolbarRadioGroupState } + +export { ToolbarSlots } + +export { ToolbarState } + +export { ToolbarToggleButton } + +export { ToolbarToggleButtonProps } + +export { ToolbarToggleButtonState } + +export { Tooltip } + +export { tooltipClassNames } + +export { TooltipProps } -// @public -export const Textarea: ForwardRefComponent; +export { TooltipSlots } -// @public (undocumented) -export const textareaClassNames: SlotClassNames; +export { TooltipState } -// @public -export type TextareaOnChangeData = { - value: string; -}; - -// @public -export type TextareaProps = Omit, 'textarea'>, 'defaultValue' | 'onChange' | 'size' | 'value'> & { - appearance?: 'outline' | 'filled-darker' | 'filled-lighter' | 'filled-darker-shadow' | 'filled-lighter-shadow'; - defaultValue?: string; - onChange?: (ev: React_2.ChangeEvent, data: TextareaOnChangeData) => void; - resize?: 'none' | 'horizontal' | 'vertical' | 'both'; - size?: 'small' | 'medium' | 'large'; - value?: string; -}; +export { TooltipTriggerProps } -// @public (undocumented) -export type TextareaSlots = { - root: NonNullable>; - textarea: NonNullable>; -}; - -// @public -export type TextareaState = ComponentState & Required>; +export { TypographyStyle } -// @public (undocumented) -export const textClassNames: SlotClassNames; +export { TypographyStyles } -// @public -export type TextPresetProps = Omit; - -// @public -export type TextProps = ComponentProps & { - align?: 'start' | 'center' | 'end' | 'justify'; - block?: boolean; - font?: 'base' | 'monospace' | 'numeric'; - italic?: boolean; - size?: 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 1000; - strikethrough?: boolean; - truncate?: boolean; - underline?: boolean; - weight?: 'regular' | 'medium' | 'semibold' | 'bold'; - wrap?: boolean; -}; +export { typographyStyles } -// @public -export type TextSlots = { - root: Slot<'span', 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'pre'>; -}; +export { UninitializedMenuListState } -// @public -export type TextState = ComponentState & Required>; +export { useAccordion_unstable } -// @public (undocumented) -export type Theme = FontSizeTokens & LineHeightTokens & BorderRadiusTokens & StrokeWidthTokens & HorizontalSpacingTokens & VerticalSpacingTokens & DurationTokens & CurveTokens & ShadowTokens & ShadowBrandTokens & FontFamilyTokens & FontWeightTokens & ColorPaletteTokens & ColorTokens; - -// @public -export function themeToTokensObject(theme: TTheme): Record; - -// @public -export const Title1: FunctionComponent; - -// @public (undocumented) -export const title1ClassNames: SlotClassNames; - -// @public -export const Title2: FunctionComponent; - -// @public (undocumented) -export const title2ClassNames: SlotClassNames; - -// @public -export const Title3: FunctionComponent; - -// @public (undocumented) -export const title3ClassNames: SlotClassNames; +export { useAccordionContext_unstable } -// @public -export const ToggleButton: ForwardRefComponent; +export { useAccordionContextValues_unstable } -// @public (undocumented) -export const toggleButtonClassNames: SlotClassNames; - -// @public (undocumented) -export type ToggleButtonProps = ButtonProps & { - defaultChecked?: boolean; - checked?: boolean; -}; - -// @public (undocumented) -export type ToggleButtonState = ButtonState & Required>; - -// @public (undocumented) -export const tokens: Record; - -// @public -export const Toolbar: ForwardRefComponent; - -// @public -export const ToolbarButton: ForwardRefComponent; - -// @public -export type ToolbarButtonProps = ComponentProps & Partial> & { - appearance?: 'primary' | 'subtle'; -} & { - vertical?: boolean; -}; - -// @public -export type ToolbarButtonState = ComponentState> & ButtonState & Required>; - -// @public (undocumented) -export const toolbarClassNames: SlotClassNames; - -// @public (undocumented) -export type ToolbarContextValue = Pick & { - handleToggleButton?: ToggableHandler; - handleRadio?: ToggableHandler; -}; - -// @public (undocumented) -export type ToolbarContextValues = { - toolbar: ToolbarContextValue; -}; - -// @public -export const ToolbarDivider: ForwardRefComponent; - -// @public -export type ToolbarDividerProps = ComponentProps> & { - vertical?: boolean; -}; - -// @public -export type ToolbarDividerState = ComponentState> & DividerState; - -// @public -export const ToolbarGroup: ForwardRefComponent; - -// @public (undocumented) -export const toolbarGroupClassNames: SlotClassNames; - -// @public -export type ToolbarGroupProps = ComponentProps; - -// @public -export type ToolbarGroupState = ComponentState; - -// @public -export type ToolbarProps = ComponentProps & { - size?: 'small' | 'medium' | 'large'; - vertical?: boolean; - checkedValues?: Record; - defaultCheckedValues?: Record; - onCheckedValueChange?: (e: ToolbarCheckedValueChangeEvent, data: ToolbarCheckedValueChangeData) => void; -}; - -// @public -export const ToolbarRadioButton: ForwardRefComponent; - -// @public -export type ToolbarRadioButtonProps = ComponentProps & Partial> & { - appearance?: 'primary' | 'subtle'; - name: string; - value: string; -}; - -// @public -export type ToolbarRadioButtonState = ComponentState> & ToggleButtonState & Required> & Pick; - -// @public -export const ToolbarRadioGroup: ForwardRefComponent; - -// @public -export type ToolbarRadioGroupProps = ComponentProps; - -// @public -export type ToolbarRadioGroupState = ComponentState; - -// @public (undocumented) -export type ToolbarSlots = { - root: Slot<'div'>; -}; - -// @public -export type ToolbarState = ComponentState & Required> & Pick & { - handleToggleButton: ToggableHandler; - handleRadio: ToggableHandler; -}; - -// @public -export const ToolbarToggleButton: ForwardRefComponent; - -// @public -export type ToolbarToggleButtonProps = ComponentProps & Partial> & { - appearance?: 'primary' | 'subtle'; - name: string; - value: string; -}; - -// @public -export type ToolbarToggleButtonState = ComponentState> & ToggleButtonState & Required> & Pick; - -// @public -export const Tooltip: React_2.FC; - -// @public (undocumented) -export const tooltipClassNames: SlotClassNames; - -// @public -export type TooltipProps = ComponentProps & TriggerProps & Pick & { - appearance?: 'normal' | 'inverted'; - hideDelay?: number; - onVisibleChange?: (event: React_2.PointerEvent | React_2.FocusEvent | undefined, data: OnVisibleChangeData) => void; - positioning?: PositioningShorthand; - relationship: 'label' | 'description' | 'inaccessible'; - showDelay?: number; - visible?: boolean; - withArrow?: boolean; -}; - -// @public -export type TooltipSlots = { - content: NonNullable>; -}; - -// @public -export type TooltipState = ComponentState & Pick & Required> & { - children?: React_2.ReactElement | null; - shouldRenderTooltip?: boolean; - arrowRef?: React_2.Ref; - arrowClassName?: string; -}; - -// @public -export type TooltipTriggerProps = { - ref?: React_2.Ref; -} & Pick, 'aria-describedby' | 'aria-label' | 'aria-labelledby' | 'onBlur' | 'onFocus' | 'onPointerEnter' | 'onPointerLeave'>; - -// @public (undocumented) -export type TypographyStyle = { - fontFamily: string; - fontSize: string; - fontWeight: string; - lineHeight: string; -}; - -// @public (undocumented) -export type TypographyStyles = { - body1: TypographyStyle; - body1Strong: TypographyStyle; - body1Stronger: TypographyStyle; - body2: TypographyStyle; - caption1: TypographyStyle; - caption1Strong: TypographyStyle; - caption1Stronger: TypographyStyle; - caption2: TypographyStyle; - caption2Strong: TypographyStyle; - subtitle1: TypographyStyle; - subtitle2: TypographyStyle; - subtitle2Stronger: TypographyStyle; - title1: TypographyStyle; - title2: TypographyStyle; - title3: TypographyStyle; - largeTitle: TypographyStyle; - display: TypographyStyle; -}; - -// @public -export const typographyStyles: TypographyStyles; - -// @public @deprecated (undocumented) -export type UninitializedMenuListState = Omit & Partial>; - -// @public -export const useAccordion_unstable: (props: AccordionProps, ref: React_2.Ref) => AccordionState; - -// @public (undocumented) -export const useAccordionContext_unstable: (selector: ContextSelector) => T; - -// @public (undocumented) -export function useAccordionContextValues_unstable(state: AccordionState): AccordionContextValues; - -// @public -export const useAccordionHeader_unstable: (props: AccordionHeaderProps, ref: React_2.Ref) => AccordionHeaderState; - -// @public (undocumented) -export function useAccordionHeaderContextValues_unstable(state: AccordionHeaderState): AccordionHeaderContextValues; +export { useAccordionHeader_unstable } -// @public -export const useAccordionHeaderStyles_unstable: (state: AccordionHeaderState) => AccordionHeaderState; +export { useAccordionHeaderContextValues_unstable } -// @public -export const useAccordionItem_unstable: (props: AccordionItemProps, ref: React_2.Ref) => AccordionItemState; +export { useAccordionHeaderStyles_unstable } -// @public (undocumented) -export const useAccordionItemContext_unstable: () => AccordionItemContextValue; +export { useAccordionItem_unstable } -// @public (undocumented) -export function useAccordionItemContextValues_unstable(state: AccordionItemState): AccordionItemContextValues; +export { useAccordionItemContext_unstable } -// @public (undocumented) -export const useAccordionItemStyles_unstable: (state: AccordionItemState) => AccordionItemState; +export { useAccordionItemContextValues_unstable } -// @public -export const useAccordionPanel_unstable: (props: AccordionPanelProps, ref: React_2.Ref) => AccordionPanelState; +export { useAccordionItemStyles_unstable } -// @public -export const useAccordionPanelStyles_unstable: (state: AccordionPanelState) => AccordionPanelState; +export { useAccordionPanel_unstable } -// @public (undocumented) -export const useAccordionStyles_unstable: (state: AccordionState) => AccordionState; +export { useAccordionPanelStyles_unstable } -// @public -export const useArrowNavigationGroup: (options?: UseArrowNavigationGroupOptions) => Types.TabsterDOMAttribute; +export { useAccordionStyles_unstable } -// @public (undocumented) -export interface UseArrowNavigationGroupOptions { - axis?: 'vertical' | 'horizontal' | 'grid' | 'both'; - circular?: boolean; - ignoreDefaultKeydown?: Types.FocusableProps['ignoreKeydown']; - memorizeCurrent?: boolean; - tabbable?: boolean; - unstable_hasDefault?: boolean; -} +export { useArrowNavigationGroup } -// @public (undocumented) -export const useAvatar_unstable: (props: AvatarProps, ref: React_2.Ref) => AvatarState; +export { UseArrowNavigationGroupOptions } -// @public -export const useAvatarGroup_unstable: (props: AvatarGroupProps, ref: React_2.Ref) => AvatarGroupState; +export { useAvatar_unstable } -// @public (undocumented) -export const useAvatarGroupContext_unstable: (selector: ContextSelector) => T; +export { useAvatarGroup_unstable } -// @public (undocumented) -export const useAvatarGroupContextValues: (state: AvatarGroupState) => AvatarGroupContextValues; +export { useAvatarGroupContext_unstable } -// @public -export const useAvatarGroupItem_unstable: (props: AvatarGroupItemProps, ref: React_2.Ref) => AvatarGroupItemState; +export { useAvatarGroupContextValues } -// @public -export const useAvatarGroupItemStyles_unstable: (state: AvatarGroupItemState) => AvatarGroupItemState; +export { useAvatarGroupItem_unstable } -// @public -export const useAvatarGroupPopover_unstable: (props: AvatarGroupPopoverProps) => AvatarGroupPopoverState; +export { useAvatarGroupItemStyles_unstable } -// @public -export const useAvatarGroupPopoverStyles_unstable: (state: AvatarGroupPopoverState) => AvatarGroupPopoverState; +export { useAvatarGroupPopover_unstable } -// @public -export const useAvatarGroupStyles_unstable: (state: AvatarGroupState) => AvatarGroupState; +export { useAvatarGroupPopoverStyles_unstable } -// @public (undocumented) -export const useAvatarStyles_unstable: (state: AvatarState) => AvatarState; +export { useAvatarGroupStyles_unstable } -// @public -export const useBadge_unstable: (props: BadgeProps, ref: React_2.Ref) => BadgeState; +export { useAvatarStyles_unstable } -// @public -export const useBadgeStyles_unstable: (state: BadgeState) => BadgeState; +export { useBadge_unstable } -// @public -export const useButton_unstable: (props: ButtonProps, ref: React_2.Ref) => ButtonState; +export { useBadgeStyles_unstable } -// @public (undocumented) -export const useButtonStyles_unstable: (state: ButtonState) => ButtonState; +export { useButton_unstable } -// @public -export const useCard_unstable: (props: CardProps, ref: React_2.Ref) => CardState; +export { useButtonStyles_unstable } -// @public -export const useCardFooter_unstable: (props: CardFooterProps, ref: React_2.Ref) => CardFooterState; +export { useCard_unstable } -// @public -export const useCardFooterStyles_unstable: (state: CardFooterState) => CardFooterState; +export { useCardFooter_unstable } -// @public -export const useCardHeader_unstable: (props: CardHeaderProps, ref: React_2.Ref) => CardHeaderState; +export { useCardFooterStyles_unstable } -// @public -export const useCardHeaderStyles_unstable: (state: CardHeaderState) => CardHeaderState; +export { useCardHeader_unstable } -// @public -export const useCardPreview_unstable: (props: CardPreviewProps, ref: React_2.Ref) => CardPreviewState; +export { useCardHeaderStyles_unstable } -// @public -export const useCardPreviewStyles_unstable: (state: CardPreviewState) => CardPreviewState; +export { useCardPreview_unstable } -// @public -export const useCardStyles_unstable: (state: CardState) => CardState; +export { useCardPreviewStyles_unstable } -// @public -export const useCheckbox_unstable: (props: CheckboxProps, ref: React_2.Ref) => CheckboxState; +export { useCardStyles_unstable } -// @public -export const useCheckboxStyles_unstable: (state: CheckboxState) => CheckboxState; +export { useCheckbox_unstable } -// @public -export const useCheckmarkStyles_unstable: (state: MenuItemSelectableState & Pick) => void; +export { useCheckboxStyles_unstable } -// @public -export const useCombobox_unstable: (props: ComboboxProps, ref: React_2.Ref) => ComboboxState; +export { useCheckmarkStyles_unstable } -// @public (undocumented) -export function useComboboxContextValues(state: ComboboxBaseState): ComboboxBaseContextValues; +export { useCombobox_unstable } -// @public -export const useComboboxStyles_unstable: (state: ComboboxState) => ComboboxState; +export { useComboboxContextValues } -// @public -export const useCompoundButton_unstable: ({ contentContainer, secondaryContent, ...props }: CompoundButtonProps, ref: React_2.Ref) => CompoundButtonState; +export { useComboboxStyles_unstable } -// @public (undocumented) -export const useCompoundButtonStyles_unstable: (state: CompoundButtonState) => CompoundButtonState; +export { useCompoundButton_unstable } -// @public -export const useCounterBadge_unstable: (props: CounterBadgeProps, ref: React_2.Ref) => CounterBadgeState; +export { useCompoundButtonStyles_unstable } -// @public -export const useCounterBadgeStyles_unstable: (state: CounterBadgeState) => CounterBadgeState; +export { useCounterBadge_unstable } -// @public -export const useDataGrid_unstable: (props: DataGridProps, ref: React_2.Ref) => DataGridState; +export { useCounterBadgeStyles_unstable } -// @public -export const useDataGridBody_unstable: (props: DataGridBodyProps, ref: React_2.Ref) => DataGridBodyState; +export { useDataGrid_unstable } -// @public -export const useDataGridBodyStyles_unstable: (state: DataGridBodyState) => DataGridBodyState; +export { useDataGridBody_unstable } -// @public -export const useDataGridCell_unstable: (props: DataGridCellProps, ref: React_2.Ref) => DataGridCellState; +export { useDataGridBodyStyles_unstable } -// @public -export const useDataGridCellStyles_unstable: (state: DataGridCellState) => DataGridCellState; +export { useDataGridCell_unstable } -// @public (undocumented) -export function useDataGridContextValues_unstable(state: DataGridState): DataGridContextValues; +export { useDataGridCellStyles_unstable } -// @public -export const useDataGridHeader_unstable: (props: DataGridHeaderProps, ref: React_2.Ref) => DataGridHeaderState; +export { useDataGridContextValues_unstable } -// @public -export const useDataGridHeaderCell_unstable: (props: DataGridHeaderCellProps, ref: React_2.Ref) => DataGridHeaderCellState; +export { useDataGridHeader_unstable } -// @public -export const useDataGridHeaderCellStyles_unstable: (state: DataGridHeaderCellState) => DataGridHeaderCellState; +export { useDataGridHeaderCell_unstable } -// @public -export const useDataGridHeaderStyles_unstable: (state: DataGridHeaderState) => DataGridHeaderState; +export { useDataGridHeaderCellStyles_unstable } -// @public -export const useDataGridRow_unstable: (props: DataGridRowProps, ref: React_2.Ref) => DataGridRowState; +export { useDataGridHeaderStyles_unstable } -// @public -export const useDataGridRowStyles_unstable: (state: DataGridRowState) => DataGridRowState; +export { useDataGridRow_unstable } -// @public -export const useDataGridSelectionCell_unstable: (props: DataGridSelectionCellProps, ref: React_2.Ref) => DataGridSelectionCellState; +export { useDataGridRowStyles_unstable } -// @public -export const useDataGridSelectionCellStyles_unstable: (state: DataGridSelectionCellState) => DataGridSelectionCellState; +export { useDataGridSelectionCell_unstable } -// @public -export const useDataGridStyles_unstable: (state: DataGridState) => DataGridState; +export { useDataGridSelectionCellStyles_unstable } -// @public -export const useDialog_unstable: (props: DialogProps) => DialogState; +export { useDataGridStyles_unstable } -// @public -export const useDialogActions_unstable: (props: DialogActionsProps, ref: React_2.Ref) => DialogActionsState; +export { useDialog_unstable } -// @public -export const useDialogActionsStyles_unstable: (state: DialogActionsState) => DialogActionsState; +export { useDialogActions_unstable } -// @public -export const useDialogBody_unstable: (props: DialogBodyProps, ref: React_2.Ref) => DialogBodyState; +export { useDialogActionsStyles_unstable } -// @public -export const useDialogBodyStyles_unstable: (state: DialogBodyState) => DialogBodyState; +export { useDialogBody_unstable } -// @public -export const useDialogContent_unstable: (props: DialogContentProps, ref: React_2.Ref) => DialogContentState; +export { useDialogBodyStyles_unstable } -// @public -export const useDialogContentStyles_unstable: (state: DialogContentState) => DialogContentState; +export { useDialogContent_unstable } -// @public -export const useDialogSurface_unstable: (props: DialogSurfaceProps, ref: React_2.Ref) => DialogSurfaceState; +export { useDialogContentStyles_unstable } -// @public -export const useDialogSurfaceStyles_unstable: (state: DialogSurfaceState) => DialogSurfaceState; +export { useDialogSurface_unstable } -// @public -export const useDialogTitle_unstable: (props: DialogTitleProps, ref: React_2.Ref) => DialogTitleState; +export { useDialogSurfaceStyles_unstable } -// @public -export const useDialogTitleStyles_unstable: (state: DialogTitleState) => DialogTitleState; +export { useDialogTitle_unstable } -// @public -export const useDialogTrigger_unstable: (props: DialogTriggerProps) => DialogTriggerState; +export { useDialogTitleStyles_unstable } -// @public -export const useDivider_unstable: (props: DividerProps, ref: React_2.Ref) => DividerState; +export { useDialogTrigger_unstable } -// @public (undocumented) -export const useDividerStyles_unstable: (state: DividerState) => DividerState; +export { useDivider_unstable } -// @public -export const useDropdown_unstable: (props: DropdownProps, ref: React_2.Ref) => DropdownState; +export { useDividerStyles_unstable } -// @public -export const useDropdownStyles_unstable: (state: DropdownState) => DropdownState; +export { useDropdown_unstable } -// @public -export const useField_unstable: (props: FieldProps, ref: React_2.Ref) => FieldState; +export { useDropdownStyles_unstable } -// @public (undocumented) -export const useFieldContext_unstable: () => Readonly & { - labelFor?: string | undefined; - labelId?: string | undefined; - validationMessageId?: string | undefined; - hintId?: string | undefined; -}> | undefined; +export { useField_unstable } -// @public -export const useFieldContextValues_unstable: (state: FieldState) => FieldContextValues; +export { useFieldContext_unstable } -// @public -export function useFieldControlProps_unstable(): FieldControlProps | undefined; +export { useFieldContextValues_unstable } -// @public -export function useFieldControlProps_unstable(props: Props, options?: FieldControlPropsOptions): Props; +export { useFieldControlProps_unstable } -// @public -export const useFieldStyles_unstable: (state: FieldState) => void; +export { useFieldStyles_unstable } -// @public (undocumented) -export function useFluent(): ProviderContextValue; +export { useFluent } -// @public -export const useFluentProvider_unstable: (props: FluentProviderProps, ref: React_2.Ref) => FluentProviderState; +export { useFluentProvider_unstable } -// @public (undocumented) -export function useFluentProviderContextValues_unstable(state: FluentProviderState): FluentProviderContextValues; +export { useFluentProviderContextValues_unstable } -// @public -export const useFluentProviderStyles_unstable: (state: FluentProviderState) => FluentProviderState; +export { useFluentProviderStyles_unstable } -// @public -export const useFocusableGroup: (options?: UseFocusableGroupOptions | undefined) => Types.TabsterDOMAttribute; +export { useFocusableGroup } -// @public (undocumented) -export interface UseFocusableGroupOptions { - tabBehavior?: 'unlimited' | 'limited' | 'limited-trap-focus'; -} +export { UseFocusableGroupOptions } -// @public -export const useFocusFinders: () => { - findAllFocusable: (container: HTMLElement, acceptCondition?: ((el: HTMLElement) => boolean) | undefined) => HTMLElement[]; - findFirstFocusable: (container: HTMLElement) => HTMLElement | null | undefined; - findLastFocusable: (container: HTMLElement) => HTMLElement | null | undefined; - findNextFocusable: (currentElement: HTMLElement, options?: Pick, 'container'>) => HTMLElement | null | undefined; - findPrevFocusable: (currentElement: HTMLElement, options?: Pick, 'container'>) => HTMLElement | null | undefined; -}; +export { useFocusFinders } -// @public -export function useFocusWithin(): React_2.RefObject; +export { useFocusWithin } -// @public -export function useId(prefix?: string, providedId?: string): string; +export { useId } -// @public -export const useImage_unstable: (props: ImageProps, ref: React_2.Ref) => ImageState; +export { useImage_unstable } -// @public (undocumented) -export const useImageStyles_unstable: (state: ImageState) => void; +export { useImageStyles_unstable } -// @public -export const useInput_unstable: (props: InputProps, ref: React_2.Ref) => InputState; +export { useInput_unstable } -// @public -export const useInputStyles_unstable: (state: InputState) => InputState; +export { useInputStyles_unstable } -// @public -export const useIsomorphicLayoutEffect: typeof React_2.useEffect; +export { useIsomorphicLayoutEffect } -// @public (undocumented) -export function useIsOverflowGroupVisible(id: string): OverflowGroupState; +export { useIsOverflowGroupVisible } -// @public (undocumented) -export function useIsOverflowItemVisible(id: string): boolean; +export { useIsOverflowItemVisible } -// @public -export function useIsSSR(): boolean; +export { useIsSSR } -// @public -export function useKeyboardNavAttribute(): RefObject; +export { useKeyboardNavAttribute } -// @public -export const useLabel_unstable: (props: LabelProps, ref: React_2.Ref) => LabelState; +export { useLabel_unstable } -// @public -export const useLabelStyles_unstable: (state: LabelState) => LabelState; +export { useLabelStyles_unstable } -// @public -export const useLink_unstable: (props: LinkProps, ref: React_2.Ref) => LinkState; +export { useLink_unstable } -// @public -export const useLinkState_unstable: (state: LinkState) => LinkState; +export { useLinkState_unstable } -// @public (undocumented) -export const useLinkStyles_unstable: (state: LinkState) => LinkState; +export { useLinkStyles_unstable } -// @public -export const useListbox_unstable: (props: ListboxProps, ref: React_2.Ref) => ListboxState; +export { useListbox_unstable } -// @public (undocumented) -export function useListboxContextValues(state: ListboxState): ListboxContextValues; +export { useListboxContextValues } -// @public -export const useListboxStyles_unstable: (state: ListboxState) => ListboxState; +export { useListboxStyles_unstable } -// @public -export const useMenu_unstable: (props: MenuProps) => MenuState; +export { useMenu_unstable } -// @public -export const useMenuButton_unstable: ({ menuIcon, ...props }: MenuButtonProps, ref: React_2.Ref) => MenuButtonState; +export { useMenuButton_unstable } -// @public (undocumented) -export const useMenuButtonStyles_unstable: (state: MenuButtonState) => MenuButtonState; +export { useMenuButtonStyles_unstable } -// @public (undocumented) -export const useMenuContext_unstable: (selector: ContextSelector) => T; +export { useMenuContext_unstable } -// @public (undocumented) -export function useMenuContextValues_unstable(state: MenuState): MenuContextValues; +export { useMenuContextValues_unstable } -// @public -export const useMenuDivider_unstable: (props: MenuDividerProps, ref: React_2.Ref) => MenuDividerState; +export { useMenuDivider_unstable } -// @public (undocumented) -export const useMenuDividerStyles_unstable: (state: MenuDividerState) => MenuDividerState; +export { useMenuDividerStyles_unstable } -// @public -export function useMenuGroup_unstable(props: MenuGroupProps, ref: React_2.Ref): MenuGroupState; +export { useMenuGroup_unstable } -// @public (undocumented) -export const useMenuGroupContext_unstable: () => MenuGroupContextValue; +export { useMenuGroupContext_unstable } -// @public (undocumented) -export function useMenuGroupContextValues_unstable(state: MenuGroupState): MenuGroupContextValues; +export { useMenuGroupContextValues_unstable } -// @public -export function useMenuGroupHeader_unstable(props: MenuGroupHeaderProps, ref: React_2.Ref): MenuGroupHeaderState; +export { useMenuGroupHeader_unstable } -// @public (undocumented) -export const useMenuGroupHeaderStyles_unstable: (state: MenuGroupHeaderState) => MenuGroupHeaderState; +export { useMenuGroupHeaderStyles_unstable } -// @public (undocumented) -export const useMenuGroupStyles_unstable: (state: MenuGroupState) => MenuGroupState; +export { useMenuGroupStyles_unstable } -// @public -export const useMenuItem_unstable: (props: MenuItemProps, ref: React_2.Ref>) => MenuItemState; +export { useMenuItem_unstable } -// @public -export const useMenuItemCheckbox_unstable: (props: MenuItemCheckboxProps, ref: React_2.Ref>) => MenuItemCheckboxState; +export { useMenuItemCheckbox_unstable } -// @public (undocumented) -export const useMenuItemCheckboxStyles_unstable: (state: MenuItemCheckboxState) => void; +export { useMenuItemCheckboxStyles_unstable } -// @public -export const useMenuItemRadio_unstable: (props: MenuItemRadioProps, ref: React_2.Ref>) => MenuItemRadioState; +export { useMenuItemRadio_unstable } -// @public (undocumented) -export const useMenuItemRadioStyles_unstable: (state: MenuItemRadioState) => void; +export { useMenuItemRadioStyles_unstable } -// @public -export const useMenuItemStyles_unstable: (state: MenuItemState) => void; +export { useMenuItemStyles_unstable } -// @public -export const useMenuList_unstable: (props: MenuListProps, ref: React_2.Ref) => MenuListState; +export { useMenuList_unstable } -// @public (undocumented) -export const useMenuListContext_unstable: (selector: ContextSelector) => T; +export { useMenuListContext_unstable } -// @public (undocumented) -export function useMenuListContextValues_unstable(state: MenuListState): MenuListContextValues; +export { useMenuListContextValues_unstable } -// @public -export const useMenuListStyles_unstable: (state: MenuListState) => MenuListState; +export { useMenuListStyles_unstable } -// @public -export const useMenuPopover_unstable: (props: MenuPopoverProps, ref: React_2.Ref) => MenuPopoverState; +export { useMenuPopover_unstable } -// @public -export const useMenuPopoverStyles_unstable: (state: MenuPopoverState) => MenuPopoverState; +export { useMenuPopoverStyles_unstable } -// @public -export const useMenuSplitGroup_unstable: (props: MenuSplitGroupProps, ref: React_2.Ref) => MenuSplitGroupState; +export { useMenuSplitGroup_unstable } -// @public -export const useMenuSplitGroupStyles_unstable: (state: MenuSplitGroupState) => MenuSplitGroupState; +export { useMenuSplitGroupStyles_unstable } -// @public -export const useMenuTrigger_unstable: (props: MenuTriggerProps) => MenuTriggerState; +export { useMenuTrigger_unstable } -// @public (undocumented) -export const useMenuTriggerContext_unstable: () => boolean; +export { useMenuTriggerContext_unstable } -// @public -export function useMergedRefs(...refs: (React_2.Ref | undefined)[]): RefObjectFunction; +export { useMergedRefs } -// @public -export const useModalAttributes: (options?: UseModalAttributesOptions) => { - modalAttributes: Types.TabsterDOMAttribute; - triggerAttributes: Types.TabsterDOMAttribute; -}; +export { useModalAttributes } -// @public (undocumented) -export interface UseModalAttributesOptions { - alwaysFocusable?: boolean; - id?: string; - legacyTrapFocus?: boolean; - trapFocus?: boolean; -} +export { UseModalAttributesOptions } -// @public -export const useMotionPresence: (present: boolean, events?: UseMotionPresenceEvents | undefined) => UseMotionPresenceState; +export { useMotionPresence } -// @public -export type UseMotionPresenceEvents = { - onEntered?: () => void; - onExited?: () => void; -}; +export { UseMotionPresenceEvents } -// @public -export type UseMotionPresenceState = { - ref: React_2.RefCallback; - shouldRender: boolean; - visible: boolean; - entering: boolean; - exiting: boolean; - animating: boolean; -}; +export { UseMotionPresenceState } -// @public -export const useOption_unstable: (props: OptionProps, ref: React_2.Ref) => OptionState; +export { useOption_unstable } -// @public -export const useOptionGroup_unstable: (props: OptionGroupProps, ref: React_2.Ref) => OptionGroupState; +export { useOptionGroup_unstable } -// @public -export const useOptionGroupStyles_unstable: (state: OptionGroupState) => OptionGroupState; +export { useOptionGroupStyles_unstable } -// @public -export const useOptionStyles_unstable: (state: OptionState) => OptionState; +export { useOptionStyles_unstable } -// @public (undocumented) -export const useOverflowCount: () => number; +export { useOverflowCount } -// @public (undocumented) -export function useOverflowMenu(id?: string): { - ref: React_2.RefObject; - overflowCount: number; - isOverflowing: boolean; -}; +export { useOverflowMenu } -// @public -export const usePersona_unstable: (props: PersonaProps, ref: React_2.Ref) => PersonaState; +export { usePersona_unstable } -// @public -export const usePersonaStyles_unstable: (state: PersonaState) => PersonaState; +export { usePersonaStyles_unstable } -// @public -export const usePopover_unstable: (props: PopoverProps) => PopoverState; +export { usePopover_unstable } -// @public (undocumented) -export const usePopoverContext_unstable: (selector: ContextSelector) => T; +export { usePopoverContext_unstable } -// @public -export const usePopoverSurface_unstable: (props: PopoverSurfaceProps, ref: React_2.Ref) => PopoverSurfaceState; +export { usePopoverSurface_unstable } -// @public -export const usePopoverSurfaceStyles_unstable: (state: PopoverSurfaceState) => PopoverSurfaceState; +export { usePopoverSurfaceStyles_unstable } -// @public -export const usePopoverTrigger_unstable: (props: PopoverTriggerProps) => PopoverTriggerState; +export { usePopoverTrigger_unstable } -// @public -export const usePortal_unstable: (props: PortalProps) => PortalState; +export { usePortal_unstable } -// @public -export const usePresenceBadge_unstable: (props: PresenceBadgeProps, ref: React_2.Ref) => PresenceBadgeState; +export { usePresenceBadge_unstable } -// @public -export const usePresenceBadgeStyles_unstable: (state: PresenceBadgeState) => PresenceBadgeState; +export { usePresenceBadgeStyles_unstable } -// @public -export const useProgressBar_unstable: (props: ProgressBarProps, ref: React_2.Ref) => ProgressBarState; +export { useProgressBar_unstable } -// @public -export const useProgressBarStyles_unstable: (state: ProgressBarState) => ProgressBarState; +export { useProgressBarStyles_unstable } -// @public -export const useRadio_unstable: (props: RadioProps, ref: React_2.Ref) => RadioState; +export { useRadio_unstable } -// @public -export const useRadioGroup_unstable: (props: RadioGroupProps, ref: React_2.Ref) => RadioGroupState; +export { useRadioGroup_unstable } -// @public @deprecated (undocumented) -export const useRadioGroupContext_unstable: (selector: (ctx: RadioGroupContextValue) => T) => T; +export { useRadioGroupContext_unstable } -// @public -export const useRadioGroupContextValue_unstable: () => RadioGroupContextValue; +export { useRadioGroupContextValue_unstable } -// @public (undocumented) -export const useRadioGroupContextValues: (state: RadioGroupState) => RadioGroupContextValues; +export { useRadioGroupContextValues } -// @public -export const useRadioGroupStyles_unstable: (state: RadioGroupState) => void; +export { useRadioGroupStyles_unstable } -// @public -export const useRadioStyles_unstable: (state: RadioState) => void; +export { useRadioStyles_unstable } -// @public (undocumented) -export function useScrollbarWidth(options: UseScrollbarWidthOptions): number | undefined; +export { useScrollbarWidth } -// @public -export const useSelect_unstable: (props: SelectProps, ref: React_2.Ref) => SelectState; +export { useSelect_unstable } -// @public -export const useSelectStyles_unstable: (state: SelectState) => SelectState; +export { useSelectStyles_unstable } -// @public -export const useSkeleton_unstable: (props: SkeletonProps, ref: React_2.Ref) => SkeletonState; +export { useSkeleton_unstable } -// @public (undocumented) -export const useSkeletonContext: () => SkeletonContextValue; +export { useSkeletonContext } -// @public -export const useSkeletonItem_unstable: (props: SkeletonItemProps, ref: React_2.Ref) => SkeletonItemState; +export { useSkeletonItem_unstable } -// @public -export const useSkeletonItemStyles_unstable: (state: SkeletonItemState) => SkeletonItemState; +export { useSkeletonItemStyles_unstable } -// @public -export const useSkeletonStyles_unstable: (state: SkeletonState) => SkeletonState; +export { useSkeletonStyles_unstable } -// @public (undocumented) -export const useSlider_unstable: (props: SliderProps, ref: React_2.Ref) => SliderState; +export { useSlider_unstable } -// @public (undocumented) -export const useSliderState_unstable: (state: SliderState, props: SliderProps) => SliderState; +export { useSliderState_unstable } -// @public -export const useSliderStyles_unstable: (state: SliderState) => SliderState; +export { useSliderStyles_unstable } -// @public -export const useSpinButton_unstable: (props: SpinButtonProps, ref: React_2.Ref) => SpinButtonState; +export { useSpinButton_unstable } -// @public -export const useSpinButtonStyles_unstable: (state: SpinButtonState) => SpinButtonState; +export { useSpinButtonStyles_unstable } -// @public -export const useSpinner_unstable: (props: SpinnerProps, ref: React_2.Ref) => SpinnerState; +export { useSpinner_unstable } -// @public -export const useSpinnerStyles_unstable: (state: SpinnerState) => SpinnerState; +export { useSpinnerStyles_unstable } -// @public -export const useSplitButton_unstable: (props: SplitButtonProps, ref: React_2.Ref) => SplitButtonState; +export { useSplitButton_unstable } -// @public (undocumented) -export const useSplitButtonStyles_unstable: (state: SplitButtonState) => SplitButtonState; +export { useSplitButtonStyles_unstable } -// @public -export const useSwitch_unstable: (props: SwitchProps, ref: React_2.Ref) => SwitchState; +export { useSwitch_unstable } -// @public -export const useSwitchStyles_unstable: (state: SwitchState) => SwitchState; +export { useSwitchStyles_unstable } -// @public -export const useTab_unstable: (props: TabProps, ref: React_2.Ref) => TabState; +export { useTab_unstable } -// @public -export const useTable_unstable: (props: TableProps, ref: React_2.Ref) => TableState; +export { useTable_unstable } -// @public -export const useTableBody_unstable: (props: TableBodyProps, ref: React_2.Ref) => TableBodyState; +export { useTableBody_unstable } -// @public -export const useTableBodyStyles_unstable: (state: TableBodyState) => TableBodyState; +export { useTableBodyStyles_unstable } -// @public -export const useTableCell_unstable: (props: TableCellProps, ref: React_2.Ref) => TableCellState; +export { useTableCell_unstable } -// @public -export const useTableCellActions_unstable: (props: TableCellActionsProps, ref: React_2.Ref) => TableCellActionsState; +export { useTableCellActions_unstable } -// @public -export const useTableCellActionsStyles_unstable: (state: TableCellActionsState) => TableCellActionsState; +export { useTableCellActionsStyles_unstable } -// @public -export const useTableCellLayout_unstable: (props: TableCellLayoutProps, ref: React_2.Ref) => TableCellLayoutState; +export { useTableCellLayout_unstable } -// @public -export const useTableCellLayoutStyles_unstable: (state: TableCellLayoutState) => TableCellLayoutState; +export { useTableCellLayoutStyles_unstable } -// @public -export const useTableCellStyles_unstable: (state: TableCellState) => TableCellState; +export { useTableCellStyles_unstable } -// @public (undocumented) -export function useTableColumnSizing_unstable(params?: UseTableColumnSizingParams): (tableState: TableFeaturesState) => TableFeaturesState; +export { useTableColumnSizing_unstable } -// @public (undocumented) -export const useTableContext: () => TableContextValue; +export { useTableContext } -// @public (undocumented) -export function useTableFeatures(options: UseTableFeaturesOptions, plugins?: TableFeaturePlugin[]): TableFeaturesState; +export { useTableFeatures } -// @public (undocumented) -export interface UseTableFeaturesOptions { - // (undocumented) - columns: TableColumnDefinition[]; - // (undocumented) - getRowId?: (item: TItem) => TableRowId; - // (undocumented) - items: TItem[]; -} +export { UseTableFeaturesOptions } -// @public -export const useTableHeader_unstable: (props: TableHeaderProps, ref: React_2.Ref) => TableHeaderState; +export { useTableHeader_unstable } -// @public -export const useTableHeaderCell_unstable: (props: TableHeaderCellProps, ref: React_2.Ref) => TableHeaderCellState; +export { useTableHeaderCell_unstable } -// @public -export const useTableHeaderCellStyles_unstable: (state: TableHeaderCellState) => TableHeaderCellState; +export { useTableHeaderCellStyles_unstable } -// @public -export const useTableHeaderStyles_unstable: (state: TableHeaderState) => TableHeaderState; +export { useTableHeaderStyles_unstable } -// @public -export const useTableResizeHandle_unstable: (props: TableResizeHandleProps, ref: React_2.Ref) => TableResizeHandleState; +export { useTableResizeHandle_unstable } -// @public -export const useTableResizeHandleStyles_unstable: (state: TableResizeHandleState) => TableResizeHandleState; +export { useTableResizeHandleStyles_unstable } -// @public -export const useTableRow_unstable: (props: TableRowProps, ref: React_2.Ref) => TableRowState; +export { useTableRow_unstable } -// @public (undocumented) -export const useTableRowIdContext: () => TableRowId; +export { useTableRowIdContext } -// @public -export const useTableRowStyles_unstable: (state: TableRowState) => TableRowState; +export { useTableRowStyles_unstable } -// @public (undocumented) -export function useTableSelection(options: UseTableSelectionOptions): (tableState: TableFeaturesState) => TableFeaturesState; +export { useTableSelection } -// @public -export const useTableSelectionCell_unstable: (props: TableSelectionCellProps, ref: React_2.Ref) => TableSelectionCellState; +export { useTableSelectionCell_unstable } -// @public -export const useTableSelectionCellStyles_unstable: (state: TableSelectionCellState) => TableSelectionCellState; +export { useTableSelectionCellStyles_unstable } -// @public (undocumented) -export function useTableSort(options: UseTableSortOptions): (tableState: TableFeaturesState) => TableFeaturesState; +export { useTableSort } -// @public -export const useTableStyles_unstable: (state: TableState) => TableState; +export { useTableStyles_unstable } -// @public -export const useTabList_unstable: (props: TabListProps, ref: React_2.Ref) => TabListState; +export { useTabList_unstable } -// @public (undocumented) -export const useTabListContext_unstable: (selector: ContextSelector) => T; +export { useTabListContext_unstable } -// @public (undocumented) -export function useTabListContextValues_unstable(state: TabListState): TabListContextValues; +export { useTabListContextValues_unstable } -// @public -export const useTabListStyles_unstable: (state: TabListState) => TabListState; +export { useTabListStyles_unstable } -// @public -export const useTabStyles_unstable: (state: TabState) => TabState; +export { useTabStyles_unstable } -// @public -export const useText_unstable: (props: TextProps, ref: React_2.Ref) => TextState; +export { useText_unstable } -// @public -export const useTextarea_unstable: (props: TextareaProps, ref: React_2.Ref) => TextareaState; +export { useTextarea_unstable } -// @public -export const useTextareaStyles_unstable: (state: TextareaState) => TextareaState; +export { useTextareaStyles_unstable } -// @public -export const useTextStyles_unstable: (state: TextState) => TextState; +export { useTextStyles_unstable } -// @public (undocumented) -export function useThemeClassName(): ThemeClassNameContextValue; +export { useThemeClassName } -// @public -export const useToggleButton_unstable: (props: ToggleButtonProps, ref: React_2.Ref) => ToggleButtonState; +export { useToggleButton_unstable } -// @public (undocumented) -export const useToggleButtonStyles_unstable: (state: ToggleButtonState) => ToggleButtonState; +export { useToggleButtonStyles_unstable } -// @public (undocumented) -export function useToggleState, TButtonState extends Pick, TToggleButtonState extends Pick>(props: TToggleButtonProps, state: TButtonState): TToggleButtonState; +export { useToggleState } -// @public -export const useToolbar_unstable: (props: ToolbarProps, ref: React_2.Ref) => ToolbarState; +export { useToolbar_unstable } -// @public -export const useToolbarButton_unstable: (props: ToolbarButtonProps, ref: React_2.Ref) => ToolbarButtonState; +export { useToolbarButton_unstable } -// @public -export const useToolbarButtonStyles_unstable: (state: ToolbarButtonState) => void; +export { useToolbarButtonStyles_unstable } -// @public -export const useToolbarDivider_unstable: (props: ToolbarDividerProps, ref: React_2.Ref) => ToolbarDividerState; +export { useToolbarDivider_unstable } -// @public -export const useToolbarDividerStyles_unstable: (state: ToolbarDividerState) => ToolbarDividerState; +export { useToolbarDividerStyles_unstable } -// @public -export const useToolbarGroup_unstable: (props: ToolbarGroupProps, ref: React_2.Ref) => ToolbarGroupState; +export { useToolbarGroup_unstable } -// @public -export const useToolbarGroupStyles_unstable: (state: ToolbarGroupState) => ToolbarGroupState; +export { useToolbarGroupStyles_unstable } -// @public -export const useToolbarRadioButton_unstable: (props: ToolbarRadioButtonProps, ref: React_2.Ref) => ToolbarRadioButtonState; +export { useToolbarRadioButton_unstable } -// @public -export const useToolbarRadioButtonStyles_unstable: (state: ToolbarRadioButtonState) => void; +export { useToolbarRadioButtonStyles_unstable } -// @public -export const useToolbarStyles_unstable: (state: ToolbarState) => ToolbarState; +export { useToolbarStyles_unstable } -// @public -export const useToolbarToggleButton_unstable: (props: ToolbarToggleButtonProps, ref: React_2.Ref) => ToolbarToggleButtonState; +export { useToolbarToggleButton_unstable } -// @public -export const useToolbarToggleButtonStyles_unstable: (state: ToolbarToggleButtonState) => void; +export { useToolbarToggleButtonStyles_unstable } -// @public -export const useTooltip_unstable: (props: TooltipProps) => TooltipState; +export { useTooltip_unstable } -// @public -export const useTooltipStyles_unstable: (state: TooltipState) => TooltipState; +export { useTooltipStyles_unstable } -// @public (undocumented) -export function useTooltipVisibility(): TooltipVisibilityContextValue; +export { useTooltipVisibility } -// @public (undocumented) -export type VerticalSpacingTokens = { - spacingVerticalNone: string; - spacingVerticalXXS: string; - spacingVerticalXS: string; - spacingVerticalSNudge: string; - spacingVerticalS: string; - spacingVerticalMNudge: string; - spacingVerticalM: string; - spacingVerticalL: string; - spacingVerticalXL: string; - spacingVerticalXXL: string; - spacingVerticalXXXL: string; -}; +export { VerticalSpacingTokens } -// @public (undocumented) -export const webDarkTheme: Theme; +export { webDarkTheme } -// @public (undocumented) -export const webLightTheme: Theme; +export { webLightTheme } // (No @packageDocumentation comment for this package) diff --git a/packages/react-components/react-components/etc/react-components.unstable.api.md b/packages/react-components/react-components/etc/react-components.unstable.api.md index 13429ca41a0e12..560f9df2de9bb6 100644 --- a/packages/react-components/react-components/etc/react-components.unstable.api.md +++ b/packages/react-components/react-components/etc/react-components.unstable.api.md @@ -4,812 +4,543 @@ ```ts -/// +import { Alert } from '@fluentui/react-alert'; +import { alertClassNames } from '@fluentui/react-alert'; +import { AlertProps } from '@fluentui/react-alert'; +import { AlertSlots } from '@fluentui/react-alert'; +import { AlertState } from '@fluentui/react-alert'; +import { Drawer } from '@fluentui/react-drawer'; +import { DrawerBody } from '@fluentui/react-drawer'; +import { drawerBodyClassNames } from '@fluentui/react-drawer'; +import { DrawerBodySlots } from '@fluentui/react-drawer'; +import { DrawerBodyState } from '@fluentui/react-drawer'; +import { DrawerFooter } from '@fluentui/react-drawer'; +import { drawerFooterClassNames } from '@fluentui/react-drawer'; +import { DrawerFooterSlots } from '@fluentui/react-drawer'; +import { DrawerFooterState } from '@fluentui/react-drawer'; +import { DrawerHeader } from '@fluentui/react-drawer'; +import { drawerHeaderClassNames } from '@fluentui/react-drawer'; +import { DrawerHeaderNavigation } from '@fluentui/react-drawer'; +import { drawerHeaderNavigationClassNames } from '@fluentui/react-drawer'; +import { DrawerHeaderNavigationProps } from '@fluentui/react-drawer'; +import { DrawerHeaderNavigationSlots } from '@fluentui/react-drawer'; +import { DrawerHeaderNavigationState } from '@fluentui/react-drawer'; +import { DrawerHeaderSlots } from '@fluentui/react-drawer'; +import { DrawerHeaderState } from '@fluentui/react-drawer'; +import { DrawerHeaderTitle } from '@fluentui/react-drawer'; +import { drawerHeaderTitleClassNames } from '@fluentui/react-drawer'; +import { DrawerHeaderTitleSlots } from '@fluentui/react-drawer'; +import { DrawerHeaderTitleState } from '@fluentui/react-drawer'; +import { DrawerInline } from '@fluentui/react-drawer'; +import { drawerInlineClassNames } from '@fluentui/react-drawer'; +import { DrawerInlineProps } from '@fluentui/react-drawer'; +import { DrawerInlineSlots } from '@fluentui/react-drawer'; +import { DrawerInlineState } from '@fluentui/react-drawer'; +import { DrawerOverlay } from '@fluentui/react-drawer'; +import { drawerOverlayClassNames } from '@fluentui/react-drawer'; +import { DrawerOverlayProps } from '@fluentui/react-drawer'; +import { DrawerOverlaySlots } from '@fluentui/react-drawer'; +import { DrawerOverlayState } from '@fluentui/react-drawer'; +import { DrawerProps } from '@fluentui/react-drawer'; +import { DrawerSlots } from '@fluentui/react-drawer'; +import { DrawerState } from '@fluentui/react-drawer'; +import { flattenTree_unstable } from '@fluentui/react-tree'; +import { FlatTree } from '@fluentui/react-tree'; +import { FlatTreeItem } from '@fluentui/react-tree'; +import { FlatTreeItemProps } from '@fluentui/react-tree'; +import { FlatTreeProps } from '@fluentui/react-tree'; +import { InfoButton } from '@fluentui/react-infobutton'; +import { infoButtonClassNames } from '@fluentui/react-infobutton'; +import { InfoButtonProps } from '@fluentui/react-infobutton'; +import { InfoButtonSlots } from '@fluentui/react-infobutton'; +import { InfoButtonState } from '@fluentui/react-infobutton'; +import { InfoLabel } from '@fluentui/react-infobutton'; +import { infoLabelClassNames } from '@fluentui/react-infobutton'; +import { InfoLabelProps } from '@fluentui/react-infobutton'; +import { InfoLabelSlots } from '@fluentui/react-infobutton'; +import { InfoLabelState } from '@fluentui/react-infobutton'; +import { NestedTreeItem } from '@fluentui/react-tree'; +import { renderAlert_unstable } from '@fluentui/react-alert'; +import { renderDrawer_unstable } from '@fluentui/react-drawer'; +import { renderDrawerBody_unstable } from '@fluentui/react-drawer'; +import { renderDrawerFooter_unstable } from '@fluentui/react-drawer'; +import { renderDrawerHeader_unstable } from '@fluentui/react-drawer'; +import { renderDrawerHeaderNavigation_unstable } from '@fluentui/react-drawer'; +import { renderDrawerHeaderTitle_unstable } from '@fluentui/react-drawer'; +import { renderDrawerInline_unstable } from '@fluentui/react-drawer'; +import { renderDrawerOverlay_unstable } from '@fluentui/react-drawer'; +import { renderInfoButton_unstable } from '@fluentui/react-infobutton'; +import { renderInfoLabel_unstable } from '@fluentui/react-infobutton'; +import { renderTree_unstable } from '@fluentui/react-tree'; +import { renderTreeItem_unstable } from '@fluentui/react-tree'; +import { renderTreeItemAside_unstable } from '@fluentui/react-tree'; +import { renderTreeItemLayout_unstable } from '@fluentui/react-tree'; +import { renderTreeItemPersonaLayout_unstable } from '@fluentui/react-tree'; +import { renderVirtualizer_unstable } from '@fluentui/react-virtualizer'; +import { renderVirtualizerScrollView_unstable } from '@fluentui/react-virtualizer'; +import { renderVirtualizerScrollViewDynamic_unstable } from '@fluentui/react-virtualizer'; +import { ResizeCallbackWithRef } from '@fluentui/react-virtualizer'; +import { ScrollToInterface } from '@fluentui/react-virtualizer'; +import { scrollToItemDynamic } from '@fluentui/react-virtualizer'; +import { ScrollToItemDynamicParams } from '@fluentui/react-virtualizer'; +import { scrollToItemStatic } from '@fluentui/react-virtualizer'; +import { ScrollToItemStaticParams } from '@fluentui/react-virtualizer'; +import { Tree } from '@fluentui/react-tree'; +import { treeClassNames } from '@fluentui/react-tree'; +import { TreeContextValue } from '@fluentui/react-tree'; +import { TreeItem } from '@fluentui/react-tree'; +import { TreeItemAside } from '@fluentui/react-tree'; +import { treeItemAsideClassNames } from '@fluentui/react-tree'; +import { TreeItemAsideProps } from '@fluentui/react-tree'; +import { TreeItemAsideSlots } from '@fluentui/react-tree'; +import { TreeItemAsideState } from '@fluentui/react-tree'; +import { treeItemClassNames } from '@fluentui/react-tree'; +import { TreeItemLayout } from '@fluentui/react-tree'; +import { treeItemLayoutClassNames } from '@fluentui/react-tree'; +import { TreeItemLayoutProps } from '@fluentui/react-tree'; +import { TreeItemLayoutSlots } from '@fluentui/react-tree'; +import { TreeItemLayoutState } from '@fluentui/react-tree'; +import { treeItemLevelToken } from '@fluentui/react-tree'; +import { TreeItemPersonaLayout } from '@fluentui/react-tree'; +import { treeItemPersonaLayoutClassNames } from '@fluentui/react-tree'; +import { TreeItemPersonaLayoutProps } from '@fluentui/react-tree'; +import { TreeItemPersonaLayoutSlots } from '@fluentui/react-tree'; +import { TreeItemPersonaLayoutState } from '@fluentui/react-tree'; +import { TreeItemProps } from '@fluentui/react-tree'; +import { TreeItemProvider } from '@fluentui/react-tree'; +import { TreeItemSlots } from '@fluentui/react-tree'; +import { TreeItemState } from '@fluentui/react-tree'; +import { TreeNavigationData_unstable } from '@fluentui/react-tree'; +import { TreeNavigationEvent_unstable } from '@fluentui/react-tree'; +import { TreeOpenChangeData } from '@fluentui/react-tree'; +import { TreeOpenChangeEvent } from '@fluentui/react-tree'; +import { TreeProps } from '@fluentui/react-tree'; +import { TreeProvider } from '@fluentui/react-tree'; +import { TreeSlots } from '@fluentui/react-tree'; +import { TreeState } from '@fluentui/react-tree'; +import { useAlert_unstable } from '@fluentui/react-alert'; +import { useAlertStyles_unstable } from '@fluentui/react-alert'; +import { useDrawer_unstable } from '@fluentui/react-drawer'; +import { useDrawerBody_unstable } from '@fluentui/react-drawer'; +import { useDrawerBodyStyles_unstable } from '@fluentui/react-drawer'; +import { useDrawerFooter_unstable } from '@fluentui/react-drawer'; +import { useDrawerFooterStyles_unstable } from '@fluentui/react-drawer'; +import { useDrawerHeader_unstable } from '@fluentui/react-drawer'; +import { useDrawerHeaderNavigation_unstable } from '@fluentui/react-drawer'; +import { useDrawerHeaderNavigationStyles_unstable } from '@fluentui/react-drawer'; +import { useDrawerHeaderStyles_unstable } from '@fluentui/react-drawer'; +import { useDrawerHeaderTitle_unstable } from '@fluentui/react-drawer'; +import { useDrawerHeaderTitleStyles_unstable } from '@fluentui/react-drawer'; +import { useDrawerInline_unstable } from '@fluentui/react-drawer'; +import { useDrawerInlineStyles_unstable } from '@fluentui/react-drawer'; +import { useDrawerOverlay_unstable } from '@fluentui/react-drawer'; +import { useDrawerOverlayStyles_unstable } from '@fluentui/react-drawer'; +import { useDynamicVirtualizerMeasure } from '@fluentui/react-virtualizer'; +import { useFlatTree_unstable } from '@fluentui/react-tree'; +import { useInfoButton_unstable } from '@fluentui/react-infobutton'; +import { useInfoButtonStyles_unstable } from '@fluentui/react-infobutton'; +import { useInfoLabel_unstable } from '@fluentui/react-infobutton'; +import { useInfoLabelStyles_unstable } from '@fluentui/react-infobutton'; +import { useIntersectionObserver } from '@fluentui/react-virtualizer'; +import { useResizeObserverRef_unstable } from '@fluentui/react-virtualizer'; +import { useStaticVirtualizerMeasure } from '@fluentui/react-virtualizer'; +import { useTree_unstable } from '@fluentui/react-tree'; +import { useTreeContext_unstable } from '@fluentui/react-tree'; +import { useTreeItem_unstable } from '@fluentui/react-tree'; +import { useTreeItemAside_unstable } from '@fluentui/react-tree'; +import { useTreeItemAsideStyles_unstable } from '@fluentui/react-tree'; +import { useTreeItemContext_unstable } from '@fluentui/react-tree'; +import { useTreeItemLayout_unstable } from '@fluentui/react-tree'; +import { useTreeItemLayoutStyles_unstable } from '@fluentui/react-tree'; +import { useTreeItemPersonaLayout_unstable } from '@fluentui/react-tree'; +import { useTreeItemPersonaLayoutStyles_unstable } from '@fluentui/react-tree'; +import { useTreeItemStyles_unstable } from '@fluentui/react-tree'; +import { useTreeStyles_unstable } from '@fluentui/react-tree'; +import { useVirtualizer_unstable } from '@fluentui/react-virtualizer'; +import { useVirtualizerContext_unstable } from '@fluentui/react-virtualizer'; +import { useVirtualizerScrollView_unstable } from '@fluentui/react-virtualizer'; +import { useVirtualizerScrollViewDynamic_unstable } from '@fluentui/react-virtualizer'; +import { useVirtualizerScrollViewDynamicStyles_unstable } from '@fluentui/react-virtualizer'; +import { useVirtualizerScrollViewStyles_unstable } from '@fluentui/react-virtualizer'; +import { useVirtualizerStyles_unstable } from '@fluentui/react-virtualizer'; +import { Virtualizer } from '@fluentui/react-virtualizer'; +import { VirtualizerChildRenderFunction } from '@fluentui/react-virtualizer'; +import { virtualizerClassNames } from '@fluentui/react-virtualizer'; +import { VirtualizerContextProps } from '@fluentui/react-virtualizer'; +import { VirtualizerContextProvider } from '@fluentui/react-virtualizer'; +import { VirtualizerMeasureDynamicProps } from '@fluentui/react-virtualizer'; +import { VirtualizerMeasureProps } from '@fluentui/react-virtualizer'; +import { VirtualizerProps } from '@fluentui/react-virtualizer'; +import { VirtualizerScrollView } from '@fluentui/react-virtualizer'; +import { virtualizerScrollViewClassNames } from '@fluentui/react-virtualizer'; +import { VirtualizerScrollViewDynamic } from '@fluentui/react-virtualizer'; +import { virtualizerScrollViewDynamicClassNames } from '@fluentui/react-virtualizer'; +import { VirtualizerScrollViewDynamicProps } from '@fluentui/react-virtualizer'; +import { VirtualizerScrollViewDynamicSlots } from '@fluentui/react-virtualizer'; +import { VirtualizerScrollViewDynamicState } from '@fluentui/react-virtualizer'; +import { VirtualizerScrollViewProps } from '@fluentui/react-virtualizer'; +import { VirtualizerScrollViewSlots } from '@fluentui/react-virtualizer'; +import { VirtualizerScrollViewState } from '@fluentui/react-virtualizer'; +import { VirtualizerSlots } from '@fluentui/react-virtualizer'; +import { VirtualizerState } from '@fluentui/react-virtualizer'; -import type { Dispatch } from 'react'; -import { FC } from 'react'; -import { MutableRefObject } from 'react'; -import { Provider } from 'react'; -import { ProviderProps } from 'react'; -import * as React_2 from 'react'; -import type { RefObject } from 'react'; -import type { SetStateAction } from 'react'; +export { Alert } -// @public -export const Alert: ForwardRefComponent; +export { alertClassNames } -// @public (undocumented) -export const alertClassNames: SlotClassNames; +export { AlertProps } -// @public -export type AlertProps = ComponentProps & { - intent?: 'info' | 'success' | 'error' | 'warning'; - appearance?: 'primary' | 'inverted'; -}; +export { AlertSlots } -// @public (undocumented) -export type AlertSlots = { - root: NonNullable>; - icon?: Slot<'span'>; - action?: Slot; - avatar?: Slot; -}; +export { AlertState } -// @public -export type AlertState = ComponentState & Pick & Required>; +export { Drawer } -// @public -export const Drawer: ForwardRefComponent; +export { DrawerBody } -// @public -export const DrawerBody: ForwardRefComponent; +export { drawerBodyClassNames } -// @public (undocumented) -export const drawerBodyClassNames: SlotClassNames; +export { DrawerBodySlots } -// @public (undocumented) -export type DrawerBodySlots = { - root: Slot<'div'>; -}; +export { DrawerBodyState } -// @public -export type DrawerBodyState = ComponentState; +export { DrawerFooter } -// @public -export const DrawerFooter: ForwardRefComponent; +export { drawerFooterClassNames } -// @public (undocumented) -export const drawerFooterClassNames: SlotClassNames; +export { DrawerFooterSlots } -// @public (undocumented) -export type DrawerFooterSlots = { - root: Slot<'footer'>; -}; +export { DrawerFooterState } -// @public -export type DrawerFooterState = ComponentState; +export { DrawerHeader } -// @public -export const DrawerHeader: ForwardRefComponent; +export { drawerHeaderClassNames } -// @public (undocumented) -export const drawerHeaderClassNames: SlotClassNames; +export { DrawerHeaderNavigation } -// @public -export const DrawerHeaderNavigation: ForwardRefComponent; +export { drawerHeaderNavigationClassNames } -// @public (undocumented) -export const drawerHeaderNavigationClassNames: SlotClassNames; +export { DrawerHeaderNavigationProps } -// @public -export type DrawerHeaderNavigationProps = ComponentProps; - -// @public (undocumented) -export type DrawerHeaderNavigationSlots = { - root: Slot<'nav'>; -}; - -// @public -export type DrawerHeaderNavigationState = ComponentState; - -// @public (undocumented) -export type DrawerHeaderSlots = { - root: Slot<'header'>; -}; - -// @public -export type DrawerHeaderState = ComponentState; - -// @public -export const DrawerHeaderTitle: ForwardRefComponent; - -// @public (undocumented) -export const drawerHeaderTitleClassNames: SlotClassNames; - -// @public (undocumented) -export type DrawerHeaderTitleSlots = { - root: Slot<'div'>; - heading?: DialogTitleSlots['root']; - action?: DialogTitleSlots['action']; -}; - -// @public -export type DrawerHeaderTitleState = ComponentState; - -// @public -export const DrawerInline: ForwardRefComponent; - -// @public (undocumented) -export const drawerInlineClassNames: SlotClassNames; - -// @public -export type DrawerInlineProps = ComponentProps & DrawerBaseProps & { - separator?: boolean; -}; - -// @public (undocumented) -export type DrawerInlineSlots = { - root: Slot<'div'>; -}; - -// @public -export type DrawerInlineState = ComponentState & DrawerBaseProps & DrawerBaseState & Pick; +export { DrawerHeaderNavigationSlots } -// @public -export const DrawerOverlay: ForwardRefComponent; - -// @public (undocumented) -export const drawerOverlayClassNames: SlotClassNames; - -// @public -export type DrawerOverlayProps = ComponentProps & DrawerBaseProps & Pick; - -// @public (undocumented) -export type DrawerOverlaySlots = DialogSurfaceSlots & { - root: Slot; -}; - -// @public -export type DrawerOverlayState = ComponentState & DrawerBaseProps & DrawerBaseState & { - dialog: DialogProps; - backdropVisible: boolean; -}; - -// @public -export type DrawerProps = ComponentProps> & { - type?: 'inline' | 'overlay'; -}; - -// @public (undocumented) -export type DrawerSlots = { - root: Slot; -}; - -// @public -export type DrawerState = ComponentState; - -// @public -export const flattenTree_unstable: >(items: NestedTreeItem[]) => FlattenedTreeItem[]; - -// @public -export type FlatTree = FlatTreeItemProps> = { - getTreeProps(): FlatTreeProps; - navigate(data: TreeNavigationData_unstable): void; - getNextNavigableItem(visibleItems: FlatTreeItem[], data: TreeNavigationData_unstable): FlatTreeItem | undefined; - items(): IterableIterator>; -}; - -// @public -export type FlatTreeItem = FlatTreeItemProps> = { - index: number; - level: number; - childrenSize: number; - value: Props['value']; - parentValue: Props['parentValue']; - ref: React_2.RefObject; - getTreeItemProps(): Required> & Omit; -}; - -// @public (undocumented) -export type FlatTreeItemProps = Omit, 'itemType'> & Partial, 'itemType'>> & { - value: Value; - parentValue?: Value; -}; +export { DrawerHeaderNavigationState } -// @public (undocumented) -export type FlatTreeProps = Required, 'openItems' | 'onOpenChange' | 'onNavigation_unstable'>> & { - ref: React_2.Ref; - openItems: ImmutableSet; -}; +export { DrawerHeaderSlots } -// @public -export const InfoButton: ForwardRefComponent; +export { DrawerHeaderState } -// @public (undocumented) -export const infoButtonClassNames: SlotClassNames; +export { DrawerHeaderTitle } -// @public -export type InfoButtonProps = Omit>, 'disabled'> & { - size?: 'small' | 'medium' | 'large'; -}; +export { drawerHeaderTitleClassNames } -// @public (undocumented) -export type InfoButtonSlots = { - root: NonNullable>; - popover: NonNullable>>; - info: NonNullable>; -}; +export { DrawerHeaderTitleSlots } -// @public -export type InfoButtonState = ComponentState & Required>; - -// @public -export const InfoLabel: ForwardRefComponent; - -// @public (undocumented) -export const infoLabelClassNames: SlotClassNames; - -// @public -export type InfoLabelProps = ComponentProps, 'label'> & { - info?: InfoButtonProps['info']; -}; - -// @public (undocumented) -export type InfoLabelSlots = { - root: NonNullable>; - label: NonNullable>; - infoButton: Slot; -}; - -// @public -export type InfoLabelState = ComponentState & Pick; - -// @public (undocumented) -export type NestedTreeItem> = Omit & { - subtree?: NestedTreeItem[]; -}; - -// @public (undocumented) -export const renderAlert_unstable: (state: AlertState) => JSX.Element; - -// @public -export const renderDrawer_unstable: (state: DrawerState) => JSX.Element; - -// @public -export const renderDrawerBody_unstable: (state: DrawerBodyState) => JSX.Element; - -// @public -export const renderDrawerFooter_unstable: (state: DrawerFooterState) => JSX.Element; - -// @public -export const renderDrawerHeader_unstable: (state: DrawerHeaderState) => JSX.Element; - -// @public -export const renderDrawerHeaderNavigation_unstable: (state: DrawerHeaderNavigationState) => JSX.Element; - -// @public -export const renderDrawerHeaderTitle_unstable: (state: DrawerHeaderTitleState) => JSX.Element; - -// @public -export const renderDrawerInline_unstable: (state: DrawerInlineState) => JSX.Element | null; - -// @public -export const renderDrawerOverlay_unstable: (state: DrawerOverlayState) => JSX.Element; - -// @public -export const renderInfoButton_unstable: (state: InfoButtonState) => JSX.Element; - -// @public -export const renderInfoLabel_unstable: (state: InfoLabelState) => JSX.Element; - -// @public (undocumented) -export const renderTree_unstable: (state: TreeState, contextValues: TreeContextValues) => JSX.Element; - -// @public -export const renderTreeItem_unstable: (state: TreeItemState, contextValues: TreeItemContextValues) => JSX.Element; - -// @public -export const renderTreeItemAside_unstable: (state: TreeItemAsideState) => JSX.Element | null; - -// @public -export const renderTreeItemLayout_unstable: (state: TreeItemLayoutState) => JSX.Element; - -// @public -export const renderTreeItemPersonaLayout_unstable: (state: TreeItemPersonaLayoutState, contextValues: TreeItemPersonaLayoutContextValues) => JSX.Element; - -// @public (undocumented) -export const renderVirtualizer_unstable: (state: VirtualizerState) => JSX.Element; - -// @public (undocumented) -export const renderVirtualizerScrollView_unstable: (state: VirtualizerScrollViewState) => JSX.Element; - -// @public (undocumented) -export const renderVirtualizerScrollViewDynamic_unstable: (state: VirtualizerScrollViewDynamicState) => JSX.Element; - -// @public -export interface ResizeCallbackWithRef { - // (undocumented) - (entries: ResizeObserverEntry[], observer: ResizeObserver, scrollRef?: MutableRefObject): void; -} - -// @public (undocumented) -export type ScrollToInterface = { - scrollTo: (index: number, behavior?: ScrollBehavior, callback?: (index: number) => void) => void; -}; - -// @public (undocumented) -export const scrollToItemDynamic: (params: ScrollToItemDynamicParams) => void; - -// @public (undocumented) -export type ScrollToItemDynamicParams = { - index: number; - itemSizes: RefObject; - totalSize: number; - scrollViewRef: RefObject; - axis?: 'horizontal' | 'vertical'; - reversed?: boolean; - behavior?: ScrollBehavior; -}; - -// @public (undocumented) -export const scrollToItemStatic: (params: ScrollToItemStaticParams) => void; - -// @public (undocumented) -export type ScrollToItemStaticParams = { - index: number; - itemSize: number; - totalItems: number; - scrollViewRef: RefObject; - axis?: 'horizontal' | 'vertical'; - reversed?: boolean; - behavior?: ScrollBehavior; -}; - -// @public -export const Tree: React_2.ForwardRefExoticComponent & Omit<{ - as?: "div" | undefined; -} & Pick, HTMLDivElement>, "key" | keyof React_2.HTMLAttributes> & { - ref?: ((instance: HTMLDivElement | null) => void) | React_2.RefObject | null | undefined; -} & { - children?: React_2.ReactNode | SlotRenderFunction, HTMLDivElement>, "key" | keyof React_2.HTMLAttributes> & { - ref?: ((instance: HTMLDivElement | null) => void) | React_2.RefObject | null | undefined; - }>; -}, "ref"> & { - appearance?: "transparent" | "subtle" | "subtle-alpha" | undefined; - size?: "small" | "medium" | undefined; - openItems?: Iterable | undefined; - defaultOpenItems?: Iterable | undefined; - onOpenChange?(event: React_2.KeyboardEvent | React_2.MouseEvent, data: TreeOpenChangeData): void; - onNavigation_unstable?(event: React_2.KeyboardEvent | React_2.MouseEvent, data: TreeNavigationData_unstable): void; -} & React_2.RefAttributes> & ((props: TreeProps) => JSX.Element); - -// @public (undocumented) -export const treeClassNames: SlotClassNames; - -// @public (undocumented) -export type TreeContextValue = { - level: number; - appearance: 'subtle' | 'subtle-alpha' | 'transparent'; - size: 'small' | 'medium'; - openItems: ImmutableSet; - requestTreeResponse(request: TreeItemRequest): void; -}; - -// @public -export const TreeItem: React_2.ForwardRefExoticComponent, "root"> & Omit<{ - as?: "div" | undefined; -} & Pick, HTMLDivElement>, "key" | keyof React_2.HTMLAttributes> & { - ref?: ((instance: HTMLDivElement | null) => void) | React_2.RefObject | null | undefined; -} & { - children?: React_2.ReactNode | SlotRenderFunction, HTMLDivElement>, "key" | keyof React_2.HTMLAttributes> & { - ref?: ((instance: HTMLDivElement | null) => void) | React_2.RefObject | null | undefined; - }>; -} & { - style?: TreeItemCSSProperties | undefined; -}, "ref"> & { - value?: string | undefined; - itemType: TreeItemType; -} & React_2.RefAttributes> & ((props: TreeItemProps) => JSX.Element); - -// @public -export const TreeItemAside: ForwardRefComponent; - -// @public (undocumented) -export const treeItemAsideClassNames: SlotClassNames; - -// @public -export type TreeItemAsideProps = ComponentProps & { - actions?: boolean; - visible?: true; -}; - -// @public (undocumented) -export type TreeItemAsideSlots = { - root: Slot<'div'>; -}; - -// @public -export type TreeItemAsideState = ComponentState & { - actions: boolean; - visible: boolean; - buttonContextValue: ButtonContextValue; -}; - -// @public (undocumented) -export const treeItemClassNames: SlotClassNames; - -// @public -export const TreeItemLayout: ForwardRefComponent; - -// @public (undocumented) -export const treeItemLayoutClassNames: SlotClassNames; - -// @public -export type TreeItemLayoutProps = ComponentProps>; - -// @public (undocumented) -export type TreeItemLayoutSlots = { - root: Slot<'div'>; - expandIcon?: Slot<'div'>; - iconBefore?: Slot<'div'>; - iconAfter?: Slot<'div'>; -}; - -// @public -export type TreeItemLayoutState = ComponentState; - -// @public (undocumented) -export const treeItemLevelToken: "--fluent-TreeItem--level"; - -// @public -export const TreeItemPersonaLayout: ForwardRefComponent; - -// @public (undocumented) -export const treeItemPersonaLayoutClassNames: SlotClassNames; - -// @public -export type TreeItemPersonaLayoutProps = ComponentProps>; - -// @public (undocumented) -export type TreeItemPersonaLayoutSlots = { - root: NonNullable>; - expandIcon?: Slot<'div'>; - media: NonNullable>; - main: NonNullable>; - description?: Slot<'div'>; - content: NonNullable>; -}; - -// @public -export type TreeItemPersonaLayoutState = ComponentState & { - avatarSize: AvatarSize; -}; - -// @public -export type TreeItemProps = ComponentProps> & { - value?: Value; - itemType: TreeItemType; -}; - -// @public (undocumented) -export const TreeItemProvider: React_2.Provider & React_2.FC>; - -// @public (undocumented) -export type TreeItemSlots = { - root: Slot & { - style?: TreeItemCSSProperties; - }>>; -}; - -// @public -export type TreeItemState = ComponentState & TreeItemContextValue & { - level: number; - itemType: TreeItemType; -}; - -// @public (undocumented) -export type TreeNavigationData_unstable = { - value: Value; - target: HTMLElement; -} & ({ - event: React_2.MouseEvent; - type: 'Click'; -} | { - event: React_2.KeyboardEvent; - type: 'TypeAhead'; -} | { - event: React_2.KeyboardEvent; - type: typeof ArrowRight; -} | { - event: React_2.KeyboardEvent; - type: typeof ArrowLeft; -} | { - event: React_2.KeyboardEvent; - type: typeof ArrowUp; -} | { - event: React_2.KeyboardEvent; - type: typeof ArrowDown; -} | { - event: React_2.KeyboardEvent; - type: typeof Home; -} | { - event: React_2.KeyboardEvent; - type: typeof End; -}); - -// @public (undocumented) -export type TreeNavigationEvent_unstable = TreeNavigationData_unstable['event']; - -// @public (undocumented) -export type TreeOpenChangeData = { - open: boolean; - value: Value; -} & ({ - event: React_2.MouseEvent; - target: HTMLElement; - type: 'ExpandIconClick'; -} | { - event: React_2.MouseEvent; - target: HTMLElement; - type: 'Click'; -} | { - event: React_2.KeyboardEvent; - target: HTMLElement; - type: typeof Enter; -} | { - event: React_2.KeyboardEvent; - target: HTMLElement; - type: typeof ArrowRight; -} | { - event: React_2.KeyboardEvent; - target: HTMLElement; - type: typeof ArrowLeft; -}); - -// @public (undocumented) -export type TreeOpenChangeEvent = TreeOpenChangeData['event']; - -// @public (undocumented) -export type TreeProps = ComponentProps & { - appearance?: 'subtle' | 'subtle-alpha' | 'transparent'; - size?: 'small' | 'medium'; - openItems?: Iterable; - defaultOpenItems?: Iterable; - onOpenChange?(event: TreeOpenChangeEvent, data: TreeOpenChangeData): void; - onNavigation_unstable?(event: TreeNavigationEvent_unstable, data: TreeNavigationData_unstable): void; -}; - -// @public (undocumented) -export const TreeProvider: Provider & FC>; - -// @public (undocumented) -export type TreeSlots = { - root: Slot<'div'>; -}; - -// @public -export type TreeState = ComponentState & TreeContextValue & { - open: boolean; -}; - -// @public -export const useAlert_unstable: (props: AlertProps, ref: React_2.Ref) => AlertState; - -// @public -export const useAlertStyles_unstable: (state: AlertState) => AlertState; - -// @public -export const useDrawer_unstable: (props: DrawerProps, ref: React_2.Ref) => DrawerState; - -// @public -export const useDrawerBody_unstable: (props: DrawerBodyProps, ref: React_2.Ref) => DrawerBodyState; - -// @public -export const useDrawerBodyStyles_unstable: (state: DrawerBodyState) => DrawerBodyState; - -// @public -export const useDrawerFooter_unstable: (props: DrawerFooterProps, ref: React_2.Ref) => DrawerFooterState; - -// @public -export const useDrawerFooterStyles_unstable: (state: DrawerFooterState) => DrawerFooterState; - -// @public -export const useDrawerHeader_unstable: (props: DrawerHeaderProps, ref: React_2.Ref) => DrawerHeaderState; - -// @public -export const useDrawerHeaderNavigation_unstable: (props: DrawerHeaderNavigationProps, ref: React_2.Ref) => DrawerHeaderNavigationState; - -// @public -export const useDrawerHeaderNavigationStyles_unstable: (state: DrawerHeaderNavigationState) => DrawerHeaderNavigationState; - -// @public -export const useDrawerHeaderStyles_unstable: (state: DrawerHeaderState) => DrawerHeaderState; - -// @public -export const useDrawerHeaderTitle_unstable: (props: DrawerHeaderTitleProps, ref: React_2.Ref) => DrawerHeaderTitleState; - -// @public -export const useDrawerHeaderTitleStyles_unstable: (state: DrawerHeaderTitleState) => DrawerHeaderTitleState; - -// @public -export const useDrawerInline_unstable: (props: DrawerInlineProps, ref: React_2.Ref) => DrawerInlineState; +export { DrawerHeaderTitleState } -// @public -export const useDrawerInlineStyles_unstable: (state: DrawerInlineState) => DrawerInlineState; +export { DrawerInline } -// @public -export const useDrawerOverlay_unstable: (props: DrawerOverlayProps, ref: React_2.Ref) => DrawerOverlayState; +export { drawerInlineClassNames } -// @public -export const useDrawerOverlayStyles_unstable: (state: DrawerOverlayState) => DrawerOverlayState; +export { DrawerInlineProps } -// @public -export const useDynamicVirtualizerMeasure: (virtualizerProps: VirtualizerMeasureDynamicProps) => { - virtualizerLength: number; - bufferItems: number; - bufferSize: number; - scrollRef: (instance: TElement | null) => void; -}; +export { DrawerInlineSlots } -// @public -export function useFlatTree_unstable = FlatTreeItemProps>(flatTreeItemProps: Props[], options?: FlatTreeOptions): FlatTree; +export { DrawerInlineState } -// @public -export const useInfoButton_unstable: (props: InfoButtonProps, ref: React_2.Ref) => InfoButtonState; +export { DrawerOverlay } -// @public -export const useInfoButtonStyles_unstable: (state: InfoButtonState) => InfoButtonState; +export { drawerOverlayClassNames } -// @public -export const useInfoLabel_unstable: (props: InfoLabelProps, ref: React_2.Ref) => InfoLabelState; +export { DrawerOverlayProps } -// @public -export const useInfoLabelStyles_unstable: (state: InfoLabelState) => InfoLabelState; +export { DrawerOverlaySlots } -// @public -export const useIntersectionObserver: (callback: IntersectionObserverCallback, options?: IntersectionObserverInit | undefined) => { - setObserverList: Dispatch>; - setObserverInit: Dispatch>; - observer: MutableRefObject; -}; +export { DrawerOverlayState } -// @public -export const useResizeObserverRef_unstable: (resizeCallback: ResizeCallbackWithRef) => (instance: HTMLElement | HTMLDivElement | null) => void; +export { DrawerProps } -// @public -export const useStaticVirtualizerMeasure: (virtualizerProps: VirtualizerMeasureProps) => { - virtualizerLength: number; - bufferItems: number; - bufferSize: number; - scrollRef: (instance: TElement | null) => void; -}; +export { DrawerSlots } -// @public -export const useTree_unstable: (props: TreeProps, ref: React_2.Ref) => TreeState; +export { DrawerState } -// @public (undocumented) -export const useTreeContext_unstable: (selector: ContextSelector) => T; +export { flattenTree_unstable } -// @public -export function useTreeItem_unstable(props: TreeItemProps, ref: React_2.Ref): TreeItemState; +export { FlatTree } -// @public -export const useTreeItemAside_unstable: (props: TreeItemAsideProps, ref: React_2.Ref) => TreeItemAsideState; +export { FlatTreeItem } -// @public -export const useTreeItemAsideStyles_unstable: (state: TreeItemAsideState) => TreeItemAsideState; +export { FlatTreeItemProps } -// @public (undocumented) -export const useTreeItemContext_unstable: (selector: ContextSelector) => T; +export { FlatTreeProps } -// @public -export const useTreeItemLayout_unstable: (props: TreeItemLayoutProps, ref: React_2.Ref) => TreeItemLayoutState; +export { InfoButton } -// @public -export const useTreeItemLayoutStyles_unstable: (state: TreeItemLayoutState) => TreeItemLayoutState; +export { infoButtonClassNames } -// @public -export const useTreeItemPersonaLayout_unstable: (props: TreeItemPersonaLayoutProps, ref: React_2.Ref) => TreeItemPersonaLayoutState; +export { InfoButtonProps } -// @public -export const useTreeItemPersonaLayoutStyles_unstable: (state: TreeItemPersonaLayoutState) => TreeItemPersonaLayoutState; +export { InfoButtonSlots } -// @public -export const useTreeItemStyles_unstable: (state: TreeItemState) => TreeItemState; +export { InfoButtonState } -// @public (undocumented) -export const useTreeStyles_unstable: (state: TreeState) => TreeState; +export { InfoLabel } -// @public (undocumented) -export function useVirtualizer_unstable(props: VirtualizerProps): VirtualizerState; +export { infoLabelClassNames } -// @public (undocumented) -export const useVirtualizerContext_unstable: () => VirtualizerContextProps; +export { InfoLabelProps } -// @public (undocumented) -export function useVirtualizerScrollView_unstable(props: VirtualizerScrollViewProps): VirtualizerScrollViewState; +export { InfoLabelSlots } -// @public (undocumented) -export function useVirtualizerScrollViewDynamic_unstable(props: VirtualizerScrollViewDynamicProps): VirtualizerScrollViewDynamicState; +export { InfoLabelState } -// @public -export const useVirtualizerScrollViewDynamicStyles_unstable: (state: VirtualizerScrollViewDynamicState) => VirtualizerScrollViewDynamicState; +export { NestedTreeItem } -// @public -export const useVirtualizerScrollViewStyles_unstable: (state: VirtualizerScrollViewState) => VirtualizerScrollViewState; +export { renderAlert_unstable } -// @public -export const useVirtualizerStyles_unstable: (state: VirtualizerState) => VirtualizerState; +export { renderDrawer_unstable } -// @public -export const Virtualizer: FC; +export { renderDrawerBody_unstable } -// @public (undocumented) -export type VirtualizerChildRenderFunction = (index: number, isScrolling: boolean) => React_2.ReactNode; +export { renderDrawerFooter_unstable } -// @public (undocumented) -export const virtualizerClassNames: SlotClassNames; +export { renderDrawerHeader_unstable } -// @public (undocumented) -export type VirtualizerContextProps = { - contextIndex: number; - setContextIndex: (index: number) => void; -}; +export { renderDrawerHeaderNavigation_unstable } -// @public (undocumented) -export const VirtualizerContextProvider: React_2.Provider; +export { renderDrawerHeaderTitle_unstable } -// @public (undocumented) -export type VirtualizerMeasureDynamicProps = { - defaultItemSize: number; - currentIndex: number; - numItems: number; - getItemSize: (index: number) => number; - direction?: 'vertical' | 'horizontal'; -}; +export { renderDrawerInline_unstable } -// @public (undocumented) -export type VirtualizerMeasureProps = { - defaultItemSize: number; - direction?: 'vertical' | 'horizontal'; -}; +export { renderDrawerOverlay_unstable } -// @public (undocumented) -export type VirtualizerProps = ComponentProps> & VirtualizerConfigProps; +export { renderInfoButton_unstable } -// @public -export const VirtualizerScrollView: React_2.FC; +export { renderInfoLabel_unstable } -// @public (undocumented) -export const virtualizerScrollViewClassNames: SlotClassNames; +export { renderTree_unstable } -// @public -export const VirtualizerScrollViewDynamic: React_2.FC; +export { renderTreeItem_unstable } -// @public (undocumented) -export const virtualizerScrollViewDynamicClassNames: SlotClassNames; +export { renderTreeItemAside_unstable } -// @public (undocumented) -export type VirtualizerScrollViewDynamicProps = ComponentProps> & Partial> & { - itemSize: number; - getItemSize: (index: number) => number; - numItems: number; - children: VirtualizerChildRenderFunction; - imperativeRef?: RefObject; -}; +export { renderTreeItemLayout_unstable } -// @public (undocumented) -export type VirtualizerScrollViewDynamicSlots = VirtualizerScrollViewSlots; +export { renderTreeItemPersonaLayout_unstable } -// @public (undocumented) -export type VirtualizerScrollViewDynamicState = ComponentState & VirtualizerConfigState; +export { renderVirtualizer_unstable } -// @public (undocumented) -export type VirtualizerScrollViewProps = ComponentProps> & Partial> & { - itemSize: number; - numItems: number; - children: VirtualizerChildRenderFunction; - imperativeRef?: RefObject; -}; +export { renderVirtualizerScrollView_unstable } -// @public (undocumented) -export type VirtualizerScrollViewSlots = VirtualizerSlots & { - container: NonNullable>; -}; +export { renderVirtualizerScrollViewDynamic_unstable } -// @public (undocumented) -export type VirtualizerScrollViewState = ComponentState & VirtualizerConfigState; +export { ResizeCallbackWithRef } -// @public (undocumented) -export type VirtualizerSlots = { - before: NonNullable>; - beforeContainer: NonNullable>; - after: NonNullable>; - afterContainer: NonNullable>; -}; - -// @public (undocumented) -export type VirtualizerState = ComponentState & VirtualizerConfigState; +export { ScrollToInterface } + +export { scrollToItemDynamic } + +export { ScrollToItemDynamicParams } + +export { scrollToItemStatic } + +export { ScrollToItemStaticParams } + +export { Tree } + +export { treeClassNames } + +export { TreeContextValue } + +export { TreeItem } + +export { TreeItemAside } + +export { treeItemAsideClassNames } + +export { TreeItemAsideProps } + +export { TreeItemAsideSlots } + +export { TreeItemAsideState } + +export { treeItemClassNames } + +export { TreeItemLayout } + +export { treeItemLayoutClassNames } + +export { TreeItemLayoutProps } + +export { TreeItemLayoutSlots } + +export { TreeItemLayoutState } + +export { treeItemLevelToken } + +export { TreeItemPersonaLayout } + +export { treeItemPersonaLayoutClassNames } + +export { TreeItemPersonaLayoutProps } + +export { TreeItemPersonaLayoutSlots } + +export { TreeItemPersonaLayoutState } + +export { TreeItemProps } + +export { TreeItemProvider } + +export { TreeItemSlots } + +export { TreeItemState } + +export { TreeNavigationData_unstable } + +export { TreeNavigationEvent_unstable } + +export { TreeOpenChangeData } + +export { TreeOpenChangeEvent } + +export { TreeProps } + +export { TreeProvider } + +export { TreeSlots } + +export { TreeState } + +export { useAlert_unstable } + +export { useAlertStyles_unstable } + +export { useDrawer_unstable } + +export { useDrawerBody_unstable } + +export { useDrawerBodyStyles_unstable } + +export { useDrawerFooter_unstable } + +export { useDrawerFooterStyles_unstable } + +export { useDrawerHeader_unstable } + +export { useDrawerHeaderNavigation_unstable } + +export { useDrawerHeaderNavigationStyles_unstable } + +export { useDrawerHeaderStyles_unstable } + +export { useDrawerHeaderTitle_unstable } + +export { useDrawerHeaderTitleStyles_unstable } + +export { useDrawerInline_unstable } + +export { useDrawerInlineStyles_unstable } + +export { useDrawerOverlay_unstable } + +export { useDrawerOverlayStyles_unstable } + +export { useDynamicVirtualizerMeasure } + +export { useFlatTree_unstable } + +export { useInfoButton_unstable } + +export { useInfoButtonStyles_unstable } + +export { useInfoLabel_unstable } + +export { useInfoLabelStyles_unstable } + +export { useIntersectionObserver } + +export { useResizeObserverRef_unstable } + +export { useStaticVirtualizerMeasure } + +export { useTree_unstable } + +export { useTreeContext_unstable } + +export { useTreeItem_unstable } + +export { useTreeItemAside_unstable } + +export { useTreeItemAsideStyles_unstable } + +export { useTreeItemContext_unstable } + +export { useTreeItemLayout_unstable } + +export { useTreeItemLayoutStyles_unstable } + +export { useTreeItemPersonaLayout_unstable } + +export { useTreeItemPersonaLayoutStyles_unstable } + +export { useTreeItemStyles_unstable } + +export { useTreeStyles_unstable } + +export { useVirtualizer_unstable } + +export { useVirtualizerContext_unstable } + +export { useVirtualizerScrollView_unstable } + +export { useVirtualizerScrollViewDynamic_unstable } + +export { useVirtualizerScrollViewDynamicStyles_unstable } + +export { useVirtualizerScrollViewStyles_unstable } + +export { useVirtualizerStyles_unstable } + +export { Virtualizer } + +export { VirtualizerChildRenderFunction } + +export { virtualizerClassNames } + +export { VirtualizerContextProps } + +export { VirtualizerContextProvider } + +export { VirtualizerMeasureDynamicProps } + +export { VirtualizerMeasureProps } + +export { VirtualizerProps } + +export { VirtualizerScrollView } + +export { virtualizerScrollViewClassNames } + +export { VirtualizerScrollViewDynamic } + +export { virtualizerScrollViewDynamicClassNames } + +export { VirtualizerScrollViewDynamicProps } + +export { VirtualizerScrollViewDynamicSlots } + +export { VirtualizerScrollViewDynamicState } + +export { VirtualizerScrollViewProps } + +export { VirtualizerScrollViewSlots } + +export { VirtualizerScrollViewState } + +export { VirtualizerSlots } + +export { VirtualizerState } // (No @packageDocumentation comment for this package) diff --git a/packages/react-components/react-drawer/etc/react-drawer.api.md b/packages/react-components/react-drawer/etc/react-drawer.api.md index fbfa04d4732cf1..993e256860315f 100644 --- a/packages/react-components/react-drawer/etc/react-drawer.api.md +++ b/packages/react-components/react-drawer/etc/react-drawer.api.md @@ -6,7 +6,16 @@ /// +import type { ComponentProps } from '@fluentui/react-utilities'; +import type { ComponentState } from '@fluentui/react-utilities'; +import { DialogProps } from '@fluentui/react-dialog'; +import { DialogSurfaceProps } from '@fluentui/react-dialog'; +import { DialogSurfaceSlots } from '@fluentui/react-dialog'; +import { DialogTitleSlots } from '@fluentui/react-dialog'; +import type { ForwardRefComponent } from '@fluentui/react-utilities'; import * as React_2 from 'react'; +import type { Slot } from '@fluentui/react-utilities'; +import type { SlotClassNames } from '@fluentui/react-utilities'; // @public export const Drawer: ForwardRefComponent; diff --git a/packages/react-components/react-utilities/etc/react-utilities.api.md b/packages/react-components/react-utilities/etc/react-utilities.api.md index 7ceb0c824ec1aa..157bbd61f1a770 100644 --- a/packages/react-components/react-utilities/etc/react-utilities.api.md +++ b/packages/react-components/react-utilities/etc/react-utilities.api.md @@ -244,7 +244,7 @@ export function useIsSSR(): boolean; export function useMergedRefs(...refs: (React_2.Ref | undefined)[]): RefObjectFunction; // @public -export const useMotionPresence: (present: boolean, events?: UseMotionPresenceEvents | undefined) => UseMotionPresenceState; +export const useMotionPresence: (present: boolean, events?: UseMotionPresenceEvents) => UseMotionPresenceState; // @public export type UseMotionPresenceEvents = { From 27accc9863dba8c02023974a278bad61d57ffca4 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Thu, 22 Jun 2023 15:39:48 +0200 Subject: [PATCH 037/111] docs: regenerate API --- .../react-drawer/etc/react-drawer.api.md | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/packages/react-components/react-drawer/etc/react-drawer.api.md b/packages/react-components/react-drawer/etc/react-drawer.api.md index 51632c46c88974..993e256860315f 100644 --- a/packages/react-components/react-drawer/etc/react-drawer.api.md +++ b/packages/react-components/react-drawer/etc/react-drawer.api.md @@ -126,13 +126,7 @@ export type DrawerInlineSlots = { }; // @public -<<<<<<< HEAD export type DrawerInlineState = ComponentState & DrawerInlineProps & DrawerBaseState; -||||||| 9b5b3f33ad -export type DrawerInlineState = ComponentState & DrawerBaseTypes & Pick; -======= -export type DrawerInlineState = ComponentState & DrawerInlineProps; ->>>>>>> master // @public export const DrawerOverlay: ForwardRefComponent; @@ -149,13 +143,7 @@ export type DrawerOverlaySlots = DialogSurfaceSlots & { }; // @public -<<<<<<< HEAD export type DrawerOverlayState = ComponentState & DrawerBaseProps & DrawerBaseState & { -||||||| 9b5b3f33ad -export type DrawerOverlayState = ComponentState & DrawerBaseTypes & { -======= -export type DrawerOverlayState = ComponentState & DrawerBaseProps & { ->>>>>>> master dialog: DialogProps; backdropVisible: boolean; }; From fd8edd5ba645c5ac4449c88d87458e1745b8b9e2 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Thu, 22 Jun 2023 16:12:09 +0200 Subject: [PATCH 038/111] docs: remove testing class --- .../stories/Drawer/DrawerDisabledTransition.stories.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerDisabledTransition.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerDisabledTransition.stories.tsx index 10e733e771f995..821a1a9095c3bc 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerDisabledTransition.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerDisabledTransition.stories.tsx @@ -18,9 +18,7 @@ export const DisabledTransition = () => {

setIsOpen(open)} > From b97fe457c97721c2cfa14fa04a8ad561f30d2ca0 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Wed, 28 Jun 2023 16:55:52 +0200 Subject: [PATCH 039/111] feat: syncronize the root/backdrop transition duration --- .../useDrawerOverlayStyles.styles.ts | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts index c8e828e80365c0..6282293abe0f5f 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts @@ -14,7 +14,7 @@ export const drawerOverlayClassNames: SlotClassNames = { /** * Styles for the root slot */ -const useStyles = makeStyles({ +const useDrawerRootStyles = makeStyles({ root: { position: 'fixed', top: 0, @@ -44,7 +44,6 @@ const useBackdropStyles = makeStyles({ backdrop: { opacity: 0, transitionProperty: 'opacity', - transitionDuration: tokens.durationNormal, transitionTimingFunction: tokens.curveEasyEase, willChange: 'opacity', }, @@ -52,6 +51,20 @@ const useBackdropStyles = makeStyles({ backdropVisible: { opacity: 1, }, + + // Transition duration based on size + small: { + transitionDuration: tokens.durationNormal, + }, + medium: { + transitionDuration: tokens.durationSlow, + }, + large: { + transitionDuration: tokens.durationSlower, + }, + full: { + transitionDuration: tokens.durationUltraSlow, + }, }); /** @@ -59,7 +72,7 @@ const useBackdropStyles = makeStyles({ */ export const useDrawerOverlayStyles_unstable = (state: DrawerOverlayState): DrawerOverlayState => { const baseStyles = useDrawerBaseStyles(); - const rootStyles = useStyles(); + const rootStyles = useDrawerRootStyles(); const backdropStyles = useBackdropStyles(); const backdrop = state.root.backdrop as React.HTMLAttributes | undefined; @@ -79,6 +92,7 @@ export const useDrawerOverlayStyles_unstable = (state: DrawerOverlayState): Draw drawerOverlayClassNames.backdrop, backdropStyles.backdrop, state.backdropVisible && backdropStyles.backdropVisible, + state.size && backdropStyles[state.size], backdrop.className, ); } From 0717cd94ec6c2bd68b2f32ff95828c07ac79cd70 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Thu, 27 Jul 2023 16:02:06 +0200 Subject: [PATCH 040/111] refactor: simplify logic and use computedStyleMap if browser supports it --- .../DrawerInline/useDrawerInline.ts | 2 +- .../useDrawerInlineStyles.styles.ts | 34 +- .../DrawerOverlay/useDrawerOverlay.ts | 2 +- .../useDrawerOverlayStyles.styles.ts | 6 +- .../src/util/useDrawerBaseStyles.styles.ts | 6 +- .../Drawer/DrawerCustomAnimation.stories.tsx | 9 +- .../Drawer/DrawerCustomTransition.stories.tsx | 6 +- .../stories/Drawer/DrawerInline.stories.tsx | 28 +- .../etc/react-utilities.api.md | 6 +- .../src/hooks/useMotionPresence.ts | 307 +++++++++++------- 10 files changed, 233 insertions(+), 173 deletions(-) diff --git a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts index 502239d9c06e96..cf9772c490e994 100644 --- a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts +++ b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts @@ -30,7 +30,7 @@ export const useDrawerInline_unstable = ( initialState: false, }); - const { ref: drawerRef, shouldRender, visible, state: motionState } = useMotionPresence(open); + const { ref: drawerRef, shouldRender, visible, motionState } = useMotionPresence(open); return { components: { diff --git a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts index 3a358f9e3fcccb..03687329b9cb7d 100644 --- a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts +++ b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts @@ -2,7 +2,7 @@ import * as React from 'react'; import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; import type { DrawerInlineSlots, DrawerInlineState } from './DrawerInline.types'; import type { SlotClassNames } from '@fluentui/react-utilities'; -import { getDrawerBaseClassNames, useDrawerBaseStyles } from '../../util/useDrawerBaseStyles.styles'; +import { drawerCSSVars, getDrawerBaseClassNames, useDrawerBaseStyles } from '../../util/useDrawerBaseStyles.styles'; import { tokens } from '@fluentui/react-theme'; export const drawerInlineClassNames: SlotClassNames = { @@ -15,6 +15,9 @@ export const drawerInlineClassNames: SlotClassNames = { const useStyles = makeStyles({ root: { position: 'relative', + transform: 'translateZ(0)', + transitionProperty: 'margin', + willChange: 'margin', }, /* Separator */ @@ -25,24 +28,12 @@ const useStyles = makeStyles({ ...shorthands.borderLeft('1px', 'solid', tokens.colorNeutralBackground3), }, - /* Positioning */ - left: { - marginLeft: 'calc(var(--fui-Drawer--size) * -1)', - transitionProperty: 'margin-left', - willChange: 'margin-left', + /* Hidden */ + hiddenLeft: { + marginLeft: `calc(var(${drawerCSSVars.drawerSizeVar}) * -1)`, }, - right: { - marginRight: 'calc(var(--fui-Drawer--size) * -1)', - transitionProperty: 'margin-right', - willChange: 'margin-right', - }, - - /* Visible */ - visibleLeft: { - marginLeft: 0, - }, - visibleRight: { - marginRight: 0, + hiddenRight: { + marginRight: `calc(var(${drawerCSSVars.drawerSizeVar}) * -1)`, }, }); @@ -53,7 +44,7 @@ export const useDrawerInlineStyles_unstable = (state: DrawerInlineState): Drawer const baseStyles = useDrawerBaseStyles(); const styles = useStyles(); - const separatorClass = React.useMemo(() => { + const separatorClass = React.useCallback(() => { if (!state.separator) { return undefined; } @@ -66,9 +57,8 @@ export const useDrawerInlineStyles_unstable = (state: DrawerInlineState): Drawer baseStyles.root, styles.root, getDrawerBaseClassNames(state, baseStyles), - state.position && styles[state.position], - state.visible && (state.position === 'left' ? styles.visibleLeft : styles.visibleRight), - separatorClass, + !state.visible && (state.position === 'left' ? styles.hiddenLeft : styles.hiddenRight), + separatorClass(), state.root.className, ); diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts index fa8cda23332460..7b59633b9416b8 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts @@ -20,7 +20,7 @@ export const useDrawerOverlay_unstable = ( const { open, defaultOpen, size, position } = getDefaultDrawerProps(props); const { modalType = 'modal', inertTrapFocus, onOpenChange } = props; - const { ref: drawerRef, shouldRender, visible, state: motionState } = useMotionPresence(open); + const { ref: drawerRef, shouldRender, visible, motionState } = useMotionPresence(open); const backdropPresence = useMotionPresence(open); const backdropRef = useMergedRefs(backdropPresence.ref, drawerRef); diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts index 6282293abe0f5f..a90848b33b4450 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts @@ -4,7 +4,7 @@ import { tokens } from '@fluentui/react-theme'; import type { SlotClassNames } from '@fluentui/react-utilities'; import type { DrawerOverlaySlots, DrawerOverlayState } from './DrawerOverlay.types'; -import { getDrawerBaseClassNames, useDrawerBaseStyles } from '../../util/useDrawerBaseStyles.styles'; +import { getDrawerBaseClassNames, useDrawerBaseStyles, drawerCSSVars } from '../../util/useDrawerBaseStyles.styles'; export const drawerOverlayClassNames: SlotClassNames = { root: 'fui-DrawerOverlay', @@ -25,10 +25,10 @@ const useDrawerRootStyles = makeStyles({ /* Positioning */ left: { - transform: 'translate3D(calc(var(--fui-Drawer--size) * -1), 0, 0)', + transform: `translate3D(calc(var(${drawerCSSVars.drawerSizeVar}) * -1), 0, 0)`, }, right: { - transform: 'translate3D(calc(var(--fui-Drawer--size) * 1), 0, 0)', + transform: `translate3D(calc(var(${drawerCSSVars.drawerSizeVar}) * 1), 0, 0)`, }, /* Visible */ diff --git a/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts b/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts index 2275c90d316846..ff2f82fe830cf1 100644 --- a/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts +++ b/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts @@ -75,14 +75,14 @@ export const useDrawerBaseStyles = makeStyles({ }); export const getDrawerBaseClassNames = ( - { position, size, entering, exiting }: Partial, + { position, size, motionState }: Partial, baseStyles: ReturnType, ) => { return mergeClasses( baseStyles.reducedMotion, position && baseStyles[position], size && baseStyles[size], - entering && baseStyles.entering, - exiting && baseStyles.exiting, + motionState === 'entering' && baseStyles.entering, + motionState === 'exiting' && baseStyles.exiting, ); }; diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomAnimation.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomAnimation.stories.tsx index f9a2de39b9ac16..8f5bda1cbe520b 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomAnimation.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomAnimation.stories.tsx @@ -17,11 +17,11 @@ const hiddenKeyframe = { const useStyles = makeStyles({ drawer: { - animationDuration: tokens.durationUltraSlow, willChange: 'opacity, transform, border-radius', }, drawerEntering: { + animationDuration: '2s', animationTimingFunction: tokens.curveDecelerateMid, animationName: { '0%': hiddenKeyframe, @@ -30,6 +30,7 @@ const useStyles = makeStyles({ }, drawerExiting: { + animationDuration: '1s', animationTimingFunction: tokens.curveAccelerateMin, animationName: { '0%': visibleKeyframe, @@ -42,7 +43,7 @@ export const CustomAnimation = () => { const styles = useStyles(); const [isOpen, setIsOpen] = React.useState(false); - const { ref, state } = useMotionPresence(isOpen); + const { ref, motionState } = useMotionPresence(isOpen); return (
@@ -51,8 +52,8 @@ export const CustomAnimation = () => { open={isOpen} className={mergeClasses( styles.drawer, - state === 'entering' && styles.drawerEntering, - state === 'exiting' && styles.drawerExiting, + motionState === 'entering' && styles.drawerEntering, + motionState === 'exiting' && styles.drawerExiting, )} onOpenChange={(_, { open }) => setIsOpen(open)} > diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx index 711be48306cb7c..09ef5d55a90821 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx @@ -27,7 +27,7 @@ export const CustomTransition = () => { const styles = useStyles(); const [isOpen, setIsOpen] = React.useState(false); - const { ref, state } = useMotionPresence(isOpen); + const { ref, motionState, visible } = useMotionPresence(isOpen); return (
@@ -35,8 +35,8 @@ export const CustomTransition = () => { ref={ref} className={mergeClasses( styles.drawer, - state === 'visible' && styles.drawerVisible, - state === 'exiting' && styles.drawerExiting, + visible && styles.drawerVisible, + motionState === 'exiting' && styles.drawerExiting, )} open={isOpen} onOpenChange={(_, { open }) => setIsOpen(open)} diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerInline.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerInline.stories.tsx index c32e5071ce692c..421eef7050442a 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerInline.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerInline.stories.tsx @@ -13,6 +13,14 @@ const useStyles = makeStyles({ }, content: { + ...shorthands.flex(1), + ...shorthands.padding('16px'), + display: 'flex', + flexDirection: 'column', + rowGap: tokens.spacingHorizontalXS, + }, + + buttons: { ...shorthands.flex(1), ...shorthands.padding('16px'), display: 'flex', @@ -52,13 +60,21 @@ export const Inline = () => {
- +
+ + + +
- +
+ {Array.from({ length: 2000 }, (_, i) => ( +

Page content

+ ))} +
diff --git a/packages/react-components/react-utilities/etc/react-utilities.api.md b/packages/react-components/react-utilities/etc/react-utilities.api.md index 8b82b6c161ac10..13068ddcdacea0 100644 --- a/packages/react-components/react-utilities/etc/react-utilities.api.md +++ b/packages/react-components/react-utilities/etc/react-utilities.api.md @@ -283,7 +283,7 @@ export function useIsSSR(): boolean; export function useMergedRefs(...refs: (React_2.Ref | undefined)[]): RefObjectFunction; // @public -export const useMotionPresence: (present: boolean, events?: UseMotionPresenceEvents) => UseMotionPresenceState; +export const useMotionPresence: (present: boolean) => UseMotionPresenceState; // @public export type UseMotionPresenceEvents = { @@ -296,9 +296,7 @@ export type UseMotionPresenceState = { ref: React_2.RefCallback; shouldRender: boolean; visible: boolean; - entering: boolean; - exiting: boolean; - animating: boolean; + motionState: 'entering' | 'exiting' | 'resting'; }; // @internal (undocumented) diff --git a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts index 68afa0813ce6a3..e1feb77f350d56 100644 --- a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts +++ b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts @@ -1,7 +1,39 @@ import * as React from 'react'; -import { useTimeout } from '@fluentui/react-utilities'; +import { useTimeout } from './useTimeout'; -const noop = () => null; +/** + * CSS Typed Object Model + * @see https://drafts.css-houdini.org/css-typed-om-1/ + * @see https://developer.mozilla.org/en-US/docs/Web/API/CSSUnitValue + */ +interface CSSUnitValue { + value: number; + readonly unit: string; +} + +interface StylePropertyMapReadOnly { + [Symbol.iterator](): IterableIterator<[string, CSSUnitValue[]]>; + + get(property: string): CSSUnitValue | undefined; + getAll(property: string): CSSUnitValue[]; + has(property: string): boolean; + readonly size: number; +} + +/** + * HTMLElement with styled map. + * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/computedStyleMap + */ +interface HTMLElementWithStyledMap extends HTMLElement { + computedStyleMap(): StylePropertyMapReadOnly; +} + +interface CSSWithNumber { + number(value: number): { + value: number; + readonly unit: string; + }; +} /** * State for useMotionPresence hook. @@ -28,9 +60,9 @@ export type UseMotionPresenceState = { * * - `entering` - The element is entering the DOM. * - `exiting` - The element is exiting the DOM. - * - `stale` - The element is currently not animating. This is the final state of the element. + * - `resting` - The element is currently not animating. This is the final and initial state of the element. */ - state: 'entering' | 'exiting' | 'stale'; + motionState: 'entering' | 'exiting' | 'resting'; }; /** @@ -53,39 +85,63 @@ export type UseMotionPresenceEvents = { * @param node - DOM node. * @returns - CSS styles. */ -const getElementComputedStyle = (node: HTMLElement): Partial => { - if (node.nodeType !== 1) { - return { - getPropertyValue: () => '', - }; - } - +const getElementComputedStyle = (node: HTMLElement): CSSStyleDeclaration => { const window = node.ownerDocument?.defaultView; return window!.getComputedStyle(node, null); }; /** - * @internal * Converts a CSS duration string to milliseconds. * * @param s - CSS duration string * @returns Duration in milliseconds */ function toMs(s: string): number { - if (s.includes('auto')) { + const trimmed = s.trim(); + + if (trimmed.includes('auto')) { return 0; } - if (s.includes('ms')) { - return parseFloat(s); + if (trimmed.includes('ms')) { + return parseFloat(trimmed); } - return Number(s.slice(0, -1).replace(',', '.')) * 1000; + return Number(trimmed.slice(0, -1).replace(',', '.')) * 1000; } /** - * @internal + * Checks if the browser supports CSSOM. + * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/computedStyleMap + * + * @param node - DOM node + * @returns Whether the browser supports CSSOM + */ +const hasCSSOMSupport = (node: HTMLElementWithStyledMap) => { + /** + * As we are using the experimental CSSOM API, we need to check if the browser supports it. + * The typecast here is to allow the use of the `number` function that is not yet part of the CSSOM typings. + * @see https://www.npmjs.com/package/@types/w3c-css-typed-object-model-level-1 + */ + return Boolean(typeof CSS !== 'undefined' && (CSS as unknown as CSSWithNumber).number && node.computedStyleMap); +}; + +/** + * + * Gets the computed style of a given element. + * If the browser supports CSSOM, it will return a ComputedStyleMap object. + * Otherwise, it will return a CSSStyleDeclaration object. + */ +const getCSSStyle = (node: HTMLElementWithStyledMap): CSSStyleDeclaration | StylePropertyMapReadOnly => { + if (hasCSSOMSupport(node)) { + return node.computedStyleMap(); + } + + return getElementComputedStyle(node); +}; + +/** * Gets the maximum duration from a list of CSS durations. * * @param durations - List of CSS durations @@ -96,29 +152,62 @@ const getMaxCSSDuration = (durations: string[]) => { }; /** - * @internal + * Gets the computed map property for a given element using the CSSOM API. + * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/computedStyleMap + * + * @param computedStyle - Computed style of the element + * @param prop - CSS property + * @returns Computed map property + */ +const getComputedMapProp = (computedStyle: StylePropertyMapReadOnly, prop: string): string[] => { + const props = computedStyle.getAll(prop); + + if (props.length > 0) { + return props.map(({ value, unit }) => `${value}${unit}`); + } + + return ['0']; +}; + +/** + * Gets the computed style property for a given element using the getComputedStyle API. + * + * @param computedStyle - Computed style of the element + * @param prop - CSS property + * @returns Computed style property + */ +const getComputedStyleProp = (computedStyle: CSSStyleDeclaration, prop: string): string[] => { + const propValue = computedStyle.getPropertyValue(prop); + + return Array.isArray(propValue) ? propValue.split(',') : ['0']; +}; + +/** * Gets the motion information for a given element. * * @param computedStyle - Computed style of the element * @returns motion information */ -const getMotionInfo = (computedStyle: CSSStyleDeclaration) => { - const getProp = (prop: string) => (computedStyle?.getPropertyValue(prop) || '').split(','); +const getMotionDuration = (node: HTMLElementWithStyledMap) => { + const hasModernCSSSupport = hasCSSOMSupport(node); + const computedStyle = getCSSStyle(node); + + const getProp = (prop: string): number => { + const propValues = hasModernCSSSupport + ? getComputedMapProp(computedStyle as StylePropertyMapReadOnly, prop) + : getComputedStyleProp(computedStyle as CSSStyleDeclaration, prop); + + return getMaxCSSDuration(propValues); + }; const transitionDuration = getProp('transition-duration'); const transitionDelay = getProp('transition-delay'); const animationDuration = getProp('animation-duration'); const animationDelay = getProp('animation-delay'); - const totalTransitionDuration = getMaxCSSDuration(transitionDuration) + getMaxCSSDuration(transitionDelay); - const totalAnimationDuration = getMaxCSSDuration(animationDuration) + getMaxCSSDuration(animationDelay); + const totalTransitionDuration = transitionDuration + transitionDelay; + const totalAnimationDuration = animationDuration + animationDelay; - const hasAnimation = totalAnimationDuration > 0; - const hasTransition = totalTransitionDuration > 0; - - return { - duration: Math.max(totalTransitionDuration, totalAnimationDuration), - hasMotion: hasAnimation || hasTransition, - }; + return Math.max(totalTransitionDuration, totalAnimationDuration); }; /** @@ -127,133 +216,99 @@ const getMotionInfo = (computedStyle: CSSStyleDeclaration) => { * @param present - Whether the element should be present in the DOM * @param events - Callbacks for when the element enters or exits the DOM */ -export const useMotionPresence = ( +export const useMotionPresence = ( present: boolean, - events?: UseMotionPresenceEvents, ): UseMotionPresenceState => { - const { onEntered = noop, onExited = noop } = events || {}; - - const [shouldRender, setShouldRender] = React.useState(present); - const [visible, setVisible] = React.useState(false); - const [state, setState] = React.useState['state']>('stale'); + const [state, setState] = React.useState, 'ref'>>({ + shouldRender: present, + motionState: 'resting', + visible: false, + }); const [currentElement, setCurrentElement] = React.useState(null); - const [setAnimationTimeout, clearAnimationTimeout] = useTimeout(); - const computedStylesRef = React.useRef({} as CSSStyleDeclaration); + const processAnimation = React.useCallback( + (callback: () => void) => { + if (!currentElement) { + return; + } + + clearAnimationTimeout(); + const animationFrame = requestAnimationFrame(() => { + const duration = getMotionDuration(currentElement); + + /** + * Use CSS transition duration + 1ms to ensure the animation has finished on both enter and exit states. + * This is an alternative to using the `transitionend` event which can be unreliable as it fires multiple times + * if the transition has multiple properties. + */ + setAnimationTimeout(() => callback(), duration + 1); + }); + + return () => { + clearAnimationTimeout(); + cancelAnimationFrame(animationFrame); + }; + }, + [clearAnimationTimeout, currentElement, setAnimationTimeout], + ); + const ref: React.RefCallback = React.useCallback(node => { if (!node) { return; } - computedStylesRef.current = getElementComputedStyle(node) as CSSStyleDeclaration; setCurrentElement(node); }, []); - const notCurrentElement = React.useCallback((target: TElement) => target !== currentElement, [currentElement]); - - const onStartEntering = React.useCallback(() => { - setState('entering'); - }, []); - - const onFinishedEntering = React.useCallback(() => { - setState('stale'); - onEntered(); - }, [onEntered]); - - const onStartExiting = React.useCallback(() => { - setState('exiting'); - }, []); - - const onFinishedExiting = React.useCallback(() => { - setState('stale'); - setShouldRender(false); - onExited(); - }, [onExited]); - - const onMotionCanceled = React.useCallback( - ({ target }) => { - if (notCurrentElement(target)) { - return; - } - - setState('stale'); - setVisible(present); - setShouldRender(present); - }, - [notCurrentElement, present], - ); - React.useEffect(() => { if (present) { - setShouldRender(true); + setState({ + shouldRender: true, + visible: false, + motionState: 'resting', + }); } }, [present]); - React.useEffect(() => { - currentElement?.addEventListener('transitioncancel', onMotionCanceled); - currentElement?.addEventListener('animationcancel', onMotionCanceled); - - return () => { - currentElement?.removeEventListener('transitioncancel', onMotionCanceled); - currentElement?.removeEventListener('animationcancel', onMotionCanceled); - }; - }, [currentElement, onMotionCanceled]); - React.useEffect(() => { if (!currentElement) { return; } - const { duration, hasMotion } = getMotionInfo(computedStylesRef.current); - const animationFrame = requestAnimationFrame(() => { - setVisible(present); - - if (!hasMotion) { - setShouldRender(present); - } else { - if (present) { - onStartEntering(); - } else { - onStartExiting(); - } + setState(prevState => ({ + ...prevState, + visible: present, + motionState: present ? 'entering' : 'exiting', + })); + }); - /** - * Use CSS transition duration + 1ms to ensure the animation has finished on both enter and exit states. - * This is an alternative to using the `transitionend` event which can be unreliable as it fires multiple times - * if the transition has multiple properties. - */ - setAnimationTimeout(() => { - if (present) { - onFinishedEntering(); - } else { - onFinishedExiting(); - } - }, duration + 1); - } + processAnimation(() => { + setState(prevState => ({ + ...prevState, + motionState: 'resting', + })); }); - return () => { - cancelAnimationFrame(animationFrame); - clearAnimationTimeout(); - }; - }, [ - currentElement, - onFinishedExiting, - onFinishedEntering, - present, - onStartEntering, - onStartExiting, - setAnimationTimeout, - clearAnimationTimeout, - ]); + return () => cancelAnimationFrame(animationFrame); + }, [currentElement, present, processAnimation]); + + React.useEffect(() => { + if (state.motionState === 'exiting') { + processAnimation(() => { + setState({ + shouldRender: false, + visible: false, + motionState: 'resting', + }); + }); + } + }, [processAnimation, state.motionState]); return { ref, - shouldRender, - state, - visible, + ...state, }; }; From f08ba005398b8ce5a0ab5c1b38fab85c7f163326 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Thu, 27 Jul 2023 16:45:35 +0200 Subject: [PATCH 041/111] fix: calculate transition/animation delays in pairs --- .../src/hooks/useMotionPresence.ts | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts index e1feb77f350d56..ecfc401de8828a 100644 --- a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts +++ b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts @@ -141,16 +141,6 @@ const getCSSStyle = (node: HTMLElementWithStyledMap): CSSStyleDeclaration | Styl return getElementComputedStyle(node); }; -/** - * Gets the maximum duration from a list of CSS durations. - * - * @param durations - List of CSS durations - * @returns Maximum duration - */ -const getMaxCSSDuration = (durations: string[]) => { - return Math.max(...durations.map(d => toMs(d.trim()))); -}; - /** * Gets the computed map property for a given element using the CSSOM API. * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/computedStyleMap @@ -182,6 +172,21 @@ const getComputedStyleProp = (computedStyle: CSSStyleDeclaration, prop: string): return Array.isArray(propValue) ? propValue.split(',') : ['0']; }; +/** + * Gets the maximum duration from a list of CSS durations. + * + * @param durations - List of CSS durations + * @param delays - List of CSS delays + * @returns Maximum duration + */ +const getMaxCSSDuration = (durations: string[], delays: string[]): number => { + const totalDurations = [...durations].map((duration, index) => { + return toMs(duration.trim()) + toMs((delays[index] || '0').trim()); + }); + + return Math.max(...totalDurations); +}; + /** * Gets the motion information for a given element. * @@ -192,22 +197,16 @@ const getMotionDuration = (node: HTMLElementWithStyledMap) => { const hasModernCSSSupport = hasCSSOMSupport(node); const computedStyle = getCSSStyle(node); - const getProp = (prop: string): number => { - const propValues = hasModernCSSSupport + const getProp = (prop: string): string[] => { + return hasModernCSSSupport ? getComputedMapProp(computedStyle as StylePropertyMapReadOnly, prop) : getComputedStyleProp(computedStyle as CSSStyleDeclaration, prop); - - return getMaxCSSDuration(propValues); }; - const transitionDuration = getProp('transition-duration'); - const transitionDelay = getProp('transition-delay'); - const animationDuration = getProp('animation-duration'); - const animationDelay = getProp('animation-delay'); - const totalTransitionDuration = transitionDuration + transitionDelay; - const totalAnimationDuration = animationDuration + animationDelay; + const transitionDuration = getMaxCSSDuration(getProp('transition-duration'), getProp('transition-delay')); + const animationDuration = getMaxCSSDuration(getProp('animation-duration'), getProp('animation-delay')); - return Math.max(totalTransitionDuration, totalAnimationDuration); + return Math.max(transitionDuration, animationDuration); }; /** From 56f263861a5e5b79c48bdf630de8e86434e9b572 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Thu, 27 Jul 2023 16:50:16 +0200 Subject: [PATCH 042/111] fix: calculate transition/animation delays in pairs --- .../src/hooks/useMotionPresence.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts index ecfc401de8828a..28d9c8d2bbfe91 100644 --- a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts +++ b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts @@ -180,8 +180,20 @@ const getComputedStyleProp = (computedStyle: CSSStyleDeclaration, prop: string): * @returns Maximum duration */ const getMaxCSSDuration = (durations: string[], delays: string[]): number => { - const totalDurations = [...durations].map((duration, index) => { - return toMs(duration.trim()) + toMs((delays[index] || '0').trim()); + const totalDurations: number[] = []; + + durations.forEach(duration => { + totalDurations.push(toMs(duration.trim())); + }); + + delays.forEach((delay, index) => { + const parsedDelay = toMs(delay.trim()); + + if (totalDurations[index]) { + totalDurations[index] = totalDurations[index] + parsedDelay; + } else { + totalDurations[index] = parsedDelay; + } }); return Math.max(...totalDurations); From 0f437db6e744cf69c5be52c2676d361d851ab5a1 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Thu, 27 Jul 2023 17:42:02 +0200 Subject: [PATCH 043/111] fix: rename param --- .../react-utilities/src/hooks/useMotionPresence.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts index 28d9c8d2bbfe91..d6d6e48f8b4c34 100644 --- a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts +++ b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts @@ -94,11 +94,11 @@ const getElementComputedStyle = (node: HTMLElement): CSSStyleDeclaration => { /** * Converts a CSS duration string to milliseconds. * - * @param s - CSS duration string + * @param duration - CSS duration string * @returns Duration in milliseconds */ -function toMs(s: string): number { - const trimmed = s.trim(); +function toMs(duration: string): number { + const trimmed = duration.trim(); if (trimmed.includes('auto')) { return 0; From 0c98289ee2aaea337449e2cd0ba94c0dde248bd6 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Thu, 27 Jul 2023 21:37:19 +0200 Subject: [PATCH 044/111] feat: add unmounted state and initial tests --- .../src/hooks/useMotionPresence.test.ts | 25 +++++++++++++++++++ .../src/hooks/useMotionPresence.ts | 16 ++++++------ 2 files changed, 33 insertions(+), 8 deletions(-) create mode 100644 packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts diff --git a/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts b/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts new file mode 100644 index 00000000000000..5410dd1ed00096 --- /dev/null +++ b/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts @@ -0,0 +1,25 @@ +import { renderHook } from '@testing-library/react-hooks'; + +import { useMotionPresence } from './useMotionPresence'; + +describe('useMotionPresence', () => { + it('should return default values when presence is true', () => { + const { result } = renderHook(() => useMotionPresence(false)); + const { ref, motionState, shouldRender, visible } = result.current; + + expect(typeof ref).toBe('function'); + expect(motionState).toBe('unmounted'); + expect(shouldRender).toBe(false); + expect(visible).toBe(false); + }); + + it('should return default values when presence is false', () => { + const { result } = renderHook(() => useMotionPresence(true)); + const { ref, motionState, shouldRender, visible } = result.current; + + expect(typeof ref).toBe('function'); + expect(motionState).toBe('resting'); + expect(shouldRender).toBe(true); + expect(visible).toBe(false); + }); +}); diff --git a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts index d6d6e48f8b4c34..96731d1368e748 100644 --- a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts +++ b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts @@ -61,8 +61,9 @@ export type UseMotionPresenceState = { * - `entering` - The element is entering the DOM. * - `exiting` - The element is exiting the DOM. * - `resting` - The element is currently not animating. This is the final and initial state of the element. + * - `unmounted` - The element is not rendered in the DOM. */ - motionState: 'entering' | 'exiting' | 'resting'; + motionState: 'entering' | 'exiting' | 'resting' | 'unmounted'; }; /** @@ -227,16 +228,14 @@ const getMotionDuration = (node: HTMLElementWithStyledMap) => { * @param present - Whether the element should be present in the DOM * @param events - Callbacks for when the element enters or exits the DOM */ -export const useMotionPresence = ( - present: boolean, -): UseMotionPresenceState => { +export const useMotionPresence = (present: boolean): UseMotionPresenceState => { const [state, setState] = React.useState, 'ref'>>({ shouldRender: present, - motionState: 'resting', + motionState: present ? 'resting' : 'unmounted', visible: false, }); - const [currentElement, setCurrentElement] = React.useState(null); + const [currentElement, setCurrentElement] = React.useState(null); const [setAnimationTimeout, clearAnimationTimeout] = useTimeout(); const processAnimation = React.useCallback( @@ -270,7 +269,8 @@ export const useMotionPresence = ( return; } - setCurrentElement(node); + // Cast to HTMLElementWithStyledMap to allow the use of the experimental CSSOM API. + setCurrentElement(node as unknown as HTMLElementWithStyledMap); }, []); React.useEffect(() => { @@ -312,7 +312,7 @@ export const useMotionPresence = ( setState({ shouldRender: false, visible: false, - motionState: 'resting', + motionState: 'unmounted', }); }); } From 4dd835b706a1a489c35bb266dc5db9d1fce804a2 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Mon, 31 Jul 2023 14:33:34 +0200 Subject: [PATCH 045/111] feat: add flag to prevent element to animate on first render --- .../src/hooks/useMotionPresence.ts | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts index 96731d1368e748..8e880d27246683 100644 --- a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts +++ b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts @@ -67,18 +67,15 @@ export type UseMotionPresenceState = { }; /** - * Events for useMotionPresence hook. + * Options for useMotionPresence hook. */ -export type UseMotionPresenceEvents = { +export type UseMotionPresenceOptions = { /** - * Callback for after the element enters the DOM. - */ - onEntered?: () => void; - - /** - * Callback for after the element exits the DOM. + * Whether to animate the element on first mount. + * + * @default false */ - onExited?: () => void; + animateOnFirstMount?: boolean; }; /** @@ -228,7 +225,10 @@ const getMotionDuration = (node: HTMLElementWithStyledMap) => { * @param present - Whether the element should be present in the DOM * @param events - Callbacks for when the element enters or exits the DOM */ -export const useMotionPresence = (present: boolean): UseMotionPresenceState => { +export const useMotionPresence = ( + present: boolean, + { animateOnFirstMount = false }: UseMotionPresenceOptions = {}, +): UseMotionPresenceState => { const [state, setState] = React.useState, 'ref'>>({ shouldRender: present, motionState: present ? 'resting' : 'unmounted', @@ -283,6 +283,19 @@ export const useMotionPresence = (present: boolean } }, [present]); + React.useEffect(() => { + if (!animateOnFirstMount && present) { + setState(prevState => ({ + ...prevState, + visible: true, + })); + } + /* + * We only want to run this effect on first mount to make sure the element doesn't animate. + */ + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + React.useEffect(() => { if (!currentElement) { return; From da7a182abd986191bc8403bcba4321640789ab3c Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Mon, 31 Jul 2023 23:15:40 +0200 Subject: [PATCH 046/111] fix: add more tests and fix "animateOnFirstMount" not working when transition-duration is 0 --- .../src/hooks/useMotionPresence.test.ts | 209 ++++++++++++++++-- .../src/hooks/useMotionPresence.ts | 49 +++- 2 files changed, 231 insertions(+), 27 deletions(-) diff --git a/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts b/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts index 5410dd1ed00096..227a9047ff1f70 100644 --- a/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts +++ b/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts @@ -1,25 +1,200 @@ -import { renderHook } from '@testing-library/react-hooks'; +import { act, renderHook } from '@testing-library/react-hooks'; -import { useMotionPresence } from './useMotionPresence'; +import { useMotionPresence, UseMotionPresenceOptions } from './useMotionPresence'; + +const transitionDuration = 100; +const defaultStyles = { 'transition-duration': `${transitionDuration}ms` }; +const renderHookWithRef = ( + initialPresence: boolean, + initialOptions?: UseMotionPresenceOptions, + style: Record = defaultStyles, +) => { + const refEl = document.createElement('div'); + const hook = renderHook(({ presence, options }) => useMotionPresence(presence, options), { + initialProps: { + presence: initialPresence, + options: initialOptions, + } as { + presence: boolean; + options?: UseMotionPresenceOptions; + }, + }); + + Object.entries(style).forEach(([key, value]) => value && refEl.style.setProperty(key, value)); + + act(() => hook.result.current.ref(refEl)); + + return hook; +}; describe('useMotionPresence', () => { - it('should return default values when presence is true', () => { - const { result } = renderHook(() => useMotionPresence(false)); - const { ref, motionState, shouldRender, visible } = result.current; - - expect(typeof ref).toBe('function'); - expect(motionState).toBe('unmounted'); - expect(shouldRender).toBe(false); - expect(visible).toBe(false); + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + describe('when presence is false by default', () => { + it('should return default values when presence is false', () => { + const { result } = renderHookWithRef(false); + const { ref, motionState, shouldRender, visible } = result.current; + + expect(typeof ref).toBe('function'); + expect(motionState).toBe('unmounted'); + expect(shouldRender).toBe(false); + expect(visible).toBe(false); + }); + }); + + describe('when presence is true by default', () => { + it('should return default values', () => { + const { result } = renderHookWithRef(true); + const { ref, motionState, shouldRender, visible } = result.current; + + expect(typeof ref).toBe('function'); + expect(motionState).toBe('resting'); + expect(shouldRender).toBe(true); + expect(visible).toBe(true); + }); + + it('should not change values after timeout ', () => { + const { result } = renderHookWithRef(true); + + const assertSameValues = () => { + expect(typeof result.current.ref).toBe('function'); + expect(result.current.motionState).toBe('resting'); + expect(result.current.shouldRender).toBe(true); + expect(result.current.visible).toBe(true); + }; + + assertSameValues(); + act(() => jest.advanceTimersToNextTimer()); + assertSameValues(); + }); + + it('should change visible to true when animateOnFirstMount is true', () => { + const { result } = renderHookWithRef(true, { animateOnFirstMount: true }); + + expect(typeof result.current.ref).toBe('function'); + expect(result.current.motionState).toBe('resting'); + expect(result.current.shouldRender).toBe(true); + expect(result.current.visible).toBe(false); + + act(() => jest.advanceTimersToNextTimer()); + + expect(result.current.visible).toBe(true); + }); + }); + + describe('when presence changes', () => { + it('should toggle values starting with false', () => { + const { result, rerender } = renderHookWithRef(false); + + expect(typeof result.current.ref).toBe('function'); + expect(result.current.motionState).toBe('unmounted'); + expect(result.current.shouldRender).toBe(false); + expect(result.current.visible).toBe(false); + + rerender({ presence: true }); + + expect(result.current.shouldRender).toBe(true); + expect(result.current.motionState).toBe('resting'); + + // double requestAnimationFrame + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.visible).toBe(true); + + rerender({ presence: false }); + + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.motionState).toBe('exiting'); + expect(result.current.visible).toBe(false); + + act(() => { + // requestAnimationFrame + act(() => jest.advanceTimersToNextTimer()); + + // timeout + jest.advanceTimersByTime(transitionDuration + 1); + }); + + expect(result.current.motionState).toBe('unmounted'); + expect(result.current.shouldRender).toBe(false); + expect(result.current.visible).toBe(false); + }); + + it('should toggle values starting with true', () => { + const { result, rerender } = renderHookWithRef(true); + + expect(typeof result.current.ref).toBe('function'); + expect(result.current.motionState).toBe('resting'); + expect(result.current.shouldRender).toBe(true); + expect(result.current.visible).toBe(true); + + rerender({ presence: false }); + + // requestAnimationFrame + act(() => jest.advanceTimersToNextTimer()); + + expect(result.current.motionState).toBe('exiting'); + expect(result.current.visible).toBe(false); + + act(() => { + // requestAnimationFrame + jest.advanceTimersToNextTimer(); + + // timeout + jest.advanceTimersByTime(transitionDuration + 1); + }); + + expect(result.current.motionState).toBe('unmounted'); + expect(result.current.shouldRender).toBe(false); + expect(result.current.visible).toBe(false); + }); }); - it('should return default values when presence is false', () => { - const { result } = renderHook(() => useMotionPresence(true)); - const { ref, motionState, shouldRender, visible } = result.current; + describe.each([ + { message: 'with transition', styles: { 'transition-duration': '100ms' } }, + { message: 'with long transition', styles: { 'transition-duration': '1000ms' } }, + { message: 'with animation', styles: { 'animation-duration': '100ms' } }, + { message: 'with long animation', styles: { 'animation-duration': '1000ms' } }, + ])('when presence changes - $message', ({ styles }) => { + it('should toggle values starting with false when animateOnFirstMount is true', () => { + const { result, rerender } = renderHookWithRef( + false, + { + animateOnFirstMount: true, + }, + styles, + ); + + expect(typeof result.current.ref).toBe('function'); + expect(result.current.motionState).toBe('unmounted'); + expect(result.current.shouldRender).toBe(false); + expect(result.current.visible).toBe(false); + + rerender({ presence: true }); + + expect(result.current.shouldRender).toBe(true); + expect(result.current.motionState).toBe('resting'); + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.visible).toBe(true); + expect(result.current.motionState).toBe('entering'); + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.motionState).toBe('resting'); + + rerender({ presence: false }); + + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.visible).toBe(false); + expect(result.current.motionState).toBe('exiting'); - expect(typeof ref).toBe('function'); - expect(motionState).toBe('resting'); - expect(shouldRender).toBe(true); - expect(visible).toBe(false); + act(() => jest.advanceTimersToNextTimer()); + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.motionState).toBe('unmounted'); + expect(result.current.shouldRender).toBe(false); + }); }); }); diff --git a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts index 8e880d27246683..54c41843b1dc0f 100644 --- a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts +++ b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts @@ -167,7 +167,7 @@ const getComputedMapProp = (computedStyle: StylePropertyMapReadOnly, prop: strin const getComputedStyleProp = (computedStyle: CSSStyleDeclaration, prop: string): string[] => { const propValue = computedStyle.getPropertyValue(prop); - return Array.isArray(propValue) ? propValue.split(',') : ['0']; + return propValue ? propValue.split(',') : ['0']; }; /** @@ -227,14 +227,16 @@ const getMotionDuration = (node: HTMLElementWithStyledMap) => { */ export const useMotionPresence = ( present: boolean, - { animateOnFirstMount = false }: UseMotionPresenceOptions = {}, + options: UseMotionPresenceOptions = {}, ): UseMotionPresenceState => { + const { animateOnFirstMount } = { animateOnFirstMount: false, ...options }; const [state, setState] = React.useState, 'ref'>>({ shouldRender: present, motionState: present ? 'resting' : 'unmounted', visible: false, }); + const canHaveMotion = React.useRef(false); const [currentElement, setCurrentElement] = React.useState(null); const [setAnimationTimeout, clearAnimationTimeout] = useTimeout(); @@ -248,6 +250,11 @@ export const useMotionPresence = ( const animationFrame = requestAnimationFrame(() => { const duration = getMotionDuration(currentElement); + if (duration === 0) { + callback(); + return; + } + /** * Use CSS transition duration + 1ms to ensure the animation has finished on both enter and exit states. * This is an alternative to using the `transitionend` event which can be unreliable as it fires multiple times @@ -301,14 +308,36 @@ export const useMotionPresence = ( return; } - const animationFrame = requestAnimationFrame(() => { - setState(prevState => ({ - ...prevState, - visible: present, - motionState: present ? 'entering' : 'exiting', - })); + let animationFrame: number; + const skipFirstMount = !animateOnFirstMount && !canHaveMotion.current; + const onDestroy = () => cancelAnimationFrame(animationFrame); + + animationFrame = requestAnimationFrame(() => { + setState(prevState => { + let motionState = prevState.motionState; + + if (skipFirstMount) { + motionState = present ? 'resting' : 'unmounted'; + } else { + motionState = present ? 'entering' : 'exiting'; + } + + return { + ...prevState, + visible: present, + motionState, + }; + }); }); + if (!canHaveMotion.current) { + canHaveMotion.current = true; + } + + if (skipFirstMount) { + return onDestroy; + } + processAnimation(() => { setState(prevState => ({ ...prevState, @@ -316,8 +345,8 @@ export const useMotionPresence = ( })); }); - return () => cancelAnimationFrame(animationFrame); - }, [currentElement, present, processAnimation]); + return onDestroy; + }, [animateOnFirstMount, currentElement, present, processAnimation]); React.useEffect(() => { if (state.motionState === 'exiting') { From 6a4d05f11ec1c44882632f9c1db1474a7b356cde Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Mon, 31 Jul 2023 23:16:38 +0200 Subject: [PATCH 047/111] fix: add timer explanation --- .../react-utilities/src/hooks/useMotionPresence.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts b/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts index 227a9047ff1f70..52b39ca7449fe7 100644 --- a/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts +++ b/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts @@ -179,9 +179,11 @@ describe('useMotionPresence', () => { expect(result.current.shouldRender).toBe(true); expect(result.current.motionState).toBe('resting'); + // requestAnimationFrame act(() => jest.advanceTimersToNextTimer()); expect(result.current.visible).toBe(true); expect(result.current.motionState).toBe('entering'); + // timeout act(() => jest.advanceTimersToNextTimer()); expect(result.current.motionState).toBe('resting'); @@ -191,7 +193,9 @@ describe('useMotionPresence', () => { expect(result.current.visible).toBe(false); expect(result.current.motionState).toBe('exiting'); + // requestAnimationFrame act(() => jest.advanceTimersToNextTimer()); + // timeout act(() => jest.advanceTimersToNextTimer()); expect(result.current.motionState).toBe('unmounted'); expect(result.current.shouldRender).toBe(false); From d2287fc880f3ec3913f91c917aefff7cb58a24d7 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Tue, 1 Aug 2023 13:55:27 +0200 Subject: [PATCH 048/111] fix: improve TS types/docs and simplify logic --- .../etc/react-utilities.api.md | 9 +- .../src/hooks/useMotionPresence.test.ts | 101 ++++++++++++++---- .../src/hooks/useMotionPresence.ts | 66 ++++-------- .../react-utilities/src/index.ts | 2 +- 4 files changed, 103 insertions(+), 75 deletions(-) diff --git a/packages/react-components/react-utilities/etc/react-utilities.api.md b/packages/react-components/react-utilities/etc/react-utilities.api.md index 13068ddcdacea0..d8de8d649eb39b 100644 --- a/packages/react-components/react-utilities/etc/react-utilities.api.md +++ b/packages/react-components/react-utilities/etc/react-utilities.api.md @@ -283,12 +283,11 @@ export function useIsSSR(): boolean; export function useMergedRefs(...refs: (React_2.Ref | undefined)[]): RefObjectFunction; // @public -export const useMotionPresence: (present: boolean) => UseMotionPresenceState; +export const useMotionPresence: (present: boolean, options?: UseMotionPresenceOptions) => UseMotionPresenceState; // @public -export type UseMotionPresenceEvents = { - onEntered?: () => void; - onExited?: () => void; +export type UseMotionPresenceOptions = { + animateOnFirstMount?: boolean; }; // @public @@ -296,7 +295,7 @@ export type UseMotionPresenceState = { ref: React_2.RefCallback; shouldRender: boolean; visible: boolean; - motionState: 'entering' | 'exiting' | 'resting'; + motionState: 'entering' | 'exiting' | 'resting' | 'unmounted'; }; // @internal (undocumented) diff --git a/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts b/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts index 52b39ca7449fe7..07b2f6e04b1a44 100644 --- a/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts +++ b/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts @@ -1,24 +1,35 @@ import { act, renderHook } from '@testing-library/react-hooks'; +import * as React from 'react'; import { useMotionPresence, UseMotionPresenceOptions } from './useMotionPresence'; -const transitionDuration = 100; -const defaultStyles = { 'transition-duration': `${transitionDuration}ms` }; +const defaultDuration = 100; const renderHookWithRef = ( initialPresence: boolean, initialOptions?: UseMotionPresenceOptions, - style: Record = defaultStyles, + style: Record = { 'transition-duration': `${defaultDuration}ms` }, ) => { const refEl = document.createElement('div'); - const hook = renderHook(({ presence, options }) => useMotionPresence(presence, options), { - initialProps: { - presence: initialPresence, - options: initialOptions, - } as { - presence: boolean; - options?: UseMotionPresenceOptions; + const hook = renderHook( + ({ presence, options }) => { + const state = useMotionPresence(presence, options); + + React.useEffect(() => { + console.log(state); + }, [state]); + + return state; }, - }); + { + initialProps: { + presence: initialPresence, + options: initialOptions, + } as { + presence: boolean; + options?: UseMotionPresenceOptions; + }, + }, + ); Object.entries(style).forEach(([key, value]) => value && refEl.style.setProperty(key, value)); @@ -39,24 +50,22 @@ describe('useMotionPresence', () => { describe('when presence is false by default', () => { it('should return default values when presence is false', () => { const { result } = renderHookWithRef(false); - const { ref, motionState, shouldRender, visible } = result.current; - expect(typeof ref).toBe('function'); - expect(motionState).toBe('unmounted'); - expect(shouldRender).toBe(false); - expect(visible).toBe(false); + expect(typeof result.current.ref).toBe('function'); + expect(result.current.motionState).toBe('unmounted'); + expect(result.current.shouldRender).toBe(false); + expect(result.current.visible).toBe(false); }); }); describe('when presence is true by default', () => { it('should return default values', () => { const { result } = renderHookWithRef(true); - const { ref, motionState, shouldRender, visible } = result.current; - expect(typeof ref).toBe('function'); - expect(motionState).toBe('resting'); - expect(shouldRender).toBe(true); - expect(visible).toBe(true); + expect(typeof result.current.ref).toBe('function'); + expect(result.current.motionState).toBe('resting'); + expect(result.current.shouldRender).toBe(true); + expect(result.current.visible).toBe(true); }); it('should not change values after timeout ', () => { @@ -71,6 +80,7 @@ describe('useMotionPresence', () => { assertSameValues(); act(() => jest.advanceTimersToNextTimer()); + act(() => jest.advanceTimersToNextTimer()); assertSameValues(); }); @@ -117,7 +127,7 @@ describe('useMotionPresence', () => { act(() => jest.advanceTimersToNextTimer()); // timeout - jest.advanceTimersByTime(transitionDuration + 1); + jest.advanceTimersByTime(defaultDuration + 1); }); expect(result.current.motionState).toBe('unmounted'); @@ -146,7 +156,7 @@ describe('useMotionPresence', () => { jest.advanceTimersToNextTimer(); // timeout - jest.advanceTimersByTime(transitionDuration + 1); + jest.advanceTimersByTime(defaultDuration + 1); }); expect(result.current.motionState).toBe('unmounted'); @@ -201,4 +211,49 @@ describe('useMotionPresence', () => { expect(result.current.shouldRender).toBe(false); }); }); + + describe.each([ + { message: 'with no transition', styles: { 'transition-duration': '0' } }, + { message: 'with no animation', styles: { 'animation-duration': '0' } }, + ])('when presence changes - $message', ({ styles }) => { + it('should toggle values when transition-duration is 0', () => { + const { result, rerender } = renderHookWithRef( + false, + { + animateOnFirstMount: true, + }, + styles, + ); + + expect(typeof result.current.ref).toBe('function'); + expect(result.current.motionState).toBe('unmounted'); + expect(result.current.shouldRender).toBe(false); + expect(result.current.visible).toBe(false); + + rerender({ presence: true }); + + expect(result.current.shouldRender).toBe(true); + expect(result.current.motionState).toBe('resting'); + // requestAnimationFrame + act(() => jest.advanceTimersToNextTimer()); + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.visible).toBe(true); + // timeout + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.motionState).toBe('resting'); + + rerender({ presence: false }); + + act(() => jest.advanceTimersToNextTimer()); + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.visible).toBe(false); + + // requestAnimationFrame + act(() => jest.advanceTimersToNextTimer()); + // timeout + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.motionState).toBe('unmounted'); + expect(result.current.shouldRender).toBe(false); + }); + }); }); diff --git a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts index 54c41843b1dc0f..a0d5e114b603ee 100644 --- a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts +++ b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts @@ -11,6 +11,10 @@ interface CSSUnitValue { readonly unit: string; } +/** + * Style property map read only. + * @see https://developer.mozilla.org/en-US/docs/Web/API/StylePropertyMapReadOnly + */ interface StylePropertyMapReadOnly { [Symbol.iterator](): IterableIterator<[string, CSSUnitValue[]]>; @@ -24,9 +28,9 @@ interface StylePropertyMapReadOnly { * HTMLElement with styled map. * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/computedStyleMap */ -interface HTMLElementWithStyledMap extends HTMLElement { +type HTMLElementWithStyledMap = TElement & { computedStyleMap(): StylePropertyMapReadOnly; -} +}; interface CSSWithNumber { number(value: number): { @@ -180,9 +184,7 @@ const getComputedStyleProp = (computedStyle: CSSStyleDeclaration, prop: string): const getMaxCSSDuration = (durations: string[], delays: string[]): number => { const totalDurations: number[] = []; - durations.forEach(duration => { - totalDurations.push(toMs(duration.trim())); - }); + durations.forEach(duration => totalDurations.push(toMs(duration.trim()))); delays.forEach((delay, index) => { const parsedDelay = toMs(delay.trim()); @@ -230,15 +232,16 @@ export const useMotionPresence = ( options: UseMotionPresenceOptions = {}, ): UseMotionPresenceState => { const { animateOnFirstMount } = { animateOnFirstMount: false, ...options }; + const [state, setState] = React.useState, 'ref'>>({ shouldRender: present, motionState: present ? 'resting' : 'unmounted', visible: false, }); - const canHaveMotion = React.useRef(false); - const [currentElement, setCurrentElement] = React.useState(null); + const [currentElement, setCurrentElement] = React.useState | null>(null); const [setAnimationTimeout, clearAnimationTimeout] = useTimeout(); + const skipAnimationOnFirstRender = React.useRef(!animateOnFirstMount); const processAnimation = React.useCallback( (callback: () => void) => { @@ -271,52 +274,38 @@ export const useMotionPresence = ( [clearAnimationTimeout, currentElement, setAnimationTimeout], ); - const ref: React.RefCallback = React.useCallback(node => { + const ref: React.RefCallback> = React.useCallback(node => { if (!node) { return; } - // Cast to HTMLElementWithStyledMap to allow the use of the experimental CSSOM API. - setCurrentElement(node as unknown as HTMLElementWithStyledMap); + setCurrentElement(node); }, []); React.useEffect(() => { if (present) { setState({ shouldRender: true, - visible: false, + visible: skipAnimationOnFirstRender.current ? true : false, motionState: 'resting', }); } }, [present]); - React.useEffect(() => { - if (!animateOnFirstMount && present) { - setState(prevState => ({ - ...prevState, - visible: true, - })); - } - /* - * We only want to run this effect on first mount to make sure the element doesn't animate. - */ - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - React.useEffect(() => { if (!currentElement) { return; } let animationFrame: number; - const skipFirstMount = !animateOnFirstMount && !canHaveMotion.current; + const skipAnimation = skipAnimationOnFirstRender.current; const onDestroy = () => cancelAnimationFrame(animationFrame); animationFrame = requestAnimationFrame(() => { setState(prevState => { let motionState = prevState.motionState; - if (skipFirstMount) { + if (skipAnimation) { motionState = present ? 'resting' : 'unmounted'; } else { motionState = present ? 'entering' : 'exiting'; @@ -324,41 +313,26 @@ export const useMotionPresence = ( return { ...prevState, - visible: present, motionState, + visible: present, }; }); }); - if (!canHaveMotion.current) { - canHaveMotion.current = true; - } - - if (skipFirstMount) { + if (skipAnimation) { return onDestroy; } processAnimation(() => { setState(prevState => ({ ...prevState, - motionState: 'resting', + motionState: present ? 'resting' : 'unmounted', + shouldRender: present, })); }); return onDestroy; - }, [animateOnFirstMount, currentElement, present, processAnimation]); - - React.useEffect(() => { - if (state.motionState === 'exiting') { - processAnimation(() => { - setState({ - shouldRender: false, - visible: false, - motionState: 'unmounted', - }); - }); - } - }, [processAnimation, state.motionState]); + }, [currentElement, present, processAnimation]); return { ref, diff --git a/packages/react-components/react-utilities/src/index.ts b/packages/react-components/react-utilities/src/index.ts index 002e300c08ef45..256e276efe6a3f 100644 --- a/packages/react-components/react-utilities/src/index.ts +++ b/packages/react-components/react-utilities/src/index.ts @@ -42,7 +42,7 @@ export type { RefObjectFunction, UseControllableStateOptions, UseOnClickOrScrollOutsideOptions, - UseMotionPresenceEvents, + UseMotionPresenceOptions, UseMotionPresenceState, } from './hooks/index'; From b2d98bbf521a55ead15c5711d1d4794930ee9de9 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Tue, 1 Aug 2023 13:56:00 +0200 Subject: [PATCH 049/111] fix: add transition to box-shadow to prevent glitchy exit animation --- .../DrawerOverlay/useDrawerOverlayStyles.styles.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts index a90848b33b4450..6f78d88fb61afc 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts @@ -19,8 +19,9 @@ const useDrawerRootStyles = makeStyles({ position: 'fixed', top: 0, bottom: 0, - transitionProperty: 'transform', - willChange: 'transform', + transitionProperty: 'transform, box-shadow', + willChange: 'transform, box-shadow', + boxShadow: '0px transparent', }, /* Positioning */ @@ -34,6 +35,7 @@ const useDrawerRootStyles = makeStyles({ /* Visible */ visible: { transform: 'translate3D(0, 0, 0)', + boxShadow: tokens.shadow64, }, }); From 71a0c251808786963b043636163c1393c1898008 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Tue, 1 Aug 2023 13:59:19 +0200 Subject: [PATCH 050/111] fix: conflicts --- .../src/hooks/useMotionPresence.test.ts | 28 ++++++------------- .../src/hooks/useMotionPresence.ts | 4 +++ 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts b/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts index 07b2f6e04b1a44..f018b6c8ae8673 100644 --- a/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts +++ b/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts @@ -1,5 +1,4 @@ import { act, renderHook } from '@testing-library/react-hooks'; -import * as React from 'react'; import { useMotionPresence, UseMotionPresenceOptions } from './useMotionPresence'; @@ -10,26 +9,15 @@ const renderHookWithRef = ( style: Record = { 'transition-duration': `${defaultDuration}ms` }, ) => { const refEl = document.createElement('div'); - const hook = renderHook( - ({ presence, options }) => { - const state = useMotionPresence(presence, options); - - React.useEffect(() => { - console.log(state); - }, [state]); - - return state; + const hook = renderHook(({ presence, options }) => useMotionPresence(presence, options), { + initialProps: { + presence: initialPresence, + options: initialOptions, + } as { + presence: boolean; + options?: UseMotionPresenceOptions; }, - { - initialProps: { - presence: initialPresence, - options: initialOptions, - } as { - presence: boolean; - options?: UseMotionPresenceOptions; - }, - }, - ); + }); Object.entries(style).forEach(([key, value]) => value && refEl.style.setProperty(key, value)); diff --git a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts index a0d5e114b603ee..886d48961f69ab 100644 --- a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts +++ b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts @@ -334,6 +334,10 @@ export const useMotionPresence = ( return onDestroy; }, [currentElement, present, processAnimation]); + React.useEffect(() => { + skipAnimationOnFirstRender.current = false; + }, []); + return { ref, ...state, From 1faa4c8b64f9c08a5fd20a81c96fb264e39b2976 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Tue, 1 Aug 2023 14:15:17 +0200 Subject: [PATCH 051/111] feat: implement useMotionPresence hook --- .../react-utilities/src/hooks/index.ts | 1 + .../src/hooks/useMotionPresence.test.ts | 247 +++++++++++++ .../src/hooks/useMotionPresence.ts | 345 ++++++++++++++++++ .../react-utilities/src/index.ts | 9 +- 4 files changed, 601 insertions(+), 1 deletion(-) create mode 100644 packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts create mode 100644 packages/react-components/react-utilities/src/hooks/useMotionPresence.ts diff --git a/packages/react-components/react-utilities/src/hooks/index.ts b/packages/react-components/react-utilities/src/hooks/index.ts index e5928f400aa78f..c43fadc10fe58a 100644 --- a/packages/react-components/react-utilities/src/hooks/index.ts +++ b/packages/react-components/react-utilities/src/hooks/index.ts @@ -10,3 +10,4 @@ export * from './useOnScrollOutside'; export * from './usePrevious'; export * from './useScrollbarWidth'; export * from './useTimeout'; +export * from './useMotionPresence'; diff --git a/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts b/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts new file mode 100644 index 00000000000000..f018b6c8ae8673 --- /dev/null +++ b/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts @@ -0,0 +1,247 @@ +import { act, renderHook } from '@testing-library/react-hooks'; + +import { useMotionPresence, UseMotionPresenceOptions } from './useMotionPresence'; + +const defaultDuration = 100; +const renderHookWithRef = ( + initialPresence: boolean, + initialOptions?: UseMotionPresenceOptions, + style: Record = { 'transition-duration': `${defaultDuration}ms` }, +) => { + const refEl = document.createElement('div'); + const hook = renderHook(({ presence, options }) => useMotionPresence(presence, options), { + initialProps: { + presence: initialPresence, + options: initialOptions, + } as { + presence: boolean; + options?: UseMotionPresenceOptions; + }, + }); + + Object.entries(style).forEach(([key, value]) => value && refEl.style.setProperty(key, value)); + + act(() => hook.result.current.ref(refEl)); + + return hook; +}; + +describe('useMotionPresence', () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + describe('when presence is false by default', () => { + it('should return default values when presence is false', () => { + const { result } = renderHookWithRef(false); + + expect(typeof result.current.ref).toBe('function'); + expect(result.current.motionState).toBe('unmounted'); + expect(result.current.shouldRender).toBe(false); + expect(result.current.visible).toBe(false); + }); + }); + + describe('when presence is true by default', () => { + it('should return default values', () => { + const { result } = renderHookWithRef(true); + + expect(typeof result.current.ref).toBe('function'); + expect(result.current.motionState).toBe('resting'); + expect(result.current.shouldRender).toBe(true); + expect(result.current.visible).toBe(true); + }); + + it('should not change values after timeout ', () => { + const { result } = renderHookWithRef(true); + + const assertSameValues = () => { + expect(typeof result.current.ref).toBe('function'); + expect(result.current.motionState).toBe('resting'); + expect(result.current.shouldRender).toBe(true); + expect(result.current.visible).toBe(true); + }; + + assertSameValues(); + act(() => jest.advanceTimersToNextTimer()); + act(() => jest.advanceTimersToNextTimer()); + assertSameValues(); + }); + + it('should change visible to true when animateOnFirstMount is true', () => { + const { result } = renderHookWithRef(true, { animateOnFirstMount: true }); + + expect(typeof result.current.ref).toBe('function'); + expect(result.current.motionState).toBe('resting'); + expect(result.current.shouldRender).toBe(true); + expect(result.current.visible).toBe(false); + + act(() => jest.advanceTimersToNextTimer()); + + expect(result.current.visible).toBe(true); + }); + }); + + describe('when presence changes', () => { + it('should toggle values starting with false', () => { + const { result, rerender } = renderHookWithRef(false); + + expect(typeof result.current.ref).toBe('function'); + expect(result.current.motionState).toBe('unmounted'); + expect(result.current.shouldRender).toBe(false); + expect(result.current.visible).toBe(false); + + rerender({ presence: true }); + + expect(result.current.shouldRender).toBe(true); + expect(result.current.motionState).toBe('resting'); + + // double requestAnimationFrame + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.visible).toBe(true); + + rerender({ presence: false }); + + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.motionState).toBe('exiting'); + expect(result.current.visible).toBe(false); + + act(() => { + // requestAnimationFrame + act(() => jest.advanceTimersToNextTimer()); + + // timeout + jest.advanceTimersByTime(defaultDuration + 1); + }); + + expect(result.current.motionState).toBe('unmounted'); + expect(result.current.shouldRender).toBe(false); + expect(result.current.visible).toBe(false); + }); + + it('should toggle values starting with true', () => { + const { result, rerender } = renderHookWithRef(true); + + expect(typeof result.current.ref).toBe('function'); + expect(result.current.motionState).toBe('resting'); + expect(result.current.shouldRender).toBe(true); + expect(result.current.visible).toBe(true); + + rerender({ presence: false }); + + // requestAnimationFrame + act(() => jest.advanceTimersToNextTimer()); + + expect(result.current.motionState).toBe('exiting'); + expect(result.current.visible).toBe(false); + + act(() => { + // requestAnimationFrame + jest.advanceTimersToNextTimer(); + + // timeout + jest.advanceTimersByTime(defaultDuration + 1); + }); + + expect(result.current.motionState).toBe('unmounted'); + expect(result.current.shouldRender).toBe(false); + expect(result.current.visible).toBe(false); + }); + }); + + describe.each([ + { message: 'with transition', styles: { 'transition-duration': '100ms' } }, + { message: 'with long transition', styles: { 'transition-duration': '1000ms' } }, + { message: 'with animation', styles: { 'animation-duration': '100ms' } }, + { message: 'with long animation', styles: { 'animation-duration': '1000ms' } }, + ])('when presence changes - $message', ({ styles }) => { + it('should toggle values starting with false when animateOnFirstMount is true', () => { + const { result, rerender } = renderHookWithRef( + false, + { + animateOnFirstMount: true, + }, + styles, + ); + + expect(typeof result.current.ref).toBe('function'); + expect(result.current.motionState).toBe('unmounted'); + expect(result.current.shouldRender).toBe(false); + expect(result.current.visible).toBe(false); + + rerender({ presence: true }); + + expect(result.current.shouldRender).toBe(true); + expect(result.current.motionState).toBe('resting'); + // requestAnimationFrame + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.visible).toBe(true); + expect(result.current.motionState).toBe('entering'); + // timeout + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.motionState).toBe('resting'); + + rerender({ presence: false }); + + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.visible).toBe(false); + expect(result.current.motionState).toBe('exiting'); + + // requestAnimationFrame + act(() => jest.advanceTimersToNextTimer()); + // timeout + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.motionState).toBe('unmounted'); + expect(result.current.shouldRender).toBe(false); + }); + }); + + describe.each([ + { message: 'with no transition', styles: { 'transition-duration': '0' } }, + { message: 'with no animation', styles: { 'animation-duration': '0' } }, + ])('when presence changes - $message', ({ styles }) => { + it('should toggle values when transition-duration is 0', () => { + const { result, rerender } = renderHookWithRef( + false, + { + animateOnFirstMount: true, + }, + styles, + ); + + expect(typeof result.current.ref).toBe('function'); + expect(result.current.motionState).toBe('unmounted'); + expect(result.current.shouldRender).toBe(false); + expect(result.current.visible).toBe(false); + + rerender({ presence: true }); + + expect(result.current.shouldRender).toBe(true); + expect(result.current.motionState).toBe('resting'); + // requestAnimationFrame + act(() => jest.advanceTimersToNextTimer()); + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.visible).toBe(true); + // timeout + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.motionState).toBe('resting'); + + rerender({ presence: false }); + + act(() => jest.advanceTimersToNextTimer()); + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.visible).toBe(false); + + // requestAnimationFrame + act(() => jest.advanceTimersToNextTimer()); + // timeout + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.motionState).toBe('unmounted'); + expect(result.current.shouldRender).toBe(false); + }); + }); +}); diff --git a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts new file mode 100644 index 00000000000000..886d48961f69ab --- /dev/null +++ b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts @@ -0,0 +1,345 @@ +import * as React from 'react'; +import { useTimeout } from './useTimeout'; + +/** + * CSS Typed Object Model + * @see https://drafts.css-houdini.org/css-typed-om-1/ + * @see https://developer.mozilla.org/en-US/docs/Web/API/CSSUnitValue + */ +interface CSSUnitValue { + value: number; + readonly unit: string; +} + +/** + * Style property map read only. + * @see https://developer.mozilla.org/en-US/docs/Web/API/StylePropertyMapReadOnly + */ +interface StylePropertyMapReadOnly { + [Symbol.iterator](): IterableIterator<[string, CSSUnitValue[]]>; + + get(property: string): CSSUnitValue | undefined; + getAll(property: string): CSSUnitValue[]; + has(property: string): boolean; + readonly size: number; +} + +/** + * HTMLElement with styled map. + * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/computedStyleMap + */ +type HTMLElementWithStyledMap = TElement & { + computedStyleMap(): StylePropertyMapReadOnly; +}; + +interface CSSWithNumber { + number(value: number): { + value: number; + readonly unit: string; + }; +} + +/** + * State for useMotionPresence hook. + */ +export type UseMotionPresenceState = { + /** + * Ref to the element. + */ + ref: React.RefCallback; + + /** + * Whether the element should be rendered in the DOM. + * This should be used to conditionally render the element. + */ + shouldRender: boolean; + + /** + * Whether the element is currently visible in the DOM. + */ + visible: boolean; + + /** + * Current state of the element. + * + * - `entering` - The element is entering the DOM. + * - `exiting` - The element is exiting the DOM. + * - `resting` - The element is currently not animating. This is the final and initial state of the element. + * - `unmounted` - The element is not rendered in the DOM. + */ + motionState: 'entering' | 'exiting' | 'resting' | 'unmounted'; +}; + +/** + * Options for useMotionPresence hook. + */ +export type UseMotionPresenceOptions = { + /** + * Whether to animate the element on first mount. + * + * @default false + */ + animateOnFirstMount?: boolean; +}; + +/** + * Returns CSS styles of the given node. + * @param node - DOM node. + * @returns - CSS styles. + */ +const getElementComputedStyle = (node: HTMLElement): CSSStyleDeclaration => { + const window = node.ownerDocument?.defaultView; + + return window!.getComputedStyle(node, null); +}; + +/** + * Converts a CSS duration string to milliseconds. + * + * @param duration - CSS duration string + * @returns Duration in milliseconds + */ +function toMs(duration: string): number { + const trimmed = duration.trim(); + + if (trimmed.includes('auto')) { + return 0; + } + + if (trimmed.includes('ms')) { + return parseFloat(trimmed); + } + + return Number(trimmed.slice(0, -1).replace(',', '.')) * 1000; +} + +/** + * Checks if the browser supports CSSOM. + * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/computedStyleMap + * + * @param node - DOM node + * @returns Whether the browser supports CSSOM + */ +const hasCSSOMSupport = (node: HTMLElementWithStyledMap) => { + /** + * As we are using the experimental CSSOM API, we need to check if the browser supports it. + * The typecast here is to allow the use of the `number` function that is not yet part of the CSSOM typings. + * @see https://www.npmjs.com/package/@types/w3c-css-typed-object-model-level-1 + */ + return Boolean(typeof CSS !== 'undefined' && (CSS as unknown as CSSWithNumber).number && node.computedStyleMap); +}; + +/** + * + * Gets the computed style of a given element. + * If the browser supports CSSOM, it will return a ComputedStyleMap object. + * Otherwise, it will return a CSSStyleDeclaration object. + */ +const getCSSStyle = (node: HTMLElementWithStyledMap): CSSStyleDeclaration | StylePropertyMapReadOnly => { + if (hasCSSOMSupport(node)) { + return node.computedStyleMap(); + } + + return getElementComputedStyle(node); +}; + +/** + * Gets the computed map property for a given element using the CSSOM API. + * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/computedStyleMap + * + * @param computedStyle - Computed style of the element + * @param prop - CSS property + * @returns Computed map property + */ +const getComputedMapProp = (computedStyle: StylePropertyMapReadOnly, prop: string): string[] => { + const props = computedStyle.getAll(prop); + + if (props.length > 0) { + return props.map(({ value, unit }) => `${value}${unit}`); + } + + return ['0']; +}; + +/** + * Gets the computed style property for a given element using the getComputedStyle API. + * + * @param computedStyle - Computed style of the element + * @param prop - CSS property + * @returns Computed style property + */ +const getComputedStyleProp = (computedStyle: CSSStyleDeclaration, prop: string): string[] => { + const propValue = computedStyle.getPropertyValue(prop); + + return propValue ? propValue.split(',') : ['0']; +}; + +/** + * Gets the maximum duration from a list of CSS durations. + * + * @param durations - List of CSS durations + * @param delays - List of CSS delays + * @returns Maximum duration + */ +const getMaxCSSDuration = (durations: string[], delays: string[]): number => { + const totalDurations: number[] = []; + + durations.forEach(duration => totalDurations.push(toMs(duration.trim()))); + + delays.forEach((delay, index) => { + const parsedDelay = toMs(delay.trim()); + + if (totalDurations[index]) { + totalDurations[index] = totalDurations[index] + parsedDelay; + } else { + totalDurations[index] = parsedDelay; + } + }); + + return Math.max(...totalDurations); +}; + +/** + * Gets the motion information for a given element. + * + * @param computedStyle - Computed style of the element + * @returns motion information + */ +const getMotionDuration = (node: HTMLElementWithStyledMap) => { + const hasModernCSSSupport = hasCSSOMSupport(node); + const computedStyle = getCSSStyle(node); + + const getProp = (prop: string): string[] => { + return hasModernCSSSupport + ? getComputedMapProp(computedStyle as StylePropertyMapReadOnly, prop) + : getComputedStyleProp(computedStyle as CSSStyleDeclaration, prop); + }; + + const transitionDuration = getMaxCSSDuration(getProp('transition-duration'), getProp('transition-delay')); + const animationDuration = getMaxCSSDuration(getProp('animation-duration'), getProp('animation-delay')); + + return Math.max(transitionDuration, animationDuration); +}; + +/** + * Hook to manage the presence of an element in the DOM based on its CSS transition/animation state. + * + * @param present - Whether the element should be present in the DOM + * @param events - Callbacks for when the element enters or exits the DOM + */ +export const useMotionPresence = ( + present: boolean, + options: UseMotionPresenceOptions = {}, +): UseMotionPresenceState => { + const { animateOnFirstMount } = { animateOnFirstMount: false, ...options }; + + const [state, setState] = React.useState, 'ref'>>({ + shouldRender: present, + motionState: present ? 'resting' : 'unmounted', + visible: false, + }); + + const [currentElement, setCurrentElement] = React.useState | null>(null); + const [setAnimationTimeout, clearAnimationTimeout] = useTimeout(); + const skipAnimationOnFirstRender = React.useRef(!animateOnFirstMount); + + const processAnimation = React.useCallback( + (callback: () => void) => { + if (!currentElement) { + return; + } + + clearAnimationTimeout(); + const animationFrame = requestAnimationFrame(() => { + const duration = getMotionDuration(currentElement); + + if (duration === 0) { + callback(); + return; + } + + /** + * Use CSS transition duration + 1ms to ensure the animation has finished on both enter and exit states. + * This is an alternative to using the `transitionend` event which can be unreliable as it fires multiple times + * if the transition has multiple properties. + */ + setAnimationTimeout(() => callback(), duration + 1); + }); + + return () => { + clearAnimationTimeout(); + cancelAnimationFrame(animationFrame); + }; + }, + [clearAnimationTimeout, currentElement, setAnimationTimeout], + ); + + const ref: React.RefCallback> = React.useCallback(node => { + if (!node) { + return; + } + + setCurrentElement(node); + }, []); + + React.useEffect(() => { + if (present) { + setState({ + shouldRender: true, + visible: skipAnimationOnFirstRender.current ? true : false, + motionState: 'resting', + }); + } + }, [present]); + + React.useEffect(() => { + if (!currentElement) { + return; + } + + let animationFrame: number; + const skipAnimation = skipAnimationOnFirstRender.current; + const onDestroy = () => cancelAnimationFrame(animationFrame); + + animationFrame = requestAnimationFrame(() => { + setState(prevState => { + let motionState = prevState.motionState; + + if (skipAnimation) { + motionState = present ? 'resting' : 'unmounted'; + } else { + motionState = present ? 'entering' : 'exiting'; + } + + return { + ...prevState, + motionState, + visible: present, + }; + }); + }); + + if (skipAnimation) { + return onDestroy; + } + + processAnimation(() => { + setState(prevState => ({ + ...prevState, + motionState: present ? 'resting' : 'unmounted', + shouldRender: present, + })); + }); + + return onDestroy; + }, [currentElement, present, processAnimation]); + + React.useEffect(() => { + skipAnimationOnFirstRender.current = false; + }, []); + + return { + ref, + ...state, + }; +}; diff --git a/packages/react-components/react-utilities/src/index.ts b/packages/react-components/react-utilities/src/index.ts index bb8b54b62d6848..256e276efe6a3f 100644 --- a/packages/react-components/react-utilities/src/index.ts +++ b/packages/react-components/react-utilities/src/index.ts @@ -36,8 +36,15 @@ export { usePrevious, useScrollbarWidth, useTimeout, + useMotionPresence, +} from './hooks/index'; +export type { + RefObjectFunction, + UseControllableStateOptions, + UseOnClickOrScrollOutsideOptions, + UseMotionPresenceOptions, + UseMotionPresenceState, } from './hooks/index'; -export type { RefObjectFunction, UseControllableStateOptions, UseOnClickOrScrollOutsideOptions } from './hooks/index'; export { canUseDOM, useIsSSR, SSRProvider } from './ssr/index'; From 230e273804e13682ee7d3d4a2a286ccc970ab200 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Tue, 1 Aug 2023 14:16:49 +0200 Subject: [PATCH 052/111] docs: add missing change files --- ...act-utilities-01c80b27-0a05-464e-abdf-bdddf86d72ac.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/@fluentui-react-utilities-01c80b27-0a05-464e-abdf-bdddf86d72ac.json diff --git a/change/@fluentui-react-utilities-01c80b27-0a05-464e-abdf-bdddf86d72ac.json b/change/@fluentui-react-utilities-01c80b27-0a05-464e-abdf-bdddf86d72ac.json new file mode 100644 index 00000000000000..1b3a3e5dee2930 --- /dev/null +++ b/change/@fluentui-react-utilities-01c80b27-0a05-464e-abdf-bdddf86d72ac.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "feat: new useMotionPresence hook - get the state for css animations/transitions", + "packageName": "@fluentui/react-utilities", + "email": "marcosvmmoura@gmail.com", + "dependentChangeType": "patch" +} From 5da025806652f1611beeeae6f067a94dca963407 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Wed, 2 Aug 2023 17:09:12 +0200 Subject: [PATCH 053/111] fix: simplify backdrop slot code --- .../src/components/DrawerOverlay/useDrawerOverlay.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts index 7b59633b9416b8..bad3a6aefb8d3c 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts @@ -24,6 +24,8 @@ export const useDrawerOverlay_unstable = ( const backdropPresence = useMotionPresence(open); const backdropRef = useMergedRefs(backdropPresence.ref, drawerRef); + const hasCustomBackdrop = modalType !== 'non-modal' && props.backdrop !== null; + return { components: { root: DialogSurface, @@ -35,10 +37,14 @@ export const useDrawerOverlay_unstable = ( defaultProps: { ...props, ref: useMergedRefs(ref, drawerRef), - ...(modalType !== 'non-modal' && { + ...(hasCustomBackdrop && { backdrop: { - ...((props.backdrop || {}) as Record), - ref: backdropRef, + ...resolveShorthand(props.backdrop, { + required: true, + defaultProps: { + ref: backdropRef, + }, + }), }, }), } as DialogSurfaceProps, From b8128e74bdd881df0eaf32b58cf3df1b410bb7ba Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Wed, 2 Aug 2023 17:09:53 +0200 Subject: [PATCH 054/111] fix: improve performance of duration calculation --- .../src/hooks/useMotionPresence.ts | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts index 886d48961f69ab..883ce60315e01f 100644 --- a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts +++ b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts @@ -182,19 +182,19 @@ const getComputedStyleProp = (computedStyle: CSSStyleDeclaration, prop: string): * @returns Maximum duration */ const getMaxCSSDuration = (durations: string[], delays: string[]): number => { - const totalDurations: number[] = []; + const totalProps = Math.max(durations.length, delays.length); + const totalDurations = []; - durations.forEach(duration => totalDurations.push(toMs(duration.trim()))); + if (totalProps === 0) { + return 0; + } - delays.forEach((delay, index) => { - const parsedDelay = toMs(delay.trim()); + for (let i = 0; i < totalProps; i++) { + const duration = toMs(durations[i] || '0'); + const delay = toMs(delays[i] || '0'); - if (totalDurations[index]) { - totalDurations[index] = totalDurations[index] + parsedDelay; - } else { - totalDurations[index] = parsedDelay; - } - }); + totalDurations.push(duration + delay); + } return Math.max(...totalDurations); }; @@ -293,10 +293,6 @@ export const useMotionPresence = ( }, [present]); React.useEffect(() => { - if (!currentElement) { - return; - } - let animationFrame: number; const skipAnimation = skipAnimationOnFirstRender.current; const onDestroy = () => cancelAnimationFrame(animationFrame); @@ -332,7 +328,7 @@ export const useMotionPresence = ( }); return onDestroy; - }, [currentElement, present, processAnimation]); + }, [present, processAnimation]); React.useEffect(() => { skipAnimationOnFirstRender.current = false; From c721171e3da00bea4b4341594df1f04f1bba3339 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Thu, 3 Aug 2023 14:20:24 +0200 Subject: [PATCH 055/111] feat: implement useAnimationFrame hook to help controlling the motion presence requestAnimationFrame --- .../src/hooks/useAnimationFrame.test.ts | 106 ++++++++++++++++++ .../src/hooks/useAnimationFrame.ts | 13 +++ .../src/hooks/useBrowserTimer.test.ts | 61 ++++++++++ .../src/hooks/useBrowserTimer.ts | 47 ++++++++ .../src/hooks/useMotionPresence.ts | 13 ++- .../react-utilities/src/hooks/useTimeout.ts | 21 +--- 6 files changed, 236 insertions(+), 25 deletions(-) create mode 100644 packages/react-components/react-utilities/src/hooks/useAnimationFrame.test.ts create mode 100644 packages/react-components/react-utilities/src/hooks/useAnimationFrame.ts create mode 100644 packages/react-components/react-utilities/src/hooks/useBrowserTimer.test.ts create mode 100644 packages/react-components/react-utilities/src/hooks/useBrowserTimer.ts diff --git a/packages/react-components/react-utilities/src/hooks/useAnimationFrame.test.ts b/packages/react-components/react-utilities/src/hooks/useAnimationFrame.test.ts new file mode 100644 index 00000000000000..307eddec185bc6 --- /dev/null +++ b/packages/react-components/react-utilities/src/hooks/useAnimationFrame.test.ts @@ -0,0 +1,106 @@ +import { renderHook } from '@testing-library/react-hooks'; +import { useAnimationFrame } from './useAnimationFrame'; + +describe('useAnimationFrame', () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it('calls the callback only on the next frame', () => { + const [setTestRequestAnimationFrame] = renderHook(() => useAnimationFrame()).result.current; + const callback = jest.fn(); + + setTestRequestAnimationFrame(callback); + + expect(callback).not.toHaveBeenCalled(); + + jest.runAllTimers(); + + expect(callback).toHaveBeenCalledTimes(1); + }); + + it('does not call the callback if cancel is called', () => { + const [setTestRequestAnimationFrame, cancelTEstRequestAnimationFrame] = renderHook(() => useAnimationFrame()).result + .current; + const callback = jest.fn(); + + setTestRequestAnimationFrame(callback); + cancelTEstRequestAnimationFrame(); + + jest.runAllTimers(); + + expect(callback).not.toHaveBeenCalled(); + }); + + it('cancel the previous requestAnimationFrame if set is called again', () => { + const [setTestRequestAnimationFrame] = renderHook(() => useAnimationFrame()).result.current; + const callbackA = jest.fn(); + const callbackB = jest.fn(); + + setTestRequestAnimationFrame(callbackA); + setTestRequestAnimationFrame(callbackB); + + jest.runAllTimers(); + + expect(callbackA).not.toHaveBeenCalled(); + expect(callbackB).toHaveBeenCalledTimes(1); + }); + + it('allows another requestAnimationFrame to be set after the previous has run', () => { + const [setTestRequestAnimationFrame] = renderHook(() => useAnimationFrame()).result.current; + const callbackA = jest.fn(); + const callbackB = jest.fn(); + + setTestRequestAnimationFrame(callbackA); + + jest.runAllTimers(); + + setTestRequestAnimationFrame(callbackB); + + jest.runAllTimers(); + + expect(callbackA).toHaveBeenCalledTimes(1); + expect(callbackB).toHaveBeenCalledTimes(1); + }); + + it('does not cancel the requestAnimationFrame between renders', () => { + const { result, rerender } = renderHook(() => useAnimationFrame()); + const [setTestRequestAnimationFrame] = result.current; + const callback = jest.fn(); + + setTestRequestAnimationFrame(callback); + + rerender(); + + jest.runAllTimers(); + + expect(callback).toHaveBeenCalledTimes(1); + }); + + it('cancel the requestAnimationFrame when the component is unmounted', () => { + const { result, unmount } = renderHook(() => useAnimationFrame()); + const [setTestRequestAnimationFrame] = result.current; + const callback = jest.fn(); + + setTestRequestAnimationFrame(callback); + + unmount(); + + jest.runAllTimers(); + + expect(callback).not.toHaveBeenCalled(); + }); + + it('returns the same functions every render', () => { + const { result, rerender } = renderHook(() => useAnimationFrame()); + const [setTestRequestAnimationFrame, cancelTEstRequestAnimationFrame] = result.current; + + rerender(); + + expect(result.current).toStrictEqual([setTestRequestAnimationFrame, cancelTEstRequestAnimationFrame]); + }); +}); diff --git a/packages/react-components/react-utilities/src/hooks/useAnimationFrame.ts b/packages/react-components/react-utilities/src/hooks/useAnimationFrame.ts new file mode 100644 index 00000000000000..de44967dad2027 --- /dev/null +++ b/packages/react-components/react-utilities/src/hooks/useAnimationFrame.ts @@ -0,0 +1,13 @@ +import { useBrowserTimer } from './useBrowserTimer'; + +/** + * @internal + * Helper to manage a browser requestAnimationFrame. + * Ensures that the requestAnimationFrame isn't set multiple times at once, + * and is cleaned up when the component is unloaded. + * + * @returns A pair of [requestAnimationFrame, cancelAnimationFrame] that are stable between renders. + */ +export function useAnimationFrame() { + return useBrowserTimer(requestAnimationFrame, cancelAnimationFrame); +} diff --git a/packages/react-components/react-utilities/src/hooks/useBrowserTimer.test.ts b/packages/react-components/react-utilities/src/hooks/useBrowserTimer.test.ts new file mode 100644 index 00000000000000..26600feb7262bb --- /dev/null +++ b/packages/react-components/react-utilities/src/hooks/useBrowserTimer.test.ts @@ -0,0 +1,61 @@ +import { renderHook } from '@testing-library/react-hooks'; +import { useBrowserTimer } from './useBrowserTimer'; + +const setTimer = jest.fn((callback: jest.Func) => { + callback(); + return 0; +}); + +const cancelTimer = jest.fn(() => 0); + +describe('useBrowserTimer', () => { + it('should return array with functions', () => { + const hookValues = renderHook(() => useBrowserTimer(setTimer, cancelTimer)).result.current; + + expect(hookValues).toHaveLength(2); + expect(typeof hookValues[0]).toBe('function'); + expect(typeof hookValues[1]).toBe('function'); + expect(hookValues[0].name).toBe('set'); + expect(hookValues[1].name).toBe('cancel'); + }); + + it('calls the setter only n times', () => { + const [setTestTimer] = renderHook(() => useBrowserTimer(setTimer, cancelTimer)).result.current; + const callback = jest.fn(); + + setTestTimer(callback); + + expect(callback).toHaveBeenCalledTimes(1); + expect(callback).not.toHaveBeenCalledTimes(2); + expect(setTimer).toHaveBeenCalledTimes(1); + expect(setTimer).not.toHaveBeenCalledTimes(2); + }); + + it('setter should return timer id', () => { + const [setTestTimer] = renderHook(() => useBrowserTimer(setTimer, cancelTimer)).result.current; + const callback = jest.fn(); + + const timerId = setTestTimer(callback); + + expect(callback).toHaveBeenCalledTimes(1); + expect(timerId).toBe(0); + }); + + it('should not call the cancel callback if not setter was called', () => { + const [, cancelTestTimer] = renderHook(() => useBrowserTimer(setTimer, cancelTimer)).result.current; + + cancelTestTimer(); + + expect(cancelTimer).not.toHaveBeenCalledTimes(1); + }); + + it('calls the cancel only n times', () => { + const [setTestTimer, cancelTestTimer] = renderHook(() => useBrowserTimer(setTimer, cancelTimer)).result.current; + + setTestTimer(jest.fn()); + cancelTestTimer(); + + expect(cancelTimer).toHaveBeenCalledTimes(1); + expect(cancelTimer).not.toHaveBeenCalledTimes(2); + }); +}); diff --git a/packages/react-components/react-utilities/src/hooks/useBrowserTimer.ts b/packages/react-components/react-utilities/src/hooks/useBrowserTimer.ts new file mode 100644 index 00000000000000..5422ca5f795611 --- /dev/null +++ b/packages/react-components/react-utilities/src/hooks/useBrowserTimer.ts @@ -0,0 +1,47 @@ +import * as React from 'react'; + +type UseBrowserTimerSetter = + | ((fn: () => void, duration?: number, ...args: Record[]) => number) + | ((fn: () => void) => number); +type UseBrowserTimerCancel = ((timerId: number) => void) | (() => void); + +/** + * @internal + * Helper to manage a browser timer. + * Ensures that the timer isn't set multiple times at once, + * and is cleaned up when the component is unloaded. + * + * @param setTimer - The timer setter function + * @param cancelTimer - The timer cancel function + * @returns A pair of [setTimer, cancelTimer] that are stable between renders. + * + * @example + * const [setTimer, cancelTimer] = useBrowserTimer(setTimeout, cancelTimeout); + * + * setTimer(() => console.log('Hello world!'), 1000); + * cancelTimer(); + */ +export function useBrowserTimer( + setTimer: TSetter, + cancelTimer: TCancel, +) { + const [timeout] = React.useState(() => ({ + id: undefined as number | undefined, + set: (fn: () => void, delay?: number) => { + timeout.cancel(); + timeout.id = delay ? setTimer(fn, delay) : setTimer(fn); + return timeout.id; + }, + cancel: () => { + if (timeout.id !== undefined) { + cancelTimer(timeout.id); + timeout.id = undefined; + } + }, + })); + + // Clean up the timeout when the component is unloaded + React.useEffect(() => timeout.cancel, [timeout]); + + return [timeout.set, timeout.cancel] as const; +} diff --git a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts index 883ce60315e01f..0eb1654e8bf763 100644 --- a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts +++ b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts @@ -1,4 +1,5 @@ import * as React from 'react'; +import { useAnimationFrame } from './useAnimationFrame'; import { useTimeout } from './useTimeout'; /** @@ -241,6 +242,7 @@ export const useMotionPresence = ( const [currentElement, setCurrentElement] = React.useState | null>(null); const [setAnimationTimeout, clearAnimationTimeout] = useTimeout(); + const [setProcessingAnimationFrame, cancelProcessingAnimationFrame] = useAnimationFrame(); const skipAnimationOnFirstRender = React.useRef(!animateOnFirstMount); const processAnimation = React.useCallback( @@ -293,11 +295,10 @@ export const useMotionPresence = ( }, [present]); React.useEffect(() => { - let animationFrame: number; const skipAnimation = skipAnimationOnFirstRender.current; - const onDestroy = () => cancelAnimationFrame(animationFrame); + const onUnmount = () => cancelProcessingAnimationFrame(); - animationFrame = requestAnimationFrame(() => { + setProcessingAnimationFrame(() => { setState(prevState => { let motionState = prevState.motionState; @@ -316,7 +317,7 @@ export const useMotionPresence = ( }); if (skipAnimation) { - return onDestroy; + return onUnmount; } processAnimation(() => { @@ -327,8 +328,8 @@ export const useMotionPresence = ( })); }); - return onDestroy; - }, [present, processAnimation]); + return onUnmount; + }, [cancelProcessingAnimationFrame, present, processAnimation, setProcessingAnimationFrame]); React.useEffect(() => { skipAnimationOnFirstRender.current = false; diff --git a/packages/react-components/react-utilities/src/hooks/useTimeout.ts b/packages/react-components/react-utilities/src/hooks/useTimeout.ts index 250c64fdf9b9df..16d5c78499e89c 100644 --- a/packages/react-components/react-utilities/src/hooks/useTimeout.ts +++ b/packages/react-components/react-utilities/src/hooks/useTimeout.ts @@ -1,4 +1,4 @@ -import * as React from 'react'; +import { useBrowserTimer } from './useBrowserTimer'; /** * @internal @@ -9,22 +9,5 @@ import * as React from 'react'; * @returns A pair of [setTimeout, clearTimeout] that are stable between renders. */ export function useTimeout() { - const [timeout] = React.useState(() => ({ - id: undefined as ReturnType | undefined, - set: (fn: () => void, delay: number) => { - timeout.clear(); - timeout.id = setTimeout(fn, delay); - }, - clear: () => { - if (timeout.id !== undefined) { - clearTimeout(timeout.id); - timeout.id = undefined; - } - }, - })); - - // Clean up the timeout when the component is unloaded - React.useEffect(() => timeout.clear, [timeout]); - - return [timeout.set, timeout.clear] as const; + return useBrowserTimer(setTimeout, clearTimeout); } From 6bbf319e70065420c37e728aa6ec9f0628c55367 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Thu, 3 Aug 2023 14:32:51 +0200 Subject: [PATCH 056/111] fix: improve type definition to avoid typecast --- .../react-utilities/src/hooks/useMotionPresence.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts index 0eb1654e8bf763..5bb0d2e0074418 100644 --- a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts +++ b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts @@ -33,12 +33,12 @@ type HTMLElementWithStyledMap = TEle computedStyleMap(): StylePropertyMapReadOnly; }; -interface CSSWithNumber { +type CSSWithNumber = typeof CSS & { number(value: number): { value: number; readonly unit: string; }; -} +}; /** * State for useMotionPresence hook. @@ -127,7 +127,7 @@ const hasCSSOMSupport = (node: HTMLElementWithStyledMap) => { * The typecast here is to allow the use of the `number` function that is not yet part of the CSSOM typings. * @see https://www.npmjs.com/package/@types/w3c-css-typed-object-model-level-1 */ - return Boolean(typeof CSS !== 'undefined' && (CSS as unknown as CSSWithNumber).number && node.computedStyleMap); + return Boolean(typeof CSS !== 'undefined' && (CSS as CSSWithNumber).number && node.computedStyleMap); }; /** From 44b58d541787338baba1ba2347cfc59ef2e42a3d Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Thu, 3 Aug 2023 14:39:21 +0200 Subject: [PATCH 057/111] fix: return a blank style declaration in case global "window" do not exist --- .../react-utilities/src/hooks/useMotionPresence.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts index 5bb0d2e0074418..7b03952689d66c 100644 --- a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts +++ b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts @@ -1,4 +1,5 @@ import * as React from 'react'; +import { canUseDOM } from '../ssr/canUseDOM'; import { useAnimationFrame } from './useAnimationFrame'; import { useTimeout } from './useTimeout'; @@ -89,9 +90,15 @@ export type UseMotionPresenceOptions = { * @returns - CSS styles. */ const getElementComputedStyle = (node: HTMLElement): CSSStyleDeclaration => { - const window = node.ownerDocument?.defaultView; + const window = node.ownerDocument.defaultView; - return window!.getComputedStyle(node, null); + if (!window || !canUseDOM()) { + return { + getPropertyValue: (_: string) => '', + } as CSSStyleDeclaration; + } + + return window.getComputedStyle(node, null); }; /** From b3124d6161c15eae95df1ee3592280a07c2cc63e Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Thu, 3 Aug 2023 15:18:35 +0200 Subject: [PATCH 058/111] fix: regenerate API --- .../react-utilities/etc/react-utilities.api.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/react-components/react-utilities/etc/react-utilities.api.md b/packages/react-components/react-utilities/etc/react-utilities.api.md index 2def011e5a6434..ad9f1090de6ff6 100644 --- a/packages/react-components/react-utilities/etc/react-utilities.api.md +++ b/packages/react-components/react-utilities/etc/react-utilities.api.md @@ -282,6 +282,22 @@ export function useIsSSR(): boolean; // @public export function useMergedRefs(...refs: (React_2.Ref | undefined)[]): RefObjectFunction; +// @public +export const useMotionPresence: (present: boolean, options?: UseMotionPresenceOptions) => UseMotionPresenceState; + +// @public +export type UseMotionPresenceOptions = { + animateOnFirstMount?: boolean; +}; + +// @public +export type UseMotionPresenceState = { + ref: React_2.RefCallback; + shouldRender: boolean; + visible: boolean; + motionState: 'entering' | 'exiting' | 'resting' | 'unmounted'; +}; + // @internal (undocumented) export type UseOnClickOrScrollOutsideOptions = { element: Document | undefined; @@ -307,7 +323,7 @@ export function useScrollbarWidth(options: UseScrollbarWidthOptions): number | u export function useSelection(params: SelectionHookParams): readonly [Set, SelectionMethods]; // @internal -export function useTimeout(): readonly [(fn: () => void, delay: number) => void, () => void]; +export function useTimeout(): readonly [(fn: () => void, delay?: number | undefined) => number, () => void]; // (No @packageDocumentation comment for this package) From caef0bdedefccc75f87b1600e7f29de133b934e5 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Thu, 3 Aug 2023 15:20:33 +0200 Subject: [PATCH 059/111] fix: regenerate API --- .../react-utilities/etc/react-utilities.api.md | 2 +- .../react-utilities/src/hooks/useMotionPresence.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-components/react-utilities/etc/react-utilities.api.md b/packages/react-components/react-utilities/etc/react-utilities.api.md index ad9f1090de6ff6..96e23c5b874c4d 100644 --- a/packages/react-components/react-utilities/etc/react-utilities.api.md +++ b/packages/react-components/react-utilities/etc/react-utilities.api.md @@ -292,7 +292,7 @@ export type UseMotionPresenceOptions = { // @public export type UseMotionPresenceState = { - ref: React_2.RefCallback; + ref: React_2.Ref; shouldRender: boolean; visible: boolean; motionState: 'entering' | 'exiting' | 'resting' | 'unmounted'; diff --git a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts index 7b03952689d66c..a75cbb049ef836 100644 --- a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts +++ b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts @@ -48,7 +48,7 @@ export type UseMotionPresenceState = { /** * Ref to the element. */ - ref: React.RefCallback; + ref: React.Ref; /** * Whether the element should be rendered in the DOM. @@ -283,7 +283,7 @@ export const useMotionPresence = ( [clearAnimationTimeout, currentElement, setAnimationTimeout], ); - const ref: React.RefCallback> = React.useCallback(node => { + const ref: React.Ref> = React.useCallback(node => { if (!node) { return; } From 8fa8f4afcba989409d54ed7c99c3866a660d0b7a Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Thu, 3 Aug 2023 17:23:55 +0200 Subject: [PATCH 060/111] revert: use RefCallback instead of Ref --- .../react-utilities/src/hooks/useMotionPresence.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts index a75cbb049ef836..7b03952689d66c 100644 --- a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts +++ b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts @@ -48,7 +48,7 @@ export type UseMotionPresenceState = { /** * Ref to the element. */ - ref: React.Ref; + ref: React.RefCallback; /** * Whether the element should be rendered in the DOM. @@ -283,7 +283,7 @@ export const useMotionPresence = ( [clearAnimationTimeout, currentElement, setAnimationTimeout], ); - const ref: React.Ref> = React.useCallback(node => { + const ref: React.RefCallback> = React.useCallback(node => { if (!node) { return; } From 45ac91f46277220d7aaa0a180f6efbf695bd2192 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Thu, 3 Aug 2023 17:25:17 +0200 Subject: [PATCH 061/111] fix: regenerate API --- .../react-utilities/etc/react-utilities.api.md | 2 +- .../react-utilities/src/hooks/useMotionPresence.ts | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/react-components/react-utilities/etc/react-utilities.api.md b/packages/react-components/react-utilities/etc/react-utilities.api.md index 96e23c5b874c4d..ad9f1090de6ff6 100644 --- a/packages/react-components/react-utilities/etc/react-utilities.api.md +++ b/packages/react-components/react-utilities/etc/react-utilities.api.md @@ -292,7 +292,7 @@ export type UseMotionPresenceOptions = { // @public export type UseMotionPresenceState = { - ref: React_2.Ref; + ref: React_2.RefCallback; shouldRender: boolean; visible: boolean; motionState: 'entering' | 'exiting' | 'resting' | 'unmounted'; diff --git a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts index 7b03952689d66c..ccd6668619dde1 100644 --- a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts +++ b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts @@ -42,6 +42,8 @@ type CSSWithNumber = typeof CSS & { }; /** + * @internal + * * State for useMotionPresence hook. */ export type UseMotionPresenceState = { @@ -73,6 +75,8 @@ export type UseMotionPresenceState = { }; /** + * @internal + * * Options for useMotionPresence hook. */ export type UseMotionPresenceOptions = { @@ -85,6 +89,8 @@ export type UseMotionPresenceOptions = { }; /** + * @internal + * * Returns CSS styles of the given node. * @param node - DOM node. * @returns - CSS styles. From cceb8322baa79200de0ad1ddbd67b4c508d08644 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Thu, 3 Aug 2023 18:05:49 +0200 Subject: [PATCH 062/111] fix: regenerate API --- .../react-utilities/etc/react-utilities.api.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/react-components/react-utilities/etc/react-utilities.api.md b/packages/react-components/react-utilities/etc/react-utilities.api.md index ad9f1090de6ff6..86a785bca2c929 100644 --- a/packages/react-components/react-utilities/etc/react-utilities.api.md +++ b/packages/react-components/react-utilities/etc/react-utilities.api.md @@ -282,15 +282,18 @@ export function useIsSSR(): boolean; // @public export function useMergedRefs(...refs: (React_2.Ref | undefined)[]): RefObjectFunction; +// Warning: (ae-incompatible-release-tags) The symbol "useMotionPresence" is marked as @public, but its signature references "UseMotionPresenceOptions" which is marked as @internal +// Warning: (ae-incompatible-release-tags) The symbol "useMotionPresence" is marked as @public, but its signature references "UseMotionPresenceState" which is marked as @internal +// // @public export const useMotionPresence: (present: boolean, options?: UseMotionPresenceOptions) => UseMotionPresenceState; -// @public +// @internal export type UseMotionPresenceOptions = { animateOnFirstMount?: boolean; }; -// @public +// @internal export type UseMotionPresenceState = { ref: React_2.RefCallback; shouldRender: boolean; From 2e5f04bc85e56c8dcceb09411795338bebaf6ce2 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Thu, 3 Aug 2023 13:47:14 +0200 Subject: [PATCH 063/111] docs: improve examples --- .../stories/Drawer/DrawerCustomAnimation.stories.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomAnimation.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomAnimation.stories.tsx index 8f5bda1cbe520b..b742ac5bb2a7d5 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomAnimation.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomAnimation.stories.tsx @@ -24,7 +24,7 @@ const useStyles = makeStyles({ animationDuration: '2s', animationTimingFunction: tokens.curveDecelerateMid, animationName: { - '0%': hiddenKeyframe, + from: hiddenKeyframe, to: visibleKeyframe, }, }, @@ -33,8 +33,8 @@ const useStyles = makeStyles({ animationDuration: '1s', animationTimingFunction: tokens.curveAccelerateMin, animationName: { - '0%': visibleKeyframe, - '100%': hiddenKeyframe, + from: visibleKeyframe, + to: hiddenKeyframe, }, }, }); From 6061b95ad0b3856ae96d94271fa98a3ce82bc38e Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Mon, 7 Aug 2023 18:19:42 +0200 Subject: [PATCH 064/111] feat: simplify logic of useMotionPresence --- .../DrawerInline/renderDrawerInline.tsx | 2 +- .../DrawerInline/useDrawerInline.ts | 5 +- .../useDrawerInlineStyles.styles.ts | 4 +- .../DrawerOverlay/DrawerOverlay.types.ts | 4 +- .../DrawerOverlay/renderDrawerOverlay.tsx | 2 +- .../DrawerOverlay/useDrawerOverlay.ts | 66 +++++++++++-------- .../useDrawerOverlayStyles.styles.ts | 4 +- .../react-drawer/src/util/DrawerBase.types.ts | 4 +- .../src/util/useDrawerBaseStyles.styles.ts | 2 +- .../Drawer/DrawerCustomTransition.stories.tsx | 4 +- .../Drawer/DrawerResizable.stories.tsx | 27 +++++--- .../src/hooks/useMotionPresence.ts | 43 +++++++----- 12 files changed, 95 insertions(+), 72 deletions(-) diff --git a/packages/react-components/react-drawer/src/components/DrawerInline/renderDrawerInline.tsx b/packages/react-components/react-drawer/src/components/DrawerInline/renderDrawerInline.tsx index 3b20eb1622542c..4e3dfca5ce42c8 100644 --- a/packages/react-components/react-drawer/src/components/DrawerInline/renderDrawerInline.tsx +++ b/packages/react-components/react-drawer/src/components/DrawerInline/renderDrawerInline.tsx @@ -11,7 +11,7 @@ import type { DrawerInlineState, DrawerInlineSlots } from './DrawerInline.types' export const renderDrawerInline_unstable = (state: DrawerInlineState) => { assertSlots(state); - if (!state.shouldRender) { + if (state.motionState === 'unmounted') { return null; } diff --git a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts index 48a0bfa3c42c7f..7cfececc26f17c 100644 --- a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts +++ b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts @@ -31,7 +31,7 @@ export const useDrawerInline_unstable = ( initialState: false, }); - const { ref: drawerRef, shouldRender, visible, motionState } = useMotionPresence(open); + const { ref: drawerRef, active, motionState } = useMotionPresence(open); return { components: { @@ -50,8 +50,7 @@ export const useDrawerInline_unstable = ( position, open, separator, - shouldRender, - visible, + active, motionState, }; }; diff --git a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts index 1de0974153490e..57e911b756f831 100644 --- a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts +++ b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts @@ -59,12 +59,12 @@ export const useDrawerInlineStyles_unstable = (state: DrawerInlineState): Drawer }, [state.position, state.separator, styles.separatorRight, styles.separatorLeft]); const hiddenClass = React.useCallback(() => { - if (state.visible) { + if (state.active) { return undefined; } return state.position === 'left' ? styles.hiddenLeft : styles.hiddenRight; - }, [state.position, state.visible, styles.hiddenLeft, styles.hiddenRight]); + }, [state.position, state.active, styles.hiddenLeft, styles.hiddenRight]); state.root.className = mergeClasses( drawerInlineClassNames.root, diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/DrawerOverlay.types.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/DrawerOverlay.types.ts index 34ad2d3db1dc96..39f43ca108ffd0 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/DrawerOverlay.types.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/DrawerOverlay.types.ts @@ -1,5 +1,5 @@ import { DialogProps, DialogSurfaceProps, DialogSurfaceSlots } from '@fluentui/react-dialog'; -import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities'; +import type { ComponentProps, ComponentState, Slot, UseMotionPresenceState } from '@fluentui/react-utilities'; import { DrawerBaseProps, DrawerBaseState } from '../../util/DrawerBase.types'; export type DrawerOverlaySlots = DialogSurfaceSlots & { @@ -20,5 +20,5 @@ export type DrawerOverlayState = ComponentState & DrawerBaseProps & DrawerBaseState & { dialog: DialogProps; - backdropVisible: boolean; + backdropActive: UseMotionPresenceState['active']; }; diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/renderDrawerOverlay.tsx b/packages/react-components/react-drawer/src/components/DrawerOverlay/renderDrawerOverlay.tsx index 1045af14984936..bb00caed90b4cd 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/renderDrawerOverlay.tsx +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/renderDrawerOverlay.tsx @@ -12,7 +12,7 @@ import { Dialog } from '@fluentui/react-dialog'; export const renderDrawerOverlay_unstable = (state: DrawerOverlayState) => { assertSlots(state); - if (!state.shouldRender) { + if (state.motionState === 'unmounted') { return null; } diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts index cbc0e66b9566af..258f0d67fb16bd 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts @@ -20,48 +20,56 @@ export const useDrawerOverlay_unstable = ( const { open, defaultOpen, size, position } = getDefaultDrawerProps(props); const { modalType = 'modal', inertTrapFocus, onOpenChange } = props; - const { ref: drawerRef, shouldRender, visible, motionState } = useMotionPresence(open); - const backdropPresence = useMotionPresence(open); + const { ref: drawerRef, active, motionState } = useMotionPresence(open); + const backdropPresence = useMotionPresence(open); const hasCustomBackdrop = modalType !== 'non-modal' && props.backdrop !== null; - return { - components: { - root: DialogSurface, - backdrop: 'div', + const root = slot.always( + getNativeElementProps('div', { + ...props, + ref: useMergedRefs(ref, drawerRef), + }), + { + elementType: DialogSurface, + defaultProps: { + backdrop: slot.optional(props.backdrop, { + elementType: 'div', + renderByDefault: hasCustomBackdrop, + defaultProps: { + ref: backdropPresence.ref, + }, + }), + }, }, + ); - root: slot.always( - getNativeElementProps('div', { - ...props, - ref: useMergedRefs(ref, drawerRef), - }), - { - elementType: DialogSurface, - defaultProps: { - backdrop: slot.optional(props.backdrop, { - elementType: 'div', - renderByDefault: hasCustomBackdrop, - defaultProps: { - ref: backdropPresence.ref, - }, - }), - }, - }, - ), - dialog: { - open: shouldRender, + const dialog = slot.always( + { + open: motionState !== 'unmounted', defaultOpen, onOpenChange, inertTrapFocus, modalType, } as DialogProps, + { + elementType: 'div', + }, + ); + + return { + components: { + root: DialogSurface, + backdrop: 'div', + }, + + root, + dialog, size, position, - shouldRender, motionState, - visible, - backdropVisible: backdropPresence.visible, + active, + backdropActive: backdropPresence.active, }; }; diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts index eb6a6cad349c00..988fbdafa61cff 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts @@ -78,7 +78,7 @@ export const useDrawerOverlayStyles_unstable = (state: DrawerOverlayState): Draw getDrawerBaseClassNames(state, baseStyles), state.position && rootStyles[state.position], state.size && durationStyles[state.size], - state.visible && rootStyles.visible, + state.active && rootStyles.visible, state.root.className, ); @@ -86,7 +86,7 @@ export const useDrawerOverlayStyles_unstable = (state: DrawerOverlayState): Draw backdrop.className = mergeClasses( drawerOverlayClassNames.backdrop, backdropStyles.backdrop, - state.backdropVisible && backdropStyles.backdropVisible, + state.backdropActive && backdropStyles.backdropVisible, state.size && durationStyles[state.size], backdrop.className, ); diff --git a/packages/react-components/react-drawer/src/util/DrawerBase.types.ts b/packages/react-components/react-drawer/src/util/DrawerBase.types.ts index 8b853c3905719a..7270353dbe6f7a 100644 --- a/packages/react-components/react-drawer/src/util/DrawerBase.types.ts +++ b/packages/react-components/react-drawer/src/util/DrawerBase.types.ts @@ -35,6 +35,4 @@ export type DrawerBaseProps = { defaultOpen?: boolean; }; -export type DrawerBaseState = Pick, 'shouldRender' | 'visible'> & { - motionState: UseMotionPresenceState['state']; -}; +export type DrawerBaseState = Required, 'active' | 'motionState'>>; diff --git a/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts b/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts index d02dbddcbd2053..412cbaaf4774db 100644 --- a/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts +++ b/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts @@ -86,7 +86,7 @@ export const useDrawerDurationStyles = makeStyles({ }); export const getDrawerBaseClassNames = ( - { position, size, motionState }: Partial, + { position, size, motionState }: DrawerBaseState & DrawerBaseProps, baseStyles: ReturnType, ) => { return mergeClasses( diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx index 09ef5d55a90821..87feeb673958c9 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx @@ -27,7 +27,7 @@ export const CustomTransition = () => { const styles = useStyles(); const [isOpen, setIsOpen] = React.useState(false); - const { ref, motionState, visible } = useMotionPresence(isOpen); + const { ref, motionState, active } = useMotionPresence(isOpen); return (
@@ -35,7 +35,7 @@ export const CustomTransition = () => { ref={ref} className={mergeClasses( styles.drawer, - visible && styles.drawerVisible, + active && styles.drawerVisible, motionState === 'exiting' && styles.drawerExiting, )} open={isOpen} diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerResizable.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerResizable.stories.tsx index 0235063cab2def..e489bd3d98adc5 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerResizable.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerResizable.stories.tsx @@ -12,7 +12,13 @@ const useStyles = makeStyles({ backgroundColor: '#fff', }, - drawerResizer: { + drawer: { + willChange: 'width', + transitionProperty: 'width', + transitionDuration: '16.666ms', // 60fps + }, + + resizer: { ...shorthands.borderRight('1px', 'solid', tokens.colorNeutralBackground5), width: '8px', @@ -28,7 +34,7 @@ const useStyles = makeStyles({ }, }, - drawerResizing: { + resizerActive: { borderRightWidth: '4px', borderRightColor: tokens.colorNeutralBackground5Pressed, }, @@ -42,6 +48,7 @@ const useStyles = makeStyles({ export const Resizable = () => { const styles = useStyles(); + const animationFrame = React.useRef(0); const sidebarRef = React.useRef(null); const [isResizing, setIsResizing] = React.useState(false); const [sidebarWidth, setSidebarWidth] = React.useState(320); @@ -51,7 +58,7 @@ export const Resizable = () => { const resize = React.useCallback( ({ clientX }) => { - requestAnimationFrame(() => { + animationFrame.current = requestAnimationFrame(() => { if (isResizing && sidebarRef.current) { setSidebarWidth(clientX - sidebarRef.current.getBoundingClientRect().left); } @@ -65,6 +72,7 @@ export const Resizable = () => { window.addEventListener('mouseup', stopResizing); return () => { + cancelAnimationFrame(animationFrame.current); window.removeEventListener('mousemove', resize); window.removeEventListener('mouseup', stopResizing); }; @@ -72,11 +80,14 @@ export const Resizable = () => { return (
- e.preventDefault()}> -
+ e.preventDefault()} + > +
Default Drawer diff --git a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts index ccd6668619dde1..335cb013c24f7e 100644 --- a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts +++ b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts @@ -49,27 +49,37 @@ type CSSWithNumber = typeof CSS & { export type UseMotionPresenceState = { /** * Ref to the element. + * + * @example + * const { ref } = useMotionPresence(isOpen); + * + *
*/ ref: React.RefCallback; /** - * Whether the element should be rendered in the DOM. - * This should be used to conditionally render the element. - */ - shouldRender: boolean; - - /** - * Whether the element is currently visible in the DOM. + * Whether the element is currently active in the DOM. + * Useful to apply CSS transitions only when the element is active. + * + * @example + * const { active, ref } = useMotionPresence(isOpen); + * + *
*/ - visible: boolean; + active: boolean; /** * Current state of the element. * - * - `entering` - The element is entering the DOM. - * - `exiting` - The element is exiting the DOM. - * - `resting` - The element is currently not animating. This is the final and initial state of the element. - * - `unmounted` - The element is not rendered in the DOM. + * - `entering` - The element is performing enter animation. + * - `exiting` - The element is performing exit animation. + * - `resting` - The element is currently not animating, but rendered on screen. + * - `unmounted` - The element is not rendered or can be removed from the DOM. + * + * @example + * const { motionState, ref } = useMotionPresence(isOpen); + * + *
*/ motionState: 'entering' | 'exiting' | 'resting' | 'unmounted'; }; @@ -248,9 +258,8 @@ export const useMotionPresence = ( const { animateOnFirstMount } = { animateOnFirstMount: false, ...options }; const [state, setState] = React.useState, 'ref'>>({ - shouldRender: present, motionState: present ? 'resting' : 'unmounted', - visible: false, + active: false, }); const [currentElement, setCurrentElement] = React.useState | null>(null); @@ -300,8 +309,7 @@ export const useMotionPresence = ( React.useEffect(() => { if (present) { setState({ - shouldRender: true, - visible: skipAnimationOnFirstRender.current ? true : false, + active: skipAnimationOnFirstRender.current ? true : false, motionState: 'resting', }); } @@ -324,7 +332,7 @@ export const useMotionPresence = ( return { ...prevState, motionState, - visible: present, + active: present, }; }); }); @@ -337,7 +345,6 @@ export const useMotionPresence = ( setState(prevState => ({ ...prevState, motionState: present ? 'resting' : 'unmounted', - shouldRender: present, })); }); From d3cbdede203b812b60745d57dacf0795334b54fd Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Mon, 7 Aug 2023 18:21:54 +0200 Subject: [PATCH 065/111] feat: simplify API by removing redundant prop --- .../src/hooks/useMotionPresence.test.ts | 53 +++++++------------ .../src/hooks/useMotionPresence.ts | 43 ++++++++------- 2 files changed, 44 insertions(+), 52 deletions(-) diff --git a/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts b/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts index f018b6c8ae8673..6d0ebc0dff9095 100644 --- a/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts +++ b/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts @@ -41,8 +41,7 @@ describe('useMotionPresence', () => { expect(typeof result.current.ref).toBe('function'); expect(result.current.motionState).toBe('unmounted'); - expect(result.current.shouldRender).toBe(false); - expect(result.current.visible).toBe(false); + expect(result.current.active).toBe(false); }); }); @@ -52,8 +51,7 @@ describe('useMotionPresence', () => { expect(typeof result.current.ref).toBe('function'); expect(result.current.motionState).toBe('resting'); - expect(result.current.shouldRender).toBe(true); - expect(result.current.visible).toBe(true); + expect(result.current.active).toBe(true); }); it('should not change values after timeout ', () => { @@ -62,8 +60,7 @@ describe('useMotionPresence', () => { const assertSameValues = () => { expect(typeof result.current.ref).toBe('function'); expect(result.current.motionState).toBe('resting'); - expect(result.current.shouldRender).toBe(true); - expect(result.current.visible).toBe(true); + expect(result.current.active).toBe(true); }; assertSameValues(); @@ -72,17 +69,16 @@ describe('useMotionPresence', () => { assertSameValues(); }); - it('should change visible to true when animateOnFirstMount is true', () => { + it('should change active to true when animateOnFirstMount is true', () => { const { result } = renderHookWithRef(true, { animateOnFirstMount: true }); expect(typeof result.current.ref).toBe('function'); expect(result.current.motionState).toBe('resting'); - expect(result.current.shouldRender).toBe(true); - expect(result.current.visible).toBe(false); + expect(result.current.active).toBe(false); act(() => jest.advanceTimersToNextTimer()); - expect(result.current.visible).toBe(true); + expect(result.current.active).toBe(true); }); }); @@ -92,23 +88,21 @@ describe('useMotionPresence', () => { expect(typeof result.current.ref).toBe('function'); expect(result.current.motionState).toBe('unmounted'); - expect(result.current.shouldRender).toBe(false); - expect(result.current.visible).toBe(false); + expect(result.current.active).toBe(false); rerender({ presence: true }); - expect(result.current.shouldRender).toBe(true); expect(result.current.motionState).toBe('resting'); // double requestAnimationFrame act(() => jest.advanceTimersToNextTimer()); - expect(result.current.visible).toBe(true); + expect(result.current.active).toBe(true); rerender({ presence: false }); act(() => jest.advanceTimersToNextTimer()); expect(result.current.motionState).toBe('exiting'); - expect(result.current.visible).toBe(false); + expect(result.current.active).toBe(false); act(() => { // requestAnimationFrame @@ -119,8 +113,7 @@ describe('useMotionPresence', () => { }); expect(result.current.motionState).toBe('unmounted'); - expect(result.current.shouldRender).toBe(false); - expect(result.current.visible).toBe(false); + expect(result.current.active).toBe(false); }); it('should toggle values starting with true', () => { @@ -128,8 +121,7 @@ describe('useMotionPresence', () => { expect(typeof result.current.ref).toBe('function'); expect(result.current.motionState).toBe('resting'); - expect(result.current.shouldRender).toBe(true); - expect(result.current.visible).toBe(true); + expect(result.current.active).toBe(true); rerender({ presence: false }); @@ -137,7 +129,7 @@ describe('useMotionPresence', () => { act(() => jest.advanceTimersToNextTimer()); expect(result.current.motionState).toBe('exiting'); - expect(result.current.visible).toBe(false); + expect(result.current.active).toBe(false); act(() => { // requestAnimationFrame @@ -148,8 +140,7 @@ describe('useMotionPresence', () => { }); expect(result.current.motionState).toBe('unmounted'); - expect(result.current.shouldRender).toBe(false); - expect(result.current.visible).toBe(false); + expect(result.current.active).toBe(false); }); }); @@ -170,16 +161,14 @@ describe('useMotionPresence', () => { expect(typeof result.current.ref).toBe('function'); expect(result.current.motionState).toBe('unmounted'); - expect(result.current.shouldRender).toBe(false); - expect(result.current.visible).toBe(false); + expect(result.current.active).toBe(false); rerender({ presence: true }); - expect(result.current.shouldRender).toBe(true); expect(result.current.motionState).toBe('resting'); // requestAnimationFrame act(() => jest.advanceTimersToNextTimer()); - expect(result.current.visible).toBe(true); + expect(result.current.active).toBe(true); expect(result.current.motionState).toBe('entering'); // timeout act(() => jest.advanceTimersToNextTimer()); @@ -188,7 +177,7 @@ describe('useMotionPresence', () => { rerender({ presence: false }); act(() => jest.advanceTimersToNextTimer()); - expect(result.current.visible).toBe(false); + expect(result.current.active).toBe(false); expect(result.current.motionState).toBe('exiting'); // requestAnimationFrame @@ -196,7 +185,6 @@ describe('useMotionPresence', () => { // timeout act(() => jest.advanceTimersToNextTimer()); expect(result.current.motionState).toBe('unmounted'); - expect(result.current.shouldRender).toBe(false); }); }); @@ -215,17 +203,15 @@ describe('useMotionPresence', () => { expect(typeof result.current.ref).toBe('function'); expect(result.current.motionState).toBe('unmounted'); - expect(result.current.shouldRender).toBe(false); - expect(result.current.visible).toBe(false); + expect(result.current.active).toBe(false); rerender({ presence: true }); - expect(result.current.shouldRender).toBe(true); expect(result.current.motionState).toBe('resting'); // requestAnimationFrame act(() => jest.advanceTimersToNextTimer()); act(() => jest.advanceTimersToNextTimer()); - expect(result.current.visible).toBe(true); + expect(result.current.active).toBe(true); // timeout act(() => jest.advanceTimersToNextTimer()); expect(result.current.motionState).toBe('resting'); @@ -234,14 +220,13 @@ describe('useMotionPresence', () => { act(() => jest.advanceTimersToNextTimer()); act(() => jest.advanceTimersToNextTimer()); - expect(result.current.visible).toBe(false); + expect(result.current.active).toBe(false); // requestAnimationFrame act(() => jest.advanceTimersToNextTimer()); // timeout act(() => jest.advanceTimersToNextTimer()); expect(result.current.motionState).toBe('unmounted'); - expect(result.current.shouldRender).toBe(false); }); }); }); diff --git a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts index ccd6668619dde1..335cb013c24f7e 100644 --- a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts +++ b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts @@ -49,27 +49,37 @@ type CSSWithNumber = typeof CSS & { export type UseMotionPresenceState = { /** * Ref to the element. + * + * @example + * const { ref } = useMotionPresence(isOpen); + * + *
*/ ref: React.RefCallback; /** - * Whether the element should be rendered in the DOM. - * This should be used to conditionally render the element. - */ - shouldRender: boolean; - - /** - * Whether the element is currently visible in the DOM. + * Whether the element is currently active in the DOM. + * Useful to apply CSS transitions only when the element is active. + * + * @example + * const { active, ref } = useMotionPresence(isOpen); + * + *
*/ - visible: boolean; + active: boolean; /** * Current state of the element. * - * - `entering` - The element is entering the DOM. - * - `exiting` - The element is exiting the DOM. - * - `resting` - The element is currently not animating. This is the final and initial state of the element. - * - `unmounted` - The element is not rendered in the DOM. + * - `entering` - The element is performing enter animation. + * - `exiting` - The element is performing exit animation. + * - `resting` - The element is currently not animating, but rendered on screen. + * - `unmounted` - The element is not rendered or can be removed from the DOM. + * + * @example + * const { motionState, ref } = useMotionPresence(isOpen); + * + *
*/ motionState: 'entering' | 'exiting' | 'resting' | 'unmounted'; }; @@ -248,9 +258,8 @@ export const useMotionPresence = ( const { animateOnFirstMount } = { animateOnFirstMount: false, ...options }; const [state, setState] = React.useState, 'ref'>>({ - shouldRender: present, motionState: present ? 'resting' : 'unmounted', - visible: false, + active: false, }); const [currentElement, setCurrentElement] = React.useState | null>(null); @@ -300,8 +309,7 @@ export const useMotionPresence = ( React.useEffect(() => { if (present) { setState({ - shouldRender: true, - visible: skipAnimationOnFirstRender.current ? true : false, + active: skipAnimationOnFirstRender.current ? true : false, motionState: 'resting', }); } @@ -324,7 +332,7 @@ export const useMotionPresence = ( return { ...prevState, motionState, - visible: present, + active: present, }; }); }); @@ -337,7 +345,6 @@ export const useMotionPresence = ( setState(prevState => ({ ...prevState, motionState: present ? 'resting' : 'unmounted', - shouldRender: present, })); }); From 28fecaea2fdcbb1dffd2e94a51f9b0b821f0f204 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Thu, 17 Aug 2023 17:52:59 +0200 Subject: [PATCH 066/111] feat: implement useMotion hook instead of useMotionPresence --- .github/CODEOWNERS | 1 + .../react-drawer/package.json | 1 + .../DrawerInline/DrawerInline.types.ts | 4 +- .../DrawerInline/useDrawerInline.ts | 31 ++- .../useDrawerInlineStyles.styles.ts | 17 +- .../DrawerOverlay/DrawerOverlay.types.ts | 7 +- .../DrawerOverlay/useDrawerOverlay.ts | 36 ++- .../react-drawer/src/util/DrawerBase.types.ts | 12 +- ...rProps.ts => useBaseDrawerDefaultProps.ts} | 2 +- .../Drawer/DrawerCustomAnimation.stories.tsx | 28 +- .../DrawerCustomInlineAnimation.stories.tsx | 142 ++++++++++ .../Drawer/DrawerCustomTransition.stories.tsx | 13 +- .../stories/Drawer/DrawerDefault.stories.tsx | 6 +- .../stories/Drawer/index.stories.tsx | 1 + .../react-motion-preview/.babelrc.json | 4 + .../react-motion-preview/.eslintrc.json | 4 + .../react-motion-preview/.npmignore | 38 +++ .../react-motion-preview/.storybook/main.js | 14 + .../.storybook/preview.js | 7 + .../.storybook/tsconfig.json | 10 + .../react-motion-preview/.swcrc | 30 +++ .../react-motion-preview/LICENSE | 15 ++ .../react-motion-preview/README.md | 5 + .../config/api-extractor.json | 4 + .../react-motion-preview/config/tests.js | 1 + .../etc/react-motion-preview.api.md | 28 ++ .../react-motion-preview/jest.config.js | 21 ++ .../react-motion-preview/just.config.ts | 5 + .../react-motion-preview/package.json | 61 +++++ .../react-motion-preview/project.json | 8 + .../react-motion-preview/src/hooks/index.ts | 1 + .../src/hooks/useMotion.test.ts | 237 +++++++++++++++++ .../src/hooks/useMotion.ts} | 222 ++++++++++++---- .../react-motion-preview/src/index.ts | 2 + .../useMotion/UseMotionBestPractices.md | 5 + .../useMotion/UseMotionDefault.stories.tsx | 60 +++++ .../stories/useMotion/UseMotionDescription.md | 0 .../stories/useMotion/index.stories.tsx | 15 ++ .../react-motion-preview/tsconfig.json | 25 ++ .../react-motion-preview/tsconfig.lib.json | 22 ++ .../react-motion-preview/tsconfig.spec.json | 17 ++ .../etc/react-utilities.api.md | 23 +- .../react-utilities/src/hooks/index.ts | 2 +- .../src/hooks/useMergedRefs.ts | 2 +- .../src/hooks/useMotionPresence.test.ts | 247 ------------------ .../react-utilities/src/index.ts | 10 +- tsconfig.base.all.json | 1 + tsconfig.base.json | 1 + 48 files changed, 1067 insertions(+), 381 deletions(-) rename packages/react-components/react-drawer/src/util/{getDefaultDrawerProps.ts => useBaseDrawerDefaultProps.ts} (75%) create mode 100644 packages/react-components/react-drawer/stories/Drawer/DrawerCustomInlineAnimation.stories.tsx create mode 100644 packages/react-components/react-motion-preview/.babelrc.json create mode 100644 packages/react-components/react-motion-preview/.eslintrc.json create mode 100644 packages/react-components/react-motion-preview/.npmignore create mode 100644 packages/react-components/react-motion-preview/.storybook/main.js create mode 100644 packages/react-components/react-motion-preview/.storybook/preview.js create mode 100644 packages/react-components/react-motion-preview/.storybook/tsconfig.json create mode 100644 packages/react-components/react-motion-preview/.swcrc create mode 100644 packages/react-components/react-motion-preview/LICENSE create mode 100644 packages/react-components/react-motion-preview/README.md create mode 100644 packages/react-components/react-motion-preview/config/api-extractor.json create mode 100644 packages/react-components/react-motion-preview/config/tests.js create mode 100644 packages/react-components/react-motion-preview/etc/react-motion-preview.api.md create mode 100644 packages/react-components/react-motion-preview/jest.config.js create mode 100644 packages/react-components/react-motion-preview/just.config.ts create mode 100644 packages/react-components/react-motion-preview/package.json create mode 100644 packages/react-components/react-motion-preview/project.json create mode 100644 packages/react-components/react-motion-preview/src/hooks/index.ts create mode 100644 packages/react-components/react-motion-preview/src/hooks/useMotion.test.ts rename packages/react-components/{react-utilities/src/hooks/useMotionPresence.ts => react-motion-preview/src/hooks/useMotion.ts} (55%) create mode 100644 packages/react-components/react-motion-preview/src/index.ts create mode 100644 packages/react-components/react-motion-preview/stories/useMotion/UseMotionBestPractices.md create mode 100644 packages/react-components/react-motion-preview/stories/useMotion/UseMotionDefault.stories.tsx create mode 100644 packages/react-components/react-motion-preview/stories/useMotion/UseMotionDescription.md create mode 100644 packages/react-components/react-motion-preview/stories/useMotion/index.stories.tsx create mode 100644 packages/react-components/react-motion-preview/tsconfig.json create mode 100644 packages/react-components/react-motion-preview/tsconfig.lib.json create mode 100644 packages/react-components/react-motion-preview/tsconfig.spec.json delete mode 100644 packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d57e42c26af7ef..bc0bad4db4de0d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -239,6 +239,7 @@ packages/react-components/react-jsx-runtime @microsoft/teams-prg packages/react-components/react-toast @microsoft/teams-prg packages/react-components/react-search-preview @microsoft/cxe-coastal packages/react-components/react-colorpicker-compat @microsoft/cxe-red @sopranopillow +packages/react-components/react-motion-preview @microsoft/cxe-prg # <%= NX-CODEOWNER-PLACEHOLDER %> ## Components diff --git a/packages/react-components/react-drawer/package.json b/packages/react-components/react-drawer/package.json index 11cee668a5ea37..3aa35aa9981f8a 100644 --- a/packages/react-components/react-drawer/package.json +++ b/packages/react-components/react-drawer/package.json @@ -39,6 +39,7 @@ "@fluentui/react-jsx-runtime": "9.0.0-alpha.13", "@fluentui/react-shared-contexts": "^9.7.1", "@fluentui/react-theme": "^9.1.10", + "@fluentui/react-motion-preview": "^0.0.0", "@fluentui/react-utilities": "^9.11.0", "@griffel/react": "^1.5.7", "@swc/helpers": "^0.4.14" diff --git a/packages/react-components/react-drawer/src/components/DrawerInline/DrawerInline.types.ts b/packages/react-components/react-drawer/src/components/DrawerInline/DrawerInline.types.ts index 193cc28df728b3..9a53ff08e3aa7e 100644 --- a/packages/react-components/react-drawer/src/components/DrawerInline/DrawerInline.types.ts +++ b/packages/react-components/react-drawer/src/components/DrawerInline/DrawerInline.types.ts @@ -21,4 +21,6 @@ export type DrawerInlineProps = ComponentProps & /** * State used in rendering DrawerInline */ -export type DrawerInlineState = ComponentState & DrawerInlineProps & DrawerBaseState; +export type DrawerInlineState = ComponentState & + Omit & + DrawerBaseState; diff --git a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts index 7cfececc26f17c..8bcc2cff8aa3ab 100644 --- a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts +++ b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts @@ -1,13 +1,8 @@ import * as React from 'react'; -import { - getNativeElementProps, - useControllableState, - useMergedRefs, - useMotionPresence, - slot, -} from '@fluentui/react-utilities'; +import { getNativeElementProps, useControllableState, slot } from '@fluentui/react-utilities'; +import { useMotion } from '@fluentui/react-motion-preview'; import type { DrawerInlineProps, DrawerInlineState } from './DrawerInline.types'; -import { getDefaultDrawerProps } from '../../util/getDefaultDrawerProps'; +import { useBaseDrawerDefaultProps } from '../../util/useBaseDrawerDefaultProps'; /** * Create the state required to render DrawerInline. @@ -22,16 +17,21 @@ export const useDrawerInline_unstable = ( props: DrawerInlineProps, ref: React.Ref, ): DrawerInlineState => { - const { open: initialOpen, defaultOpen, size, position } = getDefaultDrawerProps(props); - const { separator = false } = props; + const { open: innerOpen, defaultOpen, size, position } = useBaseDrawerDefaultProps(props); + const { separator = false, motion: motionProp } = props; const [open] = useControllableState({ - state: initialOpen, + state: innerOpen, defaultState: defaultOpen, initialState: false, }); - const { ref: drawerRef, active, motionState } = useMotionPresence(open); + const motion = useMotion( + motionProp || { + presence: open, + ref, + }, + ); return { components: { @@ -40,17 +40,16 @@ export const useDrawerInline_unstable = ( root: slot.always( getNativeElementProps('div', { - ref: useMergedRefs(ref, drawerRef), + ref: motion.ref, ...props, }), { elementType: 'div' }, ), + active: motion.active, + motionState: motion.state, size, position, - open, separator, - active, - motionState, }; }; diff --git a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts index 57e911b756f831..49e0fbf580e929 100644 --- a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts +++ b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts @@ -20,9 +20,9 @@ export const drawerInlineClassNames: SlotClassNames = { const useDrawerRootStyles = makeStyles({ root: { position: 'relative', - transform: 'translateZ(0)', - transitionProperty: 'margin', - willChange: 'margin', + opacity: 1, + transitionProperty: 'opacity, transform', + willChange: 'opacity, transform', }, /* Separator */ @@ -34,11 +34,14 @@ const useDrawerRootStyles = makeStyles({ }, /* Hidden */ + hidden: { + opacity: 0, + }, hiddenLeft: { - marginLeft: `calc(var(${drawerCSSVars.drawerSizeVar}) * -1)`, + transform: `translateX(calc(var(${drawerCSSVars.drawerSizeVar}) * -1))`, }, hiddenRight: { - marginRight: `calc(var(${drawerCSSVars.drawerSizeVar}) * -1)`, + transform: `translateX(calc(var(${drawerCSSVars.drawerSizeVar})))`, }, }); @@ -63,8 +66,8 @@ export const useDrawerInlineStyles_unstable = (state: DrawerInlineState): Drawer return undefined; } - return state.position === 'left' ? styles.hiddenLeft : styles.hiddenRight; - }, [state.position, state.active, styles.hiddenLeft, styles.hiddenRight]); + return mergeClasses(styles.hidden, state.position === 'left' ? styles.hiddenLeft : styles.hiddenRight); + }, [state.active, state.position, styles.hidden, styles.hiddenLeft, styles.hiddenRight]); state.root.className = mergeClasses( drawerInlineClassNames.root, diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/DrawerOverlay.types.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/DrawerOverlay.types.ts index 39f43ca108ffd0..e409ae99dd814a 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/DrawerOverlay.types.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/DrawerOverlay.types.ts @@ -1,5 +1,6 @@ import { DialogProps, DialogSurfaceProps, DialogSurfaceSlots } from '@fluentui/react-dialog'; -import type { ComponentProps, ComponentState, Slot, UseMotionPresenceState } from '@fluentui/react-utilities'; +import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities'; +import type { MotionProps } from '@fluentui/react-motion-preview'; import { DrawerBaseProps, DrawerBaseState } from '../../util/DrawerBase.types'; export type DrawerOverlaySlots = DialogSurfaceSlots & { @@ -17,8 +18,8 @@ export type DrawerOverlayProps = ComponentProps & * State used in rendering DrawerOverlay */ export type DrawerOverlayState = ComponentState & - DrawerBaseProps & + Omit & DrawerBaseState & { dialog: DialogProps; - backdropActive: UseMotionPresenceState['active']; + backdropActive: MotionProps['active']; }; diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts index 258f0d67fb16bd..79a1b71a0f4bf5 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts @@ -1,8 +1,9 @@ import * as React from 'react'; -import { getNativeElementProps, useMergedRefs, useMotionPresence, slot } from '@fluentui/react-utilities'; +import { getNativeElementProps, slot } from '@fluentui/react-utilities'; import type { DrawerOverlayProps, DrawerOverlayState } from './DrawerOverlay.types'; import { DialogProps, DialogSurface, DialogSurfaceProps } from '@fluentui/react-dialog'; -import { getDefaultDrawerProps } from '../../util/getDefaultDrawerProps'; +import { useBaseDrawerDefaultProps } from '../../util/useBaseDrawerDefaultProps'; +import { useMotion } from '@fluentui/react-motion-preview'; /** * Create the state required to render DrawerOverlay. @@ -17,18 +18,27 @@ export const useDrawerOverlay_unstable = ( props: DrawerOverlayProps, ref: React.Ref, ): DrawerOverlayState => { - const { open, defaultOpen, size, position } = getDefaultDrawerProps(props); - const { modalType = 'modal', inertTrapFocus, onOpenChange } = props; + const { open, defaultOpen, size, position } = useBaseDrawerDefaultProps(props); + const { modalType = 'modal', inertTrapFocus, onOpenChange, motion } = props; - const { ref: drawerRef, active, motionState } = useMotionPresence(open); + const drawerMotion = useMotion( + motion || { + presence: open, + ref, + }, + ); + const backdropMotion = useMotion( + motion || { + presence: open, + }, + ); - const backdropPresence = useMotionPresence(open); const hasCustomBackdrop = modalType !== 'non-modal' && props.backdrop !== null; const root = slot.always( getNativeElementProps('div', { ...props, - ref: useMergedRefs(ref, drawerRef), + ref: drawerMotion.ref, }), { elementType: DialogSurface, @@ -37,16 +47,16 @@ export const useDrawerOverlay_unstable = ( elementType: 'div', renderByDefault: hasCustomBackdrop, defaultProps: { - ref: backdropPresence.ref, + ref: backdropMotion.ref, }, }), }, }, ); - const dialog = slot.always( + const dialog = slot.always( { - open: motionState !== 'unmounted', + open: true, defaultOpen, onOpenChange, inertTrapFocus, @@ -68,8 +78,8 @@ export const useDrawerOverlay_unstable = ( dialog, size, position, - motionState, - active, - backdropActive: backdropPresence.active, + active: drawerMotion.active, + motionState: drawerMotion.state, + backdropActive: backdropMotion.active, }; }; diff --git a/packages/react-components/react-drawer/src/util/DrawerBase.types.ts b/packages/react-components/react-drawer/src/util/DrawerBase.types.ts index 7270353dbe6f7a..32b8b481e05f13 100644 --- a/packages/react-components/react-drawer/src/util/DrawerBase.types.ts +++ b/packages/react-components/react-drawer/src/util/DrawerBase.types.ts @@ -1,4 +1,4 @@ -import { UseMotionPresenceState } from '@fluentui/react-utilities'; +import { MotionProps } from '@fluentui/react-motion-preview'; export type DrawerBaseProps = { /** @@ -33,6 +33,14 @@ export type DrawerBaseProps = { * @default false */ defaultOpen?: boolean; + + /** + * + */ + motion?: MotionProps; }; -export type DrawerBaseState = Required, 'active' | 'motionState'>>; +export type DrawerBaseState = { + active: MotionProps['active']; + motionState: MotionProps['state']; +}; diff --git a/packages/react-components/react-drawer/src/util/getDefaultDrawerProps.ts b/packages/react-components/react-drawer/src/util/useBaseDrawerDefaultProps.ts similarity index 75% rename from packages/react-components/react-drawer/src/util/getDefaultDrawerProps.ts rename to packages/react-components/react-drawer/src/util/useBaseDrawerDefaultProps.ts index af835f0465ff97..1a31263f4b0d17 100644 --- a/packages/react-components/react-drawer/src/util/getDefaultDrawerProps.ts +++ b/packages/react-components/react-drawer/src/util/useBaseDrawerDefaultProps.ts @@ -1,6 +1,6 @@ import { DrawerBaseProps } from './DrawerBase.types'; -export function getDefaultDrawerProps(props: DrawerBaseProps) { +export function useBaseDrawerDefaultProps(props: DrawerBaseProps) { const { open = false, defaultOpen = false, size = 'small', position = 'left' } = props; return { diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomAnimation.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomAnimation.stories.tsx index b742ac5bb2a7d5..573b04de20a93d 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomAnimation.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomAnimation.stories.tsx @@ -1,7 +1,8 @@ import * as React from 'react'; import { DrawerOverlay, DrawerBody, DrawerHeader, DrawerHeaderTitle } from '@fluentui/react-drawer'; -import { Button, makeStyles, mergeClasses, shorthands, tokens, useMotionPresence } from '@fluentui/react-components'; +import { Button, makeStyles, mergeClasses, shorthands, tokens } from '@fluentui/react-components'; import { Dismiss24Regular } from '@fluentui/react-icons'; +import { useMotion } from '@fluentui/react-motion-preview'; const visibleKeyframe = { ...shorthands.borderRadius(0), @@ -37,23 +38,38 @@ const useStyles = makeStyles({ to: hiddenKeyframe, }, }, + + backdropEntering: { + transitionDuration: '2s', + }, + + backdropExiting: { + transitionDuration: '1s', + }, }); export const CustomAnimation = () => { const styles = useStyles(); const [isOpen, setIsOpen] = React.useState(false); - const { ref, motionState } = useMotionPresence(isOpen); + const motion = useMotion({ + presence: isOpen, + }); return (
setIsOpen(open)} > diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomInlineAnimation.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomInlineAnimation.stories.tsx new file mode 100644 index 00000000000000..1bf17144a7cf2e --- /dev/null +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomInlineAnimation.stories.tsx @@ -0,0 +1,142 @@ +import * as React from 'react'; +import { DrawerBody, DrawerHeader, DrawerHeaderTitle, DrawerInline } from '@fluentui/react-drawer'; +import { Button, makeStyles, mergeClasses, shorthands, tokens } from '@fluentui/react-components'; +import { Dismiss24Regular } from '@fluentui/react-icons'; +import { useMotion } from '../../../react-motion-preview/src/index'; + +const visibleKeyframe = { + ...shorthands.borderRadius(0), + opacity: 1, + transform: 'translate3D(0, 0, 0) scale(1)', +}; + +const hiddenKeyframe = { + ...shorthands.borderRadius('36px'), + opacity: 0, + transform: 'translate3D(-100%, 0, 0) scale(0.5)', +}; + +const useStyles = makeStyles({ + root: { + ...shorthands.border('2px', 'solid', '#ccc'), + ...shorthands.overflow('hidden'), + display: 'flex', + height: '480px', + position: 'relative', + backgroundColor: '#fff', + }, + + drawer: { + animationDuration: tokens.durationNormal, + willChange: 'opacity, transform, border-radius', + }, + + drawerEntering: { + animationTimingFunction: tokens.curveDecelerateMid, + animationName: { + from: hiddenKeyframe, + to: visibleKeyframe, + }, + }, + + drawerExiting: { + animationTimingFunction: tokens.curveAccelerateMin, + animationName: { + from: visibleKeyframe, + to: hiddenKeyframe, + }, + }, + + content: { + ...shorthands.flex(1), + ...shorthands.padding('16px'), + + boxSizing: 'border-box', + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + transitionDuration: tokens.durationNormal, + transitionProperty: 'transform, background-color', + willChange: 'transform, background-color', + overflowY: 'auto', + }, + contentActive: { + transform: 'translate3D(320px, 0, 0)', + backgroundColor: tokens.colorNeutralBackground4, + }, + contentEntering: { + transitionTimingFunction: tokens.curveDecelerateMid, + }, + contentExiting: { + transitionTimingFunction: tokens.curveAccelerateMin, + }, + contentIdle: { + width: 'calc(100% - 320px)', + }, +}); + +export const CustomInlineAnimation = () => { + const styles = useStyles(); + + const [isOpen, setIsOpen] = React.useState(false); + const motion = useMotion({ + presence: isOpen, + }); + + return ( +
+ + + } + onClick={() => setIsOpen(false)} + /> + } + > + Left Inline Drawer + + + + +

Drawer content

+
+
+ +
+ + + {Array.from({ length: 50 }, (_, i) => ( +

+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Itaque a doloribus perspiciatis voluptas magni modi + atque, eligendi voluptate provident similique quod libero cum veniam, delectus nemo reprehenderit officia + quisquam! Corrupti? +

+ ))} +
+
+ ); +}; diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx index 87feeb673958c9..98aa9f2e7cc4ab 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx @@ -1,7 +1,8 @@ import * as React from 'react'; import { DrawerOverlay, DrawerBody, DrawerHeader, DrawerHeaderTitle } from '@fluentui/react-drawer'; -import { Button, makeStyles, mergeClasses, tokens, useMotionPresence } from '@fluentui/react-components'; +import { Button, makeStyles, mergeClasses, tokens } from '@fluentui/react-components'; import { Dismiss24Regular } from '@fluentui/react-icons'; +import { useMotion } from '@fluentui/react-motion-preview'; const useStyles = makeStyles({ drawer: { @@ -27,16 +28,18 @@ export const CustomTransition = () => { const styles = useStyles(); const [isOpen, setIsOpen] = React.useState(false); - const { ref, motionState, active } = useMotionPresence(isOpen); + const motion = useMotion({ + presence: isOpen, + }); return (
setIsOpen(open)} diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerDefault.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerDefault.stories.tsx index 27438c0e8f8d6a..48d00c32cd9ad4 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerDefault.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerDefault.stories.tsx @@ -39,6 +39,8 @@ export const Default = () => { const [isOpen, setIsOpen] = React.useState(false); const [type, setType] = React.useState('overlay'); + const onClick = () => setIsOpen(type === 'inline' ? !isOpen : true); + return (
setIsOpen(open)}> @@ -63,8 +65,8 @@ export const Default = () => {
-
diff --git a/packages/react-components/react-drawer/stories/Drawer/index.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/index.stories.tsx index fbd39a0da6386a..07707b474f8594 100644 --- a/packages/react-components/react-drawer/stories/Drawer/index.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/index.stories.tsx @@ -24,6 +24,7 @@ export { WithScroll } from './DrawerWithScroll.stories'; export { DisabledTransition } from './DrawerDisabledTransition.stories'; export { CustomTransition } from './DrawerCustomTransition.stories'; export { CustomAnimation } from './DrawerCustomAnimation.stories'; +export { CustomInlineAnimation } from './DrawerCustomInlineAnimation.stories'; export { AlwaysOpen } from './DrawerAlwaysOpen.stories'; export { PreventClose } from './DrawerPreventClose.stories'; export { Responsive } from './DrawerResponsive.stories'; diff --git a/packages/react-components/react-motion-preview/.babelrc.json b/packages/react-components/react-motion-preview/.babelrc.json new file mode 100644 index 00000000000000..45fb71ca16d2c3 --- /dev/null +++ b/packages/react-components/react-motion-preview/.babelrc.json @@ -0,0 +1,4 @@ +{ + "extends": "../../../.babelrc-v9.json", + "plugins": ["annotate-pure-calls", "@babel/transform-react-pure-annotations"] +} diff --git a/packages/react-components/react-motion-preview/.eslintrc.json b/packages/react-components/react-motion-preview/.eslintrc.json new file mode 100644 index 00000000000000..ceea884c70dccc --- /dev/null +++ b/packages/react-components/react-motion-preview/.eslintrc.json @@ -0,0 +1,4 @@ +{ + "extends": ["plugin:@fluentui/eslint-plugin/react"], + "root": true +} diff --git a/packages/react-components/react-motion-preview/.npmignore b/packages/react-components/react-motion-preview/.npmignore new file mode 100644 index 00000000000000..a5817be2414dec --- /dev/null +++ b/packages/react-components/react-motion-preview/.npmignore @@ -0,0 +1,38 @@ +.storybook/ +.vscode/ +bundle-size/ +config/ +coverage/ +docs/ +etc/ +node_modules/ +src/ +stories/ +dist/types/ +temp/ +__fixtures__ +__mocks__ +__tests__ + +*.api.json +*.log +*.spec.* +*.cy.* +*.test.* +*.yml + +# config files +*config.* +*rc.* +.editorconfig +.eslint* +.git* +.prettierignore +.swcrc +project.json + +# exclude gitignore patterns explicitly +!lib +!lib-commonjs +!lib-amd +!dist/*.d.ts diff --git a/packages/react-components/react-motion-preview/.storybook/main.js b/packages/react-components/react-motion-preview/.storybook/main.js new file mode 100644 index 00000000000000..26536b61b387f6 --- /dev/null +++ b/packages/react-components/react-motion-preview/.storybook/main.js @@ -0,0 +1,14 @@ +const rootMain = require('../../../../.storybook/main'); + +module.exports = /** @type {Omit} */ ({ + ...rootMain, + stories: [...rootMain.stories, '../stories/**/*.stories.mdx', '../stories/**/index.stories.@(ts|tsx)'], + addons: [...rootMain.addons], + webpackFinal: (config, options) => { + const localConfig = { ...rootMain.webpackFinal(config, options) }; + + // add your own webpack tweaks if needed + + return localConfig; + }, +}); diff --git a/packages/react-components/react-motion-preview/.storybook/preview.js b/packages/react-components/react-motion-preview/.storybook/preview.js new file mode 100644 index 00000000000000..1939500a3d18c7 --- /dev/null +++ b/packages/react-components/react-motion-preview/.storybook/preview.js @@ -0,0 +1,7 @@ +import * as rootPreview from '../../../../.storybook/preview'; + +/** @type {typeof rootPreview.decorators} */ +export const decorators = [...rootPreview.decorators]; + +/** @type {typeof rootPreview.parameters} */ +export const parameters = { ...rootPreview.parameters }; diff --git a/packages/react-components/react-motion-preview/.storybook/tsconfig.json b/packages/react-components/react-motion-preview/.storybook/tsconfig.json new file mode 100644 index 00000000000000..ea89218a3d916f --- /dev/null +++ b/packages/react-components/react-motion-preview/.storybook/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "", + "allowJs": true, + "checkJs": true, + "types": ["static-assets", "environment", "storybook__addons"] + }, + "include": ["../stories/**/*.stories.ts", "../stories/**/*.stories.tsx", "*.js"] +} diff --git a/packages/react-components/react-motion-preview/.swcrc b/packages/react-components/react-motion-preview/.swcrc new file mode 100644 index 00000000000000..b4ffa86dee3067 --- /dev/null +++ b/packages/react-components/react-motion-preview/.swcrc @@ -0,0 +1,30 @@ +{ + "$schema": "https://json.schemastore.org/swcrc", + "exclude": [ + "/testing", + "/**/*.cy.ts", + "/**/*.cy.tsx", + "/**/*.spec.ts", + "/**/*.spec.tsx", + "/**/*.test.ts", + "/**/*.test.tsx" + ], + "jsc": { + "parser": { + "syntax": "typescript", + "tsx": true, + "decorators": false, + "dynamicImport": false + }, + "externalHelpers": true, + "transform": { + "react": { + "runtime": "classic", + "useSpread": true + } + }, + "target": "es2019" + }, + "minify": false, + "sourceMaps": true +} diff --git a/packages/react-components/react-motion-preview/LICENSE b/packages/react-components/react-motion-preview/LICENSE new file mode 100644 index 00000000000000..6789473dee14ce --- /dev/null +++ b/packages/react-components/react-motion-preview/LICENSE @@ -0,0 +1,15 @@ +@fluentui/react-motion-preview + +Copyright (c) Microsoft Corporation + +All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Note: Usage of the fonts and icons referenced in Fluent UI React is subject to the terms listed at https://aka.ms/fluentui-assets-license diff --git a/packages/react-components/react-motion-preview/README.md b/packages/react-components/react-motion-preview/README.md new file mode 100644 index 00000000000000..b4d66fbd602960 --- /dev/null +++ b/packages/react-components/react-motion-preview/README.md @@ -0,0 +1,5 @@ +# @fluentui/react-motion-preview + +**React Motion components for [Fluent UI React](https://react.fluentui.dev/)** + +These are not production-ready components and **should never be used in product**. This space is useful for testing new components whose APIs might change before final release. diff --git a/packages/react-components/react-motion-preview/config/api-extractor.json b/packages/react-components/react-motion-preview/config/api-extractor.json new file mode 100644 index 00000000000000..e533bf30b48a2b --- /dev/null +++ b/packages/react-components/react-motion-preview/config/api-extractor.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "@fluentui/scripts-api-extractor/api-extractor.common.v-next.json" +} diff --git a/packages/react-components/react-motion-preview/config/tests.js b/packages/react-components/react-motion-preview/config/tests.js new file mode 100644 index 00000000000000..2e211ae9e21420 --- /dev/null +++ b/packages/react-components/react-motion-preview/config/tests.js @@ -0,0 +1 @@ +/** Jest test setup file. */ diff --git a/packages/react-components/react-motion-preview/etc/react-motion-preview.api.md b/packages/react-components/react-motion-preview/etc/react-motion-preview.api.md new file mode 100644 index 00000000000000..32e3232c24a72d --- /dev/null +++ b/packages/react-components/react-motion-preview/etc/react-motion-preview.api.md @@ -0,0 +1,28 @@ +## API Report File for "@fluentui/react-motion-preview" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import * as React_2 from 'react'; +import { RefObjectFunction } from '@fluentui/react-utilities'; + +// @public (undocumented) +export type MotionOptions = { + animateOnFirstMount?: boolean; +}; + +// @internal +export type MotionProps = { + presence: boolean; + ref?: RefObjectFunction | React_2.RefCallback | React_2.Ref; + active?: boolean; + state?: 'unmounted' | 'entering' | 'entered' | 'idle' | 'exiting' | 'exited'; +}; + +// @internal +export const useMotion: (props: MotionProps, options?: MotionOptions) => Required>; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/react-components/react-motion-preview/jest.config.js b/packages/react-components/react-motion-preview/jest.config.js new file mode 100644 index 00000000000000..bdaab7ff09ee93 --- /dev/null +++ b/packages/react-components/react-motion-preview/jest.config.js @@ -0,0 +1,21 @@ +// @ts-check + +/** + * @type {import('@jest/types').Config.InitialOptions} + */ +module.exports = { + displayName: 'react-motion-preview', + preset: '../../../jest.preset.js', + transform: { + '^.+\\.tsx?$': [ + 'ts-jest', + { + tsconfig: '/tsconfig.spec.json', + isolatedModules: true, + }, + ], + }, + coverageDirectory: './coverage', + setupFilesAfterEnv: ['./config/tests.js'], + snapshotSerializers: ['@griffel/jest-serializer'], +}; diff --git a/packages/react-components/react-motion-preview/just.config.ts b/packages/react-components/react-motion-preview/just.config.ts new file mode 100644 index 00000000000000..b7b2c9a33bf435 --- /dev/null +++ b/packages/react-components/react-motion-preview/just.config.ts @@ -0,0 +1,5 @@ +import { preset, task } from '@fluentui/scripts-tasks'; + +preset(); + +task('build', 'build:react-components').cached?.(); diff --git a/packages/react-components/react-motion-preview/package.json b/packages/react-components/react-motion-preview/package.json new file mode 100644 index 00000000000000..88e5d44f3897b0 --- /dev/null +++ b/packages/react-components/react-motion-preview/package.json @@ -0,0 +1,61 @@ +{ + "name": "@fluentui/react-motion-preview", + "version": "0.0.0", + "private": true, + "description": "New fluentui react package", + "main": "lib-commonjs/index.js", + "module": "lib/index.js", + "typings": "./dist/index.d.ts", + "sideEffects": false, + "repository": { + "type": "git", + "url": "https://github.com/microsoft/fluentui" + }, + "license": "MIT", + "scripts": { + "build": "just-scripts build", + "clean": "just-scripts clean", + "generate-api": "just-scripts generate-api", + "lint": "just-scripts lint", + "start": "yarn storybook", + "storybook": "start-storybook", + "test": "jest --passWithNoTests", + "test-ssr": "test-ssr \"./stories/**/*.stories.tsx\"", + "type-check": "tsc -b tsconfig.json" + }, + "devDependencies": { + "@fluentui/eslint-plugin": "*", + "@fluentui/react-conformance": "*", + "@fluentui/react-conformance-griffel": "*", + "@fluentui/scripts-api-extractor": "*", + "@fluentui/scripts-tasks": "*" + }, + "dependencies": { + "@fluentui/react-jsx-runtime": "^9.0.0-alpha.13", + "@fluentui/react-theme": "^9.1.10", + "@fluentui/react-utilities": "^9.11.0", + "@griffel/react": "^1.5.7", + "@swc/helpers": "^0.4.14" + }, + "peerDependencies": { + "@types/react": ">=16.8.0 <19.0.0", + "@types/react-dom": ">=16.8.0 <19.0.0", + "react": ">=16.8.0 <19.0.0", + "react-dom": ">=16.8.0 <19.0.0" + }, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib/index.js", + "require": "./lib-commonjs/index.js" + }, + "./package.json": "./package.json" + }, + "beachball": { + "disallowedChangeTypes": [ + "major", + "prerelease" + ] + } +} diff --git a/packages/react-components/react-motion-preview/project.json b/packages/react-components/react-motion-preview/project.json new file mode 100644 index 00000000000000..b47a10e7e354f2 --- /dev/null +++ b/packages/react-components/react-motion-preview/project.json @@ -0,0 +1,8 @@ +{ + "name": "@fluentui/react-motion-preview", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "library", + "sourceRoot": "packages/react-components/react-motion-preview/src", + "tags": ["platform:web", "vNext"], + "implicitDependencies": [] +} diff --git a/packages/react-components/react-motion-preview/src/hooks/index.ts b/packages/react-components/react-motion-preview/src/hooks/index.ts new file mode 100644 index 00000000000000..4fb9f2d6d5f179 --- /dev/null +++ b/packages/react-components/react-motion-preview/src/hooks/index.ts @@ -0,0 +1 @@ +export * from './useMotion'; diff --git a/packages/react-components/react-motion-preview/src/hooks/useMotion.test.ts b/packages/react-components/react-motion-preview/src/hooks/useMotion.test.ts new file mode 100644 index 00000000000000..ecbef73a23c804 --- /dev/null +++ b/packages/react-components/react-motion-preview/src/hooks/useMotion.test.ts @@ -0,0 +1,237 @@ +import { act, renderHook } from '@testing-library/react-hooks'; + +import { useMotion, MotionProps, MotionOptions } from './useMotion'; + +const defaultDuration = 100; +const renderHookWithRef = ( + initialState: MotionProps, + initialOptions?: MotionOptions, + style: Record = { 'transition-duration': `${defaultDuration}ms` }, +) => { + const refEl = document.createElement('div'); + const hook = renderHook(({ state, options }) => useMotion(state, options), { + initialProps: { + state: initialState, + options: initialOptions, + } as { state: MotionProps; options?: MotionOptions }, + }); + + Object.entries(style).forEach(([key, value]) => value && refEl.style.setProperty(key, value)); + + act(() => { + if (typeof hook.result.current.ref === 'function') { + hook.result.current.ref(refEl); + } + }); + + return hook; +}; + +const jumpToNextFrame = () => { + act(() => { + // requestAnimationFrame + timeout callbacks + jest.advanceTimersToNextTimer(); + jest.advanceTimersToNextTimer(); + }); +}; + +describe('useMotion', () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + describe('when presence is false by default', () => { + it('should return default values when presence is false', () => { + const { result } = renderHookWithRef({ presence: false }); + + expect(typeof result.current.ref).toBe('function'); + expect(result.current.state).toBe('unmounted'); + expect(result.current.active).toBe(false); + }); + }); + + describe('when presence is true by default', () => { + it('should return default values', () => { + const { result } = renderHookWithRef({ presence: true }); + + expect(typeof result.current.ref).toBe('function'); + expect(result.current.active).toBe(true); + }); + + it('should change visible to true when animateOnFirstMount is true', () => { + const { result } = renderHookWithRef({ presence: true }, { animateOnFirstMount: true }); + + expect(typeof result.current.ref).toBe('function'); + expect(result.current.active).toBe(false); + + jumpToNextFrame(); + + expect(result.current.active).toBe(true); + }); + }); + + describe('when presence changes', () => { + it('should toggle values starting with false', () => { + const { result, rerender } = renderHookWithRef({ presence: false }); + + expect(typeof result.current.ref).toBe('function'); + expect(result.current.state).toBe('unmounted'); + expect(result.current.active).toBe(false); + + rerender({ state: { presence: true } }); + + act(() => jest.advanceTimersToNextTimer()); + + expect(result.current.state).toBe('entering'); + expect(result.current.active).toBe(true); + + act(() => jest.advanceTimersByTime(defaultDuration + 1)); + expect(result.current.state).toBe('entered'); + + act(() => jest.advanceTimersToNextTimer()); + + expect(result.current.state).toBe('idle'); + + rerender({ state: { presence: false } }); + + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.state).toBe('exiting'); + expect(result.current.active).toBe(false); + + act(() => jest.advanceTimersByTime(defaultDuration + 1)); + expect(result.current.state).toBe('exited'); + + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.state).toBe('unmounted'); + expect(result.current.active).toBe(false); + }); + + it('should toggle values starting with true', () => { + const { result, rerender } = renderHookWithRef({ presence: true }); + + expect(typeof result.current.ref).toBe('function'); + expect(result.current.active).toBe(true); + + rerender({ state: { presence: false } }); + + act(() => jest.advanceTimersToNextTimer()); + + expect(result.current.state).toBe('exiting'); + expect(result.current.active).toBe(false); + + act(() => jest.advanceTimersByTime(defaultDuration + 1)); + expect(result.current.state).toBe('exited'); + + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.state).toBe('unmounted'); + expect(result.current.active).toBe(false); + }); + }); + + describe.each([ + { message: 'with transition', styles: { 'transition-duration': '100ms' } }, + { message: 'with long transition', styles: { 'transition-duration': '1000ms' } }, + { message: 'with animation', styles: { 'animation-duration': '100ms' } }, + { message: 'with long animation', styles: { 'animation-duration': '1000ms' } }, + ])('when presence changes - $message', ({ styles }) => { + it('should toggle values starting with false', () => { + const { result, rerender } = renderHookWithRef({ presence: false }, {}, styles); + + expect(typeof result.current.ref).toBe('function'); + expect(result.current.state).toBe('unmounted'); + expect(result.current.active).toBe(false); + + rerender({ state: { presence: true } }); + + act(() => jest.advanceTimersToNextTimer()); + + expect(result.current.state).toBe('entering'); + expect(result.current.active).toBe(true); + + act(() => jest.advanceTimersToNextTimer()); + + expect(result.current.state).toBe('entered'); + + act(() => jest.advanceTimersToNextTimer()); + + expect(result.current.state).toBe('idle'); + + rerender({ state: { presence: false } }); + + act(() => jest.advanceTimersToNextTimer()); + + expect(result.current.state).toBe('exiting'); + expect(result.current.active).toBe(false); + + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.state).toBe('exited'); + + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.state).toBe('unmounted'); + expect(result.current.active).toBe(false); + }); + }); + + describe.each([ + { message: 'with no transition', styles: { 'transition-duration': '0' } }, + { message: 'with no animation', styles: { 'animation-duration': '0' } }, + ])('when presence changes - $message', ({ styles }) => { + it('should toggle values when transition-duration is 0', () => { + const { result, rerender } = renderHookWithRef({ presence: false }, {}, styles); + + expect(typeof result.current.ref).toBe('function'); + expect(result.current.state).toBe('unmounted'); + expect(result.current.active).toBe(false); + + rerender({ state: { presence: true } }); + + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.state).toBe('entered'); + + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.state).toBe('idle'); + + // requestAnimationFrame + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.active).toBe(true); + // timeout + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.state).toBe('idle'); + + rerender({ state: { presence: false } }); + + act(() => jest.advanceTimersToNextTimer()); + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.active).toBe(false); + + // requestAnimationFrame + act(() => jest.advanceTimersToNextTimer()); + // timeout + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.state).toBe('unmounted'); + }); + }); + + describe('when motion is received', () => { + it('should return default values when presence is false', () => { + const { result } = renderHookWithRef({ presence: false, state: 'unmounted', active: false }); + + expect(typeof result.current.ref).toBe('function'); + expect(result.current.state).toBe('unmounted'); + expect(result.current.presence).toBe(false); + expect(result.current.active).toBe(false); + }); + + it('should return default values when presence is true', () => { + const { result } = renderHookWithRef({ presence: true, state: 'idle', active: true }); + + expect(typeof result.current.ref).toBe('function'); + expect(result.current.presence).toBe(true); + expect(result.current.active).toBe(true); + }); + }); +}); diff --git a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts b/packages/react-components/react-motion-preview/src/hooks/useMotion.ts similarity index 55% rename from packages/react-components/react-utilities/src/hooks/useMotionPresence.ts rename to packages/react-components/react-motion-preview/src/hooks/useMotion.ts index 335cb013c24f7e..a42087363f9de5 100644 --- a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts +++ b/packages/react-components/react-motion-preview/src/hooks/useMotion.ts @@ -1,7 +1,13 @@ import * as React from 'react'; -import { canUseDOM } from '../ssr/canUseDOM'; -import { useAnimationFrame } from './useAnimationFrame'; -import { useTimeout } from './useTimeout'; + +import { + canUseDOM, + RefObjectFunction, + useAnimationFrame, + useMergedRefs, + usePrevious, + useTimeout, +} from '@fluentui/react-utilities'; /** * CSS Typed Object Model @@ -30,10 +36,15 @@ interface StylePropertyMapReadOnly { * HTMLElement with styled map. * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/computedStyleMap */ -type HTMLElementWithStyledMap = TElement & { +type HTMLElementWithStyledMap = T & { computedStyleMap(): StylePropertyMapReadOnly; }; +/** + * CSS with number parsing. + * @see https://drafts.css-houdini.org/css-typed-om-1/#css + * @see https://developer.mozilla.org/en-US/docs/Web/API/CSS/number + */ type CSSWithNumber = typeof CSS & { number(value: number): { value: number; @@ -44,52 +55,56 @@ type CSSWithNumber = typeof CSS & { /** * @internal * - * State for useMotionPresence hook. + * Motion props. */ -export type UseMotionPresenceState = { +export type MotionProps = { + /** + * Whether the element should be present in the DOM. + * + * @default false + */ + presence: boolean; + /** * Ref to the element. * * @example - * const { ref } = useMotionPresence(isOpen); + * const motion = useMotion({ presence: isOpen }); * - *
+ *
*/ - ref: React.RefCallback; + ref?: RefObjectFunction | React.RefCallback | React.Ref; /** * Whether the element is currently active in the DOM. * Useful to apply CSS transitions only when the element is active. * * @example - * const { active, ref } = useMotionPresence(isOpen); + * const motion = useMotion({ presence: isOpen }); * - *
+ *
*/ - active: boolean; + active?: boolean; /** * Current state of the element. * + * - `unmounted` - The element is not yet rendered or can be safely removed from the DOM. * - `entering` - The element is performing enter animation. + * - `entered` - The element has finished enter animation. + * - `idle` - The element is currently not animating, but rendered on screen. * - `exiting` - The element is performing exit animation. - * - `resting` - The element is currently not animating, but rendered on screen. - * - `unmounted` - The element is not rendered or can be removed from the DOM. + * - `exited` - The element has finished exit animation. * * @example - * const { motionState, ref } = useMotionPresence(isOpen); + * const motion = useMotion({ presence: isOpen }); * - *
+ *
*/ - motionState: 'entering' | 'exiting' | 'resting' | 'unmounted'; + state?: 'unmounted' | 'entering' | 'entered' | 'idle' | 'exiting' | 'exited'; }; -/** - * @internal - * - * Options for useMotionPresence hook. - */ -export type UseMotionPresenceOptions = { +export type MotionOptions = { /** * Whether to animate the element on first mount. * @@ -106,15 +121,15 @@ export type UseMotionPresenceOptions = { * @returns - CSS styles. */ const getElementComputedStyle = (node: HTMLElement): CSSStyleDeclaration => { - const window = node.ownerDocument.defaultView; + const win = node.ownerDocument?.defaultView ? node.ownerDocument.defaultView : window; - if (!window || !canUseDOM()) { + if (!win || !canUseDOM()) { return { getPropertyValue: (_: string) => '', } as CSSStyleDeclaration; } - return window.getComputedStyle(node, null); + return win.getComputedStyle(node, null); }; /** @@ -246,36 +261,43 @@ const getMotionDuration = (node: HTMLElementWithStyledMap) => { }; /** + * @internal + * * Hook to manage the presence of an element in the DOM based on its CSS transition/animation state. * * @param present - Whether the element should be present in the DOM * @param events - Callbacks for when the element enters or exits the DOM */ -export const useMotionPresence = ( +const useMotionPresence = ( present: boolean, - options: UseMotionPresenceOptions = {}, -): UseMotionPresenceState => { + options: MotionOptions = {}, +): Required, 'presence'>> => { const { animateOnFirstMount } = { animateOnFirstMount: false, ...options }; - const [state, setState] = React.useState, 'ref'>>({ - motionState: present ? 'resting' : 'unmounted', + const [state, setState] = React.useState>>({ + state: present ? 'idle' : 'unmounted', active: false, }); - const [currentElement, setCurrentElement] = React.useState | null>(null); + const [currentElement, setCurrentElement] = React.useState | null>(null); const [setAnimationTimeout, clearAnimationTimeout] = useTimeout(); + const [setActiveAnimationFrame, cancelActiveAnimationFrame] = useAnimationFrame(); const [setProcessingAnimationFrame, cancelProcessingAnimationFrame] = useAnimationFrame(); + const [setDelayedAnimationFrame, cancelDelayedAnimationFrame] = useAnimationFrame(); const skipAnimationOnFirstRender = React.useRef(!animateOnFirstMount); const processAnimation = React.useCallback( (callback: () => void) => { - if (!currentElement) { + const targetElement = currentElement; + + if (!targetElement) { return; } clearAnimationTimeout(); - const animationFrame = requestAnimationFrame(() => { - const duration = getMotionDuration(currentElement); + cancelProcessingAnimationFrame(); + setProcessingAnimationFrame(() => { + const duration = getMotionDuration(targetElement); if (duration === 0) { callback(); @@ -292,13 +314,19 @@ export const useMotionPresence = ( return () => { clearAnimationTimeout(); - cancelAnimationFrame(animationFrame); + cancelProcessingAnimationFrame(); }; }, - [clearAnimationTimeout, currentElement, setAnimationTimeout], + [ + cancelProcessingAnimationFrame, + clearAnimationTimeout, + currentElement, + setAnimationTimeout, + setProcessingAnimationFrame, + ], ); - const ref: React.RefCallback> = React.useCallback(node => { + const ref: React.RefCallback> = React.useCallback(node => { if (!node) { return; } @@ -308,30 +336,33 @@ export const useMotionPresence = ( React.useEffect(() => { if (present) { - setState({ + setState(prevState => ({ + ...prevState, + state: 'entering', active: skipAnimationOnFirstRender.current ? true : false, - motionState: 'resting', - }); + })); } }, [present]); React.useEffect(() => { const skipAnimation = skipAnimationOnFirstRender.current; - const onUnmount = () => cancelProcessingAnimationFrame(); + const onUnmount = () => { + cancelActiveAnimationFrame(); + cancelDelayedAnimationFrame(); + }; - setProcessingAnimationFrame(() => { + setActiveAnimationFrame(() => { setState(prevState => { - let motionState = prevState.motionState; + let newState = prevState.state; if (skipAnimation) { - motionState = present ? 'resting' : 'unmounted'; + newState = present ? 'idle' : 'unmounted'; } else { - motionState = present ? 'entering' : 'exiting'; + newState = present ? 'entering' : 'exiting'; } return { - ...prevState, - motionState, + state: newState, active: present, }; }); @@ -344,12 +375,26 @@ export const useMotionPresence = ( processAnimation(() => { setState(prevState => ({ ...prevState, - motionState: present ? 'resting' : 'unmounted', + state: present ? 'entered' : 'exited', })); + + setDelayedAnimationFrame(() => { + setState(prevState => ({ + ...prevState, + state: present ? 'idle' : 'unmounted', + })); + }); }); return onUnmount; - }, [cancelProcessingAnimationFrame, present, processAnimation, setProcessingAnimationFrame]); + }, [ + cancelActiveAnimationFrame, + cancelDelayedAnimationFrame, + present, + processAnimation, + setActiveAnimationFrame, + setDelayedAnimationFrame, + ]); React.useEffect(() => { skipAnimationOnFirstRender.current = false; @@ -360,3 +405,80 @@ export const useMotionPresence = ( ...state, }; }; + +/** + * @internal + * + * Hook to manage the presence of an element in the DOM based on its CSS transition/animation state. + * + * @param props - Motion props to manage the presence of an element in the DOM + * @param options - Motion options to configure the hook + */ +export const useMotion = ( + props: MotionProps, + options?: MotionOptions, +): Required> => { + const { ref, presence, active, state } = props; + const previousProps = usePrevious(props); + const mergedRef = useMergedRefs(ref); + + /** + * Heads up! + * We don't want these warnings in production even though it is against native behavior + */ + if (process.env.NODE_ENV !== 'production') { + // eslint-disable-next-line react-hooks/rules-of-hooks + React.useEffect(() => { + if (previousProps && Object.keys(props).length !== Object.keys(previousProps).length) { + // eslint-disable-next-line no-console + console.error( + [ + 'useMotion: The hook needs to be called with the same amount of props on every render.', + 'This is to ensure the internal state of the hook is stable and can be used to accurately detect the motion state.', + 'Please make sure to not change the props on subsequent renders or to use the hook conditionally.', + '\nCurrent props:', + JSON.stringify(props, null, 2), + '\nPrevious props:', + JSON.stringify(previousProps, null, 2), + ].join(' '), + ); + } + }, [props, previousProps]); + } + + if (typeof ref !== 'undefined' && typeof state !== 'undefined') { + const isMounted = state !== 'unmounted'; + + return { + ref: mergedRef, + state, + presence: presence ?? isMounted, + active: active ?? (isMounted && state !== 'exited'), + }; + } + + if (process.env.NODE_ENV !== 'production') { + if (typeof presence === 'undefined') { + throw new Error('useMotion: The hook needs either a `ref` and `state` or `presence` prop to work.'); + } + } + + const isPresent = !!presence; + /** + * Heads up! + * This hook returns a MotionProps but also accepts MotionProps as an argument. + * In case the hook is called with a MotionProps argument, we don't need to perform the expensive computation of the + * motion state and can just return the props as is. This is intentional as it allows others to use the hook on their + * side without having to worry about the performance impact of the hook. + */ + // eslint-disable-next-line react-hooks/rules-of-hooks + const { ref: motionRef, ...motionPresence } = useMotionPresence(isPresent, options); + + return { + presence: isPresent, + // eslint-disable-next-line react-hooks/rules-of-hooks + ref: useMergedRefs(ref, motionRef as RefObjectFunction), + active: motionPresence.active, + state: motionPresence.state, + }; +}; diff --git a/packages/react-components/react-motion-preview/src/index.ts b/packages/react-components/react-motion-preview/src/index.ts new file mode 100644 index 00000000000000..0a75af31e5294d --- /dev/null +++ b/packages/react-components/react-motion-preview/src/index.ts @@ -0,0 +1,2 @@ +export { useMotion } from './hooks'; +export type { MotionProps, MotionOptions } from './hooks'; diff --git a/packages/react-components/react-motion-preview/stories/useMotion/UseMotionBestPractices.md b/packages/react-components/react-motion-preview/stories/useMotion/UseMotionBestPractices.md new file mode 100644 index 00000000000000..08ff8ddeeb5f86 --- /dev/null +++ b/packages/react-components/react-motion-preview/stories/useMotion/UseMotionBestPractices.md @@ -0,0 +1,5 @@ +## Best practices + +### Do + +### Don't diff --git a/packages/react-components/react-motion-preview/stories/useMotion/UseMotionDefault.stories.tsx b/packages/react-components/react-motion-preview/stories/useMotion/UseMotionDefault.stories.tsx new file mode 100644 index 00000000000000..b6628f71ebd436 --- /dev/null +++ b/packages/react-components/react-motion-preview/stories/useMotion/UseMotionDefault.stories.tsx @@ -0,0 +1,60 @@ +import * as React from 'react'; + +import { useMotion } from '@fluentui/react-motion-preview'; +import { Button, makeStyles, mergeClasses, shorthands, tokens } from '@fluentui/react-components'; + +const useStyles = makeStyles({ + root: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + rowGap: '24px', + }, + + rectangle: { + ...shorthands.borderRadius('8px'), + + width: '200px', + height: '150px', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + backgroundColor: tokens.colorBrandBackground2, + opacity: 0, + transform: 'translate3D(0, 0, 0) scale(0.25)', + transitionDuration: `${tokens.durationNormal}, ${tokens.durationNormal}, ${tokens.durationUltraSlow}`, + transitionDelay: `${tokens.durationFast}, 0, ${tokens.durationSlow}`, + transitionProperty: 'opacity, transform, background-color', + willChange: 'opacity, transform, background-color', + color: '#fff', + }, + + visible: { + opacity: 1, + transform: 'translate3D(0, 0, 0) scale(1)', + backgroundColor: tokens.colorBrandBackground, + }, +}); + +export const Default = () => { + const styles = useStyles(); + + const [open, setOpen] = React.useState(false); + const motion = useMotion({ + presence: open, + }); + + return ( +
+ + +
+ Lorem ipsum +
+
+ ); +}; diff --git a/packages/react-components/react-motion-preview/stories/useMotion/UseMotionDescription.md b/packages/react-components/react-motion-preview/stories/useMotion/UseMotionDescription.md new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/packages/react-components/react-motion-preview/stories/useMotion/index.stories.tsx b/packages/react-components/react-motion-preview/stories/useMotion/index.stories.tsx new file mode 100644 index 00000000000000..a9cbc03f36c839 --- /dev/null +++ b/packages/react-components/react-motion-preview/stories/useMotion/index.stories.tsx @@ -0,0 +1,15 @@ +import descriptionMd from './UseMotionDescription.md'; +import bestPracticesMd from './UseMotionBestPractices.md'; + +export { Default } from './UseMotionDefault.stories'; + +export default { + title: 'Preview Components/useMotion', + parameters: { + docs: { + description: { + component: [descriptionMd, bestPracticesMd].join('\n'), + }, + }, + }, +}; diff --git a/packages/react-components/react-motion-preview/tsconfig.json b/packages/react-components/react-motion-preview/tsconfig.json new file mode 100644 index 00000000000000..1941a041d46c19 --- /dev/null +++ b/packages/react-components/react-motion-preview/tsconfig.json @@ -0,0 +1,25 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "target": "ES2019", + "noEmit": true, + "isolatedModules": true, + "importHelpers": true, + "jsx": "react", + "noUnusedLocals": true, + "preserveConstEnums": true + }, + "include": [], + "files": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + }, + { + "path": "./.storybook/tsconfig.json" + } + ] +} diff --git a/packages/react-components/react-motion-preview/tsconfig.lib.json b/packages/react-components/react-motion-preview/tsconfig.lib.json new file mode 100644 index 00000000000000..2de444c2059a53 --- /dev/null +++ b/packages/react-components/react-motion-preview/tsconfig.lib.json @@ -0,0 +1,22 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": false, + "lib": ["ES2019", "dom"], + "declaration": true, + "declarationDir": "../../../dist/out-tsc/types", + "outDir": "../../../dist/out-tsc", + "inlineSources": true, + "types": ["static-assets", "environment"] + }, + "exclude": [ + "src/components/AnimatePresence/testing/**", + "**/*.spec.ts", + "**/*.spec.tsx", + "**/*.test.ts", + "**/*.test.tsx", + "**/*.stories.ts", + "**/*.stories.tsx" + ], + "include": ["./src/**/*.ts", "./src/**/*.tsx"] +} diff --git a/packages/react-components/react-motion-preview/tsconfig.spec.json b/packages/react-components/react-motion-preview/tsconfig.spec.json new file mode 100644 index 00000000000000..a7e6c90d91b8ea --- /dev/null +++ b/packages/react-components/react-motion-preview/tsconfig.spec.json @@ -0,0 +1,17 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "CommonJS", + "outDir": "dist", + "types": ["jest", "node"] + }, + "include": [ + "**/*.spec.ts", + "**/*.spec.tsx", + "**/*.test.ts", + "**/*.test.tsx", + "**/*.d.ts", + "src/components/AnimatePresence/testing/**/*.ts", + "src/components/AnimatePresence/testing/**/*.tsx" + ] +} diff --git a/packages/react-components/react-utilities/etc/react-utilities.api.md b/packages/react-components/react-utilities/etc/react-utilities.api.md index 1d6150c8f9df44..7ceb4afc93cd8b 100644 --- a/packages/react-components/react-utilities/etc/react-utilities.api.md +++ b/packages/react-components/react-utilities/etc/react-utilities.api.md @@ -294,6 +294,9 @@ export type UnknownSlotProps = Pick, 'childr as?: keyof JSX.IntrinsicElements; }; +// @internal +export function useAnimationFrame(): readonly [(fn: () => void, delay?: number | undefined) => number, () => void]; + // @internal export const useControllableState: (options: UseControllableStateOptions) => [State, React_2.Dispatch>]; @@ -323,23 +326,7 @@ export const useIsomorphicLayoutEffect: typeof React_2.useEffect; export function useIsSSR(): boolean; // @public -export function useMergedRefs(...refs: (React_2.Ref | undefined)[]): RefObjectFunction; - -// @public -export const useMotionPresence: (present: boolean, options?: UseMotionPresenceOptions) => UseMotionPresenceState; - -// @public -export type UseMotionPresenceOptions = { - animateOnFirstMount?: boolean; -}; - -// @public -export type UseMotionPresenceState = { - ref: React_2.RefCallback; - shouldRender: boolean; - visible: boolean; - motionState: 'entering' | 'exiting' | 'resting' | 'unmounted'; -}; +export function useMergedRefs(...refs: (React_2.Ref | RefObjectFunction | undefined)[]): RefObjectFunction; // @internal (undocumented) export type UseOnClickOrScrollOutsideOptions = { @@ -366,7 +353,7 @@ export function useScrollbarWidth(options: UseScrollbarWidthOptions): number | u export function useSelection(params: SelectionHookParams): readonly [Set, SelectionMethods]; // @internal -export function useTimeout(): readonly [(fn: () => void, delay: number) => void, () => void]; +export function useTimeout(): readonly [(fn: () => void, delay?: number | undefined) => number, () => void]; // (No @packageDocumentation comment for this package) diff --git a/packages/react-components/react-utilities/src/hooks/index.ts b/packages/react-components/react-utilities/src/hooks/index.ts index c43fadc10fe58a..487ebcf72f8050 100644 --- a/packages/react-components/react-utilities/src/hooks/index.ts +++ b/packages/react-components/react-utilities/src/hooks/index.ts @@ -10,4 +10,4 @@ export * from './useOnScrollOutside'; export * from './usePrevious'; export * from './useScrollbarWidth'; export * from './useTimeout'; -export * from './useMotionPresence'; +export * from './useAnimationFrame'; diff --git a/packages/react-components/react-utilities/src/hooks/useMergedRefs.ts b/packages/react-components/react-utilities/src/hooks/useMergedRefs.ts index b2ae3599b3faf7..a8366ce33b21d5 100644 --- a/packages/react-components/react-utilities/src/hooks/useMergedRefs.ts +++ b/packages/react-components/react-utilities/src/hooks/useMergedRefs.ts @@ -12,7 +12,7 @@ export type RefObjectFunction = React.RefObject & ((value: T) => void); * @param refs - Refs to collectively update with one ref value. * @returns A function with an attached "current" prop, so that it can be treated like a RefObject. */ -export function useMergedRefs(...refs: (React.Ref | undefined)[]): RefObjectFunction { +export function useMergedRefs(...refs: (React.Ref | RefObjectFunction | undefined)[]): RefObjectFunction { const mergedCallback: RefObjectFunction = React.useCallback( (value: T) => { // Update the "current" prop hanging on the function. diff --git a/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts b/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts deleted file mode 100644 index f018b6c8ae8673..00000000000000 --- a/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts +++ /dev/null @@ -1,247 +0,0 @@ -import { act, renderHook } from '@testing-library/react-hooks'; - -import { useMotionPresence, UseMotionPresenceOptions } from './useMotionPresence'; - -const defaultDuration = 100; -const renderHookWithRef = ( - initialPresence: boolean, - initialOptions?: UseMotionPresenceOptions, - style: Record = { 'transition-duration': `${defaultDuration}ms` }, -) => { - const refEl = document.createElement('div'); - const hook = renderHook(({ presence, options }) => useMotionPresence(presence, options), { - initialProps: { - presence: initialPresence, - options: initialOptions, - } as { - presence: boolean; - options?: UseMotionPresenceOptions; - }, - }); - - Object.entries(style).forEach(([key, value]) => value && refEl.style.setProperty(key, value)); - - act(() => hook.result.current.ref(refEl)); - - return hook; -}; - -describe('useMotionPresence', () => { - beforeEach(() => { - jest.useFakeTimers(); - }); - - afterEach(() => { - jest.useRealTimers(); - }); - - describe('when presence is false by default', () => { - it('should return default values when presence is false', () => { - const { result } = renderHookWithRef(false); - - expect(typeof result.current.ref).toBe('function'); - expect(result.current.motionState).toBe('unmounted'); - expect(result.current.shouldRender).toBe(false); - expect(result.current.visible).toBe(false); - }); - }); - - describe('when presence is true by default', () => { - it('should return default values', () => { - const { result } = renderHookWithRef(true); - - expect(typeof result.current.ref).toBe('function'); - expect(result.current.motionState).toBe('resting'); - expect(result.current.shouldRender).toBe(true); - expect(result.current.visible).toBe(true); - }); - - it('should not change values after timeout ', () => { - const { result } = renderHookWithRef(true); - - const assertSameValues = () => { - expect(typeof result.current.ref).toBe('function'); - expect(result.current.motionState).toBe('resting'); - expect(result.current.shouldRender).toBe(true); - expect(result.current.visible).toBe(true); - }; - - assertSameValues(); - act(() => jest.advanceTimersToNextTimer()); - act(() => jest.advanceTimersToNextTimer()); - assertSameValues(); - }); - - it('should change visible to true when animateOnFirstMount is true', () => { - const { result } = renderHookWithRef(true, { animateOnFirstMount: true }); - - expect(typeof result.current.ref).toBe('function'); - expect(result.current.motionState).toBe('resting'); - expect(result.current.shouldRender).toBe(true); - expect(result.current.visible).toBe(false); - - act(() => jest.advanceTimersToNextTimer()); - - expect(result.current.visible).toBe(true); - }); - }); - - describe('when presence changes', () => { - it('should toggle values starting with false', () => { - const { result, rerender } = renderHookWithRef(false); - - expect(typeof result.current.ref).toBe('function'); - expect(result.current.motionState).toBe('unmounted'); - expect(result.current.shouldRender).toBe(false); - expect(result.current.visible).toBe(false); - - rerender({ presence: true }); - - expect(result.current.shouldRender).toBe(true); - expect(result.current.motionState).toBe('resting'); - - // double requestAnimationFrame - act(() => jest.advanceTimersToNextTimer()); - expect(result.current.visible).toBe(true); - - rerender({ presence: false }); - - act(() => jest.advanceTimersToNextTimer()); - expect(result.current.motionState).toBe('exiting'); - expect(result.current.visible).toBe(false); - - act(() => { - // requestAnimationFrame - act(() => jest.advanceTimersToNextTimer()); - - // timeout - jest.advanceTimersByTime(defaultDuration + 1); - }); - - expect(result.current.motionState).toBe('unmounted'); - expect(result.current.shouldRender).toBe(false); - expect(result.current.visible).toBe(false); - }); - - it('should toggle values starting with true', () => { - const { result, rerender } = renderHookWithRef(true); - - expect(typeof result.current.ref).toBe('function'); - expect(result.current.motionState).toBe('resting'); - expect(result.current.shouldRender).toBe(true); - expect(result.current.visible).toBe(true); - - rerender({ presence: false }); - - // requestAnimationFrame - act(() => jest.advanceTimersToNextTimer()); - - expect(result.current.motionState).toBe('exiting'); - expect(result.current.visible).toBe(false); - - act(() => { - // requestAnimationFrame - jest.advanceTimersToNextTimer(); - - // timeout - jest.advanceTimersByTime(defaultDuration + 1); - }); - - expect(result.current.motionState).toBe('unmounted'); - expect(result.current.shouldRender).toBe(false); - expect(result.current.visible).toBe(false); - }); - }); - - describe.each([ - { message: 'with transition', styles: { 'transition-duration': '100ms' } }, - { message: 'with long transition', styles: { 'transition-duration': '1000ms' } }, - { message: 'with animation', styles: { 'animation-duration': '100ms' } }, - { message: 'with long animation', styles: { 'animation-duration': '1000ms' } }, - ])('when presence changes - $message', ({ styles }) => { - it('should toggle values starting with false when animateOnFirstMount is true', () => { - const { result, rerender } = renderHookWithRef( - false, - { - animateOnFirstMount: true, - }, - styles, - ); - - expect(typeof result.current.ref).toBe('function'); - expect(result.current.motionState).toBe('unmounted'); - expect(result.current.shouldRender).toBe(false); - expect(result.current.visible).toBe(false); - - rerender({ presence: true }); - - expect(result.current.shouldRender).toBe(true); - expect(result.current.motionState).toBe('resting'); - // requestAnimationFrame - act(() => jest.advanceTimersToNextTimer()); - expect(result.current.visible).toBe(true); - expect(result.current.motionState).toBe('entering'); - // timeout - act(() => jest.advanceTimersToNextTimer()); - expect(result.current.motionState).toBe('resting'); - - rerender({ presence: false }); - - act(() => jest.advanceTimersToNextTimer()); - expect(result.current.visible).toBe(false); - expect(result.current.motionState).toBe('exiting'); - - // requestAnimationFrame - act(() => jest.advanceTimersToNextTimer()); - // timeout - act(() => jest.advanceTimersToNextTimer()); - expect(result.current.motionState).toBe('unmounted'); - expect(result.current.shouldRender).toBe(false); - }); - }); - - describe.each([ - { message: 'with no transition', styles: { 'transition-duration': '0' } }, - { message: 'with no animation', styles: { 'animation-duration': '0' } }, - ])('when presence changes - $message', ({ styles }) => { - it('should toggle values when transition-duration is 0', () => { - const { result, rerender } = renderHookWithRef( - false, - { - animateOnFirstMount: true, - }, - styles, - ); - - expect(typeof result.current.ref).toBe('function'); - expect(result.current.motionState).toBe('unmounted'); - expect(result.current.shouldRender).toBe(false); - expect(result.current.visible).toBe(false); - - rerender({ presence: true }); - - expect(result.current.shouldRender).toBe(true); - expect(result.current.motionState).toBe('resting'); - // requestAnimationFrame - act(() => jest.advanceTimersToNextTimer()); - act(() => jest.advanceTimersToNextTimer()); - expect(result.current.visible).toBe(true); - // timeout - act(() => jest.advanceTimersToNextTimer()); - expect(result.current.motionState).toBe('resting'); - - rerender({ presence: false }); - - act(() => jest.advanceTimersToNextTimer()); - act(() => jest.advanceTimersToNextTimer()); - expect(result.current.visible).toBe(false); - - // requestAnimationFrame - act(() => jest.advanceTimersToNextTimer()); - // timeout - act(() => jest.advanceTimersToNextTimer()); - expect(result.current.motionState).toBe('unmounted'); - expect(result.current.shouldRender).toBe(false); - }); - }); -}); diff --git a/packages/react-components/react-utilities/src/index.ts b/packages/react-components/react-utilities/src/index.ts index 56fe2286e0d15c..bee5d0eb364bca 100644 --- a/packages/react-components/react-utilities/src/index.ts +++ b/packages/react-components/react-utilities/src/index.ts @@ -42,15 +42,9 @@ export { usePrevious, useScrollbarWidth, useTimeout, - useMotionPresence, -} from './hooks/index'; -export type { - RefObjectFunction, - UseControllableStateOptions, - UseOnClickOrScrollOutsideOptions, - UseMotionPresenceOptions, - UseMotionPresenceState, + useAnimationFrame, } from './hooks/index'; +export type { RefObjectFunction, UseControllableStateOptions, UseOnClickOrScrollOutsideOptions } from './hooks/index'; export { canUseDOM, useIsSSR, SSRProvider } from './ssr/index'; diff --git a/tsconfig.base.all.json b/tsconfig.base.all.json index d885a03a78aaaa..eaa0d6c402d0a2 100644 --- a/tsconfig.base.all.json +++ b/tsconfig.base.all.json @@ -118,6 +118,7 @@ "@fluentui/react-menu": ["packages/react-components/react-menu/src/index.ts"], "@fluentui/react-migration-v0-v9": ["packages/react-components/react-migration-v0-v9/src/index.ts"], "@fluentui/react-migration-v8-v9": ["packages/react-components/react-migration-v8-v9/src/index.ts"], + "@fluentui/react-motion-preview": ["packages/react-components/react-motion-preview/src/index.ts"], "@fluentui/react-overflow": ["packages/react-components/react-overflow/src/index.ts"], "@fluentui/react-persona": ["packages/react-components/react-persona/src/index.ts"], "@fluentui/react-popover": ["packages/react-components/react-popover/src/index.ts"], diff --git a/tsconfig.base.json b/tsconfig.base.json index 4819b7aea14183..c8c42cf0e81c38 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -52,6 +52,7 @@ "@fluentui/react-menu": ["packages/react-components/react-menu/src/index.ts"], "@fluentui/react-migration-v0-v9": ["packages/react-components/react-migration-v0-v9/src/index.ts"], "@fluentui/react-migration-v8-v9": ["packages/react-components/react-migration-v8-v9/src/index.ts"], + "@fluentui/react-motion-preview": ["packages/react-components/react-motion-preview/src/index.ts"], "@fluentui/react-overflow": ["packages/react-components/react-overflow/src/index.ts"], "@fluentui/react-persona": ["packages/react-components/react-persona/src/index.ts"], "@fluentui/react-popover": ["packages/react-components/react-popover/src/index.ts"], From ab9ff66eaa9cc393a76d4e7c7882121a2157eaa6 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Thu, 17 Aug 2023 18:06:54 +0200 Subject: [PATCH 067/111] feat: implement useMotion hook instead of useMotionPresence --- .github/CODEOWNERS | 3 +- .../react-motion-preview/.babelrc.json | 4 + .../react-motion-preview/.eslintrc.json | 4 + .../react-motion-preview/.npmignore | 38 ++ .../react-motion-preview/.storybook/main.js | 14 + .../.storybook/preview.js | 7 + .../.storybook/tsconfig.json | 10 + .../react-motion-preview/.swcrc | 30 ++ .../react-motion-preview/LICENSE | 15 + .../react-motion-preview/README.md | 5 + .../config/api-extractor.json | 4 + .../react-motion-preview/config/tests.js | 1 + .../etc/react-motion-preview.api.md | 28 + .../react-motion-preview/jest.config.js | 21 + .../react-motion-preview/just.config.ts | 5 + .../react-motion-preview/package.json | 61 +++ .../react-motion-preview/project.json | 8 + .../react-motion-preview/src/hooks/index.ts | 1 + .../src/hooks/useMotion.test.ts | 237 +++++++++ .../src/hooks/useMotion.ts | 484 ++++++++++++++++++ .../react-motion-preview/src/index.ts | 2 + .../useMotion/UseMotionBestPractices.md | 5 + .../useMotion/UseMotionDefault.stories.tsx | 60 +++ .../stories/useMotion/UseMotionDescription.md | 0 .../stories/useMotion/index.stories.tsx | 15 + .../react-motion-preview/tsconfig.json | 25 + .../react-motion-preview/tsconfig.lib.json | 22 + .../react-motion-preview/tsconfig.spec.json | 17 + tsconfig.base.all.json | 1 + tsconfig.base.json | 1 + 30 files changed, 1127 insertions(+), 1 deletion(-) create mode 100644 packages/react-components/react-motion-preview/.babelrc.json create mode 100644 packages/react-components/react-motion-preview/.eslintrc.json create mode 100644 packages/react-components/react-motion-preview/.npmignore create mode 100644 packages/react-components/react-motion-preview/.storybook/main.js create mode 100644 packages/react-components/react-motion-preview/.storybook/preview.js create mode 100644 packages/react-components/react-motion-preview/.storybook/tsconfig.json create mode 100644 packages/react-components/react-motion-preview/.swcrc create mode 100644 packages/react-components/react-motion-preview/LICENSE create mode 100644 packages/react-components/react-motion-preview/README.md create mode 100644 packages/react-components/react-motion-preview/config/api-extractor.json create mode 100644 packages/react-components/react-motion-preview/config/tests.js create mode 100644 packages/react-components/react-motion-preview/etc/react-motion-preview.api.md create mode 100644 packages/react-components/react-motion-preview/jest.config.js create mode 100644 packages/react-components/react-motion-preview/just.config.ts create mode 100644 packages/react-components/react-motion-preview/package.json create mode 100644 packages/react-components/react-motion-preview/project.json create mode 100644 packages/react-components/react-motion-preview/src/hooks/index.ts create mode 100644 packages/react-components/react-motion-preview/src/hooks/useMotion.test.ts create mode 100644 packages/react-components/react-motion-preview/src/hooks/useMotion.ts create mode 100644 packages/react-components/react-motion-preview/src/index.ts create mode 100644 packages/react-components/react-motion-preview/stories/useMotion/UseMotionBestPractices.md create mode 100644 packages/react-components/react-motion-preview/stories/useMotion/UseMotionDefault.stories.tsx create mode 100644 packages/react-components/react-motion-preview/stories/useMotion/UseMotionDescription.md create mode 100644 packages/react-components/react-motion-preview/stories/useMotion/index.stories.tsx create mode 100644 packages/react-components/react-motion-preview/tsconfig.json create mode 100644 packages/react-components/react-motion-preview/tsconfig.lib.json create mode 100644 packages/react-components/react-motion-preview/tsconfig.spec.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index a217cfad365b75..7f96317e797c90 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -232,13 +232,14 @@ packages/react-components/react-migration-v0-v9 @microsoft/teams-prg packages/react-components/react-datepicker-compat @microsoft/cxe-red @sopranopillow @khmakoto packages/react-components/react-migration-v8-v9 @microsoft/cxe-red @microsoft/cxe-coastal @geoffcoxmsft packages/react-components/react-breadcrumb-preview @microsoft/cxe-prg -packages/react-components/react-drawer @microsoft/cxe-prg +packages/react-components/react-drawer @microsoft/cxe-prg @marcosmoura packages/react-components/react-storybook-addon-codesandbox @microsoft/fluentui-react-build packages/react-components/babel-preset-storybook-full-source @microsoft/fluentui-react-build packages/react-components/react-jsx-runtime @microsoft/teams-prg packages/react-components/react-toast @microsoft/teams-prg packages/react-components/react-search-preview @microsoft/cxe-coastal packages/react-components/react-colorpicker-compat @microsoft/cxe-red @sopranopillow +packages/react-components/react-motion-preview @microsoft/cxe-prg @marcosmoura packages/react-components/react-nav-preview @microsoft/cxe-red @mltejera # <%= NX-CODEOWNER-PLACEHOLDER %> diff --git a/packages/react-components/react-motion-preview/.babelrc.json b/packages/react-components/react-motion-preview/.babelrc.json new file mode 100644 index 00000000000000..45fb71ca16d2c3 --- /dev/null +++ b/packages/react-components/react-motion-preview/.babelrc.json @@ -0,0 +1,4 @@ +{ + "extends": "../../../.babelrc-v9.json", + "plugins": ["annotate-pure-calls", "@babel/transform-react-pure-annotations"] +} diff --git a/packages/react-components/react-motion-preview/.eslintrc.json b/packages/react-components/react-motion-preview/.eslintrc.json new file mode 100644 index 00000000000000..ceea884c70dccc --- /dev/null +++ b/packages/react-components/react-motion-preview/.eslintrc.json @@ -0,0 +1,4 @@ +{ + "extends": ["plugin:@fluentui/eslint-plugin/react"], + "root": true +} diff --git a/packages/react-components/react-motion-preview/.npmignore b/packages/react-components/react-motion-preview/.npmignore new file mode 100644 index 00000000000000..a5817be2414dec --- /dev/null +++ b/packages/react-components/react-motion-preview/.npmignore @@ -0,0 +1,38 @@ +.storybook/ +.vscode/ +bundle-size/ +config/ +coverage/ +docs/ +etc/ +node_modules/ +src/ +stories/ +dist/types/ +temp/ +__fixtures__ +__mocks__ +__tests__ + +*.api.json +*.log +*.spec.* +*.cy.* +*.test.* +*.yml + +# config files +*config.* +*rc.* +.editorconfig +.eslint* +.git* +.prettierignore +.swcrc +project.json + +# exclude gitignore patterns explicitly +!lib +!lib-commonjs +!lib-amd +!dist/*.d.ts diff --git a/packages/react-components/react-motion-preview/.storybook/main.js b/packages/react-components/react-motion-preview/.storybook/main.js new file mode 100644 index 00000000000000..26536b61b387f6 --- /dev/null +++ b/packages/react-components/react-motion-preview/.storybook/main.js @@ -0,0 +1,14 @@ +const rootMain = require('../../../../.storybook/main'); + +module.exports = /** @type {Omit} */ ({ + ...rootMain, + stories: [...rootMain.stories, '../stories/**/*.stories.mdx', '../stories/**/index.stories.@(ts|tsx)'], + addons: [...rootMain.addons], + webpackFinal: (config, options) => { + const localConfig = { ...rootMain.webpackFinal(config, options) }; + + // add your own webpack tweaks if needed + + return localConfig; + }, +}); diff --git a/packages/react-components/react-motion-preview/.storybook/preview.js b/packages/react-components/react-motion-preview/.storybook/preview.js new file mode 100644 index 00000000000000..1939500a3d18c7 --- /dev/null +++ b/packages/react-components/react-motion-preview/.storybook/preview.js @@ -0,0 +1,7 @@ +import * as rootPreview from '../../../../.storybook/preview'; + +/** @type {typeof rootPreview.decorators} */ +export const decorators = [...rootPreview.decorators]; + +/** @type {typeof rootPreview.parameters} */ +export const parameters = { ...rootPreview.parameters }; diff --git a/packages/react-components/react-motion-preview/.storybook/tsconfig.json b/packages/react-components/react-motion-preview/.storybook/tsconfig.json new file mode 100644 index 00000000000000..ea89218a3d916f --- /dev/null +++ b/packages/react-components/react-motion-preview/.storybook/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "", + "allowJs": true, + "checkJs": true, + "types": ["static-assets", "environment", "storybook__addons"] + }, + "include": ["../stories/**/*.stories.ts", "../stories/**/*.stories.tsx", "*.js"] +} diff --git a/packages/react-components/react-motion-preview/.swcrc b/packages/react-components/react-motion-preview/.swcrc new file mode 100644 index 00000000000000..b4ffa86dee3067 --- /dev/null +++ b/packages/react-components/react-motion-preview/.swcrc @@ -0,0 +1,30 @@ +{ + "$schema": "https://json.schemastore.org/swcrc", + "exclude": [ + "/testing", + "/**/*.cy.ts", + "/**/*.cy.tsx", + "/**/*.spec.ts", + "/**/*.spec.tsx", + "/**/*.test.ts", + "/**/*.test.tsx" + ], + "jsc": { + "parser": { + "syntax": "typescript", + "tsx": true, + "decorators": false, + "dynamicImport": false + }, + "externalHelpers": true, + "transform": { + "react": { + "runtime": "classic", + "useSpread": true + } + }, + "target": "es2019" + }, + "minify": false, + "sourceMaps": true +} diff --git a/packages/react-components/react-motion-preview/LICENSE b/packages/react-components/react-motion-preview/LICENSE new file mode 100644 index 00000000000000..6789473dee14ce --- /dev/null +++ b/packages/react-components/react-motion-preview/LICENSE @@ -0,0 +1,15 @@ +@fluentui/react-motion-preview + +Copyright (c) Microsoft Corporation + +All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Note: Usage of the fonts and icons referenced in Fluent UI React is subject to the terms listed at https://aka.ms/fluentui-assets-license diff --git a/packages/react-components/react-motion-preview/README.md b/packages/react-components/react-motion-preview/README.md new file mode 100644 index 00000000000000..b4d66fbd602960 --- /dev/null +++ b/packages/react-components/react-motion-preview/README.md @@ -0,0 +1,5 @@ +# @fluentui/react-motion-preview + +**React Motion components for [Fluent UI React](https://react.fluentui.dev/)** + +These are not production-ready components and **should never be used in product**. This space is useful for testing new components whose APIs might change before final release. diff --git a/packages/react-components/react-motion-preview/config/api-extractor.json b/packages/react-components/react-motion-preview/config/api-extractor.json new file mode 100644 index 00000000000000..e533bf30b48a2b --- /dev/null +++ b/packages/react-components/react-motion-preview/config/api-extractor.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "@fluentui/scripts-api-extractor/api-extractor.common.v-next.json" +} diff --git a/packages/react-components/react-motion-preview/config/tests.js b/packages/react-components/react-motion-preview/config/tests.js new file mode 100644 index 00000000000000..2e211ae9e21420 --- /dev/null +++ b/packages/react-components/react-motion-preview/config/tests.js @@ -0,0 +1 @@ +/** Jest test setup file. */ diff --git a/packages/react-components/react-motion-preview/etc/react-motion-preview.api.md b/packages/react-components/react-motion-preview/etc/react-motion-preview.api.md new file mode 100644 index 00000000000000..32e3232c24a72d --- /dev/null +++ b/packages/react-components/react-motion-preview/etc/react-motion-preview.api.md @@ -0,0 +1,28 @@ +## API Report File for "@fluentui/react-motion-preview" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import * as React_2 from 'react'; +import { RefObjectFunction } from '@fluentui/react-utilities'; + +// @public (undocumented) +export type MotionOptions = { + animateOnFirstMount?: boolean; +}; + +// @internal +export type MotionProps = { + presence: boolean; + ref?: RefObjectFunction | React_2.RefCallback | React_2.Ref; + active?: boolean; + state?: 'unmounted' | 'entering' | 'entered' | 'idle' | 'exiting' | 'exited'; +}; + +// @internal +export const useMotion: (props: MotionProps, options?: MotionOptions) => Required>; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/react-components/react-motion-preview/jest.config.js b/packages/react-components/react-motion-preview/jest.config.js new file mode 100644 index 00000000000000..bdaab7ff09ee93 --- /dev/null +++ b/packages/react-components/react-motion-preview/jest.config.js @@ -0,0 +1,21 @@ +// @ts-check + +/** + * @type {import('@jest/types').Config.InitialOptions} + */ +module.exports = { + displayName: 'react-motion-preview', + preset: '../../../jest.preset.js', + transform: { + '^.+\\.tsx?$': [ + 'ts-jest', + { + tsconfig: '/tsconfig.spec.json', + isolatedModules: true, + }, + ], + }, + coverageDirectory: './coverage', + setupFilesAfterEnv: ['./config/tests.js'], + snapshotSerializers: ['@griffel/jest-serializer'], +}; diff --git a/packages/react-components/react-motion-preview/just.config.ts b/packages/react-components/react-motion-preview/just.config.ts new file mode 100644 index 00000000000000..b7b2c9a33bf435 --- /dev/null +++ b/packages/react-components/react-motion-preview/just.config.ts @@ -0,0 +1,5 @@ +import { preset, task } from '@fluentui/scripts-tasks'; + +preset(); + +task('build', 'build:react-components').cached?.(); diff --git a/packages/react-components/react-motion-preview/package.json b/packages/react-components/react-motion-preview/package.json new file mode 100644 index 00000000000000..88e5d44f3897b0 --- /dev/null +++ b/packages/react-components/react-motion-preview/package.json @@ -0,0 +1,61 @@ +{ + "name": "@fluentui/react-motion-preview", + "version": "0.0.0", + "private": true, + "description": "New fluentui react package", + "main": "lib-commonjs/index.js", + "module": "lib/index.js", + "typings": "./dist/index.d.ts", + "sideEffects": false, + "repository": { + "type": "git", + "url": "https://github.com/microsoft/fluentui" + }, + "license": "MIT", + "scripts": { + "build": "just-scripts build", + "clean": "just-scripts clean", + "generate-api": "just-scripts generate-api", + "lint": "just-scripts lint", + "start": "yarn storybook", + "storybook": "start-storybook", + "test": "jest --passWithNoTests", + "test-ssr": "test-ssr \"./stories/**/*.stories.tsx\"", + "type-check": "tsc -b tsconfig.json" + }, + "devDependencies": { + "@fluentui/eslint-plugin": "*", + "@fluentui/react-conformance": "*", + "@fluentui/react-conformance-griffel": "*", + "@fluentui/scripts-api-extractor": "*", + "@fluentui/scripts-tasks": "*" + }, + "dependencies": { + "@fluentui/react-jsx-runtime": "^9.0.0-alpha.13", + "@fluentui/react-theme": "^9.1.10", + "@fluentui/react-utilities": "^9.11.0", + "@griffel/react": "^1.5.7", + "@swc/helpers": "^0.4.14" + }, + "peerDependencies": { + "@types/react": ">=16.8.0 <19.0.0", + "@types/react-dom": ">=16.8.0 <19.0.0", + "react": ">=16.8.0 <19.0.0", + "react-dom": ">=16.8.0 <19.0.0" + }, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib/index.js", + "require": "./lib-commonjs/index.js" + }, + "./package.json": "./package.json" + }, + "beachball": { + "disallowedChangeTypes": [ + "major", + "prerelease" + ] + } +} diff --git a/packages/react-components/react-motion-preview/project.json b/packages/react-components/react-motion-preview/project.json new file mode 100644 index 00000000000000..b47a10e7e354f2 --- /dev/null +++ b/packages/react-components/react-motion-preview/project.json @@ -0,0 +1,8 @@ +{ + "name": "@fluentui/react-motion-preview", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "library", + "sourceRoot": "packages/react-components/react-motion-preview/src", + "tags": ["platform:web", "vNext"], + "implicitDependencies": [] +} diff --git a/packages/react-components/react-motion-preview/src/hooks/index.ts b/packages/react-components/react-motion-preview/src/hooks/index.ts new file mode 100644 index 00000000000000..4fb9f2d6d5f179 --- /dev/null +++ b/packages/react-components/react-motion-preview/src/hooks/index.ts @@ -0,0 +1 @@ +export * from './useMotion'; diff --git a/packages/react-components/react-motion-preview/src/hooks/useMotion.test.ts b/packages/react-components/react-motion-preview/src/hooks/useMotion.test.ts new file mode 100644 index 00000000000000..ecbef73a23c804 --- /dev/null +++ b/packages/react-components/react-motion-preview/src/hooks/useMotion.test.ts @@ -0,0 +1,237 @@ +import { act, renderHook } from '@testing-library/react-hooks'; + +import { useMotion, MotionProps, MotionOptions } from './useMotion'; + +const defaultDuration = 100; +const renderHookWithRef = ( + initialState: MotionProps, + initialOptions?: MotionOptions, + style: Record = { 'transition-duration': `${defaultDuration}ms` }, +) => { + const refEl = document.createElement('div'); + const hook = renderHook(({ state, options }) => useMotion(state, options), { + initialProps: { + state: initialState, + options: initialOptions, + } as { state: MotionProps; options?: MotionOptions }, + }); + + Object.entries(style).forEach(([key, value]) => value && refEl.style.setProperty(key, value)); + + act(() => { + if (typeof hook.result.current.ref === 'function') { + hook.result.current.ref(refEl); + } + }); + + return hook; +}; + +const jumpToNextFrame = () => { + act(() => { + // requestAnimationFrame + timeout callbacks + jest.advanceTimersToNextTimer(); + jest.advanceTimersToNextTimer(); + }); +}; + +describe('useMotion', () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + describe('when presence is false by default', () => { + it('should return default values when presence is false', () => { + const { result } = renderHookWithRef({ presence: false }); + + expect(typeof result.current.ref).toBe('function'); + expect(result.current.state).toBe('unmounted'); + expect(result.current.active).toBe(false); + }); + }); + + describe('when presence is true by default', () => { + it('should return default values', () => { + const { result } = renderHookWithRef({ presence: true }); + + expect(typeof result.current.ref).toBe('function'); + expect(result.current.active).toBe(true); + }); + + it('should change visible to true when animateOnFirstMount is true', () => { + const { result } = renderHookWithRef({ presence: true }, { animateOnFirstMount: true }); + + expect(typeof result.current.ref).toBe('function'); + expect(result.current.active).toBe(false); + + jumpToNextFrame(); + + expect(result.current.active).toBe(true); + }); + }); + + describe('when presence changes', () => { + it('should toggle values starting with false', () => { + const { result, rerender } = renderHookWithRef({ presence: false }); + + expect(typeof result.current.ref).toBe('function'); + expect(result.current.state).toBe('unmounted'); + expect(result.current.active).toBe(false); + + rerender({ state: { presence: true } }); + + act(() => jest.advanceTimersToNextTimer()); + + expect(result.current.state).toBe('entering'); + expect(result.current.active).toBe(true); + + act(() => jest.advanceTimersByTime(defaultDuration + 1)); + expect(result.current.state).toBe('entered'); + + act(() => jest.advanceTimersToNextTimer()); + + expect(result.current.state).toBe('idle'); + + rerender({ state: { presence: false } }); + + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.state).toBe('exiting'); + expect(result.current.active).toBe(false); + + act(() => jest.advanceTimersByTime(defaultDuration + 1)); + expect(result.current.state).toBe('exited'); + + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.state).toBe('unmounted'); + expect(result.current.active).toBe(false); + }); + + it('should toggle values starting with true', () => { + const { result, rerender } = renderHookWithRef({ presence: true }); + + expect(typeof result.current.ref).toBe('function'); + expect(result.current.active).toBe(true); + + rerender({ state: { presence: false } }); + + act(() => jest.advanceTimersToNextTimer()); + + expect(result.current.state).toBe('exiting'); + expect(result.current.active).toBe(false); + + act(() => jest.advanceTimersByTime(defaultDuration + 1)); + expect(result.current.state).toBe('exited'); + + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.state).toBe('unmounted'); + expect(result.current.active).toBe(false); + }); + }); + + describe.each([ + { message: 'with transition', styles: { 'transition-duration': '100ms' } }, + { message: 'with long transition', styles: { 'transition-duration': '1000ms' } }, + { message: 'with animation', styles: { 'animation-duration': '100ms' } }, + { message: 'with long animation', styles: { 'animation-duration': '1000ms' } }, + ])('when presence changes - $message', ({ styles }) => { + it('should toggle values starting with false', () => { + const { result, rerender } = renderHookWithRef({ presence: false }, {}, styles); + + expect(typeof result.current.ref).toBe('function'); + expect(result.current.state).toBe('unmounted'); + expect(result.current.active).toBe(false); + + rerender({ state: { presence: true } }); + + act(() => jest.advanceTimersToNextTimer()); + + expect(result.current.state).toBe('entering'); + expect(result.current.active).toBe(true); + + act(() => jest.advanceTimersToNextTimer()); + + expect(result.current.state).toBe('entered'); + + act(() => jest.advanceTimersToNextTimer()); + + expect(result.current.state).toBe('idle'); + + rerender({ state: { presence: false } }); + + act(() => jest.advanceTimersToNextTimer()); + + expect(result.current.state).toBe('exiting'); + expect(result.current.active).toBe(false); + + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.state).toBe('exited'); + + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.state).toBe('unmounted'); + expect(result.current.active).toBe(false); + }); + }); + + describe.each([ + { message: 'with no transition', styles: { 'transition-duration': '0' } }, + { message: 'with no animation', styles: { 'animation-duration': '0' } }, + ])('when presence changes - $message', ({ styles }) => { + it('should toggle values when transition-duration is 0', () => { + const { result, rerender } = renderHookWithRef({ presence: false }, {}, styles); + + expect(typeof result.current.ref).toBe('function'); + expect(result.current.state).toBe('unmounted'); + expect(result.current.active).toBe(false); + + rerender({ state: { presence: true } }); + + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.state).toBe('entered'); + + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.state).toBe('idle'); + + // requestAnimationFrame + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.active).toBe(true); + // timeout + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.state).toBe('idle'); + + rerender({ state: { presence: false } }); + + act(() => jest.advanceTimersToNextTimer()); + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.active).toBe(false); + + // requestAnimationFrame + act(() => jest.advanceTimersToNextTimer()); + // timeout + act(() => jest.advanceTimersToNextTimer()); + expect(result.current.state).toBe('unmounted'); + }); + }); + + describe('when motion is received', () => { + it('should return default values when presence is false', () => { + const { result } = renderHookWithRef({ presence: false, state: 'unmounted', active: false }); + + expect(typeof result.current.ref).toBe('function'); + expect(result.current.state).toBe('unmounted'); + expect(result.current.presence).toBe(false); + expect(result.current.active).toBe(false); + }); + + it('should return default values when presence is true', () => { + const { result } = renderHookWithRef({ presence: true, state: 'idle', active: true }); + + expect(typeof result.current.ref).toBe('function'); + expect(result.current.presence).toBe(true); + expect(result.current.active).toBe(true); + }); + }); +}); diff --git a/packages/react-components/react-motion-preview/src/hooks/useMotion.ts b/packages/react-components/react-motion-preview/src/hooks/useMotion.ts new file mode 100644 index 00000000000000..a42087363f9de5 --- /dev/null +++ b/packages/react-components/react-motion-preview/src/hooks/useMotion.ts @@ -0,0 +1,484 @@ +import * as React from 'react'; + +import { + canUseDOM, + RefObjectFunction, + useAnimationFrame, + useMergedRefs, + usePrevious, + useTimeout, +} from '@fluentui/react-utilities'; + +/** + * CSS Typed Object Model + * @see https://drafts.css-houdini.org/css-typed-om-1/ + * @see https://developer.mozilla.org/en-US/docs/Web/API/CSSUnitValue + */ +interface CSSUnitValue { + value: number; + readonly unit: string; +} + +/** + * Style property map read only. + * @see https://developer.mozilla.org/en-US/docs/Web/API/StylePropertyMapReadOnly + */ +interface StylePropertyMapReadOnly { + [Symbol.iterator](): IterableIterator<[string, CSSUnitValue[]]>; + + get(property: string): CSSUnitValue | undefined; + getAll(property: string): CSSUnitValue[]; + has(property: string): boolean; + readonly size: number; +} + +/** + * HTMLElement with styled map. + * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/computedStyleMap + */ +type HTMLElementWithStyledMap = T & { + computedStyleMap(): StylePropertyMapReadOnly; +}; + +/** + * CSS with number parsing. + * @see https://drafts.css-houdini.org/css-typed-om-1/#css + * @see https://developer.mozilla.org/en-US/docs/Web/API/CSS/number + */ +type CSSWithNumber = typeof CSS & { + number(value: number): { + value: number; + readonly unit: string; + }; +}; + +/** + * @internal + * + * Motion props. + */ +export type MotionProps = { + /** + * Whether the element should be present in the DOM. + * + * @default false + */ + presence: boolean; + + /** + * Ref to the element. + * + * @example + * const motion = useMotion({ presence: isOpen }); + * + *
+ */ + ref?: RefObjectFunction | React.RefCallback | React.Ref; + + /** + * Whether the element is currently active in the DOM. + * Useful to apply CSS transitions only when the element is active. + * + * @example + * const motion = useMotion({ presence: isOpen }); + * + *
+ */ + active?: boolean; + + /** + * Current state of the element. + * + * - `unmounted` - The element is not yet rendered or can be safely removed from the DOM. + * - `entering` - The element is performing enter animation. + * - `entered` - The element has finished enter animation. + * - `idle` - The element is currently not animating, but rendered on screen. + * - `exiting` - The element is performing exit animation. + * - `exited` - The element has finished exit animation. + * + * @example + * const motion = useMotion({ presence: isOpen }); + * + *
+ */ + state?: 'unmounted' | 'entering' | 'entered' | 'idle' | 'exiting' | 'exited'; +}; + +export type MotionOptions = { + /** + * Whether to animate the element on first mount. + * + * @default false + */ + animateOnFirstMount?: boolean; +}; + +/** + * @internal + * + * Returns CSS styles of the given node. + * @param node - DOM node. + * @returns - CSS styles. + */ +const getElementComputedStyle = (node: HTMLElement): CSSStyleDeclaration => { + const win = node.ownerDocument?.defaultView ? node.ownerDocument.defaultView : window; + + if (!win || !canUseDOM()) { + return { + getPropertyValue: (_: string) => '', + } as CSSStyleDeclaration; + } + + return win.getComputedStyle(node, null); +}; + +/** + * Converts a CSS duration string to milliseconds. + * + * @param duration - CSS duration string + * @returns Duration in milliseconds + */ +function toMs(duration: string): number { + const trimmed = duration.trim(); + + if (trimmed.includes('auto')) { + return 0; + } + + if (trimmed.includes('ms')) { + return parseFloat(trimmed); + } + + return Number(trimmed.slice(0, -1).replace(',', '.')) * 1000; +} + +/** + * Checks if the browser supports CSSOM. + * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/computedStyleMap + * + * @param node - DOM node + * @returns Whether the browser supports CSSOM + */ +const hasCSSOMSupport = (node: HTMLElementWithStyledMap) => { + /** + * As we are using the experimental CSSOM API, we need to check if the browser supports it. + * The typecast here is to allow the use of the `number` function that is not yet part of the CSSOM typings. + * @see https://www.npmjs.com/package/@types/w3c-css-typed-object-model-level-1 + */ + return Boolean(typeof CSS !== 'undefined' && (CSS as CSSWithNumber).number && node.computedStyleMap); +}; + +/** + * + * Gets the computed style of a given element. + * If the browser supports CSSOM, it will return a ComputedStyleMap object. + * Otherwise, it will return a CSSStyleDeclaration object. + */ +const getCSSStyle = (node: HTMLElementWithStyledMap): CSSStyleDeclaration | StylePropertyMapReadOnly => { + if (hasCSSOMSupport(node)) { + return node.computedStyleMap(); + } + + return getElementComputedStyle(node); +}; + +/** + * Gets the computed map property for a given element using the CSSOM API. + * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/computedStyleMap + * + * @param computedStyle - Computed style of the element + * @param prop - CSS property + * @returns Computed map property + */ +const getComputedMapProp = (computedStyle: StylePropertyMapReadOnly, prop: string): string[] => { + const props = computedStyle.getAll(prop); + + if (props.length > 0) { + return props.map(({ value, unit }) => `${value}${unit}`); + } + + return ['0']; +}; + +/** + * Gets the computed style property for a given element using the getComputedStyle API. + * + * @param computedStyle - Computed style of the element + * @param prop - CSS property + * @returns Computed style property + */ +const getComputedStyleProp = (computedStyle: CSSStyleDeclaration, prop: string): string[] => { + const propValue = computedStyle.getPropertyValue(prop); + + return propValue ? propValue.split(',') : ['0']; +}; + +/** + * Gets the maximum duration from a list of CSS durations. + * + * @param durations - List of CSS durations + * @param delays - List of CSS delays + * @returns Maximum duration + */ +const getMaxCSSDuration = (durations: string[], delays: string[]): number => { + const totalProps = Math.max(durations.length, delays.length); + const totalDurations = []; + + if (totalProps === 0) { + return 0; + } + + for (let i = 0; i < totalProps; i++) { + const duration = toMs(durations[i] || '0'); + const delay = toMs(delays[i] || '0'); + + totalDurations.push(duration + delay); + } + + return Math.max(...totalDurations); +}; + +/** + * Gets the motion information for a given element. + * + * @param computedStyle - Computed style of the element + * @returns motion information + */ +const getMotionDuration = (node: HTMLElementWithStyledMap) => { + const hasModernCSSSupport = hasCSSOMSupport(node); + const computedStyle = getCSSStyle(node); + + const getProp = (prop: string): string[] => { + return hasModernCSSSupport + ? getComputedMapProp(computedStyle as StylePropertyMapReadOnly, prop) + : getComputedStyleProp(computedStyle as CSSStyleDeclaration, prop); + }; + + const transitionDuration = getMaxCSSDuration(getProp('transition-duration'), getProp('transition-delay')); + const animationDuration = getMaxCSSDuration(getProp('animation-duration'), getProp('animation-delay')); + + return Math.max(transitionDuration, animationDuration); +}; + +/** + * @internal + * + * Hook to manage the presence of an element in the DOM based on its CSS transition/animation state. + * + * @param present - Whether the element should be present in the DOM + * @param events - Callbacks for when the element enters or exits the DOM + */ +const useMotionPresence = ( + present: boolean, + options: MotionOptions = {}, +): Required, 'presence'>> => { + const { animateOnFirstMount } = { animateOnFirstMount: false, ...options }; + + const [state, setState] = React.useState>>({ + state: present ? 'idle' : 'unmounted', + active: false, + }); + + const [currentElement, setCurrentElement] = React.useState | null>(null); + const [setAnimationTimeout, clearAnimationTimeout] = useTimeout(); + const [setActiveAnimationFrame, cancelActiveAnimationFrame] = useAnimationFrame(); + const [setProcessingAnimationFrame, cancelProcessingAnimationFrame] = useAnimationFrame(); + const [setDelayedAnimationFrame, cancelDelayedAnimationFrame] = useAnimationFrame(); + const skipAnimationOnFirstRender = React.useRef(!animateOnFirstMount); + + const processAnimation = React.useCallback( + (callback: () => void) => { + const targetElement = currentElement; + + if (!targetElement) { + return; + } + + clearAnimationTimeout(); + cancelProcessingAnimationFrame(); + setProcessingAnimationFrame(() => { + const duration = getMotionDuration(targetElement); + + if (duration === 0) { + callback(); + return; + } + + /** + * Use CSS transition duration + 1ms to ensure the animation has finished on both enter and exit states. + * This is an alternative to using the `transitionend` event which can be unreliable as it fires multiple times + * if the transition has multiple properties. + */ + setAnimationTimeout(() => callback(), duration + 1); + }); + + return () => { + clearAnimationTimeout(); + cancelProcessingAnimationFrame(); + }; + }, + [ + cancelProcessingAnimationFrame, + clearAnimationTimeout, + currentElement, + setAnimationTimeout, + setProcessingAnimationFrame, + ], + ); + + const ref: React.RefCallback> = React.useCallback(node => { + if (!node) { + return; + } + + setCurrentElement(node); + }, []); + + React.useEffect(() => { + if (present) { + setState(prevState => ({ + ...prevState, + state: 'entering', + active: skipAnimationOnFirstRender.current ? true : false, + })); + } + }, [present]); + + React.useEffect(() => { + const skipAnimation = skipAnimationOnFirstRender.current; + const onUnmount = () => { + cancelActiveAnimationFrame(); + cancelDelayedAnimationFrame(); + }; + + setActiveAnimationFrame(() => { + setState(prevState => { + let newState = prevState.state; + + if (skipAnimation) { + newState = present ? 'idle' : 'unmounted'; + } else { + newState = present ? 'entering' : 'exiting'; + } + + return { + state: newState, + active: present, + }; + }); + }); + + if (skipAnimation) { + return onUnmount; + } + + processAnimation(() => { + setState(prevState => ({ + ...prevState, + state: present ? 'entered' : 'exited', + })); + + setDelayedAnimationFrame(() => { + setState(prevState => ({ + ...prevState, + state: present ? 'idle' : 'unmounted', + })); + }); + }); + + return onUnmount; + }, [ + cancelActiveAnimationFrame, + cancelDelayedAnimationFrame, + present, + processAnimation, + setActiveAnimationFrame, + setDelayedAnimationFrame, + ]); + + React.useEffect(() => { + skipAnimationOnFirstRender.current = false; + }, []); + + return { + ref, + ...state, + }; +}; + +/** + * @internal + * + * Hook to manage the presence of an element in the DOM based on its CSS transition/animation state. + * + * @param props - Motion props to manage the presence of an element in the DOM + * @param options - Motion options to configure the hook + */ +export const useMotion = ( + props: MotionProps, + options?: MotionOptions, +): Required> => { + const { ref, presence, active, state } = props; + const previousProps = usePrevious(props); + const mergedRef = useMergedRefs(ref); + + /** + * Heads up! + * We don't want these warnings in production even though it is against native behavior + */ + if (process.env.NODE_ENV !== 'production') { + // eslint-disable-next-line react-hooks/rules-of-hooks + React.useEffect(() => { + if (previousProps && Object.keys(props).length !== Object.keys(previousProps).length) { + // eslint-disable-next-line no-console + console.error( + [ + 'useMotion: The hook needs to be called with the same amount of props on every render.', + 'This is to ensure the internal state of the hook is stable and can be used to accurately detect the motion state.', + 'Please make sure to not change the props on subsequent renders or to use the hook conditionally.', + '\nCurrent props:', + JSON.stringify(props, null, 2), + '\nPrevious props:', + JSON.stringify(previousProps, null, 2), + ].join(' '), + ); + } + }, [props, previousProps]); + } + + if (typeof ref !== 'undefined' && typeof state !== 'undefined') { + const isMounted = state !== 'unmounted'; + + return { + ref: mergedRef, + state, + presence: presence ?? isMounted, + active: active ?? (isMounted && state !== 'exited'), + }; + } + + if (process.env.NODE_ENV !== 'production') { + if (typeof presence === 'undefined') { + throw new Error('useMotion: The hook needs either a `ref` and `state` or `presence` prop to work.'); + } + } + + const isPresent = !!presence; + /** + * Heads up! + * This hook returns a MotionProps but also accepts MotionProps as an argument. + * In case the hook is called with a MotionProps argument, we don't need to perform the expensive computation of the + * motion state and can just return the props as is. This is intentional as it allows others to use the hook on their + * side without having to worry about the performance impact of the hook. + */ + // eslint-disable-next-line react-hooks/rules-of-hooks + const { ref: motionRef, ...motionPresence } = useMotionPresence(isPresent, options); + + return { + presence: isPresent, + // eslint-disable-next-line react-hooks/rules-of-hooks + ref: useMergedRefs(ref, motionRef as RefObjectFunction), + active: motionPresence.active, + state: motionPresence.state, + }; +}; diff --git a/packages/react-components/react-motion-preview/src/index.ts b/packages/react-components/react-motion-preview/src/index.ts new file mode 100644 index 00000000000000..0a75af31e5294d --- /dev/null +++ b/packages/react-components/react-motion-preview/src/index.ts @@ -0,0 +1,2 @@ +export { useMotion } from './hooks'; +export type { MotionProps, MotionOptions } from './hooks'; diff --git a/packages/react-components/react-motion-preview/stories/useMotion/UseMotionBestPractices.md b/packages/react-components/react-motion-preview/stories/useMotion/UseMotionBestPractices.md new file mode 100644 index 00000000000000..08ff8ddeeb5f86 --- /dev/null +++ b/packages/react-components/react-motion-preview/stories/useMotion/UseMotionBestPractices.md @@ -0,0 +1,5 @@ +## Best practices + +### Do + +### Don't diff --git a/packages/react-components/react-motion-preview/stories/useMotion/UseMotionDefault.stories.tsx b/packages/react-components/react-motion-preview/stories/useMotion/UseMotionDefault.stories.tsx new file mode 100644 index 00000000000000..b6628f71ebd436 --- /dev/null +++ b/packages/react-components/react-motion-preview/stories/useMotion/UseMotionDefault.stories.tsx @@ -0,0 +1,60 @@ +import * as React from 'react'; + +import { useMotion } from '@fluentui/react-motion-preview'; +import { Button, makeStyles, mergeClasses, shorthands, tokens } from '@fluentui/react-components'; + +const useStyles = makeStyles({ + root: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + rowGap: '24px', + }, + + rectangle: { + ...shorthands.borderRadius('8px'), + + width: '200px', + height: '150px', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + backgroundColor: tokens.colorBrandBackground2, + opacity: 0, + transform: 'translate3D(0, 0, 0) scale(0.25)', + transitionDuration: `${tokens.durationNormal}, ${tokens.durationNormal}, ${tokens.durationUltraSlow}`, + transitionDelay: `${tokens.durationFast}, 0, ${tokens.durationSlow}`, + transitionProperty: 'opacity, transform, background-color', + willChange: 'opacity, transform, background-color', + color: '#fff', + }, + + visible: { + opacity: 1, + transform: 'translate3D(0, 0, 0) scale(1)', + backgroundColor: tokens.colorBrandBackground, + }, +}); + +export const Default = () => { + const styles = useStyles(); + + const [open, setOpen] = React.useState(false); + const motion = useMotion({ + presence: open, + }); + + return ( +
+ + +
+ Lorem ipsum +
+
+ ); +}; diff --git a/packages/react-components/react-motion-preview/stories/useMotion/UseMotionDescription.md b/packages/react-components/react-motion-preview/stories/useMotion/UseMotionDescription.md new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/packages/react-components/react-motion-preview/stories/useMotion/index.stories.tsx b/packages/react-components/react-motion-preview/stories/useMotion/index.stories.tsx new file mode 100644 index 00000000000000..a9cbc03f36c839 --- /dev/null +++ b/packages/react-components/react-motion-preview/stories/useMotion/index.stories.tsx @@ -0,0 +1,15 @@ +import descriptionMd from './UseMotionDescription.md'; +import bestPracticesMd from './UseMotionBestPractices.md'; + +export { Default } from './UseMotionDefault.stories'; + +export default { + title: 'Preview Components/useMotion', + parameters: { + docs: { + description: { + component: [descriptionMd, bestPracticesMd].join('\n'), + }, + }, + }, +}; diff --git a/packages/react-components/react-motion-preview/tsconfig.json b/packages/react-components/react-motion-preview/tsconfig.json new file mode 100644 index 00000000000000..1941a041d46c19 --- /dev/null +++ b/packages/react-components/react-motion-preview/tsconfig.json @@ -0,0 +1,25 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "target": "ES2019", + "noEmit": true, + "isolatedModules": true, + "importHelpers": true, + "jsx": "react", + "noUnusedLocals": true, + "preserveConstEnums": true + }, + "include": [], + "files": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + }, + { + "path": "./.storybook/tsconfig.json" + } + ] +} diff --git a/packages/react-components/react-motion-preview/tsconfig.lib.json b/packages/react-components/react-motion-preview/tsconfig.lib.json new file mode 100644 index 00000000000000..2de444c2059a53 --- /dev/null +++ b/packages/react-components/react-motion-preview/tsconfig.lib.json @@ -0,0 +1,22 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": false, + "lib": ["ES2019", "dom"], + "declaration": true, + "declarationDir": "../../../dist/out-tsc/types", + "outDir": "../../../dist/out-tsc", + "inlineSources": true, + "types": ["static-assets", "environment"] + }, + "exclude": [ + "src/components/AnimatePresence/testing/**", + "**/*.spec.ts", + "**/*.spec.tsx", + "**/*.test.ts", + "**/*.test.tsx", + "**/*.stories.ts", + "**/*.stories.tsx" + ], + "include": ["./src/**/*.ts", "./src/**/*.tsx"] +} diff --git a/packages/react-components/react-motion-preview/tsconfig.spec.json b/packages/react-components/react-motion-preview/tsconfig.spec.json new file mode 100644 index 00000000000000..a7e6c90d91b8ea --- /dev/null +++ b/packages/react-components/react-motion-preview/tsconfig.spec.json @@ -0,0 +1,17 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "CommonJS", + "outDir": "dist", + "types": ["jest", "node"] + }, + "include": [ + "**/*.spec.ts", + "**/*.spec.tsx", + "**/*.test.ts", + "**/*.test.tsx", + "**/*.d.ts", + "src/components/AnimatePresence/testing/**/*.ts", + "src/components/AnimatePresence/testing/**/*.tsx" + ] +} diff --git a/tsconfig.base.all.json b/tsconfig.base.all.json index 23cba2e23e0740..12123bda7698aa 100644 --- a/tsconfig.base.all.json +++ b/tsconfig.base.all.json @@ -122,6 +122,7 @@ "@fluentui/react-menu": ["packages/react-components/react-menu/src/index.ts"], "@fluentui/react-migration-v0-v9": ["packages/react-components/react-migration-v0-v9/src/index.ts"], "@fluentui/react-migration-v8-v9": ["packages/react-components/react-migration-v8-v9/src/index.ts"], + "@fluentui/react-motion-preview": ["packages/react-components/react-motion-preview/src/index.ts"], "@fluentui/react-overflow": ["packages/react-components/react-overflow/src/index.ts"], "@fluentui/react-persona": ["packages/react-components/react-persona/src/index.ts"], "@fluentui/react-popover": ["packages/react-components/react-popover/src/index.ts"], diff --git a/tsconfig.base.json b/tsconfig.base.json index f68a109eb4c573..a651e6c671d559 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -56,6 +56,7 @@ "@fluentui/react-menu": ["packages/react-components/react-menu/src/index.ts"], "@fluentui/react-migration-v0-v9": ["packages/react-components/react-migration-v0-v9/src/index.ts"], "@fluentui/react-migration-v8-v9": ["packages/react-components/react-migration-v8-v9/src/index.ts"], + "@fluentui/react-motion-preview": ["packages/react-components/react-motion-preview/src/index.ts"], "@fluentui/react-nav-preview": ["packages/react-components/react-nav-preview/src/index.ts"], "@fluentui/react-overflow": ["packages/react-components/react-overflow/src/index.ts"], "@fluentui/react-persona": ["packages/react-components/react-persona/src/index.ts"], From 76d1b821bde991c5d29e425ca11e1b0c9f1ca75b Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Thu, 17 Aug 2023 18:12:29 +0200 Subject: [PATCH 068/111] feat: remove useMotionPresence hook --- .../etc/react-utilities.api.md | 22 +- .../react-utilities/src/hooks/index.ts | 2 +- .../src/hooks/useMotionPresence.test.ts | 232 ----------- .../src/hooks/useMotionPresence.ts | 362 ------------------ .../react-utilities/src/index.ts | 10 +- 5 files changed, 6 insertions(+), 622 deletions(-) delete mode 100644 packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts delete mode 100644 packages/react-components/react-utilities/src/hooks/useMotionPresence.ts diff --git a/packages/react-components/react-utilities/etc/react-utilities.api.md b/packages/react-components/react-utilities/etc/react-utilities.api.md index e4af98d9d07e67..832fd9488e27ff 100644 --- a/packages/react-components/react-utilities/etc/react-utilities.api.md +++ b/packages/react-components/react-utilities/etc/react-utilities.api.md @@ -294,6 +294,9 @@ export type UnknownSlotProps = Pick, 'childr as?: keyof JSX.IntrinsicElements; }; +// @internal +export function useAnimationFrame(): readonly [(fn: () => void, delay?: number | undefined) => number, () => void]; + // @internal export const useControllableState: (options: UseControllableStateOptions) => [State, React_2.Dispatch>]; @@ -325,25 +328,6 @@ export function useIsSSR(): boolean; // @public export function useMergedRefs(...refs: (React_2.Ref | undefined)[]): RefObjectFunction; -// Warning: (ae-incompatible-release-tags) The symbol "useMotionPresence" is marked as @public, but its signature references "UseMotionPresenceOptions" which is marked as @internal -// Warning: (ae-incompatible-release-tags) The symbol "useMotionPresence" is marked as @public, but its signature references "UseMotionPresenceState" which is marked as @internal -// -// @public -export const useMotionPresence: (present: boolean, options?: UseMotionPresenceOptions) => UseMotionPresenceState; - -// @internal -export type UseMotionPresenceOptions = { - animateOnFirstMount?: boolean; -}; - -// @internal -export type UseMotionPresenceState = { - ref: React_2.RefCallback; - shouldRender: boolean; - visible: boolean; - motionState: 'entering' | 'exiting' | 'resting' | 'unmounted'; -}; - // @internal (undocumented) export type UseOnClickOrScrollOutsideOptions = { element: Document | undefined; diff --git a/packages/react-components/react-utilities/src/hooks/index.ts b/packages/react-components/react-utilities/src/hooks/index.ts index c43fadc10fe58a..487ebcf72f8050 100644 --- a/packages/react-components/react-utilities/src/hooks/index.ts +++ b/packages/react-components/react-utilities/src/hooks/index.ts @@ -10,4 +10,4 @@ export * from './useOnScrollOutside'; export * from './usePrevious'; export * from './useScrollbarWidth'; export * from './useTimeout'; -export * from './useMotionPresence'; +export * from './useAnimationFrame'; diff --git a/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts b/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts deleted file mode 100644 index 6d0ebc0dff9095..00000000000000 --- a/packages/react-components/react-utilities/src/hooks/useMotionPresence.test.ts +++ /dev/null @@ -1,232 +0,0 @@ -import { act, renderHook } from '@testing-library/react-hooks'; - -import { useMotionPresence, UseMotionPresenceOptions } from './useMotionPresence'; - -const defaultDuration = 100; -const renderHookWithRef = ( - initialPresence: boolean, - initialOptions?: UseMotionPresenceOptions, - style: Record = { 'transition-duration': `${defaultDuration}ms` }, -) => { - const refEl = document.createElement('div'); - const hook = renderHook(({ presence, options }) => useMotionPresence(presence, options), { - initialProps: { - presence: initialPresence, - options: initialOptions, - } as { - presence: boolean; - options?: UseMotionPresenceOptions; - }, - }); - - Object.entries(style).forEach(([key, value]) => value && refEl.style.setProperty(key, value)); - - act(() => hook.result.current.ref(refEl)); - - return hook; -}; - -describe('useMotionPresence', () => { - beforeEach(() => { - jest.useFakeTimers(); - }); - - afterEach(() => { - jest.useRealTimers(); - }); - - describe('when presence is false by default', () => { - it('should return default values when presence is false', () => { - const { result } = renderHookWithRef(false); - - expect(typeof result.current.ref).toBe('function'); - expect(result.current.motionState).toBe('unmounted'); - expect(result.current.active).toBe(false); - }); - }); - - describe('when presence is true by default', () => { - it('should return default values', () => { - const { result } = renderHookWithRef(true); - - expect(typeof result.current.ref).toBe('function'); - expect(result.current.motionState).toBe('resting'); - expect(result.current.active).toBe(true); - }); - - it('should not change values after timeout ', () => { - const { result } = renderHookWithRef(true); - - const assertSameValues = () => { - expect(typeof result.current.ref).toBe('function'); - expect(result.current.motionState).toBe('resting'); - expect(result.current.active).toBe(true); - }; - - assertSameValues(); - act(() => jest.advanceTimersToNextTimer()); - act(() => jest.advanceTimersToNextTimer()); - assertSameValues(); - }); - - it('should change active to true when animateOnFirstMount is true', () => { - const { result } = renderHookWithRef(true, { animateOnFirstMount: true }); - - expect(typeof result.current.ref).toBe('function'); - expect(result.current.motionState).toBe('resting'); - expect(result.current.active).toBe(false); - - act(() => jest.advanceTimersToNextTimer()); - - expect(result.current.active).toBe(true); - }); - }); - - describe('when presence changes', () => { - it('should toggle values starting with false', () => { - const { result, rerender } = renderHookWithRef(false); - - expect(typeof result.current.ref).toBe('function'); - expect(result.current.motionState).toBe('unmounted'); - expect(result.current.active).toBe(false); - - rerender({ presence: true }); - - expect(result.current.motionState).toBe('resting'); - - // double requestAnimationFrame - act(() => jest.advanceTimersToNextTimer()); - expect(result.current.active).toBe(true); - - rerender({ presence: false }); - - act(() => jest.advanceTimersToNextTimer()); - expect(result.current.motionState).toBe('exiting'); - expect(result.current.active).toBe(false); - - act(() => { - // requestAnimationFrame - act(() => jest.advanceTimersToNextTimer()); - - // timeout - jest.advanceTimersByTime(defaultDuration + 1); - }); - - expect(result.current.motionState).toBe('unmounted'); - expect(result.current.active).toBe(false); - }); - - it('should toggle values starting with true', () => { - const { result, rerender } = renderHookWithRef(true); - - expect(typeof result.current.ref).toBe('function'); - expect(result.current.motionState).toBe('resting'); - expect(result.current.active).toBe(true); - - rerender({ presence: false }); - - // requestAnimationFrame - act(() => jest.advanceTimersToNextTimer()); - - expect(result.current.motionState).toBe('exiting'); - expect(result.current.active).toBe(false); - - act(() => { - // requestAnimationFrame - jest.advanceTimersToNextTimer(); - - // timeout - jest.advanceTimersByTime(defaultDuration + 1); - }); - - expect(result.current.motionState).toBe('unmounted'); - expect(result.current.active).toBe(false); - }); - }); - - describe.each([ - { message: 'with transition', styles: { 'transition-duration': '100ms' } }, - { message: 'with long transition', styles: { 'transition-duration': '1000ms' } }, - { message: 'with animation', styles: { 'animation-duration': '100ms' } }, - { message: 'with long animation', styles: { 'animation-duration': '1000ms' } }, - ])('when presence changes - $message', ({ styles }) => { - it('should toggle values starting with false when animateOnFirstMount is true', () => { - const { result, rerender } = renderHookWithRef( - false, - { - animateOnFirstMount: true, - }, - styles, - ); - - expect(typeof result.current.ref).toBe('function'); - expect(result.current.motionState).toBe('unmounted'); - expect(result.current.active).toBe(false); - - rerender({ presence: true }); - - expect(result.current.motionState).toBe('resting'); - // requestAnimationFrame - act(() => jest.advanceTimersToNextTimer()); - expect(result.current.active).toBe(true); - expect(result.current.motionState).toBe('entering'); - // timeout - act(() => jest.advanceTimersToNextTimer()); - expect(result.current.motionState).toBe('resting'); - - rerender({ presence: false }); - - act(() => jest.advanceTimersToNextTimer()); - expect(result.current.active).toBe(false); - expect(result.current.motionState).toBe('exiting'); - - // requestAnimationFrame - act(() => jest.advanceTimersToNextTimer()); - // timeout - act(() => jest.advanceTimersToNextTimer()); - expect(result.current.motionState).toBe('unmounted'); - }); - }); - - describe.each([ - { message: 'with no transition', styles: { 'transition-duration': '0' } }, - { message: 'with no animation', styles: { 'animation-duration': '0' } }, - ])('when presence changes - $message', ({ styles }) => { - it('should toggle values when transition-duration is 0', () => { - const { result, rerender } = renderHookWithRef( - false, - { - animateOnFirstMount: true, - }, - styles, - ); - - expect(typeof result.current.ref).toBe('function'); - expect(result.current.motionState).toBe('unmounted'); - expect(result.current.active).toBe(false); - - rerender({ presence: true }); - - expect(result.current.motionState).toBe('resting'); - // requestAnimationFrame - act(() => jest.advanceTimersToNextTimer()); - act(() => jest.advanceTimersToNextTimer()); - expect(result.current.active).toBe(true); - // timeout - act(() => jest.advanceTimersToNextTimer()); - expect(result.current.motionState).toBe('resting'); - - rerender({ presence: false }); - - act(() => jest.advanceTimersToNextTimer()); - act(() => jest.advanceTimersToNextTimer()); - expect(result.current.active).toBe(false); - - // requestAnimationFrame - act(() => jest.advanceTimersToNextTimer()); - // timeout - act(() => jest.advanceTimersToNextTimer()); - expect(result.current.motionState).toBe('unmounted'); - }); - }); -}); diff --git a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts b/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts deleted file mode 100644 index 335cb013c24f7e..00000000000000 --- a/packages/react-components/react-utilities/src/hooks/useMotionPresence.ts +++ /dev/null @@ -1,362 +0,0 @@ -import * as React from 'react'; -import { canUseDOM } from '../ssr/canUseDOM'; -import { useAnimationFrame } from './useAnimationFrame'; -import { useTimeout } from './useTimeout'; - -/** - * CSS Typed Object Model - * @see https://drafts.css-houdini.org/css-typed-om-1/ - * @see https://developer.mozilla.org/en-US/docs/Web/API/CSSUnitValue - */ -interface CSSUnitValue { - value: number; - readonly unit: string; -} - -/** - * Style property map read only. - * @see https://developer.mozilla.org/en-US/docs/Web/API/StylePropertyMapReadOnly - */ -interface StylePropertyMapReadOnly { - [Symbol.iterator](): IterableIterator<[string, CSSUnitValue[]]>; - - get(property: string): CSSUnitValue | undefined; - getAll(property: string): CSSUnitValue[]; - has(property: string): boolean; - readonly size: number; -} - -/** - * HTMLElement with styled map. - * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/computedStyleMap - */ -type HTMLElementWithStyledMap = TElement & { - computedStyleMap(): StylePropertyMapReadOnly; -}; - -type CSSWithNumber = typeof CSS & { - number(value: number): { - value: number; - readonly unit: string; - }; -}; - -/** - * @internal - * - * State for useMotionPresence hook. - */ -export type UseMotionPresenceState = { - /** - * Ref to the element. - * - * @example - * const { ref } = useMotionPresence(isOpen); - * - *
- */ - ref: React.RefCallback; - - /** - * Whether the element is currently active in the DOM. - * Useful to apply CSS transitions only when the element is active. - * - * @example - * const { active, ref } = useMotionPresence(isOpen); - * - *
- */ - active: boolean; - - /** - * Current state of the element. - * - * - `entering` - The element is performing enter animation. - * - `exiting` - The element is performing exit animation. - * - `resting` - The element is currently not animating, but rendered on screen. - * - `unmounted` - The element is not rendered or can be removed from the DOM. - * - * @example - * const { motionState, ref } = useMotionPresence(isOpen); - * - *
- */ - motionState: 'entering' | 'exiting' | 'resting' | 'unmounted'; -}; - -/** - * @internal - * - * Options for useMotionPresence hook. - */ -export type UseMotionPresenceOptions = { - /** - * Whether to animate the element on first mount. - * - * @default false - */ - animateOnFirstMount?: boolean; -}; - -/** - * @internal - * - * Returns CSS styles of the given node. - * @param node - DOM node. - * @returns - CSS styles. - */ -const getElementComputedStyle = (node: HTMLElement): CSSStyleDeclaration => { - const window = node.ownerDocument.defaultView; - - if (!window || !canUseDOM()) { - return { - getPropertyValue: (_: string) => '', - } as CSSStyleDeclaration; - } - - return window.getComputedStyle(node, null); -}; - -/** - * Converts a CSS duration string to milliseconds. - * - * @param duration - CSS duration string - * @returns Duration in milliseconds - */ -function toMs(duration: string): number { - const trimmed = duration.trim(); - - if (trimmed.includes('auto')) { - return 0; - } - - if (trimmed.includes('ms')) { - return parseFloat(trimmed); - } - - return Number(trimmed.slice(0, -1).replace(',', '.')) * 1000; -} - -/** - * Checks if the browser supports CSSOM. - * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/computedStyleMap - * - * @param node - DOM node - * @returns Whether the browser supports CSSOM - */ -const hasCSSOMSupport = (node: HTMLElementWithStyledMap) => { - /** - * As we are using the experimental CSSOM API, we need to check if the browser supports it. - * The typecast here is to allow the use of the `number` function that is not yet part of the CSSOM typings. - * @see https://www.npmjs.com/package/@types/w3c-css-typed-object-model-level-1 - */ - return Boolean(typeof CSS !== 'undefined' && (CSS as CSSWithNumber).number && node.computedStyleMap); -}; - -/** - * - * Gets the computed style of a given element. - * If the browser supports CSSOM, it will return a ComputedStyleMap object. - * Otherwise, it will return a CSSStyleDeclaration object. - */ -const getCSSStyle = (node: HTMLElementWithStyledMap): CSSStyleDeclaration | StylePropertyMapReadOnly => { - if (hasCSSOMSupport(node)) { - return node.computedStyleMap(); - } - - return getElementComputedStyle(node); -}; - -/** - * Gets the computed map property for a given element using the CSSOM API. - * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/computedStyleMap - * - * @param computedStyle - Computed style of the element - * @param prop - CSS property - * @returns Computed map property - */ -const getComputedMapProp = (computedStyle: StylePropertyMapReadOnly, prop: string): string[] => { - const props = computedStyle.getAll(prop); - - if (props.length > 0) { - return props.map(({ value, unit }) => `${value}${unit}`); - } - - return ['0']; -}; - -/** - * Gets the computed style property for a given element using the getComputedStyle API. - * - * @param computedStyle - Computed style of the element - * @param prop - CSS property - * @returns Computed style property - */ -const getComputedStyleProp = (computedStyle: CSSStyleDeclaration, prop: string): string[] => { - const propValue = computedStyle.getPropertyValue(prop); - - return propValue ? propValue.split(',') : ['0']; -}; - -/** - * Gets the maximum duration from a list of CSS durations. - * - * @param durations - List of CSS durations - * @param delays - List of CSS delays - * @returns Maximum duration - */ -const getMaxCSSDuration = (durations: string[], delays: string[]): number => { - const totalProps = Math.max(durations.length, delays.length); - const totalDurations = []; - - if (totalProps === 0) { - return 0; - } - - for (let i = 0; i < totalProps; i++) { - const duration = toMs(durations[i] || '0'); - const delay = toMs(delays[i] || '0'); - - totalDurations.push(duration + delay); - } - - return Math.max(...totalDurations); -}; - -/** - * Gets the motion information for a given element. - * - * @param computedStyle - Computed style of the element - * @returns motion information - */ -const getMotionDuration = (node: HTMLElementWithStyledMap) => { - const hasModernCSSSupport = hasCSSOMSupport(node); - const computedStyle = getCSSStyle(node); - - const getProp = (prop: string): string[] => { - return hasModernCSSSupport - ? getComputedMapProp(computedStyle as StylePropertyMapReadOnly, prop) - : getComputedStyleProp(computedStyle as CSSStyleDeclaration, prop); - }; - - const transitionDuration = getMaxCSSDuration(getProp('transition-duration'), getProp('transition-delay')); - const animationDuration = getMaxCSSDuration(getProp('animation-duration'), getProp('animation-delay')); - - return Math.max(transitionDuration, animationDuration); -}; - -/** - * Hook to manage the presence of an element in the DOM based on its CSS transition/animation state. - * - * @param present - Whether the element should be present in the DOM - * @param events - Callbacks for when the element enters or exits the DOM - */ -export const useMotionPresence = ( - present: boolean, - options: UseMotionPresenceOptions = {}, -): UseMotionPresenceState => { - const { animateOnFirstMount } = { animateOnFirstMount: false, ...options }; - - const [state, setState] = React.useState, 'ref'>>({ - motionState: present ? 'resting' : 'unmounted', - active: false, - }); - - const [currentElement, setCurrentElement] = React.useState | null>(null); - const [setAnimationTimeout, clearAnimationTimeout] = useTimeout(); - const [setProcessingAnimationFrame, cancelProcessingAnimationFrame] = useAnimationFrame(); - const skipAnimationOnFirstRender = React.useRef(!animateOnFirstMount); - - const processAnimation = React.useCallback( - (callback: () => void) => { - if (!currentElement) { - return; - } - - clearAnimationTimeout(); - const animationFrame = requestAnimationFrame(() => { - const duration = getMotionDuration(currentElement); - - if (duration === 0) { - callback(); - return; - } - - /** - * Use CSS transition duration + 1ms to ensure the animation has finished on both enter and exit states. - * This is an alternative to using the `transitionend` event which can be unreliable as it fires multiple times - * if the transition has multiple properties. - */ - setAnimationTimeout(() => callback(), duration + 1); - }); - - return () => { - clearAnimationTimeout(); - cancelAnimationFrame(animationFrame); - }; - }, - [clearAnimationTimeout, currentElement, setAnimationTimeout], - ); - - const ref: React.RefCallback> = React.useCallback(node => { - if (!node) { - return; - } - - setCurrentElement(node); - }, []); - - React.useEffect(() => { - if (present) { - setState({ - active: skipAnimationOnFirstRender.current ? true : false, - motionState: 'resting', - }); - } - }, [present]); - - React.useEffect(() => { - const skipAnimation = skipAnimationOnFirstRender.current; - const onUnmount = () => cancelProcessingAnimationFrame(); - - setProcessingAnimationFrame(() => { - setState(prevState => { - let motionState = prevState.motionState; - - if (skipAnimation) { - motionState = present ? 'resting' : 'unmounted'; - } else { - motionState = present ? 'entering' : 'exiting'; - } - - return { - ...prevState, - motionState, - active: present, - }; - }); - }); - - if (skipAnimation) { - return onUnmount; - } - - processAnimation(() => { - setState(prevState => ({ - ...prevState, - motionState: present ? 'resting' : 'unmounted', - })); - }); - - return onUnmount; - }, [cancelProcessingAnimationFrame, present, processAnimation, setProcessingAnimationFrame]); - - React.useEffect(() => { - skipAnimationOnFirstRender.current = false; - }, []); - - return { - ref, - ...state, - }; -}; diff --git a/packages/react-components/react-utilities/src/index.ts b/packages/react-components/react-utilities/src/index.ts index 56fe2286e0d15c..97fc981ebeb745 100644 --- a/packages/react-components/react-utilities/src/index.ts +++ b/packages/react-components/react-utilities/src/index.ts @@ -30,6 +30,7 @@ export type { export { IdPrefixProvider, resetIdsForTests, + useAnimationFrame, useControllableState, useEventCallback, useFirstMount, @@ -42,15 +43,8 @@ export { usePrevious, useScrollbarWidth, useTimeout, - useMotionPresence, -} from './hooks/index'; -export type { - RefObjectFunction, - UseControllableStateOptions, - UseOnClickOrScrollOutsideOptions, - UseMotionPresenceOptions, - UseMotionPresenceState, } from './hooks/index'; +export type { RefObjectFunction, UseControllableStateOptions, UseOnClickOrScrollOutsideOptions } from './hooks/index'; export { canUseDOM, useIsSSR, SSRProvider } from './ssr/index'; From 7fd87f9b458c988f42cde7b0aef9d9f037cc52ba Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Thu, 17 Aug 2023 18:18:06 +0200 Subject: [PATCH 069/111] fix: remove old changefile --- ...act-utilities-01c80b27-0a05-464e-abdf-bdddf86d72ac.json | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 change/@fluentui-react-utilities-01c80b27-0a05-464e-abdf-bdddf86d72ac.json diff --git a/change/@fluentui-react-utilities-01c80b27-0a05-464e-abdf-bdddf86d72ac.json b/change/@fluentui-react-utilities-01c80b27-0a05-464e-abdf-bdddf86d72ac.json deleted file mode 100644 index 1b3a3e5dee2930..00000000000000 --- a/change/@fluentui-react-utilities-01c80b27-0a05-464e-abdf-bdddf86d72ac.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "minor", - "comment": "feat: new useMotionPresence hook - get the state for css animations/transitions", - "packageName": "@fluentui/react-utilities", - "email": "marcosvmmoura@gmail.com", - "dependentChangeType": "patch" -} From 1e1674308cb5a78915bf6a4b80b4647986c1fca9 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Thu, 17 Aug 2023 18:18:33 +0200 Subject: [PATCH 070/111] feat: expose useAnimationFrame hook --- packages/react-components/react-utilities/src/hooks/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-components/react-utilities/src/hooks/index.ts b/packages/react-components/react-utilities/src/hooks/index.ts index 487ebcf72f8050..018e521c02bb6b 100644 --- a/packages/react-components/react-utilities/src/hooks/index.ts +++ b/packages/react-components/react-utilities/src/hooks/index.ts @@ -1,3 +1,4 @@ +export * from './useAnimationFrame'; export * from './useControllableState'; export * from './useEventCallback'; export * from './useFirstMount'; @@ -10,4 +11,3 @@ export * from './useOnScrollOutside'; export * from './usePrevious'; export * from './useScrollbarWidth'; export * from './useTimeout'; -export * from './useAnimationFrame'; From 41e9e8ce1ef213e1e89a77af1095c62f282d143a Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Thu, 17 Aug 2023 18:19:17 +0200 Subject: [PATCH 071/111] fix: add missing changefile --- ...act-utilities-c9616c2d-de84-45eb-bef4-f7509f49cded.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/@fluentui-react-utilities-c9616c2d-de84-45eb-bef4-f7509f49cded.json diff --git a/change/@fluentui-react-utilities-c9616c2d-de84-45eb-bef4-f7509f49cded.json b/change/@fluentui-react-utilities-c9616c2d-de84-45eb-bef4-f7509f49cded.json new file mode 100644 index 00000000000000..7a3c251867b099 --- /dev/null +++ b/change/@fluentui-react-utilities-c9616c2d-de84-45eb-bef4-f7509f49cded.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "feat: create useAnimationFrame hook", + "packageName": "@fluentui/react-utilities", + "email": "marcosvmmoura@gmail.com", + "dependentChangeType": "patch" +} From f384b82974fd1fc2b03bcb617346d2f1411e2ce3 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Thu, 17 Aug 2023 18:27:23 +0200 Subject: [PATCH 072/111] fix: mismatch dependencies --- .../react-components/react-motion-preview/package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/react-components/react-motion-preview/package.json b/packages/react-components/react-motion-preview/package.json index 88e5d44f3897b0..a8cee6eb58d21f 100644 --- a/packages/react-components/react-motion-preview/package.json +++ b/packages/react-components/react-motion-preview/package.json @@ -31,10 +31,10 @@ "@fluentui/scripts-tasks": "*" }, "dependencies": { - "@fluentui/react-jsx-runtime": "^9.0.0-alpha.13", - "@fluentui/react-theme": "^9.1.10", - "@fluentui/react-utilities": "^9.11.0", - "@griffel/react": "^1.5.7", + "@fluentui/react-jsx-runtime": "^9.0.0", + "@fluentui/react-theme": "^9.1.11", + "@fluentui/react-utilities": "^9.11.1", + "@griffel/react": "^1.5.14", "@swc/helpers": "^0.4.14" }, "peerDependencies": { From ce4d18386e4c36be3b91d6df70da01fa3c5d3917 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Thu, 17 Aug 2023 18:29:44 +0200 Subject: [PATCH 073/111] feat: add motion docs to public site --- apps/public-docsite-v9/package.json | 1 + .../react-motion-preview/stories/useMotion/index.stories.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/public-docsite-v9/package.json b/apps/public-docsite-v9/package.json index a14427594c743c..e549ed058a3edc 100644 --- a/apps/public-docsite-v9/package.json +++ b/apps/public-docsite-v9/package.json @@ -36,6 +36,7 @@ "@fluentui/react-storybook-addon-codesandbox": "*", "@fluentui/theme-designer": "*", "@fluentui/react-search-preview": "*", + "@fluentui/react-motion-preview": "*", "@griffel/react": "^1.5.14", "react": "17.0.2", "react-dom": "17.0.2", diff --git a/packages/react-components/react-motion-preview/stories/useMotion/index.stories.tsx b/packages/react-components/react-motion-preview/stories/useMotion/index.stories.tsx index a9cbc03f36c839..9a6e793d138f4f 100644 --- a/packages/react-components/react-motion-preview/stories/useMotion/index.stories.tsx +++ b/packages/react-components/react-motion-preview/stories/useMotion/index.stories.tsx @@ -4,7 +4,7 @@ import bestPracticesMd from './UseMotionBestPractices.md'; export { Default } from './UseMotionDefault.stories'; export default { - title: 'Preview Components/useMotion', + title: 'Utilities/Motion/useMotion', parameters: { docs: { description: { From 05ba7e16393fe5c63c6749f1f528f84dd06634bb Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Thu, 17 Aug 2023 18:38:40 +0200 Subject: [PATCH 074/111] docs: add documentatio for the hook --- .../stories/useMotion/UseMotionBestPractices.md | 5 ----- .../stories/useMotion/UseMotionDefault.stories.tsx | 8 +++++--- .../stories/useMotion/UseMotionDescription.md | 1 + .../stories/useMotion/index.stories.tsx | 3 +-- 4 files changed, 7 insertions(+), 10 deletions(-) delete mode 100644 packages/react-components/react-motion-preview/stories/useMotion/UseMotionBestPractices.md diff --git a/packages/react-components/react-motion-preview/stories/useMotion/UseMotionBestPractices.md b/packages/react-components/react-motion-preview/stories/useMotion/UseMotionBestPractices.md deleted file mode 100644 index 08ff8ddeeb5f86..00000000000000 --- a/packages/react-components/react-motion-preview/stories/useMotion/UseMotionBestPractices.md +++ /dev/null @@ -1,5 +0,0 @@ -## Best practices - -### Do - -### Don't diff --git a/packages/react-components/react-motion-preview/stories/useMotion/UseMotionDefault.stories.tsx b/packages/react-components/react-motion-preview/stories/useMotion/UseMotionDefault.stories.tsx index b6628f71ebd436..ede228c5031694 100644 --- a/packages/react-components/react-motion-preview/stories/useMotion/UseMotionDefault.stories.tsx +++ b/packages/react-components/react-motion-preview/stories/useMotion/UseMotionDefault.stories.tsx @@ -52,9 +52,11 @@ export const Default = () => { Toggle -
- Lorem ipsum -
+ {motion.state !== 'unmounted' && ( +
+ Lorem ipsum +
+ )}
); }; diff --git a/packages/react-components/react-motion-preview/stories/useMotion/UseMotionDescription.md b/packages/react-components/react-motion-preview/stories/useMotion/UseMotionDescription.md index e69de29bb2d1d6..70098da049b451 100644 --- a/packages/react-components/react-motion-preview/stories/useMotion/UseMotionDescription.md +++ b/packages/react-components/react-motion-preview/stories/useMotion/UseMotionDescription.md @@ -0,0 +1 @@ +A tracker hook, that monitors the state of animations and transitions for a particular element. This hook does not directly create animations but instead synchronizes with CSS properties to determine the rendering status, visibility, entering, leaving, and ongoing animation of a component. If any CSS changes or properties are overridden, this hook will automatically adjust and stay synchronized. diff --git a/packages/react-components/react-motion-preview/stories/useMotion/index.stories.tsx b/packages/react-components/react-motion-preview/stories/useMotion/index.stories.tsx index 9a6e793d138f4f..628a07e28bb598 100644 --- a/packages/react-components/react-motion-preview/stories/useMotion/index.stories.tsx +++ b/packages/react-components/react-motion-preview/stories/useMotion/index.stories.tsx @@ -1,5 +1,4 @@ import descriptionMd from './UseMotionDescription.md'; -import bestPracticesMd from './UseMotionBestPractices.md'; export { Default } from './UseMotionDefault.stories'; @@ -8,7 +7,7 @@ export default { parameters: { docs: { description: { - component: [descriptionMd, bestPracticesMd].join('\n'), + component: [descriptionMd].join('\n'), }, }, }, From 552408870c67b7e4a80a0e169475b1b4a46f14f3 Mon Sep 17 00:00:00 2001 From: Bernardo Sunderhus Date: Fri, 18 Aug 2023 14:45:47 +0000 Subject: [PATCH 075/111] chore: motion with slots --- .../react-dialog/package.json | 1 + .../src/components/Dialog/Dialog.types.ts | 3 +- .../src/components/Dialog/useDialog.ts | 23 +- .../DialogSurface/DialogSurface.types.ts | 9 +- .../DialogSurface/useDialogSurface.ts | 14 +- .../src/contexts/dialogContext.ts | 5 +- .../react-drawer/etc/react-drawer.api.md | 10 +- .../DrawerInline/renderDrawerInline.tsx | 2 +- .../DrawerInline/useDrawerInline.ts | 23 +- .../useDrawerInlineStyles.styles.ts | 4 +- .../DrawerOverlay/DrawerOverlay.types.ts | 6 +- .../DrawerOverlay/renderDrawerOverlay.tsx | 2 +- .../DrawerOverlay/useDrawerOverlay.ts | 25 +- .../useDrawerOverlayStyles.styles.ts | 4 +- .../react-drawer/src/util/DrawerBase.types.ts | 12 +- .../src/util/useDrawerBaseStyles.styles.ts | 6 +- .../Drawer/DrawerCustomAnimation.stories.tsx | 16 +- .../DrawerCustomInlineAnimation.stories.tsx | 4 +- .../Drawer/DrawerCustomTransition.stories.tsx | 4 +- .../etc/react-motion-preview.api.md | 26 +- .../src/hooks/useIsMotion.ts | 43 +++ .../src/hooks/useMotion.test.ts | 49 +-- .../src/hooks/useMotion.ts | 353 +++--------------- .../react-motion-preview/src/index.ts | 2 +- .../react-motion-preview/src/utils/style.ts | 191 ++++++++++ 25 files changed, 407 insertions(+), 430 deletions(-) create mode 100644 packages/react-components/react-motion-preview/src/hooks/useIsMotion.ts create mode 100644 packages/react-components/react-motion-preview/src/utils/style.ts diff --git a/packages/react-components/react-dialog/package.json b/packages/react-components/react-dialog/package.json index ba89db93f04d0f..83ac72ba7447f6 100644 --- a/packages/react-components/react-dialog/package.json +++ b/packages/react-components/react-dialog/package.json @@ -41,6 +41,7 @@ "@fluentui/keyboard-keys": "^9.0.3", "@fluentui/react-context-selector": "^9.1.28", "@fluentui/react-shared-contexts": "^9.7.2", + "@fluentui/react-motion-preview": "0.0.0", "@fluentui/react-aria": "^9.3.30", "@fluentui/react-icons": "^2.0.207", "@fluentui/react-tabster": "^9.12.2", diff --git a/packages/react-components/react-dialog/src/components/Dialog/Dialog.types.ts b/packages/react-components/react-dialog/src/components/Dialog/Dialog.types.ts index 6a05e1fe8576df..ce163b62857e6c 100644 --- a/packages/react-components/react-dialog/src/components/Dialog/Dialog.types.ts +++ b/packages/react-components/react-dialog/src/components/Dialog/Dialog.types.ts @@ -2,6 +2,7 @@ import type * as React from 'react'; import type { ComponentProps, ComponentState } from '@fluentui/react-utilities'; import type { DialogContextValue, DialogSurfaceContextValue } from '../../contexts'; import type { DialogSurfaceElement } from '../DialogSurface/DialogSurface.types'; +import type { MotionShorthand } from '@fluentui/react-motion-preview'; export type DialogSlots = {}; @@ -68,7 +69,7 @@ export type DialogProps = ComponentProps> & { * Controls the open state of the dialog * @default false */ - open?: boolean; + open?: MotionShorthand; /** * Default value for the uncontrolled open state of the dialog. * @default false diff --git a/packages/react-components/react-dialog/src/components/Dialog/useDialog.ts b/packages/react-components/react-dialog/src/components/Dialog/useDialog.ts index ffce253ffac893..3e1477c1e6e993 100644 --- a/packages/react-components/react-dialog/src/components/Dialog/useDialog.ts +++ b/packages/react-components/react-dialog/src/components/Dialog/useDialog.ts @@ -1,11 +1,18 @@ import * as React from 'react'; -import { useControllableState, useEventCallback, useId, useIsomorphicLayoutEffect } from '@fluentui/react-utilities'; +import { + useControllableState, + useEventCallback, + useId, + useIsomorphicLayoutEffect, + useMergedRefs, +} from '@fluentui/react-utilities'; import { useHasParentContext } from '@fluentui/react-context-selector'; import { useDisableBodyScroll, useFocusFirstElement } from '../../utils'; import { DialogContext } from '../../contexts'; import type { DialogOpenChangeData, DialogProps, DialogState } from './Dialog.types'; import { useModalAttributes } from '@fluentui/react-tabster'; +import { useMotion } from '@fluentui/react-motion-preview'; /** * Create the state required to render Dialog. @@ -26,6 +33,8 @@ export const useDialog_unstable = (props: DialogProps): DialogState => { initialState: false, }); + const motion = useMotion(open); + const requestOpenChange = useEventCallback((data: DialogOpenChangeData) => { onOpenChange?.(data.event, data); @@ -36,7 +45,9 @@ export const useDialog_unstable = (props: DialogProps): DialogState => { } }); - const focusRef = useFocusFirstElement(open, modalType); + const isVisible = motion.isVisible(); + + const focusRef = useFocusFirstElement(isVisible, modalType); const disableBodyScroll = useDisableBodyScroll(); const isBodyScrollLocked = Boolean(open && modalType !== 'non-modal'); @@ -52,13 +63,11 @@ export const useDialog_unstable = (props: DialogProps): DialogState => { }); return { - components: { - backdrop: 'div', - }, + motion, + components: {}, inertTrapFocus, - open, modalType, - content: open ? content : null, + content: isVisible ? content : null, trigger, requestOpenChange, dialogTitleId: useId('dialog-title-'), diff --git a/packages/react-components/react-dialog/src/components/DialogSurface/DialogSurface.types.ts b/packages/react-components/react-dialog/src/components/DialogSurface/DialogSurface.types.ts index a0d72f37fc6049..1d701722d15bfc 100644 --- a/packages/react-components/react-dialog/src/components/DialogSurface/DialogSurface.types.ts +++ b/packages/react-components/react-dialog/src/components/DialogSurface/DialogSurface.types.ts @@ -1,5 +1,10 @@ -import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities'; +import type { ComponentProps, ComponentState, ExtractSlotProps, Slot } from '@fluentui/react-utilities'; import { DialogSurfaceContextValue } from '../../contexts'; +import { MotionShorthand } from '../../../../react-motion-preview/src/index'; + +export type DialogBackdropProps = ExtractSlotProps> & { + motion?: MotionShorthand; +}; export type DialogSurfaceSlots = { /** @@ -9,7 +14,7 @@ export type DialogSurfaceSlots = { * The backdrop should have `aria-hidden="true"`. * */ - backdrop?: Slot<'div'>; + backdrop?: Slot; root: Slot<'div'>; }; diff --git a/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurface.ts b/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurface.ts index ddfd5f4a8720a5..c31b97ee721c58 100644 --- a/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurface.ts +++ b/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurface.ts @@ -9,6 +9,7 @@ import { import type { DialogSurfaceElement, DialogSurfaceProps, DialogSurfaceState } from './DialogSurface.types'; import { useDialogContext_unstable } from '../../contexts'; import { Escape } from '@fluentui/keyboard-keys'; +import { useMotion } from '../../../../react-motion-preview/src/index'; /** * Create the state required to render DialogSurface. @@ -26,7 +27,7 @@ export const useDialogSurface_unstable = ( const modalType = useDialogContext_unstable(ctx => ctx.modalType); const modalAttributes = useDialogContext_unstable(ctx => ctx.modalAttributes); const dialogRef = useDialogContext_unstable(ctx => ctx.dialogRef); - const open = useDialogContext_unstable(ctx => ctx.open); + const motion = useDialogContext_unstable(ctx => ctx.motion); const requestOpenChange = useDialogContext_unstable(ctx => ctx.requestOpenChange); const dialogTitleID = useDialogContext_unstable(ctx => ctx.dialogTitleId); @@ -58,16 +59,21 @@ export const useDialogSurface_unstable = ( } }); - const backdrop = slot.optional(props.backdrop, { - renderByDefault: open && modalType !== 'non-modal', + const [backdropMotion, backdropProps] = useMotionFromSlot(props.backdrop, motion.isVisible()); + + const backdrop = slot.optional(backdropProps, { + renderByDefault: motion.isVisible() && modalType !== 'non-modal', defaultProps: { 'aria-hidden': 'true', + ref: backdropMotion.ref, }, elementType: 'div', }); + if (backdrop) { backdrop.onClick = handledBackdropClick; } + return { components: { backdrop: 'div', root: 'div' }, backdrop, @@ -80,7 +86,7 @@ export const useDialogSurface_unstable = ( ...props, ...modalAttributes, onKeyDown: handleKeyDown, - ref: useMergedRefs(ref, dialogRef), + ref: useMergedRefs(ref, dialogRef, motion.ref), }), { elementType: 'div' }, ), diff --git a/packages/react-components/react-dialog/src/contexts/dialogContext.ts b/packages/react-components/react-dialog/src/contexts/dialogContext.ts index c186f2f0b5ddf4..441091df52ebb0 100644 --- a/packages/react-components/react-dialog/src/contexts/dialogContext.ts +++ b/packages/react-components/react-dialog/src/contexts/dialogContext.ts @@ -4,9 +4,10 @@ import { DialogSurfaceElement } from '../DialogSurface'; import type { Context } from '@fluentui/react-context-selector'; import type { DialogModalType, DialogOpenChangeData } from '../Dialog'; import { useModalAttributes } from '@fluentui/react-tabster'; +import type { MotionState, MotionType } from '@fluentui/react-motion-preview'; export type DialogContextValue = { - open: boolean; + motion: MotionState; inertTrapFocus: boolean; dialogTitleId?: string; isNestedDialog: boolean; @@ -19,7 +20,7 @@ export type DialogContextValue = { } & Partial>; const defaultContextValue: DialogContextValue = { - open: false, + motion: {} as unknown as MotionState, inertTrapFocus: false, modalType: 'modal', isNestedDialog: false, diff --git a/packages/react-components/react-drawer/etc/react-drawer.api.md b/packages/react-components/react-drawer/etc/react-drawer.api.md index 993e256860315f..5685224433460d 100644 --- a/packages/react-components/react-drawer/etc/react-drawer.api.md +++ b/packages/react-components/react-drawer/etc/react-drawer.api.md @@ -13,6 +13,8 @@ import { DialogSurfaceProps } from '@fluentui/react-dialog'; import { DialogSurfaceSlots } from '@fluentui/react-dialog'; import { DialogTitleSlots } from '@fluentui/react-dialog'; import type { ForwardRefComponent } from '@fluentui/react-utilities'; +import { Motion } from '@fluentui/react-motion-preview'; +import { MotionShorthand } from '@fluentui/react-motion-preview'; import * as React_2 from 'react'; import type { Slot } from '@fluentui/react-utilities'; import type { SlotClassNames } from '@fluentui/react-utilities'; @@ -126,7 +128,7 @@ export type DrawerInlineSlots = { }; // @public -export type DrawerInlineState = ComponentState & DrawerInlineProps & DrawerBaseState; +export type DrawerInlineState = ComponentState & Omit & DrawerBaseState; // @public export const DrawerOverlay: ForwardRefComponent; @@ -143,9 +145,9 @@ export type DrawerOverlaySlots = DialogSurfaceSlots & { }; // @public -export type DrawerOverlayState = ComponentState & DrawerBaseProps & DrawerBaseState & { +export type DrawerOverlayState = ComponentState & Omit & DrawerBaseState & { dialog: DialogProps; - backdropVisible: boolean; + backdropMotion: Motion; }; // @public @@ -183,7 +185,7 @@ export const renderDrawerHeaderTitle_unstable: (state: DrawerHeaderTitleState) = export const renderDrawerInline_unstable: (state: DrawerInlineState) => JSX.Element | null; // @public -export const renderDrawerOverlay_unstable: (state: DrawerOverlayState) => JSX.Element; +export const renderDrawerOverlay_unstable: (state: DrawerOverlayState) => JSX.Element | null; // @public export const useDrawer_unstable: (props: DrawerProps, ref: React_2.Ref) => DrawerState; diff --git a/packages/react-components/react-drawer/src/components/DrawerInline/renderDrawerInline.tsx b/packages/react-components/react-drawer/src/components/DrawerInline/renderDrawerInline.tsx index 4e3dfca5ce42c8..06ad0b63fc3228 100644 --- a/packages/react-components/react-drawer/src/components/DrawerInline/renderDrawerInline.tsx +++ b/packages/react-components/react-drawer/src/components/DrawerInline/renderDrawerInline.tsx @@ -11,7 +11,7 @@ import type { DrawerInlineState, DrawerInlineSlots } from './DrawerInline.types' export const renderDrawerInline_unstable = (state: DrawerInlineState) => { assertSlots(state); - if (state.motionState === 'unmounted') { + if (state.motion.type === 'unmounted') { return null; } diff --git a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts index 8bcc2cff8aa3ab..df9076caaa6bbd 100644 --- a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts +++ b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts @@ -1,5 +1,5 @@ import * as React from 'react'; -import { getNativeElementProps, useControllableState, slot } from '@fluentui/react-utilities'; +import { getNativeElementProps, useControllableState, slot, useMergedRefs } from '@fluentui/react-utilities'; import { useMotion } from '@fluentui/react-motion-preview'; import type { DrawerInlineProps, DrawerInlineState } from './DrawerInline.types'; import { useBaseDrawerDefaultProps } from '../../util/useBaseDrawerDefaultProps'; @@ -17,21 +17,17 @@ export const useDrawerInline_unstable = ( props: DrawerInlineProps, ref: React.Ref, ): DrawerInlineState => { - const { open: innerOpen, defaultOpen, size, position } = useBaseDrawerDefaultProps(props); - const { separator = false, motion: motionProp } = props; + const { size, position } = useBaseDrawerDefaultProps(props); const [open] = useControllableState({ - state: innerOpen, - defaultState: defaultOpen, + state: props.open, + defaultState: props.defaultOpen, initialState: false, }); - const motion = useMotion( - motionProp || { - presence: open, - ref, - }, - ); + const { separator = false, motion = open } = props; + + const drawerMotion = useMotion(motion); return { components: { @@ -40,14 +36,13 @@ export const useDrawerInline_unstable = ( root: slot.always( getNativeElementProps('div', { - ref: motion.ref, + ref: useMergedRefs(ref, drawerMotion.ref), ...props, }), { elementType: 'div' }, ), - active: motion.active, - motionState: motion.state, + motion: drawerMotion, size, position, separator, diff --git a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts index 49e0fbf580e929..071e12bb3b2130 100644 --- a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts +++ b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts @@ -62,12 +62,12 @@ export const useDrawerInlineStyles_unstable = (state: DrawerInlineState): Drawer }, [state.position, state.separator, styles.separatorRight, styles.separatorLeft]); const hiddenClass = React.useCallback(() => { - if (state.active) { + if (state.motion.active) { return undefined; } return mergeClasses(styles.hidden, state.position === 'left' ? styles.hiddenLeft : styles.hiddenRight); - }, [state.active, state.position, styles.hidden, styles.hiddenLeft, styles.hiddenRight]); + }, [state.motion.active, state.position, styles.hidden, styles.hiddenLeft, styles.hiddenRight]); state.root.className = mergeClasses( drawerInlineClassNames.root, diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/DrawerOverlay.types.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/DrawerOverlay.types.ts index e409ae99dd814a..afedae6e04f81b 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/DrawerOverlay.types.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/DrawerOverlay.types.ts @@ -1,7 +1,7 @@ import { DialogProps, DialogSurfaceProps, DialogSurfaceSlots } from '@fluentui/react-dialog'; import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities'; -import type { MotionProps } from '@fluentui/react-motion-preview'; -import { DrawerBaseProps, DrawerBaseState } from '../../util/DrawerBase.types'; +import type { MotionState } from '@fluentui/react-motion-preview'; +import type { DrawerBaseProps, DrawerBaseState } from '../../util/DrawerBase.types'; export type DrawerOverlaySlots = DialogSurfaceSlots & { root: Slot; @@ -21,5 +21,5 @@ export type DrawerOverlayState = ComponentState & Omit & DrawerBaseState & { dialog: DialogProps; - backdropActive: MotionProps['active']; + backdropMotion: MotionState; }; diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/renderDrawerOverlay.tsx b/packages/react-components/react-drawer/src/components/DrawerOverlay/renderDrawerOverlay.tsx index bb00caed90b4cd..b25c38f1476f89 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/renderDrawerOverlay.tsx +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/renderDrawerOverlay.tsx @@ -12,7 +12,7 @@ import { Dialog } from '@fluentui/react-dialog'; export const renderDrawerOverlay_unstable = (state: DrawerOverlayState) => { assertSlots(state); - if (state.motionState === 'unmounted') { + if (state.motion.type === 'unmounted') { return null; } diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts index 79a1b71a0f4bf5..9b0c46def05b7a 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts @@ -1,5 +1,5 @@ import * as React from 'react'; -import { getNativeElementProps, slot } from '@fluentui/react-utilities'; +import { getNativeElementProps, isResolvedShorthand, slot, useMergedRefs } from '@fluentui/react-utilities'; import type { DrawerOverlayProps, DrawerOverlayState } from './DrawerOverlay.types'; import { DialogProps, DialogSurface, DialogSurfaceProps } from '@fluentui/react-dialog'; import { useBaseDrawerDefaultProps } from '../../util/useBaseDrawerDefaultProps'; @@ -19,26 +19,17 @@ export const useDrawerOverlay_unstable = ( ref: React.Ref, ): DrawerOverlayState => { const { open, defaultOpen, size, position } = useBaseDrawerDefaultProps(props); - const { modalType = 'modal', inertTrapFocus, onOpenChange, motion } = props; + const { modalType = 'modal', inertTrapFocus, onOpenChange, motion = open } = props; - const drawerMotion = useMotion( - motion || { - presence: open, - ref, - }, - ); - const backdropMotion = useMotion( - motion || { - presence: open, - }, - ); + const drawerMotion = useMotion(motion); + const backdropMotion = useMotion(isResolvedShorthand(props.backdrop) ? props.backdrop.motion ?? open : open); const hasCustomBackdrop = modalType !== 'non-modal' && props.backdrop !== null; const root = slot.always( getNativeElementProps('div', { ...props, - ref: drawerMotion.ref, + ref: useMergedRefs(ref, drawerMotion.ref), }), { elementType: DialogSurface, @@ -47,6 +38,7 @@ export const useDrawerOverlay_unstable = ( elementType: 'div', renderByDefault: hasCustomBackdrop, defaultProps: { + motion: backdropMotion, ref: backdropMotion.ref, }, }), @@ -78,8 +70,7 @@ export const useDrawerOverlay_unstable = ( dialog, size, position, - active: drawerMotion.active, - motionState: drawerMotion.state, - backdropActive: backdropMotion.active, + motion: drawerMotion, + backdropMotion, }; }; diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts index 988fbdafa61cff..7bd5640b44cc67 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts @@ -78,7 +78,7 @@ export const useDrawerOverlayStyles_unstable = (state: DrawerOverlayState): Draw getDrawerBaseClassNames(state, baseStyles), state.position && rootStyles[state.position], state.size && durationStyles[state.size], - state.active && rootStyles.visible, + state.motion.active && rootStyles.visible, state.root.className, ); @@ -86,7 +86,7 @@ export const useDrawerOverlayStyles_unstable = (state: DrawerOverlayState): Draw backdrop.className = mergeClasses( drawerOverlayClassNames.backdrop, backdropStyles.backdrop, - state.backdropActive && backdropStyles.backdropVisible, + state.backdropMotion.active && backdropStyles.backdropVisible, state.size && durationStyles[state.size], backdrop.className, ); diff --git a/packages/react-components/react-drawer/src/util/DrawerBase.types.ts b/packages/react-components/react-drawer/src/util/DrawerBase.types.ts index 32b8b481e05f13..cda446426853b6 100644 --- a/packages/react-components/react-drawer/src/util/DrawerBase.types.ts +++ b/packages/react-components/react-drawer/src/util/DrawerBase.types.ts @@ -1,4 +1,4 @@ -import { MotionProps } from '@fluentui/react-motion-preview'; +import { MotionShorthand, MotionState } from '@fluentui/react-motion-preview'; export type DrawerBaseProps = { /** @@ -25,7 +25,7 @@ export type DrawerBaseProps = { * * @default false */ - open?: boolean; + open?: MotionShorthand; /** * Default value for the uncontrolled open state of the Drawer. @@ -33,14 +33,8 @@ export type DrawerBaseProps = { * @default false */ defaultOpen?: boolean; - - /** - * - */ - motion?: MotionProps; }; export type DrawerBaseState = { - active: MotionProps['active']; - motionState: MotionProps['state']; + motion: MotionState; }; diff --git a/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts b/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts index 412cbaaf4774db..b3584724e08f1c 100644 --- a/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts +++ b/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts @@ -86,14 +86,14 @@ export const useDrawerDurationStyles = makeStyles({ }); export const getDrawerBaseClassNames = ( - { position, size, motionState }: DrawerBaseState & DrawerBaseProps, + { position, size, motion }: DrawerBaseState & DrawerBaseProps, baseStyles: ReturnType, ) => { return mergeClasses( baseStyles.reducedMotion, position && baseStyles[position], size && baseStyles[size], - motionState === 'entering' && baseStyles.entering, - motionState === 'exiting' && baseStyles.exiting, + motion.type === 'entering' && baseStyles.entering, + motion.type === 'exiting' && baseStyles.exiting, ); }; diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomAnimation.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomAnimation.stories.tsx index 573b04de20a93d..c748a6f44af36a 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomAnimation.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomAnimation.stories.tsx @@ -52,24 +52,24 @@ export const CustomAnimation = () => { const styles = useStyles(); const [isOpen, setIsOpen] = React.useState(false); - const motion = useMotion({ - presence: isOpen, - }); + const motion = useMotion(isOpen); + const backdropMotion = useMotion(isOpen); return (
setIsOpen(open)} > diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomInlineAnimation.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomInlineAnimation.stories.tsx index 1bf17144a7cf2e..45290be7d92dcc 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomInlineAnimation.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomInlineAnimation.stories.tsx @@ -81,9 +81,7 @@ export const CustomInlineAnimation = () => { const styles = useStyles(); const [isOpen, setIsOpen] = React.useState(false); - const motion = useMotion({ - presence: isOpen, - }); + const motion = useMotion(isOpen); return (
diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx index 98aa9f2e7cc4ab..13bae607fe622b 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomTransition.stories.tsx @@ -28,9 +28,7 @@ export const CustomTransition = () => { const styles = useStyles(); const [isOpen, setIsOpen] = React.useState(false); - const motion = useMotion({ - presence: isOpen, - }); + const motion = useMotion(isOpen); return (
diff --git a/packages/react-components/react-motion-preview/etc/react-motion-preview.api.md b/packages/react-components/react-motion-preview/etc/react-motion-preview.api.md index 32e3232c24a72d..0f64da03c3e01a 100644 --- a/packages/react-components/react-motion-preview/etc/react-motion-preview.api.md +++ b/packages/react-components/react-motion-preview/etc/react-motion-preview.api.md @@ -5,23 +5,27 @@ ```ts import * as React_2 from 'react'; -import { RefObjectFunction } from '@fluentui/react-utilities'; // @public (undocumented) -export type MotionOptions = { - animateOnFirstMount?: boolean; +export type Motion = { + ref: React_2.Ref; + active: boolean; + state: MotionStateType; }; -// @internal -export type MotionProps = { - presence: boolean; - ref?: RefObjectFunction | React_2.RefCallback | React_2.Ref; - active?: boolean; - state?: 'unmounted' | 'entering' | 'entered' | 'idle' | 'exiting' | 'exited'; -}; +// @public (undocumented) +export type MotionShorthand = MotionShorthandValue | Motion; + +// @public (undocumented) +export type MotionStateType = 'unmounted' | 'entering' | 'entered' | 'idle' | 'exiting' | 'exited'; // @internal -export const useMotion: (props: MotionProps, options?: MotionOptions) => Required>; +export function useMotion(shorthand: MotionShorthand, options?: UseMotionOptions): Motion; + +// @public (undocumented) +export type UseMotionOptions = { + animateOnFirstMount?: boolean; +}; // (No @packageDocumentation comment for this package) diff --git a/packages/react-components/react-motion-preview/src/hooks/useIsMotion.ts b/packages/react-components/react-motion-preview/src/hooks/useIsMotion.ts new file mode 100644 index 00000000000000..a93b5a52697431 --- /dev/null +++ b/packages/react-components/react-motion-preview/src/hooks/useIsMotion.ts @@ -0,0 +1,43 @@ +import * as React from 'react'; +import { usePrevious } from '@fluentui/react-utilities'; +import type { MotionState, MotionShorthand } from './useMotion'; + +/** + * @internal + * + * This method emits a warning if the hook is called with + * a different typeof of shorthand on subsequent renders, + * since this can lead breaking the rules of hooks. + * + * It also return a boolean indicating whether the shorthand is a motion object. + */ +export function useIsMotion( + shorthand: MotionShorthand, +): shorthand is MotionState { + const previousShorthand = usePrevious(shorthand); + + /** + * Heads up! + * We don't want these warnings in production even though it is against native behavior + */ + if (process.env.NODE_ENV !== 'production') { + // eslint-disable-next-line react-hooks/rules-of-hooks + React.useEffect(() => { + if (typeof previousShorthand !== typeof shorthand) { + // eslint-disable-next-line no-console + console.error( + [ + 'useMotion: The hook needs to be called with the same typeof of shorthand on every render.', + 'This is to ensure the internal state of the hook is stable and can be used to accurately detect the motion state.', + 'Please make sure to not change the shorthand on subsequent renders or to use the hook conditionally.', + '\nCurrent shorthand:', + JSON.stringify(shorthand, null, 2), + '\nPrevious props:', + JSON.stringify(previousShorthand, null, 2), + ].join(' '), + ); + } + }, [shorthand, previousShorthand]); + } + return typeof shorthand === 'object'; +} diff --git a/packages/react-components/react-motion-preview/src/hooks/useMotion.test.ts b/packages/react-components/react-motion-preview/src/hooks/useMotion.test.ts index ecbef73a23c804..4e2540fc44382a 100644 --- a/packages/react-components/react-motion-preview/src/hooks/useMotion.test.ts +++ b/packages/react-components/react-motion-preview/src/hooks/useMotion.test.ts @@ -1,11 +1,12 @@ +import * as React from 'react'; import { act, renderHook } from '@testing-library/react-hooks'; -import { useMotion, MotionProps, MotionOptions } from './useMotion'; +import { useMotion, UseMotionOptions, MotionShorthand } from './useMotion'; const defaultDuration = 100; const renderHookWithRef = ( - initialState: MotionProps, - initialOptions?: MotionOptions, + initialState: MotionShorthand, + initialOptions?: UseMotionOptions, style: Record = { 'transition-duration': `${defaultDuration}ms` }, ) => { const refEl = document.createElement('div'); @@ -13,7 +14,7 @@ const renderHookWithRef = ( initialProps: { state: initialState, options: initialOptions, - } as { state: MotionProps; options?: MotionOptions }, + } as { state: MotionShorthand; options?: UseMotionOptions }, }); Object.entries(style).forEach(([key, value]) => value && refEl.style.setProperty(key, value)); @@ -46,7 +47,7 @@ describe('useMotion', () => { describe('when presence is false by default', () => { it('should return default values when presence is false', () => { - const { result } = renderHookWithRef({ presence: false }); + const { result } = renderHookWithRef(false); expect(typeof result.current.ref).toBe('function'); expect(result.current.state).toBe('unmounted'); @@ -56,14 +57,14 @@ describe('useMotion', () => { describe('when presence is true by default', () => { it('should return default values', () => { - const { result } = renderHookWithRef({ presence: true }); + const { result } = renderHookWithRef(true); expect(typeof result.current.ref).toBe('function'); expect(result.current.active).toBe(true); }); it('should change visible to true when animateOnFirstMount is true', () => { - const { result } = renderHookWithRef({ presence: true }, { animateOnFirstMount: true }); + const { result } = renderHookWithRef(true, { animateOnFirstMount: true }); expect(typeof result.current.ref).toBe('function'); expect(result.current.active).toBe(false); @@ -76,13 +77,13 @@ describe('useMotion', () => { describe('when presence changes', () => { it('should toggle values starting with false', () => { - const { result, rerender } = renderHookWithRef({ presence: false }); + const { result, rerender } = renderHookWithRef(false); expect(typeof result.current.ref).toBe('function'); expect(result.current.state).toBe('unmounted'); expect(result.current.active).toBe(false); - rerender({ state: { presence: true } }); + rerender({ state: true }); act(() => jest.advanceTimersToNextTimer()); @@ -96,7 +97,7 @@ describe('useMotion', () => { expect(result.current.state).toBe('idle'); - rerender({ state: { presence: false } }); + rerender({ state: false }); act(() => jest.advanceTimersToNextTimer()); expect(result.current.state).toBe('exiting'); @@ -111,12 +112,12 @@ describe('useMotion', () => { }); it('should toggle values starting with true', () => { - const { result, rerender } = renderHookWithRef({ presence: true }); + const { result, rerender } = renderHookWithRef(true); expect(typeof result.current.ref).toBe('function'); expect(result.current.active).toBe(true); - rerender({ state: { presence: false } }); + rerender({ state: false }); act(() => jest.advanceTimersToNextTimer()); @@ -139,13 +140,13 @@ describe('useMotion', () => { { message: 'with long animation', styles: { 'animation-duration': '1000ms' } }, ])('when presence changes - $message', ({ styles }) => { it('should toggle values starting with false', () => { - const { result, rerender } = renderHookWithRef({ presence: false }, {}, styles); + const { result, rerender } = renderHookWithRef(false, {}, styles); expect(typeof result.current.ref).toBe('function'); expect(result.current.state).toBe('unmounted'); expect(result.current.active).toBe(false); - rerender({ state: { presence: true } }); + rerender({ state: true }); act(() => jest.advanceTimersToNextTimer()); @@ -160,7 +161,7 @@ describe('useMotion', () => { expect(result.current.state).toBe('idle'); - rerender({ state: { presence: false } }); + rerender({ state: false }); act(() => jest.advanceTimersToNextTimer()); @@ -181,13 +182,13 @@ describe('useMotion', () => { { message: 'with no animation', styles: { 'animation-duration': '0' } }, ])('when presence changes - $message', ({ styles }) => { it('should toggle values when transition-duration is 0', () => { - const { result, rerender } = renderHookWithRef({ presence: false }, {}, styles); + const { result, rerender } = renderHookWithRef(false, {}, styles); expect(typeof result.current.ref).toBe('function'); expect(result.current.state).toBe('unmounted'); expect(result.current.active).toBe(false); - rerender({ state: { presence: true } }); + rerender({ state: true }); act(() => jest.advanceTimersToNextTimer()); expect(result.current.state).toBe('entered'); @@ -202,7 +203,7 @@ describe('useMotion', () => { act(() => jest.advanceTimersToNextTimer()); expect(result.current.state).toBe('idle'); - rerender({ state: { presence: false } }); + rerender({ state: false }); act(() => jest.advanceTimersToNextTimer()); act(() => jest.advanceTimersToNextTimer()); @@ -218,19 +219,19 @@ describe('useMotion', () => { describe('when motion is received', () => { it('should return default values when presence is false', () => { - const { result } = renderHookWithRef({ presence: false, state: 'unmounted', active: false }); + const ref = React.createRef(); + const { result } = renderHookWithRef({ state: 'unmounted', active: false, ref }); - expect(typeof result.current.ref).toBe('function'); expect(result.current.state).toBe('unmounted'); - expect(result.current.presence).toBe(false); + expect(result.current.ref).toBe(ref); expect(result.current.active).toBe(false); }); it('should return default values when presence is true', () => { - const { result } = renderHookWithRef({ presence: true, state: 'idle', active: true }); + const ref = React.createRef(); + const { result } = renderHookWithRef({ state: 'idle', active: true, ref }); - expect(typeof result.current.ref).toBe('function'); - expect(result.current.presence).toBe(true); + expect(result.current.ref).toBe(ref); expect(result.current.active).toBe(true); }); }); diff --git a/packages/react-components/react-motion-preview/src/hooks/useMotion.ts b/packages/react-components/react-motion-preview/src/hooks/useMotion.ts index a42087363f9de5..e541f045d70738 100644 --- a/packages/react-components/react-motion-preview/src/hooks/useMotion.ts +++ b/packages/react-components/react-motion-preview/src/hooks/useMotion.ts @@ -1,90 +1,31 @@ import * as React from 'react'; +import { HTMLElementWithStyledMap, getMotionDuration } from '../utils/style'; +import { useAnimationFrame, useTimeout } from '@fluentui/react-utilities'; +import { useIsMotion } from './useIsMotion'; -import { - canUseDOM, - RefObjectFunction, - useAnimationFrame, - useMergedRefs, - usePrevious, - useTimeout, -} from '@fluentui/react-utilities'; - -/** - * CSS Typed Object Model - * @see https://drafts.css-houdini.org/css-typed-om-1/ - * @see https://developer.mozilla.org/en-US/docs/Web/API/CSSUnitValue - */ -interface CSSUnitValue { - value: number; - readonly unit: string; -} - -/** - * Style property map read only. - * @see https://developer.mozilla.org/en-US/docs/Web/API/StylePropertyMapReadOnly - */ -interface StylePropertyMapReadOnly { - [Symbol.iterator](): IterableIterator<[string, CSSUnitValue[]]>; - - get(property: string): CSSUnitValue | undefined; - getAll(property: string): CSSUnitValue[]; - has(property: string): boolean; - readonly size: number; -} - -/** - * HTMLElement with styled map. - * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/computedStyleMap - */ -type HTMLElementWithStyledMap = T & { - computedStyleMap(): StylePropertyMapReadOnly; -}; - -/** - * CSS with number parsing. - * @see https://drafts.css-houdini.org/css-typed-om-1/#css - * @see https://developer.mozilla.org/en-US/docs/Web/API/CSS/number - */ -type CSSWithNumber = typeof CSS & { - number(value: number): { - value: number; - readonly unit: string; - }; -}; - -/** - * @internal - * - * Motion props. - */ -export type MotionProps = { +export type UseMotionOptions = { /** - * Whether the element should be present in the DOM. + * Whether to animate the element on first mount. * * @default false */ - presence: boolean; + animateOnFirstMount?: boolean; +}; +export type MotionType = 'unmounted' | 'entering' | 'entered' | 'idle' | 'exiting' | 'exited'; + +export type MotionState = { /** * Ref to the element. * * @example - * const motion = useMotion({ presence: isOpen }); + * ```tsx + * const motion = useMotion(isOpen); * *
+ * ``` */ - ref?: RefObjectFunction | React.RefCallback | React.Ref; - - /** - * Whether the element is currently active in the DOM. - * Useful to apply CSS transitions only when the element is active. - * - * @example - * const motion = useMotion({ presence: isOpen }); - * - *
- */ - active?: boolean; + ref: React.Ref; /** * Current state of the element. @@ -97,168 +38,21 @@ export type MotionProps = { * - `exited` - The element has finished exit animation. * * @example - * const motion = useMotion({ presence: isOpen }); + * ```tsx + * const motion = useMotion(isOpen); * *
+ * ``` */ - state?: 'unmounted' | 'entering' | 'entered' | 'idle' | 'exiting' | 'exited'; -}; - -export type MotionOptions = { - /** - * Whether to animate the element on first mount. - * - * @default false - */ - animateOnFirstMount?: boolean; -}; - -/** - * @internal - * - * Returns CSS styles of the given node. - * @param node - DOM node. - * @returns - CSS styles. - */ -const getElementComputedStyle = (node: HTMLElement): CSSStyleDeclaration => { - const win = node.ownerDocument?.defaultView ? node.ownerDocument.defaultView : window; - - if (!win || !canUseDOM()) { - return { - getPropertyValue: (_: string) => '', - } as CSSStyleDeclaration; - } - - return win.getComputedStyle(node, null); -}; - -/** - * Converts a CSS duration string to milliseconds. - * - * @param duration - CSS duration string - * @returns Duration in milliseconds - */ -function toMs(duration: string): number { - const trimmed = duration.trim(); - - if (trimmed.includes('auto')) { - return 0; - } - - if (trimmed.includes('ms')) { - return parseFloat(trimmed); - } - - return Number(trimmed.slice(0, -1).replace(',', '.')) * 1000; -} - -/** - * Checks if the browser supports CSSOM. - * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/computedStyleMap - * - * @param node - DOM node - * @returns Whether the browser supports CSSOM - */ -const hasCSSOMSupport = (node: HTMLElementWithStyledMap) => { - /** - * As we are using the experimental CSSOM API, we need to check if the browser supports it. - * The typecast here is to allow the use of the `number` function that is not yet part of the CSSOM typings. - * @see https://www.npmjs.com/package/@types/w3c-css-typed-object-model-level-1 - */ - return Boolean(typeof CSS !== 'undefined' && (CSS as CSSWithNumber).number && node.computedStyleMap); -}; - -/** - * - * Gets the computed style of a given element. - * If the browser supports CSSOM, it will return a ComputedStyleMap object. - * Otherwise, it will return a CSSStyleDeclaration object. - */ -const getCSSStyle = (node: HTMLElementWithStyledMap): CSSStyleDeclaration | StylePropertyMapReadOnly => { - if (hasCSSOMSupport(node)) { - return node.computedStyleMap(); - } - - return getElementComputedStyle(node); -}; - -/** - * Gets the computed map property for a given element using the CSSOM API. - * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/computedStyleMap - * - * @param computedStyle - Computed style of the element - * @param prop - CSS property - * @returns Computed map property - */ -const getComputedMapProp = (computedStyle: StylePropertyMapReadOnly, prop: string): string[] => { - const props = computedStyle.getAll(prop); - - if (props.length > 0) { - return props.map(({ value, unit }) => `${value}${unit}`); - } - - return ['0']; -}; - -/** - * Gets the computed style property for a given element using the getComputedStyle API. - * - * @param computedStyle - Computed style of the element - * @param prop - CSS property - * @returns Computed style property - */ -const getComputedStyleProp = (computedStyle: CSSStyleDeclaration, prop: string): string[] => { - const propValue = computedStyle.getPropertyValue(prop); - - return propValue ? propValue.split(',') : ['0']; -}; - -/** - * Gets the maximum duration from a list of CSS durations. - * - * @param durations - List of CSS durations - * @param delays - List of CSS delays - * @returns Maximum duration - */ -const getMaxCSSDuration = (durations: string[], delays: string[]): number => { - const totalProps = Math.max(durations.length, delays.length); - const totalDurations = []; - - if (totalProps === 0) { - return 0; - } + type: MotionType; - for (let i = 0; i < totalProps; i++) { - const duration = toMs(durations[i] || '0'); - const delay = toMs(delays[i] || '0'); - - totalDurations.push(duration + delay); - } - - return Math.max(...totalDurations); + isActive(): boolean; + isVisible(): boolean; }; -/** - * Gets the motion information for a given element. - * - * @param computedStyle - Computed style of the element - * @returns motion information - */ -const getMotionDuration = (node: HTMLElementWithStyledMap) => { - const hasModernCSSSupport = hasCSSOMSupport(node); - const computedStyle = getCSSStyle(node); - - const getProp = (prop: string): string[] => { - return hasModernCSSSupport - ? getComputedMapProp(computedStyle as StylePropertyMapReadOnly, prop) - : getComputedStyleProp(computedStyle as CSSStyleDeclaration, prop); - }; +export type MotionShorthandValue = boolean; - const transitionDuration = getMaxCSSDuration(getProp('transition-duration'), getProp('transition-delay')); - const animationDuration = getMaxCSSDuration(getProp('animation-duration'), getProp('animation-delay')); - - return Math.max(transitionDuration, animationDuration); -}; +export type MotionShorthand = MotionShorthandValue | MotionState; /** * @internal @@ -268,18 +62,18 @@ const getMotionDuration = (node: HTMLElementWithStyledMap) => { * @param present - Whether the element should be present in the DOM * @param events - Callbacks for when the element enters or exits the DOM */ -const useMotionPresence = ( +export function useMotionPresence( present: boolean, - options: MotionOptions = {}, -): Required, 'presence'>> => { + options: UseMotionOptions = {}, +): MotionState { const { animateOnFirstMount } = { animateOnFirstMount: false, ...options }; - const [state, setState] = React.useState>>({ - state: present ? 'idle' : 'unmounted', + const [state, setState] = React.useState, 'active' | 'type'>>({ + type: present ? 'idle' : 'unmounted', active: false, }); - const [currentElement, setCurrentElement] = React.useState | null>(null); + const [currentElement, setCurrentElement] = React.useState | null>(null); const [setAnimationTimeout, clearAnimationTimeout] = useTimeout(); const [setActiveAnimationFrame, cancelActiveAnimationFrame] = useAnimationFrame(); const [setProcessingAnimationFrame, cancelProcessingAnimationFrame] = useAnimationFrame(); @@ -326,7 +120,7 @@ const useMotionPresence = ( ], ); - const ref: React.RefCallback> = React.useCallback(node => { + const ref: React.RefCallback> = React.useCallback(node => { if (!node) { return; } @@ -338,7 +132,7 @@ const useMotionPresence = ( if (present) { setState(prevState => ({ ...prevState, - state: 'entering', + type: 'entering', active: skipAnimationOnFirstRender.current ? true : false, })); } @@ -353,7 +147,7 @@ const useMotionPresence = ( setActiveAnimationFrame(() => { setState(prevState => { - let newState = prevState.state; + let newState = prevState.type; if (skipAnimation) { newState = present ? 'idle' : 'unmounted'; @@ -362,7 +156,7 @@ const useMotionPresence = ( } return { - state: newState, + type: newState, active: present, }; }); @@ -375,13 +169,13 @@ const useMotionPresence = ( processAnimation(() => { setState(prevState => ({ ...prevState, - state: present ? 'entered' : 'exited', + type: present ? 'entered' : 'exited', })); setDelayedAnimationFrame(() => { setState(prevState => ({ ...prevState, - state: present ? 'idle' : 'unmounted', + type: present ? 'idle' : 'unmounted', })); }); }); @@ -400,11 +194,8 @@ const useMotionPresence = ( skipAnimationOnFirstRender.current = false; }, []); - return { - ref, - ...state, - }; -}; + return React.useMemo(() => ({ ref, ...state }), [ref, state]); +} /** * @internal @@ -414,71 +205,17 @@ const useMotionPresence = ( * @param props - Motion props to manage the presence of an element in the DOM * @param options - Motion options to configure the hook */ -export const useMotion = ( - props: MotionProps, - options?: MotionOptions, -): Required> => { - const { ref, presence, active, state } = props; - const previousProps = usePrevious(props); - const mergedRef = useMergedRefs(ref); - - /** - * Heads up! - * We don't want these warnings in production even though it is against native behavior - */ - if (process.env.NODE_ENV !== 'production') { - // eslint-disable-next-line react-hooks/rules-of-hooks - React.useEffect(() => { - if (previousProps && Object.keys(props).length !== Object.keys(previousProps).length) { - // eslint-disable-next-line no-console - console.error( - [ - 'useMotion: The hook needs to be called with the same amount of props on every render.', - 'This is to ensure the internal state of the hook is stable and can be used to accurately detect the motion state.', - 'Please make sure to not change the props on subsequent renders or to use the hook conditionally.', - '\nCurrent props:', - JSON.stringify(props, null, 2), - '\nPrevious props:', - JSON.stringify(previousProps, null, 2), - ].join(' '), - ); - } - }, [props, previousProps]); - } - - if (typeof ref !== 'undefined' && typeof state !== 'undefined') { - const isMounted = state !== 'unmounted'; - - return { - ref: mergedRef, - state, - presence: presence ?? isMounted, - active: active ?? (isMounted && state !== 'exited'), - }; - } - - if (process.env.NODE_ENV !== 'production') { - if (typeof presence === 'undefined') { - throw new Error('useMotion: The hook needs either a `ref` and `state` or `presence` prop to work.'); - } - } - - const isPresent = !!presence; +export function useMotion( + shorthand: MotionShorthand, + options?: UseMotionOptions, +): MotionState { /** * Heads up! - * This hook returns a MotionProps but also accepts MotionProps as an argument. - * In case the hook is called with a MotionProps argument, we don't need to perform the expensive computation of the - * motion state and can just return the props as is. This is intentional as it allows others to use the hook on their + * This hook returns a Motion but also accepts Motion as an argument. + * In case the hook is called with a Motion as argument, we don't need to perform the expensive computation of the + * motion state and can just return the motion as is. This is intentional as it allows others to use the hook on their * side without having to worry about the performance impact of the hook. */ // eslint-disable-next-line react-hooks/rules-of-hooks - const { ref: motionRef, ...motionPresence } = useMotionPresence(isPresent, options); - - return { - presence: isPresent, - // eslint-disable-next-line react-hooks/rules-of-hooks - ref: useMergedRefs(ref, motionRef as RefObjectFunction), - active: motionPresence.active, - state: motionPresence.state, - }; -}; + return useIsMotion(shorthand) ? shorthand : useMotionPresence(shorthand, options); +} diff --git a/packages/react-components/react-motion-preview/src/index.ts b/packages/react-components/react-motion-preview/src/index.ts index 0a75af31e5294d..dc043f28779729 100644 --- a/packages/react-components/react-motion-preview/src/index.ts +++ b/packages/react-components/react-motion-preview/src/index.ts @@ -1,2 +1,2 @@ export { useMotion } from './hooks'; -export type { MotionProps, MotionOptions } from './hooks'; +export type { UseMotionOptions, MotionType, MotionState, MotionShorthand } from './hooks'; diff --git a/packages/react-components/react-motion-preview/src/utils/style.ts b/packages/react-components/react-motion-preview/src/utils/style.ts new file mode 100644 index 00000000000000..3078f7bc807011 --- /dev/null +++ b/packages/react-components/react-motion-preview/src/utils/style.ts @@ -0,0 +1,191 @@ +import { canUseDOM } from '@fluentui/react-utilities'; + +/** + * CSS Typed Object Model + * @see https://drafts.css-houdini.org/css-typed-om-1/ + * @see https://developer.mozilla.org/en-US/docs/Web/API/CSSUnitValue + */ +export interface CSSUnitValue { + value: number; + readonly unit: string; +} + +/** + * Style property map read only. + * @see https://developer.mozilla.org/en-US/docs/Web/API/StylePropertyMapReadOnly + */ +export interface StylePropertyMapReadOnly { + [Symbol.iterator](): IterableIterator<[string, CSSUnitValue[]]>; + + get(property: string): CSSUnitValue | undefined; + getAll(property: string): CSSUnitValue[]; + has(property: string): boolean; + readonly size: number; +} + +/** + * HTMLElement with styled map. + * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/computedStyleMap + */ +export type HTMLElementWithStyledMap = T & { + computedStyleMap(): StylePropertyMapReadOnly; +}; + +/** + * CSS with number parsing. + * @see https://drafts.css-houdini.org/css-typed-om-1/#css + * @see https://developer.mozilla.org/en-US/docs/Web/API/CSS/number + */ +export type CSSWithNumber = typeof CSS & { + number(value: number): { + value: number; + readonly unit: string; + }; +}; + +/** + * + * Gets the computed style of a given element. + * If the browser supports CSSOM, it will return a ComputedStyleMap object. + * Otherwise, it will return a CSSStyleDeclaration object. + */ +export const getCSSStyle = (node: HTMLElementWithStyledMap): CSSStyleDeclaration | StylePropertyMapReadOnly => { + if (hasCSSOMSupport(node)) { + return node.computedStyleMap() as unknown as StylePropertyMapReadOnly; + } + + return getElementComputedStyle(node); +}; + +/** + * Checks if the browser supports CSSOM. + * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/computedStyleMap + * + * @param node - DOM node + * @returns Whether the browser supports CSSOM + */ +export const hasCSSOMSupport = (node: HTMLElementWithStyledMap) => { + /** + * As we are using the experimental CSSOM API, we need to check if the browser supports it. + * The typecast here is to allow the use of the `number` function that is not yet part of the CSSOM typings. + * @see https://www.npmjs.com/package/@types/w3c-css-typed-object-model-level-1 + */ + return Boolean(typeof CSS !== 'undefined' && (CSS as CSSWithNumber).number && node.computedStyleMap); +}; + +/** + * @internal + * + * Returns CSS styles of the given node. + * @param node - DOM node. + * @returns - CSS styles. + */ +export const getElementComputedStyle = (node: HTMLElement): CSSStyleDeclaration => { + const win = node.ownerDocument?.defaultView ? node.ownerDocument.defaultView : window; + + if (!win || !canUseDOM()) { + return { + getPropertyValue: (_: string) => '', + } as CSSStyleDeclaration; + } + + return win.getComputedStyle(node, null); +}; + +/** + * Converts a CSS duration string to milliseconds. + * + * @param duration - CSS duration string + * @returns Duration in milliseconds + */ +export function toMs(duration: string): number { + const trimmed = duration.trim(); + + if (trimmed.includes('auto')) { + return 0; + } + + if (trimmed.includes('ms')) { + return parseFloat(trimmed); + } + + return Number(trimmed.slice(0, -1).replace(',', '.')) * 1000; +} + +/** + * Gets the computed map property for a given element using the CSSOM API. + * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/computedStyleMap + * + * @param computedStyle - Computed style of the element + * @param prop - CSS property + * @returns Computed map property + */ +export const getComputedMapProp = (computedStyle: StylePropertyMapReadOnly, prop: string): string[] => { + const props = computedStyle.getAll(prop); + + if (props.length > 0) { + return props.map(({ value, unit }) => `${value}${unit}`); + } + + return ['0']; +}; + +/** + * Gets the computed style property for a given element using the getComputedStyle API. + * + * @param computedStyle - Computed style of the element + * @param prop - CSS property + * @returns Computed style property + */ +export const getComputedStyleProp = (computedStyle: CSSStyleDeclaration, prop: string): string[] => { + const propValue = computedStyle.getPropertyValue(prop); + + return propValue ? propValue.split(',') : ['0']; +}; + +/** + * Gets the maximum duration from a list of CSS durations. + * + * @param durations - List of CSS durations + * @param delays - List of CSS delays + * @returns Maximum duration + */ +export const getMaxCSSDuration = (durations: string[], delays: string[]): number => { + const totalProps = Math.max(durations.length, delays.length); + const totalDurations = []; + + if (totalProps === 0) { + return 0; + } + + for (let i = 0; i < totalProps; i++) { + const duration = toMs(durations[i] || '0'); + const delay = toMs(delays[i] || '0'); + + totalDurations.push(duration + delay); + } + + return Math.max(...totalDurations); +}; + +/** + * Gets the motion information for a given element. + * + * @param computedStyle - Computed style of the element + * @returns motion information + */ +export const getMotionDuration = (node: HTMLElementWithStyledMap) => { + const hasModernCSSSupport = hasCSSOMSupport(node); + const computedStyle = getCSSStyle(node); + + const getProp = (prop: string): string[] => { + return hasModernCSSSupport + ? getComputedMapProp(computedStyle as StylePropertyMapReadOnly, prop) + : getComputedStyleProp(computedStyle as CSSStyleDeclaration, prop); + }; + + const transitionDuration = getMaxCSSDuration(getProp('transition-duration'), getProp('transition-delay')); + const animationDuration = getMaxCSSDuration(getProp('animation-duration'), getProp('animation-delay')); + + return Math.max(transitionDuration, animationDuration); +}; From 206800123de0f38c112d4e0a3c44c583e9379a1c Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Sun, 20 Aug 2023 21:10:07 +0200 Subject: [PATCH 076/111] feat: implement useMotionFromSlot to better resolve slots with motion prop --- .../react-components/src/index.ts | 3 - .../src/components/Dialog/useDialog.ts | 12 +-- .../Dialog/useDialogContextValues.ts | 18 +++-- .../DialogSurface/DialogSurface.types.ts | 7 +- .../DialogSurface/useDialogSurface.ts | 28 ++++--- .../useDialogSurfaceStyles.styles.ts | 74 ++++++++++++++----- .../src/contexts/dialogContext.ts | 11 ++- .../src/testing/mockUseDialogContext.ts | 6 ++ .../DrawerInline/DrawerInline.types.ts | 2 +- .../DrawerInline/useDrawerInline.ts | 15 ++-- .../useDrawerInlineStyles.styles.ts | 4 +- .../DrawerOverlay/useDrawerOverlay.ts | 16 ++-- .../useDrawerOverlayStyles.styles.ts | 4 +- .../DrawerCustomInlineAnimation.stories.tsx | 14 ++-- .../Drawer/DrawerCustomTransition.stories.tsx | 7 +- .../react-motion-preview/src/hooks/index.ts | 1 + .../src/hooks/useMotion.ts | 63 ++++++++-------- .../src/hooks/useMotionFromSlot.ts | 26 +++++++ .../react-motion-preview/src/index.ts | 4 +- 19 files changed, 194 insertions(+), 121 deletions(-) create mode 100644 packages/react-components/react-motion-preview/src/hooks/useMotionFromSlot.ts diff --git a/packages/react-components/react-components/src/index.ts b/packages/react-components/react-components/src/index.ts index 9937039da4e9c0..2d5303655d4f2b 100644 --- a/packages/react-components/react-components/src/index.ts +++ b/packages/react-components/react-components/src/index.ts @@ -111,7 +111,6 @@ export { useIsSSR, useMergedRefs, useScrollbarWidth, - useMotionPresence, } from '@fluentui/react-utilities'; export type { ComponentProps, @@ -125,8 +124,6 @@ export type { SlotClassNames, SlotPropsRecord, SlotRenderFunction, - UseMotionPresenceEvents, - UseMotionPresenceState, } from '@fluentui/react-utilities'; // Components diff --git a/packages/react-components/react-dialog/src/components/Dialog/useDialog.ts b/packages/react-components/react-dialog/src/components/Dialog/useDialog.ts index 3e1477c1e6e993..b247490e1524c6 100644 --- a/packages/react-components/react-dialog/src/components/Dialog/useDialog.ts +++ b/packages/react-components/react-dialog/src/components/Dialog/useDialog.ts @@ -1,11 +1,5 @@ import * as React from 'react'; -import { - useControllableState, - useEventCallback, - useId, - useIsomorphicLayoutEffect, - useMergedRefs, -} from '@fluentui/react-utilities'; +import { useControllableState, useEventCallback, useId, useIsomorphicLayoutEffect } from '@fluentui/react-utilities'; import { useHasParentContext } from '@fluentui/react-context-selector'; import { useDisableBodyScroll, useFocusFirstElement } from '../../utils'; import { DialogContext } from '../../contexts'; @@ -34,6 +28,7 @@ export const useDialog_unstable = (props: DialogProps): DialogState => { }); const motion = useMotion(open); + const isVisible = motion.isVisible(); const requestOpenChange = useEventCallback((data: DialogOpenChangeData) => { onOpenChange?.(data.event, data); @@ -45,8 +40,6 @@ export const useDialog_unstable = (props: DialogProps): DialogState => { } }); - const isVisible = motion.isVisible(); - const focusRef = useFocusFirstElement(isVisible, modalType); const disableBodyScroll = useDisableBodyScroll(); const isBodyScrollLocked = Boolean(open && modalType !== 'non-modal'); @@ -63,6 +56,7 @@ export const useDialog_unstable = (props: DialogProps): DialogState => { }); return { + open, motion, components: {}, inertTrapFocus, diff --git a/packages/react-components/react-dialog/src/components/Dialog/useDialogContextValues.ts b/packages/react-components/react-dialog/src/components/Dialog/useDialogContextValues.ts index 7670d1f8eb8ed8..9ce005e1ed5ba2 100644 --- a/packages/react-components/react-dialog/src/components/Dialog/useDialogContextValues.ts +++ b/packages/react-components/react-dialog/src/components/Dialog/useDialogContextValues.ts @@ -3,14 +3,15 @@ import type { DialogContextValues, DialogState } from './Dialog.types'; export function useDialogContextValues_unstable(state: DialogState): DialogContextValues { const { - modalType, - open, dialogRef, dialogTitleId, - isNestedDialog, inertTrapFocus, - requestOpenChange, + isNestedDialog, modalAttributes, + modalType, + motion, + open, + requestOpenChange, triggerAttributes, } = state; @@ -19,15 +20,16 @@ export function useDialogContextValues_unstable(state: DialogState): DialogConte * there is no sense to memoize it */ const dialog: DialogContextValue = { - open, - modalType, dialogRef, dialogTitleId, - isNestedDialog, inertTrapFocus, + isNestedDialog, modalAttributes, - triggerAttributes, + modalType, + motion, + open, requestOpenChange, + triggerAttributes, }; const dialogSurface: DialogSurfaceContextValue = false; diff --git a/packages/react-components/react-dialog/src/components/DialogSurface/DialogSurface.types.ts b/packages/react-components/react-dialog/src/components/DialogSurface/DialogSurface.types.ts index 1d701722d15bfc..71e067d2036e31 100644 --- a/packages/react-components/react-dialog/src/components/DialogSurface/DialogSurface.types.ts +++ b/packages/react-components/react-dialog/src/components/DialogSurface/DialogSurface.types.ts @@ -1,6 +1,6 @@ import type { ComponentProps, ComponentState, ExtractSlotProps, Slot } from '@fluentui/react-utilities'; import { DialogSurfaceContextValue } from '../../contexts'; -import { MotionShorthand } from '../../../../react-motion-preview/src/index'; +import { MotionShorthand, MotionState } from '../../../../react-motion-preview/src/index'; export type DialogBackdropProps = ExtractSlotProps> & { motion?: MotionShorthand; @@ -35,4 +35,7 @@ export type DialogSurfaceContextValues = { /** * State used in rendering DialogSurface */ -export type DialogSurfaceState = ComponentState; +export type DialogSurfaceState = ComponentState & { + motion: MotionState; + backdropMotion: MotionState; +}; diff --git a/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurface.ts b/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurface.ts index c31b97ee721c58..a504093ee45a78 100644 --- a/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurface.ts +++ b/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurface.ts @@ -6,10 +6,10 @@ import { isResolvedShorthand, slot, } from '@fluentui/react-utilities'; -import type { DialogSurfaceElement, DialogSurfaceProps, DialogSurfaceState } from './DialogSurface.types'; +import { DialogSurfaceElement, DialogSurfaceProps, DialogSurfaceState } from './DialogSurface.types'; import { useDialogContext_unstable } from '../../contexts'; import { Escape } from '@fluentui/keyboard-keys'; -import { useMotion } from '../../../../react-motion-preview/src/index'; +import { useMotionFromSlot } from '@fluentui/react-motion-preview'; /** * Create the state required to render DialogSurface. @@ -28,6 +28,7 @@ export const useDialogSurface_unstable = ( const modalAttributes = useDialogContext_unstable(ctx => ctx.modalAttributes); const dialogRef = useDialogContext_unstable(ctx => ctx.dialogRef); const motion = useDialogContext_unstable(ctx => ctx.motion); + const open = useDialogContext_unstable(ctx => ctx.open); const requestOpenChange = useDialogContext_unstable(ctx => ctx.requestOpenChange); const dialogTitleID = useDialogContext_unstable(ctx => ctx.dialogTitleId); @@ -59,16 +60,18 @@ export const useDialogSurface_unstable = ( } }); - const [backdropMotion, backdropProps] = useMotionFromSlot(props.backdrop, motion.isVisible()); + const [backdropProps, backdropMotion] = useMotionFromSlot(props.backdrop, open); - const backdrop = slot.optional(backdropProps, { - renderByDefault: motion.isVisible() && modalType !== 'non-modal', - defaultProps: { - 'aria-hidden': 'true', - ref: backdropMotion.ref, - }, - elementType: 'div', - }); + const backdrop = + motion.isVisible() && modalType !== 'non-modal' + ? slot.optional(backdropProps, { + defaultProps: { + 'aria-hidden': 'true', + ref: backdropMotion.ref as React.Ref, + }, + elementType: 'div', + }) + : undefined; if (backdrop) { backdrop.onClick = handledBackdropClick; @@ -90,5 +93,8 @@ export const useDialogSurface_unstable = ( }), { elementType: 'div' }, ), + + motion, + backdropMotion, }; }; diff --git a/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurfaceStyles.styles.ts b/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurfaceStyles.styles.ts index 90c97977b91864..450506473deb16 100644 --- a/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurfaceStyles.styles.ts +++ b/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurfaceStyles.styles.ts @@ -18,39 +18,70 @@ export const dialogSurfaceClassNames: SlotClassNames = { /** * Styles for the root slot */ -const useStyles = makeStyles({ +const backdropBackground = { + backgroundColor: 'rgba(0, 0, 0, 0.4)', +}; +const useSurfaceStyles = makeStyles({ focusOutline: createFocusOutlineStyle(), root: { - display: 'block', - userSelect: 'unset', - visibility: 'unset', ...shorthands.inset(0), ...shorthands.padding(0), ...shorthands.padding(SURFACE_PADDING), ...shorthands.margin('auto'), ...shorthands.borderStyle('none'), ...shorthands.overflow('unset'), - '&::backdrop': { - backgroundColor: 'rgba(0, 0, 0, 0.4)', - }, + ...shorthands.border(SURFACE_BORDER_WIDTH, 'solid', tokens.colorTransparentStroke), + ...shorthands.borderRadius(tokens.borderRadiusXLarge), + + display: 'block', + userSelect: 'unset', + visibility: 'unset', position: 'fixed', height: 'fit-content', maxWidth: '600px', maxHeight: '100vh', boxSizing: 'border-box', - boxShadow: tokens.shadow64, backgroundColor: tokens.colorNeutralBackground1, color: tokens.colorNeutralForeground1, - ...shorthands.border(SURFACE_BORDER_WIDTH, 'solid', tokens.colorTransparentStroke), - ...shorthands.borderRadius(tokens.borderRadiusXLarge), + opacity: 0, + transform: 'scale(0.85) translate3D(0, 10%, 0)', + transitionDuration: tokens.durationNormal, + transitionProperty: 'opacity, transform, box-shadow', + + '&::backdrop': backdropBackground, + [MEDIA_QUERY_BREAKPOINT_SELECTOR]: { maxWidth: '100vw', }, }, + + visible: { + boxShadow: tokens.shadow64, + opacity: 1, + transform: 'scale(1) translate3D(0, 0, 0)', + }, + + entering: { + transitionTimingFunction: tokens.curveDecelerateMid, + }, + exiting: { + transitionTimingFunction: tokens.curveAccelerateMin, + }, +}); + +const useBackdropStyles = makeStyles({ backdrop: { - position: 'fixed', - backgroundColor: 'rgba(0, 0, 0, 0.4)', ...shorthands.inset('0px'), + ...backdropBackground, + position: 'fixed', + transitionDuration: tokens.durationNormal, + transitionTimingFunction: tokens.curveLinear, + transitionProperty: 'opacity', + willChange: 'opacity', + opacity: 0, + }, + visible: { + opacity: 1, }, nestedDialogBackdrop: { backgroundColor: 'transparent', @@ -66,23 +97,30 @@ const useStyles = makeStyles({ * Apply styling to the DialogSurface slots based on the state */ export const useDialogSurfaceStyles_unstable = (state: DialogSurfaceState): DialogSurfaceState => { - const styles = useStyles(); + const surfaceStyles = useSurfaceStyles(); + const backdropStyles = useBackdropStyles(); const isNestedDialog = useDialogContext_unstable(ctx => ctx.isNestedDialog); state.root.className = mergeClasses( dialogSurfaceClassNames.root, - styles.root, - styles.focusOutline, - isNestedDialog && styles.nestedNativeDialogBackdrop, + surfaceStyles.root, + surfaceStyles.focusOutline, + isNestedDialog && backdropStyles.nestedNativeDialogBackdrop, + state.motion.isActive() && surfaceStyles.visible, + state.motion.type === 'entering' && surfaceStyles.entering, + state.motion.type === 'exiting' && surfaceStyles.exiting, state.root.className, ); + if (state.backdrop) { state.backdrop.className = mergeClasses( dialogSurfaceClassNames.backdrop, - styles.backdrop, - isNestedDialog && styles.nestedDialogBackdrop, + backdropStyles.backdrop, + isNestedDialog && backdropStyles.nestedDialogBackdrop, + state.backdropMotion.isActive() && backdropStyles.visible, state.backdrop.className, ); } + return state; }; diff --git a/packages/react-components/react-dialog/src/contexts/dialogContext.ts b/packages/react-components/react-dialog/src/contexts/dialogContext.ts index 441091df52ebb0..7bb70cf665db14 100644 --- a/packages/react-components/react-dialog/src/contexts/dialogContext.ts +++ b/packages/react-components/react-dialog/src/contexts/dialogContext.ts @@ -4,9 +4,10 @@ import { DialogSurfaceElement } from '../DialogSurface'; import type { Context } from '@fluentui/react-context-selector'; import type { DialogModalType, DialogOpenChangeData } from '../Dialog'; import { useModalAttributes } from '@fluentui/react-tabster'; -import type { MotionState, MotionType } from '@fluentui/react-motion-preview'; +import type { MotionShorthand, MotionState } from '@fluentui/react-motion-preview'; export type DialogContextValue = { + open: MotionShorthand; motion: MotionState; inertTrapFocus: boolean; dialogTitleId?: string; @@ -20,7 +21,13 @@ export type DialogContextValue = { } & Partial>; const defaultContextValue: DialogContextValue = { - motion: {} as unknown as MotionState, + open: false, + motion: { + ref: { current: null }, + type: 'unmounted', + isActive: () => false, + isVisible: () => false, + }, inertTrapFocus: false, modalType: 'modal', isNestedDialog: false, diff --git a/packages/react-components/react-dialog/src/testing/mockUseDialogContext.ts b/packages/react-components/react-dialog/src/testing/mockUseDialogContext.ts index 9d66e16e935b01..95087462189519 100644 --- a/packages/react-components/react-dialog/src/testing/mockUseDialogContext.ts +++ b/packages/react-components/react-dialog/src/testing/mockUseDialogContext.ts @@ -8,6 +8,12 @@ import type { DialogContextValue } from '../contexts/dialogContext'; */ export const mockUseDialogContext = (options: Partial = {}) => { const mockContext: DialogContextValue = { + motion: { + ref: { current: null }, + type: 'unmounted', + isActive: () => false, + isVisible: () => false, + }, open: false, modalType: 'modal', inertTrapFocus: false, diff --git a/packages/react-components/react-drawer/src/components/DrawerInline/DrawerInline.types.ts b/packages/react-components/react-drawer/src/components/DrawerInline/DrawerInline.types.ts index 9a53ff08e3aa7e..58703b441f500d 100644 --- a/packages/react-components/react-drawer/src/components/DrawerInline/DrawerInline.types.ts +++ b/packages/react-components/react-drawer/src/components/DrawerInline/DrawerInline.types.ts @@ -22,5 +22,5 @@ export type DrawerInlineProps = ComponentProps & * State used in rendering DrawerInline */ export type DrawerInlineState = ComponentState & - Omit & + Omit & DrawerBaseState; diff --git a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts index df9076caaa6bbd..210e7f48beca78 100644 --- a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts +++ b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInline.ts @@ -1,6 +1,6 @@ import * as React from 'react'; import { getNativeElementProps, useControllableState, slot, useMergedRefs } from '@fluentui/react-utilities'; -import { useMotion } from '@fluentui/react-motion-preview'; +import { useMotionFromSlot } from '@fluentui/react-motion-preview'; import type { DrawerInlineProps, DrawerInlineState } from './DrawerInline.types'; import { useBaseDrawerDefaultProps } from '../../util/useBaseDrawerDefaultProps'; @@ -17,17 +17,16 @@ export const useDrawerInline_unstable = ( props: DrawerInlineProps, ref: React.Ref, ): DrawerInlineState => { - const { size, position } = useBaseDrawerDefaultProps(props); + const { size, position, ...defaultProps } = useBaseDrawerDefaultProps(props); + const { separator = false } = props; const [open] = useControllableState({ - state: props.open, - defaultState: props.defaultOpen, + state: defaultProps.open, + defaultState: defaultProps.defaultOpen, initialState: false, }); - const { separator = false, motion = open } = props; - - const drawerMotion = useMotion(motion); + const [drawerProps, drawerMotion] = useMotionFromSlot(props, open); return { components: { @@ -36,8 +35,8 @@ export const useDrawerInline_unstable = ( root: slot.always( getNativeElementProps('div', { + ...drawerProps, ref: useMergedRefs(ref, drawerMotion.ref), - ...props, }), { elementType: 'div' }, ), diff --git a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts index 071e12bb3b2130..25b791a2c78a52 100644 --- a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts +++ b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts @@ -62,12 +62,12 @@ export const useDrawerInlineStyles_unstable = (state: DrawerInlineState): Drawer }, [state.position, state.separator, styles.separatorRight, styles.separatorLeft]); const hiddenClass = React.useCallback(() => { - if (state.motion.active) { + if (state.motion.isActive()) { return undefined; } return mergeClasses(styles.hidden, state.position === 'left' ? styles.hiddenLeft : styles.hiddenRight); - }, [state.motion.active, state.position, styles.hidden, styles.hiddenLeft, styles.hiddenRight]); + }, [state.motion, state.position, styles.hidden, styles.hiddenLeft, styles.hiddenRight]); state.root.className = mergeClasses( drawerInlineClassNames.root, diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts index 9b0c46def05b7a..198db940ee6c34 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlay.ts @@ -1,9 +1,9 @@ import * as React from 'react'; -import { getNativeElementProps, isResolvedShorthand, slot, useMergedRefs } from '@fluentui/react-utilities'; +import { getNativeElementProps, slot, useMergedRefs } from '@fluentui/react-utilities'; import type { DrawerOverlayProps, DrawerOverlayState } from './DrawerOverlay.types'; import { DialogProps, DialogSurface, DialogSurfaceProps } from '@fluentui/react-dialog'; import { useBaseDrawerDefaultProps } from '../../util/useBaseDrawerDefaultProps'; -import { useMotion } from '@fluentui/react-motion-preview'; +import { useMotionFromSlot } from '@fluentui/react-motion-preview'; /** * Create the state required to render DrawerOverlay. @@ -19,22 +19,22 @@ export const useDrawerOverlay_unstable = ( ref: React.Ref, ): DrawerOverlayState => { const { open, defaultOpen, size, position } = useBaseDrawerDefaultProps(props); - const { modalType = 'modal', inertTrapFocus, onOpenChange, motion = open } = props; + const { modalType = 'modal', inertTrapFocus, onOpenChange } = props; - const drawerMotion = useMotion(motion); - const backdropMotion = useMotion(isResolvedShorthand(props.backdrop) ? props.backdrop.motion ?? open : open); + const [drawerProps, drawerMotion] = useMotionFromSlot(props, open); + const [backdropProps, backdropMotion] = useMotionFromSlot(props.backdrop, open); - const hasCustomBackdrop = modalType !== 'non-modal' && props.backdrop !== null; + const hasCustomBackdrop = modalType !== 'non-modal' && backdropProps !== null; const root = slot.always( getNativeElementProps('div', { - ...props, + ...drawerProps, ref: useMergedRefs(ref, drawerMotion.ref), }), { elementType: DialogSurface, defaultProps: { - backdrop: slot.optional(props.backdrop, { + backdrop: slot.optional(backdropProps, { elementType: 'div', renderByDefault: hasCustomBackdrop, defaultProps: { diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts index 7bd5640b44cc67..2072b3f54a1be7 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts @@ -78,7 +78,7 @@ export const useDrawerOverlayStyles_unstable = (state: DrawerOverlayState): Draw getDrawerBaseClassNames(state, baseStyles), state.position && rootStyles[state.position], state.size && durationStyles[state.size], - state.motion.active && rootStyles.visible, + state.motion.isActive() && rootStyles.visible, state.root.className, ); @@ -86,7 +86,7 @@ export const useDrawerOverlayStyles_unstable = (state: DrawerOverlayState): Draw backdrop.className = mergeClasses( drawerOverlayClassNames.backdrop, backdropStyles.backdrop, - state.backdropMotion.active && backdropStyles.backdropVisible, + state.backdropMotion.isActive() && backdropStyles.backdropVisible, state.size && durationStyles[state.size], backdrop.className, ); diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomInlineAnimation.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomInlineAnimation.stories.tsx index 45290be7d92dcc..788e51d8a0bf10 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerCustomInlineAnimation.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerCustomInlineAnimation.stories.tsx @@ -87,11 +87,11 @@ export const CustomInlineAnimation = () => {
@@ -117,10 +117,10 @@ export const CustomInlineAnimation = () => {
- {motion.state !== 'unmounted' && ( -
+ {motion.canRender() && ( +
Lorem ipsum
)} From d8cdb72d6aa78769f488c57df7301ff0c87f84f7 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Mon, 21 Aug 2023 14:50:03 +0200 Subject: [PATCH 086/111] feat: create useMotionStyles to make it easier motion styles to any component --- .../useDialogSurfaceStyles.styles.ts | 22 ++++++++++--- .../useDrawerInlineStyles.styles.ts | 19 +++++------ .../useDrawerOverlayStyles.styles.ts | 33 ++++++++----------- .../src/util/useDrawerBaseStyles.styles.ts | 23 ++++++------- .../react-motion-preview/src/hooks/index.ts | 1 + .../src/hooks/useMotionStyles.styles.ts | 23 +++++++++++++ .../react-motion-preview/src/index.ts | 2 +- 7 files changed, 75 insertions(+), 48 deletions(-) create mode 100644 packages/react-components/react-motion-preview/src/hooks/useMotionStyles.styles.ts diff --git a/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurfaceStyles.styles.ts b/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurfaceStyles.styles.ts index 450506473deb16..e28177263c3c64 100644 --- a/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurfaceStyles.styles.ts +++ b/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurfaceStyles.styles.ts @@ -2,6 +2,7 @@ import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; import type { SlotClassNames } from '@fluentui/react-utilities'; import { tokens } from '@fluentui/react-theme'; import { createFocusOutlineStyle } from '@fluentui/react-tabster'; + import { MEDIA_QUERY_BREAKPOINT_SELECTOR, SURFACE_BORDER_WIDTH, @@ -9,6 +10,7 @@ import { useDialogContext_unstable, } from '../../contexts'; import type { DialogSurfaceSlots, DialogSurfaceState } from './DialogSurface.types'; +import { useMotionStyles } from '@fluentui/react-motion-preview/src/index'; export const dialogSurfaceClassNames: SlotClassNames = { root: 'fui-DialogSurface', @@ -101,14 +103,26 @@ export const useDialogSurfaceStyles_unstable = (state: DialogSurfaceState): Dial const backdropStyles = useBackdropStyles(); const isNestedDialog = useDialogContext_unstable(ctx => ctx.isNestedDialog); + const motionClasses = useMotionStyles( + state.motion, + mergeClasses( + state.motion.isActive() && surfaceStyles.visible, + state.motion.type === 'entering' && surfaceStyles.entering, + state.motion.type === 'exiting' && surfaceStyles.exiting, + ), + ); + + const backdropMotionClasses = useMotionStyles( + state.backdropMotion, + mergeClasses(state.backdropMotion.isActive() && backdropStyles.visible), + ); + state.root.className = mergeClasses( dialogSurfaceClassNames.root, surfaceStyles.root, surfaceStyles.focusOutline, isNestedDialog && backdropStyles.nestedNativeDialogBackdrop, - state.motion.isActive() && surfaceStyles.visible, - state.motion.type === 'entering' && surfaceStyles.entering, - state.motion.type === 'exiting' && surfaceStyles.exiting, + motionClasses, state.root.className, ); @@ -117,7 +131,7 @@ export const useDialogSurfaceStyles_unstable = (state: DialogSurfaceState): Dial dialogSurfaceClassNames.backdrop, backdropStyles.backdrop, isNestedDialog && backdropStyles.nestedDialogBackdrop, - state.backdropMotion.isActive() && backdropStyles.visible, + backdropMotionClasses, state.backdrop.className, ); } diff --git a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts index 1050cb81907eb4..3eccb772c8fb68 100644 --- a/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts +++ b/packages/react-components/react-drawer/src/components/DrawerInline/useDrawerInlineStyles.styles.ts @@ -1,14 +1,16 @@ import * as React from 'react'; import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; -import type { DrawerInlineSlots, DrawerInlineState } from './DrawerInline.types'; import type { SlotClassNames } from '@fluentui/react-utilities'; +import { useMotionStyles } from '@fluentui/react-motion-preview'; +import { tokens } from '@fluentui/react-theme'; + +import type { DrawerInlineSlots, DrawerInlineState } from './DrawerInline.types'; import { drawerCSSVars, useDrawerBaseClassNames, useDrawerBaseStyles, useDrawerDurationStyles, } from '../../util/useDrawerBaseStyles.styles'; -import { tokens } from '@fluentui/react-theme'; export const drawerInlineClassNames: SlotClassNames = { root: 'fui-DrawerInline', @@ -68,18 +70,15 @@ export const useDrawerInlineStyles_unstable = (state: DrawerInlineState): Drawer return state.position === 'start' ? rootStyles.separatorStart : rootStyles.separatorEnd; }, [state.position, state.separator, rootStyles.separatorEnd, rootStyles.separatorStart]); - const motionClasses = React.useMemo(() => { - if (!state.motion.hasInternalMotion) { - return; - } - - return mergeClasses( + const motionClasses = useMotionStyles( + state.motion, + mergeClasses( rootMotionStyles.root, state.size && durationStyles[state.size], state.position && !state.motion.isActive() && rootMotionStyles[state.position], state.motion.isActive() && rootMotionStyles.visible, - ); - }, [durationStyles, rootMotionStyles, state.motion, state.position, state.size]); + ), + ); state.root.className = mergeClasses( drawerInlineClassNames.root, diff --git a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts index f3d4181971bf73..74c19e2236bea3 100644 --- a/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts +++ b/packages/react-components/react-drawer/src/components/DrawerOverlay/useDrawerOverlayStyles.styles.ts @@ -2,6 +2,7 @@ import * as React from 'react'; import { makeStyles, mergeClasses } from '@griffel/react'; import { tokens } from '@fluentui/react-theme'; import type { SlotClassNames } from '@fluentui/react-utilities'; +import { useMotionStyles } from '@fluentui/react-motion-preview'; import type { DrawerOverlaySlots, DrawerOverlayState } from './DrawerOverlay.types'; import { @@ -81,43 +82,35 @@ export const useDrawerOverlayStyles_unstable = (state: DrawerOverlayState): Draw const backdrop = state.root.backdrop as React.HTMLAttributes | undefined; - const motionClasses = React.useMemo(() => { - return mergeClasses( + const motionClasses = useMotionStyles( + state.motion, + mergeClasses( state.position && rootMotionStyles[state.position], state.size && durationStyles[state.size], state.motion.isActive() && rootMotionStyles.visible, state.root.className, - ); - }, [durationStyles, rootMotionStyles, state.motion, state.position, state.root.className, state.size]); + ), + ); - const backdropMotionClasses = React.useMemo(() => { - return mergeClasses( + const backdropMotionClasses = useMotionStyles( + state.backdropMotion, + mergeClasses( backdropMotionStyles.backdrop, state.backdropMotion.isActive() && backdropMotionStyles.backdropVisible, state.size && durationStyles[state.size], - ); - }, [ - backdropMotionStyles.backdrop, - backdropMotionStyles.backdropVisible, - durationStyles, - state.backdropMotion, - state.size, - ]); + ), + ); state.root.className = mergeClasses( drawerOverlayClassNames.root, useDrawerBaseClassNames(state, baseStyles), rootStyles.root, - state.motion.hasInternalMotion && motionClasses, + motionClasses, state.root.className, ); if (backdrop) { - backdrop.className = mergeClasses( - drawerOverlayClassNames.backdrop, - state.backdropMotion.hasInternalMotion && backdropMotionClasses, - backdrop.className, - ); + backdrop.className = mergeClasses(drawerOverlayClassNames.backdrop, backdropMotionClasses, backdrop.className); } return state; diff --git a/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts b/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts index 68b1b68be107c3..c4659b71235c7f 100644 --- a/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts +++ b/packages/react-components/react-drawer/src/util/useDrawerBaseStyles.styles.ts @@ -1,7 +1,9 @@ +import * as React from 'react'; import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; import { tokens } from '@fluentui/react-theme'; +import { useMotionStyles } from '@fluentui/react-motion-preview'; + import { DrawerBaseProps, DrawerBaseState } from './DrawerBase.types'; -import * as React from 'react'; /** * CSS variable names used internally for uniform styling in Drawer. @@ -38,11 +40,6 @@ export const useDrawerBaseStyles = makeStyles({ exiting: { transitionTimingFunction: tokens.curveAccelerateMin, }, - reducedMotion: { - '@media screen and (prefers-reduced-motion: reduce)': { - transitionDuration: '0.001ms', - }, - }, /* Positioning */ start: { @@ -89,16 +86,16 @@ export const useDrawerBaseClassNames = ( baseStyles: ReturnType, ) => { const motionClasses = React.useMemo(() => { - if (!motion.hasInternalMotion) { - return; - } - return mergeClasses( - baseStyles.reducedMotion, motion.type === 'entering' && baseStyles.entering, motion.type === 'exiting' && baseStyles.exiting, ); - }, [baseStyles.entering, baseStyles.exiting, baseStyles.reducedMotion, motion.hasInternalMotion, motion.type]); + }, [baseStyles.entering, baseStyles.exiting, motion.type]); - return mergeClasses(baseStyles.root, position && baseStyles[position], size && baseStyles[size], motionClasses); + return mergeClasses( + baseStyles.root, + position && baseStyles[position], + size && baseStyles[size], + useMotionStyles(motion, motionClasses), + ); }; diff --git a/packages/react-components/react-motion-preview/src/hooks/index.ts b/packages/react-components/react-motion-preview/src/hooks/index.ts index 3883d10f49a97d..724d4c889c0e88 100644 --- a/packages/react-components/react-motion-preview/src/hooks/index.ts +++ b/packages/react-components/react-motion-preview/src/hooks/index.ts @@ -1,2 +1,3 @@ export * from './useMotion'; export * from './useMotionFromSlot'; +export * from './useMotionStyles.styles'; diff --git a/packages/react-components/react-motion-preview/src/hooks/useMotionStyles.styles.ts b/packages/react-components/react-motion-preview/src/hooks/useMotionStyles.styles.ts new file mode 100644 index 00000000000000..886ba8dfafab5f --- /dev/null +++ b/packages/react-components/react-motion-preview/src/hooks/useMotionStyles.styles.ts @@ -0,0 +1,23 @@ +import * as React from 'react'; +import { makeStyles, mergeClasses } from '@griffel/react'; +import { MotionState } from '@fluentui/react-motion-preview'; + +const useStyles = makeStyles({ + reducedMotion: { + '@media screen and (prefers-reduced-motion: reduce)': { + transitionDuration: '0.001ms', + }, + }, +}); + +export function useMotionStyles(motion: MotionState, classNames: string): string { + const styles = useStyles(); + + return React.useMemo(() => { + if (!motion.hasInternalMotion) { + return ''; + } + + return mergeClasses(styles.reducedMotion, classNames); + }, [classNames, motion.hasInternalMotion, styles.reducedMotion]); +} diff --git a/packages/react-components/react-motion-preview/src/index.ts b/packages/react-components/react-motion-preview/src/index.ts index f05472219c6cc9..f03411d86641e4 100644 --- a/packages/react-components/react-motion-preview/src/index.ts +++ b/packages/react-components/react-motion-preview/src/index.ts @@ -1,4 +1,4 @@ -export { getDefaultMotionState, useMotion, useMotionFromSlot } from './hooks'; +export { getDefaultMotionState, useMotion, useMotionFromSlot, useMotionStyles } from './hooks'; export type { MotionShorthand, MotionShorthandValue, From 6debd9832e63ab164b948c02ece9718956da9e1a Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Mon, 21 Aug 2023 14:58:44 +0200 Subject: [PATCH 087/111] feat: make package public --- packages/react-components/react-motion-preview/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react-components/react-motion-preview/package.json b/packages/react-components/react-motion-preview/package.json index a8cee6eb58d21f..a60bd40c1e8adc 100644 --- a/packages/react-components/react-motion-preview/package.json +++ b/packages/react-components/react-motion-preview/package.json @@ -1,7 +1,6 @@ { "name": "@fluentui/react-motion-preview", "version": "0.0.0", - "private": true, "description": "New fluentui react package", "main": "lib-commonjs/index.js", "module": "lib/index.js", From 7f27429cd67dbb62ff268ee7b631ff8b593c7ca2 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Mon, 21 Aug 2023 15:01:22 +0200 Subject: [PATCH 088/111] fix: add missing change file --- ...otion-preview-a0e15534-5b7d-4fb8-a6dd-f28e388add2a.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/@fluentui-react-motion-preview-a0e15534-5b7d-4fb8-a6dd-f28e388add2a.json diff --git a/change/@fluentui-react-motion-preview-a0e15534-5b7d-4fb8-a6dd-f28e388add2a.json b/change/@fluentui-react-motion-preview-a0e15534-5b7d-4fb8-a6dd-f28e388add2a.json new file mode 100644 index 00000000000000..3ee9028024c3d8 --- /dev/null +++ b/change/@fluentui-react-motion-preview-a0e15534-5b7d-4fb8-a6dd-f28e388add2a.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "feat: create react-motion-preview package with useMotion hook", + "packageName": "@fluentui/react-motion-preview", + "email": "marcosvmmoura@gmail.com", + "dependentChangeType": "patch" +} From 95930261ae0bb43c54319c0e7933b008e676042e Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Mon, 21 Aug 2023 15:44:49 +0200 Subject: [PATCH 089/111] fix: join two related hooks in one file to be easier to test --- .../src/hooks/useIsMotion.ts | 43 ------------------ .../src/hooks/useMotion.test.ts | 26 ++++++++++- .../src/hooks/useMotion.ts | 45 ++++++++++++++++++- 3 files changed, 68 insertions(+), 46 deletions(-) delete mode 100644 packages/react-components/react-motion-preview/src/hooks/useIsMotion.ts diff --git a/packages/react-components/react-motion-preview/src/hooks/useIsMotion.ts b/packages/react-components/react-motion-preview/src/hooks/useIsMotion.ts deleted file mode 100644 index 5f2f1204b22d9c..00000000000000 --- a/packages/react-components/react-motion-preview/src/hooks/useIsMotion.ts +++ /dev/null @@ -1,43 +0,0 @@ -import * as React from 'react'; -import { usePrevious } from '@fluentui/react-utilities'; -import type { MotionState, MotionShorthand } from './useMotion'; - -/** - * @internal - * - * This method emits a warning if the hook is called with - * a different typeof of shorthand on subsequent renders, - * since this can lead breaking the rules of hooks. - * - * It also return a boolean indicating whether the shorthand is a motion object. - */ -export function useIsMotion( - shorthand: MotionShorthand, -): shorthand is MotionState { - const previousShorthand = usePrevious(shorthand); - - /** - * Heads up! - * We don't want these warnings in production even though it is against native behavior - */ - if (process.env.NODE_ENV !== 'production') { - // eslint-disable-next-line react-hooks/rules-of-hooks - React.useEffect(() => { - if (previousShorthand !== null && typeof previousShorthand !== typeof shorthand) { - // eslint-disable-next-line no-console - console.error( - [ - 'useMotion: The hook needs to be called with the same typeof of shorthand on every render.', - 'This is to ensure the internal state of the hook is stable and can be used to accurately detect the motion state.', - 'Please make sure to not change the shorthand on subsequent renders or to use the hook conditionally.', - '\nCurrent shorthand:', - JSON.stringify(shorthand, null, 2), - '\nPrevious shorthand:', - JSON.stringify(previousShorthand, null, 2), - ].join(' '), - ); - } - }, [shorthand, previousShorthand]); - } - return typeof shorthand === 'object'; -} diff --git a/packages/react-components/react-motion-preview/src/hooks/useMotion.test.ts b/packages/react-components/react-motion-preview/src/hooks/useMotion.test.ts index 3da78ca040178c..32fb49792d9910 100644 --- a/packages/react-components/react-motion-preview/src/hooks/useMotion.test.ts +++ b/packages/react-components/react-motion-preview/src/hooks/useMotion.test.ts @@ -1,6 +1,7 @@ import { act, renderHook } from '@testing-library/react-hooks'; +import { fail } from 'assert'; -import { useMotion, UseMotionOptions, MotionShorthand, getDefaultMotionState } from './useMotion'; +import { useMotion, UseMotionOptions, MotionShorthand, getDefaultMotionState, useIsMotion } from './useMotion'; const defaultDuration = 100; const renderHookWithRef = ( @@ -42,6 +43,7 @@ describe('useMotion', () => { afterEach(() => { jest.useRealTimers(); + jest.resetAllMocks(); }); describe('when presence is false by default', () => { @@ -234,4 +236,26 @@ describe('useMotion', () => { expect(result.current.isActive()).toStrictEqual(true); }); }); + + it('should show error when motion changes to a different type', () => { + const spy = jest.spyOn(console, 'error').mockImplementation(() => ({})); + let defaultMotion: MotionShorthand = getDefaultMotionState(); + const { rerender } = renderHook(() => useIsMotion(defaultMotion)); + + defaultMotion = false; + + rerender(); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + [ + 'useMotion: The hook needs to be called with the same typeof of shorthand on every render.', + 'This is to ensure the internal state of the hook is stable and can be used to accurately detect the motion state.', + 'Please make sure to not change the shorthand on subsequent renders or to use the hook conditionally.', + '\nCurrent shorthand:', + JSON.stringify(defaultMotion, null, 2), + '\nPrevious shorthand:', + JSON.stringify(getDefaultMotionState(), null, 2), + ].join(' '), + ); + }); }); diff --git a/packages/react-components/react-motion-preview/src/hooks/useMotion.ts b/packages/react-components/react-motion-preview/src/hooks/useMotion.ts index 53d5391c2f4ca1..fd40fbca61109f 100644 --- a/packages/react-components/react-motion-preview/src/hooks/useMotion.ts +++ b/packages/react-components/react-motion-preview/src/hooks/useMotion.ts @@ -1,8 +1,7 @@ import * as React from 'react'; import { unstable_batchedUpdates } from 'react-dom'; import { HTMLElementWithStyledMap, getMotionDuration } from '../utils/dom-style'; -import { useAnimationFrame, useTimeout } from '@fluentui/react-utilities'; -import { useIsMotion } from './useIsMotion'; +import { useAnimationFrame, useTimeout, usePrevious } from '@fluentui/react-utilities'; export type UseMotionOptions = { /** @@ -220,3 +219,45 @@ export function useMotion( // eslint-disable-next-line react-hooks/rules-of-hooks return useIsMotion(shorthand) ? shorthand : useMotionPresence(shorthand, options); } + +/** + * @internal + * + * This method emits a warning if the hook is called with + * a different typeof of shorthand on subsequent renders, + * since this can lead breaking the rules of hooks. + * + * It also return a boolean indicating whether the shorthand is a motion object. + */ +export function useIsMotion( + shorthand: MotionShorthand, +): shorthand is MotionState { + const previousShorthand = usePrevious(shorthand); + + /** + * Heads up! + * We don't want these warnings in production even though it is against native behavior + */ + if (process.env.NODE_ENV !== 'production') { + // eslint-disable-next-line react-hooks/rules-of-hooks + React.useEffect(() => { + if (previousShorthand !== null && typeof previousShorthand !== typeof shorthand) { + const stringifyShorthand = (value: MotionShorthand) => JSON.stringify(value, null, 2); + + // eslint-disable-next-line no-console + console.error( + [ + 'useMotion: The hook needs to be called with the same typeof of shorthand on every render.', + 'This is to ensure the internal state of the hook is stable and can be used to accurately detect the motion state.', + 'Please make sure to not change the shorthand on subsequent renders or to use the hook conditionally.', + '\nCurrent shorthand:', + stringifyShorthand(shorthand), + '\nPrevious shorthand:', + stringifyShorthand(previousShorthand), + ].join(' '), + ); + } + }, [shorthand, previousShorthand]); + } + return typeof shorthand === 'object'; +} From fd9970f6c29d64527eb15a6afb1f0fdb644b7d69 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Mon, 21 Aug 2023 16:59:47 +0200 Subject: [PATCH 090/111] fix: remove unused imports --- .../react-motion-preview/src/hooks/useMotion.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react-components/react-motion-preview/src/hooks/useMotion.test.ts b/packages/react-components/react-motion-preview/src/hooks/useMotion.test.ts index 32fb49792d9910..5150ef6f6d55c4 100644 --- a/packages/react-components/react-motion-preview/src/hooks/useMotion.test.ts +++ b/packages/react-components/react-motion-preview/src/hooks/useMotion.test.ts @@ -1,5 +1,4 @@ import { act, renderHook } from '@testing-library/react-hooks'; -import { fail } from 'assert'; import { useMotion, UseMotionOptions, MotionShorthand, getDefaultMotionState, useIsMotion } from './useMotion'; From 5cb0a56de1081b31a4c208123678ed88d6d4eb26 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Mon, 21 Aug 2023 18:18:53 +0200 Subject: [PATCH 091/111] docs: move stories to public site --- .../useMotion/MotionExample.stories.tsx | 2 +- .../Motion/useMotion/index.stories.mdx | 79 +++++++++++++++++++ .../stories/useMotion/UseMotionDescription.md | 1 - .../stories/useMotion/index.stories.tsx | 14 ---- 4 files changed, 80 insertions(+), 16 deletions(-) rename packages/react-components/react-motion-preview/stories/useMotion/UseMotionDefault.stories.tsx => apps/public-docsite-v9/src/Utilities/Motion/useMotion/MotionExample.stories.tsx (97%) create mode 100644 apps/public-docsite-v9/src/Utilities/Motion/useMotion/index.stories.mdx delete mode 100644 packages/react-components/react-motion-preview/stories/useMotion/UseMotionDescription.md delete mode 100644 packages/react-components/react-motion-preview/stories/useMotion/index.stories.tsx diff --git a/packages/react-components/react-motion-preview/stories/useMotion/UseMotionDefault.stories.tsx b/apps/public-docsite-v9/src/Utilities/Motion/useMotion/MotionExample.stories.tsx similarity index 97% rename from packages/react-components/react-motion-preview/stories/useMotion/UseMotionDefault.stories.tsx rename to apps/public-docsite-v9/src/Utilities/Motion/useMotion/MotionExample.stories.tsx index f53cc2f09184c7..34c07534817bd9 100644 --- a/packages/react-components/react-motion-preview/stories/useMotion/UseMotionDefault.stories.tsx +++ b/apps/public-docsite-v9/src/Utilities/Motion/useMotion/MotionExample.stories.tsx @@ -38,7 +38,7 @@ const useStyles = makeStyles({ }, }); -export const Default = () => { +export const MotionExample = () => { const styles = useStyles(); const [open, setOpen] = React.useState(false); diff --git a/apps/public-docsite-v9/src/Utilities/Motion/useMotion/index.stories.mdx b/apps/public-docsite-v9/src/Utilities/Motion/useMotion/index.stories.mdx new file mode 100644 index 00000000000000..4797461ed3ec2d --- /dev/null +++ b/apps/public-docsite-v9/src/Utilities/Motion/useMotion/index.stories.mdx @@ -0,0 +1,79 @@ +import { Meta, Description } from '@storybook/addon-docs'; + +import { MotionExample } from './MotionExample.stories'; + + + + + A tracker hook, that monitors the state of animations and transitions for a particular element. This hook does not + directly create animations but instead synchronizes with CSS properties to determine the rendering status, visibility, + entering, leaving, and ongoing animation of a component. If any CSS changes or properties are overridden, this hook + will automatically adjust and stay synchronized. + + +## Usage + +```tsx +import * as React from 'react'; + +import { useMotion } from '@fluentui/react-motion-preview'; +import { Button, makeStyles, mergeClasses, shorthands, tokens } from '@fluentui/react-components'; + +const useStyles = makeStyles({ + root: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + rowGap: '24px', + }, + + rectangle: { + ...shorthands.borderRadius('8px'), + + width: '200px', + height: '150px', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + backgroundColor: tokens.colorBrandBackground2, + opacity: 0, + transform: 'translate3D(0, 0, 0) scale(0.25)', + transitionDuration: `${tokens.durationNormal}, ${tokens.durationNormal}, ${tokens.durationUltraSlow}`, + transitionDelay: `${tokens.durationFast}, 0, ${tokens.durationSlow}`, + transitionProperty: 'opacity, transform, background-color', + willChange: 'opacity, transform, background-color', + color: '#fff', + }, + + visible: { + opacity: 1, + transform: 'translate3D(0, 0, 0) scale(1)', + backgroundColor: tokens.colorBrandBackground, + }, +}); + +export const MotionExample = () => { + const styles = useStyles(); + + const [open, setOpen] = React.useState(false); + const motion = useMotion(open); + + return ( +
+ + + {motion.canRender() && ( +
+ Lorem ipsum +
+ )} +
+ ); +}; +``` + + diff --git a/packages/react-components/react-motion-preview/stories/useMotion/UseMotionDescription.md b/packages/react-components/react-motion-preview/stories/useMotion/UseMotionDescription.md deleted file mode 100644 index 70098da049b451..00000000000000 --- a/packages/react-components/react-motion-preview/stories/useMotion/UseMotionDescription.md +++ /dev/null @@ -1 +0,0 @@ -A tracker hook, that monitors the state of animations and transitions for a particular element. This hook does not directly create animations but instead synchronizes with CSS properties to determine the rendering status, visibility, entering, leaving, and ongoing animation of a component. If any CSS changes or properties are overridden, this hook will automatically adjust and stay synchronized. diff --git a/packages/react-components/react-motion-preview/stories/useMotion/index.stories.tsx b/packages/react-components/react-motion-preview/stories/useMotion/index.stories.tsx deleted file mode 100644 index 628a07e28bb598..00000000000000 --- a/packages/react-components/react-motion-preview/stories/useMotion/index.stories.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import descriptionMd from './UseMotionDescription.md'; - -export { Default } from './UseMotionDefault.stories'; - -export default { - title: 'Utilities/Motion/useMotion', - parameters: { - docs: { - description: { - component: [descriptionMd].join('\n'), - }, - }, - }, -}; From 92ecfed7a659690b3727de5d49adb64751941bf3 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Mon, 21 Aug 2023 19:26:04 +0200 Subject: [PATCH 092/111] docs: add title to example --- .../src/Utilities/Motion/useMotion/index.stories.mdx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/public-docsite-v9/src/Utilities/Motion/useMotion/index.stories.mdx b/apps/public-docsite-v9/src/Utilities/Motion/useMotion/index.stories.mdx index 4797461ed3ec2d..6d2544660a211b 100644 --- a/apps/public-docsite-v9/src/Utilities/Motion/useMotion/index.stories.mdx +++ b/apps/public-docsite-v9/src/Utilities/Motion/useMotion/index.stories.mdx @@ -1,7 +1,9 @@ -import { Meta, Description } from '@storybook/addon-docs'; +import { Title, Subtitle, Meta, Description } from '@storybook/addon-docs'; import { MotionExample } from './MotionExample.stories'; +useMotion + @@ -11,7 +13,7 @@ import { MotionExample } from './MotionExample.stories'; will automatically adjust and stay synchronized. -## Usage +Usage ```tsx import * as React from 'react'; @@ -76,4 +78,6 @@ export const MotionExample = () => { }; ``` +Example + From 9086d1ab98b07a2f0e3a655a4175703a9dfe0f8f Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Tue, 22 Aug 2023 15:30:50 +0200 Subject: [PATCH 093/111] fix: remove leftover property --- .../react-components/react-motion-preview/src/hooks/useMotion.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react-components/react-motion-preview/src/hooks/useMotion.ts b/packages/react-components/react-motion-preview/src/hooks/useMotion.ts index fd40fbca61109f..4bd2449649deb8 100644 --- a/packages/react-components/react-motion-preview/src/hooks/useMotion.ts +++ b/packages/react-components/react-motion-preview/src/hooks/useMotion.ts @@ -182,7 +182,6 @@ function useMotionPresence( type, canRender, isActive, - hasInternalMotion: true, }; }, [active, ref, type]); } From 969ce8750cc7dc3f0561b42b219098750fd9d9ad Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Tue, 22 Aug 2023 15:32:14 +0200 Subject: [PATCH 094/111] fix: add type to improve useMemo --- .../react-motion-preview/src/hooks/useMotion.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-components/react-motion-preview/src/hooks/useMotion.ts b/packages/react-components/react-motion-preview/src/hooks/useMotion.ts index 4bd2449649deb8..4015a0d4d0a562 100644 --- a/packages/react-components/react-motion-preview/src/hooks/useMotion.ts +++ b/packages/react-components/react-motion-preview/src/hooks/useMotion.ts @@ -173,7 +173,7 @@ function useMotionPresence( skipAnimationOnFirstRender.current = false; }, []); - return React.useMemo(() => { + return React.useMemo>(() => { const canRender = () => type !== 'unmounted'; const isActive = () => active; From 32d414404165c4eeaa4d811e1b6aa2dbc7b4b52c Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Tue, 22 Aug 2023 20:24:21 +0200 Subject: [PATCH 095/111] fix: use correct import paths --- .../src/components/DialogSurface/DialogSurface.types.ts | 2 +- .../components/DialogSurface/useDialogSurfaceStyles.styles.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-components/react-dialog/src/components/DialogSurface/DialogSurface.types.ts b/packages/react-components/react-dialog/src/components/DialogSurface/DialogSurface.types.ts index 71e067d2036e31..305505e599dd99 100644 --- a/packages/react-components/react-dialog/src/components/DialogSurface/DialogSurface.types.ts +++ b/packages/react-components/react-dialog/src/components/DialogSurface/DialogSurface.types.ts @@ -1,6 +1,6 @@ import type { ComponentProps, ComponentState, ExtractSlotProps, Slot } from '@fluentui/react-utilities'; import { DialogSurfaceContextValue } from '../../contexts'; -import { MotionShorthand, MotionState } from '../../../../react-motion-preview/src/index'; +import { MotionShorthand, MotionState } from '@fluentui/react-motion-preview'; export type DialogBackdropProps = ExtractSlotProps> & { motion?: MotionShorthand; diff --git a/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurfaceStyles.styles.ts b/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurfaceStyles.styles.ts index e28177263c3c64..9473cbee8e3024 100644 --- a/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurfaceStyles.styles.ts +++ b/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurfaceStyles.styles.ts @@ -10,7 +10,7 @@ import { useDialogContext_unstable, } from '../../contexts'; import type { DialogSurfaceSlots, DialogSurfaceState } from './DialogSurface.types'; -import { useMotionStyles } from '@fluentui/react-motion-preview/src/index'; +import { useMotionStyles } from '@fluentui/react-motion-preview'; export const dialogSurfaceClassNames: SlotClassNames = { root: 'fui-DialogSurface', From f09ff57059113d43e909cebe45a7f8065349fe5e Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Tue, 22 Aug 2023 20:32:18 +0200 Subject: [PATCH 096/111] feat: use boolean instead of function --- .../etc/react-motion-preview.api.md | 4 +- .../src/hooks/useMotion.test.ts | 42 +++++++++---------- .../src/hooks/useMotion.ts | 24 +++++------ 3 files changed, 34 insertions(+), 36 deletions(-) diff --git a/packages/react-components/react-motion-preview/etc/react-motion-preview.api.md b/packages/react-components/react-motion-preview/etc/react-motion-preview.api.md index ff9f3747df6303..c56021c458ed19 100644 --- a/packages/react-components/react-motion-preview/etc/react-motion-preview.api.md +++ b/packages/react-components/react-motion-preview/etc/react-motion-preview.api.md @@ -19,8 +19,8 @@ export type MotionShorthandValue = boolean; export type MotionState = { ref: React_2.Ref; type: MotionType; - isActive(): boolean; - canRender(): boolean; + active: boolean; + canRender: boolean; }; // @public (undocumented) diff --git a/packages/react-components/react-motion-preview/src/hooks/useMotion.test.ts b/packages/react-components/react-motion-preview/src/hooks/useMotion.test.ts index 5150ef6f6d55c4..0056bd8fc2fac4 100644 --- a/packages/react-components/react-motion-preview/src/hooks/useMotion.test.ts +++ b/packages/react-components/react-motion-preview/src/hooks/useMotion.test.ts @@ -51,7 +51,7 @@ describe('useMotion', () => { expect(typeof result.current.ref).toBe('function'); expect(result.current.type).toBe('unmounted'); - expect(result.current.isActive()).toBe(false); + expect(result.current.active).toBe(false); }); }); @@ -60,18 +60,18 @@ describe('useMotion', () => { const { result } = renderHookWithRef(true); expect(typeof result.current.ref).toBe('function'); - expect(result.current.isActive()).toBe(true); + expect(result.current.active).toBe(true); }); it('should change visible to true when animateOnFirstMount is true', () => { const { result } = renderHookWithRef(true, { animateOnFirstMount: true }); expect(typeof result.current.ref).toBe('function'); - expect(result.current.isActive()).toBe(false); + expect(result.current.active).toBe(false); jumpToNextFrame(); - expect(result.current.isActive()).toBe(true); + expect(result.current.active).toBe(true); }); }); @@ -81,14 +81,14 @@ describe('useMotion', () => { expect(typeof result.current.ref).toBe('function'); expect(result.current.type).toBe('unmounted'); - expect(result.current.isActive()).toBe(false); + expect(result.current.active).toBe(false); rerender({ motion: true }); act(() => jest.advanceTimersToNextTimer()); expect(result.current.type).toBe('entering'); - expect(result.current.isActive()).toBe(true); + expect(result.current.active).toBe(true); act(() => jest.advanceTimersByTime(defaultDuration + 1)); expect(result.current.type).toBe('entered'); @@ -101,35 +101,35 @@ describe('useMotion', () => { act(() => jest.advanceTimersToNextTimer()); expect(result.current.type).toBe('exiting'); - expect(result.current.isActive()).toBe(false); + expect(result.current.active).toBe(false); act(() => jest.advanceTimersByTime(defaultDuration + 1)); expect(result.current.type).toBe('exited'); act(() => jest.advanceTimersToNextTimer()); expect(result.current.type).toBe('unmounted'); - expect(result.current.isActive()).toBe(false); + expect(result.current.active).toBe(false); }); it('should toggle values starting with true', () => { const { result, rerender } = renderHookWithRef(true); expect(typeof result.current.ref).toBe('function'); - expect(result.current.isActive()).toBe(true); + expect(result.current.active).toBe(true); rerender({ motion: false }); act(() => jest.advanceTimersToNextTimer()); expect(result.current.type).toBe('exiting'); - expect(result.current.isActive()).toBe(false); + expect(result.current.active).toBe(false); act(() => jest.advanceTimersByTime(defaultDuration + 1)); expect(result.current.type).toBe('exited'); act(() => jest.advanceTimersToNextTimer()); expect(result.current.type).toBe('unmounted'); - expect(result.current.isActive()).toBe(false); + expect(result.current.active).toBe(false); }); }); @@ -144,14 +144,14 @@ describe('useMotion', () => { expect(typeof result.current.ref).toBe('function'); expect(result.current.type).toBe('unmounted'); - expect(result.current.isActive()).toBe(false); + expect(result.current.active).toBe(false); rerender({ motion: true }); act(() => jest.advanceTimersToNextTimer()); expect(result.current.type).toBe('entering'); - expect(result.current.isActive()).toBe(true); + expect(result.current.active).toBe(true); act(() => jest.advanceTimersToNextTimer()); @@ -166,14 +166,14 @@ describe('useMotion', () => { act(() => jest.advanceTimersToNextTimer()); expect(result.current.type).toBe('exiting'); - expect(result.current.isActive()).toBe(false); + expect(result.current.active).toBe(false); act(() => jest.advanceTimersToNextTimer()); expect(result.current.type).toBe('exited'); act(() => jest.advanceTimersToNextTimer()); expect(result.current.type).toBe('unmounted'); - expect(result.current.isActive()).toBe(false); + expect(result.current.active).toBe(false); }); }); @@ -186,7 +186,7 @@ describe('useMotion', () => { expect(typeof result.current.ref).toBe('function'); expect(result.current.type).toBe('unmounted'); - expect(result.current.isActive()).toBe(false); + expect(result.current.active).toBe(false); rerender({ motion: true }); @@ -198,7 +198,7 @@ describe('useMotion', () => { // requestAnimationFrame act(() => jest.advanceTimersToNextTimer()); - expect(result.current.isActive()).toBe(true); + expect(result.current.active).toBe(true); // timeout act(() => jest.advanceTimersToNextTimer()); expect(result.current.type).toBe('idle'); @@ -207,7 +207,7 @@ describe('useMotion', () => { act(() => jest.advanceTimersToNextTimer()); act(() => jest.advanceTimersToNextTimer()); - expect(result.current.isActive()).toBe(false); + expect(result.current.active).toBe(false); // requestAnimationFrame act(() => jest.advanceTimersToNextTimer()); @@ -224,15 +224,15 @@ describe('useMotion', () => { expect(result.current.type).toStrictEqual('unmounted'); expect(result.current.ref).toStrictEqual(defaultState.ref); - expect(result.current.isActive()).toStrictEqual(false); + expect(result.current.active).toStrictEqual(false); }); it('should return default values when presence is true', () => { const defaultState = getDefaultMotionState(); - const { result } = renderHookWithRef({ ...getDefaultMotionState(), isActive: () => true }); + const { result } = renderHookWithRef({ ...getDefaultMotionState(), active: true }); expect(result.current.ref).toStrictEqual(defaultState.ref); - expect(result.current.isActive()).toStrictEqual(true); + expect(result.current.active).toStrictEqual(true); }); }); diff --git a/packages/react-components/react-motion-preview/src/hooks/useMotion.ts b/packages/react-components/react-motion-preview/src/hooks/useMotion.ts index 4015a0d4d0a562..7f5dc0c421ccd2 100644 --- a/packages/react-components/react-motion-preview/src/hooks/useMotion.ts +++ b/packages/react-components/react-motion-preview/src/hooks/useMotion.ts @@ -36,13 +36,13 @@ export type MotionState = { * Indicates whether the component is currently rendered and visible. * Useful to apply CSS transitions only when the element is active. */ - isActive(): boolean; + active: boolean; /** * Indicates whether the component can be rendered. * This can be used to avoid rendering the component when it is not visible anymore. */ - canRender(): boolean; + canRender: boolean; }; export type MotionShorthandValue = boolean; @@ -173,17 +173,15 @@ function useMotionPresence( skipAnimationOnFirstRender.current = false; }, []); - return React.useMemo>(() => { - const canRender = () => type !== 'unmounted'; - const isActive = () => active; - - return { + return React.useMemo>( + () => ({ ref, type, - canRender, - isActive, - }; - }, [active, ref, type]); + canRender: type !== 'unmounted', + active, + }), + [active, ref, type], + ); } /** @@ -193,8 +191,8 @@ export function getDefaultMotionState(): MotionStat return { ref: React.createRef(), type: 'unmounted', - isActive: () => false, - canRender: () => false, + active: false, + canRender: false, }; } From a1616d2a86aedb9a13b2d81833ea6299bed49c86 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Wed, 23 Aug 2023 21:10:24 +0200 Subject: [PATCH 097/111] fix: remove outdated changefile --- ...act-utilities-c9616c2d-de84-45eb-bef4-f7509f49cded.json | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 change/@fluentui-react-utilities-c9616c2d-de84-45eb-bef4-f7509f49cded.json diff --git a/change/@fluentui-react-utilities-c9616c2d-de84-45eb-bef4-f7509f49cded.json b/change/@fluentui-react-utilities-c9616c2d-de84-45eb-bef4-f7509f49cded.json deleted file mode 100644 index 7a3c251867b099..00000000000000 --- a/change/@fluentui-react-utilities-c9616c2d-de84-45eb-bef4-f7509f49cded.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "minor", - "comment": "feat: create useAnimationFrame hook", - "packageName": "@fluentui/react-utilities", - "email": "marcosvmmoura@gmail.com", - "dependentChangeType": "patch" -} From 74b8257b1a959aad83563023b32650f5b121be11 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Thu, 24 Aug 2023 00:53:35 +0200 Subject: [PATCH 098/111] fix: use correct boolean values --- .../src/Utilities/Motion/useMotion/MotionExample.stories.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/public-docsite-v9/src/Utilities/Motion/useMotion/MotionExample.stories.tsx b/apps/public-docsite-v9/src/Utilities/Motion/useMotion/MotionExample.stories.tsx index 34c07534817bd9..2362d993402914 100644 --- a/apps/public-docsite-v9/src/Utilities/Motion/useMotion/MotionExample.stories.tsx +++ b/apps/public-docsite-v9/src/Utilities/Motion/useMotion/MotionExample.stories.tsx @@ -50,8 +50,8 @@ export const MotionExample = () => { Toggle - {motion.canRender() && ( -
+ {motion.canRender && ( +
Lorem ipsum
)} From 7942e80d705e94225958a9c9271c40bc6af39e4f Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Thu, 24 Aug 2023 01:15:44 +0200 Subject: [PATCH 099/111] fix: improve typings and documentation --- .../etc/react-motion-preview.api.md | 14 +++++----- .../src/hooks/useMotion.ts | 26 ++++++++++--------- .../react-motion-preview/src/index.ts | 2 +- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/packages/react-components/react-motion-preview/etc/react-motion-preview.api.md b/packages/react-components/react-motion-preview/etc/react-motion-preview.api.md index c56021c458ed19..208840c392ac1c 100644 --- a/packages/react-components/react-motion-preview/etc/react-motion-preview.api.md +++ b/packages/react-components/react-motion-preview/etc/react-motion-preview.api.md @@ -9,6 +9,11 @@ import * as React_2 from 'react'; // @public export function getDefaultMotionState(): MotionState; +// @public (undocumented) +export type MotionOptions = { + animateOnFirstMount?: boolean; +}; + // @public (undocumented) export type MotionShorthand = MotionShorthandValue | MotionState; @@ -19,20 +24,15 @@ export type MotionShorthandValue = boolean; export type MotionState = { ref: React_2.Ref; type: MotionType; - active: boolean; canRender: boolean; + active: boolean; }; // @public (undocumented) export type MotionType = 'unmounted' | 'entering' | 'entered' | 'idle' | 'exiting' | 'exited'; // @public -export function useMotion(shorthand: MotionShorthand, options?: UseMotionOptions): MotionState; - -// @public (undocumented) -export type UseMotionOptions = { - animateOnFirstMount?: boolean; -}; +export function useMotion(shorthand: MotionShorthand, options?: MotionOptions): MotionState; // (No @packageDocumentation comment for this package) diff --git a/packages/react-components/react-motion-preview/src/hooks/useMotion.ts b/packages/react-components/react-motion-preview/src/hooks/useMotion.ts index 7f5dc0c421ccd2..13db15a8ed04d9 100644 --- a/packages/react-components/react-motion-preview/src/hooks/useMotion.ts +++ b/packages/react-components/react-motion-preview/src/hooks/useMotion.ts @@ -3,7 +3,7 @@ import { unstable_batchedUpdates } from 'react-dom'; import { HTMLElementWithStyledMap, getMotionDuration } from '../utils/dom-style'; import { useAnimationFrame, useTimeout, usePrevious } from '@fluentui/react-utilities'; -export type UseMotionOptions = { +export type MotionOptions = { /** * Whether to animate the element on first mount. * @@ -33,16 +33,16 @@ export type MotionState = { type: MotionType; /** - * Indicates whether the component is currently rendered and visible. - * Useful to apply CSS transitions only when the element is active. + * Indicates whether the component can be rendered. + * Useful to render the element before animating it or to remove it from the DOM after exit animation. */ - active: boolean; + canRender: boolean; /** - * Indicates whether the component can be rendered. - * This can be used to avoid rendering the component when it is not visible anymore. + * Indicates whether the component is ready to receive a CSS transition className. + * Useful to apply CSS transitions when the element is mounted and ready to be animated. */ - canRender: boolean; + active: boolean; }; export type MotionShorthandValue = boolean; @@ -59,7 +59,7 @@ export type MotionShorthand = MotionS */ function useMotionPresence( presence: boolean, - options: UseMotionOptions = {}, + options: MotionOptions = {}, ): MotionState { const { animateOnFirstMount } = { animateOnFirstMount: false, ...options }; @@ -177,8 +177,8 @@ function useMotionPresence( () => ({ ref, type, - canRender: type !== 'unmounted', active, + canRender: type !== 'unmounted', }), [active, ref, type], ); @@ -204,7 +204,7 @@ export function getDefaultMotionState(): MotionStat */ export function useMotion( shorthand: MotionShorthand, - options?: UseMotionOptions, + options?: MotionOptions, ): MotionState { /** * Heads up! @@ -217,6 +217,10 @@ export function useMotion( return useIsMotion(shorthand) ? shorthand : useMotionPresence(shorthand, options); } +const stringifyShorthand = (value: MotionShorthand) => { + return JSON.stringify(value, null, 2); +}; + /** * @internal * @@ -239,8 +243,6 @@ export function useIsMotion( // eslint-disable-next-line react-hooks/rules-of-hooks React.useEffect(() => { if (previousShorthand !== null && typeof previousShorthand !== typeof shorthand) { - const stringifyShorthand = (value: MotionShorthand) => JSON.stringify(value, null, 2); - // eslint-disable-next-line no-console console.error( [ diff --git a/packages/react-components/react-motion-preview/src/index.ts b/packages/react-components/react-motion-preview/src/index.ts index b70d2c237cae24..dc8bd773780164 100644 --- a/packages/react-components/react-motion-preview/src/index.ts +++ b/packages/react-components/react-motion-preview/src/index.ts @@ -1,2 +1,2 @@ export { getDefaultMotionState, useMotion } from './hooks'; -export type { MotionShorthand, MotionShorthandValue, MotionState, MotionType, UseMotionOptions } from './hooks'; +export type { MotionShorthand, MotionShorthandValue, MotionState, MotionType, MotionOptions } from './hooks'; From b37b1065a14f809c07409320e8b34715abe2362f Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Mon, 28 Aug 2023 17:18:31 +0200 Subject: [PATCH 100/111] Discard changes to packages/react-charting/src/components/AreaChart/AreaChart.base.tsx --- .../src/components/AreaChart/AreaChart.base.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-charting/src/components/AreaChart/AreaChart.base.tsx b/packages/react-charting/src/components/AreaChart/AreaChart.base.tsx index 22adbd700a7e5f..40209941efafae 100644 --- a/packages/react-charting/src/components/AreaChart/AreaChart.base.tsx +++ b/packages/react-charting/src/components/AreaChart/AreaChart.base.tsx @@ -293,7 +293,7 @@ export class AreaChartBase extends React.Component { this._onLegendClick(singleChartData.legend); }, From 48c370e7b5b975adf17e1459219e3da0cb259e56 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Mon, 28 Aug 2023 17:18:40 +0200 Subject: [PATCH 101/111] Discard changes to packages/react-charting/src/components/HorizontalBarChart/HorizontalBarChart.base.tsx --- .../components/HorizontalBarChart/HorizontalBarChart.base.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-charting/src/components/HorizontalBarChart/HorizontalBarChart.base.tsx b/packages/react-charting/src/components/HorizontalBarChart/HorizontalBarChart.base.tsx index 82b83cac3919d3..72f3f627659feb 100644 --- a/packages/react-charting/src/components/HorizontalBarChart/HorizontalBarChart.base.tsx +++ b/packages/react-charting/src/components/HorizontalBarChart/HorizontalBarChart.base.tsx @@ -199,7 +199,7 @@ export class HorizontalBarChartBase extends React.Component Date: Mon, 28 Aug 2023 17:18:44 +0200 Subject: [PATCH 102/111] Discard changes to packages/react-charting/src/components/StackedBarChart/MultiStackedBarChart.base.tsx --- .../MultiStackedBarChart.base.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/react-charting/src/components/StackedBarChart/MultiStackedBarChart.base.tsx b/packages/react-charting/src/components/StackedBarChart/MultiStackedBarChart.base.tsx index 27857d3df1d197..944b857a1c50ce 100644 --- a/packages/react-charting/src/components/StackedBarChart/MultiStackedBarChart.base.tsx +++ b/packages/react-charting/src/components/StackedBarChart/MultiStackedBarChart.base.tsx @@ -267,8 +267,8 @@ export class MultiStackedBarChartBase extends React.Component { this._onClick(point.legend!); }, @@ -495,7 +495,7 @@ export class MultiStackedBarChartBase extends React.Component { this._onClick(point.legend!); }, @@ -556,7 +556,7 @@ export class MultiStackedBarChartBase extends React.Component Date: Mon, 28 Aug 2023 17:18:54 +0200 Subject: [PATCH 103/111] Discard changes to packages/react-charting/src/components/LineChart/LineChartRTL.test.tsx --- .../src/components/LineChart/LineChartRTL.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-charting/src/components/LineChart/LineChartRTL.test.tsx b/packages/react-charting/src/components/LineChart/LineChartRTL.test.tsx index 57c3e6d8b24321..d550da2db6238b 100644 --- a/packages/react-charting/src/components/LineChart/LineChartRTL.test.tsx +++ b/packages/react-charting/src/components/LineChart/LineChartRTL.test.tsx @@ -587,7 +587,7 @@ describe('Line chart - Subcomponent Event', () => { testWithWait( 'Should render events with defined data', LineChart, - { data: simplePoints, eventAnnotationProps, tickValues, tickFormat: '%m/%d' }, + { data: simplePoints, eventAnnotationProps: eventAnnotationProps, tickValues: tickValues, tickFormat: '%m/%d' }, container => { // Arrange const event = screen.queryByText('3 events'); From 74f6a08ab3c7d424ed6b3d3b8c99d1335af9238f Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Mon, 28 Aug 2023 17:18:58 +0200 Subject: [PATCH 104/111] Discard changes to packages/react-charting/src/components/StackedBarChart/StackedBarChart.base.tsx --- .../components/StackedBarChart/StackedBarChart.base.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/react-charting/src/components/StackedBarChart/StackedBarChart.base.tsx b/packages/react-charting/src/components/StackedBarChart/StackedBarChart.base.tsx index dd764ab2c965a5..fe71c5212a60c6 100644 --- a/packages/react-charting/src/components/StackedBarChart/StackedBarChart.base.tsx +++ b/packages/react-charting/src/components/StackedBarChart/StackedBarChart.base.tsx @@ -276,7 +276,7 @@ export class StackedBarChartBase extends React.Component 0 ? () => { @@ -316,7 +316,7 @@ export class StackedBarChartBase extends React.Component Date: Sun, 3 Sep 2023 22:06:04 +0200 Subject: [PATCH 105/111] Discard changes to .github/CODEOWNERS --- .github/CODEOWNERS | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 49296d735b7ff5..cb7af7f60a7e0b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -239,7 +239,6 @@ packages/react-components/react-jsx-runtime @microsoft/teams-prg packages/react-components/react-toast @microsoft/teams-prg packages/react-components/react-search-preview @microsoft/cxe-coastal packages/react-components/react-colorpicker-compat @microsoft/cxe-red @sopranopillow -packages/react-components/react-motion-preview @microsoft/cxe-prg @marcosmoura packages/react-components/react-nav-preview @microsoft/cxe-red @mltejera packages/react-components/react-motion-preview @microsoft/cxe-prg @marcosmoura # <%= NX-CODEOWNER-PLACEHOLDER %> From dd3cf563f4050a487e1781298dccb0062699c5a9 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Sun, 3 Sep 2023 22:06:07 +0200 Subject: [PATCH 106/111] Discard changes to apps/vr-tests/src/stories/react-charting/StackedBarChart.stories.tsx --- .../src/stories/react-charting/StackedBarChart.stories.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/vr-tests/src/stories/react-charting/StackedBarChart.stories.tsx b/apps/vr-tests/src/stories/react-charting/StackedBarChart.stories.tsx index d122357ea3b306..e3732504b4727e 100644 --- a/apps/vr-tests/src/stories/react-charting/StackedBarChart.stories.tsx +++ b/apps/vr-tests/src/stories/react-charting/StackedBarChart.stories.tsx @@ -87,7 +87,7 @@ storiesOf('react-charting/StackedBarChart', module) const chartTitle = 'Stacked bar chart 2nd example'; const data: IChartProps = { - chartTitle, + chartTitle: chartTitle, chartData: points, }; From ccf11887b4f93252be5cd47ad8abd25c5a0feab7 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Sun, 3 Sep 2023 22:06:28 +0200 Subject: [PATCH 107/111] Discard changes to packages/react-components/react-drawer/stories/Drawer/DrawerDefault.stories.tsx --- .../react-drawer/stories/Drawer/DrawerDefault.stories.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerDefault.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerDefault.stories.tsx index 0f05d22b1146fb..5e468fb6621a8d 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerDefault.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerDefault.stories.tsx @@ -39,8 +39,6 @@ export const Default = () => { const [isOpen, setIsOpen] = React.useState(false); const [type, setType] = React.useState('overlay'); - const onClick = () => setIsOpen(type === 'inline' ? !isOpen : true); - return (
setIsOpen(open)}> From 482064c70cdfdc17459e0e9591c75a3fe059f86c Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Sun, 3 Sep 2023 22:06:36 +0200 Subject: [PATCH 108/111] Discard changes to packages/react-components/react-drawer/stories/Drawer/DrawerInline.stories.tsx --- .../react-drawer/stories/Drawer/DrawerInline.stories.tsx | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/react-components/react-drawer/stories/Drawer/DrawerInline.stories.tsx b/packages/react-components/react-drawer/stories/Drawer/DrawerInline.stories.tsx index eebcd8a7a79f5c..f343d39d4e860e 100644 --- a/packages/react-components/react-drawer/stories/Drawer/DrawerInline.stories.tsx +++ b/packages/react-components/react-drawer/stories/Drawer/DrawerInline.stories.tsx @@ -30,14 +30,6 @@ const useStyles = makeStyles({ right: '-16px', left: '-16px', display: 'flex', - flexDirection: 'column', - rowGap: tokens.spacingHorizontalXS, - }, - - buttons: { - ...shorthands.flex(1), - ...shorthands.padding('16px'), - display: 'flex', justifyContent: 'center', alignItems: 'flex-start', columnGap: tokens.spacingHorizontalXS, From 88180b43a721dd52cb680e272b6d97e1d89aee13 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Sun, 3 Sep 2023 22:07:30 +0200 Subject: [PATCH 109/111] Discard changes to packages/react-components/react-motion-preview/src/hooks/useIsMotion.ts --- .../src/hooks/useIsMotion.ts | 43 ------------------- 1 file changed, 43 deletions(-) delete mode 100644 packages/react-components/react-motion-preview/src/hooks/useIsMotion.ts diff --git a/packages/react-components/react-motion-preview/src/hooks/useIsMotion.ts b/packages/react-components/react-motion-preview/src/hooks/useIsMotion.ts deleted file mode 100644 index 5f2f1204b22d9c..00000000000000 --- a/packages/react-components/react-motion-preview/src/hooks/useIsMotion.ts +++ /dev/null @@ -1,43 +0,0 @@ -import * as React from 'react'; -import { usePrevious } from '@fluentui/react-utilities'; -import type { MotionState, MotionShorthand } from './useMotion'; - -/** - * @internal - * - * This method emits a warning if the hook is called with - * a different typeof of shorthand on subsequent renders, - * since this can lead breaking the rules of hooks. - * - * It also return a boolean indicating whether the shorthand is a motion object. - */ -export function useIsMotion( - shorthand: MotionShorthand, -): shorthand is MotionState { - const previousShorthand = usePrevious(shorthand); - - /** - * Heads up! - * We don't want these warnings in production even though it is against native behavior - */ - if (process.env.NODE_ENV !== 'production') { - // eslint-disable-next-line react-hooks/rules-of-hooks - React.useEffect(() => { - if (previousShorthand !== null && typeof previousShorthand !== typeof shorthand) { - // eslint-disable-next-line no-console - console.error( - [ - 'useMotion: The hook needs to be called with the same typeof of shorthand on every render.', - 'This is to ensure the internal state of the hook is stable and can be used to accurately detect the motion state.', - 'Please make sure to not change the shorthand on subsequent renders or to use the hook conditionally.', - '\nCurrent shorthand:', - JSON.stringify(shorthand, null, 2), - '\nPrevious shorthand:', - JSON.stringify(previousShorthand, null, 2), - ].join(' '), - ); - } - }, [shorthand, previousShorthand]); - } - return typeof shorthand === 'object'; -} From 02baa6c2eeb420bd10aaf170b5d63e0f6035bd24 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Sun, 3 Sep 2023 22:07:48 +0200 Subject: [PATCH 110/111] Discard changes to packages/react-components/react-motion-preview/stories/useMotion/index.stories.tsx --- .../stories/useMotion/index.stories.tsx | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 packages/react-components/react-motion-preview/stories/useMotion/index.stories.tsx diff --git a/packages/react-components/react-motion-preview/stories/useMotion/index.stories.tsx b/packages/react-components/react-motion-preview/stories/useMotion/index.stories.tsx deleted file mode 100644 index a9cbc03f36c839..00000000000000 --- a/packages/react-components/react-motion-preview/stories/useMotion/index.stories.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import descriptionMd from './UseMotionDescription.md'; -import bestPracticesMd from './UseMotionBestPractices.md'; - -export { Default } from './UseMotionDefault.stories'; - -export default { - title: 'Preview Components/useMotion', - parameters: { - docs: { - description: { - component: [descriptionMd, bestPracticesMd].join('\n'), - }, - }, - }, -}; From cad2b49ac569a87243bb9547680193264e69a964 Mon Sep 17 00:00:00 2001 From: Marcos Moura Date: Wed, 4 Oct 2023 14:18:18 +0300 Subject: [PATCH 111/111] upgrade styles --- .../DialogSurface/DialogSurface.types.ts | 8 +++--- .../DialogSurface/useDialogSurface.ts | 8 +----- .../useDialogSurfaceStyles.styles.ts | 13 +++++---- .../etc/react-motion-preview.api.md | 5 ---- .../react-motion-preview/src/hooks/index.ts | 1 - .../src/hooks/useMotion.ts | 10 +------ .../src/hooks/useMotionStyles.styles.ts | 23 --------------- .../react-motion-preview/src/index.ts | 2 +- yarn.lock | 28 +++++++++++++++++++ 9 files changed, 43 insertions(+), 55 deletions(-) delete mode 100644 packages/react-components/react-motion-preview/src/hooks/useMotionStyles.styles.ts diff --git a/packages/react-components/react-dialog/src/components/DialogSurface/DialogSurface.types.ts b/packages/react-components/react-dialog/src/components/DialogSurface/DialogSurface.types.ts index e5576992f4607e..594c3d217b158a 100644 --- a/packages/react-components/react-dialog/src/components/DialogSurface/DialogSurface.types.ts +++ b/packages/react-components/react-dialog/src/components/DialogSurface/DialogSurface.types.ts @@ -36,7 +36,7 @@ export type DialogSurfaceContextValues = { /** * State used in rendering DialogSurface */ -export type DialogSurfaceState = ComponentState & { - motion: MotionState; - backdropMotion: MotionState; -} & Pick; +export type DialogSurfaceState = ComponentState & + Pick & { + motion: MotionState; + }; diff --git a/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurface.ts b/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurface.ts index ed893df1e42a45..31c846be12819c 100644 --- a/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurface.ts +++ b/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurface.ts @@ -9,7 +9,6 @@ import { import { DialogSurfaceElement, DialogSurfaceProps, DialogSurfaceState } from './DialogSurface.types'; import { useDialogContext_unstable } from '../../contexts'; import { Escape } from '@fluentui/keyboard-keys'; -import { useMotionFromSlot } from '@fluentui/react-motion-preview'; /** * Create the state required to render DialogSurface. @@ -28,7 +27,6 @@ export const useDialogSurface_unstable = ( const modalAttributes = useDialogContext_unstable(ctx => ctx.modalAttributes); const dialogRef = useDialogContext_unstable(ctx => ctx.dialogRef); const motion = useDialogContext_unstable(ctx => ctx.motion); - const open = useDialogContext_unstable(ctx => ctx.open); const requestOpenChange = useDialogContext_unstable(ctx => ctx.requestOpenChange); const dialogTitleID = useDialogContext_unstable(ctx => ctx.dialogTitleId); @@ -60,14 +58,11 @@ export const useDialogSurface_unstable = ( } }); - const [backdropProps, backdropMotion] = useMotionFromSlot(props.backdrop, open); - const backdrop = motion.canRender && modalType !== 'non-modal' - ? slot.optional(backdropProps, { + ? slot.optional(props.backdrop, { defaultProps: { 'aria-hidden': 'true', - ref: backdropMotion.ref as React.Ref, }, elementType: 'div', }) @@ -96,6 +91,5 @@ export const useDialogSurface_unstable = ( ), motion, - backdropMotion, }; }; diff --git a/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurfaceStyles.styles.ts b/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurfaceStyles.styles.ts index b4e5d7e8728d46..49c1aaa6f65dcc 100644 --- a/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurfaceStyles.styles.ts +++ b/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurfaceStyles.styles.ts @@ -71,15 +71,16 @@ const useRootStyles = makeStyles({ const useRootMotionStyles = makeStyles({ root: { opacity: 0, - transform: 'scale(0.85) translate3D(0, 10%, 0)', - transitionDuration: tokens.durationNormal, + boxShadow: '0px 0px 0px 0px rgba(0, 0, 0, 0.1)', + transform: 'scale(0.85) translateZ(0)', + transitionDuration: tokens.durationGentle, transitionProperty: 'opacity, transform, box-shadow', }, visible: { boxShadow: tokens.shadow64, opacity: 1, - transform: 'scale(1) translate3D(0, 0, 0)', + transform: 'scale(1) translateZ(0)', }, entering: { @@ -102,7 +103,7 @@ const useBackdropResetStyles = makeResetStyles({ const useBackdropMotionStyles = makeStyles({ backdrop: { - transitionDuration: tokens.durationNormal, + transitionDuration: tokens.durationGentle, transitionTimingFunction: tokens.curveLinear, transitionProperty: 'opacity', willChange: 'opacity', @@ -128,6 +129,7 @@ export const useDialogSurfaceStyles_unstable = (state: DialogSurfaceState): Dial dialogSurfaceClassNames.root, surfaceResetStyles, isNestedDialog && styles.nestedNativeDialogBackdrop, + motionStyles.root, state.motion.active && motionStyles.visible, state.motion.type === 'entering' && motionStyles.entering, state.motion.type === 'exiting' && motionStyles.exiting, @@ -139,7 +141,8 @@ export const useDialogSurfaceStyles_unstable = (state: DialogSurfaceState): Dial dialogSurfaceClassNames.backdrop, backdropResetStyles, isNestedDialog && styles.nestedDialogBackdrop, - state.backdropMotion.active && backdropMotionStyles.visible, + backdropMotionStyles.backdrop, + state.motion.active && backdropMotionStyles.visible, state.backdrop.className, ); } diff --git a/packages/react-components/react-motion-preview/etc/react-motion-preview.api.md b/packages/react-components/react-motion-preview/etc/react-motion-preview.api.md index 1e2acef84cde40..249e0fe3ee6524 100644 --- a/packages/react-components/react-motion-preview/etc/react-motion-preview.api.md +++ b/packages/react-components/react-motion-preview/etc/react-motion-preview.api.md @@ -4,7 +4,6 @@ ```ts -import { MotionState as MotionState_2 } from '@fluentui/react-motion-preview'; import * as React_2 from 'react'; import { SlotShorthandValue } from '@fluentui/react-utilities'; import { UnknownSlotProps } from '@fluentui/react-utilities'; @@ -29,7 +28,6 @@ export type MotionState = { type: MotionType; canRender: boolean; active: boolean; - hasInternalMotion: boolean; }; // @public (undocumented) @@ -50,9 +48,6 @@ export function useMotion(shorthand: MotionShorthan // @internal export function useMotionFromSlot(props: Props | SlotShorthandValue | undefined | null, shorthand: MotionShorthand, options?: MotionOptions): [Props, MotionState]; -// @public (undocumented) -export function useMotionStyles(motion: MotionState_2, classNames: string): string; - // (No @packageDocumentation comment for this package) ``` diff --git a/packages/react-components/react-motion-preview/src/hooks/index.ts b/packages/react-components/react-motion-preview/src/hooks/index.ts index 724d4c889c0e88..3883d10f49a97d 100644 --- a/packages/react-components/react-motion-preview/src/hooks/index.ts +++ b/packages/react-components/react-motion-preview/src/hooks/index.ts @@ -1,3 +1,2 @@ export * from './useMotion'; export * from './useMotionFromSlot'; -export * from './useMotionStyles.styles'; diff --git a/packages/react-components/react-motion-preview/src/hooks/useMotion.ts b/packages/react-components/react-motion-preview/src/hooks/useMotion.ts index 47a040d21f348c..76e903866f6b4f 100644 --- a/packages/react-components/react-motion-preview/src/hooks/useMotion.ts +++ b/packages/react-components/react-motion-preview/src/hooks/useMotion.ts @@ -45,12 +45,6 @@ export type MotionState = { * Useful to apply CSS transitions when the element is mounted and ready to be animated. */ active: boolean; - - /** - * Indicates whether the component has internal motion. - * Useful to avoid applying internal motion when the component is being overridden by a parent. - */ - hasInternalMotion: boolean; }; export type MotionShorthandValue = boolean; @@ -184,7 +178,6 @@ function useMotionPresence( type, active, canRender: type !== 'unmounted', - hasInternalMotion: true, }), // No need to add ref to the deps array as it is stable // eslint-disable-next-line react-hooks/exhaustive-deps @@ -201,7 +194,6 @@ export function getDefaultMotionState(): MotionStat type: 'unmounted', active: false, canRender: false, - hasInternalMotion: true, }; } @@ -223,7 +215,7 @@ export function useMotion( * on their side without having to worry about the performance impact of the hook. */ // eslint-disable-next-line react-hooks/rules-of-hooks - return useIsMotion(shorthand) ? { ...shorthand, hasInternalMotion: false } : useMotionPresence(shorthand, options); + return useIsMotion(shorthand) ? shorthand : useMotionPresence(shorthand, options); } const stringifyShorthand = (value: MotionShorthand) => { diff --git a/packages/react-components/react-motion-preview/src/hooks/useMotionStyles.styles.ts b/packages/react-components/react-motion-preview/src/hooks/useMotionStyles.styles.ts deleted file mode 100644 index 886ba8dfafab5f..00000000000000 --- a/packages/react-components/react-motion-preview/src/hooks/useMotionStyles.styles.ts +++ /dev/null @@ -1,23 +0,0 @@ -import * as React from 'react'; -import { makeStyles, mergeClasses } from '@griffel/react'; -import { MotionState } from '@fluentui/react-motion-preview'; - -const useStyles = makeStyles({ - reducedMotion: { - '@media screen and (prefers-reduced-motion: reduce)': { - transitionDuration: '0.001ms', - }, - }, -}); - -export function useMotionStyles(motion: MotionState, classNames: string): string { - const styles = useStyles(); - - return React.useMemo(() => { - if (!motion.hasInternalMotion) { - return ''; - } - - return mergeClasses(styles.reducedMotion, classNames); - }, [classNames, motion.hasInternalMotion, styles.reducedMotion]); -} diff --git a/packages/react-components/react-motion-preview/src/index.ts b/packages/react-components/react-motion-preview/src/index.ts index 73b569d327d67e..cb2443d707117b 100644 --- a/packages/react-components/react-motion-preview/src/index.ts +++ b/packages/react-components/react-motion-preview/src/index.ts @@ -1,4 +1,4 @@ -export { getDefaultMotionState, useIsMotion, useMotion, useMotionFromSlot, useMotionStyles } from './hooks'; +export { getDefaultMotionState, useIsMotion, useMotion, useMotionFromSlot } from './hooks'; export type { MotionOptions, MotionShorthand, diff --git a/yarn.lock b/yarn.lock index 2a174e8a2c7087..e7af786f84a0de 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1832,6 +1832,34 @@ "@griffel/react" "^1.0.0" tslib "^2.1.0" +"@fluentui/react-jsx-runtime@^9.0.13": + version "9.0.13" + resolved "https://registry.yarnpkg.com/@fluentui/react-jsx-runtime/-/react-jsx-runtime-9.0.13.tgz#0695d11256929be07a8a585c5e793e78244a84f1" + integrity sha512-+RZ58VA8n8cPoNqKSgM42Bz5n/opqnPzb3458D8Pv4htD+rpVl6Y0ubQLMfIwN+sDqecGi8LUvQtT+UYOGCKuw== + dependencies: + "@fluentui/react-utilities" "^9.14.0" + "@swc/helpers" "^0.5.1" + +"@fluentui/react-motion-preview@0.0.0": + version "0.2.10" + resolved "https://registry.yarnpkg.com/@fluentui/react-motion-preview/-/react-motion-preview-0.2.10.tgz#35584caaf71e66a067f1f281a47e1992e87d0a66" + integrity sha512-8vc83HqT7TQEczd7TjKvVFNsvBt+5IvayVOLOFqVJ5pUF6WDFZ8q1cdgGgPrQ4nHgptIjyNqhMwCL4nvPbr9LA== + dependencies: + "@fluentui/react-jsx-runtime" "^9.0.13" + "@fluentui/react-shared-contexts" "^9.9.2" + "@fluentui/react-theme" "^9.1.14" + "@fluentui/react-utilities" "^9.14.0" + "@griffel/react" "^1.5.14" + "@swc/helpers" "^0.5.1" + +"@fluentui/react-utilities@^9.14.0": + version "9.14.0" + resolved "https://registry.yarnpkg.com/@fluentui/react-utilities/-/react-utilities-9.14.0.tgz#3a509255bdd264829947fd823f3422415d1ad262" + integrity sha512-oH/0uhbBwldckg+ZjD7l9FRKGJaBn/ptt2G+aBMMv510njgvSZjlscbN1Mfm89UTK68onsw/SOXGgessjc1tJA== + dependencies: + "@fluentui/keyboard-keys" "^9.0.6" + "@swc/helpers" "^0.5.1" + "@gar/promisify@^1.1.3": version "1.1.3" resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6"