@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.
- package/README.md +21 -1
- package/dist/client/calendar.d.mts +1 -2
- package/dist/client/calendar.mjs +4 -4
- package/dist/client/color-picker.d.mts +94 -0
- package/dist/client/color-picker.mjs +392 -0
- package/dist/client/core.d.mts +243 -557
- package/dist/client/core.mjs +351 -1462
- package/dist/client/error.d.mts +41 -41
- package/dist/client/error.mjs +35 -35
- package/dist/client/gallery.d.mts +175 -0
- package/dist/client/gallery.mjs +546 -0
- package/dist/client/hooks.d.mts +57 -39
- package/dist/client/hooks.mjs +29 -7
- package/dist/client/spreadsheet.d.mts +30 -27
- package/dist/client/spreadsheet.mjs +80 -80
- package/dist/client/table.d.mts +66 -33
- package/dist/client/table.mjs +87 -54
- package/dist/client/theme.mjs +1 -1
- package/dist/command.d.mts +6 -4
- package/dist/command.mjs +3 -3
- package/dist/compact.d.mts +97 -95
- package/dist/compact.mjs +336 -322
- package/dist/dashboard.d.mts +614 -422
- package/dist/dashboard.mjs +1051 -762
- package/dist/{dropdown-wrapper-B86u9Fri.mjs → dropdown-wrapper-B9nRDUlz.mjs} +25 -35
- package/dist/forms.d.mts +1037 -972
- package/dist/forms.mjs +2849 -2721
- package/dist/index.d.mts +218 -152
- package/dist/index.mjs +357 -264
- package/dist/layouts.d.mts +94 -94
- package/dist/layouts.mjs +115 -110
- package/dist/phone-input-B9_XPNvv.mjs +429 -0
- package/dist/phone-input-CLH_UjQZ.d.mts +31 -0
- package/dist/{search-context-DR7DBs7S.mjs → search-context-1g3ZmOvx.mjs} +1 -1
- package/dist/search.d.mts +168 -164
- package/dist/search.mjs +305 -301
- package/dist/{sheet-wrapper-C13Y-Q6w.mjs → sheet-wrapper-B2uxookb.mjs} +1 -1
- package/dist/timeline-Bgu1mIe9.d.mts +373 -0
- package/dist/timeline-HJtWf4Op.mjs +804 -0
- package/dist/{use-base-search-BGgWnWaF.d.mts → use-base-search-DFC4QKYU.d.mts} +1 -1
- package/dist/{use-media-query-BnVNIKT4.mjs → use-media-query-ChLfFChU.mjs} +6 -7
- package/package.json +10 -2
- /package/dist/{api-pagination-CJ0vR_w6.d.mts → api-pagination-C30ser2L.d.mts} +0 -0
- /package/dist/{filter-utils-DqMmy_v-.mjs → filter-utils-BGIvtq1R.mjs} +0 -0
- /package/dist/{filter-utils-IZ0GtuPo.d.mts → filter-utils-DOFTBWm1.d.mts} +0 -0
- /package/dist/{use-debounce-xmZucz5e.mjs → use-debounce-BNoNiEon.mjs} +0 -0
- /package/dist/{use-keyboard-shortcut-Bl6YM5Q7.mjs → use-keyboard-shortcut-C_Vk-36P.mjs} +0 -0
- /package/dist/{use-keyboard-shortcut-_mRCh3QO.d.mts → use-keyboard-shortcut-Q4CSPzSI.d.mts} +0 -0
- /package/dist/{use-mobile-BX3SQVo2.mjs → use-mobile-CnEmFiQx.mjs} +0 -0
- /package/dist/{use-scroll-detection-CsgsQYvy.mjs → use-scroll-detection-BKfqkmEC.mjs} +0 -0
- /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 };
|
package/dist/client/hooks.d.mts
CHANGED
|
@@ -1,49 +1,24 @@
|
|
|
1
|
-
import { n as UseBaseSearchReturn, r as useBaseSearch, t as UseBaseSearchConfig } from "../use-base-search-
|
|
2
|
-
import { n as useKeyboardShortcut, t as UseKeyboardShortcutOptions } from "../use-keyboard-shortcut-
|
|
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/
|
|
6
|
-
|
|
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
|
-
*
|
|
8
|
+
* Factory that pre-configures useBaseSearch with typed defaults.
|
|
36
9
|
*
|
|
37
10
|
* @example
|
|
38
|
-
* ```
|
|
39
|
-
* const
|
|
40
|
-
*
|
|
41
|
-
* },
|
|
11
|
+
* ```ts
|
|
12
|
+
* const useOrderSearch = createSearchHook({
|
|
13
|
+
* basePath: "/dashboard/orders",
|
|
14
|
+
* searchFields: { name: "Name", sku: "SKU", barcode: "Barcode" },
|
|
15
|
+
* });
|
|
42
16
|
*
|
|
43
|
-
*
|
|
17
|
+
* // In component:
|
|
18
|
+
* const search = useOrderSearch();
|
|
44
19
|
* ```
|
|
45
20
|
*/
|
|
46
|
-
declare function
|
|
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 };
|