'use client' import { useEffect } from 'react' import { cn } from '../../lib/cn' import { Kbd } from '../Kbd' import { Text } from '../Text' import { PressScale } from '../PressScale' import { Ripple } from '../Ripple' import { Tooltip } from '../Tooltip' import type { IconComponent } from '../../lib/types' export interface QuickAction { id: string label: string icon: IconComponent /** Marks the primary action. Exactly one, or none. */ primary?: boolean /** Single character. Pressing it with the modifier runs the action. */ shortcut?: string /** Longer description, shown as a tooltip. */ hint?: string disabled?: boolean onSelect: () => void } export interface QuickActionsProps { actions: QuickAction[] /** Accessible name for the group. */ label?: string /** Register the shortcuts on the document. */ shortcuts?: boolean /** Modifier the shortcuts require. */ modifier?: 'meta' | 'alt' | 'none' /** Merged last, so it wins. */ className?: string } /** * The icon-over-label action row from the balance card, generalised. * * The dashboard repeats this shape wherever a surface has three or four primary * verbs, and it is the one row a user hits most often — so it gets the * accelerators the rest of the library does not: optional keyboard shortcuts * bound while it is mounted, with the key shown on the control rather than * hidden in a help page. * * Ripple and PressScale give it touch feedback without touching the button * underneath, which keeps its focus ring and semantics intact. */ export function QuickActions({ actions, label = 'Quick actions', shortcuts = false, modifier = 'meta', className, }: QuickActionsProps) { useEffect(() => { if (!shortcuts) return const onKeyDown = (event: KeyboardEvent) => { const target = event.target as HTMLElement | null if (target && /^(INPUT|TEXTAREA|SELECT)$/.test(target.tagName)) return const held = modifier === 'none' || (modifier === 'meta' && (event.metaKey || event.ctrlKey)) || (modifier === 'alt' && event.altKey) if (!held) return const match = actions.find( (action) => !action.disabled && action.shortcut?.toLowerCase() === event.key.toLowerCase(), ) if (!match) return event.preventDefault() match.onSelect() } document.addEventListener('keydown', onKeyDown) return () => document.removeEventListener('keydown', onKeyDown) }, [actions, shortcuts, modifier]) const modifierGlyph = modifier === 'meta' ? '⌘' : modifier === 'alt' ? '⌥' : '' return ( ) }