refactor: Update bottom controls for improved styling and functionality
- Adjusted button styles across various components for consistency and better UX. - Enhanced layout of action controls to utilize whitespace more effectively. - Updated slider styles for a more modern appearance and improved usability. - Refined input fields and labels for better accessibility and readability. - Introduced new app surface styles for a cohesive design across the application. - Added tests for canvas cursor behavior to ensure correct cursor display during operations.
This commit is contained in:
@@ -17,6 +17,9 @@ import { addAdjustmentLayer, addArtboard, addEmptyLayer, addGroupLayer, addLayer
|
||||
import { MaskOperationButtons, MaskStatus } from "./layers/MaskControls";
|
||||
import { LayerThumbnail } from "./layers/LayerThumbnail";
|
||||
import { createLayerThumbnailIndex, type LayerThumbnailModel } from "./layers/thumbnailModel";
|
||||
import { BottomControlColorPicker } from "./bottom-controls/ColorPicker";
|
||||
import { BottomControlSlider } from "./bottom-controls/Slider";
|
||||
import { BottomControlSelectMenu, type BottomControlSelectOption } from "./bottom-controls/SelectMenu";
|
||||
|
||||
export type LayersSheetProps = {
|
||||
document: ImageDocument;
|
||||
@@ -36,7 +39,7 @@ export function LayersSheet({ document, selection, maskEdit, open, dispatch, doc
|
||||
id="layers-sheet"
|
||||
aria-hidden={!open}
|
||||
aria-label="Layers"
|
||||
className={`pointer-events-auto absolute bottom-6 right-4 top-24 z-20 flex w-[28rem] flex-col overflow-hidden rounded-[2.5rem] px-4 text-sm text-white backdrop-blur-xl transition-all duration-200 ${
|
||||
className={`app-surface pointer-events-auto absolute bottom-3 right-3 top-[4.5rem] z-20 flex w-[22rem] flex-col overflow-hidden rounded-xl px-3 text-xs text-white transition-all duration-200 ${
|
||||
open ? "translate-x-0 opacity-100" : "pointer-events-none translate-x-8 opacity-0"
|
||||
}`}
|
||||
>
|
||||
@@ -81,14 +84,14 @@ function LayersSheetBody({
|
||||
|
||||
return (
|
||||
<>
|
||||
<header className="flex h-20 items-center">
|
||||
<header className="flex h-14 items-center border-b border-white/[0.06]">
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<button type="button" className={labeledToolbarButtonClass()} aria-label="Add artboard" title="Add artboard" onClick={() => addArtboard(document, dispatch)}>
|
||||
<span className="grid w-12 flex-none place-items-center"><Plus size={24} /></span>
|
||||
<span className="grid w-8 flex-none place-items-center"><Plus size={17} /></span>
|
||||
<span className="min-w-0 flex-1 text-center">Artboard</span>
|
||||
</button>
|
||||
<button type="button" className={labeledToolbarButtonClass()} aria-label="Add layer" title="Add layer" disabled={!selectedArtboardId} onClick={() => selectedArtboardId && addEmptyLayer(document, selectedArtboardId, selectedLayer, dispatch)}>
|
||||
<span className="grid w-12 flex-none place-items-center"><Plus size={24} /></span>
|
||||
<span className="grid w-8 flex-none place-items-center"><Plus size={17} /></span>
|
||||
<span className="min-w-0 flex-1 text-center">Layer</span>
|
||||
</button>
|
||||
<button type="button" className={toolbarButtonClass()} aria-label="Add group" title="Add group" disabled={!selectedArtboardId} onClick={() => selectedArtboardId && addGroupLayer(selectedArtboardId, dispatch)}>
|
||||
@@ -100,7 +103,7 @@ function LayersSheetBody({
|
||||
<button type="button" className={toolbarButtonClass()} aria-label="Add text" title="Add text layer" disabled={!selectedArtboardId} onClick={() => selectedArtboardId && addTextLayer(document, selectedArtboardId, selectedLayer, dispatch)}><TextT size={24} /></button>
|
||||
</div>
|
||||
</header>
|
||||
<div className="mb-4 grid grid-cols-5 gap-2">
|
||||
<div className="my-2 grid grid-cols-5 gap-1 border-b border-white/[0.06] pb-2">
|
||||
<button type="button" className={toolbarButtonClass()} aria-label="Group selected" title="Group selected" disabled={!canGroup} onClick={() => selection.artboardId && groupLayers(selection.artboardId, selection.layerIds, dispatch)}>
|
||||
<Stack size={24} />
|
||||
</button>
|
||||
@@ -124,9 +127,9 @@ function LayersSheetBody({
|
||||
const displayLayerCount = documentIndex.displayLayerCountByArtboardId.get(artboard.id) ?? 0;
|
||||
|
||||
return (
|
||||
<section key={artboard.id} className="mb-5 last:mb-0">
|
||||
<section key={artboard.id} className="mb-3 last:mb-0">
|
||||
<div
|
||||
className={`flex h-12 w-full items-center gap-3 rounded-full px-4 text-left transition ${selection.artboardId === artboard.id && selection.layerIds.length === 0 ? "bg-white text-black" : "text-white/70 hover:bg-white/[0.06] hover:text-white"}`}
|
||||
className={`flex h-9 w-full items-center gap-2 rounded-lg px-2 text-left transition ${selection.artboardId === artboard.id && selection.layerIds.length === 0 ? "bg-sky-300 text-slate-950" : "text-white/70 hover:bg-white/[0.06] hover:text-white"}`}
|
||||
onDragOver={(event) => {
|
||||
if (draggedLayerId.current) event.preventDefault();
|
||||
}}
|
||||
@@ -174,11 +177,11 @@ function LayersSheetBody({
|
||||
>
|
||||
<DownloadSimple size={24} weight="regular" />
|
||||
</button>
|
||||
<span className={selection.artboardId === artboard.id && selection.layerIds.length === 0 ? "min-w-8 rounded-full bg-black/10 px-2 py-1 text-center text-xs text-black/45" : "min-w-8 rounded-full bg-white/10 px-2 py-1 text-center text-xs text-white/45"}>{displayLayerCount}</span>
|
||||
<span className={selection.artboardId === artboard.id && selection.layerIds.length === 0 ? "min-w-6 rounded bg-black/10 px-1.5 py-0.5 text-center text-[0.65rem] text-black/45" : "min-w-6 rounded bg-white/[0.06] px-1.5 py-0.5 text-center text-[0.65rem] text-white/40"}>{displayLayerCount}</span>
|
||||
</div>
|
||||
<div className="mt-2 space-y-2 pl-5">
|
||||
<div className="mt-1 space-y-1 pl-3">
|
||||
{displayLayerCount === 0 ? (
|
||||
<div className="rounded-[1.5rem] border border-dashed border-white/10 px-4 py-5 text-center text-white/35">No layers yet</div>
|
||||
<div className="rounded-lg border border-dashed border-white/10 px-3 py-3 text-center text-white/30">No layers yet</div>
|
||||
) : (
|
||||
artboard.layers.map((layer) => (
|
||||
<LayerRow
|
||||
@@ -211,24 +214,49 @@ function LayersSheetBody({
|
||||
function TextInspector({ layer, dispatch }: { layer: TextLayer; dispatch: AppStore["dispatch"] }) {
|
||||
const [content, setContent] = useState(layer.content);
|
||||
const [style, setStyle] = useState<TextStyle>({ ...layer.style });
|
||||
useEffect(() => { setContent(layer.content); setStyle({ ...layer.style }); }, [layer.id, layer.content, layer.style]);
|
||||
const commit = () => dispatch(commandIds.documentSetTextLayer, { layerId: layer.id, content, style });
|
||||
return <section aria-label="Text settings" className="mb-3 space-y-2 rounded-[1.5rem] bg-white/[0.05] p-3">
|
||||
<textarea aria-label="Text content" className="w-full resize-y rounded-xl bg-black/25 p-2 text-sm outline-none ring-1 ring-white/10" rows={2} value={content} disabled={layer.locked} onChange={(event) => setContent(event.target.value)} onBlur={commit} onKeyDown={(event) => event.stopPropagation()} />
|
||||
const [fontSizeDraft, setFontSizeDraft] = useState(String(layer.style.fontSize));
|
||||
const [lineHeightDraft, setLineHeightDraft] = useState(String(layer.style.lineHeight));
|
||||
useEffect(() => { setContent(layer.content); setStyle({ ...layer.style }); setFontSizeDraft(String(layer.style.fontSize)); setLineHeightDraft(String(layer.style.lineHeight)); }, [layer.id, layer.content, layer.style]);
|
||||
const updateText = (nextContent: string) => {
|
||||
setContent(nextContent);
|
||||
dispatch(commandIds.documentSetTextLayer, { layerId: layer.id, content: nextContent, style });
|
||||
};
|
||||
const updateStyle = (nextStyle: TextStyle) => {
|
||||
setStyle(nextStyle);
|
||||
dispatch(commandIds.documentSetTextLayer, { layerId: layer.id, content, style: nextStyle });
|
||||
};
|
||||
const commitNumber = (field: "fontSize" | "lineHeight", draft: string, reset: (value: string) => void) => {
|
||||
const next = Number(draft.trim());
|
||||
if (draft.trim() === "" || !Number.isFinite(next)) { reset(String(layer.style[field])); return; }
|
||||
const nextStyle = { ...style, [field]: next };
|
||||
setStyle(nextStyle);
|
||||
dispatch(commandIds.documentSetTextLayer, { layerId: layer.id, content, style: nextStyle });
|
||||
};
|
||||
return <section aria-label="Text settings" className="mb-2 space-y-2 border-b border-white/[0.07] px-0.5 pb-3">
|
||||
<textarea aria-label="Text content" className="w-full resize-y rounded-md bg-white/[0.035] p-2 text-xs outline-none ring-1 ring-white/[0.07] focus:ring-sky-300/50" rows={2} value={content} disabled={layer.locked} onChange={(event) => updateText(event.target.value)} onKeyDown={(event) => event.stopPropagation()} />
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
<select aria-label="Font family" className={textFieldClass()} value={style.fontFamily} disabled={layer.locked} onChange={(event) => setStyle({ ...style, fontFamily: event.target.value as TextStyle["fontFamily"] })} onBlur={commit}>{builtInTextFonts.map((font) => <option key={font}>{font}</option>)}</select>
|
||||
<input aria-label="Font size" className={textFieldClass()} type="number" min="1" max="1000" value={style.fontSize} disabled={layer.locked} onChange={(event) => setStyle({ ...style, fontSize: Number(event.target.value) })} onBlur={commit} />
|
||||
<input aria-label="Text color" className={textFieldClass()} type="color" value={style.color} disabled={layer.locked} onChange={(event) => setStyle({ ...style, color: event.target.value })} onBlur={commit} />
|
||||
<select aria-label="Font weight" className={textFieldClass()} value={style.fontWeight} disabled={layer.locked} onChange={(event) => setStyle({ ...style, fontWeight: Number(event.target.value) as 400 | 700 })} onBlur={commit}><option value="400">Regular</option><option value="700">Bold</option></select>
|
||||
<select aria-label="Font style" className={textFieldClass()} value={style.fontStyle} disabled={layer.locked} onChange={(event) => setStyle({ ...style, fontStyle: event.target.value as TextStyle["fontStyle"] })} onBlur={commit}><option value="normal">Normal</option><option value="italic">Italic</option></select>
|
||||
<select aria-label="Text alignment" className={textFieldClass()} value={style.alignment} disabled={layer.locked} onChange={(event) => setStyle({ ...style, alignment: event.target.value as TextStyle["alignment"] })} onBlur={commit}><option value="left">Left</option><option value="center">Center</option><option value="right">Right</option></select>
|
||||
<input aria-label="Line height" className={textFieldClass()} type="number" min="0.5" max="5" step="0.1" value={style.lineHeight} disabled={layer.locked} onChange={(event) => setStyle({ ...style, lineHeight: Number(event.target.value) })} onBlur={commit} />
|
||||
<InspectorSelect value={style.fontFamily} options={fontOptions} ariaLabel="Font family" onValueChange={(fontFamily) => updateStyle({ ...style, fontFamily })} />
|
||||
<input aria-label="Font size" className={textFieldClass()} type="text" inputMode="numeric" value={fontSizeDraft} disabled={layer.locked} onFocus={(event) => event.currentTarget.select()} onChange={(event) => { const draft = event.target.value; setFontSizeDraft(draft); const next = Number(draft); if (draft.trim() && Number.isFinite(next)) updateStyle({ ...style, fontSize: next }); }} onBlur={() => commitNumber("fontSize", fontSizeDraft, setFontSizeDraft)} onKeyDown={(event) => { event.stopPropagation(); if (event.key === "Enter") event.currentTarget.blur(); if (event.key === "Escape") { setFontSizeDraft(String(layer.style.fontSize)); event.currentTarget.blur(); } }} />
|
||||
<BottomControlColorPicker value={style.color} aria-label="Text color" onValueChange={(color) => updateStyle({ ...style, color })} />
|
||||
<InspectorSelect value={String(style.fontWeight)} options={weightOptions} ariaLabel="Font weight" onValueChange={(fontWeight) => updateStyle({ ...style, fontWeight: Number(fontWeight) as 400 | 700 })} />
|
||||
<InspectorSelect value={style.fontStyle} options={styleOptions} ariaLabel="Font style" onValueChange={(fontStyle) => updateStyle({ ...style, fontStyle })} />
|
||||
<InspectorSelect value={style.alignment} options={alignmentOptions} ariaLabel="Text alignment" onValueChange={(alignment) => updateStyle({ ...style, alignment })} />
|
||||
<input aria-label="Line height" className={textFieldClass()} type="text" inputMode="decimal" value={lineHeightDraft} disabled={layer.locked} onFocus={(event) => event.currentTarget.select()} onChange={(event) => { const draft = event.target.value; setLineHeightDraft(draft); const next = Number(draft); if (draft.trim() && Number.isFinite(next)) updateStyle({ ...style, lineHeight: next }); }} onBlur={() => commitNumber("lineHeight", lineHeightDraft, setLineHeightDraft)} onKeyDown={(event) => { event.stopPropagation(); if (event.key === "Enter") event.currentTarget.blur(); if (event.key === "Escape") { setLineHeightDraft(String(layer.style.lineHeight)); event.currentTarget.blur(); } }} />
|
||||
</div>
|
||||
<p className="text-[10px] text-white/40">Built-in system fonts only. Missing fonts fall back to Arial/sans-serif; exact glyph shapes can vary by platform.</p>
|
||||
</section>;
|
||||
}
|
||||
|
||||
function textFieldClass() { return "min-w-0 rounded-lg bg-black/25 px-2 py-1.5 text-xs text-white outline-none ring-1 ring-white/10"; }
|
||||
const fontOptions = builtInTextFonts.map((font) => ({ value: font, label: font }));
|
||||
const weightOptions = [{ value: "400", label: "Regular" }, { value: "700", label: "Bold" }] as const;
|
||||
const styleOptions = [{ value: "normal", label: "Normal" }, { value: "italic", label: "Italic" }] as const;
|
||||
const alignmentOptions = [{ value: "left", label: "Left" }, { value: "center", label: "Center" }, { value: "right", label: "Right" }] as const;
|
||||
|
||||
function InspectorSelect<T extends string>({ value, options, ariaLabel, onValueChange }: { value: T; options: readonly BottomControlSelectOption<T>[]; ariaLabel: string; onValueChange: (value: T) => void }) {
|
||||
return <BottomControlSelectMenu value={value} options={options} aria-label={ariaLabel} placement="inline" onValueChange={onValueChange} />;
|
||||
}
|
||||
|
||||
function textFieldClass() { return "h-7 min-w-0 rounded bg-black/20 px-1.5 font-mono text-xs text-white outline-none ring-1 ring-white/[0.07] focus:ring-sky-300/50"; }
|
||||
|
||||
function AdjustmentInspector({ layer, dispatch }: { layer: Extract<Layer, { type: "adjustment" }>; dispatch: AppStore["dispatch"] }) {
|
||||
const [draft, setDraft] = useState<ColorAdjustment>(() => cloneAdjustment(layer.adjustment));
|
||||
@@ -243,13 +271,8 @@ function AdjustmentInspector({ layer, dispatch }: { layer: Extract<Layer, { type
|
||||
["Blue", "blue", draft.colorBalance.blue],
|
||||
] as const;
|
||||
|
||||
const commit = () => {
|
||||
if (JSON.stringify(draft) === JSON.stringify(layer.adjustment)) return;
|
||||
dispatch(commandIds.documentSetAdjustment, { layerId: layer.id, adjustment: draft });
|
||||
};
|
||||
|
||||
return (
|
||||
<section aria-label="Adjustment settings" className="mb-3 rounded-[1.5rem] bg-white/[0.05] p-3">
|
||||
<section aria-label="Adjustment settings" className="mb-2 border-b border-white/[0.07] px-0.5 pb-3">
|
||||
<p className="mb-2 text-xs text-white/45">
|
||||
Affects visible artboard layers beneath it. Adjustment layers stay at artboard level.
|
||||
</p>
|
||||
@@ -257,17 +280,19 @@ function AdjustmentInspector({ layer, dispatch }: { layer: Extract<Layer, { type
|
||||
{fields.map(([label, key, value]) => (
|
||||
<label key={key} className="text-[0.7rem] text-white/55">
|
||||
<span>{label}</span>
|
||||
<input
|
||||
className="mt-1 w-full accent-violet-300"
|
||||
type="range"
|
||||
min="-1"
|
||||
max="1"
|
||||
step="0.01"
|
||||
<BottomControlSlider
|
||||
className="mt-1 w-full"
|
||||
min={-1}
|
||||
max={1}
|
||||
step={0.01}
|
||||
value={value}
|
||||
disabled={layer.locked}
|
||||
onChange={(event) => setDraft(withAdjustmentValue(draft, key, Number(event.currentTarget.value)))}
|
||||
onPointerUp={commit}
|
||||
onBlur={commit}
|
||||
aria-label={`${label} adjustment`}
|
||||
onValueChange={(value) => {
|
||||
const next = withAdjustmentValue(draft, key, value);
|
||||
setDraft(next);
|
||||
dispatch(commandIds.documentSetAdjustment, { layerId: layer.id, adjustment: next });
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
))}
|
||||
@@ -331,7 +356,7 @@ function LayerRow({
|
||||
<div>
|
||||
<div
|
||||
draggable
|
||||
className={`group flex h-12 w-full items-center gap-3 rounded-full px-4 text-left transition ${editingMask ? "bg-sky-300 text-black" : selected ? "bg-white text-black" : "text-white/75 hover:bg-white/[0.07] hover:text-white"}`}
|
||||
className={`group flex h-10 w-full items-center gap-2 rounded-lg px-2 text-left transition ${editingMask ? "bg-sky-300 text-slate-950" : selected ? "bg-white text-slate-950" : "text-white/70 hover:bg-white/[0.06] hover:text-white"}`}
|
||||
style={{ paddingLeft: rowPadding }}
|
||||
onDragStart={(event) => {
|
||||
event.dataTransfer.effectAllowed = "move";
|
||||
@@ -383,13 +408,13 @@ function LayerRow({
|
||||
</button>
|
||||
)}
|
||||
{layerMask ? (
|
||||
<span className={`inline-flex items-center gap-1 rounded-full px-3 py-1 text-xs ${editingMask || selected ? "bg-black/10 text-black/65" : "bg-sky-400/10 text-sky-100/70"}`}>
|
||||
<span className={`inline-flex items-center gap-1 rounded px-2 py-0.5 text-[0.68rem] ${editingMask || selected ? "bg-black/10 text-black/65" : "bg-sky-400/10 text-sky-100/70"}`}>
|
||||
<Stack size={13} weight="fill" /> Mask
|
||||
</span>
|
||||
) : canAddMask && layerInfo ? (
|
||||
<button
|
||||
type="button"
|
||||
className={editingMask || selected ? "rounded-full bg-black/10 px-3 py-1 text-xs text-black/65 transition hover:bg-black/15" : "rounded-full bg-white/5 px-3 py-1 text-xs text-white/45 transition hover:bg-sky-400/15 hover:text-sky-100"}
|
||||
className={editingMask || selected ? "rounded bg-black/10 px-2 py-0.5 text-[0.68rem] text-black/65 transition hover:bg-black/15" : "rounded bg-white/[0.04] px-2 py-0.5 text-[0.68rem] text-white/45 transition hover:bg-sky-400/15 hover:text-sky-100"}
|
||||
onClick={() => addLayerMask(documentIndex, layerInfo, dispatch)}
|
||||
>
|
||||
Add mask
|
||||
@@ -400,7 +425,7 @@ function LayerRow({
|
||||
</button>
|
||||
</div>
|
||||
{layerMask ? (
|
||||
<div role="group" aria-label={`Mask attached to ${layer.name}${editingMask ? ", currently editing" : ""}`} className={`relative mt-2 ml-10 flex min-h-14 flex-wrap items-center gap-2 rounded-[1.5rem] border-l-2 py-2 pl-4 pr-2 text-sm transition ${editingMask ? "border-sky-300 bg-sky-300/15 text-sky-50 ring-1 ring-inset ring-sky-300/30" : "border-sky-300/30 text-sky-100/70 hover:bg-white/[0.04]"}`}>
|
||||
<div role="group" aria-label={`Mask attached to ${layer.name}${editingMask ? ", currently editing" : ""}`} className={`relative mt-1 ml-8 flex min-h-11 flex-wrap items-center gap-1.5 border-l-2 py-1.5 pl-3 pr-1.5 text-xs transition ${editingMask ? "border-sky-300 text-sky-50" : "border-sky-300/25 text-sky-100/65"}`}>
|
||||
<span aria-hidden="true" className="absolute -left-2 top-1/2 h-px w-2 bg-sky-300/40" />
|
||||
{maskThumbnail ? <LayerThumbnail model={maskThumbnail} label={`${layer.name} mask preview`} /> : <Stack size={24} weight="fill" className="shrink-0" />}
|
||||
<span className="min-w-28 flex-1 truncate font-medium">{maskLayer ? editingMask ? "Editing layer mask" : "Layer mask" : "Layer mask missing"}</span>
|
||||
@@ -409,7 +434,7 @@ function LayerRow({
|
||||
<div className="flex flex-wrap items-center gap-1">
|
||||
<button
|
||||
type="button"
|
||||
className={`h-9 rounded-full px-4 text-sm font-medium transition ${editingMask ? "bg-sky-300 text-black" : "text-sky-100/75 hover:bg-white/10 hover:text-sky-50"}`}
|
||||
className={`h-8 rounded-md px-3 text-xs font-medium transition ${editingMask ? "bg-sky-300 text-black" : "text-sky-100/70 hover:bg-white/[0.07] hover:text-sky-50"}`}
|
||||
onClick={() =>
|
||||
editingMask
|
||||
? dispatch(commandIds.toolExitMaskEdit, undefined)
|
||||
@@ -447,7 +472,7 @@ function LayerRow({
|
||||
) : null}
|
||||
<button
|
||||
type="button"
|
||||
className="h-9 rounded-full px-4 text-sm font-medium text-sky-100/55 transition hover:bg-red-400/15 hover:text-red-100"
|
||||
className="h-8 rounded-md px-3 text-xs font-medium text-sky-100/50 transition hover:bg-red-400/15 hover:text-red-100"
|
||||
onClick={() => dispatch(commandIds.documentRemoveLayerMask, { layerId: layer.id })}
|
||||
>
|
||||
Remove
|
||||
@@ -487,7 +512,7 @@ function RenameInput({ value, onChange, onCommit, onCancel }: { value: string; o
|
||||
<input
|
||||
autoFocus
|
||||
aria-label="Rename item"
|
||||
className="min-w-0 flex-1 rounded-full bg-white px-3 py-1.5 font-medium text-black outline-none ring-1 ring-black/10 focus:ring-sky-300/50"
|
||||
className="min-w-0 flex-1 rounded-md bg-white px-2.5 py-1 font-medium text-black outline-none ring-1 ring-black/10 focus:ring-sky-300/50"
|
||||
value={value}
|
||||
onChange={(event) => onChange(event.target.value)}
|
||||
onBlur={onCommit}
|
||||
@@ -521,13 +546,13 @@ function dropLayer(
|
||||
}
|
||||
|
||||
function toolbarButtonClass() {
|
||||
return "inline-flex size-12 items-center justify-center rounded-full text-white/75 transition hover:bg-white/10 hover:text-white disabled:pointer-events-none disabled:opacity-35 focus:outline-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/30";
|
||||
return "inline-flex size-8 items-center justify-center rounded-lg text-white/60 transition hover:bg-white/[0.07] hover:text-white disabled:pointer-events-none disabled:opacity-30 focus:outline-none focus-visible:ring-2 focus-visible:ring-sky-300/50 [&>svg]:size-[17px]";
|
||||
}
|
||||
|
||||
function labeledToolbarButtonClass() {
|
||||
return "inline-flex h-12 flex-1 items-center rounded-full pr-4 text-sm font-medium text-white/75 transition hover:bg-white/10 hover:text-white disabled:pointer-events-none disabled:opacity-35 focus:outline-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/30";
|
||||
return "inline-flex h-8 flex-1 items-center rounded-lg pr-2 text-xs font-medium text-white/65 transition hover:bg-white/[0.07] hover:text-white disabled:pointer-events-none disabled:opacity-30 focus:outline-none focus-visible:ring-2 focus-visible:ring-sky-300/50";
|
||||
}
|
||||
|
||||
function maskActionButtonClass() {
|
||||
return "rounded-full bg-white/5 px-2.5 py-1 text-[0.7rem] font-semibold text-white/60 transition hover:bg-white/10 hover:text-white disabled:pointer-events-none disabled:opacity-35";
|
||||
return "rounded-md bg-white/[0.05] px-2 py-1 text-[0.68rem] font-semibold text-white/55 transition hover:bg-white/[0.09] hover:text-white disabled:pointer-events-none disabled:opacity-35";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user