mirror of
https://github.com/bitwarden/browser
synced 2026-02-21 20:04:02 +00:00
PoC class variance authority
This commit is contained in:
@@ -2,32 +2,100 @@
|
||||
// @ts-strict-ignore
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, ElementRef, HostBinding, Input } from "@angular/core";
|
||||
import { VariantProps, cva } from "class-variance-authority";
|
||||
|
||||
import { FocusableElement } from "../shared/focusable-element";
|
||||
|
||||
export type BadgeVariant = "primary" | "secondary" | "success" | "danger" | "warning" | "info";
|
||||
export type BadgeVariant = VariantProps<typeof badge>["variant"];
|
||||
|
||||
const styles: Record<BadgeVariant, string[]> = {
|
||||
primary: ["tw-bg-primary-100", "tw-border-primary-700", "!tw-text-primary-700"],
|
||||
secondary: ["tw-bg-secondary-100", "tw-border-secondary-700", "!tw-text-secondary-700"],
|
||||
success: ["tw-bg-success-100", "tw-border-success-700", "!tw-text-success-700"],
|
||||
danger: ["tw-bg-danger-100", "tw-border-danger-700", "!tw-text-danger-700"],
|
||||
warning: ["tw-bg-warning-100", "tw-border-warning-700", "!tw-text-warning-700"],
|
||||
info: ["tw-bg-info-100", "tw-border-info-700", "!tw-text-info-700"],
|
||||
};
|
||||
|
||||
const hoverStyles: Record<BadgeVariant, string[]> = {
|
||||
primary: ["hover:tw-bg-primary-600", "hover:tw-border-primary-600", "hover:!tw-text-contrast"],
|
||||
secondary: [
|
||||
"hover:tw-bg-secondary-600",
|
||||
"hover:tw-border-secondary-600",
|
||||
"hover:!tw-text-contrast",
|
||||
const badge = cva(
|
||||
[
|
||||
"tw-inline-block",
|
||||
"tw-py-1",
|
||||
"tw-px-2",
|
||||
"tw-font-medium",
|
||||
"tw-text-center",
|
||||
"tw-align-text-top",
|
||||
"tw-rounded-full",
|
||||
"tw-border-[0.5px]",
|
||||
"tw-border-solid",
|
||||
"tw-box-border",
|
||||
"tw-whitespace-nowrap",
|
||||
"tw-text-xs",
|
||||
"hover:tw-no-underline",
|
||||
"focus-visible:tw-outline-none",
|
||||
"focus-visible:tw-ring-2",
|
||||
"focus-visible:tw-ring-offset-2",
|
||||
"focus-visible:tw-ring-primary-600",
|
||||
"disabled:tw-bg-secondary-300",
|
||||
"disabled:hover:tw-bg-secondary-300",
|
||||
"disabled:tw-border-secondary-300",
|
||||
"disabled:hover:tw-border-secondary-300",
|
||||
"disabled:!tw-text-muted",
|
||||
"disabled:hover:!tw-text-muted",
|
||||
"disabled:tw-cursor-not-allowed",
|
||||
],
|
||||
success: ["hover:tw-bg-success-600", "hover:tw-border-success-600", "hover:!tw-text-contrast"],
|
||||
danger: ["hover:tw-bg-danger-600", "hover:tw-border-danger-600", "hover:!tw-text-contrast"],
|
||||
warning: ["hover:tw-bg-warning-600", "hover:tw-border-warning-600", "hover:!tw-text-black"],
|
||||
info: ["hover:tw-bg-info-600", "hover:tw-border-info-600", "hover:!tw-text-black"],
|
||||
};
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
primary: ["tw-bg-primary-100", "tw-border-primary-700", "!tw-text-primary-700"],
|
||||
secondary: ["tw-bg-secondary-100", "tw-border-secondary-700", "!tw-text-secondary-700"],
|
||||
success: ["tw-bg-success-100", "tw-border-success-700", "!tw-text-success-700"],
|
||||
danger: ["tw-bg-danger-100", "tw-border-danger-700", "!tw-text-danger-700"],
|
||||
warning: ["tw-bg-warning-100", "tw-border-warning-700", "!tw-text-warning-700"],
|
||||
info: ["tw-bg-info-100", "tw-border-info-700", "!tw-text-info-700"],
|
||||
},
|
||||
hover: {
|
||||
true: null,
|
||||
false: null,
|
||||
},
|
||||
},
|
||||
compoundVariants: [
|
||||
{
|
||||
variant: "primary",
|
||||
hover: true,
|
||||
class: [
|
||||
"hover:tw-bg-primary-600",
|
||||
"hover:tw-border-primary-600",
|
||||
"hover:!tw-text-contrast",
|
||||
],
|
||||
},
|
||||
{
|
||||
variant: "secondary",
|
||||
hover: true,
|
||||
class: [
|
||||
"hover:tw-bg-secondary-600",
|
||||
"hover:tw-border-secondary-600",
|
||||
"hover:!tw-text-contrast",
|
||||
],
|
||||
},
|
||||
{
|
||||
variant: "success",
|
||||
hover: true,
|
||||
class: [
|
||||
"hover:tw-bg-success-600",
|
||||
"hover:tw-border-success-600",
|
||||
"hover:!tw-text-contrast",
|
||||
],
|
||||
},
|
||||
{
|
||||
variant: "danger",
|
||||
hover: true,
|
||||
class: ["hover:tw-bg-danger-600", "hover:tw-border-danger-600", "hover:!tw-text-contrast"],
|
||||
},
|
||||
{
|
||||
variant: "warning",
|
||||
hover: true,
|
||||
class: ["hover:tw-bg-warning-600", "hover:tw-border-warning-600", "hover:!tw-text-black"],
|
||||
},
|
||||
{
|
||||
variant: "info",
|
||||
hover: true,
|
||||
class: ["hover:tw-bg-info-600", "hover:tw-border-info-600", "hover:!tw-text-black"],
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
@Component({
|
||||
selector: "span[bitBadge], a[bitBadge], button[bitBadge]",
|
||||
@@ -38,36 +106,13 @@ const hoverStyles: Record<BadgeVariant, string[]> = {
|
||||
})
|
||||
export class BadgeComponent implements FocusableElement {
|
||||
@HostBinding("class") get classList() {
|
||||
return [
|
||||
"tw-inline-block",
|
||||
"tw-py-1",
|
||||
"tw-px-2",
|
||||
"tw-font-medium",
|
||||
"tw-text-center",
|
||||
"tw-align-text-top",
|
||||
"tw-rounded-full",
|
||||
"tw-border-[0.5px]",
|
||||
"tw-border-solid",
|
||||
"tw-box-border",
|
||||
"tw-whitespace-nowrap",
|
||||
"tw-text-xs",
|
||||
"hover:tw-no-underline",
|
||||
"focus-visible:tw-outline-none",
|
||||
"focus-visible:tw-ring-2",
|
||||
"focus-visible:tw-ring-offset-2",
|
||||
"focus-visible:tw-ring-primary-600",
|
||||
"disabled:tw-bg-secondary-300",
|
||||
"disabled:hover:tw-bg-secondary-300",
|
||||
"disabled:tw-border-secondary-300",
|
||||
"disabled:hover:tw-border-secondary-300",
|
||||
"disabled:!tw-text-muted",
|
||||
"disabled:hover:!tw-text-muted",
|
||||
"disabled:tw-cursor-not-allowed",
|
||||
]
|
||||
.concat(styles[this.variant])
|
||||
.concat(this.hasHoverEffects ? [...hoverStyles[this.variant], "tw-min-w-10"] : [])
|
||||
.concat(this.truncate ? this.maxWidthClass : []);
|
||||
return badge({
|
||||
variant: this.variant,
|
||||
hover: this.hasHoverEffects,
|
||||
class: this.truncate ? this.maxWidthClass : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
@HostBinding("attr.title") get titleAttr() {
|
||||
if (this.title !== undefined) {
|
||||
return this.title;
|
||||
|
||||
@@ -4,6 +4,7 @@ import { coerceBooleanProperty } from "@angular/cdk/coercion";
|
||||
import { NgClass } from "@angular/common";
|
||||
import { Input, HostBinding, Component, model, computed } from "@angular/core";
|
||||
import { toObservable, toSignal } from "@angular/core/rxjs-interop";
|
||||
import { cva } from "class-variance-authority";
|
||||
import { debounce, interval } from "rxjs";
|
||||
|
||||
import { ButtonLikeAbstraction, ButtonType } from "../shared/button-like.abstraction";
|
||||
@@ -14,36 +15,71 @@ const focusRing = [
|
||||
"focus-visible:tw-ring-primary-600",
|
||||
"focus-visible:tw-z-10",
|
||||
];
|
||||
|
||||
const buttonStyles: Record<ButtonType, string[]> = {
|
||||
primary: [
|
||||
"tw-border-primary-600",
|
||||
"tw-bg-primary-600",
|
||||
"!tw-text-contrast",
|
||||
"hover:tw-bg-primary-700",
|
||||
"hover:tw-border-primary-700",
|
||||
...focusRing,
|
||||
const button = cva(
|
||||
[
|
||||
"tw-font-semibold",
|
||||
"tw-py-1.5",
|
||||
"tw-px-3",
|
||||
"tw-rounded-full",
|
||||
"tw-transition",
|
||||
"tw-border-2",
|
||||
"tw-border-solid",
|
||||
"tw-text-center",
|
||||
"tw-no-underline",
|
||||
"hover:tw-no-underline",
|
||||
"focus:tw-outline-none",
|
||||
],
|
||||
secondary: [
|
||||
"tw-bg-transparent",
|
||||
"tw-border-primary-600",
|
||||
"!tw-text-primary-600",
|
||||
"hover:tw-bg-primary-600",
|
||||
"hover:tw-border-primary-600",
|
||||
"hover:!tw-text-contrast",
|
||||
...focusRing,
|
||||
],
|
||||
danger: [
|
||||
"tw-bg-transparent",
|
||||
"tw-border-danger-600",
|
||||
"!tw-text-danger",
|
||||
"hover:tw-bg-danger-600",
|
||||
"hover:tw-border-danger-600",
|
||||
"hover:!tw-text-contrast",
|
||||
...focusRing,
|
||||
],
|
||||
unstyled: [],
|
||||
};
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
primary: [
|
||||
"tw-border-primary-600",
|
||||
"tw-bg-primary-600",
|
||||
"!tw-text-contrast",
|
||||
"hover:tw-bg-primary-700",
|
||||
"hover:tw-border-primary-700",
|
||||
...focusRing,
|
||||
],
|
||||
secondary: [
|
||||
"tw-bg-transparent",
|
||||
"tw-border-primary-600",
|
||||
"!tw-text-primary-600",
|
||||
"hover:tw-bg-primary-600",
|
||||
"hover:tw-border-primary-600",
|
||||
"hover:!tw-text-contrast",
|
||||
...focusRing,
|
||||
],
|
||||
danger: [
|
||||
"tw-bg-transparent",
|
||||
"tw-border-danger-600",
|
||||
"!tw-text-danger",
|
||||
"hover:tw-bg-danger-600",
|
||||
"hover:tw-border-danger-600",
|
||||
"hover:!tw-text-contrast",
|
||||
...focusRing,
|
||||
],
|
||||
unstyled: [],
|
||||
},
|
||||
disabled: {
|
||||
true: [
|
||||
"disabled:tw-bg-secondary-300",
|
||||
"disabled:hover:tw-bg-secondary-300",
|
||||
"disabled:tw-border-secondary-300",
|
||||
"disabled:hover:tw-border-secondary-300",
|
||||
"disabled:!tw-text-muted",
|
||||
"disabled:hover:!tw-text-muted",
|
||||
"disabled:tw-cursor-not-allowed",
|
||||
"disabled:hover:tw-no-underline",
|
||||
],
|
||||
false: null,
|
||||
},
|
||||
block: {
|
||||
true: ["tw-w-full", "tw-block"],
|
||||
false: ["tw-inline-block"],
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
@Component({
|
||||
selector: "button[bitButton], a[bitButton]",
|
||||
@@ -57,35 +93,11 @@ const buttonStyles: Record<ButtonType, string[]> = {
|
||||
})
|
||||
export class ButtonComponent implements ButtonLikeAbstraction {
|
||||
@HostBinding("class") get classList() {
|
||||
return [
|
||||
"tw-font-semibold",
|
||||
"tw-py-1.5",
|
||||
"tw-px-3",
|
||||
"tw-rounded-full",
|
||||
"tw-transition",
|
||||
"tw-border-2",
|
||||
"tw-border-solid",
|
||||
"tw-text-center",
|
||||
"tw-no-underline",
|
||||
"hover:tw-no-underline",
|
||||
"focus:tw-outline-none",
|
||||
]
|
||||
.concat(this.block ? ["tw-w-full", "tw-block"] : ["tw-inline-block"])
|
||||
.concat(buttonStyles[this.buttonType ?? "secondary"])
|
||||
.concat(
|
||||
this.showDisabledStyles() || this.disabled()
|
||||
? [
|
||||
"disabled:tw-bg-secondary-300",
|
||||
"disabled:hover:tw-bg-secondary-300",
|
||||
"disabled:tw-border-secondary-300",
|
||||
"disabled:hover:tw-border-secondary-300",
|
||||
"disabled:!tw-text-muted",
|
||||
"disabled:hover:!tw-text-muted",
|
||||
"disabled:tw-cursor-not-allowed",
|
||||
"disabled:hover:tw-no-underline",
|
||||
]
|
||||
: [],
|
||||
);
|
||||
return button({
|
||||
variant: this.buttonType ?? "secondary",
|
||||
block: this.block,
|
||||
disabled: this.showDisabledStyles() || this.disabled(),
|
||||
});
|
||||
}
|
||||
|
||||
protected disabledAttr = computed(() => {
|
||||
|
||||
Reference in New Issue
Block a user