@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,24 @@
|
|
|
1
|
+
import type { Action } from 'svelte/action';
|
|
2
|
+
import { FOCUSABLE_SELECTORS, getFocusables, resolveInitialElement, handleTabKey } from './focusables';
|
|
3
|
+
import { type FocusTrapAllowOutsideTarget, type FocusTrapAllowOutside, type FocusTrapEntry, getTopTrap, resolveAllowOutsideElement, isNodeAllowedOutside } from './trapStack';
|
|
4
|
+
import { type NuiFocusTrapOptions, type NuiFocusTrapInput, toFocusTrapOptions } from './focusTrap.types';
|
|
5
|
+
export { FOCUSABLE_SELECTORS, getFocusables, resolveInitialElement, handleTabKey };
|
|
6
|
+
export { type FocusTrapAllowOutsideTarget, type FocusTrapAllowOutside, type FocusTrapEntry, getTopTrap, resolveAllowOutsideElement, isNodeAllowedOutside };
|
|
7
|
+
export { type NuiFocusTrapOptions, type NuiFocusTrapInput, toFocusTrapOptions };
|
|
8
|
+
/**
|
|
9
|
+
* Svelte Action for trapping Tab key navigation inside an element.
|
|
10
|
+
*
|
|
11
|
+
* Essential in FiveM NUI to prevent keyboard navigation from leaking out of CEF
|
|
12
|
+
* modals or into the background game window.
|
|
13
|
+
*
|
|
14
|
+
* @param node - The container DOM element confining focus.
|
|
15
|
+
* @param parameter - Configuration options or boolean enabled flag.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```svelte
|
|
19
|
+
* <div class="modal" use:nuiFocusTrap={isOpen}>...</div>
|
|
20
|
+
* <div class="dialog" use:nuiFocusTrap={{ enabled: true, allowOutside: '.datepicker-portal' }}>...</div>
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare const nuiFocusTrap: Action<HTMLElement, NuiFocusTrapInput>;
|
|
24
|
+
//# sourceMappingURL=focusTrap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"focusTrap.d.ts","sourceRoot":"","sources":["../../src/actions/focusTrap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EACL,mBAAmB,EACnB,aAAa,EACb,qBAAqB,EACrB,YAAY,EACb,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,KAAK,2BAA2B,EAChC,KAAK,qBAAqB,EAC1B,KAAK,cAAc,EACnB,UAAU,EACV,0BAA0B,EAC1B,oBAAoB,EAGrB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,kBAAkB,EACnB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,mBAAmB,EACnB,aAAa,EACb,qBAAqB,EACrB,YAAY,EACb,CAAC;AACF,OAAO,EACL,KAAK,2BAA2B,EAChC,KAAK,qBAAqB,EAC1B,KAAK,cAAc,EACnB,UAAU,EACV,0BAA0B,EAC1B,oBAAoB,EACrB,CAAC;AACF,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,kBAAkB,EACnB,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,WAAW,EAAE,iBAAiB,CAkM/D,CAAC"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { FOCUSABLE_SELECTORS, getFocusables, resolveInitialElement, handleTabKey } from './focusables';
|
|
2
|
+
import { getTopTrap, resolveAllowOutsideElement, isNodeAllowedOutside, pushTrap, removeTrap } from './trapStack';
|
|
3
|
+
import { toFocusTrapOptions } from './focusTrap.types';
|
|
4
|
+
export { FOCUSABLE_SELECTORS, getFocusables, resolveInitialElement, handleTabKey };
|
|
5
|
+
export { getTopTrap, resolveAllowOutsideElement, isNodeAllowedOutside };
|
|
6
|
+
export { toFocusTrapOptions };
|
|
7
|
+
/**
|
|
8
|
+
* Svelte Action for trapping Tab key navigation inside an element.
|
|
9
|
+
*
|
|
10
|
+
* Essential in FiveM NUI to prevent keyboard navigation from leaking out of CEF
|
|
11
|
+
* modals or into the background game window.
|
|
12
|
+
*
|
|
13
|
+
* @param node - The container DOM element confining focus.
|
|
14
|
+
* @param parameter - Configuration options or boolean enabled flag.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```svelte
|
|
18
|
+
* <div class="modal" use:nuiFocusTrap={isOpen}>...</div>
|
|
19
|
+
* <div class="dialog" use:nuiFocusTrap={{ enabled: true, allowOutside: '.datepicker-portal' }}>...</div>
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export const nuiFocusTrap = (node, parameter) => {
|
|
23
|
+
let currentParam = parameter;
|
|
24
|
+
let active = false;
|
|
25
|
+
let previouslyFocused = null;
|
|
26
|
+
let rafId = null;
|
|
27
|
+
let controller = null;
|
|
28
|
+
let addedTabindex = false;
|
|
29
|
+
function isEnabled() {
|
|
30
|
+
const opts = toFocusTrapOptions(currentParam);
|
|
31
|
+
const { enabled } = opts;
|
|
32
|
+
return typeof enabled === 'function' ? enabled() : (enabled ?? true);
|
|
33
|
+
}
|
|
34
|
+
function containsOrAllowed(target) {
|
|
35
|
+
if (!target)
|
|
36
|
+
return false;
|
|
37
|
+
if (node.contains(target))
|
|
38
|
+
return true;
|
|
39
|
+
const opts = toFocusTrapOptions(currentParam);
|
|
40
|
+
return isNodeAllowedOutside(target, opts.allowOutside);
|
|
41
|
+
}
|
|
42
|
+
function focusFirst() {
|
|
43
|
+
if (!active || !node.isConnected)
|
|
44
|
+
return;
|
|
45
|
+
const focusables = getFocusables(node);
|
|
46
|
+
if (focusables.length > 0) {
|
|
47
|
+
if (addedTabindex) {
|
|
48
|
+
node.removeAttribute('tabindex');
|
|
49
|
+
addedTabindex = false;
|
|
50
|
+
}
|
|
51
|
+
focusables[0].focus();
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
if (!node.hasAttribute('tabindex')) {
|
|
55
|
+
node.setAttribute('tabindex', '-1');
|
|
56
|
+
addedTabindex = true;
|
|
57
|
+
}
|
|
58
|
+
node.focus();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function focusInitial() {
|
|
62
|
+
if (!active || !node.isConnected)
|
|
63
|
+
return;
|
|
64
|
+
const opts = toFocusTrapOptions(currentParam);
|
|
65
|
+
if (opts.autofocus === false)
|
|
66
|
+
return;
|
|
67
|
+
const targetEl = resolveInitialElement(node, opts.initialFocus);
|
|
68
|
+
if (targetEl &&
|
|
69
|
+
targetEl.isConnected &&
|
|
70
|
+
(node.contains(targetEl) || isNodeAllowedOutside(targetEl, opts.allowOutside))) {
|
|
71
|
+
if (addedTabindex) {
|
|
72
|
+
node.removeAttribute('tabindex');
|
|
73
|
+
addedTabindex = false;
|
|
74
|
+
}
|
|
75
|
+
targetEl.focus();
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (!containsOrAllowed(document.activeElement)) {
|
|
79
|
+
focusFirst();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function checkIsTopmost() {
|
|
83
|
+
const opts = toFocusTrapOptions(currentParam);
|
|
84
|
+
if (typeof opts.isTopmost === 'function') {
|
|
85
|
+
return opts.isTopmost();
|
|
86
|
+
}
|
|
87
|
+
return getTopTrap()?.node === node;
|
|
88
|
+
}
|
|
89
|
+
function onFocusIn(event) {
|
|
90
|
+
if (!active || !node.isConnected)
|
|
91
|
+
return;
|
|
92
|
+
if (!checkIsTopmost())
|
|
93
|
+
return;
|
|
94
|
+
const target = event.target;
|
|
95
|
+
if (!target || containsOrAllowed(target))
|
|
96
|
+
return;
|
|
97
|
+
focusFirst();
|
|
98
|
+
}
|
|
99
|
+
function onKeyDown(event) {
|
|
100
|
+
if (!active || !checkIsTopmost())
|
|
101
|
+
return;
|
|
102
|
+
const opts = toFocusTrapOptions(currentParam);
|
|
103
|
+
handleTabKey(event, node, opts.allowOutside, focusFirst);
|
|
104
|
+
}
|
|
105
|
+
function restoreFocus() {
|
|
106
|
+
const opts = toFocusTrapOptions(currentParam);
|
|
107
|
+
if (opts.returnFocus === false)
|
|
108
|
+
return;
|
|
109
|
+
if (opts.returnFocus instanceof HTMLElement && opts.returnFocus.isConnected) {
|
|
110
|
+
opts.returnFocus.focus();
|
|
111
|
+
}
|
|
112
|
+
else if (previouslyFocused && previouslyFocused.isConnected) {
|
|
113
|
+
previouslyFocused.focus();
|
|
114
|
+
}
|
|
115
|
+
const topEntry = getTopTrap();
|
|
116
|
+
if (topEntry && !topEntry.containsOrAllowed(document.activeElement)) {
|
|
117
|
+
topEntry.focusFirst();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function teardown() {
|
|
121
|
+
if (rafId !== null) {
|
|
122
|
+
if (typeof cancelAnimationFrame !== 'undefined') {
|
|
123
|
+
cancelAnimationFrame(rafId);
|
|
124
|
+
}
|
|
125
|
+
rafId = null;
|
|
126
|
+
}
|
|
127
|
+
if (controller) {
|
|
128
|
+
controller.abort();
|
|
129
|
+
controller = null;
|
|
130
|
+
}
|
|
131
|
+
removeTrap(node);
|
|
132
|
+
}
|
|
133
|
+
const entry = {
|
|
134
|
+
node,
|
|
135
|
+
focusFirst,
|
|
136
|
+
containsOrAllowed,
|
|
137
|
+
isTopmost: () => {
|
|
138
|
+
const opts = toFocusTrapOptions(currentParam);
|
|
139
|
+
return typeof opts.isTopmost === 'function' ? opts.isTopmost() : undefined;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
function setup() {
|
|
143
|
+
teardown();
|
|
144
|
+
if (!isEnabled())
|
|
145
|
+
return;
|
|
146
|
+
previouslyFocused = document.activeElement;
|
|
147
|
+
if (previouslyFocused && node.contains(previouslyFocused)) {
|
|
148
|
+
previouslyFocused = null;
|
|
149
|
+
}
|
|
150
|
+
active = true;
|
|
151
|
+
pushTrap(entry);
|
|
152
|
+
controller = new AbortController();
|
|
153
|
+
document.addEventListener('keydown', onKeyDown, { capture: true, signal: controller.signal });
|
|
154
|
+
document.addEventListener('focusin', onFocusIn, { signal: controller.signal });
|
|
155
|
+
if (typeof requestAnimationFrame !== 'undefined') {
|
|
156
|
+
rafId = requestAnimationFrame(() => {
|
|
157
|
+
rafId = null;
|
|
158
|
+
if (active && node.isConnected) {
|
|
159
|
+
focusInitial();
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
else if (typeof queueMicrotask !== 'undefined') {
|
|
164
|
+
queueMicrotask(() => {
|
|
165
|
+
if (active && node.isConnected) {
|
|
166
|
+
focusInitial();
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
function deactivate() {
|
|
172
|
+
const wasActive = active;
|
|
173
|
+
active = false;
|
|
174
|
+
teardown();
|
|
175
|
+
if (addedTabindex) {
|
|
176
|
+
node.removeAttribute('tabindex');
|
|
177
|
+
addedTabindex = false;
|
|
178
|
+
}
|
|
179
|
+
if (wasActive) {
|
|
180
|
+
restoreFocus();
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
setup();
|
|
184
|
+
return {
|
|
185
|
+
update(newParam) {
|
|
186
|
+
const wasEnabled = active;
|
|
187
|
+
currentParam = newParam;
|
|
188
|
+
const nowEnabled = isEnabled();
|
|
189
|
+
if (!wasEnabled && nowEnabled) {
|
|
190
|
+
setup();
|
|
191
|
+
}
|
|
192
|
+
else if (wasEnabled && !nowEnabled) {
|
|
193
|
+
deactivate();
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
destroy() {
|
|
197
|
+
deactivate();
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
};
|
|
201
|
+
//# sourceMappingURL=focusTrap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"focusTrap.js","sourceRoot":"","sources":["../../src/actions/focusTrap.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { FocusTrapAllowOutside } from './trapStack';
|
|
2
|
+
export interface NuiFocusTrapOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Whether the focus trap is actively confining focus.
|
|
5
|
+
* Accepts a boolean or a reactive getter `() => boolean`.
|
|
6
|
+
* @defaultValue true
|
|
7
|
+
*/
|
|
8
|
+
enabled?: boolean | (() => boolean);
|
|
9
|
+
/**
|
|
10
|
+
* Custom element, selector or getter to receive initial focus upon activation.
|
|
11
|
+
* If omitted, the first focusable element inside the node receives focus.
|
|
12
|
+
*/
|
|
13
|
+
initialFocus?: HTMLElement | string | (() => HTMLElement | null | undefined);
|
|
14
|
+
/**
|
|
15
|
+
* Whether to restore focus to the previously focused element upon deactivation.
|
|
16
|
+
* Can also be a specific HTMLElement to return focus to.
|
|
17
|
+
* @defaultValue true
|
|
18
|
+
*/
|
|
19
|
+
returnFocus?: boolean | HTMLElement;
|
|
20
|
+
/**
|
|
21
|
+
* Whether to automatically move focus inside the trap on activation.
|
|
22
|
+
* @defaultValue true
|
|
23
|
+
*/
|
|
24
|
+
autofocus?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Elements, selectors, or getters outside the container that are permitted to receive focus
|
|
27
|
+
* without being redirected back into the trap.
|
|
28
|
+
*
|
|
29
|
+
* Essential for portaled child elements such as select dropdowns, date pickers, or tooltips
|
|
30
|
+
* rendered into `document.body` outside the modal container.
|
|
31
|
+
*/
|
|
32
|
+
allowOutside?: FocusTrapAllowOutside;
|
|
33
|
+
/**
|
|
34
|
+
* Optional custom predicate to determine if this trap is currently topmost.
|
|
35
|
+
* When provided, takes precedence over the default `trapStack` activation-order check.
|
|
36
|
+
* Used by coordinators such as `nuiLayer` to synchronize focus trapping with priority order.
|
|
37
|
+
*
|
|
38
|
+
* @remarks
|
|
39
|
+
* If providing custom `isTopmost` predicates manually, ensure only one active trap
|
|
40
|
+
* evaluates to `true` at any given time to avoid conflicting focus containment.
|
|
41
|
+
* Note that `nuiLayer` automatically controls and overrides this predicate based on layer priority.
|
|
42
|
+
*/
|
|
43
|
+
isTopmost?: () => boolean;
|
|
44
|
+
}
|
|
45
|
+
export type NuiFocusTrapInput = boolean | NuiFocusTrapOptions | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* Normalizes boolean or option object parameters into `NuiFocusTrapOptions`.
|
|
48
|
+
*/
|
|
49
|
+
export declare function toFocusTrapOptions(input: NuiFocusTrapInput): NuiFocusTrapOptions;
|
|
50
|
+
//# sourceMappingURL=focusTrap.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"focusTrap.types.d.ts","sourceRoot":"","sources":["../../src/actions/focusTrap.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEzD,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC,CAAC;IAEpC;;;OAGG;IACH,YAAY,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,CAAC,MAAM,WAAW,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IAE7E;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,GAAG,WAAW,CAAC;IAEpC;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,qBAAqB,CAAC;IAErC;;;;;;;;;OASG;IACH,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC;CAC3B;AAED,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,mBAAmB,GAAG,SAAS,CAAC;AAE1E;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,iBAAiB,GACvB,mBAAmB,CAKrB"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalizes boolean or option object parameters into `NuiFocusTrapOptions`.
|
|
3
|
+
*/
|
|
4
|
+
export function toFocusTrapOptions(input) {
|
|
5
|
+
if (input === undefined || typeof input === 'boolean') {
|
|
6
|
+
return { enabled: input ?? true };
|
|
7
|
+
}
|
|
8
|
+
return input;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=focusTrap.types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"focusTrap.types.js","sourceRoot":"","sources":["../../src/actions/focusTrap.types.ts"],"names":[],"mappings":"AAqDA;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAChC,KAAwB;IAExB,IAAI,KAAK,KAAK,SAAS,IAAI,OAAO,KAAK,KAAK,SAAS,EAAE,CAAC;QACtD,OAAO,EAAE,OAAO,EAAE,KAAK,IAAI,IAAI,EAAE,CAAC;IACpC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type FocusTrapAllowOutside } from './trapStack';
|
|
2
|
+
/**
|
|
3
|
+
* CSS selector matching all natively and custom focusable elements in HTML documents.
|
|
4
|
+
*/
|
|
5
|
+
export declare const FOCUSABLE_SELECTORS: string;
|
|
6
|
+
/**
|
|
7
|
+
* Returns all currently focusable elements in Tab order within a container.
|
|
8
|
+
* Filters out disabled elements (including `<fieldset disabled>`), negative tabindexes,
|
|
9
|
+
* aria-hidden, inert, and hidden elements.
|
|
10
|
+
* Correctly scopes radio button groups per form so only the active radio button is in Tab order.
|
|
11
|
+
*
|
|
12
|
+
* @remarks
|
|
13
|
+
* Assumes DOM order for keyboard navigation. Elements with positive tabindex (> 0)
|
|
14
|
+
* alter standard browser tab sequencing and should generally be avoided.
|
|
15
|
+
*
|
|
16
|
+
* @param container - The root element to query focusable elements from.
|
|
17
|
+
* @returns Array of focusable HTML elements within the container.
|
|
18
|
+
*/
|
|
19
|
+
export declare function getFocusables(container: HTMLElement): HTMLElement[];
|
|
20
|
+
/**
|
|
21
|
+
* Resolves the designated initial focus element from an element reference, selector, or getter.
|
|
22
|
+
*
|
|
23
|
+
* @param container - The container DOM element.
|
|
24
|
+
* @param initialFocus - Target element, selector string, or getter function.
|
|
25
|
+
* @returns The resolved HTMLElement, or null if none was found.
|
|
26
|
+
*/
|
|
27
|
+
export declare function resolveInitialElement(container: HTMLElement, initialFocus?: HTMLElement | string | (() => HTMLElement | null | undefined)): HTMLElement | null;
|
|
28
|
+
/**
|
|
29
|
+
* Confines Tab and Shift+Tab keyboard navigation within a container element.
|
|
30
|
+
* Wraps around when reaching the start or end of the focus sequence.
|
|
31
|
+
* Skips redirection if the currently focused element is allowed outside (e.g. portaled child).
|
|
32
|
+
*
|
|
33
|
+
* @param event - The keyboard event.
|
|
34
|
+
* @param container - The container element confining focus.
|
|
35
|
+
* @param allowOutside - Elements or selectors permitted to retain focus.
|
|
36
|
+
* @param onEmptyFallback - Callback when no focusable elements exist in container.
|
|
37
|
+
*/
|
|
38
|
+
export declare function handleTabKey(event: KeyboardEvent, container: HTMLElement, allowOutside?: FocusTrapAllowOutside, onEmptyFallback?: () => void): void;
|
|
39
|
+
//# sourceMappingURL=focusables.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"focusables.d.ts","sourceRoot":"","sources":["../../src/actions/focusables.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,qBAAqB,EAAwB,MAAM,aAAa,CAAC;AAE/E;;GAEG;AACH,eAAO,MAAM,mBAAmB,QAapB,CAAC;AAEb;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,WAAW,GAAG,WAAW,EAAE,CAmDnE;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,WAAW,EACtB,YAAY,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,CAAC,MAAM,WAAW,GAAG,IAAI,GAAG,SAAS,CAAC,GAC3E,WAAW,GAAG,IAAI,CAYpB;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,aAAa,EACpB,SAAS,EAAE,WAAW,EACtB,YAAY,CAAC,EAAE,qBAAqB,EACpC,eAAe,CAAC,EAAE,MAAM,IAAI,GAC3B,IAAI,CA6BN"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { isNodeAllowedOutside } from './trapStack';
|
|
2
|
+
/**
|
|
3
|
+
* CSS selector matching all natively and custom focusable elements in HTML documents.
|
|
4
|
+
*/
|
|
5
|
+
export const FOCUSABLE_SELECTORS = [
|
|
6
|
+
'a[href]',
|
|
7
|
+
'area[href]',
|
|
8
|
+
'input:not([type="hidden"])',
|
|
9
|
+
'select',
|
|
10
|
+
'textarea',
|
|
11
|
+
'button',
|
|
12
|
+
'iframe',
|
|
13
|
+
'summary',
|
|
14
|
+
'audio[controls]',
|
|
15
|
+
'video[controls]',
|
|
16
|
+
'[contenteditable]:not([contenteditable="false"])',
|
|
17
|
+
'[tabindex]'
|
|
18
|
+
].join(', ');
|
|
19
|
+
/**
|
|
20
|
+
* Returns all currently focusable elements in Tab order within a container.
|
|
21
|
+
* Filters out disabled elements (including `<fieldset disabled>`), negative tabindexes,
|
|
22
|
+
* aria-hidden, inert, and hidden elements.
|
|
23
|
+
* Correctly scopes radio button groups per form so only the active radio button is in Tab order.
|
|
24
|
+
*
|
|
25
|
+
* @remarks
|
|
26
|
+
* Assumes DOM order for keyboard navigation. Elements with positive tabindex (> 0)
|
|
27
|
+
* alter standard browser tab sequencing and should generally be avoided.
|
|
28
|
+
*
|
|
29
|
+
* @param container - The root element to query focusable elements from.
|
|
30
|
+
* @returns Array of focusable HTML elements within the container.
|
|
31
|
+
*/
|
|
32
|
+
export function getFocusables(container) {
|
|
33
|
+
const elements = Array.from(container.querySelectorAll(FOCUSABLE_SELECTORS));
|
|
34
|
+
const focusables = [];
|
|
35
|
+
const radioGroups = new Map();
|
|
36
|
+
for (const el of elements) {
|
|
37
|
+
if (el.matches(':disabled') || el.closest('fieldset[disabled]'))
|
|
38
|
+
continue;
|
|
39
|
+
if (el.tabIndex < 0)
|
|
40
|
+
continue;
|
|
41
|
+
if (el.closest('[aria-hidden="true"], [inert]'))
|
|
42
|
+
continue;
|
|
43
|
+
if (typeof el.checkVisibility === 'function') {
|
|
44
|
+
if (!el.checkVisibility())
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
const isVisible = el.offsetWidth > 0 ||
|
|
49
|
+
el.offsetHeight > 0 ||
|
|
50
|
+
el.getClientRects().length > 0 ||
|
|
51
|
+
el.offsetParent !== null;
|
|
52
|
+
if (!isVisible)
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (el instanceof HTMLInputElement && el.type === 'radio' && el.name) {
|
|
56
|
+
const form = el.form;
|
|
57
|
+
let formRadios = radioGroups.get(form);
|
|
58
|
+
if (!formRadios) {
|
|
59
|
+
formRadios = new Map();
|
|
60
|
+
radioGroups.set(form, formRadios);
|
|
61
|
+
}
|
|
62
|
+
const currentRadio = formRadios.get(el.name);
|
|
63
|
+
if (el.checked) {
|
|
64
|
+
formRadios.set(el.name, el);
|
|
65
|
+
}
|
|
66
|
+
else if (!currentRadio) {
|
|
67
|
+
formRadios.set(el.name, el);
|
|
68
|
+
}
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
focusables.push(el);
|
|
72
|
+
}
|
|
73
|
+
if (radioGroups.size > 0) {
|
|
74
|
+
const keep = new Set(focusables);
|
|
75
|
+
for (const formRadios of radioGroups.values()) {
|
|
76
|
+
for (const radio of formRadios.values()) {
|
|
77
|
+
keep.add(radio);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return elements.filter((el) => keep.has(el));
|
|
81
|
+
}
|
|
82
|
+
return focusables;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Resolves the designated initial focus element from an element reference, selector, or getter.
|
|
86
|
+
*
|
|
87
|
+
* @param container - The container DOM element.
|
|
88
|
+
* @param initialFocus - Target element, selector string, or getter function.
|
|
89
|
+
* @returns The resolved HTMLElement, or null if none was found.
|
|
90
|
+
*/
|
|
91
|
+
export function resolveInitialElement(container, initialFocus) {
|
|
92
|
+
if (!initialFocus)
|
|
93
|
+
return null;
|
|
94
|
+
if (typeof initialFocus === 'string') {
|
|
95
|
+
return (container.querySelector(initialFocus) ??
|
|
96
|
+
document.querySelector(initialFocus));
|
|
97
|
+
}
|
|
98
|
+
if (typeof initialFocus === 'function') {
|
|
99
|
+
return initialFocus() ?? null;
|
|
100
|
+
}
|
|
101
|
+
return initialFocus;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Confines Tab and Shift+Tab keyboard navigation within a container element.
|
|
105
|
+
* Wraps around when reaching the start or end of the focus sequence.
|
|
106
|
+
* Skips redirection if the currently focused element is allowed outside (e.g. portaled child).
|
|
107
|
+
*
|
|
108
|
+
* @param event - The keyboard event.
|
|
109
|
+
* @param container - The container element confining focus.
|
|
110
|
+
* @param allowOutside - Elements or selectors permitted to retain focus.
|
|
111
|
+
* @param onEmptyFallback - Callback when no focusable elements exist in container.
|
|
112
|
+
*/
|
|
113
|
+
export function handleTabKey(event, container, allowOutside, onEmptyFallback) {
|
|
114
|
+
if (event.key !== 'Tab')
|
|
115
|
+
return;
|
|
116
|
+
const activeEl = document.activeElement;
|
|
117
|
+
if (isNodeAllowedOutside(activeEl, allowOutside))
|
|
118
|
+
return;
|
|
119
|
+
const focusables = getFocusables(container);
|
|
120
|
+
if (focusables.length === 0) {
|
|
121
|
+
event.preventDefault();
|
|
122
|
+
if (!container.contains(activeEl)) {
|
|
123
|
+
onEmptyFallback?.();
|
|
124
|
+
}
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const first = focusables[0];
|
|
128
|
+
const last = focusables[focusables.length - 1];
|
|
129
|
+
if (event.shiftKey) {
|
|
130
|
+
if (activeEl === first || !container.contains(activeEl)) {
|
|
131
|
+
event.preventDefault();
|
|
132
|
+
last.focus();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
if (activeEl === last || !container.contains(activeEl)) {
|
|
137
|
+
event.preventDefault();
|
|
138
|
+
first.focus();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=focusables.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"focusables.js","sourceRoot":"","sources":["../../src/actions/focusables.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { nuiEscapeAction, NuiEscapeTarget, type NuiEscapeParameter, type NuiEscapeInput } from './escape';
|
|
2
|
+
export { nuiFocusTrap, type NuiFocusTrapOptions, type NuiFocusTrapInput, type FocusTrapAllowOutsideTarget, type FocusTrapAllowOutside, type FocusTrapEntry, FOCUSABLE_SELECTORS, getFocusables } from './focusTrap';
|
|
3
|
+
export { nuiClickOutside, type NuiClickOutsideParameter, type NuiClickOutsideInput, type ClickOutsideTarget } from './clickOutside';
|
|
4
|
+
export { nuiLayer, type NuiLayerOptions, type NuiLayerInput, NuiLayerDismissReason, type LayerInstance, getLayerStack, getTopLayer, resetLayerStack } from './layer';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/actions/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,eAAe,EACf,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACpB,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,YAAY,EACZ,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,2BAA2B,EAChC,KAAK,qBAAqB,EAC1B,KAAK,cAAc,EACnB,mBAAmB,EACnB,aAAa,EACd,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,eAAe,EACf,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACxB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,QAAQ,EACR,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,qBAAqB,EACrB,KAAK,aAAa,EAClB,aAAa,EACb,WAAW,EACX,eAAe,EAChB,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { nuiEscapeAction, NuiEscapeTarget } from './escape';
|
|
2
|
+
export { nuiFocusTrap, FOCUSABLE_SELECTORS, getFocusables } from './focusTrap';
|
|
3
|
+
export { nuiClickOutside } from './clickOutside';
|
|
4
|
+
export { nuiLayer, NuiLayerDismissReason, getLayerStack, getTopLayer, resetLayerStack } from './layer';
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/actions/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,eAAe,EAGhB,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,YAAY,EAMZ,mBAAmB,EACnB,aAAa,EACd,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,eAAe,EAIhB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,QAAQ,EAGR,qBAAqB,EAErB,aAAa,EACb,WAAW,EACX,eAAe,EAChB,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { nuiLayer } from './layer';
|
|
2
|
+
export { NuiLayerDismissReason, type NuiLayerOptions, type NuiLayerInput, type LayerInstance, toLayerOptions } from './types';
|
|
3
|
+
export { getLayerStack, getTopLayer, resetLayerStack } from './layerStack';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/actions/layer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EACL,qBAAqB,EACrB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,cAAc,EACf,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,aAAa,EACb,WAAW,EACX,eAAe,EAChB,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/actions/layer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EACL,qBAAqB,EAIrB,cAAc,EACf,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,aAAa,EACb,WAAW,EACX,eAAe,EAChB,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Action } from 'svelte/action';
|
|
2
|
+
import { type NuiLayerInput } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Managing an overlay layer in FiveM NUI.
|
|
5
|
+
*
|
|
6
|
+
* Coordinates:
|
|
7
|
+
* - **Escape key**: Traverses the layer stack top-to-bottom. The topmost active layer that handles Escape
|
|
8
|
+
* receives the event and dismisses. If the topmost layer disables Escape (`onEscape: false`),
|
|
9
|
+
* lower layers can handle it.
|
|
10
|
+
* - **Outside click**: Evaluates strictly the topmost active layer. If the topmost layer disables outside
|
|
11
|
+
* clicks (`onClickOutside: false`), clicks outside are blocked and do NOT fall through to underlying layers,
|
|
12
|
+
* preserving modal barrier isolation.
|
|
13
|
+
* - **Focus trap**: Integrates `nuiFocusTrap` synchronized with layer priority so focus containment and Tab
|
|
14
|
+
* navigation are enforced by the topmost layer even when layers have varying priorities.
|
|
15
|
+
* - **Portal integration**: `allowOutside` allows portaled children (selects, datepickers, tooltips)
|
|
16
|
+
* without triggering outside clicks or breaking focus containment.
|
|
17
|
+
*
|
|
18
|
+
* @param node - The DOM element attaching the layer action.
|
|
19
|
+
* @param parameter - Dismissal callback function or layer configuration options object.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```svelte
|
|
23
|
+
* <div use:nuiLayer={() => (isOpen = false)}>...</div>
|
|
24
|
+
* <div use:nuiLayer={{ onDismiss: close, allowOutside: '.portal-menu' }}>...</div>
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare const nuiLayer: Action<HTMLElement, NuiLayerInput>;
|
|
28
|
+
//# sourceMappingURL=layer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layer.d.ts","sourceRoot":"","sources":["../../../src/actions/layer/layer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,EAEL,KAAK,aAAa,EAGnB,MAAM,SAAS,CAAC;AAQjB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,WAAW,EAAE,aAAa,CAoGvD,CAAC"}
|