mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-17 23:13:51 +01:00
Add .oxfmtignore to skip generated bindings and wasm-pack output. Add npm format script, update DEVELOPMENT.md for Vite+ toolchain, and format all non-generated files with oxfmt. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
96 lines
2.5 KiB
TypeScript
96 lines
2.5 KiB
TypeScript
import classNames from "classnames";
|
|
import type { MouseEvent } from "react";
|
|
import { forwardRef, useCallback } from "react";
|
|
import { useTimedBoolean } from "../../hooks/useTimedBoolean";
|
|
import type { ButtonProps } from "./Button";
|
|
import { Button } from "./Button";
|
|
import type { IconProps } from "./Icon";
|
|
import { Icon } from "./Icon";
|
|
import { LoadingIcon } from "./LoadingIcon";
|
|
|
|
export type IconButtonProps = IconProps &
|
|
ButtonProps & {
|
|
showConfirm?: boolean;
|
|
iconClassName?: string;
|
|
iconSize?: IconProps["size"];
|
|
iconColor?: IconProps["color"];
|
|
title: string;
|
|
showBadge?: boolean;
|
|
};
|
|
|
|
export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(function IconButton(
|
|
{
|
|
showConfirm,
|
|
icon,
|
|
color = "default",
|
|
spin,
|
|
onClick,
|
|
className,
|
|
iconClassName,
|
|
tabIndex,
|
|
size = "md",
|
|
iconSize,
|
|
showBadge,
|
|
iconColor,
|
|
isLoading,
|
|
type = "button",
|
|
...props
|
|
}: IconButtonProps,
|
|
ref,
|
|
) {
|
|
const [confirmed, setConfirmed] = useTimedBoolean();
|
|
const handleClick = useCallback(
|
|
(e: MouseEvent<HTMLButtonElement>) => {
|
|
if (showConfirm) setConfirmed();
|
|
onClick?.(e);
|
|
},
|
|
[onClick, setConfirmed, showConfirm],
|
|
);
|
|
|
|
return (
|
|
<Button
|
|
ref={ref}
|
|
aria-hidden={icon === "empty"}
|
|
disabled={icon === "empty"}
|
|
tabIndex={(tabIndex ?? icon === "empty") ? -1 : undefined}
|
|
onClick={handleClick}
|
|
innerClassName="flex items-center justify-center"
|
|
size={size}
|
|
color={color}
|
|
type={type}
|
|
className={classNames(
|
|
className,
|
|
"group/button relative flex-shrink-0",
|
|
"!px-0",
|
|
size === "md" && "w-md",
|
|
size === "sm" && "w-sm",
|
|
size === "xs" && "w-xs",
|
|
size === "2xs" && "w-5",
|
|
)}
|
|
{...props}
|
|
>
|
|
{showBadge && (
|
|
<div className="absolute top-0 right-0 w-1/2 h-1/2 flex items-center justify-center">
|
|
<div className="w-2.5 h-2.5 bg-pink-500 rounded-full" />
|
|
</div>
|
|
)}
|
|
{isLoading ? (
|
|
<LoadingIcon size={iconSize} className={iconClassName} />
|
|
) : (
|
|
<Icon
|
|
size={iconSize}
|
|
icon={confirmed ? "check" : icon}
|
|
spin={spin}
|
|
color={iconColor}
|
|
className={classNames(
|
|
iconClassName,
|
|
"group-hover/button:text-text",
|
|
confirmed && "!text-success", // Don't use Icon.color here because it won't override the hover color
|
|
props.disabled && "opacity-70",
|
|
)}
|
|
/>
|
|
)}
|
|
</Button>
|
|
);
|
|
});
|