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,20 @@
import * as React from 'react';
/**
* Given `children`, return an object mapping key to child.
*/ export function getChildMapping(children) {
const childMapping = {};
if (children) {
React.Children.toArray(children).forEach((child)=>{
if (React.isValidElement(child)) {
var _child_key;
childMapping[(_child_key = child.key) !== null && _child_key !== void 0 ? _child_key : ''] = {
appear: false,
element: child,
visible: true,
unmountOnExit: true
};
}
});
}
return childMapping;
}

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/utils/groups/getChildMapping.ts"],"sourcesContent":["import * as React from 'react';\nimport type { PresenceGroupChildMapping } from './types';\n\n/**\n * Given `children`, return an object mapping key to child.\n */\nexport function getChildMapping(children: React.ReactNode | undefined): PresenceGroupChildMapping {\n const childMapping: PresenceGroupChildMapping = {};\n\n if (children) {\n React.Children.toArray(children).forEach(child => {\n if (React.isValidElement(child)) {\n childMapping[child.key ?? ''] = {\n appear: false,\n element: child,\n visible: true,\n unmountOnExit: true,\n };\n }\n });\n }\n\n return childMapping;\n}\n"],"names":["React","getChildMapping","children","childMapping","Children","toArray","forEach","child","isValidElement","key","appear","element","visible","unmountOnExit"],"mappings":"AAAA,YAAYA,WAAW,QAAQ;AAG/B;;CAEC,GACD,OAAO,SAASC,gBAAgBC,QAAqC;IACnE,MAAMC,eAA0C,CAAC;IAEjD,IAAID,UAAU;QACZF,MAAMI,QAAQ,CAACC,OAAO,CAACH,UAAUI,OAAO,CAACC,CAAAA;YACvC,IAAIP,MAAMQ,cAAc,CAACD,QAAQ;oBAClBA;gBAAbJ,YAAY,CAACI,CAAAA,aAAAA,MAAME,GAAG,cAATF,wBAAAA,aAAa,GAAG,GAAG;oBAC9BG,QAAQ;oBACRC,SAASJ;oBACTK,SAAS;oBACTC,eAAe;gBACjB;YACF;QACF;IACF;IAEA,OAAOV;AACT"}

View File

@@ -0,0 +1,30 @@
import { mergeChildMappings } from './mergeChildMappings';
export function getNextChildMapping(prevChildMapping, nextChildMapping) {
const childrenMapping = mergeChildMappings(prevChildMapping, nextChildMapping);
Object.entries(childrenMapping).forEach(([key, childDefinition])=>{
const hasPrev = key in prevChildMapping;
const hasNext = key in nextChildMapping;
if (hasNext) {
// Case 1: item hasn't changed transition states
if (hasPrev) {
childrenMapping[key] = {
...childDefinition
};
return;
}
// Case 2: item is new (entering)
childrenMapping[key] = {
...childDefinition,
appear: true,
visible: true
};
return;
}
// Case 3: item is leaving
childrenMapping[key] = {
...childDefinition,
visible: false
};
});
return childrenMapping;
}

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/utils/groups/getNextChildMapping.ts"],"sourcesContent":["import { mergeChildMappings } from './mergeChildMappings';\nimport type { PresenceGroupChildMapping } from './types';\n\nexport function getNextChildMapping(\n prevChildMapping: PresenceGroupChildMapping,\n nextChildMapping: PresenceGroupChildMapping,\n): PresenceGroupChildMapping {\n const childrenMapping = mergeChildMappings(prevChildMapping, nextChildMapping);\n\n Object.entries(childrenMapping).forEach(([key, childDefinition]) => {\n const hasPrev = key in prevChildMapping;\n const hasNext = key in nextChildMapping;\n\n if (hasNext) {\n // Case 1: item hasn't changed transition states\n if (hasPrev) {\n childrenMapping[key] = { ...childDefinition };\n return;\n }\n\n // Case 2: item is new (entering)\n childrenMapping[key] = {\n ...childDefinition,\n appear: true,\n visible: true,\n };\n return;\n }\n\n // Case 3: item is leaving\n childrenMapping[key] = {\n ...childDefinition,\n visible: false,\n };\n });\n\n return childrenMapping;\n}\n"],"names":["mergeChildMappings","getNextChildMapping","prevChildMapping","nextChildMapping","childrenMapping","Object","entries","forEach","key","childDefinition","hasPrev","hasNext","appear","visible"],"mappings":"AAAA,SAASA,kBAAkB,QAAQ,uBAAuB;AAG1D,OAAO,SAASC,oBACdC,gBAA2C,EAC3CC,gBAA2C;IAE3C,MAAMC,kBAAkBJ,mBAAmBE,kBAAkBC;IAE7DE,OAAOC,OAAO,CAACF,iBAAiBG,OAAO,CAAC,CAAC,CAACC,KAAKC,gBAAgB;QAC7D,MAAMC,UAAUF,OAAON;QACvB,MAAMS,UAAUH,OAAOL;QAEvB,IAAIQ,SAAS;YACX,gDAAgD;YAChD,IAAID,SAAS;gBACXN,eAAe,CAACI,IAAI,GAAG;oBAAE,GAAGC,eAAe;gBAAC;gBAC5C;YACF;YAEA,iCAAiC;YACjCL,eAAe,CAACI,IAAI,GAAG;gBACrB,GAAGC,eAAe;gBAClBG,QAAQ;gBACRC,SAAS;YACX;YACA;QACF;QAEA,0BAA0B;QAC1BT,eAAe,CAACI,IAAI,GAAG;YACrB,GAAGC,eAAe;YAClBI,SAAS;QACX;IACF;IAEA,OAAOT;AACT"}

View File

@@ -0,0 +1,39 @@
/**
* When you're adding or removing children some may be added or removed in the same render pass. We want to show *both*
* since we want to simultaneously animate elements in and out. This function takes a previous set of keys and a new set
* of keys and merges them with its best guess of the correct ordering.
*/ export function mergeChildMappings(prevMapping, nextMapping) {
function getValueForKey(key) {
return key in nextMapping ? nextMapping[key] : prevMapping[key];
}
// For each key of `next`, the list of keys to insert before that key in
// the combined list
const nextKeysPending = {};
let pendingKeys = [];
// eslint-disable-next-line guard-for-in
for(const prevKey in prevMapping){
if (prevKey in nextMapping) {
if (pendingKeys.length) {
nextKeysPending[prevKey] = pendingKeys;
pendingKeys = [];
}
continue;
}
pendingKeys.push(prevKey);
}
const childMapping = {};
// eslint-disable-next-line guard-for-in
for(const nextKey in nextMapping){
if (nextKeysPending[nextKey]) {
for (const pendingNextKey of nextKeysPending[nextKey]){
childMapping[pendingNextKey] = getValueForKey(pendingNextKey);
}
}
childMapping[nextKey] = getValueForKey(nextKey);
}
// Finally, add the keys which didn't appear before any key in `next`
for (const pendingKey of pendingKeys){
childMapping[pendingKey] = getValueForKey(pendingKey);
}
return childMapping;
}

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/utils/groups/mergeChildMappings.ts"],"sourcesContent":["import type { PresenceGroupChildMapping } from './types';\n\n/**\n * When you're adding or removing children some may be added or removed in the same render pass. We want to show *both*\n * since we want to simultaneously animate elements in and out. This function takes a previous set of keys and a new set\n * of keys and merges them with its best guess of the correct ordering.\n */\nexport function mergeChildMappings(\n prevMapping: PresenceGroupChildMapping,\n nextMapping: PresenceGroupChildMapping,\n): PresenceGroupChildMapping {\n function getValueForKey(key: string) {\n return key in nextMapping ? nextMapping[key] : prevMapping[key];\n }\n\n // For each key of `next`, the list of keys to insert before that key in\n // the combined list\n const nextKeysPending: Record<string, string[]> = {};\n let pendingKeys: string[] = [];\n\n // eslint-disable-next-line guard-for-in\n for (const prevKey in prevMapping) {\n if (prevKey in nextMapping) {\n if (pendingKeys.length) {\n nextKeysPending[prevKey] = pendingKeys;\n pendingKeys = [];\n }\n\n continue;\n }\n\n pendingKeys.push(prevKey);\n }\n\n const childMapping: PresenceGroupChildMapping = {};\n\n // eslint-disable-next-line guard-for-in\n for (const nextKey in nextMapping) {\n if (nextKeysPending[nextKey]) {\n for (const pendingNextKey of nextKeysPending[nextKey]) {\n childMapping[pendingNextKey] = getValueForKey(pendingNextKey);\n }\n }\n\n childMapping[nextKey] = getValueForKey(nextKey);\n }\n\n // Finally, add the keys which didn't appear before any key in `next`\n for (const pendingKey of pendingKeys) {\n childMapping[pendingKey] = getValueForKey(pendingKey);\n }\n\n return childMapping;\n}\n"],"names":["mergeChildMappings","prevMapping","nextMapping","getValueForKey","key","nextKeysPending","pendingKeys","prevKey","length","push","childMapping","nextKey","pendingNextKey","pendingKey"],"mappings":"AAEA;;;;CAIC,GACD,OAAO,SAASA,mBACdC,WAAsC,EACtCC,WAAsC;IAEtC,SAASC,eAAeC,GAAW;QACjC,OAAOA,OAAOF,cAAcA,WAAW,CAACE,IAAI,GAAGH,WAAW,CAACG,IAAI;IACjE;IAEA,wEAAwE;IACxE,oBAAoB;IACpB,MAAMC,kBAA4C,CAAC;IACnD,IAAIC,cAAwB,EAAE;IAE9B,wCAAwC;IACxC,IAAK,MAAMC,WAAWN,YAAa;QACjC,IAAIM,WAAWL,aAAa;YAC1B,IAAII,YAAYE,MAAM,EAAE;gBACtBH,eAAe,CAACE,QAAQ,GAAGD;gBAC3BA,cAAc,EAAE;YAClB;YAEA;QACF;QAEAA,YAAYG,IAAI,CAACF;IACnB;IAEA,MAAMG,eAA0C,CAAC;IAEjD,wCAAwC;IACxC,IAAK,MAAMC,WAAWT,YAAa;QACjC,IAAIG,eAAe,CAACM,QAAQ,EAAE;YAC5B,KAAK,MAAMC,kBAAkBP,eAAe,CAACM,QAAQ,CAAE;gBACrDD,YAAY,CAACE,eAAe,GAAGT,eAAeS;YAChD;QACF;QAEAF,YAAY,CAACC,QAAQ,GAAGR,eAAeQ;IACzC;IAEA,qEAAqE;IACrE,KAAK,MAAME,cAAcP,YAAa;QACpCI,YAAY,CAACG,WAAW,GAAGV,eAAeU;IAC5C;IAEA,OAAOH;AACT"}

View File

@@ -0,0 +1 @@
export { };

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/utils/groups/types.ts"],"sourcesContent":["import type { JSXElement } from '@fluentui/react-utilities';\n\nexport type PresenceGroupChild = {\n element: JSXElement;\n\n appear: boolean;\n visible: boolean;\n unmountOnExit: boolean;\n};\n\nexport type PresenceGroupChildMapping = Record<string, PresenceGroupChild>;\n"],"names":[],"mappings":"AAUA,WAA2E"}

View File

@@ -0,0 +1,25 @@
/**
* Checks if the animation is running at the moment.
*/ export function isAnimationRunning(animation) {
if (animation.playState === 'running') {
var _animation_effect;
// Heads up!
//
// There is an edge case where the animation is running, but the overall progress is 0 or 1. In this case, we
// consider the animation to be not running. If it will be reversed it will flip from 1 to 0, and we will observe a
// glitch.
// "overallProgress" is not supported in all browsers, so we need to check if it exists.
// We will fall back to the currentTime and duration if "overallProgress" is not supported.
if (animation.overallProgress !== undefined) {
var _animation_overallProgress;
const overallProgress = (_animation_overallProgress = animation.overallProgress) !== null && _animation_overallProgress !== void 0 ? _animation_overallProgress : 0;
return overallProgress > 0 && overallProgress < 1;
}
var _animation_currentTime;
const currentTime = Number((_animation_currentTime = animation.currentTime) !== null && _animation_currentTime !== void 0 ? _animation_currentTime : 0);
var _animation_effect_getTiming_duration;
const totalTime = Number((_animation_effect_getTiming_duration = (_animation_effect = animation.effect) === null || _animation_effect === void 0 ? void 0 : _animation_effect.getTiming().duration) !== null && _animation_effect_getTiming_duration !== void 0 ? _animation_effect_getTiming_duration : 0);
return currentTime > 0 && currentTime < totalTime;
}
return false;
}

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/utils/isAnimationRunning.ts"],"sourcesContent":["/**\n * Checks if the animation is running at the moment.\n */\nexport function isAnimationRunning(animation: Animation & { readonly overallProgress?: number | null }): boolean {\n if (animation.playState === 'running') {\n // Heads up!\n //\n // There is an edge case where the animation is running, but the overall progress is 0 or 1. In this case, we\n // consider the animation to be not running. If it will be reversed it will flip from 1 to 0, and we will observe a\n // glitch.\n\n // \"overallProgress\" is not supported in all browsers, so we need to check if it exists.\n // We will fall back to the currentTime and duration if \"overallProgress\" is not supported.\n if (animation.overallProgress !== undefined) {\n const overallProgress = animation.overallProgress ?? 0;\n\n return overallProgress > 0 && overallProgress < 1;\n }\n\n const currentTime = Number(animation.currentTime ?? 0);\n const totalTime = Number(animation.effect?.getTiming().duration ?? 0);\n\n return currentTime > 0 && currentTime < totalTime;\n }\n\n return false;\n}\n"],"names":["isAnimationRunning","animation","playState","overallProgress","undefined","currentTime","Number","totalTime","effect","getTiming","duration"],"mappings":"AAAA;;CAEC,GACD,OAAO,SAASA,mBAAmBC,SAAmE;IACpG,IAAIA,UAAUC,SAAS,KAAK,WAAW;YAgBZD;QAfzB,YAAY;QACZ,EAAE;QACF,6GAA6G;QAC7G,mHAAmH;QACnH,UAAU;QAEV,wFAAwF;QACxF,2FAA2F;QAC3F,IAAIA,UAAUE,eAAe,KAAKC,WAAW;gBACnBH;YAAxB,MAAME,kBAAkBF,CAAAA,6BAAAA,UAAUE,eAAe,cAAzBF,wCAAAA,6BAA6B;YAErD,OAAOE,kBAAkB,KAAKA,kBAAkB;QAClD;YAE2BF;QAA3B,MAAMI,cAAcC,OAAOL,CAAAA,yBAAAA,UAAUI,WAAW,cAArBJ,oCAAAA,yBAAyB;YAC3BA;QAAzB,MAAMM,YAAYD,OAAOL,CAAAA,wCAAAA,oBAAAA,UAAUO,MAAM,cAAhBP,wCAAAA,kBAAkBQ,SAAS,GAAGC,QAAQ,cAAtCT,kDAAAA,uCAA0C;QAEnE,OAAOI,cAAc,KAAKA,cAAcE;IAC1C;IAEA,OAAO;AACT"}

View File

@@ -0,0 +1,40 @@
'use client';
import * as React from 'react';
import { getReactElementRef, useMergedRefs } from '@fluentui/react-utilities';
const CHILD_ERROR_MESSAGE = [
'@fluentui/react-motion: Invalid child element.',
'\n',
'Motion factories require a single child element to be passed. ',
'That element element should support ref forwarding i.e. it should be either an intrinsic element (e.g. div) or a component that uses React.forwardRef().'
].join('');
/**
* Validates the child and returns a cloned child element with a ref.
*
* Throws an error if the child is not a valid React element, similar to "React.Children.only".
* Logs a warning in development mode if the ref is not set as the component remains functional.
*/ export function useChildElement(children, mounted = true) {
const childRef = React.useRef(null);
React.useEffect(()=>{
if (process.env.NODE_ENV !== 'production') {
if (mounted && !childRef.current) {
// eslint-disable-next-line no-console
console.error(CHILD_ERROR_MESSAGE);
}
}
}, [
mounted
]);
try {
const child = React.Children.only(children);
if (React.isValidElement(child)) {
return [
React.cloneElement(child, {
ref: useMergedRefs(childRef, getReactElementRef(child))
}),
childRef
];
}
} catch {
/* empty */ }
throw new Error(CHILD_ERROR_MESSAGE);
}

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/utils/useChildElement.ts"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { getReactElementRef, useMergedRefs } from '@fluentui/react-utilities';\nimport type { JSXElement } from '@fluentui/react-utilities';\n\nconst CHILD_ERROR_MESSAGE = [\n '@fluentui/react-motion: Invalid child element.',\n '\\n',\n 'Motion factories require a single child element to be passed. ',\n 'That element element should support ref forwarding i.e. it should be either an intrinsic element (e.g. div) or a component that uses React.forwardRef().',\n].join('');\n\n/**\n * Validates the child and returns a cloned child element with a ref.\n *\n * Throws an error if the child is not a valid React element, similar to \"React.Children.only\".\n * Logs a warning in development mode if the ref is not set as the component remains functional.\n */\nexport function useChildElement(\n children: JSXElement,\n mounted: boolean = true,\n): [JSXElement, React.RefObject<HTMLElement | null>] {\n const childRef = React.useRef<HTMLElement>(null);\n\n React.useEffect(() => {\n if (process.env.NODE_ENV !== 'production') {\n if (mounted && !childRef.current) {\n // eslint-disable-next-line no-console\n console.error(CHILD_ERROR_MESSAGE);\n }\n }\n }, [mounted]);\n\n try {\n const child = React.Children.only(children) as Parameters<typeof React.isValidElement>[0];\n\n if (React.isValidElement(child)) {\n return [\n React.cloneElement(child as React.ReactElement<{ ref: React.Ref<HTMLElement> }>, {\n ref: useMergedRefs(childRef, getReactElementRef(child)),\n }),\n childRef,\n ];\n }\n } catch {\n /* empty */\n }\n\n throw new Error(CHILD_ERROR_MESSAGE);\n}\n"],"names":["React","getReactElementRef","useMergedRefs","CHILD_ERROR_MESSAGE","join","useChildElement","children","mounted","childRef","useRef","useEffect","process","env","NODE_ENV","current","console","error","child","Children","only","isValidElement","cloneElement","ref","Error"],"mappings":"AAAA;AAEA,YAAYA,WAAW,QAAQ;AAC/B,SAASC,kBAAkB,EAAEC,aAAa,QAAQ,4BAA4B;AAG9E,MAAMC,sBAAsB;IAC1B;IACA;IACA;IACA;CACD,CAACC,IAAI,CAAC;AAEP;;;;;CAKC,GACD,OAAO,SAASC,gBACdC,QAAoB,EACpBC,UAAmB,IAAI;IAEvB,MAAMC,WAAWR,MAAMS,MAAM,CAAc;IAE3CT,MAAMU,SAAS,CAAC;QACd,IAAIC,QAAQC,GAAG,CAACC,QAAQ,KAAK,cAAc;YACzC,IAAIN,WAAW,CAACC,SAASM,OAAO,EAAE;gBAChC,sCAAsC;gBACtCC,QAAQC,KAAK,CAACb;YAChB;QACF;IACF,GAAG;QAACI;KAAQ;IAEZ,IAAI;QACF,MAAMU,QAAQjB,MAAMkB,QAAQ,CAACC,IAAI,CAACb;QAElC,IAAIN,MAAMoB,cAAc,CAACH,QAAQ;YAC/B,OAAO;gBACLjB,MAAMqB,YAAY,CAACJ,OAA8D;oBAC/EK,KAAKpB,cAAcM,UAAUP,mBAAmBgB;gBAClD;gBACAT;aACD;QACH;IACF,EAAE,OAAM;IACN,SAAS,GACX;IAEA,MAAM,IAAIe,MAAMpB;AAClB"}