fix: Launchwindow Button instead of IconButton, reusing button.tsx

add: button.tsx "iconSize" variants for different icons size, replacing effectively iconButton. and can be reused accross the whole app.
fix: RecordingControls with those same ui component !
This commit is contained in:
Alan Trebugeais
2026-05-06 19:36:52 +02:00
parent b1a0667a1c
commit 729392c06c
3 changed files with 91 additions and 38 deletions
+39 -20
View File
@@ -127,9 +127,8 @@ export function DropdownItem({
);
}
export function Separator({ dropdown = false }: { dropdown?: boolean }) {
return <div className={dropdown ? styles.ddSep : styles.sep} />;
}
import { Separator } from "@/components/ui/separator";
import { Button } from "../ui/button";
function MicDeviceRow({
device,
@@ -997,15 +996,20 @@ export function LaunchWindow() {
</ContentClamp>
<ChevronUp
size={10}
className={`text-[#6b6b78] ml-0.5 transition-transform duration-200 ${activeDropdown === "sources" ? "" : "rotate-180"}`}
className={`text-[#6b6b78] ml-0.5 transition-transform duration-200 ${
activeDropdown === "sources" ? "" : "rotate-180"
}`}
/>
</button>
<Separator />
<Separator orientation="vertical" className="mx-[5px] h-6" />
</>
)}
<IconButton
<Button
variant="ghost"
size="icon"
iconSize="lg"
onClick={toggleMicrophone}
title={
microphoneEnabled
@@ -1015,25 +1019,31 @@ export function LaunchWindow() {
className={microphoneEnabled ? styles.ibActive : ""}
>
{microphoneEnabled ? <Mic size={18} /> : <MicOff size={18} />}
</IconButton>
</Button>
<IconButton
<Button
variant="ghost"
size="icon"
iconSize="lg"
onClick={toggleWebcam}
title={webcamEnabled ? t("recording.disableWebcam") : t("recording.enableWebcam")}
className={webcamEnabled ? styles.ibActive : ""}
>
{webcamEnabled ? <Video size={18} /> : <VideoOff size={18} />}
</IconButton>
</Button>
<IconButton
<Button
variant="ghost"
size="icon"
iconSize="lg"
onClick={() => toggleDropdown("countdown")}
title={t("recording.countdownDelay")}
className={countdownDelay > 0 ? styles.ibActive : ""}
>
<Timer size={18} />
</IconButton>
</Button>
<Separator />
<Separator orientation="vertical" className="mx-[5px] h-6" />
<button
type="button"
@@ -1049,29 +1059,38 @@ export function LaunchWindow() {
<div className={styles.recDot} />
</button>
<Separator />
<Separator orientation="vertical" className="mx-[5px] h-6" />
<IconButton
buttonRef={moreButtonRef}
<Button
variant="ghost"
size="icon"
iconSize="lg"
ref={moreButtonRef}
onClick={() => toggleDropdown("more")}
title={t("recording.more")}
>
<MoreVertical size={18} />
</IconButton>
</Button>
<IconButton
<Button
variant="ghost"
size="icon"
iconSize="lg"
onClick={() => window.electronAPI?.hudOverlayHide?.()}
title={t("recording.hideHud")}
>
<Minus size={16} />
</IconButton>
</Button>
<IconButton
<Button
variant="ghost"
size="icon"
iconSize="lg"
onClick={() => window.electronAPI?.hudOverlayClose?.()}
title={t("recording.closeApp")}
>
<X size={16} />
</IconButton>
</Button>
</>
);
+37 -15
View File
@@ -1,8 +1,8 @@
import { Microphone as Mic, MicrophoneSlash as MicOff, Minus, Pause, Play, Stop as Square, X } from "@phosphor-icons/react";
import { useMemo } from "react";
import { useScopedT } from "@/contexts/I18nContext";
import { IconButton } from "./LaunchWindow";
import { Separator } from "./LaunchWindow";
import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
import styles from "./LaunchWindow.module.css";
interface RecordingControlsProps {
@@ -56,9 +56,12 @@ export const RecordingControls = ({
{formatTime(elapsed)}
</span>
<Separator />
<Separator orientation="vertical" className="mx-[5px] h-6" />
<IconButton
<Button
variant="ghost"
size="icon"
iconSize="lg"
title={
microphoneEnabled
? t("recording.disableMicrophone")
@@ -67,12 +70,19 @@ export const RecordingControls = ({
className={microphoneEnabled ? styles.ibActive : ""}
onClick={onToggleMicrophone}
>
{microphoneEnabled ? <Mic size={18} /> : <MicOff size={18} />}
</IconButton>
{microphoneEnabled ? (
<Mic size={18} />
) : (
<MicOff size={18} />
)}
</Button>
<Separator />
<Separator orientation="vertical" className="mx-[5px] h-6" />
<IconButton
<Button
variant={paused ? "default" : "ghost"}
size="icon"
iconSize="lg"
onClick={onPauseResume}
title={paused ? t("recording.resume") : t("recording.pause")}
className={paused ? styles.ibGreen : ""}
@@ -82,26 +92,38 @@ export const RecordingControls = ({
) : (
<Pause size={18} />
)}
</IconButton>
</Button>
<IconButton
<Button
variant="ghost"
size="icon"
iconSize="lg"
onClick={onStopRecording}
title={t("recording.stop")}
className={styles.ibRed}
>
<Square size={16} fill="currentColor" strokeWidth={0} />
</IconButton>
</Button>
<IconButton
<Button
variant="ghost"
size="icon"
iconSize="lg"
onClick={onHideHud}
title={t("recording.hideHud")}
>
<Minus size={16} />
</IconButton>
</Button>
<IconButton onClick={onCancelRecording} title={t("recording.cancel")}>
<Button
variant="ghost"
size="icon"
iconSize="lg"
onClick={onCancelRecording}
title={t("recording.cancel")}
>
<X size={18} />
</IconButton>
</Button>
</>
);
}, [
+15 -3
View File
@@ -5,7 +5,7 @@ import * as React from "react";
import { cn } from "@/lib/utils";
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
{
variants: {
variant: {
@@ -24,10 +24,18 @@ const buttonVariants = cva(
lg: "h-10 rounded-md px-8",
icon: "h-9 w-9",
},
// Special variant for icon buttons with consistent sizing
iconSize: {
default: "[&_svg]:size-4",
sm: "[&_svg]:size-3.5",
lg: "[&_svg]:size-5",
xl: "[&_svg]:size-6",
},
},
defaultVariants: {
variant: "default",
size: "default",
iconSize: "default",
},
},
);
@@ -35,15 +43,18 @@ const buttonVariants = cva(
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
/** Whether to render the button as a child component (useful for composition) */
asChild?: boolean;
/** Size of the icon inside the button */
iconSize?: "default" | "sm" | "lg" | "xl";
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
({ className, variant, size, iconSize, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button";
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
className={cn(buttonVariants({ variant, size, iconSize, className }))}
ref={ref}
{...props}
/>
@@ -53,3 +64,4 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
Button.displayName = "Button";
export { Button, buttonVariants };