@classytic/fluid 0.2.1 → 0.3.2

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 (69) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +149 -62
  3. package/dist/api-pagination-CJ0vR_w6.d.mts +34 -0
  4. package/dist/api-pagination-DBTE0yk4.mjs +190 -0
  5. package/dist/chunk-DQk6qfdC.mjs +18 -0
  6. package/dist/client/calendar.d.mts +105 -0
  7. package/dist/client/calendar.mjs +202 -0
  8. package/dist/client/core.d.mts +1614 -0
  9. package/dist/client/core.mjs +2779 -0
  10. package/dist/client/error.d.mts +125 -0
  11. package/dist/client/error.mjs +166 -0
  12. package/dist/client/hooks.d.mts +162 -0
  13. package/dist/client/hooks.mjs +447 -0
  14. package/dist/client/table.d.mts +84 -0
  15. package/dist/client/table.mjs +373 -0
  16. package/dist/client/theme.d.mts +6 -0
  17. package/dist/client/theme.mjs +65 -0
  18. package/dist/command.d.mts +134 -0
  19. package/dist/command.mjs +132 -0
  20. package/dist/compact.d.mts +359 -0
  21. package/dist/compact.mjs +892 -0
  22. package/dist/dashboard.d.mts +778 -0
  23. package/dist/dashboard.mjs +1617 -0
  24. package/dist/filter-utils-DqMmy_v-.mjs +72 -0
  25. package/dist/filter-utils-IZ0GtuPo.d.mts +40 -0
  26. package/dist/forms.d.mts +1549 -0
  27. package/dist/forms.mjs +3740 -0
  28. package/dist/index.d.mts +296 -0
  29. package/dist/index.mjs +432 -0
  30. package/dist/layouts.d.mts +215 -0
  31. package/dist/layouts.mjs +460 -0
  32. package/dist/search-context-DR7DBs7S.mjs +19 -0
  33. package/dist/search.d.mts +254 -0
  34. package/dist/search.mjs +523 -0
  35. package/dist/sheet-wrapper-CWNCvYMD.mjs +211 -0
  36. package/dist/use-base-search-BGgWnWaF.d.mts +35 -0
  37. package/dist/use-debounce-xmZucz5e.mjs +53 -0
  38. package/dist/use-keyboard-shortcut-Bl6YM5Q7.mjs +82 -0
  39. package/dist/use-keyboard-shortcut-_mRCh3QO.d.mts +24 -0
  40. package/dist/use-media-query-BnVNIKT4.mjs +17 -0
  41. package/dist/use-mobile-BX3SQVo2.mjs +20 -0
  42. package/dist/use-scroll-detection-CsgsQYvy.mjs +43 -0
  43. package/dist/utils-CDue7cEt.d.mts +6 -0
  44. package/dist/utils-DQ5SCVoW.mjs +10 -0
  45. package/package.json +85 -45
  46. package/styles.css +2 -2
  47. package/dist/chunk-GUHK2DTW.js +0 -15
  48. package/dist/chunk-GUHK2DTW.js.map +0 -1
  49. package/dist/chunk-H3NFL3GJ.js +0 -57
  50. package/dist/chunk-H3NFL3GJ.js.map +0 -1
  51. package/dist/chunk-J2YRTQE4.js +0 -293
  52. package/dist/chunk-J2YRTQE4.js.map +0 -1
  53. package/dist/compact.d.ts +0 -217
  54. package/dist/compact.js +0 -986
  55. package/dist/compact.js.map +0 -1
  56. package/dist/dashboard.d.ts +0 -386
  57. package/dist/dashboard.js +0 -1032
  58. package/dist/dashboard.js.map +0 -1
  59. package/dist/index.d.ts +0 -2141
  60. package/dist/index.js +0 -6460
  61. package/dist/index.js.map +0 -1
  62. package/dist/layout.d.ts +0 -25
  63. package/dist/layout.js +0 -4
  64. package/dist/layout.js.map +0 -1
  65. package/dist/search.d.ts +0 -172
  66. package/dist/search.js +0 -341
  67. package/dist/search.js.map +0 -1
  68. package/dist/use-base-search-AS5Z3SAy.d.ts +0 -64
  69. package/dist/utils-Cbsgs0XP.d.ts +0 -5
@@ -0,0 +1,373 @@
1
+ "use client";
2
+
3
+ import { t as cn } from "../utils-DQ5SCVoW.mjs";
4
+ import { t as useIsMobile } from "../use-mobile-BX3SQVo2.mjs";
5
+ import { t as useScrollDetection } from "../use-scroll-detection-CsgsQYvy.mjs";
6
+ import { t as ApiPagination } from "../api-pagination-DBTE0yk4.mjs";
7
+ import { n as useSearch } from "../search-context-DR7DBs7S.mjs";
8
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
+ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
10
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
11
+ import { ArrowDown, ArrowUp, ArrowUpDown, ChevronLeft, ChevronRight, Search, SlidersHorizontal, X } from "lucide-react";
12
+ import { Button } from "@/components/ui/button";
13
+ import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
14
+ import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
15
+ import { Badge } from "@/components/ui/badge";
16
+ import { Sheet, SheetContent, SheetHeader, SheetTitle } from "@/components/ui/sheet";
17
+ import { flexRender, getCoreRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
18
+ import { Input } from "@/components/ui/input";
19
+
20
+ //#region src/components/data-table.tsx
21
+ const ScrollButton = React.memo(function ScrollButton({ direction, onClick, visible, className }) {
22
+ if (!visible) return null;
23
+ const handleClick = (e) => {
24
+ e.preventDefault();
25
+ e.stopPropagation();
26
+ onClick();
27
+ };
28
+ return /* @__PURE__ */ jsx("button", {
29
+ type: "button",
30
+ className: cn("absolute top-1/2 -translate-y-1/2 z-50", "flex items-center justify-center", "size-10 rounded-md shadow-lg border-2 border-border", "bg-background hover:bg-muted", "transition-transform duration-200", "hover:scale-105 active:scale-95", direction === "left" ? "left-2" : "right-2", className),
31
+ onClick: handleClick,
32
+ onMouseDown: (e) => e.stopPropagation(),
33
+ "aria-label": `Scroll ${direction}`,
34
+ children: direction === "left" ? /* @__PURE__ */ jsx(ChevronLeft, { className: "size-5" }) : /* @__PURE__ */ jsx(ChevronRight, { className: "size-5" })
35
+ });
36
+ });
37
+ function DataTable({ columns, data, isLoading = false, pagination, enableSorting = false, enableRowSelection = false, onRowSelectionChange, className, loadingState: customLoadingState, emptyState: customEmptyState }) {
38
+ const [sorting, setSorting] = useState([]);
39
+ const [rowSelection, setRowSelection] = useState({});
40
+ const scrollAreaRef = useRef(null);
41
+ const { canScrollLeft, canScrollRight, isScrollable, checkScroll } = useScrollDetection(scrollAreaRef);
42
+ const { total = 0, limit = 10, pages = 1, page = 1, hasNext = false, hasPrev = false, onPageChange = () => {} } = pagination ?? {};
43
+ const table = useReactTable({
44
+ data,
45
+ columns,
46
+ getCoreRowModel: getCoreRowModel(),
47
+ getSortedRowModel: enableSorting ? getSortedRowModel() : void 0,
48
+ onSortingChange: setSorting,
49
+ onRowSelectionChange: enableRowSelection ? (updater) => {
50
+ setRowSelection(updater);
51
+ if (onRowSelectionChange) {
52
+ const newSelection = typeof updater === "function" ? updater(rowSelection) : updater;
53
+ onRowSelectionChange(data.filter((_, index) => newSelection[index]));
54
+ }
55
+ } : void 0,
56
+ state: {
57
+ sorting,
58
+ rowSelection: enableRowSelection ? rowSelection : void 0
59
+ },
60
+ enableRowSelection
61
+ });
62
+ const scrollHorizontally = useCallback((direction) => {
63
+ const scrollContainer = scrollAreaRef.current?.querySelector("[data-slot=\"scroll-area-viewport\"]");
64
+ if (!scrollContainer) return;
65
+ const scrollAmount = Math.min(300, scrollContainer.clientWidth * .8);
66
+ scrollContainer.scrollBy({
67
+ left: direction === "left" ? -scrollAmount : scrollAmount,
68
+ behavior: "smooth"
69
+ });
70
+ }, []);
71
+ const handleWheel = useCallback((e) => {
72
+ if (!isScrollable) return;
73
+ const scrollContainer = scrollAreaRef.current?.querySelector("[data-slot=\"scroll-area-viewport\"]");
74
+ if (!scrollContainer?.contains(e.target)) return;
75
+ if (e.shiftKey || Math.abs(e.deltaX) > Math.abs(e.deltaY)) {
76
+ e.preventDefault();
77
+ const delta = e.deltaY || e.deltaX;
78
+ scrollContainer.scrollBy({
79
+ left: delta,
80
+ behavior: "auto"
81
+ });
82
+ }
83
+ }, [isScrollable]);
84
+ useEffect(() => {
85
+ const scrollContainer = scrollAreaRef.current?.querySelector("[data-slot=\"scroll-area-viewport\"]");
86
+ if (!scrollContainer) return;
87
+ checkScroll();
88
+ scrollContainer.addEventListener("scroll", checkScroll, { passive: true });
89
+ scrollContainer.addEventListener("wheel", handleWheel, { passive: false });
90
+ return () => {
91
+ scrollContainer.removeEventListener("scroll", checkScroll);
92
+ scrollContainer.removeEventListener("wheel", handleWheel);
93
+ };
94
+ }, [checkScroll, handleWheel]);
95
+ useEffect(() => {
96
+ const timer = setTimeout(checkScroll, 150);
97
+ return () => clearTimeout(timer);
98
+ }, [data, checkScroll]);
99
+ const defaultLoadingState = useMemo(() => /* @__PURE__ */ jsx("div", {
100
+ className: "w-full h-full min-h-[24rem] flex items-center justify-center bg-background/50 rounded-lg border border-border",
101
+ children: /* @__PURE__ */ jsxs("div", {
102
+ className: "flex flex-col items-center gap-3",
103
+ children: [/* @__PURE__ */ jsx("div", { className: "animate-spin rounded-full h-8 w-8 border-2 border-primary border-t-transparent" }), /* @__PURE__ */ jsx("p", {
104
+ className: "text-sm text-muted-foreground",
105
+ children: "Loading data..."
106
+ })]
107
+ })
108
+ }), []);
109
+ const defaultEmptyState = useMemo(() => /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, {
110
+ colSpan: columns.length,
111
+ className: "h-32 text-center",
112
+ children: /* @__PURE__ */ jsxs("div", {
113
+ className: "flex flex-col items-center justify-center py-8",
114
+ children: [
115
+ /* @__PURE__ */ jsx("div", {
116
+ className: "rounded-full bg-muted p-3 mb-4",
117
+ children: /* @__PURE__ */ jsx(Search, { className: "h-6 w-6 text-muted-foreground" })
118
+ }),
119
+ /* @__PURE__ */ jsx("p", {
120
+ className: "text-lg font-medium text-foreground mb-1",
121
+ children: "No results found"
122
+ }),
123
+ /* @__PURE__ */ jsx("p", {
124
+ className: "text-sm text-muted-foreground",
125
+ children: "Try adjusting your search or filters"
126
+ })
127
+ ]
128
+ })
129
+ }) }), [columns.length]);
130
+ if (isLoading) return /* @__PURE__ */ jsx(Fragment, { children: customLoadingState || defaultLoadingState });
131
+ return /* @__PURE__ */ jsxs("div", {
132
+ className: cn("flex flex-col h-full gap-4", className),
133
+ children: [/* @__PURE__ */ jsxs("div", {
134
+ className: "flex-1 min-h-0 rounded-lg border overflow-hidden bg-background shadow-sm relative",
135
+ children: [
136
+ /* @__PURE__ */ jsxs(ScrollArea, {
137
+ ref: scrollAreaRef,
138
+ className: "h-full w-full",
139
+ children: [/* @__PURE__ */ jsx("div", {
140
+ className: "min-w-full",
141
+ children: /* @__PURE__ */ jsxs(Table, { children: [/* @__PURE__ */ jsx(TableHeader, {
142
+ className: "sticky top-0 z-10 bg-muted/50 backdrop-blur-sm",
143
+ children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx(TableRow, {
144
+ className: "border-b border-border hover:bg-transparent",
145
+ children: headerGroup.headers.map((header) => /* @__PURE__ */ jsx(TableHead, {
146
+ className: cn("bg-muted/50 font-semibold text-foreground h-12 px-4 whitespace-nowrap", "first:rounded-tl-lg last:rounded-tr-lg"),
147
+ children: header.isPlaceholder ? null : enableSorting && header.column.getCanSort() ? /* @__PURE__ */ jsxs(Button, {
148
+ variant: "ghost",
149
+ onClick: () => header.column.toggleSorting(header.column.getIsSorted() === "asc"),
150
+ className: "h-auto p-0 font-semibold hover:bg-transparent whitespace-nowrap",
151
+ children: [flexRender(header.column.columnDef.header, header.getContext()), header.column.getIsSorted() === "asc" ? /* @__PURE__ */ jsx(ArrowUp, { className: "ml-2 h-4 w-4" }) : header.column.getIsSorted() === "desc" ? /* @__PURE__ */ jsx(ArrowDown, { className: "ml-2 h-4 w-4" }) : /* @__PURE__ */ jsx(ArrowUpDown, { className: "ml-2 h-4 w-4 opacity-50" })]
152
+ }) : flexRender(header.column.columnDef.header, header.getContext())
153
+ }, header.id))
154
+ }, headerGroup.id))
155
+ }), /* @__PURE__ */ jsx(TableBody, { children: table.getRowModel().rows?.length ? table.getRowModel().rows.map((row, index) => /* @__PURE__ */ jsx(TableRow, {
156
+ className: cn("hover:bg-muted/50 transition-colors border-b border-border/50", index % 2 === 0 ? "bg-background" : "bg-muted/20"),
157
+ children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx(TableCell, {
158
+ className: "px-4 py-3 text-sm whitespace-nowrap",
159
+ children: flexRender(cell.column.columnDef.cell, cell.getContext())
160
+ }, cell.id))
161
+ }, row.id)) : customEmptyState ? /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, {
162
+ colSpan: columns.length,
163
+ className: "h-32 text-center",
164
+ children: customEmptyState
165
+ }) }) : defaultEmptyState })] })
166
+ }), /* @__PURE__ */ jsx(ScrollBar, { orientation: "horizontal" })]
167
+ }),
168
+ canScrollLeft && /* @__PURE__ */ jsx(ScrollButton, {
169
+ direction: "left",
170
+ onClick: () => scrollHorizontally("left"),
171
+ visible: true
172
+ }),
173
+ canScrollRight && /* @__PURE__ */ jsx(ScrollButton, {
174
+ direction: "right",
175
+ onClick: () => scrollHorizontally("right"),
176
+ visible: true
177
+ })
178
+ ]
179
+ }), pagination && /* @__PURE__ */ jsx(ApiPagination, {
180
+ total,
181
+ limit,
182
+ pages,
183
+ page,
184
+ hasNext,
185
+ hasPrev,
186
+ onPageChange
187
+ })]
188
+ });
189
+ }
190
+
191
+ //#endregion
192
+ //#region src/components/data-table-toolbar.tsx
193
+ /**
194
+ * DataTableToolbar - Composable toolbar with search, filters, and bulk actions
195
+ *
196
+ * Must be used inside a `Search.Root` context.
197
+ *
198
+ * @example
199
+ * ```tsx
200
+ * <Search.Root hook={searchHook}>
201
+ * <DataTableToolbar
202
+ * searchPlaceholder="Search users..."
203
+ * filterContent={<SelectInput name="role" items={roles} />}
204
+ * totalResults={data?.total}
205
+ * selectedCount={selected.length}
206
+ * bulkActions={<Button variant="destructive">Delete</Button>}
207
+ * />
208
+ * <DataTable columns={columns} data={data?.items ?? []} />
209
+ * </Search.Root>
210
+ * ```
211
+ */
212
+ function DataTableToolbar({ children, className, showSearch = true, searchPlaceholder = "Search...", showFilters = true, filterContent, filterTitle = "Filters", selectedCount, bulkActions, totalResults, showResultCount }) {
213
+ const search = useSearch();
214
+ const isMobile = useIsMobile();
215
+ const [filterOpen, setFilterOpen] = useState(false);
216
+ const hasSelection = selectedCount != null && selectedCount > 0;
217
+ const showResults = showResultCount ?? (totalResults != null && totalResults >= 0);
218
+ const handleSearchInput = useCallback((e) => {
219
+ search.setSearchValue(e.target.value);
220
+ }, [search]);
221
+ if (hasSelection) return /* @__PURE__ */ jsxs("div", {
222
+ className: cn("flex items-center gap-3 py-2", className),
223
+ children: [
224
+ /* @__PURE__ */ jsxs(Badge, {
225
+ variant: "secondary",
226
+ className: "shrink-0",
227
+ children: [selectedCount, " selected"]
228
+ }),
229
+ /* @__PURE__ */ jsx("div", {
230
+ className: "flex items-center gap-2",
231
+ children: bulkActions
232
+ }),
233
+ /* @__PURE__ */ jsx("div", { className: "flex-1" }),
234
+ children
235
+ ]
236
+ });
237
+ const filterElement = showFilters && filterContent && /* @__PURE__ */ jsx(Fragment, { children: isMobile ? /* @__PURE__ */ jsxs(Sheet, {
238
+ open: filterOpen,
239
+ onOpenChange: setFilterOpen,
240
+ children: [/* @__PURE__ */ jsxs(Button, {
241
+ variant: "outline",
242
+ size: "sm",
243
+ onClick: () => setFilterOpen(true),
244
+ className: "shrink-0",
245
+ children: [
246
+ /* @__PURE__ */ jsx(SlidersHorizontal, { className: "mr-2 h-4 w-4" }),
247
+ filterTitle,
248
+ search.hasActiveFilters && /* @__PURE__ */ jsx(Badge, {
249
+ variant: "default",
250
+ className: "ml-1.5 h-5 min-w-5 px-1",
251
+ children: "!"
252
+ })
253
+ ]
254
+ }), /* @__PURE__ */ jsxs(SheetContent, {
255
+ side: "bottom",
256
+ children: [
257
+ /* @__PURE__ */ jsx(SheetHeader, { children: /* @__PURE__ */ jsx(SheetTitle, { children: filterTitle }) }),
258
+ /* @__PURE__ */ jsx("div", {
259
+ className: "py-4 space-y-4",
260
+ children: filterContent
261
+ }),
262
+ /* @__PURE__ */ jsxs("div", {
263
+ className: "flex gap-2 pt-2",
264
+ children: [/* @__PURE__ */ jsx(Button, {
265
+ variant: "outline",
266
+ className: "flex-1",
267
+ onClick: () => {
268
+ search.clearSearch();
269
+ setFilterOpen(false);
270
+ },
271
+ children: "Clear"
272
+ }), /* @__PURE__ */ jsx(Button, {
273
+ className: "flex-1",
274
+ onClick: () => {
275
+ search.handleSearch();
276
+ setFilterOpen(false);
277
+ },
278
+ children: "Apply"
279
+ })]
280
+ })
281
+ ]
282
+ })]
283
+ }) : /* @__PURE__ */ jsxs(Popover, {
284
+ open: filterOpen,
285
+ onOpenChange: setFilterOpen,
286
+ children: [/* @__PURE__ */ jsx(PopoverTrigger, { render: /* @__PURE__ */ jsxs(Button, {
287
+ variant: "outline",
288
+ size: "sm",
289
+ className: "shrink-0",
290
+ children: [
291
+ /* @__PURE__ */ jsx(SlidersHorizontal, { className: "mr-2 h-4 w-4" }),
292
+ filterTitle,
293
+ search.hasActiveFilters && /* @__PURE__ */ jsx(Badge, {
294
+ variant: "default",
295
+ className: "ml-1.5 h-5 min-w-5 px-1",
296
+ children: "!"
297
+ })
298
+ ]
299
+ }) }), /* @__PURE__ */ jsx(PopoverContent, {
300
+ className: "w-80",
301
+ align: "end",
302
+ children: /* @__PURE__ */ jsxs("div", {
303
+ className: "space-y-4",
304
+ children: [
305
+ /* @__PURE__ */ jsx("h4", {
306
+ className: "font-medium text-sm",
307
+ children: filterTitle
308
+ }),
309
+ filterContent,
310
+ /* @__PURE__ */ jsxs("div", {
311
+ className: "flex gap-2 pt-2",
312
+ children: [/* @__PURE__ */ jsx(Button, {
313
+ variant: "outline",
314
+ size: "sm",
315
+ className: "flex-1",
316
+ onClick: () => {
317
+ search.clearSearch();
318
+ setFilterOpen(false);
319
+ },
320
+ children: "Clear"
321
+ }), /* @__PURE__ */ jsx(Button, {
322
+ size: "sm",
323
+ className: "flex-1",
324
+ onClick: () => {
325
+ search.handleSearch();
326
+ setFilterOpen(false);
327
+ },
328
+ children: "Apply"
329
+ })]
330
+ })
331
+ ]
332
+ })
333
+ })]
334
+ }) });
335
+ return /* @__PURE__ */ jsxs("div", {
336
+ className: cn("flex items-center gap-3 py-2", className),
337
+ children: [
338
+ showSearch && /* @__PURE__ */ jsxs("div", {
339
+ className: "relative flex-1 max-w-sm",
340
+ children: [
341
+ /* @__PURE__ */ jsx(Search, { className: "absolute left-2.5 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }),
342
+ /* @__PURE__ */ jsx(Input, {
343
+ placeholder: searchPlaceholder,
344
+ defaultValue: search.searchValue,
345
+ onChange: handleSearchInput,
346
+ className: "pl-8 h-9"
347
+ }),
348
+ search.hasActiveSearch && /* @__PURE__ */ jsx("button", {
349
+ type: "button",
350
+ onClick: search.clearSearch,
351
+ className: "absolute right-2 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground",
352
+ "aria-label": "Clear search",
353
+ children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" })
354
+ })
355
+ ]
356
+ }),
357
+ filterElement,
358
+ showResults && totalResults != null && /* @__PURE__ */ jsxs("span", {
359
+ className: "text-sm text-muted-foreground shrink-0",
360
+ children: [
361
+ totalResults,
362
+ " result",
363
+ totalResults !== 1 ? "s" : ""
364
+ ]
365
+ }),
366
+ /* @__PURE__ */ jsx("div", { className: "flex-1" }),
367
+ children
368
+ ]
369
+ });
370
+ }
371
+
372
+ //#endregion
373
+ export { DataTable, DataTableToolbar };
@@ -0,0 +1,6 @@
1
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
2
+
3
+ //#region src/components/mode-toggle.d.ts
4
+ declare function ModeToggle(): react_jsx_runtime0.JSX.Element;
5
+ //#endregion
6
+ export { ModeToggle };
@@ -0,0 +1,65 @@
1
+ "use client";
2
+
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ import * as React$1 from "react";
5
+ import { Moon, Sun } from "lucide-react";
6
+ import { Button } from "@/components/ui/button";
7
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
8
+ import { useTheme } from "next-themes";
9
+
10
+ //#region src/components/mode-toggle.tsx
11
+ function ModeToggle() {
12
+ const { theme, setTheme } = useTheme();
13
+ const [mounted, setMounted] = React$1.useState(false);
14
+ React$1.useEffect(() => {
15
+ setMounted(true);
16
+ }, []);
17
+ if (!mounted) return /* @__PURE__ */ jsxs(Button, {
18
+ variant: "ghost",
19
+ size: "icon",
20
+ children: [/* @__PURE__ */ jsx(Sun, { className: "h-[1.2rem] w-[1.2rem]" }), /* @__PURE__ */ jsx("span", {
21
+ className: "sr-only",
22
+ children: "Toggle theme"
23
+ })]
24
+ });
25
+ return /* @__PURE__ */ jsxs(DropdownMenu, {
26
+ modal: false,
27
+ children: [/* @__PURE__ */ jsxs(DropdownMenuTrigger, {
28
+ render: /* @__PURE__ */ jsx(Button, {
29
+ variant: "ghost",
30
+ size: "icon"
31
+ }),
32
+ children: [
33
+ /* @__PURE__ */ jsx(Sun, { className: "h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" }),
34
+ /* @__PURE__ */ jsx(Moon, { className: "absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" }),
35
+ /* @__PURE__ */ jsx("span", {
36
+ className: "sr-only",
37
+ children: "Toggle theme"
38
+ })
39
+ ]
40
+ }), /* @__PURE__ */ jsx(DropdownMenuContent, {
41
+ align: "end",
42
+ children: /* @__PURE__ */ jsxs(DropdownMenuRadioGroup, {
43
+ value: theme,
44
+ onValueChange: setTheme,
45
+ children: [
46
+ /* @__PURE__ */ jsx(DropdownMenuRadioItem, {
47
+ value: "light",
48
+ children: "Light"
49
+ }),
50
+ /* @__PURE__ */ jsx(DropdownMenuRadioItem, {
51
+ value: "dark",
52
+ children: "Dark"
53
+ }),
54
+ /* @__PURE__ */ jsx(DropdownMenuRadioItem, {
55
+ value: "system",
56
+ children: "System"
57
+ })
58
+ ]
59
+ })
60
+ })]
61
+ });
62
+ }
63
+
64
+ //#endregion
65
+ export { ModeToggle };
@@ -0,0 +1,134 @@
1
+ import { n as useKeyboardShortcut } from "./use-keyboard-shortcut-_mRCh3QO.mjs";
2
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
3
+ import { ReactNode } from "react";
4
+
5
+ //#region src/components/command/command-search.d.ts
6
+ interface CommandSearchContextValue {
7
+ open: boolean;
8
+ setOpen: (open: boolean) => void;
9
+ }
10
+ /**
11
+ * useCommandSearch — Access command search open/close state.
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * const { open, setOpen } = useCommandSearch();
16
+ * ```
17
+ */
18
+ declare function useCommandSearch(): CommandSearchContextValue;
19
+ interface CommandSearchProps {
20
+ children: ReactNode;
21
+ /** Controlled open state */
22
+ open?: boolean;
23
+ /** Open state change handler */
24
+ onOpenChange?: (open: boolean) => void;
25
+ /** Keyboard shortcut to toggle (default: "mod+k") */
26
+ shortcut?: string;
27
+ /** Whether to register the keyboard shortcut (default: true) */
28
+ enableShortcut?: boolean;
29
+ /** Dialog title for accessibility (default: "Command Palette") */
30
+ title?: string;
31
+ /** Dialog description for accessibility */
32
+ description?: string;
33
+ className?: string;
34
+ }
35
+ interface CommandSearchInputProps {
36
+ placeholder?: string;
37
+ className?: string;
38
+ }
39
+ interface CommandSearchGroupProps {
40
+ heading?: string;
41
+ children: ReactNode;
42
+ className?: string;
43
+ }
44
+ interface CommandSearchItemProps {
45
+ children: ReactNode;
46
+ /** Icon element rendered before the label */
47
+ icon?: ReactNode;
48
+ /** Keyboard shortcut displayed via Kbd component */
49
+ shortcut?: string;
50
+ /** Called when the item is selected */
51
+ onSelect?: (value: string) => void;
52
+ /** Value for filtering */
53
+ value?: string;
54
+ disabled?: boolean;
55
+ className?: string;
56
+ }
57
+ interface CommandSearchEmptyProps {
58
+ children?: ReactNode;
59
+ className?: string;
60
+ }
61
+ interface CommandSearchListProps {
62
+ children: ReactNode;
63
+ className?: string;
64
+ }
65
+ /**
66
+ * CommandSearch — Command palette dialog with keyboard shortcut.
67
+ * Wraps consumer's @/components/ui/command (Base UI shadcn).
68
+ *
69
+ * @example
70
+ * ```tsx
71
+ * <CommandSearch shortcut="mod+k">
72
+ * <CommandSearch.Input placeholder="Type a command or search..." />
73
+ * <CommandSearch.List>
74
+ * <CommandSearch.Empty>No results found.</CommandSearch.Empty>
75
+ * <CommandSearch.Group heading="Navigation">
76
+ * <CommandSearch.Item icon={<Home />} shortcut="⌘H" onSelect={() => navigate("/")}>
77
+ * Home
78
+ * </CommandSearch.Item>
79
+ * </CommandSearch.Group>
80
+ * </CommandSearch.List>
81
+ * </CommandSearch>
82
+ * ```
83
+ */
84
+ declare function CommandSearchRoot({
85
+ children,
86
+ open: controlledOpen,
87
+ onOpenChange: controlledOnOpenChange,
88
+ shortcut,
89
+ enableShortcut,
90
+ title,
91
+ description,
92
+ className
93
+ }: CommandSearchProps): react_jsx_runtime0.JSX.Element;
94
+ declare function CommandSearchInput({
95
+ placeholder,
96
+ className
97
+ }: CommandSearchInputProps): react_jsx_runtime0.JSX.Element;
98
+ declare function CommandSearchList({
99
+ children,
100
+ className
101
+ }: CommandSearchListProps): react_jsx_runtime0.JSX.Element;
102
+ declare function CommandSearchGroup({
103
+ heading,
104
+ children,
105
+ className
106
+ }: CommandSearchGroupProps): react_jsx_runtime0.JSX.Element;
107
+ declare function CommandSearchItemComponent({
108
+ children,
109
+ icon,
110
+ shortcut: shortcutDisplay,
111
+ onSelect,
112
+ value,
113
+ disabled,
114
+ className
115
+ }: CommandSearchItemProps): react_jsx_runtime0.JSX.Element;
116
+ declare function CommandSearchEmpty({
117
+ children,
118
+ className
119
+ }: CommandSearchEmptyProps): react_jsx_runtime0.JSX.Element;
120
+ declare function CommandSearchSeparator({
121
+ className
122
+ }: {
123
+ className?: string;
124
+ }): react_jsx_runtime0.JSX.Element;
125
+ declare const CommandSearch: typeof CommandSearchRoot & {
126
+ Input: typeof CommandSearchInput;
127
+ List: typeof CommandSearchList;
128
+ Group: typeof CommandSearchGroup;
129
+ Item: typeof CommandSearchItemComponent;
130
+ Empty: typeof CommandSearchEmpty;
131
+ Separator: typeof CommandSearchSeparator;
132
+ };
133
+ //#endregion
134
+ export { CommandSearch, type CommandSearchEmptyProps, type CommandSearchGroupProps, type CommandSearchInputProps, type CommandSearchItemProps, type CommandSearchListProps, type CommandSearchProps, useCommandSearch, useKeyboardShortcut };