Migrate icon buttons to Button component and simplify size variants
Replace raw <button> elements with Button variant="ghost" in stat-block panel, toast, player modals. Add icon-sm size variant (h-6 w-6) for compact contexts. Consolidate text button sizes into a single default (h-8 px-3), removing the redundant sm variant. Add size prop to ConfirmButton for consistent sizing. Button now has three sizes: default (text), icon, icon-sm. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -553,9 +553,7 @@ export function ActionBar({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!browseMode && nameInput.length >= 2 && !hasSuggestions && (
|
{!browseMode && nameInput.length >= 2 && !hasSuggestions && (
|
||||||
<Button type="submit" size="sm">
|
<Button type="submit">Add</Button>
|
||||||
Add
|
|
||||||
</Button>
|
|
||||||
)}
|
)}
|
||||||
{showRollAllInitiative && onRollAllInitiative && (
|
{showRollAllInitiative && onRollAllInitiative && (
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -28,9 +28,7 @@ export function BulkImportPrompt({
|
|||||||
<div className="rounded-md border border-green-500/50 bg-green-500/10 px-3 py-2 text-sm text-green-400">
|
<div className="rounded-md border border-green-500/50 bg-green-500/10 px-3 py-2 text-sm text-green-400">
|
||||||
All sources loaded
|
All sources loaded
|
||||||
</div>
|
</div>
|
||||||
<Button size="sm" onClick={onDone}>
|
<Button onClick={onDone}>Done</Button>
|
||||||
Done
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -42,9 +40,7 @@ export function BulkImportPrompt({
|
|||||||
Loaded {importState.completed}/{importState.total} sources (
|
Loaded {importState.completed}/{importState.total} sources (
|
||||||
{importState.failed} failed)
|
{importState.failed} failed)
|
||||||
</div>
|
</div>
|
||||||
<Button size="sm" onClick={onDone}>
|
<Button onClick={onDone}>Done</Button>
|
||||||
Done
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -103,11 +99,7 @@ export function BulkImportPrompt({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button
|
<Button onClick={() => onStartImport(baseUrl)} disabled={isDisabled}>
|
||||||
size="sm"
|
|
||||||
onClick={() => onStartImport(baseUrl)}
|
|
||||||
disabled={isDisabled}
|
|
||||||
>
|
|
||||||
Load All
|
Load All
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -100,13 +100,14 @@ export function CreatePlayerModal({
|
|||||||
<h2 className="text-lg font-semibold text-foreground">
|
<h2 className="text-lg font-semibold text-foreground">
|
||||||
{isEdit ? "Edit Player" : "Create Player"}
|
{isEdit ? "Edit Player" : "Create Player"}
|
||||||
</h2>
|
</h2>
|
||||||
<button
|
<Button
|
||||||
type="button"
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="text-muted-foreground hover:text-hover-neutral transition-colors"
|
className="text-muted-foreground"
|
||||||
>
|
>
|
||||||
<X size={20} />
|
<X size={20} />
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit} className="flex flex-col gap-4">
|
<form onSubmit={handleSubmit} className="flex flex-col gap-4">
|
||||||
|
|||||||
@@ -52,19 +52,20 @@ export function PlayerManagement({
|
|||||||
<h2 className="text-lg font-semibold text-foreground">
|
<h2 className="text-lg font-semibold text-foreground">
|
||||||
Player Characters
|
Player Characters
|
||||||
</h2>
|
</h2>
|
||||||
<button
|
<Button
|
||||||
type="button"
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="text-muted-foreground hover:text-hover-neutral transition-colors"
|
className="text-muted-foreground"
|
||||||
>
|
>
|
||||||
<X size={20} />
|
<X size={20} />
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{characters.length === 0 ? (
|
{characters.length === 0 ? (
|
||||||
<div className="flex flex-col items-center gap-3 py-8 text-center">
|
<div className="flex flex-col items-center gap-3 py-8 text-center">
|
||||||
<p className="text-muted-foreground">No player characters yet</p>
|
<p className="text-muted-foreground">No player characters yet</p>
|
||||||
<Button onClick={onCreate} size="sm">
|
<Button onClick={onCreate}>
|
||||||
<Plus size={16} />
|
<Plus size={16} />
|
||||||
Create your first player character
|
Create your first player character
|
||||||
</Button>
|
</Button>
|
||||||
@@ -92,25 +93,27 @@ export function PlayerManagement({
|
|||||||
<span className="text-xs tabular-nums text-muted-foreground">
|
<span className="text-xs tabular-nums text-muted-foreground">
|
||||||
HP {pc.maxHp}
|
HP {pc.maxHp}
|
||||||
</span>
|
</span>
|
||||||
<button
|
<Button
|
||||||
type="button"
|
variant="ghost"
|
||||||
|
size="icon-sm"
|
||||||
onClick={() => onEdit(pc)}
|
onClick={() => onEdit(pc)}
|
||||||
className="text-muted-foreground hover:text-hover-neutral transition-colors"
|
className="text-muted-foreground"
|
||||||
title="Edit"
|
title="Edit"
|
||||||
>
|
>
|
||||||
<Pencil size={14} />
|
<Pencil size={14} />
|
||||||
</button>
|
</Button>
|
||||||
<ConfirmButton
|
<ConfirmButton
|
||||||
icon={<Trash2 size={14} />}
|
icon={<Trash2 size={14} />}
|
||||||
label="Delete player character"
|
label="Delete player character"
|
||||||
onConfirm={() => onDelete(pc.id)}
|
onConfirm={() => onDelete(pc.id)}
|
||||||
className="h-6 w-6 text-muted-foreground"
|
size="icon-sm"
|
||||||
|
className="text-muted-foreground"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
<div className="mt-2 flex justify-end">
|
<div className="mt-2 flex justify-end">
|
||||||
<Button onClick={onCreate} size="sm" variant="ghost">
|
<Button onClick={onCreate} variant="ghost">
|
||||||
<Plus size={16} />
|
<Plus size={16} />
|
||||||
Add
|
Add
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -88,11 +88,7 @@ export function SourceFetchPrompt({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Button
|
<Button onClick={handleFetch} disabled={status === "fetching" || !url}>
|
||||||
size="sm"
|
|
||||||
onClick={handleFetch}
|
|
||||||
disabled={status === "fetching" || !url}
|
|
||||||
>
|
|
||||||
{status === "fetching" ? (
|
{status === "fetching" ? (
|
||||||
<Loader2 className="mr-1 h-3 w-3 animate-spin" />
|
<Loader2 className="mr-1 h-3 w-3 animate-spin" />
|
||||||
) : (
|
) : (
|
||||||
@@ -104,7 +100,6 @@ export function SourceFetchPrompt({
|
|||||||
<span className="text-xs text-muted-foreground">or</span>
|
<span className="text-xs text-muted-foreground">or</span>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => fileInputRef.current?.click()}
|
onClick={() => fileInputRef.current?.click()}
|
||||||
disabled={status === "fetching"}
|
disabled={status === "fetching"}
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ export function SourceManager({ onCacheCleared }: SourceManagerProps) {
|
|||||||
Cached Sources
|
Cached Sources
|
||||||
</span>
|
</span>
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="hover:text-hover-destructive hover:border-hover-destructive"
|
className="hover:text-hover-destructive hover:border-hover-destructive"
|
||||||
onClick={handleClearAll}
|
onClick={handleClearAll}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { useSwipeToDismiss } from "../hooks/use-swipe-to-dismiss.js";
|
|||||||
import { BulkImportPrompt } from "./bulk-import-prompt.js";
|
import { BulkImportPrompt } from "./bulk-import-prompt.js";
|
||||||
import { SourceFetchPrompt } from "./source-fetch-prompt.js";
|
import { SourceFetchPrompt } from "./source-fetch-prompt.js";
|
||||||
import { StatBlock } from "./stat-block.js";
|
import { StatBlock } from "./stat-block.js";
|
||||||
|
import { Button } from "./ui/button.js";
|
||||||
|
|
||||||
interface StatBlockPanelProps {
|
interface StatBlockPanelProps {
|
||||||
creatureId: CreatureId | null;
|
creatureId: CreatureId | null;
|
||||||
@@ -81,36 +82,39 @@ function PanelHeader({
|
|||||||
<div className="flex items-center justify-between border-b border-border px-4 py-2">
|
<div className="flex items-center justify-between border-b border-border px-4 py-2">
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
{panelRole === "browse" && (
|
{panelRole === "browse" && (
|
||||||
<button
|
<Button
|
||||||
type="button"
|
variant="ghost"
|
||||||
|
size="icon-sm"
|
||||||
onClick={onToggleFold}
|
onClick={onToggleFold}
|
||||||
className="text-muted-foreground hover:text-hover-neutral"
|
className="text-muted-foreground"
|
||||||
aria-label="Fold stat block panel"
|
aria-label="Fold stat block panel"
|
||||||
>
|
>
|
||||||
<PanelRightClose className="h-4 w-4" />
|
<PanelRightClose className="h-4 w-4" />
|
||||||
</button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
{panelRole === "browse" && showPinButton && (
|
{panelRole === "browse" && showPinButton && (
|
||||||
<button
|
<Button
|
||||||
type="button"
|
variant="ghost"
|
||||||
|
size="icon-sm"
|
||||||
onClick={onPin}
|
onClick={onPin}
|
||||||
className="text-muted-foreground hover:text-hover-neutral"
|
className="text-muted-foreground"
|
||||||
aria-label="Pin creature"
|
aria-label="Pin creature"
|
||||||
>
|
>
|
||||||
<Pin className="h-4 w-4" />
|
<Pin className="h-4 w-4" />
|
||||||
</button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{panelRole === "pinned" && (
|
{panelRole === "pinned" && (
|
||||||
<button
|
<Button
|
||||||
type="button"
|
variant="ghost"
|
||||||
|
size="icon-sm"
|
||||||
onClick={onUnpin}
|
onClick={onUnpin}
|
||||||
className="text-muted-foreground hover:text-hover-neutral"
|
className="text-muted-foreground"
|
||||||
aria-label="Unpin creature"
|
aria-label="Unpin creature"
|
||||||
>
|
>
|
||||||
<PinOff className="h-4 w-4" />
|
<PinOff className="h-4 w-4" />
|
||||||
</button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -195,14 +199,15 @@ function MobileDrawer({
|
|||||||
{...handlers}
|
{...handlers}
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between border-b border-border px-4 py-2">
|
<div className="flex items-center justify-between border-b border-border px-4 py-2">
|
||||||
<button
|
<Button
|
||||||
type="button"
|
variant="ghost"
|
||||||
|
size="icon-sm"
|
||||||
onClick={onDismiss}
|
onClick={onDismiss}
|
||||||
className="text-muted-foreground hover:text-hover-neutral"
|
className="text-muted-foreground"
|
||||||
aria-label="Fold stat block panel"
|
aria-label="Fold stat block panel"
|
||||||
>
|
>
|
||||||
<PanelRightClose className="h-4 w-4" />
|
<PanelRightClose className="h-4 w-4" />
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="h-[calc(100%-41px)] overflow-y-auto p-4">
|
<div className="h-[calc(100%-41px)] overflow-y-auto p-4">
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { X } from "lucide-react";
|
import { X } from "lucide-react";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { createPortal } from "react-dom";
|
import { createPortal } from "react-dom";
|
||||||
|
import { Button } from "./ui/button.js";
|
||||||
|
|
||||||
interface ToastProps {
|
interface ToastProps {
|
||||||
message: string;
|
message: string;
|
||||||
@@ -33,13 +34,14 @@ export function Toast({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<button
|
<Button
|
||||||
type="button"
|
variant="ghost"
|
||||||
|
size="icon-sm"
|
||||||
onClick={onDismiss}
|
onClick={onDismiss}
|
||||||
className="text-muted-foreground hover:text-hover-neutral"
|
className="text-muted-foreground"
|
||||||
>
|
>
|
||||||
<X className="h-3 w-3" />
|
<X className="h-3 w-3" />
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>,
|
</div>,
|
||||||
document.body,
|
document.body,
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ const buttonVariants = cva(
|
|||||||
ghost: "hover:bg-hover-neutral-bg hover:text-hover-neutral",
|
ghost: "hover:bg-hover-neutral-bg hover:text-hover-neutral",
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
default: "h-9 px-4 py-2",
|
default: "h-8 px-3 text-xs",
|
||||||
sm: "h-8 px-3 text-xs",
|
|
||||||
icon: "h-8 w-8",
|
icon: "h-8 w-8",
|
||||||
|
"icon-sm": "h-6 w-6",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ interface ConfirmButtonProps {
|
|||||||
readonly onConfirm: () => void;
|
readonly onConfirm: () => void;
|
||||||
readonly icon: ReactElement;
|
readonly icon: ReactElement;
|
||||||
readonly label: string;
|
readonly label: string;
|
||||||
|
readonly size?: "icon" | "icon-sm";
|
||||||
readonly className?: string;
|
readonly className?: string;
|
||||||
readonly disabled?: boolean;
|
readonly disabled?: boolean;
|
||||||
}
|
}
|
||||||
@@ -23,6 +24,7 @@ export function ConfirmButton({
|
|||||||
onConfirm,
|
onConfirm,
|
||||||
icon,
|
icon,
|
||||||
label,
|
label,
|
||||||
|
size = "icon",
|
||||||
className,
|
className,
|
||||||
disabled,
|
disabled,
|
||||||
}: ConfirmButtonProps) {
|
}: ConfirmButtonProps) {
|
||||||
@@ -94,7 +96,7 @@ export function ConfirmButton({
|
|||||||
<div ref={wrapperRef} className="inline-flex">
|
<div ref={wrapperRef} className="inline-flex">
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size={size}
|
||||||
className={cn(
|
className={cn(
|
||||||
className,
|
className,
|
||||||
isConfirming
|
isConfirming
|
||||||
|
|||||||
Reference in New Issue
Block a user