mirror of
https://github.com/bitwarden/browser
synced 2026-02-03 18:23:57 +00:00
WIP
This commit is contained in:
@@ -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,
|
||||
|
||||
0
libs/components/src/chip-toggle/index.ts
Normal file
0
libs/components/src/chip-toggle/index.ts
Normal file
93
libs/components/src/chip/chip.component.css
Normal file
93
libs/components/src/chip/chip.component.css
Normal 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 */
|
||||
}
|
||||
38
libs/components/src/chip/chip.component.html
Normal file
38
libs/components/src/chip/chip.component.html
Normal 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>
|
||||
15
libs/components/src/chip/chip.component.ts
Normal file
15
libs/components/src/chip/chip.component.ts
Normal 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));
|
||||
}
|
||||
225
libs/components/src/chip/chip.stories.ts
Normal file
225
libs/components/src/chip/chip.stories.ts
Normal 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",
|
||||
// },
|
||||
// };
|
||||
@@ -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";
|
||||
|
||||
Reference in New Issue
Block a user