diff --git a/.changeset/afraid-teams-throw.md b/.changeset/afraid-teams-throw.md new file mode 100644 index 00000000000..45e461705de --- /dev/null +++ b/.changeset/afraid-teams-throw.md @@ -0,0 +1,6 @@ +--- +'@primer/react': major +'@primer/styled-react': minor +--- + +Remove sx from deprecated ActionList component. diff --git a/packages/react/src/FilteredActionList/FilteredActionList.module.css b/packages/react/src/FilteredActionList/FilteredActionList.module.css index c09649da9a2..4613a4e96a8 100644 --- a/packages/react/src/FilteredActionList/FilteredActionList.module.css +++ b/packages/react/src/FilteredActionList/FilteredActionList.module.css @@ -4,6 +4,12 @@ overflow: hidden; } +.Header { + /* stylelint-disable-next-line primer/box-shadow */ + box-shadow: 0 1px 0 var(--borderColor-default); + z-index: 1; +} + .Container { display: flex; height: 100%; diff --git a/packages/react/src/FilteredActionList/FilteredActionList.tsx b/packages/react/src/FilteredActionList/FilteredActionList.tsx index cc01dc685ab..61ccb33538e 100644 --- a/packages/react/src/FilteredActionList/FilteredActionList.tsx +++ b/packages/react/src/FilteredActionList/FilteredActionList.tsx @@ -3,10 +3,8 @@ import {scrollIntoView, FocusKeys} from '@primer/behaviors' import type {KeyboardEventHandler} from 'react' import type React from 'react' import {useCallback, useEffect, useRef, useState} from 'react' -import styled from 'styled-components' import type {TextInputProps} from '../TextInput' import TextInput from '../TextInput' -import {get} from '../constants' import {ActionList} from '../ActionList' import type {GroupedListProps, ListPropsBase, ItemInput, RenderItemFn} from './' import {useFocusZone} from '../hooks/useFocusZone' @@ -49,11 +47,6 @@ export interface FilteredActionListProps extends Partial void } -const StyledHeader = styled.div` - box-shadow: 0 1px 0 ${get('colors.border.default')}; - z-index: 1; -` - export function FilteredActionList({ loading = false, placeholderText, @@ -346,7 +339,7 @@ export function FilteredActionList({ return (
- +
- +
Items will be filtered as you type {onSelectAllChange !== undefined && (
diff --git a/packages/react/src/deprecated/ActionList/Divider.module.css b/packages/react/src/deprecated/ActionList/Divider.module.css new file mode 100644 index 00000000000..e0050fc4ea3 --- /dev/null +++ b/packages/react/src/deprecated/ActionList/Divider.module.css @@ -0,0 +1,8 @@ +.Divider { + height: var(--borderWidth-thin); + /* stylelint-disable-next-line primer/colors */ + background: var(--borderColor-muted); + /* stylelint-disable-next-line primer/spacing */ + margin-top: calc(var(--base-size-8) - 1px); + margin-bottom: var(--base-size-8); +} diff --git a/packages/react/src/deprecated/ActionList/Divider.tsx b/packages/react/src/deprecated/ActionList/Divider.tsx index 48b5660736c..2679e7b2da4 100644 --- a/packages/react/src/deprecated/ActionList/Divider.tsx +++ b/packages/react/src/deprecated/ActionList/Divider.tsx @@ -1,18 +1,10 @@ -import styled from 'styled-components' -import {get} from '../../constants' - -export const StyledDivider = styled.div` - height: 1px; - background: ${get('colors.border.muted')}; - margin-top: calc(${get('space.2')} - 1px); - margin-bottom: ${get('space.2')}; -` +import classes from './Divider.module.css' /** - * Visually separates `Item`s or `Group`s in an `ActionList`. + * Visually separates `Items or `Groups in an `ActionList`. */ export function Divider(): JSX.Element { - return + return
} /** diff --git a/packages/react/src/deprecated/ActionList/Group.tsx b/packages/react/src/deprecated/ActionList/Group.tsx index a0b27711d0b..cbf6769bb50 100644 --- a/packages/react/src/deprecated/ActionList/Group.tsx +++ b/packages/react/src/deprecated/ActionList/Group.tsx @@ -1,14 +1,11 @@ import type React from 'react' -import styled from 'styled-components' -import type {SxProp} from '../../sx' -import sx from '../../sx' import type {HeaderProps} from './Header' import {Header} from './Header' /** * Contract for props passed to the `Group` component. */ -export interface GroupProps extends React.ComponentPropsWithoutRef<'div'>, SxProp { +export interface GroupProps extends React.ComponentPropsWithoutRef<'div'> { /** * Props for a `Header` to render in the `Group`. */ @@ -30,18 +27,14 @@ export interface GroupProps extends React.ComponentPropsWithoutRef<'div'>, SxPro showItemDividers?: boolean } -const StyledGroup = styled.div` - ${sx} -` - /** * Collects related `Items` in an `ActionList`. */ export function Group({header, items, ...props}: GroupProps): JSX.Element { return ( - +
{header &&
} {items} - +
) } diff --git a/packages/react/src/deprecated/ActionList/Header.module.css b/packages/react/src/deprecated/ActionList/Header.module.css new file mode 100644 index 00000000000..f9cbf6c2bee --- /dev/null +++ b/packages/react/src/deprecated/ActionList/Header.module.css @@ -0,0 +1,25 @@ +.Header { + /* 6px vertical padding + 20px line height = 32px total height + * + * TODO: When rem-based spacing on a 4px scale lands, replace + * hardcoded '6px' with 'calc((${get('space.s32')} - ${get('space.20')}) / 2)'. + */ + /* stylelint-disable-next-line primer/spacing */ + padding: 6px var(--base-size-16); + font-size: var(--text-body-size-small); + font-weight: var(--text-title-weight-large); + color: var(--fgColor-muted); + + &:where([data-filled]) { + background: var(--bgColor-muted); + margin: var(--base-size-8) 0; + /* stylelint-disable-next-line primer/colors */ + border-top: var(--borderWidth-thin) solid var(--bgColor-neutral-muted); + /* stylelint-disable-next-line primer/colors */ + border-bottom: var(--borderWidth-thin) solid var(--bgColor-neutral-muted); + + &:first-child { + margin-top: 0; + } + } +} diff --git a/packages/react/src/deprecated/ActionList/Header.tsx b/packages/react/src/deprecated/ActionList/Header.tsx index 8c6efbdd6aa..dc820d4eee8 100644 --- a/packages/react/src/deprecated/ActionList/Header.tsx +++ b/packages/react/src/deprecated/ActionList/Header.tsx @@ -1,13 +1,11 @@ import type React from 'react' -import styled, {css} from 'styled-components' -import {get} from '../../constants' -import type {SxProp} from '../../sx' -import sx from '../../sx' +import {clsx} from 'clsx' +import classes from './Header.module.css' /** * Contract for props passed to the `Header` component. */ -export interface HeaderProps extends React.ComponentPropsWithoutRef<'div'>, SxProp { +export interface HeaderProps extends React.ComponentPropsWithoutRef<'div'> { /** * Style variations. Usage is discretionary. * @@ -27,35 +25,6 @@ export interface HeaderProps extends React.ComponentPropsWithoutRef<'div'>, SxPr auxiliaryText?: string } -export const StyledHeader = styled.div<{variant: HeaderProps['variant']} & SxProp>` - { - /* 6px vertical padding + 20px line height = 32px total height - * - * TODO: When rem-based spacing on a 4px scale lands, replace - * hardcoded '6px' with 'calc((${get('space.s32')} - ${get('space.20')}) / 2)'. - */ - } - padding: 6px ${get('space.3')}; - font-size: ${get('fontSizes.0')}; - font-weight: ${get('fontWeights.bold')}; - color: ${get('colors.fg.muted')}; - - ${({variant}) => - variant === 'filled' && - css` - background: ${get('colors.canvas.subtle')}; - margin: ${get('space.2')} 0; - border-top: 1px solid ${get('colors.neutral.muted')}; - border-bottom: 1px solid ${get('colors.neutral.muted')}; - - &:first-child { - margin-top: 0; - } - `} - - ${sx} -` - /** * Displays the name and description of a `Group`. */ @@ -64,12 +33,19 @@ export function Header({ title, auxiliaryText, children: _children, + className, ...props }: HeaderProps): JSX.Element { return ( - +
{title} {auxiliaryText && {auxiliaryText}} - +
) } diff --git a/packages/react/src/deprecated/ActionList/Item.module.css b/packages/react/src/deprecated/ActionList/Item.module.css new file mode 100644 index 00000000000..39500c1c5fa --- /dev/null +++ b/packages/react/src/deprecated/ActionList/Item.module.css @@ -0,0 +1,253 @@ +/* stylelint-disable selector-max-specificity */ +.DividedContent { + display: flex; + min-width: 0; + + /* Required for dividers */ + position: relative; + flex-grow: 1; +} + +.MainContent { + align-items: baseline; + display: flex; + min-width: 0; + flex-direction: var(--main-content-flex-direction); + flex-grow: 1; +} + +.Item { + /* 6px vertical padding + 20px line height = 32px total height */ + /* stylelint-disable-next-line primer/spacing */ + padding: 6px var(--base-size-8); + display: flex; + border-radius: var(--borderRadius-medium); + color: var(--fgColor-default); + /* 2 frames on a 60hz monitor */ + transition: background 33.333ms linear; + text-decoration: none; +} + +.Item--danger { + color: var(--fgColor-danger); +} + +.Item--disabled { + color: var(--fgColor-disabled); + cursor: default; +} + +@media (hover: hover) and (pointer: fine) { + .Item:hover { + /* allow override in case another item in the list is active/focused */ + background: var(--item-hover-bg-override, var(--bgColor-muted)); + cursor: pointer; + } + + .Item--danger:hover { + background: var(--item-hover-bg-override, var(--bgColor-danger-muted)); + color: var(--fgColor-danger); + } + + .Item--disabled:hover { + background: transparent; + cursor: default; + } +} + +/* Item dividers */ +.Item:not(:first-of-type):not([data-component='ActionList.Divider'] + *):not([data-component='ActionList.Header'] + *) { + margin-top: 0; + + :where([data-divider]) { + /* stylelint-disable-next-line primer/spacing */ + margin-top: 1px; + } +} + +.Item:not(:first-of-type):not([data-component='ActionList.Divider'] + *):not([data-component='ActionList.Header'] + *) + .DividedContent::before { + content: ' '; + display: block; + position: absolute; + width: 100%; + /* stylelint-disable-next-line primer/spacing */ + top: -7px; + border: 0 solid var(--borderColor-muted); + border-top-width: 0; +} + +:where([data-divider]) { + .DividedContent::before { + /* stylelint-disable-next-line primer/borders */ + border-top-width: 1px; + } +} + +/* Item dividers should not be visible above/below hovered items */ +.Item:hover .DividedContent::before, +:hover + .Item .DividedContent::before { + /* allow override in case another item in the list is active/focused */ + /* stylelint-disable-next-line primer/colors */ + border-color: var(--item-hover-divider-border-color-override, transparent) !important; +} + +/* Item dividers should not be visible above/below focused items */ +.Item:focus .DividedContent::before, +:focus + .Item .DividedContent::before, +.Item[data-is-active-descendant] .DividedContent::before, +[data-active-descendant] + .Item .DividedContent::before { + /* !important because all the :not's above give higher specificity */ + border-color: transparent !important; +} + +/* Active Descendant */ +.Item[data-is-active-descendant='activated-directly'] { + background: var(--bgColor-accent-muted); +} + +.Item[data-is-active-descendant='activated-indirectly'] { + background: var(--bgColor-muted); +} + +.Item:focus { + background: var(--bgColor-accent-muted); + outline: none; +} + +.Item:active { + background: var(--bgColor-accent-muted); +} + +.MainContent--inline { + flex-direction: row; +} + +.MainContent--block { + flex-direction: column; +} + +.BaseVisualContainer { + /* Match visual height to adjacent text line height. */ + height: 20px; + width: var(--base-size-16); + margin-right: var(--base-size-8); + display: flex; + justify-content: center; + align-items: center; + flex-shrink: 0; +} + +.LeadingVisualContainer { + /* Match visual height to adjacent text line height. */ + height: 20px; + width: var(--base-size-16); + margin-right: var(--base-size-8); + align-items: center; + flex-shrink: 0; + display: flex; + flex-direction: column; + justify-content: center; +} + +.LeadingVisualContainer svg { + fill: var(--fgColor-muted); + font-size: var(--text-body-size-small); +} + +.LeadingVisualContainer:where([data-variant='danger']) svg { + fill: var(--fgColor-danger); +} + +.LeadingVisualContainer:where([data-disabled]) svg { + fill: var(--fgColor-disabled); +} + +.TrailingContent { + /* Match visual height to adjacent text line height. */ + color: var(--fgColor-muted); + height: 20px; + width: auto; + margin-left: var(--base-size-8); + margin-right: 0; + display: flex; + justify-content: center; + align-items: center; + flex-shrink: 0; +} + +.TrailingContent:where([data-variant='danger']) { + color: var(--fgColor-muted); +} + +.TrailingContent:where([data-disabled]) { + color: var(--fgColor-disabled); +} + +.TrailingContent div:nth-child(2) { + margin-left: var(--base-size-8); +} + +.TrailingContent svg { + fill: var(--fgColor-muted); + font-size: var(--text-body-size-small); +} + +.TrailingContent:where([data-variant='danger']) svg { + fill: var(--fgColor-danger); +} + +.TrailingContent:where([data-disabled]) svg { + fill: var(--fgColor-disabled); +} + +.DescriptionContainer { + color: var(--fgColor-muted); + font-size: var(--text-body-size-small); + /* stylelint-disable-next-line primer/typography */ + line-height: 16px; + min-width: 0; + flex-grow: 1; + /* stylelint-disable-next-line primer/spacing */ + margin-left: var(--description-container-margin-left); + flex-basis: var(--description-container-flex-basis); +} + +.MultiSelectIcon { + color: var(--fgColor-muted); +} + +.MultiSelectIcon rect { + fill: var(--bgColor-default); + stroke: var(--borderColor-default); + shape-rendering: auto; /* this is a workaround to override global style in github/github, see primer/react#1666 */ +} + +.MultiSelectIcon path { + fill: var(--fgColor-onEmphasis); + box-shadow: var(--shadow-resting-small); + opacity: 0; +} + +.MultiSelectIcon:where([data-selected]) rect { + fill: var(--bgColor-accent-emphasis); + stroke: var(--bgColor-accent-emphasis); +} + +.MultiSelectIcon:where([data-selected]) path { + opacity: 1; +} + +.Item[data-divider]:not(:first-of-type):not([data-component='ActionList.Divider'] + .Item):not( + [data-component='ActionList.Header'] + .Item + ) + .DividedContent::before { + border-top-width: var(--borderWidth-thin); +} + +.Item[data-divider]:not(:first-of-type):not([data-component='ActionList.Divider'] + .Item):not( + [data-component='ActionList.Header'] + .Item + ) { + /* stylelint-disable-next-line primer/spacing */ + margin-top: 1px; +} diff --git a/packages/react/src/deprecated/ActionList/Item.tsx b/packages/react/src/deprecated/ActionList/Item.tsx index e5768ecb7fa..176491d8f7f 100644 --- a/packages/react/src/deprecated/ActionList/Item.tsx +++ b/packages/react/src/deprecated/ActionList/Item.tsx @@ -2,27 +2,20 @@ import {CheckIcon} from '@primer/octicons-react' import React, {useCallback} from 'react' import {isValidElementType} from 'react-is' import {get} from '../../constants' -import type {SxProp} from '../../sx' -import sx from '../../sx' import Truncate from '../../Truncate' import type {ItemInput} from './List' -import styled from 'styled-components' -import {StyledHeader} from './Header' -import {StyledDivider} from './Divider' import {useTheme} from '../../ThemeProvider' -import { - activeDescendantActivatedDirectly, - activeDescendantActivatedIndirectly, - isActiveDescendantAttribute, -} from '@primer/behaviors' import {useId} from '../../hooks/useId' import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../../utils/polymorphic' import type {AriaRole} from '../../utils/types' +import classes from './Item.module.css' +import {clsx} from 'clsx' + /** * Contract for props passed to the `Item` component. */ -export interface ItemProps extends SxProp { +export interface ItemProps { /** * Primary text which names an `Item`. */ @@ -122,218 +115,12 @@ export interface ItemProps extends SxProp { item?: ItemInput } -const getItemVariant = (variant = 'default', disabled?: boolean) => { - if (disabled) { - return { - color: get('colors.primer.fg.disabled'), - iconColor: get('colors.primer.fg.disabled'), - annotationColor: get('colors.primer.fg.disabled'), - hoverCursor: 'default', - } - } - - switch (variant) { - case 'danger': - return { - color: get('colors.danger.fg'), - iconColor: get('colors.danger.fg'), - annotationColor: get('colors.fg.muted'), - hoverCursor: 'pointer', - hoverBg: get('colors.actionListItem.danger.hoverBg'), - focusBg: get('colors.actionListItem.danger.activeBg'), - hoverText: get('colors.actionListItem.danger.hoverText'), - } - default: - return { - color: get('colors.fg.default'), - iconColor: get('colors.fg.muted'), - annotationColor: get('colors.fg.muted'), - hoverCursor: 'pointer', - hoverBg: get('colors.actionListItem.default.hoverBg'), - focusBg: get('colors.actionListItem.default.activeBg'), - } - } -} - -const DividedContent = styled.div` - display: flex; - min-width: 0; - - /* Required for dividers */ - position: relative; - flex-grow: 1; -` - -const MainContent = styled.div` - align-items: baseline; - display: flex; - min-width: 0; - flex-direction: var(--main-content-flex-direction); - flex-grow: 1; -` - -const StyledItem = styled.div< - { - variant: ItemProps['variant'] - showDivider: ItemProps['showDivider'] - item?: ItemInput - } & SxProp ->` - /* 6px vertical padding + 20px line height = 32px total height - * - * TODO: When rem-based spacing on a 4px scale lands, replace - * hardcoded '6px' with 'calc((${get('space.s32')} - ${get('space.20')}) / 2)'. - */ - padding: 6px ${get('space.2')}; - display: flex; - border-radius: ${get('radii.2')}; - color: ${({variant, item}) => getItemVariant(variant, item?.disabled).color}; - // 2 frames on a 60hz monitor - transition: background 33.333ms linear; - text-decoration: none; - - @media (hover: hover) and (pointer: fine) { - :hover { - // allow override in case another item in the list is active/focused - background: var( - --item-hover-bg-override, - ${({variant, item}) => getItemVariant(variant, item?.disabled).hoverBg} - ); - color: ${({variant, item}) => getItemVariant(variant, item?.disabled).hoverText}; - cursor: ${({variant, item}) => getItemVariant(variant, item?.disabled).hoverCursor}; - } - } - - // Item dividers - :not(:first-of-type):not(${StyledDivider} + &):not(${StyledHeader} + &) { - margin-top: ${({showDivider}) => (showDivider ? `1px` : '0')}; - - ${DividedContent}::before { - content: ' '; - display: block; - position: absolute; - width: 100%; - top: -7px; - // NB: This 'get' won’t execute if it’s moved into the arrow function below. - border: 0 solid ${get('colors.border.muted')}; - border-top-width: ${({showDivider}) => (showDivider ? `1px` : '0')}; - } - } - - // Item dividers should not be visible: - // - above Hovered - &:hover ${DividedContent}::before, - // - below Hovered - // '*' instead of '&' because '&' maps to separate class names depending on 'variant' - :hover + * ${DividedContent}::before { - // allow override in case another item in the list is active/focused - border-color: var(--item-hover-divider-border-color-override, transparent) !important; - } - - // - above Focused - &:focus ${DividedContent}::before, - // - below Focused - // '*' instead of '&' because '&' maps to separate class names depending on 'variant' - :focus + * ${DividedContent}::before, - // - above Active Descendent - &[${isActiveDescendantAttribute}] ${DividedContent}::before, - // - below Active Descendent - [${isActiveDescendantAttribute}] + & ${DividedContent}::before { - // '!important' because all the ':not's above give higher specificity - border-color: transparent !important; - } - - // Active Descendant - &[${isActiveDescendantAttribute}='${activeDescendantActivatedDirectly}'] { - background: ${({variant, item}) => getItemVariant(variant, item?.disabled).focusBg}; - } - &[${isActiveDescendantAttribute}='${activeDescendantActivatedIndirectly}'] { - background: ${({variant, item}) => getItemVariant(variant, item?.disabled).hoverBg}; - } - - &:focus { - background: ${({variant, item}) => getItemVariant(variant, item?.disabled).focusBg}; - outline: none; - } - - &:active { - background: ${({variant, item}) => getItemVariant(variant, item?.disabled).focusBg}; - } - - ${sx} -` - -export const TextContainer = styled.span<{ - dangerouslySetInnerHtml?: React.DOMAttributes['dangerouslySetInnerHTML'] -}>`` - -const BaseVisualContainer = styled.div<{variant?: ItemProps['variant']; disabled?: boolean}>` - // Match visual height to adjacent text line height. - // TODO: When rem-based spacing on a 4px scale lands, replace - // hardcoded '20px' with '${get('space.s20')}'. - height: 20px; - width: ${get('space.3')}; - margin-right: ${get('space.2')}; - display: flex; - justify-content: center; - align-items: center; - flex-shrink: 0; -` - -const ColoredVisualContainer = styled(BaseVisualContainer)` - svg { - fill: ${({variant, disabled}) => getItemVariant(variant, disabled).iconColor}; - font-size: ${get('fontSizes.0')}; - } -` - -const LeadingVisualContainer = styled(ColoredVisualContainer)` - display: flex; - flex-direction: column; - justify-content: center; -` - -const TrailingContent = styled(ColoredVisualContainer)` - color: ${({variant, disabled}) => getItemVariant(variant, disabled).annotationColor}; - margin-left: ${get('space.2')}; - margin-right: 0; - width: auto; - div:nth-child(2) { - margin-left: ${get('space.2')}; - } -` - -const DescriptionContainer = styled.span` - color: ${get('colors.fg.muted')}; - font-size: ${get('fontSizes.0')}; - // TODO: When rem-based spacing on a 4px scale lands, replace - // hardcoded '16px' with '${get('lh-12')}'. - line-height: 16px; - margin-left: var(--description-container-margin-left); - min-width: 0; - flex-grow: 1; - flex-basis: var(--description-container-flex-basis); -` - -const MultiSelectIcon = styled.svg<{selected?: boolean}>` - rect { - fill: ${({selected}) => (selected ? get('colors.accent.fg') : get('colors.canvas.default'))}; - stroke: ${({selected}) => (selected ? get('colors.accent.fg') : get('colors.border.default'))}; - shape-rendering: auto; // this is a workaround to override global style in github/github, see primer/react#1666 - } - path { - fill: ${get('colors.fg.onEmphasis')}; - boxshadow: ${get('shadow.small')}; - opacity: ${({selected}) => (selected ? 1 : 0)}; - } -` - /** * An actionable or selectable `Item` with an optional icon and description. */ export const Item = React.forwardRef((itemProps, ref) => { const { - as: Component, + as: Component = 'div', text, description, descriptionVariant = 'inline', @@ -351,6 +138,7 @@ export const Item = React.forwardRef((itemProps, ref) => { children, onClick, id, + className, ...props } = itemProps @@ -387,31 +175,32 @@ export const Item = React.forwardRef((itemProps, ref) => { const {theme} = useTheme() return ( - {!!selected === selected && ( - +
{selectionVariant === 'multiple' ? ( <> - {/** - * we use a svg instead of an input because there should not - * be an interactive element inside an option - * svg copied from primer/css - */} - { strokeWidth="0" d="M4.03231 8.69862C3.84775 8.20646 4.49385 7.77554 4.95539 7.77554C5.41693 7.77554 6.80154 9.85246 6.80154 9.85246C6.80154 9.85246 10.2631 4.314 10.4938 4.08323C10.7246 3.85246 11.8785 4.08323 11.4169 5.00631C11.0081 5.82388 7.26308 11.4678 7.26308 11.4678C7.26308 11.4678 6.80154 12.1602 6.34 11.4678C5.87846 10.7755 4.21687 9.19077 4.03231 8.69862Z" /> - + ) : ( selected && )} - +
)} {LeadingVisual && ( - +
- +
)} - - +
{children} - {text ? {text} : null} + {text ? {text} : null} {description ? ( - { '--description-container-flex-basis': descriptionVariant === 'inline' ? 0 : 'auto', } as React.CSSProperties } + className={classes.DescriptionContainer} > {descriptionVariant === 'block' ? ( description @@ -461,26 +256,26 @@ export const Item = React.forwardRef((itemProps, ref) => { {description} )} - + ) : null} - +
{/* backward compatibility: prefer TrailingVisual but fallback to TrailingIcon */} {TrailingVisual ? ( - +
{typeof TrailingVisual !== 'string' && isValidElementType(TrailingVisual) ? ( ) : ( TrailingVisual )} - +
) : TrailingIcon || trailingText ? ( - +
{trailingText} {TrailingIcon && } - +
) : null} -
-
+
+ ) }) as PolymorphicForwardRefComponent<'div', ItemProps> diff --git a/packages/react/src/deprecated/ActionList/List.module.css b/packages/react/src/deprecated/ActionList/List.module.css new file mode 100644 index 00000000000..2c97f08a294 --- /dev/null +++ b/packages/react/src/deprecated/ActionList/List.module.css @@ -0,0 +1,5 @@ +.List[data-has-active-descendant], +.List:focus-within { + --item-hover-bg-override: none; + --item-hover-divider-border-color-override: var(--borderColor-muted); +} diff --git a/packages/react/src/deprecated/ActionList/List.tsx b/packages/react/src/deprecated/ActionList/List.tsx index 3550c817062..a598e596745 100644 --- a/packages/react/src/deprecated/ActionList/List.tsx +++ b/packages/react/src/deprecated/ActionList/List.tsx @@ -6,9 +6,9 @@ import {Group} from './Group' import type {ItemProps} from './Item' import {Item} from './Item' import {Divider} from './Divider' -import {hasActiveDescendantAttribute} from '@primer/behaviors' +import {clsx} from 'clsx' import type {Merge} from '../../utils/types/Merge' -import {BoxWithFallback} from '../../internal/components/BoxWithFallback' +import classes from './List.module.css' export type RenderItemFn = (props: ItemProps) => React.ReactElement @@ -71,6 +71,8 @@ export interface ListPropsBase { * Whether to display a divider above each `Item` in this `List` when it does not follow a `Header` or `Divider`. */ showItemDividers?: boolean + + className?: string } /** @@ -175,7 +177,7 @@ export const List = React.forwardRef((props, forwarde selectionVariant={restProps.selectionVariant} {...itemProps} key={key} - sx={{...itemStyle, ...itemProps.sx}} + style={itemStyle} item={item} /> ) @@ -228,19 +230,14 @@ export const List = React.forwardRef((props, forwarde } return ( - {groups.map(({header, ...groupProps}, index) => { const hasFilledHeader = header?.variant === 'filled' @@ -249,15 +246,15 @@ export const List = React.forwardRef((props, forwarde {shouldShowDivider ? : null} {renderGroup({ - sx: { + style: { ...(index === 0 && firstGroupStyle), ...(index === groups.length - 1 && lastGroupStyle), - ...(index > 0 && !shouldShowDivider && {mt: 2}), + ...(index > 0 && !shouldShowDivider && {marginTop: 'var(--base-size-8, 8px)'}), }, ...(header && { header: { ...header, - sx: {...headerStyle, ...header.sx}, + style: headerStyle, }, }), ...groupProps, @@ -265,7 +262,7 @@ export const List = React.forwardRef((props, forwarde ) })} - +
) }) diff --git a/packages/react/src/stories/deprecated/ActionList.stories.tsx b/packages/react/src/stories/deprecated/ActionList.stories.tsx index 0b802a0434b..06586452d06 100644 --- a/packages/react/src/stories/deprecated/ActionList.stories.tsx +++ b/packages/react/src/stories/deprecated/ActionList.stories.tsx @@ -163,7 +163,7 @@ export function ComplexListInsetVariantStory(): JSX.Element { {groupId: '0'}, {groupId: '1', header: {title: 'Live query', variant: 'filled', 'aria-level': 3}}, {groupId: '2', header: {title: 'Layout', variant: 'subtle', 'aria-level': 3}, showItemDividers: true}, - {groupId: '3', renderItem: props => }, + {groupId: '3', renderItem: props => }, { groupId: '4', renderItem: ({leadingVisual: LeadingVisual, ...props}) => ( @@ -176,9 +176,7 @@ export function ComplexListInsetVariantStory(): JSX.Element { )} /> ), - renderGroup: ({sx: sxProps, ...props}) => ( - - ), + renderGroup: props => , }, ]} items={[ @@ -194,7 +192,7 @@ export function ComplexListInsetVariantStory(): JSX.Element { leadingVisual: SearchIcon, text: 'repo:github/memex,github/github', groupId: '1', - renderItem: props => , + renderItem: props => , }, { leadingVisual: NoteIcon, @@ -237,7 +235,7 @@ export function ComplexListFullVariantStory(): JSX.Element { {groupId: '0'}, {groupId: '1', header: {title: 'Live query', variant: 'filled', 'aria-level': 3}}, {groupId: '2', header: {title: 'Layout', variant: 'subtle', 'aria-level': 3}}, - {groupId: '3', renderItem: props => }, + {groupId: '3', renderItem: props => }, { groupId: '4', renderItem: ({leadingVisual: LeadingVisual, ...props}) => ( @@ -250,9 +248,7 @@ export function ComplexListFullVariantStory(): JSX.Element { )} /> ), - renderGroup: ({sx: sxProps, ...props}) => ( - - ), + renderGroup: props => , }, ]} items={[ @@ -262,7 +258,7 @@ export function ComplexListFullVariantStory(): JSX.Element { leadingVisual: SearchIcon, text: 'repo:github/memex,github/github', groupId: '1', - renderItem: props => , + renderItem: props => , }, { leadingVisual: NoteIcon, diff --git a/packages/react/src/stories/deprecated/ActionListStories.module.css b/packages/react/src/stories/deprecated/ActionListStories.module.css index 02f8e972f5f..b8dc0fe5aa4 100644 --- a/packages/react/src/stories/deprecated/ActionListStories.module.css +++ b/packages/react/src/stories/deprecated/ActionListStories.module.css @@ -16,3 +16,26 @@ /* stylelint-disable-next-line color-named */ fill: white; } + +.CustomGroupStyle { + /* stylelint-disable-next-line color-named */ + background-color: cornflowerblue; + /* stylelint-disable-next-line color-named */ + color: white; +} + +.ActionListEmphasis { + /* stylelint-disable-next-line primer/typography */ + font-weight: 700; +} + +.ActionListAccent { + /* stylelint-disable-next-line color-named */ + color: rebeccapurple; +} + +.Item[data-is-active-descendant] .ActionListAccent, +.Item:focus .ActionListAccent, +.Item:active .ActionListAccent { + color: var(--fgColor-onEmphasis); +} diff --git a/packages/styled-react/src/__tests__/__snapshots__/exports.test.ts.snap b/packages/styled-react/src/__tests__/__snapshots__/exports.test.ts.snap index 3cf32383c51..8c3a1edd8d6 100644 --- a/packages/styled-react/src/__tests__/__snapshots__/exports.test.ts.snap +++ b/packages/styled-react/src/__tests__/__snapshots__/exports.test.ts.snap @@ -57,6 +57,7 @@ exports[`@primer/styled-react exports 1`] = ` exports[`@primer/styled-react/deprecated exports 1`] = ` [ + "ActionList", "Dialog", "Octicon", "TabNav", diff --git a/packages/styled-react/src/components/Tooltip.tsx b/packages/styled-react/src/components/Tooltip.tsx index da923ed48b6..ba3bbfb34d8 100644 --- a/packages/styled-react/src/components/Tooltip.tsx +++ b/packages/styled-react/src/components/Tooltip.tsx @@ -1,8 +1,4 @@ import {Tooltip as PrimerTooltip, type TooltipProps as PrimerTooltipProps, type SxProp} from '@primer/react' -import { - Tooltip as PrimerDeprecatedTooltip, - type TooltipProps as PrimerDeprecatedTooltipProps, -} from '@primer/react/deprecated' import {Box} from './Box' import {forwardRef, type ForwardRefExoticComponent, type RefAttributes} from 'react' @@ -16,10 +12,3 @@ const Tooltip: ForwardRefExoticComponent -} -export {DeprecatedTooltip, type DeprecatedTooltipProps} diff --git a/packages/styled-react/src/components/deprecated/ActionList.tsx b/packages/styled-react/src/components/deprecated/ActionList.tsx new file mode 100644 index 00000000000..88908a51ac0 --- /dev/null +++ b/packages/styled-react/src/components/deprecated/ActionList.tsx @@ -0,0 +1,47 @@ +import {type PropsWithChildren, forwardRef} from 'react' +import { + ActionList as PrimerActionList, + type ActionListProps as PrimerActionListProps, + type ActionListItemProps as PrimerActionListItemProps, + type ActionListGroupProps as PrimerActionListGroupProps, +} from '@primer/react/deprecated' +import {sx, type SxProp} from '../../sx' +import {Box} from '../Box' +import styled from 'styled-components' +import type {ActionListDividerProps} from '@primer/react' + +type ActionListProps = PropsWithChildren +// Add explicit `as` prop since polymorphic typing doesn't carry over to type aliases +type ActionListItemProps = PropsWithChildren +type ActionListGroupProps = PropsWithChildren + +const ActionListImpl = forwardRef(function ActionList(props, ref) { + return +}) + +const StyledActionListItem = styled(PrimerActionList.Item).withConfig({ + shouldForwardProp: prop => (prop as keyof ActionListItemProps) !== 'sx', +})` + ${sx} +` + +const ActionListItem = forwardRef(({as, ...props}, ref) => ( + +)) + +function ActionListGroup(props: ActionListGroupProps) { + return +} + +function ActionListDivider(props: ActionListDividerProps) { + return +} + +const ActionList = Object.assign(ActionListImpl, { + Item: ActionListItem, + Group: ActionListGroup, + Divider: ActionListDivider, +}) + +export type {ActionListProps, ActionListItemProps, ActionListGroupProps} +export {ActionList} diff --git a/packages/styled-react/src/components/DialogV1.tsx b/packages/styled-react/src/components/deprecated/DialogV1.tsx similarity index 90% rename from packages/styled-react/src/components/DialogV1.tsx rename to packages/styled-react/src/components/deprecated/DialogV1.tsx index a9907282fde..98eba8edcb1 100644 --- a/packages/styled-react/src/components/DialogV1.tsx +++ b/packages/styled-react/src/components/deprecated/DialogV1.tsx @@ -3,10 +3,10 @@ import type { DialogProps as PrimerDialogProps, DialogHeaderProps as PrimerDialogHeaderProps, } from '@primer/react/deprecated' -import {Box} from './Box' -import type {SxProp} from '../sx' +import {Box} from '../Box' +import type {SxProp} from '../../sx' import {forwardRef} from 'react' -import type {ForwardRefComponent} from '../polymorphic' +import type {ForwardRefComponent} from '../../polymorphic' type DialogProps = PrimerDialogProps & SxProp diff --git a/packages/styled-react/src/components/Octicon.tsx b/packages/styled-react/src/components/deprecated/Octicon.tsx similarity index 95% rename from packages/styled-react/src/components/Octicon.tsx rename to packages/styled-react/src/components/deprecated/Octicon.tsx index 3a7422824cd..beb019beb83 100644 --- a/packages/styled-react/src/components/Octicon.tsx +++ b/packages/styled-react/src/components/deprecated/Octicon.tsx @@ -1,6 +1,6 @@ import {Octicon as PrimerOcticon, type OcticonProps as PrimerOcticonProps} from '@primer/react/deprecated' import styled from 'styled-components' -import {type SxProp, sx} from '../sx' +import {type SxProp, sx} from '../../sx' import {forwardRef} from 'react' /** diff --git a/packages/styled-react/src/components/TabNav.tsx b/packages/styled-react/src/components/deprecated/TabNav.tsx similarity index 96% rename from packages/styled-react/src/components/TabNav.tsx rename to packages/styled-react/src/components/deprecated/TabNav.tsx index 961d66ee51f..7cbdd34f46e 100644 --- a/packages/styled-react/src/components/TabNav.tsx +++ b/packages/styled-react/src/components/deprecated/TabNav.tsx @@ -1,6 +1,6 @@ import {TabNav as PrimerTabNav} from '@primer/react/deprecated' import type {TabNavProps as PrimerTabNavProps, TabNavLinkProps as PrimerTabNavLinkProps} from '@primer/react/deprecated' -import {sx, type SxProp} from '../sx' +import {sx, type SxProp} from '../../sx' import styled from 'styled-components' import {forwardRef} from 'react' diff --git a/packages/styled-react/src/components/deprecated/Tooltip.tsx b/packages/styled-react/src/components/deprecated/Tooltip.tsx new file mode 100644 index 00000000000..9af0200ffb9 --- /dev/null +++ b/packages/styled-react/src/components/deprecated/Tooltip.tsx @@ -0,0 +1,29 @@ +import {Tooltip as PrimerTooltip, type TooltipProps as PrimerTooltipProps} from '@primer/react/deprecated' +import {forwardRef} from 'react' +import {sx, type SxProp} from '../../sx' +import {type PropsWithChildren} from 'react' +import styled from 'styled-components' + +// Add explicit `as` prop since polymorphic typing doesn't carry over to type aliases +type TooltipProps = PropsWithChildren + +const StyledTooltip = styled(PrimerTooltip).withConfig({ + shouldForwardProp: prop => (prop as keyof TooltipProps) !== 'sx', +})` + ${sx} +` + +const TooltipImpl = forwardRef(function Tooltip({as, ...props}, ref) { + return +}) + +const Tooltip = TooltipImpl as typeof TooltipImpl & { + alignments: string[] + directions: string[] +} + +// Preserve static properties from the original component +Tooltip.alignments = PrimerTooltip.alignments +Tooltip.directions = PrimerTooltip.directions + +export {Tooltip, type TooltipProps} diff --git a/packages/styled-react/src/deprecated.tsx b/packages/styled-react/src/deprecated.tsx index 9c2b14703fa..0d06314dd35 100644 --- a/packages/styled-react/src/deprecated.tsx +++ b/packages/styled-react/src/deprecated.tsx @@ -1,4 +1,10 @@ -export {TabNav, type TabNavProps, type TabNavLinkProps} from './components/TabNav' -export {Dialog, type DialogProps, type DialogHeaderProps} from './components/DialogV1' -export {Octicon, type OcticonProps} from './components/Octicon' -export {DeprecatedTooltip as Tooltip, type DeprecatedTooltipProps as TooltipProps} from './components/Tooltip' +export {TabNav, type TabNavProps, type TabNavLinkProps} from './components/deprecated/TabNav' +export {Dialog, type DialogProps, type DialogHeaderProps} from './components/deprecated/DialogV1' +export {Octicon, type OcticonProps} from './components/deprecated/Octicon' +export {Tooltip, type TooltipProps} from './components/deprecated/Tooltip' +export { + ActionList, + type ActionListProps, + type ActionListItemProps, + type ActionListGroupProps, +} from './components/deprecated/ActionList'