@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
package/README.md
CHANGED
|
@@ -1,3 +1,285 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @fivem-nui/svelte
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Svelte adapter and reactive toolkit for FiveM NUI communication, engineered for **Svelte 5** Runes (`$state`, `$derived`, `$effect`).
|
|
4
|
+
|
|
5
|
+
[](https://github.com/rian-be/FiveM.Nui/blob/main/LICENSE)
|
|
6
|
+
[](#requirements)
|
|
7
|
+
[](#requirements)
|
|
8
|
+
[](https://svelte.dev)
|
|
9
|
+
[](#contributing)
|
|
10
|
+
|
|
11
|
+
`@fivem-nui/svelte` provides seamless, reactive integration between client C# scripts (.NET 10) and Svelte 5 NUI panels. It wraps `@fivem-nui/core` with native Svelte 5 Runes, DOM actions (`use:action`), keyboard shortcuts, focus trapping, and browser mocking for rapid local development.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @fivem-nui/svelte @fivem-nui/core svelte
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
> **Note:** `@fivem-nui/core` (`>=1.0.0`) and `svelte` (`>=5.0.0`) are peer dependencies.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Package Entry Points
|
|
26
|
+
|
|
27
|
+
The package exposes modular subpaths for tree-shaking and isolation:
|
|
28
|
+
|
|
29
|
+
| Import Path | Description | Contents |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| `@fivem-nui/svelte` | Main library | Runes (`useNuiState`, `useNuiQuery`, `useNuiWindow`), client, context, error re-exports |
|
|
32
|
+
| `@fivem-nui/svelte/actions` | DOM actions | `nuiEscapeAction`, `nuiFocusTrap`, `nuiClickOutside`, `nuiLayer` |
|
|
33
|
+
| `@fivem-nui/svelte/mock` | Browser mocking | `setupNuiMock`, mock transport and window emission helpers |
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { nui, useNuiState, useNuiQuery } from '@fivem-nui/svelte';
|
|
37
|
+
import { nuiFocusTrap, nuiClickOutside } from '@fivem-nui/svelte/actions';
|
|
38
|
+
import { setupNuiMock } from '@fivem-nui/svelte/mock';
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Runes API
|
|
44
|
+
|
|
45
|
+
### 1. `useNuiState` (Reactive C# → UI Events)
|
|
46
|
+
|
|
47
|
+
Synchronizes incoming NUI events with a reactive `$state` rune, supporting functional updates:
|
|
48
|
+
|
|
49
|
+
```svelte
|
|
50
|
+
<script lang="ts">
|
|
51
|
+
import { useNuiState } from '@fivem-nui/svelte';
|
|
52
|
+
|
|
53
|
+
interface PlayerStats {
|
|
54
|
+
health: number;
|
|
55
|
+
armor: number;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Automatically listens to 'player:stats' and unregisters on component unmount
|
|
59
|
+
const stats = useNuiState<PlayerStats>('player:stats', { health: 100, armor: 0 });
|
|
60
|
+
|
|
61
|
+
function heal(amount: number) {
|
|
62
|
+
stats.update(prev => ({ ...prev, health: Math.min(100, prev.health + amount) }));
|
|
63
|
+
}
|
|
64
|
+
</script>
|
|
65
|
+
|
|
66
|
+
<div class="hud">
|
|
67
|
+
<p>Health: {stats.value.health}%</p>
|
|
68
|
+
<p>Armor: {stats.value.armor}%</p>
|
|
69
|
+
{#if stats.updatedAt}
|
|
70
|
+
<small>Last updated: {new Date(stats.updatedAt).toLocaleTimeString()}</small>
|
|
71
|
+
{/if}
|
|
72
|
+
</div>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
### 2. `useNuiQuery` (Reactive UI → C# Request/Response)
|
|
78
|
+
|
|
79
|
+
Manages outgoing `nui.request` calls with automated reactive states (`loading`, `error`, `data`), retries, and cancellation:
|
|
80
|
+
|
|
81
|
+
```svelte
|
|
82
|
+
<script lang="ts">
|
|
83
|
+
import { useNuiQuery } from '@fivem-nui/svelte';
|
|
84
|
+
|
|
85
|
+
interface BuyRequest { itemId: string; quantity: number; }
|
|
86
|
+
interface BuyResponse { success: boolean; balance: number; }
|
|
87
|
+
|
|
88
|
+
const buy = useNuiQuery<BuyRequest, BuyResponse>('shop:buy');
|
|
89
|
+
</script>
|
|
90
|
+
|
|
91
|
+
<button
|
|
92
|
+
onclick={() => buy.execute({ itemId: 'water', quantity: 2 })}
|
|
93
|
+
disabled={buy.loading}
|
|
94
|
+
>
|
|
95
|
+
{buy.loading ? 'Purchasing...' : 'Buy Water'}
|
|
96
|
+
</button>
|
|
97
|
+
|
|
98
|
+
<button onclick={() => buy.refetch()} disabled={!buy.called || buy.loading}>
|
|
99
|
+
Repeat Purchase
|
|
100
|
+
</button>
|
|
101
|
+
|
|
102
|
+
{#if buy.error}
|
|
103
|
+
<p class="error">{buy.error.message}</p>
|
|
104
|
+
{/if}
|
|
105
|
+
|
|
106
|
+
{#if buy.data}
|
|
107
|
+
<p>Remaining Balance: ${buy.data.balance}</p>
|
|
108
|
+
{/if}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
### 3. `useNuiWindow` (Window Visibility & Lifecycle)
|
|
114
|
+
|
|
115
|
+
Synchronizes window visibility with `onWindow` events emitted from C# (`open`, `close`, `focus`, `unfocus`) and handles the `Escape` key by default:
|
|
116
|
+
|
|
117
|
+
```svelte
|
|
118
|
+
<script lang="ts">
|
|
119
|
+
import { useNuiWindow } from '@fivem-nui/svelte';
|
|
120
|
+
|
|
121
|
+
// closeOnEscape is enabled by default (true)
|
|
122
|
+
const panel = useNuiWindow('inventory', {
|
|
123
|
+
onClose: () => console.log('Window closed')
|
|
124
|
+
});
|
|
125
|
+
</script>
|
|
126
|
+
|
|
127
|
+
{#if panel.visible}
|
|
128
|
+
<div class="panel">
|
|
129
|
+
<h2>Inventory</h2>
|
|
130
|
+
<button onclick={() => panel.close()}>Close (or press ESC)</button>
|
|
131
|
+
</div>
|
|
132
|
+
{/if}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## UI Utilities, Keyboard & Svelte Actions
|
|
138
|
+
|
|
139
|
+
The package provides two integration paths: **Svelte Actions (`use:action`)** for declarative DOM binding and **Runes Composables** for programmatic control in `<script>`:
|
|
140
|
+
|
|
141
|
+
| Feature | Svelte Action (`use:action`) | Runes Composable | Use Case |
|
|
142
|
+
|---|---|---|---|
|
|
143
|
+
| **Escape Key** | `nuiEscapeAction` | `useEscape()` / `useNuiEscape()` | Close modals or panels on ESC |
|
|
144
|
+
| **Focus Trap** | `nuiFocusTrap` | `useFocusTrap()` | Prevent keyboard Tab key from escaping modal |
|
|
145
|
+
| **Click Outside** | `nuiClickOutside` | `useClickOutside()` | Dismiss dropdowns and context menus |
|
|
146
|
+
| **Layer Stack (Z-Index)** | `nuiLayer` | `useLayer()` | Manage stacking order of nested modals |
|
|
147
|
+
| **Keyboard Shortcuts** | — | `useNuiKey()` | Listen for custom hotkeys (e.g., F1, Enter) |
|
|
148
|
+
|
|
149
|
+
### 1. Keyboard Shortcuts (`useNuiEscape` & `useNuiKey`)
|
|
150
|
+
|
|
151
|
+
```svelte
|
|
152
|
+
<script lang="ts">
|
|
153
|
+
import { useNuiEscape, useNuiKey, nui } from '@fivem-nui/svelte';
|
|
154
|
+
|
|
155
|
+
// Cleaned up automatically on component unmount
|
|
156
|
+
useNuiEscape(() => nui.notify('close'));
|
|
157
|
+
|
|
158
|
+
// Listen to arbitrary keys with optional condition:
|
|
159
|
+
useNuiKey('F1', () => console.log('Help toggled'), { enabled: () => true });
|
|
160
|
+
</script>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Or declaratively in template:
|
|
164
|
+
```svelte
|
|
165
|
+
<div use:nuiEscapeAction={() => panel.close()}>...</div>
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### 2. Focus Trap (`nuiFocusTrap` / `useFocusTrap`)
|
|
169
|
+
|
|
170
|
+
Keeps keyboard Tab focus trapped inside the active dialog:
|
|
171
|
+
|
|
172
|
+
```svelte
|
|
173
|
+
<script lang="ts">
|
|
174
|
+
import { nuiFocusTrap } from '@fivem-nui/svelte/actions';
|
|
175
|
+
</script>
|
|
176
|
+
|
|
177
|
+
<div class="modal-dialog" use:nuiFocusTrap>
|
|
178
|
+
<input type="text" placeholder="Character Name..." />
|
|
179
|
+
<button>Save</button>
|
|
180
|
+
</div>
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### 3. Click Outside (`nuiClickOutside` / `useClickOutside`)
|
|
184
|
+
|
|
185
|
+
Automatically closes popups or dropdowns when clicking outside their element:
|
|
186
|
+
|
|
187
|
+
```svelte
|
|
188
|
+
<script lang="ts">
|
|
189
|
+
import { nuiClickOutside } from '@fivem-nui/svelte/actions';
|
|
190
|
+
|
|
191
|
+
let open = $state(true);
|
|
192
|
+
</script>
|
|
193
|
+
|
|
194
|
+
{#if open}
|
|
195
|
+
<div class="dropdown" use:nuiClickOutside={() => (open = false)}>
|
|
196
|
+
Menu Content...
|
|
197
|
+
</div>
|
|
198
|
+
{/if}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Environment Detection
|
|
204
|
+
|
|
205
|
+
Detect whether the UI is running inside GTA V / FiveM CEF or in a standard browser:
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
import { isNui, isBrowser, getResourceName } from '@fivem-nui/svelte';
|
|
209
|
+
|
|
210
|
+
if (isBrowser()) {
|
|
211
|
+
console.log('Running locally in browser (outside FiveM CEF)');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
console.log('Resource Name:', getResourceName());
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Browser Mocking (`setupNuiMock`)
|
|
220
|
+
|
|
221
|
+
Test and style your UI panels in Google Chrome (`npm run dev`) with instant Vite HMR without launching FiveM:
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
import { isBrowser, setupNuiMock } from '@fivem-nui/svelte/mock';
|
|
225
|
+
|
|
226
|
+
if (isBrowser()) {
|
|
227
|
+
setupNuiMock({
|
|
228
|
+
requests: {
|
|
229
|
+
'shop:buy': async (payload) => {
|
|
230
|
+
return { success: true, balance: 4500, bought: payload.itemId };
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
callbacks: {
|
|
234
|
+
'close': () => console.log('Panel closed by UI'),
|
|
235
|
+
'ready': () => console.log('Panel ready acknowledged')
|
|
236
|
+
},
|
|
237
|
+
onSend: {
|
|
238
|
+
'comps:log': (toast) => console.log('Toast sent from UI:', toast)
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// You can also simulate incoming C# traffic from browser DevTools:
|
|
243
|
+
// window.__nuiMock.emitEvent('player:stats', { health: 80, armor: 50 });
|
|
244
|
+
// window.__nuiMock.emitWindow('open', 'inventory');
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## Svelte Context & Custom Clients
|
|
251
|
+
|
|
252
|
+
When you need an isolated NUI client passed down the component hierarchy:
|
|
253
|
+
|
|
254
|
+
```svelte
|
|
255
|
+
<script lang="ts">
|
|
256
|
+
import { setNuiClient, SvelteNuiClient } from '@fivem-nui/svelte';
|
|
257
|
+
|
|
258
|
+
const customClient = new SvelteNuiClient({ resource: 'my_resource' });
|
|
259
|
+
setNuiClient(customClient);
|
|
260
|
+
</script>
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
In any descendant component:
|
|
264
|
+
```svelte
|
|
265
|
+
<script lang="ts">
|
|
266
|
+
import { useNuiClient } from '@fivem-nui/svelte';
|
|
267
|
+
|
|
268
|
+
const client = useNuiClient();
|
|
269
|
+
</script>
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Re-exports & Error Handling
|
|
275
|
+
|
|
276
|
+
`@fivem-nui/svelte` directly re-exports the entire `@fivem-nui/error` toolkit and core contract types:
|
|
277
|
+
|
|
278
|
+
- **Error handling:** `installGlobalErrorHandlers`, `withRetry`, `globalErrorHandler`, `NuiErrorCategory`, `NuiErrorSeverity`, `captureRenderError`, `isAbortError`.
|
|
279
|
+
- **Core contracts:** `NuiEventMap`, `NuiEventKey`, `NuiEventRequest`, `NuiEventResponse`, `NuiMessage`, `NuiError`.
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## License
|
|
284
|
+
|
|
285
|
+
[MIT](../../../LICENSE) © 2026 Rian.be
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Action } from 'svelte/action';
|
|
2
|
+
import { type NuiClickOutsideInput } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Svelte Action for reliable outside click detection in FiveM CEF.
|
|
5
|
+
*
|
|
6
|
+
* Uses `event.composedPath()` to eliminate the detached DOM node bug when
|
|
7
|
+
* inner elements mutate during click events.
|
|
8
|
+
*
|
|
9
|
+
* @remarks
|
|
10
|
+
* **Multiple Overlays in CEF**:
|
|
11
|
+
* Because `pointerdown` is used with `capture: true`, clicking into another open NUI panel
|
|
12
|
+
* will trigger an outside click unless that other panel is explicitly included in `ignore`.
|
|
13
|
+
*
|
|
14
|
+
* @param node - The DOM element to monitor for outside clicks.
|
|
15
|
+
* @param parameter - Callback function or configuration options object.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```svelte
|
|
19
|
+
* <div use:nuiClickOutside={() => (isOpen = false)}>...</div>
|
|
20
|
+
* <div use:nuiClickOutside={{ onClickOutside: close, ignore: [triggerEl, '.header-btn'] }}>...</div>
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare const nuiClickOutside: Action<HTMLElement, NuiClickOutsideInput>;
|
|
24
|
+
//# sourceMappingURL=clickOutside.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clickOutside.d.ts","sourceRoot":"","sources":["../../../src/actions/clickOutside/clickOutside.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EACL,KAAK,oBAAoB,EAE1B,MAAM,SAAS,CAAC;AAGjB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,WAAW,EAAE,oBAAoB,CAkDrE,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { toClickOutsideOptions } from './types';
|
|
2
|
+
import { createClickOutsideHandler } from './handler';
|
|
3
|
+
/**
|
|
4
|
+
* Svelte Action for reliable outside click detection in FiveM CEF.
|
|
5
|
+
*
|
|
6
|
+
* Uses `event.composedPath()` to eliminate the detached DOM node bug when
|
|
7
|
+
* inner elements mutate during click events.
|
|
8
|
+
*
|
|
9
|
+
* @remarks
|
|
10
|
+
* **Multiple Overlays in CEF**:
|
|
11
|
+
* Because `pointerdown` is used with `capture: true`, clicking into another open NUI panel
|
|
12
|
+
* will trigger an outside click unless that other panel is explicitly included in `ignore`.
|
|
13
|
+
*
|
|
14
|
+
* @param node - The DOM element to monitor for outside clicks.
|
|
15
|
+
* @param parameter - Callback function or configuration options object.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```svelte
|
|
19
|
+
* <div use:nuiClickOutside={() => (isOpen = false)}>...</div>
|
|
20
|
+
* <div use:nuiClickOutside={{ onClickOutside: close, ignore: [triggerEl, '.header-btn'] }}>...</div>
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export const nuiClickOutside = (node, parameter) => {
|
|
24
|
+
let currentParam = parameter;
|
|
25
|
+
let controller = null;
|
|
26
|
+
const handler = createClickOutsideHandler(() => node, () => toClickOutsideOptions(currentParam));
|
|
27
|
+
function getCapture() {
|
|
28
|
+
const opts = toClickOutsideOptions(currentParam);
|
|
29
|
+
return opts?.capture ?? true;
|
|
30
|
+
}
|
|
31
|
+
function setup() {
|
|
32
|
+
teardown();
|
|
33
|
+
controller = new AbortController();
|
|
34
|
+
document.addEventListener('pointerdown', handler, {
|
|
35
|
+
capture: getCapture(),
|
|
36
|
+
signal: controller.signal
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function teardown() {
|
|
40
|
+
if (controller) {
|
|
41
|
+
controller.abort();
|
|
42
|
+
controller = null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
setup();
|
|
46
|
+
return {
|
|
47
|
+
update(newParam) {
|
|
48
|
+
const prevCapture = getCapture();
|
|
49
|
+
currentParam = newParam;
|
|
50
|
+
// Only changes to `capture` require re-binding the document listener.
|
|
51
|
+
// All other options (`enabled`, `onClickOutside`, `ignore`)
|
|
52
|
+
// are read lazily on each pointerdown event via getOptions().
|
|
53
|
+
if (getCapture() !== prevCapture) {
|
|
54
|
+
setup();
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
destroy() {
|
|
58
|
+
teardown();
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=clickOutside.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clickOutside.js","sourceRoot":"","sources":["../../../src/actions/clickOutside/clickOutside.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,qBAAqB,EACtB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAC;AAEtD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,MAAM,eAAe,GAA8C,CACxE,IAAI,EACJ,SAAS,EACT,EAAE;IACF,IAAI,YAAY,GAAG,SAAS,CAAC;IAC7B,IAAI,UAAU,GAA2B,IAAI,CAAC;IAE9C,MAAM,OAAO,GAAG,yBAAyB,CACvC,GAAG,EAAE,CAAC,IAAI,EACV,GAAG,EAAE,CAAC,qBAAqB,CAAC,YAAY,CAAC,CAC1C,CAAC;IAEF,SAAS,UAAU;QACjB,MAAM,IAAI,GAAG,qBAAqB,CAAC,YAAY,CAAC,CAAC;QACjD,OAAO,IAAI,EAAE,OAAO,IAAI,IAAI,CAAC;IAC/B,CAAC;IAED,SAAS,KAAK;QACZ,QAAQ,EAAE,CAAC;QACX,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACnC,QAAQ,CAAC,gBAAgB,CAAC,aAAa,EAAE,OAAO,EAAE;YAChD,OAAO,EAAE,UAAU,EAAE;YACrB,MAAM,EAAE,UAAU,CAAC,MAAM;SAC1B,CAAC,CAAC;IACL,CAAC;IAED,SAAS,QAAQ;QACf,IAAI,UAAU,EAAE,CAAC;YACf,UAAU,CAAC,KAAK,EAAE,CAAC;YACnB,UAAU,GAAG,IAAI,CAAC;QACpB,CAAC;IACH,CAAC;IAED,KAAK,EAAE,CAAC;IAER,OAAO;QACL,MAAM,CAAC,QAAQ;YACb,MAAM,WAAW,GAAG,UAAU,EAAE,CAAC;YACjC,YAAY,GAAG,QAAQ,CAAC;YACxB,sEAAsE;YACtE,4DAA4D;YAC5D,8DAA8D;YAC9D,IAAI,UAAU,EAAE,KAAK,WAAW,EAAE,CAAC;gBACjC,KAAK,EAAE,CAAC;YACV,CAAC;QACH,CAAC;QACD,OAAO;YACL,QAAQ,EAAE,CAAC;QACb,CAAC;KACF,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ClickOutsideTarget, NuiClickOutsideParameter } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Resolves a DOM Element from an Element reference, CSS selector string, or getter function.
|
|
4
|
+
*/
|
|
5
|
+
export declare function resolveElement(target: ClickOutsideTarget): Element | null;
|
|
6
|
+
/**
|
|
7
|
+
* Creates a reusable pointerdown event handler for outside click detection.
|
|
8
|
+
* Shared between Svelte action and composable to eliminate duplication.
|
|
9
|
+
*/
|
|
10
|
+
export declare function createClickOutsideHandler(getNode: () => Element | null | undefined, getOptions: () => NuiClickOutsideParameter | undefined): (event: PointerEvent) => void;
|
|
11
|
+
//# sourceMappingURL=handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/actions/clickOutside/handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAC;AAE5E;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,GAAG,IAAI,CAQzE;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,MAAM,OAAO,GAAG,IAAI,GAAG,SAAS,EACzC,UAAU,EAAE,MAAM,wBAAwB,GAAG,SAAS,WAEvC,YAAY,UAyB5B"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves a DOM Element from an Element reference, CSS selector string, or getter function.
|
|
3
|
+
*/
|
|
4
|
+
export function resolveElement(target) {
|
|
5
|
+
if (typeof target === 'string') {
|
|
6
|
+
return document.querySelector(target);
|
|
7
|
+
}
|
|
8
|
+
if (typeof target === 'function') {
|
|
9
|
+
return target() ?? null;
|
|
10
|
+
}
|
|
11
|
+
return target ?? null;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Creates a reusable pointerdown event handler for outside click detection.
|
|
15
|
+
* Shared between Svelte action and composable to eliminate duplication.
|
|
16
|
+
*/
|
|
17
|
+
export function createClickOutsideHandler(getNode, getOptions) {
|
|
18
|
+
return (event) => {
|
|
19
|
+
const node = getNode();
|
|
20
|
+
const opts = getOptions();
|
|
21
|
+
if (!node?.isConnected || !opts)
|
|
22
|
+
return;
|
|
23
|
+
const enabled = typeof opts.enabled === 'function' ? opts.enabled() : opts.enabled ?? true;
|
|
24
|
+
if (!enabled || event.button !== 0)
|
|
25
|
+
return;
|
|
26
|
+
const path = event.composedPath();
|
|
27
|
+
if (path.includes(node))
|
|
28
|
+
return;
|
|
29
|
+
for (const item of opts.ignore ?? []) {
|
|
30
|
+
const el = resolveElement(item);
|
|
31
|
+
if (el && path.includes(el))
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const callback = opts.onClickOutside ?? opts.callback;
|
|
35
|
+
if (callback) {
|
|
36
|
+
try {
|
|
37
|
+
callback(event);
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
console.error('[NuiClickOutside] Error in onClickOutside callback:', err);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.js","sourceRoot":"","sources":["../../../src/actions/clickOutside/handler.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,MAA0B;IACvD,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC/B,OAAO,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IACxC,CAAC;IACD,IAAI,OAAO,MAAM,KAAK,UAAU,EAAE,CAAC;QACjC,OAAO,MAAM,EAAE,IAAI,IAAI,CAAC;IAC1B,CAAC;IACD,OAAO,MAAM,IAAI,IAAI,CAAC;AACxB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CACvC,OAAyC,EACzC,UAAsD;IAEtD,OAAO,CAAC,KAAmB,EAAE,EAAE;QAC7B,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;QAC1B,IAAI,CAAC,IAAI,EAAE,WAAW,IAAI,CAAC,IAAI;YAAE,OAAO;QAExC,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC;QAC3F,IAAI,CAAC,OAAO,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAE3C,MAAM,IAAI,GAAG,KAAK,CAAC,YAAY,EAAE,CAAC;QAClC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,OAAO;QAEhC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;YACrC,MAAM,EAAE,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;YAChC,IAAI,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAAE,OAAO;QACtC,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,QAAQ,CAAC;QACtD,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,CAAC;gBACH,QAAQ,CAAC,KAAK,CAAC,CAAC;YAClB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,qDAAqD,EAAE,GAAG,CAAC,CAAC;YAC5E,CAAC;QACH,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { nuiClickOutside } from './clickOutside';
|
|
2
|
+
export { type ClickOutsideTarget, type NuiClickOutsideParameter, type NuiClickOutsideInput, toClickOutsideOptions } from './types';
|
|
3
|
+
export { resolveElement, createClickOutsideHandler } from './handler';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/actions/clickOutside/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,qBAAqB,EACtB,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,cAAc,EACd,yBAAyB,EAC1B,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/actions/clickOutside/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAIL,qBAAqB,EACtB,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,cAAc,EACd,yBAAyB,EAC1B,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export type ClickOutsideTarget = Element | string | null | undefined | (() => Element | null | undefined);
|
|
2
|
+
export interface NuiClickOutsideParameter {
|
|
3
|
+
/**
|
|
4
|
+
* Callback invoked when a click or touch occurs outside the target node.
|
|
5
|
+
*/
|
|
6
|
+
onClickOutside?: (event: PointerEvent) => void;
|
|
7
|
+
/**
|
|
8
|
+
* @deprecated Use `onClickOutside` instead.
|
|
9
|
+
*/
|
|
10
|
+
callback?: (event: PointerEvent) => void;
|
|
11
|
+
/**
|
|
12
|
+
* Whether the outside click detector is active.
|
|
13
|
+
* Can be a static boolean or a reactive getter `() => boolean`.
|
|
14
|
+
* @defaultValue true
|
|
15
|
+
*/
|
|
16
|
+
enabled?: boolean | (() => boolean);
|
|
17
|
+
/**
|
|
18
|
+
* Elements, selectors, or getters to exclude from triggering outside click.
|
|
19
|
+
*
|
|
20
|
+
* @remarks
|
|
21
|
+
* **Trigger / Toggle Buttons**:
|
|
22
|
+
* Because `capture: true` listens on the capturing phase (`pointerdown` fires before `click`),
|
|
23
|
+
* clicking a toggle/trigger button without adding it to `ignore` will cause an immediate
|
|
24
|
+
* flicker (the outside click closes the popover during `pointerdown`, and the button's
|
|
25
|
+
* subsequent `click` immediately re-opens it). Always pass your toggle button in `ignore`.
|
|
26
|
+
*/
|
|
27
|
+
ignore?: ClickOutsideTarget[];
|
|
28
|
+
/**
|
|
29
|
+
* Listen during capture phase to intercept before bubbling mutations.
|
|
30
|
+
* @defaultValue true
|
|
31
|
+
*/
|
|
32
|
+
capture?: boolean;
|
|
33
|
+
}
|
|
34
|
+
export type NuiClickOutsideInput = ((event: PointerEvent) => void) | NuiClickOutsideParameter;
|
|
35
|
+
export declare function toClickOutsideOptions(param: NuiClickOutsideInput | undefined): NuiClickOutsideParameter | undefined;
|
|
36
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/actions/clickOutside/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAC1B,OAAO,GACP,MAAM,GACN,IAAI,GACJ,SAAS,GACT,CAAC,MAAM,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;AAEvC,MAAM,WAAW,wBAAwB;IACvC;;OAEG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IAE/C;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IAEzC;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC,CAAC;IAEpC;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAE9B;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,MAAM,oBAAoB,GAC5B,CAAC,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC,GAC/B,wBAAwB,CAAC;AAE7B,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,oBAAoB,GAAG,SAAS,GACtC,wBAAwB,GAAG,SAAS,CAGtC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/actions/clickOutside/types.ts"],"names":[],"mappings":"AAgDA,MAAM,UAAU,qBAAqB,CACnC,KAAuC;IAEvC,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,OAAO,OAAO,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;AACzE,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { Action } from 'svelte/action';
|
|
2
|
+
export type ClickOutsideTarget = Element | string | null | undefined | (() => Element | null | undefined);
|
|
3
|
+
export interface NuiClickOutsideParameter {
|
|
4
|
+
/**
|
|
5
|
+
* Callback invoked when a click or touch occurs outside the target node.
|
|
6
|
+
*/
|
|
7
|
+
onClickOutside?: (event: PointerEvent) => void;
|
|
8
|
+
/**
|
|
9
|
+
* @deprecated Use `onClickOutside` instead.
|
|
10
|
+
*/
|
|
11
|
+
callback?: (event: PointerEvent) => void;
|
|
12
|
+
/**
|
|
13
|
+
* Whether the outside click detector is active.
|
|
14
|
+
* Can be a static boolean or a reactive getter `() => boolean`.
|
|
15
|
+
* @defaultValue true
|
|
16
|
+
*/
|
|
17
|
+
enabled?: boolean | (() => boolean);
|
|
18
|
+
/**
|
|
19
|
+
* Elements, selectors, or getters to exclude from triggering outside click.
|
|
20
|
+
*
|
|
21
|
+
* @remarks
|
|
22
|
+
* **Trigger / Toggle Buttons**:
|
|
23
|
+
* Because `capture: true` listens on the capturing phase (`pointerdown` fires before `click`),
|
|
24
|
+
* clicking a toggle/trigger button without adding it to `ignore` will cause an immediate
|
|
25
|
+
* flicker (the outside click closes the popover during `pointerdown`, and the button's
|
|
26
|
+
* subsequent `click` immediately re-opens it). Always pass your toggle button in `ignore`.
|
|
27
|
+
*/
|
|
28
|
+
ignore?: ClickOutsideTarget[];
|
|
29
|
+
/**
|
|
30
|
+
* Listen during capture phase to intercept before bubbling mutations.
|
|
31
|
+
* @defaultValue true
|
|
32
|
+
*/
|
|
33
|
+
capture?: boolean;
|
|
34
|
+
}
|
|
35
|
+
export type NuiClickOutsideInput = ((event: PointerEvent) => void) | NuiClickOutsideParameter;
|
|
36
|
+
export declare function resolveElement(target: ClickOutsideTarget): Element | null;
|
|
37
|
+
/**
|
|
38
|
+
* Creates a reusable pointerdown event handler for outside click detection.
|
|
39
|
+
* Shared between Svelte action and composable to eliminate duplication.
|
|
40
|
+
*/
|
|
41
|
+
export declare function createClickOutsideHandler(getNode: () => Element | null | undefined, getOptions: () => NuiClickOutsideParameter | undefined): (event: PointerEvent) => void;
|
|
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 declare const nuiClickOutside: Action<HTMLElement, NuiClickOutsideInput>;
|
|
63
|
+
//# sourceMappingURL=clickOutside.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clickOutside.d.ts","sourceRoot":"","sources":["../../src/actions/clickOutside.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C,MAAM,MAAM,kBAAkB,GAC1B,OAAO,GACP,MAAM,GACN,IAAI,GACJ,SAAS,GACT,CAAC,MAAM,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;AAEvC,MAAM,WAAW,wBAAwB;IACvC;;OAEG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IAE/C;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IAEzC;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC,CAAC;IAEpC;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAE9B;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,MAAM,oBAAoB,GAC5B,CAAC,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC,GAC/B,wBAAwB,CAAC;AAE7B,wBAAgB,cAAc,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,GAAG,IAAI,CAQzE;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,MAAM,OAAO,GAAG,IAAI,GAAG,SAAS,EACzC,UAAU,EAAE,MAAM,wBAAwB,GAAG,SAAS,WAEvC,YAAY,UAyB5B;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,WAAW,EAAE,oBAAoB,CAuDrE,CAAC"}
|