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
50 changes: 50 additions & 0 deletions packages/react/src/Token/_RemoveTokenButton.module.css
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
.TokenButton {
display: inline-flex;
padding: 0;
margin-left: var(--base-size-4);
font-family: inherit;
color: currentColor;
text-decoration: none;
cursor: pointer;
user-select: none;
background-color: transparent;
border: 0;
border-radius: var(--borderRadius-full);
justify-content: center;
align-items: center;
appearance: none;
align-self: baseline;
}

.TokenButton[data-size='small'] {
width: var(--base-size-16);
height: var(--base-size-16);
}

.TokenButton[data-size='medium'] {
width: 20px;
height: 20px;
}

.TokenButton[data-size='large'] {
width: var(--base-size-24);
height: var(--base-size-24);
margin-left: var(--base-size-8);
}

.TokenButton[data-size='xlarge'] {
width: var(--base-size-32);
height: var(--base-size-32);
margin-left: var(--base-size-8);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious could you add this to the .TokenButton[data-size='xlarge'] and .TokenButton[data-size='large'] selectors instead of adding .Bigger?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good callout! Update

}

.TokenButton:hover,
.TokenButton:focus {
/* TODO: choose a better functional color variable for this */
background-color: var(--bgColor-neutral-muted);
}

.TokenButton:active {
/* TODO: choose a better functional color variable for this */
background-color: var(--bgColor-neutral-muted);
}
115 changes: 69 additions & 46 deletions packages/react/src/Token/_RemoveTokenButton.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,19 +2,24 @@ import React from 'react'
import {XIcon} from '@primer/octicons-react'
import styled, {css} from 'styled-components'
import {variant} from 'styled-system'
import {clsx} from 'clsx'
import {get} from '../constants'
import type {SxProp} from '../sx'
import sx from '../sx'
import type {ComponentProps} from '../utils/types'
import sx, {type SxProp} from '../sx'
import type {TokenSizeKeys} from './TokenBase'
import {tokenSizes, defaultTokenSize} from './TokenBase'
import {toggleStyledComponent} from '../internal/utils/toggleStyledComponent'
import {useFeatureFlag} from '../FeatureFlags'

interface TokenButtonProps {
import classes from './_RemoveTokenButton.module.css'

interface TokenButtonProps extends SxProp {
borderOffset?: number
size?: TokenSizeKeys
isParentInteractive?: boolean
}

const CSS_MODULES_FEATURE_FLAG = 'primer_react_css_modules_team'

const variants = variant<{height: string; width: string}, TokenSizeKeys>({
prop: 'size',
variants: {
Expand All@@ -39,66 +44,84 @@ const variants = variant<{height: string; width: string}, TokenSizeKeys>({

const getTokenButtonIconSize = (size?: TokenSizeKeys) => parseInt(tokenSizes[size || defaultTokenSize], 10) * 0.75

const StyledTokenButton = styled.span<TokenButtonProps & SxProp>`
background-color: transparent;
font-family: inherit;
color: currentColor;
cursor: pointer;
display: inline-flex;
justify-content: center;
align-items: center;
user-select: none;
appearance: none;
text-decoration: none;
padding: 0;
transform: ${props => `translate(${props.borderOffset}px, -${props.borderOffset}px)`};
align-self: baseline;
border: 0;
border-radius: 999px;
const StyledTokenButton = toggleStyledComponent(
CSS_MODULES_FEATURE_FLAG,
'span',
styled.span<TokenButtonProps>`
background-color: transparent;
font-family: inherit;
color: currentColor;
cursor: pointer;
display: inline-flex;
justify-content: center;
align-items: center;
user-select: none;
appearance: none;
text-decoration: none;
padding: 0;
transform: ${props => `translate(${props.borderOffset}px, -${props.borderOffset}px)`};
align-self: baseline;
border: 0;
border-radius: 999px;

${props => {
switch (props.size) {
case 'large':
case 'xlarge':
return css`
margin-left: ${get('space.2')};
`
default:
return css`
margin-left: ${get('space.1')};
`
}
}}

${props => {
switch (props.size) {
case 'large':
case 'xlarge':
return css`
margin-left: ${get('space.2')};
`
default:
return css`
margin-left: ${get('space.1')};
`
&:hover,
&:focus {
// TODO: choose a better functional color variable for this
background-color: ${get('colors.neutral.muted')};
}
}}

&:hover,
&:focus {
// TODO: choose a better functional color variable for this
background-color: ${get('colors.neutral.muted')};
}
&:active {
// TODO: choose a better functional color variable for this
background-color: ${get('colors.neutral.subtle')};
}

&:active {
// TODO: choose a better functional color variable for this
background-color: ${get('colors.neutral.subtle')};
}
${variants}
${sx}
`,
)

${variants}
${sx}
`
type RemoveTokenButtonProps = TokenButtonProps & Omit<React.HTMLProps<HTMLSpanElement | HTMLButtonElement>, 'size'>

const RemoveTokenButton: React.FC<React.PropsWithChildren<ComponentProps<typeof StyledTokenButton>>> = ({
const RemoveTokenButton = ({
'aria-label': ariaLabel,
isParentInteractive,
size = defaultTokenSize,
className,
...rest
}) => {
}: React.PropsWithChildren<RemoveTokenButtonProps>) => {
delete rest.children

const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG)

return (
<StyledTokenButton
as={isParentInteractive ? 'span' : 'button'}
tabIndex={isParentInteractive ? -1 : undefined}
aria-label={!isParentInteractive ? 'Remove token' : ariaLabel}
size={size}
data-size={size}
className={clsx(enabled && classes.TokenButton, className)}
style={
enabled
? {
transform: `translate(${rest.borderOffset}px, -${rest.borderOffset}px)`,
}
: {}
}
{...rest}
>
<XIcon size={getTokenButtonIconSize(size)} />
Expand Down
46 changes: 46 additions & 0 deletions packages/react/src/Token/_TokenTextContainer.module.css
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
.TokenTextContainer {
width: auto;
min-width: 0;
padding: 0;
margin: 0;
overflow: hidden;
font: inherit;
/* stylelint-disable-next-line primer/typography */
line-height: normal;
color: inherit;

/* reset anchor styles */
color: currentColor;
text-decoration: none;
text-overflow: ellipsis;
white-space: nowrap;

/* reset button styles, make the cursor a pointer, and add line-height */
background: transparent;
border: none;
flex-grow: 1;
-webkit-font-smoothing: inherit;
-moz-osx-font-smoothing: inherit;
appearance: none;
}

/* Position psuedo-element above text content, but below the
remove button.
This ensures the <a> or <button> receives the click no
matter where on the token the user clicks. */
.TokenTextContainer:is(a, button, [tabIndex='0']) {
cursor: pointer;
}

/* Position psuedo-element above text content, but below the
remove button.
This ensures the <a> or <button> receives the click no
matter where on the token the user clicks. */
.TokenTextContainer:is(a, button, [tabIndex='0'])::after {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
content: '';
}
101 changes: 61 additions & 40 deletions packages/react/src/Token/_TokenTextContainer.tsx
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,67 @@
import styled from 'styled-components'
import type {TokenBaseProps} from './TokenBase'
import {toggleStyledComponent} from '../internal/utils/toggleStyledComponent'
import React from 'react'
import classes from './_TokenTextContainer.module.css'
import {useFeatureFlag} from '../FeatureFlags'
import {clsx} from 'clsx'

const TokenTextContainer = styled('span')<Partial<TokenBaseProps>>`
flex-grow: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;

// reset button styles, make the cursor a pointer, and add line-height
background: transparent;
border: none;
color: inherit;
font: inherit;
margin: 0;
padding: 0;
width: auto;
-webkit-font-smoothing: inherit;
-moz-osx-font-smoothing: inherit;
-webkit-appearance: none;
line-height: normal;

// reset anchor styles
color: currentColor;
text-decoration: none;

// Position psuedo-element above text content, but below the
// remove button.
// This ensures the <a> or <button> receives the click no
// matter where on the token the user clicks.
&:is(a, button, [tabIndex='0']) {
cursor: pointer;

&:after {
content: '';
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
const CSS_MODULES_FEATURE_FLAG = 'primer_react_css_modules_team'

const StyledTokenTextContainer = toggleStyledComponent(
CSS_MODULES_FEATURE_FLAG,
'span',
styled('span')`
flex-grow: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;

// reset button styles, make the cursor a pointer, and add line-height
background: transparent;
border: none;
color: inherit;
font: inherit;
margin: 0;
padding: 0;
width: auto;
-webkit-font-smoothing: inherit;
-moz-osx-font-smoothing: inherit;
-webkit-appearance: none;
line-height: normal;

// reset anchor styles
color: currentColor;
text-decoration: none;

// Position psuedo-element above text content, but below the
// remove button.
// This ensures the <a> or <button> receives the click no
// matter where on the token the user clicks.
&:is(a, button, [tabIndex='0']) {
cursor: pointer;

&:after {
content: '';
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
}
}
`
`,
)

const TokenTextContainer = ({children, ...props}: React.PropsWithChildren<Partial<TokenBaseProps>>) => {
const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG)

return (
<StyledTokenTextContainer className={clsx(enabled && classes.TokenTextContainer)} {...props}>
{children}
</StyledTokenTextContainer>
)
}

export default TokenTextContainer
Loading