1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-03 18:23:57 +00:00
This commit is contained in:
Bryan Cunningham
2025-10-08 11:56:34 -04:00
parent 1e56e7b151
commit ecac9b1a94
9 changed files with 373 additions and 1 deletions

View File

@@ -1,6 +1,6 @@
<div
bitTypography="body2"
class="tw-inline-flex tw-items-center tw-rounded-full tw-w-full tw-border-solid tw-border tw-gap-1.5 tw-group/chip-select"
class="tw-inline-flex tw-items-center tw-rounded-full tw-w-full tw-border-solid tw-border tw-gap-1.5 tw-group/chip-select [&:has(button[aria-expanded]):focus]:tw-border-secondary-700"
[ngClass]="{
'tw-bg-text-muted hover:tw-bg-secondary-700 tw-text-contrast hover:!tw-border-secondary-700':
selectedOption && !disabled,

View File

View File

@@ -0,0 +1,93 @@
.bit-chip {
border-radius: 9999px;
border: 1px solid rgb(var(--color-text-muted));
display: inline-flex;
align-items: center;
gap: 0.375rem; /* 6px */
padding: 0.25rem 0.5rem; /* 4px 8px */
width: fit-content;
color: rgb(var(--color-text-muted));
font-size: 0.875rem; /* 14px */
line-height: 1.25rem; /* 20px */
transition-property: background-color, border-color;
transition-duration: 100ms;
&[data-is-interactive="true"][data-is-disabled="false"] {
&:hover {
background-color: rgb(var(--color-secondary-100));
color: rgb(var(--color-secondary-700));
border-color: rgb(var(--color-secondary-700));
}
&[data-is-selected="true"] {
&:hover {
background-color: rgb(var(--color-secondary-700));
border-color: rgb(var(--color-secondary-700));
color: rgb(var(--color-text-contrast));
}
}
&[data-is-disabled="true"] {
&:hover {
background-color: rgb(var(--color-secondary-700));
border-color: rgb(var(--color-secondary-700));
}
}
}
&[data-is-selected="true"] {
background-color: rgb(var(--color-text-muted));
color: rgb(var(--color-text-contrast));
}
&[data-is-disabled="true"] {
background-color: rgb(var(--color-secondary-300));
color: rgb(var(--color-text-muted));
border-color: transparent;
}
}
.bit-chip-dismiss-button {
--hover-bg-color: rgb(var(--color-secondary-100));
--focus-ring-color: rgb(var(--color-text-muted));
position: relative;
display: flex;
align-self: center;
justify-content: center;
background-color: transparent;
border: 1px solid transparent;
outline: 2px solid transparent;
outline-offset: 2px;
border-radius: 9999px;
width: 1.5rem; /* 24px */
height: 1.5rem; /* 24px */
.bit-chip[data-is-selected="true"] & {
--hover-bg-color: var(--color-hover-contrast);
--focus-ring-color: var(--tw-ring-offset-color);
}
&:hover {
background-color: var(--hover-bg-color);
}
&:focus,
&:focus-visible {
background-color: rgb(var(--color-hover-contrast));
box-shadow:
0px 0px 0px 0px var(--focus-ring-color),
0px 0px 0px 2px var(--focus-ring-color),
0px 0px transparent;
}
i {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: inherit;
}
/* 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 */
}

View File

@@ -0,0 +1,38 @@
<!-- <div class="bit-chip"
class="tw-inline-flex tw-items-center tw-rounded-full tw-w-full tw-border-solid tw-border tw-gap-1.5 tw-group/chip-select [&:has(button[aria-expanded=])]:focus:tw-border-secondary-700"
[ngClass]="{
'tw-bg-text-muted hover:tw-bg-secondary-700 tw-text-contrast hover:!tw-border-secondary-700':
isSelected && !isDisabled,
'tw-bg-transparent hover:tw-border-secondary-700 !tw-text-muted hover:tw-bg-secondary-100':
!isSelected && !isDisabled,
'tw-bg-secondary-300 tw-text-muted tw-border-transparent': isDisabled,
'tw-border-text-muted': !isDisabled,
'tw-ring-2 tw-ring-primary-600 tw-ring-offset-1': focusVisibleWithin(),
}">
<ng-content select="chip-select-trigger"></ng-content>
</div>
<ng-content select="chip-select-menu"></ng-content> -->
<div
class="bit-chip"
bitTypography="body2"
[attr.data-is-interactive]="isInteractive()"
[attr.data-is-disabled]="isDisabled()"
[attr.data-is-dismissible]="isDismissible()"
[attr.data-is-selected]="isSelected()"
>
Chip component
@if (isDismissible()) {
<button
type="button"
[attr.aria-label]="'removeItem' | i18n: label"
[disabled]="disabled"
class="bit-chip-dismiss-button"
(click)="clear()"
>
<i class="bwi bwi-close tw-text-xs" aria-hidden="true"></i>
</button>
}
</div>

View File

@@ -0,0 +1,15 @@
import { input, Component, booleanAttribute } from "@angular/core";
import { SharedModule } from "../shared";
@Component({
selector: "bit-chip",
templateUrl: "chip.component.html",
imports: [SharedModule],
})
export class ChipComponent {
protected readonly isInteractive = input<boolean>(booleanAttribute(false));
protected readonly isDisabled = input<boolean>(booleanAttribute(false));
protected readonly isDismissible = input<boolean>(booleanAttribute(false));
protected readonly isSelected = input<boolean>(booleanAttribute(false));
}

View File

@@ -0,0 +1,225 @@
import { FormsModule } from "@angular/forms";
import { Meta, StoryObj, moduleMetadata } from "@storybook/angular";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { MenuModule } from "../menu";
import { I18nMockService } from "../utils/i18n-mock.service";
import { ChipComponent } from "./chip.component";
import { formatArgsForCodeSnippet } from ".storybook/format-args-for-code-snippet";
export default {
title: "Component Library/Chip",
component: ChipComponent,
decorators: [
moduleMetadata({
imports: [MenuModule, FormsModule],
providers: [
{
provide: I18nService,
useFactory: () => {
return new I18nMockService({
viewItemsIn: (name) => `View items in ${name}`,
back: "Back",
backTo: (name) => `Back to ${name}`,
removeItem: (name) => `Remove ${name}`,
});
},
},
],
}),
],
parameters: {
design: {
type: "figma",
url: "https://www.figma.com/design/Zt3YSeb6E6lebAffrNLa0h/Tailwind-Component-Library?node-id=16329-29548&t=b5tDKylm5sWm2yKo-4",
},
},
args: {
isSelected: false,
isDisabled: false,
isDismissible: false,
isInteractive: false,
},
} as Meta;
type Story = StoryObj<ChipComponent>;
export const Default: Story = {
render: (args) => ({
props: args,
template: /* html */ `
<bit-chip ${formatArgsForCodeSnippet(args)}></bit-chip>
`,
}),
};
// export const MenuOpen: Story = {
// render: (args) => ({
// props: {
// ...args,
// },
// template: /* html */ `
// <bit-chip-select
// placeholderText="Folder"
// placeholderIcon="bwi-folder"
// [options]="options"
// [ngModel]="value"
// ></bit-chip-select>
// `,
// }),
// args: {
// options: [
// {
// label: "Foo",
// value: "foo",
// icon: "bwi-folder",
// },
// {
// label: "Bar",
// value: "bar",
// icon: "bwi-exclamation-triangle tw-text-danger",
// },
// {
// label: "Baz",
// value: "baz",
// disabled: true,
// },
// ],
// },
// play: async (context) => {
// const canvas = context.canvasElement;
// const buttons = getAllByRole(canvas, "button");
// await userEvent.click(buttons[0]);
// },
// };
// export const FullWidth: Story = {
// render: (args) => ({
// props: {
// ...args,
// },
// template: /* html */ `
// <div class="tw-w-40">
// <bit-chip-select
// placeholderText="Folder"
// placeholderIcon="bwi-folder"
// [options]="options"
// [ngModel]="value"
// fullWidth
// ></bit-chip-select>
// </div>
// `,
// }),
// args: {
// options: [
// {
// label: "Foo",
// value: "foo",
// icon: "bwi-folder",
// },
// {
// label: "Bar",
// value: "bar",
// icon: "bwi-exclamation-triangle tw-text-danger",
// },
// {
// label: "Baz",
// value: "baz",
// disabled: true,
// },
// ],
// },
// };
// export const NestedOptions: Story = {
// ...Default,
// args: {
// options: [
// {
// label: "Foo",
// value: "foo",
// icon: "bwi-folder",
// children: [
// {
// label: "Foo1 very long name of folder but even longer than you thought",
// value: "foo1",
// icon: "bwi-folder",
// children: [
// {
// label: "Foo2",
// value: "foo2",
// icon: "bwi-folder",
// children: [
// {
// label: "Foo3",
// value: "foo3",
// },
// ],
// },
// ],
// },
// ],
// },
// {
// label: "Bar",
// value: "bar",
// icon: "bwi-folder",
// },
// {
// label: "Baz",
// value: "baz",
// icon: "bwi-folder",
// },
// ],
// value: "foo1",
// },
// };
// export const TextOverflow: Story = {
// ...Default,
// args: {
// options: [
// {
// label: "Fooooooooooooooooooooooooooooooooooooooooooooo",
// value: "foo",
// },
// ],
// value: "foo",
// },
// };
// export const Disabled: Story = {
// render: (args) => ({
// props: {
// ...args,
// },
// template: /* html */ `
// <bit-chip-select
// placeholderText="Folder"
// placeholderIcon="bwi-folder"
// [options]="options"
// disabled
// ></bit-chip-select>
// <bit-chip-select
// placeholderText="Folder"
// placeholderIcon="bwi-folder"
// [options]="options"
// [ngModel]="value"
// disabled
// ></bit-chip-select>
// `,
// }),
// args: {
// options: [
// {
// label: "Foo",
// value: "foo",
// icon: "bwi-folder",
// },
// ],
// value: "foo",
// },
// };

View File

@@ -2,6 +2,7 @@
@import "@angular/cdk/a11y-prebuilt.css";
@import "@angular/cdk/text-field-prebuilt.css";
@import "./reset.css";
@import "./chip/chip.component.css";
@import "./popover/popover.component.css";
@import "./toast/toast.tokens.css";
@import "./toast/toastr.css";