Private
Public Access
1
0

feat: Fluent UI Outlook Lite + connections mockup

This commit is contained in:
2026-04-14 18:52:25 +00:00
parent 1199eff6c3
commit dfa4010406
34820 changed files with 1003813 additions and 205 deletions

View File

@@ -0,0 +1,14 @@
export { useAnimationFrame } from './useAnimationFrame';
export { useApplyScrollbarWidth } from './useApplyScrollbarWidth';
export { useControllableState } from './useControllableState';
export { useEventCallback } from './useEventCallback';
export { useFirstMount } from './useFirstMount';
export { useForceUpdate } from './useForceUpdate';
export { IdPrefixProvider, resetIdsForTests, useId } from './useId';
export { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect';
export { useMergedRefs } from './useMergedRefs';
export { useOnClickOutside } from './useOnClickOutside';
export { useOnScrollOutside } from './useOnScrollOutside';
export { usePrevious } from './usePrevious';
export { useScrollbarWidth } from './useScrollbarWidth';
export { useTimeout } from './useTimeout';

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/hooks/index.ts"],"sourcesContent":["export { useAnimationFrame } from './useAnimationFrame';\nexport { useApplyScrollbarWidth } from './useApplyScrollbarWidth';\nexport type { UseControllableStateOptions } from './useControllableState';\nexport { useControllableState } from './useControllableState';\nexport { useEventCallback } from './useEventCallback';\nexport { useFirstMount } from './useFirstMount';\nexport { useForceUpdate } from './useForceUpdate';\nexport { IdPrefixProvider, resetIdsForTests, useId } from './useId';\nexport { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect';\nexport type { RefObjectFunction } from './useMergedRefs';\nexport { useMergedRefs } from './useMergedRefs';\nexport type { UseOnClickOrScrollOutsideOptions } from './useOnClickOutside';\nexport { useOnClickOutside } from './useOnClickOutside';\nexport { useOnScrollOutside } from './useOnScrollOutside';\nexport { usePrevious } from './usePrevious';\nexport { useScrollbarWidth } from './useScrollbarWidth';\nexport { useTimeout } from './useTimeout';\n"],"names":["useAnimationFrame","useApplyScrollbarWidth","useControllableState","useEventCallback","useFirstMount","useForceUpdate","IdPrefixProvider","resetIdsForTests","useId","useIsomorphicLayoutEffect","useMergedRefs","useOnClickOutside","useOnScrollOutside","usePrevious","useScrollbarWidth","useTimeout"],"mappings":"AAAA,SAASA,iBAAiB,QAAQ,sBAAsB;AACxD,SAASC,sBAAsB,QAAQ,2BAA2B;AAElE,SAASC,oBAAoB,QAAQ,yBAAyB;AAC9D,SAASC,gBAAgB,QAAQ,qBAAqB;AACtD,SAASC,aAAa,QAAQ,kBAAkB;AAChD,SAASC,cAAc,QAAQ,mBAAmB;AAClD,SAASC,gBAAgB,EAAEC,gBAAgB,EAAEC,KAAK,QAAQ,UAAU;AACpE,SAASC,yBAAyB,QAAQ,8BAA8B;AAExE,SAASC,aAAa,QAAQ,kBAAkB;AAEhD,SAASC,iBAAiB,QAAQ,sBAAsB;AACxD,SAASC,kBAAkB,QAAQ,uBAAuB;AAC1D,SAASC,WAAW,QAAQ,gBAAgB;AAC5C,SAASC,iBAAiB,QAAQ,sBAAsB;AACxD,SAASC,UAAU,QAAQ,eAAe"}

View File

@@ -0,0 +1,22 @@
'use client';
import { useBrowserTimer } from './useBrowserTimer';
import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';
const setAnimationFrameNoop = (callback)=>{
callback(0);
return 0;
};
const cancelAnimationFrameNoop = (handle)=>handle;
/**
* @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() {
const { targetDocument } = useFluent();
const win = targetDocument === null || targetDocument === void 0 ? void 0 : targetDocument.defaultView;
const setAnimationFrame = win ? win.requestAnimationFrame : setAnimationFrameNoop;
const clearAnimationFrame = win ? win.cancelAnimationFrame : cancelAnimationFrameNoop;
return useBrowserTimer(setAnimationFrame, clearAnimationFrame);
}

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/hooks/useAnimationFrame.ts"],"sourcesContent":["'use client';\n\nimport { useBrowserTimer } from './useBrowserTimer';\nimport { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';\n\nconst setAnimationFrameNoop = (callback: FrameRequestCallback) => {\n callback(0);\n return 0;\n};\nconst cancelAnimationFrameNoop = (handle: number) => handle;\n\n/**\n * @internal\n * Helper to manage a browser requestAnimationFrame.\n * Ensures that the requestAnimationFrame isn't set multiple times at once and is cleaned up\n * when the component is unloaded.\n *\n * @returns A pair of [requestAnimationFrame, cancelAnimationFrame] that are stable between renders.\n */\nexport function useAnimationFrame(): readonly [(fn: FrameRequestCallback) => number, () => void] {\n const { targetDocument } = useFluent();\n const win = targetDocument?.defaultView;\n\n const setAnimationFrame = win ? win.requestAnimationFrame : setAnimationFrameNoop;\n const clearAnimationFrame = win ? win.cancelAnimationFrame : cancelAnimationFrameNoop;\n\n return useBrowserTimer(setAnimationFrame, clearAnimationFrame) as [(fn: FrameRequestCallback) => number, () => void];\n}\n"],"names":["useBrowserTimer","useFluent_unstable","useFluent","setAnimationFrameNoop","callback","cancelAnimationFrameNoop","handle","useAnimationFrame","targetDocument","win","defaultView","setAnimationFrame","requestAnimationFrame","clearAnimationFrame","cancelAnimationFrame"],"mappings":"AAAA;AAEA,SAASA,eAAe,QAAQ,oBAAoB;AACpD,SAASC,sBAAsBC,SAAS,QAAQ,kCAAkC;AAElF,MAAMC,wBAAwB,CAACC;IAC7BA,SAAS;IACT,OAAO;AACT;AACA,MAAMC,2BAA2B,CAACC,SAAmBA;AAErD;;;;;;;CAOC,GACD,OAAO,SAASC;IACd,MAAM,EAAEC,cAAc,EAAE,GAAGN;IAC3B,MAAMO,MAAMD,2BAAAA,qCAAAA,eAAgBE,WAAW;IAEvC,MAAMC,oBAAoBF,MAAMA,IAAIG,qBAAqB,GAAGT;IAC5D,MAAMU,sBAAsBJ,MAAMA,IAAIK,oBAAoB,GAAGT;IAE7D,OAAOL,gBAAgBW,mBAAmBE;AAC5C"}

View File

@@ -0,0 +1,40 @@
'use client';
import * as React from 'react';
import { measureScrollbarWidth } from '../utils/measureScrollBarWidth';
const cache = new WeakMap();
/**
* A React hook that provides a ref for applying the browser's scrollbar width as a CSS property.
*
* This hook is SSR-safe and caches measurements per document to avoid redundant calculations.
* When the ref is attached to an element, the hook automatically applies the measured scrollbar
* width to the specified CSS property (defaults to 'width').
*
* @example
* ```tsx
* const scrollbarRef = useApplyScrollbarWidth({ targetDocument: document });
* return <div ref={scrollbarRef} />;
* ```
*/ export function useApplyScrollbarWidth(options = {}) {
const { force, property = 'width' } = options;
const applyScrollbarWidth = React.useCallback((element)=>{
if (!element) {
return;
}
// If we have a cached value, use it
if (!force && cache.has(element.ownerDocument)) {
const cachedWidth = cache.get(element.ownerDocument);
if (cachedWidth !== undefined) {
element.style.setProperty(property, `${cachedWidth}px`);
return;
}
}
// Measure the scrollbar width and apply it to the element
const scrollbarWidth = measureScrollbarWidth(element.ownerDocument);
cache.set(element.ownerDocument, scrollbarWidth);
element.style.setProperty(property, `${scrollbarWidth}px`);
}, [
force,
property
]);
return applyScrollbarWidth;
}

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/hooks/useApplyScrollbarWidth.ts"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { measureScrollbarWidth } from '../utils/measureScrollBarWidth';\n\nconst cache = new WeakMap<Document, number>();\n\ninterface UseApplyScrollbarWidthOptions {\n /**\n * Does not use the cache and recalculates the scrollbar width\n */\n force?: boolean;\n\n /**\n * CSS property to apply the scrollbar width to.\n * @default 'width'\n */\n property?: string;\n}\n\n/**\n * A React hook that provides a ref for applying the browser's scrollbar width as a CSS property.\n *\n * This hook is SSR-safe and caches measurements per document to avoid redundant calculations.\n * When the ref is attached to an element, the hook automatically applies the measured scrollbar\n * width to the specified CSS property (defaults to 'width').\n *\n * @example\n * ```tsx\n * const scrollbarRef = useApplyScrollbarWidth({ targetDocument: document });\n * return <div ref={scrollbarRef} />;\n * ```\n */\nexport function useApplyScrollbarWidth<T extends HTMLElement>(\n options: UseApplyScrollbarWidthOptions = {},\n): React.RefCallback<T> {\n const { force, property = 'width' } = options;\n\n const applyScrollbarWidth = React.useCallback(\n (element: T | null) => {\n if (!element) {\n return;\n }\n\n // If we have a cached value, use it\n if (!force && cache.has(element.ownerDocument)) {\n const cachedWidth = cache.get(element.ownerDocument);\n if (cachedWidth !== undefined) {\n element.style.setProperty(property, `${cachedWidth}px`);\n return;\n }\n }\n\n // Measure the scrollbar width and apply it to the element\n const scrollbarWidth = measureScrollbarWidth(element.ownerDocument);\n cache.set(element.ownerDocument, scrollbarWidth);\n element.style.setProperty(property, `${scrollbarWidth}px`);\n },\n [force, property],\n );\n\n return applyScrollbarWidth;\n}\n"],"names":["React","measureScrollbarWidth","cache","WeakMap","useApplyScrollbarWidth","options","force","property","applyScrollbarWidth","useCallback","element","has","ownerDocument","cachedWidth","get","undefined","style","setProperty","scrollbarWidth","set"],"mappings":"AAAA;AAEA,YAAYA,WAAW,QAAQ;AAC/B,SAASC,qBAAqB,QAAQ,iCAAiC;AAEvE,MAAMC,QAAQ,IAAIC;AAelB;;;;;;;;;;;;CAYC,GACD,OAAO,SAASC,uBACdC,UAAyC,CAAC,CAAC;IAE3C,MAAM,EAAEC,KAAK,EAAEC,WAAW,OAAO,EAAE,GAAGF;IAEtC,MAAMG,sBAAsBR,MAAMS,WAAW,CAC3C,CAACC;QACC,IAAI,CAACA,SAAS;YACZ;QACF;QAEA,oCAAoC;QACpC,IAAI,CAACJ,SAASJ,MAAMS,GAAG,CAACD,QAAQE,aAAa,GAAG;YAC9C,MAAMC,cAAcX,MAAMY,GAAG,CAACJ,QAAQE,aAAa;YACnD,IAAIC,gBAAgBE,WAAW;gBAC7BL,QAAQM,KAAK,CAACC,WAAW,CAACV,UAAU,GAAGM,YAAY,EAAE,CAAC;gBACtD;YACF;QACF;QAEA,0DAA0D;QAC1D,MAAMK,iBAAiBjB,sBAAsBS,QAAQE,aAAa;QAClEV,MAAMiB,GAAG,CAACT,QAAQE,aAAa,EAAEM;QACjCR,QAAQM,KAAK,CAACC,WAAW,CAACV,UAAU,GAAGW,eAAe,EAAE,CAAC;IAC3D,GACA;QAACZ;QAAOC;KAAS;IAGnB,OAAOC;AACT"}

View File

@@ -0,0 +1,46 @@
'use client';
import * as React from 'react';
/**
* @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, cancelTimer) {
const id = React.useRef(undefined);
const set = React.useCallback((fn, delay)=>{
if (id.current !== undefined) {
cancelTimer(id.current);
}
id.current = setTimer(fn, delay);
return id.current;
}, [
cancelTimer,
setTimer
]);
const cancel = React.useCallback(()=>{
if (id.current !== undefined) {
cancelTimer(id.current);
id.current = undefined;
}
}, [
cancelTimer
]);
// Clean up the timeout when the component is unloaded
React.useEffect(()=>cancel, [
cancel
]);
return [
set,
cancel
];
}

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/hooks/useBrowserTimer.ts"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\n\ntype BrowserTimerSetter =\n | ((fn: () => void, duration?: number, ...args: Record<string, unknown>[]) => number)\n | ((fn: () => void) => number);\n\n/**\n * @internal\n * Helper to manage a browser timer.\n * Ensures that the timer isn't set multiple times at once,\n * and is cleaned up when the component is unloaded.\n *\n * @param setTimer - The timer setter function\n * @param cancelTimer - The timer cancel function\n * @returns A pair of [setTimer, cancelTimer] that are stable between renders.\n *\n * @example\n * const [setTimer, cancelTimer] = useBrowserTimer(setTimeout, cancelTimeout);\n *\n * setTimer(() => console.log('Hello world!'), 1000);\n * cancelTimer();\n */\nexport function useBrowserTimer(\n setTimer: BrowserTimerSetter,\n cancelTimer: (id: number) => void,\n): [(fn: () => void, delay?: number) => number, () => void] {\n const id = React.useRef<number | undefined>(undefined);\n\n const set = React.useCallback(\n (fn: () => void, delay?: number) => {\n if (id.current !== undefined) {\n cancelTimer(id.current);\n }\n\n id.current = setTimer(fn, delay);\n return id.current;\n },\n [cancelTimer, setTimer],\n );\n\n const cancel = React.useCallback(() => {\n if (id.current !== undefined) {\n cancelTimer(id.current);\n id.current = undefined;\n }\n }, [cancelTimer]);\n\n // Clean up the timeout when the component is unloaded\n React.useEffect(() => cancel, [cancel]);\n\n return [set, cancel] as const;\n}\n"],"names":["React","useBrowserTimer","setTimer","cancelTimer","id","useRef","undefined","set","useCallback","fn","delay","current","cancel","useEffect"],"mappings":"AAAA;AAEA,YAAYA,WAAW,QAAQ;AAM/B;;;;;;;;;;;;;;;CAeC,GACD,OAAO,SAASC,gBACdC,QAA4B,EAC5BC,WAAiC;IAEjC,MAAMC,KAAKJ,MAAMK,MAAM,CAAqBC;IAE5C,MAAMC,MAAMP,MAAMQ,WAAW,CAC3B,CAACC,IAAgBC;QACf,IAAIN,GAAGO,OAAO,KAAKL,WAAW;YAC5BH,YAAYC,GAAGO,OAAO;QACxB;QAEAP,GAAGO,OAAO,GAAGT,SAASO,IAAIC;QAC1B,OAAON,GAAGO,OAAO;IACnB,GACA;QAACR;QAAaD;KAAS;IAGzB,MAAMU,SAASZ,MAAMQ,WAAW,CAAC;QAC/B,IAAIJ,GAAGO,OAAO,KAAKL,WAAW;YAC5BH,YAAYC,GAAGO,OAAO;YACtBP,GAAGO,OAAO,GAAGL;QACf;IACF,GAAG;QAACH;KAAY;IAEhB,sDAAsD;IACtDH,MAAMa,SAAS,CAAC,IAAMD,QAAQ;QAACA;KAAO;IAEtC,OAAO;QAACL;QAAKK;KAAO;AACtB"}

View File

@@ -0,0 +1,92 @@
'use client';
import * as React from 'react';
function isFactoryDispatch(newState) {
return typeof newState === 'function';
}
/**
* @internal
*
* A [`useState`](https://reactjs.org/docs/hooks-reference.html#usestate)-like hook
* to manage a value that could be either `controlled` or `uncontrolled`,
* such as a checked state or text input string.
*
* @see https://react.dev/learn/sharing-state-between-components#controlled-and-uncontrolled-components for more details on `controlled`/`uncontrolled`
*
* @returns an array of the current value and an updater (dispatcher) function.
* The updater function is referentially stable (won't change during the component's lifecycle).
* It can take either a new value, or a function which is passed the previous value and returns the new value.
*
* ❗️❗️ Calls to the dispatcher will only modify the state if the state is `uncontrolled`.
* Meaning that if a state is `controlled`, calls to the dispatcher do not modify the state.
*
*/ export const useControllableState = (options)=>{
'use no memo';
if (process.env.NODE_ENV !== 'production') {
if (options.state !== undefined && options.defaultState !== undefined) {
// eslint-disable-next-line no-console
console.error(`@fluentui/react-utilities [useControllableState]:
A component must be either controlled or uncontrolled (specify either the state or the defaultState, but not both).
Decide between using a controlled or uncontrolled component and remove one of this props.
More info: https://reactjs.org/link/controlled-components
${new Error().stack}`);
}
}
const [internalState, setInternalState] = React.useState(()=>{
if (options.defaultState === undefined) {
return options.initialState;
}
return isInitializer(options.defaultState) ? options.defaultState() : options.defaultState;
});
// Heads up!
// This part is specific for controlled mode and mocks behavior of React dispatcher function.
const stateValueRef = React.useRef(options.state);
React.useEffect(()=>{
stateValueRef.current = options.state;
}, [
options.state
]);
const setControlledState = React.useCallback((newState)=>{
if (isFactoryDispatch(newState)) {
newState(stateValueRef.current);
}
}, []);
return useIsControlled(options.state) ? [
options.state,
setControlledState
] : [
internalState,
setInternalState
];
};
function isInitializer(value) {
return typeof value === 'function';
}
/**
* Helper hook to handle previous comparison of controlled/uncontrolled
* Prints an error when isControlled value switches between subsequent renders
* @returns - whether the value is controlled
*/ const useIsControlled = (controlledValue)=>{
'use no memo';
const [isControlled] = React.useState(()=>controlledValue !== undefined);
if (process.env.NODE_ENV !== 'production') {
// We don't want these warnings in production even though it is against native behaviour
// eslint-disable-next-line react-hooks/rules-of-hooks
React.useEffect(()=>{
if (isControlled !== (controlledValue !== undefined)) {
const error = new Error();
const controlWarning = isControlled ? 'a controlled value to be uncontrolled' : 'an uncontrolled value to be controlled';
const undefinedWarning = isControlled ? 'defined to an undefined' : 'undefined to a defined';
// eslint-disable-next-line no-console
console.error(`@fluentui/react-utilities [useControllableState]:
A component is changing ${controlWarning}. This is likely caused by the value changing from ${undefinedWarning} value, which should not happen.
Decide between using a controlled or uncontrolled input element for the lifetime of the component.
More info: https://reactjs.org/link/controlled-components
${error.stack}`);
}
}, [
isControlled,
controlledValue
]);
}
return isControlled;
};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,31 @@
'use client';
import * as React from 'react';
import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect';
/**
* @internal
* https://reactjs.org/docs/hooks-faq.html#how-to-read-an-often-changing-value-from-usecallback
*
* Modified `useCallback` that can be used when dependencies change too frequently. Can occur when
* e.g. user props are dependencies which could change on every render
* e.g. volatile values (i.e. useState/useDispatch) are dependencies which could change frequently
*
* This should not be used often, but can be a useful re-render optimization since the callback is a ref and
* will not be invalidated between re-renders
*
* @param fn - The callback function that will be used
*/ export const useEventCallback = (fn)=>{
const callbackRef = React.useRef(()=>{
throw new Error('Cannot call an event handler while rendering');
});
useIsomorphicLayoutEffect(()=>{
callbackRef.current = fn;
}, [
fn
]);
return React.useCallback((...args)=>{
const callback = callbackRef.current;
return callback(...args);
}, [
callbackRef
]);
};

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/hooks/useEventCallback.ts"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect';\n\n/**\n * @internal\n * https://reactjs.org/docs/hooks-faq.html#how-to-read-an-often-changing-value-from-usecallback\n *\n * Modified `useCallback` that can be used when dependencies change too frequently. Can occur when\n * e.g. user props are dependencies which could change on every render\n * e.g. volatile values (i.e. useState/useDispatch) are dependencies which could change frequently\n *\n * This should not be used often, but can be a useful re-render optimization since the callback is a ref and\n * will not be invalidated between re-renders\n *\n * @param fn - The callback function that will be used\n */\nexport const useEventCallback = <Args extends unknown[], Return>(\n fn: (...args: Args) => Return,\n): ((...args: Args) => Return) => {\n const callbackRef = React.useRef<typeof fn>(() => {\n throw new Error('Cannot call an event handler while rendering');\n });\n\n useIsomorphicLayoutEffect(() => {\n callbackRef.current = fn;\n }, [fn]);\n\n return React.useCallback(\n (...args: Args) => {\n const callback = callbackRef.current;\n return callback(...args);\n },\n [callbackRef],\n );\n};\n"],"names":["React","useIsomorphicLayoutEffect","useEventCallback","fn","callbackRef","useRef","Error","current","useCallback","args","callback"],"mappings":"AAAA;AAEA,YAAYA,WAAW,QAAQ;AAC/B,SAASC,yBAAyB,QAAQ,8BAA8B;AAExE;;;;;;;;;;;;CAYC,GACD,OAAO,MAAMC,mBAAmB,CAC9BC;IAEA,MAAMC,cAAcJ,MAAMK,MAAM,CAAY;QAC1C,MAAM,IAAIC,MAAM;IAClB;IAEAL,0BAA0B;QACxBG,YAAYG,OAAO,GAAGJ;IACxB,GAAG;QAACA;KAAG;IAEP,OAAOH,MAAMQ,WAAW,CACtB,CAAC,GAAGC;QACF,MAAMC,WAAWN,YAAYG,OAAO;QACpC,OAAOG,YAAYD;IACrB,GACA;QAACL;KAAY;AAEjB,EAAE"}

View File

@@ -0,0 +1,19 @@
'use client';
import * as React from 'react';
/**
* @internal
* Checks if components was mounted the first time.
* Supports React concurrent/strict mode by using `useEffect`
* to track the first mount instead of mutating refs during render.
*
* @example
* const isFirstMount = useFirstMount();
*/ export function useFirstMount() {
const isFirst = React.useRef(true);
React.useEffect(()=>{
if (isFirst.current) {
isFirst.current = false;
}
}, []);
return isFirst.current;
}

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/hooks/useFirstMount.ts"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\n\n/**\n * @internal\n * Checks if components was mounted the first time.\n * Supports React concurrent/strict mode by using `useEffect`\n * to track the first mount instead of mutating refs during render.\n *\n * @example\n * const isFirstMount = useFirstMount();\n */\nexport function useFirstMount(): boolean {\n const isFirst = React.useRef(true);\n\n React.useEffect(() => {\n if (isFirst.current) {\n isFirst.current = false;\n }\n }, []);\n\n return isFirst.current;\n}\n"],"names":["React","useFirstMount","isFirst","useRef","useEffect","current"],"mappings":"AAAA;AAEA,YAAYA,WAAW,QAAQ;AAE/B;;;;;;;;CAQC,GACD,OAAO,SAASC;IACd,MAAMC,UAAUF,MAAMG,MAAM,CAAC;IAE7BH,MAAMI,SAAS,CAAC;QACd,IAAIF,QAAQG,OAAO,EAAE;YACnBH,QAAQG,OAAO,GAAG;QACpB;IACF,GAAG,EAAE;IAEL,OAAOH,QAAQG,OAAO;AACxB"}

View File

@@ -0,0 +1,8 @@
'use client';
import * as React from 'react';
/**
* @internal
* Forces a re-render, similar to `forceUpdate` in class components.
*/ export function useForceUpdate() {
return React.useReducer((x)=>x + 1, 0)[1];
}

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/hooks/useForceUpdate.ts"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\n/**\n * @internal\n * Forces a re-render, similar to `forceUpdate` in class components.\n */\nexport function useForceUpdate(): React.DispatchWithoutAction {\n return React.useReducer(x => x + 1, 0)[1];\n}\n"],"names":["React","useForceUpdate","useReducer","x"],"mappings":"AAAA;AAEA,YAAYA,WAAW,QAAQ;AAC/B;;;CAGC,GACD,OAAO,SAASC;IACd,OAAOD,MAAME,UAAU,CAACC,CAAAA,IAAKA,IAAI,GAAG,EAAE,CAAC,EAAE;AAC3C"}

View File

@@ -0,0 +1,53 @@
'use client';
import * as React from 'react';
import { defaultSSRContextValue, useSSRContext } from '../ssr/index';
const IdPrefixContext = React.createContext(undefined);
/**
* Allows to define a prefix that will be used for all IDs generated by useId() hook. It's useful to avoid collisions
* between different bundles.
*/ export const IdPrefixProvider = IdPrefixContext.Provider;
function useIdPrefix() {
return React.useContext(IdPrefixContext) || '';
}
/**
* Resets generated IDs, should be used only in tests.
*/ export function resetIdsForTests() {
defaultSSRContextValue.current = 0;
}
/**
* Hook to generate a unique ID.
*
* @param prefix - Optional prefix for the ID. Defaults to 'fui-'.
* @param providedId - Optional id provided by a parent component. Defaults to the provided value if present,
* without conditioning the hook call
* @returns The ID
*/ export function useId(prefix = 'fui-', providedId) {
'use no memo';
const contextValue = useSSRContext();
const idPrefix = useIdPrefix();
// Checking if useId is available on React, if it is, we use it to generate the id. String concatenation is used to
// prevent bundlers from complaining with older versions of React.
const _useId = React['use' + 'Id'];
if (_useId) {
const generatedId = _useId();
// eslint-disable-next-line react-hooks/rules-of-hooks
const escapedId = React.useMemo(()=>generatedId.replace(/:/g, ''), [
generatedId
]);
return providedId || `${idPrefix}${prefix}${escapedId}`;
}
// Hooks appear to be running conditionally, but they will always run in the same order since it's based on
// the version of React being used. This is safe to ignore.
// eslint-disable-next-line react-hooks/rules-of-hooks
return React.useMemo(()=>{
if (providedId) {
return providedId;
}
return `${idPrefix}${prefix}${++contextValue.current}`;
}, [
idPrefix,
prefix,
providedId,
contextValue
]);
}

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/hooks/useId.ts"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { defaultSSRContextValue, useSSRContext } from '../ssr/index';\n\nconst IdPrefixContext = React.createContext<string | undefined>(undefined);\n\n/**\n * Allows to define a prefix that will be used for all IDs generated by useId() hook. It's useful to avoid collisions\n * between different bundles.\n */\nexport const IdPrefixProvider = IdPrefixContext.Provider;\n\nfunction useIdPrefix(): string {\n return React.useContext(IdPrefixContext) || '';\n}\n\n/**\n * Resets generated IDs, should be used only in tests.\n */\nexport function resetIdsForTests(): void {\n defaultSSRContextValue.current = 0;\n}\n\n/**\n * Hook to generate a unique ID.\n *\n * @param prefix - Optional prefix for the ID. Defaults to 'fui-'.\n * @param providedId - Optional id provided by a parent component. Defaults to the provided value if present,\n * without conditioning the hook call\n * @returns The ID\n */\nexport function useId(prefix: string = 'fui-', providedId?: string): string {\n 'use no memo';\n\n const contextValue = useSSRContext();\n const idPrefix = useIdPrefix();\n\n // Checking if useId is available on React, if it is, we use it to generate the id. String concatenation is used to\n // prevent bundlers from complaining with older versions of React.\n const _useId = (React as never)['use' + 'Id'] as (() => string) | undefined;\n\n if (_useId) {\n const generatedId = _useId();\n\n // eslint-disable-next-line react-hooks/rules-of-hooks\n const escapedId = React.useMemo(() => generatedId.replace(/:/g, ''), [generatedId]);\n\n return providedId || `${idPrefix}${prefix}${escapedId}`;\n }\n\n // Hooks appear to be running conditionally, but they will always run in the same order since it's based on\n // the version of React being used. This is safe to ignore.\n // eslint-disable-next-line react-hooks/rules-of-hooks\n return React.useMemo(() => {\n if (providedId) {\n return providedId;\n }\n\n return `${idPrefix}${prefix}${++contextValue.current}`;\n }, [idPrefix, prefix, providedId, contextValue]);\n}\n"],"names":["React","defaultSSRContextValue","useSSRContext","IdPrefixContext","createContext","undefined","IdPrefixProvider","Provider","useIdPrefix","useContext","resetIdsForTests","current","useId","prefix","providedId","contextValue","idPrefix","_useId","generatedId","escapedId","useMemo","replace"],"mappings":"AAAA;AAEA,YAAYA,WAAW,QAAQ;AAC/B,SAASC,sBAAsB,EAAEC,aAAa,QAAQ,eAAe;AAErE,MAAMC,kBAAkBH,MAAMI,aAAa,CAAqBC;AAEhE;;;CAGC,GACD,OAAO,MAAMC,mBAAmBH,gBAAgBI,QAAQ,CAAC;AAEzD,SAASC;IACP,OAAOR,MAAMS,UAAU,CAACN,oBAAoB;AAC9C;AAEA;;CAEC,GACD,OAAO,SAASO;IACdT,uBAAuBU,OAAO,GAAG;AACnC;AAEA;;;;;;;CAOC,GACD,OAAO,SAASC,MAAMC,SAAiB,MAAM,EAAEC,UAAmB;IAChE;IAEA,MAAMC,eAAeb;IACrB,MAAMc,WAAWR;IAEjB,mHAAmH;IACnH,kEAAkE;IAClE,MAAMS,SAAS,AAACjB,KAAe,CAAC,QAAQ,KAAK;IAE7C,IAAIiB,QAAQ;QACV,MAAMC,cAAcD;QAEpB,sDAAsD;QACtD,MAAME,YAAYnB,MAAMoB,OAAO,CAAC,IAAMF,YAAYG,OAAO,CAAC,MAAM,KAAK;YAACH;SAAY;QAElF,OAAOJ,cAAc,GAAGE,WAAWH,SAASM,WAAW;IACzD;IAEA,2GAA2G;IAC3G,2DAA2D;IAC3D,sDAAsD;IACtD,OAAOnB,MAAMoB,OAAO,CAAC;QACnB,IAAIN,YAAY;YACd,OAAOA;QACT;QAEA,OAAO,GAAGE,WAAWH,SAAS,EAAEE,aAAaJ,OAAO,EAAE;IACxD,GAAG;QAACK;QAAUH;QAAQC;QAAYC;KAAa;AACjD"}

View File

@@ -0,0 +1,13 @@
'use client';
import * as React from 'react';
import { canUseDOM } from '../ssr/index';
/**
* React currently throws a warning when using useLayoutEffect on the server. To get around it, we can conditionally
* useEffect on the server (no-op) and useLayoutEffect in the browser. We occasionally need useLayoutEffect to
* ensure we don't get a render flash for certain operations, but we may also need affected components to render on
* the server.
*
* https://gist.github.com/gaearon/e7d97cdf38a2907924ea12e4ebdf3c85
* https://github.com/reduxjs/react-redux/blob/master/src/utils/useIsomorphicLayoutEffect.js
*/ // eslint-disable-next-line no-restricted-properties
export const useIsomorphicLayoutEffect = canUseDOM() ? React.useLayoutEffect : React.useEffect;

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/hooks/useIsomorphicLayoutEffect.ts"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { canUseDOM } from '../ssr/index';\n\n/**\n * React currently throws a warning when using useLayoutEffect on the server. To get around it, we can conditionally\n * useEffect on the server (no-op) and useLayoutEffect in the browser. We occasionally need useLayoutEffect to\n * ensure we don't get a render flash for certain operations, but we may also need affected components to render on\n * the server.\n *\n * https://gist.github.com/gaearon/e7d97cdf38a2907924ea12e4ebdf3c85\n * https://github.com/reduxjs/react-redux/blob/master/src/utils/useIsomorphicLayoutEffect.js\n */\n// eslint-disable-next-line no-restricted-properties\nexport const useIsomorphicLayoutEffect: typeof React.useEffect = canUseDOM() ? React.useLayoutEffect : React.useEffect;\n"],"names":["React","canUseDOM","useIsomorphicLayoutEffect","useLayoutEffect","useEffect"],"mappings":"AAAA;AAEA,YAAYA,WAAW,QAAQ;AAC/B,SAASC,SAAS,QAAQ,eAAe;AAEzC;;;;;;;;CAQC,GACD,oDAAoD;AACpD,OAAO,MAAMC,4BAAoDD,cAAcD,MAAMG,eAAe,GAAGH,MAAMI,SAAS,CAAC"}

View File

@@ -0,0 +1,34 @@
'use client';
import * as React from 'react';
/**
* React hook to merge multiple React refs (either MutableRefObjects or ref callbacks) into a single ref callback that
* updates all provided refs
* @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.
*/ // LegacyRef is actually not supported, but in React v18 types this is leaking directly from forwardRef component declaration
export function useMergedRefs(...refs) {
'use no memo';
const mergedCallback = React.useCallback((value)=>{
// Update the "current" prop hanging on the function.
mergedCallback.current = value;
for (const ref of refs){
if (typeof ref === 'string' && process.env.NODE_ENV !== 'production') {
// eslint-disable-next-line no-console
console.error(`@fluentui/react-utilities [useMergedRefs]:
This hook does not support the usage of string refs. Please use React.useRef instead.
For more info on 'React.useRef', see https://react.dev/reference/react/useRef.
For more info on string refs, see https://react.dev/blog/2024/04/25/react-19-upgrade-guide#removed-string-refs.`);
}
if (typeof ref === 'function') {
ref(value);
} else if (ref) {
ref.current = value;
}
}
}, // eslint-disable-next-line react-hooks/exhaustive-deps -- already exhaustive
[
...refs
]);
return mergedCallback;
}

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/hooks/useMergedRefs.ts"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\n\n/**\n * A Ref function which can be treated like a ref object in that it has an attached\n * current property, which will be updated as the ref is evaluated.\n */\nexport type RefObjectFunction<T> = React.RefObject<T | null> & ((value: T | null) => void);\n\n/**\n * React hook to merge multiple React refs (either MutableRefObjects or ref callbacks) into a single ref callback that\n * updates all provided refs\n * @param refs - Refs to collectively update with one ref value.\n * @returns A function with an attached \"current\" prop, so that it can be treated like a RefObject.\n */\n// LegacyRef is actually not supported, but in React v18 types this is leaking directly from forwardRef component declaration\nexport function useMergedRefs<T>(...refs: (React.Ref<T> | undefined)[]): RefObjectFunction<T> {\n 'use no memo';\n\n const mergedCallback = React.useCallback(\n (value: T | null) => {\n // Update the \"current\" prop hanging on the function.\n mergedCallback.current = value;\n\n for (const ref of refs) {\n if (typeof ref === 'string' && process.env.NODE_ENV !== 'production') {\n // eslint-disable-next-line no-console\n console.error(/** #__DE-INDENT__ */ `\n @fluentui/react-utilities [useMergedRefs]:\n This hook does not support the usage of string refs. Please use React.useRef instead.\n\n For more info on 'React.useRef', see https://react.dev/reference/react/useRef.\n For more info on string refs, see https://react.dev/blog/2024/04/25/react-19-upgrade-guide#removed-string-refs.\n `);\n }\n if (typeof ref === 'function') {\n ref(value);\n } else if (ref) {\n ref.current = value;\n }\n }\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps -- already exhaustive\n [...refs],\n ) as RefObjectFunction<T>;\n\n return mergedCallback;\n}\n"],"names":["React","useMergedRefs","refs","mergedCallback","useCallback","value","current","ref","process","env","NODE_ENV","console","error"],"mappings":"AAAA;AAEA,YAAYA,WAAW,QAAQ;AAQ/B;;;;;CAKC,GACD,6HAA6H;AAC7H,OAAO,SAASC,cAAiB,GAAGC,IAAkC;IACpE;IAEA,MAAMC,iBAAiBH,MAAMI,WAAW,CACtC,CAACC;QACC,qDAAqD;QACrDF,eAAeG,OAAO,GAAGD;QAEzB,KAAK,MAAME,OAAOL,KAAM;YACtB,IAAI,OAAOK,QAAQ,YAAYC,QAAQC,GAAG,CAACC,QAAQ,KAAK,cAAc;gBACpE,sCAAsC;gBACtCC,QAAQC,KAAK,CAAuB,CAAC;;;;+GAMrC,CAAC;YACH;YACA,IAAI,OAAOL,QAAQ,YAAY;gBAC7BA,IAAIF;YACN,OAAO,IAAIE,KAAK;gBACdA,IAAID,OAAO,GAAGD;YAChB;QACF;IACF,GACA,6EAA6E;IAC7E;WAAIH;KAAK;IAGX,OAAOC;AACT"}

View File

@@ -0,0 +1,150 @@
'use client';
import * as React from 'react';
import { useEventCallback } from './useEventCallback';
import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';
const DEFAULT_CONTAINS = (parent, child)=>!!(parent === null || parent === void 0 ? void 0 : parent.contains(child));
/**
* @internal
* Utility to perform checks where a click/touch event was made outside a component
*/ export const useOnClickOutside = (options)=>{
const { targetDocument } = useFluent();
const win = targetDocument === null || targetDocument === void 0 ? void 0 : targetDocument.defaultView;
const { refs, callback, element, disabled, disabledFocusOnIframe, contains = DEFAULT_CONTAINS } = options;
const timeoutId = React.useRef(undefined);
useIFrameFocus({
element,
disabled: disabledFocusOnIframe || disabled,
callback,
refs,
contains
});
const isMouseDownInsideRef = React.useRef(false);
const listener = useEventCallback((ev)=>{
if (isMouseDownInsideRef.current) {
isMouseDownInsideRef.current = false;
return;
}
const target = ev.composedPath()[0];
const isOutside = refs.every((ref)=>!contains(ref.current || null, target));
if (isOutside && !disabled) {
callback(ev);
}
});
const handleMouseDown = useEventCallback((ev)=>{
// Selecting text from inside to outside will rigger click event.
// In this case click event target is outside but mouse down event target is inside.
// And this click event should be considered as inside click.
isMouseDownInsideRef.current = refs.some((ref)=>contains(ref.current || null, ev.target));
});
React.useEffect(()=>{
if (disabled) {
return;
}
// Store the current event to avoid triggering handlers immediately
// Note this depends on a deprecated but extremely well supported quirk of the web platform
// https://github.com/facebook/react/issues/20074
let currentEvent = getWindowEvent(win);
const conditionalHandler = (event)=>{
// Skip if this event is the same as the one running when we added the handlers
if (event === currentEvent) {
currentEvent = undefined;
return;
}
listener(event);
};
// use capture phase because React can update DOM before the event bubbles to the document
element === null || element === void 0 ? void 0 : element.addEventListener('click', conditionalHandler, true);
element === null || element === void 0 ? void 0 : element.addEventListener('touchstart', conditionalHandler, true);
element === null || element === void 0 ? void 0 : element.addEventListener('contextmenu', conditionalHandler, true);
element === null || element === void 0 ? void 0 : element.addEventListener('mousedown', handleMouseDown, true);
// Garbage collect this event after it's no longer useful to avoid memory leaks
timeoutId.current = win === null || win === void 0 ? void 0 : win.setTimeout(()=>{
currentEvent = undefined;
}, 1);
return ()=>{
element === null || element === void 0 ? void 0 : element.removeEventListener('click', conditionalHandler, true);
element === null || element === void 0 ? void 0 : element.removeEventListener('touchstart', conditionalHandler, true);
element === null || element === void 0 ? void 0 : element.removeEventListener('contextmenu', conditionalHandler, true);
element === null || element === void 0 ? void 0 : element.removeEventListener('mousedown', handleMouseDown, true);
win === null || win === void 0 ? void 0 : win.clearTimeout(timeoutId.current);
currentEvent = undefined;
};
}, [
listener,
element,
disabled,
handleMouseDown,
win
]);
};
const getWindowEvent = (target)=>{
if (target) {
var _target_ownerDocument_defaultView, _target_ownerDocument;
if (typeof target.window === 'object' && target.window === target) {
// eslint-disable-next-line @typescript-eslint/no-deprecated
return target.event;
}
var _target_ownerDocument_defaultView_event;
// eslint-disable-next-line @typescript-eslint/no-deprecated
return (_target_ownerDocument_defaultView_event = (_target_ownerDocument = target.ownerDocument) === null || _target_ownerDocument === void 0 ? void 0 : (_target_ownerDocument_defaultView = _target_ownerDocument.defaultView) === null || _target_ownerDocument_defaultView === void 0 ? void 0 : _target_ownerDocument_defaultView.event) !== null && _target_ownerDocument_defaultView_event !== void 0 ? _target_ownerDocument_defaultView_event : undefined;
}
return undefined;
};
const FUI_FRAME_EVENT = 'fuiframefocus';
/**
* Since click events do not propagate past iframes, we use focus to detect if a
* click has happened inside an iframe, since the only ways of focusing inside an
* iframe are:
* - clicking inside
* - tabbing inside
*
* Polls the value of `document.activeElement`. If it is an iframe, then dispatch
* a custom DOM event. When the custom event is received call the provided callback
*/ const useIFrameFocus = (options)=>{
const { disabled, element: targetDocument, callback, contains = DEFAULT_CONTAINS, pollDuration = 100, refs } = options;
const timeoutRef = React.useRef(undefined);
const listener = useEventCallback((e)=>{
const isOutside = refs.every((ref)=>!contains(ref.current || null, e.target));
if (isOutside && !disabled) {
callback(e);
}
});
// Adds listener to the custom iframe focus event
React.useEffect(()=>{
if (disabled) {
return;
}
targetDocument === null || targetDocument === void 0 ? void 0 : targetDocument.addEventListener(FUI_FRAME_EVENT, listener, true);
return ()=>{
targetDocument === null || targetDocument === void 0 ? void 0 : targetDocument.removeEventListener(FUI_FRAME_EVENT, listener, true);
};
}, [
targetDocument,
disabled,
listener
]);
// Starts polling for the active element
React.useEffect(()=>{
var _targetDocument_defaultView;
if (disabled) {
return;
}
timeoutRef.current = targetDocument === null || targetDocument === void 0 ? void 0 : (_targetDocument_defaultView = targetDocument.defaultView) === null || _targetDocument_defaultView === void 0 ? void 0 : _targetDocument_defaultView.setInterval(()=>{
const activeElement = targetDocument === null || targetDocument === void 0 ? void 0 : targetDocument.activeElement;
if ((activeElement === null || activeElement === void 0 ? void 0 : activeElement.tagName) === 'IFRAME' || (activeElement === null || activeElement === void 0 ? void 0 : activeElement.tagName) === 'WEBVIEW') {
const event = new CustomEvent(FUI_FRAME_EVENT, {
bubbles: true
});
activeElement.dispatchEvent(event);
}
}, pollDuration);
return ()=>{
var _targetDocument_defaultView;
targetDocument === null || targetDocument === void 0 ? void 0 : (_targetDocument_defaultView = targetDocument.defaultView) === null || _targetDocument_defaultView === void 0 ? void 0 : _targetDocument_defaultView.clearInterval(timeoutRef.current);
};
}, [
targetDocument,
disabled,
pollDuration
]);
};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,32 @@
'use client';
import * as React from 'react';
import { useEventCallback } from './useEventCallback';
/**
* @internal
* Utility to perform checks where a click/touch event was made outside a component
*/ export const useOnScrollOutside = (options)=>{
const { refs, callback, element, disabled, contains: containsProp } = options;
const listener = useEventCallback((ev)=>{
const contains = containsProp || ((parent, child)=>!!(parent === null || parent === void 0 ? void 0 : parent.contains(child)));
const target = ev.composedPath()[0];
const isOutside = refs.every((ref)=>!contains(ref.current || null, target));
if (isOutside && !disabled) {
callback(ev);
}
});
React.useEffect(()=>{
if (disabled) {
return;
}
element === null || element === void 0 ? void 0 : element.addEventListener('wheel', listener);
element === null || element === void 0 ? void 0 : element.addEventListener('touchmove', listener);
return ()=>{
element === null || element === void 0 ? void 0 : element.removeEventListener('wheel', listener);
element === null || element === void 0 ? void 0 : element.removeEventListener('touchmove', listener);
};
}, [
listener,
element,
disabled
]);
};

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/hooks/useOnScrollOutside.ts"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { useEventCallback } from './useEventCallback';\nimport type { UseOnClickOrScrollOutsideOptions } from './useOnClickOutside';\n\n/**\n * @internal\n * Utility to perform checks where a click/touch event was made outside a component\n */\nexport const useOnScrollOutside = (options: UseOnClickOrScrollOutsideOptions): void => {\n const { refs, callback, element, disabled, contains: containsProp } = options;\n\n const listener = useEventCallback((ev: MouseEvent | TouchEvent) => {\n const contains: UseOnClickOrScrollOutsideOptions['contains'] =\n containsProp || ((parent, child) => !!parent?.contains(child));\n\n const target = ev.composedPath()[0] as HTMLElement;\n const isOutside = refs.every(ref => !contains(ref.current || null, target));\n\n if (isOutside && !disabled) {\n callback(ev);\n }\n });\n\n React.useEffect(() => {\n if (disabled) {\n return;\n }\n\n element?.addEventListener('wheel', listener);\n element?.addEventListener('touchmove', listener);\n\n return () => {\n element?.removeEventListener('wheel', listener);\n element?.removeEventListener('touchmove', listener);\n };\n }, [listener, element, disabled]);\n};\n"],"names":["React","useEventCallback","useOnScrollOutside","options","refs","callback","element","disabled","contains","containsProp","listener","ev","parent","child","target","composedPath","isOutside","every","ref","current","useEffect","addEventListener","removeEventListener"],"mappings":"AAAA;AAEA,YAAYA,WAAW,QAAQ;AAC/B,SAASC,gBAAgB,QAAQ,qBAAqB;AAGtD;;;CAGC,GACD,OAAO,MAAMC,qBAAqB,CAACC;IACjC,MAAM,EAAEC,IAAI,EAAEC,QAAQ,EAAEC,OAAO,EAAEC,QAAQ,EAAEC,UAAUC,YAAY,EAAE,GAAGN;IAEtE,MAAMO,WAAWT,iBAAiB,CAACU;QACjC,MAAMH,WACJC,gBAAiB,CAAA,CAACG,QAAQC,QAAU,CAAC,EAACD,mBAAAA,6BAAAA,OAAQJ,QAAQ,CAACK,OAAK;QAE9D,MAAMC,SAASH,GAAGI,YAAY,EAAE,CAAC,EAAE;QACnC,MAAMC,YAAYZ,KAAKa,KAAK,CAACC,CAAAA,MAAO,CAACV,SAASU,IAAIC,OAAO,IAAI,MAAML;QAEnE,IAAIE,aAAa,CAACT,UAAU;YAC1BF,SAASM;QACX;IACF;IAEAX,MAAMoB,SAAS,CAAC;QACd,IAAIb,UAAU;YACZ;QACF;QAEAD,oBAAAA,8BAAAA,QAASe,gBAAgB,CAAC,SAASX;QACnCJ,oBAAAA,8BAAAA,QAASe,gBAAgB,CAAC,aAAaX;QAEvC,OAAO;YACLJ,oBAAAA,8BAAAA,QAASgB,mBAAmB,CAAC,SAASZ;YACtCJ,oBAAAA,8BAAAA,QAASgB,mBAAmB,CAAC,aAAaZ;QAC5C;IACF,GAAG;QAACA;QAAUJ;QAASC;KAAS;AAClC,EAAE"}

View File

@@ -0,0 +1,13 @@
'use client';
import * as React from 'react';
/**
* @internal
*/ export const usePrevious = (value)=>{
const ref = React.useRef(null);
React.useEffect(()=>{
ref.current = value;
}, [
value
]);
return ref.current;
};

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/hooks/usePrevious.ts"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\n\n/**\n * @internal\n */\nexport const usePrevious = <ValueType = unknown>(value: ValueType): ValueType | null => {\n const ref = React.useRef<ValueType | null>(null);\n React.useEffect(() => {\n ref.current = value;\n }, [value]);\n return ref.current;\n};\n"],"names":["React","usePrevious","value","ref","useRef","useEffect","current"],"mappings":"AAAA;AAEA,YAAYA,WAAW,QAAQ;AAE/B;;CAEC,GACD,OAAO,MAAMC,cAAc,CAAsBC;IAC/C,MAAMC,MAAMH,MAAMI,MAAM,CAAmB;IAC3CJ,MAAMK,SAAS,CAAC;QACdF,IAAIG,OAAO,GAAGJ;IAChB,GAAG;QAACA;KAAM;IACV,OAAOC,IAAIG,OAAO;AACpB,EAAE"}

View File

@@ -0,0 +1,24 @@
'use client';
import * as React from 'react';
import { measureScrollbarWidth } from '../utils/measureScrollBarWidth';
const cache = new WeakMap();
/**
* @returns The width in pixels of the scrollbar in the user agent
* @remarks This hook is not SSR-safe. For SSR-safe scrollbar width application, use the `useApplyScrollbarWidth` from {@link file://./useApplyScrollbarWidth.ts} instead.
*/ export function useScrollbarWidth(options) {
const { targetDocument, force } = options;
return React.useMemo(()=>{
if (!targetDocument) {
return 0;
}
if (!force && cache.has(targetDocument)) {
return cache.get(targetDocument);
}
const scrollbarWidth = measureScrollbarWidth(targetDocument);
cache.set(targetDocument, scrollbarWidth);
return scrollbarWidth;
}, [
targetDocument,
force
]);
}

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/hooks/useScrollbarWidth.ts"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { measureScrollbarWidth } from '../utils/measureScrollBarWidth';\n\nconst cache = new WeakMap<Document, number>();\n\ninterface UseScrollbarWidthOptions {\n /**\n * Reference document to measure the scrollbar width\n */\n targetDocument: Document | null | undefined;\n\n /**\n * Does not use the cache and recalculates the scrollbar width\n */\n force?: boolean;\n}\n\n/**\n * @returns The width in pixels of the scrollbar in the user agent\n * @remarks This hook is not SSR-safe. For SSR-safe scrollbar width application, use the `useApplyScrollbarWidth` from {@link file://./useApplyScrollbarWidth.ts} instead.\n */\nexport function useScrollbarWidth(options: UseScrollbarWidthOptions): number | undefined {\n const { targetDocument, force } = options;\n\n return React.useMemo(() => {\n if (!targetDocument) {\n return 0;\n }\n\n if (!force && cache.has(targetDocument)) {\n return cache.get(targetDocument);\n }\n\n const scrollbarWidth = measureScrollbarWidth(targetDocument);\n cache.set(targetDocument, scrollbarWidth);\n\n return scrollbarWidth;\n }, [targetDocument, force]);\n}\n"],"names":["React","measureScrollbarWidth","cache","WeakMap","useScrollbarWidth","options","targetDocument","force","useMemo","has","get","scrollbarWidth","set"],"mappings":"AAAA;AAEA,YAAYA,WAAW,QAAQ;AAC/B,SAASC,qBAAqB,QAAQ,iCAAiC;AAEvE,MAAMC,QAAQ,IAAIC;AAclB;;;CAGC,GACD,OAAO,SAASC,kBAAkBC,OAAiC;IACjE,MAAM,EAAEC,cAAc,EAAEC,KAAK,EAAE,GAAGF;IAElC,OAAOL,MAAMQ,OAAO,CAAC;QACnB,IAAI,CAACF,gBAAgB;YACnB,OAAO;QACT;QAEA,IAAI,CAACC,SAASL,MAAMO,GAAG,CAACH,iBAAiB;YACvC,OAAOJ,MAAMQ,GAAG,CAACJ;QACnB;QAEA,MAAMK,iBAAiBV,sBAAsBK;QAC7CJ,MAAMU,GAAG,CAACN,gBAAgBK;QAE1B,OAAOA;IACT,GAAG;QAACL;QAAgBC;KAAM;AAC5B"}

View File

@@ -0,0 +1,19 @@
'use client';
import { useBrowserTimer } from './useBrowserTimer';
import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';
const setTimeoutNoop = (_callback)=>-1;
const clearTimeoutNoop = (_handle)=>undefined;
/**
* @internal
* Helper to manage a browser timeout.
* Ensures that the timeout isn't set multiple times at once and is cleaned up
* when the component is unloaded.
*
* @returns A pair of [setTimeout, clearTimeout] that are stable between renders.
*/ export function useTimeout() {
const { targetDocument } = useFluent();
const win = targetDocument === null || targetDocument === void 0 ? void 0 : targetDocument.defaultView;
const setTimerFn = win ? win.setTimeout : setTimeoutNoop;
const clearTimerFn = win ? win.clearTimeout : clearTimeoutNoop;
return useBrowserTimer(setTimerFn, clearTimerFn);
}

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/hooks/useTimeout.ts"],"sourcesContent":["'use client';\n\nimport { useBrowserTimer } from './useBrowserTimer';\nimport { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';\n\nconst setTimeoutNoop = (_callback: Function) => -1;\nconst clearTimeoutNoop = (_handle: number) => undefined;\n\n/**\n * @internal\n * Helper to manage a browser timeout.\n * Ensures that the timeout isn't set multiple times at once and is cleaned up\n * when the component is unloaded.\n *\n * @returns A pair of [setTimeout, clearTimeout] that are stable between renders.\n */\nexport function useTimeout(): readonly [(fn: () => void, delay?: number) => number, () => void] {\n const { targetDocument } = useFluent();\n const win = targetDocument?.defaultView;\n\n const setTimerFn = win ? win.setTimeout : setTimeoutNoop;\n const clearTimerFn = win ? win.clearTimeout : clearTimeoutNoop;\n\n return useBrowserTimer(setTimerFn, clearTimerFn);\n}\n"],"names":["useBrowserTimer","useFluent_unstable","useFluent","setTimeoutNoop","_callback","clearTimeoutNoop","_handle","undefined","useTimeout","targetDocument","win","defaultView","setTimerFn","setTimeout","clearTimerFn","clearTimeout"],"mappings":"AAAA;AAEA,SAASA,eAAe,QAAQ,oBAAoB;AACpD,SAASC,sBAAsBC,SAAS,QAAQ,kCAAkC;AAElF,MAAMC,iBAAiB,CAACC,YAAwB,CAAC;AACjD,MAAMC,mBAAmB,CAACC,UAAoBC;AAE9C;;;;;;;CAOC,GACD,OAAO,SAASC;IACd,MAAM,EAAEC,cAAc,EAAE,GAAGP;IAC3B,MAAMQ,MAAMD,2BAAAA,qCAAAA,eAAgBE,WAAW;IAEvC,MAAMC,aAAaF,MAAMA,IAAIG,UAAU,GAAGV;IAC1C,MAAMW,eAAeJ,MAAMA,IAAIK,YAAY,GAAGV;IAE9C,OAAOL,gBAAgBY,YAAYE;AACrC"}