@fivem-nui/svelte-components 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/README.md +101 -2
  2. package/dist/components/actions/NuiButton.svelte +292 -0
  3. package/dist/components/actions/NuiButton.svelte.d.ts +60 -0
  4. package/dist/components/data/NuiBadge.svelte +277 -0
  5. package/dist/components/data/NuiBadge.svelte.d.ts +74 -0
  6. package/dist/components/data/NuiCodeBlock.svelte +256 -0
  7. package/dist/components/data/NuiCodeBlock.svelte.d.ts +49 -0
  8. package/dist/components/data/NuiList.svelte +341 -0
  9. package/dist/components/data/NuiList.svelte.d.ts +52 -0
  10. package/dist/components/data/NuiProgressBar.svelte +264 -0
  11. package/dist/components/data/NuiProgressBar.svelte.d.ts +51 -0
  12. package/dist/components/data/NuiRichText.svelte +260 -0
  13. package/dist/components/data/NuiRichText.svelte.d.ts +33 -0
  14. package/dist/components/data/NuiTable.svelte +470 -0
  15. package/dist/components/data/NuiTable.svelte.d.ts +53 -0
  16. package/dist/components/data/chart/NuiChart.svelte +384 -0
  17. package/dist/components/data/chart/NuiChart.svelte.d.ts +49 -0
  18. package/dist/components/data/chart/color.d.ts +9 -0
  19. package/dist/components/data/chart/color.js +78 -0
  20. package/dist/components/data/chart/index.d.ts +10 -0
  21. package/dist/components/data/chart/index.js +10 -0
  22. package/dist/components/data/chart/registry.d.ts +14 -0
  23. package/dist/components/data/chart/registry.js +53 -0
  24. package/dist/components/data/chart/renderers/bar.d.ts +8 -0
  25. package/dist/components/data/chart/renderers/bar.js +79 -0
  26. package/dist/components/data/chart/renderers/base.d.ts +18 -0
  27. package/dist/components/data/chart/renderers/base.js +10 -0
  28. package/dist/components/data/chart/renderers/line.d.ts +8 -0
  29. package/dist/components/data/chart/renderers/line.js +89 -0
  30. package/dist/components/data/chart/renderers/pie.d.ts +10 -0
  31. package/dist/components/data/chart/renderers/pie.js +55 -0
  32. package/dist/components/data/chart/scale.d.ts +22 -0
  33. package/dist/components/data/chart/scale.js +101 -0
  34. package/dist/components/data/chart/types.d.ts +84 -0
  35. package/dist/components/data/chart/types.js +1 -0
  36. package/dist/components/data/chart/validator.d.ts +10 -0
  37. package/dist/components/data/chart/validator.js +82 -0
  38. package/dist/components/forms/NuiCheckbox.svelte +218 -0
  39. package/dist/components/forms/NuiCheckbox.svelte.d.ts +37 -0
  40. package/dist/components/forms/NuiDatepicker.svelte +755 -0
  41. package/dist/components/forms/NuiDatepicker.svelte.d.ts +34 -0
  42. package/dist/components/forms/NuiFileInput.svelte +1047 -0
  43. package/dist/components/forms/NuiFileInput.svelte.d.ts +93 -0
  44. package/dist/components/forms/NuiFileInput.types.d.ts +28 -0
  45. package/dist/components/forms/NuiFileInput.types.js +1 -0
  46. package/dist/components/forms/NuiForm.svelte +391 -0
  47. package/dist/components/forms/NuiForm.svelte.d.ts +69 -0
  48. package/dist/components/forms/NuiInput.svelte +296 -0
  49. package/dist/components/forms/NuiInput.svelte.d.ts +62 -0
  50. package/dist/components/forms/NuiRadioGroup.svelte +238 -0
  51. package/dist/components/forms/NuiRadioGroup.svelte.d.ts +47 -0
  52. package/dist/components/forms/NuiSelect.svelte +819 -0
  53. package/dist/components/forms/NuiSelect.svelte.d.ts +49 -0
  54. package/dist/components/layout/NuiCard.svelte +314 -0
  55. package/dist/components/layout/NuiCard.svelte.d.ts +48 -0
  56. package/dist/components/layout/NuiCardGrid.svelte +98 -0
  57. package/dist/components/layout/NuiCardGrid.svelte.d.ts +22 -0
  58. package/dist/components/layout/NuiFlex.svelte +121 -0
  59. package/dist/components/layout/NuiFlex.svelte.d.ts +23 -0
  60. package/dist/components/layout/NuiGrid.svelte +66 -0
  61. package/dist/components/layout/NuiGrid.svelte.d.ts +14 -0
  62. package/dist/components/layout/NuiStack.svelte +138 -0
  63. package/dist/components/layout/NuiStack.svelte.d.ts +26 -0
  64. package/dist/components/navigation/NuiAccordion.svelte +323 -0
  65. package/dist/components/navigation/NuiAccordion.svelte.d.ts +38 -0
  66. package/dist/components/navigation/NuiBreadcrumbs.svelte +133 -0
  67. package/dist/components/navigation/NuiBreadcrumbs.svelte.d.ts +19 -0
  68. package/dist/components/navigation/NuiMenu.svelte +596 -0
  69. package/dist/components/navigation/NuiMenu.svelte.d.ts +28 -0
  70. package/dist/components/navigation/NuiPagination.svelte +306 -0
  71. package/dist/components/navigation/NuiPagination.svelte.d.ts +40 -0
  72. package/dist/components/navigation/NuiSidebar.svelte +186 -0
  73. package/dist/components/navigation/NuiSidebar.svelte.d.ts +26 -0
  74. package/dist/components/navigation/NuiTabs.svelte +249 -0
  75. package/dist/components/navigation/NuiTabs.svelte.d.ts +31 -0
  76. package/dist/components/overlay/NuiDialog.svelte +118 -0
  77. package/dist/components/overlay/NuiDialog.svelte.d.ts +34 -0
  78. package/dist/components/overlay/NuiModal.svelte +992 -0
  79. package/dist/components/overlay/NuiModal.svelte.d.ts +72 -0
  80. package/dist/components/overlay/NuiTooltip.svelte +159 -0
  81. package/dist/components/overlay/NuiTooltip.svelte.d.ts +13 -0
  82. package/dist/composables/useClickOutside.d.ts +43 -0
  83. package/dist/composables/useClickOutside.js +73 -0
  84. package/dist/composables/useFormValidation.d.ts +23 -0
  85. package/dist/composables/useFormValidation.js +72 -0
  86. package/dist/composables/useIdle.svelte.d.ts +18 -0
  87. package/dist/composables/useIdle.svelte.js +144 -0
  88. package/dist/composables/useKeyboardShortcut.svelte.d.ts +114 -0
  89. package/dist/composables/useKeyboardShortcut.svelte.js +153 -0
  90. package/dist/composables/useLocalStorage.svelte.d.ts +22 -0
  91. package/dist/composables/useLocalStorage.svelte.js +105 -0
  92. package/dist/composables/useMediaQuery.svelte.d.ts +51 -0
  93. package/dist/composables/useMediaQuery.svelte.js +80 -0
  94. package/dist/index.d.ts +46 -0
  95. package/dist/index.js +42 -0
  96. package/package.json +58 -3
  97. package/src/components/actions/NuiButton.svelte +292 -0
  98. package/src/components/data/NuiBadge.svelte +277 -0
  99. package/src/components/data/NuiCodeBlock.svelte +256 -0
  100. package/src/components/data/NuiList.svelte +341 -0
  101. package/src/components/data/NuiProgressBar.svelte +264 -0
  102. package/src/components/data/NuiRichText.svelte +260 -0
  103. package/src/components/data/NuiTable.svelte +470 -0
  104. package/src/components/data/chart/NuiChart.svelte +384 -0
  105. package/src/components/data/chart/color.ts +83 -0
  106. package/src/components/data/chart/index.ts +10 -0
  107. package/src/components/data/chart/registry.ts +71 -0
  108. package/src/components/data/chart/renderers/bar.ts +90 -0
  109. package/src/components/data/chart/renderers/base.ts +32 -0
  110. package/src/components/data/chart/renderers/line.ts +99 -0
  111. package/src/components/data/chart/renderers/pie.ts +66 -0
  112. package/src/components/data/chart/scale.ts +139 -0
  113. package/src/components/data/chart/types.ts +98 -0
  114. package/src/components/data/chart/validator.ts +98 -0
  115. package/src/components/forms/NuiCheckbox.svelte +218 -0
  116. package/src/components/forms/NuiDatepicker.svelte +755 -0
  117. package/src/components/forms/NuiFileInput.svelte +1047 -0
  118. package/src/components/forms/NuiFileInput.types.ts +29 -0
  119. package/src/components/forms/NuiForm.svelte +391 -0
  120. package/src/components/forms/NuiInput.svelte +296 -0
  121. package/src/components/forms/NuiRadioGroup.svelte +238 -0
  122. package/src/components/forms/NuiSelect.svelte +819 -0
  123. package/src/components/layout/NuiCard.svelte +314 -0
  124. package/src/components/layout/NuiCardGrid.svelte +98 -0
  125. package/src/components/layout/NuiFlex.svelte +121 -0
  126. package/src/components/layout/NuiGrid.svelte +66 -0
  127. package/src/components/layout/NuiStack.svelte +138 -0
  128. package/src/components/navigation/NuiAccordion.svelte +323 -0
  129. package/src/components/navigation/NuiBreadcrumbs.svelte +133 -0
  130. package/src/components/navigation/NuiMenu.svelte +596 -0
  131. package/src/components/navigation/NuiPagination.svelte +306 -0
  132. package/src/components/navigation/NuiSidebar.svelte +186 -0
  133. package/src/components/navigation/NuiTabs.svelte +249 -0
  134. package/src/components/overlay/NuiDialog.svelte +118 -0
  135. package/src/components/overlay/NuiModal.svelte +992 -0
  136. package/src/components/overlay/NuiTooltip.svelte +159 -0
  137. package/src/composables/useClickOutside.ts +132 -0
  138. package/src/composables/useFormValidation.ts +105 -0
  139. package/src/composables/useIdle.svelte.ts +191 -0
  140. package/src/composables/useKeyboardShortcut.svelte.ts +278 -0
  141. package/src/composables/useLocalStorage.svelte.ts +137 -0
  142. package/src/composables/useMediaQuery.svelte.ts +102 -0
  143. package/src/index.ts +66 -0
package/README.md CHANGED
@@ -1,3 +1,102 @@
1
- # Temporary Holding Version
1
+ # @fivem-nui/svelte-components
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ > Modern, reactive UI component library for **FiveM NUI**, built completely with **Svelte 5** (Runes, Snippets, `$bindable`).
4
+
5
+ Designed for seamless integration with `@fivem-nui/core` and `@fivem-nui/svelte`. Styled using CSS custom properties (`--nui-*`) for effortless theming and custom styling across all in-game panels.
6
+
7
+ ---
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install @fivem-nui/svelte-components @fivem-nui/svelte @fivem-nui/core
13
+ ```
14
+
15
+ Peer Dependencies:
16
+ * `svelte`: `^5.20.0`
17
+ * `@fivem-nui/core`: `^1.0.0`
18
+ * `@fivem-nui/svelte`: `^1.0.0`
19
+
20
+ ---
21
+
22
+ ## Theming System (CSS Variables)
23
+
24
+ All components read their values from standard CSS variables. You can override them in `:root` or inside any window container:
25
+
26
+ ```css
27
+ :root {
28
+ --nui-primary: #3b82f6;
29
+ --nui-primary-hover: #2563eb;
30
+ --nui-surface: #18181b;
31
+ --nui-surface-alt: #101012;
32
+ --nui-border: #27272a;
33
+ --nui-fg: #fafafa;
34
+ --nui-fg-secondary: #8b8b99;
35
+ --nui-radius: 0.375rem;
36
+ --nui-success: #22c55e;
37
+ --nui-warning: #f59e0b;
38
+ --nui-danger: #ef4444;
39
+ --nui-info: #06b6d4;
40
+ --nui-scrim: rgba(0, 0, 0, 0.65);
41
+ }
42
+ ```
43
+
44
+ ---
45
+
46
+ ## Component Overview
47
+
48
+ > Comprehensive guides, props, events, and code recipes for all components are available in the [UI Components Documentation Portal](../../../docs/developers/components/README.md).
49
+
50
+ ### 1. Actions — [Read Guide](../../../docs/developers/components/actions.md)
51
+ * **`NuiButton`** — Button with built-in FiveM NUI event posting (`eventName`, `payload`), `loading` state, spinner, style variants (`primary`, `secondary`, `danger`, `link`), and reactive disable conditions.
52
+
53
+ ### 2. Forms — [Read Guide](../../../docs/developers/components/forms.md)
54
+ * **`NuiForm`** — Form container with automated field validation (`validateForm`, `ValidationRules`), submit events, and error handling.
55
+ * **`NuiInput`** — Text input (`text`, `number`, `password`, `search`) with two-way `bind:value`, debounce, and icons.
56
+ * **`NuiCheckbox`** — FiveM-styled toggle checkbox with `bind:checked` and indeterminate state support.
57
+ * **`NuiRadioGroup`** — Radio option group with descriptions and selection states.
58
+ * **`NuiSelect`** — Dropdown menu supporting single or multi-select (`multiple`), search filtering (`searchable`), and labels.
59
+ * **`NuiDatepicker`** — Interactive calendar with month navigation and `YYYY-MM-DD` formatting.
60
+ * **`NuiFileInput`** — File selection control with size validation (`maxSize`), file limits, and previews.
61
+
62
+ ### 3. Layout — [Read Guide](../../../docs/developers/components/layout.md)
63
+ * **`NuiCard`** — Card container with title, subtitle, badges, and custom snippet slots (`headerActions`, `footer`).
64
+ * **`NuiCardGrid`** — Responsive auto-fit grid for cards (`minCardWidth`, `gap`).
65
+ * **`NuiFlex`** — Flexbox layout container (`direction`, `justify`, `align`, `gap`, `wrap`).
66
+ * **`NuiGrid`** — CSS Grid layout helper (`columns`, `gap`, `rowGap`, `colGap`).
67
+ * **`NuiStack`** — Vertical or horizontal stack with uniform spacing and optional dividers.
68
+
69
+ ### 4. Navigation — [Read Guide](../../../docs/developers/components/navigation.md)
70
+ * **`NuiTabs`** — Tab list with keyboard accessibility (arrow navigation, Home/End) and active tab binding.
71
+ * **`NuiMenu`** — Floating dropdown / context menu with nested items and shortcuts.
72
+ * **`NuiSidebar`** — Collapsible side navigation drawer for administration panels.
73
+ * **`NuiBreadcrumbs`** — Breadcrumb navigation trail.
74
+ * **`NuiPagination`** — Pagination controls for data grids and lists.
75
+ * **`NuiAccordion`** — Collapsible panels for FAQs, rules, or settings.
76
+
77
+ ### 5. Overlays — [Read Guide](../../../docs/developers/components/overlay.md)
78
+ * **`NuiModal`** — Accessible dialog with backdrop dimming, focus trapping, Escape key support, and `ModalAction[]`.
79
+ * **`NuiDialog`** — Lightweight confirm/alert popups.
80
+ * **`NuiTooltip`** — Floating tooltips with custom placement (`top`, `bottom`, `left`, `right`).
81
+
82
+ ### 6. Data Display — [Read Guide](../../../docs/developers/components/data.md)
83
+ * **`NuiBadge`** — Status badge (`primary`, `secondary`, `success`, `warning`, `danger`, `info`).
84
+ * **`NuiProgressBar`** — Progress bar with striped animation and custom labels.
85
+ * **`NuiTable`** — Sortable, paginated data table (`NuiTableColumn<T>[]`).
86
+ * **`NuiList`** — Interactive, selectable item list (`NuiListItem[]`).
87
+ * **`NuiCodeBlock`** — Formatted code display with copy-to-clipboard button.
88
+ * **`NuiRichText`** — Sanitized HTML / text renderer.
89
+ * **`NuiChart`** — High-performance canvas chart (`bar`, `line`, `pie`) with `ChartData` and `ChartOptions`.
90
+
91
+ ---
92
+
93
+ ## Composables & Helpers — [Read Guide](../../../docs/developers/components/composables.md)
94
+
95
+ * **`useKeyboardShortcut`** — Register hotkeys that automatically ignore events when typing in inputs/textareas.
96
+ * **`validateForm`** / **`validateField`** — Client side form validation rules.
97
+
98
+ ---
99
+
100
+ ## License
101
+
102
+ MIT © Rainbe
@@ -0,0 +1,292 @@
1
+ <script lang="ts">
2
+ import { nui } from '@fivem-nui/svelte';
3
+ import type { SvelteNuiClient } from '@fivem-nui/svelte';
4
+
5
+ interface Props {
6
+ /** Event name sent to the game when the button is clicked. */
7
+ eventName?: string;
8
+ /** Payload sent along with {@link eventName}. */
9
+ payload?: unknown;
10
+ client?: SvelteNuiClient;
11
+ loading?: boolean;
12
+ disabled?: boolean;
13
+ type?: 'button' | 'submit' | 'reset';
14
+ variant?: 'primary' | 'secondary' | 'danger' | 'link';
15
+ size?: 'xs' | 'sm' | 'md' | 'lg';
16
+ /** Event sent right after the main NUI operation succeeds. */
17
+ onSuccess?: string;
18
+ /**
19
+ * Event sent when the local handler or main NUI operation fails.
20
+ * Note: A failure in sending onSuccess will not trigger this.
21
+ */
22
+ onError?: string;
23
+ onSuccessPayload?: unknown;
24
+ onErrorPayload?: unknown;
25
+ /** Local callback invoked when the operation fails. */
26
+ onFailure?: (error: unknown) => void;
27
+ /** Local callback invoked when the operation succeeds. */
28
+ onSuccessCallback?: (result?: unknown) => void;
29
+ /**
30
+ * Predicate deciding whether the button is currently enabled.
31
+ * Function must read reactive Svelte state to update automatically.
32
+ */
33
+ condition?: (() => boolean) | null;
34
+ /**
35
+ * Predicate or boolean that disables the button when true.
36
+ * Function must read reactive Svelte state to update automatically.
37
+ */
38
+ disabledWhen?: (() => boolean) | boolean | null;
39
+ /**
40
+ * When true, keeps loading state true upon successful completion.
41
+ * The parent component is responsible for resetting loading via bind:loading.
42
+ * On error, loading is always reset to false if autoReset is true.
43
+ */
44
+ keepLoadingOnSuccess?: boolean;
45
+ /**
46
+ * @deprecated Use {@link keepLoadingOnSuccess} instead.
47
+ */
48
+ autoUpdateOnSuccess?: boolean;
49
+ /**
50
+ * Reset loading state automatically after click.
51
+ * Set to false if parent exclusively manages loading state via bind:loading.
52
+ */
53
+ autoReset?: boolean;
54
+ /**
55
+ * Local click handler, invoked before the NUI event. Can be async.
56
+ * Return `false` or call `event.preventDefault()` to cancel the subsequent NUI event.
57
+ */
58
+ onclick?: (event: MouseEvent) => boolean | void | Promise<boolean | void>;
59
+ children?: import('svelte').Snippet;
60
+ }
61
+
62
+ let {
63
+ eventName = '',
64
+ payload = undefined,
65
+ client = nui,
66
+ loading = $bindable(false),
67
+ disabled = $bindable(false),
68
+ type = 'button',
69
+ variant = 'primary',
70
+ size = 'md',
71
+ onSuccess = '',
72
+ onError = '',
73
+ onSuccessPayload = {},
74
+ onErrorPayload = {},
75
+ onFailure,
76
+ onSuccessCallback,
77
+ condition = null,
78
+ disabledWhen = null,
79
+ keepLoadingOnSuccess = false,
80
+ autoUpdateOnSuccess = false,
81
+ autoReset = true,
82
+ onclick,
83
+ children
84
+ }: Props = $props();
85
+
86
+ const shouldKeepLoading = $derived(keepLoadingOnSuccess || autoUpdateOnSuccess);
87
+
88
+ const isEnabled = $derived.by(() => {
89
+ if (condition && typeof condition === 'function' && !condition()) return false;
90
+ if (disabledWhen) {
91
+ if (typeof disabledWhen === 'function') {
92
+ if (disabledWhen()) return false;
93
+ } else if (typeof disabledWhen === 'boolean') {
94
+ if (disabledWhen) return false;
95
+ }
96
+ }
97
+ return true;
98
+ });
99
+
100
+ async function dispatchClientEvent(name: string, data?: unknown): Promise<unknown> {
101
+ if ('sendAndWait' in client && typeof client.sendAndWait === 'function') {
102
+ return await client.sendAndWait(name, data);
103
+ }
104
+ return client.send(name, data);
105
+ }
106
+
107
+ async function handleClick(event: MouseEvent) {
108
+ if (!isEnabled || loading || disabled) return;
109
+
110
+ // If there is no NUI event and no custom onclick, let native action proceed
111
+ if (!eventName && !onclick) {
112
+ return;
113
+ }
114
+
115
+ loading = true;
116
+ let hasError = false;
117
+
118
+ try {
119
+ // 1. Run local onclick hook
120
+ if (onclick) {
121
+ const initialPrevented = event.defaultPrevented;
122
+ const result = await onclick(event);
123
+
124
+ // Cancel if onclick returned false or marked event as defaultPrevented
125
+ if (result === false || (!initialPrevented && event.defaultPrevented)) {
126
+ if (autoReset) {
127
+ loading = false;
128
+ }
129
+ return;
130
+ }
131
+ }
132
+
133
+ // 2. Prevent native form submission/reset if proceeding with NUI event
134
+ if (eventName && (type === 'submit' || type === 'reset')) {
135
+ event.preventDefault();
136
+ }
137
+
138
+ // 3. Send main NUI event
139
+ let operationResult: unknown = undefined;
140
+ if (eventName) {
141
+ operationResult = await dispatchClientEvent(eventName, payload);
142
+
143
+ // Handle clients or mocks returning an explicit failure envelope { success: false, error: ... }
144
+ if (
145
+ operationResult &&
146
+ typeof operationResult === 'object' &&
147
+ 'success' in operationResult &&
148
+ (operationResult as { success?: boolean }).success === false
149
+ ) {
150
+ const envelope = operationResult as { error?: { message?: string } | string };
151
+ const errMsg =
152
+ typeof envelope.error === 'object' && envelope.error !== null
153
+ ? envelope.error.message || 'NUI operation returned failure'
154
+ : typeof envelope.error === 'string'
155
+ ? envelope.error
156
+ : 'NUI operation returned failure';
157
+ throw new Error(errMsg);
158
+ }
159
+ }
160
+
161
+ // 4. Trigger local success callback
162
+ onSuccessCallback?.(operationResult);
163
+
164
+ // 5. Send success notification event (isolated so its failure does not trigger onError)
165
+ if (onSuccess) {
166
+ try {
167
+ await dispatchClientEvent(onSuccess, onSuccessPayload);
168
+ } catch (successSendError) {
169
+ console.warn(
170
+ `[NuiButton] Main operation succeeded, but failed to dispatch onSuccess event '${onSuccess}':`,
171
+ successSendError
172
+ );
173
+ }
174
+ }
175
+ } catch (error) {
176
+ hasError = true;
177
+ const message = error instanceof Error ? error.message : String(error);
178
+
179
+ // Trigger local failure callback
180
+ onFailure?.(error);
181
+
182
+ const basePayload =
183
+ typeof onErrorPayload === 'object' && onErrorPayload !== null && !Array.isArray(onErrorPayload)
184
+ ? onErrorPayload
185
+ : { payload: onErrorPayload };
186
+
187
+ if (onError) {
188
+ try {
189
+ await dispatchClientEvent(onError, { ...basePayload, error: message });
190
+ } catch (sendErr) {
191
+ console.error(`[NuiButton] Failed to dispatch onError event '${onError}':`, sendErr);
192
+ }
193
+ }
194
+
195
+ console.error(`[NuiButton] Error handling click for event '${eventName || 'local'}':`, error);
196
+ } finally {
197
+ if (autoReset) {
198
+ if (hasError || !shouldKeepLoading) {
199
+ loading = false;
200
+ }
201
+ }
202
+ }
203
+ }
204
+ </script>
205
+
206
+ <button
207
+ {type}
208
+ class="nui-btn nui-btn--{variant} nui-btn--{size}"
209
+ disabled={!isEnabled || disabled || loading}
210
+ aria-busy={loading}
211
+ onclick={handleClick}
212
+ >
213
+ {#if loading}
214
+ <span class="nui-btn__spinner" aria-hidden="true"></span>
215
+ {/if}
216
+ {@render children?.()}
217
+ </button>
218
+
219
+ <style>
220
+ .nui-btn {
221
+ display: inline-flex;
222
+ align-items: center;
223
+ justify-content: center;
224
+ gap: 0.5rem;
225
+ border: 1px solid transparent;
226
+ border-radius: var(--nui-radius, 0.375rem);
227
+ font-family: inherit;
228
+ font-weight: 600;
229
+ line-height: 1;
230
+ cursor: pointer;
231
+ transition: background 0.15s ease, border-color 0.15s ease, opacity 0.15s ease;
232
+ box-sizing: border-box;
233
+ }
234
+
235
+ .nui-btn:disabled {
236
+ opacity: 0.5;
237
+ cursor: not-allowed;
238
+ }
239
+
240
+ .nui-btn--xs { padding: 0.2rem 0.5rem; font-size: 0.75rem; }
241
+ .nui-btn--sm { padding: 0.25rem 0.625rem; font-size: 0.8125rem; }
242
+ .nui-btn--md { padding: 0.5rem 0.875rem; font-size: 0.9375rem; }
243
+ .nui-btn--lg { padding: 0.75rem 1.25rem; font-size: 1.0625rem; }
244
+
245
+ .nui-btn--primary {
246
+ background: var(--nui-primary, #3b82f6);
247
+ color: #fff;
248
+ }
249
+ .nui-btn--primary:hover:not(:disabled) {
250
+ background: var(--nui-primary-hover, #2563eb);
251
+ }
252
+
253
+ .nui-btn--secondary {
254
+ background: transparent;
255
+ border-color: var(--nui-border, #3f3f46);
256
+ color: var(--nui-fg, #fafafa);
257
+ }
258
+ .nui-btn--secondary:hover:not(:disabled) {
259
+ background: var(--nui-hover, rgb(255 255 255 / 0.06));
260
+ }
261
+
262
+ .nui-btn--danger {
263
+ background: var(--nui-danger, #ef4444);
264
+ color: #fff;
265
+ }
266
+ .nui-btn--danger:hover:not(:disabled) {
267
+ background: var(--nui-danger-hover, #dc2626);
268
+ }
269
+
270
+ .nui-btn--link {
271
+ background: transparent;
272
+ color: var(--nui-primary, #3b82f6);
273
+ padding-left: 0;
274
+ padding-right: 0;
275
+ }
276
+ .nui-btn--link:hover:not(:disabled) {
277
+ text-decoration: underline;
278
+ }
279
+
280
+ .nui-btn__spinner {
281
+ width: 0.85em;
282
+ height: 0.85em;
283
+ border: 2px solid currentColor;
284
+ border-right-color: transparent;
285
+ border-radius: 50%;
286
+ animation: nui-btn-spin 0.6s linear infinite;
287
+ }
288
+
289
+ @keyframes nui-btn-spin {
290
+ to { transform: rotate(360deg); }
291
+ }
292
+ </style>
@@ -0,0 +1,60 @@
1
+ import type { SvelteNuiClient } from '@fivem-nui/svelte';
2
+ interface Props {
3
+ /** Event name sent to the game when the button is clicked. */
4
+ eventName?: string;
5
+ /** Payload sent along with {@link eventName}. */
6
+ payload?: unknown;
7
+ client?: SvelteNuiClient;
8
+ loading?: boolean;
9
+ disabled?: boolean;
10
+ type?: 'button' | 'submit' | 'reset';
11
+ variant?: 'primary' | 'secondary' | 'danger' | 'link';
12
+ size?: 'xs' | 'sm' | 'md' | 'lg';
13
+ /** Event sent right after the main NUI operation succeeds. */
14
+ onSuccess?: string;
15
+ /**
16
+ * Event sent when the local handler or main NUI operation fails.
17
+ * Note: A failure in sending onSuccess will not trigger this.
18
+ */
19
+ onError?: string;
20
+ onSuccessPayload?: unknown;
21
+ onErrorPayload?: unknown;
22
+ /** Local callback invoked when the operation fails. */
23
+ onFailure?: (error: unknown) => void;
24
+ /** Local callback invoked when the operation succeeds. */
25
+ onSuccessCallback?: (result?: unknown) => void;
26
+ /**
27
+ * Predicate deciding whether the button is currently enabled.
28
+ * Function must read reactive Svelte state to update automatically.
29
+ */
30
+ condition?: (() => boolean) | null;
31
+ /**
32
+ * Predicate or boolean that disables the button when true.
33
+ * Function must read reactive Svelte state to update automatically.
34
+ */
35
+ disabledWhen?: (() => boolean) | boolean | null;
36
+ /**
37
+ * When true, keeps loading state true upon successful completion.
38
+ * The parent component is responsible for resetting loading via bind:loading.
39
+ * On error, loading is always reset to false if autoReset is true.
40
+ */
41
+ keepLoadingOnSuccess?: boolean;
42
+ /**
43
+ * @deprecated Use {@link keepLoadingOnSuccess} instead.
44
+ */
45
+ autoUpdateOnSuccess?: boolean;
46
+ /**
47
+ * Reset loading state automatically after click.
48
+ * Set to false if parent exclusively manages loading state via bind:loading.
49
+ */
50
+ autoReset?: boolean;
51
+ /**
52
+ * Local click handler, invoked before the NUI event. Can be async.
53
+ * Return `false` or call `event.preventDefault()` to cancel the subsequent NUI event.
54
+ */
55
+ onclick?: (event: MouseEvent) => boolean | void | Promise<boolean | void>;
56
+ children?: import('svelte').Snippet;
57
+ }
58
+ declare const NuiButton: import("svelte").Component<Props, {}, "loading" | "disabled">;
59
+ type NuiButton = ReturnType<typeof NuiButton>;
60
+ export default NuiButton;