Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "feat: Implement avatar context for slot overrides",
"packageName": "@fluentui/react-avatar",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "feat: Use AvatarContext to override avatar size",
"packageName": "@fluentui/react-table",
"email": "[email protected]",
"dependentChangeType": "patch"
}
12 changes: 12 additions & 0 deletions packages/react-components/react-avatar/etc/react-avatar.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ export const Avatar: ForwardRefComponent<AvatarProps>;
// @public (undocumented)
export const avatarClassNames: SlotClassNames<AvatarSlots>;

// @internal (undocumented)
export const AvatarContextProvider: React_2.Provider<AvatarContextValue | undefined>;

// @internal (undocumented)
export interface AvatarContextValue {
// (undocumented)
size?: AvatarSizes;
}

// @public
export const AvatarGroup: ForwardRefComponent<AvatarGroupProps>;

Expand Down Expand Up @@ -178,6 +187,9 @@ export const renderAvatarGroupPopover_unstable: (state: AvatarGroupPopoverState,
// @public (undocumented)
export const useAvatar_unstable: (props: AvatarProps, ref: React_2.Ref<HTMLElement>) => AvatarState;

// @internal (undocumented)
export const useAvatarContext: () => AvatarContextValue;

// @public
export const useAvatarGroup_unstable: (props: AvatarGroupProps, ref: React_2.Ref<HTMLElement>) => AvatarGroupState;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { PresenceBadge } from '@fluentui/react-badge';
import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities';

/**
* Sizes for the avatar
*/
export type AvatarSizes = 16 | 20 | 24 | 28 | 32 | 36 | 40 | 48 | 56 | 64 | 72 | 96 | 120 | 128;

export type AvatarSlots = {
root: Slot<'span'>;

Expand Down Expand Up @@ -69,11 +74,6 @@ export type AvatarNamedColor =
| 'platinum'
| 'anchor';

/**
* Sizes that can be used for the Avatar
*/
export type AvatarSizes = 16 | 20 | 24 | 28 | 32 | 36 | 40 | 48 | 56 | 64 | 72 | 96 | 120 | 128;

/**
* Properties for Avatar
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { AvatarNamedColor, AvatarProps, AvatarState } from './Avatar.types'
import { PersonRegular } from '@fluentui/react-icons';
import { PresenceBadge } from '@fluentui/react-badge';
import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';
import { useAvatarContext } from '../../contexts/AvatarContext';

export const DEFAULT_STRINGS = {
active: 'active',
Expand All @@ -13,7 +14,15 @@ export const DEFAULT_STRINGS = {

export const useAvatar_unstable = (props: AvatarProps, ref: React.Ref<HTMLElement>): AvatarState => {
const { dir } = useFluent();
const { name, size = 32, shape = 'circular', active = 'unset', activeAppearance = 'ring', idForColor } = props;
const { size: contextSize } = useAvatarContext();
const {
name,
size = contextSize ?? (32 as const),
shape = 'circular',
active = 'unset',
activeAppearance = 'ring',
idForColor,
} = props;
let { color = 'neutral' } = props;

// Resolve 'colorful' to a specific color name
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import * as React from 'react';
import { AvatarSizes } from '../components/Avatar/Avatar.types';

const avatarContext = React.createContext<AvatarContextValue | undefined>(undefined);

/**
* @internal
*/
export interface AvatarContextValue {
size?: AvatarSizes;
}

const avatarContextDefaultValue: AvatarContextValue = {};

/**
* @internal
*/
export const AvatarContextProvider = avatarContext.Provider;

/**
* @internal
*/
export const useAvatarContext = () => React.useContext(avatarContext) ?? avatarContextDefaultValue;
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './AvatarGroupContext';
export * from './AvatarContext';
8 changes: 7 additions & 1 deletion packages/react-components/react-avatar/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,10 @@ export {
useAvatarGroupPopover_unstable,
} from './AvatarGroupPopover';
export type { AvatarGroupPopoverProps, AvatarGroupPopoverSlots, AvatarGroupPopoverState } from './AvatarGroupPopover';
export { AvatarGroupProvider, useAvatarGroupContext_unstable } from './contexts/index';
export {
AvatarGroupProvider,
useAvatarGroupContext_unstable,
AvatarContextProvider,
useAvatarContext,
} from './contexts/index';
export type { AvatarContextValue } from './contexts/index';
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
/// <reference types="react" />

import { ARIAButtonSlotProps } from '@fluentui/react-aria';
import type { AvatarSizes } from '@fluentui/react-avatar';
import type { Checkbox } from '@fluentui/react-checkbox';
import type { CheckboxProps } from '@fluentui/react-checkbox';
import type { ComponentProps } from '@fluentui/react-utilities';
Expand Down Expand Up @@ -40,7 +41,7 @@ export const renderTableCell_unstable: (state: TableCellState) => JSX.Element;
export const renderTableCellActions_unstable: (state: TableCellActionsState) => JSX.Element;

// @public
export const renderTableCellLayout_unstable: (state: TableCellLayoutState) => JSX.Element;
export const renderTableCellLayout_unstable: (state: TableCellLayoutState, contextValues: TableCellLayoutContextValues) => JSX.Element;

// @public
export const renderTableHeader_unstable: (state: TableHeaderState) => JSX.Element;
Expand Down Expand Up @@ -138,7 +139,9 @@ export type TableCellLayoutSlots = {
};

// @public
export type TableCellLayoutState = ComponentState<TableCellLayoutSlots> & Pick<TableCellLayoutProps, 'appearance'>;
export type TableCellLayoutState = ComponentState<TableCellLayoutSlots> & Pick<TableCellLayoutProps, 'appearance'> & {
avatarSize: AvatarSizes | undefined;
};

// @public
export type TableCellProps = ComponentProps<TableCellSlots> & {};
Expand Down
1 change: 1 addition & 0 deletions packages/react-components/react-table/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
},
"dependencies": {
"@fluentui/react-aria": "^9.2.0",
"@fluentui/react-avatar": "^9.1.1",
"@fluentui/react-checkbox": "^9.0.6",
"@fluentui/react-icons": "^2.0.175",
"@fluentui/react-tabster": "^9.1.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as React from 'react';
import { useTableCellLayout_unstable } from './useTableCellLayout';
import { renderTableCellLayout_unstable } from './renderTableCellLayout';
import { useTableCellLayoutStyles_unstable } from './useTableCellLayoutStyles';
import { useTableCellLayoutContextValues_unstable } from './useTableCellLayoutContextValues';
import type { TableCellLayoutProps } from './TableCellLayout.types';
import type { ForwardRefComponent } from '@fluentui/react-utilities';

Expand All @@ -12,7 +13,7 @@ export const TableCellLayout: ForwardRefComponent<TableCellLayoutProps> = React.
const state = useTableCellLayout_unstable(props, ref);

useTableCellLayoutStyles_unstable(state);
return renderTableCellLayout_unstable(state);
return renderTableCellLayout_unstable(state, useTableCellLayoutContextValues_unstable(state));
});

TableCellLayout.displayName = 'TableCellLayout';
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities';
import type { AvatarSizes } from '@fluentui/react-avatar';

export type TableCellLayoutContextValues = {
avatar: {
size?: AvatarSizes;
};
};

export type TableCellLayoutSlots = {
root: Slot<'div'>;
Expand Down Expand Up @@ -34,4 +41,5 @@ export type TableCellLayoutProps = ComponentProps<Partial<TableCellLayoutSlots>>
/**
* State used in rendering TableCellLayout
*/
export type TableCellLayoutState = ComponentState<TableCellLayoutSlots> & Pick<TableCellLayoutProps, 'appearance'>;
export type TableCellLayoutState = ComponentState<TableCellLayoutSlots> &
Pick<TableCellLayoutProps, 'appearance'> & { avatarSize: AvatarSizes | undefined };
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
import * as React from 'react';
import { getSlots } from '@fluentui/react-utilities';
import type { TableCellLayoutState, TableCellLayoutSlots } from './TableCellLayout.types';
import { AvatarContextProvider } from '@fluentui/react-avatar';
import type { TableCellLayoutState, TableCellLayoutSlots, TableCellLayoutContextValues } from './TableCellLayout.types';

/**
* Render the final JSX of TableCellLayout
*/
export const renderTableCellLayout_unstable = (state: TableCellLayoutState) => {
export const renderTableCellLayout_unstable = (
state: TableCellLayoutState,
contextValues: TableCellLayoutContextValues,
) => {
const { slots, slotProps } = getSlots<TableCellLayoutSlots>(state);

return (
<slots.root {...slotProps.root}>
{slots.media && <slots.media {...slotProps.media} />}
{slots.media && (
<AvatarContextProvider value={contextValues.avatar}>
<slots.media {...slotProps.media} />
</AvatarContextProvider>
)}
{slots.wrapper && (
<slots.wrapper {...slotProps.wrapper}>
{slots.main && <slots.main {...slotProps.main}>{slotProps.root.children}</slots.main>}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import * as React from 'react';
import { getNativeElementProps, resolveShorthand } from '@fluentui/react-utilities';
import type { TableCellLayoutProps, TableCellLayoutState } from './TableCellLayout.types';
import { useTableContext } from '../../contexts/tableContext';

const tableAvatarSizeMap = {
medium: 32,
small: 24,
smaller: 20,
} as const;

/**
* Create the state required to render TableCellLayout.
Expand All @@ -15,6 +22,8 @@ export const useTableCellLayout_unstable = (
props: TableCellLayoutProps,
ref: React.Ref<HTMLElement>,
): TableCellLayoutState => {
const { size } = useTableContext();

return {
components: {
root: 'div',
Expand All @@ -29,5 +38,6 @@ export const useTableCellLayout_unstable = (
media: resolveShorthand(props.media),
description: resolveShorthand(props.description),
wrapper: resolveShorthand(props.wrapper, { required: !!props.description || !!props.children }),
avatarSize: tableAvatarSizeMap[size],
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as React from 'react';
import type { TableCellLayoutState, TableCellLayoutContextValues } from './TableCellLayout.types';

export function useTableCellLayoutContextValues_unstable(state: TableCellLayoutState): TableCellLayoutContextValues {
const { avatarSize } = state;

const avatar = React.useMemo(
() => ({
size: avatarSize,
}),
[avatarSize],
);

return {
avatar,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import {
DocumentPdfRegular,
VideoRegular,
} from '@fluentui/react-icons';
import { PresenceBadgeStatus, Avatar } from '@fluentui/react-components';
import { Avatar } from '@fluentui/react-components';
import { TableBody, TableCell, TableRow, Table, TableHeader, TableHeaderCell } from '../..';
import { TableCellLayout } from '../../components/TableCellLayout/TableCellLayout';

const items = [
{
file: { label: 'Meeting notes', icon: <DocumentRegular /> },
author: { label: 'Max Mustermann', status: 'available' },
author: { label: 'Max Mustermann', status: 'available' as const },
lastUpdated: { label: '7h ago', timestamp: 1 },
lastUpdate: {
label: 'You edited this',
Expand All @@ -24,7 +24,7 @@ const items = [
},
{
file: { label: 'Thursday presentation', icon: <FolderRegular /> },
author: { label: 'Erika Mustermann', status: 'busy' },
author: { label: 'Erika Mustermann', status: 'busy' as const },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
Expand All @@ -33,7 +33,7 @@ const items = [
},
{
file: { label: 'Training recording', icon: <VideoRegular /> },
author: { label: 'John Doe', status: 'away' },
author: { label: 'John Doe', status: 'away' as const },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
Expand All @@ -42,7 +42,7 @@ const items = [
},
{
file: { label: 'Purchase order', icon: <DocumentPdfRegular /> },
author: { label: 'Jane Doe', status: 'offline' },
author: { label: 'Jane Doe', status: 'offline' as const },
lastUpdated: { label: 'Tue at 9:30 AM', timestamp: 3 },
lastUpdate: {
label: 'You shared this in a Teams chat',
Expand Down Expand Up @@ -75,15 +75,7 @@ export const SizeSmall = () => {
<TableCellLayout media={item.file.icon}>{item.file.label}</TableCellLayout>
</TableCell>
<TableCell>
<TableCellLayout
media={
<Avatar
name={item.author.label}
badge={{ status: item.author.status as PresenceBadgeStatus }}
size={24}
/>
}
>
<TableCellLayout media={<Avatar name={item.author.label} badge={{ status: item.author.status }} />}>
{item.author.label}
</TableCellLayout>
</TableCell>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import {
DocumentPdfRegular,
VideoRegular,
} from '@fluentui/react-icons';
import { PresenceBadgeStatus, Avatar } from '@fluentui/react-components';
import { Avatar } from '@fluentui/react-components';
import { TableBody, TableCell, TableRow, Table, TableHeader, TableHeaderCell, TableCellLayout } from '../..';

const items = [
{
file: { label: 'Meeting notes', icon: <DocumentRegular /> },
author: { label: 'Max Mustermann', status: 'available' },
author: { label: 'Max Mustermann', status: 'available' as const },
lastUpdated: { label: '7h ago', timestamp: 1 },
lastUpdate: {
label: 'You edited this',
Expand All @@ -23,7 +23,7 @@ const items = [
},
{
file: { label: 'Thursday presentation', icon: <FolderRegular /> },
author: { label: 'Erika Mustermann', status: 'busy' },
author: { label: 'Erika Mustermann', status: 'busy' as const },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
Expand All @@ -32,7 +32,7 @@ const items = [
},
{
file: { label: 'Training recording', icon: <VideoRegular /> },
author: { label: 'John Doe', status: 'away' },
author: { label: 'John Doe', status: 'away' as const },
lastUpdated: { label: 'Yesterday at 1:45 PM', timestamp: 2 },
lastUpdate: {
label: 'You recently opened this',
Expand All @@ -41,7 +41,7 @@ const items = [
},
{
file: { label: 'Purchase order', icon: <DocumentPdfRegular /> },
author: { label: 'Jane Doe', status: 'offline' },
author: { label: 'Jane Doe', status: 'offline' as const },
lastUpdated: { label: 'Tue at 9:30 AM', timestamp: 3 },
lastUpdate: {
label: 'You shared this in a Teams chat',
Expand Down Expand Up @@ -74,15 +74,7 @@ export const SizeSmaller = () => {
<TableCellLayout media={item.file.icon}>{item.file.label}</TableCellLayout>
</TableCell>
<TableCell>
<TableCellLayout
media={
<Avatar
name={item.author.label}
badge={{ status: item.author.status as PresenceBadgeStatus }}
size={20}
/>
}
>
<TableCellLayout media={<Avatar name={item.author.label} badge={{ status: item.author.status }} />}>
{item.author.label}
</TableCellLayout>
</TableCell>
Expand Down