111 lines
3.8 KiB
TypeScript
111 lines
3.8 KiB
TypeScript
import React, { MouseEventHandler } from "react";
|
|
import { Link } from "./Link";
|
|
import { Ico, Icon } from "components/Ico";
|
|
import { cIf, cJoin } from "helpers/className";
|
|
import { ConditionalWrapper, Wrapper } from "helpers/component";
|
|
import { isDefined, isDefinedAndNotEmpty } from "helpers/others";
|
|
|
|
/*
|
|
* ╭─────────────╮
|
|
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
|
*/
|
|
|
|
interface Props {
|
|
id?: string;
|
|
className?: string;
|
|
href?: string;
|
|
active?: boolean;
|
|
icon?: Icon;
|
|
text?: string | null | undefined;
|
|
alwaysNewTab?: boolean;
|
|
onClick?: MouseEventHandler<HTMLDivElement>;
|
|
draggable?: boolean;
|
|
badgeNumber?: number;
|
|
disabled?: boolean;
|
|
size?: "normal" | "small";
|
|
}
|
|
|
|
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
|
|
|
export const Button = ({
|
|
draggable,
|
|
id,
|
|
onClick,
|
|
active,
|
|
className,
|
|
icon,
|
|
text,
|
|
href,
|
|
alwaysNewTab = false,
|
|
badgeNumber,
|
|
disabled,
|
|
size = "normal",
|
|
}: Props): JSX.Element => (
|
|
<ConditionalWrapper
|
|
isWrapping={isDefinedAndNotEmpty(href)}
|
|
wrapperProps={{ href: href ?? "", alwaysNewTab }}
|
|
wrapper={LinkWrapper}
|
|
>
|
|
<div className="relative">
|
|
<div
|
|
draggable={draggable}
|
|
id={id}
|
|
onClick={onClick}
|
|
onFocus={(event) => event.target.blur()}
|
|
className={cJoin(
|
|
`group grid cursor-pointer select-none grid-flow-col place-content-center
|
|
place-items-center gap-2 rounded-full border-[1px] border-dark py-3 px-4
|
|
leading-none text-dark transition-all`,
|
|
cIf(
|
|
active,
|
|
"!border-black bg-black !text-light drop-shadow-black-lg",
|
|
`hover:bg-dark hover:text-light hover:drop-shadow-shade-lg active:hover:!border-black
|
|
active:hover:bg-black active:hover:!text-light active:hover:drop-shadow-black-lg`
|
|
),
|
|
cIf(size === "small", "px-3 py-1 text-xs"),
|
|
cIf(disabled, "cursor-not-allowed"),
|
|
className
|
|
)}
|
|
>
|
|
{isDefined(badgeNumber) && (
|
|
<div
|
|
className={cJoin(
|
|
`absolute -top-3 -right-2 grid h-8 w-8 place-items-center
|
|
rounded-full bg-dark font-bold text-light transition-opacity group-hover:opacity-0`,
|
|
cIf(size === "small", "-top-2 -right-2 h-5 w-5")
|
|
)}
|
|
>
|
|
<p className="-translate-y-[0.05em]">{badgeNumber}</p>
|
|
</div>
|
|
)}
|
|
{isDefinedAndNotEmpty(icon) && (
|
|
<Ico className="[font-size:150%] [line-height:0.66]" icon={icon} />
|
|
)}
|
|
{isDefinedAndNotEmpty(text) && (
|
|
<p className="-translate-y-[0.05em] text-center">{text}</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</ConditionalWrapper>
|
|
);
|
|
|
|
/*
|
|
* ╭──────────────────────╮
|
|
* ───────────────────────────────────╯ PRIVATE COMPONENTS ╰──────────────────────────────────────
|
|
*/
|
|
|
|
interface LinkWrapperProps {
|
|
href: string;
|
|
alwaysNewTab: boolean;
|
|
}
|
|
|
|
const LinkWrapper = ({
|
|
children,
|
|
alwaysNewTab,
|
|
href,
|
|
}: LinkWrapperProps & Wrapper) => (
|
|
<Link href={href} alwaysNewTab={alwaysNewTab}>
|
|
{children}
|
|
</Link>
|
|
);
|