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

File diff suppressed because it is too large Load Diff

15
node_modules/@fluentui/react-context-selector/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,15 @@
@fluentui/react-context-selector
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

124
node_modules/@fluentui/react-context-selector/README.md generated vendored Normal file
View File

@@ -0,0 +1,124 @@
# `@fluentui/react-context-selector`
React `useContextSelector()` hook in userland.
## Introduction
[React Context](https://reactjs.org/docs/context.html) and [`useContext()`](https://reactjs.org/docs/hooks-reference.html#usecontext) is often used to avoid prop drilling,
however it's known that there's a performance issue. When a context value is changed, all components that are subscribed with `useContext()` will re-render.
[useContextSelector](https://github.com/reactjs/rfcs/pull/119) is recently proposed. While waiting for the process, this library provides the API in userland.
# Installation
**NPM**
```bash
npm install --save @fluentui/react-context-selector
```
**Yarn**
```bash
yarn add @fluentui/react-context-selector
```
## Usage
### Getting started
```tsx
import * as React from 'react';
import { createContext, useContextSelector, ContextSelector } from '@fluentui/react-context-selector';
interface CounterContextValue {
count1: number;
count2: number;
incrementCount1: () => void;
incrementCount2: () => void;
}
// 💡 The same syntax as native React context API
// https://reactjs.org/docs/context.html#reactcreatecontext
const CounterContext = createContext<CounterContextValue>({} as CounterContextValue);
const CounterProvider = CounterContext.Provider;
// not necessary but can be a good layer to mock for unit testing
const useCounterContext = <T,>(selector: ContextSelector<CounterContextValue, T>) =>
useContextSelector(CounterContext, selector);
const Counter1 = () => {
// 💡 Context updates will be propagated only when result of a selector function will change
// "Object.is()" is used for internal comparisons
const count1 = useCounterContext(context => context.count1);
const increment = useCounterContext(context => context.incrementCount1);
return <button onClick={increment}>Counter 1: {count1}</button>;
};
const Counter2 = () => {
const count2 = useCounterContext(context => context.count2);
const increment = useCounterContext(context => context.incrementCount2);
return <button onClick={increment}>Counter 2: {count2}</button>;
};
export default function App() {
const [state, setState] = React.useState({ count1: 0, count2: 0 });
const incrementCount1 = React.useCallback(() => setState(s => ({ ...s, count1: s.count1 + 1 })), [setState]);
const incrementCount2 = React.useCallback(() => setState(s => ({ ...s, count2: s.count2 + 1 })), [setState]);
return (
<div className="App">
<CounterProvider
value={{
count1: state.count1,
count2: state.count2,
incrementCount1,
incrementCount2,
}}
>
<Counter1 />
<Counter2 />
</CounterProvider>
</div>
);
}
```
### useHasParentContext
This helper hook will allow you to know if a component is wrapped by a context selector provider
```tsx
const Foo = () => {
// An easy way to test if a context provider is wrapped around this component
// since it's more complicated to compare with a default context value
const isWrappedWithContext = useHasParentContext(CounterContext);
if (isWrappedWithContext) {
return <div>I am inside context selector provider</div>;
} else {
return <div>I can only use default context value</div>;
}
};
```
## Technical memo
React context by nature triggers propagation of component re-rendering if a value is changed. To avoid this, this library uses undocumented feature of `calculateChangedBits`. It then uses a subscription model to force update when a component needs to re-render.
## Limitations
- In order to stop propagation, `children` of a context provider has to be either created outside of the provider or memoized with `React.memo`.
- `<Consumer />` components are not supported.
- The [stale props](https://react-redux.js.org/api/hooks#stale-props-and-zombie-children) issue can't be solved in userland. (workaround with try-catch)
## Related projects
The implementation is heavily inspired by:
- [use-context-selector](https://github.com/dai-shi/use-context-selector)
- [react-tracked](https://github.com/dai-shi/react-tracked)

View File

@@ -0,0 +1,61 @@
import * as React_2 from 'react';
/**
* @internal
*/
export declare type Context<Value> = React_2.Context<Value> & {
Provider: React_2.FC<React_2.ProviderProps<Value>>;
Consumer: never;
};
export declare type ContextSelector<Value, SelectedValue> = (value: Value) => SelectedValue;
/**
* @internal
*/
export declare type ContextValue<Value> = {
/** Holds a set of subscribers from components. */
listeners: ((payload: readonly [ContextVersion, Value]) => void)[];
/** Holds an actual value of React's context that will be propagated down for computations. */
value: React_2.MutableRefObject<Value>;
/** A version field is used to sync a context value and consumers. */
version: React_2.MutableRefObject<ContextVersion>;
};
/**
* @internal
*/
export declare type ContextValues<Value> = ContextValue<Value> & {
/** List of listners to publish changes */
listeners: ((payload: readonly [ContextVersion, Record<string, Value>]) => void)[];
};
/**
* @internal
*/
export declare type ContextVersion = number;
/**
* @internal
*/
export declare const createContext: <Value>(defaultValue: Value) => Context<Value>;
/**
* @internal
* This hook returns context selected value by selector.
* It will only accept context created by `createContext`.
* It will trigger re-render if only the selected value is referentially changed.
*/
export declare const useContextSelector: <Value, SelectedValue>(context: Context<Value>, selector: ContextSelector<Value, SelectedValue>) => SelectedValue;
/**
* @internal
* Utility hook for contexts created by react-context-selector to determine if a parent context exists
* WARNING: This hook will not work for native React contexts
*
* @param context - context created by react-context-selector
* @returns whether the hook is wrapped by a parent context
*/
export declare function useHasParentContext<Value>(context: Context<Value>): boolean;
export { }

View File

@@ -0,0 +1,69 @@
'use client';
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "createContext", {
enumerable: true,
get: function() {
return createContext;
}
});
const _interop_require_wildcard = require("@swc/helpers/_/_interop_require_wildcard");
const _reactutilities = require("@fluentui/react-utilities");
const _react = /*#__PURE__*/ _interop_require_wildcard._(require("react"));
const _scheduler = require("scheduler");
const createProvider = (Original)=>{
const Provider = (props)=>{
// Holds an actual "props.value"
const valueRef = _react.useRef(props.value);
// Used to sync context updates and avoid stale values, can be considered as render/effect counter of Provider.
const versionRef = _react.useRef(0);
// A stable object, is used to avoid context updates via mutation of its values.
const contextValue = _react.useRef(null);
if (!contextValue.current) {
contextValue.current = {
value: valueRef,
version: versionRef,
listeners: []
};
}
(0, _reactutilities.useIsomorphicLayoutEffect)(()=>{
valueRef.current = props.value;
versionRef.current += 1;
(0, _scheduler.unstable_runWithPriority)(_scheduler.unstable_NormalPriority, ()=>{
contextValue.current.listeners.forEach((listener)=>{
listener([
versionRef.current,
props.value
]);
});
});
}, [
props.value
]);
return _react.createElement(Original, {
value: contextValue.current
}, props.children);
};
/* istanbul ignore else */ if (process.env.NODE_ENV !== 'production') {
Provider.displayName = 'ContextSelector.Provider';
}
return Provider;
};
const createContext = (defaultValue)=>{
// eslint-disable-next-line @fluentui/no-context-default-value
const context = _react.createContext({
value: {
current: defaultValue
},
version: {
current: -1
},
listeners: []
});
context.Provider = createProvider(context.Provider);
// We don't support Consumer API
delete context.Consumer;
return context;
};

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/createContext.ts"],"sourcesContent":["'use client';\n\nimport { useIsomorphicLayoutEffect } from '@fluentui/react-utilities';\nimport * as React from 'react';\nimport { unstable_NormalPriority as NormalPriority, unstable_runWithPriority as runWithPriority } from 'scheduler';\n\nimport { Context, ContextValue } from './types';\n\nconst createProvider = <Value>(Original: React.Provider<ContextValue<Value>>) => {\n const Provider: React.FC<React.ProviderProps<Value>> = props => {\n // Holds an actual \"props.value\"\n const valueRef = React.useRef(props.value);\n // Used to sync context updates and avoid stale values, can be considered as render/effect counter of Provider.\n const versionRef = React.useRef(0);\n\n // A stable object, is used to avoid context updates via mutation of its values.\n const contextValue = React.useRef<ContextValue<Value>>(null);\n\n if (!contextValue.current) {\n contextValue.current = {\n value: valueRef,\n version: versionRef,\n listeners: [],\n };\n }\n\n useIsomorphicLayoutEffect(() => {\n valueRef.current = props.value;\n versionRef.current += 1;\n\n runWithPriority(NormalPriority, () => {\n (contextValue.current as ContextValue<Value>).listeners.forEach(listener => {\n listener([versionRef.current, props.value]);\n });\n });\n }, [props.value]);\n\n return React.createElement(Original, { value: contextValue.current }, props.children);\n };\n\n /* istanbul ignore else */\n if (process.env.NODE_ENV !== 'production') {\n Provider.displayName = 'ContextSelector.Provider';\n }\n\n return Provider as unknown as React.Provider<ContextValue<Value>>;\n};\n\n/**\n * @internal\n */\nexport const createContext = <Value>(defaultValue: Value): Context<Value> => {\n // eslint-disable-next-line @fluentui/no-context-default-value\n const context = React.createContext<ContextValue<Value>>({\n value: { current: defaultValue },\n version: { current: -1 },\n listeners: [],\n });\n\n context.Provider = createProvider<Value>(context.Provider);\n\n // We don't support Consumer API\n delete (context as unknown as Context<Value>).Consumer;\n\n return context as unknown as Context<Value>;\n};\n"],"names":["createContext","createProvider","Original","Provider","props","valueRef","React","useRef","value","versionRef","contextValue","current","version","listeners","useIsomorphicLayoutEffect","runWithPriority","NormalPriority","forEach","listener","createElement","children","process","env","NODE_ENV","displayName","defaultValue","context","Consumer"],"mappings":"AAAA;;;;;+BAmDaA;;;eAAAA;;;;gCAjD6B;iEACnB;2BACgF;AAIvG,MAAMC,iBAAiB,CAAQC;IAC7B,MAAMC,WAAiDC,CAAAA;QACrD,gCAAgC;QAChC,MAAMC,WAAWC,OAAMC,MAAM,CAACH,MAAMI,KAAK;QACzC,+GAA+G;QAC/G,MAAMC,aAAaH,OAAMC,MAAM,CAAC;QAEhC,gFAAgF;QAChF,MAAMG,eAAeJ,OAAMC,MAAM,CAAsB;QAEvD,IAAI,CAACG,aAAaC,OAAO,EAAE;YACzBD,aAAaC,OAAO,GAAG;gBACrBH,OAAOH;gBACPO,SAASH;gBACTI,WAAW,EAAE;YACf;QACF;QAEAC,IAAAA,yCAAyB,EAAC;YACxBT,SAASM,OAAO,GAAGP,MAAMI,KAAK;YAC9BC,WAAWE,OAAO,IAAI;YAEtBI,IAAAA,mCAAe,EAACC,kCAAc,EAAE;gBAC7BN,aAAaC,OAAO,CAAyBE,SAAS,CAACI,OAAO,CAACC,CAAAA;oBAC9DA,SAAS;wBAACT,WAAWE,OAAO;wBAAEP,MAAMI,KAAK;qBAAC;gBAC5C;YACF;QACF,GAAG;YAACJ,MAAMI,KAAK;SAAC;QAEhB,OAAOF,OAAMa,aAAa,CAACjB,UAAU;YAAEM,OAAOE,aAAaC,OAAO;QAAC,GAAGP,MAAMgB,QAAQ;IACtF;IAEA,wBAAwB,GACxB,IAAIC,QAAQC,GAAG,CAACC,QAAQ,KAAK,cAAc;QACzCpB,SAASqB,WAAW,GAAG;IACzB;IAEA,OAAOrB;AACT;AAKO,MAAMH,gBAAgB,CAAQyB;IACnC,8DAA8D;IAC9D,MAAMC,UAAUpB,OAAMN,aAAa,CAAsB;QACvDQ,OAAO;YAAEG,SAASc;QAAa;QAC/Bb,SAAS;YAAED,SAAS,CAAC;QAAE;QACvBE,WAAW,EAAE;IACf;IAEAa,QAAQvB,QAAQ,GAAGF,eAAsByB,QAAQvB,QAAQ;IAEzD,gCAAgC;IAChC,OAAO,AAACuB,QAAsCC,QAAQ;IAEtD,OAAOD;AACT"}

View File

@@ -0,0 +1,24 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
createContext: function() {
return _createContext.createContext;
},
useContextSelector: function() {
return _useContextSelector.useContextSelector;
},
useHasParentContext: function() {
return _useHasParentContext.useHasParentContext;
}
});
const _createContext = require("./createContext");
const _useContextSelector = require("./useContextSelector");
const _useHasParentContext = require("./useHasParentContext");

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export { createContext } from './createContext';\nexport { useContextSelector } from './useContextSelector';\nexport { useHasParentContext } from './useHasParentContext';\n// eslint-disable-next-line @fluentui/ban-context-export\nexport type { Context, ContextSelector, ContextValue, ContextValues, ContextVersion } from './types';\n"],"names":["createContext","useContextSelector","useHasParentContext"],"mappings":";;;;;;;;;;;IAASA,aAAa;eAAbA,4BAAa;;IACbC,kBAAkB;eAAlBA,sCAAkB;;IAClBC,mBAAmB;eAAnBA,wCAAmB;;;+BAFE;oCACK;qCACC"}

View File

@@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
const _interop_require_wildcard = require("@swc/helpers/_/_interop_require_wildcard");
const _react = /*#__PURE__*/ _interop_require_wildcard._(require("react"));

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["import * as React from 'react';\n\n/**\n * @internal\n */\nexport type Context<Value> = React.Context<Value> & {\n Provider: React.FC<React.ProviderProps<Value>>;\n Consumer: never;\n};\n\nexport type ContextSelector<Value, SelectedValue> = (value: Value) => SelectedValue;\n\n/**\n * @internal\n */\nexport type ContextVersion = number;\n\n/**\n * @internal\n */\nexport type ContextValue<Value> = {\n /** Holds a set of subscribers from components. */\n listeners: ((payload: readonly [ContextVersion, Value]) => void)[];\n\n /** Holds an actual value of React's context that will be propagated down for computations. */\n value: // eslint-disable-next-line @typescript-eslint/no-deprecated\n React.MutableRefObject<Value>;\n\n /** A version field is used to sync a context value and consumers. */\n version: // eslint-disable-next-line @typescript-eslint/no-deprecated\n React.MutableRefObject<ContextVersion>;\n};\n\n/**\n * @internal\n */\nexport type ContextValues<Value> = ContextValue<Value> & {\n /** List of listners to publish changes */\n listeners: ((payload: readonly [ContextVersion, Record<string, Value>]) => void)[];\n};\n"],"names":[],"mappings":";;;;;iEAAuB"}

View File

@@ -0,0 +1,80 @@
'use client';
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "useContextSelector", {
enumerable: true,
get: function() {
return useContextSelector;
}
});
const _interop_require_wildcard = require("@swc/helpers/_/_interop_require_wildcard");
const _reactutilities = require("@fluentui/react-utilities");
const _react = /*#__PURE__*/ _interop_require_wildcard._(require("react"));
const useContextSelector = (context, selector)=>{
const contextValue = _react.useContext(context);
const { value: { current: value }, version: { current: version }, listeners } = contextValue;
const selected = selector(value);
const [state, setState] = _react.useState([
value,
selected
]);
const dispatch = (payload)=>{
setState((prevState)=>{
if (!payload) {
// early bail out when is dispatched during render
return [
value,
selected
];
}
if (payload[0] <= version) {
if (Object.is(prevState[1], selected)) {
return prevState; // bail out
}
return [
value,
selected
];
}
try {
if (Object.is(prevState[0], payload[1])) {
return prevState; // do not update
}
const nextSelected = selector(payload[1]);
if (Object.is(prevState[1], nextSelected)) {
return prevState; // do not update
}
return [
payload[1],
nextSelected
];
} catch (e) {
// ignored (stale props or some other reason)
}
// explicitly spread to enforce typing
return [
prevState[0],
prevState[1]
]; // schedule update
});
};
if (!Object.is(state[1], selected)) {
// schedule re-render
// this is safe because it's self contained
dispatch(undefined);
}
const stableDispatch = (0, _reactutilities.useEventCallback)(dispatch);
(0, _reactutilities.useIsomorphicLayoutEffect)(()=>{
listeners.push(stableDispatch);
return ()=>{
const index = listeners.indexOf(stableDispatch);
listeners.splice(index, 1);
};
}, [
stableDispatch,
listeners
]);
return state[1];
};

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/useContextSelector.ts"],"sourcesContent":["'use client';\n\nimport { useEventCallback, useIsomorphicLayoutEffect } from '@fluentui/react-utilities';\nimport * as React from 'react';\n\nimport { Context, ContextSelector, ContextValue, ContextVersion } from './types';\n\n/**\n * @internal\n * This hook returns context selected value by selector.\n * It will only accept context created by `createContext`.\n * It will trigger re-render if only the selected value is referentially changed.\n */\nexport const useContextSelector = <Value, SelectedValue>(\n context: Context<Value>,\n selector: ContextSelector<Value, SelectedValue>,\n): SelectedValue => {\n const contextValue = React.useContext(context as unknown as Context<ContextValue<Value>>);\n\n const {\n value: { current: value },\n version: { current: version },\n listeners,\n } = contextValue;\n const selected = selector(value);\n\n const [state, setState] = React.useState<readonly [Value, SelectedValue]>([value, selected]);\n const dispatch = (\n payload:\n | undefined // undefined from render below\n | readonly [ContextVersion, Value], // from provider effect\n ) => {\n setState(prevState => {\n if (!payload) {\n // early bail out when is dispatched during render\n return [value, selected] as const;\n }\n\n if (payload[0] <= version) {\n if (Object.is(prevState[1], selected)) {\n return prevState; // bail out\n }\n\n return [value, selected] as const;\n }\n\n try {\n if (Object.is(prevState[0], payload[1])) {\n return prevState; // do not update\n }\n\n const nextSelected = selector(payload[1]);\n\n if (Object.is(prevState[1], nextSelected)) {\n return prevState; // do not update\n }\n\n return [payload[1], nextSelected] as const;\n } catch (e) {\n // ignored (stale props or some other reason)\n }\n\n // explicitly spread to enforce typing\n return [prevState[0], prevState[1]] as const; // schedule update\n });\n };\n\n if (!Object.is(state[1], selected)) {\n // schedule re-render\n // this is safe because it's self contained\n dispatch(undefined);\n }\n\n const stableDispatch = useEventCallback(dispatch);\n\n useIsomorphicLayoutEffect(() => {\n listeners.push(stableDispatch);\n\n return () => {\n const index = listeners.indexOf(stableDispatch);\n listeners.splice(index, 1);\n };\n }, [stableDispatch, listeners]);\n\n return state[1] as SelectedValue;\n};\n"],"names":["useContextSelector","context","selector","contextValue","React","useContext","value","current","version","listeners","selected","state","setState","useState","dispatch","payload","prevState","Object","is","nextSelected","e","undefined","stableDispatch","useEventCallback","useIsomorphicLayoutEffect","push","index","indexOf","splice"],"mappings":"AAAA;;;;;+BAaaA;;;eAAAA;;;;gCAX+C;iEACrC;AAUhB,MAAMA,qBAAqB,CAChCC,SACAC;IAEA,MAAMC,eAAeC,OAAMC,UAAU,CAACJ;IAEtC,MAAM,EACJK,OAAO,EAAEC,SAASD,KAAK,EAAE,EACzBE,SAAS,EAAED,SAASC,OAAO,EAAE,EAC7BC,SAAS,EACV,GAAGN;IACJ,MAAMO,WAAWR,SAASI;IAE1B,MAAM,CAACK,OAAOC,SAAS,GAAGR,OAAMS,QAAQ,CAAkC;QAACP;QAAOI;KAAS;IAC3F,MAAMI,WAAW,CACfC;QAIAH,SAASI,CAAAA;YACP,IAAI,CAACD,SAAS;gBACZ,kDAAkD;gBAClD,OAAO;oBAACT;oBAAOI;iBAAS;YAC1B;YAEA,IAAIK,OAAO,CAAC,EAAE,IAAIP,SAAS;gBACzB,IAAIS,OAAOC,EAAE,CAACF,SAAS,CAAC,EAAE,EAAEN,WAAW;oBACrC,OAAOM,WAAW,WAAW;gBAC/B;gBAEA,OAAO;oBAACV;oBAAOI;iBAAS;YAC1B;YAEA,IAAI;gBACF,IAAIO,OAAOC,EAAE,CAACF,SAAS,CAAC,EAAE,EAAED,OAAO,CAAC,EAAE,GAAG;oBACvC,OAAOC,WAAW,gBAAgB;gBACpC;gBAEA,MAAMG,eAAejB,SAASa,OAAO,CAAC,EAAE;gBAExC,IAAIE,OAAOC,EAAE,CAACF,SAAS,CAAC,EAAE,EAAEG,eAAe;oBACzC,OAAOH,WAAW,gBAAgB;gBACpC;gBAEA,OAAO;oBAACD,OAAO,CAAC,EAAE;oBAAEI;iBAAa;YACnC,EAAE,OAAOC,GAAG;YACV,6CAA6C;YAC/C;YAEA,sCAAsC;YACtC,OAAO;gBAACJ,SAAS,CAAC,EAAE;gBAAEA,SAAS,CAAC,EAAE;aAAC,EAAW,kBAAkB;QAClE;IACF;IAEA,IAAI,CAACC,OAAOC,EAAE,CAACP,KAAK,CAAC,EAAE,EAAED,WAAW;QAClC,qBAAqB;QACrB,2CAA2C;QAC3CI,SAASO;IACX;IAEA,MAAMC,iBAAiBC,IAAAA,gCAAgB,EAACT;IAExCU,IAAAA,yCAAyB,EAAC;QACxBf,UAAUgB,IAAI,CAACH;QAEf,OAAO;YACL,MAAMI,QAAQjB,UAAUkB,OAAO,CAACL;YAChCb,UAAUmB,MAAM,CAACF,OAAO;QAC1B;IACF,GAAG;QAACJ;QAAgBb;KAAU;IAE9B,OAAOE,KAAK,CAAC,EAAE;AACjB"}

View File

@@ -0,0 +1,20 @@
'use client';
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "useHasParentContext", {
enumerable: true,
get: function() {
return useHasParentContext;
}
});
const _interop_require_wildcard = require("@swc/helpers/_/_interop_require_wildcard");
const _react = /*#__PURE__*/ _interop_require_wildcard._(require("react"));
function useHasParentContext(context) {
const contextValue = _react.useContext(context);
if (contextValue.version) {
return contextValue.version.current !== -1;
}
return false;
}

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/useHasParentContext.ts"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { Context, ContextValue } from './types';\n\n/**\n * @internal\n * Utility hook for contexts created by react-context-selector to determine if a parent context exists\n * WARNING: This hook will not work for native React contexts\n *\n * @param context - context created by react-context-selector\n * @returns whether the hook is wrapped by a parent context\n */\nexport function useHasParentContext<Value>(context: Context<Value>): boolean {\n const contextValue = React.useContext(context as unknown as Context<ContextValue<Value>>);\n\n if (contextValue.version) {\n return contextValue.version.current !== -1;\n }\n\n return false;\n}\n"],"names":["useHasParentContext","context","contextValue","React","useContext","version","current"],"mappings":"AAAA;;;;;+BAagBA;;;eAAAA;;;;iEAXO;AAWhB,SAASA,oBAA2BC,OAAuB;IAChE,MAAMC,eAAeC,OAAMC,UAAU,CAACH;IAEtC,IAAIC,aAAaG,OAAO,EAAE;QACxB,OAAOH,aAAaG,OAAO,CAACC,OAAO,KAAK,CAAC;IAC3C;IAEA,OAAO;AACT"}

View File

@@ -0,0 +1,60 @@
'use client';
import { useIsomorphicLayoutEffect } from '@fluentui/react-utilities';
import * as React from 'react';
import { unstable_NormalPriority as NormalPriority, unstable_runWithPriority as runWithPriority } from 'scheduler';
const createProvider = (Original)=>{
const Provider = (props)=>{
// Holds an actual "props.value"
const valueRef = React.useRef(props.value);
// Used to sync context updates and avoid stale values, can be considered as render/effect counter of Provider.
const versionRef = React.useRef(0);
// A stable object, is used to avoid context updates via mutation of its values.
const contextValue = React.useRef(null);
if (!contextValue.current) {
contextValue.current = {
value: valueRef,
version: versionRef,
listeners: []
};
}
useIsomorphicLayoutEffect(()=>{
valueRef.current = props.value;
versionRef.current += 1;
runWithPriority(NormalPriority, ()=>{
contextValue.current.listeners.forEach((listener)=>{
listener([
versionRef.current,
props.value
]);
});
});
}, [
props.value
]);
return React.createElement(Original, {
value: contextValue.current
}, props.children);
};
/* istanbul ignore else */ if (process.env.NODE_ENV !== 'production') {
Provider.displayName = 'ContextSelector.Provider';
}
return Provider;
};
/**
* @internal
*/ export const createContext = (defaultValue)=>{
// eslint-disable-next-line @fluentui/no-context-default-value
const context = React.createContext({
value: {
current: defaultValue
},
version: {
current: -1
},
listeners: []
});
context.Provider = createProvider(context.Provider);
// We don't support Consumer API
delete context.Consumer;
return context;
};

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/createContext.ts"],"sourcesContent":["'use client';\n\nimport { useIsomorphicLayoutEffect } from '@fluentui/react-utilities';\nimport * as React from 'react';\nimport { unstable_NormalPriority as NormalPriority, unstable_runWithPriority as runWithPriority } from 'scheduler';\n\nimport { Context, ContextValue } from './types';\n\nconst createProvider = <Value>(Original: React.Provider<ContextValue<Value>>) => {\n const Provider: React.FC<React.ProviderProps<Value>> = props => {\n // Holds an actual \"props.value\"\n const valueRef = React.useRef(props.value);\n // Used to sync context updates and avoid stale values, can be considered as render/effect counter of Provider.\n const versionRef = React.useRef(0);\n\n // A stable object, is used to avoid context updates via mutation of its values.\n const contextValue = React.useRef<ContextValue<Value>>(null);\n\n if (!contextValue.current) {\n contextValue.current = {\n value: valueRef,\n version: versionRef,\n listeners: [],\n };\n }\n\n useIsomorphicLayoutEffect(() => {\n valueRef.current = props.value;\n versionRef.current += 1;\n\n runWithPriority(NormalPriority, () => {\n (contextValue.current as ContextValue<Value>).listeners.forEach(listener => {\n listener([versionRef.current, props.value]);\n });\n });\n }, [props.value]);\n\n return React.createElement(Original, { value: contextValue.current }, props.children);\n };\n\n /* istanbul ignore else */\n if (process.env.NODE_ENV !== 'production') {\n Provider.displayName = 'ContextSelector.Provider';\n }\n\n return Provider as unknown as React.Provider<ContextValue<Value>>;\n};\n\n/**\n * @internal\n */\nexport const createContext = <Value>(defaultValue: Value): Context<Value> => {\n // eslint-disable-next-line @fluentui/no-context-default-value\n const context = React.createContext<ContextValue<Value>>({\n value: { current: defaultValue },\n version: { current: -1 },\n listeners: [],\n });\n\n context.Provider = createProvider<Value>(context.Provider);\n\n // We don't support Consumer API\n delete (context as unknown as Context<Value>).Consumer;\n\n return context as unknown as Context<Value>;\n};\n"],"names":["useIsomorphicLayoutEffect","React","unstable_NormalPriority","NormalPriority","unstable_runWithPriority","runWithPriority","createProvider","Original","Provider","props","valueRef","useRef","value","versionRef","contextValue","current","version","listeners","forEach","listener","createElement","children","process","env","NODE_ENV","displayName","createContext","defaultValue","context","Consumer"],"mappings":"AAAA;AAEA,SAASA,yBAAyB,QAAQ,4BAA4B;AACtE,YAAYC,WAAW,QAAQ;AAC/B,SAASC,2BAA2BC,cAAc,EAAEC,4BAA4BC,eAAe,QAAQ,YAAY;AAInH,MAAMC,iBAAiB,CAAQC;IAC7B,MAAMC,WAAiDC,CAAAA;QACrD,gCAAgC;QAChC,MAAMC,WAAWT,MAAMU,MAAM,CAACF,MAAMG,KAAK;QACzC,+GAA+G;QAC/G,MAAMC,aAAaZ,MAAMU,MAAM,CAAC;QAEhC,gFAAgF;QAChF,MAAMG,eAAeb,MAAMU,MAAM,CAAsB;QAEvD,IAAI,CAACG,aAAaC,OAAO,EAAE;YACzBD,aAAaC,OAAO,GAAG;gBACrBH,OAAOF;gBACPM,SAASH;gBACTI,WAAW,EAAE;YACf;QACF;QAEAjB,0BAA0B;YACxBU,SAASK,OAAO,GAAGN,MAAMG,KAAK;YAC9BC,WAAWE,OAAO,IAAI;YAEtBV,gBAAgBF,gBAAgB;gBAC7BW,aAAaC,OAAO,CAAyBE,SAAS,CAACC,OAAO,CAACC,CAAAA;oBAC9DA,SAAS;wBAACN,WAAWE,OAAO;wBAAEN,MAAMG,KAAK;qBAAC;gBAC5C;YACF;QACF,GAAG;YAACH,MAAMG,KAAK;SAAC;QAEhB,OAAOX,MAAMmB,aAAa,CAACb,UAAU;YAAEK,OAAOE,aAAaC,OAAO;QAAC,GAAGN,MAAMY,QAAQ;IACtF;IAEA,wBAAwB,GACxB,IAAIC,QAAQC,GAAG,CAACC,QAAQ,KAAK,cAAc;QACzChB,SAASiB,WAAW,GAAG;IACzB;IAEA,OAAOjB;AACT;AAEA;;CAEC,GACD,OAAO,MAAMkB,gBAAgB,CAAQC;IACnC,8DAA8D;IAC9D,MAAMC,UAAU3B,MAAMyB,aAAa,CAAsB;QACvDd,OAAO;YAAEG,SAASY;QAAa;QAC/BX,SAAS;YAAED,SAAS,CAAC;QAAE;QACvBE,WAAW,EAAE;IACf;IAEAW,QAAQpB,QAAQ,GAAGF,eAAsBsB,QAAQpB,QAAQ;IAEzD,gCAAgC;IAChC,OAAO,AAACoB,QAAsCC,QAAQ;IAEtD,OAAOD;AACT,EAAE"}

View File

@@ -0,0 +1,3 @@
export { createContext } from './createContext';
export { useContextSelector } from './useContextSelector';
export { useHasParentContext } from './useHasParentContext';

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export { createContext } from './createContext';\nexport { useContextSelector } from './useContextSelector';\nexport { useHasParentContext } from './useHasParentContext';\n// eslint-disable-next-line @fluentui/ban-context-export\nexport type { Context, ContextSelector, ContextValue, ContextValues, ContextVersion } from './types';\n"],"names":["createContext","useContextSelector","useHasParentContext"],"mappings":"AAAA,SAASA,aAAa,QAAQ,kBAAkB;AAChD,SAASC,kBAAkB,QAAQ,uBAAuB;AAC1D,SAASC,mBAAmB,QAAQ,wBAAwB"}

View File

@@ -0,0 +1 @@
import * as React from 'react';

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["import * as React from 'react';\n\n/**\n * @internal\n */\nexport type Context<Value> = React.Context<Value> & {\n Provider: React.FC<React.ProviderProps<Value>>;\n Consumer: never;\n};\n\nexport type ContextSelector<Value, SelectedValue> = (value: Value) => SelectedValue;\n\n/**\n * @internal\n */\nexport type ContextVersion = number;\n\n/**\n * @internal\n */\nexport type ContextValue<Value> = {\n /** Holds a set of subscribers from components. */\n listeners: ((payload: readonly [ContextVersion, Value]) => void)[];\n\n /** Holds an actual value of React's context that will be propagated down for computations. */\n value: // eslint-disable-next-line @typescript-eslint/no-deprecated\n React.MutableRefObject<Value>;\n\n /** A version field is used to sync a context value and consumers. */\n version: // eslint-disable-next-line @typescript-eslint/no-deprecated\n React.MutableRefObject<ContextVersion>;\n};\n\n/**\n * @internal\n */\nexport type ContextValues<Value> = ContextValue<Value> & {\n /** List of listners to publish changes */\n listeners: ((payload: readonly [ContextVersion, Record<string, Value>]) => void)[];\n};\n"],"names":["React"],"mappings":"AAAA,YAAYA,WAAW,QAAQ"}

View File

@@ -0,0 +1,74 @@
'use client';
import { useEventCallback, useIsomorphicLayoutEffect } from '@fluentui/react-utilities';
import * as React from 'react';
/**
* @internal
* This hook returns context selected value by selector.
* It will only accept context created by `createContext`.
* It will trigger re-render if only the selected value is referentially changed.
*/ export const useContextSelector = (context, selector)=>{
const contextValue = React.useContext(context);
const { value: { current: value }, version: { current: version }, listeners } = contextValue;
const selected = selector(value);
const [state, setState] = React.useState([
value,
selected
]);
const dispatch = (payload)=>{
setState((prevState)=>{
if (!payload) {
// early bail out when is dispatched during render
return [
value,
selected
];
}
if (payload[0] <= version) {
if (Object.is(prevState[1], selected)) {
return prevState; // bail out
}
return [
value,
selected
];
}
try {
if (Object.is(prevState[0], payload[1])) {
return prevState; // do not update
}
const nextSelected = selector(payload[1]);
if (Object.is(prevState[1], nextSelected)) {
return prevState; // do not update
}
return [
payload[1],
nextSelected
];
} catch (e) {
// ignored (stale props or some other reason)
}
// explicitly spread to enforce typing
return [
prevState[0],
prevState[1]
]; // schedule update
});
};
if (!Object.is(state[1], selected)) {
// schedule re-render
// this is safe because it's self contained
dispatch(undefined);
}
const stableDispatch = useEventCallback(dispatch);
useIsomorphicLayoutEffect(()=>{
listeners.push(stableDispatch);
return ()=>{
const index = listeners.indexOf(stableDispatch);
listeners.splice(index, 1);
};
}, [
stableDispatch,
listeners
]);
return state[1];
};

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/useContextSelector.ts"],"sourcesContent":["'use client';\n\nimport { useEventCallback, useIsomorphicLayoutEffect } from '@fluentui/react-utilities';\nimport * as React from 'react';\n\nimport { Context, ContextSelector, ContextValue, ContextVersion } from './types';\n\n/**\n * @internal\n * This hook returns context selected value by selector.\n * It will only accept context created by `createContext`.\n * It will trigger re-render if only the selected value is referentially changed.\n */\nexport const useContextSelector = <Value, SelectedValue>(\n context: Context<Value>,\n selector: ContextSelector<Value, SelectedValue>,\n): SelectedValue => {\n const contextValue = React.useContext(context as unknown as Context<ContextValue<Value>>);\n\n const {\n value: { current: value },\n version: { current: version },\n listeners,\n } = contextValue;\n const selected = selector(value);\n\n const [state, setState] = React.useState<readonly [Value, SelectedValue]>([value, selected]);\n const dispatch = (\n payload:\n | undefined // undefined from render below\n | readonly [ContextVersion, Value], // from provider effect\n ) => {\n setState(prevState => {\n if (!payload) {\n // early bail out when is dispatched during render\n return [value, selected] as const;\n }\n\n if (payload[0] <= version) {\n if (Object.is(prevState[1], selected)) {\n return prevState; // bail out\n }\n\n return [value, selected] as const;\n }\n\n try {\n if (Object.is(prevState[0], payload[1])) {\n return prevState; // do not update\n }\n\n const nextSelected = selector(payload[1]);\n\n if (Object.is(prevState[1], nextSelected)) {\n return prevState; // do not update\n }\n\n return [payload[1], nextSelected] as const;\n } catch (e) {\n // ignored (stale props or some other reason)\n }\n\n // explicitly spread to enforce typing\n return [prevState[0], prevState[1]] as const; // schedule update\n });\n };\n\n if (!Object.is(state[1], selected)) {\n // schedule re-render\n // this is safe because it's self contained\n dispatch(undefined);\n }\n\n const stableDispatch = useEventCallback(dispatch);\n\n useIsomorphicLayoutEffect(() => {\n listeners.push(stableDispatch);\n\n return () => {\n const index = listeners.indexOf(stableDispatch);\n listeners.splice(index, 1);\n };\n }, [stableDispatch, listeners]);\n\n return state[1] as SelectedValue;\n};\n"],"names":["useEventCallback","useIsomorphicLayoutEffect","React","useContextSelector","context","selector","contextValue","useContext","value","current","version","listeners","selected","state","setState","useState","dispatch","payload","prevState","Object","is","nextSelected","e","undefined","stableDispatch","push","index","indexOf","splice"],"mappings":"AAAA;AAEA,SAASA,gBAAgB,EAAEC,yBAAyB,QAAQ,4BAA4B;AACxF,YAAYC,WAAW,QAAQ;AAI/B;;;;;CAKC,GACD,OAAO,MAAMC,qBAAqB,CAChCC,SACAC;IAEA,MAAMC,eAAeJ,MAAMK,UAAU,CAACH;IAEtC,MAAM,EACJI,OAAO,EAAEC,SAASD,KAAK,EAAE,EACzBE,SAAS,EAAED,SAASC,OAAO,EAAE,EAC7BC,SAAS,EACV,GAAGL;IACJ,MAAMM,WAAWP,SAASG;IAE1B,MAAM,CAACK,OAAOC,SAAS,GAAGZ,MAAMa,QAAQ,CAAkC;QAACP;QAAOI;KAAS;IAC3F,MAAMI,WAAW,CACfC;QAIAH,SAASI,CAAAA;YACP,IAAI,CAACD,SAAS;gBACZ,kDAAkD;gBAClD,OAAO;oBAACT;oBAAOI;iBAAS;YAC1B;YAEA,IAAIK,OAAO,CAAC,EAAE,IAAIP,SAAS;gBACzB,IAAIS,OAAOC,EAAE,CAACF,SAAS,CAAC,EAAE,EAAEN,WAAW;oBACrC,OAAOM,WAAW,WAAW;gBAC/B;gBAEA,OAAO;oBAACV;oBAAOI;iBAAS;YAC1B;YAEA,IAAI;gBACF,IAAIO,OAAOC,EAAE,CAACF,SAAS,CAAC,EAAE,EAAED,OAAO,CAAC,EAAE,GAAG;oBACvC,OAAOC,WAAW,gBAAgB;gBACpC;gBAEA,MAAMG,eAAehB,SAASY,OAAO,CAAC,EAAE;gBAExC,IAAIE,OAAOC,EAAE,CAACF,SAAS,CAAC,EAAE,EAAEG,eAAe;oBACzC,OAAOH,WAAW,gBAAgB;gBACpC;gBAEA,OAAO;oBAACD,OAAO,CAAC,EAAE;oBAAEI;iBAAa;YACnC,EAAE,OAAOC,GAAG;YACV,6CAA6C;YAC/C;YAEA,sCAAsC;YACtC,OAAO;gBAACJ,SAAS,CAAC,EAAE;gBAAEA,SAAS,CAAC,EAAE;aAAC,EAAW,kBAAkB;QAClE;IACF;IAEA,IAAI,CAACC,OAAOC,EAAE,CAACP,KAAK,CAAC,EAAE,EAAED,WAAW;QAClC,qBAAqB;QACrB,2CAA2C;QAC3CI,SAASO;IACX;IAEA,MAAMC,iBAAiBxB,iBAAiBgB;IAExCf,0BAA0B;QACxBU,UAAUc,IAAI,CAACD;QAEf,OAAO;YACL,MAAME,QAAQf,UAAUgB,OAAO,CAACH;YAChCb,UAAUiB,MAAM,CAACF,OAAO;QAC1B;IACF,GAAG;QAACF;QAAgBb;KAAU;IAE9B,OAAOE,KAAK,CAAC,EAAE;AACjB,EAAE"}

View File

@@ -0,0 +1,16 @@
'use client';
import * as React from 'react';
/**
* @internal
* Utility hook for contexts created by react-context-selector to determine if a parent context exists
* WARNING: This hook will not work for native React contexts
*
* @param context - context created by react-context-selector
* @returns whether the hook is wrapped by a parent context
*/ export function useHasParentContext(context) {
const contextValue = React.useContext(context);
if (contextValue.version) {
return contextValue.version.current !== -1;
}
return false;
}

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/useHasParentContext.ts"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { Context, ContextValue } from './types';\n\n/**\n * @internal\n * Utility hook for contexts created by react-context-selector to determine if a parent context exists\n * WARNING: This hook will not work for native React contexts\n *\n * @param context - context created by react-context-selector\n * @returns whether the hook is wrapped by a parent context\n */\nexport function useHasParentContext<Value>(context: Context<Value>): boolean {\n const contextValue = React.useContext(context as unknown as Context<ContextValue<Value>>);\n\n if (contextValue.version) {\n return contextValue.version.current !== -1;\n }\n\n return false;\n}\n"],"names":["React","useHasParentContext","context","contextValue","useContext","version","current"],"mappings":"AAAA;AAEA,YAAYA,WAAW,QAAQ;AAG/B;;;;;;;CAOC,GACD,OAAO,SAASC,oBAA2BC,OAAuB;IAChE,MAAMC,eAAeH,MAAMI,UAAU,CAACF;IAEtC,IAAIC,aAAaE,OAAO,EAAE;QACxB,OAAOF,aAAaE,OAAO,CAACC,OAAO,KAAK,CAAC;IAC3C;IAEA,OAAO;AACT"}

View File

@@ -0,0 +1,46 @@
{
"name": "@fluentui/react-context-selector",
"version": "9.2.15",
"description": "React useContextSelector hook in userland",
"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",
"dependencies": {
"@fluentui/react-utilities": "^9.26.2",
"@swc/helpers": "^0.5.1"
},
"peerDependencies": {
"@types/react": ">=16.14.0 <20.0.0",
"@types/react-dom": ">=16.9.0 <20.0.0",
"react": ">=16.14.0 <20.0.0",
"react-dom": ">=16.14.0 <20.0.0",
"scheduler": ">=0.19.0"
},
"beachball": {
"disallowedChangeTypes": [
"major",
"prerelease"
]
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"node": "./lib-commonjs/index.js",
"import": "./lib/index.js",
"require": "./lib-commonjs/index.js"
},
"./package.json": "./package.json"
},
"files": [
"*.md",
"dist/*.d.ts",
"lib",
"lib-commonjs"
]
}