پروایدر کلیدمیانبر (HotkeyProvider)
registry سراسری برای ترکیبهای keyboard — یک listener سراسری، cheatsheet readback، disable سراسری
معرفی
HotkeyProvider یک registry سراسری برای ترکیبهای keyboard است. یکبار wrap کنید، descendantها با useHotkey() ترکیبها را register میکنند، و هر UI (CommandPalette، "?" cheatsheet، صفحهی help) لیست فعال را با useHotkeyRegistry() میخواند.
چه زمانی استفاده کنیم:
- اپ شما بیش از ۲-۳ ترکیب keyboard دارد (Cmd+K برای palette، Esc برای modal، /? برای cheatsheet…)
- میخواهید یک صفحه help که همهی شورتکاتهای فعال را لیست میکند داشته باشید
- میخواهید disable سراسری هنگام modal باز ی full-screen tutorial داشته باشید
- میخواهید جلوی همپوشانی شورتکات را با dedupe-by-id بگیرید
چه زمانی استفاده نکنیم:
- اگر اپ شما فقط
mod+kبرای CommandPalette دارد → ازuseHotkeysمعمولی استفاده کنید (registry overhead لازم نیست) - اگر فقط شورتکات scoped درون یک panel/modal دارید →
useHotkeysباtargetref scoped میکند
اختیاری است — useHotkey() خارج از provider silently no-op میکند، پس میتوانید provider را بهصورت تدریجی اضافه کنید بدون refactor sweep.
استفاده پایه
import { HotkeyProvider, useHotkey, useHotkeyRegistry } from '@parto-system-design/ui'
function App() {
return (
<HotkeyProvider>
<Layout />
</HotkeyProvider>
)
}
function GlobalShortcuts({ onOpenPalette, onSearchFocus }) {
useHotkey('palette', 'mod+k', onOpenPalette, {
description: 'باز کردن پالت دستور',
group: 'سراسری',
})
useHotkey('search', '/', onSearchFocus, {
description: 'فوکوس جستجو',
group: 'سراسری',
})
return null
}نمایش cheatsheet
import { useHotkeyRegistry } from '@parto-system-design/ui'
function ShortcutsCheatsheet() {
const { hotkeys } = useHotkeyRegistry()
// Group by `group` field for nicer rendering
const groups = hotkeys.reduce<Record<string, typeof hotkeys>>((acc, h) => {
const key = h.group ?? 'سایر'
;(acc[key] ??= []).push(h)
return acc
}, {})
return Object.entries(groups).map(([group, list]) => (
<section key={group}>
<h3>{group}</h3>
<dl>
{list.map((h) => (
<React.Fragment key={h.id}>
<dt>{Array.isArray(h.combo) ? h.combo[0] : h.combo}</dt>
<dd>{h.description}</dd>
</React.Fragment>
))}
</dl>
</section>
))
}useHotkeyRegistry() خارج از provider لیست خالی میدهد — پس Cheatsheet هرکجا باشد crash نمیکند.
Disable سراسری
const [tutorialOpen, setTutorialOpen] = React.useState(true)
// In tutorial mode، همهی hotkeys را disable کن تا با کلاس آموزش تداخل نداشته باشند
<HotkeyProvider enabled={!tutorialOpen}>
<App />
</HotkeyProvider>enabled={false} listenerها را متوقف میکند ولی registry list دستنخورده میماند (cheatsheet کار میکند).
ویژگیهای کلیدی پیادهسازی
- Two-context split داخلی:
registerfn برای مادامالعمر provider stable است، entries list reactive. این از infinite-loop trap که consumer effects را در یک Context واحد میکشد جلوگیری میکند (test-discovered). - Latest-handler ref: میتوانید inline arrow هر render پاس بدهید — با handlerRef بدون re-register هر تغییر متوجه میشود.
- Dedupe by id:
useHotkey('palette', ...)در یک کامپوننت دیگر با همانid، entry قبلی را replace میکند نه duplicate. - یک
useHotkeysper entry: parsing key، ignore-when-typing، و OR-array logic در hook موجود میماند — این provider صرفاً orchestrate میکند.
جدول ویژگیها
HotkeyProvider
useHotkey(id, combo, handler, options?)
useHotkeyRegistry() → { hotkeys: RegisteredHotkey[] }
برمیگرداند آرایهی فعال entries در ترتیب register-time. خارج از provider لیست خالی برمیگرداند.
دسترسیپذیری
- ترکیبهای ثبتشده باید همگی discoverable باشند — یک cheatsheet (
?یا منوی Help) که ازuseHotkeyRegistry()میخواند، شرط استفاده صحیح است؛ keyboard-only userها بدون کشف، شورتکاتها را پیدا نمیکنند. - در نمایش shortcut از
<Kbd>+formatHotkey('mod+k')استفاده کنید تا روی macOS «⌘K» و روی Windows/Linux «Ctrl+K» مناسب رندر شود. - قاعدهی ignore-when-typing در
useHotkeysپیادهسازی شده — ترکیبها وقتی فوکوس روی input/textarea/contentEditable است fire نمیشوند تا تایپ کاربر را قطع نکنند. - هنگام باز شدن modal یا full-screen tutorial از
enabled={false}استفاده کنید تا shortcutهای سراسری با focus-trap modal تداخل نکنند. - handler هیچ ARIA live region یا announcement خودکار ارائه نمیکند؛ اگر یک shortcut state قابلتوجهی را تغییر میدهد (مثل toggling sidebar)، خود consumer باید
aria-liveمناسب اضافه کند.
راهنمای استفاده
بکنید
- برای هر app یک HotkeyProvider در ریشهی Layout قرار دهید — provider light است (هیچ DOM رندر نمیکند) - id ها را
بهصورت
feature-actionنامگذاری کنید (palette،search،nav-back) — در replace + dedupe کمک میکند - برای modal-scoped shortcuts ازuseHotkeys(نهuseHotkey) استفاده کنید باtarget={modalRef}— registry سراسری برای global shortcuts است نه local
نکنید
- id ها را dynamic نسازید (مثل
palette-${counter}) — هر بار register میکند نه replace - hook را در render شرطی نگذارید — قاعدهی hooks - dispatcher را override نکنید مگر دلیل خاص — useHotkeys logic در یک جا تستشده است
کامپوننتهای مرتبط
- اگر فقط یک hotkey global →
useHotkeys(بدون registry overhead) - برای palette command →
CommandPaletteمیتواندuseHotkeyRegistry()را برای item list استفاده کند - توالی keyboard interaction در یک scope →
useHotkeys({ target: ref }) - نمایش shortcut در button →
Kbd+formatHotkey('mod+k')