sass-references/angular-material/material/chips/chip.scss

742 lines
24 KiB
SCSS
Raw Permalink Normal View History

2024-12-06 10:42:08 +08:00
@use '@angular/cdk';
@use '../core/style/layout-common';
@use '../core/focus-indicators/private' as focus-indicators-private;
@use '../core/tokens/m2/mdc/chip' as tokens-mdc-chip;
@use '../core/tokens/m2/mat/chip' as tokens-mat-chip;
@use '../core/style/vendor-prefixes';
@use '../core/tokens/token-utils';
$_checkmark-size: 20px;
$_trailing-icon-size: 18px;
$_action-padding: 12px;
$_graphic-padding: 6px;
$_trailing-action-padding: 8px;
$_avatar-leading-padding: 4px;
$_avatar-trailing-padding: 8px;
.mdc-evolution-chip,
.mdc-evolution-chip__cell,
.mdc-evolution-chip__action {
display: inline-flex;
align-items: center;
}
.mdc-evolution-chip {
position: relative;
max-width: 100%;
}
.mdc-evolution-chip__cell,
.mdc-evolution-chip__action {
height: 100%;
}
.mdc-evolution-chip__cell--primary {
// Ensures that the trailing icon is pushed to the end if the chip has a set width.
flex-basis: 100%;
overflow-x: hidden;
}
.mdc-evolution-chip__cell--trailing {
flex: 1 0 auto;
}
.mdc-evolution-chip__action {
align-items: center;
background: none;
border: none;
box-sizing: content-box;
cursor: pointer;
display: inline-flex;
justify-content: center;
outline: none;
padding: 0;
text-decoration: none;
color: inherit;
}
.mdc-evolution-chip__action--presentational {
cursor: auto;
}
.mdc-evolution-chip--disabled,
.mdc-evolution-chip__action:disabled {
pointer-events: none;
}
.mdc-evolution-chip__action--primary {
// This element can be placed on a `button` node which usually has some user agent styles.
// Reset the font so that the typography from the root element can propagate down.
font: inherit;
letter-spacing: inherit;
white-space: inherit;
overflow-x: hidden;
@include token-utils.use-tokens(tokens-mdc-chip.$prefix, tokens-mdc-chip.get-token-slots()) {
.mat-mdc-standard-chip &::before {
@include token-utils.create-token-slot(border-width, outline-width);
@include token-utils.create-token-slot(border-radius, container-shape-radius);
box-sizing: border-box;
content: '';
height: 100%;
left: 0;
position: absolute;
pointer-events: none;
top: 0;
width: 100%;
z-index: 1;
border-style: solid;
}
.mat-mdc-standard-chip & {
padding-left: $_action-padding;
padding-right: $_action-padding;
}
.mat-mdc-standard-chip.mdc-evolution-chip--with-primary-graphic & {
padding-left: 0;
padding-right: $_action-padding;
}
[dir='rtl'] .mat-mdc-standard-chip.mdc-evolution-chip--with-primary-graphic & {
padding-left: $_action-padding;
padding-right: 0;
}
.mat-mdc-standard-chip:not(.mdc-evolution-chip--disabled) &::before {
@include token-utils.create-token-slot(border-color, outline-color);
}
&:not(.mdc-evolution-chip__action--presentational):not(.mdc-ripple-upgraded):focus::before {
@include token-utils.create-token-slot(border-color, focus-outline-color);
}
.mat-mdc-standard-chip.mdc-evolution-chip--disabled &::before {
@include token-utils.create-token-slot(border-color, disabled-outline-color);
}
.mat-mdc-standard-chip.mdc-evolution-chip--selected &::before {
@include token-utils.create-token-slot(border-width, flat-selected-outline-width);
}
}
// Keeps basic listbox chips looking consistent with the other variations. Listbox chips don't
// inherit the font size, because they wrap the label in a `button` that has user agent styles.
.mat-mdc-basic-chip & {
font: inherit;
}
// Moved out into variables, because the selectors are too long.
$with-graphic: '.mdc-evolution-chip--with-primary-graphic';
$with-trailing: '.mdc-evolution-chip--with-trailing-action';
.mat-mdc-standard-chip#{$with-trailing} & {
padding-left: $_action-padding;
padding-right: 0;
}
[dir='rtl'] .mat-mdc-standard-chip#{$with-trailing} & {
padding-left: 0;
padding-right: $_action-padding;
}
.mat-mdc-standard-chip#{$with-graphic}#{$with-trailing} & {
padding-left: 0;
padding-right: 0;
}
[dir='rtl'] .mat-mdc-standard-chip#{$with-graphic}#{$with-trailing} & {
padding-left: 0;
padding-right: 0;
}
.mdc-evolution-chip--with-avatar#{$with-graphic} & {
padding-left: 0;
padding-right: $_action-padding;
}
[dir='rtl'] .mdc-evolution-chip--with-avatar#{$with-graphic} & {
padding-left: $_action-padding;
padding-right: 0;
}
.mdc-evolution-chip--with-avatar#{$with-graphic}#{$with-trailing} & {
padding-left: 0;
padding-right: 0;
}
[dir='rtl'] .mdc-evolution-chip--with-avatar#{$with-graphic}#{$with-trailing} & {
padding-left: 0;
padding-right: 0;
}
}
.mdc-evolution-chip__action--trailing {
position: relative;
overflow: visible;
@include token-utils.use-tokens(tokens-mdc-chip.$prefix, tokens-mdc-chip.get-token-slots()) {
.mat-mdc-standard-chip:not(.mdc-evolution-chip--disabled) & {
@include token-utils.create-token-slot(color, with-trailing-icon-trailing-icon-color);
}
.mat-mdc-standard-chip.mdc-evolution-chip--disabled & {
@include token-utils.create-token-slot(color,
with-trailing-icon-disabled-trailing-icon-color);
}
}
// Moved out into variables, because the selectors are too long.
$with-graphic: '.mdc-evolution-chip--with-primary-graphic';
$with-trailing: '.mdc-evolution-chip--with-trailing-action';
.mat-mdc-standard-chip#{$with-trailing} & {
padding-left: $_trailing-action-padding;
padding-right: $_trailing-action-padding;
}
.mat-mdc-standard-chip#{$with-graphic}#{$with-trailing} & {
padding-left: $_trailing-action-padding;
padding-right: $_trailing-action-padding;
}
.mdc-evolution-chip--with-avatar#{$with-graphic}#{$with-trailing} & {
padding-left: $_avatar-trailing-padding;
padding-right: $_avatar-trailing-padding;
}
[dir='rtl'] .mdc-evolution-chip--with-avatar#{$with-graphic}#{$with-trailing} & {
padding-left: $_avatar-trailing-padding;
padding-right: $_avatar-trailing-padding;
}
}
.mdc-evolution-chip__text-label {
@include vendor-prefixes.user-select(none);
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
@include token-utils.use-tokens(tokens-mdc-chip.$prefix, tokens-mdc-chip.get-token-slots()) {
.mat-mdc-standard-chip & {
@include token-utils.create-token-slot(font-family, label-text-font);
@include token-utils.create-token-slot(line-height, label-text-line-height);
@include token-utils.create-token-slot(font-size, label-text-size);
@include token-utils.create-token-slot(font-weight, label-text-weight);
@include token-utils.create-token-slot(letter-spacing, label-text-tracking);
}
.mat-mdc-standard-chip:not(.mdc-evolution-chip--disabled) & {
@include token-utils.create-token-slot(color, label-text-color);
}
.mat-mdc-standard-chip.mdc-evolution-chip--selected:not(.mdc-evolution-chip--disabled) & {
@include token-utils.create-token-slot(color, selected-label-text-color);
}
.mat-mdc-standard-chip.mdc-evolution-chip--disabled &,
.mat-mdc-standard-chip.mdc-evolution-chip--selected.mdc-evolution-chip--disabled & {
@include token-utils.create-token-slot(color, disabled-label-text-color);
}
}
}
.mdc-evolution-chip__graphic {
align-items: center;
display: inline-flex;
justify-content: center;
overflow: hidden;
pointer-events: none;
position: relative;
flex: 1 0 auto;
@include token-utils.use-tokens(tokens-mdc-chip.$prefix, tokens-mdc-chip.get-token-slots()) {
.mat-mdc-standard-chip & {
@include token-utils.create-token-slot(width, with-avatar-avatar-size);
@include token-utils.create-token-slot(height, with-avatar-avatar-size);
@include token-utils.create-token-slot(font-size, with-avatar-avatar-size);
}
}
.mdc-evolution-chip--selecting & {
transition: width 150ms 0ms cubic-bezier(0.4, 0, 0.2, 1);
}
// Moved out into variables, because the selectors are too long.
$with-icon: '.mdc-evolution-chip--with-primary-icon';
$with-graphic: '.mdc-evolution-chip--with-primary-graphic';
$with-trailing: '.mdc-evolution-chip--with-trailing-action';
.mdc-evolution-chip--selectable:not(.mdc-evolution-chip--selected):not(#{$with-icon}) & {
width: 0;
}
.mat-mdc-standard-chip#{$with-graphic} & {
padding-left: $_graphic-padding;
padding-right: $_graphic-padding;
}
.mdc-evolution-chip--with-avatar#{$with-graphic} & {
padding-left: $_avatar-leading-padding;
padding-right: $_avatar-trailing-padding;
}
[dir='rtl'] .mdc-evolution-chip--with-avatar#{$with-graphic} & {
padding-left: $_avatar-trailing-padding;
padding-right: $_avatar-leading-padding;
}
.mat-mdc-standard-chip#{$with-graphic}#{$with-trailing} & {
padding-left: $_graphic-padding;
padding-right: $_graphic-padding;
}
.mdc-evolution-chip--with-avatar#{$with-graphic}#{$with-trailing} & {
padding-left: $_avatar-leading-padding;
padding-right: $_avatar-trailing-padding;
}
[dir='rtl'] .mdc-evolution-chip--with-avatar#{$with-graphic}#{$with-trailing} & {
padding-left: $_avatar-trailing-padding;
padding-right: $_avatar-leading-padding;
}
}
.mdc-evolution-chip__checkmark {
position: absolute;
opacity: 0;
top: 50%;
left: 50%;
height: $_checkmark-size;
width: $_checkmark-size;
@include token-utils.use-tokens(tokens-mdc-chip.$prefix, tokens-mdc-chip.get-token-slots()) {
.mat-mdc-standard-chip:not(.mdc-evolution-chip--disabled) & {
@include token-utils.create-token-slot(color, with-icon-selected-icon-color);
}
.mat-mdc-standard-chip.mdc-evolution-chip--disabled & {
@include token-utils.create-token-slot(color, with-icon-disabled-icon-color);
}
}
.mdc-evolution-chip--selecting & {
transition: transform 150ms 0ms cubic-bezier(0.4, 0, 0.2, 1);
transform: translate(-75%, -50%);
}
.mdc-evolution-chip--selected & {
transform: translate(-50%, -50%);
opacity: 1;
}
}
.mdc-evolution-chip__checkmark-svg {
display: block;
}
.mdc-evolution-chip__checkmark-path {
stroke-width: 2px;
stroke-dasharray: 29.7833385;
stroke-dashoffset: 29.7833385;
stroke: currentColor;
.mdc-evolution-chip--selecting & {
transition: stroke-dashoffset 150ms 45ms cubic-bezier(0.4, 0, 0.2, 1);
}
.mdc-evolution-chip--selected & {
stroke-dashoffset: 0;
}
@include cdk.high-contrast {
// SVG colors won't be changed in high contrast mode and since the checkmark is white
// by default, it'll blend in with the background in black-on-white mode. Override the
// color to ensure that it's visible. We need !important, because the theme styles are
// very specific.
stroke: CanvasText !important;
}
}
.mdc-evolution-chip__icon--trailing {
.mat-mdc-standard-chip & {
height: $_trailing-icon-size;
width: $_trailing-icon-size;
font-size: $_trailing-icon-size;
}
$disabled-icon-opacity: null;
@include token-utils.use-tokens(tokens-mdc-chip.$prefix, tokens-mdc-chip.get-token-slots()) {
$disabled-icon-opacity:
token-utils.get-token-variable(with-trailing-icon-disabled-trailing-icon-opacity);
}
// If the trailing icon is a chip-remove button, we have to factor in the trailing action
// opacity as well as the disabled opacity.
.mdc-evolution-chip--disabled &.mat-mdc-chip-remove {
@include token-utils.use-tokens(tokens-mat-chip.$prefix, tokens-mat-chip.get-token-slots()) {
$action-opacity: token-utils.get-token-variable(trailing-action-opacity);
opacity: calc(#{$action-opacity} * #{$disabled-icon-opacity});
&:focus {
$action-focus-opacity: token-utils.get-token-variable(trailing-action-focus-opacity);
opacity: calc(#{$action-focus-opacity} * #{$disabled-icon-opacity});
}
}
}
}
.mat-mdc-standard-chip {
@include token-utils.use-tokens(tokens-mdc-chip.$prefix, tokens-mdc-chip.get-token-slots()) {
@include token-utils.create-token-slot(border-radius, container-shape-radius);
@include token-utils.create-token-slot(height, container-height);
&:not(.mdc-evolution-chip--disabled) {
@include token-utils.create-token-slot(background-color, elevated-container-color);
}
&.mdc-evolution-chip--disabled {
@include token-utils.create-token-slot(background-color, elevated-disabled-container-color);
}
&.mdc-evolution-chip--selected:not(.mdc-evolution-chip--disabled) {
@include token-utils.create-token-slot(background-color, elevated-selected-container-color);
}
&.mdc-evolution-chip--selected.mdc-evolution-chip--disabled {
@include token-utils.create-token-slot(background-color,
flat-disabled-selected-container-color);
}
}
@include cdk.high-contrast {
outline: solid 1px;
}
}
.mdc-evolution-chip__icon--primary {
@include token-utils.use-tokens(tokens-mdc-chip.$prefix, tokens-mdc-chip.get-token-slots()) {
.mat-mdc-standard-chip & {
@include token-utils.create-token-slot(border-radius, with-avatar-avatar-shape-radius);
@include token-utils.create-token-slot(width, with-icon-icon-size);
@include token-utils.create-token-slot(height, with-icon-icon-size);
@include token-utils.create-token-slot(font-size, with-icon-icon-size);
}
.mdc-evolution-chip--selected & {
opacity: 0;
}
.mat-mdc-standard-chip:not(.mdc-evolution-chip--disabled) & {
@include token-utils.create-token-slot(color, with-icon-icon-color);
}
.mat-mdc-standard-chip.mdc-evolution-chip--disabled & {
@include token-utils.create-token-slot(color, with-icon-disabled-icon-color);
}
}
}
// The highlighted attribute is used to make the chip appear as selected on-demand,
// aside from showing the selected indicator. We achieve this by re-mapping the base
// tokens to the highlighted ones. Note that we only need to do this for the tokens
// that we don't re-implement ourselves below.
// TODO(crisbeto): with some future refactors we may be able to clean this up.
.mat-mdc-chip-highlighted {
@include token-utils.use-tokens(tokens-mdc-chip.$prefix, tokens-mdc-chip.get-token-slots()) {
$highlighted-remapped-tokens: (
with-icon-icon-color: with-icon-selected-icon-color,
elevated-container-color: elevated-selected-container-color,
label-text-color: selected-label-text-color,
outline-width: flat-selected-outline-width,
);
@each $selected, $base in $highlighted-remapped-tokens {
#{token-utils.get-token-variable-name($selected)}: token-utils.get-token-variable($base);
}
}
}
// Add additional slots for the MDC chip tokens, needed in Angular Material.
@include token-utils.use-tokens(tokens-mdc-chip.$prefix, tokens-mdc-chip.get-token-slots()) {
.mat-mdc-chip-focus-overlay {
@include token-utils.create-token-slot(background, focus-state-layer-color);
.mat-mdc-chip-selected &,
.mat-mdc-chip-highlighted & {
@include token-utils.create-token-slot(background, selected-focus-state-layer-color);
}
.mat-mdc-chip:hover & {
@include token-utils.create-token-slot(background, hover-state-layer-color);
@include token-utils.create-token-slot(opacity, hover-state-layer-opacity);
}
.mat-mdc-chip-selected:hover,
.mat-mdc-chip-highlighted:hover & {
@include token-utils.create-token-slot(background, selected-hover-state-layer-color);
@include token-utils.create-token-slot(opacity, selected-hover-state-layer-opacity);
}
.mat-mdc-chip.cdk-focused & {
@include token-utils.create-token-slot(background, focus-state-layer-color);
@include token-utils.create-token-slot(opacity, focus-state-layer-opacity);
}
.mat-mdc-chip-selected.cdk-focused &,
.mat-mdc-chip-highlighted.cdk-focused & {
@include token-utils.create-token-slot(background, selected-focus-state-layer-color);
@include token-utils.create-token-slot(opacity, selected-focus-state-layer-opacity);
}
}
.mdc-evolution-chip--disabled:not(.mdc-evolution-chip--selected) .mat-mdc-chip-avatar {
@include token-utils.create-token-slot(opacity, with-avatar-disabled-avatar-opacity);
}
.mdc-evolution-chip--disabled .mdc-evolution-chip__icon--trailing {
@include token-utils.create-token-slot(
opacity, with-trailing-icon-disabled-trailing-icon-opacity);
}
.mdc-evolution-chip--disabled.mdc-evolution-chip--selected .mdc-evolution-chip__checkmark {
@include token-utils.create-token-slot(opacity, with-icon-disabled-icon-opacity);
}
}
@include token-utils.use-tokens(tokens-mat-chip.$prefix, tokens-mat-chip.get-token-slots()) {
// Historically, MDC did not support disabled chips, so we needed our own disabled styles.
// Now that MDC supports disabled styles, we should switch to using theirs.
.mat-mdc-standard-chip {
&.mdc-evolution-chip--disabled {
@include token-utils.create-token-slot(opacity, disabled-container-opacity);
}
&.mdc-evolution-chip--selected,
&.mat-mdc-chip-highlighted {
.mdc-evolution-chip__icon--trailing {
@include token-utils.create-token-slot(color, selected-trailing-icon-color);
}
&.mdc-evolution-chip--disabled .mdc-evolution-chip__icon--trailing {
@include token-utils.create-token-slot(color, selected-disabled-trailing-icon-color);
}
}
}
.mat-mdc-chip-remove {
@include token-utils.create-token-slot(opacity, trailing-action-opacity);
&:focus {
@include token-utils.create-token-slot(opacity, trailing-action-focus-opacity);
}
&::after {
@include token-utils.create-token-slot(background-color, trailing-action-state-layer-color);
}
&:hover::after {
@include token-utils.create-token-slot(opacity, trailing-action-hover-state-layer-opacity);
}
&:focus::after {
@include token-utils.create-token-slot(opacity, trailing-action-focus-state-layer-opacity);
}
}
.mat-mdc-chip-selected .mat-mdc-chip-remove::after,
.mat-mdc-chip-highlighted .mat-mdc-chip-remove::after {
@include token-utils.create-token-slot(background-color,
selected-trailing-action-state-layer-color);
}
}
.mat-mdc-standard-chip {
-webkit-tap-highlight-color: transparent;
// MDC sets `overflow: hidden` on these elements in order to truncate the text. This is
// unnecessary since our chips don't truncate their text and it makes it difficult to style
// the strong focus indicators so we need to override it.
.mdc-evolution-chip__cell--primary,
.mdc-evolution-chip__action--primary,
.mat-mdc-chip-action-label {
overflow: visible;
}
// MDC sizes and positions this element using `width`, `height` and `padding`.
// This usually works, but it's common for apps to add `box-sizing: border-box`
// to all elements on the page which can cause the graphic to be clipped.
// Set an explicit `box-sizing` in order to avoid these issues.
.mat-mdc-chip-graphic,
.mat-mdc-chip-trailing-icon {
box-sizing: content-box;
}
&._mat-animation-noopable {
&,
.mdc-evolution-chip__graphic,
.mdc-evolution-chip__checkmark,
.mdc-evolution-chip__checkmark-path {
// MDC listens to `transitionend` events on some of these
// elements so we can't disable the transitions completely.
transition-duration: 1ms;
animation-duration: 1ms;
}
}
}
// MDC's focus and hover indication is handled through their ripple which we currently
// don't use due to size concerns so we have to re-implement it ourselves.
.mat-mdc-chip-focus-overlay {
@include layout-common.fill;
pointer-events: none;
opacity: 0;
border-radius: inherit;
transition: opacity 150ms linear;
._mat-animation-noopable & {
transition: none;
}
.mat-mdc-basic-chip & {
display: none;
}
}
// The ripple container should match the bounds of the entire chip.
.mat-mdc-chip .mat-ripple.mat-mdc-chip-ripple {
@include layout-common.fill;
// Disable pointer events for the ripple container and state overlay because the container
// will overlay the user content and we don't want to disable mouse events on the user content.
// Pointer events can be safely disabled because the ripple trigger element is the host element.
pointer-events: none;
// Inherit the border radius from the parent so that state overlay and ripples don't exceed the
// parent button boundaries.
border-radius: inherit;
}
.mat-mdc-chip-avatar {
// In case an icon or text is used as an avatar.
text-align: center;
line-height: 1;
// Technically the avatar is only supposed to have an image, but we also allow for icons.
// Set the color so the icons inherit the correct color.
color: var(--mdc-chip-with-icon-icon-color, currentColor);
}
// Required for the strong focus indicator to fill the chip.
.mat-mdc-chip {
position: relative;
// `.mat-mdc-chip-action-label` below sets a `z-index: 1` to put the label above the focus
// overlay, but that can also cause it to appear above other elements like sticky columns
// (see #26793). Set an explicit `z-index` to prevent the label from leaking out.
z-index: 0;
}
.mat-mdc-chip-action-label {
// MDC centers the text, but we have a lot of internal customers who have it at the start.
text-align: left;
// Give the text label a higher z-index than the focus overlay to ensure that the focus overlay
// does not affect the color of the text. Material spec requires state layer to not interfere with
// text color.
z-index: 1;
[dir='rtl'] & {
text-align: right;
}
// When a chip has a trailing action, it'll have two focusable elements when navigating with
// the arrow keys: the primary action and the trailing one. If that's the case, we apply
// `position: relative` to the primary action container so that the indicators is only around
// the text label. This allows for it to be distinguished from the indicator on the trailing icon.
.mat-mdc-chip.mdc-evolution-chip--with-trailing-action & {
position: relative;
}
.mat-mdc-chip-primary-focus-indicator {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
pointer-events: none;
}
// For the chip element, default inset/offset values are necessary to ensure that
// the focus indicator is sufficiently contrastive and renders appropriately.
.mat-focus-indicator::before {
$default-border-width: focus-indicators-private.$default-border-width;
$border-width: var(--mat-focus-indicator-border-width, #{$default-border-width});
$offset: calc(#{$border-width} + 2px);
margin: calc(#{$offset} * -1);
}
}
.mat-mdc-chip-remove {
&::before {
$default-border-width: focus-indicators-private.$default-border-width;
$offset: var(--mat-focus-indicator-border-width, #{$default-border-width});
margin: calc(#{$offset} * -1);
// MDC sets a padding a on the chip button which stretches out the focus indicator.
left: 8px;
right: 8px;
}
// Used as a state overlay.
&::after {
$_touch-target-size: 48px;
$_ripple-size: 24px;
$offset: 3px;
content: '';
display: block;
opacity: 0;
position: absolute;
top: 0 - $offset;
bottom: 0 - $offset;
left: 8px - $offset;
right: 8px - $offset;
border-radius: 50%;
box-sizing: border-box;
padding: calc(($_touch-target-size - $_ripple-size)/2);
margin: calc((($_touch-target-size - $_ripple-size)/2) * -1);
// stylelint-disable material/no-prefixes
background-clip: content-box;
}
.mat-icon {
width: $_trailing-icon-size;
height: $_trailing-icon-size;
font-size: $_trailing-icon-size;
box-sizing: content-box;
}
}
.mat-chip-edit-input {
cursor: text;
display: inline-block;
color: inherit;
outline: 0;
}
// Single-selection chips show their selected state using a background color which won't be visible
// in high contrast mode. This isn't necessary in multi-selection since there's a checkmark.
.mat-mdc-chip-selected:not(.mat-mdc-chip-multiple) {
@include cdk.high-contrast {
outline-width: 3px;
}
}
// The chip has multiple focus targets so we have to put the indicator on
// a separate element, rather than on the focusable element itself.
.mat-mdc-chip-action:focus .mat-focus-indicator::before {
content: '';
}