@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,128 @@
|
|
|
1
|
+
import { isNuiUrl, parseNuiUrl } from './url';
|
|
2
|
+
/**
|
|
3
|
+
* Extracts the body payload from a `fetch` invocation, supporting both `init.body`
|
|
4
|
+
* and body text inside a `Request` instance.
|
|
5
|
+
*/
|
|
6
|
+
export async function extractRequestBody(input, init) {
|
|
7
|
+
let bodyText = typeof init?.body === 'string' ? init.body : null;
|
|
8
|
+
if (!bodyText && typeof Request !== 'undefined' && input instanceof Request) {
|
|
9
|
+
try {
|
|
10
|
+
bodyText = await input.clone().text();
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
// body already consumed or unreadable
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
if (bodyText) {
|
|
17
|
+
try {
|
|
18
|
+
return JSON.parse(bodyText);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return bodyText;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Creates the `window.fetch` interceptor function that simulates FiveM CEF transport.
|
|
28
|
+
*/
|
|
29
|
+
export function createFetchInterceptor(ctx) {
|
|
30
|
+
const { targetResource, latencyMs, log, activeRequests, activeCallbacks, activeSendHandlers, originalFetch } = ctx;
|
|
31
|
+
return async (input, init) => {
|
|
32
|
+
const url = typeof input === 'string'
|
|
33
|
+
? input
|
|
34
|
+
: input instanceof URL
|
|
35
|
+
? input.toString()
|
|
36
|
+
: input.url;
|
|
37
|
+
if (!isNuiUrl(url, targetResource)) {
|
|
38
|
+
return originalFetch(input, init);
|
|
39
|
+
}
|
|
40
|
+
const { resourceName, callbackName } = parseNuiUrl(url);
|
|
41
|
+
const bodyObj = await extractRequestBody(input, init);
|
|
42
|
+
const eventName = bodyObj?.event;
|
|
43
|
+
const correlationId = bodyObj?.id;
|
|
44
|
+
const payload = bodyObj?.payload ?? bodyObj?.data;
|
|
45
|
+
const messageType = bodyObj?.type;
|
|
46
|
+
if (log) {
|
|
47
|
+
console.debug(`[NUI Mock] -> POST https://${resourceName}/${callbackName} type='${messageType ?? 'event'}' event='${eventName ?? callbackName}' id=${correlationId ?? '-'}`, payload);
|
|
48
|
+
}
|
|
49
|
+
// Simulate consistent transport latency across all NUI operations
|
|
50
|
+
if (latencyMs > 0) {
|
|
51
|
+
await new Promise((resolve) => setTimeout(resolve, latencyMs));
|
|
52
|
+
}
|
|
53
|
+
let handled = false;
|
|
54
|
+
// Check callback notifications
|
|
55
|
+
if (activeCallbacks[callbackName]) {
|
|
56
|
+
handled = true;
|
|
57
|
+
try {
|
|
58
|
+
activeCallbacks[callbackName](payload);
|
|
59
|
+
}
|
|
60
|
+
catch (e) {
|
|
61
|
+
console.error(`[NUI Mock] Error in callback handler '${callbackName}':`, e);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Check send handlers
|
|
65
|
+
if (eventName && activeSendHandlers[eventName]) {
|
|
66
|
+
handled = true;
|
|
67
|
+
try {
|
|
68
|
+
activeSendHandlers[eventName](payload);
|
|
69
|
+
}
|
|
70
|
+
catch (e) {
|
|
71
|
+
console.error(`[NUI Mock] Error in send handler '${eventName}':`, e);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Handle correlated request/response
|
|
75
|
+
if (eventName && activeRequests[eventName]) {
|
|
76
|
+
handled = true;
|
|
77
|
+
try {
|
|
78
|
+
const result = await activeRequests[eventName](payload);
|
|
79
|
+
// In FiveM CEF, HTTP POST to https://<resource>/<callback> is strictly an outgoing transport ACK;
|
|
80
|
+
// the native C# backend delivers the correlated response envelope asynchronously via
|
|
81
|
+
// SendNuiMessage -> window.postMessage.
|
|
82
|
+
if (correlationId) {
|
|
83
|
+
window.postMessage({
|
|
84
|
+
type: 'response',
|
|
85
|
+
id: correlationId,
|
|
86
|
+
success: true,
|
|
87
|
+
payload: result,
|
|
88
|
+
data: result
|
|
89
|
+
}, '*');
|
|
90
|
+
}
|
|
91
|
+
return new Response(JSON.stringify({ ok: true }), {
|
|
92
|
+
status: 200,
|
|
93
|
+
headers: { 'Content-Type': 'application/json' }
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
const errorEnvelope = {
|
|
98
|
+
code: err?.code ?? 'MOCK_ERROR',
|
|
99
|
+
message: err instanceof Error ? err.message : String(err)
|
|
100
|
+
};
|
|
101
|
+
if (correlationId) {
|
|
102
|
+
window.postMessage({
|
|
103
|
+
type: 'response',
|
|
104
|
+
id: correlationId,
|
|
105
|
+
success: false,
|
|
106
|
+
error: errorEnvelope
|
|
107
|
+
}, '*');
|
|
108
|
+
}
|
|
109
|
+
return new Response(JSON.stringify({
|
|
110
|
+
success: false,
|
|
111
|
+
error: errorEnvelope
|
|
112
|
+
}), {
|
|
113
|
+
status: 500,
|
|
114
|
+
headers: { 'Content-Type': 'application/json' }
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (!handled && log) {
|
|
119
|
+
const targetEvent = eventName ?? callbackName;
|
|
120
|
+
console.warn(`[NUI Mock] Unhandled NUI message: no mock handler registered for '${targetEvent}'.`);
|
|
121
|
+
}
|
|
122
|
+
return new Response(JSON.stringify({ ok: true }), {
|
|
123
|
+
status: 200,
|
|
124
|
+
headers: { 'Content-Type': 'application/json' }
|
|
125
|
+
});
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=interceptor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interceptor.js","sourceRoot":"","sources":["../../src/mock/interceptor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { NuiWindowAction } from '@fivem-nui/core';
|
|
2
|
+
/**
|
|
3
|
+
* Message types for synthetic NUI postMessage envelopes in the mock environment.
|
|
4
|
+
*/
|
|
5
|
+
export declare enum NuiMessageType {
|
|
6
|
+
/**
|
|
7
|
+
* Standard event message.
|
|
8
|
+
*/
|
|
9
|
+
Event = "event",
|
|
10
|
+
/**
|
|
11
|
+
* RPC request message.
|
|
12
|
+
*/
|
|
13
|
+
Request = "request",
|
|
14
|
+
/**
|
|
15
|
+
* RPC response message.
|
|
16
|
+
*/
|
|
17
|
+
Response = "response"
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Configuration options for {@link setupNuiMock}.
|
|
21
|
+
*/
|
|
22
|
+
export interface NuiMockOptions {
|
|
23
|
+
/**
|
|
24
|
+
* Expected FiveM resource name to intercept.
|
|
25
|
+
* If omitted, falls back to `getResourceName('')` or `'nui'`.
|
|
26
|
+
*/
|
|
27
|
+
resourceName?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Mock handlers for requests sent via `nui.request(eventName, payload)`.
|
|
30
|
+
* Return the response payload or a Promise resolving to it.
|
|
31
|
+
*/
|
|
32
|
+
requests?: Record<string, (payload: any) => Promise<any> | any>;
|
|
33
|
+
/**
|
|
34
|
+
* Mock callbacks registered with `nui.notify(callbackName, payload)`.
|
|
35
|
+
*/
|
|
36
|
+
callbacks?: Record<string, (payload: any) => void>;
|
|
37
|
+
/**
|
|
38
|
+
* Listeners invoked when UI sends fire-and-forget messages (`nui.send`).
|
|
39
|
+
*/
|
|
40
|
+
onSend?: Record<string, (payload: any) => void>;
|
|
41
|
+
/**
|
|
42
|
+
* Delay in milliseconds to simulate network latency in the browser.
|
|
43
|
+
* @defaultValue 50
|
|
44
|
+
*/
|
|
45
|
+
latencyMs?: number;
|
|
46
|
+
/**
|
|
47
|
+
* Whether to log mock events and requests in the browser console.
|
|
48
|
+
* @defaultValue true
|
|
49
|
+
*/
|
|
50
|
+
log?: boolean;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Controller interface returned by {@link setupNuiMock} for controlling the mock runtime.
|
|
54
|
+
*/
|
|
55
|
+
export interface NuiMockController {
|
|
56
|
+
/**
|
|
57
|
+
* Simulates an event dispatched from C#/server to NUI.
|
|
58
|
+
*
|
|
59
|
+
* @param eventName - Name of the simulated event.
|
|
60
|
+
* @param payload - Optional data payload.
|
|
61
|
+
* @param delayMs - Optional delay in milliseconds before dispatching.
|
|
62
|
+
*/
|
|
63
|
+
emitEvent(eventName: string, payload?: unknown, delayMs?: number): void;
|
|
64
|
+
/**
|
|
65
|
+
* Simulates a window lifecycle action (`'open'`, `'close'`, `'focus'`, `'blur'`).
|
|
66
|
+
*
|
|
67
|
+
* @param action - The window lifecycle action.
|
|
68
|
+
* @param windowName - Optional target window identifier.
|
|
69
|
+
* @param delayMs - Optional delay in milliseconds before dispatching.
|
|
70
|
+
*/
|
|
71
|
+
emitWindow(action: NuiWindowAction, windowName?: string, delayMs?: number): void;
|
|
72
|
+
/**
|
|
73
|
+
* Dynamically adds or updates a request mock handler.
|
|
74
|
+
*
|
|
75
|
+
* @param eventName - RPC request event name to intercept.
|
|
76
|
+
* @param handler - Handler function returning the mock response.
|
|
77
|
+
* @returns Unsubscribe function to remove the mock handler.
|
|
78
|
+
*/
|
|
79
|
+
mockRequest<TRequest = unknown, TResponse = unknown>(eventName: string, handler: (payload: TRequest) => Promise<TResponse> | TResponse): () => void;
|
|
80
|
+
/**
|
|
81
|
+
* Removes a mock request handler.
|
|
82
|
+
*
|
|
83
|
+
* @param eventName - Name of the request to remove.
|
|
84
|
+
*/
|
|
85
|
+
removeRequest(eventName: string): void;
|
|
86
|
+
/**
|
|
87
|
+
* Dynamically adds a listener for UI-sent messages (`nui.send`).
|
|
88
|
+
*
|
|
89
|
+
* @param eventName - Event name to listen for.
|
|
90
|
+
* @param handler - Callback invoked with the sent payload.
|
|
91
|
+
* @returns Unsubscribe function to remove the listener.
|
|
92
|
+
*/
|
|
93
|
+
onSend<T = unknown>(eventName: string, handler: (payload: T) => void): () => void;
|
|
94
|
+
/**
|
|
95
|
+
* Restores original window.fetch and cleans up mock handlers.
|
|
96
|
+
*/
|
|
97
|
+
destroy(): void;
|
|
98
|
+
}
|
|
99
|
+
export declare const NOOP_CONTROLLER: NuiMockController;
|
|
100
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/mock/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD;;GAEG;AACH,oBAAY,cAAc;IACxB;;OAEG;IACH,KAAK,UAAU;IAEf;;OAEG;IACH,OAAO,YAAY;IAEnB;;OAEG;IACH,QAAQ,aAAa;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;IAEhE;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI,CAAC,CAAC;IAEnD;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI,CAAC,CAAC;IAEhD;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;;;OAMG;IACH,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAExE;;;;;;OAMG;IACH,UAAU,CAAC,MAAM,EAAE,eAAe,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEjF;;;;;;OAMG;IACH,WAAW,CAAC,QAAQ,GAAG,OAAO,EAAE,SAAS,GAAG,OAAO,EACjD,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,OAAO,CAAC,SAAS,CAAC,GAAG,SAAS,GAC7D,MAAM,IAAI,CAAC;IAEd;;;;OAIG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAEvC;;;;;;OAMG;IACH,MAAM,CAAC,CAAC,GAAG,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAElF;;OAEG;IACH,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,eAAO,MAAM,eAAe,EAAE,iBAO7B,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message types for synthetic NUI postMessage envelopes in the mock environment.
|
|
3
|
+
*/
|
|
4
|
+
export var NuiMessageType;
|
|
5
|
+
(function (NuiMessageType) {
|
|
6
|
+
/**
|
|
7
|
+
* Standard event message.
|
|
8
|
+
*/
|
|
9
|
+
NuiMessageType["Event"] = "event";
|
|
10
|
+
/**
|
|
11
|
+
* RPC request message.
|
|
12
|
+
*/
|
|
13
|
+
NuiMessageType["Request"] = "request";
|
|
14
|
+
/**
|
|
15
|
+
* RPC response message.
|
|
16
|
+
*/
|
|
17
|
+
NuiMessageType["Response"] = "response";
|
|
18
|
+
})(NuiMessageType || (NuiMessageType = {}));
|
|
19
|
+
export const NOOP_CONTROLLER = {
|
|
20
|
+
emitEvent: () => { },
|
|
21
|
+
emitWindow: () => { },
|
|
22
|
+
mockRequest: () => () => { },
|
|
23
|
+
removeRequest: () => { },
|
|
24
|
+
onSend: () => () => { },
|
|
25
|
+
destroy: () => { }
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/mock/types.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,CAAN,IAAY,cAeX;AAfD,WAAY,cAAc;IACxB;;OAEG;IACH,iCAAe,CAAA;IAEf;;OAEG;IACH,qCAAmB,CAAA;IAEnB;;OAEG;IACH,uCAAqB,CAAA;AACvB,CAAC,EAfW,cAAc,KAAd,cAAc,QAezB;AAiGD,MAAM,CAAC,MAAM,eAAe,GAAsB;IAChD,SAAS,EAAE,GAAG,EAAE,GAAE,CAAC;IACnB,UAAU,EAAE,GAAG,EAAE,GAAE,CAAC;IACpB,WAAW,EAAE,GAAG,EAAE,CAAC,GAAG,EAAE,GAAE,CAAC;IAC3B,aAAa,EAAE,GAAG,EAAE,GAAE,CAAC;IACvB,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,EAAE,GAAE,CAAC;IACtB,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC;CAClB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks whether a given URL matches a targeted FiveM NUI endpoint:
|
|
3
|
+
* `https://<resourceName>/<callback>` or `https://cfx-nui-<resourceName>/<callback>`.
|
|
4
|
+
* Rejects localhost, IP addresses, ports, and external domains.
|
|
5
|
+
*/
|
|
6
|
+
export declare function isNuiUrl(url: string, targetResource: string): boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Extracts and safely decodes the resource name and callback endpoint from a NUI URL.
|
|
9
|
+
*/
|
|
10
|
+
export declare function parseNuiUrl(url: string): {
|
|
11
|
+
resourceName: string;
|
|
12
|
+
callbackName: string;
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=url.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../src/mock/url.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAgBrE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAUvF"}
|
package/dist/mock/url.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks whether a given URL matches a targeted FiveM NUI endpoint:
|
|
3
|
+
* `https://<resourceName>/<callback>` or `https://cfx-nui-<resourceName>/<callback>`.
|
|
4
|
+
* Rejects localhost, IP addresses, ports, and external domains.
|
|
5
|
+
*/
|
|
6
|
+
export function isNuiUrl(url, targetResource) {
|
|
7
|
+
try {
|
|
8
|
+
const parsed = new URL(url);
|
|
9
|
+
if (parsed.protocol !== 'https:' || parsed.port) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
const host = parsed.hostname.toLowerCase();
|
|
13
|
+
if (host === 'localhost' || host === '127.0.0.1' || host === '::1' || host.includes('.')) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
const expected = targetResource.toLowerCase();
|
|
17
|
+
return host === expected || host === `cfx-nui-${expected}`;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Extracts and safely decodes the resource name and callback endpoint from a NUI URL.
|
|
25
|
+
*/
|
|
26
|
+
export function parseNuiUrl(url) {
|
|
27
|
+
const urlParts = url.replace('https://', '').split('/');
|
|
28
|
+
const resourceName = urlParts[0];
|
|
29
|
+
let callbackName = urlParts[1] ?? 'nui';
|
|
30
|
+
try {
|
|
31
|
+
callbackName = decodeURIComponent(callbackName);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// malformed URI sequence, keep raw callbackName
|
|
35
|
+
}
|
|
36
|
+
return { resourceName, callbackName };
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=url.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.js","sourceRoot":"","sources":["../../src/mock/url.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAC,GAAW,EAAE,cAAsB;IAC1D,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;YAChD,OAAO,KAAK,CAAC;QACf,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;QAC3C,IAAI,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACzF,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,QAAQ,GAAG,cAAc,CAAC,WAAW,EAAE,CAAC;QAC9C,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,WAAW,QAAQ,EAAE,CAAC;IAC7D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACxD,MAAM,YAAY,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IACjC,IAAI,YAAY,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC;IACxC,IAAI,CAAC;QACH,YAAY,GAAG,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,gDAAgD;IAClD,CAAC;IACD,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC;AACxC,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type ClickOutsideTarget, type NuiClickOutsideParameter } from '../actions/clickOutside';
|
|
2
|
+
export type { ClickOutsideTarget, NuiClickOutsideParameter };
|
|
3
|
+
/**
|
|
4
|
+
* Detects clicks outside a target element and invokes a callback.
|
|
5
|
+
* Automatically cleans up listeners on unmount.
|
|
6
|
+
*
|
|
7
|
+
* @param target - Getter returning the target DOM element.
|
|
8
|
+
* @param getOptions - Getter returning options (onClickOutside, ignore, enabled).
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```svelte
|
|
12
|
+
* <script>
|
|
13
|
+
* let panelEl = $state<HTMLElement>();
|
|
14
|
+
* let triggerEl = $state<HTMLElement>();
|
|
15
|
+
* let isOpen = $state(false);
|
|
16
|
+
*
|
|
17
|
+
* useClickOutside(
|
|
18
|
+
* () => panelEl,
|
|
19
|
+
* () => ({
|
|
20
|
+
* onClickOutside: () => { isOpen = false; },
|
|
21
|
+
* ignore: [triggerEl],
|
|
22
|
+
* enabled: isOpen
|
|
23
|
+
* })
|
|
24
|
+
* );
|
|
25
|
+
* </script>
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare function useClickOutside(target: () => Element | null | undefined, getOptions: () => NuiClickOutsideParameter): void;
|
|
29
|
+
//# sourceMappingURL=clickOutside.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clickOutside.svelte.d.ts","sourceRoot":"","sources":["../../src/runes/clickOutside.svelte.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,EAC9B,MAAM,yBAAyB,CAAC;AAEjC,YAAY,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,CAAC;AAE7D;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,OAAO,GAAG,IAAI,GAAG,SAAS,EACxC,UAAU,EAAE,MAAM,wBAAwB,GACzC,IAAI,CAeN"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { untrack } from 'svelte';
|
|
2
|
+
import { createClickOutsideHandler } from '../actions/clickOutside';
|
|
3
|
+
/**
|
|
4
|
+
* Detects clicks outside a target element and invokes a callback.
|
|
5
|
+
* Automatically cleans up listeners on unmount.
|
|
6
|
+
*
|
|
7
|
+
* @param target - Getter returning the target DOM element.
|
|
8
|
+
* @param getOptions - Getter returning options (onClickOutside, ignore, enabled).
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```svelte
|
|
12
|
+
* <script>
|
|
13
|
+
* let panelEl = $state<HTMLElement>();
|
|
14
|
+
* let triggerEl = $state<HTMLElement>();
|
|
15
|
+
* let isOpen = $state(false);
|
|
16
|
+
*
|
|
17
|
+
* useClickOutside(
|
|
18
|
+
* () => panelEl,
|
|
19
|
+
* () => ({
|
|
20
|
+
* onClickOutside: () => { isOpen = false; },
|
|
21
|
+
* ignore: [triggerEl],
|
|
22
|
+
* enabled: isOpen
|
|
23
|
+
* })
|
|
24
|
+
* );
|
|
25
|
+
* </script>
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export function useClickOutside(target, getOptions) {
|
|
29
|
+
const handler = createClickOutsideHandler(target, getOptions);
|
|
30
|
+
$effect(() => {
|
|
31
|
+
if (!target())
|
|
32
|
+
return;
|
|
33
|
+
const capture = untrack(() => getOptions().capture ?? true);
|
|
34
|
+
const ac = new AbortController();
|
|
35
|
+
document.addEventListener('pointerdown', handler, {
|
|
36
|
+
capture,
|
|
37
|
+
signal: ac.signal
|
|
38
|
+
});
|
|
39
|
+
return () => ac.abort();
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=clickOutside.svelte.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clickOutside.svelte.js","sourceRoot":"","sources":["../../src/runes/clickOutside.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACjC,OAAO,EACL,yBAAyB,EAG1B,MAAM,yBAAyB,CAAC;AAIjC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,eAAe,CAC7B,MAAwC,EACxC,UAA0C;IAE1C,MAAM,OAAO,GAAG,yBAAyB,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAE9D,OAAO,CAAC,GAAG,EAAE;QACX,IAAI,CAAC,MAAM,EAAE;YAAE,OAAO;QAEtB,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,UAAU,EAAE,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC;QAC5D,MAAM,EAAE,GAAG,IAAI,eAAe,EAAE,CAAC;QACjC,QAAQ,CAAC,gBAAgB,CAAC,aAAa,EAAE,OAAO,EAAE;YAChD,OAAO;YACP,MAAM,EAAE,EAAE,CAAC,MAAM;SAClB,CAAC,CAAC;QAEH,OAAO,GAAG,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type NuiEscapeParameter, NuiEscapeTarget } from '../actions/escape';
|
|
2
|
+
export { NuiEscapeTarget };
|
|
3
|
+
export type NuiEscapeComposableParameter = Omit<NuiEscapeParameter, 'target'> & {
|
|
4
|
+
/**
|
|
5
|
+
* Where to attach the global listener:
|
|
6
|
+
* - `'document'` / `NuiEscapeTarget.Document`: listens globally on the document (default)
|
|
7
|
+
* - `'window'` / `NuiEscapeTarget.Window`: listens globally on the window
|
|
8
|
+
*
|
|
9
|
+
* Note: To scope Escape to a specific DOM element with focus, use the `nuiEscapeAction` action instead.
|
|
10
|
+
* @defaultValue NuiEscapeTarget.Document
|
|
11
|
+
*/
|
|
12
|
+
target?: NuiEscapeTarget.Window | NuiEscapeTarget.Document;
|
|
13
|
+
};
|
|
14
|
+
export type NuiEscapeComposableInput = ((event: KeyboardEvent) => void) | NuiEscapeComposableParameter;
|
|
15
|
+
/**
|
|
16
|
+
* Listens for the Escape key and invokes a callback.
|
|
17
|
+
* Automatically cleans up listeners on unmount.
|
|
18
|
+
*
|
|
19
|
+
* @param getOptions - Getter returning options or an onEscape callback.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```svelte
|
|
23
|
+
* <script>
|
|
24
|
+
* let isOpen = $state(false);
|
|
25
|
+
*
|
|
26
|
+
* useEscape(() => ({
|
|
27
|
+
* onEscape: () => { isOpen = false; },
|
|
28
|
+
* enabled: isOpen
|
|
29
|
+
* }));
|
|
30
|
+
* </script>
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare function useEscape(getOptions: () => NuiEscapeComposableInput): void;
|
|
34
|
+
//# sourceMappingURL=escape.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"escape.svelte.d.ts","sourceRoot":"","sources":["../../src/runes/escape.svelte.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,kBAAkB,EACvB,eAAe,EAChB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,eAAe,EAAE,CAAC;AAE3B,MAAM,MAAM,4BAA4B,GAAG,IAAI,CAAC,kBAAkB,EAAE,QAAQ,CAAC,GAAG;IAC9E;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC,MAAM,GAAG,eAAe,CAAC,QAAQ,CAAC;CAC5D,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAChC,CAAC,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC,GAChC,4BAA4B,CAAC;AAEjC;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,SAAS,CACvB,UAAU,EAAE,MAAM,wBAAwB,GACzC,IAAI,CAwBN"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { untrack } from 'svelte';
|
|
2
|
+
import { createEscapeHandler, toEscapeOptions, NuiEscapeTarget } from '../actions/escape';
|
|
3
|
+
export { NuiEscapeTarget };
|
|
4
|
+
/**
|
|
5
|
+
* Listens for the Escape key and invokes a callback.
|
|
6
|
+
* Automatically cleans up listeners on unmount.
|
|
7
|
+
*
|
|
8
|
+
* @param getOptions - Getter returning options or an onEscape callback.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```svelte
|
|
12
|
+
* <script>
|
|
13
|
+
* let isOpen = $state(false);
|
|
14
|
+
*
|
|
15
|
+
* useEscape(() => ({
|
|
16
|
+
* onEscape: () => { isOpen = false; },
|
|
17
|
+
* enabled: isOpen
|
|
18
|
+
* }));
|
|
19
|
+
* </script>
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function useEscape(getOptions) {
|
|
23
|
+
const normalized = () => toEscapeOptions(getOptions());
|
|
24
|
+
const handler = createEscapeHandler(normalized);
|
|
25
|
+
$effect(() => {
|
|
26
|
+
// Intentionally untracked: attach the DOM listener once upon mount,
|
|
27
|
+
// preserving event listener order. Reactive state (`enabled`, `onEscape`, etc.)
|
|
28
|
+
// is read lazily on each keydown event. Cleanup is called on component unmount.
|
|
29
|
+
const { target, capture } = untrack(() => {
|
|
30
|
+
const o = normalized();
|
|
31
|
+
return {
|
|
32
|
+
target: o?.target === NuiEscapeTarget.Window ? window : document,
|
|
33
|
+
capture: o?.capture ?? false
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
const ac = new AbortController();
|
|
37
|
+
target.addEventListener('keydown', handler, {
|
|
38
|
+
capture,
|
|
39
|
+
signal: ac.signal
|
|
40
|
+
});
|
|
41
|
+
return () => ac.abort();
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=escape.svelte.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"escape.svelte.js","sourceRoot":"","sources":["../../src/runes/escape.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACjC,OAAO,EACL,mBAAmB,EACnB,eAAe,EAEf,eAAe,EAChB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,eAAe,EAAE,CAAC;AAkB3B;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,SAAS,CACvB,UAA0C;IAE1C,MAAM,UAAU,GAAG,GAAG,EAAE,CAAC,eAAe,CAA+B,UAAU,EAAE,CAAC,CAAC;IACrF,MAAM,OAAO,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC;IAEhD,OAAO,CAAC,GAAG,EAAE;QACX,oEAAoE;QACpE,gFAAgF;QAChF,gFAAgF;QAChF,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE;YACvC,MAAM,CAAC,GAAG,UAAU,EAAE,CAAC;YACvB,OAAO;gBACL,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ;gBAChE,OAAO,EAAE,CAAC,EAAE,OAAO,IAAI,KAAK;aAC7B,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,MAAM,EAAE,GAAG,IAAI,eAAe,EAAE,CAAC;QACjC,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,OAAwB,EAAE;YAC3D,OAAO;YACP,MAAM,EAAE,EAAE,CAAC,MAAM;SAClB,CAAC,CAAC;QAEH,OAAO,GAAG,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type NuiFocusTrapInput, type NuiFocusTrapOptions, type FocusTrapAllowOutsideTarget, type FocusTrapAllowOutside } from '../actions/focusTrap';
|
|
2
|
+
export type { NuiFocusTrapInput, NuiFocusTrapOptions, FocusTrapAllowOutsideTarget, FocusTrapAllowOutside };
|
|
3
|
+
/**
|
|
4
|
+
* Traps Tab key navigation inside a container element.
|
|
5
|
+
* Automatically activates and tears down as the target element mounts/unmounts.
|
|
6
|
+
*
|
|
7
|
+
* @param target - Getter returning the container element.
|
|
8
|
+
* @param getOptions - Optional getter returning options or boolean enabled flag.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```svelte
|
|
12
|
+
* <script>
|
|
13
|
+
* let modalEl = $state<HTMLElement>();
|
|
14
|
+
* let isOpen = $state(false);
|
|
15
|
+
*
|
|
16
|
+
* useFocusTrap(() => modalEl, () => isOpen);
|
|
17
|
+
* </script>
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export declare function useFocusTrap(target: () => HTMLElement | null | undefined, getOptions?: () => NuiFocusTrapInput): void;
|
|
21
|
+
//# sourceMappingURL=focusTrap.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"focusTrap.svelte.d.ts","sourceRoot":"","sources":["../../src/runes/focusTrap.svelte.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,2BAA2B,EAChC,KAAK,qBAAqB,EAC3B,MAAM,sBAAsB,CAAC;AAE9B,YAAY,EACV,iBAAiB,EACjB,mBAAmB,EACnB,2BAA2B,EAC3B,qBAAqB,EACtB,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,WAAW,GAAG,IAAI,GAAG,SAAS,EAC5C,UAAU,CAAC,EAAE,MAAM,iBAAiB,GACnC,IAAI,CAmBN"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { untrack } from 'svelte';
|
|
2
|
+
import { nuiFocusTrap } from '../actions/focusTrap';
|
|
3
|
+
/**
|
|
4
|
+
* Traps Tab key navigation inside a container element.
|
|
5
|
+
* Automatically activates and tears down as the target element mounts/unmounts.
|
|
6
|
+
*
|
|
7
|
+
* @param target - Getter returning the container element.
|
|
8
|
+
* @param getOptions - Optional getter returning options or boolean enabled flag.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```svelte
|
|
12
|
+
* <script>
|
|
13
|
+
* let modalEl = $state<HTMLElement>();
|
|
14
|
+
* let isOpen = $state(false);
|
|
15
|
+
*
|
|
16
|
+
* useFocusTrap(() => modalEl, () => isOpen);
|
|
17
|
+
* </script>
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export function useFocusTrap(target, getOptions) {
|
|
21
|
+
let action;
|
|
22
|
+
$effect(() => {
|
|
23
|
+
const node = target();
|
|
24
|
+
if (!node)
|
|
25
|
+
return;
|
|
26
|
+
action = nuiFocusTrap(node, untrack(() => getOptions?.() ?? true));
|
|
27
|
+
return () => {
|
|
28
|
+
action?.destroy?.();
|
|
29
|
+
action = undefined;
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
$effect(() => {
|
|
33
|
+
const opts = getOptions?.() ?? true;
|
|
34
|
+
action?.update?.(opts);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=focusTrap.svelte.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"focusTrap.svelte.js","sourceRoot":"","sources":["../../src/runes/focusTrap.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACjC,OAAO,EACL,YAAY,EAKb,MAAM,sBAAsB,CAAC;AAS9B;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,YAAY,CAC1B,MAA4C,EAC5C,UAAoC;IAEpC,IAAI,MAAmD,CAAC;IAExD,OAAO,CAAC,GAAG,EAAE;QACX,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC;QACtB,IAAI,CAAC,IAAI;YAAE,OAAO;QAElB,MAAM,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,UAAU,EAAE,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC;QAEnE,OAAO,GAAG,EAAE;YACV,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC;YACpB,MAAM,GAAG,SAAS,CAAC;QACrB,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,GAAG,EAAE;QACX,MAAM,IAAI,GAAG,UAAU,EAAE,EAAE,IAAI,IAAI,CAAC;QACpC,MAAM,EAAE,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { useNuiState, NuiStateController, type NuiState, type NuiStateOptions } from './state.svelte';
|
|
2
|
+
export { useNuiQuery, NuiQueryController, type NuiQuery, type NuiQueryOptions, NuiQueryStatus } from './query.svelte';
|
|
3
|
+
export { useNuiWindow, NuiWindowController, type NuiWindow, type NuiWindowOptions, NuiWindowActionType, NuiWindowNotification } from './window.svelte';
|
|
4
|
+
export { useClickOutside } from './clickOutside.svelte';
|
|
5
|
+
export { useEscape, type NuiEscapeComposableParameter, type NuiEscapeComposableInput } from './escape.svelte';
|
|
6
|
+
export { useFocusTrap } from './focusTrap.svelte';
|
|
7
|
+
export { useLayer, type NuiLayerComposableInput } from './layer.svelte';
|
|
8
|
+
//# sourceMappingURL=index.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.svelte.d.ts","sourceRoot":"","sources":["../../src/runes/index.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,KAAK,QAAQ,EACb,KAAK,eAAe,EACrB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,KAAK,QAAQ,EACb,KAAK,eAAe,EACpB,cAAc,EACf,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,KAAK,SAAS,EACd,KAAK,gBAAgB,EACrB,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,EACL,SAAS,EACT,KAAK,4BAA4B,EACjC,KAAK,wBAAwB,EAC9B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EACL,QAAQ,EACR,KAAK,uBAAuB,EAC7B,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { useNuiState, NuiStateController } from './state.svelte';
|
|
2
|
+
export { useNuiQuery, NuiQueryController, NuiQueryStatus } from './query.svelte';
|
|
3
|
+
export { useNuiWindow, NuiWindowController, NuiWindowActionType, NuiWindowNotification } from './window.svelte';
|
|
4
|
+
export { useClickOutside } from './clickOutside.svelte';
|
|
5
|
+
export { useEscape } from './escape.svelte';
|
|
6
|
+
export { useFocusTrap } from './focusTrap.svelte';
|
|
7
|
+
export { useLayer } from './layer.svelte';
|
|
8
|
+
//# sourceMappingURL=index.svelte.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.svelte.js","sourceRoot":"","sources":["../../src/runes/index.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,kBAAkB,EAGnB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,WAAW,EACX,kBAAkB,EAGlB,cAAc,EACf,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,YAAY,EACZ,mBAAmB,EAGnB,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,EACL,SAAS,EAGV,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EACL,QAAQ,EAET,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { NuiLayerDismissReason, type NuiLayerInput, type NuiLayerOptions, type LayerInstance } from '../actions/layer';
|
|
2
|
+
export { NuiLayerDismissReason };
|
|
3
|
+
export type { NuiLayerInput, NuiLayerOptions, LayerInstance };
|
|
4
|
+
/**
|
|
5
|
+
* Reactive options getter for `useLayer`.
|
|
6
|
+
*/
|
|
7
|
+
export type NuiLayerComposableInput = () => NuiLayerOptions;
|
|
8
|
+
/**
|
|
9
|
+
* Manages an overlay layer (modal, dialog, drawer) in FiveM NUI.
|
|
10
|
+
* Coordinates Escape key, outside click, and focus trapping through a central layer stack.
|
|
11
|
+
*
|
|
12
|
+
* @param target - Getter returning the container element.
|
|
13
|
+
* @param getOptions - Optional getter returning layer options.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```svelte
|
|
17
|
+
* <script>
|
|
18
|
+
* let modalEl = $state<HTMLElement>();
|
|
19
|
+
* let isOpen = $state(false);
|
|
20
|
+
*
|
|
21
|
+
* useLayer(
|
|
22
|
+
* () => modalEl,
|
|
23
|
+
* () => ({ onDismiss: () => { isOpen = false; }, enabled: isOpen })
|
|
24
|
+
* );
|
|
25
|
+
* </script>
|
|
26
|
+
*
|
|
27
|
+
* {#if isOpen}
|
|
28
|
+
* <div bind:this={modalEl} class="modal">
|
|
29
|
+
* <h2>Modal</h2>
|
|
30
|
+
* <button onclick={() => { isOpen = false; }}>Close</button>
|
|
31
|
+
* </div>
|
|
32
|
+
* {/if}
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export declare function useLayer(target: () => HTMLElement | null | undefined, getOptions?: () => NuiLayerOptions): void;
|
|
36
|
+
//# sourceMappingURL=layer.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layer.svelte.d.ts","sourceRoot":"","sources":["../../src/runes/layer.svelte.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,qBAAqB,EACrB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,aAAa,EACnB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,qBAAqB,EACtB,CAAC;AAEF,YAAY,EACV,aAAa,EACb,eAAe,EACf,aAAa,EACd,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAAG,MAAM,eAAe,CAAC;AAE5D;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,QAAQ,CACtB,MAAM,EAAE,MAAM,WAAW,GAAG,IAAI,GAAG,SAAS,EAC5C,UAAU,CAAC,EAAE,MAAM,eAAe,GACjC,IAAI,CAmBN"}
|