@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
@@ -0,0 +1,306 @@
1
+ <script module lang="ts">
2
+ export type PageItem = number | "ellipsis-start" | "ellipsis-end";
3
+
4
+ /**
5
+ * Returns a stable-width list of page items.
6
+ * Always renders `slots` items (when total > slots): first, last, current window and ellipses.
7
+ */
8
+ export function getPageItems(
9
+ total: number,
10
+ current: number,
11
+ maxVisible: number,
12
+ ellipsis: boolean,
13
+ ): PageItem[] {
14
+ const tot = Math.max(1, Number.isFinite(total) ? Math.floor(total) : 1);
15
+ const slots = Math.max(
16
+ 5,
17
+ Number.isFinite(maxVisible) ? Math.floor(maxVisible) : 5,
18
+ );
19
+ const cur = Math.min(Math.max(1, Math.floor(current) || 1), tot);
20
+
21
+ if (tot <= slots) return Array.from({ length: tot }, (_, i) => i + 1);
22
+
23
+ // Without ellipses we simply slide a window of `slots` pages
24
+ if (!ellipsis) {
25
+ const start = Math.min(
26
+ Math.max(1, cur - Math.floor(slots / 2)),
27
+ tot - slots + 1,
28
+ );
29
+ return Array.from({ length: slots }, (_, i) => start + i);
30
+ }
31
+
32
+ const inner = slots - 2; // slots minus first and last
33
+ const nearStart = cur <= Math.ceil(slots / 2);
34
+ const nearEnd = cur > tot - Math.ceil(slots / 2);
35
+
36
+ if (nearStart) {
37
+ const pages = Array.from({ length: inner }, (_, i) => i + 1);
38
+ return [...pages, "ellipsis-end", tot];
39
+ }
40
+
41
+ if (nearEnd) {
42
+ const start = tot - inner + 1;
43
+ const pages = Array.from({ length: inner }, (_, i) => start + i);
44
+ return [1, "ellipsis-start", ...pages];
45
+ }
46
+
47
+ const middle = inner - 2; // minus two ellipses
48
+ const start = cur - Math.floor((middle - 1) / 2);
49
+ const pages = Array.from({ length: middle }, (_, i) => start + i);
50
+ return [1, "ellipsis-start", ...pages, "ellipsis-end", tot];
51
+ }
52
+ </script>
53
+
54
+ <script lang="ts">
55
+ import type { HTMLAttributes } from "svelte/elements";
56
+
57
+ interface Labels {
58
+ nav: string;
59
+ first: string;
60
+ previous: string;
61
+ next: string;
62
+ last: string;
63
+ page: (n: number) => string;
64
+ status: (page: number, total: number) => string;
65
+ }
66
+
67
+ interface Props extends Omit<HTMLAttributes<HTMLElement>, "children"> {
68
+ /** Total number of items */
69
+ totalItems: number;
70
+ /** Items per page */
71
+ pageSize?: number;
72
+ /** Current page (1-based). Supports `bind:page` */
73
+ page?: number;
74
+ /** Number of slots in the list (min 5). The width stays stable while navigating */
75
+ maxPages?: number;
76
+ /** Show first/last buttons */
77
+ showFirstLast?: boolean;
78
+ /** Show ellipses for skipped ranges. When false, a sliding window is used */
79
+ showEllipsis?: boolean;
80
+ /**
81
+ * Called after the page changed through direct user interaction (clicking a page button or next/prev).
82
+ * Note: programmatic adjustments (such as clamp adjustments via $effect when `totalItems` decreases) do not trigger this callback.
83
+ */
84
+ onChange?: (page: number) => void;
85
+ /** Override texts (localization / accessibility) */
86
+ labels?: Partial<Labels>;
87
+ }
88
+
89
+ const defaultLabels: Labels = {
90
+ nav: "Pagination",
91
+ first: "First page",
92
+ previous: "Previous page",
93
+ next: "Next page",
94
+ last: "Last page",
95
+ page: (n) => `Page ${n}`,
96
+ status: (p, t) => `Page ${p} of ${t}`,
97
+ };
98
+
99
+ let {
100
+ totalItems,
101
+ pageSize = 10,
102
+ page = $bindable(1),
103
+ maxPages = 7,
104
+ showFirstLast = true,
105
+ showEllipsis = true,
106
+ onChange,
107
+ labels,
108
+ class: className = "",
109
+ ...rest
110
+ }: Props = $props();
111
+
112
+ const t = $derived({ ...defaultLabels, ...labels });
113
+
114
+ const safeSize = $derived(
115
+ Number.isFinite(pageSize) && pageSize >= 1 ? Math.floor(pageSize) : 10,
116
+ );
117
+ const safeItems = $derived(
118
+ Number.isFinite(totalItems) && totalItems > 0 ? totalItems : 0,
119
+ );
120
+ const totalPages = $derived(Math.max(1, Math.ceil(safeItems / safeSize)));
121
+ const current = $derived(
122
+ Math.min(Math.max(1, Math.floor(page) || 1), totalPages),
123
+ );
124
+
125
+ const items = $derived(
126
+ getPageItems(totalPages, current, maxPages, showEllipsis),
127
+ );
128
+
129
+ // Keep the bound value valid after totalItems/pageSize shrink
130
+ $effect(() => {
131
+ if (page !== current) page = current;
132
+ });
133
+
134
+ function goToPage(next: number) {
135
+ if (next < 1 || next > totalPages || next === current) return;
136
+ page = next;
137
+ onChange?.(next);
138
+ }
139
+ </script>
140
+
141
+ <nav {...rest} class={["nui-pagination", className]} aria-label={t.nav}>
142
+ <span class="nui-pagination__info" aria-live="polite">
143
+ {t.status(current, totalPages)}
144
+ </span>
145
+
146
+ <ol class="nui-pagination__list">
147
+ {#if showFirstLast}
148
+ <li>
149
+ <button
150
+ type="button"
151
+ class="nui-pagination__btn nui-pagination__btn--edge"
152
+ aria-label={t.first}
153
+ disabled={current === 1}
154
+ onclick={() => goToPage(1)}
155
+ >
156
+ <span aria-hidden="true">«</span>
157
+ </button>
158
+ </li>
159
+ {/if}
160
+
161
+ <li>
162
+ <button
163
+ type="button"
164
+ class="nui-pagination__btn"
165
+ aria-label={t.previous}
166
+ disabled={current === 1}
167
+ onclick={() => goToPage(current - 1)}
168
+ >
169
+ <span aria-hidden="true">‹</span>
170
+ </button>
171
+ </li>
172
+
173
+ {#each items as item (item)}
174
+ {#if typeof item === "string"}
175
+ <li class="nui-pagination__ellipsis" aria-hidden="true">…</li>
176
+ {:else}
177
+ <li>
178
+ <button
179
+ type="button"
180
+ class="nui-pagination__btn"
181
+ class:nui-pagination__btn--active={item === current}
182
+ aria-label={t.page(item)}
183
+ aria-current={item === current ? "page" : undefined}
184
+ onclick={() => goToPage(item)}
185
+ >
186
+ {item}
187
+ </button>
188
+ </li>
189
+ {/if}
190
+ {/each}
191
+
192
+ <li>
193
+ <button
194
+ type="button"
195
+ class="nui-pagination__btn"
196
+ aria-label={t.next}
197
+ disabled={current === totalPages}
198
+ onclick={() => goToPage(current + 1)}
199
+ >
200
+ <span aria-hidden="true">›</span>
201
+ </button>
202
+ </li>
203
+
204
+ {#if showFirstLast}
205
+ <li>
206
+ <button
207
+ type="button"
208
+ class="nui-pagination__btn nui-pagination__btn--edge"
209
+ aria-label={t.last}
210
+ disabled={current === totalPages}
211
+ onclick={() => goToPage(totalPages)}
212
+ >
213
+ <span aria-hidden="true">»</span>
214
+ </button>
215
+ </li>
216
+ {/if}
217
+ </ol>
218
+ </nav>
219
+
220
+ <style>
221
+ .nui-pagination {
222
+ --_size: var(--nui-pagination-size, 2.5rem);
223
+ display: flex;
224
+ flex-direction: column;
225
+ align-items: center;
226
+ gap: 0.5rem;
227
+ font-family: inherit;
228
+ box-sizing: border-box;
229
+ }
230
+
231
+ .nui-pagination__info {
232
+ font-size: 0.875rem;
233
+ opacity: 0.7;
234
+ }
235
+
236
+ .nui-pagination__list {
237
+ display: flex;
238
+ flex-wrap: wrap;
239
+ justify-content: center;
240
+ gap: 0.25rem;
241
+ list-style: none;
242
+ margin: 0;
243
+ padding: 0;
244
+ }
245
+
246
+ .nui-pagination__btn {
247
+ display: flex;
248
+ align-items: center;
249
+ justify-content: center;
250
+ width: var(--_size);
251
+ height: var(--_size);
252
+ padding: 0;
253
+ background: transparent;
254
+ color: inherit;
255
+ border: 1px solid var(--nui-border, #3f3f46);
256
+ border-radius: 0.25rem;
257
+ font: inherit;
258
+ cursor: pointer;
259
+ transition:
260
+ background 0.15s ease,
261
+ border-color 0.15s ease;
262
+ }
263
+
264
+ .nui-pagination__btn:hover:not(:disabled) {
265
+ background: var(--nui-hover, rgb(255 255 255 / 0.06));
266
+ }
267
+
268
+ .nui-pagination__btn:focus-visible {
269
+ outline: 2px solid var(--nui-primary, #3b82f6);
270
+ outline-offset: 2px;
271
+ }
272
+
273
+ .nui-pagination__btn:disabled {
274
+ opacity: 0.4;
275
+ cursor: not-allowed;
276
+ }
277
+
278
+ .nui-pagination__btn--active {
279
+ background: var(--nui-primary, #3b82f6);
280
+ border-color: var(--nui-primary, #3b82f6);
281
+ color: var(--nui-primary-fg, #fff);
282
+ }
283
+
284
+ .nui-pagination__btn--active:hover:not(:disabled) {
285
+ background: var(--nui-primary, #3b82f6);
286
+ }
287
+
288
+ .nui-pagination__btn--edge {
289
+ width: calc(var(--_size) * 0.8);
290
+ font-size: 0.75rem;
291
+ }
292
+
293
+ .nui-pagination__ellipsis {
294
+ display: flex;
295
+ align-items: center;
296
+ justify-content: center;
297
+ width: var(--_size);
298
+ opacity: 0.5;
299
+ }
300
+
301
+ @media (prefers-reduced-motion: reduce) {
302
+ .nui-pagination__btn {
303
+ transition: none;
304
+ }
305
+ }
306
+ </style>
@@ -0,0 +1,186 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+ import type { HTMLAttributes } from "svelte/elements";
4
+
5
+ interface Labels {
6
+ nav: string;
7
+ toggle: string;
8
+ }
9
+
10
+ interface Props extends Omit<HTMLAttributes<HTMLElement>, "children"> {
11
+ /** Whether the sidebar is open. Supports `bind:open` */
12
+ open?: boolean;
13
+ /** Show the toggle button */
14
+ collapsible?: boolean;
15
+ /** Sidebar width. Numbers are treated as px */
16
+ width?: string | number;
17
+ /** Called after the user toggled the sidebar */
18
+ onToggle?: (open: boolean) => void;
19
+ /** Optional class applied directly to the inner <aside> panel */
20
+ panelClass?: string;
21
+ /** Override texts (localization) */
22
+ labels?: Partial<Labels>;
23
+ brand?: Snippet;
24
+ children?: Snippet;
25
+ footer?: Snippet;
26
+ }
27
+
28
+ const uid = $props.id();
29
+ const panelId = `nui-sidebar-${uid}`;
30
+
31
+ const defaultLabels: Labels = { nav: "Sidebar", toggle: "Toggle sidebar" };
32
+
33
+ let {
34
+ id: idProp,
35
+ open = $bindable(true),
36
+ collapsible = false,
37
+ width = "250px",
38
+ onToggle,
39
+ labels,
40
+ brand,
41
+ children,
42
+ footer,
43
+ class: className = "",
44
+ style,
45
+ panelClass = "",
46
+ ...rest
47
+ }: Props = $props();
48
+
49
+ const resolvedId = $derived(idProp || panelId);
50
+ const t = $derived({ ...defaultLabels, ...labels });
51
+ const toCss = (v: string | number) => (typeof v === "number" ? `${v}px` : v);
52
+ const styleVars = $derived(`--nui-sidebar-width:${toCss(width)};`);
53
+ const styleExtra = $derived(typeof style === "string" ? style : "");
54
+
55
+ function toggle() {
56
+ open = !open;
57
+ onToggle?.(open);
58
+ }
59
+ </script>
60
+
61
+ <div
62
+ class={[
63
+ "nui-sidebar-wrap",
64
+ !open && "nui-sidebar-wrap--collapsed",
65
+ className,
66
+ ]}
67
+ style={styleVars + styleExtra}
68
+ >
69
+ <aside
70
+ {...rest}
71
+ id={resolvedId}
72
+ class={["nui-sidebar", panelClass]}
73
+ aria-label={t.nav}
74
+ inert={!open || undefined}
75
+ >
76
+ {#if brand}
77
+ <div class="nui-sidebar__brand">{@render brand()}</div>
78
+ {/if}
79
+
80
+ <div class="nui-sidebar__content">{@render children?.()}</div>
81
+
82
+ {#if footer}
83
+ <div class="nui-sidebar__footer">{@render footer()}</div>
84
+ {/if}
85
+ </aside>
86
+
87
+ {#if collapsible}
88
+ <button
89
+ type="button"
90
+ class="nui-sidebar__toggle"
91
+ aria-label={t.toggle}
92
+ aria-expanded={open}
93
+ aria-controls={resolvedId}
94
+ onclick={toggle}
95
+ >
96
+ <span aria-hidden="true">{open ? "◀" : "▶"}</span>
97
+ </button>
98
+ {/if}
99
+ </div>
100
+
101
+ <style>
102
+ .nui-sidebar-wrap {
103
+ position: relative;
104
+ flex: none;
105
+ width: var(--nui-sidebar-width, 250px);
106
+ max-width: 100%;
107
+ height: 100%;
108
+ box-sizing: border-box;
109
+ transition: width 0.25s ease;
110
+ }
111
+
112
+ .nui-sidebar-wrap--collapsed {
113
+ width: 0;
114
+ }
115
+
116
+ .nui-sidebar {
117
+ display: flex;
118
+ flex-direction: column;
119
+ width: var(--nui-sidebar-width, 250px);
120
+ height: 100%;
121
+ background: var(--nui-surface-alt, #18181b);
122
+ border-right: 1px solid var(--nui-border, #27272a);
123
+ font-family: inherit;
124
+ box-sizing: border-box;
125
+ overflow: hidden;
126
+ transition:
127
+ transform 0.25s ease,
128
+ visibility 0.25s;
129
+ }
130
+
131
+ .nui-sidebar-wrap--collapsed .nui-sidebar {
132
+ transform: translateX(-100%);
133
+ visibility: hidden;
134
+ }
135
+
136
+ .nui-sidebar__brand {
137
+ border-bottom: 1px solid var(--nui-border, #27272a);
138
+ }
139
+
140
+ .nui-sidebar__content {
141
+ flex: 1;
142
+ overflow-y: auto;
143
+ min-height: 0;
144
+ padding: 1rem;
145
+ }
146
+
147
+ .nui-sidebar__footer {
148
+ border-top: 1px solid var(--nui-border, #27272a);
149
+ }
150
+
151
+ .nui-sidebar__toggle {
152
+ position: absolute;
153
+ top: 0.5rem;
154
+ left: 100%;
155
+ display: flex;
156
+ align-items: center;
157
+ justify-content: center;
158
+ padding: 0.5rem 0.4rem;
159
+ background: var(--nui-surface-alt, #18181b);
160
+ border: 1px solid var(--nui-border, #27272a);
161
+ border-left: 0;
162
+ border-radius: 0 var(--nui-radius, 0.375rem) var(--nui-radius, 0.375rem) 0;
163
+ color: var(--nui-fg-secondary, #8b8b99);
164
+ font-size: 0.85rem;
165
+ cursor: pointer;
166
+ z-index: var(--nui-z-sidebar-toggle, 50);
167
+ transition: color 0.15s ease;
168
+ }
169
+
170
+ .nui-sidebar__toggle:hover {
171
+ color: var(--nui-fg, #fafafa);
172
+ }
173
+
174
+ .nui-sidebar__toggle:focus-visible {
175
+ outline: 2px solid var(--nui-primary, #3b82f6);
176
+ outline-offset: 2px;
177
+ }
178
+
179
+ @media (prefers-reduced-motion: reduce) {
180
+ .nui-sidebar-wrap,
181
+ .nui-sidebar,
182
+ .nui-sidebar__toggle {
183
+ transition: none;
184
+ }
185
+ }
186
+ </style>