mirror of
https://github.com/supabase/supabase.git
synced 2026-06-17 21:23:59 +08:00
## Summary Wires Linear-style keyboard shortcuts across all observability pages — refresh, time picker, filters, and sub-page navigation — with hover tooltips surfacing each binding. | Page | Shortcut | Action | | --- | --- | --- | | Overview | `Shift+R` | Refresh report | | Overview | `Shift+P` | Open time picker | | Query Performance | `Shift+R` | Refresh report | | Query Performance | `R` then `C` | Reset report (`pg_stat_statements_reset`) | | Query Performance | `Shift+F` | Search queries | | Query Performance | `F` then `C` | Reset filters | | API Gateway | `Shift+R` | Refresh report | | API Gateway | `Shift+P` | Open time picker | | API Gateway | `Shift+F` | Add filter | | API Gateway | `F` then `C` | Reset filters | | API Gateway | `Shift+S` | Filter requests by service | | Database | `Shift+R` | Refresh report | | Database | `Shift+P` | Open time picker | | Auth | `Shift+R` | Refresh report | | Auth | `Shift+P` | Open time picker | | Data API | `Shift+R` | Refresh report | | Data API | `Shift+P` | Open time picker | | Storage | `Shift+R` | Refresh report | | Storage | `Shift+P` | Open time picker | | Realtime | `Shift+R` | Refresh report | | Realtime | `Shift+P` | Open time picker | | Edge Functions | `Shift+R` | Refresh report | | Edge Functions | `Shift+P` | Open time picker | | All observability pages | `U` then `O/Q/G/D/P/A/F/S/L` | Jump to sub-page | ## Test plan - [ ] Each shortcut fires on its page; tooltip on hover shows the binding - [ ] Picker shortcut toggles the popover open/closed without leaving the tooltip visible - [ ] Reset-report on Query Performance opens the confirm modal - [ ] `Escape` on the query search clears the value, then blurs - [ ] No "Shift+R already registered" / Tooltip controlled-uncontrolled warnings in the console <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Keyboard shortcuts to navigate Observability pages and perform common actions (refresh, toggle date picker/interval, focus search, reset filters, create reports). * Shortcut hints shown on relevant buttons and controls; date pickers and interval dropdowns can be controlled via shortcuts. * Global shortcut groups/registries added for Observability navigation and page actions. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/46277?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
80 lines
2.6 KiB
TypeScript
80 lines
2.6 KiB
TypeScript
import { TooltipContentProps } from '@ui/components/shadcn/ui/tooltip'
|
|
import { Fragment, useState, type ReactNode } from 'react'
|
|
import { KeyboardShortcut, Tooltip, TooltipContent, TooltipTrigger } from 'ui'
|
|
|
|
import { hotkeyToKeys } from '@/state/shortcuts/formatShortcut'
|
|
import { SHORTCUT_DEFINITIONS, type ShortcutId } from '@/state/shortcuts/registry'
|
|
|
|
interface ShortcutTooltipProps {
|
|
shortcutId: ShortcutId
|
|
children: ReactNode
|
|
side?: TooltipContentProps['side']
|
|
align?: TooltipContentProps['align']
|
|
sideOffset?: number
|
|
delayDuration?: number
|
|
/**
|
|
* Override the label from the registry. Use when the wrapped element's
|
|
* action is a narrower/contextual variant of the registered shortcut.
|
|
*/
|
|
label?: string
|
|
/**
|
|
* Override the tooltip's open state. Pass `false` to force the tooltip closed
|
|
* (e.g. while a popover or dialog opened by the wrapped element is visible).
|
|
* Leave `undefined` to let hover/focus drive it. The tooltip is always
|
|
* controlled internally so toggling this prop won't trigger Radix's
|
|
* controlled/uncontrolled warning.
|
|
*/
|
|
open?: boolean
|
|
}
|
|
|
|
/**
|
|
* Wraps any element to show its bound keyboard shortcut on hover/focus, in the
|
|
* style of Linear's shortcut tooltips: `"<label> <key> [then <key>]"`.
|
|
*
|
|
* Uses Radix's `asChild` trigger, so the wrapped element remains fully
|
|
* interactive — clicks, focus, and event handlers pass through untouched.
|
|
*
|
|
* @example
|
|
* <ShortcutTooltip shortcutId={SHORTCUT_IDS.RESULTS_COPY_MARKDOWN}>
|
|
* <Button onClick={handleCopy}>Copy</Button>
|
|
* </ShortcutTooltip>
|
|
*/
|
|
export const ShortcutTooltip = ({
|
|
shortcutId,
|
|
children,
|
|
side,
|
|
align,
|
|
sideOffset,
|
|
delayDuration,
|
|
label: labelOverride,
|
|
open,
|
|
}: ShortcutTooltipProps) => {
|
|
const def = SHORTCUT_DEFINITIONS[shortcutId]
|
|
const label = labelOverride ?? def.label
|
|
|
|
const [hoverOpen, setHoverOpen] = useState(false)
|
|
const resolvedOpen = open ?? hoverOpen
|
|
|
|
return (
|
|
<Tooltip delayDuration={delayDuration} open={resolvedOpen} onOpenChange={setHoverOpen}>
|
|
<TooltipTrigger asChild>{children}</TooltipTrigger>
|
|
<TooltipContent
|
|
side={side}
|
|
align={align}
|
|
sideOffset={sideOffset}
|
|
className="flex items-center gap-2"
|
|
>
|
|
<span>{label}</span>
|
|
<span className="flex items-center gap-1">
|
|
{def.sequence.map((step, i) => (
|
|
<Fragment key={i}>
|
|
{i > 0 && <span className="text-foreground-lighter text-[11px]">then</span>}
|
|
<KeyboardShortcut keys={hotkeyToKeys(step)} />
|
|
</Fragment>
|
|
))}
|
|
</span>
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
)
|
|
}
|