Skip to content

Commit 3fef755

Browse files
committed
feat(react-provider): use feature flag
1 parent f0c77eb commit 3fef755

8 files changed

Lines changed: 104 additions & 13 deletions

File tree

packages/react-components/react-provider/library/src/components/FluentProvider/FluentProvider.types.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import type { IconDirectionContextValue } from '@fluentui/react-icons/lib/providers';
77
import type {
88
OverridesContextValue_unstable as OverridesContextValue,
9+
PointerInteractionsContextValue_unstable as PointerInteractionsContextValue,
910
ProviderContextValue_unstable as ProviderContextValue,
1011
TooltipVisibilityContextValue_unstable as TooltipVisibilityContextValue,
1112
ThemeClassNameContextValue_unstable as ThemeClassNameContextValue,
@@ -45,12 +46,23 @@ export type FluentProviderProps = Omit<ComponentProps<FluentProviderSlots>, 'dir
4546

4647
// eslint-disable-next-line @typescript-eslint/naming-convention
4748
overrides_unstable?: OverridesContextValue;
49+
50+
/**
51+
* Enables pointer-aware hover behavior. When enabled, hover only fires for
52+
* mouse/pen interactions, not touch. This fixes "sticky hover" on touch devices.
53+
* @default false
54+
*/
55+
// eslint-disable-next-line @typescript-eslint/naming-convention
56+
UNSTABLE_usePointerHover?: boolean;
4857
};
4958

5059
export type FluentProviderState = ComponentState<FluentProviderSlots> &
5160
Pick<FluentProviderProps, 'targetDocument'> &
5261
Required<
53-
Pick<FluentProviderProps, 'applyStylesToPortals' | 'customStyleHooks_unstable' | 'dir' | 'overrides_unstable'>
62+
Pick<
63+
FluentProviderProps,
64+
'applyStylesToPortals' | 'customStyleHooks_unstable' | 'dir' | 'overrides_unstable' | 'UNSTABLE_usePointerHover'
65+
>
5466
> & {
5567
theme: ThemeContextValue;
5668
themeClassName: string;
@@ -78,4 +90,5 @@ export type FluentProviderContextValues = Pick<
7890
textDirection: 'ltr' | 'rtl';
7991
iconDirection: IconDirectionContextValue;
8092
tooltip: TooltipVisibilityContextValue;
93+
pointerInteractions: PointerInteractionsContextValue;
8194
};

packages/react-components/react-provider/library/src/components/FluentProvider/renderFluentProvider.tsx

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { JSXElement } from '@fluentui/react-utilities';
55
import { TextDirectionProvider } from '@griffel/react';
66
import {
77
OverridesProvider_unstable as OverridesProvider,
8+
PointerInteractionsProvider_unstable as PointerInteractionsProvider,
89
Provider_unstable as Provider,
910
TooltipVisibilityProvider_unstable as TooltipVisibilityProvider,
1011
ThemeProvider_unstable as ThemeProvider,
@@ -39,19 +40,21 @@ export const renderFluentProvider_unstable = (
3940
<TextDirectionProvider dir={contextValues.textDirection}>
4041
<IconDirectionContextProvider value={contextValues.iconDirection}>
4142
<OverridesProvider value={contextValues.overrides_unstable}>
42-
<state.root>
43-
{canUseDOM() ? null : (
44-
<style
45-
// Using dangerous HTML because react can escape characters
46-
// which can lead to invalid CSS.
47-
// eslint-disable-next-line react/no-danger
48-
dangerouslySetInnerHTML={{ __html: state.serverStyleProps.cssRule }}
49-
{...state.serverStyleProps.attributes}
50-
/>
51-
)}
43+
<PointerInteractionsProvider value={contextValues.pointerInteractions}>
44+
<state.root>
45+
{canUseDOM() ? null : (
46+
<style
47+
// Using dangerous HTML because react can escape characters
48+
// which can lead to invalid CSS.
49+
// eslint-disable-next-line react/no-danger
50+
dangerouslySetInnerHTML={{ __html: state.serverStyleProps.cssRule }}
51+
{...state.serverStyleProps.attributes}
52+
/>
53+
)}
5254

53-
{state.root.children}
54-
</state.root>
55+
{state.root.children}
56+
</state.root>
57+
</PointerInteractionsProvider>
5558
</OverridesProvider>
5659
</IconDirectionContextProvider>
5760
</TextDirectionProvider>

packages/react-components/react-provider/library/src/components/FluentProvider/useFluentProvider.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
ThemeContext_unstable as ThemeContext,
77
useFluent_unstable as useFluent,
88
useOverrides_unstable as useOverrides,
9+
usePointerInteractions_unstable as usePointerInteractions,
910
CustomStyleHooksContext_unstable as CustomStyleHooksContext,
1011
} from '@fluentui/react-shared-contexts';
1112
import type {
@@ -40,6 +41,7 @@ export const useFluentProvider_unstable = (
4041
const parentContext = useFluent();
4142
const parentTheme = useTheme();
4243
const parentOverrides = useOverrides();
44+
const parentPointerInteractions = usePointerInteractions();
4345
const parentCustomStyleHooks: CustomStyleHooksContextValue =
4446
React.useContext(CustomStyleHooksContext) || DEFAULT_STYLE_HOOKS;
4547

@@ -56,6 +58,8 @@ export const useFluentProvider_unstable = (
5658
targetDocument = parentContext.targetDocument,
5759
theme,
5860
overrides_unstable: overrides = {},
61+
// eslint-disable-next-line @typescript-eslint/naming-convention
62+
UNSTABLE_usePointerHover = parentPointerInteractions.usePointerHover,
5963
} = props;
6064

6165
const mergedTheme = shallowMerge(parentTheme, theme);
@@ -98,6 +102,8 @@ export const useFluentProvider_unstable = (
98102
theme: mergedTheme,
99103
// eslint-disable-next-line @typescript-eslint/naming-convention
100104
overrides_unstable: mergedOverrides,
105+
// eslint-disable-next-line @typescript-eslint/naming-convention
106+
UNSTABLE_usePointerHover,
101107
themeClassName: styleTagId,
102108

103109
components: {

packages/react-components/react-provider/library/src/components/FluentProvider/useFluentProviderContextValues.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,18 @@ export function useFluentProviderContextValues_unstable(state: FluentProviderSta
1515
themeClassName,
1616
// eslint-disable-next-line @typescript-eslint/naming-convention
1717
overrides_unstable,
18+
// eslint-disable-next-line @typescript-eslint/naming-convention
19+
UNSTABLE_usePointerHover,
1820
} = state;
1921

2022
const provider = React.useMemo(() => ({ dir, targetDocument }), [dir, targetDocument]);
2123
// "Tooltip" component mutates an object in this context, instance should be stable
2224
const [tooltip] = React.useState(() => ({}));
2325
const iconDirection = React.useMemo(() => ({ textDirection: dir }), [dir]);
26+
const pointerInteractions = React.useMemo(
27+
() => ({ usePointerHover: UNSTABLE_usePointerHover }),
28+
[UNSTABLE_usePointerHover],
29+
);
2430

2531
return {
2632
// eslint-disable-next-line @typescript-eslint/naming-convention
@@ -33,5 +39,6 @@ export function useFluentProviderContextValues_unstable(state: FluentProviderSta
3339
tooltip,
3440
theme,
3541
themeClassName: applyStylesToPortals ? root.className! : themeClassName,
42+
pointerInteractions,
3643
};
3744
}

packages/react-components/react-shared-contexts/library/etc/react-shared-contexts.api.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,14 @@ export type OverridesContextValue_unstable = {
632632
// @internal (undocumented)
633633
export const OverridesProvider_unstable: React_2.Provider<OverridesContextValue_unstable | undefined>;
634634

635+
// @internal
636+
export type PointerInteractionsContextValue_unstable = {
637+
usePointerHover: boolean;
638+
};
639+
640+
// @internal (undocumented)
641+
export const PointerInteractionsProvider_unstable: React_2.Provider<PointerInteractionsContextValue_unstable>;
642+
635643
// @internal (undocumented)
636644
export const PortalMountNodeProvider: React_2.Provider<PortalMountNodeContextValue>;
637645

@@ -690,6 +698,9 @@ export function useFluent_unstable(): ProviderContextValue_unstable;
690698
// @public (undocumented)
691699
export function useOverrides_unstable(): OverridesContextValue_unstable;
692700

701+
// @internal
702+
export function usePointerInteractions_unstable(): PointerInteractionsContextValue_unstable;
703+
693704
// @public (undocumented)
694705
export function usePortalMountNode(): PortalMountNodeContextValue;
695706

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
'use client';
2+
3+
import * as React from 'react';
4+
5+
/**
6+
* Context value for pointer-aware interaction behavior.
7+
* @internal
8+
*/
9+
export type PointerInteractionsContextValue = {
10+
/**
11+
* Whether pointer-aware hover behavior is enabled.
12+
* When enabled, hover only fires for mouse/pen interactions, not touch.
13+
* This fixes "sticky hover" on touch devices.
14+
*/
15+
usePointerHover: boolean;
16+
};
17+
18+
const defaultValue: PointerInteractionsContextValue = {
19+
usePointerHover: false,
20+
};
21+
22+
/**
23+
* @internal
24+
*/
25+
export const PointerInteractionsContext = React.createContext<PointerInteractionsContextValue>(defaultValue);
26+
27+
/**
28+
* @internal
29+
*/
30+
export const PointerInteractionsProvider = PointerInteractionsContext.Provider;
31+
32+
/**
33+
* Hook to access pointer interactions context.
34+
* Returns whether pointer-aware hover behavior is enabled.
35+
* @internal
36+
*/
37+
export function usePointerInteractions(): PointerInteractionsContextValue {
38+
return React.useContext(PointerInteractionsContext);
39+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export type { PointerInteractionsContextValue } from './PointerInteractionsContext';
2+
export {
3+
PointerInteractionsContext,
4+
PointerInteractionsProvider,
5+
usePointerInteractions,
6+
} from './PointerInteractionsContext';

packages/react-components/react-shared-contexts/library/src/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ export {
2929
} from './CustomStyleHooksContext';
3030
export type { CustomStyleHooksContextValue as CustomStyleHooksContextValue_unstable } from './CustomStyleHooksContext';
3131

32+
export {
33+
PointerInteractionsProvider as PointerInteractionsProvider_unstable,
34+
usePointerInteractions as usePointerInteractions_unstable,
35+
} from './PointerInteractionsContext';
36+
export type { PointerInteractionsContextValue as PointerInteractionsContextValue_unstable } from './PointerInteractionsContext';
37+
3238
export { BackgroundAppearanceProvider, useBackgroundAppearance } from './BackgroundAppearanceContext';
3339
export type { BackgroundAppearanceContextValue } from './BackgroundAppearanceContext';
3440

0 commit comments

Comments
 (0)