@classytic/fluid 0.4.1 → 0.5.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 (51) hide show
  1. package/README.md +21 -1
  2. package/dist/client/calendar.d.mts +1 -2
  3. package/dist/client/calendar.mjs +4 -4
  4. package/dist/client/color-picker.d.mts +94 -0
  5. package/dist/client/color-picker.mjs +392 -0
  6. package/dist/client/core.d.mts +243 -557
  7. package/dist/client/core.mjs +351 -1462
  8. package/dist/client/error.d.mts +41 -41
  9. package/dist/client/error.mjs +35 -35
  10. package/dist/client/gallery.d.mts +175 -0
  11. package/dist/client/gallery.mjs +546 -0
  12. package/dist/client/hooks.d.mts +57 -39
  13. package/dist/client/hooks.mjs +29 -7
  14. package/dist/client/spreadsheet.d.mts +30 -27
  15. package/dist/client/spreadsheet.mjs +80 -80
  16. package/dist/client/table.d.mts +66 -33
  17. package/dist/client/table.mjs +87 -54
  18. package/dist/client/theme.mjs +1 -1
  19. package/dist/command.d.mts +6 -4
  20. package/dist/command.mjs +3 -3
  21. package/dist/compact.d.mts +97 -95
  22. package/dist/compact.mjs +336 -322
  23. package/dist/dashboard.d.mts +614 -422
  24. package/dist/dashboard.mjs +1051 -762
  25. package/dist/{dropdown-wrapper-B86u9Fri.mjs → dropdown-wrapper-B9nRDUlz.mjs} +25 -35
  26. package/dist/forms.d.mts +1037 -972
  27. package/dist/forms.mjs +2849 -2721
  28. package/dist/index.d.mts +218 -152
  29. package/dist/index.mjs +357 -264
  30. package/dist/layouts.d.mts +94 -94
  31. package/dist/layouts.mjs +115 -110
  32. package/dist/phone-input-B9_XPNvv.mjs +429 -0
  33. package/dist/phone-input-CLH_UjQZ.d.mts +31 -0
  34. package/dist/{search-context-DR7DBs7S.mjs → search-context-1g3ZmOvx.mjs} +1 -1
  35. package/dist/search.d.mts +168 -164
  36. package/dist/search.mjs +305 -301
  37. package/dist/{sheet-wrapper-C13Y-Q6w.mjs → sheet-wrapper-B2uxookb.mjs} +1 -1
  38. package/dist/timeline-Bgu1mIe9.d.mts +373 -0
  39. package/dist/timeline-HJtWf4Op.mjs +804 -0
  40. package/dist/{use-base-search-BGgWnWaF.d.mts → use-base-search-DFC4QKYU.d.mts} +1 -1
  41. package/dist/{use-media-query-BnVNIKT4.mjs → use-media-query-ChLfFChU.mjs} +6 -7
  42. package/package.json +10 -2
  43. /package/dist/{api-pagination-CJ0vR_w6.d.mts → api-pagination-C30ser2L.d.mts} +0 -0
  44. /package/dist/{filter-utils-DqMmy_v-.mjs → filter-utils-BGIvtq1R.mjs} +0 -0
  45. /package/dist/{filter-utils-IZ0GtuPo.d.mts → filter-utils-DOFTBWm1.d.mts} +0 -0
  46. /package/dist/{use-debounce-xmZucz5e.mjs → use-debounce-BNoNiEon.mjs} +0 -0
  47. /package/dist/{use-keyboard-shortcut-Bl6YM5Q7.mjs → use-keyboard-shortcut-C_Vk-36P.mjs} +0 -0
  48. /package/dist/{use-keyboard-shortcut-_mRCh3QO.d.mts → use-keyboard-shortcut-Q4CSPzSI.d.mts} +0 -0
  49. /package/dist/{use-mobile-BX3SQVo2.mjs → use-mobile-CnEmFiQx.mjs} +0 -0
  50. /package/dist/{use-scroll-detection-CsgsQYvy.mjs → use-scroll-detection-BKfqkmEC.mjs} +0 -0
  51. /package/dist/{utils-CDue7cEt.d.mts → utils-rqvYP1by.d.mts} +0 -0
@@ -0,0 +1,546 @@
1
+ "use client";
2
+
3
+ import { t as cn } from "../utils-DQ5SCVoW.mjs";
4
+ import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
5
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
6
+ import { ChevronLeft, ChevronRight, RotateCcw, X, ZoomIn, ZoomOut } from "lucide-react";
7
+ import Image from "next/image";
8
+
9
+ //#region src/components/gallery/gallery-context.tsx
10
+ const GalleryContext = createContext(null);
11
+ function useGallery() {
12
+ const context = useContext(GalleryContext);
13
+ if (!context) throw new Error("useGallery must be used within a GalleryProvider");
14
+ return context;
15
+ }
16
+ function useGalleryOptional() {
17
+ return useContext(GalleryContext);
18
+ }
19
+ function GalleryProvider({ children, images, defaultIndex = 0, classNames, title, onIndexChange }) {
20
+ const [selectedIndex, setSelectedIndexState] = useState(defaultIndex);
21
+ const [lightboxOpen, setLightboxOpen] = useState(false);
22
+ const setSelectedIndex = useCallback((index) => {
23
+ setSelectedIndexState(index);
24
+ onIndexChange?.(index);
25
+ }, [onIndexChange]);
26
+ const goToNext = useCallback(() => {
27
+ setSelectedIndex(selectedIndex === images.length - 1 ? 0 : selectedIndex + 1);
28
+ }, [
29
+ selectedIndex,
30
+ images.length,
31
+ setSelectedIndex
32
+ ]);
33
+ const goToPrevious = useCallback(() => {
34
+ setSelectedIndex(selectedIndex === 0 ? images.length - 1 : selectedIndex - 1);
35
+ }, [
36
+ selectedIndex,
37
+ images.length,
38
+ setSelectedIndex
39
+ ]);
40
+ const value = useMemo(() => ({
41
+ images,
42
+ selectedIndex,
43
+ setSelectedIndex,
44
+ lightboxOpen,
45
+ setLightboxOpen,
46
+ goToNext,
47
+ goToPrevious,
48
+ classNames,
49
+ title
50
+ }), [
51
+ images,
52
+ selectedIndex,
53
+ setSelectedIndex,
54
+ lightboxOpen,
55
+ goToNext,
56
+ goToPrevious,
57
+ classNames,
58
+ title
59
+ ]);
60
+ return /* @__PURE__ */ jsx(GalleryContext.Provider, {
61
+ value,
62
+ children
63
+ });
64
+ }
65
+
66
+ //#endregion
67
+ //#region src/components/gallery/gallery-dots.tsx
68
+ function GalleryDots({ showOnDesktop = false, className }) {
69
+ const { images, selectedIndex, setSelectedIndex, classNames } = useGallery();
70
+ if (images.length <= 1) return null;
71
+ return /* @__PURE__ */ jsx("div", {
72
+ className: cn("flex justify-center gap-2 mt-3", !showOnDesktop && "sm:hidden", classNames?.dots, className),
73
+ children: images.map((_, index) => /* @__PURE__ */ jsx("button", {
74
+ type: "button",
75
+ onClick: () => setSelectedIndex(index),
76
+ className: cn("h-2 rounded-full transition-all duration-300", selectedIndex === index ? cn("bg-foreground w-6", classNames?.dotActive) : cn("bg-muted-foreground/30 hover:bg-muted-foreground/50 w-2", classNames?.dot)),
77
+ "aria-label": `Go to image ${index + 1}`,
78
+ "aria-current": selectedIndex === index ? "true" : void 0
79
+ }, index))
80
+ });
81
+ }
82
+
83
+ //#endregion
84
+ //#region src/components/gallery/gallery-lightbox.tsx
85
+ const MIN_ZOOM = 1;
86
+ const MAX_ZOOM = 4;
87
+ const ZOOM_STEP = .5;
88
+ function GalleryLightbox({ className }) {
89
+ const { images, selectedIndex, setSelectedIndex, lightboxOpen, setLightboxOpen, goToNext, goToPrevious, classNames, title } = useGallery();
90
+ const [zoom, setZoom] = useState(1);
91
+ const [position, setPosition] = useState({
92
+ x: 0,
93
+ y: 0
94
+ });
95
+ const [isDragging, setIsDragging] = useState(false);
96
+ const [dragStart, setDragStart] = useState({
97
+ x: 0,
98
+ y: 0
99
+ });
100
+ const [imageLoaded, setImageLoaded] = useState(false);
101
+ useEffect(() => {
102
+ setZoom(1);
103
+ setPosition({
104
+ x: 0,
105
+ y: 0
106
+ });
107
+ setImageLoaded(false);
108
+ }, [selectedIndex, lightboxOpen]);
109
+ useEffect(() => {
110
+ if (!lightboxOpen) return;
111
+ const handleKeyDown = (e) => {
112
+ switch (e.key) {
113
+ case "Escape":
114
+ setLightboxOpen(false);
115
+ break;
116
+ case "ArrowLeft":
117
+ goToPrevious();
118
+ break;
119
+ case "ArrowRight":
120
+ goToNext();
121
+ break;
122
+ case "+":
123
+ case "=":
124
+ setZoom((prev) => Math.min(prev + ZOOM_STEP, MAX_ZOOM));
125
+ break;
126
+ case "-":
127
+ setZoom((prev) => {
128
+ const newZoom = Math.max(prev - ZOOM_STEP, MIN_ZOOM);
129
+ if (newZoom === 1) setPosition({
130
+ x: 0,
131
+ y: 0
132
+ });
133
+ return newZoom;
134
+ });
135
+ break;
136
+ case "0":
137
+ setZoom(1);
138
+ setPosition({
139
+ x: 0,
140
+ y: 0
141
+ });
142
+ break;
143
+ }
144
+ };
145
+ document.addEventListener("keydown", handleKeyDown);
146
+ return () => document.removeEventListener("keydown", handleKeyDown);
147
+ }, [
148
+ lightboxOpen,
149
+ goToNext,
150
+ goToPrevious,
151
+ setLightboxOpen
152
+ ]);
153
+ useEffect(() => {
154
+ if (lightboxOpen) document.body.style.overflow = "hidden";
155
+ else document.body.style.overflow = "";
156
+ return () => {
157
+ document.body.style.overflow = "";
158
+ };
159
+ }, [lightboxOpen]);
160
+ const handleWheel = useCallback((e) => {
161
+ e.preventDefault();
162
+ const delta = e.deltaY > 0 ? -ZOOM_STEP : ZOOM_STEP;
163
+ setZoom((prev) => {
164
+ const newZoom = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, prev + delta));
165
+ if (newZoom === 1) setPosition({
166
+ x: 0,
167
+ y: 0
168
+ });
169
+ return newZoom;
170
+ });
171
+ }, []);
172
+ const handleMouseDown = (e) => {
173
+ if (zoom > 1) {
174
+ setIsDragging(true);
175
+ setDragStart({
176
+ x: e.clientX - position.x,
177
+ y: e.clientY - position.y
178
+ });
179
+ }
180
+ };
181
+ const handleMouseMove = (e) => {
182
+ if (isDragging && zoom > 1) setPosition({
183
+ x: e.clientX - dragStart.x,
184
+ y: e.clientY - dragStart.y
185
+ });
186
+ };
187
+ const handleMouseUp = () => setIsDragging(false);
188
+ const handleTouchStart = (e) => {
189
+ if (zoom > 1 && e.touches.length === 1) {
190
+ setIsDragging(true);
191
+ setDragStart({
192
+ x: e.touches[0].clientX - position.x,
193
+ y: e.touches[0].clientY - position.y
194
+ });
195
+ }
196
+ };
197
+ const handleTouchMove = (e) => {
198
+ if (isDragging && zoom > 1 && e.touches.length === 1) setPosition({
199
+ x: e.touches[0].clientX - dragStart.x,
200
+ y: e.touches[0].clientY - dragStart.y
201
+ });
202
+ };
203
+ const handleDoubleClick = () => {
204
+ if (zoom > 1) {
205
+ setZoom(1);
206
+ setPosition({
207
+ x: 0,
208
+ y: 0
209
+ });
210
+ } else setZoom(2);
211
+ };
212
+ if (!lightboxOpen) return null;
213
+ const currentImage = images[selectedIndex];
214
+ return /* @__PURE__ */ jsxs("div", {
215
+ className: cn("fixed inset-0 z-50 bg-black", classNames?.lightbox, className),
216
+ role: "dialog",
217
+ "aria-modal": "true",
218
+ "aria-label": `${title || "Gallery"} - Image ${selectedIndex + 1} of ${images.length}`,
219
+ children: [
220
+ /* @__PURE__ */ jsxs("div", {
221
+ className: cn("absolute top-0 left-0 right-0 z-50", "flex items-center justify-between p-4", "bg-gradient-to-b from-black/60 to-transparent", classNames?.lightboxHeader),
222
+ children: [
223
+ /* @__PURE__ */ jsxs("span", {
224
+ className: "text-white text-sm font-medium",
225
+ children: [
226
+ selectedIndex + 1,
227
+ " / ",
228
+ images.length
229
+ ]
230
+ }),
231
+ /* @__PURE__ */ jsxs("div", {
232
+ className: "flex items-center gap-2",
233
+ children: [
234
+ /* @__PURE__ */ jsx("button", {
235
+ type: "button",
236
+ onClick: () => setZoom((prev) => {
237
+ const newZoom = Math.max(prev - ZOOM_STEP, MIN_ZOOM);
238
+ if (newZoom === 1) setPosition({
239
+ x: 0,
240
+ y: 0
241
+ });
242
+ return newZoom;
243
+ }),
244
+ disabled: zoom <= MIN_ZOOM,
245
+ className: "p-2 rounded-full bg-white/10 hover:bg-white/20 transition-colors disabled:opacity-30",
246
+ "aria-label": "Zoom out",
247
+ children: /* @__PURE__ */ jsx(ZoomOut, { className: "h-5 w-5 text-white" })
248
+ }),
249
+ /* @__PURE__ */ jsxs("span", {
250
+ className: "text-white text-sm min-w-[3rem] text-center",
251
+ children: [Math.round(zoom * 100), "%"]
252
+ }),
253
+ /* @__PURE__ */ jsx("button", {
254
+ type: "button",
255
+ onClick: () => setZoom((prev) => Math.min(prev + ZOOM_STEP, MAX_ZOOM)),
256
+ disabled: zoom >= MAX_ZOOM,
257
+ className: "p-2 rounded-full bg-white/10 hover:bg-white/20 transition-colors disabled:opacity-30",
258
+ "aria-label": "Zoom in",
259
+ children: /* @__PURE__ */ jsx(ZoomIn, { className: "h-5 w-5 text-white" })
260
+ }),
261
+ /* @__PURE__ */ jsx("button", {
262
+ type: "button",
263
+ onClick: () => {
264
+ setZoom(1);
265
+ setPosition({
266
+ x: 0,
267
+ y: 0
268
+ });
269
+ },
270
+ disabled: zoom === 1,
271
+ className: "p-2 rounded-full bg-white/10 hover:bg-white/20 transition-colors disabled:opacity-30",
272
+ "aria-label": "Reset zoom",
273
+ children: /* @__PURE__ */ jsx(RotateCcw, { className: "h-5 w-5 text-white" })
274
+ })
275
+ ]
276
+ }),
277
+ /* @__PURE__ */ jsx("button", {
278
+ type: "button",
279
+ onClick: () => setLightboxOpen(false),
280
+ className: "p-2 rounded-full bg-white/10 hover:bg-white/20 transition-colors",
281
+ "aria-label": "Close",
282
+ children: /* @__PURE__ */ jsx(X, { className: "h-6 w-6 text-white" })
283
+ })
284
+ ]
285
+ }),
286
+ /* @__PURE__ */ jsxs("div", {
287
+ className: cn("absolute inset-0 flex items-center justify-center", zoom > 1 ? "cursor-grab" : "cursor-zoom-in", isDragging && "cursor-grabbing"),
288
+ onWheel: handleWheel,
289
+ onMouseDown: handleMouseDown,
290
+ onMouseMove: handleMouseMove,
291
+ onMouseUp: handleMouseUp,
292
+ onMouseLeave: handleMouseUp,
293
+ onTouchStart: handleTouchStart,
294
+ onTouchMove: handleTouchMove,
295
+ onTouchEnd: handleMouseUp,
296
+ onDoubleClick: handleDoubleClick,
297
+ children: [!imageLoaded && /* @__PURE__ */ jsx("div", {
298
+ className: "absolute inset-0 flex items-center justify-center",
299
+ children: /* @__PURE__ */ jsx("div", { className: "w-12 h-12 border-4 border-white/20 border-t-white rounded-full animate-spin" })
300
+ }), /* @__PURE__ */ jsx("div", {
301
+ className: "relative w-full h-full transition-transform duration-100",
302
+ style: { transform: `scale(${zoom}) translate(${position.x / zoom}px, ${position.y / zoom}px)` },
303
+ children: /* @__PURE__ */ jsx(Image, {
304
+ src: currentImage.src,
305
+ alt: currentImage.alt || `${title || "Gallery"} - Image ${selectedIndex + 1}`,
306
+ fill: true,
307
+ className: cn("object-contain transition-opacity duration-300", imageLoaded ? "opacity-100" : "opacity-0", classNames?.lightboxImage),
308
+ sizes: "100vw",
309
+ quality: 100,
310
+ priority: true,
311
+ unoptimized: true,
312
+ onLoad: () => setImageLoaded(true)
313
+ })
314
+ })]
315
+ }),
316
+ images.length > 1 && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("button", {
317
+ type: "button",
318
+ onClick: goToPrevious,
319
+ className: "absolute left-4 top-1/2 -translate-y-1/2 z-50 p-3 rounded-full bg-white/10 hover:bg-white/20 transition-colors",
320
+ "aria-label": "Previous",
321
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-8 w-8 text-white" })
322
+ }), /* @__PURE__ */ jsx("button", {
323
+ type: "button",
324
+ onClick: goToNext,
325
+ className: "absolute right-4 top-1/2 -translate-y-1/2 z-50 p-3 rounded-full bg-white/10 hover:bg-white/20 transition-colors",
326
+ "aria-label": "Next",
327
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-8 w-8 text-white" })
328
+ })] }),
329
+ images.length > 1 && /* @__PURE__ */ jsxs("div", {
330
+ className: cn("absolute bottom-0 left-0 right-0 z-50 p-4", "bg-gradient-to-t from-black/60 to-transparent", classNames?.lightboxFooter),
331
+ children: [/* @__PURE__ */ jsx("div", {
332
+ className: "flex justify-center gap-2 overflow-x-auto max-w-full pb-2",
333
+ children: images.map((img, index) => /* @__PURE__ */ jsx("button", {
334
+ type: "button",
335
+ onClick: () => setSelectedIndex(index),
336
+ className: cn("w-16 h-16 md:w-20 md:h-20 overflow-hidden border-2 transition-all shrink-0 rounded-md relative", selectedIndex === index ? "border-white opacity-100" : "border-transparent opacity-50 hover:opacity-80"),
337
+ "aria-label": `View image ${index + 1}`,
338
+ children: /* @__PURE__ */ jsx(Image, {
339
+ src: img.thumbnail || img.src,
340
+ alt: img.alt || `Thumbnail ${index + 1}`,
341
+ fill: true,
342
+ className: "object-cover",
343
+ sizes: "80px"
344
+ })
345
+ }, index))
346
+ }), /* @__PURE__ */ jsx("p", {
347
+ className: "text-white/50 text-xs text-center mt-2 hidden md:block",
348
+ children: "Scroll to zoom · Double-click to toggle · Drag to pan · Arrow keys to navigate"
349
+ })]
350
+ })
351
+ ]
352
+ });
353
+ }
354
+
355
+ //#endregion
356
+ //#region src/components/gallery/gallery-main.tsx
357
+ function GalleryMain({ children, badges, showNav = true, aspectRatio = "aspect-[4/5]", className }) {
358
+ const { images, selectedIndex, setLightboxOpen, goToNext, goToPrevious, classNames, title } = useGallery();
359
+ const [imagesLoaded, setImagesLoaded] = useState(new Set([0]));
360
+ const [touchStart, setTouchStart] = useState(null);
361
+ const [touchEnd, setTouchEnd] = useState(null);
362
+ const sliderRef = useRef(null);
363
+ const minSwipeDistance = 50;
364
+ useEffect(() => {
365
+ images.forEach((img, index) => {
366
+ if (index === 0) return;
367
+ const preloadImg = new window.Image();
368
+ preloadImg.src = img.src;
369
+ preloadImg.onload = () => {
370
+ setImagesLoaded((prev) => new Set([...prev, index]));
371
+ };
372
+ });
373
+ }, [images]);
374
+ const onTouchStart = useCallback((e) => {
375
+ setTouchEnd(null);
376
+ setTouchStart(e.targetTouches[0].clientX);
377
+ }, []);
378
+ const onTouchMove = useCallback((e) => {
379
+ setTouchEnd(e.targetTouches[0].clientX);
380
+ }, []);
381
+ const onTouchEnd = useCallback(() => {
382
+ if (!touchStart || !touchEnd) return;
383
+ const distance = touchStart - touchEnd;
384
+ if (distance > minSwipeDistance) goToNext();
385
+ if (distance < -minSwipeDistance) goToPrevious();
386
+ }, [
387
+ touchStart,
388
+ touchEnd,
389
+ goToNext,
390
+ goToPrevious
391
+ ]);
392
+ const handleImageLoad = useCallback((index) => {
393
+ setImagesLoaded((prev) => new Set([...prev, index]));
394
+ }, []);
395
+ return /* @__PURE__ */ jsx("div", {
396
+ className: cn("relative", className),
397
+ children: /* @__PURE__ */ jsxs("div", {
398
+ ref: sliderRef,
399
+ className: cn(aspectRatio, "bg-muted overflow-hidden relative rounded-lg", classNames?.main),
400
+ onTouchStart,
401
+ onTouchMove,
402
+ onTouchEnd,
403
+ children: [
404
+ /* @__PURE__ */ jsx("div", {
405
+ className: cn("absolute inset-0 flex transition-transform duration-300 ease-out", classNames?.slider),
406
+ style: { transform: `translateX(-${selectedIndex * 100}%)` },
407
+ children: images.map((img, index) => /* @__PURE__ */ jsxs("div", {
408
+ className: "w-full h-full shrink-0 relative cursor-zoom-in",
409
+ onClick: () => setLightboxOpen(true),
410
+ children: [/* @__PURE__ */ jsx(Image, {
411
+ src: img.src,
412
+ alt: img.alt || `${title || "Gallery"} - Image ${index + 1}`,
413
+ fill: true,
414
+ className: cn("object-cover transition-opacity duration-300", imagesLoaded.has(index) ? "opacity-100" : "opacity-0", classNames?.mainImage),
415
+ sizes: "(max-width: 768px) 100vw, 50vw",
416
+ priority: index === 0,
417
+ onLoad: () => handleImageLoad(index)
418
+ }), !imagesLoaded.has(index) && /* @__PURE__ */ jsx("div", {
419
+ className: "absolute inset-0 flex items-center justify-center bg-muted",
420
+ children: /* @__PURE__ */ jsx("div", { className: "w-8 h-8 border-2 border-muted-foreground/20 border-t-muted-foreground rounded-full animate-spin" })
421
+ })]
422
+ }, index))
423
+ }),
424
+ badges && badges.length > 0 && /* @__PURE__ */ jsx("div", {
425
+ className: cn("absolute top-4 left-4 flex flex-col gap-2 z-10", classNames?.badges),
426
+ children: badges.map((badge, index) => /* @__PURE__ */ jsx("span", {
427
+ className: cn("px-3 py-1 text-xs font-medium uppercase tracking-wider", classNames?.badge, badge.className),
428
+ children: badge.label
429
+ }, index))
430
+ }),
431
+ showNav && images.length > 1 && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("button", {
432
+ type: "button",
433
+ onClick: (e) => {
434
+ e.stopPropagation();
435
+ goToPrevious();
436
+ },
437
+ className: cn("hidden sm:flex absolute left-3 top-1/2 -translate-y-1/2 z-10", "w-10 h-10 items-center justify-center rounded-full", "bg-background/80 backdrop-blur-sm shadow-md hover:bg-background transition-colors", classNames?.nav, classNames?.navPrev),
438
+ "aria-label": "Previous image",
439
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-5 w-5" })
440
+ }), /* @__PURE__ */ jsx("button", {
441
+ type: "button",
442
+ onClick: (e) => {
443
+ e.stopPropagation();
444
+ goToNext();
445
+ },
446
+ className: cn("hidden sm:flex absolute right-3 top-1/2 -translate-y-1/2 z-10", "w-10 h-10 items-center justify-center rounded-full", "bg-background/80 backdrop-blur-sm shadow-md hover:bg-background transition-colors", classNames?.nav, classNames?.navNext),
447
+ "aria-label": "Next image",
448
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-5 w-5" })
449
+ })] }),
450
+ children
451
+ ]
452
+ })
453
+ });
454
+ }
455
+
456
+ //#endregion
457
+ //#region src/components/gallery/gallery-nav.tsx
458
+ function GalleryNav({ direction, className, children }) {
459
+ const { images, goToNext, goToPrevious, classNames } = useGallery();
460
+ if (images.length <= 1) return null;
461
+ const isPrev = direction === "prev";
462
+ return /* @__PURE__ */ jsx("button", {
463
+ type: "button",
464
+ onClick: isPrev ? goToPrevious : goToNext,
465
+ className: cn("flex items-center justify-center", "w-10 h-10 rounded-full", "bg-background/80 backdrop-blur-sm shadow-md hover:bg-background transition-colors", classNames?.nav, isPrev ? classNames?.navPrev : classNames?.navNext, className),
466
+ "aria-label": isPrev ? "Previous image" : "Next image",
467
+ children: children || (isPrev ? /* @__PURE__ */ jsx(ChevronLeft, { className: "h-5 w-5" }) : /* @__PURE__ */ jsx(ChevronRight, { className: "h-5 w-5" }))
468
+ });
469
+ }
470
+
471
+ //#endregion
472
+ //#region src/components/gallery/gallery-thumbnails.tsx
473
+ function GalleryThumbnails({ showOnMobile = true, orientation = "horizontal", sizeClassName = "w-16 h-20 sm:w-20 sm:h-24", className }) {
474
+ const { images, selectedIndex, setSelectedIndex, classNames, title } = useGallery();
475
+ if (images.length <= 1) return null;
476
+ return /* @__PURE__ */ jsx("div", {
477
+ className: cn("flex gap-2 sm:gap-3 scrollbar-hide", orientation === "vertical" ? "flex-col overflow-y-auto pr-2" : "overflow-x-auto pb-2", !showOnMobile && "hidden sm:flex", classNames?.thumbnails, className),
478
+ children: images.map((img, index) => /* @__PURE__ */ jsx("button", {
479
+ type: "button",
480
+ onClick: () => setSelectedIndex(index),
481
+ className: cn("bg-muted overflow-hidden border-2 transition-all duration-200 shrink-0 rounded-md relative", sizeClassName, selectedIndex === index ? cn("border-foreground", classNames?.thumbnailActive) : cn("border-transparent opacity-60 hover:opacity-100", classNames?.thumbnail)),
482
+ "aria-label": `View image ${index + 1}`,
483
+ children: /* @__PURE__ */ jsx(Image, {
484
+ src: img.thumbnail || img.src,
485
+ alt: img.alt || `${title || "Gallery"} thumbnail ${index + 1}`,
486
+ fill: true,
487
+ className: "object-cover",
488
+ sizes: "80px"
489
+ })
490
+ }, index))
491
+ });
492
+ }
493
+
494
+ //#endregion
495
+ //#region src/components/gallery/index.tsx
496
+ function ImageGalleryRoot({ children, images, defaultIndex = 0, classNames, title, onIndexChange, className }) {
497
+ return /* @__PURE__ */ jsx(GalleryProvider, {
498
+ images,
499
+ defaultIndex,
500
+ classNames,
501
+ title,
502
+ onIndexChange,
503
+ children: /* @__PURE__ */ jsx("div", {
504
+ className: cn("space-y-4", classNames?.root, className),
505
+ children
506
+ })
507
+ });
508
+ }
509
+ function ImageGallerySimple({ images, title, badges, defaultIndex = 0, showMobileThumbnails = true, aspectRatio = "aspect-[4/5]", showNav = true, classNames, onIndexChange, className }) {
510
+ if (!images || images.length === 0) return /* @__PURE__ */ jsx("div", {
511
+ className: cn(aspectRatio, "bg-muted flex items-center justify-center rounded-lg", className),
512
+ children: /* @__PURE__ */ jsx("span", {
513
+ className: "text-muted-foreground",
514
+ children: "No image available"
515
+ })
516
+ });
517
+ return /* @__PURE__ */ jsxs(ImageGalleryRoot, {
518
+ images,
519
+ defaultIndex,
520
+ classNames,
521
+ title,
522
+ onIndexChange,
523
+ className,
524
+ children: [
525
+ /* @__PURE__ */ jsx(GalleryMain, {
526
+ badges,
527
+ showNav,
528
+ aspectRatio
529
+ }),
530
+ !showMobileThumbnails && /* @__PURE__ */ jsx(GalleryDots, {}),
531
+ /* @__PURE__ */ jsx(GalleryThumbnails, { showOnMobile: showMobileThumbnails }),
532
+ /* @__PURE__ */ jsx(GalleryLightbox, {})
533
+ ]
534
+ });
535
+ }
536
+ const ImageGallery = Object.assign(ImageGallerySimple, {
537
+ Root: ImageGalleryRoot,
538
+ Main: GalleryMain,
539
+ Thumbnails: GalleryThumbnails,
540
+ Dots: GalleryDots,
541
+ Nav: GalleryNav,
542
+ Lightbox: GalleryLightbox
543
+ });
544
+
545
+ //#endregion
546
+ export { ImageGallery, useGallery, useGalleryOptional };
@@ -1,49 +1,24 @@
1
- import { n as UseBaseSearchReturn, r as useBaseSearch, t as UseBaseSearchConfig } from "../use-base-search-BGgWnWaF.mjs";
2
- import { n as useKeyboardShortcut, t as UseKeyboardShortcutOptions } from "../use-keyboard-shortcut-_mRCh3QO.mjs";
1
+ import { n as UseBaseSearchReturn, r as useBaseSearch, t as UseBaseSearchConfig } from "../use-base-search-DFC4QKYU.mjs";
2
+ import { n as useKeyboardShortcut, t as UseKeyboardShortcutOptions } from "../use-keyboard-shortcut-Q4CSPzSI.mjs";
3
3
  import { RefObject } from "react";
4
4
 
5
- //#region src/hooks/use-mobile.d.ts
6
- declare function useIsMobile(): boolean;
7
- //#endregion
8
- //#region src/hooks/use-media-query.d.ts
9
- declare function useMediaQuery(query: string, defaultValue?: boolean): boolean;
10
- //#endregion
11
- //#region src/hooks/use-scroll-detection.d.ts
12
- declare const useScrollDetection: (ref: RefObject<HTMLDivElement | null>, delay?: number) => {
13
- checkScroll: () => void;
14
- canScrollLeft: boolean;
15
- canScrollRight: boolean;
16
- isScrollable: boolean;
17
- };
18
- //#endregion
19
- //#region src/hooks/use-debounce.d.ts
20
- /**
21
- * useDebounce — Returns a debounced version of the input value.
22
- *
23
- * @example
24
- * ```tsx
25
- * const [search, setSearch] = useState("");
26
- * const debouncedSearch = useDebounce(search, 300);
27
- *
28
- * useEffect(() => {
29
- * fetchResults(debouncedSearch);
30
- * }, [debouncedSearch]);
31
- * ```
32
- */
33
- declare function useDebounce<T>(value: T, delay?: number): T;
5
+ //#region src/hooks/create-search-hook.d.ts
6
+ interface CreateSearchHookConfig extends UseBaseSearchConfig {}
34
7
  /**
35
- * useDebouncedCallback Returns a debounced version of a callback function.
8
+ * Factory that pre-configures useBaseSearch with typed defaults.
36
9
  *
37
10
  * @example
38
- * ```tsx
39
- * const debouncedSave = useDebouncedCallback((value: string) => {
40
- * saveToApi(value);
41
- * }, 500);
11
+ * ```ts
12
+ * const useOrderSearch = createSearchHook({
13
+ * basePath: "/dashboard/orders",
14
+ * searchFields: { name: "Name", sku: "SKU", barcode: "Barcode" },
15
+ * });
42
16
  *
43
- * <Input onChange={(e) => debouncedSave(e.target.value)} />
17
+ * // In component:
18
+ * const search = useOrderSearch();
44
19
  * ```
45
20
  */
46
- declare function useDebouncedCallback<T extends (...args: any[]) => any>(callback: T, delay?: number): (...args: Parameters<T>) => void;
21
+ declare function createSearchHook(config: CreateSearchHookConfig): () => UseBaseSearchReturn;
47
22
  //#endregion
48
23
  //#region src/hooks/use-copy-to-clipboard.d.ts
49
24
  interface UseCopyToClipboardReturn {
@@ -72,6 +47,35 @@ interface UseCopyToClipboardReturn {
72
47
  */
73
48
  declare function useCopyToClipboard(resetDelay?: number): UseCopyToClipboardReturn;
74
49
  //#endregion
50
+ //#region src/hooks/use-debounce.d.ts
51
+ /**
52
+ * useDebounce — Returns a debounced version of the input value.
53
+ *
54
+ * @example
55
+ * ```tsx
56
+ * const [search, setSearch] = useState("");
57
+ * const debouncedSearch = useDebounce(search, 300);
58
+ *
59
+ * useEffect(() => {
60
+ * fetchResults(debouncedSearch);
61
+ * }, [debouncedSearch]);
62
+ * ```
63
+ */
64
+ declare function useDebounce<T>(value: T, delay?: number): T;
65
+ /**
66
+ * useDebouncedCallback — Returns a debounced version of a callback function.
67
+ *
68
+ * @example
69
+ * ```tsx
70
+ * const debouncedSave = useDebouncedCallback((value: string) => {
71
+ * saveToApi(value);
72
+ * }, 500);
73
+ *
74
+ * <Input onChange={(e) => debouncedSave(e.target.value)} />
75
+ * ```
76
+ */
77
+ declare function useDebouncedCallback<T extends (...args: any[]) => any>(callback: T, delay?: number): (...args: Parameters<T>) => void;
78
+ //#endregion
75
79
  //#region src/hooks/use-local-storage.d.ts
76
80
  /**
77
81
  * useLocalStorage — Persist state in localStorage with type safety and optional expiry.
@@ -92,6 +96,20 @@ declare function useCopyToClipboard(resetDelay?: number): UseCopyToClipboardRetu
92
96
  */
93
97
  declare function useLocalStorage<T>(key: string, initialValue: T, ttl?: number): [T, (value: T | ((prev: T) => T)) => void, () => void];
94
98
  //#endregion
99
+ //#region src/hooks/use-media-query.d.ts
100
+ declare function useMediaQuery(query: string, defaultValue?: boolean): boolean;
101
+ //#endregion
102
+ //#region src/hooks/use-mobile.d.ts
103
+ declare function useIsMobile(): boolean;
104
+ //#endregion
105
+ //#region src/hooks/use-scroll-detection.d.ts
106
+ declare const useScrollDetection: (ref: RefObject<HTMLDivElement | null>, delay?: number) => {
107
+ checkScroll: () => void;
108
+ canScrollLeft: boolean;
109
+ canScrollRight: boolean;
110
+ isScrollable: boolean;
111
+ };
112
+ //#endregion
95
113
  //#region src/lib/storage.d.ts
96
114
  /**
97
115
  * A utility module for handling localStorage operations with error handling and SSR safety
@@ -163,4 +181,4 @@ declare const storage: {
163
181
  };
164
182
  };
165
183
  //#endregion
166
- export { TTL, type UseBaseSearchConfig, type UseBaseSearchReturn, type UseCopyToClipboardReturn, type UseKeyboardShortcutOptions, clearStorage, generateUUID, getStorageItem, isStorageEmpty, removeStorageItem, setStorageItem, storage, useBaseSearch, useCopyToClipboard, useDebounce, useDebouncedCallback, useIsMobile, useKeyboardShortcut, useLocalStorage, useMediaQuery, useScrollDetection };
184
+ export { type CreateSearchHookConfig, TTL, type UseBaseSearchConfig, type UseBaseSearchReturn, type UseCopyToClipboardReturn, type UseKeyboardShortcutOptions, clearStorage, createSearchHook, generateUUID, getStorageItem, isStorageEmpty, removeStorageItem, setStorageItem, storage, useBaseSearch, useCopyToClipboard, useDebounce, useDebouncedCallback, useIsMobile, useKeyboardShortcut, useLocalStorage, useMediaQuery, useScrollDetection };