diff --git a/.changeset/hot-chicken-tickle.md b/.changeset/hot-chicken-tickle.md deleted file mode 100644 index d09a216a5a4..00000000000 --- a/.changeset/hot-chicken-tickle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@primer/react": minor ---- - -Update `AvatarStack` component to use CSS modules behind the feature flag primer_react_css_modules_team diff --git a/packages/react/src/AvatarStack/AvatarStack.module.css b/packages/react/src/AvatarStack/AvatarStack.module.css deleted file mode 100644 index 5ebe9926dd0..00000000000 --- a/packages/react/src/AvatarStack/AvatarStack.module.css +++ /dev/null @@ -1,186 +0,0 @@ -/* stylelint-disable max-nesting-depth */ -/* stylelint-disable selector-max-specificity */ -.AvatarStack { - --avatar-border-width: 1px; - --avatar-two-margin: calc(var(--avatar-stack-size) * -0.55); - --avatar-three-margin: calc(var(--avatar-stack-size) * -0.85); - - position: relative; - display: flex; - min-width: var(--avatar-stack-size); - height: var(--avatar-stack-size); - - &:where([data-responsive]) { - @media screen and (--viewportRange-narrow) { - --avatar-stack-size: var(--stackSize-narrow); - } - - @media screen and (--viewportRange-regular) { - --avatar-stack-size: var(--stackSize-regular); - } - - @media screen and (--viewportRange-wide) { - --avatar-stack-size: var(--stackSize-wide); - } - } - - &:where([data-avatar-count='1']) { - .AvatarItem { - /* stylelint-disable-next-line primer/box-shadow */ - box-shadow: 0 0 0 var(--avatar-border-width) var(--avatar-borderColor) !important; - } - } - - &:where([data-avatar-count='2']) { - /* this calc explained: */ - - /* 1. avatar size + the non-overlapping part of the second avatar */ - - /* 2. + the border widths of the first two avatars;thiscalcexplained */ - min-width: calc( - var(--avatar-stack-size) + calc(var(--avatar-stack-size) + var(--avatar-two-margin)) + var(--avatar-border-width) - ); - } - - &:where([data-avatar-count='3']) { - /* this calc explained: */ - - /* 1. avatar size + the non-overlapping part of the second avatar */ - - /* 2. + the non-overlapping part of the third avatar;thiscalcexplained */ - min-width: calc( - var(--avatar-stack-size) + - calc( - calc(var(--avatar-stack-size) + var(--avatar-two-margin)) + - calc(var(--avatar-stack-size) + var(--avatar-three-margin)) - ) - ); - } - - &:where([data-avatar-count='3+']) { - /* this calc explained: */ - - /* 1. avatar size + the non-overlapping part of the second avatar */ - - /* 2. + the non-overlapping part of the third and fourth avatar;thiscalcexplained */ - min-width: calc( - var(--avatar-stack-size) + - calc( - calc(var(--avatar-stack-size) + var(--avatar-two-margin)) + - calc(var(--avatar-stack-size) + var(--avatar-three-margin)) * 2 - ) - ); - } - - &:where([data-align-right]) { - justify-content: flex-end; - - .AvatarItem { - margin-left: 0 !important; - - &:first-child { - margin-right: 0; - } - - &:nth-child(n + 2) { - /* stylelint-disable-next-line primer/spacing */ - margin-right: var(--avatar-two-margin); - } - - &:nth-child(n + 3) { - /* stylelint-disable-next-line primer/spacing */ - margin-right: var(--avatar-three-margin); - } - } - - .AvatarStackBody { - flex-direction: row-reverse; - - &:not([data-disable-expand]):hover, - &:not([data-disable-expand]):focus-within { - .AvatarItem { - margin-right: var(--base-size-4) !important; - margin-left: 0 !important; - - &:first-child { - margin-right: 0 !important; - } - } - } - } - } -} - -.AvatarStackBody { - position: absolute; - display: flex; - - &:where([data-disable-expand]) { - position: relative; - } -} - -.AvatarItem { - --avatarSize-regular: var(--avatar-stack-size); - - position: relative; - width: var(--avatar-stack-size); - height: var(--avatar-stack-size); - overflow: hidden; - flex-shrink: 0; - /* stylelint-disable-next-line primer/box-shadow */ - box-shadow: 0 0 0 var(--avatar-border-width) var(--bgColor-default) !important; - - &:first-child { - z-index: 10; - margin-left: 0; - } - - &:nth-child(n + 2) { - z-index: 9; - /* stylelint-disable-next-line primer/spacing */ - margin-left: var(--avatar-two-margin); - } - - &:nth-child(n + 3) { - z-index: 8; - /* stylelint-disable-next-line primer/spacing */ - margin-left: var(--avatar-three-margin); - opacity: 0.55; - } - - &:nth-child(n + 4) { - z-index: 7; - opacity: 0.4; - } - - &:nth-child(n + 5) { - z-index: 6; - opacity: 0.25; - } - - &:nth-child(n + 6) { - visibility: hidden; - opacity: 0; - } -} - -.AvatarStackBody:not([data-disable-expand]):hover, -.AvatarStackBody:not([data-disable-expand]):focus-within { - width: auto; - - .AvatarItem { - margin-left: var(--base-size-4); - visibility: visible; - opacity: 1; - transition: - margin 0.2s ease-in-out, - opacity 0.2s ease-in-out, - visibility 0.2s ease-in-out, - box-shadow 0.1s ease-in-out; - - &:first-child { - margin-left: 0; - } - } -} diff --git a/packages/react/src/AvatarStack/AvatarStack.tsx b/packages/react/src/AvatarStack/AvatarStack.tsx index 6f809dd3845..ccd87f1e24f 100644 --- a/packages/react/src/AvatarStack/AvatarStack.tsx +++ b/packages/react/src/AvatarStack/AvatarStack.tsx @@ -12,9 +12,6 @@ import {isResponsiveValue} from '../hooks/useResponsiveValue' import {getBreakpointDeclarations} from '../utils/getBreakpointDeclarations' import {defaultSxProp} from '../utils/defaultSxProp' import type {WidthOnlyViewportRangeKeys} from '../utils/types/ViewportRangeKeys' -import classes from './AvatarStack.module.css' -import {toggleStyledComponent} from '../internal/utils/toggleStyledComponent' -import {useFeatureFlag} from '../FeatureFlags' import {hasInteractiveNodes} from '../internal/utils/hasInteractiveNodes' import getGlobalFocusStyles from '../internal/utils/getGlobalFocusStyles' @@ -22,178 +19,170 @@ type StyledAvatarStackWrapperProps = { count?: number } & SxProp -const CSS_MODULES_FEATURE_FLAG = 'primer_react_css_modules_team' +const AvatarStackWrapper = styled.span` + --avatar-border-width: 1px; + --avatar-two-margin: calc(var(--avatar-stack-size) * -0.55); + --avatar-three-margin: calc(var(--avatar-stack-size) * -0.85); -const AvatarStackWrapper = toggleStyledComponent( - CSS_MODULES_FEATURE_FLAG, - 'span', - styled.span` - --avatar-border-width: 1px; - --avatar-two-margin: calc(var(--avatar-stack-size) * -0.55); - --avatar-three-margin: calc(var(--avatar-stack-size) * -0.85); + display: flex; + position: relative; + height: var(--avatar-stack-size); + min-width: var(--avatar-stack-size); + .pc-AvatarStackBody { display: flex; - position: relative; - height: var(--avatar-stack-size); - min-width: var(--avatar-stack-size); - - .pc-AvatarStackBody { - display: flex; - position: absolute; + position: absolute; - ${getGlobalFocusStyles('1px')} - } - - .pc-AvatarItem { - --avatar-size: var(--avatar-stack-size); - flex-shrink: 0; - height: var(--avatar-stack-size); - width: var(--avatar-stack-size); - box-shadow: 0 0 0 var(--avatar-border-width) - ${props => (props.count === 1 ? get('colors.avatar.border') : get('colors.canvas.default'))}; - position: relative; - overflow: hidden; - - &:first-child { - margin-left: 0; - z-index: 10; - } - - &:nth-child(n + 2) { - margin-left: var(--avatar-two-margin); - z-index: 9; - } + ${getGlobalFocusStyles('1px')} + } - &:nth-child(n + 3) { - margin-left: var(--avatar-three-margin); - opacity: ${100 - 3 * 15}%; - z-index: 8; - } + .pc-AvatarItem { + --avatar-size: var(--avatar-stack-size); + flex-shrink: 0; + height: var(--avatar-stack-size); + width: var(--avatar-stack-size); + box-shadow: 0 0 0 var(--avatar-border-width) + ${props => (props.count === 1 ? get('colors.avatar.border') : get('colors.canvas.default'))}; + position: relative; + overflow: hidden; - &:nth-child(n + 4) { - opacity: ${100 - 4 * 15}%; - z-index: 7; - } + &:first-child { + margin-left: 0; + z-index: 10; + } - &:nth-child(n + 5) { - opacity: ${100 - 5 * 15}%; - z-index: 6; - } + &:nth-child(n + 2) { + margin-left: var(--avatar-two-margin); + z-index: 9; + } - &:nth-child(n + 6) { - opacity: 0; - visibility: hidden; - } + &:nth-child(n + 3) { + margin-left: var(--avatar-three-margin); + opacity: ${100 - 3 * 15}%; + z-index: 8; } - &.pc-AvatarStack--two { - // this calc explained: - // 1. avatar size + the non-overlapping part of the second avatar - // 2. + the border widths of the first two avatars - min-width: calc( - var(--avatar-stack-size) + calc(var(--avatar-stack-size) + var(--avatar-two-margin)) + - var(--avatar-border-width) - ); + &:nth-child(n + 4) { + opacity: ${100 - 4 * 15}%; + z-index: 7; } - &.pc-AvatarStack--three { - // this calc explained: - // 1. avatar size + the non-overlapping part of the second avatar - // 2. + the non-overlapping part of the third avatar - min-width: calc( - var(--avatar-stack-size) + - calc( - calc(var(--avatar-stack-size) + var(--avatar-two-margin)) + - calc(var(--avatar-stack-size) + var(--avatar-three-margin)) - ) - ); + &:nth-child(n + 5) { + opacity: ${100 - 5 * 15}%; + z-index: 6; } - &.pc-AvatarStack--three-plus { - // this calc explained: - // 1. avatar size + the non-overlapping part of the second avatar - // 2. + the non-overlapping part of the third and fourth avatar - min-width: calc( - var(--avatar-stack-size) + - calc( - calc(var(--avatar-stack-size) + var(--avatar-two-margin)) + - calc(var(--avatar-stack-size) + var(--avatar-three-margin)) * 2 - ) - ); + &:nth-child(n + 6) { + opacity: 0; + visibility: hidden; } + } - &.pc-AvatarStack--right { - justify-content: flex-end; - .pc-AvatarItem { - margin-left: 0 !important; + &.pc-AvatarStack--two { + // this calc explained: + // 1. avatar size + the non-overlapping part of the second avatar + // 2. + the border widths of the first two avatars + min-width: calc( + var(--avatar-stack-size) + calc(var(--avatar-stack-size) + var(--avatar-two-margin)) + var(--avatar-border-width) + ); + } - &:first-child { - margin-right: 0; - } + &.pc-AvatarStack--three { + // this calc explained: + // 1. avatar size + the non-overlapping part of the second avatar + // 2. + the non-overlapping part of the third avatar + min-width: calc( + var(--avatar-stack-size) + + calc( + calc(var(--avatar-stack-size) + var(--avatar-two-margin)) + + calc(var(--avatar-stack-size) + var(--avatar-three-margin)) + ) + ); + } - &:nth-child(n + 2) { - margin-right: var(--avatar-two-margin); - } + &.pc-AvatarStack--three-plus { + // this calc explained: + // 1. avatar size + the non-overlapping part of the second avatar + // 2. + the non-overlapping part of the third and fourth avatar + min-width: calc( + var(--avatar-stack-size) + + calc( + calc(var(--avatar-stack-size) + var(--avatar-two-margin)) + + calc(var(--avatar-stack-size) + var(--avatar-three-margin)) * 2 + ) + ); + } - &:nth-child(n + 3) { - margin-right: var(--avatar-three-margin); - } + &.pc-AvatarStack--right { + justify-content: flex-end; + .pc-AvatarItem { + margin-left: 0 !important; + + &:first-child { + margin-right: 0; + } + + &:nth-child(n + 2) { + margin-right: var(--avatar-two-margin); + } + + &:nth-child(n + 3) { + margin-right: var(--avatar-three-margin); } + } - .pc-AvatarStackBody { - flex-direction: row-reverse; + .pc-AvatarStackBody { + flex-direction: row-reverse; - &:not(.pc-AvatarStack--disableExpand):hover, - &:not(.pc-AvatarStack--disableExpand):focus-within { - .pc-AvatarItem { - margin-right: ${get('space.1')}!important; - margin-left: 0 !important; + &:not(.pc-AvatarStack--disableExpand):hover, + &:not(.pc-AvatarStack--disableExpand):focus-within { + .pc-AvatarItem { + margin-right: ${get('space.1')}!important; + margin-left: 0 !important; - &:first-child { - margin-right: 0 !important; - } + &:first-child { + margin-right: 0 !important; } } } } + } - .pc-AvatarStackBody:not(.pc-AvatarStack--disableExpand):hover, - .pc-AvatarStackBody:not(.pc-AvatarStack--disableExpand):focus-within { - width: auto; + .pc-AvatarStackBody:not(.pc-AvatarStack--disableExpand):hover, + .pc-AvatarStackBody:not(.pc-AvatarStack--disableExpand):focus-within { + width: auto; - .pc-AvatarItem { - margin-left: ${get('space.1')}; - opacity: 100%; - visibility: visible; - ${props => (props.count === 1 ? '' : `box-shadow: inset 0 0 0 4px ${get('colors.canvas.default')};`)} - transition: + .pc-AvatarItem { + margin-left: ${get('space.1')}; + opacity: 100%; + visibility: visible; + ${props => (props.count === 1 ? '' : `box-shadow: inset 0 0 0 4px ${get('colors.canvas.default')};`)} + transition: margin 0.2s ease-in-out, opacity 0.2s ease-in-out, visibility 0.2s ease-in-out, box-shadow 0.1s ease-in-out; - ${getGlobalFocusStyles('1px')} + ${getGlobalFocusStyles('1px')} - &:first-child { - margin-left: 0; - } + &:first-child { + margin-left: 0; } } + } - .pc-AvatarStack--disableExpand { - position: relative; - } - - ${sx}; - `, -) + .pc-AvatarStack--disableExpand { + position: relative; + } -const transformChildren = (children: React.ReactNode, enabled: boolean) => { + ${sx}; +` +const transformChildren = (children: React.ReactNode) => { return React.Children.map(children, child => { if (!React.isValidElement(child)) return child return React.cloneElement(child, { ...child.props, - className: clsx(child.props.className, 'pc-AvatarItem', {[classes.AvatarItem]: enabled}), + className: clsx(child.props.className, 'pc-AvatarItem'), }) }) } @@ -206,46 +195,6 @@ export type AvatarStackProps = { children: React.ReactNode } & SxProp -const AvatarStackBody = ({ - disableExpand, - hasInteractiveChildren, - stackContainer, - children, -}: { - disableExpand: boolean | undefined - hasInteractiveChildren: boolean | undefined - stackContainer: React.RefObject -} & React.ComponentPropsWithoutRef<'div'>) => { - const bodyClassNames = clsx('pc-AvatarStackBody', { - 'pc-AvatarStack--disableExpand': disableExpand, - }) - const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG) - - if (enabled) { - return ( -
- {' '} - {children} -
- ) - } - return ( - - {' '} - {children} - - ) -} - const AvatarStack = ({ children, alignRight, @@ -254,7 +203,6 @@ const AvatarStack = ({ className, sx: sxProp = defaultSxProp, }: AvatarStackProps) => { - const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG) const [hasInteractiveChildren, setHasInteractiveChildren] = useState(false) const stackContainer = useRef(null) @@ -268,6 +216,9 @@ const AvatarStack = ({ }, className, ) + const bodyClassNames = clsx('pc-AvatarStackBody', { + 'pc-AvatarStack--disableExpand': disableExpand, + }) const getAvatarChildSizes = () => { const avatarSizeMap: Record = { @@ -308,7 +259,6 @@ const AvatarStack = ({ }, ) } - const childSizes = getAvatarChildSizes() useEffect(() => { if (stackContainer.current) { @@ -332,16 +282,8 @@ const AvatarStack = ({ const getResponsiveAvatarSizeStyles = () => { // if there is no size set on the AvatarStack, use the `size` props of the Avatar children to set the `--avatar-stack-size` CSS variable if (!size) { - if (enabled) { - return { - '--stackSize-narrow': `${childSizes.narrow}px`, - '--stackSize-regular': `${childSizes.regular}px`, - '--stackSize-wide': `${childSizes.wide}px`, - } - } - return getBreakpointDeclarations( - childSizes, + getAvatarChildSizes(), '--avatar-stack-size' as keyof React.CSSProperties, value => `${value}px`, ) @@ -349,14 +291,6 @@ const AvatarStack = ({ // if the `size` prop is set and responsive, set the `--avatar-stack-size` CSS variable for each viewport if (isResponsiveValue(size)) { - if (enabled) { - return { - '--stackSize-narrow': `${size.narrow || DEFAULT_AVATAR_SIZE}px`, - '--stackSize-regular': `${size.regular || DEFAULT_AVATAR_SIZE}px`, - '--stackSize-wide': `${size.wide || DEFAULT_AVATAR_SIZE}px`, - } - } - return getBreakpointDeclarations( size, '--avatar-stack-size' as keyof React.CSSProperties, @@ -374,23 +308,15 @@ const AvatarStack = ({ ) return ( - 3 ? '3+' : count) : undefined} - data-align-right={enabled && alignRight ? '' : undefined} - data-responsive={enabled && (!size || isResponsiveValue(size)) ? '' : undefined} - className={clsx(wrapperClassNames, {[classes.AvatarStack]: enabled})} - // @ts-ignore - it's not allowing CSS properties here - style={enabled ? (getResponsiveAvatarSizeStyles() as React.CSSProperties) : undefined} - sx={enabled ? undefined : avatarStackSx} - > - + - {transformChildren(children, enabled)} - + {' '} + {transformChildren(children)} + ) }