@smwb/ui-react 2.8.0 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. package/AI_LESS_OVERRIDES.md +84 -21
  2. package/AI_THEMING.md +72 -28
  3. package/README.md +654 -0
  4. package/dist/assets/styles/less/components/appBar/appBar.entry.css +1 -1
  5. package/dist/assets/styles/less/components/button/button.entry.css +1 -1
  6. package/dist/assets/styles/less/components/checkbox/checkbox.entry.css +1 -1
  7. package/dist/assets/styles/less/components/chip/chip.entry.css +1 -1
  8. package/dist/assets/styles/less/components/commandPalette/commandPalette.entry.css +1 -0
  9. package/dist/assets/styles/less/components/composer/composer.entry.css +1 -0
  10. package/dist/assets/styles/less/components/dagView/dagView.entry.css +1 -0
  11. package/dist/assets/styles/less/components/diffStat/diffStat.entry.css +1 -0
  12. package/dist/assets/styles/less/components/kbd/kbd.entry.css +1 -0
  13. package/dist/assets/styles/less/components/list/list.entry.css +1 -1
  14. package/dist/assets/styles/less/components/qrCode/qrCode.entry.css +1 -0
  15. package/dist/assets/styles/less/components/radioButton/radioButton.entry.css +1 -1
  16. package/dist/assets/styles/less/components/selectField/selectField.entry.css +1 -1
  17. package/dist/assets/styles/less/components/sidebar/sidebar.entry.css +1 -1
  18. package/dist/assets/styles/less/components/statusDot/statusDot.entry.css +1 -0
  19. package/dist/assets/styles/less/components/swimlanes/swimlanes.entry.css +1 -0
  20. package/dist/assets/styles/less/components/tabGroups/tabGroups.entry.css +1 -0
  21. package/dist/assets/styles/less/components/tabs/tab/tab.entry.css +1 -1
  22. package/dist/assets/styles/less/components/tabs/tabPanel/tabPanel.entry.css +1 -0
  23. package/dist/assets/styles/less/components/tabs/tabs.entry.css +1 -1
  24. package/dist/assets/styles/less/components/textField/textField.entry.css +1 -1
  25. package/dist/assets/styles/less/components/toggle/toggle.entry.css +1 -1
  26. package/dist/assets/styles/less/components/treeView/treeView.entry.css +1 -1
  27. package/dist/assets/styles/less/tokens.entry.css +1 -1
  28. package/dist/components/dataDisplay/chat/chat.d.ts +43 -1
  29. package/dist/components/dataDisplay/chat/chat.js +179 -150
  30. package/dist/components/dataDisplay/chat/chat.js.map +1 -1
  31. package/dist/components/dataDisplay/chat/chatComposer.d.ts +17 -0
  32. package/dist/components/dataDisplay/chat/chatComposer.js +31 -29
  33. package/dist/components/dataDisplay/chat/chatComposer.js.map +1 -1
  34. package/dist/components/dataDisplay/chat/index.d.ts +1 -1
  35. package/dist/components/dataDisplay/chat/utils.d.ts +14 -0
  36. package/dist/components/dataDisplay/chat/utils.js.map +1 -1
  37. package/dist/components/dataDisplay/dagView/dagLayout.d.ts +74 -0
  38. package/dist/components/dataDisplay/dagView/dagLayout.js +289 -0
  39. package/dist/components/dataDisplay/dagView/dagLayout.js.map +1 -0
  40. package/dist/components/dataDisplay/dagView/dagView.d.ts +157 -0
  41. package/dist/components/dataDisplay/dagView/dagView.js +543 -0
  42. package/dist/components/dataDisplay/dagView/dagView.js.map +1 -0
  43. package/dist/components/dataDisplay/dagView/index.d.ts +4 -0
  44. package/dist/components/dataDisplay/dagView/panZoom.d.ts +75 -0
  45. package/dist/components/dataDisplay/dagView/panZoom.js +171 -0
  46. package/dist/components/dataDisplay/dagView/panZoom.js.map +1 -0
  47. package/dist/components/dataDisplay/diffStat/diffStat.d.ts +54 -0
  48. package/dist/components/dataDisplay/diffStat/diffStat.js +63 -0
  49. package/dist/components/dataDisplay/diffStat/diffStat.js.map +1 -0
  50. package/dist/components/dataDisplay/diffStat/index.d.ts +2 -0
  51. package/dist/components/dataDisplay/kbd/index.d.ts +4 -0
  52. package/dist/components/dataDisplay/kbd/kbd.d.ts +40 -0
  53. package/dist/components/dataDisplay/kbd/kbd.js +27 -0
  54. package/dist/components/dataDisplay/kbd/kbd.js.map +1 -0
  55. package/dist/components/dataDisplay/kbd/kbdKeys.d.ts +18 -0
  56. package/dist/components/dataDisplay/kbd/kbdKeys.js +72 -0
  57. package/dist/components/dataDisplay/kbd/kbdKeys.js.map +1 -0
  58. package/dist/components/dataDisplay/list/listItem.d.ts +34 -0
  59. package/dist/components/dataDisplay/list/listItem.js +47 -24
  60. package/dist/components/dataDisplay/list/listItem.js.map +1 -1
  61. package/dist/components/dataDisplay/qrCode/index.d.ts +2 -0
  62. package/dist/components/dataDisplay/qrCode/qrCode.d.ts +28 -0
  63. package/dist/components/dataDisplay/qrCode/qrCode.js +78 -0
  64. package/dist/components/dataDisplay/qrCode/qrCode.js.map +1 -0
  65. package/dist/components/dataDisplay/qrCode/qrCodePath.d.ts +7 -0
  66. package/dist/components/dataDisplay/qrCode/qrCodePath.js +20 -0
  67. package/dist/components/dataDisplay/qrCode/qrCodePath.js.map +1 -0
  68. package/dist/components/dataDisplay/statusDot/index.d.ts +2 -0
  69. package/dist/components/dataDisplay/statusDot/statusDot.d.ts +35 -0
  70. package/dist/components/dataDisplay/statusDot/statusDot.js +30 -0
  71. package/dist/components/dataDisplay/statusDot/statusDot.js.map +1 -0
  72. package/dist/components/dataDisplay/swimlanes/index.d.ts +2 -0
  73. package/dist/components/dataDisplay/swimlanes/swimlanes.d.ts +53 -0
  74. package/dist/components/dataDisplay/swimlanes/swimlanes.js +357 -0
  75. package/dist/components/dataDisplay/swimlanes/swimlanes.js.map +1 -0
  76. package/dist/components/dataDisplay/treeView/treeView.d.ts +17 -1
  77. package/dist/components/dataDisplay/treeView/treeView.js +113 -92
  78. package/dist/components/dataDisplay/treeView/treeView.js.map +1 -1
  79. package/dist/components/feedBack/modal/modal.js +84 -63
  80. package/dist/components/feedBack/modal/modal.js.map +1 -1
  81. package/dist/components/inputs/button/button.d.ts +13 -1
  82. package/dist/components/inputs/button/button.js +57 -51
  83. package/dist/components/inputs/button/button.js.map +1 -1
  84. package/dist/components/inputs/composer/composer.d.ts +149 -0
  85. package/dist/components/inputs/composer/composer.js +219 -0
  86. package/dist/components/inputs/composer/composer.js.map +1 -0
  87. package/dist/components/inputs/composer/composerFiles.d.ts +26 -0
  88. package/dist/components/inputs/composer/composerFiles.js +19 -0
  89. package/dist/components/inputs/composer/composerFiles.js.map +1 -0
  90. package/dist/components/inputs/composer/composerKeys.d.ts +29 -0
  91. package/dist/components/inputs/composer/composerKeys.js +7 -0
  92. package/dist/components/inputs/composer/composerKeys.js.map +1 -0
  93. package/dist/components/inputs/composer/index.d.ts +3 -0
  94. package/dist/components/layout/sheet/sheet.d.ts +12 -0
  95. package/dist/components/layout/sheet/sheet.js +152 -137
  96. package/dist/components/layout/sheet/sheet.js.map +1 -1
  97. package/dist/components/layout/tabGroups/index.d.ts +4 -0
  98. package/dist/components/layout/tabGroups/tabGroups.d.ts +94 -0
  99. package/dist/components/layout/tabGroups/tabGroups.js +783 -0
  100. package/dist/components/layout/tabGroups/tabGroups.js.map +1 -0
  101. package/dist/components/layout/tabGroups/tabGroupsFocus.d.ts +55 -0
  102. package/dist/components/layout/tabGroups/tabGroupsFocus.js +199 -0
  103. package/dist/components/layout/tabGroups/tabGroupsFocus.js.map +1 -0
  104. package/dist/components/layout/tabGroups/tabGroupsLayout.d.ts +250 -0
  105. package/dist/components/layout/tabGroups/tabGroupsLayout.js +497 -0
  106. package/dist/components/layout/tabGroups/tabGroupsLayout.js.map +1 -0
  107. package/dist/components/navigation/appBar/appBar.d.ts +22 -0
  108. package/dist/components/navigation/appBar/appBar.js +31 -26
  109. package/dist/components/navigation/appBar/appBar.js.map +1 -1
  110. package/dist/components/navigation/commandPalette/commandPalette.d.ts +111 -0
  111. package/dist/components/navigation/commandPalette/commandPalette.js +337 -0
  112. package/dist/components/navigation/commandPalette/commandPalette.js.map +1 -0
  113. package/dist/components/navigation/commandPalette/commandPaletteFilter.d.ts +11 -0
  114. package/dist/components/navigation/commandPalette/commandPaletteFilter.js +16 -0
  115. package/dist/components/navigation/commandPalette/commandPaletteFilter.js.map +1 -0
  116. package/dist/components/navigation/commandPalette/commandPaletteNavigation.d.ts +13 -0
  117. package/dist/components/navigation/commandPalette/commandPaletteNavigation.js +38 -0
  118. package/dist/components/navigation/commandPalette/commandPaletteNavigation.js.map +1 -0
  119. package/dist/components/navigation/commandPalette/commandPaletteTypes.d.ts +39 -0
  120. package/dist/components/navigation/commandPalette/index.d.ts +4 -0
  121. package/dist/components/navigation/sidebar/sidebar.d.ts +10 -0
  122. package/dist/components/navigation/sidebar/sidebar.js +61 -59
  123. package/dist/components/navigation/sidebar/sidebar.js.map +1 -1
  124. package/dist/components/navigation/tabs/index.d.ts +3 -1
  125. package/dist/components/navigation/tabs/tab/tab.d.ts +88 -1
  126. package/dist/components/navigation/tabs/tab/tab.js +121 -42
  127. package/dist/components/navigation/tabs/tab/tab.js.map +1 -1
  128. package/dist/components/navigation/tabs/tabPanel/tabPanel.d.ts +19 -0
  129. package/dist/components/navigation/tabs/tabPanel/tabPanel.js +27 -0
  130. package/dist/components/navigation/tabs/tabPanel/tabPanel.js.map +1 -0
  131. package/dist/components/navigation/tabs/tabs.d.ts +75 -5
  132. package/dist/components/navigation/tabs/tabs.js +369 -145
  133. package/dist/components/navigation/tabs/tabs.js.map +1 -1
  134. package/dist/components/navigation/tabs/tabs.utils.d.ts +20 -0
  135. package/dist/components/navigation/tabs/tabs.utils.js +11 -0
  136. package/dist/components/navigation/tabs/tabs.utils.js.map +1 -0
  137. package/dist/components/navigation/tabs/tabsContext.d.ts +11 -0
  138. package/dist/components/navigation/tabs/tabsContext.js +6 -0
  139. package/dist/components/navigation/tabs/tabsContext.js.map +1 -0
  140. package/dist/headless/components/dataDisplay/chat/chat.js +179 -150
  141. package/dist/headless/components/dataDisplay/chat/chat.js.map +1 -1
  142. package/dist/headless/components/dataDisplay/chat/chatComposer.js +31 -29
  143. package/dist/headless/components/dataDisplay/chat/chatComposer.js.map +1 -1
  144. package/dist/headless/components/dataDisplay/chat/utils.js.map +1 -1
  145. package/dist/headless/components/dataDisplay/dagView/dagLayout.js +289 -0
  146. package/dist/headless/components/dataDisplay/dagView/dagLayout.js.map +1 -0
  147. package/dist/headless/components/dataDisplay/dagView/dagView.js +541 -0
  148. package/dist/headless/components/dataDisplay/dagView/dagView.js.map +1 -0
  149. package/dist/headless/components/dataDisplay/dagView/panZoom.js +171 -0
  150. package/dist/headless/components/dataDisplay/dagView/panZoom.js.map +1 -0
  151. package/dist/headless/components/dataDisplay/diffStat/diffStat.js +61 -0
  152. package/dist/headless/components/dataDisplay/diffStat/diffStat.js.map +1 -0
  153. package/dist/headless/components/dataDisplay/kbd/kbd.js +25 -0
  154. package/dist/headless/components/dataDisplay/kbd/kbd.js.map +1 -0
  155. package/dist/headless/components/dataDisplay/kbd/kbdKeys.js +72 -0
  156. package/dist/headless/components/dataDisplay/kbd/kbdKeys.js.map +1 -0
  157. package/dist/headless/components/dataDisplay/list/listItem.js +47 -24
  158. package/dist/headless/components/dataDisplay/list/listItem.js.map +1 -1
  159. package/dist/headless/components/dataDisplay/qrCode/qrCode.js +76 -0
  160. package/dist/headless/components/dataDisplay/qrCode/qrCode.js.map +1 -0
  161. package/dist/headless/components/dataDisplay/qrCode/qrCodePath.js +20 -0
  162. package/dist/headless/components/dataDisplay/qrCode/qrCodePath.js.map +1 -0
  163. package/dist/headless/components/dataDisplay/statusDot/statusDot.js +28 -0
  164. package/dist/headless/components/dataDisplay/statusDot/statusDot.js.map +1 -0
  165. package/dist/headless/components/dataDisplay/swimlanes/swimlanes.js +355 -0
  166. package/dist/headless/components/dataDisplay/swimlanes/swimlanes.js.map +1 -0
  167. package/dist/headless/components/dataDisplay/treeView/treeView.js +113 -92
  168. package/dist/headless/components/dataDisplay/treeView/treeView.js.map +1 -1
  169. package/dist/headless/components/feedBack/modal/modal.js +85 -64
  170. package/dist/headless/components/feedBack/modal/modal.js.map +1 -1
  171. package/dist/headless/components/inputs/button/button.js +56 -50
  172. package/dist/headless/components/inputs/button/button.js.map +1 -1
  173. package/dist/headless/components/inputs/composer/composer.js +217 -0
  174. package/dist/headless/components/inputs/composer/composer.js.map +1 -0
  175. package/dist/headless/components/inputs/composer/composerFiles.js +19 -0
  176. package/dist/headless/components/inputs/composer/composerFiles.js.map +1 -0
  177. package/dist/headless/components/inputs/composer/composerKeys.js +7 -0
  178. package/dist/headless/components/inputs/composer/composerKeys.js.map +1 -0
  179. package/dist/headless/components/layout/sheet/sheet.js +160 -145
  180. package/dist/headless/components/layout/sheet/sheet.js.map +1 -1
  181. package/dist/headless/components/layout/tabGroups/tabGroups.js +781 -0
  182. package/dist/headless/components/layout/tabGroups/tabGroups.js.map +1 -0
  183. package/dist/headless/components/layout/tabGroups/tabGroupsFocus.js +199 -0
  184. package/dist/headless/components/layout/tabGroups/tabGroupsFocus.js.map +1 -0
  185. package/dist/headless/components/layout/tabGroups/tabGroupsLayout.js +497 -0
  186. package/dist/headless/components/layout/tabGroups/tabGroupsLayout.js.map +1 -0
  187. package/dist/headless/components/navigation/appBar/appBar.js +35 -30
  188. package/dist/headless/components/navigation/appBar/appBar.js.map +1 -1
  189. package/dist/headless/components/navigation/commandPalette/commandPalette.js +335 -0
  190. package/dist/headless/components/navigation/commandPalette/commandPalette.js.map +1 -0
  191. package/dist/headless/components/navigation/commandPalette/commandPaletteFilter.js +16 -0
  192. package/dist/headless/components/navigation/commandPalette/commandPaletteFilter.js.map +1 -0
  193. package/dist/headless/components/navigation/commandPalette/commandPaletteNavigation.js +38 -0
  194. package/dist/headless/components/navigation/commandPalette/commandPaletteNavigation.js.map +1 -0
  195. package/dist/headless/components/navigation/sidebar/sidebar.js +56 -54
  196. package/dist/headless/components/navigation/sidebar/sidebar.js.map +1 -1
  197. package/dist/headless/components/navigation/tabs/tab/tab.js +121 -42
  198. package/dist/headless/components/navigation/tabs/tab/tab.js.map +1 -1
  199. package/dist/headless/components/navigation/tabs/tabPanel/tabPanel.js +25 -0
  200. package/dist/headless/components/navigation/tabs/tabPanel/tabPanel.js.map +1 -0
  201. package/dist/headless/components/navigation/tabs/tabs.js +369 -145
  202. package/dist/headless/components/navigation/tabs/tabs.js.map +1 -1
  203. package/dist/headless/components/navigation/tabs/tabs.utils.js +11 -0
  204. package/dist/headless/components/navigation/tabs/tabs.utils.js.map +1 -0
  205. package/dist/headless/components/navigation/tabs/tabsContext.js +6 -0
  206. package/dist/headless/components/navigation/tabs/tabsContext.js.map +1 -0
  207. package/dist/headless/hooks/useFocusTrap.js +24 -21
  208. package/dist/headless/hooks/useFocusTrap.js.map +1 -1
  209. package/dist/headless/hooks/useKeyboardShortcut.js +19 -0
  210. package/dist/headless/hooks/useKeyboardShortcut.js.map +1 -0
  211. package/dist/headless/index.js +360 -316
  212. package/dist/headless/index.js.map +1 -1
  213. package/dist/headless/utils/keyboardShortcut.js +49 -0
  214. package/dist/headless/utils/keyboardShortcut.js.map +1 -0
  215. package/dist/headless/utils/linkAttrs.js +13 -0
  216. package/dist/headless/utils/linkAttrs.js.map +1 -0
  217. package/dist/headless/utils/restoreFocus.js +10 -0
  218. package/dist/headless/utils/restoreFocus.js.map +1 -0
  219. package/dist/hooks/useFocusTrap.d.ts +6 -1
  220. package/dist/hooks/useFocusTrap.js +24 -21
  221. package/dist/hooks/useFocusTrap.js.map +1 -1
  222. package/dist/hooks/useKeyboardShortcut.d.ts +30 -0
  223. package/dist/hooks/useKeyboardShortcut.js +19 -0
  224. package/dist/hooks/useKeyboardShortcut.js.map +1 -0
  225. package/dist/index.cjs +3 -2
  226. package/dist/index.cjs.map +1 -1
  227. package/dist/index.d.ts +24 -3
  228. package/dist/index.js +360 -316
  229. package/dist/index.js.map +1 -1
  230. package/dist/utils/keyboardShortcut.d.ts +12 -0
  231. package/dist/utils/keyboardShortcut.js +49 -0
  232. package/dist/utils/keyboardShortcut.js.map +1 -0
  233. package/dist/utils/linkAttrs.d.ts +13 -0
  234. package/dist/utils/linkAttrs.js +13 -0
  235. package/dist/utils/linkAttrs.js.map +1 -0
  236. package/dist/utils/restoreFocus.d.ts +8 -0
  237. package/dist/utils/restoreFocus.js +10 -0
  238. package/dist/utils/restoreFocus.js.map +1 -0
  239. package/package.json +5 -3
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,49 @@
1
+ import { detectKbdPlatform as d } from "../components/dataDisplay/kbd/kbdKeys.js";
2
+ const p = {
3
+ mod: "mod",
4
+ ctrl: "ctrl",
5
+ control: "ctrl",
6
+ alt: "alt",
7
+ option: "alt",
8
+ shift: "shift",
9
+ meta: "meta",
10
+ cmd: "meta",
11
+ command: "meta"
12
+ }, h = {
13
+ esc: "escape",
14
+ space: " ",
15
+ spacebar: " ",
16
+ up: "arrowup",
17
+ down: "arrowdown",
18
+ left: "arrowleft",
19
+ right: "arrowright",
20
+ plus: "+",
21
+ return: "enter",
22
+ del: "delete"
23
+ };
24
+ function y(t, m, n = d()) {
25
+ var i, c;
26
+ let l = !1, s = !1, f = !1, a = !1, e = null;
27
+ for (const u of m) {
28
+ const o = u.trim().toLowerCase() || u, r = p[o];
29
+ if (r === "mod")
30
+ n === "mac" ? a = !0 : l = !0;
31
+ else if (r === "ctrl")
32
+ l = !0;
33
+ else if (r === "alt")
34
+ s = !0;
35
+ else if (r === "shift")
36
+ f = !0;
37
+ else if (r === "meta")
38
+ a = !0;
39
+ else if (e === null)
40
+ e = (i = h[o]) != null ? i : o;
41
+ else
42
+ return !1;
43
+ }
44
+ return e === null || t.ctrlKey !== l || t.altKey !== s || t.shiftKey !== f || t.metaKey !== a ? !1 : ((c = t.key) != null ? c : "").toLowerCase() === e ? !0 : /^[a-z]$/.test(e) ? t.code === `Key${e.toUpperCase()}` : /^[0-9]$/.test(e) ? t.code === `Digit${e}` : !1;
45
+ }
46
+ export {
47
+ y as matchesKeyboardShortcut
48
+ };
49
+ //# sourceMappingURL=keyboardShortcut.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyboardShortcut.js","sources":["../../../src/utils/keyboardShortcut.ts"],"sourcesContent":["import { detectKbdPlatform, type KbdPlatform } from \"../components/dataDisplay/kbd/kbdKeys\";\n\ntype ShortcutEvent = Pick<KeyboardEvent, \"key\" | \"code\" | \"ctrlKey\" | \"metaKey\" | \"altKey\" | \"shiftKey\">;\n\n// Modifier names (lower case) and the flag each one asks for. `mod` is resolved per platform.\nconst MODIFIERS: Record<string, \"mod\" | \"ctrl\" | \"alt\" | \"shift\" | \"meta\"> = {\n mod: \"mod\",\n ctrl: \"ctrl\",\n control: \"ctrl\",\n alt: \"alt\",\n option: \"alt\",\n shift: \"shift\",\n meta: \"meta\",\n cmd: \"meta\",\n command: \"meta\",\n};\n\n// Names (lower case) that stand for another `KeyboardEvent.key`.\nconst KEY_ALIASES: Record<string, string> = {\n esc: \"escape\",\n space: \" \",\n spacebar: \" \",\n up: \"arrowup\",\n down: \"arrowdown\",\n left: \"arrowleft\",\n right: \"arrowright\",\n plus: \"+\",\n return: \"enter\",\n del: \"delete\",\n};\n\n/**\n * Whether a keyboard event is the shortcut `keys` (in `Kbd` key names, e.g. `[\"Mod\", \"K\"]`).\n * `Mod` is Meta on `mac` and Ctrl elsewhere. The modifiers must match exactly, so Mod+K does not\n * match Mod+Shift+K. The list needs exactly one key that is not a modifier. It is compared with\n * `event.key` ignoring case; a single ASCII letter or digit also matches `event.code`\n * (`KeyK`, `Digit1`), so a shortcut keeps working on a layout that types other letters.\n * `platform` is the running browser's by default.\n */\nexport function matchesKeyboardShortcut(\n event: ShortcutEvent,\n keys: string[],\n platform: KbdPlatform = detectKbdPlatform()\n): boolean {\n let ctrl = false;\n let alt = false;\n let shift = false;\n let meta = false;\n let main: string | null = null;\n for (const key of keys) {\n const lower = key.trim().toLowerCase() || key; // a lone \" \" is the space bar\n const modifier = MODIFIERS[lower];\n if (modifier === \"mod\") {\n if (platform === \"mac\") {\n meta = true;\n } else {\n ctrl = true;\n }\n } else if (modifier === \"ctrl\") {\n ctrl = true;\n } else if (modifier === \"alt\") {\n alt = true;\n } else if (modifier === \"shift\") {\n shift = true;\n } else if (modifier === \"meta\") {\n meta = true;\n } else if (main === null) {\n main = KEY_ALIASES[lower] ?? lower;\n } else {\n return false;\n }\n }\n if (main === null) {\n return false;\n }\n if (event.ctrlKey !== ctrl || event.altKey !== alt || event.shiftKey !== shift || event.metaKey !== meta) {\n return false;\n }\n if ((event.key ?? \"\").toLowerCase() === main) {\n return true;\n }\n if (/^[a-z]$/.test(main)) {\n return event.code === `Key${main.toUpperCase()}`;\n }\n if (/^[0-9]$/.test(main)) {\n return event.code === `Digit${main}`;\n }\n return false;\n}\n"],"names":["MODIFIERS","KEY_ALIASES","matchesKeyboardShortcut","event","keys","platform","detectKbdPlatform","ctrl","alt","shift","meta","main","key","lower","modifier","_a","_b"],"mappings":";AAKA,MAAMA,IAAuE;AAAA,EAC3E,KAAK;AAAA,EACL,MAAM;AAAA,EACN,SAAS;AAAA,EACT,KAAK;AAAA,EACL,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,KAAK;AAAA,EACL,SAAS;AACX,GAGMC,IAAsC;AAAA,EAC1C,KAAK;AAAA,EACL,OAAO;AAAA,EACP,UAAU;AAAA,EACV,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AAAA,EACP,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,KAAK;AACP;AAUO,SAASC,EACdC,GACAC,GACAC,IAAwBC,KACf;;AACT,MAAIC,IAAO,IACPC,IAAM,IACNC,IAAQ,IACRC,IAAO,IACPC,IAAsB;AAC1B,aAAWC,KAAOR,GAAM;AACtB,UAAMS,IAAQD,EAAI,KAAA,EAAO,iBAAiBA,GACpCE,IAAWd,EAAUa,CAAK;AAChC,QAAIC,MAAa;AACf,MAAIT,MAAa,QACfK,IAAO,KAEPH,IAAO;AAAA,aAEAO,MAAa;AACtB,MAAAP,IAAO;AAAA,aACEO,MAAa;AACtB,MAAAN,IAAM;AAAA,aACGM,MAAa;AACtB,MAAAL,IAAQ;AAAA,aACCK,MAAa;AACtB,MAAAJ,IAAO;AAAA,aACEC,MAAS;AAClB,MAAAA,KAAOI,IAAAd,EAAYY,CAAK,MAAjB,OAAAE,IAAsBF;AAAA;AAE7B,aAAO;AAAA,EAEX;AAIA,SAHIF,MAAS,QAGTR,EAAM,YAAYI,KAAQJ,EAAM,WAAWK,KAAOL,EAAM,aAAaM,KAASN,EAAM,YAAYO,IAC3F,OAEJM,IAAAb,EAAM,QAAN,OAAAa,IAAa,IAAI,YAAA,MAAkBL,IAC/B,KAEL,UAAU,KAAKA,CAAI,IACdR,EAAM,SAAS,MAAMQ,EAAK,aAAa,KAE5C,UAAU,KAAKA,CAAI,IACdR,EAAM,SAAS,QAAQQ,CAAI,KAE7B;AACT;"}
@@ -0,0 +1,13 @@
1
+ const o = "_blank";
2
+ function r(e) {
3
+ return e === void 0 ? o : e === null || e === "" ? void 0 : e;
4
+ }
5
+ function l(e, n) {
6
+ return e !== void 0 ? e || void 0 : n !== void 0 && n !== "_self" && n !== "_parent" && n !== "_top" ? "noreferrer" : void 0;
7
+ }
8
+ export {
9
+ o as DEFAULT_LINK_TARGET,
10
+ l as resolveLinkRel,
11
+ r as resolveLinkTarget
12
+ };
13
+ //# sourceMappingURL=linkAttrs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"linkAttrs.js","sources":["../../../src/utils/linkAttrs.ts"],"sourcesContent":["/** Where a link opens unless its `target` says otherwise. */\nexport const DEFAULT_LINK_TARGET = \"_blank\";\n\n/**\n * The `target` attribute for a link: `undefined` (not given) keeps `\"_blank\"`, `null` (or an\n * empty string) leaves the attribute out so the link opens in place, any other string is used as is.\n */\nexport function resolveLinkTarget(target: string | null | undefined): string | undefined {\n if (target === undefined) return DEFAULT_LINK_TARGET;\n return target === null || target === \"\" ? undefined : target;\n}\n\n/**\n * The `rel` attribute for a link: the caller's wins (an empty string drops it), `undefined` keeps the\n * default. The default is `\"noreferrer\"` for a link that opens another browsing context (a new tab or\n * a named window), which also implies `noopener`, and none for a link that opens in place.\n */\nexport function resolveLinkRel(rel: string | undefined, target: string | undefined): string | undefined {\n if (rel !== undefined) return rel || undefined;\n const elsewhere = target !== undefined && target !== \"_self\" && target !== \"_parent\" && target !== \"_top\";\n return elsewhere ? \"noreferrer\" : undefined;\n}\n"],"names":["DEFAULT_LINK_TARGET","resolveLinkTarget","target","resolveLinkRel","rel"],"mappings":"AACO,MAAMA,IAAsB;AAM5B,SAASC,EAAkBC,GAAuD;AACvF,SAAIA,MAAW,SAAkBF,IAC1BE,MAAW,QAAQA,MAAW,KAAK,SAAYA;AACxD;AAOO,SAASC,EAAeC,GAAyBF,GAAgD;AACtG,SAAIE,MAAQ,SAAkBA,KAAO,SACnBF,MAAW,UAAaA,MAAW,WAAWA,MAAW,aAAaA,MAAW,SAChF,eAAe;AACpC;"}
@@ -0,0 +1,10 @@
1
+ function c(e, n) {
2
+ if (!(e instanceof HTMLElement) || e === document.body || !e.isConnected)
3
+ return;
4
+ const t = document.activeElement;
5
+ t && t !== document.body && !(n != null && n.contains(t)) || e.focus({ preventScroll: !0 });
6
+ }
7
+ export {
8
+ c as restoreFocus
9
+ };
10
+ //# sourceMappingURL=restoreFocus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"restoreFocus.js","sources":["../../../src/utils/restoreFocus.ts"],"sourcesContent":["/**\n * Hands focus back to `opener`, the element that had it when an overlay opened, once the\n * overlay `root` closes. Focus the app moved elsewhere on purpose stays where it is: it\n * is restored only while it is still inside `root` or has been dropped on the body (the\n * root is gone, or the element that had it was removed). An opener that left the document\n * is skipped, and one that cannot take focus (disabled, hidden) simply ignores the call.\n */\nexport function restoreFocus(opener: Element | null, root: Element | null | undefined): void {\n if (!(opener instanceof HTMLElement) || opener === document.body || !opener.isConnected) {\n return;\n }\n const active = document.activeElement;\n if (active && active !== document.body && !root?.contains(active)) {\n return;\n }\n opener.focus({ preventScroll: true });\n}\n"],"names":["restoreFocus","opener","root","active"],"mappings":"AAOO,SAASA,EAAaC,GAAwBC,GAAwC;AAC3F,MAAI,EAAED,aAAkB,gBAAgBA,MAAW,SAAS,QAAQ,CAACA,EAAO;AAC1E;AAEF,QAAME,IAAS,SAAS;AACxB,EAAIA,KAAUA,MAAW,SAAS,QAAQ,EAACD,KAAA,QAAAA,EAAM,SAASC,OAG1DF,EAAO,MAAM,EAAE,eAAe,GAAA,CAAM;AACtC;"}
@@ -8,10 +8,15 @@ export interface UseFocusTrapOptions {
8
8
  restoreFocus?: boolean;
9
9
  /** Focus the first focusable element on activate. */
10
10
  autoFocus?: boolean;
11
+ /**
12
+ * Checked on every Tab. While it returns false the trap leaves the key alone, so a trap stacked above
13
+ * this one (a dialog opened from inside the container) handles it. Defaults to always true.
14
+ */
15
+ isTopmost?: () => boolean;
11
16
  }
12
17
  /**
13
18
  * Confines Tab/Shift+Tab focus within `containerRef` while `active`.
14
19
  * Restores focus to the previously focused element on teardown.
15
20
  */
16
- export declare function useFocusTrap<T extends HTMLElement>(containerRef: RefObject<T>, { active, restoreFocus, autoFocus }: UseFocusTrapOptions): void;
21
+ export declare function useFocusTrap<T extends HTMLElement>(containerRef: RefObject<T>, { active, restoreFocus, autoFocus, isTopmost }: UseFocusTrapOptions): void;
17
22
  export default useFocusTrap;
@@ -1,39 +1,42 @@
1
- import { useRef as m, useEffect as p } from "react";
2
- const b = 'a[href],button:not([disabled]),textarea:not([disabled]),input:not([disabled]),select:not([disabled]),[tabindex]:not([tabindex="-1"])';
3
- function E(c, { active: s, restoreFocus: f = !0, autoFocus: l = !0 }) {
4
- const r = m(null);
5
- p(() => {
6
- if (!s)
1
+ import { useRef as y, useEffect as E } from "react";
2
+ const v = 'a[href],button:not([disabled]),textarea:not([disabled]),input:not([disabled]),select:not([disabled]),[tabindex]:not([tabindex="-1"])';
3
+ function D(s, { active: f, restoreFocus: l = !0, autoFocus: i = !0, isTopmost: a }) {
4
+ const u = y(null), d = y(a);
5
+ d.current = a, E(() => {
6
+ if (!f)
7
7
  return;
8
- const t = c.current;
8
+ const t = s.current;
9
9
  if (!t)
10
10
  return;
11
- r.current = document.activeElement;
12
- const i = () => Array.from(t.querySelectorAll(b)).filter(
11
+ u.current = document.activeElement;
12
+ const m = () => Array.from(t.querySelectorAll(v)).filter(
13
13
  (e) => e.offsetParent !== null || e === document.activeElement
14
14
  );
15
- if (l) {
16
- const e = i()[0];
15
+ if (i && !t.contains(document.activeElement)) {
16
+ const e = m()[0];
17
17
  (e != null ? e : t).focus({ preventScroll: !0 });
18
18
  }
19
- const a = (e) => {
19
+ const p = (e) => {
20
20
  if (e.key !== "Tab")
21
21
  return;
22
- const n = i();
22
+ const b = d.current;
23
+ if (b && !b())
24
+ return;
25
+ const n = m();
23
26
  if (n.length === 0) {
24
- e.preventDefault();
27
+ e.preventDefault(), t.focus({ preventScroll: !0 });
25
28
  return;
26
29
  }
27
- const o = n[0], d = n[n.length - 1], u = document.activeElement;
28
- e.shiftKey && u === o ? (e.preventDefault(), d.focus()) : !e.shiftKey && u === d ? (e.preventDefault(), o.focus()) : t.contains(u) || (e.preventDefault(), o.focus());
30
+ const r = n[0], c = n[n.length - 1], o = document.activeElement;
31
+ o === t ? (e.preventDefault(), (e.shiftKey ? c : r).focus()) : e.shiftKey && o === r ? (e.preventDefault(), c.focus()) : !e.shiftKey && o === c ? (e.preventDefault(), r.focus()) : t.contains(o) || (e.preventDefault(), r.focus());
29
32
  };
30
- return document.addEventListener("keydown", a, !0), () => {
31
- document.removeEventListener("keydown", a, !0), f && r.current && r.current.focus({ preventScroll: !0 });
33
+ return document.addEventListener("keydown", p, !0), () => {
34
+ document.removeEventListener("keydown", p, !0), l && u.current && u.current.focus({ preventScroll: !0 });
32
35
  };
33
- }, [s, l, f, c]);
36
+ }, [f, i, l, s]);
34
37
  }
35
38
  export {
36
- b as FOCUSABLE,
37
- E as useFocusTrap
39
+ v as FOCUSABLE,
40
+ D as useFocusTrap
38
41
  };
39
42
  //# sourceMappingURL=useFocusTrap.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useFocusTrap.js","sources":["../../src/hooks/useFocusTrap.ts"],"sourcesContent":["import { RefObject, useEffect, useRef } from \"react\";\n\n/** Elements in the Tab order (hidden ones match too). */\nexport const FOCUSABLE =\n 'a[href],button:not([disabled]),textarea:not([disabled]),input:not([disabled]),select:not([disabled]),[tabindex]:not([tabindex=\"-1\"])';\n\nexport interface UseFocusTrapOptions {\n /** Trap is active only while true. */\n active: boolean;\n /** Restore focus to the previously focused element on deactivate. */\n restoreFocus?: boolean;\n /** Focus the first focusable element on activate. */\n autoFocus?: boolean;\n}\n\n/**\n * Confines Tab/Shift+Tab focus within `containerRef` while `active`.\n * Restores focus to the previously focused element on teardown.\n */\nexport function useFocusTrap<T extends HTMLElement>(\n containerRef: RefObject<T>,\n { active, restoreFocus = true, autoFocus = true }: UseFocusTrapOptions\n): void {\n const prevFocused = useRef<HTMLElement | null>(null);\n\n useEffect(() => {\n if (!active) {\n return;\n }\n const container = containerRef.current;\n if (!container) {\n return;\n }\n\n prevFocused.current = document.activeElement as HTMLElement | null;\n\n const focusables = () =>\n Array.from(container.querySelectorAll<HTMLElement>(FOCUSABLE)).filter(\n (el) => el.offsetParent !== null || el === document.activeElement\n );\n\n if (autoFocus) {\n const first = focusables()[0];\n (first ?? container).focus({ preventScroll: true });\n }\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key !== \"Tab\") {\n return;\n }\n const items = focusables();\n if (items.length === 0) {\n event.preventDefault();\n return;\n }\n const first = items[0];\n const last = items[items.length - 1];\n const activeEl = document.activeElement;\n\n if (event.shiftKey && activeEl === first) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && activeEl === last) {\n event.preventDefault();\n first.focus();\n } else if (!container.contains(activeEl)) {\n event.preventDefault();\n first.focus();\n }\n };\n\n document.addEventListener(\"keydown\", onKeyDown, true);\n return () => {\n document.removeEventListener(\"keydown\", onKeyDown, true);\n if (restoreFocus && prevFocused.current) {\n prevFocused.current.focus({ preventScroll: true });\n }\n };\n }, [active, autoFocus, restoreFocus, containerRef]);\n}\n\nexport default useFocusTrap;\n"],"names":["FOCUSABLE","useFocusTrap","containerRef","active","restoreFocus","autoFocus","prevFocused","useRef","useEffect","container","focusables","el","first","onKeyDown","event","items","last","activeEl"],"mappings":";AAGO,MAAMA,IACX;AAeK,SAASC,EACdC,GACA,EAAE,QAAAC,GAAQ,cAAAC,IAAe,IAAM,WAAAC,IAAY,MACrC;AACN,QAAMC,IAAcC,EAA2B,IAAI;AAEnD,EAAAC,EAAU,MAAM;AACd,QAAI,CAACL;AACH;AAEF,UAAMM,IAAYP,EAAa;AAC/B,QAAI,CAACO;AACH;AAGF,IAAAH,EAAY,UAAU,SAAS;AAE/B,UAAMI,IAAa,MACjB,MAAM,KAAKD,EAAU,iBAA8BT,CAAS,CAAC,EAAE;AAAA,MAC7D,CAACW,MAAOA,EAAG,iBAAiB,QAAQA,MAAO,SAAS;AAAA,IAAA;AAGxD,QAAIN,GAAW;AACb,YAAMO,IAAQF,EAAA,EAAa,CAAC;AAC5B,OAACE,KAAA,OAAAA,IAASH,GAAW,MAAM,EAAE,eAAe,IAAM;AAAA,IACpD;AAEA,UAAMI,IAAY,CAACC,MAAyB;AAC1C,UAAIA,EAAM,QAAQ;AAChB;AAEF,YAAMC,IAAQL,EAAA;AACd,UAAIK,EAAM,WAAW,GAAG;AACtB,QAAAD,EAAM,eAAA;AACN;AAAA,MACF;AACA,YAAMF,IAAQG,EAAM,CAAC,GACfC,IAAOD,EAAMA,EAAM,SAAS,CAAC,GAC7BE,IAAW,SAAS;AAE1B,MAAIH,EAAM,YAAYG,MAAaL,KACjCE,EAAM,eAAA,GACNE,EAAK,MAAA,KACI,CAACF,EAAM,YAAYG,MAAaD,KACzCF,EAAM,eAAA,GACNF,EAAM,MAAA,KACIH,EAAU,SAASQ,CAAQ,MACrCH,EAAM,eAAA,GACNF,EAAM,MAAA;AAAA,IAEV;AAEA,oBAAS,iBAAiB,WAAWC,GAAW,EAAI,GAC7C,MAAM;AACX,eAAS,oBAAoB,WAAWA,GAAW,EAAI,GACnDT,KAAgBE,EAAY,WAC9BA,EAAY,QAAQ,MAAM,EAAE,eAAe,IAAM;AAAA,IAErD;AAAA,EACF,GAAG,CAACH,GAAQE,GAAWD,GAAcF,CAAY,CAAC;AACpD;"}
1
+ {"version":3,"file":"useFocusTrap.js","sources":["../../src/hooks/useFocusTrap.ts"],"sourcesContent":["import { RefObject, useEffect, useRef } from \"react\";\n\n/** Elements in the Tab order (hidden ones match too). */\nexport const FOCUSABLE =\n 'a[href],button:not([disabled]),textarea:not([disabled]),input:not([disabled]),select:not([disabled]),[tabindex]:not([tabindex=\"-1\"])';\n\nexport interface UseFocusTrapOptions {\n /** Trap is active only while true. */\n active: boolean;\n /** Restore focus to the previously focused element on deactivate. */\n restoreFocus?: boolean;\n /** Focus the first focusable element on activate. */\n autoFocus?: boolean;\n /**\n * Checked on every Tab. While it returns false the trap leaves the key alone, so a trap stacked above\n * this one (a dialog opened from inside the container) handles it. Defaults to always true.\n */\n isTopmost?: () => boolean;\n}\n\n/**\n * Confines Tab/Shift+Tab focus within `containerRef` while `active`.\n * Restores focus to the previously focused element on teardown.\n */\nexport function useFocusTrap<T extends HTMLElement>(\n containerRef: RefObject<T>,\n { active, restoreFocus = true, autoFocus = true, isTopmost }: UseFocusTrapOptions\n): void {\n const prevFocused = useRef<HTMLElement | null>(null);\n // Read at key time, never as an effect dependency: an inline callback would re-arm the trap on every\n // render and re-run `autoFocus`.\n const isTopmostRef = useRef(isTopmost);\n isTopmostRef.current = isTopmost;\n\n useEffect(() => {\n if (!active) {\n return;\n }\n const container = containerRef.current;\n if (!container) {\n return;\n }\n\n prevFocused.current = document.activeElement as HTMLElement | null;\n\n const focusables = () =>\n Array.from(container.querySelectorAll<HTMLElement>(FOCUSABLE)).filter(\n (el) => el.offsetParent !== null || el === document.activeElement\n );\n\n // A child that has already taken the focus (an `autoFocus` field, or one focusing itself in an\n // effect) keeps it; the trap only brings in a focus that is still outside.\n if (autoFocus && !container.contains(document.activeElement)) {\n const first = focusables()[0];\n (first ?? container).focus({ preventScroll: true });\n }\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key !== \"Tab\") {\n return;\n }\n const isTopmost = isTopmostRef.current;\n if (isTopmost && !isTopmost()) {\n return;\n }\n const items = focusables();\n if (items.length === 0) {\n event.preventDefault();\n // Nothing left to tab through (e.g. every control disabled) and Chromium has dropped the\n // focus to <body>: pull it back into the container instead of leaving it behind the dialog.\n container.focus({ preventScroll: true });\n return;\n }\n const first = items[0];\n const last = items[items.length - 1];\n const activeEl = document.activeElement;\n\n if (activeEl === container) {\n // The container itself holds the focus (a press on its empty area, or the `autoFocus`\n // fallback): it is not a tab stop, so the browser would step to the page behind it.\n event.preventDefault();\n (event.shiftKey ? last : first).focus();\n } else if (event.shiftKey && activeEl === first) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && activeEl === last) {\n event.preventDefault();\n first.focus();\n } else if (!container.contains(activeEl)) {\n event.preventDefault();\n first.focus();\n }\n };\n\n document.addEventListener(\"keydown\", onKeyDown, true);\n return () => {\n document.removeEventListener(\"keydown\", onKeyDown, true);\n if (restoreFocus && prevFocused.current) {\n prevFocused.current.focus({ preventScroll: true });\n }\n };\n }, [active, autoFocus, restoreFocus, containerRef]);\n}\n\nexport default useFocusTrap;\n"],"names":["FOCUSABLE","useFocusTrap","containerRef","active","restoreFocus","autoFocus","isTopmost","prevFocused","useRef","isTopmostRef","useEffect","container","focusables","el","first","onKeyDown","event","items","last","activeEl"],"mappings":";AAGO,MAAMA,IACX;AAoBK,SAASC,EACdC,GACA,EAAE,QAAAC,GAAQ,cAAAC,IAAe,IAAM,WAAAC,IAAY,IAAM,WAAAC,KAC3C;AACN,QAAMC,IAAcC,EAA2B,IAAI,GAG7CC,IAAeD,EAAOF,CAAS;AACrC,EAAAG,EAAa,UAAUH,GAEvBI,EAAU,MAAM;AACd,QAAI,CAACP;AACH;AAEF,UAAMQ,IAAYT,EAAa;AAC/B,QAAI,CAACS;AACH;AAGF,IAAAJ,EAAY,UAAU,SAAS;AAE/B,UAAMK,IAAa,MACjB,MAAM,KAAKD,EAAU,iBAA8BX,CAAS,CAAC,EAAE;AAAA,MAC7D,CAACa,MAAOA,EAAG,iBAAiB,QAAQA,MAAO,SAAS;AAAA,IAAA;AAKxD,QAAIR,KAAa,CAACM,EAAU,SAAS,SAAS,aAAa,GAAG;AAC5D,YAAMG,IAAQF,EAAA,EAAa,CAAC;AAC5B,OAACE,KAAA,OAAAA,IAASH,GAAW,MAAM,EAAE,eAAe,IAAM;AAAA,IACpD;AAEA,UAAMI,IAAY,CAACC,MAAyB;AAC1C,UAAIA,EAAM,QAAQ;AAChB;AAEF,YAAMV,IAAYG,EAAa;AAC/B,UAAIH,KAAa,CAACA;AAChB;AAEF,YAAMW,IAAQL,EAAA;AACd,UAAIK,EAAM,WAAW,GAAG;AACtB,QAAAD,EAAM,eAAA,GAGNL,EAAU,MAAM,EAAE,eAAe,GAAA,CAAM;AACvC;AAAA,MACF;AACA,YAAMG,IAAQG,EAAM,CAAC,GACfC,IAAOD,EAAMA,EAAM,SAAS,CAAC,GAC7BE,IAAW,SAAS;AAE1B,MAAIA,MAAaR,KAGfK,EAAM,eAAA,IACLA,EAAM,WAAWE,IAAOJ,GAAO,MAAA,KACvBE,EAAM,YAAYG,MAAaL,KACxCE,EAAM,eAAA,GACNE,EAAK,MAAA,KACI,CAACF,EAAM,YAAYG,MAAaD,KACzCF,EAAM,eAAA,GACNF,EAAM,MAAA,KACIH,EAAU,SAASQ,CAAQ,MACrCH,EAAM,eAAA,GACNF,EAAM,MAAA;AAAA,IAEV;AAEA,oBAAS,iBAAiB,WAAWC,GAAW,EAAI,GAC7C,MAAM;AACX,eAAS,oBAAoB,WAAWA,GAAW,EAAI,GACnDX,KAAgBG,EAAY,WAC9BA,EAAY,QAAQ,MAAM,EAAE,eAAe,IAAM;AAAA,IAErD;AAAA,EACF,GAAG,CAACJ,GAAQE,GAAWD,GAAcF,CAAY,CAAC;AACpD;"}
@@ -0,0 +1,30 @@
1
+ export interface UseKeyboardShortcutOptions {
2
+ /** The shortcut in `Kbd` key names, e.g. `["Mod", "K"]` (Command on macOS, Ctrl elsewhere). */
3
+ keys: string[];
4
+ /** Runs on a matching keydown anywhere in the document; the event's default action is prevented. */
5
+ onTrigger: (event: KeyboardEvent) => void;
6
+ /** Listen only while true; `true` by default. */
7
+ enabled?: boolean;
8
+ }
9
+ /**
10
+ * Binds a global keyboard shortcut while the component is mounted — the Mod+K that opens a
11
+ * `CommandPalette`, or any other app-wide key. One `keydown` listener on the document, added while
12
+ * `enabled` and removed on cleanup; nothing runs on the server.
13
+ *
14
+ * An event another handler already used (`defaultPrevented`), a key repeat and an IME composition
15
+ * are ignored. On a match the default action is prevented first — Chrome and Firefox otherwise take
16
+ * Ctrl+K for their own search — and then `onTrigger` runs. Modifiers must match exactly, so Mod+K
17
+ * does not fire on Mod+Shift+K, and a single letter or digit also matches by `event.code`, so the
18
+ * shortcut keeps working on a keyboard layout that types other letters.
19
+ *
20
+ * `keys` and `onTrigger` are read when the event arrives, so a new function identity on each render
21
+ * never re-subscribes the listener.
22
+ *
23
+ * ```tsx
24
+ * const [open, setOpen] = useState(false);
25
+ * useKeyboardShortcut({ keys: ["Mod", "K"], onTrigger: () => setOpen((was) => !was) });
26
+ * <Kbd keys={["Mod", "K"]} />
27
+ * ```
28
+ */
29
+ export declare function useKeyboardShortcut({ keys, onTrigger, enabled }: UseKeyboardShortcutOptions): void;
30
+ export default useKeyboardShortcut;
@@ -0,0 +1,19 @@
1
+ import { useRef as c, useEffect as f } from "react";
2
+ import { matchesKeyboardShortcut as s } from "../utils/keyboardShortcut.js";
3
+ function m({ keys: t, onTrigger: o, enabled: u = !0 }) {
4
+ const e = c({ keys: t, onTrigger: o });
5
+ e.current = { keys: t, onTrigger: o }, f(() => {
6
+ if (!u)
7
+ return;
8
+ const n = (r) => {
9
+ r.defaultPrevented || r.repeat || r.isComposing || r.keyCode === 229 || s(r, e.current.keys) && (r.preventDefault(), e.current.onTrigger(r));
10
+ };
11
+ return document.addEventListener("keydown", n), () => {
12
+ document.removeEventListener("keydown", n);
13
+ };
14
+ }, [u]);
15
+ }
16
+ export {
17
+ m as useKeyboardShortcut
18
+ };
19
+ //# sourceMappingURL=useKeyboardShortcut.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useKeyboardShortcut.js","sources":["../../src/hooks/useKeyboardShortcut.ts"],"sourcesContent":["import { useEffect, useRef } from \"react\";\nimport { matchesKeyboardShortcut } from \"../utils/keyboardShortcut\";\n\nexport interface UseKeyboardShortcutOptions {\n /** The shortcut in `Kbd` key names, e.g. `[\"Mod\", \"K\"]` (Command on macOS, Ctrl elsewhere). */\n keys: string[];\n /** Runs on a matching keydown anywhere in the document; the event's default action is prevented. */\n onTrigger: (event: KeyboardEvent) => void;\n /** Listen only while true; `true` by default. */\n enabled?: boolean;\n}\n\n/**\n * Binds a global keyboard shortcut while the component is mounted — the Mod+K that opens a\n * `CommandPalette`, or any other app-wide key. One `keydown` listener on the document, added while\n * `enabled` and removed on cleanup; nothing runs on the server.\n *\n * An event another handler already used (`defaultPrevented`), a key repeat and an IME composition\n * are ignored. On a match the default action is prevented first — Chrome and Firefox otherwise take\n * Ctrl+K for their own search — and then `onTrigger` runs. Modifiers must match exactly, so Mod+K\n * does not fire on Mod+Shift+K, and a single letter or digit also matches by `event.code`, so the\n * shortcut keeps working on a keyboard layout that types other letters.\n *\n * `keys` and `onTrigger` are read when the event arrives, so a new function identity on each render\n * never re-subscribes the listener.\n *\n * ```tsx\n * const [open, setOpen] = useState(false);\n * useKeyboardShortcut({ keys: [\"Mod\", \"K\"], onTrigger: () => setOpen((was) => !was) });\n * <Kbd keys={[\"Mod\", \"K\"]} />\n * ```\n */\nexport function useKeyboardShortcut({ keys, onTrigger, enabled = true }: UseKeyboardShortcutOptions): void {\n const latest = useRef({ keys, onTrigger });\n latest.current = { keys, onTrigger };\n\n useEffect(() => {\n if (!enabled) {\n return;\n }\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.defaultPrevented || event.repeat || event.isComposing || event.keyCode === 229) {\n return;\n }\n if (!matchesKeyboardShortcut(event, latest.current.keys)) {\n return;\n }\n event.preventDefault();\n latest.current.onTrigger(event);\n };\n document.addEventListener(\"keydown\", onKeyDown);\n return () => {\n document.removeEventListener(\"keydown\", onKeyDown);\n };\n }, [enabled]);\n}\n\nexport default useKeyboardShortcut;\n"],"names":["useKeyboardShortcut","keys","onTrigger","enabled","latest","useRef","useEffect","onKeyDown","event","matchesKeyboardShortcut"],"mappings":";;AAgCO,SAASA,EAAoB,EAAE,MAAAC,GAAM,WAAAC,GAAW,SAAAC,IAAU,MAA0C;AACzG,QAAMC,IAASC,EAAO,EAAE,MAAAJ,GAAM,WAAAC,GAAW;AACzC,EAAAE,EAAO,UAAU,EAAE,MAAAH,GAAM,WAAAC,EAAA,GAEzBI,EAAU,MAAM;AACd,QAAI,CAACH;AACH;AAEF,UAAMI,IAAY,CAACC,MAAyB;AAC1C,MAAIA,EAAM,oBAAoBA,EAAM,UAAUA,EAAM,eAAeA,EAAM,YAAY,OAGhFC,EAAwBD,GAAOJ,EAAO,QAAQ,IAAI,MAGvDI,EAAM,eAAA,GACNJ,EAAO,QAAQ,UAAUI,CAAK;AAAA,IAChC;AACA,oBAAS,iBAAiB,WAAWD,CAAS,GACvC,MAAM;AACX,eAAS,oBAAoB,WAAWA,CAAS;AAAA,IACnD;AAAA,EACF,GAAG,CAACJ,CAAO,CAAC;AACd;"}