Added proper dark mode support for buttons (#5603)
* Added proper dark mode support for buttons, and converted buttons to use CVA for better maintainable variant styling. * Added animations to buttons. * Added cva types to buttonbase type since thats imported in different places * Fixed issue with styled buttons when false was pas for disabled instead of undefined. Added a small util function that now accepts arrays of variants, and creates all the possible combinations. This way we have less duplicate compoundvariants defined. This fixes the styles in the eventsinglelayout component. * Undo disabling of api jest tests. * Fixed remaining buttons using combined prop, which is replace by button group. Co-authored-by: Peer Richelsen <peeroke@gmail.com> Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>pull/5641/head
parent
16eede9bb0
commit
d64400d66b
|
@ -6,5 +6,8 @@
|
|||
},
|
||||
"typescript.preferences.importModuleSpecifier": "non-relative",
|
||||
"spellright.language": ["en"],
|
||||
"spellright.documentTypes": ["markdown", "typescript"]
|
||||
"spellright.documentTypes": ["markdown", "typescript"],
|
||||
"tailwindCSS.experimental.classRegex": [
|
||||
["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 2219900e06c3a683c85ce066e6ea3eb2d6ae14e9
|
||||
Subproject commit 7d53a77c07ab2b2f0fd0567b4d8ee4905d448442
|
|
@ -19,7 +19,7 @@ export function VariantsTable({
|
|||
<div
|
||||
className={classNames(
|
||||
isDark &&
|
||||
"relative py-8 before:absolute before:left-0 before:top-0 before:block before:h-full before:w-screen before:bg-[#22252A]"
|
||||
"relative py-8 before:absolute before:left-0 before:top-0 before:block before:h-full before:w-screen before:bg-[#1C1C1C]"
|
||||
)}>
|
||||
<div className="z-1 relative mx-auto w-full max-w-[1200px] overflow-auto pr-8 pt-6">
|
||||
<table>
|
||||
|
|
|
@ -860,7 +860,7 @@ const BookingPage = ({
|
|||
{!eventType.disableGuests && !guestToggle && (
|
||||
<Button
|
||||
type="button"
|
||||
color="minimalSecondary"
|
||||
color="minimal"
|
||||
size="icon"
|
||||
tooltip={t("additional_guests")}
|
||||
StartIcon={Icon.FiUserPlus}
|
||||
|
@ -878,7 +878,6 @@ const BookingPage = ({
|
|||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
className="dark:bg-darkmodebrand dark:text-darkmodebrandcontrast dark:hover:border-darkmodebrandcontrast mr-auto dark:border-transparent"
|
||||
data-testid={rescheduleUid ? "confirm-reschedule-button" : "confirm-book-button"}
|
||||
loading={mutation.isLoading || recurringMutation.isLoading}>
|
||||
{rescheduleUid ? t("reschedule") : t("confirm")}
|
||||
|
|
|
@ -202,7 +202,6 @@ function EventTypeSingleLayout({
|
|||
color="secondary"
|
||||
size="icon"
|
||||
StartIcon={Icon.FiLink}
|
||||
combined
|
||||
tooltip={t("copy_link")}
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(permalink);
|
||||
|
|
|
@ -4,14 +4,14 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
|
|||
import { trpc } from "@calcom/trpc/react";
|
||||
import ConfirmationDialogContent from "@calcom/ui/ConfirmationDialogContent";
|
||||
import { Dialog } from "@calcom/ui/Dialog";
|
||||
import { ButtonBaseProps } from "@calcom/ui/components/button";
|
||||
import { ButtonProps } from "@calcom/ui/components/button";
|
||||
import showToast from "@calcom/ui/v2/core/notifications";
|
||||
|
||||
export default function DisconnectIntegration(props: {
|
||||
/** Integration credential id */
|
||||
id: number;
|
||||
externalId?: string;
|
||||
render: (renderProps: ButtonBaseProps) => JSX.Element;
|
||||
render: (renderProps: ButtonProps) => JSX.Element;
|
||||
onOpenChange: (isOpen: boolean) => unknown | Promise<unknown>;
|
||||
}) {
|
||||
const { id, externalId = "" } = props;
|
||||
|
|
|
@ -311,7 +311,6 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
|
|||
size="icon"
|
||||
href={calLink}
|
||||
StartIcon={Icon.FiExternalLink}
|
||||
combined
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
|
@ -324,7 +323,6 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
|
|||
showToast(t("link_copied"), "success");
|
||||
navigator.clipboard.writeText(calLink);
|
||||
}}
|
||||
combined
|
||||
/>
|
||||
</Tooltip>
|
||||
<Dropdown modal={false}>
|
||||
|
@ -336,7 +334,6 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
|
|||
type="button"
|
||||
size="icon"
|
||||
color="secondary"
|
||||
combined
|
||||
StartIcon={Icon.FiMoreHorizontal}
|
||||
/>
|
||||
</DropdownMenuTrigger>
|
||||
|
|
|
@ -123,7 +123,7 @@ const OnboardingPage = (props: IOnboardingPageProps) => {
|
|||
{headers[currentStepIndex]?.skipText && (
|
||||
<div className="flex w-full flex-row justify-center">
|
||||
<Button
|
||||
color="minimalSecondary"
|
||||
color="minimal"
|
||||
data-testid="skip-step"
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
|
|
|
@ -136,7 +136,6 @@ export const FormActionsDropdown = ({ form, children }: { form: RoutingForm; chi
|
|||
<Button
|
||||
type="button"
|
||||
size="icon"
|
||||
combined
|
||||
color="secondary"
|
||||
className={classNames("radix-state-open:rounded-r-md", disabled && "opacity-30")}
|
||||
StartIcon={Icon.FiMoreHorizontal}
|
||||
|
|
|
@ -78,7 +78,6 @@ export default function RoutingForms({
|
|||
target="_blank"
|
||||
StartIcon={Icon.FiExternalLink}
|
||||
color="secondary"
|
||||
combined
|
||||
size="icon"
|
||||
disabled={disabled}
|
||||
/>
|
||||
|
@ -87,7 +86,6 @@ export default function RoutingForms({
|
|||
routingForm={form}
|
||||
action="copyLink"
|
||||
color="secondary"
|
||||
combined
|
||||
size="icon"
|
||||
StartIcon={Icon.FiLink}
|
||||
disabled={disabled}
|
||||
|
|
|
@ -125,7 +125,6 @@ export default function MemberListItem(props: Props) {
|
|||
color="secondary"
|
||||
size="icon"
|
||||
StartIcon={Icon.FiClock}
|
||||
combined
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip content={t("view_public_page")}>
|
||||
|
@ -136,7 +135,6 @@ export default function MemberListItem(props: Props) {
|
|||
className={classNames(!editMode ? "rounded-r-md" : "")}
|
||||
size="icon"
|
||||
StartIcon={Icon.FiExternalLink}
|
||||
combined
|
||||
/>
|
||||
</Tooltip>
|
||||
{editMode && (
|
||||
|
@ -147,7 +145,6 @@ export default function MemberListItem(props: Props) {
|
|||
size="icon"
|
||||
className="rounded-r-md"
|
||||
StartIcon={Icon.FiMoreHorizontal}
|
||||
combined
|
||||
/>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
|
|
|
@ -149,7 +149,6 @@ export default function TeamListItem(props: Props) {
|
|||
}}
|
||||
size="icon"
|
||||
StartIcon={Icon.FiLink}
|
||||
combined
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
|
|
@ -144,7 +144,6 @@ export default function WorkflowListPage({ workflows }: Props) {
|
|||
type="button"
|
||||
color="secondary"
|
||||
size="icon"
|
||||
combined
|
||||
StartIcon={Icon.FiEdit2}
|
||||
onClick={async () => await router.replace("/workflows/" + workflow.id)}
|
||||
/>
|
||||
|
@ -156,7 +155,6 @@ export default function WorkflowListPage({ workflows }: Props) {
|
|||
setwWorkflowToDeleteId(workflow.id);
|
||||
}}
|
||||
color="secondary"
|
||||
combined
|
||||
size="icon"
|
||||
StartIcon={Icon.FiTrash2}
|
||||
/>
|
||||
|
@ -178,7 +176,6 @@ export default function WorkflowListPage({ workflows }: Props) {
|
|||
<Button
|
||||
type="button"
|
||||
color="minimal"
|
||||
combined
|
||||
StartIcon={Icon.FiEdit2}
|
||||
onClick={async () => await router.replace("/workflows/" + workflow.id)}>
|
||||
{t("edit")}
|
||||
|
|
|
@ -384,7 +384,7 @@ const CopyTimes = ({
|
|||
</div>
|
||||
<hr />
|
||||
<div className="space-x-2 px-2">
|
||||
<Button color="minimalSecondary" onClick={() => onCancel()}>
|
||||
<Button color="minimal" onClick={() => onCancel()}>
|
||||
{t("cancel")}
|
||||
</Button>
|
||||
<Button color="primary" onClick={() => onClick(selected)}>
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
import { applyStyleToMultipleVariants } from "./cva";
|
||||
|
||||
describe("CVA Utils", () => {
|
||||
it("Should return an array of all possible variants", () => {
|
||||
const variants = {
|
||||
color: ["blue", "red"],
|
||||
size: ["small", "medium", "large"],
|
||||
className: "text-blue w-10",
|
||||
};
|
||||
|
||||
const result = applyStyleToMultipleVariants(variants);
|
||||
expect(result).toEqual([
|
||||
{ color: "blue", size: "small", className: "text-blue w-10" },
|
||||
{ color: "blue", size: "medium", className: "text-blue w-10" },
|
||||
{ color: "blue", size: "large", className: "text-blue w-10" },
|
||||
{ color: "red", size: "small", className: "text-blue w-10" },
|
||||
{ color: "red", size: "medium", className: "text-blue w-10" },
|
||||
{ color: "red", size: "large", className: "text-blue w-10" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("Should no erorr when no arrays are passed in", () => {
|
||||
const variants = {
|
||||
color: "blue",
|
||||
size: "large",
|
||||
className: "text-blue w-10",
|
||||
};
|
||||
|
||||
const result = applyStyleToMultipleVariants(variants);
|
||||
expect(result).toEqual([{ color: "blue", size: "large", className: "text-blue w-10" }]);
|
||||
});
|
||||
|
||||
it("Should accept numbers, null values, booleans and undefined in arrays as well", () => {
|
||||
const variants = {
|
||||
color: ["blue", null],
|
||||
size: ["small", 30, false, undefined],
|
||||
className: "text-blue w-10",
|
||||
};
|
||||
|
||||
const result = applyStyleToMultipleVariants(variants);
|
||||
expect(result).toEqual([
|
||||
{ color: "blue", size: "small", className: "text-blue w-10" },
|
||||
{ color: "blue", size: 30, className: "text-blue w-10" },
|
||||
{ color: "blue", size: false, className: "text-blue w-10" },
|
||||
{ color: "blue", size: undefined, className: "text-blue w-10" },
|
||||
{ color: null, size: "small", className: "text-blue w-10" },
|
||||
{ color: null, size: 30, className: "text-blue w-10" },
|
||||
{ color: null, size: false, className: "text-blue w-10" },
|
||||
{ color: null, size: undefined, className: "text-blue w-10" },
|
||||
]);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,61 @@
|
|||
type ValidVariantTypes = string | number | null | boolean | undefined;
|
||||
type Variants = Record<string, ValidVariantTypes | ValidVariantTypes[]> & { className: string };
|
||||
|
||||
/**
|
||||
* Lets you use arrays for variants as well. This util combines all possible
|
||||
* variants and returns an array with all possible options. Simply
|
||||
* spread this in the compoundVariants.
|
||||
*/
|
||||
export const applyStyleToMultipleVariants = (variants: Variants) => {
|
||||
const allKeysThatAreArrays = Object.keys(variants).filter((key) => Array.isArray(variants[key]));
|
||||
const allKeysThatAreNotArrays = Object.keys(variants).filter((key) => !Array.isArray(variants[key]));
|
||||
// Creates an object of all static options, ready to be merged in later with the array values.
|
||||
const nonArrayOptions = allKeysThatAreNotArrays.reduce((acc, key) => {
|
||||
return { ...acc, [key]: variants[key] };
|
||||
}, {});
|
||||
|
||||
// Creates an array of all possible combinations of the array values.
|
||||
// Eg if the variants object is { color: ["blue", "red"], size: ["small", "medium"] }
|
||||
// then the result will be:
|
||||
// [
|
||||
// { color: "blue", size: "small" },
|
||||
// { color: "blue", size: "medium" },
|
||||
// { color: "red", size: "small" },
|
||||
// { color: "red", size: "medium" },
|
||||
// ]
|
||||
const cartesianProductOfAllArrays = cartesianProduct(
|
||||
allKeysThatAreArrays.map((key) => variants[key]) as ValidVariantTypes[][]
|
||||
);
|
||||
|
||||
return cartesianProductOfAllArrays.map((variant) => {
|
||||
const variantObject = variant.reduce((acc, value, index) => {
|
||||
return { ...acc, [allKeysThatAreArrays[index]]: value };
|
||||
}, {});
|
||||
|
||||
return {
|
||||
...nonArrayOptions,
|
||||
...variantObject,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* A cartesian product is a final array that combines multiple arrays in ALL
|
||||
* variations possible. For example:
|
||||
*
|
||||
* You have 3 arrays: [a, b], [1, 2], [y, z]
|
||||
* The final result will be an array with all the different combinations:
|
||||
* ["a", 1, "y"], ["a", 1, "z"], ["a", 2, "y"], ["a", 2, "z"], ["b", 1, "y"], etc
|
||||
*
|
||||
* We use this to create a params object for the static pages that combine multiple
|
||||
* dynamic properties like 'stage' and 'meansOfTransport'. Resulting in an array
|
||||
* with all different path combinations possible.
|
||||
*
|
||||
* @source: https://stackoverflow.com/questions/12303989/cartesian-product-of-multiple-arrays-in-javascript
|
||||
* TS Inspiration: https://gist.github.com/ssippe/1f92625532eef28be6974f898efb23ef
|
||||
*/
|
||||
export const cartesianProduct = <T extends ValidVariantTypes>(sets: T[][]) =>
|
||||
sets.reduce<T[][]>(
|
||||
(accSets, set) => accSets.flatMap((accSet) => set.map((value) => [...accSet, value])),
|
||||
[[]]
|
||||
);
|
|
@ -0,0 +1 @@
|
|||
export * from "./cva";
|
|
@ -1,24 +1,14 @@
|
|||
import { cva, VariantProps } from "class-variance-authority";
|
||||
import Link, { LinkProps } from "next/link";
|
||||
import React, { forwardRef } from "react";
|
||||
import { Icon } from "react-feather";
|
||||
|
||||
import classNames from "@calcom/lib/classNames";
|
||||
import { applyStyleToMultipleVariants } from "@calcom/lib/cva";
|
||||
|
||||
import Tooltip from "../../v2/core/Tooltip";
|
||||
|
||||
export type ButtonBaseProps = {
|
||||
/* Primary: Signals most important actions at any given point in the application.
|
||||
Secondary: Gives visual weight to actions that are important
|
||||
Minimal: Used for actions that we want to give very little significane to */
|
||||
color?: keyof typeof variantClassName;
|
||||
/**Default: H = 36px (default)
|
||||
Large: H = 38px (Onboarding, modals)
|
||||
Icon: Makes the button be an icon button */
|
||||
size?: "base" | "lg" | "icon";
|
||||
/**Signals the button is loading */
|
||||
loading?: boolean;
|
||||
/** Disables the button from being clicked */
|
||||
disabled?: boolean;
|
||||
/** Action that happens when the button is clicked */
|
||||
onClick?: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
|
||||
/**Left aligned icon*/
|
||||
|
@ -28,34 +18,119 @@ export type ButtonBaseProps = {
|
|||
shallow?: boolean;
|
||||
/**Tool tip used when icon size is set to small */
|
||||
tooltip?: string;
|
||||
/** @deprecated This has now been replaced by button group. */
|
||||
combined?: boolean;
|
||||
flex?: boolean;
|
||||
};
|
||||
} & VariantProps<typeof buttonClasses>;
|
||||
|
||||
export type ButtonProps = ButtonBaseProps &
|
||||
(
|
||||
| (Omit<JSX.IntrinsicElements["a"], "href" | "onClick" | "ref"> & LinkProps)
|
||||
| (Omit<JSX.IntrinsicElements["button"], "onClick" | "ref"> & { href?: never })
|
||||
);
|
||||
|
||||
const variantClassName = {
|
||||
primary:
|
||||
"border border-transparent text-white bg-brand-500 hover:bg-brand-400 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-brand-500",
|
||||
secondary: "border border-gray-200 text-brand-900 bg-white hover:bg-gray-100",
|
||||
minimal:
|
||||
"text-gray-700 bg-transparent hover:bg-gray-100 focus:outline-none focus:ring-offset-1 focus:bg-gray-100 focus:ring-brand-900 dark:text-darkgray-900 hover:dark:text-gray-50",
|
||||
minimalSecondary:
|
||||
"text-gray-700 bg-transparent hover:bg-gray-100 dark:hover:bg-darkgray-200 focus:outline-none focus:ring-2 focus:ring-offset-1 focus:bg-gray-100 focus:ring-brand-900 dark:text-darkgray-900 hover:dark:text-gray-50 border border-transparent hover:border-gray-300 dark:hover:border-darkgray-300",
|
||||
destructive:
|
||||
"text-gray-900 focus:text-red-700 bg-transparent hover:bg-red-100 hover:text-red-700 focus:outline-none focus:ring-2 focus:ring-offset-1 focus:bg-red-100 focus:ring-red-700",
|
||||
};
|
||||
const variantDisabledClassName = {
|
||||
primary: "border border-transparent bg-brand-500 bg-opacity-20 text-white",
|
||||
secondary: "border border-gray-200 text-brand-900 bg-white opacity-30",
|
||||
minimal: "text-gray-400 bg-transparent",
|
||||
minimalSecondary: "text-gray-400 bg-transparent",
|
||||
destructive: "text-red-700 bg-transparent opacity-30",
|
||||
};
|
||||
const buttonClasses = cva(
|
||||
"inline-flex items-center text-sm font-medium relative rounded-md transition-colors",
|
||||
{
|
||||
variants: {
|
||||
color: {
|
||||
primary: "text-white dark:text-black",
|
||||
secondary: "text-gray-900 dark:text-darkgray-900",
|
||||
minimal: "text-gray-900 dark:text-darkgray-900",
|
||||
destructive: "",
|
||||
},
|
||||
size: {
|
||||
base: "h-9 px-4 py-2.5 ",
|
||||
lg: "h-[36px] px-4 py-2.5 ",
|
||||
icon: "flex justify-center min-h-[36px] min-w-[36px] ",
|
||||
},
|
||||
loading: {
|
||||
true: "cursor-wait",
|
||||
},
|
||||
disabled: {
|
||||
true: "cursor-not-allowed",
|
||||
},
|
||||
},
|
||||
compoundVariants: [
|
||||
// Primary variants
|
||||
{
|
||||
disabled: true,
|
||||
color: "primary",
|
||||
className: "bg-gray-800 bg-opacity-30 dark:bg-opacity-30 dark:bg-darkgray-800",
|
||||
},
|
||||
{
|
||||
loading: true,
|
||||
color: "primary",
|
||||
className: "bg-gray-800/30 text-white/30 dark:bg-opacity-30 dark:bg-darkgray-700 dark:text-black/30",
|
||||
},
|
||||
...applyStyleToMultipleVariants({
|
||||
disabled: [undefined, false],
|
||||
color: "primary",
|
||||
className:
|
||||
"bg-brand-500 hover:bg-brand-400 focus:border focus:border-white focus:outline-none focus:ring-2 focus:ring-offset focus:ring-brand-500 dark:hover:bg-darkgray-600 dark:bg-darkgray-900",
|
||||
}),
|
||||
// Secondary variants
|
||||
{
|
||||
disabled: true,
|
||||
color: "secondary",
|
||||
className:
|
||||
"border border-gray-200 bg-opacity-30 text-gray-900/30 bg-white dark:bg-darkgray-100 dark:text-darkgray-900/30 dark:border-darkgray-200",
|
||||
},
|
||||
{
|
||||
loading: true,
|
||||
color: "secondary",
|
||||
className:
|
||||
"bg-gray-100 text-gray-900/30 dark:bg-darkgray-100 dark:text-darkgray-900/30 dark:border-darkgray-200",
|
||||
},
|
||||
...applyStyleToMultipleVariants({
|
||||
disabled: [undefined, false],
|
||||
color: "secondary",
|
||||
className:
|
||||
"border border-gray-300 dark:border-darkgray-300 hover:bg-gray-50 hover:border-gray-400 focus:bg-gray-100 dark:hover:bg-darkgray-200 dark:focus:bg-darkgray-200 focus:outline-none focus:ring-2 focus:ring-offset focus:ring-gray-900 dark:focus:ring-white",
|
||||
}),
|
||||
// Minimal variants
|
||||
{
|
||||
disabled: true,
|
||||
color: "minimal",
|
||||
className:
|
||||
"border:gray-200 bg-opacity-30 text-gray-900/30 dark:bg-darkgray-100 dark:text-darkgray-900/30 dark:border-darkgray-200",
|
||||
},
|
||||
{
|
||||
loading: true,
|
||||
color: "minimal",
|
||||
className:
|
||||
"bg-gray-100 text-gray-900/30 dark:bg-darkgray-100 dark:text-darkgray-900/30 dark:border-darkgray-200",
|
||||
},
|
||||
applyStyleToMultipleVariants({
|
||||
disabled: [undefined, false],
|
||||
color: "minimal",
|
||||
className:
|
||||
"hover:bg-gray-100 focus:bg-gray-100 dark:hover:bg-darkgray-200 dark:focus:bg-darkgray-200 focus:outline-none focus:ring-2 focus:ring-offset focus:ring-gray-900 dark:focus:ring-white",
|
||||
}),
|
||||
// Destructive variants
|
||||
{
|
||||
disabled: true,
|
||||
color: "destructive",
|
||||
className:
|
||||
"text-red-700/30 dark:text-red-700/30 bg-red-100/40 dark:bg-red-100/80 border border-red-200",
|
||||
},
|
||||
{
|
||||
loading: true,
|
||||
color: "destructive",
|
||||
className:
|
||||
"text-red-700/30 dark:text-red-700/30 hover:text-red-700/30 bg-red-100 border border-red-200",
|
||||
},
|
||||
...applyStyleToMultipleVariants({
|
||||
disabled: [false, undefined],
|
||||
color: "destructive",
|
||||
className:
|
||||
"border dark:text-white text-gray-900 hover:text-red-700 focus:text-red-700 dark:hover:text-red-700 dark:focus:text-red-700 hover:border-red-100 focus:border-red-100 hover:bg-red-100 focus:bg-red-100 focus:outline-none focus:ring-2 focus:ring-offset focus:ring-red-700",
|
||||
}),
|
||||
],
|
||||
defaultVariants: {
|
||||
color: "primary",
|
||||
size: "base",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export const Button = forwardRef<HTMLAnchorElement | HTMLButtonElement, ButtonProps>(function Button(
|
||||
props: ButtonProps,
|
||||
|
@ -63,13 +138,12 @@ export const Button = forwardRef<HTMLAnchorElement | HTMLButtonElement, ButtonPr
|
|||
) {
|
||||
const {
|
||||
loading = false,
|
||||
color = "primary",
|
||||
size = "base",
|
||||
color,
|
||||
size,
|
||||
type = "button",
|
||||
StartIcon,
|
||||
EndIcon,
|
||||
shallow,
|
||||
combined = false,
|
||||
// attributes propagated from `HTMLAnchorProps` or `HTMLButtonProps`
|
||||
...passThroughProps
|
||||
} = props;
|
||||
|
@ -86,17 +160,7 @@ export const Button = forwardRef<HTMLAnchorElement | HTMLButtonElement, ButtonPr
|
|||
type: !isLink ? type : undefined,
|
||||
ref: forwardedRef,
|
||||
className: classNames(
|
||||
// base styles independent what type of button it is
|
||||
"inline-flex items-center text-sm font-medium relative",
|
||||
// different styles depending on size
|
||||
size === "base" && "h-9 px-4 py-2.5 ",
|
||||
size === "lg" && "h-[36px] px-4 py-2.5 ",
|
||||
size === "icon" && "flex justify-center min-h-[36px] min-w-[36px] ",
|
||||
combined ? "" : "rounded-md",
|
||||
// different styles depending on color
|
||||
// set not-allowed cursor if disabled
|
||||
disabled ? variantDisabledClassName[color] : variantClassName[color],
|
||||
loading ? "cursor-wait" : disabled ? "cursor-not-allowed" : "",
|
||||
buttonClasses({ color, size, loading, disabled: props.disabled }),
|
||||
props.className
|
||||
),
|
||||
// if we click a disabled button, we prevent going through the click handler
|
||||
|
@ -116,10 +180,7 @@ export const Button = forwardRef<HTMLAnchorElement | HTMLButtonElement, ButtonPr
|
|||
{loading && (
|
||||
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 transform">
|
||||
<svg
|
||||
className={classNames(
|
||||
"mx-4 h-5 w-5 animate-spin",
|
||||
color === "primary" ? "text-white dark:text-black" : "text-black"
|
||||
)}
|
||||
className="mx-4 h-5 w-5 animate-spin text-black dark:text-white"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24">
|
||||
|
|
|
@ -80,31 +80,36 @@ Button are clickable elements that initiates user actions. Labels in the button
|
|||
|
||||
<Canvas>
|
||||
<Story name="All variants">
|
||||
<VariantsTable titles={['Primary', 'Secondary', 'Minimal']} columnMinWidth={150}>
|
||||
<VariantsTable titles={['Primary', 'Secondary', 'Minimal', 'Destructive']} columnMinWidth={150}>
|
||||
<VariantRow variant="Default">
|
||||
<Button>Button text</Button>
|
||||
<Button color="secondary">Button text</Button>
|
||||
<Button color="minimal">Button text</Button>
|
||||
<Button color="destructive">Button text</Button>
|
||||
</VariantRow>
|
||||
<VariantRow variant="Hover">
|
||||
<Button className="sb-pseudo--hover">Button text</Button>
|
||||
<Button className="sb-pseudo--hover" color="secondary">Button text</Button>
|
||||
<Button className="sb-pseudo--hover" color="minimal">Button text</Button>
|
||||
<Button className="sb-pseudo--hover" color="destructive">Button text</Button>
|
||||
</VariantRow>
|
||||
<VariantRow variant="Focus">
|
||||
<Button className="sb-pseudo--focus">Button text</Button>
|
||||
<Button className="sb-pseudo--focus" color="secondary">Button text</Button>
|
||||
<Button className="sb-pseudo--focus" color="minimal">Button text</Button>
|
||||
<Button className="sb-pseudo--focus" color="destructive">Button text</Button>
|
||||
</VariantRow>
|
||||
<VariantRow variant="Loading">
|
||||
<Button loading>Button text</Button>
|
||||
<Button loading color="secondary">Button text</Button>
|
||||
<Button loading color="minimal">Button text</Button>
|
||||
<Button loading color="destructive">Button text</Button>
|
||||
</VariantRow>
|
||||
<VariantRow variant="Disabled">
|
||||
<Button disabled>Button text</Button>
|
||||
<Button disabled color="secondary">Button text</Button>
|
||||
<Button disabled color="minimal">Button text</Button>
|
||||
<Button disabled color="destructive">Button text</Button>
|
||||
</VariantRow>
|
||||
</VariantsTable>
|
||||
</Story>
|
||||
|
|
|
@ -12,18 +12,19 @@
|
|||
"lint:report": "eslint . --format json --output-file ../../lint-results/ui.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tanstack/react-query": "^4.3.9",
|
||||
"@calcom/lib": "*",
|
||||
"@calcom/trpc": "*",
|
||||
"@formkit/auto-animate": "^1.0.0-beta.1",
|
||||
"@radix-ui/react-dialog": "^1.0.0",
|
||||
"@radix-ui/react-portal": "^1.0.0",
|
||||
"@radix-ui/react-select": "^0.1.1",
|
||||
"@tanstack/react-query": "^4.3.9",
|
||||
"class-variance-authority": "^0.3.0",
|
||||
"downshift": "^6.1.9",
|
||||
"next": "^12.3.1",
|
||||
"react": "^18.2.0",
|
||||
"react-colorful": "^5.6.0",
|
||||
"react-feather": "^2.0.10",
|
||||
"@formkit/auto-animate": "^1.0.0-beta.1",
|
||||
"react-hook-form": "^7.34.2",
|
||||
"react-icons": "^4.4.0",
|
||||
"react-select": "^5.4.0"
|
||||
|
|
|
@ -291,12 +291,7 @@ const MobileSettingsContainer = (props: { onSideContainerOpen?: () => void }) =>
|
|||
<>
|
||||
<nav className="fixed z-20 flex w-full items-center justify-between border-b border-gray-100 bg-gray-50 p-4 sm:relative lg:hidden">
|
||||
<div className="flex items-center space-x-3 ">
|
||||
<Button
|
||||
StartIcon={Icon.FiMenu}
|
||||
color="minimalSecondary"
|
||||
size="icon"
|
||||
onClick={props.onSideContainerOpen}
|
||||
/>
|
||||
<Button StartIcon={Icon.FiMenu} color="minimal" size="icon" onClick={props.onSideContainerOpen} />
|
||||
<a href="/" className="flex items-center space-x-2 rounded-md px-3 py-1 hover:bg-gray-200">
|
||||
<Icon.FiArrowLeft className="text-gray-700" />
|
||||
<p className="font-semibold text-black">{t("settings")}</p>
|
||||
|
|
12
yarn.lock
12
yarn.lock
|
@ -7222,11 +7222,16 @@
|
|||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/node@*", "@types/node@16.9.1", "@types/node@>=8.1.0", "@types/node@^12.12.54", "@types/node@^12.12.6", "@types/node@^14.0.10 || ^16.0.0", "@types/node@^14.14.20 || ^16.0.0":
|
||||
"@types/node@*", "@types/node@16.9.1", "@types/node@>=8.1.0", "@types/node@^14.0.10 || ^16.0.0", "@types/node@^14.14.20 || ^16.0.0":
|
||||
version "16.9.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.9.1.tgz#0611b37db4246c937feef529ddcc018cf8e35708"
|
||||
integrity sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==
|
||||
|
||||
"@types/node@^12.12.54", "@types/node@^12.12.6":
|
||||
version "12.20.55"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240"
|
||||
integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==
|
||||
|
||||
"@types/nodemailer@^6.4.5":
|
||||
version "6.4.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/nodemailer/-/nodemailer-6.4.5.tgz#09011ac73259245475d1688e4ba101860567dc39"
|
||||
|
@ -10272,6 +10277,11 @@ class-utils@^0.3.5:
|
|||
isobject "^3.0.0"
|
||||
static-extend "^0.1.1"
|
||||
|
||||
class-variance-authority@^0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/class-variance-authority/-/class-variance-authority-0.3.0.tgz#40c824bfbe5c604ecb3e337a2353b4b86278888e"
|
||||
integrity sha512-TFO+pzY9Gedqv8crPhprd647wxhvfpKevPPjiMcteEWsnkHX9yZrD1xMY3ZhRZnLwHUHCCP0LYO6KZIVag/5wQ==
|
||||
|
||||
classnames@^2.2.6, classnames@^2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e"
|
||||
|
|
Loading…
Reference in New Issue