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,17 @@
'use client';
import * as React from 'react';
import { useDialog_unstable } from './useDialog';
import { renderDialog_unstable } from './renderDialog';
import { useDialogContextValues_unstable } from './useDialogContextValues';
/**
* The `Dialog` root level component serves as an interface for interaction with all possible behaviors exposed.
* It provides context down the hierarchy to `children` compound components to allow functionality.
* This component expects to receive as children either a `DialogSurface` or a `DialogTrigger`
* and a `DialogSurface` (or some component that will eventually render one of those compound components)
* in this specific order
*/ export const Dialog = /*#__PURE__*/ React.memo((props)=>{
const state = useDialog_unstable(props);
const contextValues = useDialogContextValues_unstable(state);
return renderDialog_unstable(state, contextValues);
});
Dialog.displayName = 'Dialog';

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/Dialog/Dialog.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { useDialog_unstable } from './useDialog';\nimport { renderDialog_unstable } from './renderDialog';\nimport type { DialogProps } from './Dialog.types';\nimport { useDialogContextValues_unstable } from './useDialogContextValues';\n\n/**\n * The `Dialog` root level component serves as an interface for interaction with all possible behaviors exposed.\n * It provides context down the hierarchy to `children` compound components to allow functionality.\n * This component expects to receive as children either a `DialogSurface` or a `DialogTrigger`\n * and a `DialogSurface` (or some component that will eventually render one of those compound components)\n * in this specific order\n */\nexport const Dialog: React.FC<DialogProps> = React.memo(props => {\n const state = useDialog_unstable(props);\n const contextValues = useDialogContextValues_unstable(state);\n\n return renderDialog_unstable(state, contextValues);\n});\n\nDialog.displayName = 'Dialog';\n"],"names":["React","useDialog_unstable","renderDialog_unstable","useDialogContextValues_unstable","Dialog","memo","props","state","contextValues","displayName"],"mappings":"AAAA;AAEA,YAAYA,WAAW,QAAQ;AAC/B,SAASC,kBAAkB,QAAQ,cAAc;AACjD,SAASC,qBAAqB,QAAQ,iBAAiB;AAEvD,SAASC,+BAA+B,QAAQ,2BAA2B;AAE3E;;;;;;CAMC,GACD,OAAO,MAAMC,uBAAgCJ,MAAMK,IAAI,CAACC,CAAAA;IACtD,MAAMC,QAAQN,mBAAmBK;IACjC,MAAME,gBAAgBL,gCAAgCI;IAEtD,OAAOL,sBAAsBK,OAAOC;AACtC,GAAG;AAEHJ,OAAOK,WAAW,GAAG"}

View File

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

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/Dialog/Dialog.types.ts"],"sourcesContent":["import type * as React from 'react';\nimport type { PresenceMotionSlotProps } from '@fluentui/react-motion';\nimport type { ComponentProps, ComponentState, JSXElement, Slot } from '@fluentui/react-utilities';\n\nimport type { DialogContextValue, DialogSurfaceContextValue } from '../../contexts';\nimport type { DialogSurfaceElement } from '../DialogSurface/DialogSurface.types';\n\nexport type DialogSlots = {\n /**\n * For more information refer to the [Motion docs page](https://react.fluentui.dev/?path=/docs/motion-motion-slot--docs).\n *\n */\n surfaceMotion: Slot<PresenceMotionSlotProps>;\n};\n\nexport type InternalDialogSlots = {\n surfaceMotion: NonNullable<Slot<PresenceMotionSlotProps>>;\n};\n\nexport type DialogOpenChangeEvent = DialogOpenChangeData['event'];\n\nexport type DialogOpenChangeData =\n | {\n type: 'escapeKeyDown';\n open: boolean;\n event: React.KeyboardEvent<DialogSurfaceElement>;\n }\n | {\n type: 'backdropClick';\n open: boolean;\n event: React.MouseEvent<DialogSurfaceElement>;\n }\n | {\n type: 'triggerClick';\n open: boolean;\n event: React.MouseEvent<DialogSurfaceElement>;\n };\n\nexport type DialogModalType = 'modal' | 'non-modal' | 'alert';\n\n/**\n * Callback fired when the component changes value from open state.\n *\n * @param event - a React's Synthetic event or a KeyboardEvent in case of `documentEscapeKeyDown`\n * @param data - A data object with relevant information,\n * such as open value and type of interaction that created the event\n */\nexport type DialogOpenChangeEventHandler = (event: DialogOpenChangeEvent, data: DialogOpenChangeData) => void;\n\nexport type DialogContextValues = {\n dialog: DialogContextValue;\n /**\n * dialogSurface context is provided by Dialog as false\n * to ensure components inside Dialog but outside DialogSurface will consume this as false\n */\n dialogSurface: DialogSurfaceContextValue;\n};\n\nexport type DialogProps = ComponentProps<Partial<DialogSlots>> & {\n /**\n * Dialog variations.\n *\n * `modal`: When this type of dialog is open, the rest of the page is dimmed out and cannot be interacted with.\n * The tab sequence is kept within the dialog and moving the focus outside\n * the dialog will imply closing it. This is the default type of the component.\n *\n * `non-modal`: When a non-modal dialog is open, the rest of the page is not dimmed out\n * and users can interact with the rest of the page.\n * This also implies that the tab focus can move outside the dialog when it reaches the last focusable element.\n *\n * `alert`: is a special type of modal dialogs that interrupts the user's workflow\n * to communicate an important message or ask for a decision.\n * Unlike a typical modal dialog, the user must take an action through the options given to dismiss the dialog,\n * and it cannot be dismissed through the dimmed background.\n *\n * @default modal\n */\n modalType?: DialogModalType;\n /**\n * Controls the open state of the dialog\n * @default false\n */\n open?: boolean;\n /**\n * Default value for the uncontrolled open state of the dialog.\n * @default false\n */\n defaultOpen?: boolean;\n /**\n * Callback fired when the component changes value from open state.\n *\n * @param event - a React's Synthetic event or a KeyboardEvent in case of `documentEscapeKeyDown`\n * @param data - A data object with relevant information,\n * such as open value and type of interaction that created the event\n */\n // eslint-disable-next-line @nx/workspace-consistent-callback-type -- can't change type of existing callback\n onOpenChange?: DialogOpenChangeEventHandler;\n /**\n * Can contain two children including `DialogTrigger` and `DialogSurface`.\n * Alternatively can only contain `DialogSurface` if using trigger outside dialog, or controlling state.\n */\n children: [JSXElement, JSXElement] | JSXElement;\n /**\n * Enables standard behavior according to the [HTML dialog spec](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/showModal)\n * where the focus trap involves setting outside elements inert.\n *\n * @default false\n */\n inertTrapFocus?: boolean;\n /**\n * Decides whether the dialog should be removed from the DOM tree when it is closed.\n * This can be useful when dealing with components that may contain state that should not\n * be reset when the dialog is closed.\n *\n * @default true\n */\n unmountOnClose?: boolean;\n};\n\nexport type DialogState = ComponentState<InternalDialogSlots> &\n DialogContextValue & {\n content: React.ReactNode;\n trigger: React.ReactNode;\n };\n"],"names":[],"mappings":"AAuHA,WAII"}

View File

@@ -0,0 +1,4 @@
export { Dialog } from './Dialog';
export { renderDialog_unstable } from './renderDialog';
export { useDialog_unstable } from './useDialog';
export { useDialogContextValues_unstable } from './useDialogContextValues';

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/Dialog/index.ts"],"sourcesContent":["export { Dialog } from './Dialog';\nexport type {\n DialogContextValues,\n DialogModalType,\n DialogOpenChangeData,\n DialogOpenChangeEvent,\n DialogOpenChangeEventHandler,\n DialogProps,\n DialogSlots,\n DialogState,\n InternalDialogSlots,\n} from './Dialog.types';\nexport { renderDialog_unstable } from './renderDialog';\nexport { useDialog_unstable } from './useDialog';\nexport { useDialogContextValues_unstable } from './useDialogContextValues';\n"],"names":["Dialog","renderDialog_unstable","useDialog_unstable","useDialogContextValues_unstable"],"mappings":"AAAA,SAASA,MAAM,QAAQ,WAAW;AAYlC,SAASC,qBAAqB,QAAQ,iBAAiB;AACvD,SAASC,kBAAkB,QAAQ,cAAc;AACjD,SAASC,+BAA+B,QAAQ,2BAA2B"}

View File

@@ -0,0 +1,24 @@
import { jsx as _jsx, jsxs as _jsxs } from "@fluentui/react-jsx-runtime/jsx-runtime";
import { assertSlots } from '@fluentui/react-utilities';
import * as React from 'react';
import { MotionRefForwarder } from '@fluentui/react-motion';
import { DialogProvider, DialogSurfaceProvider } from '../../contexts';
/**
* Render the final JSX of Dialog
*/ export const renderDialog_unstable = (state, contextValues)=>{
assertSlots(state);
return /*#__PURE__*/ _jsx(DialogProvider, {
value: contextValues.dialog,
children: /*#__PURE__*/ _jsxs(DialogSurfaceProvider, {
value: contextValues.dialogSurface,
children: [
state.trigger,
state.content && /*#__PURE__*/ _jsx(state.surfaceMotion, {
children: /*#__PURE__*/ _jsx(MotionRefForwarder, {
children: state.content
})
})
]
})
});
};

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/Dialog/renderDialog.tsx"],"sourcesContent":["/** @jsxRuntime automatic */\n/** @jsxImportSource @fluentui/react-jsx-runtime */\n\nimport { assertSlots } from '@fluentui/react-utilities';\nimport type { JSXElement } from '@fluentui/react-utilities';\nimport * as React from 'react';\n\nimport { MotionRefForwarder } from '@fluentui/react-motion';\nimport { DialogProvider, DialogSurfaceProvider } from '../../contexts';\nimport type { DialogState, DialogContextValues, InternalDialogSlots } from './Dialog.types';\n\n/**\n * Render the final JSX of Dialog\n */\nexport const renderDialog_unstable = (state: DialogState, contextValues: DialogContextValues): JSXElement => {\n assertSlots<InternalDialogSlots>(state);\n\n return (\n <DialogProvider value={contextValues.dialog}>\n <DialogSurfaceProvider value={contextValues.dialogSurface}>\n {state.trigger}\n {state.content && (\n <state.surfaceMotion>\n <MotionRefForwarder>\n {/* Casting here as content should be equivalent to <DialogSurface/> */}\n {/* FIXME: content should not be ReactNode it should be ReactElement instead. */}\n {state.content as React.ReactElement}\n </MotionRefForwarder>\n </state.surfaceMotion>\n )}\n </DialogSurfaceProvider>\n </DialogProvider>\n );\n};\n"],"names":["assertSlots","React","MotionRefForwarder","DialogProvider","DialogSurfaceProvider","renderDialog_unstable","state","contextValues","value","dialog","dialogSurface","trigger","content","surfaceMotion"],"mappings":"AAAA,0BAA0B,GAC1B,iDAAiD;AAEjD,SAASA,WAAW,QAAQ,4BAA4B;AAExD,YAAYC,WAAW,QAAQ;AAE/B,SAASC,kBAAkB,QAAQ,yBAAyB;AAC5D,SAASC,cAAc,EAAEC,qBAAqB,QAAQ,iBAAiB;AAGvE;;CAEC,GACD,OAAO,MAAMC,wBAAwB,CAACC,OAAoBC;IACxDP,YAAiCM;IAEjC,qBACE,KAACH;QAAeK,OAAOD,cAAcE,MAAM;kBACzC,cAAA,MAACL;YAAsBI,OAAOD,cAAcG,aAAa;;gBACtDJ,MAAMK,OAAO;gBACbL,MAAMM,OAAO,kBACZ,KAACN,MAAMO,aAAa;8BAClB,cAAA,KAACX;kCAGEI,MAAMM,OAAO;;;;;;AAO5B,EAAE"}

View File

@@ -0,0 +1,95 @@
'use client';
import * as React from 'react';
import { useHasParentContext } from '@fluentui/react-context-selector';
import { useModalAttributes } from '@fluentui/react-tabster';
import { presenceMotionSlot } from '@fluentui/react-motion';
import { useControllableState, useEventCallback, useId } from '@fluentui/react-utilities';
import { useFocusFirstElement } from '../../utils';
import { DialogContext } from '../../contexts';
import { DialogSurfaceMotion } from '../DialogSurfaceMotion';
/**
* Create the state required to render Dialog.
*
* The returned state can be modified with hooks such as useDialogStyles_unstable,
* before being passed to renderDialog_unstable.
*
* @param props - props from this instance of Dialog
*/ export const useDialog_unstable = (props)=>{
const { children, modalType = 'modal', onOpenChange, inertTrapFocus = false, unmountOnClose = true } = props;
const dialogTitleId = useId('dialog-title-');
const [trigger, content] = childrenToTriggerAndContent(children);
const [open, setOpen] = useControllableState({
state: props.open,
defaultState: props.defaultOpen,
initialState: false
});
const requestOpenChange = useEventCallback((data)=>{
onOpenChange === null || onOpenChange === void 0 ? void 0 : onOpenChange(data.event, data);
// if user prevents default then do not change state value
// otherwise updates state value and trigger reference to the element that caused the opening
if (!data.event.isDefaultPrevented()) {
setOpen(data.open);
}
});
const dialogRef = useFocusFirstElement(open, modalType);
const { modalAttributes, triggerAttributes } = useModalAttributes({
trapFocus: modalType !== 'non-modal',
legacyTrapFocus: !inertTrapFocus
});
const isNestedDialog = useHasParentContext(DialogContext);
return {
components: {
surfaceMotion: DialogSurfaceMotion
},
inertTrapFocus,
open,
modalType,
content,
trigger,
requestOpenChange,
dialogTitleId,
isNestedDialog,
unmountOnClose,
dialogRef,
modalAttributes,
triggerAttributes,
surfaceMotion: presenceMotionSlot(props.surfaceMotion, {
elementType: DialogSurfaceMotion,
defaultProps: {
visible: open,
appear: unmountOnClose,
unmountOnExit: unmountOnClose
}
})
};
};
/**
* Extracts trigger and content from children
*/ function childrenToTriggerAndContent(children) {
const childrenArray = React.Children.toArray(children);
if (process.env.NODE_ENV !== 'production') {
if (childrenArray.length !== 1 && childrenArray.length !== 2) {
// eslint-disable-next-line no-console
console.warn(`@fluentui/react-dialog [useDialog]:
Dialog must contain at least one child <DialogSurface/>,
and at most two children <DialogTrigger/> <DialogSurface/> (in this order).`);
}
}
switch(childrenArray.length){
// case where there's a trigger followed by content
case 2:
return childrenArray;
// case where there's only content
case 1:
return [
undefined,
childrenArray[0]
];
// unknown case
default:
return [
undefined,
undefined
];
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,23 @@
export function useDialogContextValues_unstable(state) {
const { modalType, open, dialogRef, dialogTitleId, isNestedDialog, inertTrapFocus, requestOpenChange, modalAttributes, triggerAttributes, unmountOnClose } = state;
/**
* This context is created with "@fluentui/react-context-selector",
* there is no sense to memoize it
*/ const dialog = {
open,
modalType,
dialogRef,
dialogTitleId,
isNestedDialog,
inertTrapFocus,
modalAttributes,
triggerAttributes,
unmountOnClose,
requestOpenChange
};
const dialogSurface = false;
return {
dialog,
dialogSurface
};
}

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/Dialog/useDialogContextValues.ts"],"sourcesContent":["import type { DialogContextValue, DialogSurfaceContextValue } from '../../contexts';\nimport type { DialogContextValues, DialogState } from './Dialog.types';\n\nexport function useDialogContextValues_unstable(state: DialogState): DialogContextValues {\n const {\n modalType,\n open,\n dialogRef,\n dialogTitleId,\n isNestedDialog,\n inertTrapFocus,\n requestOpenChange,\n modalAttributes,\n triggerAttributes,\n unmountOnClose,\n } = state;\n\n /**\n * This context is created with \"@fluentui/react-context-selector\",\n * there is no sense to memoize it\n */\n const dialog: DialogContextValue = {\n open,\n modalType,\n dialogRef,\n dialogTitleId,\n isNestedDialog,\n inertTrapFocus,\n modalAttributes,\n triggerAttributes,\n unmountOnClose,\n requestOpenChange,\n };\n\n const dialogSurface: DialogSurfaceContextValue = false;\n\n return { dialog, dialogSurface };\n}\n"],"names":["useDialogContextValues_unstable","state","modalType","open","dialogRef","dialogTitleId","isNestedDialog","inertTrapFocus","requestOpenChange","modalAttributes","triggerAttributes","unmountOnClose","dialog","dialogSurface"],"mappings":"AAGA,OAAO,SAASA,gCAAgCC,KAAkB;IAChE,MAAM,EACJC,SAAS,EACTC,IAAI,EACJC,SAAS,EACTC,aAAa,EACbC,cAAc,EACdC,cAAc,EACdC,iBAAiB,EACjBC,eAAe,EACfC,iBAAiB,EACjBC,cAAc,EACf,GAAGV;IAEJ;;;GAGC,GACD,MAAMW,SAA6B;QACjCT;QACAD;QACAE;QACAC;QACAC;QACAC;QACAE;QACAC;QACAC;QACAH;IACF;IAEA,MAAMK,gBAA2C;IAEjD,OAAO;QAAED;QAAQC;IAAc;AACjC"}