mirror of
https://github.com/bitwarden/browser
synced 2026-02-10 05:30:01 +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(() => {
|
||||
|
||||
20
package-lock.json
generated
20
package-lock.json
generated
@@ -40,6 +40,7 @@
|
||||
"buffer": "6.0.3",
|
||||
"bufferutil": "4.0.9",
|
||||
"chalk": "4.1.2",
|
||||
"class-variance-authority": "0.7.1",
|
||||
"commander": "11.1.0",
|
||||
"core-js": "3.40.0",
|
||||
"form-data": "4.0.1",
|
||||
@@ -15771,6 +15772,17 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/class-variance-authority": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
|
||||
"integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==",
|
||||
"dependencies": {
|
||||
"clsx": "^2.1.1"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://polar.sh/cva"
|
||||
}
|
||||
},
|
||||
"node_modules/clean-css": {
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz",
|
||||
@@ -15939,6 +15951,14 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/clsx": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/co": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
||||
|
||||
@@ -170,6 +170,7 @@
|
||||
"buffer": "6.0.3",
|
||||
"bufferutil": "4.0.9",
|
||||
"chalk": "4.1.2",
|
||||
"class-variance-authority": "0.7.1",
|
||||
"commander": "11.1.0",
|
||||
"core-js": "3.40.0",
|
||||
"form-data": "4.0.1",
|
||||
|
||||
Reference in New Issue
Block a user