1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-11 13:53:34 +00:00

[CL-712] Update icon button, components using it, and affected virtual scroll heights (#15683)

This commit is contained in:
Vicki League
2025-08-05 10:58:49 -04:00
committed by GitHub
parent 40a1a0a2b7
commit 26c0176e2e
34 changed files with 90 additions and 187 deletions

View File

@@ -1,4 +1,4 @@
<div class="tw-mr-[5px] tw-mt-1"> <div class="tw-me-2 tw-mt-1">
<button <button
*ngIf="currentAccount$ | async as currentAccount; else defaultButton" *ngIf="currentAccount$ | async as currentAccount; else defaultButton"
type="button" type="button"

View File

@@ -1,16 +1,21 @@
<!--
end padding is less than start padding to prioritize visual alignment when icon buttons are used at the end of the end slot.
other elements used at the end of the end slot may need to add their own margin/padding to achieve visual alignment.
-->
<header <header
class="tw-p-3 bit-compact:tw-p-2 tw-pl-4 bit-compact:tw-pl-3 tw-transition-colors tw-duration-200 tw-border-0 tw-border-b tw-border-solid" class="tw-py-3 bit-compact:tw-py-2 tw-pe-1 bit-compact:tw-pe-0.5 tw-transition-colors tw-duration-200 tw-border-0 tw-border-b tw-border-solid"
[ngClass]="{ [ngClass]="{
'tw-bg-background-alt tw-border-transparent': 'tw-bg-background-alt tw-border-transparent':
this.background === 'alt' && !pageContentScrolled(), this.background === 'alt' && !pageContentScrolled(),
'tw-bg-background tw-border-secondary-300': 'tw-bg-background tw-border-secondary-300':
(this.background === 'alt' && pageContentScrolled()) || this.background === 'default', (this.background === 'alt' && pageContentScrolled()) || this.background === 'default',
'tw-ps-4 bit-compact:tw-ps-3': !showBackButton,
'tw-ps-1 bit-compact:tw-ps-0': showBackButton,
}" }"
> >
<div class="tw-max-w-screen-sm tw-mx-auto tw-flex tw-justify-between tw-w-full"> <div class="tw-max-w-screen-sm tw-mx-auto tw-flex tw-justify-between tw-w-full">
<div class="tw-inline-flex tw-items-center tw-gap-2 tw-h-9"> <div class="tw-inline-flex tw-items-center tw-gap-2 tw-h-9">
<button <button
class="-tw-ml-1"
bitIconButton="bwi-angle-left" bitIconButton="bwi-angle-left"
type="button" type="button"
*ngIf="showBackButton" *ngIf="showBackButton"

View File

@@ -117,7 +117,7 @@ class MockPopoutButtonComponent {}
@Component({ @Component({
selector: "mock-current-account", selector: "mock-current-account",
template: ` template: `
<button class="tw-bg-transparent tw-border-none" type="button"> <button class="tw-bg-transparent tw-border-none tw-p-0 tw-me-1" type="button">
<bit-avatar text="Ash Ketchum" size="small"></bit-avatar> <bit-avatar text="Ash Ketchum" size="small"></bit-avatar>
</button> </button>
`, `,
@@ -654,7 +654,7 @@ export const WithVirtualScrollChild: Story = {
<bit-section> <bit-section>
@defer (on immediate) { @defer (on immediate) {
<bit-item-group aria-label="Mock Vault Items"> <bit-item-group aria-label="Mock Vault Items">
<cdk-virtual-scroll-viewport itemSize="61" bitScrollLayout> <cdk-virtual-scroll-viewport itemSize="59" bitScrollLayout>
<bit-item *cdkVirtualFor="let item of data; index as i"> <bit-item *cdkVirtualFor="let item of data; index as i">
<button type="button" bit-item-content> <button type="button" bit-item-content>
<i <i

View File

@@ -89,8 +89,8 @@ export class GroupsComponent {
protected searchControl = new FormControl(""); protected searchControl = new FormControl("");
// Fixed sizes used for cdkVirtualScroll // Fixed sizes used for cdkVirtualScroll
protected rowHeight = 52; protected rowHeight = 50;
protected rowHeightClass = `tw-h-[52px]`; protected rowHeightClass = `tw-h-[50px]`;
protected ModalTabType = GroupAddEditTabType; protected ModalTabType = GroupAddEditTabType;
private refreshGroups$ = new BehaviorSubject<void>(null); private refreshGroups$ = new BehaviorSubject<void>(null);

View File

@@ -111,8 +111,8 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
protected showUserManagementControls$: Observable<boolean>; protected showUserManagementControls$: Observable<boolean>;
// Fixed sizes used for cdkVirtualScroll // Fixed sizes used for cdkVirtualScroll
protected rowHeight = 69; protected rowHeight = 66;
protected rowHeightClass = `tw-h-[69px]`; protected rowHeightClass = `tw-h-[66px]`;
private organizationUsersCount = 0; private organizationUsersCount = 0;

View File

@@ -60,7 +60,6 @@
<button <button
type="button" type="button"
bitIconButton="bwi-ellipsis-v" bitIconButton="bwi-ellipsis-v"
buttonType="secondary"
[bitMenuTriggerFor]="appListDropdown" [bitMenuTriggerFor]="appListDropdown"
class="tw-border-0 tw-bg-transparent tw-p-0" class="tw-border-0 tw-bg-transparent tw-p-0"
></button> ></button>

View File

@@ -28,8 +28,8 @@ import { VaultItem } from "./vault-item";
import { VaultItemEvent } from "./vault-item-event"; import { VaultItemEvent } from "./vault-item-event";
// Fixed manual row height required due to how cdk-virtual-scroll works // Fixed manual row height required due to how cdk-virtual-scroll works
export const RowHeight = 75.5; export const RowHeight = 75;
export const RowHeightClass = `tw-h-[75.5px]`; export const RowHeightClass = `tw-h-[75px]`;
const MaxSelectionCount = 500; const MaxSelectionCount = 500;

View File

@@ -52,8 +52,8 @@ export class MembersComponent extends BaseMembersComponent<ProviderUser> {
dataSource = new MembersTableDataSource(); dataSource = new MembersTableDataSource();
loading = true; loading = true;
providerId: string; providerId: string;
rowHeight = 69; rowHeight = 70;
rowHeightClass = `tw-h-[69px]`; rowHeightClass = `tw-h-[70px]`;
status: ProviderUserStatusType = null; status: ProviderUserStatusType = null;
userStatusType = ProviderUserStatusType; userStatusType = ProviderUserStatusType;

View File

@@ -1,5 +1,5 @@
<div <div
class="tw-rounded-2xl tw-bg-primary-100 tw-border-primary-600 tw-border-solid tw-border tw-p-4 tw-pt-3 tw-flex tw-flex-col tw-gap-2 tw-mb-4" class="tw-rounded-2xl tw-bg-primary-100 tw-border-primary-600 tw-border-solid tw-border tw-p-4 tw-pt-2 tw-flex tw-flex-col tw-gap-2 tw-mb-4"
> >
<div class="tw-flex tw-justify-between tw-items-start tw-flex-grow"> <div class="tw-flex tw-justify-between tw-items-start tw-flex-grow">
<div> <div>
@@ -20,6 +20,7 @@
(click)="handleDismiss()" (click)="handleDismiss()"
[attr.title]="'close' | i18n" [attr.title]="'close' | i18n"
[attr.aria-label]="'close' | i18n" [attr.aria-label]="'close' | i18n"
class="-tw-me-2"
></button> ></button>
</div> </div>

View File

@@ -35,7 +35,7 @@ const template = `
<button class="tw-me-2" type="button" buttonType="secondary" bitButton bitFormButton>Cancel</button> <button class="tw-me-2" type="button" buttonType="secondary" bitButton bitFormButton>Cancel</button>
<button class="tw-me-2" type="button" buttonType="danger" bitButton bitFormButton [bitAction]="delete">Delete</button> <button class="tw-me-2" type="button" buttonType="danger" bitButton bitFormButton [bitAction]="delete">Delete</button>
<button class="tw-me-2" type="button" buttonType="secondary" bitButton bitFormButton [disabled]="true">Disabled</button> <button class="tw-me-2" type="button" buttonType="secondary" bitButton bitFormButton [disabled]="true">Disabled</button>
<button class="tw-me-2" type="button" buttonType="secondary" bitIconButton="bwi-star" bitFormButton [bitAction]="delete">Delete</button> <button class="tw-me-2" type="button" buttonType="muted" bitIconButton="bwi-star" bitFormButton [bitAction]="delete">Delete</button>
</form>`; </form>`;
@Component({ @Component({

View File

@@ -1,5 +1,5 @@
<div <div
class="tw-flex tw-items-center tw-gap-2 tw-p-2 tw-ps-4 tw-text-main tw-border-transparent tw-bg-clip-padding tw-border-solid tw-border-b tw-border-0" class="tw-flex tw-items-center tw-gap-4 tw-p-2 tw-ps-4 tw-text-main tw-border-transparent tw-bg-clip-padding tw-border-solid tw-border-b tw-border-0"
[ngClass]="bannerClass" [ngClass]="bannerClass"
[attr.role]="useAlertRole() ? 'status' : null" [attr.role]="useAlertRole() ? 'status' : null"
[attr.aria-live]="useAlertRole() ? 'polite' : null" [attr.aria-live]="useAlertRole() ? 'polite' : null"
@@ -14,11 +14,10 @@
<!-- Overriding hover and focus-visible colors for a11y against colored background --> <!-- Overriding hover and focus-visible colors for a11y against colored background -->
@if (showClose()) { @if (showClose()) {
<button <button
class="hover:tw-border-text-main focus-visible:before:tw-ring-text-main"
type="button" type="button"
bitIconButton="bwi-close" bitIconButton="bwi-close"
buttonType="main" buttonType="main"
size="default" size="small"
(click)="onClose.emit()" (click)="onClose.emit()"
[attr.title]="'close' | i18n" [attr.title]="'close' | i18n"
[attr.aria-label]="'close' | i18n" [attr.aria-label]="'close' | i18n"

View File

@@ -46,7 +46,7 @@
type="button" type="button"
[attr.aria-label]="'removeItem' | i18n: label" [attr.aria-label]="'removeItem' | i18n: label"
[disabled]="disabled" [disabled]="disabled"
class="tw-bg-transparent hover:tw-bg-transparent tw-outline-none tw-rounded-full tw-py-0.5 tw-px-1 tw-me-1 tw-text-[color:inherit] tw-text-[length:inherit] tw-border-solid tw-border tw-border-transparent hover:tw-border-text-contrast hover:disabled:tw-border-transparent tw-flex tw-items-center tw-justify-center focus-visible:tw-ring-2 tw-ring-text-contrast focus-visible:hover:tw-border-transparent" class="tw-bg-transparent hover:tw-bg-hover-contrast tw-outline-none tw-rounded-full tw-py-0.5 tw-px-1 tw-me-1 tw-text-[color:inherit] tw-text-[length:inherit] tw-border-solid tw-border tw-border-transparent tw-flex tw-items-center tw-justify-center focus-visible:tw-ring-2 tw-ring-text-contrast hover:disabled:tw-bg-transparent"
[ngClass]="{ [ngClass]="{
'tw-cursor-not-allowed': disabled, 'tw-cursor-not-allowed': disabled,
}" }"

View File

@@ -93,7 +93,10 @@ class StoryDialogContentComponent {
@Component({ @Component({
template: ` template: `
<bit-dialog title="Dialog Title" dialogSize="large"> <bit-dialog
title="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore"
dialogSize="large"
>
<span bitDialogContent> <span bitDialogContent>
Dialog body text goes here. Dialog body text goes here.
<br /> <br />

View File

@@ -10,8 +10,8 @@
<header <header
class="tw-flex tw-justify-between tw-items-center tw-gap-4 tw-border-0 tw-border-b tw-border-solid" class="tw-flex tw-justify-between tw-items-center tw-gap-4 tw-border-0 tw-border-b tw-border-solid"
[ngClass]="{ [ngClass]="{
'tw-p-4': !isDrawer, 'tw-p-4 has-[[biticonbutton]]:tw-pe-2': !isDrawer,
'tw-p-6 tw-pb-4': isDrawer, 'tw-px-6 tw-py-4 has-[[biticonbutton]]:tw-pe-4': isDrawer,
'tw-border-secondary-300': showHeaderBorder, 'tw-border-secondary-300': showHeaderBorder,
'tw-border-transparent': !showHeaderBorder, 'tw-border-transparent': !showHeaderBorder,
}" }"
@@ -75,7 +75,7 @@
[ngClass]="[isDrawer ? 'tw-px-6 tw-py-4' : 'tw-p-4']" [ngClass]="[isDrawer ? 'tw-px-6 tw-py-4' : 'tw-p-4']"
[ngClass]="{ [ngClass]="{
'tw-px-6 tw-py-4': isDrawer, 'tw-px-6 tw-py-4': isDrawer,
'tw-p-4': !isDrawer, 'tw-p-4 has-[[biticonbutton]]:tw-pe-2': !isDrawer,
'tw-border-secondary-300': showFooterBorder, 'tw-border-secondary-300': showFooterBorder,
'tw-border-transparent': !showFooterBorder, 'tw-border-transparent': !showFooterBorder,
}" }"

View File

@@ -1,5 +1,5 @@
<header class="tw-flex tw-justify-between tw-items-center"> <header class="tw-flex tw-justify-between tw-items-center tw-gap-4">
<div class="tw-flex tw-items-center tw-gap-1 tw-overflow-auto"> <div class="tw-flex tw-items-center tw-gap-4 tw-overflow-auto">
<ng-content select="[slot=start]"></ng-content> <ng-content select="[slot=start]"></ng-content>
<h2 bitTypography="h3" noMargin class="tw-text-main tw-mb-0 tw-truncate" [attr.title]="title()"> <h2 bitTypography="h3" noMargin class="tw-text-main tw-mb-0 tw-truncate" [attr.title]="title()">
{{ title() }} {{ title() }}

View File

@@ -50,13 +50,13 @@
> >
<div <div
#prefixContainer #prefixContainer
class="tw-flex tw-items-center tw-gap-1 tw-ps-3 tw-py-2" class="tw-flex tw-items-center tw-gap-1 tw-ps-3 has-[[biticonbutton]]:tw-ps-1 tw-py-1"
[hidden]="!prefixHasChildren()" [hidden]="!prefixHasChildren()"
> >
<ng-container *ngTemplateOutlet="prefixContent"></ng-container> <ng-container *ngTemplateOutlet="prefixContent"></ng-container>
</div> </div>
<div <div
class="tw-w-full tw-relative tw-py-2 has-[bit-select]:tw-p-0 has-[bit-multi-select]:tw-p-0 has-[input:read-only:not([hidden])]:tw-bg-secondary-100 has-[textarea:read-only:not([hidden])]:tw-bg-secondary-100" class="tw-w-full tw-relative tw-py-1 has-[bit-select]:tw-p-0 has-[bit-multi-select]:tw-p-0 has-[input:read-only:not([hidden])]:tw-bg-secondary-100 has-[textarea:read-only:not([hidden])]:tw-bg-secondary-100"
data-default-content data-default-content
[ngClass]="[ [ngClass]="[
prefixHasChildren() ? '' : 'tw-rounded-s-lg tw-ps-3', prefixHasChildren() ? '' : 'tw-rounded-s-lg tw-ps-3',
@@ -67,7 +67,7 @@
</div> </div>
<div <div
#suffixContainer #suffixContainer
class="tw-flex tw-items-center tw-gap-1 tw-pe-3 tw-py-2" class="tw-flex tw-items-center tw-pe-3 has-[[biticonbutton]]:tw-pe-1 tw-py-1"
[hidden]="!suffixHasChildren()" [hidden]="!suffixHasChildren()"
> >
<ng-container *ngTemplateOutlet="suffixContent"></ng-container> <ng-container *ngTemplateOutlet="suffixContent"></ng-container>
@@ -102,11 +102,7 @@
> >
<ng-container *ngTemplateOutlet="defaultContent"></ng-container> <ng-container *ngTemplateOutlet="defaultContent"></ng-container>
</div> </div>
<div <div #suffixContainer [hidden]="!suffixHasChildren()" class="tw-flex tw-items-center tw-pe-1">
#suffixContainer
[hidden]="!suffixHasChildren()"
class="tw-flex tw-items-center tw-gap-1 tw-pe-1"
>
<ng-container *ngTemplateOutlet="suffixContent"></ng-container> <ng-container *ngTemplateOutlet="suffixContent"></ng-container>
</div> </div>
</div> </div>

View File

@@ -350,9 +350,7 @@ export const ButtonInputGroup: Story = {
<input bitInput placeholder="Placeholder" /> <input bitInput placeholder="Placeholder" />
<button bitSuffix bitIconButton="bwi-eye" [appA11yTitle]="'Hide Label'"></button> <button bitSuffix bitIconButton="bwi-eye" [appA11yTitle]="'Hide Label'"></button>
<button bitSuffix bitIconButton="bwi-clone" [appA11yTitle]="'Clone Label'"></button> <button bitSuffix bitIconButton="bwi-clone" [appA11yTitle]="'Clone Label'"></button>
<button bitSuffix bitLink> <button bitSuffix bitIconButton="bwi-ellipsis-v" [appA11yTitle]="'Menu Label'"></button>
Apply
</button>
</bit-form-field> </bit-form-field>
`, `,
}), }),
@@ -369,9 +367,8 @@ export const DisabledButtonInputGroup: Story = {
<input bitInput placeholder="Placeholder" disabled /> <input bitInput placeholder="Placeholder" disabled />
<button bitSuffix bitIconButton="bwi-eye" disabled [appA11yTitle]="'Hide Label'"></button> <button bitSuffix bitIconButton="bwi-eye" disabled [appA11yTitle]="'Hide Label'"></button>
<button bitSuffix bitIconButton="bwi-clone" disabled [appA11yTitle]="'Clone Label'"></button> <button bitSuffix bitIconButton="bwi-clone" disabled [appA11yTitle]="'Clone Label'"></button>
<button bitSuffix bitLink disabled> <button bitSuffix bitIconButton="bwi-ellipsis-v" disabled [appA11yTitle]="'Menu Label'"></button>
Apply
</button>
</bit-form-field> </bit-form-field>
`, `,
}), }),
@@ -387,9 +384,7 @@ export const PartiallyDisabledButtonInputGroup: Story = {
<input bitInput placeholder="Placeholder" disabled /> <input bitInput placeholder="Placeholder" disabled />
<button bitSuffix bitIconButton="bwi-eye" [appA11yTitle]="'Hide Label'"></button> <button bitSuffix bitIconButton="bwi-eye" [appA11yTitle]="'Hide Label'"></button>
<button bitSuffix bitIconButton="bwi-clone" [appA11yTitle]="'Clone Label'"></button> <button bitSuffix bitIconButton="bwi-clone" [appA11yTitle]="'Clone Label'"></button>
<button bitSuffix bitLink disabled> <button bitSuffix bitIconButton="bwi-ellipsis-v" disabled [appA11yTitle]="'Menu Label'"></button>
Apply
</button>
</bit-form-field> </bit-form-field>
`, `,
}), }),

View File

@@ -1,5 +1,5 @@
<span class="tw-relative"> <span class="tw-relative tw-inline-block tw-leading-[0px]">
<span [ngClass]="{ 'tw-invisible': showLoadingStyle() }"> <span class="tw-inline-block tw-leading-[0px]" [ngClass]="{ 'tw-invisible': showLoadingStyle() }">
<i class="bwi" [ngClass]="iconClass" aria-hidden="true"></i> <i class="bwi" [ngClass]="iconClass" aria-hidden="true"></i>
</span> </span>
<span <span

View File

@@ -5,10 +5,10 @@ import { Component, computed, ElementRef, HostBinding, input, model } from "@ang
import { toObservable, toSignal } from "@angular/core/rxjs-interop"; import { toObservable, toSignal } from "@angular/core/rxjs-interop";
import { debounce, interval } from "rxjs"; import { debounce, interval } from "rxjs";
import { ButtonLikeAbstraction, ButtonType } from "../shared/button-like.abstraction"; import { ButtonLikeAbstraction } from "../shared/button-like.abstraction";
import { FocusableElement } from "../shared/focusable-element"; import { FocusableElement } from "../shared/focusable-element";
export type IconButtonType = ButtonType | "contrast" | "main" | "muted" | "light"; export type IconButtonType = "primary" | "danger" | "contrast" | "main" | "muted" | "nav-contrast";
const focusRing = [ const focusRing = [
// Workaround for box-shadow with transparent offset issue: // Workaround for box-shadow with transparent offset issue:
@@ -20,7 +20,7 @@ const focusRing = [
"before:tw-content-['']", "before:tw-content-['']",
"before:tw-block", "before:tw-block",
"before:tw-absolute", "before:tw-absolute",
"before:-tw-inset-[2px]", "before:-tw-inset-[1px]",
"before:tw-rounded-lg", "before:tw-rounded-lg",
"before:tw-transition", "before:tw-transition",
"before:tw-ring-2", "before:tw-ring-2",
@@ -30,122 +30,38 @@ const focusRing = [
const styles: Record<IconButtonType, string[]> = { const styles: Record<IconButtonType, string[]> = {
contrast: [ contrast: [
"tw-bg-transparent",
"!tw-text-contrast", "!tw-text-contrast",
"tw-border-transparent", "tw-border-transparent",
"hover:tw-bg-transparent-hover", "hover:!tw-bg-hover-contrast",
"hover:tw-border-text-contrast",
"focus-visible:before:tw-ring-text-contrast", "focus-visible:before:tw-ring-text-contrast",
...focusRing, ...focusRing,
], ],
main: [ main: ["!tw-text-main", "focus-visible:before:tw-ring-primary-600", ...focusRing],
"tw-bg-transparent",
"!tw-text-main",
"tw-border-transparent",
"hover:tw-bg-transparent-hover",
"hover:tw-border-primary-600",
"focus-visible:before:tw-ring-primary-600",
...focusRing,
],
muted: [ muted: [
"tw-bg-transparent",
"!tw-text-muted", "!tw-text-muted",
"tw-border-transparent", "tw-border-transparent",
"aria-expanded:tw-bg-text-muted", "aria-expanded:tw-bg-text-muted",
"aria-expanded:!tw-text-contrast", "aria-expanded:!tw-text-contrast",
"hover:tw-bg-transparent-hover",
"hover:tw-border-primary-600",
"focus-visible:before:tw-ring-primary-600", "focus-visible:before:tw-ring-primary-600",
"aria-expanded:hover:tw-bg-secondary-700", "aria-expanded:hover:tw-bg-secondary-700",
"aria-expanded:hover:tw-border-secondary-700", "aria-expanded:hover:tw-border-secondary-700",
...focusRing, ...focusRing,
], ],
primary: [ primary: ["!tw-text-primary-600", "focus-visible:before:tw-ring-primary-600", ...focusRing],
"tw-bg-primary-600", danger: ["!tw-text-danger-600", "focus-visible:before:tw-ring-primary-600", ...focusRing],
"!tw-text-contrast", "nav-contrast": [
"tw-border-primary-600",
"hover:tw-bg-primary-600",
"hover:tw-border-primary-600",
"focus-visible:before:tw-ring-primary-600",
...focusRing,
],
secondary: [
"tw-bg-transparent",
"!tw-text-muted",
"tw-border-text-muted",
"hover:!tw-text-contrast",
"hover:tw-bg-text-muted",
"focus-visible:before:tw-ring-primary-600",
...focusRing,
],
danger: [
"tw-bg-transparent",
"!tw-text-danger-600",
"tw-border-transparent",
"hover:!tw-text-danger-600",
"hover:tw-bg-transparent",
"hover:tw-border-primary-600",
"focus-visible:before:tw-ring-primary-600",
...focusRing,
],
light: [
"tw-bg-transparent",
"!tw-text-alt2", "!tw-text-alt2",
"tw-border-transparent", "hover:!tw-bg-hover-contrast",
"hover:tw-bg-transparent-hover",
"hover:tw-border-text-alt2",
"focus-visible:before:tw-ring-text-alt2", "focus-visible:before:tw-ring-text-alt2",
...focusRing, ...focusRing,
], ],
unstyled: [],
};
const disabledStyles: Record<IconButtonType, string[]> = {
contrast: [
"disabled:tw-opacity-60",
"disabled:hover:tw-border-transparent",
"disabled:hover:tw-bg-transparent",
],
main: [
"disabled:!tw-text-secondary-300",
"disabled:hover:tw-border-transparent",
"disabled:hover:tw-bg-transparent",
],
muted: [
"disabled:!tw-text-secondary-300",
"disabled:hover:tw-border-transparent",
"disabled:hover:tw-bg-transparent",
],
primary: [
"disabled:tw-opacity-60",
"disabled:hover:tw-border-primary-600",
"disabled:hover:tw-bg-primary-600",
],
secondary: [
"disabled:tw-opacity-60",
"disabled:hover:tw-border-text-muted",
"disabled:hover:tw-bg-transparent",
"disabled:hover:!tw-text-muted",
],
danger: [
"disabled:!tw-text-secondary-300",
"disabled:hover:tw-border-transparent",
"disabled:hover:tw-bg-transparent",
"disabled:hover:!tw-text-secondary-300",
],
light: [
"disabled:tw-opacity-60",
"disabled:hover:tw-border-transparent",
"disabled:hover:tw-bg-transparent",
],
unstyled: [],
}; };
export type IconButtonSize = "default" | "small"; export type IconButtonSize = "default" | "small";
const sizes: Record<IconButtonSize, string[]> = { const sizes: Record<IconButtonSize, string[]> = {
default: ["tw-px-2.5", "tw-py-1.5"], default: ["tw-text-xl", "tw-p-2.5", "tw-rounded-md"],
small: ["tw-leading-none", "tw-text-base", "tw-p-1"], small: ["tw-text-base", "tw-p-2", "tw-rounded"],
}; };
/** /**
* Icon buttons are used when no text accompanies the button. It consists of an icon that may be updated to any icon in the `bwi-font`, a `title` attribute, and an `aria-label`. * Icon buttons are used when no text accompanies the button. It consists of an icon that may be updated to any icon in the `bwi-font`, a `title` attribute, and an `aria-label`.
@@ -164,6 +80,13 @@ const sizes: Record<IconButtonSize, string[]> = {
imports: [NgClass], imports: [NgClass],
host: { host: {
"[attr.disabled]": "disabledAttr()", "[attr.disabled]": "disabledAttr()",
/**
* When the `bitIconButton` input is dynamic from a consumer, Angular doesn't put the
* `bitIconButton` attribute into the DOM. We use the attribute as a css selector in
* a number of components, so this manual attr binding makes sure that the css selector
* works when the input is dynamic.
*/
"[attr.bitIconButton]": "icon()",
}, },
}) })
export class BitIconButtonComponent implements ButtonLikeAbstraction, FocusableElement { export class BitIconButtonComponent implements ButtonLikeAbstraction, FocusableElement {
@@ -176,17 +99,20 @@ export class BitIconButtonComponent implements ButtonLikeAbstraction, FocusableE
@HostBinding("class") get classList() { @HostBinding("class") get classList() {
return [ return [
"tw-font-semibold", "tw-font-semibold",
"tw-border", "tw-leading-[0px]",
"tw-border-solid", "tw-border-none",
"tw-rounded-lg",
"tw-transition", "tw-transition",
"tw-bg-transparent",
"hover:tw-no-underline", "hover:tw-no-underline",
"hover:tw-bg-hover-default",
"focus:tw-outline-none", "focus:tw-outline-none",
] ]
.concat(styles[this.buttonType()]) .concat(styles[this.buttonType()])
.concat(sizes[this.size()]) .concat(sizes[this.size()])
.concat( .concat(
this.showDisabledStyles() || this.disabled() ? disabledStyles[this.buttonType()] : [], this.showDisabledStyles() || this.disabled()
? ["disabled:tw-opacity-60", "disabled:hover:!tw-bg-transparent"]
: [],
); );
} }

View File

@@ -23,9 +23,6 @@ Icon buttons can be found in other components such as: the
## Styles ## Styles
There are 4 common styles for button main, muted, contrast, and danger. The other styles follow the
button component styles.
### Main ### Main
Used for general icon buttons appearing on the themes main `background` Used for general icon buttons appearing on the themes main `background`
@@ -59,22 +56,11 @@ square.
<Canvas of={stories.Primary} /> <Canvas of={stories.Primary} />
### Secondary ### Nav Contrast
Used in place of the main button component if no text is used. This allows the button to display Used on the side nav background that is dark in both light theme and dark theme.
square.
<Canvas of={stories.Secondary} /> <Canvas of={stories.NavContrast} />
### Light
Used on a background that is dark in both light theme and dark theme. Example: end user navigation
styles.
<Canvas of={stories.Light} />
**Note:** Main and contrast styles appear on backgrounds where using `primary-700` as a focus
indicator does not meet WCAG graphic contrast guidelines.
## Sizes ## Sizes

View File

@@ -49,13 +49,6 @@ export const Primary: Story = {
}, },
}; };
export const Secondary: Story = {
...Default,
args: {
buttonType: "secondary",
},
};
export const Danger: Story = { export const Danger: Story = {
...Default, ...Default,
args: { args: {
@@ -77,18 +70,18 @@ export const Muted: Story = {
}, },
}; };
export const Light: Story = { export const NavContrast: Story = {
render: (args) => ({ render: (args) => ({
props: args, props: args,
template: /*html*/ ` template: /*html*/ `
<div class="tw-bg-background-alt2 tw-p-6 tw-w-full tw-inline-block"> <div class="tw-bg-background-alt3 tw-p-6 tw-w-full tw-inline-block">
<!-- <div> used only to provide dark background color --> <!-- <div> used only to provide dark background color -->
<button ${formatArgsForCodeSnippet<BitIconButtonComponent>(args)}>Button</button> <button ${formatArgsForCodeSnippet<BitIconButtonComponent>(args)}>Button</button>
</div> </div>
`, `,
}), }),
args: { args: {
buttonType: "light", buttonType: "nav-contrast",
}, },
}; };

View File

@@ -10,7 +10,7 @@ import { Component } from "@angular/core";
* `top` and `bottom` units should be kept in sync with `item-content.component.ts`'s y-axis padding. * `top` and `bottom` units should be kept in sync with `item-content.component.ts`'s y-axis padding.
* we want this `:after` element to be the same height as the `item-content` * we want this `:after` element to be the same height as the `item-content`
*/ */
"[&>button]:tw-relative [&>button:not([bit-item-content])]:after:tw-content-[''] [&>button]:after:tw-absolute [&>button]:after:tw-block bit-compact:[&>button]:after:tw-top-[-0.7rem] bit-compact:[&>button]:after:tw-bottom-[-0.7rem] [&>button]:after:tw-top-[-0.8rem] [&>button]:after:tw-bottom-[-0.80rem] [&>button]:after:tw-right-[-0.25rem] [&>button]:after:tw-left-[-0.25rem]", "[&>button]:tw-relative [&>button:not([bit-item-content])]:after:tw-content-[''] [&>button]:after:tw-absolute [&>button]:after:tw-block bit-compact:[&>button]:after:tw-top-[-0.7rem] bit-compact:[&>button]:after:tw-bottom-[-0.7rem] [&>button]:after:tw-top-[-0.8rem] [&>button]:after:tw-bottom-[-0.80rem] [&>button]:after:tw-right-0 [&>button]:after:tw-left-0",
}, },
}) })
export class ItemActionComponent {} export class ItemActionComponent {}

View File

@@ -4,7 +4,7 @@
<div <div
#endSlot #endSlot
class="tw-p-2 tw-flex tw-gap-1 tw-items-center" class="tw-px-2 tw-flex tw-items-center tw-gap-2 [&_button[biticonbutton]]:-tw-mx-1"
[hidden]="endSlot.childElementCount === 0" [hidden]="endSlot.childElementCount === 0"
> >
<ng-content select="[slot=end]"></ng-content> <ng-content select="[slot=end]"></ng-content>

View File

@@ -397,7 +397,7 @@ export const VirtualScrolling: Story = {
data: Array.from(Array(100000).keys()), data: Array.from(Array(100000).keys()),
}, },
template: /*html*/ ` template: /*html*/ `
<cdk-virtual-scroll-viewport [itemSize]="59" class="tw-h-[500px]"> <cdk-virtual-scroll-viewport [itemSize]="54" class="tw-h-[500px]">
<bit-item-group aria-label="Virtual Scrolling"> <bit-item-group aria-label="Virtual Scrolling">
<bit-item *cdkVirtualFor="let item of data"> <bit-item *cdkVirtualFor="let item of data">
<button bit-item-content> <button bit-item-content>

View File

@@ -15,7 +15,7 @@
type="button" type="button"
class="tw-ms-auto" class="tw-ms-auto"
[bitIconButton]="open() ? 'bwi-angle-up' : 'bwi-angle-down'" [bitIconButton]="open() ? 'bwi-angle-up' : 'bwi-angle-down'"
[buttonType]="'light'" [buttonType]="'nav-contrast'"
(click)="toggle($event)" (click)="toggle($event)"
size="small" size="small"
[title]="'toggleCollapse' | i18n" [title]="'toggleCollapse' | i18n"

View File

@@ -66,7 +66,7 @@
@if (open) { @if (open) {
<div <div
class="tw-flex tw-items-center tw-pe-1.5 tw-gap-1 [&>*:focus-visible::before]:!tw-ring-text-alt2 [&>*:hover]:!tw-border-text-alt2 [&>*]:tw-text-alt2 empty:tw-hidden" class="tw-flex tw-items-center tw-pe-1 tw-gap-1 [&>*:focus-visible::before]:!tw-ring-text-alt2 [&>*:hover]:!tw-border-text-alt2 [&>*]:tw-text-alt2 empty:tw-hidden"
> >
<ng-content select="[slot=end]"></ng-content> <ng-content select="[slot=end]"></ng-content>
</div> </div>

View File

@@ -92,7 +92,7 @@ export const WithChildButtons: Story = {
slot="end" slot="end"
class="tw-ms-auto" class="tw-ms-auto"
[bitIconButton]="'bwi-pencil-square'" [bitIconButton]="'bwi-pencil-square'"
[buttonType]="'light'" [buttonType]="'nav-contrast'"
size="small" size="small"
aria-label="option 2" aria-label="option 2"
></button> ></button>
@@ -100,7 +100,7 @@ export const WithChildButtons: Story = {
slot="end" slot="end"
class="tw-ms-auto" class="tw-ms-auto"
[bitIconButton]="'bwi-check'" [bitIconButton]="'bwi-check'"
[buttonType]="'light'" [buttonType]="'nav-contrast'"
size="small" size="small"
aria-label="option 3" aria-label="option 3"
></button> ></button>

View File

@@ -34,7 +34,7 @@
type="button" type="button"
class="tw-mx-auto tw-block tw-max-w-fit" class="tw-mx-auto tw-block tw-max-w-fit"
[bitIconButton]="data.open ? 'bwi-angle-left' : 'bwi-angle-right'" [bitIconButton]="data.open ? 'bwi-angle-left' : 'bwi-angle-right'"
buttonType="light" buttonType="nav-contrast"
size="small" size="small"
(click)="sideNavService.toggle()" (click)="sideNavService.toggle()"
[attr.aria-label]="'toggleSideNavigation' | i18n" [attr.aria-label]="'toggleSideNavigation' | i18n"

View File

@@ -4,8 +4,8 @@
<div <div
class="tw-relative tw-z-20 tw-w-72 tw-break-words tw-bg-background tw-pb-4 tw-pt-2 tw-text-main" class="tw-relative tw-z-20 tw-w-72 tw-break-words tw-bg-background tw-pb-4 tw-pt-2 tw-text-main"
> >
<div class="tw-mb-1 tw-me-2 tw-flex tw-items-start tw-justify-between tw-gap-4 tw-ps-4"> <div class="tw-me-2 tw-flex tw-items-start tw-justify-between tw-gap-4 tw-ps-4">
<h2 bitTypography="h5" class="tw-mt-1 tw-font-semibold"> <h2 bitTypography="h5" class="tw-font-semibold tw-mt-1">
{{ title() }} {{ title() }}
</h2> </h2>
<button <button
@@ -15,7 +15,6 @@
[attr.aria-label]="'close' | i18n" [attr.aria-label]="'close' | i18n"
(click)="closed.emit()" (click)="closed.emit()"
size="small" size="small"
class="tw-mt-0.5"
></button> ></button>
</div> </div>
<div bitTypography="body2" class="tw-px-4"> <div bitTypography="body2" class="tw-px-4">

View File

@@ -68,7 +68,7 @@ const popoverContent = /*html*/ `
<li>Esse labore veniam tempora</li> <li>Esse labore veniam tempora</li>
<li>Adipisicing elit ipsum <a href="#" bitLink>iustolaborum</a></li> <li>Adipisicing elit ipsum <a href="#" bitLink>iustolaborum</a></li>
</ul> </ul>
<button bitButton class="tw-mt-3" (click)="triggerRef.closePopover()">Close</button> <button bitButton class="tw-mt-4" (click)="triggerRef.closePopover()">Close</button>
</bit-popover> </bit-popover>
`; `;

View File

@@ -2,7 +2,7 @@
role="search" role="search"
(mouseenter)="isFormHovered.set(true)" (mouseenter)="isFormHovered.set(true)"
(mouseleave)="isFormHovered.set(false)" (mouseleave)="isFormHovered.set(false)"
class="tw-relative tw-flex tw-items-center tw-w-full" class="tw-relative tw-flex tw-items-center tw-w-full tw-h-10"
> >
<label class="tw-sr-only" [for]="id">{{ "search" | i18n }}</label> <label class="tw-sr-only" [for]="id">{{ "search" | i18n }}</label>
<label <label
@@ -18,7 +18,7 @@
[type]="inputType" [type]="inputType"
[id]="id" [id]="id"
[placeholder]="placeholder() ?? ('search' | i18n)" [placeholder]="placeholder() ?? ('search' | i18n)"
class="tw-ps-9" class="tw-ps-9 tw-h-full"
name="searchText" name="searchText"
[ngModel]="searchText" [ngModel]="searchText"
(ngModelChange)="onChange($event)" (ngModelChange)="onChange($event)"

View File

@@ -19,7 +19,7 @@ import { TableDataSource, TableModule } from "../../../table";
ScrollLayoutDirective, ScrollLayoutDirective,
], ],
template: /*html*/ `<bit-section> template: /*html*/ `<bit-section>
<cdk-virtual-scroll-viewport bitScrollLayout itemSize="63.5"> <cdk-virtual-scroll-viewport bitScrollLayout itemSize="49.5">
<bit-table [dataSource]="dataSource"> <bit-table [dataSource]="dataSource">
<ng-container header> <ng-container header>
<tr> <tr>

View File

@@ -5,6 +5,6 @@ import { Directive, HostBinding } from "@angular/core";
}) })
export class CellDirective { export class CellDirective {
@HostBinding("class") get classList() { @HostBinding("class") get classList() {
return ["tw-p-3"]; return ["tw-p-3", "has-[[biticonbutton]]:tw-py-1"];
} }
} }

View File

@@ -23,6 +23,7 @@
bitIconButton="bwi-close" bitIconButton="bwi-close"
buttonType="main" buttonType="main"
type="button" type="button"
size="small"
(click)="this.onClose.emit()" (click)="this.onClose.emit()"
></button> ></button>
</div> </div>