@fivem-nui/svelte 0.0.0-stage → 1.0.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 (222) hide show
  1. package/README.md +284 -2
  2. package/dist/actions/clickOutside/clickOutside.d.ts +24 -0
  3. package/dist/actions/clickOutside/clickOutside.d.ts.map +1 -0
  4. package/dist/actions/clickOutside/clickOutside.js +62 -0
  5. package/dist/actions/clickOutside/clickOutside.js.map +1 -0
  6. package/dist/actions/clickOutside/handler.d.ts +11 -0
  7. package/dist/actions/clickOutside/handler.d.ts.map +1 -0
  8. package/dist/actions/clickOutside/handler.js +45 -0
  9. package/dist/actions/clickOutside/handler.js.map +1 -0
  10. package/dist/actions/clickOutside/index.d.ts +4 -0
  11. package/dist/actions/clickOutside/index.d.ts.map +1 -0
  12. package/dist/actions/clickOutside/index.js +4 -0
  13. package/dist/actions/clickOutside/index.js.map +1 -0
  14. package/dist/actions/clickOutside/types.d.ts +36 -0
  15. package/dist/actions/clickOutside/types.d.ts.map +1 -0
  16. package/dist/actions/clickOutside/types.js +6 -0
  17. package/dist/actions/clickOutside/types.js.map +1 -0
  18. package/dist/actions/clickOutside.d.ts +63 -0
  19. package/dist/actions/clickOutside.d.ts.map +1 -0
  20. package/dist/actions/clickOutside.js +106 -0
  21. package/dist/actions/clickOutside.js.map +1 -0
  22. package/dist/actions/escape/escape.d.ts +26 -0
  23. package/dist/actions/escape/escape.d.ts.map +1 -0
  24. package/dist/actions/escape/escape.js +74 -0
  25. package/dist/actions/escape/escape.js.map +1 -0
  26. package/dist/actions/escape/handler.d.ts +11 -0
  27. package/dist/actions/escape/handler.d.ts.map +1 -0
  28. package/dist/actions/escape/handler.js +50 -0
  29. package/dist/actions/escape/handler.js.map +1 -0
  30. package/dist/actions/escape/index.d.ts +4 -0
  31. package/dist/actions/escape/index.d.ts.map +1 -0
  32. package/dist/actions/escape/index.js +4 -0
  33. package/dist/actions/escape/index.js.map +1 -0
  34. package/dist/actions/escape/types.d.ts +70 -0
  35. package/dist/actions/escape/types.d.ts.map +1 -0
  36. package/dist/actions/escape/types.js +12 -0
  37. package/dist/actions/escape/types.js.map +1 -0
  38. package/dist/actions/escape.d.ts +99 -0
  39. package/dist/actions/escape.d.ts.map +1 -0
  40. package/dist/actions/escape.js +128 -0
  41. package/dist/actions/escape.js.map +1 -0
  42. package/dist/actions/focusTrap/focusTrap.d.ts +19 -0
  43. package/dist/actions/focusTrap/focusTrap.d.ts.map +1 -0
  44. package/dist/actions/focusTrap/focusTrap.js +198 -0
  45. package/dist/actions/focusTrap/focusTrap.js.map +1 -0
  46. package/dist/actions/focusTrap/focusables.d.ts +39 -0
  47. package/dist/actions/focusTrap/focusables.d.ts.map +1 -0
  48. package/dist/actions/focusTrap/focusables.js +142 -0
  49. package/dist/actions/focusTrap/focusables.js.map +1 -0
  50. package/dist/actions/focusTrap/index.d.ts +5 -0
  51. package/dist/actions/focusTrap/index.d.ts.map +1 -0
  52. package/dist/actions/focusTrap/index.js +5 -0
  53. package/dist/actions/focusTrap/index.js.map +1 -0
  54. package/dist/actions/focusTrap/trapStack.d.ts +33 -0
  55. package/dist/actions/focusTrap/trapStack.d.ts.map +1 -0
  56. package/dist/actions/focusTrap/trapStack.js +84 -0
  57. package/dist/actions/focusTrap/trapStack.js.map +1 -0
  58. package/dist/actions/focusTrap/types.d.ts +50 -0
  59. package/dist/actions/focusTrap/types.d.ts.map +1 -0
  60. package/dist/actions/focusTrap/types.js +10 -0
  61. package/dist/actions/focusTrap/types.js.map +1 -0
  62. package/dist/actions/focusTrap.d.ts +24 -0
  63. package/dist/actions/focusTrap.d.ts.map +1 -0
  64. package/dist/actions/focusTrap.js +201 -0
  65. package/dist/actions/focusTrap.js.map +1 -0
  66. package/dist/actions/focusTrap.types.d.ts +50 -0
  67. package/dist/actions/focusTrap.types.d.ts.map +1 -0
  68. package/dist/actions/focusTrap.types.js +10 -0
  69. package/dist/actions/focusTrap.types.js.map +1 -0
  70. package/dist/actions/focusables.d.ts +39 -0
  71. package/dist/actions/focusables.d.ts.map +1 -0
  72. package/dist/actions/focusables.js +142 -0
  73. package/dist/actions/focusables.js.map +1 -0
  74. package/dist/actions/index.d.ts +5 -0
  75. package/dist/actions/index.d.ts.map +1 -0
  76. package/dist/actions/index.js +5 -0
  77. package/dist/actions/index.js.map +1 -0
  78. package/dist/actions/layer/index.d.ts +4 -0
  79. package/dist/actions/layer/index.d.ts.map +1 -0
  80. package/dist/actions/layer/index.js +4 -0
  81. package/dist/actions/layer/index.js.map +1 -0
  82. package/dist/actions/layer/layer.d.ts +28 -0
  83. package/dist/actions/layer/layer.d.ts.map +1 -0
  84. package/dist/actions/layer/layer.js +118 -0
  85. package/dist/actions/layer/layer.js.map +1 -0
  86. package/dist/actions/layer/layerStack.d.ts +10 -0
  87. package/dist/actions/layer/layerStack.d.ts.map +1 -0
  88. package/dist/actions/layer/layerStack.js +153 -0
  89. package/dist/actions/layer/layerStack.js.map +1 -0
  90. package/dist/actions/layer/types.d.ts +105 -0
  91. package/dist/actions/layer/types.d.ts.map +1 -0
  92. package/dist/actions/layer/types.js +14 -0
  93. package/dist/actions/layer/types.js.map +1 -0
  94. package/dist/actions/layer.d.ts +133 -0
  95. package/dist/actions/layer.d.ts.map +1 -0
  96. package/dist/actions/layer.js +269 -0
  97. package/dist/actions/layer.js.map +1 -0
  98. package/dist/actions/trapStack.d.ts +33 -0
  99. package/dist/actions/trapStack.d.ts.map +1 -0
  100. package/dist/actions/trapStack.js +84 -0
  101. package/dist/actions/trapStack.js.map +1 -0
  102. package/dist/client.d.ts +179 -0
  103. package/dist/client.d.ts.map +1 -0
  104. package/dist/client.js +176 -0
  105. package/dist/client.js.map +1 -0
  106. package/dist/context/index.d.ts +68 -0
  107. package/dist/context/index.d.ts.map +1 -0
  108. package/dist/context/index.js +112 -0
  109. package/dist/context/index.js.map +1 -0
  110. package/dist/index.d.ts +22 -0
  111. package/dist/index.d.ts.map +1 -0
  112. package/dist/index.js +30 -0
  113. package/dist/index.js.map +1 -0
  114. package/dist/mock/index.d.ts +27 -0
  115. package/dist/mock/index.d.ts.map +1 -0
  116. package/dist/mock/index.js +137 -0
  117. package/dist/mock/index.js.map +1 -0
  118. package/dist/mock/interceptor.d.ts +19 -0
  119. package/dist/mock/interceptor.d.ts.map +1 -0
  120. package/dist/mock/interceptor.js +128 -0
  121. package/dist/mock/interceptor.js.map +1 -0
  122. package/dist/mock/types.d.ts +100 -0
  123. package/dist/mock/types.d.ts.map +1 -0
  124. package/dist/mock/types.js +27 -0
  125. package/dist/mock/types.js.map +1 -0
  126. package/dist/mock/url.d.ts +14 -0
  127. package/dist/mock/url.d.ts.map +1 -0
  128. package/dist/mock/url.js +38 -0
  129. package/dist/mock/url.js.map +1 -0
  130. package/dist/runes/clickOutside.svelte.d.ts +29 -0
  131. package/dist/runes/clickOutside.svelte.d.ts.map +1 -0
  132. package/dist/runes/clickOutside.svelte.js +42 -0
  133. package/dist/runes/clickOutside.svelte.js.map +1 -0
  134. package/dist/runes/escape.svelte.d.ts +34 -0
  135. package/dist/runes/escape.svelte.d.ts.map +1 -0
  136. package/dist/runes/escape.svelte.js +44 -0
  137. package/dist/runes/escape.svelte.js.map +1 -0
  138. package/dist/runes/focusTrap.svelte.d.ts +21 -0
  139. package/dist/runes/focusTrap.svelte.d.ts.map +1 -0
  140. package/dist/runes/focusTrap.svelte.js +37 -0
  141. package/dist/runes/focusTrap.svelte.js.map +1 -0
  142. package/dist/runes/index.svelte.d.ts +8 -0
  143. package/dist/runes/index.svelte.d.ts.map +1 -0
  144. package/dist/runes/index.svelte.js +8 -0
  145. package/dist/runes/index.svelte.js.map +1 -0
  146. package/dist/runes/layer.svelte.d.ts +36 -0
  147. package/dist/runes/layer.svelte.d.ts.map +1 -0
  148. package/dist/runes/layer.svelte.js +48 -0
  149. package/dist/runes/layer.svelte.js.map +1 -0
  150. package/dist/runes/query.svelte.d.ts +104 -0
  151. package/dist/runes/query.svelte.d.ts.map +1 -0
  152. package/dist/runes/query.svelte.js +179 -0
  153. package/dist/runes/query.svelte.js.map +1 -0
  154. package/dist/runes/state.svelte.d.ts +72 -0
  155. package/dist/runes/state.svelte.d.ts.map +1 -0
  156. package/dist/runes/state.svelte.js +98 -0
  157. package/dist/runes/state.svelte.js.map +1 -0
  158. package/dist/runes/window.svelte.d.ts +120 -0
  159. package/dist/runes/window.svelte.d.ts.map +1 -0
  160. package/dist/runes/window.svelte.js +196 -0
  161. package/dist/runes/window.svelte.js.map +1 -0
  162. package/dist/types/index.d.ts +2 -0
  163. package/dist/types/index.d.ts.map +1 -0
  164. package/dist/types/index.js +3 -0
  165. package/dist/types/index.js.map +1 -0
  166. package/dist/types/nui.d.ts +34 -0
  167. package/dist/types/nui.d.ts.map +1 -0
  168. package/dist/types/nui.js +17 -0
  169. package/dist/types/nui.js.map +1 -0
  170. package/dist/utils/env.d.ts +21 -0
  171. package/dist/utils/env.d.ts.map +1 -0
  172. package/dist/utils/env.js +36 -0
  173. package/dist/utils/env.js.map +1 -0
  174. package/dist/utils/error.d.ts +11 -0
  175. package/dist/utils/error.d.ts.map +1 -0
  176. package/dist/utils/error.js +30 -0
  177. package/dist/utils/error.js.map +1 -0
  178. package/dist/utils/index.d.ts +3 -0
  179. package/dist/utils/index.d.ts.map +1 -0
  180. package/dist/utils/index.js +3 -0
  181. package/dist/utils/index.js.map +1 -0
  182. package/dist/utils/keyboard.d.ts +127 -0
  183. package/dist/utils/keyboard.d.ts.map +1 -0
  184. package/dist/utils/keyboard.js +152 -0
  185. package/dist/utils/keyboard.js.map +1 -0
  186. package/package.json +76 -4
  187. package/src/actions/clickOutside/clickOutside.ts +78 -0
  188. package/src/actions/clickOutside/handler.ts +49 -0
  189. package/src/actions/clickOutside/index.ts +11 -0
  190. package/src/actions/clickOutside/types.ts +54 -0
  191. package/src/actions/escape/escape.ts +90 -0
  192. package/src/actions/escape/handler.ts +52 -0
  193. package/src/actions/escape/index.ts +11 -0
  194. package/src/actions/escape/types.ts +88 -0
  195. package/src/actions/focusTrap/focusTrap.ts +228 -0
  196. package/src/actions/focusTrap/focusables.ts +155 -0
  197. package/src/actions/focusTrap/index.ts +22 -0
  198. package/src/actions/focusTrap/trapStack.ts +109 -0
  199. package/src/actions/focusTrap/types.ts +64 -0
  200. package/src/actions/index.ts +35 -0
  201. package/src/actions/layer/index.ts +13 -0
  202. package/src/actions/layer/layer.ts +140 -0
  203. package/src/actions/layer/layerStack.ts +169 -0
  204. package/src/actions/layer/types.ts +132 -0
  205. package/src/client.ts +323 -0
  206. package/src/context/index.ts +127 -0
  207. package/src/index.ts +53 -0
  208. package/src/mock/index.ts +176 -0
  209. package/src/mock/interceptor.ts +177 -0
  210. package/src/mock/types.ts +125 -0
  211. package/src/mock/url.ts +37 -0
  212. package/src/runes/clickOutside.svelte.ts +53 -0
  213. package/src/runes/escape.svelte.ts +71 -0
  214. package/src/runes/focusTrap.svelte.ts +56 -0
  215. package/src/runes/index.svelte.ts +38 -0
  216. package/src/runes/layer.svelte.ts +74 -0
  217. package/src/runes/query.svelte.ts +313 -0
  218. package/src/runes/state.svelte.ts +175 -0
  219. package/src/runes/window.svelte.ts +308 -0
  220. package/src/utils/env.ts +36 -0
  221. package/src/utils/index.ts +2 -0
  222. package/src/utils/keyboard.ts +244 -0
@@ -0,0 +1,106 @@
1
+ export function resolveElement(target) {
2
+ if (typeof target === 'string') {
3
+ return document.querySelector(target);
4
+ }
5
+ if (typeof target === 'function') {
6
+ return target() ?? null;
7
+ }
8
+ return target ?? null;
9
+ }
10
+ /**
11
+ * Creates a reusable pointerdown event handler for outside click detection.
12
+ * Shared between Svelte action and composable to eliminate duplication.
13
+ */
14
+ export function createClickOutsideHandler(getNode, getOptions) {
15
+ return (event) => {
16
+ const node = getNode();
17
+ const opts = getOptions();
18
+ if (!node?.isConnected || !opts)
19
+ return;
20
+ const enabled = typeof opts.enabled === 'function' ? opts.enabled() : opts.enabled ?? true;
21
+ if (!enabled || event.button !== 0)
22
+ return;
23
+ const path = event.composedPath();
24
+ if (path.includes(node))
25
+ return;
26
+ for (const item of opts.ignore ?? []) {
27
+ const el = resolveElement(item);
28
+ if (el && path.includes(el))
29
+ return;
30
+ }
31
+ const callback = opts.onClickOutside ?? opts.callback;
32
+ if (callback) {
33
+ try {
34
+ callback(event);
35
+ }
36
+ catch (err) {
37
+ console.error('[NuiClickOutside] Error in onClickOutside callback:', err);
38
+ }
39
+ }
40
+ };
41
+ }
42
+ /**
43
+ * Svelte Action for reliable outside click detection in FiveM CEF.
44
+ *
45
+ * Uses `event.composedPath()` to eliminate the detached DOM node bug when
46
+ * inner elements mutate during click events.
47
+ *
48
+ * @remarks
49
+ * **Multiple Overlays in CEF**:
50
+ * Because `pointerdown` is used with `capture: true`, clicking into another open NUI panel
51
+ * will trigger an outside click unless that other panel is explicitly included in `ignore`.
52
+ *
53
+ * @param node - The DOM element to monitor for outside clicks.
54
+ * @param parameter - Callback function or configuration options object.
55
+ *
56
+ * @example
57
+ * ```svelte
58
+ * <div use:nuiClickOutside={() => (isOpen = false)}>...</div>
59
+ * <div use:nuiClickOutside={{ onClickOutside: close, ignore: [triggerEl, '.header-btn'] }}>...</div>
60
+ * ```
61
+ */
62
+ export const nuiClickOutside = (node, parameter) => {
63
+ let currentParam = parameter;
64
+ let controller = null;
65
+ const toOptions = (param) => {
66
+ if (!param)
67
+ return undefined;
68
+ return typeof param === 'function' ? { onClickOutside: param } : param;
69
+ };
70
+ const handler = createClickOutsideHandler(() => node, () => toOptions(currentParam));
71
+ function getCapture() {
72
+ const opts = toOptions(currentParam);
73
+ return opts?.capture ?? true;
74
+ }
75
+ function setup() {
76
+ teardown();
77
+ controller = new AbortController();
78
+ document.addEventListener('pointerdown', handler, {
79
+ capture: getCapture(),
80
+ signal: controller.signal
81
+ });
82
+ }
83
+ function teardown() {
84
+ if (controller) {
85
+ controller.abort();
86
+ controller = null;
87
+ }
88
+ }
89
+ setup();
90
+ return {
91
+ update(newParam) {
92
+ const prevCapture = getCapture();
93
+ currentParam = newParam;
94
+ // Only changes to `capture` require re-binding the document listener.
95
+ // All other options (`enabled`, `onClickOutside`, `ignore`)
96
+ // are read lazily on each pointerdown event via getOptions().
97
+ if (getCapture() !== prevCapture) {
98
+ setup();
99
+ }
100
+ },
101
+ destroy() {
102
+ teardown();
103
+ }
104
+ };
105
+ };
106
+ //# sourceMappingURL=clickOutside.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clickOutside.js","sourceRoot":"","sources":["../../src/actions/clickOutside.ts"],"names":[],"mappings":"AAkDA,MAAM,UAAU,cAAc,CAAC,MAA0B;IACvD,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC/B,OAAO,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IACxC,CAAC;IACD,IAAI,OAAO,MAAM,KAAK,UAAU,EAAE,CAAC;QACjC,OAAO,MAAM,EAAE,IAAI,IAAI,CAAC;IAC1B,CAAC;IACD,OAAO,MAAM,IAAI,IAAI,CAAC;AACxB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CACvC,OAAyC,EACzC,UAAsD;IAEtD,OAAO,CAAC,KAAmB,EAAE,EAAE;QAC7B,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;QAC1B,IAAI,CAAC,IAAI,EAAE,WAAW,IAAI,CAAC,IAAI;YAAE,OAAO;QAExC,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC;QAC3F,IAAI,CAAC,OAAO,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAE3C,MAAM,IAAI,GAAG,KAAK,CAAC,YAAY,EAAE,CAAC;QAClC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,OAAO;QAEhC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;YACrC,MAAM,EAAE,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;YAChC,IAAI,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAAE,OAAO;QACtC,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,QAAQ,CAAC;QACtD,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,CAAC;gBACH,QAAQ,CAAC,KAAK,CAAC,CAAC;YAClB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,qDAAqD,EAAE,GAAG,CAAC,CAAC;YAC5E,CAAC;QACH,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,MAAM,eAAe,GAA8C,CACxE,IAAI,EACJ,SAAS,EACT,EAAE;IACF,IAAI,YAAY,GAAG,SAAS,CAAC;IAC7B,IAAI,UAAU,GAA2B,IAAI,CAAC;IAE9C,MAAM,SAAS,GAAG,CAAC,KAAuC,EAAwC,EAAE;QAClG,IAAI,CAAC,KAAK;YAAE,OAAO,SAAS,CAAC;QAC7B,OAAO,OAAO,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;IACzE,CAAC,CAAC;IAEF,MAAM,OAAO,GAAG,yBAAyB,CACvC,GAAG,EAAE,CAAC,IAAI,EACV,GAAG,EAAE,CAAC,SAAS,CAAC,YAAY,CAAC,CAC9B,CAAC;IAEF,SAAS,UAAU;QACjB,MAAM,IAAI,GAAG,SAAS,CAAC,YAAY,CAAC,CAAC;QACrC,OAAO,IAAI,EAAE,OAAO,IAAI,IAAI,CAAC;IAC/B,CAAC;IAED,SAAS,KAAK;QACZ,QAAQ,EAAE,CAAC;QACX,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACnC,QAAQ,CAAC,gBAAgB,CAAC,aAAa,EAAE,OAAO,EAAE;YAChD,OAAO,EAAE,UAAU,EAAE;YACrB,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAC;IACL,CAAC;IAED,SAAS,QAAQ;QACf,IAAI,UAAU,EAAE,CAAC;YACf,UAAU,CAAC,KAAK,EAAE,CAAC;YACnB,UAAU,GAAG,IAAI,CAAC;QACpB,CAAC;IACH,CAAC;IAED,KAAK,EAAE,CAAC;IAER,OAAO;QACL,MAAM,CAAC,QAAQ;YACb,MAAM,WAAW,GAAG,UAAU,EAAE,CAAC;YACjC,YAAY,GAAG,QAAQ,CAAC;YACxB,sEAAsE;YACtE,4DAA4D;YAC5D,8DAA8D;YAC9D,IAAI,UAAU,EAAE,KAAK,WAAW,EAAE,CAAC;gBACjC,KAAK,EAAE,CAAC;YACV,CAAC;QACH,CAAC;QACD,OAAO;YACL,QAAQ,EAAE,CAAC;QACb,CAAC;KACF,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,26 @@
1
+ import type { Action } from 'svelte/action';
2
+ import { type NuiEscapeInput } from './types';
3
+ /**
4
+ * Svelte Action for handling Escape key presses in FiveM NUI.
5
+ *
6
+ * Designed specifically for FiveM NUI where Escape is the universal close/dismiss action.
7
+ * Defaults to listening on `document` so closing works without requiring focus on a specific `div`.
8
+ *
9
+ * @remarks
10
+ * **Nested Modals & Target Scoping**:
11
+ * When using `target: NuiEscapeTarget.Document` (default), all registered listeners on `document` will receive
12
+ * keydown in registration order. If you have nested modals where only the topmost modal should close,
13
+ * consider using `nuiLayer` (which manages a centralized layer stack so only the topmost active layer dismisses),
14
+ * or configure `ignoreHandled: true`, or use `target: NuiEscapeTarget.Element` with a focus trap (`nuiFocusTrap`) and `stopPropagation: true`.
15
+ *
16
+ * @param node - The DOM element attaching the action.
17
+ * @param parameter - Callback function or configuration options object.
18
+ *
19
+ * @example
20
+ * ```svelte
21
+ * <div use:nuiEscapeAction={() => win.close()}>...</div>
22
+ * <div use:nuiEscapeAction={{ onEscape: close, stopPropagation: true }}>...</div>
23
+ * ```
24
+ */
25
+ export declare const nuiEscapeAction: Action<HTMLElement, NuiEscapeInput>;
26
+ //# sourceMappingURL=escape.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escape.d.ts","sourceRoot":"","sources":["../../../src/actions/escape/escape.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EACL,KAAK,cAAc,EAEpB,MAAM,SAAS,CAAC;AAGjB;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,WAAW,EAAE,cAAc,CA4D/D,CAAC"}
@@ -0,0 +1,74 @@
1
+ import { toEscapeOptions } from './types';
2
+ import { resolveTarget, createEscapeHandler } from './handler';
3
+ /**
4
+ * Svelte Action for handling Escape key presses in FiveM NUI.
5
+ *
6
+ * Designed specifically for FiveM NUI where Escape is the universal close/dismiss action.
7
+ * Defaults to listening on `document` so closing works without requiring focus on a specific `div`.
8
+ *
9
+ * @remarks
10
+ * **Nested Modals & Target Scoping**:
11
+ * When using `target: NuiEscapeTarget.Document` (default), all registered listeners on `document` will receive
12
+ * keydown in registration order. If you have nested modals where only the topmost modal should close,
13
+ * consider using `nuiLayer` (which manages a centralized layer stack so only the topmost active layer dismisses),
14
+ * or configure `ignoreHandled: true`, or use `target: NuiEscapeTarget.Element` with a focus trap (`nuiFocusTrap`) and `stopPropagation: true`.
15
+ *
16
+ * @param node - The DOM element attaching the action.
17
+ * @param parameter - Callback function or configuration options object.
18
+ *
19
+ * @example
20
+ * ```svelte
21
+ * <div use:nuiEscapeAction={() => win.close()}>...</div>
22
+ * <div use:nuiEscapeAction={{ onEscape: close, stopPropagation: true }}>...</div>
23
+ * ```
24
+ */
25
+ export const nuiEscapeAction = (node, parameter) => {
26
+ let currentParam = parameter;
27
+ let controller = null;
28
+ const handler = createEscapeHandler(() => toEscapeOptions(currentParam));
29
+ function getTarget() {
30
+ const opts = toEscapeOptions(currentParam);
31
+ return resolveTarget(node, opts?.target);
32
+ }
33
+ function getCapture() {
34
+ const opts = toEscapeOptions(currentParam);
35
+ return opts?.capture ?? false;
36
+ }
37
+ function setup() {
38
+ teardown();
39
+ const eventTarget = getTarget();
40
+ controller = new AbortController();
41
+ eventTarget.addEventListener('keydown', handler, {
42
+ capture: getCapture(),
43
+ signal: controller.signal
44
+ });
45
+ }
46
+ function teardown() {
47
+ if (controller) {
48
+ controller.abort();
49
+ controller = null;
50
+ }
51
+ }
52
+ setup();
53
+ return {
54
+ update(newParam) {
55
+ const prevOpts = toEscapeOptions(currentParam);
56
+ currentParam = newParam;
57
+ const nextOpts = toEscapeOptions(currentParam);
58
+ const prevTarget = resolveTarget(node, prevOpts?.target);
59
+ const nextTarget = resolveTarget(node, nextOpts?.target);
60
+ const prevCapture = prevOpts?.capture ?? false;
61
+ const nextCapture = nextOpts?.capture ?? false;
62
+ // Only target or capture changes require re-binding DOM listeners.
63
+ // Other options (enabled, onEscape, preventDefault, stopPropagation, ignoreRepeat)
64
+ // are read dynamically inside createEscapeHandler on each keydown event.
65
+ if (prevTarget !== nextTarget || prevCapture !== nextCapture) {
66
+ setup();
67
+ }
68
+ },
69
+ destroy() {
70
+ teardown();
71
+ }
72
+ };
73
+ };
74
+ //# sourceMappingURL=escape.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escape.js","sourceRoot":"","sources":["../../../src/actions/escape/escape.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,eAAe,EAChB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAE/D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,MAAM,eAAe,GAAwC,CAClE,IAAI,EACJ,SAAS,EACT,EAAE;IACF,IAAI,YAAY,GAAG,SAAS,CAAC;IAC7B,IAAI,UAAU,GAA2B,IAAI,CAAC;IAE9C,MAAM,OAAO,GAAG,mBAAmB,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC,CAAC;IAEzE,SAAS,SAAS;QAChB,MAAM,IAAI,GAAG,eAAe,CAAC,YAAY,CAAC,CAAC;QAC3C,OAAO,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IAC3C,CAAC;IAED,SAAS,UAAU;QACjB,MAAM,IAAI,GAAG,eAAe,CAAC,YAAY,CAAC,CAAC;QAC3C,OAAO,IAAI,EAAE,OAAO,IAAI,KAAK,CAAC;IAChC,CAAC;IAED,SAAS,KAAK;QACZ,QAAQ,EAAE,CAAC;QACX,MAAM,WAAW,GAAG,SAAS,EAAE,CAAC;QAChC,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACnC,WAAW,CAAC,gBAAgB,CAAC,SAAS,EAAE,OAAwB,EAAE;YAChE,OAAO,EAAE,UAAU,EAAE;YACrB,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAC;IACL,CAAC;IAED,SAAS,QAAQ;QACf,IAAI,UAAU,EAAE,CAAC;YACf,UAAU,CAAC,KAAK,EAAE,CAAC;YACnB,UAAU,GAAG,IAAI,CAAC;QACpB,CAAC;IACH,CAAC;IAED,KAAK,EAAE,CAAC;IAER,OAAO;QACL,MAAM,CAAC,QAAQ;YACb,MAAM,QAAQ,GAAG,eAAe,CAAC,YAAY,CAAC,CAAC;YAC/C,YAAY,GAAG,QAAQ,CAAC;YACxB,MAAM,QAAQ,GAAG,eAAe,CAAC,YAAY,CAAC,CAAC;YAE/C,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;YACzD,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;YACzD,MAAM,WAAW,GAAG,QAAQ,EAAE,OAAO,IAAI,KAAK,CAAC;YAC/C,MAAM,WAAW,GAAG,QAAQ,EAAE,OAAO,IAAI,KAAK,CAAC;YAE/C,mEAAmE;YACnE,mFAAmF;YACnF,yEAAyE;YACzE,IAAI,UAAU,KAAK,UAAU,IAAI,WAAW,KAAK,WAAW,EAAE,CAAC;gBAC7D,KAAK,EAAE,CAAC;YACV,CAAC;QACH,CAAC;QACD,OAAO;YACL,QAAQ,EAAE,CAAC;QACb,CAAC;KACF,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { NuiEscapeTarget, type NuiEscapeParameter } from './types';
2
+ /**
3
+ * Resolves the appropriate EventTarget to attach the Escape key listener to.
4
+ */
5
+ export declare function resolveTarget(node: HTMLElement, targetType?: NuiEscapeTarget): EventTarget;
6
+ /**
7
+ * Creates a reusable keydown event handler for Escape key detection.
8
+ * Shared between Svelte action and composable to eliminate duplication.
9
+ */
10
+ export declare function createEscapeHandler(getOptions: () => NuiEscapeParameter | undefined): (event: KeyboardEvent) => void;
11
+ //# sourceMappingURL=handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/actions/escape/handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,KAAK,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAEnE;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,EAAE,UAAU,CAAC,EAAE,eAAe,GAAG,WAAW,CAI1F;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,MAAM,kBAAkB,GAAG,SAAS,WAEjC,aAAa,UAiC7B"}
@@ -0,0 +1,50 @@
1
+ import { NuiEscapeTarget } from './types';
2
+ /**
3
+ * Resolves the appropriate EventTarget to attach the Escape key listener to.
4
+ */
5
+ export function resolveTarget(node, targetType) {
6
+ if (targetType === NuiEscapeTarget.Window)
7
+ return window;
8
+ if (targetType === NuiEscapeTarget.Element)
9
+ return node;
10
+ return document;
11
+ }
12
+ /**
13
+ * Creates a reusable keydown event handler for Escape key detection.
14
+ * Shared between Svelte action and composable to eliminate duplication.
15
+ */
16
+ export function createEscapeHandler(getOptions) {
17
+ return (event) => {
18
+ const opts = getOptions();
19
+ if (!opts || event.key !== 'Escape' || event.isComposing)
20
+ return;
21
+ if (opts.ignoreHandled && event.defaultPrevented)
22
+ return;
23
+ const enabled = typeof opts.enabled === 'function' ? opts.enabled() : opts.enabled ?? true;
24
+ const handler = opts.onEscape ?? opts.callback;
25
+ if (!enabled || !handler)
26
+ return;
27
+ if ((opts.ignoreRepeat ?? true) && event.repeat) {
28
+ if (opts.preventDefault ?? true) {
29
+ event.preventDefault();
30
+ }
31
+ return;
32
+ }
33
+ if (opts.preventDefault ?? true) {
34
+ event.preventDefault();
35
+ }
36
+ if (opts.stopPropagation) {
37
+ event.stopPropagation();
38
+ }
39
+ if (opts.stopImmediatePropagation) {
40
+ event.stopImmediatePropagation();
41
+ }
42
+ try {
43
+ handler(event);
44
+ }
45
+ catch (err) {
46
+ console.error('[NuiEscape] Error in onEscape callback:', err);
47
+ }
48
+ };
49
+ }
50
+ //# sourceMappingURL=handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.js","sourceRoot":"","sources":["../../../src/actions/escape/handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAA2B,MAAM,SAAS,CAAC;AAEnE;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,IAAiB,EAAE,UAA4B;IAC3E,IAAI,UAAU,KAAK,eAAe,CAAC,MAAM;QAAE,OAAO,MAAM,CAAC;IACzD,IAAI,UAAU,KAAK,eAAe,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IACxD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CACjC,UAAgD;IAEhD,OAAO,CAAC,KAAoB,EAAE,EAAE;QAC9B,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;QAC1B,IAAI,CAAC,IAAI,IAAI,KAAK,CAAC,GAAG,KAAK,QAAQ,IAAI,KAAK,CAAC,WAAW;YAAE,OAAO;QAEjE,IAAI,IAAI,CAAC,aAAa,IAAI,KAAK,CAAC,gBAAgB;YAAE,OAAO;QAEzD,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC;QAC3F,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC;QAC/C,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO;YAAE,OAAO;QAEjC,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YAChD,IAAI,IAAI,CAAC,cAAc,IAAI,IAAI,EAAE,CAAC;gBAChC,KAAK,CAAC,cAAc,EAAE,CAAC;YACzB,CAAC;YACD,OAAO;QACT,CAAC;QAED,IAAI,IAAI,CAAC,cAAc,IAAI,IAAI,EAAE,CAAC;YAChC,KAAK,CAAC,cAAc,EAAE,CAAC;QACzB,CAAC;QACD,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,KAAK,CAAC,eAAe,EAAE,CAAC;QAC1B,CAAC;QACD,IAAI,IAAI,CAAC,wBAAwB,EAAE,CAAC;YAClC,KAAK,CAAC,wBAAwB,EAAE,CAAC;QACnC,CAAC;QAED,IAAI,CAAC;YACH,OAAO,CAAC,KAAK,CAAC,CAAC;QACjB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,yCAAyC,EAAE,GAAG,CAAC,CAAC;QAChE,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { nuiEscapeAction } from './escape';
2
+ export { NuiEscapeTarget, type NuiEscapeParameter, type NuiEscapeInput, toEscapeOptions } from './types';
3
+ export { resolveTarget, createEscapeHandler } from './handler';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/actions/escape/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EACL,eAAe,EACf,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,eAAe,EAChB,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,aAAa,EACb,mBAAmB,EACpB,MAAM,WAAW,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { nuiEscapeAction } from './escape';
2
+ export { NuiEscapeTarget, toEscapeOptions } from './types';
3
+ export { resolveTarget, createEscapeHandler } from './handler';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/actions/escape/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EACL,eAAe,EAGf,eAAe,EAChB,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,aAAa,EACb,mBAAmB,EACpB,MAAM,WAAW,CAAC"}
@@ -0,0 +1,70 @@
1
+ export declare enum NuiEscapeTarget {
2
+ Element = "element",
3
+ Window = "window",
4
+ Document = "document"
5
+ }
6
+ export interface NuiEscapeParameter {
7
+ /**
8
+ * Callback invoked when the Escape key is pressed.
9
+ */
10
+ onEscape?: (event: KeyboardEvent) => void;
11
+ /**
12
+ * @deprecated Use `onEscape` instead.
13
+ */
14
+ callback?: (event: KeyboardEvent) => void;
15
+ /**
16
+ * Whether the Escape listener is active.
17
+ * Accepts a boolean or a reactive getter `() => boolean`.
18
+ * @defaultValue true
19
+ */
20
+ enabled?: boolean | (() => boolean);
21
+ /**
22
+ * Where to attach the listener:
23
+ * - `NuiEscapeTarget.Document`: listens globally on the document (default; does not require element focus)
24
+ * - `NuiEscapeTarget.Window`: listens globally on the window
25
+ * - `NuiEscapeTarget.Element`: listens only for keydown events within this element (requires the element to have focus, e.g. tabindex="-1")
26
+ * @defaultValue NuiEscapeTarget.Document
27
+ */
28
+ target?: NuiEscapeTarget;
29
+ /**
30
+ * Whether to listen during the capturing phase.
31
+ * Useful when inner elements might stop propagation before bubbling reaches window/document.
32
+ * @defaultValue false
33
+ */
34
+ capture?: boolean;
35
+ /**
36
+ * Whether to call `event.preventDefault()`.
37
+ * @defaultValue true
38
+ */
39
+ preventDefault?: boolean;
40
+ /**
41
+ * Whether to stop event propagation to parent elements.
42
+ * @defaultValue false
43
+ */
44
+ stopPropagation?: boolean;
45
+ /**
46
+ * Whether to stop immediate propagation to other listeners on the same target.
47
+ * @defaultValue false
48
+ */
49
+ stopImmediatePropagation?: boolean;
50
+ /**
51
+ * Whether to ignore repeated keydown events fired while holding the Escape key down.
52
+ *
53
+ * @remarks
54
+ * If `true` and `preventDefault: true`, `event.preventDefault()` will still be invoked
55
+ * on repeated keydown events to suppress browser/CEF default actions, but the callback itself
56
+ * will only be executed once on the initial keydown.
57
+ * @defaultValue true
58
+ */
59
+ ignoreRepeat?: boolean;
60
+ /**
61
+ * Whether to ignore the Escape keydown event if an earlier handler has already called `event.preventDefault()`.
62
+ * Useful when multiple independent Escape listeners are registered on `document` to avoid firing handlers
63
+ * after an inner/prior component has already handled the event.
64
+ * @defaultValue false
65
+ */
66
+ ignoreHandled?: boolean;
67
+ }
68
+ export type NuiEscapeInput = ((event: KeyboardEvent) => void) | NuiEscapeParameter;
69
+ export declare function toEscapeOptions<T extends NuiEscapeParameter = NuiEscapeParameter>(param?: ((event: KeyboardEvent) => void) | T): T | undefined;
70
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/actions/escape/types.ts"],"names":[],"mappings":"AAAA,oBAAY,eAAe;IACzB,OAAO,YAAY;IACnB,MAAM,WAAW;IACjB,QAAQ,aAAa;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAE1C;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAE1C;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC,CAAC;IAEpC;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC;IAEzB;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B;;;OAGG;IACH,wBAAwB,CAAC,EAAE,OAAO,CAAC;IAEnC;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,MAAM,cAAc,GACtB,CAAC,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC,GAChC,kBAAkB,CAAC;AAEvB,wBAAgB,eAAe,CAAC,CAAC,SAAS,kBAAkB,GAAG,kBAAkB,EAC/E,KAAK,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC,GAAG,CAAC,GAC3C,CAAC,GAAG,SAAS,CAGf"}
@@ -0,0 +1,12 @@
1
+ export var NuiEscapeTarget;
2
+ (function (NuiEscapeTarget) {
3
+ NuiEscapeTarget["Element"] = "element";
4
+ NuiEscapeTarget["Window"] = "window";
5
+ NuiEscapeTarget["Document"] = "document";
6
+ })(NuiEscapeTarget || (NuiEscapeTarget = {}));
7
+ export function toEscapeOptions(param) {
8
+ if (!param)
9
+ return undefined;
10
+ return typeof param === 'function' ? { onEscape: param } : param;
11
+ }
12
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/actions/escape/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,eAIX;AAJD,WAAY,eAAe;IACzB,sCAAmB,CAAA;IACnB,oCAAiB,CAAA;IACjB,wCAAqB,CAAA;AACvB,CAAC,EAJW,eAAe,KAAf,eAAe,QAI1B;AA8ED,MAAM,UAAU,eAAe,CAC7B,KAA4C;IAE5C,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,OAAO,OAAO,KAAK,KAAK,UAAU,CAAC,CAAC,CAAE,EAAE,QAAQ,EAAE,KAAK,EAAQ,CAAC,CAAC,CAAC,KAAK,CAAC;AAC1E,CAAC"}
@@ -0,0 +1,99 @@
1
+ import type { Action } from 'svelte/action';
2
+ export declare enum NuiEscapeTarget {
3
+ Element = "element",
4
+ Window = "window",
5
+ Document = "document"
6
+ }
7
+ export interface NuiEscapeParameter {
8
+ /**
9
+ * Callback invoked when the Escape key is pressed.
10
+ */
11
+ onEscape?: (event: KeyboardEvent) => void;
12
+ /**
13
+ * @deprecated Use `onEscape` instead.
14
+ */
15
+ callback?: (event: KeyboardEvent) => void;
16
+ /**
17
+ * Whether the Escape listener is active.
18
+ * Accepts a boolean or a reactive getter `() => boolean`.
19
+ * @defaultValue true
20
+ */
21
+ enabled?: boolean | (() => boolean);
22
+ /**
23
+ * Where to attach the listener:
24
+ * - `NuiEscapeTarget.Document`: listens globally on the document (default; does not require element focus)
25
+ * - `NuiEscapeTarget.Window`: listens globally on the window
26
+ * - `NuiEscapeTarget.Element`: listens only for keydown events within this element (requires the element to have focus, e.g. tabindex="-1")
27
+ * @defaultValue NuiEscapeTarget.Document
28
+ */
29
+ target?: NuiEscapeTarget;
30
+ /**
31
+ * Whether to listen during the capturing phase.
32
+ * Useful when inner elements might stop propagation before bubbling reaches window/document.
33
+ * @defaultValue false
34
+ */
35
+ capture?: boolean;
36
+ /**
37
+ * Whether to call `event.preventDefault()`.
38
+ * @defaultValue true
39
+ */
40
+ preventDefault?: boolean;
41
+ /**
42
+ * Whether to stop event propagation to parent elements.
43
+ * @defaultValue false
44
+ */
45
+ stopPropagation?: boolean;
46
+ /**
47
+ * Whether to stop immediate propagation to other listeners on the same target.
48
+ * @defaultValue false
49
+ */
50
+ stopImmediatePropagation?: boolean;
51
+ /**
52
+ * Whether to ignore repeated keydown events fired while holding the Escape key down.
53
+ *
54
+ * @remarks
55
+ * If `true` and `preventDefault: true`, `event.preventDefault()` will still be invoked
56
+ * on repeated keydown events to suppress browser/CEF default actions, but the callback itself
57
+ * will only be executed once on the initial keydown.
58
+ * @defaultValue true
59
+ */
60
+ ignoreRepeat?: boolean;
61
+ /**
62
+ * Whether to ignore the Escape keydown event if an earlier handler has already called `event.preventDefault()`.
63
+ * Useful when multiple independent Escape listeners are registered on `document` to avoid firing handlers
64
+ * after an inner/prior component has already handled the event.
65
+ * @defaultValue false
66
+ */
67
+ ignoreHandled?: boolean;
68
+ }
69
+ export type NuiEscapeInput = ((event: KeyboardEvent) => void) | NuiEscapeParameter;
70
+ export declare function toEscapeOptions<T extends NuiEscapeParameter = NuiEscapeParameter>(param?: ((event: KeyboardEvent) => void) | T): T | undefined;
71
+ /**
72
+ * Creates a reusable keydown event handler for Escape key detection.
73
+ * Shared between Svelte action and composable to eliminate duplication.
74
+ */
75
+ export declare function createEscapeHandler(getOptions: () => NuiEscapeParameter | undefined): (event: KeyboardEvent) => void;
76
+ /**
77
+ * Svelte Action for handling Escape key presses in FiveM NUI.
78
+ *
79
+ * Designed specifically for FiveM NUI where Escape is the universal close/dismiss action.
80
+ * Defaults to listening on `document` so closing works without requiring focus on a specific `div`.
81
+ *
82
+ * @remarks
83
+ * **Nested Modals & Target Scoping**:
84
+ * When using `target: NuiEscapeTarget.Document` (default), all registered listeners on `document` will receive
85
+ * keydown in registration order. If you have nested modals where only the topmost modal should close,
86
+ * consider using `nuiLayer` (which manages a centralized layer stack so only the topmost active layer dismisses),
87
+ * or configure `ignoreHandled: true`, or use `target: NuiEscapeTarget.Element` with a focus trap (`nuiFocusTrap`) and `stopPropagation: true`.
88
+ *
89
+ * @param node - The DOM element attaching the action.
90
+ * @param parameter - Callback function or configuration options object.
91
+ *
92
+ * @example
93
+ * ```svelte
94
+ * <div use:nuiEscapeAction={() => win.close()}>...</div>
95
+ * <div use:nuiEscapeAction={{ onEscape: close, stopPropagation: true }}>...</div>
96
+ * ```
97
+ */
98
+ export declare const nuiEscapeAction: Action<HTMLElement, NuiEscapeInput>;
99
+ //# sourceMappingURL=escape.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escape.d.ts","sourceRoot":"","sources":["../../src/actions/escape.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C,oBAAY,eAAe;IACzB,OAAO,YAAY;IACnB,MAAM,WAAW;IACjB,QAAQ,aAAa;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAE1C;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAE1C;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC,CAAC;IAEpC;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC;IAEzB;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B;;;OAGG;IACH,wBAAwB,CAAC,EAAE,OAAO,CAAC;IAEnC;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,MAAM,cAAc,GACtB,CAAC,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC,GAChC,kBAAkB,CAAC;AAEvB,wBAAgB,eAAe,CAAC,CAAC,SAAS,kBAAkB,GAAG,kBAAkB,EAC/E,KAAK,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC,GAAG,CAAC,GAC3C,CAAC,GAAG,SAAS,CAGf;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,MAAM,kBAAkB,GAAG,SAAS,WAEjC,aAAa,UAiC7B;AAQD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,WAAW,EAAE,cAAc,CA4D/D,CAAC"}
@@ -0,0 +1,128 @@
1
+ export var NuiEscapeTarget;
2
+ (function (NuiEscapeTarget) {
3
+ NuiEscapeTarget["Element"] = "element";
4
+ NuiEscapeTarget["Window"] = "window";
5
+ NuiEscapeTarget["Document"] = "document";
6
+ })(NuiEscapeTarget || (NuiEscapeTarget = {}));
7
+ export function toEscapeOptions(param) {
8
+ if (!param)
9
+ return undefined;
10
+ return typeof param === 'function' ? { onEscape: param } : param;
11
+ }
12
+ /**
13
+ * Creates a reusable keydown event handler for Escape key detection.
14
+ * Shared between Svelte action and composable to eliminate duplication.
15
+ */
16
+ export function createEscapeHandler(getOptions) {
17
+ return (event) => {
18
+ const opts = getOptions();
19
+ if (!opts || event.key !== 'Escape' || event.isComposing)
20
+ return;
21
+ if (opts.ignoreHandled && event.defaultPrevented)
22
+ return;
23
+ const enabled = typeof opts.enabled === 'function' ? opts.enabled() : opts.enabled ?? true;
24
+ const handler = opts.onEscape ?? opts.callback;
25
+ if (!enabled || !handler)
26
+ return;
27
+ if ((opts.ignoreRepeat ?? true) && event.repeat) {
28
+ if (opts.preventDefault ?? true) {
29
+ event.preventDefault();
30
+ }
31
+ return;
32
+ }
33
+ if (opts.preventDefault ?? true) {
34
+ event.preventDefault();
35
+ }
36
+ if (opts.stopPropagation) {
37
+ event.stopPropagation();
38
+ }
39
+ if (opts.stopImmediatePropagation) {
40
+ event.stopImmediatePropagation();
41
+ }
42
+ try {
43
+ handler(event);
44
+ }
45
+ catch (err) {
46
+ console.error('[NuiEscape] Error in onEscape callback:', err);
47
+ }
48
+ };
49
+ }
50
+ function resolveTarget(node, targetType) {
51
+ if (targetType === NuiEscapeTarget.Window)
52
+ return window;
53
+ if (targetType === NuiEscapeTarget.Element)
54
+ return node;
55
+ return document;
56
+ }
57
+ /**
58
+ * Svelte Action for handling Escape key presses in FiveM NUI.
59
+ *
60
+ * Designed specifically for FiveM NUI where Escape is the universal close/dismiss action.
61
+ * Defaults to listening on `document` so closing works without requiring focus on a specific `div`.
62
+ *
63
+ * @remarks
64
+ * **Nested Modals & Target Scoping**:
65
+ * When using `target: NuiEscapeTarget.Document` (default), all registered listeners on `document` will receive
66
+ * keydown in registration order. If you have nested modals where only the topmost modal should close,
67
+ * consider using `nuiLayer` (which manages a centralized layer stack so only the topmost active layer dismisses),
68
+ * or configure `ignoreHandled: true`, or use `target: NuiEscapeTarget.Element` with a focus trap (`nuiFocusTrap`) and `stopPropagation: true`.
69
+ *
70
+ * @param node - The DOM element attaching the action.
71
+ * @param parameter - Callback function or configuration options object.
72
+ *
73
+ * @example
74
+ * ```svelte
75
+ * <div use:nuiEscapeAction={() => win.close()}>...</div>
76
+ * <div use:nuiEscapeAction={{ onEscape: close, stopPropagation: true }}>...</div>
77
+ * ```
78
+ */
79
+ export const nuiEscapeAction = (node, parameter) => {
80
+ let currentParam = parameter;
81
+ let controller = null;
82
+ const handler = createEscapeHandler(() => toEscapeOptions(currentParam));
83
+ function getTarget() {
84
+ const opts = toEscapeOptions(currentParam);
85
+ return resolveTarget(node, opts?.target);
86
+ }
87
+ function getCapture() {
88
+ const opts = toEscapeOptions(currentParam);
89
+ return opts?.capture ?? false;
90
+ }
91
+ function setup() {
92
+ teardown();
93
+ const eventTarget = getTarget();
94
+ controller = new AbortController();
95
+ eventTarget.addEventListener('keydown', handler, {
96
+ capture: getCapture(),
97
+ signal: controller.signal
98
+ });
99
+ }
100
+ function teardown() {
101
+ if (controller) {
102
+ controller.abort();
103
+ controller = null;
104
+ }
105
+ }
106
+ setup();
107
+ return {
108
+ update(newParam) {
109
+ const prevOpts = toEscapeOptions(currentParam);
110
+ currentParam = newParam;
111
+ const nextOpts = toEscapeOptions(currentParam);
112
+ const prevTarget = resolveTarget(node, prevOpts?.target);
113
+ const nextTarget = resolveTarget(node, nextOpts?.target);
114
+ const prevCapture = prevOpts?.capture ?? false;
115
+ const nextCapture = nextOpts?.capture ?? false;
116
+ // Only target or capture changes require re-binding DOM listeners.
117
+ // Other options (enabled, onEscape, preventDefault, stopPropagation, ignoreRepeat)
118
+ // are read dynamically inside createEscapeHandler on each keydown event.
119
+ if (prevTarget !== nextTarget || prevCapture !== nextCapture) {
120
+ setup();
121
+ }
122
+ },
123
+ destroy() {
124
+ teardown();
125
+ }
126
+ };
127
+ };
128
+ //# sourceMappingURL=escape.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escape.js","sourceRoot":"","sources":["../../src/actions/escape.ts"],"names":[],"mappings":"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"}