@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.
- package/README.md +101 -2
- package/dist/components/actions/NuiButton.svelte +292 -0
- package/dist/components/actions/NuiButton.svelte.d.ts +60 -0
- package/dist/components/data/NuiBadge.svelte +277 -0
- package/dist/components/data/NuiBadge.svelte.d.ts +74 -0
- package/dist/components/data/NuiCodeBlock.svelte +256 -0
- package/dist/components/data/NuiCodeBlock.svelte.d.ts +49 -0
- package/dist/components/data/NuiList.svelte +341 -0
- package/dist/components/data/NuiList.svelte.d.ts +52 -0
- package/dist/components/data/NuiProgressBar.svelte +264 -0
- package/dist/components/data/NuiProgressBar.svelte.d.ts +51 -0
- package/dist/components/data/NuiRichText.svelte +260 -0
- package/dist/components/data/NuiRichText.svelte.d.ts +33 -0
- package/dist/components/data/NuiTable.svelte +470 -0
- package/dist/components/data/NuiTable.svelte.d.ts +53 -0
- package/dist/components/data/chart/NuiChart.svelte +384 -0
- package/dist/components/data/chart/NuiChart.svelte.d.ts +49 -0
- package/dist/components/data/chart/color.d.ts +9 -0
- package/dist/components/data/chart/color.js +78 -0
- package/dist/components/data/chart/index.d.ts +10 -0
- package/dist/components/data/chart/index.js +10 -0
- package/dist/components/data/chart/registry.d.ts +14 -0
- package/dist/components/data/chart/registry.js +53 -0
- package/dist/components/data/chart/renderers/bar.d.ts +8 -0
- package/dist/components/data/chart/renderers/bar.js +79 -0
- package/dist/components/data/chart/renderers/base.d.ts +18 -0
- package/dist/components/data/chart/renderers/base.js +10 -0
- package/dist/components/data/chart/renderers/line.d.ts +8 -0
- package/dist/components/data/chart/renderers/line.js +89 -0
- package/dist/components/data/chart/renderers/pie.d.ts +10 -0
- package/dist/components/data/chart/renderers/pie.js +55 -0
- package/dist/components/data/chart/scale.d.ts +22 -0
- package/dist/components/data/chart/scale.js +101 -0
- package/dist/components/data/chart/types.d.ts +84 -0
- package/dist/components/data/chart/types.js +1 -0
- package/dist/components/data/chart/validator.d.ts +10 -0
- package/dist/components/data/chart/validator.js +82 -0
- package/dist/components/forms/NuiCheckbox.svelte +218 -0
- package/dist/components/forms/NuiCheckbox.svelte.d.ts +37 -0
- package/dist/components/forms/NuiDatepicker.svelte +755 -0
- package/dist/components/forms/NuiDatepicker.svelte.d.ts +34 -0
- package/dist/components/forms/NuiFileInput.svelte +1047 -0
- package/dist/components/forms/NuiFileInput.svelte.d.ts +93 -0
- package/dist/components/forms/NuiFileInput.types.d.ts +28 -0
- package/dist/components/forms/NuiFileInput.types.js +1 -0
- package/dist/components/forms/NuiForm.svelte +391 -0
- package/dist/components/forms/NuiForm.svelte.d.ts +69 -0
- package/dist/components/forms/NuiInput.svelte +296 -0
- package/dist/components/forms/NuiInput.svelte.d.ts +62 -0
- package/dist/components/forms/NuiRadioGroup.svelte +238 -0
- package/dist/components/forms/NuiRadioGroup.svelte.d.ts +47 -0
- package/dist/components/forms/NuiSelect.svelte +819 -0
- package/dist/components/forms/NuiSelect.svelte.d.ts +49 -0
- package/dist/components/layout/NuiCard.svelte +314 -0
- package/dist/components/layout/NuiCard.svelte.d.ts +48 -0
- package/dist/components/layout/NuiCardGrid.svelte +98 -0
- package/dist/components/layout/NuiCardGrid.svelte.d.ts +22 -0
- package/dist/components/layout/NuiFlex.svelte +121 -0
- package/dist/components/layout/NuiFlex.svelte.d.ts +23 -0
- package/dist/components/layout/NuiGrid.svelte +66 -0
- package/dist/components/layout/NuiGrid.svelte.d.ts +14 -0
- package/dist/components/layout/NuiStack.svelte +138 -0
- package/dist/components/layout/NuiStack.svelte.d.ts +26 -0
- package/dist/components/navigation/NuiAccordion.svelte +323 -0
- package/dist/components/navigation/NuiAccordion.svelte.d.ts +38 -0
- package/dist/components/navigation/NuiBreadcrumbs.svelte +133 -0
- package/dist/components/navigation/NuiBreadcrumbs.svelte.d.ts +19 -0
- package/dist/components/navigation/NuiMenu.svelte +596 -0
- package/dist/components/navigation/NuiMenu.svelte.d.ts +28 -0
- package/dist/components/navigation/NuiPagination.svelte +306 -0
- package/dist/components/navigation/NuiPagination.svelte.d.ts +40 -0
- package/dist/components/navigation/NuiSidebar.svelte +186 -0
- package/dist/components/navigation/NuiSidebar.svelte.d.ts +26 -0
- package/dist/components/navigation/NuiTabs.svelte +249 -0
- package/dist/components/navigation/NuiTabs.svelte.d.ts +31 -0
- package/dist/components/overlay/NuiDialog.svelte +118 -0
- package/dist/components/overlay/NuiDialog.svelte.d.ts +34 -0
- package/dist/components/overlay/NuiModal.svelte +992 -0
- package/dist/components/overlay/NuiModal.svelte.d.ts +72 -0
- package/dist/components/overlay/NuiTooltip.svelte +159 -0
- package/dist/components/overlay/NuiTooltip.svelte.d.ts +13 -0
- package/dist/composables/useClickOutside.d.ts +43 -0
- package/dist/composables/useClickOutside.js +73 -0
- package/dist/composables/useFormValidation.d.ts +23 -0
- package/dist/composables/useFormValidation.js +72 -0
- package/dist/composables/useIdle.svelte.d.ts +18 -0
- package/dist/composables/useIdle.svelte.js +144 -0
- package/dist/composables/useKeyboardShortcut.svelte.d.ts +114 -0
- package/dist/composables/useKeyboardShortcut.svelte.js +153 -0
- package/dist/composables/useLocalStorage.svelte.d.ts +22 -0
- package/dist/composables/useLocalStorage.svelte.js +105 -0
- package/dist/composables/useMediaQuery.svelte.d.ts +51 -0
- package/dist/composables/useMediaQuery.svelte.js +80 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.js +42 -0
- package/package.json +58 -3
- package/src/components/actions/NuiButton.svelte +292 -0
- package/src/components/data/NuiBadge.svelte +277 -0
- package/src/components/data/NuiCodeBlock.svelte +256 -0
- package/src/components/data/NuiList.svelte +341 -0
- package/src/components/data/NuiProgressBar.svelte +264 -0
- package/src/components/data/NuiRichText.svelte +260 -0
- package/src/components/data/NuiTable.svelte +470 -0
- package/src/components/data/chart/NuiChart.svelte +384 -0
- package/src/components/data/chart/color.ts +83 -0
- package/src/components/data/chart/index.ts +10 -0
- package/src/components/data/chart/registry.ts +71 -0
- package/src/components/data/chart/renderers/bar.ts +90 -0
- package/src/components/data/chart/renderers/base.ts +32 -0
- package/src/components/data/chart/renderers/line.ts +99 -0
- package/src/components/data/chart/renderers/pie.ts +66 -0
- package/src/components/data/chart/scale.ts +139 -0
- package/src/components/data/chart/types.ts +98 -0
- package/src/components/data/chart/validator.ts +98 -0
- package/src/components/forms/NuiCheckbox.svelte +218 -0
- package/src/components/forms/NuiDatepicker.svelte +755 -0
- package/src/components/forms/NuiFileInput.svelte +1047 -0
- package/src/components/forms/NuiFileInput.types.ts +29 -0
- package/src/components/forms/NuiForm.svelte +391 -0
- package/src/components/forms/NuiInput.svelte +296 -0
- package/src/components/forms/NuiRadioGroup.svelte +238 -0
- package/src/components/forms/NuiSelect.svelte +819 -0
- package/src/components/layout/NuiCard.svelte +314 -0
- package/src/components/layout/NuiCardGrid.svelte +98 -0
- package/src/components/layout/NuiFlex.svelte +121 -0
- package/src/components/layout/NuiGrid.svelte +66 -0
- package/src/components/layout/NuiStack.svelte +138 -0
- package/src/components/navigation/NuiAccordion.svelte +323 -0
- package/src/components/navigation/NuiBreadcrumbs.svelte +133 -0
- package/src/components/navigation/NuiMenu.svelte +596 -0
- package/src/components/navigation/NuiPagination.svelte +306 -0
- package/src/components/navigation/NuiSidebar.svelte +186 -0
- package/src/components/navigation/NuiTabs.svelte +249 -0
- package/src/components/overlay/NuiDialog.svelte +118 -0
- package/src/components/overlay/NuiModal.svelte +992 -0
- package/src/components/overlay/NuiTooltip.svelte +159 -0
- package/src/composables/useClickOutside.ts +132 -0
- package/src/composables/useFormValidation.ts +105 -0
- package/src/composables/useIdle.svelte.ts +191 -0
- package/src/composables/useKeyboardShortcut.svelte.ts +278 -0
- package/src/composables/useLocalStorage.svelte.ts +137 -0
- package/src/composables/useMediaQuery.svelte.ts +102 -0
- package/src/index.ts +66 -0
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component NuiBadge
|
|
3
|
+
Status badge and notification counter component.
|
|
4
|
+
-->
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
interface Props {
|
|
7
|
+
/** Text or snippet content rendered inside the badge. */
|
|
8
|
+
children?: import("svelte").Snippet;
|
|
9
|
+
/**
|
|
10
|
+
* Numerical counter value (e.g. notification count).
|
|
11
|
+
* Negative, non-numeric, or infinite values are normalized to 0.
|
|
12
|
+
* @defaultValue 0
|
|
13
|
+
*/
|
|
14
|
+
count?: number;
|
|
15
|
+
/**
|
|
16
|
+
* Whether to display the counter when count is 0.
|
|
17
|
+
* When false (default), a count of 0 falls back to children or a dot indicator.
|
|
18
|
+
* @defaultValue false
|
|
19
|
+
*/
|
|
20
|
+
showZero?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Maximum displayed counter value (e.g. 99 displays as "99+").
|
|
23
|
+
* @defaultValue 99
|
|
24
|
+
*/
|
|
25
|
+
maxCount?: number;
|
|
26
|
+
/**
|
|
27
|
+
* Color variant.
|
|
28
|
+
* @defaultValue 'primary'
|
|
29
|
+
*/
|
|
30
|
+
variant?:
|
|
31
|
+
| "primary"
|
|
32
|
+
| "secondary"
|
|
33
|
+
| "success"
|
|
34
|
+
| "warning"
|
|
35
|
+
| "danger"
|
|
36
|
+
| "info";
|
|
37
|
+
/**
|
|
38
|
+
* Size of the badge.
|
|
39
|
+
* @defaultValue 'md'
|
|
40
|
+
*/
|
|
41
|
+
size?: "xs" | "sm" | "md" | "lg";
|
|
42
|
+
/**
|
|
43
|
+
* Outline style with transparent background and colored border.
|
|
44
|
+
* @defaultValue false
|
|
45
|
+
*/
|
|
46
|
+
outline?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Pill-shaped rounded borders.
|
|
49
|
+
* @defaultValue true
|
|
50
|
+
*/
|
|
51
|
+
rounded?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Visibility of the badge.
|
|
54
|
+
* @defaultValue true
|
|
55
|
+
*/
|
|
56
|
+
visible?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Whether the badge is interactive (automatically true when onclick is provided).
|
|
59
|
+
* @defaultValue false
|
|
60
|
+
*/
|
|
61
|
+
clickable?: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Whether the clickable badge is disabled.
|
|
64
|
+
* @defaultValue false
|
|
65
|
+
*/
|
|
66
|
+
disabled?: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Accessible description for screen readers.
|
|
69
|
+
* Recommended for interactive badges or badges displaying only symbols/numbers.
|
|
70
|
+
*/
|
|
71
|
+
ariaLabel?: string;
|
|
72
|
+
/**
|
|
73
|
+
* Optional role (defaults to "status" when ariaLabel is provided on a non-interactive <span>).
|
|
74
|
+
*/
|
|
75
|
+
role?: string;
|
|
76
|
+
/** Click event handler. */
|
|
77
|
+
onclick?: (event: MouseEvent) => void;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let {
|
|
81
|
+
children,
|
|
82
|
+
count = 0,
|
|
83
|
+
showZero = false,
|
|
84
|
+
maxCount = 99,
|
|
85
|
+
variant = "primary",
|
|
86
|
+
size = "md",
|
|
87
|
+
outline = false,
|
|
88
|
+
rounded = true,
|
|
89
|
+
visible = true,
|
|
90
|
+
clickable = false,
|
|
91
|
+
disabled = false,
|
|
92
|
+
ariaLabel,
|
|
93
|
+
role,
|
|
94
|
+
onclick,
|
|
95
|
+
}: Props = $props();
|
|
96
|
+
|
|
97
|
+
const normalizedCount = $derived(
|
|
98
|
+
Number.isFinite(count) ? Math.max(0, Math.floor(count)) : 0,
|
|
99
|
+
);
|
|
100
|
+
const normalizedMaxCount = $derived(
|
|
101
|
+
Number.isFinite(maxCount) ? Math.max(1, Math.floor(maxCount)) : 99,
|
|
102
|
+
);
|
|
103
|
+
const showCount = $derived(
|
|
104
|
+
normalizedCount > 0 || (showZero && count !== undefined),
|
|
105
|
+
);
|
|
106
|
+
const displayCount = $derived(
|
|
107
|
+
normalizedCount > normalizedMaxCount
|
|
108
|
+
? `${normalizedMaxCount}+`
|
|
109
|
+
: normalizedCount,
|
|
110
|
+
);
|
|
111
|
+
const isClickable = $derived(clickable || typeof onclick === "function");
|
|
112
|
+
const resolvedRole = $derived(
|
|
113
|
+
role ?? (ariaLabel && !isClickable ? "status" : undefined),
|
|
114
|
+
);
|
|
115
|
+
</script>
|
|
116
|
+
|
|
117
|
+
{#if visible}
|
|
118
|
+
{#if isClickable}
|
|
119
|
+
<button
|
|
120
|
+
type="button"
|
|
121
|
+
class="nui-badge nui-badge--{variant} nui-badge--{size} nui-badge--clickable"
|
|
122
|
+
class:nui-badge--outline={outline}
|
|
123
|
+
class:nui-badge--rounded={rounded}
|
|
124
|
+
class:nui-badge--square={!rounded}
|
|
125
|
+
aria-label={ariaLabel}
|
|
126
|
+
{disabled}
|
|
127
|
+
{onclick}
|
|
128
|
+
>
|
|
129
|
+
{#if showCount}
|
|
130
|
+
{displayCount}
|
|
131
|
+
{:else if children}
|
|
132
|
+
{@render children()}
|
|
133
|
+
{:else}
|
|
134
|
+
<span aria-hidden="true">●</span>
|
|
135
|
+
{/if}
|
|
136
|
+
</button>
|
|
137
|
+
{:else}
|
|
138
|
+
<span
|
|
139
|
+
class="nui-badge nui-badge--{variant} nui-badge--{size}"
|
|
140
|
+
class:nui-badge--outline={outline}
|
|
141
|
+
class:nui-badge--rounded={rounded}
|
|
142
|
+
class:nui-badge--square={!rounded}
|
|
143
|
+
aria-label={ariaLabel}
|
|
144
|
+
role={resolvedRole}
|
|
145
|
+
>
|
|
146
|
+
{#if showCount}
|
|
147
|
+
{displayCount}
|
|
148
|
+
{:else if children}
|
|
149
|
+
{@render children()}
|
|
150
|
+
{:else}
|
|
151
|
+
<span aria-hidden="true">●</span>
|
|
152
|
+
{/if}
|
|
153
|
+
</span>
|
|
154
|
+
{/if}
|
|
155
|
+
{/if}
|
|
156
|
+
|
|
157
|
+
<style>
|
|
158
|
+
.nui-badge {
|
|
159
|
+
display: inline-flex;
|
|
160
|
+
align-items: center;
|
|
161
|
+
justify-content: center;
|
|
162
|
+
font: inherit;
|
|
163
|
+
font-weight: 600;
|
|
164
|
+
line-height: 1;
|
|
165
|
+
text-align: center;
|
|
166
|
+
border: 1px solid transparent;
|
|
167
|
+
transition:
|
|
168
|
+
opacity 0.15s ease,
|
|
169
|
+
background 0.15s ease,
|
|
170
|
+
border-color 0.15s ease;
|
|
171
|
+
box-sizing: border-box;
|
|
172
|
+
vertical-align: middle;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
button.nui-badge {
|
|
176
|
+
appearance: none;
|
|
177
|
+
-webkit-appearance: none;
|
|
178
|
+
color: inherit;
|
|
179
|
+
padding: 0;
|
|
180
|
+
cursor: pointer;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
button.nui-badge:focus-visible {
|
|
184
|
+
outline: 2px solid currentColor;
|
|
185
|
+
outline-offset: 2px;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.nui-badge--clickable {
|
|
189
|
+
cursor: pointer;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.nui-badge--clickable:hover:not(:disabled) {
|
|
193
|
+
opacity: 0.85;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.nui-badge--clickable:disabled {
|
|
197
|
+
opacity: 0.5;
|
|
198
|
+
cursor: not-allowed;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.nui-badge--primary {
|
|
202
|
+
background: var(--nui-primary, #3b82f6);
|
|
203
|
+
color: #fff;
|
|
204
|
+
}
|
|
205
|
+
.nui-badge--secondary {
|
|
206
|
+
background: var(--nui-surface-alt, #3f3f46);
|
|
207
|
+
color: var(--nui-fg, #fafafa);
|
|
208
|
+
}
|
|
209
|
+
.nui-badge--success {
|
|
210
|
+
background: var(--nui-success, #22c55e);
|
|
211
|
+
color: #fff;
|
|
212
|
+
}
|
|
213
|
+
.nui-badge--warning {
|
|
214
|
+
background: var(--nui-warning, #f59e0b);
|
|
215
|
+
color: #18181b;
|
|
216
|
+
}
|
|
217
|
+
.nui-badge--danger {
|
|
218
|
+
background: var(--nui-danger, #ef4444);
|
|
219
|
+
color: #fff;
|
|
220
|
+
}
|
|
221
|
+
.nui-badge--info {
|
|
222
|
+
background: var(--nui-info, #06b6d4);
|
|
223
|
+
color: #fff;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.nui-badge.nui-badge--outline {
|
|
227
|
+
background: transparent;
|
|
228
|
+
}
|
|
229
|
+
.nui-badge.nui-badge--outline.nui-badge--primary {
|
|
230
|
+
border-color: var(--nui-primary, #3b82f6);
|
|
231
|
+
color: var(--nui-primary, #3b82f6);
|
|
232
|
+
}
|
|
233
|
+
.nui-badge.nui-badge--outline.nui-badge--secondary {
|
|
234
|
+
border-color: var(--nui-border, #3f3f46);
|
|
235
|
+
color: var(--nui-fg-secondary, #8b8b99);
|
|
236
|
+
}
|
|
237
|
+
.nui-badge.nui-badge--outline.nui-badge--success {
|
|
238
|
+
border-color: var(--nui-success, #22c55e);
|
|
239
|
+
color: var(--nui-success, #22c55e);
|
|
240
|
+
}
|
|
241
|
+
.nui-badge.nui-badge--outline.nui-badge--warning {
|
|
242
|
+
border-color: var(--nui-warning, #f59e0b);
|
|
243
|
+
color: var(--nui-warning, #f59e0b);
|
|
244
|
+
}
|
|
245
|
+
.nui-badge.nui-badge--outline.nui-badge--danger {
|
|
246
|
+
border-color: var(--nui-danger, #ef4444);
|
|
247
|
+
color: var(--nui-danger, #ef4444);
|
|
248
|
+
}
|
|
249
|
+
.nui-badge.nui-badge--outline.nui-badge--info {
|
|
250
|
+
border-color: var(--nui-info, #06b6d4);
|
|
251
|
+
color: var(--nui-info, #06b6d4);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.nui-badge--xs {
|
|
255
|
+
padding: 0.125rem 0.35rem;
|
|
256
|
+
font-size: 0.625rem;
|
|
257
|
+
}
|
|
258
|
+
.nui-badge--sm {
|
|
259
|
+
padding: 0.2rem 0.5rem;
|
|
260
|
+
font-size: 0.75rem;
|
|
261
|
+
}
|
|
262
|
+
.nui-badge--md {
|
|
263
|
+
padding: 0.3rem 0.65rem;
|
|
264
|
+
font-size: 0.8125rem;
|
|
265
|
+
}
|
|
266
|
+
.nui-badge--lg {
|
|
267
|
+
padding: 0.4rem 0.85rem;
|
|
268
|
+
font-size: 0.9375rem;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.nui-badge--rounded {
|
|
272
|
+
border-radius: 9999px;
|
|
273
|
+
}
|
|
274
|
+
.nui-badge--square {
|
|
275
|
+
border-radius: var(--nui-radius, 0.25rem);
|
|
276
|
+
}
|
|
277
|
+
</style>
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
/** Text or snippet content rendered inside the badge. */
|
|
3
|
+
children?: import("svelte").Snippet;
|
|
4
|
+
/**
|
|
5
|
+
* Numerical counter value (e.g. notification count).
|
|
6
|
+
* Negative, non-numeric, or infinite values are normalized to 0.
|
|
7
|
+
* @defaultValue 0
|
|
8
|
+
*/
|
|
9
|
+
count?: number;
|
|
10
|
+
/**
|
|
11
|
+
* Whether to display the counter when count is 0.
|
|
12
|
+
* When false (default), a count of 0 falls back to children or a dot indicator.
|
|
13
|
+
* @defaultValue false
|
|
14
|
+
*/
|
|
15
|
+
showZero?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Maximum displayed counter value (e.g. 99 displays as "99+").
|
|
18
|
+
* @defaultValue 99
|
|
19
|
+
*/
|
|
20
|
+
maxCount?: number;
|
|
21
|
+
/**
|
|
22
|
+
* Color variant.
|
|
23
|
+
* @defaultValue 'primary'
|
|
24
|
+
*/
|
|
25
|
+
variant?: "primary" | "secondary" | "success" | "warning" | "danger" | "info";
|
|
26
|
+
/**
|
|
27
|
+
* Size of the badge.
|
|
28
|
+
* @defaultValue 'md'
|
|
29
|
+
*/
|
|
30
|
+
size?: "xs" | "sm" | "md" | "lg";
|
|
31
|
+
/**
|
|
32
|
+
* Outline style with transparent background and colored border.
|
|
33
|
+
* @defaultValue false
|
|
34
|
+
*/
|
|
35
|
+
outline?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Pill-shaped rounded borders.
|
|
38
|
+
* @defaultValue true
|
|
39
|
+
*/
|
|
40
|
+
rounded?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Visibility of the badge.
|
|
43
|
+
* @defaultValue true
|
|
44
|
+
*/
|
|
45
|
+
visible?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Whether the badge is interactive (automatically true when onclick is provided).
|
|
48
|
+
* @defaultValue false
|
|
49
|
+
*/
|
|
50
|
+
clickable?: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Whether the clickable badge is disabled.
|
|
53
|
+
* @defaultValue false
|
|
54
|
+
*/
|
|
55
|
+
disabled?: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Accessible description for screen readers.
|
|
58
|
+
* Recommended for interactive badges or badges displaying only symbols/numbers.
|
|
59
|
+
*/
|
|
60
|
+
ariaLabel?: string;
|
|
61
|
+
/**
|
|
62
|
+
* Optional role (defaults to "status" when ariaLabel is provided on a non-interactive <span>).
|
|
63
|
+
*/
|
|
64
|
+
role?: string;
|
|
65
|
+
/** Click event handler. */
|
|
66
|
+
onclick?: (event: MouseEvent) => void;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* NuiBadge
|
|
70
|
+
* Status badge and notification counter component.
|
|
71
|
+
*/
|
|
72
|
+
declare const NuiBadge: import("svelte").Component<Props, {}, "">;
|
|
73
|
+
type NuiBadge = ReturnType<typeof NuiBadge>;
|
|
74
|
+
export default NuiBadge;
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component NuiCodeBlock
|
|
3
|
+
Code snippet block with optional title, language syntax classes, and clipboard copy button.
|
|
4
|
+
-->
|
|
5
|
+
<script module lang="ts">
|
|
6
|
+
export type NuiCodeLanguage =
|
|
7
|
+
| 'csharp'
|
|
8
|
+
| 'javascript'
|
|
9
|
+
| 'typescript'
|
|
10
|
+
| 'lua'
|
|
11
|
+
| 'sql'
|
|
12
|
+
| 'json'
|
|
13
|
+
| 'xml'
|
|
14
|
+
| 'bash'
|
|
15
|
+
| 'css'
|
|
16
|
+
| 'markdown'
|
|
17
|
+
| (string & {});
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<script lang="ts">
|
|
21
|
+
import { onDestroy } from 'svelte';
|
|
22
|
+
|
|
23
|
+
interface Props {
|
|
24
|
+
/** Code snippet to display. */
|
|
25
|
+
code?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Programming language for syntax highlighting classes.
|
|
28
|
+
* Integrates with external highlighters like highlight.js or Prism.
|
|
29
|
+
* @defaultValue 'csharp'
|
|
30
|
+
*/
|
|
31
|
+
language?: NuiCodeLanguage;
|
|
32
|
+
/** Optional title or filename displayed in the header. */
|
|
33
|
+
title?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Whether to show the copy-to-clipboard button.
|
|
36
|
+
* @defaultValue true
|
|
37
|
+
*/
|
|
38
|
+
showCopyButton?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Rounded container borders.
|
|
41
|
+
* @defaultValue true
|
|
42
|
+
*/
|
|
43
|
+
rounded?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Label for copy button in idle state.
|
|
46
|
+
* @defaultValue 'Copy'
|
|
47
|
+
*/
|
|
48
|
+
copyLabel?: string;
|
|
49
|
+
/**
|
|
50
|
+
* Label for copy button in copied state.
|
|
51
|
+
* @defaultValue 'Copied!'
|
|
52
|
+
*/
|
|
53
|
+
copiedLabel?: string;
|
|
54
|
+
/**
|
|
55
|
+
* Label for copy button in error state.
|
|
56
|
+
* @defaultValue 'Failed'
|
|
57
|
+
*/
|
|
58
|
+
errorLabel?: string;
|
|
59
|
+
/** Callback executed on successful clipboard copy. */
|
|
60
|
+
onCopy?: (code: string) => void;
|
|
61
|
+
/** Callback executed when clipboard copy fails. */
|
|
62
|
+
onCopyError?: (error: unknown) => void;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let {
|
|
66
|
+
code = '',
|
|
67
|
+
language = 'csharp',
|
|
68
|
+
title,
|
|
69
|
+
showCopyButton = true,
|
|
70
|
+
rounded = true,
|
|
71
|
+
copyLabel = 'Copy',
|
|
72
|
+
copiedLabel = 'Copied!',
|
|
73
|
+
errorLabel = 'Failed',
|
|
74
|
+
onCopy,
|
|
75
|
+
onCopyError
|
|
76
|
+
}: Props = $props();
|
|
77
|
+
|
|
78
|
+
let copying = $state(false);
|
|
79
|
+
let copied = $state(false);
|
|
80
|
+
let copyFailed = $state(false);
|
|
81
|
+
let copyTimeout: ReturnType<typeof setTimeout> | undefined;
|
|
82
|
+
|
|
83
|
+
onDestroy(() => {
|
|
84
|
+
if (copyTimeout !== undefined) {
|
|
85
|
+
clearTimeout(copyTimeout);
|
|
86
|
+
copyTimeout = undefined;
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
async function copyToClipboard() {
|
|
91
|
+
if (copying) return;
|
|
92
|
+
|
|
93
|
+
if (copyTimeout !== undefined) {
|
|
94
|
+
clearTimeout(copyTimeout);
|
|
95
|
+
copyTimeout = undefined;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
copying = true;
|
|
99
|
+
copied = false;
|
|
100
|
+
copyFailed = false;
|
|
101
|
+
|
|
102
|
+
let writeSuccess = false;
|
|
103
|
+
let writeError: unknown;
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
if (typeof navigator === 'undefined' || !navigator.clipboard?.writeText) {
|
|
107
|
+
throw new Error('Clipboard API not available');
|
|
108
|
+
}
|
|
109
|
+
await navigator.clipboard.writeText(code);
|
|
110
|
+
writeSuccess = true;
|
|
111
|
+
} catch (e) {
|
|
112
|
+
writeError = e;
|
|
113
|
+
console.warn('[fivem-nui-components] Clipboard copy failed:', e);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
copying = false;
|
|
117
|
+
|
|
118
|
+
if (writeSuccess) {
|
|
119
|
+
copied = true;
|
|
120
|
+
try {
|
|
121
|
+
onCopy?.(code);
|
|
122
|
+
} catch (cbErr) {
|
|
123
|
+
console.error('[fivem-nui-components] onCopy callback error:', cbErr);
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
copyFailed = true;
|
|
127
|
+
try {
|
|
128
|
+
onCopyError?.(writeError);
|
|
129
|
+
} catch (cbErr) {
|
|
130
|
+
console.error('[fivem-nui-components] onCopyError callback error:', cbErr);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
copyTimeout = setTimeout(() => {
|
|
135
|
+
copied = false;
|
|
136
|
+
copyFailed = false;
|
|
137
|
+
copyTimeout = undefined;
|
|
138
|
+
}, 2000);
|
|
139
|
+
}
|
|
140
|
+
</script>
|
|
141
|
+
|
|
142
|
+
<div class="nui-code-block" class:nui-code-block--rounded={rounded}>
|
|
143
|
+
{#if title || showCopyButton}
|
|
144
|
+
<div class="nui-code-block__header">
|
|
145
|
+
{#if title}<span class="nui-code-block__title">{title}</span>{/if}
|
|
146
|
+
{#if showCopyButton}
|
|
147
|
+
<button
|
|
148
|
+
type="button"
|
|
149
|
+
class="nui-code-block__copy"
|
|
150
|
+
class:nui-code-block__copy--copied={copied}
|
|
151
|
+
class:nui-code-block__copy--error={copyFailed}
|
|
152
|
+
disabled={copying}
|
|
153
|
+
onclick={copyToClipboard}
|
|
154
|
+
aria-label={copied ? copiedLabel : copyFailed ? errorLabel : copyLabel}
|
|
155
|
+
>
|
|
156
|
+
<span aria-live="polite" aria-atomic="true">
|
|
157
|
+
{copied ? copiedLabel : copyFailed ? errorLabel : copyLabel}
|
|
158
|
+
</span>
|
|
159
|
+
</button>
|
|
160
|
+
{/if}
|
|
161
|
+
</div>
|
|
162
|
+
{/if}
|
|
163
|
+
|
|
164
|
+
<pre class="nui-code-block__pre"><code class="language-{language}">{code}</code></pre>
|
|
165
|
+
</div>
|
|
166
|
+
|
|
167
|
+
<style>
|
|
168
|
+
.nui-code-block {
|
|
169
|
+
width: 100%;
|
|
170
|
+
font-family: 'Consolas', 'Monaco', monospace;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.nui-code-block__header {
|
|
174
|
+
display: flex;
|
|
175
|
+
justify-content: space-between;
|
|
176
|
+
align-items: center;
|
|
177
|
+
padding: 0.5rem 1rem;
|
|
178
|
+
background: var(--nui-surface-alt, #101012);
|
|
179
|
+
border: 1px solid var(--nui-border, #27272a);
|
|
180
|
+
border-bottom: none;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.nui-code-block__title {
|
|
184
|
+
font-size: 0.8125rem;
|
|
185
|
+
color: var(--nui-fg-secondary, #8b8b99);
|
|
186
|
+
font-weight: 500;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.nui-code-block__copy {
|
|
190
|
+
background: transparent;
|
|
191
|
+
border: 1px solid var(--nui-border, #3f3f46);
|
|
192
|
+
border-radius: var(--nui-radius, 0.25rem);
|
|
193
|
+
padding: 0.25rem 0.5rem;
|
|
194
|
+
font-size: 0.75rem;
|
|
195
|
+
color: var(--nui-fg, #fafafa);
|
|
196
|
+
cursor: pointer;
|
|
197
|
+
transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.nui-code-block__copy:hover:not(:disabled) {
|
|
201
|
+
background: var(--nui-hover, rgb(255 255 255 / 0.06));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.nui-code-block__copy:disabled {
|
|
205
|
+
opacity: 0.6;
|
|
206
|
+
cursor: default;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.nui-code-block__copy:focus-visible {
|
|
210
|
+
outline: 2px solid var(--nui-primary, #3b82f6);
|
|
211
|
+
outline-offset: 2px;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.nui-code-block__copy--copied {
|
|
215
|
+
border-color: var(--nui-success, #22c55e);
|
|
216
|
+
color: var(--nui-success, #22c55e);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.nui-code-block__copy--error {
|
|
220
|
+
border-color: var(--nui-danger, #ef4444);
|
|
221
|
+
color: var(--nui-danger, #ef4444);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.nui-code-block__pre {
|
|
225
|
+
margin: 0;
|
|
226
|
+
padding: 1rem;
|
|
227
|
+
background: var(--nui-surface, #18181b);
|
|
228
|
+
border: 1px solid var(--nui-border, #27272a);
|
|
229
|
+
overflow-x: auto;
|
|
230
|
+
color: var(--nui-fg, #fafafa);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.nui-code-block--rounded .nui-code-block__header {
|
|
234
|
+
border-radius: var(--nui-radius, 0.5rem) var(--nui-radius, 0.5rem) 0 0;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.nui-code-block--rounded .nui-code-block__pre {
|
|
238
|
+
border-radius: var(--nui-radius, 0.5rem);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.nui-code-block--rounded .nui-code-block__header + .nui-code-block__pre {
|
|
242
|
+
border-radius: 0 0 var(--nui-radius, 0.5rem) var(--nui-radius, 0.5rem);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.nui-code-block__pre code {
|
|
246
|
+
font-size: 0.8125rem;
|
|
247
|
+
line-height: 1.5;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/* Scoped syntax highlighting tokens (when external highlighter is applied) */
|
|
251
|
+
.nui-code-block__pre :global(.string) { color: #facc15; }
|
|
252
|
+
.nui-code-block__pre :global(.keyword) { color: #60a5fa; font-weight: 600; }
|
|
253
|
+
.nui-code-block__pre :global(.function) { color: #a7f3d0; }
|
|
254
|
+
.nui-code-block__pre :global(.comment) { color: #6b7280; opacity: 0.8; font-style: italic; }
|
|
255
|
+
.nui-code-block__pre :global(.number) { color: #f472b6; }
|
|
256
|
+
</style>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export type NuiCodeLanguage = 'csharp' | 'javascript' | 'typescript' | 'lua' | 'sql' | 'json' | 'xml' | 'bash' | 'css' | 'markdown' | (string & {});
|
|
2
|
+
interface Props {
|
|
3
|
+
/** Code snippet to display. */
|
|
4
|
+
code?: string;
|
|
5
|
+
/**
|
|
6
|
+
* Programming language for syntax highlighting classes.
|
|
7
|
+
* Integrates with external highlighters like highlight.js or Prism.
|
|
8
|
+
* @defaultValue 'csharp'
|
|
9
|
+
*/
|
|
10
|
+
language?: NuiCodeLanguage;
|
|
11
|
+
/** Optional title or filename displayed in the header. */
|
|
12
|
+
title?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Whether to show the copy-to-clipboard button.
|
|
15
|
+
* @defaultValue true
|
|
16
|
+
*/
|
|
17
|
+
showCopyButton?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Rounded container borders.
|
|
20
|
+
* @defaultValue true
|
|
21
|
+
*/
|
|
22
|
+
rounded?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Label for copy button in idle state.
|
|
25
|
+
* @defaultValue 'Copy'
|
|
26
|
+
*/
|
|
27
|
+
copyLabel?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Label for copy button in copied state.
|
|
30
|
+
* @defaultValue 'Copied!'
|
|
31
|
+
*/
|
|
32
|
+
copiedLabel?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Label for copy button in error state.
|
|
35
|
+
* @defaultValue 'Failed'
|
|
36
|
+
*/
|
|
37
|
+
errorLabel?: string;
|
|
38
|
+
/** Callback executed on successful clipboard copy. */
|
|
39
|
+
onCopy?: (code: string) => void;
|
|
40
|
+
/** Callback executed when clipboard copy fails. */
|
|
41
|
+
onCopyError?: (error: unknown) => void;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* NuiCodeBlock
|
|
45
|
+
* Code snippet block with optional title, language syntax classes, and clipboard copy button.
|
|
46
|
+
*/
|
|
47
|
+
declare const NuiCodeBlock: import("svelte").Component<Props, {}, "">;
|
|
48
|
+
type NuiCodeBlock = ReturnType<typeof NuiCodeBlock>;
|
|
49
|
+
export default NuiCodeBlock;
|