import * as React from 'react'; import { AvatarExamples } from '@fluentui/example-data'; import { PrimaryButton, SpinButton, Stack, ThemeProvider } from '@fluentui/react'; import { Avatar, AvatarProps, renderAvatar, useAvatar, useAvatarStyles } from '@fluentui/react-avatar'; import { useBoolean } from '@fluentui/react-hooks'; import { CalendarIcon, CatIcon, ChatBotIcon, ContactIcon, GroupIcon, IDBadgeIcon, RoomIcon, TelemarketerIcon, } from '@fluentui/react-icons-mdl2'; import { mergeClasses, makeStyles } from '@fluentui/react-make-styles'; import { StoryExample } from '../utils/StoryExample'; const examples = { ...AvatarExamples, icon: [ /* eslint-disable react/jsx-key */ , , , , , , /* eslint-enable react/jsx-key */ ], badge: [ 'available', 'away', 'busy', 'doNotDisturb', 'offline', 'outOfOffice', { status: 'available', outOfOffice: true }, { status: 'away', outOfOffice: true }, { status: 'busy', outOfOffice: true }, { status: 'doNotDisturb', outOfOffice: true }, { status: 'offline', outOfOffice: true }, { status: 'outOfOffice', outOfOffice: true }, ], } as const; export const Basic = () => ( <> } /> } /> ); export const AllSizes = () => ( <> ); export const Colors = () => ( <> {examples.namedColors.map(color => ( ))} {examples.name.map(name => ( ))} ); export const Active = () => ( <> ); export const ActiveAnimation = () => { const [active, setActive] = React.useState(false); const [size, nextSize, prevSize] = useValueSelectorState(examples.size, 96); const [activeDisplay, nextActiveDisplay, prevActiveDisplay] = useValueSelectorState(examples.activeDisplay, 'ring'); const [display, nextDisplay, prevDisplay] = useValueSelectorState(['image', 'icon', 'label'], 'image'); React.useEffect(() => { const id = setTimeout(() => setActive(true), 500); return () => clearTimeout(id); }, []); return (
: undefined} />
setActive(a => !a), [])}>Toggle Active
); }; export const CustomSizes = () => ( ); const useRobotAvatarStyles = makeStyles({ root: { borderRadius: '0' }, 20: { width: '24px' }, 24: { width: '28px' }, 28: { width: '32px' }, 32: { width: '36px' }, 36: { width: '40px' }, 40: { width: '44px' }, 48: { width: '56px' }, 56: { width: '64px' }, 64: { width: '72px' }, 72: { width: '80px' }, 96: { width: '108px' }, 120: { width: '128px' }, 128: { width: '136px' }, label: { background: `url('${examples.hexagon}') 0px/contain no-repeat`, borderRadius: '0', }, }); const RobotAvatar = React.forwardRef((props: AvatarProps, ref: React.Ref) => { const state = useAvatar(props, ref, { icon: , }); const styles = useRobotAvatarStyles(); state.className = mergeClasses(styles.root, styles[state.size], state.className); state.label.className = mergeClasses(styles.label, state.label.className); useAvatarStyles(state); return renderAvatar(state); }); export const RobotExample = () => { return ( ); }; export const AvatarPlayground = () => { const [nameAndImage, nextNameAndImage, prevNameAndImage] = useValueSelectorState(examples.nameAndImage); const propSelectors = [ useValueSelector('size', useValueSelectorState(examples.size, 96), true), useValueSelector('square', useValueSelectorState([true, false])), useValueSelector('badge', useValueSelectorState(examples.badge), false, badgeToString), useValueSelector('name', [nameAndImage.name, nextNameAndImage, prevNameAndImage], true), useValueSelector('image', [nameAndImage.image, nextNameAndImage, prevNameAndImage], true, getFilenameFromUrl), useValueSelector('icon', useValueSelectorState(examples.icon), false, iconToString), useValueSelector('color', useValueSelectorState([...examples.color, ...examples.namedColors])), useValueSelector('active', useValueSelectorState(['active', 'inactive'] as const)), useValueSelector('activeDisplay', useValueSelectorState(examples.activeDisplay)), ]; // Build an AvatarProps object with the selected property values const propValues: AvatarProps = {}; propSelectors.forEach(({ assignValue }) => assignValue(propValues)); return (
{...propSelectors.map(p => p.renderSelector())}
{` p.renderValue())} {`/>`}
); }; // // Helpers // /** * Generate a list of Avatars with sample properties */ const AvatarExampleList: React.FC< AvatarProps & { names?: readonly string[]; images?: readonly string[]; icons?: readonly JSX.Element[]; exampleIndex?: number; } > = props => { const { names, images, icons, exampleIndex = 0, ...restOfProps } = props; const offset = exampleIndex * examples.size.length; return ( {examples.size.map((size, i) => ( ))} ); }; const iconToString = (icon: JSX.Element | undefined): string => `<${icon?.type.displayName} />`; const badgeToString = (badge: typeof examples.badge[number] | undefined): string => typeof badge === 'object' ? `{ status: '${badge.status}', outOfOffice: ${badge.outOfOffice} }` : `${badge}`; const getFilenameFromUrl = (url: string) => url.substring(url.lastIndexOf('/') + 1); type ValueSelectorState = [/*value:*/ T, /*next:*/ () => void, /*prev:*/ () => void]; /** * Select a value from an array of values, with next/previous methods */ const useValueSelectorState = function (values: readonly T[], initialValue: T = values[0]): ValueSelectorState { const count = values.length; const [index, setIndex] = React.useState(() => values.indexOf(initialValue)); const next = React.useCallback(() => setIndex(i => (i + 1) % count), [count]); const prev = React.useCallback(() => setIndex(i => (i - 1 + count) % count), [count]); return [values[index], next, prev]; }; /** * Create a selector UI for a property value, allowing the user to toggle among the available property values */ const useValueSelector = ( name: Prop, [value, next, prev]: ValueSelectorState, initialEnabled: boolean = false, valueToString: (v: AvatarProps[Prop] | undefined) => string = v => `${v}`, ) => { const [enabled, { toggle: toggleEnabled }] = useBoolean(initialEnabled); return { /** Assign this property's value to the given props object, if the property is set */ assignValue: (props: AvatarProps) => enabled && (props[name] = value), /** Render the UI to select the property value */ renderSelector: () => (
), /** Render a span with propName="propValue" inside, if the property is set */ renderValue: () => { const quotes = typeof value === 'string' ? '""' : '{}'; return enabled && {`${name}=${quotes[0]}${valueToString(value)}${quotes[1]}`}; }, }; };