@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.
- package/README.md +284 -2
- package/dist/actions/clickOutside/clickOutside.d.ts +24 -0
- package/dist/actions/clickOutside/clickOutside.d.ts.map +1 -0
- package/dist/actions/clickOutside/clickOutside.js +62 -0
- package/dist/actions/clickOutside/clickOutside.js.map +1 -0
- package/dist/actions/clickOutside/handler.d.ts +11 -0
- package/dist/actions/clickOutside/handler.d.ts.map +1 -0
- package/dist/actions/clickOutside/handler.js +45 -0
- package/dist/actions/clickOutside/handler.js.map +1 -0
- package/dist/actions/clickOutside/index.d.ts +4 -0
- package/dist/actions/clickOutside/index.d.ts.map +1 -0
- package/dist/actions/clickOutside/index.js +4 -0
- package/dist/actions/clickOutside/index.js.map +1 -0
- package/dist/actions/clickOutside/types.d.ts +36 -0
- package/dist/actions/clickOutside/types.d.ts.map +1 -0
- package/dist/actions/clickOutside/types.js +6 -0
- package/dist/actions/clickOutside/types.js.map +1 -0
- package/dist/actions/clickOutside.d.ts +63 -0
- package/dist/actions/clickOutside.d.ts.map +1 -0
- package/dist/actions/clickOutside.js +106 -0
- package/dist/actions/clickOutside.js.map +1 -0
- package/dist/actions/escape/escape.d.ts +26 -0
- package/dist/actions/escape/escape.d.ts.map +1 -0
- package/dist/actions/escape/escape.js +74 -0
- package/dist/actions/escape/escape.js.map +1 -0
- package/dist/actions/escape/handler.d.ts +11 -0
- package/dist/actions/escape/handler.d.ts.map +1 -0
- package/dist/actions/escape/handler.js +50 -0
- package/dist/actions/escape/handler.js.map +1 -0
- package/dist/actions/escape/index.d.ts +4 -0
- package/dist/actions/escape/index.d.ts.map +1 -0
- package/dist/actions/escape/index.js +4 -0
- package/dist/actions/escape/index.js.map +1 -0
- package/dist/actions/escape/types.d.ts +70 -0
- package/dist/actions/escape/types.d.ts.map +1 -0
- package/dist/actions/escape/types.js +12 -0
- package/dist/actions/escape/types.js.map +1 -0
- package/dist/actions/escape.d.ts +99 -0
- package/dist/actions/escape.d.ts.map +1 -0
- package/dist/actions/escape.js +128 -0
- package/dist/actions/escape.js.map +1 -0
- package/dist/actions/focusTrap/focusTrap.d.ts +19 -0
- package/dist/actions/focusTrap/focusTrap.d.ts.map +1 -0
- package/dist/actions/focusTrap/focusTrap.js +198 -0
- package/dist/actions/focusTrap/focusTrap.js.map +1 -0
- package/dist/actions/focusTrap/focusables.d.ts +39 -0
- package/dist/actions/focusTrap/focusables.d.ts.map +1 -0
- package/dist/actions/focusTrap/focusables.js +142 -0
- package/dist/actions/focusTrap/focusables.js.map +1 -0
- package/dist/actions/focusTrap/index.d.ts +5 -0
- package/dist/actions/focusTrap/index.d.ts.map +1 -0
- package/dist/actions/focusTrap/index.js +5 -0
- package/dist/actions/focusTrap/index.js.map +1 -0
- package/dist/actions/focusTrap/trapStack.d.ts +33 -0
- package/dist/actions/focusTrap/trapStack.d.ts.map +1 -0
- package/dist/actions/focusTrap/trapStack.js +84 -0
- package/dist/actions/focusTrap/trapStack.js.map +1 -0
- package/dist/actions/focusTrap/types.d.ts +50 -0
- package/dist/actions/focusTrap/types.d.ts.map +1 -0
- package/dist/actions/focusTrap/types.js +10 -0
- package/dist/actions/focusTrap/types.js.map +1 -0
- package/dist/actions/focusTrap.d.ts +24 -0
- package/dist/actions/focusTrap.d.ts.map +1 -0
- package/dist/actions/focusTrap.js +201 -0
- package/dist/actions/focusTrap.js.map +1 -0
- package/dist/actions/focusTrap.types.d.ts +50 -0
- package/dist/actions/focusTrap.types.d.ts.map +1 -0
- package/dist/actions/focusTrap.types.js +10 -0
- package/dist/actions/focusTrap.types.js.map +1 -0
- package/dist/actions/focusables.d.ts +39 -0
- package/dist/actions/focusables.d.ts.map +1 -0
- package/dist/actions/focusables.js +142 -0
- package/dist/actions/focusables.js.map +1 -0
- package/dist/actions/index.d.ts +5 -0
- package/dist/actions/index.d.ts.map +1 -0
- package/dist/actions/index.js +5 -0
- package/dist/actions/index.js.map +1 -0
- package/dist/actions/layer/index.d.ts +4 -0
- package/dist/actions/layer/index.d.ts.map +1 -0
- package/dist/actions/layer/index.js +4 -0
- package/dist/actions/layer/index.js.map +1 -0
- package/dist/actions/layer/layer.d.ts +28 -0
- package/dist/actions/layer/layer.d.ts.map +1 -0
- package/dist/actions/layer/layer.js +118 -0
- package/dist/actions/layer/layer.js.map +1 -0
- package/dist/actions/layer/layerStack.d.ts +10 -0
- package/dist/actions/layer/layerStack.d.ts.map +1 -0
- package/dist/actions/layer/layerStack.js +153 -0
- package/dist/actions/layer/layerStack.js.map +1 -0
- package/dist/actions/layer/types.d.ts +105 -0
- package/dist/actions/layer/types.d.ts.map +1 -0
- package/dist/actions/layer/types.js +14 -0
- package/dist/actions/layer/types.js.map +1 -0
- package/dist/actions/layer.d.ts +133 -0
- package/dist/actions/layer.d.ts.map +1 -0
- package/dist/actions/layer.js +269 -0
- package/dist/actions/layer.js.map +1 -0
- package/dist/actions/trapStack.d.ts +33 -0
- package/dist/actions/trapStack.d.ts.map +1 -0
- package/dist/actions/trapStack.js +84 -0
- package/dist/actions/trapStack.js.map +1 -0
- package/dist/client.d.ts +179 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +176 -0
- package/dist/client.js.map +1 -0
- package/dist/context/index.d.ts +68 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +112 -0
- package/dist/context/index.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/mock/index.d.ts +27 -0
- package/dist/mock/index.d.ts.map +1 -0
- package/dist/mock/index.js +137 -0
- package/dist/mock/index.js.map +1 -0
- package/dist/mock/interceptor.d.ts +19 -0
- package/dist/mock/interceptor.d.ts.map +1 -0
- package/dist/mock/interceptor.js +128 -0
- package/dist/mock/interceptor.js.map +1 -0
- package/dist/mock/types.d.ts +100 -0
- package/dist/mock/types.d.ts.map +1 -0
- package/dist/mock/types.js +27 -0
- package/dist/mock/types.js.map +1 -0
- package/dist/mock/url.d.ts +14 -0
- package/dist/mock/url.d.ts.map +1 -0
- package/dist/mock/url.js +38 -0
- package/dist/mock/url.js.map +1 -0
- package/dist/runes/clickOutside.svelte.d.ts +29 -0
- package/dist/runes/clickOutside.svelte.d.ts.map +1 -0
- package/dist/runes/clickOutside.svelte.js +42 -0
- package/dist/runes/clickOutside.svelte.js.map +1 -0
- package/dist/runes/escape.svelte.d.ts +34 -0
- package/dist/runes/escape.svelte.d.ts.map +1 -0
- package/dist/runes/escape.svelte.js +44 -0
- package/dist/runes/escape.svelte.js.map +1 -0
- package/dist/runes/focusTrap.svelte.d.ts +21 -0
- package/dist/runes/focusTrap.svelte.d.ts.map +1 -0
- package/dist/runes/focusTrap.svelte.js +37 -0
- package/dist/runes/focusTrap.svelte.js.map +1 -0
- package/dist/runes/index.svelte.d.ts +8 -0
- package/dist/runes/index.svelte.d.ts.map +1 -0
- package/dist/runes/index.svelte.js +8 -0
- package/dist/runes/index.svelte.js.map +1 -0
- package/dist/runes/layer.svelte.d.ts +36 -0
- package/dist/runes/layer.svelte.d.ts.map +1 -0
- package/dist/runes/layer.svelte.js +48 -0
- package/dist/runes/layer.svelte.js.map +1 -0
- package/dist/runes/query.svelte.d.ts +104 -0
- package/dist/runes/query.svelte.d.ts.map +1 -0
- package/dist/runes/query.svelte.js +179 -0
- package/dist/runes/query.svelte.js.map +1 -0
- package/dist/runes/state.svelte.d.ts +72 -0
- package/dist/runes/state.svelte.d.ts.map +1 -0
- package/dist/runes/state.svelte.js +98 -0
- package/dist/runes/state.svelte.js.map +1 -0
- package/dist/runes/window.svelte.d.ts +120 -0
- package/dist/runes/window.svelte.d.ts.map +1 -0
- package/dist/runes/window.svelte.js +196 -0
- package/dist/runes/window.svelte.js.map +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/nui.d.ts +34 -0
- package/dist/types/nui.d.ts.map +1 -0
- package/dist/types/nui.js +17 -0
- package/dist/types/nui.js.map +1 -0
- package/dist/utils/env.d.ts +21 -0
- package/dist/utils/env.d.ts.map +1 -0
- package/dist/utils/env.js +36 -0
- package/dist/utils/env.js.map +1 -0
- package/dist/utils/error.d.ts +11 -0
- package/dist/utils/error.d.ts.map +1 -0
- package/dist/utils/error.js +30 -0
- package/dist/utils/error.js.map +1 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/keyboard.d.ts +127 -0
- package/dist/utils/keyboard.d.ts.map +1 -0
- package/dist/utils/keyboard.js +152 -0
- package/dist/utils/keyboard.js.map +1 -0
- package/package.json +76 -4
- package/src/actions/clickOutside/clickOutside.ts +78 -0
- package/src/actions/clickOutside/handler.ts +49 -0
- package/src/actions/clickOutside/index.ts +11 -0
- package/src/actions/clickOutside/types.ts +54 -0
- package/src/actions/escape/escape.ts +90 -0
- package/src/actions/escape/handler.ts +52 -0
- package/src/actions/escape/index.ts +11 -0
- package/src/actions/escape/types.ts +88 -0
- package/src/actions/focusTrap/focusTrap.ts +228 -0
- package/src/actions/focusTrap/focusables.ts +155 -0
- package/src/actions/focusTrap/index.ts +22 -0
- package/src/actions/focusTrap/trapStack.ts +109 -0
- package/src/actions/focusTrap/types.ts +64 -0
- package/src/actions/index.ts +35 -0
- package/src/actions/layer/index.ts +13 -0
- package/src/actions/layer/layer.ts +140 -0
- package/src/actions/layer/layerStack.ts +169 -0
- package/src/actions/layer/types.ts +132 -0
- package/src/client.ts +323 -0
- package/src/context/index.ts +127 -0
- package/src/index.ts +53 -0
- package/src/mock/index.ts +176 -0
- package/src/mock/interceptor.ts +177 -0
- package/src/mock/types.ts +125 -0
- package/src/mock/url.ts +37 -0
- package/src/runes/clickOutside.svelte.ts +53 -0
- package/src/runes/escape.svelte.ts +71 -0
- package/src/runes/focusTrap.svelte.ts +56 -0
- package/src/runes/index.svelte.ts +38 -0
- package/src/runes/layer.svelte.ts +74 -0
- package/src/runes/query.svelte.ts +313 -0
- package/src/runes/state.svelte.ts +175 -0
- package/src/runes/window.svelte.ts +308 -0
- package/src/utils/env.ts +36 -0
- package/src/utils/index.ts +2 -0
- 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":"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"}
|
|
@@ -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 @@
|
|
|
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 @@
|
|
|
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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"}
|