// Copyright (c) Meta Platforms, Inc. and affiliates. 'use client'; /** * @file Kbd.tsx * @input Uses React, StyleX, theme tokens, shortcut key names and aliases * @output Exports Kbd component and KbdProps with canonical shortcut labels * @position Core implementation; renders styled keyboard shortcut indicators * * SYNC: When modified, update: * - /packages/core/src/Kbd/index.ts * - /packages/cli/assets/templates/blocks/components/Kbd/ (showcase blocks) */ import React, { useSyncExternalStore } from 'react'; import * as stylex from "../style-runtime.js"; import { mergeProps } from "../utils/index.js"; import { isApplePlatform } from "../utils/isApplePlatform.js"; import type { BaseProps } from "../BaseProps.js"; import { themeProps } from "../utils/themeProps.js"; import "../theme/tokens.stylex.js"; import { colorVars, spacingVars, radiusVars, typographyVars, fontWeightVars, typeScaleVars } from "../theme/tokens.stylex.js"; const styles = { wrapper: { k1xSpc: "x3nfvp2", kGNEyG: "x6s0dn4", kOIVth: "xzye2dw", kmuXW: "x2lah0s", $$css: true as const } }; /** * Map of modifier key names to display symbols. * Note: `mod` is not in this map — it resolves dynamically via platform * detection inside the component. */ const KEY_DISPLAY = new Map([['ctrl', '\u2303'], // ⌃ ['alt', '\u2325'], // ⌥ ['shift', '\u21E7'], // ⇧ ['enter', '\u21B5'], // ↵ ['backspace', '\u232B'], // ⌫ ['escape', 'Esc'], ['tab', '\u21E5'], // ⇥ ['up', '\u2191'], ['down', '\u2193'], ['left', '\u2190'], ['right', '\u2192'], ['plus', '+']]); const KBD_KEY_ALIASES = new Map([['esc', 'escape'], ['return', 'enter']]); /** * Resolves a key name to its display string. Handles the platform-aware * `mod` key (⌘ on macOS, Ctrl on other platforms) and falls back to * KEY_DISPLAY or uppercased key name. */ function getKeyDisplay(key: string, isMac: boolean): string { if (key === 'mod') { return isMac ? '\u2318' : 'Ctrl'; } return KEY_DISPLAY.get(key) ?? key.toUpperCase(); } /** * Spoken-word labels for screen readers. The visual `KEY_DISPLAY` uses glyphs * (⌘, ⇧, ↵, …) that assistive tech cannot announce meaningfully, so the * accessible name for the shortcut is built from these words instead. */ const KEY_LABEL = new Map([['ctrl', 'Control'], ['alt', 'Alt'], ['shift', 'Shift'], ['enter', 'Enter'], ['backspace', 'Backspace'], ['escape', 'Escape'], ['tab', 'Tab'], ['up', 'Up arrow'], ['down', 'Down arrow'], ['left', 'Left arrow'], ['right', 'Right arrow'], ['plus', 'Plus']]); function getKeyLabel(key: string, isMac: boolean): string { if (key === 'mod') { return isMac ? 'Command' : 'Control'; } return KEY_LABEL.get(key) ?? key.toUpperCase(); } function subscribeToPlatformChanges(): () => void { return () => {}; } function getServerPlatformSnapshot(): boolean { return false; } export interface KbdProps extends BaseProps { ref?: React.Ref; /** * Keyboard shortcut string. Use "+" to separate keys. * Special keys: mod (Cmd on Mac), ctrl, alt, shift, enter, backspace, escape, * tab, up, down, left, and right. * Aliases: "esc" for "escape" and "return" for "enter". * Use "plus" to render a literal "+" key (e.g. "shift+plus"). * * @example * ``` * "mod+k" * "mod+shift+p" * "shift+plus" * "enter" * ``` */ keys: string; } /** * Displays a keyboard shortcut as styled elements. * * A general-purpose component for rendering keyboard shortcuts * anywhere in the system — tooltips, menus, documentation, etc. * * Platform-aware: `mod` renders as ⌘ on macOS and Ctrl elsewhere. * SSR-safe — defers platform detection through useSyncExternalStore to avoid * hydration mismatches. * * @example * ``` * * ``` */ export function Kbd({ keys, ref, xstyle, className, style, ...rest }: KbdProps) { const isMac = useSyncExternalStore(subscribeToPlatformChanges, isApplePlatform, getServerPlatformSnapshot); const keyOccurrences = new Map(); const parts = keys.split('+').map(key => key.trim().toLowerCase()).map(sourceKey => { const occurrence = keyOccurrences.get(sourceKey) ?? 0; keyOccurrences.set(sourceKey, occurrence + 1); return { key: KBD_KEY_ALIASES.get(sourceKey) ?? sourceKey, reactKey: `${sourceKey}:${occurrence}` }; }); // Screen-reader name: the joined spoken labels (e.g. "Command + K"), since // the visual glyphs below are announced meaninglessly by assistive tech. const accessibleName = parts.map(({ key }) => getKeyLabel(key, isMac)).join(' + '); return {parts.map(({ key, reactKey }) => )} ; } Kbd.displayName = 'Kbd';