@almadar/ui 2.5.2 → 2.7.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.
@@ -1,14 +1,14 @@
1
1
  import { useTheme, useUISlots } from './chunk-DKQN5FVU.js';
2
- import { useTranslate, useQuerySingleton } from './chunk-GOZKH7QW.js';
2
+ import { useTranslate, useInfiniteScroll, useQuerySingleton, useLongPress, useSwipeGesture, useDragReorder, usePullToRefresh } from './chunk-WGJIL4YR.js';
3
3
  import { useEventBus } from './chunk-YXZM3WCF.js';
4
- import { cn, debugGroup, debug, debugGroupEnd, getNestedValue, isDebugEnabled } from './chunk-KKCVDUK7.js';
4
+ import { cn, debugGroup, debug, debugGroupEnd, getNestedValue, isDebugEnabled } from './chunk-RPYMP7ZC.js';
5
5
  import { isPortalSlot } from './chunk-K2D5D3WK.js';
6
6
  import { __publicField } from './chunk-PKBMQBKP.js';
7
7
  import * as LucideIcons from 'lucide-react';
8
- import { Loader2, ChevronDown, X, Check, Copy, AlertCircle, User, Sun, Moon, FileQuestion, Inbox, Search, Info, XCircle, CheckCircle, AlertTriangle, ChevronRight, Filter, Plus, ChevronLeft, HelpCircle, ChevronUp, MoreHorizontal, TrendingUp, TrendingDown, Minus, ArrowLeft, Calendar, Tag, Clock, CheckCircle2, DollarSign, FileText, Package } from 'lucide-react';
8
+ import { Loader2, ChevronDown, X, ArrowRight, TrendingDown, TrendingUp, Check, Copy, AlertCircle, User, Sun, Moon, FileQuestion, Inbox, Search, Info, XCircle, CheckCircle, AlertTriangle, ChevronRight, Filter, Plus, ChevronLeft, HelpCircle, ChevronUp, Minus, Star, FileWarning, Upload, MoreHorizontal, ArrowLeft, Calendar, Tag, Clock, CheckCircle2, DollarSign, FileText, Package } from 'lucide-react';
9
9
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
10
- import * as React50 from 'react';
11
- import React50__default, { useCallback, useRef, useState, useLayoutEffect, useEffect, lazy, createContext, useMemo, useId, useContext, Suspense } from 'react';
10
+ import * as React69 from 'react';
11
+ import React69__default, { useCallback, useState, useRef, useLayoutEffect, useEffect, lazy, createContext, useMemo, useId, useContext, Suspense } from 'react';
12
12
  import { evaluate, createMinimalContext } from '@almadar/evaluator';
13
13
  import { createPortal } from 'react-dom';
14
14
  import ReactMarkdown from 'react-markdown';
@@ -157,7 +157,7 @@ var iconSizeStyles = {
157
157
  md: "h-4 w-4",
158
158
  lg: "h-5 w-5"
159
159
  };
160
- var Button = React50__default.forwardRef(
160
+ var Button = React69__default.forwardRef(
161
161
  ({
162
162
  className,
163
163
  variant = "primary",
@@ -215,7 +215,7 @@ var Button = React50__default.forwardRef(
215
215
  }
216
216
  );
217
217
  Button.displayName = "Button";
218
- var Input = React50__default.forwardRef(
218
+ var Input = React69__default.forwardRef(
219
219
  ({
220
220
  className,
221
221
  inputType,
@@ -327,7 +327,7 @@ var Input = React50__default.forwardRef(
327
327
  }
328
328
  );
329
329
  Input.displayName = "Input";
330
- var Label = React50__default.forwardRef(
330
+ var Label = React69__default.forwardRef(
331
331
  ({ className, required, children, ...props }, ref) => {
332
332
  return /* @__PURE__ */ jsxs(
333
333
  "label",
@@ -347,7 +347,7 @@ var Label = React50__default.forwardRef(
347
347
  }
348
348
  );
349
349
  Label.displayName = "Label";
350
- var Textarea = React50__default.forwardRef(
350
+ var Textarea = React69__default.forwardRef(
351
351
  ({ className, error, ...props }, ref) => {
352
352
  return /* @__PURE__ */ jsx(
353
353
  "textarea",
@@ -370,7 +370,7 @@ var Textarea = React50__default.forwardRef(
370
370
  }
371
371
  );
372
372
  Textarea.displayName = "Textarea";
373
- var Select = React50__default.forwardRef(
373
+ var Select = React69__default.forwardRef(
374
374
  ({ className, options, placeholder, error, ...props }, ref) => {
375
375
  return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
376
376
  /* @__PURE__ */ jsxs(
@@ -406,7 +406,7 @@ var Select = React50__default.forwardRef(
406
406
  }
407
407
  );
408
408
  Select.displayName = "Select";
409
- var Checkbox = React50__default.forwardRef(
409
+ var Checkbox = React69__default.forwardRef(
410
410
  ({ className, label, id, ...props }, ref) => {
411
411
  const inputId = id || `checkbox-${Math.random().toString(36).substr(2, 9)}`;
412
412
  return /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
@@ -472,7 +472,7 @@ var shadowStyles = {
472
472
  md: "shadow-[var(--shadow-main)]",
473
473
  lg: "shadow-[var(--shadow-lg)]"
474
474
  };
475
- var Card = React50__default.forwardRef(
475
+ var Card = React69__default.forwardRef(
476
476
  ({
477
477
  className,
478
478
  variant = "bordered",
@@ -508,9 +508,9 @@ var Card = React50__default.forwardRef(
508
508
  }
509
509
  );
510
510
  Card.displayName = "Card";
511
- var CardHeader = React50__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("mb-4", className), ...props }));
511
+ var CardHeader = React69__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("mb-4", className), ...props }));
512
512
  CardHeader.displayName = "CardHeader";
513
- var CardTitle = React50__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
513
+ var CardTitle = React69__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
514
514
  "h3",
515
515
  {
516
516
  ref,
@@ -523,11 +523,11 @@ var CardTitle = React50__default.forwardRef(({ className, ...props }, ref) => /*
523
523
  }
524
524
  ));
525
525
  CardTitle.displayName = "CardTitle";
526
- var CardContent = React50__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("", className), ...props }));
526
+ var CardContent = React69__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("", className), ...props }));
527
527
  CardContent.displayName = "CardContent";
528
528
  var CardBody = CardContent;
529
529
  CardBody.displayName = "CardBody";
530
- var CardFooter = React50__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
530
+ var CardFooter = React69__default.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
531
531
  "div",
532
532
  {
533
533
  ref,
@@ -573,8 +573,8 @@ var sizeStyles2 = {
573
573
  md: "px-2.5 py-1 text-sm",
574
574
  lg: "px-3 py-1.5 text-base"
575
575
  };
576
- var Badge = React50__default.forwardRef(
577
- ({ className, variant = "default", size = "sm", label, icon, children, ...props }, ref) => {
576
+ var Badge = React69__default.forwardRef(
577
+ ({ className, variant = "default", size = "sm", amount, label, icon, children, ...props }, ref) => {
578
578
  const iconSizes2 = { sm: "w-3 h-3", md: "w-3.5 h-3.5", lg: "w-4 h-4" };
579
579
  const resolvedIcon = typeof icon === "string" ? (() => {
580
580
  const I = resolveIcon(icon);
@@ -593,7 +593,7 @@ var Badge = React50__default.forwardRef(
593
593
  ...props,
594
594
  children: [
595
595
  resolvedIcon,
596
- children || label
596
+ children || (amount != null ? `${label ? `${label} ` : ""}${amount}` : label)
597
597
  ]
598
598
  }
599
599
  );
@@ -606,7 +606,7 @@ var sizeStyles3 = {
606
606
  md: "h-6 w-6",
607
607
  lg: "h-8 w-8"
608
608
  };
609
- var Spinner = React50__default.forwardRef(
609
+ var Spinner = React69__default.forwardRef(
610
610
  ({ className, size = "md", ...props }, ref) => {
611
611
  return /* @__PURE__ */ jsx(
612
612
  "div",
@@ -871,7 +871,7 @@ var positionStyles = {
871
871
  fixed: "fixed",
872
872
  sticky: "sticky"
873
873
  };
874
- var Box = React50__default.forwardRef(
874
+ var Box = React69__default.forwardRef(
875
875
  ({
876
876
  padding,
877
877
  paddingX,
@@ -1216,7 +1216,7 @@ var ProgressBar = ({
1216
1216
  return null;
1217
1217
  };
1218
1218
  ProgressBar.displayName = "ProgressBar";
1219
- var Radio = React50__default.forwardRef(
1219
+ var Radio = React69__default.forwardRef(
1220
1220
  ({
1221
1221
  label,
1222
1222
  helperText,
@@ -1320,7 +1320,7 @@ var Radio = React50__default.forwardRef(
1320
1320
  }
1321
1321
  );
1322
1322
  Radio.displayName = "Radio";
1323
- var Switch = React50.forwardRef(
1323
+ var Switch = React69.forwardRef(
1324
1324
  ({
1325
1325
  checked,
1326
1326
  defaultChecked = false,
@@ -1331,10 +1331,10 @@ var Switch = React50.forwardRef(
1331
1331
  name,
1332
1332
  className
1333
1333
  }, ref) => {
1334
- const [isChecked, setIsChecked] = React50.useState(
1334
+ const [isChecked, setIsChecked] = React69.useState(
1335
1335
  checked !== void 0 ? checked : defaultChecked
1336
1336
  );
1337
- React50.useEffect(() => {
1337
+ React69.useEffect(() => {
1338
1338
  if (checked !== void 0) {
1339
1339
  setIsChecked(checked);
1340
1340
  }
@@ -1467,7 +1467,8 @@ var Stack = ({
1467
1467
  role,
1468
1468
  tabIndex,
1469
1469
  action,
1470
- actionPayload
1470
+ actionPayload,
1471
+ responsive = false
1471
1472
  }) => {
1472
1473
  const eventBus = useEventBus();
1473
1474
  const handleClick = (e) => {
@@ -1476,7 +1477,8 @@ var Stack = ({
1476
1477
  }
1477
1478
  onClick?.(e);
1478
1479
  };
1479
- const directionClass = direction === "horizontal" ? reverse ? "flex-row-reverse" : "flex-row" : reverse ? "flex-col-reverse" : "flex-col";
1480
+ const isHorizontal = direction === "horizontal";
1481
+ const directionClass = responsive && isHorizontal ? reverse ? "flex-col-reverse md:flex-row-reverse" : "flex-col md:flex-row" : isHorizontal ? reverse ? "flex-row-reverse" : "flex-row" : reverse ? "flex-col-reverse" : "flex-col";
1480
1482
  return /* @__PURE__ */ jsx(
1481
1483
  Component,
1482
1484
  {
@@ -2005,8 +2007,8 @@ var LawReferenceTooltip = ({
2005
2007
  position = "top",
2006
2008
  className
2007
2009
  }) => {
2008
- const [isVisible, setIsVisible] = React50__default.useState(false);
2009
- const timeoutRef = React50__default.useRef(null);
2010
+ const [isVisible, setIsVisible] = React69__default.useState(false);
2011
+ const timeoutRef = React69__default.useRef(null);
2010
2012
  const handleMouseEnter = () => {
2011
2013
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
2012
2014
  timeoutRef.current = setTimeout(() => setIsVisible(true), 200);
@@ -2015,7 +2017,7 @@ var LawReferenceTooltip = ({
2015
2017
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
2016
2018
  setIsVisible(false);
2017
2019
  };
2018
- React50__default.useEffect(() => {
2020
+ React69__default.useEffect(() => {
2019
2021
  return () => {
2020
2022
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
2021
2023
  };
@@ -2182,6 +2184,567 @@ function TimeSlotCell({
2182
2184
  );
2183
2185
  }
2184
2186
  TimeSlotCell.displayName = "TimeSlotCell";
2187
+ var statusColors = {
2188
+ online: "bg-[var(--color-success)]",
2189
+ offline: "bg-[var(--color-muted-foreground)]",
2190
+ away: "bg-[var(--color-warning)]",
2191
+ busy: "bg-[var(--color-error)]",
2192
+ warning: "bg-[var(--color-warning)]",
2193
+ critical: "bg-[var(--color-error)]"
2194
+ };
2195
+ var pulseRingColors = {
2196
+ online: "ring-[var(--color-success)]",
2197
+ offline: "ring-[var(--color-muted-foreground)]",
2198
+ away: "ring-[var(--color-warning)]",
2199
+ busy: "ring-[var(--color-error)]",
2200
+ warning: "ring-[var(--color-warning)]",
2201
+ critical: "ring-[var(--color-error)]"
2202
+ };
2203
+ var sizeStyles5 = {
2204
+ sm: "w-2 h-2",
2205
+ md: "w-2.5 h-2.5",
2206
+ lg: "w-3 h-3"
2207
+ };
2208
+ var StatusDot = React69__default.forwardRef(
2209
+ ({ className, status = "offline", pulse = false, size = "md", label, ...props }, ref) => {
2210
+ return /* @__PURE__ */ jsx(
2211
+ "span",
2212
+ {
2213
+ ref,
2214
+ className: cn(
2215
+ "inline-block rounded-full flex-shrink-0",
2216
+ statusColors[status],
2217
+ sizeStyles5[size],
2218
+ pulse && [
2219
+ "animate-pulse",
2220
+ "ring-2 ring-offset-1",
2221
+ pulseRingColors[status],
2222
+ "ring-opacity-40"
2223
+ ],
2224
+ className
2225
+ ),
2226
+ role: "status",
2227
+ "aria-label": label ?? status,
2228
+ ...props
2229
+ }
2230
+ );
2231
+ }
2232
+ );
2233
+ StatusDot.displayName = "StatusDot";
2234
+ var sizeStyles6 = {
2235
+ sm: { icon: "w-3 h-3", text: "text-xs" },
2236
+ md: { icon: "w-4 h-4", text: "text-sm" },
2237
+ lg: { icon: "w-5 h-5", text: "text-base" }
2238
+ };
2239
+ function resolveDirection(value, direction) {
2240
+ if (direction) return direction;
2241
+ if (value === void 0 || value === 0) return "flat";
2242
+ return value > 0 ? "up" : "down";
2243
+ }
2244
+ function resolveColor(dir, invert) {
2245
+ if (dir === "flat") return "text-[var(--color-muted-foreground)]";
2246
+ const isPositive = dir === "up";
2247
+ const isGood = invert ? !isPositive : isPositive;
2248
+ return isGood ? "text-[var(--color-success)]" : "text-[var(--color-error)]";
2249
+ }
2250
+ var iconMap = {
2251
+ up: TrendingUp,
2252
+ down: TrendingDown,
2253
+ flat: ArrowRight
2254
+ };
2255
+ var TrendIndicator = React69__default.forwardRef(
2256
+ ({
2257
+ className,
2258
+ value,
2259
+ direction,
2260
+ showValue = true,
2261
+ invert = false,
2262
+ label,
2263
+ size = "md",
2264
+ ...props
2265
+ }, ref) => {
2266
+ const dir = resolveDirection(value, direction);
2267
+ const colorClass = resolveColor(dir, invert);
2268
+ const IconComponent = iconMap[dir];
2269
+ const styles = sizeStyles6[size];
2270
+ const formattedValue = value !== void 0 ? `${value > 0 ? "+" : ""}${value}%` : void 0;
2271
+ const ariaLabel = label ?? (formattedValue ? `${dir} ${formattedValue}` : dir);
2272
+ return /* @__PURE__ */ jsxs(
2273
+ "span",
2274
+ {
2275
+ ref,
2276
+ className: cn(
2277
+ "inline-flex items-center gap-1 font-medium",
2278
+ colorClass,
2279
+ styles.text,
2280
+ className
2281
+ ),
2282
+ role: "status",
2283
+ "aria-label": ariaLabel,
2284
+ ...props,
2285
+ children: [
2286
+ /* @__PURE__ */ jsx(IconComponent, { className: styles.icon }),
2287
+ showValue && formattedValue && /* @__PURE__ */ jsx("span", { children: formattedValue })
2288
+ ]
2289
+ }
2290
+ );
2291
+ }
2292
+ );
2293
+ TrendIndicator.displayName = "TrendIndicator";
2294
+ function useSafeEventBus() {
2295
+ try {
2296
+ return useEventBus();
2297
+ } catch {
2298
+ return { emit: () => {
2299
+ }, on: () => () => {
2300
+ }, once: () => {
2301
+ } };
2302
+ }
2303
+ }
2304
+ var trackSizes = {
2305
+ sm: "h-1",
2306
+ md: "h-1.5",
2307
+ lg: "h-2"
2308
+ };
2309
+ var thumbSizes = {
2310
+ sm: "w-3 h-3",
2311
+ md: "w-4 h-4",
2312
+ lg: "w-5 h-5"
2313
+ };
2314
+ var RangeSlider = React69__default.forwardRef(
2315
+ ({
2316
+ className,
2317
+ min = 0,
2318
+ max = 100,
2319
+ value = 0,
2320
+ step = 1,
2321
+ showTooltip = false,
2322
+ showTicks = false,
2323
+ buffered,
2324
+ size = "md",
2325
+ disabled = false,
2326
+ action,
2327
+ actionPayload,
2328
+ onChange,
2329
+ formatValue: formatValue4,
2330
+ ...props
2331
+ }, ref) => {
2332
+ const [isDragging, setIsDragging] = useState(false);
2333
+ const [showTip, setShowTip] = useState(false);
2334
+ const inputRef = useRef(null);
2335
+ const eventBus = useSafeEventBus();
2336
+ const percentage = max !== min ? (value - min) / (max - min) * 100 : 0;
2337
+ const bufferedPercentage = buffered !== void 0 ? Math.min(buffered, 100) : void 0;
2338
+ const displayValue = formatValue4 ? formatValue4(value) : String(value);
2339
+ const handleChange = useCallback(
2340
+ (e) => {
2341
+ const newValue = Number(e.target.value);
2342
+ onChange?.(newValue);
2343
+ if (action) {
2344
+ eventBus.emit(`UI:${action}`, { ...actionPayload, value: newValue });
2345
+ }
2346
+ },
2347
+ [onChange, action, actionPayload, eventBus]
2348
+ );
2349
+ const tickCount = showTicks ? Math.min(Math.floor((max - min) / step), 20) : 0;
2350
+ return /* @__PURE__ */ jsxs(
2351
+ "div",
2352
+ {
2353
+ ref,
2354
+ className: cn(
2355
+ "relative w-full",
2356
+ disabled && "opacity-50 cursor-not-allowed",
2357
+ className
2358
+ ),
2359
+ ...props,
2360
+ children: [
2361
+ /* @__PURE__ */ jsxs("div", { className: "relative w-full py-2", children: [
2362
+ /* @__PURE__ */ jsx(
2363
+ "div",
2364
+ {
2365
+ className: cn(
2366
+ "absolute inset-x-0 rounded-full bg-[var(--color-muted)]",
2367
+ trackSizes[size]
2368
+ ),
2369
+ style: { top: "50%", transform: "translateY(-50%)" }
2370
+ }
2371
+ ),
2372
+ bufferedPercentage !== void 0 && /* @__PURE__ */ jsx(
2373
+ "div",
2374
+ {
2375
+ className: cn(
2376
+ "absolute rounded-full bg-[var(--color-muted-foreground)] opacity-30",
2377
+ trackSizes[size]
2378
+ ),
2379
+ style: {
2380
+ top: "50%",
2381
+ transform: "translateY(-50%)",
2382
+ left: 0,
2383
+ width: `${bufferedPercentage}%`
2384
+ }
2385
+ }
2386
+ ),
2387
+ /* @__PURE__ */ jsx(
2388
+ "div",
2389
+ {
2390
+ className: cn(
2391
+ "absolute rounded-full bg-[var(--color-primary)]",
2392
+ trackSizes[size]
2393
+ ),
2394
+ style: {
2395
+ top: "50%",
2396
+ transform: "translateY(-50%)",
2397
+ left: 0,
2398
+ width: `${percentage}%`
2399
+ }
2400
+ }
2401
+ ),
2402
+ /* @__PURE__ */ jsx(
2403
+ "input",
2404
+ {
2405
+ ref: inputRef,
2406
+ type: "range",
2407
+ min,
2408
+ max,
2409
+ step,
2410
+ value,
2411
+ disabled,
2412
+ onChange: handleChange,
2413
+ onMouseDown: () => {
2414
+ setIsDragging(true);
2415
+ setShowTip(true);
2416
+ },
2417
+ onMouseUp: () => {
2418
+ setIsDragging(false);
2419
+ setShowTip(false);
2420
+ },
2421
+ onTouchStart: () => {
2422
+ setIsDragging(true);
2423
+ setShowTip(true);
2424
+ },
2425
+ onTouchEnd: () => {
2426
+ setIsDragging(false);
2427
+ setShowTip(false);
2428
+ },
2429
+ onFocus: () => setShowTip(true),
2430
+ onBlur: () => {
2431
+ if (!isDragging) setShowTip(false);
2432
+ },
2433
+ className: cn(
2434
+ "absolute inset-0 w-full opacity-0 cursor-pointer",
2435
+ disabled && "cursor-not-allowed",
2436
+ // Thumb sizing via pseudo-element
2437
+ "[&::-webkit-slider-thumb]:appearance-none",
2438
+ "[&::-webkit-slider-thumb]:w-5 [&::-webkit-slider-thumb]:h-5",
2439
+ "[&::-moz-range-thumb]:w-5 [&::-moz-range-thumb]:h-5"
2440
+ ),
2441
+ style: { height: "100%", margin: 0 },
2442
+ "aria-label": props["aria-label"] ?? "Range slider",
2443
+ "aria-valuemin": min,
2444
+ "aria-valuemax": max,
2445
+ "aria-valuenow": value,
2446
+ "aria-valuetext": displayValue
2447
+ }
2448
+ ),
2449
+ /* @__PURE__ */ jsx(
2450
+ "div",
2451
+ {
2452
+ className: cn(
2453
+ "absolute rounded-full bg-[var(--color-primary-foreground)]",
2454
+ "border-2 border-[var(--color-primary)]",
2455
+ "shadow-[var(--shadow-sm)]",
2456
+ "pointer-events-none",
2457
+ "transition-transform duration-100",
2458
+ isDragging && "scale-110",
2459
+ thumbSizes[size]
2460
+ ),
2461
+ style: {
2462
+ top: "50%",
2463
+ transform: `translateY(-50%) translateX(-50%)${isDragging ? " scale(1.1)" : ""}`,
2464
+ left: `${percentage}%`
2465
+ }
2466
+ }
2467
+ ),
2468
+ showTooltip && showTip && /* @__PURE__ */ jsx(
2469
+ "div",
2470
+ {
2471
+ className: cn(
2472
+ "absolute -top-8 px-2 py-0.5 rounded",
2473
+ "bg-[var(--color-foreground)] text-[var(--color-background)]",
2474
+ "text-xs font-medium whitespace-nowrap",
2475
+ "pointer-events-none"
2476
+ ),
2477
+ style: {
2478
+ left: `${percentage}%`,
2479
+ transform: "translateX(-50%)"
2480
+ },
2481
+ children: displayValue
2482
+ }
2483
+ )
2484
+ ] }),
2485
+ showTicks && tickCount > 0 && /* @__PURE__ */ jsx("div", { className: "relative w-full h-2 mt-1", children: Array.from({ length: tickCount + 1 }, (_, i) => {
2486
+ const tickPercent = i / tickCount * 100;
2487
+ return /* @__PURE__ */ jsx(
2488
+ "div",
2489
+ {
2490
+ className: "absolute w-px h-1.5 bg-[var(--color-muted-foreground)]",
2491
+ style: { left: `${tickPercent}%` }
2492
+ },
2493
+ i
2494
+ );
2495
+ }) })
2496
+ ]
2497
+ }
2498
+ );
2499
+ }
2500
+ );
2501
+ RangeSlider.displayName = "RangeSlider";
2502
+ function easeOut(t) {
2503
+ return t * (2 - t);
2504
+ }
2505
+ var AnimatedCounter = ({
2506
+ value,
2507
+ duration = 600,
2508
+ prefix,
2509
+ suffix,
2510
+ className
2511
+ }) => {
2512
+ const [displayValue, setDisplayValue] = useState(value);
2513
+ const previousValueRef = useRef(value);
2514
+ const animationFrameRef = useRef(null);
2515
+ useEffect(() => {
2516
+ const from = previousValueRef.current;
2517
+ const to = value;
2518
+ previousValueRef.current = value;
2519
+ if (from === to) {
2520
+ setDisplayValue(to);
2521
+ return;
2522
+ }
2523
+ const startTime = performance.now();
2524
+ const diff = to - from;
2525
+ function animate(currentTime) {
2526
+ const elapsed = currentTime - startTime;
2527
+ const progress = Math.min(elapsed / duration, 1);
2528
+ const easedProgress = easeOut(progress);
2529
+ setDisplayValue(from + diff * easedProgress);
2530
+ if (progress < 1) {
2531
+ animationFrameRef.current = requestAnimationFrame(animate);
2532
+ } else {
2533
+ setDisplayValue(to);
2534
+ }
2535
+ }
2536
+ animationFrameRef.current = requestAnimationFrame(animate);
2537
+ return () => {
2538
+ if (animationFrameRef.current !== null) {
2539
+ cancelAnimationFrame(animationFrameRef.current);
2540
+ }
2541
+ };
2542
+ }, [value, duration]);
2543
+ const decimalPlaces = Number.isInteger(value) ? 0 : String(value).split(".")[1]?.length ?? 0;
2544
+ const formattedValue = displayValue.toFixed(decimalPlaces);
2545
+ return /* @__PURE__ */ jsxs(Typography, { variant: "h3", className: cn("tabular-nums", className), children: [
2546
+ prefix,
2547
+ formattedValue,
2548
+ suffix
2549
+ ] });
2550
+ };
2551
+ AnimatedCounter.displayName = "AnimatedCounter";
2552
+ var InfiniteScrollSentinel = ({
2553
+ loadMoreEvent,
2554
+ loadMorePayload,
2555
+ isLoading = false,
2556
+ hasMore = true,
2557
+ threshold = "200px",
2558
+ className
2559
+ }) => {
2560
+ const eventBus = useEventBus();
2561
+ const onLoadMore = useCallback(() => {
2562
+ eventBus.emit(`UI:${loadMoreEvent}`, loadMorePayload ?? {});
2563
+ }, [eventBus, loadMoreEvent, loadMorePayload]);
2564
+ const { sentinelRef } = useInfiniteScroll(onLoadMore, {
2565
+ rootMargin: threshold,
2566
+ hasMore,
2567
+ isLoading
2568
+ });
2569
+ return /* @__PURE__ */ jsxs(
2570
+ Box,
2571
+ {
2572
+ className: cn("flex items-center justify-center py-4", className),
2573
+ children: [
2574
+ /* @__PURE__ */ jsx(Box, { ref: sentinelRef, className: "h-1 w-full", "aria-hidden": "true" }),
2575
+ isLoading && /* @__PURE__ */ jsx(Spinner, { size: "sm" })
2576
+ ]
2577
+ }
2578
+ );
2579
+ };
2580
+ InfiniteScrollSentinel.displayName = "InfiniteScrollSentinel";
2581
+ var CONFETTI_COLORS = [
2582
+ "var(--color-primary)",
2583
+ "var(--color-success)",
2584
+ "var(--color-warning)",
2585
+ "var(--color-error)",
2586
+ "gold",
2587
+ "dodgerblue"
2588
+ ];
2589
+ var particleIdCounter = 0;
2590
+ function createParticles(count) {
2591
+ return Array.from({ length: count }, () => {
2592
+ particleIdCounter += 1;
2593
+ return {
2594
+ id: particleIdCounter,
2595
+ color: CONFETTI_COLORS[Math.floor(Math.random() * CONFETTI_COLORS.length)],
2596
+ left: 30 + Math.random() * 40,
2597
+ delay: Math.random() * 300,
2598
+ angle: Math.random() * 360,
2599
+ distance: 40 + Math.random() * 80,
2600
+ rotation: Math.random() * 720 - 360,
2601
+ size: 4 + Math.random() * 6
2602
+ };
2603
+ });
2604
+ }
2605
+ var ConfettiEffect = ({
2606
+ trigger,
2607
+ duration = 2e3,
2608
+ particleCount = 30,
2609
+ className
2610
+ }) => {
2611
+ const [particles, setParticles] = useState([]);
2612
+ const previousTriggerRef = useRef(false);
2613
+ useEffect(() => {
2614
+ const wasFalse = !previousTriggerRef.current;
2615
+ previousTriggerRef.current = trigger;
2616
+ if (trigger && wasFalse) {
2617
+ const newParticles = createParticles(particleCount);
2618
+ setParticles(newParticles);
2619
+ const timer = window.setTimeout(() => {
2620
+ setParticles([]);
2621
+ }, duration);
2622
+ return () => {
2623
+ window.clearTimeout(timer);
2624
+ };
2625
+ }
2626
+ return void 0;
2627
+ }, [trigger, particleCount, duration]);
2628
+ if (particles.length === 0) {
2629
+ return null;
2630
+ }
2631
+ return /* @__PURE__ */ jsxs(
2632
+ Box,
2633
+ {
2634
+ position: "absolute",
2635
+ className: cn(
2636
+ "inset-0 pointer-events-none overflow-hidden z-50",
2637
+ className
2638
+ ),
2639
+ "aria-hidden": "true",
2640
+ children: [
2641
+ particles.map((p) => {
2642
+ const rad = p.angle * Math.PI / 180;
2643
+ const tx = Math.cos(rad) * p.distance;
2644
+ const ty = Math.sin(rad) * p.distance - 20;
2645
+ return /* @__PURE__ */ jsx(
2646
+ Box,
2647
+ {
2648
+ className: "absolute rounded-sm",
2649
+ style: {
2650
+ left: `${p.left}%`,
2651
+ top: "50%",
2652
+ width: p.size,
2653
+ height: p.size,
2654
+ backgroundColor: p.color,
2655
+ animation: `confetti-burst ${duration - p.delay}ms ease-out ${p.delay}ms forwards`,
2656
+ opacity: 0,
2657
+ // Use CSS custom properties for the animation endpoint
2658
+ // @ts-expect-error -- CSS custom properties are not typed in CSSProperties
2659
+ "--confetti-tx": `${tx}px`,
2660
+ "--confetti-ty": `${ty}px`,
2661
+ "--confetti-rotate": `${p.rotation}deg`
2662
+ }
2663
+ },
2664
+ p.id
2665
+ );
2666
+ }),
2667
+ /* @__PURE__ */ jsx("style", { children: `
2668
+ @keyframes confetti-burst {
2669
+ 0% {
2670
+ opacity: 1;
2671
+ transform: translate(0, 0) rotate(0deg) scale(1);
2672
+ }
2673
+ 70% {
2674
+ opacity: 1;
2675
+ }
2676
+ 100% {
2677
+ opacity: 0;
2678
+ transform: translate(var(--confetti-tx), var(--confetti-ty)) rotate(var(--confetti-rotate)) scale(0.5);
2679
+ }
2680
+ }
2681
+ ` })
2682
+ ]
2683
+ }
2684
+ );
2685
+ };
2686
+ ConfettiEffect.displayName = "ConfettiEffect";
2687
+ var TypewriterText = ({
2688
+ text,
2689
+ speed = 40,
2690
+ startDelay = 0,
2691
+ className,
2692
+ onComplete
2693
+ }) => {
2694
+ const [charCount, setCharCount] = useState(0);
2695
+ const [started, setStarted] = useState(startDelay === 0);
2696
+ const onCompleteRef = useRef(onComplete);
2697
+ onCompleteRef.current = onComplete;
2698
+ useEffect(() => {
2699
+ if (startDelay <= 0) {
2700
+ setStarted(true);
2701
+ return void 0;
2702
+ }
2703
+ setStarted(false);
2704
+ setCharCount(0);
2705
+ const delayTimer = window.setTimeout(() => {
2706
+ setStarted(true);
2707
+ }, startDelay);
2708
+ return () => {
2709
+ window.clearTimeout(delayTimer);
2710
+ };
2711
+ }, [startDelay, text]);
2712
+ useEffect(() => {
2713
+ setCharCount(0);
2714
+ }, [text]);
2715
+ useEffect(() => {
2716
+ if (!started) return void 0;
2717
+ if (charCount >= text.length) {
2718
+ onCompleteRef.current?.();
2719
+ return void 0;
2720
+ }
2721
+ const interval = window.setInterval(() => {
2722
+ setCharCount((prev) => {
2723
+ const next = prev + 1;
2724
+ if (next >= text.length) {
2725
+ window.clearInterval(interval);
2726
+ }
2727
+ return next;
2728
+ });
2729
+ }, speed);
2730
+ return () => {
2731
+ window.clearInterval(interval);
2732
+ };
2733
+ }, [started, text, speed, charCount]);
2734
+ const isComplete = charCount >= text.length;
2735
+ const displayedText = text.slice(0, charCount);
2736
+ return /* @__PURE__ */ jsxs(Typography, { variant: "body", className: cn("inline", className), children: [
2737
+ displayedText,
2738
+ !isComplete && /* @__PURE__ */ jsx(
2739
+ "span",
2740
+ {
2741
+ className: "inline-block w-[2px] h-[1em] bg-[var(--color-foreground)] ml-[1px] align-text-bottom animate-pulse",
2742
+ "aria-hidden": "true"
2743
+ }
2744
+ )
2745
+ ] });
2746
+ };
2747
+ TypewriterText.displayName = "TypewriterText";
2185
2748
  var heartIcon = (filled, size) => /* @__PURE__ */ jsx(
2186
2749
  "svg",
2187
2750
  {
@@ -2264,9 +2827,9 @@ function ScoreDisplay({
2264
2827
  animated = true,
2265
2828
  locale = "en-US"
2266
2829
  }) {
2267
- const [displayValue, setDisplayValue] = React50.useState(value);
2268
- const [isAnimating, setIsAnimating] = React50.useState(false);
2269
- React50.useEffect(() => {
2830
+ const [displayValue, setDisplayValue] = React69.useState(value);
2831
+ const [isAnimating, setIsAnimating] = React69.useState(false);
2832
+ React69.useEffect(() => {
2270
2833
  if (!animated || displayValue === value) {
2271
2834
  setDisplayValue(value);
2272
2835
  return;
@@ -2339,9 +2902,9 @@ function ControlButton({
2339
2902
  className
2340
2903
  }) {
2341
2904
  const eventBus = useEventBus();
2342
- const [isPressed, setIsPressed] = React50.useState(false);
2905
+ const [isPressed, setIsPressed] = React69.useState(false);
2343
2906
  const actualPressed = pressed ?? isPressed;
2344
- const handlePointerDown = React50.useCallback(
2907
+ const handlePointerDown = React69.useCallback(
2345
2908
  (e) => {
2346
2909
  e.preventDefault();
2347
2910
  if (disabled) return;
@@ -2351,7 +2914,7 @@ function ControlButton({
2351
2914
  },
2352
2915
  [disabled, pressEvent, eventBus, onPress]
2353
2916
  );
2354
- const handlePointerUp = React50.useCallback(
2917
+ const handlePointerUp = React69.useCallback(
2355
2918
  (e) => {
2356
2919
  e.preventDefault();
2357
2920
  if (disabled) return;
@@ -2361,7 +2924,7 @@ function ControlButton({
2361
2924
  },
2362
2925
  [disabled, releaseEvent, eventBus, onRelease]
2363
2926
  );
2364
- const handlePointerLeave = React50.useCallback(
2927
+ const handlePointerLeave = React69.useCallback(
2365
2928
  (e) => {
2366
2929
  if (isPressed) {
2367
2930
  setIsPressed(false);
@@ -2589,7 +3152,7 @@ var ErrorState = ({
2589
3152
  );
2590
3153
  };
2591
3154
  ErrorState.displayName = "ErrorState";
2592
- var ErrorBoundary = class extends React50__default.Component {
3155
+ var ErrorBoundary = class extends React69__default.Component {
2593
3156
  constructor(props) {
2594
3157
  super(props);
2595
3158
  __publicField(this, "reset", () => {
@@ -2993,7 +3556,7 @@ var variantIconColors = {
2993
3556
  warning: "text-[var(--color-warning)]",
2994
3557
  error: "text-[var(--color-error)]"
2995
3558
  };
2996
- var iconMap = {
3559
+ var iconMap2 = {
2997
3560
  info: Info,
2998
3561
  success: CheckCircle,
2999
3562
  warning: AlertTriangle,
@@ -3032,7 +3595,7 @@ var Alert = ({
3032
3595
  /* @__PURE__ */ jsx("div", { className: "flex-shrink-0 mt-0.5", children: /* @__PURE__ */ jsx(
3033
3596
  Icon,
3034
3597
  {
3035
- icon: iconMap[variant],
3598
+ icon: iconMap2[variant],
3036
3599
  size: "md",
3037
3600
  className: variantIconColors[variant]
3038
3601
  }
@@ -3144,7 +3707,7 @@ var Breadcrumb = ({
3144
3707
  );
3145
3708
  };
3146
3709
  Breadcrumb.displayName = "Breadcrumb";
3147
- function useSafeEventBus() {
3710
+ function useSafeEventBus2() {
3148
3711
  try {
3149
3712
  return useEventBus();
3150
3713
  } catch {
@@ -3165,7 +3728,7 @@ var ButtonGroup = ({
3165
3728
  entity: _entity,
3166
3729
  filters
3167
3730
  }) => {
3168
- const eventBus = useSafeEventBus();
3731
+ const eventBus = useSafeEventBus2();
3169
3732
  const variantClasses2 = {
3170
3733
  default: "gap-0",
3171
3734
  segmented: "gap-0 [&>button]:rounded-none [&>button:first-child]:rounded-l-lg [&>button:last-child]:rounded-r-lg [&>button:not(:first-child)]:border-l-0",
@@ -3631,11 +4194,19 @@ function Card2({
3631
4194
  children,
3632
4195
  onClick,
3633
4196
  className = "",
3634
- action
4197
+ action,
4198
+ longPressEvent,
4199
+ longPressPayload
3635
4200
  }) {
3636
4201
  const eventBus = useEventBus();
3637
4202
  const { t } = useTranslate();
3638
4203
  const isClickable = !!onClick || !!action;
4204
+ const handleLongPress = useCallback(() => {
4205
+ if (longPressEvent) {
4206
+ eventBus.emit(`UI:${longPressEvent}`, longPressPayload ?? {});
4207
+ }
4208
+ }, [longPressEvent, longPressPayload, eventBus]);
4209
+ const longPress = useLongPress(handleLongPress);
3639
4210
  const handleClick = () => {
3640
4211
  if (action) eventBus.emit(`UI:${action}`, {});
3641
4212
  onClick?.();
@@ -3654,6 +4225,12 @@ function Card2({
3654
4225
  role: isClickable ? "button" : void 0,
3655
4226
  tabIndex: isClickable ? 0 : void 0,
3656
4227
  onKeyDown: isClickable ? (e) => e.key === "Enter" && handleClick() : void 0,
4228
+ ...longPressEvent ? {
4229
+ onPointerDown: longPress.onPointerDown,
4230
+ onPointerMove: longPress.onPointerMove,
4231
+ onPointerUp: longPress.onPointerUp,
4232
+ onPointerCancel: longPress.onPointerCancel
4233
+ } : {},
3657
4234
  children: [
3658
4235
  image && /* @__PURE__ */ jsx("div", { className: "aspect-video w-full overflow-hidden rounded-t-lg", children: /* @__PURE__ */ jsx(
3659
4236
  "img",
@@ -3694,7 +4271,7 @@ function Card2({
3694
4271
  );
3695
4272
  }
3696
4273
  Card2.displayName = "Card";
3697
- var sizeStyles5 = {
4274
+ var sizeStyles7 = {
3698
4275
  xs: "max-w-xs",
3699
4276
  // 320px
3700
4277
  sm: "max-w-screen-sm",
@@ -3731,7 +4308,7 @@ var Container = ({
3731
4308
  {
3732
4309
  className: cn(
3733
4310
  "w-full",
3734
- sizeStyles5[resolvedSize],
4311
+ sizeStyles7[resolvedSize],
3735
4312
  paddingStyles3[padding],
3736
4313
  center && "mx-auto",
3737
4314
  className
@@ -4197,8 +4774,8 @@ var Menu = ({
4197
4774
  "bottom-start": "top-full left-0 mt-2",
4198
4775
  "bottom-end": "top-full right-0 mt-2"
4199
4776
  };
4200
- const triggerChild = React50__default.isValidElement(trigger) ? trigger : /* @__PURE__ */ jsx("span", { children: trigger });
4201
- const triggerElement = React50__default.cloneElement(
4777
+ const triggerChild = React69__default.isValidElement(trigger) ? trigger : /* @__PURE__ */ jsx("span", { children: trigger });
4778
+ const triggerElement = React69__default.cloneElement(
4202
4779
  triggerChild,
4203
4780
  {
4204
4781
  ref: triggerRef,
@@ -4314,11 +4891,15 @@ var Modal = ({
4314
4891
  closeOnOverlayClick = true,
4315
4892
  closeOnEscape = true,
4316
4893
  className,
4317
- closeEvent
4894
+ closeEvent,
4895
+ swipeDownToClose = true
4318
4896
  }) => {
4319
4897
  const eventBus = useEventBus();
4320
4898
  const modalRef = useRef(null);
4321
4899
  const previousActiveElement = useRef(null);
4900
+ const [dragY, setDragY] = useState(0);
4901
+ const dragStartY = useRef(0);
4902
+ const isDragging = useRef(false);
4322
4903
  useEffect(() => {
4323
4904
  if (isOpen) {
4324
4905
  previousActiveElement.current = document.activeElement;
@@ -4371,7 +4952,11 @@ var Modal = ({
4371
4952
  className: "z-40"
4372
4953
  }
4373
4954
  ),
4374
- /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center p-4 pointer-events-none", children: /* @__PURE__ */ jsxs(
4955
+ /* @__PURE__ */ jsx("div", { className: cn(
4956
+ "fixed inset-0 z-50 pointer-events-none",
4957
+ "flex items-center justify-center p-4",
4958
+ "max-sm:items-end max-sm:p-0"
4959
+ ), children: /* @__PURE__ */ jsxs(
4375
4960
  Box,
4376
4961
  {
4377
4962
  ref: modalRef,
@@ -4382,12 +4967,47 @@ var Modal = ({
4382
4967
  className: cn(
4383
4968
  "pointer-events-auto w-full flex flex-col max-h-[90vh]",
4384
4969
  sizeClasses4[size],
4970
+ "max-sm:max-w-full max-sm:max-h-[85vh] max-sm:rounded-b-none max-sm:rounded-t-2xl",
4385
4971
  className
4386
4972
  ),
4973
+ style: dragY > 0 ? {
4974
+ transform: `translateY(${dragY}px)`,
4975
+ transition: isDragging.current ? "none" : "transform 200ms ease-out"
4976
+ } : void 0,
4387
4977
  role: "dialog",
4388
4978
  "aria-modal": "true",
4389
4979
  ...title && { "aria-labelledby": "modal-title" },
4390
4980
  children: [
4981
+ /* @__PURE__ */ jsx(
4982
+ "div",
4983
+ {
4984
+ className: "hidden max-sm:flex justify-center py-2 cursor-grab active:cursor-grabbing touch-none",
4985
+ onPointerDown: (e) => {
4986
+ if (!swipeDownToClose) return;
4987
+ dragStartY.current = e.clientY;
4988
+ isDragging.current = true;
4989
+ e.target.setPointerCapture(e.pointerId);
4990
+ },
4991
+ onPointerMove: (e) => {
4992
+ if (!isDragging.current) return;
4993
+ const dy = Math.max(0, e.clientY - dragStartY.current);
4994
+ setDragY(dy);
4995
+ },
4996
+ onPointerUp: () => {
4997
+ if (!isDragging.current) return;
4998
+ isDragging.current = false;
4999
+ if (dragY > 100) {
5000
+ handleClose();
5001
+ }
5002
+ setDragY(0);
5003
+ },
5004
+ onPointerCancel: () => {
5005
+ isDragging.current = false;
5006
+ setDragY(0);
5007
+ },
5008
+ children: /* @__PURE__ */ jsx("div", { className: "w-10 h-1 rounded-full bg-[var(--color-border)]" })
5009
+ }
5010
+ ),
4391
5011
  (title || showCloseButton) && /* @__PURE__ */ jsxs(
4392
5012
  "div",
4393
5013
  {
@@ -4660,8 +5280,8 @@ var Popover = ({
4660
5280
  onMouseEnter: handleOpen,
4661
5281
  onMouseLeave: handleClose
4662
5282
  };
4663
- const childElement = React50__default.isValidElement(children) ? children : /* @__PURE__ */ jsx("span", { children });
4664
- const triggerElement = React50__default.cloneElement(
5283
+ const childElement = React69__default.isValidElement(children) ? children : /* @__PURE__ */ jsx("span", { children });
5284
+ const triggerElement = React69__default.cloneElement(
4665
5285
  childElement,
4666
5286
  {
4667
5287
  ref: triggerRef,
@@ -5241,7 +5861,7 @@ var variantClasses = {
5241
5861
  info: "bg-[var(--color-card)] border-[length:var(--border-width)] border-[var(--color-info)]",
5242
5862
  warning: "bg-[var(--color-card)] border-[length:var(--border-width)] border-[var(--color-warning)]"
5243
5863
  };
5244
- var iconMap2 = {
5864
+ var iconMap3 = {
5245
5865
  success: CheckCircle,
5246
5866
  error: AlertCircle,
5247
5867
  info: Info,
@@ -5299,7 +5919,7 @@ var Toast = ({
5299
5919
  /* @__PURE__ */ jsx("div", { className: "flex-shrink-0 mt-0.5", children: /* @__PURE__ */ jsx(
5300
5920
  Icon,
5301
5921
  {
5302
- icon: iconMap2[variant],
5922
+ icon: iconMap3[variant],
5303
5923
  size: "md",
5304
5924
  className: iconColors[variant]
5305
5925
  }
@@ -5391,8 +6011,8 @@ var Tooltip = ({
5391
6011
  if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);
5392
6012
  };
5393
6013
  }, []);
5394
- const triggerElement = React50__default.isValidElement(children) ? children : /* @__PURE__ */ jsx("span", { children });
5395
- const trigger = React50__default.cloneElement(triggerElement, {
6014
+ const triggerElement = React69__default.isValidElement(children) ? children : /* @__PURE__ */ jsx("span", { children });
6015
+ const trigger = React69__default.cloneElement(triggerElement, {
5396
6016
  ref: triggerRef,
5397
6017
  onMouseEnter: handleMouseEnter,
5398
6018
  onMouseLeave: handleMouseLeave,
@@ -5641,7 +6261,7 @@ var WizardProgress = ({
5641
6261
  children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: steps.map((step, index) => {
5642
6262
  const isActive = index === currentStep;
5643
6263
  const isCompleted = index < currentStep;
5644
- return /* @__PURE__ */ jsxs(React50__default.Fragment, { children: [
6264
+ return /* @__PURE__ */ jsxs(React69__default.Fragment, { children: [
5645
6265
  /* @__PURE__ */ jsx(
5646
6266
  "button",
5647
6267
  {
@@ -5690,7 +6310,7 @@ var WizardProgress = ({
5690
6310
  );
5691
6311
  };
5692
6312
  WizardProgress.displayName = "WizardProgress";
5693
- function useSafeEventBus2() {
6313
+ function useSafeEventBus3() {
5694
6314
  try {
5695
6315
  return useEventBus();
5696
6316
  } catch {
@@ -5719,7 +6339,7 @@ var WizardNavigation = ({
5719
6339
  compact = false,
5720
6340
  className
5721
6341
  }) => {
5722
- const eventBus = useSafeEventBus2();
6342
+ const eventBus = useSafeEventBus3();
5723
6343
  const { t } = useTranslate();
5724
6344
  const resolvedBackLabel = backLabel ?? t("wizard.back");
5725
6345
  const resolvedNextLabel = nextLabel ?? t("wizard.next");
@@ -5771,7 +6391,7 @@ var WizardNavigation = ({
5771
6391
  );
5772
6392
  };
5773
6393
  WizardNavigation.displayName = "WizardNavigation";
5774
- var MarkdownContent = React50__default.memo(
6394
+ var MarkdownContent = React69__default.memo(
5775
6395
  ({ content, direction, className }) => {
5776
6396
  const { t: _t } = useTranslate();
5777
6397
  return /* @__PURE__ */ jsx(
@@ -5872,7 +6492,7 @@ var MarkdownContent = React50__default.memo(
5872
6492
  (prev, next) => prev.content === next.content && prev.className === next.className && prev.direction === next.direction
5873
6493
  );
5874
6494
  MarkdownContent.displayName = "MarkdownContent";
5875
- var CodeBlock = React50__default.memo(
6495
+ var CodeBlock = React69__default.memo(
5876
6496
  ({
5877
6497
  code,
5878
6498
  language = "text",
@@ -6133,8 +6753,14 @@ function CalendarGrid({
6133
6753
  onSlotClick,
6134
6754
  onDayClick,
6135
6755
  onEventClick,
6136
- className
6756
+ className,
6757
+ longPressEvent,
6758
+ longPressPayload,
6759
+ swipeLeftEvent,
6760
+ swipeRightEvent
6137
6761
  }) {
6762
+ const eventBus = useEventBus();
6763
+ const longPressTimer = useRef(null);
6138
6764
  const resolvedWeekStart = useMemo(
6139
6765
  () => weekStart ? getStartOfWeek(weekStart) : getStartOfWeek(/* @__PURE__ */ new Date()),
6140
6766
  [weekStart]
@@ -6166,6 +6792,23 @@ function CalendarGrid({
6166
6792
  ).length,
6167
6793
  [events]
6168
6794
  );
6795
+ const swipeCallbacks = useMemo(() => ({
6796
+ onSwipeLeft: swipeLeftEvent ? () => eventBus.emit(`UI:${swipeLeftEvent}`, {}) : void 0,
6797
+ onSwipeRight: swipeRightEvent ? () => eventBus.emit(`UI:${swipeRightEvent}`, {}) : void 0
6798
+ }), [swipeLeftEvent, swipeRightEvent, eventBus]);
6799
+ const swipe = useSwipeGesture(swipeCallbacks);
6800
+ const clearLongPress = useCallback(() => {
6801
+ if (longPressTimer.current) {
6802
+ clearTimeout(longPressTimer.current);
6803
+ longPressTimer.current = null;
6804
+ }
6805
+ }, []);
6806
+ const startLongPress = useCallback((day, time) => {
6807
+ if (!longPressEvent) return;
6808
+ longPressTimer.current = setTimeout(() => {
6809
+ eventBus.emit(`UI:${longPressEvent}`, { date: day.toISOString(), time, ...longPressPayload });
6810
+ }, 500);
6811
+ }, [longPressEvent, longPressPayload, eventBus]);
6169
6812
  const renderEvent = (event) => /* @__PURE__ */ jsx(
6170
6813
  Box,
6171
6814
  {
@@ -6181,70 +6824,87 @@ function CalendarGrid({
6181
6824
  },
6182
6825
  event.id
6183
6826
  );
6184
- return /* @__PURE__ */ jsx(Box, { className: cn("overflow-x-auto", className), children: /* @__PURE__ */ jsxs(Box, { className: "min-w-[800px]", children: [
6185
- /* @__PURE__ */ jsxs(Box, { className: "grid grid-cols-8 border-b border-[var(--color-border)]", children: [
6186
- /* @__PURE__ */ jsx(Box, { className: "p-2" }),
6187
- weekDays.map((day) => {
6188
- const isToday = day.toDateString() === (/* @__PURE__ */ new Date()).toDateString();
6189
- const count = eventsForDayCount(day);
6190
- return /* @__PURE__ */ jsxs(
6827
+ return /* @__PURE__ */ jsx(
6828
+ Box,
6829
+ {
6830
+ className: cn("overflow-x-auto", className),
6831
+ ...swipeLeftEvent || swipeRightEvent ? {
6832
+ onPointerDown: swipe.onPointerDown,
6833
+ onPointerMove: swipe.onPointerMove,
6834
+ onPointerUp: swipe.onPointerUp,
6835
+ onPointerCancel: swipe.onPointerCancel
6836
+ } : {},
6837
+ children: /* @__PURE__ */ jsxs(Box, { className: "min-w-[800px]", children: [
6838
+ /* @__PURE__ */ jsxs(Box, { className: "grid grid-cols-8 border-b border-[var(--color-border)]", children: [
6839
+ /* @__PURE__ */ jsx(Box, { className: "p-2" }),
6840
+ weekDays.map((day) => {
6841
+ const isToday = day.toDateString() === (/* @__PURE__ */ new Date()).toDateString();
6842
+ const count = eventsForDayCount(day);
6843
+ return /* @__PURE__ */ jsxs(
6844
+ Box,
6845
+ {
6846
+ className: "border-l border-[var(--color-border)]",
6847
+ children: [
6848
+ /* @__PURE__ */ jsx(
6849
+ DayCell,
6850
+ {
6851
+ date: day,
6852
+ isToday,
6853
+ onClick: onDayClick
6854
+ }
6855
+ ),
6856
+ count > 0 && /* @__PURE__ */ jsx(Box, { className: "text-center pb-1", children: /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", children: count }) })
6857
+ ]
6858
+ },
6859
+ day.toISOString()
6860
+ );
6861
+ })
6862
+ ] }),
6863
+ /* @__PURE__ */ jsx(Box, { className: "max-h-[500px] overflow-y-auto", children: resolvedTimeSlots.map((time) => /* @__PURE__ */ jsxs(
6191
6864
  Box,
6192
6865
  {
6193
- className: "border-l border-[var(--color-border)]",
6866
+ className: "grid grid-cols-8 border-b border-[var(--color-border)]",
6194
6867
  children: [
6195
- /* @__PURE__ */ jsx(
6196
- DayCell,
6868
+ /* @__PURE__ */ jsx(Box, { className: "p-2 text-right pr-3", children: /* @__PURE__ */ jsx(
6869
+ Typography,
6197
6870
  {
6198
- date: day,
6199
- isToday,
6200
- onClick: onDayClick
6871
+ variant: "small",
6872
+ className: "text-[var(--color-muted-foreground)]",
6873
+ children: time
6201
6874
  }
6202
- ),
6203
- count > 0 && /* @__PURE__ */ jsx(Box, { className: "text-center pb-1", children: /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", children: count }) })
6875
+ ) }),
6876
+ weekDays.map((day) => {
6877
+ const slotEvents = events.filter(
6878
+ (ev) => eventInSlot(ev, day, time)
6879
+ );
6880
+ const isToday = day.toDateString() === (/* @__PURE__ */ new Date()).toDateString();
6881
+ return /* @__PURE__ */ jsx(
6882
+ TimeSlotCell,
6883
+ {
6884
+ time,
6885
+ isOccupied: slotEvents.length > 0,
6886
+ onClick: () => handleSlotClick(day, time),
6887
+ className: cn(
6888
+ "border-l border-[var(--color-border)]",
6889
+ isToday && "bg-blue-50/30"
6890
+ ),
6891
+ ...longPressEvent ? {
6892
+ onPointerDown: () => startLongPress(day, time),
6893
+ onPointerUp: clearLongPress,
6894
+ onPointerCancel: clearLongPress
6895
+ } : {},
6896
+ children: /* @__PURE__ */ jsx(VStack, { gap: "xs", children: slotEvents.map(renderEvent) })
6897
+ },
6898
+ `${day.toISOString()}-${time}`
6899
+ );
6900
+ })
6204
6901
  ]
6205
6902
  },
6206
- day.toISOString()
6207
- );
6208
- })
6209
- ] }),
6210
- /* @__PURE__ */ jsx(Box, { className: "max-h-[500px] overflow-y-auto", children: resolvedTimeSlots.map((time) => /* @__PURE__ */ jsxs(
6211
- Box,
6212
- {
6213
- className: "grid grid-cols-8 border-b border-[var(--color-border)]",
6214
- children: [
6215
- /* @__PURE__ */ jsx(Box, { className: "p-2 text-right pr-3", children: /* @__PURE__ */ jsx(
6216
- Typography,
6217
- {
6218
- variant: "small",
6219
- className: "text-[var(--color-muted-foreground)]",
6220
- children: time
6221
- }
6222
- ) }),
6223
- weekDays.map((day) => {
6224
- const slotEvents = events.filter(
6225
- (ev) => eventInSlot(ev, day, time)
6226
- );
6227
- const isToday = day.toDateString() === (/* @__PURE__ */ new Date()).toDateString();
6228
- return /* @__PURE__ */ jsx(
6229
- TimeSlotCell,
6230
- {
6231
- time,
6232
- isOccupied: slotEvents.length > 0,
6233
- onClick: () => handleSlotClick(day, time),
6234
- className: cn(
6235
- "border-l border-[var(--color-border)]",
6236
- isToday && "bg-blue-50/30"
6237
- ),
6238
- children: /* @__PURE__ */ jsx(VStack, { gap: "xs", children: slotEvents.map(renderEvent) })
6239
- },
6240
- `${day.toISOString()}-${time}`
6241
- );
6242
- })
6243
- ]
6244
- },
6245
- time
6246
- )) })
6247
- ] }) });
6903
+ time
6904
+ )) })
6905
+ ] })
6906
+ }
6907
+ );
6248
6908
  }
6249
6909
  CalendarGrid.displayName = "CalendarGrid";
6250
6910
  var RepeatableFormSection = ({
@@ -6880,8 +7540,8 @@ function DPad({
6880
7540
  }) {
6881
7541
  const eventBus = useEventBus();
6882
7542
  const sizes = sizeMap5[size];
6883
- const [activeDirections, setActiveDirections] = React50.useState(/* @__PURE__ */ new Set());
6884
- const handlePress = React50.useCallback(
7543
+ const [activeDirections, setActiveDirections] = React69.useState(/* @__PURE__ */ new Set());
7544
+ const handlePress = React69.useCallback(
6885
7545
  (direction) => {
6886
7546
  setActiveDirections((prev) => new Set(prev).add(direction));
6887
7547
  if (directionEvent) eventBus.emit(`UI:${directionEvent}`, { direction, pressed: true });
@@ -6889,7 +7549,7 @@ function DPad({
6889
7549
  },
6890
7550
  [directionEvent, eventBus, onDirection]
6891
7551
  );
6892
- const handleRelease = React50.useCallback(
7552
+ const handleRelease = React69.useCallback(
6893
7553
  (direction) => {
6894
7554
  setActiveDirections((prev) => {
6895
7555
  const next = new Set(prev);
@@ -6947,8 +7607,8 @@ function ActionButtons({
6947
7607
  disabled
6948
7608
  }) {
6949
7609
  const eventBus = useEventBus();
6950
- const [activeButtons, setActiveButtons] = React50.useState(/* @__PURE__ */ new Set());
6951
- const handlePress = React50.useCallback(
7610
+ const [activeButtons, setActiveButtons] = React69.useState(/* @__PURE__ */ new Set());
7611
+ const handlePress = React69.useCallback(
6952
7612
  (id) => {
6953
7613
  setActiveButtons((prev) => new Set(prev).add(id));
6954
7614
  if (actionEvent) eventBus.emit(`UI:${actionEvent}`, { id, pressed: true });
@@ -6956,7 +7616,7 @@ function ActionButtons({
6956
7616
  },
6957
7617
  [actionEvent, eventBus, onAction]
6958
7618
  );
6959
- const handleRelease = React50.useCallback(
7619
+ const handleRelease = React69.useCallback(
6960
7620
  (id) => {
6961
7621
  setActiveButtons((prev) => {
6962
7622
  const next = new Set(prev);
@@ -7545,217 +8205,667 @@ function MapView({
7545
8205
  }
7546
8206
  );
7547
8207
  }
7548
- function fieldLabel(key) {
7549
- return key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
7550
- }
7551
- function statusVariant(value) {
7552
- const v = value.toLowerCase();
7553
- if (["active", "completed", "done", "approved", "published", "resolved", "open", "online"].includes(v)) return "success";
7554
- if (["pending", "in_progress", "in-progress", "review", "draft", "processing", "warning"].includes(v)) return "warning";
7555
- if (["inactive", "deleted", "rejected", "failed", "error", "blocked", "closed", "offline"].includes(v)) return "error";
7556
- if (["new", "created", "scheduled", "queued", "info"].includes(v)) return "info";
7557
- return "default";
7558
- }
7559
- function formatDate(value) {
7560
- if (!value) return "";
7561
- const d = new Date(String(value));
7562
- if (isNaN(d.getTime())) return String(value);
7563
- return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
7564
- }
7565
- function formatValue(value, format) {
7566
- if (value === void 0 || value === null) return "";
7567
- switch (format) {
7568
- case "date":
7569
- return formatDate(value);
7570
- case "currency":
7571
- return typeof value === "number" ? `$${value.toFixed(2)}` : String(value);
7572
- case "number":
7573
- return typeof value === "number" ? value.toLocaleString() : String(value);
7574
- case "percent":
7575
- return typeof value === "number" ? `${Math.round(value)}%` : String(value);
7576
- case "boolean":
7577
- return value ? "Yes" : "No";
7578
- default:
7579
- return String(value);
8208
+ function useSafeEventBus4() {
8209
+ try {
8210
+ return useEventBus();
8211
+ } catch {
8212
+ return { emit: () => {
8213
+ }, on: () => () => {
8214
+ }, once: () => {
8215
+ } };
7580
8216
  }
7581
8217
  }
7582
- var gapStyles5 = {
7583
- none: "gap-0",
7584
- sm: "gap-2",
7585
- md: "gap-4",
7586
- lg: "gap-6",
7587
- xl: "gap-8"
8218
+ var sizeStyles8 = {
8219
+ sm: {
8220
+ button: "w-7 h-7",
8221
+ text: "text-sm min-w-[2rem]",
8222
+ icon: "w-3 h-3"
8223
+ },
8224
+ md: {
8225
+ button: "w-9 h-9",
8226
+ text: "text-base min-w-[2.5rem]",
8227
+ icon: "w-4 h-4"
8228
+ },
8229
+ lg: {
8230
+ button: "w-11 h-11",
8231
+ text: "text-lg min-w-[3rem]",
8232
+ icon: "w-5 h-5"
8233
+ }
7588
8234
  };
7589
- var DataGrid = ({
7590
- entity,
7591
- fields: fieldsProp,
7592
- columns: columnsProp,
7593
- itemActions,
7594
- cols,
7595
- gap = "md",
7596
- minCardWidth = 280,
8235
+ var LONG_PRESS_DELAY = 400;
8236
+ var LONG_PRESS_INTERVAL = 100;
8237
+ var NumberStepper = ({
8238
+ value = 0,
8239
+ min,
8240
+ max,
8241
+ step = 1,
8242
+ size = "md",
8243
+ disabled = false,
8244
+ onChange,
8245
+ action,
8246
+ actionPayload,
7597
8247
  className,
7598
- isLoading = false,
7599
- error = null,
7600
- imageField
8248
+ label
7601
8249
  }) => {
7602
- const eventBus = useEventBus();
7603
- const { t } = useTranslate();
7604
- const fields = fieldsProp ?? columnsProp ?? [];
7605
- const data = Array.isArray(entity) ? entity : entity ? [entity] : [];
7606
- const titleField = fields.find((f) => f.variant === "h3" || f.variant === "h4") ?? fields[0];
7607
- const badgeFields = fields.filter((f) => f.variant === "badge" && f !== titleField);
7608
- const bodyFields = fields.filter((f) => f !== titleField && !badgeFields.includes(f));
7609
- const primaryActions = itemActions?.filter((a) => a.variant !== "danger") ?? [];
7610
- const dangerActions = itemActions?.filter((a) => a.variant === "danger") ?? [];
7611
- const handleActionClick = (action, itemData) => (e) => {
7612
- e.stopPropagation();
7613
- eventBus.emit(`UI:${action.event}`, { row: itemData });
8250
+ const eventBus = useSafeEventBus4();
8251
+ const intervalRef = useRef(null);
8252
+ const timeoutRef = useRef(null);
8253
+ const isAtMin = min !== void 0 && value <= min;
8254
+ const isAtMax = max !== void 0 && value >= max;
8255
+ const styles = sizeStyles8[size];
8256
+ const emitChange = useCallback(
8257
+ (newValue) => {
8258
+ const clamped = Math.round(newValue / step) * step;
8259
+ const finalValue = Math.max(min ?? -Infinity, Math.min(max ?? Infinity, clamped));
8260
+ onChange?.(finalValue);
8261
+ if (action) {
8262
+ eventBus.emit(`UI:${action}`, { ...actionPayload, value: finalValue });
8263
+ }
8264
+ },
8265
+ [onChange, action, actionPayload, eventBus, min, max, step]
8266
+ );
8267
+ const startLongPress = useCallback(
8268
+ (delta) => {
8269
+ timeoutRef.current = setTimeout(() => {
8270
+ intervalRef.current = setInterval(() => {
8271
+ emitChange(value + delta);
8272
+ }, LONG_PRESS_INTERVAL);
8273
+ }, LONG_PRESS_DELAY);
8274
+ },
8275
+ [emitChange, value]
8276
+ );
8277
+ const stopLongPress = useCallback(() => {
8278
+ if (timeoutRef.current) {
8279
+ clearTimeout(timeoutRef.current);
8280
+ timeoutRef.current = null;
8281
+ }
8282
+ if (intervalRef.current) {
8283
+ clearInterval(intervalRef.current);
8284
+ intervalRef.current = null;
8285
+ }
8286
+ }, []);
8287
+ useEffect(() => {
8288
+ return () => {
8289
+ stopLongPress();
8290
+ };
8291
+ }, [stopLongPress]);
8292
+ const handleDecrement = () => {
8293
+ if (!isAtMin) emitChange(value - step);
7614
8294
  };
7615
- const gridTemplateColumns = cols ? void 0 : `repeat(auto-fit, minmax(min(${minCardWidth}px, 100%), 1fr))`;
7616
- const colsClass = cols ? {
7617
- 1: "grid-cols-1",
7618
- 2: "sm:grid-cols-2",
7619
- 3: "sm:grid-cols-2 lg:grid-cols-3",
7620
- 4: "sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4",
7621
- 5: "sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5",
7622
- 6: "sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-6"
7623
- }[cols] : void 0;
7624
- if (isLoading) {
7625
- return /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: t("loading.items") || "Loading..." }) });
7626
- }
7627
- if (error) {
7628
- return /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "error", children: error.message }) });
7629
- }
7630
- if (data.length === 0) {
7631
- return /* @__PURE__ */ jsx(Box, { className: "text-center py-12", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: t("empty.noItems") || "No items found" }) });
8295
+ const handleIncrement = () => {
8296
+ if (!isAtMax) emitChange(value + step);
8297
+ };
8298
+ return /* @__PURE__ */ jsxs(
8299
+ "div",
8300
+ {
8301
+ className: cn(
8302
+ "inline-flex items-center",
8303
+ "rounded-[var(--radius-sm)]",
8304
+ "border-[length:var(--border-width)] border-[var(--color-border)]",
8305
+ "bg-[var(--color-surface)]",
8306
+ disabled && "opacity-50 cursor-not-allowed",
8307
+ className
8308
+ ),
8309
+ role: "group",
8310
+ "aria-label": label ?? "Number stepper",
8311
+ children: [
8312
+ /* @__PURE__ */ jsx(
8313
+ "button",
8314
+ {
8315
+ type: "button",
8316
+ onClick: handleDecrement,
8317
+ onMouseDown: () => !isAtMin && startLongPress(-step),
8318
+ onMouseUp: stopLongPress,
8319
+ onMouseLeave: stopLongPress,
8320
+ onTouchStart: () => !isAtMin && startLongPress(-step),
8321
+ onTouchEnd: stopLongPress,
8322
+ disabled: disabled || isAtMin,
8323
+ className: cn(
8324
+ "inline-flex items-center justify-center",
8325
+ "rounded-l-[var(--radius-sm)]",
8326
+ "text-[var(--color-foreground)]",
8327
+ "hover:bg-[var(--color-muted)]",
8328
+ "active:bg-[var(--color-muted)]",
8329
+ "transition-colors duration-100",
8330
+ "disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:bg-transparent",
8331
+ "focus:outline-none focus:ring-[length:var(--focus-ring-width)] focus:ring-[var(--color-ring)] focus:ring-inset",
8332
+ styles.button
8333
+ ),
8334
+ "aria-label": "Decrease",
8335
+ children: /* @__PURE__ */ jsx(Minus, { className: styles.icon })
8336
+ }
8337
+ ),
8338
+ /* @__PURE__ */ jsx(
8339
+ "span",
8340
+ {
8341
+ className: cn(
8342
+ "text-center font-medium tabular-nums",
8343
+ "text-[var(--color-foreground)]",
8344
+ "border-x-[length:var(--border-width)] border-[var(--color-border)]",
8345
+ "px-1 select-none",
8346
+ styles.text
8347
+ ),
8348
+ "aria-live": "polite",
8349
+ "aria-atomic": "true",
8350
+ children: value
8351
+ }
8352
+ ),
8353
+ /* @__PURE__ */ jsx(
8354
+ "button",
8355
+ {
8356
+ type: "button",
8357
+ onClick: handleIncrement,
8358
+ onMouseDown: () => !isAtMax && startLongPress(step),
8359
+ onMouseUp: stopLongPress,
8360
+ onMouseLeave: stopLongPress,
8361
+ onTouchStart: () => !isAtMax && startLongPress(step),
8362
+ onTouchEnd: stopLongPress,
8363
+ disabled: disabled || isAtMax,
8364
+ className: cn(
8365
+ "inline-flex items-center justify-center",
8366
+ "rounded-r-[var(--radius-sm)]",
8367
+ "text-[var(--color-foreground)]",
8368
+ "hover:bg-[var(--color-muted)]",
8369
+ "active:bg-[var(--color-muted)]",
8370
+ "transition-colors duration-100",
8371
+ "disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:bg-transparent",
8372
+ "focus:outline-none focus:ring-[length:var(--focus-ring-width)] focus:ring-[var(--color-ring)] focus:ring-inset",
8373
+ styles.button
8374
+ ),
8375
+ "aria-label": "Increase",
8376
+ children: /* @__PURE__ */ jsx(Plus, { className: styles.icon })
8377
+ }
8378
+ )
8379
+ ]
8380
+ }
8381
+ );
8382
+ };
8383
+ NumberStepper.displayName = "NumberStepper";
8384
+ function useSafeEventBus5() {
8385
+ try {
8386
+ return useEventBus();
8387
+ } catch {
8388
+ return { emit: () => {
8389
+ }, on: () => () => {
8390
+ }, once: () => {
8391
+ } };
7632
8392
  }
8393
+ }
8394
+ var sizeStyles9 = {
8395
+ sm: { star: "w-4 h-4", gap: "gap-0.5" },
8396
+ md: { star: "w-6 h-6", gap: "gap-1" },
8397
+ lg: { star: "w-8 h-8", gap: "gap-1.5" }
8398
+ };
8399
+ var StarRating = ({
8400
+ value = 0,
8401
+ max = 5,
8402
+ readOnly = false,
8403
+ precision = "full",
8404
+ size = "md",
8405
+ action,
8406
+ actionPayload,
8407
+ onChange,
8408
+ className,
8409
+ label
8410
+ }) => {
8411
+ const [hoverValue, setHoverValue] = useState(null);
8412
+ const eventBus = useSafeEventBus5();
8413
+ const styles = sizeStyles9[size];
8414
+ const displayValue = hoverValue ?? value;
8415
+ const emitChange = useCallback(
8416
+ (newValue) => {
8417
+ onChange?.(newValue);
8418
+ if (action) {
8419
+ eventBus.emit(`UI:${action}`, { ...actionPayload, value: newValue });
8420
+ }
8421
+ },
8422
+ [onChange, action, actionPayload, eventBus]
8423
+ );
8424
+ const handleStarClick = (starIndex, isHalf) => {
8425
+ if (readOnly) return;
8426
+ const newValue = isHalf && precision === "half" ? starIndex + 0.5 : starIndex + 1;
8427
+ emitChange(newValue);
8428
+ };
8429
+ const handleStarHover = (starIndex, isHalf) => {
8430
+ if (readOnly) return;
8431
+ const newValue = isHalf && precision === "half" ? starIndex + 0.5 : starIndex + 1;
8432
+ setHoverValue(newValue);
8433
+ };
8434
+ const handleKeyDown = (e) => {
8435
+ if (readOnly) return;
8436
+ const stepSize = precision === "half" ? 0.5 : 1;
8437
+ if (e.key === "ArrowRight" || e.key === "ArrowUp") {
8438
+ e.preventDefault();
8439
+ emitChange(Math.min(max, value + stepSize));
8440
+ } else if (e.key === "ArrowLeft" || e.key === "ArrowDown") {
8441
+ e.preventDefault();
8442
+ emitChange(Math.max(0, value - stepSize));
8443
+ }
8444
+ };
7633
8445
  return /* @__PURE__ */ jsx(
7634
- Box,
8446
+ "div",
7635
8447
  {
7636
- className: cn("grid", gapStyles5[gap], colsClass, className),
7637
- style: gridTemplateColumns ? { gridTemplateColumns } : void 0,
7638
- children: data.map((item, index) => {
7639
- const itemData = item;
7640
- const id = itemData.id || String(index);
7641
- const titleValue = getNestedValue(itemData, titleField?.name ?? "");
8448
+ className: cn(
8449
+ "inline-flex items-center",
8450
+ styles.gap,
8451
+ !readOnly && "cursor-pointer",
8452
+ className
8453
+ ),
8454
+ role: readOnly ? "img" : "slider",
8455
+ "aria-label": label ?? `Rating: ${value} out of ${max}`,
8456
+ "aria-valuemin": 0,
8457
+ "aria-valuemax": max,
8458
+ "aria-valuenow": value,
8459
+ tabIndex: readOnly ? void 0 : 0,
8460
+ onKeyDown: handleKeyDown,
8461
+ onMouseLeave: () => setHoverValue(null),
8462
+ children: Array.from({ length: max }, (_, i) => {
8463
+ const fillLevel = Math.max(0, Math.min(1, displayValue - i));
8464
+ const isFull = fillLevel >= 1;
8465
+ const isHalf = fillLevel >= 0.5 && fillLevel < 1;
7642
8466
  return /* @__PURE__ */ jsxs(
7643
- Box,
8467
+ "span",
7644
8468
  {
7645
- "data-entity-row": true,
7646
- className: cn(
7647
- "bg-[var(--color-card)] rounded-[var(--radius-lg)]",
7648
- "border border-[var(--color-border)]",
7649
- "shadow-[var(--shadow-sm)] hover:shadow-[var(--shadow-hover)]",
7650
- "hover:border-[var(--color-primary)] transition-all",
7651
- "flex flex-col"
7652
- ),
8469
+ className: "relative inline-block",
8470
+ onClick: () => handleStarClick(i, false),
8471
+ onMouseMove: (e) => {
8472
+ if (readOnly) return;
8473
+ const rect = e.currentTarget.getBoundingClientRect();
8474
+ const isLeftHalf = e.clientX - rect.left < rect.width / 2;
8475
+ handleStarHover(i, isLeftHalf);
8476
+ },
7653
8477
  children: [
7654
- imageField && (() => {
7655
- const imgUrl = getNestedValue(itemData, imageField);
7656
- if (!imgUrl || typeof imgUrl !== "string") return null;
7657
- return /* @__PURE__ */ jsx(Box, { className: "w-full aspect-video overflow-hidden rounded-t-[var(--radius-lg)]", children: /* @__PURE__ */ jsx(
7658
- "img",
7659
- {
7660
- src: imgUrl,
7661
- alt: titleValue !== void 0 ? String(titleValue) : "",
7662
- className: "w-full h-full object-cover",
7663
- loading: "lazy"
7664
- }
7665
- ) });
7666
- })(),
7667
- /* @__PURE__ */ jsx(Box, { className: "p-4 pb-0", children: /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "justify-between items-start", children: [
7668
- /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "flex-1 min-w-0", children: [
7669
- titleValue !== void 0 && titleValue !== null && /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
7670
- titleField?.icon && /* @__PURE__ */ jsx(Icon, { name: titleField.icon, size: "sm", className: "text-[var(--color-primary)] flex-shrink-0" }),
7671
- /* @__PURE__ */ jsx(
7672
- Typography,
7673
- {
7674
- variant: titleField?.variant === "h3" ? "h3" : "h4",
7675
- className: "font-semibold truncate",
7676
- children: String(titleValue)
7677
- }
7678
- )
7679
- ] }),
7680
- badgeFields.length > 0 && /* @__PURE__ */ jsx(HStack, { gap: "xs", className: "flex-wrap", children: badgeFields.map((field) => {
7681
- const val = getNestedValue(itemData, field.name);
7682
- if (val === void 0 || val === null) return null;
7683
- return /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
7684
- field.icon && /* @__PURE__ */ jsx(Icon, { name: field.icon, size: "xs" }),
7685
- /* @__PURE__ */ jsx(Badge, { variant: statusVariant(String(val)), children: String(val) })
7686
- ] }, field.name);
7687
- }) })
7688
- ] }),
7689
- dangerActions.length > 0 && /* @__PURE__ */ jsx(HStack, { gap: "xs", className: "flex-shrink-0", children: dangerActions.map((action, idx) => /* @__PURE__ */ jsxs(
7690
- Button,
7691
- {
7692
- variant: "ghost",
7693
- size: "sm",
7694
- onClick: handleActionClick(action, itemData),
7695
- "data-testid": `action-${action.event}`,
7696
- className: "text-[var(--color-error)] hover:bg-[var(--color-error)]/10 px-2",
7697
- children: [
7698
- action.icon && /* @__PURE__ */ jsx(Icon, { name: action.icon, size: "xs" }),
7699
- action.label
7700
- ]
7701
- },
7702
- idx
7703
- )) })
7704
- ] }) }),
7705
- bodyFields.length > 0 && /* @__PURE__ */ jsx(Box, { className: "px-4 py-3 flex-1", children: /* @__PURE__ */ jsx(VStack, { gap: "xs", children: bodyFields.map((field) => {
7706
- const value = getNestedValue(itemData, field.name);
7707
- if (value === void 0 || value === null || value === "") return null;
7708
- if (field.format === "boolean") {
7709
- return /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "justify-between items-center", children: [
7710
- /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
7711
- field.icon && /* @__PURE__ */ jsx(Icon, { name: field.icon, size: "xs", className: "text-[var(--color-muted-foreground)]" }),
7712
- /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: field.label ?? fieldLabel(field.name) })
7713
- ] }),
7714
- /* @__PURE__ */ jsx(Badge, { variant: value ? "success" : "neutral", children: value ? t("common.yes") || "Yes" : t("common.no") || "No" })
7715
- ] }, field.name);
8478
+ /* @__PURE__ */ jsx(
8479
+ Star,
8480
+ {
8481
+ className: cn(
8482
+ styles.star,
8483
+ "text-[var(--color-muted)]",
8484
+ "transition-colors duration-100"
8485
+ ),
8486
+ strokeWidth: 1.5
7716
8487
  }
7717
- return /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "justify-between items-center", children: [
7718
- /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
7719
- field.icon && /* @__PURE__ */ jsx(Icon, { name: field.icon, size: "xs", className: "text-[var(--color-muted-foreground)]" }),
7720
- /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: field.label ?? fieldLabel(field.name) })
7721
- ] }),
7722
- /* @__PURE__ */ jsx(
7723
- Typography,
7724
- {
7725
- variant: field.variant === "caption" ? "caption" : "small",
7726
- className: "text-right truncate max-w-[60%]",
7727
- children: formatValue(value, field.format)
7728
- }
7729
- )
7730
- ] }, field.name);
7731
- }) }) }),
7732
- primaryActions.length > 0 && /* @__PURE__ */ jsx(Box, { className: "px-4 py-3 mt-auto border-t border-[var(--color-border)]", children: /* @__PURE__ */ jsx(HStack, { gap: "sm", className: "justify-end", children: primaryActions.map((action, idx) => /* @__PURE__ */ jsxs(
7733
- Button,
8488
+ ),
8489
+ (isFull || isHalf) && /* @__PURE__ */ jsx(
8490
+ Star,
7734
8491
  {
7735
- variant: action.variant === "primary" ? "primary" : "ghost",
7736
- size: "sm",
7737
- onClick: handleActionClick(action, itemData),
7738
- "data-testid": `action-${action.event}`,
7739
- children: [
7740
- action.icon && /* @__PURE__ */ jsx(Icon, { name: action.icon, size: "xs", className: "mr-1" }),
7741
- action.label
7742
- ]
7743
- },
7744
- idx
7745
- )) }) })
8492
+ className: cn(
8493
+ styles.star,
8494
+ "absolute inset-0",
8495
+ "text-[var(--color-warning)] fill-[var(--color-warning)]",
8496
+ "transition-colors duration-100"
8497
+ ),
8498
+ strokeWidth: 1.5,
8499
+ style: isHalf ? { clipPath: "inset(0 50% 0 0)" } : void 0
8500
+ }
8501
+ ),
8502
+ !readOnly && precision === "half" && /* @__PURE__ */ jsx(
8503
+ "span",
8504
+ {
8505
+ className: "absolute inset-0 w-1/2",
8506
+ onClick: (e) => {
8507
+ e.stopPropagation();
8508
+ handleStarClick(i, true);
8509
+ }
8510
+ }
8511
+ )
7746
8512
  ]
7747
8513
  },
7748
- id
8514
+ i
7749
8515
  );
7750
8516
  })
7751
8517
  }
7752
8518
  );
7753
8519
  };
7754
- DataGrid.displayName = "DataGrid";
7755
- function fieldLabel2(key) {
8520
+ StarRating.displayName = "StarRating";
8521
+ function useSafeEventBus6() {
8522
+ try {
8523
+ return useEventBus();
8524
+ } catch {
8525
+ return { emit: () => {
8526
+ }, on: () => () => {
8527
+ }, once: () => {
8528
+ } };
8529
+ }
8530
+ }
8531
+ function formatFileSize(bytes) {
8532
+ if (bytes < 1024) return `${bytes}B`;
8533
+ if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)}KB`;
8534
+ return `${Math.round(bytes / (1024 * 1024))}MB`;
8535
+ }
8536
+ var UploadDropZone = ({
8537
+ accept,
8538
+ maxSize,
8539
+ maxFiles = 1,
8540
+ label = "Drop files here or click to browse",
8541
+ description,
8542
+ disabled = false,
8543
+ action,
8544
+ actionPayload,
8545
+ onFiles,
8546
+ className
8547
+ }) => {
8548
+ const [isDragOver, setIsDragOver] = useState(false);
8549
+ const [error, setError] = useState(null);
8550
+ const inputRef = useRef(null);
8551
+ const eventBus = useSafeEventBus6();
8552
+ const defaultDescription = [
8553
+ accept ? `Accepted: ${accept}` : null,
8554
+ maxSize ? `Max size: ${formatFileSize(maxSize)}` : null,
8555
+ maxFiles > 1 ? `Up to ${maxFiles} files` : null
8556
+ ].filter(Boolean).join(". ");
8557
+ const validateFiles = useCallback(
8558
+ (files) => {
8559
+ if (files.length > maxFiles) {
8560
+ return { valid: [], error: `Maximum ${maxFiles} file${maxFiles > 1 ? "s" : ""} allowed` };
8561
+ }
8562
+ if (accept) {
8563
+ const acceptedTypes = accept.split(",").map((t) => t.trim());
8564
+ const invalid = files.filter((file) => {
8565
+ return !acceptedTypes.some((type) => {
8566
+ if (type.endsWith("/*")) {
8567
+ return file.type.startsWith(type.replace("/*", "/"));
8568
+ }
8569
+ return file.type === type || file.name.endsWith(type);
8570
+ });
8571
+ });
8572
+ if (invalid.length > 0) {
8573
+ return { valid: [], error: `Invalid file type: ${invalid[0].name}` };
8574
+ }
8575
+ }
8576
+ if (maxSize) {
8577
+ const tooLarge = files.filter((file) => file.size > maxSize);
8578
+ if (tooLarge.length > 0) {
8579
+ return {
8580
+ valid: [],
8581
+ error: `File too large: ${tooLarge[0].name} (max ${formatFileSize(maxSize)})`
8582
+ };
8583
+ }
8584
+ }
8585
+ return { valid: files, error: null };
8586
+ },
8587
+ [accept, maxSize, maxFiles]
8588
+ );
8589
+ const handleFiles = useCallback(
8590
+ (files) => {
8591
+ const { valid, error: validationError } = validateFiles(files);
8592
+ setError(validationError);
8593
+ if (valid.length > 0) {
8594
+ onFiles?.(valid);
8595
+ if (action) {
8596
+ eventBus.emit(`UI:${action}`, {
8597
+ ...actionPayload,
8598
+ files: valid.map((f) => ({ name: f.name, size: f.size, type: f.type }))
8599
+ });
8600
+ }
8601
+ }
8602
+ },
8603
+ [validateFiles, onFiles, action, actionPayload, eventBus]
8604
+ );
8605
+ const handleDragOver = (e) => {
8606
+ e.preventDefault();
8607
+ e.stopPropagation();
8608
+ if (!disabled) setIsDragOver(true);
8609
+ };
8610
+ const handleDragLeave = (e) => {
8611
+ e.preventDefault();
8612
+ e.stopPropagation();
8613
+ setIsDragOver(false);
8614
+ };
8615
+ const handleDrop = (e) => {
8616
+ e.preventDefault();
8617
+ e.stopPropagation();
8618
+ setIsDragOver(false);
8619
+ if (disabled) return;
8620
+ const files = Array.from(e.dataTransfer.files);
8621
+ handleFiles(files);
8622
+ };
8623
+ const handleClick = () => {
8624
+ if (!disabled) inputRef.current?.click();
8625
+ };
8626
+ const handleInputChange = (e) => {
8627
+ const files = Array.from(e.target.files ?? []);
8628
+ if (files.length > 0) handleFiles(files);
8629
+ e.target.value = "";
8630
+ };
8631
+ return /* @__PURE__ */ jsxs(
8632
+ "div",
8633
+ {
8634
+ className: cn(
8635
+ "relative flex flex-col items-center justify-center",
8636
+ "p-8 rounded-[var(--radius-sm)]",
8637
+ "border-2 border-dashed",
8638
+ "transition-colors duration-150",
8639
+ "cursor-pointer",
8640
+ isDragOver ? "border-[var(--color-primary)] bg-[var(--color-primary)] bg-opacity-5" : "border-[var(--color-border)] bg-[var(--color-surface)]",
8641
+ error && "border-[var(--color-error)]",
8642
+ disabled && "opacity-50 cursor-not-allowed",
8643
+ "hover:border-[var(--color-primary)] hover:bg-[var(--color-muted)]",
8644
+ className
8645
+ ),
8646
+ onDragOver: handleDragOver,
8647
+ onDragLeave: handleDragLeave,
8648
+ onDrop: handleDrop,
8649
+ onClick: handleClick,
8650
+ role: "button",
8651
+ tabIndex: disabled ? -1 : 0,
8652
+ onKeyDown: (e) => {
8653
+ if (e.key === "Enter" || e.key === " ") {
8654
+ e.preventDefault();
8655
+ handleClick();
8656
+ }
8657
+ },
8658
+ "aria-label": label,
8659
+ children: [
8660
+ /* @__PURE__ */ jsx(
8661
+ "input",
8662
+ {
8663
+ ref: inputRef,
8664
+ type: "file",
8665
+ accept,
8666
+ multiple: maxFiles > 1,
8667
+ onChange: handleInputChange,
8668
+ className: "hidden",
8669
+ disabled,
8670
+ "aria-hidden": "true"
8671
+ }
8672
+ ),
8673
+ error ? /* @__PURE__ */ jsx(Icon, { icon: FileWarning, size: "lg", className: "text-[var(--color-error)] mb-2" }) : /* @__PURE__ */ jsx(Icon, { icon: Upload, size: "lg", className: "text-[var(--color-muted-foreground)] mb-2" }),
8674
+ /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-center font-medium mb-1", children: isDragOver ? "Drop files here" : label }),
8675
+ error ? /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "error", className: "text-center", children: error }) : /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", className: "text-center", children: description ?? defaultDescription })
8676
+ ]
8677
+ }
8678
+ );
8679
+ };
8680
+ UploadDropZone.displayName = "UploadDropZone";
8681
+ function useSafeEventBus7() {
8682
+ try {
8683
+ return useEventBus();
8684
+ } catch {
8685
+ return { emit: () => {
8686
+ }, on: () => () => {
8687
+ }, once: () => {
8688
+ } };
8689
+ }
8690
+ }
8691
+ var Lightbox = ({
8692
+ images,
8693
+ currentIndex = 0,
8694
+ isOpen = false,
8695
+ showCounter = true,
8696
+ closeAction,
8697
+ onClose,
8698
+ onIndexChange,
8699
+ className
8700
+ }) => {
8701
+ const [index, setIndex] = useState(currentIndex);
8702
+ const [touchStartX, setTouchStartX] = useState(null);
8703
+ const eventBus = useSafeEventBus7();
8704
+ useEffect(() => {
8705
+ setIndex(currentIndex);
8706
+ }, [currentIndex]);
8707
+ const handleClose = useCallback(() => {
8708
+ if (closeAction) {
8709
+ eventBus.emit(`UI:${closeAction}`, {});
8710
+ }
8711
+ onClose?.();
8712
+ }, [closeAction, eventBus, onClose]);
8713
+ const goTo = useCallback(
8714
+ (newIndex) => {
8715
+ const clamped = Math.max(0, Math.min(images.length - 1, newIndex));
8716
+ setIndex(clamped);
8717
+ onIndexChange?.(clamped);
8718
+ },
8719
+ [images.length, onIndexChange]
8720
+ );
8721
+ const goPrev = useCallback(() => goTo(index - 1), [goTo, index]);
8722
+ const goNext = useCallback(() => goTo(index + 1), [goTo, index]);
8723
+ useEffect(() => {
8724
+ if (!isOpen) return;
8725
+ const handleKeyDown = (e) => {
8726
+ switch (e.key) {
8727
+ case "Escape":
8728
+ handleClose();
8729
+ break;
8730
+ case "ArrowLeft":
8731
+ goPrev();
8732
+ break;
8733
+ case "ArrowRight":
8734
+ goNext();
8735
+ break;
8736
+ }
8737
+ };
8738
+ document.addEventListener("keydown", handleKeyDown);
8739
+ return () => document.removeEventListener("keydown", handleKeyDown);
8740
+ }, [isOpen, handleClose, goPrev, goNext]);
8741
+ useEffect(() => {
8742
+ if (isOpen) {
8743
+ document.body.style.overflow = "hidden";
8744
+ return () => {
8745
+ document.body.style.overflow = "";
8746
+ };
8747
+ }
8748
+ }, [isOpen]);
8749
+ if (!isOpen || images.length === 0) return null;
8750
+ const currentImage = images[index];
8751
+ const hasPrev = index > 0;
8752
+ const hasNext = index < images.length - 1;
8753
+ const handleTouchStart = (e) => {
8754
+ setTouchStartX(e.touches[0].clientX);
8755
+ };
8756
+ const handleTouchEnd = (e) => {
8757
+ if (touchStartX === null) return;
8758
+ const diff = e.changedTouches[0].clientX - touchStartX;
8759
+ const threshold = 50;
8760
+ if (diff > threshold && hasPrev) goPrev();
8761
+ if (diff < -threshold && hasNext) goNext();
8762
+ setTouchStartX(null);
8763
+ };
8764
+ return /* @__PURE__ */ jsxs(
8765
+ "div",
8766
+ {
8767
+ className: cn(
8768
+ "fixed inset-0 z-50 flex items-center justify-center",
8769
+ "bg-black bg-opacity-90",
8770
+ className
8771
+ ),
8772
+ onClick: handleClose,
8773
+ role: "dialog",
8774
+ "aria-modal": "true",
8775
+ "aria-label": currentImage?.alt ?? "Image viewer",
8776
+ children: [
8777
+ /* @__PURE__ */ jsx(
8778
+ "button",
8779
+ {
8780
+ type: "button",
8781
+ onClick: (e) => {
8782
+ e.stopPropagation();
8783
+ handleClose();
8784
+ },
8785
+ className: cn(
8786
+ "absolute top-4 right-4 z-10",
8787
+ "p-2 rounded-full",
8788
+ "text-white bg-black bg-opacity-50",
8789
+ "hover:bg-opacity-70 transition-opacity",
8790
+ "focus:outline-none focus:ring-2 focus:ring-white"
8791
+ ),
8792
+ "aria-label": "Close",
8793
+ children: /* @__PURE__ */ jsx(X, { className: "w-6 h-6" })
8794
+ }
8795
+ ),
8796
+ hasPrev && images.length > 1 && /* @__PURE__ */ jsx(
8797
+ "button",
8798
+ {
8799
+ type: "button",
8800
+ onClick: (e) => {
8801
+ e.stopPropagation();
8802
+ goPrev();
8803
+ },
8804
+ className: cn(
8805
+ "absolute left-4 z-10",
8806
+ "p-2 rounded-full",
8807
+ "text-white bg-black bg-opacity-50",
8808
+ "hover:bg-opacity-70 transition-opacity",
8809
+ "focus:outline-none focus:ring-2 focus:ring-white"
8810
+ ),
8811
+ "aria-label": "Previous image",
8812
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "w-8 h-8" })
8813
+ }
8814
+ ),
8815
+ /* @__PURE__ */ jsx(
8816
+ "div",
8817
+ {
8818
+ className: "flex items-center justify-center w-full h-full p-12",
8819
+ onClick: (e) => e.stopPropagation(),
8820
+ onTouchStart: handleTouchStart,
8821
+ onTouchEnd: handleTouchEnd,
8822
+ children: currentImage && /* @__PURE__ */ jsx(
8823
+ "img",
8824
+ {
8825
+ src: currentImage.src,
8826
+ alt: currentImage.alt ?? "",
8827
+ className: "max-w-full max-h-full object-contain select-none",
8828
+ draggable: false
8829
+ }
8830
+ )
8831
+ }
8832
+ ),
8833
+ hasNext && images.length > 1 && /* @__PURE__ */ jsx(
8834
+ "button",
8835
+ {
8836
+ type: "button",
8837
+ onClick: (e) => {
8838
+ e.stopPropagation();
8839
+ goNext();
8840
+ },
8841
+ className: cn(
8842
+ "absolute right-4 z-10",
8843
+ "p-2 rounded-full",
8844
+ "text-white bg-black bg-opacity-50",
8845
+ "hover:bg-opacity-70 transition-opacity",
8846
+ "focus:outline-none focus:ring-2 focus:ring-white"
8847
+ ),
8848
+ "aria-label": "Next image",
8849
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "w-8 h-8" })
8850
+ }
8851
+ ),
8852
+ /* @__PURE__ */ jsxs("div", { className: "absolute bottom-4 left-0 right-0 text-center", children: [
8853
+ showCounter && images.length > 1 && /* @__PURE__ */ jsxs("div", { className: "text-white text-sm mb-1", children: [
8854
+ index + 1,
8855
+ " of ",
8856
+ images.length
8857
+ ] }),
8858
+ currentImage?.caption && /* @__PURE__ */ jsx("div", { className: "text-white text-sm opacity-80 px-8", children: currentImage.caption })
8859
+ ] })
8860
+ ]
8861
+ }
8862
+ );
8863
+ };
8864
+ Lightbox.displayName = "Lightbox";
8865
+ function fieldLabel(key) {
7756
8866
  return key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
7757
8867
  }
7758
- function statusVariant2(value) {
8868
+ function statusVariant(value) {
7759
8869
  const v = value.toLowerCase();
7760
8870
  if (["active", "completed", "done", "approved", "published", "resolved", "open", "online"].includes(v)) return "success";
7761
8871
  if (["pending", "in_progress", "in-progress", "review", "draft", "processing", "warning"].includes(v)) return "warning";
@@ -7763,17 +8873,17 @@ function statusVariant2(value) {
7763
8873
  if (["new", "created", "scheduled", "queued", "info"].includes(v)) return "info";
7764
8874
  return "default";
7765
8875
  }
7766
- function formatDate2(value) {
8876
+ function formatDate(value) {
7767
8877
  if (!value) return "";
7768
8878
  const d = new Date(String(value));
7769
8879
  if (isNaN(d.getTime())) return String(value);
7770
8880
  return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
7771
8881
  }
7772
- function formatValue2(value, format) {
8882
+ function formatValue(value, format) {
7773
8883
  if (value === void 0 || value === null) return "";
7774
8884
  switch (format) {
7775
8885
  case "date":
7776
- return formatDate2(value);
8886
+ return formatDate(value);
7777
8887
  case "currency":
7778
8888
  return typeof value === "number" ? `$${value.toFixed(2)}` : String(value);
7779
8889
  case "number":
@@ -7786,31 +8896,81 @@ function formatValue2(value, format) {
7786
8896
  return String(value);
7787
8897
  }
7788
8898
  }
7789
- var DataList = ({
8899
+ var gapStyles5 = {
8900
+ none: "gap-0",
8901
+ sm: "gap-2",
8902
+ md: "gap-4",
8903
+ lg: "gap-6",
8904
+ xl: "gap-8"
8905
+ };
8906
+ var DataGrid = ({
7790
8907
  entity,
7791
8908
  fields: fieldsProp,
7792
8909
  columns: columnsProp,
7793
8910
  itemActions,
7794
- gap = "none",
7795
- variant = "default",
8911
+ cols,
8912
+ gap = "md",
8913
+ minCardWidth = 280,
7796
8914
  className,
7797
8915
  isLoading = false,
7798
- error = null
8916
+ error = null,
8917
+ imageField,
8918
+ selectable = false,
8919
+ selectionEvent,
8920
+ infiniteScroll,
8921
+ loadMoreEvent,
8922
+ hasMore,
8923
+ children,
8924
+ pageSize = 0
7799
8925
  }) => {
7800
8926
  const eventBus = useEventBus();
7801
8927
  const { t } = useTranslate();
8928
+ const [selectedIds, setSelectedIds] = useState(/* @__PURE__ */ new Set());
8929
+ const [visibleCount, setVisibleCount] = useState(pageSize || Infinity);
7802
8930
  const fields = fieldsProp ?? columnsProp ?? [];
7803
- const data = Array.isArray(entity) ? entity : entity ? [entity] : [];
8931
+ const allData = Array.isArray(entity) ? entity : entity ? [entity] : [];
8932
+ const data = pageSize > 0 ? allData.slice(0, visibleCount) : allData;
8933
+ const hasMoreLocal = pageSize > 0 && visibleCount < allData.length;
8934
+ const toggleSelection = useCallback((id) => {
8935
+ setSelectedIds((prev) => {
8936
+ const next = new Set(prev);
8937
+ if (next.has(id)) next.delete(id);
8938
+ else next.add(id);
8939
+ if (selectionEvent) {
8940
+ eventBus.emit(`UI:${selectionEvent}`, { selectedIds: Array.from(next) });
8941
+ }
8942
+ return next;
8943
+ });
8944
+ }, [selectionEvent, eventBus]);
8945
+ const toggleAll = useCallback(() => {
8946
+ setSelectedIds((prev) => {
8947
+ const allIds2 = data.map((item, i) => item.id || String(i));
8948
+ const allSelected2 = allIds2.length > 0 && allIds2.every((id) => prev.has(id));
8949
+ const next = allSelected2 ? /* @__PURE__ */ new Set() : new Set(allIds2);
8950
+ if (selectionEvent) {
8951
+ eventBus.emit(`UI:${selectionEvent}`, { selectedIds: Array.from(next) });
8952
+ }
8953
+ return next;
8954
+ });
8955
+ }, [data, selectionEvent, eventBus]);
7804
8956
  const titleField = fields.find((f) => f.variant === "h3" || f.variant === "h4") ?? fields[0];
7805
8957
  const badgeFields = fields.filter((f) => f.variant === "badge" && f !== titleField);
7806
- const progressFields = fields.filter((f) => f.variant === "progress");
7807
- const bodyFields = fields.filter(
7808
- (f) => f !== titleField && !badgeFields.includes(f) && !progressFields.includes(f)
7809
- );
8958
+ const bodyFields = fields.filter((f) => f !== titleField && !badgeFields.includes(f));
8959
+ const primaryActions = itemActions?.filter((a) => a.variant !== "danger") ?? [];
8960
+ const dangerActions = itemActions?.filter((a) => a.variant === "danger") ?? [];
7810
8961
  const handleActionClick = (action, itemData) => (e) => {
7811
8962
  e.stopPropagation();
7812
- eventBus.emit(`UI:${action.event}`, { row: itemData });
8963
+ eventBus.emit(`UI:${action.event}`, { id: itemData.id, row: itemData });
7813
8964
  };
8965
+ const gridTemplateColumns = cols ? void 0 : `repeat(auto-fit, minmax(min(${minCardWidth}px, 100%), 1fr))`;
8966
+ const colsClass = cols ? {
8967
+ 1: "grid-cols-1",
8968
+ 2: "sm:grid-cols-2",
8969
+ 3: "sm:grid-cols-2 lg:grid-cols-3",
8970
+ 4: "sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4",
8971
+ 5: "sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5",
8972
+ 6: "sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-6"
8973
+ }[cols] : void 0;
7814
8974
  if (isLoading) {
7815
8975
  return /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: t("loading.items") || "Loading..." }) });
7816
8976
  }
@@ -7820,126 +8980,1428 @@ var DataList = ({
7820
8980
  if (data.length === 0) {
7821
8981
  return /* @__PURE__ */ jsx(Box, { className: "text-center py-12", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: t("empty.noItems") || "No items found" }) });
7822
8982
  }
7823
- const gapClass = {
7824
- none: "",
7825
- sm: "gap-1",
7826
- md: "gap-2",
7827
- lg: "gap-4"
7828
- }[gap];
7829
- const isCard = variant === "card";
7830
- const isCompact = variant === "compact";
7831
- return /* @__PURE__ */ jsx(
8983
+ const hasRenderProp = typeof children === "function";
8984
+ const allIds = data.map((item, i) => item.id || String(i));
8985
+ const allSelected = allIds.length > 0 && allIds.every((id) => selectedIds.has(id));
8986
+ const someSelected = selectedIds.size > 0;
8987
+ return /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
8988
+ selectable && someSelected && /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "items-center px-2 py-2 bg-[var(--color-muted)] rounded-[var(--radius-sm)]", children: [
8989
+ /* @__PURE__ */ jsx(
8990
+ "input",
8991
+ {
8992
+ type: "checkbox",
8993
+ checked: allSelected,
8994
+ onChange: toggleAll,
8995
+ className: "w-4 h-4 accent-[var(--color-primary)]",
8996
+ "aria-label": "Select all"
8997
+ }
8998
+ ),
8999
+ /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "font-semibold", children: [
9000
+ selectedIds.size,
9001
+ " ",
9002
+ t("common.selected") || "selected"
9003
+ ] })
9004
+ ] }),
9005
+ /* @__PURE__ */ jsx(
9006
+ Box,
9007
+ {
9008
+ className: cn("grid", gapStyles5[gap], colsClass, className),
9009
+ style: gridTemplateColumns ? { gridTemplateColumns } : void 0,
9010
+ children: data.map((item, index) => {
9011
+ const itemData = item;
9012
+ const id = itemData.id || String(index);
9013
+ const isSelected = selectedIds.has(id);
9014
+ if (hasRenderProp) {
9015
+ return /* @__PURE__ */ jsx(
9016
+ Box,
9017
+ {
9018
+ "data-entity-row": true,
9019
+ className: cn(
9020
+ "bg-[var(--color-card)] rounded-[var(--radius-lg)]",
9021
+ "border border-[var(--color-border)]",
9022
+ "shadow-[var(--shadow-sm)] hover:shadow-[var(--shadow-hover)]",
9023
+ "hover:border-[var(--color-primary)] transition-all",
9024
+ "p-4",
9025
+ isSelected && "ring-2 ring-[var(--color-primary)] border-[var(--color-primary)]"
9026
+ ),
9027
+ children: children(itemData, index)
9028
+ },
9029
+ id
9030
+ );
9031
+ }
9032
+ const titleValue = getNestedValue(itemData, titleField?.name ?? "");
9033
+ return /* @__PURE__ */ jsxs(
9034
+ Box,
9035
+ {
9036
+ "data-entity-row": true,
9037
+ className: cn(
9038
+ "bg-[var(--color-card)] rounded-[var(--radius-lg)]",
9039
+ "border border-[var(--color-border)]",
9040
+ "shadow-[var(--shadow-sm)] hover:shadow-[var(--shadow-hover)]",
9041
+ "hover:border-[var(--color-primary)] transition-all",
9042
+ "flex flex-col",
9043
+ isSelected && "ring-2 ring-[var(--color-primary)] border-[var(--color-primary)]"
9044
+ ),
9045
+ children: [
9046
+ imageField && (() => {
9047
+ const imgUrl = getNestedValue(itemData, imageField);
9048
+ if (!imgUrl || typeof imgUrl !== "string") return null;
9049
+ return /* @__PURE__ */ jsx(Box, { className: "w-full aspect-video overflow-hidden rounded-t-[var(--radius-lg)]", children: /* @__PURE__ */ jsx(
9050
+ "img",
9051
+ {
9052
+ src: imgUrl,
9053
+ alt: titleValue !== void 0 ? String(titleValue) : "",
9054
+ className: "w-full h-full object-cover",
9055
+ loading: "lazy"
9056
+ }
9057
+ ) });
9058
+ })(),
9059
+ /* @__PURE__ */ jsx(Box, { className: "p-4 pb-0", children: /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "justify-between items-start", children: [
9060
+ selectable && /* @__PURE__ */ jsx(
9061
+ "input",
9062
+ {
9063
+ type: "checkbox",
9064
+ checked: isSelected,
9065
+ onChange: () => toggleSelection(id),
9066
+ onClick: (e) => e.stopPropagation(),
9067
+ className: "w-4 h-4 mt-1 flex-shrink-0 accent-[var(--color-primary)]",
9068
+ "aria-label": `Select ${titleValue !== void 0 ? String(titleValue) : "item"}`
9069
+ }
9070
+ ),
9071
+ /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "flex-1 min-w-0", children: [
9072
+ titleValue !== void 0 && titleValue !== null && /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
9073
+ titleField?.icon && /* @__PURE__ */ jsx(Icon, { name: titleField.icon, size: "sm", className: "text-[var(--color-primary)] flex-shrink-0" }),
9074
+ /* @__PURE__ */ jsx(
9075
+ Typography,
9076
+ {
9077
+ variant: titleField?.variant === "h3" ? "h3" : "h4",
9078
+ className: "font-semibold truncate",
9079
+ children: String(titleValue)
9080
+ }
9081
+ )
9082
+ ] }),
9083
+ badgeFields.length > 0 && /* @__PURE__ */ jsx(HStack, { gap: "xs", className: "flex-wrap", children: badgeFields.map((field) => {
9084
+ const val = getNestedValue(itemData, field.name);
9085
+ if (val === void 0 || val === null) return null;
9086
+ return /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
9087
+ field.icon && /* @__PURE__ */ jsx(Icon, { name: field.icon, size: "xs" }),
9088
+ /* @__PURE__ */ jsx(Badge, { variant: statusVariant(String(val)), children: String(val) })
9089
+ ] }, field.name);
9090
+ }) })
9091
+ ] }),
9092
+ dangerActions.length > 0 && /* @__PURE__ */ jsx(HStack, { gap: "xs", className: "flex-shrink-0", children: dangerActions.map((action, idx) => /* @__PURE__ */ jsxs(
9093
+ Button,
9094
+ {
9095
+ variant: "ghost",
9096
+ size: "sm",
9097
+ onClick: handleActionClick(action, itemData),
9098
+ "data-testid": `action-${action.event}`,
9099
+ className: "text-[var(--color-error)] hover:bg-[var(--color-error)]/10 px-2",
9100
+ children: [
9101
+ action.icon && /* @__PURE__ */ jsx(Icon, { name: action.icon, size: "xs" }),
9102
+ action.label
9103
+ ]
9104
+ },
9105
+ idx
9106
+ )) })
9107
+ ] }) }),
9108
+ bodyFields.length > 0 && /* @__PURE__ */ jsx(Box, { className: "px-4 py-3 flex-1", children: /* @__PURE__ */ jsx(VStack, { gap: "xs", children: bodyFields.map((field) => {
9109
+ const value = getNestedValue(itemData, field.name);
9110
+ if (value === void 0 || value === null || value === "") return null;
9111
+ if (field.format === "boolean") {
9112
+ return /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "justify-between items-center", children: [
9113
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
9114
+ field.icon && /* @__PURE__ */ jsx(Icon, { name: field.icon, size: "xs", className: "text-[var(--color-muted-foreground)]" }),
9115
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: field.label ?? fieldLabel(field.name) })
9116
+ ] }),
9117
+ /* @__PURE__ */ jsx(Badge, { variant: value ? "success" : "neutral", children: value ? t("common.yes") || "Yes" : t("common.no") || "No" })
9118
+ ] }, field.name);
9119
+ }
9120
+ return /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "justify-between items-center", children: [
9121
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
9122
+ field.icon && /* @__PURE__ */ jsx(Icon, { name: field.icon, size: "xs", className: "text-[var(--color-muted-foreground)]" }),
9123
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: field.label ?? fieldLabel(field.name) })
9124
+ ] }),
9125
+ /* @__PURE__ */ jsx(
9126
+ Typography,
9127
+ {
9128
+ variant: field.variant === "caption" ? "caption" : "small",
9129
+ className: "text-right truncate max-w-[60%]",
9130
+ children: formatValue(value, field.format)
9131
+ }
9132
+ )
9133
+ ] }, field.name);
9134
+ }) }) }),
9135
+ primaryActions.length > 0 && /* @__PURE__ */ jsx(Box, { className: "px-4 py-3 mt-auto border-t border-[var(--color-border)]", children: /* @__PURE__ */ jsx(HStack, { gap: "sm", className: "justify-end", children: primaryActions.map((action, idx) => /* @__PURE__ */ jsxs(
9136
+ Button,
9137
+ {
9138
+ variant: action.variant === "primary" ? "primary" : "ghost",
9139
+ size: "sm",
9140
+ onClick: handleActionClick(action, itemData),
9141
+ "data-testid": `action-${action.event}`,
9142
+ children: [
9143
+ action.icon && /* @__PURE__ */ jsx(Icon, { name: action.icon, size: "xs", className: "mr-1" }),
9144
+ action.label
9145
+ ]
9146
+ },
9147
+ idx
9148
+ )) }) })
9149
+ ]
9150
+ },
9151
+ id
9152
+ );
9153
+ })
9154
+ }
9155
+ ),
9156
+ hasMoreLocal && /* @__PURE__ */ jsx(Box, { className: "flex justify-center py-3", children: /* @__PURE__ */ jsxs(
9157
+ Button,
9158
+ {
9159
+ variant: "ghost",
9160
+ size: "sm",
9161
+ onClick: () => setVisibleCount((prev) => prev + (pageSize || 5)),
9162
+ children: [
9163
+ /* @__PURE__ */ jsx(Icon, { name: "chevron-down", size: "xs", className: "mr-1" }),
9164
+ t("common.showMore") || "Show More",
9165
+ " (",
9166
+ allData.length - visibleCount,
9167
+ " remaining)"
9168
+ ]
9169
+ }
9170
+ ) }),
9171
+ infiniteScroll && loadMoreEvent && /* @__PURE__ */ jsx(
9172
+ InfiniteScrollSentinel,
9173
+ {
9174
+ loadMoreEvent,
9175
+ isLoading,
9176
+ hasMore
9177
+ }
9178
+ )
9179
+ ] });
9180
+ };
9181
+ DataGrid.displayName = "DataGrid";
9182
+ function fieldLabel2(key) {
9183
+ return key.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
9184
+ }
9185
+ function statusVariant2(value) {
9186
+ const v = value.toLowerCase();
9187
+ if (["active", "completed", "done", "approved", "published", "resolved", "open", "online"].includes(v)) return "success";
9188
+ if (["pending", "in_progress", "in-progress", "review", "draft", "processing", "warning"].includes(v)) return "warning";
9189
+ if (["inactive", "deleted", "rejected", "failed", "error", "blocked", "closed", "offline"].includes(v)) return "error";
9190
+ if (["new", "created", "scheduled", "queued", "info"].includes(v)) return "info";
9191
+ return "default";
9192
+ }
9193
+ function formatDate2(value) {
9194
+ if (!value) return "";
9195
+ const d = new Date(String(value));
9196
+ if (isNaN(d.getTime())) return String(value);
9197
+ return d.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
9198
+ }
9199
+ function formatValue2(value, format) {
9200
+ if (value === void 0 || value === null) return "";
9201
+ switch (format) {
9202
+ case "date":
9203
+ return formatDate2(value);
9204
+ case "currency":
9205
+ return typeof value === "number" ? `$${value.toFixed(2)}` : String(value);
9206
+ case "number":
9207
+ return typeof value === "number" ? value.toLocaleString() : String(value);
9208
+ case "percent":
9209
+ return typeof value === "number" ? `${Math.round(value)}%` : String(value);
9210
+ case "boolean":
9211
+ return value ? "Yes" : "No";
9212
+ default:
9213
+ return String(value);
9214
+ }
9215
+ }
9216
+ function groupData(items, field) {
9217
+ const groups = /* @__PURE__ */ new Map();
9218
+ for (const item of items) {
9219
+ const key = String(getNestedValue(item, field) ?? "");
9220
+ const group = groups.get(key);
9221
+ if (group) group.push(item);
9222
+ else groups.set(key, [item]);
9223
+ }
9224
+ return Array.from(groups.entries()).map(([label, groupItems]) => ({ label, items: groupItems }));
9225
+ }
9226
+ var DataList = ({
9227
+ entity,
9228
+ fields: fieldsProp,
9229
+ columns: columnsProp,
9230
+ itemActions,
9231
+ gap = "none",
9232
+ variant = "default",
9233
+ groupBy,
9234
+ senderField,
9235
+ currentUser,
9236
+ className,
9237
+ isLoading = false,
9238
+ error = null,
9239
+ // Gesture props: reorderable, swipeLeftEvent, swipeRightEvent, longPressEvent
9240
+ // are consumed by the compiler to wrap items in SwipeableRow/SortableList.
9241
+ // DataList destructures them here to prevent DOM passthrough.
9242
+ reorderable: _reorderable,
9243
+ reorderEvent: _reorderEvent,
9244
+ swipeLeftEvent: _swipeLeftEvent,
9245
+ swipeLeftActions: _swipeLeftActions,
9246
+ swipeRightEvent: _swipeRightEvent,
9247
+ swipeRightActions: _swipeRightActions,
9248
+ longPressEvent: _longPressEvent,
9249
+ infiniteScroll,
9250
+ loadMoreEvent,
9251
+ hasMore,
9252
+ children,
9253
+ pageSize = 5
9254
+ }) => {
9255
+ const eventBus = useEventBus();
9256
+ const { t } = useTranslate();
9257
+ const [visibleCount, setVisibleCount] = React69__default.useState(pageSize || Infinity);
9258
+ const fields = fieldsProp ?? columnsProp ?? [];
9259
+ const allData = Array.isArray(entity) ? entity : entity ? [entity] : [];
9260
+ const data = pageSize > 0 ? allData.slice(0, visibleCount) : allData;
9261
+ const hasMoreLocal = pageSize > 0 && visibleCount < allData.length;
9262
+ const titleField = fields.find((f) => f.variant === "h3" || f.variant === "h4") ?? fields[0];
9263
+ const badgeFields = fields.filter((f) => f.variant === "badge" && f !== titleField);
9264
+ const progressFields = fields.filter((f) => f.variant === "progress");
9265
+ const bodyFields = fields.filter(
9266
+ (f) => f !== titleField && !badgeFields.includes(f) && !progressFields.includes(f)
9267
+ );
9268
+ const handleActionClick = (action, itemData) => (e) => {
9269
+ e.stopPropagation();
9270
+ eventBus.emit(`UI:${action.event}`, { id: itemData.id, row: itemData });
9271
+ };
9272
+ if (isLoading) {
9273
+ return /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: t("loading.items") || "Loading..." }) });
9274
+ }
9275
+ if (error) {
9276
+ return /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "error", children: error.message }) });
9277
+ }
9278
+ if (data.length === 0) {
9279
+ return /* @__PURE__ */ jsx(Box, { className: "text-center py-12", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: t("empty.noItems") || "No items found" }) });
9280
+ }
9281
+ const gapClass = {
9282
+ none: "",
9283
+ sm: "gap-1",
9284
+ md: "gap-2",
9285
+ lg: "gap-4"
9286
+ }[gap];
9287
+ const isCard = variant === "card";
9288
+ const isCompact = variant === "compact";
9289
+ const isMessage = variant === "message";
9290
+ if (isMessage) {
9291
+ const items2 = data.map((item) => item);
9292
+ const groups2 = groupBy ? groupData(items2, groupBy) : [{ label: "", items: items2 }];
9293
+ const contentField = titleField?.name ?? fields[0]?.name ?? "";
9294
+ return /* @__PURE__ */ jsx(VStack, { gap: "sm", className: cn("py-2", className), children: groups2.map((group, gi) => /* @__PURE__ */ jsxs(React69__default.Fragment, { children: [
9295
+ group.label && /* @__PURE__ */ jsx(Divider, { label: group.label, className: "my-2" }),
9296
+ group.items.map((itemData, index) => {
9297
+ const id = itemData.id || `${gi}-${index}`;
9298
+ const sender = senderField ? String(getNestedValue(itemData, senderField) ?? "") : "";
9299
+ const isSent = Boolean(currentUser && sender === currentUser);
9300
+ const content = getNestedValue(itemData, contentField);
9301
+ const timestampField = fields.find((f) => f.format === "date");
9302
+ const timestamp = timestampField ? getNestedValue(itemData, timestampField.name) : null;
9303
+ return /* @__PURE__ */ jsx(
9304
+ Box,
9305
+ {
9306
+ className: cn(
9307
+ "flex px-4",
9308
+ isSent ? "justify-end" : "justify-start"
9309
+ ),
9310
+ children: /* @__PURE__ */ jsxs(
9311
+ Box,
9312
+ {
9313
+ className: cn(
9314
+ "max-w-[75%] px-4 py-2",
9315
+ isSent ? "bg-[var(--color-primary)] text-[var(--color-primary-foreground)] rounded-2xl rounded-br-sm" : "bg-[var(--color-muted)] text-[var(--color-foreground)] rounded-2xl rounded-bl-sm"
9316
+ ),
9317
+ children: [
9318
+ !isSent && senderField && /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "font-semibold mb-0.5", children: sender }),
9319
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: content !== void 0 && content !== null ? String(content) : "" }),
9320
+ timestamp != null ? /* @__PURE__ */ jsx(
9321
+ Typography,
9322
+ {
9323
+ variant: "caption",
9324
+ className: cn(
9325
+ "mt-1 text-[0.65rem]",
9326
+ isSent ? "opacity-70" : "text-[var(--color-muted-foreground)]"
9327
+ ),
9328
+ children: formatDate2(timestamp)
9329
+ }
9330
+ ) : null
9331
+ ]
9332
+ }
9333
+ )
9334
+ },
9335
+ id
9336
+ );
9337
+ })
9338
+ ] }, gi)) });
9339
+ }
9340
+ const hasRenderProp = typeof children === "function";
9341
+ const items = data.map((item) => item);
9342
+ const groups = groupBy ? groupData(items, groupBy) : [{ label: "", items }];
9343
+ const renderItem = (itemData, index, isLast) => {
9344
+ if (hasRenderProp) {
9345
+ const id2 = itemData.id || String(index);
9346
+ return /* @__PURE__ */ jsxs(Box, { "data-entity-row": true, children: [
9347
+ /* @__PURE__ */ jsxs(
9348
+ Box,
9349
+ {
9350
+ className: cn(
9351
+ "group flex items-center gap-4 transition-all duration-200",
9352
+ isCompact ? "px-4 py-2" : "px-6 py-4",
9353
+ "hover:bg-[var(--color-muted)]/80",
9354
+ !isCard && !isCompact && "rounded-[var(--radius-lg)] border border-transparent hover:border-[var(--color-border)]"
9355
+ ),
9356
+ children: [
9357
+ /* @__PURE__ */ jsx(Box, { className: "flex-1 min-w-0", children: children(itemData, index) }),
9358
+ itemActions && itemActions.length > 0 && /* @__PURE__ */ jsx(
9359
+ HStack,
9360
+ {
9361
+ gap: "xs",
9362
+ className: cn(
9363
+ "flex-shrink-0 transition-opacity duration-200",
9364
+ "opacity-0 group-hover:opacity-100"
9365
+ ),
9366
+ children: itemActions.map((action, idx) => /* @__PURE__ */ jsxs(
9367
+ Button,
9368
+ {
9369
+ variant: action.variant ?? "ghost",
9370
+ size: "sm",
9371
+ onClick: handleActionClick(action, itemData),
9372
+ "data-testid": `action-${action.event}`,
9373
+ className: cn(
9374
+ action.variant === "danger" && "text-[var(--color-error)] hover:bg-[var(--color-error)]/10"
9375
+ ),
9376
+ children: [
9377
+ action.icon && /* @__PURE__ */ jsx(Icon, { name: action.icon, size: "xs", className: "mr-1" }),
9378
+ action.label
9379
+ ]
9380
+ },
9381
+ idx
9382
+ ))
9383
+ }
9384
+ )
9385
+ ]
9386
+ }
9387
+ ),
9388
+ isCard && !isLast && /* @__PURE__ */ jsx(Box, { className: "mx-6 border-b border-[var(--color-border)]/40" })
9389
+ ] }, id2);
9390
+ }
9391
+ const id = itemData.id || String(index);
9392
+ const titleValue = getNestedValue(itemData, titleField?.name ?? "");
9393
+ return /* @__PURE__ */ jsxs(Box, { "data-entity-row": true, children: [
9394
+ /* @__PURE__ */ jsxs(
9395
+ Box,
9396
+ {
9397
+ className: cn(
9398
+ "group flex items-center gap-4 transition-all duration-200",
9399
+ isCompact ? "px-4 py-2" : "px-6 py-4",
9400
+ "hover:bg-[var(--color-muted)]/80",
9401
+ !isCard && !isCompact && "rounded-[var(--radius-lg)] border border-transparent hover:border-[var(--color-border)]"
9402
+ ),
9403
+ children: [
9404
+ /* @__PURE__ */ jsxs(Box, { className: "flex-1 min-w-0", children: [
9405
+ /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "items-center", children: [
9406
+ titleField?.icon && /* @__PURE__ */ jsx(
9407
+ Icon,
9408
+ {
9409
+ name: titleField.icon,
9410
+ size: isCompact ? "xs" : "sm",
9411
+ className: "text-[var(--color-primary)] flex-shrink-0"
9412
+ }
9413
+ ),
9414
+ titleValue !== void 0 && titleValue !== null && /* @__PURE__ */ jsx(
9415
+ Typography,
9416
+ {
9417
+ variant: titleField?.variant === "h3" ? "h3" : "h4",
9418
+ className: cn("font-semibold truncate flex-1", isCompact && "text-sm"),
9419
+ children: String(titleValue)
9420
+ }
9421
+ ),
9422
+ badgeFields.map((field) => {
9423
+ const val = getNestedValue(itemData, field.name);
9424
+ if (val === void 0 || val === null) return null;
9425
+ return /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center flex-shrink-0", children: [
9426
+ field.icon && /* @__PURE__ */ jsx(Icon, { name: field.icon, size: "xs" }),
9427
+ /* @__PURE__ */ jsx(Badge, { variant: statusVariant2(String(val)), children: String(val) })
9428
+ ] }, field.name);
9429
+ })
9430
+ ] }),
9431
+ bodyFields.length > 0 && !isCompact && /* @__PURE__ */ jsx(HStack, { gap: "md", className: "mt-1.5 flex-wrap", children: bodyFields.map((field) => {
9432
+ const value = getNestedValue(itemData, field.name);
9433
+ if (value === void 0 || value === null || value === "") return null;
9434
+ return /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
9435
+ field.icon && /* @__PURE__ */ jsx(Icon, { name: field.icon, size: "xs", className: "text-[var(--color-muted-foreground)]" }),
9436
+ /* @__PURE__ */ jsxs(Typography, { variant: "caption", color: "secondary", children: [
9437
+ field.label ?? fieldLabel2(field.name),
9438
+ ":"
9439
+ ] }),
9440
+ /* @__PURE__ */ jsx(Typography, { variant: "small", children: formatValue2(value, field.format) })
9441
+ ] }, field.name);
9442
+ }) }),
9443
+ progressFields.map((field) => {
9444
+ const value = getNestedValue(itemData, field.name);
9445
+ if (typeof value !== "number") return null;
9446
+ return /* @__PURE__ */ jsxs(Box, { className: "mt-2 max-w-xs", children: [
9447
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center mb-1", children: [
9448
+ field.icon && /* @__PURE__ */ jsx(Icon, { name: field.icon, size: "xs", className: "text-[var(--color-muted-foreground)]" }),
9449
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: field.label ?? fieldLabel2(field.name) })
9450
+ ] }),
9451
+ /* @__PURE__ */ jsx(ProgressBar, { value, max: 100 })
9452
+ ] }, field.name);
9453
+ })
9454
+ ] }),
9455
+ itemActions && itemActions.length > 0 && /* @__PURE__ */ jsx(
9456
+ HStack,
9457
+ {
9458
+ gap: "xs",
9459
+ className: cn(
9460
+ "flex-shrink-0 transition-opacity duration-200",
9461
+ "opacity-0 group-hover:opacity-100"
9462
+ ),
9463
+ children: itemActions.map((action, idx) => /* @__PURE__ */ jsxs(
9464
+ Button,
9465
+ {
9466
+ variant: action.variant ?? "ghost",
9467
+ size: "sm",
9468
+ onClick: handleActionClick(action, itemData),
9469
+ "data-testid": `action-${action.event}`,
9470
+ className: cn(
9471
+ action.variant === "danger" && "text-[var(--color-error)] hover:bg-[var(--color-error)]/10"
9472
+ ),
9473
+ children: [
9474
+ action.icon && /* @__PURE__ */ jsx(Icon, { name: action.icon, size: "xs", className: "mr-1" }),
9475
+ action.label
9476
+ ]
9477
+ },
9478
+ idx
9479
+ ))
9480
+ }
9481
+ )
9482
+ ]
9483
+ }
9484
+ ),
9485
+ isCard && !isLast && /* @__PURE__ */ jsx(Box, { className: "mx-6 border-b border-[var(--color-border)]/40" })
9486
+ ] }, id);
9487
+ };
9488
+ return /* @__PURE__ */ jsxs(
9489
+ Box,
9490
+ {
9491
+ className: cn(
9492
+ isCard && "bg-[var(--color-card)] rounded-[var(--radius-xl)] border border-[var(--color-border)] shadow-[var(--shadow-lg)] overflow-hidden",
9493
+ !isCard && gapClass,
9494
+ className
9495
+ ),
9496
+ children: [
9497
+ groups.map((group, gi) => /* @__PURE__ */ jsxs(React69__default.Fragment, { children: [
9498
+ group.label && /* @__PURE__ */ jsx(Divider, { label: group.label, className: gi > 0 ? "mt-4" : "mt-0" }),
9499
+ group.items.map(
9500
+ (itemData, index) => renderItem(itemData, index, gi === groups.length - 1 && index === group.items.length - 1)
9501
+ )
9502
+ ] }, gi)),
9503
+ hasMoreLocal && /* @__PURE__ */ jsx(Box, { className: "flex justify-center py-3", children: /* @__PURE__ */ jsxs(
9504
+ Button,
9505
+ {
9506
+ variant: "ghost",
9507
+ size: "sm",
9508
+ onClick: () => setVisibleCount((prev) => prev + (pageSize || 5)),
9509
+ children: [
9510
+ /* @__PURE__ */ jsx(Icon, { name: "chevron-down", size: "xs", className: "mr-1" }),
9511
+ t("common.showMore") || "Show More",
9512
+ " (",
9513
+ allData.length - visibleCount,
9514
+ " remaining)"
9515
+ ]
9516
+ }
9517
+ ) }),
9518
+ infiniteScroll && loadMoreEvent && /* @__PURE__ */ jsx(
9519
+ InfiniteScrollSentinel,
9520
+ {
9521
+ loadMoreEvent,
9522
+ isLoading,
9523
+ hasMore
9524
+ }
9525
+ )
9526
+ ]
9527
+ }
9528
+ );
9529
+ };
9530
+ DataList.displayName = "DataList";
9531
+ function formatValue3(value, format, max) {
9532
+ if (value == null) return "0";
9533
+ const v = typeof value === "number" ? value : value;
9534
+ let formatted;
9535
+ switch (format) {
9536
+ case "currency":
9537
+ formatted = typeof v === "number" ? `$${v.toFixed(2)}` : String(v);
9538
+ break;
9539
+ case "percent":
9540
+ formatted = typeof v === "number" ? `${Math.round(v)}%` : String(v);
9541
+ break;
9542
+ case "number":
9543
+ formatted = typeof v === "number" ? v.toLocaleString() : String(v);
9544
+ break;
9545
+ default:
9546
+ formatted = String(v);
9547
+ }
9548
+ if (max != null) return `${formatted} / ${max}`;
9549
+ return formatted;
9550
+ }
9551
+ var variantColor = {
9552
+ default: "text-[var(--color-foreground)]",
9553
+ primary: "text-[var(--color-primary)]",
9554
+ success: "text-[var(--color-success)]",
9555
+ warning: "text-[var(--color-warning)]",
9556
+ error: "text-[var(--color-error)]",
9557
+ info: "text-[var(--color-info)]"
9558
+ };
9559
+ var StatDisplay = ({
9560
+ label,
9561
+ value,
9562
+ max,
9563
+ icon: iconProp,
9564
+ iconBg = "bg-[var(--color-muted)]",
9565
+ iconColor = "text-[var(--color-foreground)]",
9566
+ format,
9567
+ size = "md",
9568
+ variant = "default",
9569
+ compact = false,
9570
+ className,
9571
+ isLoading = false,
9572
+ error = null
9573
+ }) => {
9574
+ const ResolvedIcon = typeof iconProp === "string" ? resolveIcon(iconProp) : null;
9575
+ const iconSizes2 = { sm: "h-4 w-4", md: "h-5 w-5", lg: "h-6 w-6" };
9576
+ const valueSizes = { sm: "text-lg", md: "text-2xl", lg: "text-3xl" };
9577
+ const padSizes = { sm: "p-3", md: "p-4", lg: "p-6" };
9578
+ const displayValue = formatValue3(value, format, max);
9579
+ if (error) {
9580
+ return /* @__PURE__ */ jsx(Card, { className: cn(padSizes[size], className), children: /* @__PURE__ */ jsx(Typography, { variant: "small", color: "error", children: error.message }) });
9581
+ }
9582
+ if (isLoading) {
9583
+ return /* @__PURE__ */ jsx(Card, { className: cn(padSizes[size], className), children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", className: "animate-pulse", children: [
9584
+ /* @__PURE__ */ jsx(Box, { className: "h-3 bg-[var(--color-muted)] rounded w-16" }),
9585
+ /* @__PURE__ */ jsx(Box, { className: "h-6 bg-[var(--color-muted)] rounded w-12" })
9586
+ ] }) });
9587
+ }
9588
+ if (compact) {
9589
+ return /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: cn("items-center", className), children: [
9590
+ ResolvedIcon && /* @__PURE__ */ jsx(ResolvedIcon, { className: cn(iconSizes2[size], iconColor) }),
9591
+ typeof iconProp !== "string" && iconProp,
9592
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: label }),
9593
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue })
9594
+ ] });
9595
+ }
9596
+ return /* @__PURE__ */ jsx(Card, { className: cn(padSizes[size], className), children: /* @__PURE__ */ jsxs(HStack, { align: "start", justify: "between", children: [
9597
+ /* @__PURE__ */ jsxs(VStack, { gap: "none", className: "space-y-1", children: [
9598
+ /* @__PURE__ */ jsx(Typography, { variant: "overline", color: "secondary", children: label }),
9599
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue })
9600
+ ] }),
9601
+ (ResolvedIcon || typeof iconProp !== "string" && iconProp) && /* @__PURE__ */ jsx(Box, { className: cn("p-3 rounded-[var(--radius-md)]", iconBg), children: ResolvedIcon ? /* @__PURE__ */ jsx(ResolvedIcon, { className: cn(iconSizes2[size], iconColor) }) : iconProp })
9602
+ ] }) });
9603
+ };
9604
+ StatDisplay.displayName = "StatDisplay";
9605
+ var DEFAULT_THRESHOLDS = [
9606
+ { value: 30, color: "var(--color-error)" },
9607
+ { value: 70, color: "var(--color-warning)" },
9608
+ { value: 100, color: "var(--color-success)" }
9609
+ ];
9610
+ function getColorForValue(value, max, thresholds) {
9611
+ const percentage = value / max * 100;
9612
+ for (const threshold of thresholds) {
9613
+ if (percentage <= threshold.value) {
9614
+ return threshold.color;
9615
+ }
9616
+ }
9617
+ return thresholds[thresholds.length - 1]?.color ?? "var(--color-primary)";
9618
+ }
9619
+ var radialSizes = {
9620
+ sm: { size: 80, stroke: 6, fontSize: "12px" },
9621
+ md: { size: 120, stroke: 8, fontSize: "16px" },
9622
+ lg: { size: 160, stroke: 10, fontSize: "20px" }
9623
+ };
9624
+ var Meter = ({
9625
+ value,
9626
+ min = 0,
9627
+ max = 100,
9628
+ label,
9629
+ unit,
9630
+ variant = "linear",
9631
+ thresholds = DEFAULT_THRESHOLDS,
9632
+ segments = 5,
9633
+ showValue = true,
9634
+ size = "md",
9635
+ actions,
9636
+ isLoading = false,
9637
+ error,
9638
+ className
9639
+ }) => {
9640
+ const eventBus = useEventBus();
9641
+ const handleAction = useCallback(
9642
+ (action) => {
9643
+ if (action.event) {
9644
+ eventBus.emit(`UI:${action.event}`, { value });
9645
+ }
9646
+ },
9647
+ [eventBus, value]
9648
+ );
9649
+ const percentage = useMemo(() => {
9650
+ const range = max - min;
9651
+ if (range <= 0) return 0;
9652
+ return Math.min(Math.max((value - min) / range * 100, 0), 100);
9653
+ }, [value, min, max]);
9654
+ const activeColor = useMemo(
9655
+ () => getColorForValue(value, max, thresholds),
9656
+ [value, max, thresholds]
9657
+ );
9658
+ const displayValue = useMemo(() => {
9659
+ const formatted = Number.isInteger(value) ? value : value.toFixed(1);
9660
+ return unit ? `${formatted}${unit}` : `${formatted}`;
9661
+ }, [value, unit]);
9662
+ if (isLoading) {
9663
+ return /* @__PURE__ */ jsx(LoadingState, { message: "Loading meter...", className });
9664
+ }
9665
+ if (error) {
9666
+ return /* @__PURE__ */ jsx(
9667
+ ErrorState,
9668
+ {
9669
+ title: "Meter error",
9670
+ message: error.message,
9671
+ className
9672
+ }
9673
+ );
9674
+ }
9675
+ if (variant === "radial") {
9676
+ const dims = radialSizes[size];
9677
+ const radius = (dims.size - dims.stroke * 2) / 2;
9678
+ const circumference = 2 * Math.PI * radius;
9679
+ const offset = circumference - percentage / 100 * circumference;
9680
+ const center = dims.size / 2;
9681
+ return /* @__PURE__ */ jsx(Card, { className: cn("p-4", className), children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", align: "center", children: [
9682
+ label && /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", weight: "medium", children: label }),
9683
+ /* @__PURE__ */ jsxs(Box, { className: "relative inline-flex items-center justify-center", children: [
9684
+ /* @__PURE__ */ jsxs(
9685
+ "svg",
9686
+ {
9687
+ width: dims.size,
9688
+ height: dims.size,
9689
+ viewBox: `0 0 ${dims.size} ${dims.size}`,
9690
+ className: "transform -rotate-90",
9691
+ children: [
9692
+ /* @__PURE__ */ jsx(
9693
+ "circle",
9694
+ {
9695
+ cx: center,
9696
+ cy: center,
9697
+ r: radius,
9698
+ fill: "none",
9699
+ stroke: "var(--color-muted)",
9700
+ strokeWidth: dims.stroke
9701
+ }
9702
+ ),
9703
+ /* @__PURE__ */ jsx(
9704
+ "circle",
9705
+ {
9706
+ cx: center,
9707
+ cy: center,
9708
+ r: radius,
9709
+ fill: "none",
9710
+ stroke: activeColor,
9711
+ strokeWidth: dims.stroke,
9712
+ strokeDasharray: circumference,
9713
+ strokeDashoffset: offset,
9714
+ strokeLinecap: "round",
9715
+ className: "transition-all duration-500 ease-out"
9716
+ }
9717
+ )
9718
+ ]
9719
+ }
9720
+ ),
9721
+ showValue && /* @__PURE__ */ jsx(Box, { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsx(
9722
+ Typography,
9723
+ {
9724
+ variant: "h5",
9725
+ weight: "bold",
9726
+ className: "tabular-nums",
9727
+ style: { fontSize: dims.fontSize },
9728
+ children: displayValue
9729
+ }
9730
+ ) })
9731
+ ] }),
9732
+ actions && actions.length > 0 && /* @__PURE__ */ jsx(HStack, { gap: "xs", children: actions.map((action, idx) => /* @__PURE__ */ jsx(
9733
+ Badge,
9734
+ {
9735
+ variant: "default",
9736
+ className: "cursor-pointer hover:opacity-80 transition-opacity",
9737
+ onClick: () => handleAction(action),
9738
+ children: action.label
9739
+ },
9740
+ idx
9741
+ )) })
9742
+ ] }) });
9743
+ }
9744
+ if (variant === "segmented") {
9745
+ const activeSegments = Math.round(percentage / 100 * segments);
9746
+ return /* @__PURE__ */ jsx(Card, { className: cn("p-4", className), children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
9747
+ (label || showValue) && /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "center", children: [
9748
+ label && /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", weight: "medium", children: label }),
9749
+ showValue && /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "tabular-nums", children: displayValue })
9750
+ ] }),
9751
+ /* @__PURE__ */ jsx(HStack, { gap: "xs", className: "w-full", children: Array.from({ length: segments }).map((_, idx) => {
9752
+ const isActive = idx < activeSegments;
9753
+ const segColor = isActive ? getColorForValue((idx + 1) / segments * max, max, thresholds) : void 0;
9754
+ return /* @__PURE__ */ jsx(
9755
+ Box,
9756
+ {
9757
+ className: cn(
9758
+ "h-2 flex-1 rounded-[var(--radius-sm)] transition-all duration-300",
9759
+ !isActive && "bg-[var(--color-muted)]"
9760
+ ),
9761
+ style: isActive ? { backgroundColor: segColor } : void 0
9762
+ },
9763
+ idx
9764
+ );
9765
+ }) }),
9766
+ thresholds.some((t) => t.label) && /* @__PURE__ */ jsx(HStack, { justify: "between", className: "w-full", children: thresholds.map((t, idx) => /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: t.label || "" }, idx)) })
9767
+ ] }) });
9768
+ }
9769
+ return /* @__PURE__ */ jsx(Card, { className: cn("p-4", className), children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
9770
+ (label || showValue) && /* @__PURE__ */ jsxs(HStack, { justify: "between", align: "center", children: [
9771
+ label && /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", weight: "medium", children: label }),
9772
+ showValue && /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", className: "tabular-nums", children: displayValue })
9773
+ ] }),
9774
+ /* @__PURE__ */ jsx(Box, { className: "w-full h-3 bg-[var(--color-muted)] rounded-[var(--radius-full)] overflow-hidden", children: /* @__PURE__ */ jsx(
9775
+ Box,
9776
+ {
9777
+ className: "h-full rounded-[var(--radius-full)] transition-all duration-500 ease-out",
9778
+ style: {
9779
+ width: `${percentage}%`,
9780
+ backgroundColor: activeColor
9781
+ }
9782
+ }
9783
+ ) }),
9784
+ /* @__PURE__ */ jsxs(HStack, { justify: "between", className: "w-full", children: [
9785
+ /* @__PURE__ */ jsxs(Typography, { variant: "caption", color: "secondary", children: [
9786
+ min,
9787
+ unit ? ` ${unit}` : ""
9788
+ ] }),
9789
+ /* @__PURE__ */ jsxs(Typography, { variant: "caption", color: "secondary", children: [
9790
+ max,
9791
+ unit ? ` ${unit}` : ""
9792
+ ] })
9793
+ ] }),
9794
+ actions && actions.length > 0 && /* @__PURE__ */ jsx(HStack, { gap: "xs", children: actions.map((action, idx) => /* @__PURE__ */ jsx(
9795
+ Badge,
9796
+ {
9797
+ variant: "default",
9798
+ className: "cursor-pointer hover:opacity-80 transition-opacity",
9799
+ onClick: () => handleAction(action),
9800
+ children: action.label
9801
+ },
9802
+ idx
9803
+ )) })
9804
+ ] }) });
9805
+ };
9806
+ Meter.displayName = "Meter";
9807
+ function useSafeEventBus8() {
9808
+ try {
9809
+ return useEventBus();
9810
+ } catch {
9811
+ return { emit: () => {
9812
+ }, on: () => () => {
9813
+ }, once: () => {
9814
+ } };
9815
+ }
9816
+ }
9817
+ var SwipeableRow = ({
9818
+ leftActions = [],
9819
+ rightActions = [],
9820
+ threshold = 80,
9821
+ children,
9822
+ itemData,
9823
+ className
9824
+ }) => {
9825
+ const eventBus = useSafeEventBus8();
9826
+ const [revealState, setRevealState] = useState("closed");
9827
+ const [offsetX, setOffsetX] = useState(0);
9828
+ const [isSwiping, setIsSwiping] = useState(false);
9829
+ const startXRef = useRef(0);
9830
+ const startYRef = useRef(0);
9831
+ const trackingRef = useRef(false);
9832
+ const lockedAxisRef = useRef(null);
9833
+ const actionsWidth = threshold;
9834
+ const handleActionClick = useCallback(
9835
+ (action) => {
9836
+ eventBus.emit(`UI:${action.event}`, {
9837
+ ...action.eventPayload,
9838
+ row: itemData
9839
+ });
9840
+ setRevealState("closed");
9841
+ setOffsetX(0);
9842
+ },
9843
+ [eventBus, itemData]
9844
+ );
9845
+ const handlePointerDown = useCallback(
9846
+ (e) => {
9847
+ if (e.button !== 0) return;
9848
+ startXRef.current = e.clientX;
9849
+ startYRef.current = e.clientY;
9850
+ trackingRef.current = true;
9851
+ lockedAxisRef.current = null;
9852
+ setIsSwiping(false);
9853
+ e.currentTarget.setPointerCapture(e.pointerId);
9854
+ },
9855
+ []
9856
+ );
9857
+ const handlePointerMove = useCallback(
9858
+ (e) => {
9859
+ if (!trackingRef.current) return;
9860
+ const dx = e.clientX - startXRef.current;
9861
+ const dy = e.clientY - startYRef.current;
9862
+ if (lockedAxisRef.current === null && (Math.abs(dx) > 5 || Math.abs(dy) > 5)) {
9863
+ lockedAxisRef.current = Math.abs(dx) > Math.abs(dy) ? "horizontal" : "vertical";
9864
+ }
9865
+ if (lockedAxisRef.current !== "horizontal") return;
9866
+ setIsSwiping(true);
9867
+ let newOffset = dx;
9868
+ if (revealState === "left") {
9869
+ newOffset = actionsWidth + dx;
9870
+ } else if (revealState === "right") {
9871
+ newOffset = -actionsWidth + dx;
9872
+ }
9873
+ const maxLeft = leftActions.length > 0 ? actionsWidth : 0;
9874
+ const maxRight = rightActions.length > 0 ? actionsWidth : 0;
9875
+ const clamped = Math.max(-maxRight, Math.min(maxLeft, newOffset));
9876
+ setOffsetX(clamped);
9877
+ },
9878
+ [revealState, actionsWidth, leftActions.length, rightActions.length]
9879
+ );
9880
+ const handlePointerUp = useCallback(
9881
+ () => {
9882
+ trackingRef.current = false;
9883
+ if (!isSwiping) {
9884
+ if (revealState !== "closed") {
9885
+ setRevealState("closed");
9886
+ setOffsetX(0);
9887
+ }
9888
+ setIsSwiping(false);
9889
+ return;
9890
+ }
9891
+ setIsSwiping(false);
9892
+ if (offsetX > threshold / 2 && leftActions.length > 0) {
9893
+ setRevealState("right");
9894
+ setOffsetX(actionsWidth);
9895
+ } else if (offsetX < -(threshold / 2) && rightActions.length > 0) {
9896
+ setRevealState("left");
9897
+ setOffsetX(-actionsWidth);
9898
+ } else {
9899
+ setRevealState("closed");
9900
+ setOffsetX(0);
9901
+ }
9902
+ },
9903
+ [isSwiping, offsetX, threshold, leftActions.length, rightActions.length, actionsWidth, revealState]
9904
+ );
9905
+ const handlePointerCancel = useCallback(() => {
9906
+ trackingRef.current = false;
9907
+ setIsSwiping(false);
9908
+ setRevealState("closed");
9909
+ setOffsetX(0);
9910
+ }, []);
9911
+ const renderActions = (actions, side) => {
9912
+ if (actions.length === 0) return null;
9913
+ return /* @__PURE__ */ jsx(
9914
+ HStack,
9915
+ {
9916
+ gap: "xs",
9917
+ align: "stretch",
9918
+ className: cn(
9919
+ "absolute top-0 bottom-0",
9920
+ side === "left" ? "left-0" : "right-0"
9921
+ ),
9922
+ style: {
9923
+ width: `${actionsWidth}px`
9924
+ },
9925
+ children: actions.map((action, idx) => /* @__PURE__ */ jsxs(
9926
+ Button,
9927
+ {
9928
+ variant: action.variant ?? "secondary",
9929
+ size: "sm",
9930
+ icon: action.icon,
9931
+ onClick: () => handleActionClick(action),
9932
+ className: "flex-1 rounded-none h-full",
9933
+ children: [
9934
+ action.icon ? /* @__PURE__ */ jsx(Icon, { name: action.icon, size: "sm" }) : null,
9935
+ action.label
9936
+ ]
9937
+ },
9938
+ `${action.event}-${idx}`
9939
+ ))
9940
+ }
9941
+ );
9942
+ };
9943
+ return /* @__PURE__ */ jsxs(
7832
9944
  Box,
7833
9945
  {
9946
+ overflow: "hidden",
9947
+ position: "relative",
7834
9948
  className: cn(
7835
- isCard && "bg-[var(--color-card)] rounded-[var(--radius-xl)] border border-[var(--color-border)] shadow-[var(--shadow-lg)] overflow-hidden",
7836
- !isCard && gapClass,
9949
+ "touch-pan-y",
7837
9950
  className
7838
9951
  ),
7839
- children: data.map((item, index) => {
7840
- const itemData = item;
7841
- const id = itemData.id || String(index);
7842
- const titleValue = getNestedValue(itemData, titleField?.name ?? "");
7843
- const isLast = index === data.length - 1;
7844
- return /* @__PURE__ */ jsxs(Box, { "data-entity-row": true, children: [
7845
- /* @__PURE__ */ jsxs(
9952
+ children: [
9953
+ renderActions(leftActions, "left"),
9954
+ renderActions(rightActions, "right"),
9955
+ /* @__PURE__ */ jsx(
9956
+ Box,
9957
+ {
9958
+ style: {
9959
+ transform: `translateX(${offsetX}px)`,
9960
+ transition: isSwiping ? "none" : "transform 200ms ease-out"
9961
+ },
9962
+ className: cn(
9963
+ "relative bg-[var(--color-surface)]",
9964
+ "select-none"
9965
+ ),
9966
+ onPointerDown: handlePointerDown,
9967
+ onPointerMove: handlePointerMove,
9968
+ onPointerUp: handlePointerUp,
9969
+ onPointerCancel: handlePointerCancel,
9970
+ children
9971
+ }
9972
+ )
9973
+ ]
9974
+ }
9975
+ );
9976
+ };
9977
+ SwipeableRow.displayName = "SwipeableRow";
9978
+ function useSafeEventBus9() {
9979
+ try {
9980
+ return useEventBus();
9981
+ } catch {
9982
+ return { emit: () => {
9983
+ }, on: () => () => {
9984
+ }, once: () => {
9985
+ } };
9986
+ }
9987
+ }
9988
+ function SortableListInner({
9989
+ items: initialItems,
9990
+ renderItem,
9991
+ reorderEvent,
9992
+ reorderPayload,
9993
+ dragHandlePosition = "left",
9994
+ className
9995
+ }) {
9996
+ const eventBus = useSafeEventBus9();
9997
+ const handleReorder = useCallback(
9998
+ (fromIndex, toIndex, item) => {
9999
+ eventBus.emit(`UI:${reorderEvent}`, {
10000
+ fromIndex,
10001
+ toIndex,
10002
+ item,
10003
+ ...reorderPayload
10004
+ });
10005
+ },
10006
+ [eventBus, reorderEvent, reorderPayload]
10007
+ );
10008
+ const {
10009
+ items,
10010
+ dragIndex,
10011
+ dragOverIndex,
10012
+ isDragging,
10013
+ getDragHandleProps,
10014
+ getItemProps
10015
+ } = useDragReorder(initialItems, handleReorder);
10016
+ return /* @__PURE__ */ jsx(VStack, { gap: "none", className: cn("w-full", className), children: items.map((item, index) => {
10017
+ const { "aria-dropeffect": ariaDropEffect, ...itemProps } = getItemProps(index);
10018
+ const { "aria-grabbed": ariaGrabbed, ...handleRest } = getDragHandleProps(index);
10019
+ const isBeingDragged = dragIndex === index;
10020
+ const showDropIndicator = isDragging && dragOverIndex === index && dragOverIndex !== dragIndex;
10021
+ const dragHandle = /* @__PURE__ */ jsx(
10022
+ Box,
10023
+ {
10024
+ className: cn(
10025
+ "flex items-center justify-center",
10026
+ "text-[var(--color-muted-foreground)]",
10027
+ "hover:text-[var(--color-foreground)]",
10028
+ "transition-colors duration-100",
10029
+ "px-1"
10030
+ ),
10031
+ "aria-grabbed": ariaGrabbed,
10032
+ ...handleRest,
10033
+ children: /* @__PURE__ */ jsx(Icon, { name: "grip-vertical", size: "sm" })
10034
+ }
10035
+ );
10036
+ return /* @__PURE__ */ jsxs(
10037
+ Box,
10038
+ {
10039
+ "aria-dropeffect": ariaDropEffect,
10040
+ ...itemProps,
10041
+ children: [
10042
+ showDropIndicator && /* @__PURE__ */ jsx(
7846
10043
  Box,
7847
10044
  {
10045
+ className: "h-0.5 bg-[var(--color-primary)] rounded-full",
10046
+ style: { margin: "0 8px" }
10047
+ }
10048
+ ),
10049
+ /* @__PURE__ */ jsxs(
10050
+ HStack,
10051
+ {
10052
+ gap: "sm",
10053
+ align: "center",
7848
10054
  className: cn(
7849
- "group flex items-center gap-4 transition-all duration-200",
7850
- isCompact ? "px-4 py-2" : "px-6 py-4",
7851
- "hover:bg-[var(--color-muted)]/80",
7852
- !isCard && !isCompact && "rounded-[var(--radius-lg)] border border-transparent hover:border-[var(--color-border)]"
10055
+ "transition-opacity duration-150",
10056
+ isBeingDragged && "opacity-50"
7853
10057
  ),
7854
10058
  children: [
7855
- /* @__PURE__ */ jsxs(Box, { className: "flex-1 min-w-0", children: [
7856
- /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "items-center", children: [
7857
- titleField?.icon && /* @__PURE__ */ jsx(
7858
- Icon,
7859
- {
7860
- name: titleField.icon,
7861
- size: isCompact ? "xs" : "sm",
7862
- className: "text-[var(--color-primary)] flex-shrink-0"
7863
- }
7864
- ),
7865
- titleValue !== void 0 && titleValue !== null && /* @__PURE__ */ jsx(
7866
- Typography,
7867
- {
7868
- variant: titleField?.variant === "h3" ? "h3" : "h4",
7869
- className: cn("font-semibold truncate flex-1", isCompact && "text-sm"),
7870
- children: String(titleValue)
7871
- }
7872
- ),
7873
- badgeFields.map((field) => {
7874
- const val = getNestedValue(itemData, field.name);
7875
- if (val === void 0 || val === null) return null;
7876
- return /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center flex-shrink-0", children: [
7877
- field.icon && /* @__PURE__ */ jsx(Icon, { name: field.icon, size: "xs" }),
7878
- /* @__PURE__ */ jsx(Badge, { variant: statusVariant2(String(val)), children: String(val) })
7879
- ] }, field.name);
7880
- })
7881
- ] }),
7882
- bodyFields.length > 0 && !isCompact && /* @__PURE__ */ jsx(HStack, { gap: "md", className: "mt-1.5 flex-wrap", children: bodyFields.map((field) => {
7883
- const value = getNestedValue(itemData, field.name);
7884
- if (value === void 0 || value === null || value === "") return null;
7885
- return /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
7886
- field.icon && /* @__PURE__ */ jsx(Icon, { name: field.icon, size: "xs", className: "text-[var(--color-muted-foreground)]" }),
7887
- /* @__PURE__ */ jsxs(Typography, { variant: "caption", color: "secondary", children: [
7888
- field.label ?? fieldLabel2(field.name),
7889
- ":"
7890
- ] }),
7891
- /* @__PURE__ */ jsx(Typography, { variant: "small", children: formatValue2(value, field.format) })
7892
- ] }, field.name);
7893
- }) }),
7894
- progressFields.map((field) => {
7895
- const value = getNestedValue(itemData, field.name);
7896
- if (typeof value !== "number") return null;
7897
- return /* @__PURE__ */ jsxs(Box, { className: "mt-2 max-w-xs", children: [
7898
- /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center mb-1", children: [
7899
- field.icon && /* @__PURE__ */ jsx(Icon, { name: field.icon, size: "xs", className: "text-[var(--color-muted-foreground)]" }),
7900
- /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: field.label ?? fieldLabel2(field.name) })
7901
- ] }),
7902
- /* @__PURE__ */ jsx(ProgressBar, { value, max: 100 })
7903
- ] }, field.name);
7904
- })
7905
- ] }),
7906
- itemActions && itemActions.length > 0 && /* @__PURE__ */ jsx(
7907
- HStack,
7908
- {
7909
- gap: "xs",
7910
- className: cn(
7911
- "flex-shrink-0 transition-opacity duration-200",
7912
- "opacity-0 group-hover:opacity-100"
7913
- ),
7914
- children: itemActions.map((action, idx) => /* @__PURE__ */ jsxs(
7915
- Button,
7916
- {
7917
- variant: action.variant ?? "ghost",
7918
- size: "sm",
7919
- onClick: handleActionClick(action, itemData),
7920
- "data-testid": `action-${action.event}`,
7921
- className: cn(
7922
- action.variant === "danger" && "text-[var(--color-error)] hover:bg-[var(--color-error)]/10"
7923
- ),
7924
- children: [
7925
- action.icon && /* @__PURE__ */ jsx(Icon, { name: action.icon, size: "xs", className: "mr-1" }),
7926
- action.label
7927
- ]
7928
- },
7929
- idx
7930
- ))
7931
- }
7932
- )
10059
+ dragHandlePosition === "left" && dragHandle,
10060
+ /* @__PURE__ */ jsx(Box, { className: "flex-1 min-w-0", children: renderItem(item, index) }),
10061
+ dragHandlePosition === "right" && dragHandle
7933
10062
  ]
7934
10063
  }
7935
- ),
7936
- isCard && !isLast && /* @__PURE__ */ jsx(Box, { className: "mx-6 border-b border-[var(--color-border)]/40" })
7937
- ] }, id);
7938
- })
7939
- }
10064
+ )
10065
+ ]
10066
+ },
10067
+ index
10068
+ );
10069
+ }) });
10070
+ }
10071
+ var SortableList = SortableListInner;
10072
+ SortableList.displayName = "SortableList";
10073
+ function useSafeEventBus10() {
10074
+ try {
10075
+ return useEventBus();
10076
+ } catch {
10077
+ return { emit: () => {
10078
+ }, on: () => () => {
10079
+ }, once: () => {
10080
+ } };
10081
+ }
10082
+ }
10083
+ var Carousel = ({
10084
+ items,
10085
+ renderItem,
10086
+ autoPlay = false,
10087
+ autoPlayInterval = 5e3,
10088
+ showDots = true,
10089
+ showArrows = true,
10090
+ loop = false,
10091
+ slideChangeEvent,
10092
+ slideChangePayload,
10093
+ className
10094
+ }) => {
10095
+ const [activeIndex, setActiveIndex] = useState(0);
10096
+ const scrollRef = useRef(null);
10097
+ const autoPlayRef = useRef(null);
10098
+ const eventBus = useSafeEventBus10();
10099
+ const totalSlides = items.length;
10100
+ const emitSlideChange = useCallback(
10101
+ (newIndex) => {
10102
+ if (slideChangeEvent) {
10103
+ eventBus.emit(`UI:${slideChangeEvent}`, {
10104
+ index: newIndex,
10105
+ ...slideChangePayload
10106
+ });
10107
+ }
10108
+ },
10109
+ [slideChangeEvent, slideChangePayload, eventBus]
10110
+ );
10111
+ const scrollToSlide = useCallback(
10112
+ (index) => {
10113
+ const container = scrollRef.current;
10114
+ if (!container) return;
10115
+ const slideWidth = container.offsetWidth;
10116
+ container.scrollTo({
10117
+ left: slideWidth * index,
10118
+ behavior: "smooth"
10119
+ });
10120
+ },
10121
+ []
10122
+ );
10123
+ const goToSlide = useCallback(
10124
+ (index) => {
10125
+ let targetIndex = index;
10126
+ if (loop) {
10127
+ if (targetIndex < 0) targetIndex = totalSlides - 1;
10128
+ if (targetIndex >= totalSlides) targetIndex = 0;
10129
+ } else {
10130
+ targetIndex = Math.max(0, Math.min(totalSlides - 1, targetIndex));
10131
+ }
10132
+ setActiveIndex(targetIndex);
10133
+ scrollToSlide(targetIndex);
10134
+ emitSlideChange(targetIndex);
10135
+ },
10136
+ [loop, totalSlides, scrollToSlide, emitSlideChange]
10137
+ );
10138
+ const goNext = useCallback(() => {
10139
+ goToSlide(activeIndex + 1);
10140
+ }, [goToSlide, activeIndex]);
10141
+ const goPrev = useCallback(() => {
10142
+ goToSlide(activeIndex - 1);
10143
+ }, [goToSlide, activeIndex]);
10144
+ const swipeHandlers = useSwipeGesture(
10145
+ {
10146
+ onSwipeLeft: goNext,
10147
+ onSwipeRight: goPrev
10148
+ },
10149
+ { threshold: 40 }
7940
10150
  );
10151
+ useEffect(() => {
10152
+ if (!autoPlay || totalSlides <= 1) return;
10153
+ autoPlayRef.current = setInterval(() => {
10154
+ setActiveIndex((prev) => {
10155
+ const next = loop ? (prev + 1) % totalSlides : Math.min(prev + 1, totalSlides - 1);
10156
+ setTimeout(() => {
10157
+ scrollToSlide(next);
10158
+ emitSlideChange(next);
10159
+ }, 0);
10160
+ return next;
10161
+ });
10162
+ }, autoPlayInterval);
10163
+ return () => {
10164
+ if (autoPlayRef.current) {
10165
+ clearInterval(autoPlayRef.current);
10166
+ autoPlayRef.current = null;
10167
+ }
10168
+ };
10169
+ }, [autoPlay, autoPlayInterval, loop, totalSlides, scrollToSlide, emitSlideChange]);
10170
+ const handleScroll = useCallback(() => {
10171
+ const container = scrollRef.current;
10172
+ if (!container) return;
10173
+ const slideWidth = container.offsetWidth;
10174
+ if (slideWidth === 0) return;
10175
+ const newIndex = Math.round(container.scrollLeft / slideWidth);
10176
+ setActiveIndex((prev) => {
10177
+ if (prev !== newIndex && newIndex >= 0 && newIndex < totalSlides) {
10178
+ return newIndex;
10179
+ }
10180
+ return prev;
10181
+ });
10182
+ }, [totalSlides]);
10183
+ const hasPrev = loop || activeIndex > 0;
10184
+ const hasNext = loop || activeIndex < totalSlides - 1;
10185
+ if (totalSlides === 0) return null;
10186
+ return /* @__PURE__ */ jsxs(Box, { position: "relative", overflow: "hidden", className: cn("w-full", className), children: [
10187
+ /* @__PURE__ */ jsx(
10188
+ Box,
10189
+ {
10190
+ ref: scrollRef,
10191
+ display: "flex",
10192
+ overflow: "auto",
10193
+ className: cn(
10194
+ "w-full",
10195
+ "scroll-smooth",
10196
+ "scrollbar-none",
10197
+ "[scroll-snap-type:x_mandatory]",
10198
+ "[&::-webkit-scrollbar]:hidden",
10199
+ "[-ms-overflow-style:none]"
10200
+ ),
10201
+ style: {
10202
+ scrollSnapType: "x mandatory",
10203
+ WebkitOverflowScrolling: "touch"
10204
+ },
10205
+ onScroll: handleScroll,
10206
+ onPointerDown: swipeHandlers.onPointerDown,
10207
+ onPointerMove: swipeHandlers.onPointerMove,
10208
+ onPointerUp: swipeHandlers.onPointerUp,
10209
+ onPointerCancel: swipeHandlers.onPointerCancel,
10210
+ children: items.map((item, index) => /* @__PURE__ */ jsx(
10211
+ Box,
10212
+ {
10213
+ className: cn(
10214
+ "flex-shrink-0 w-full",
10215
+ "[scroll-snap-align:start]"
10216
+ ),
10217
+ style: {
10218
+ scrollSnapAlign: "start"
10219
+ },
10220
+ children: renderItem(item, index)
10221
+ },
10222
+ index
10223
+ ))
10224
+ }
10225
+ ),
10226
+ showArrows && hasPrev && totalSlides > 1 && /* @__PURE__ */ jsx(
10227
+ Box,
10228
+ {
10229
+ position: "absolute",
10230
+ className: "left-2 top-1/2 -translate-y-1/2 z-10",
10231
+ children: /* @__PURE__ */ jsx(
10232
+ Button,
10233
+ {
10234
+ variant: "ghost",
10235
+ size: "sm",
10236
+ onClick: goPrev,
10237
+ "aria-label": "Previous slide",
10238
+ className: cn(
10239
+ "rounded-full",
10240
+ "bg-[var(--color-surface)]/80",
10241
+ "backdrop-blur-sm",
10242
+ "shadow-[var(--shadow-sm)]",
10243
+ "hover:bg-[var(--color-surface)]"
10244
+ ),
10245
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "w-5 h-5" })
10246
+ }
10247
+ )
10248
+ }
10249
+ ),
10250
+ showArrows && hasNext && totalSlides > 1 && /* @__PURE__ */ jsx(
10251
+ Box,
10252
+ {
10253
+ position: "absolute",
10254
+ className: "right-2 top-1/2 -translate-y-1/2 z-10",
10255
+ children: /* @__PURE__ */ jsx(
10256
+ Button,
10257
+ {
10258
+ variant: "ghost",
10259
+ size: "sm",
10260
+ onClick: goNext,
10261
+ "aria-label": "Next slide",
10262
+ className: cn(
10263
+ "rounded-full",
10264
+ "bg-[var(--color-surface)]/80",
10265
+ "backdrop-blur-sm",
10266
+ "shadow-[var(--shadow-sm)]",
10267
+ "hover:bg-[var(--color-surface)]"
10268
+ ),
10269
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "w-5 h-5" })
10270
+ }
10271
+ )
10272
+ }
10273
+ ),
10274
+ showDots && totalSlides > 1 && /* @__PURE__ */ jsx(
10275
+ Box,
10276
+ {
10277
+ position: "absolute",
10278
+ className: "bottom-3 left-0 right-0 z-10",
10279
+ children: /* @__PURE__ */ jsx(HStack, { gap: "xs", align: "center", justify: "center", children: items.map((_, index) => {
10280
+ const isActive = index === activeIndex;
10281
+ return /* @__PURE__ */ jsx(
10282
+ Box,
10283
+ {
10284
+ className: cn(
10285
+ "rounded-full cursor-pointer transition-all duration-200"
10286
+ ),
10287
+ style: {
10288
+ width: isActive ? 10 : 8,
10289
+ height: isActive ? 10 : 8,
10290
+ backgroundColor: isActive ? "var(--color-primary)" : "var(--color-muted, #d4d4d8)",
10291
+ opacity: isActive ? 1 : 0.6
10292
+ },
10293
+ onClick: () => goToSlide(index),
10294
+ role: "button",
10295
+ "aria-label": `Go to slide ${index + 1}`
10296
+ },
10297
+ index
10298
+ );
10299
+ }) })
10300
+ }
10301
+ )
10302
+ ] });
7941
10303
  };
7942
- DataList.displayName = "DataList";
10304
+ Carousel.displayName = "Carousel";
10305
+ function useSafeEventBus11() {
10306
+ try {
10307
+ return useEventBus();
10308
+ } catch {
10309
+ return { emit: () => {
10310
+ }, on: () => () => {
10311
+ }, once: () => {
10312
+ } };
10313
+ }
10314
+ }
10315
+ var SAFETY_TIMEOUT_MS = 5e3;
10316
+ var PullToRefresh = ({
10317
+ refreshEvent,
10318
+ refreshPayload,
10319
+ threshold = 60,
10320
+ children,
10321
+ className
10322
+ }) => {
10323
+ const eventBus = useSafeEventBus11();
10324
+ const safetyTimerRef = useRef(null);
10325
+ const handleRefresh = useCallback(() => {
10326
+ eventBus.emit(`UI:${refreshEvent}`, refreshPayload ?? {});
10327
+ }, [eventBus, refreshEvent, refreshPayload]);
10328
+ const { pullDistance, isPulling, isRefreshing, containerProps, endRefresh } = usePullToRefresh(handleRefresh, { threshold });
10329
+ useEffect(() => {
10330
+ if (isRefreshing) {
10331
+ safetyTimerRef.current = setTimeout(() => {
10332
+ endRefresh();
10333
+ }, SAFETY_TIMEOUT_MS);
10334
+ return () => {
10335
+ if (safetyTimerRef.current) {
10336
+ clearTimeout(safetyTimerRef.current);
10337
+ safetyTimerRef.current = null;
10338
+ }
10339
+ };
10340
+ }
10341
+ }, [isRefreshing, endRefresh]);
10342
+ useEffect(() => {
10343
+ return () => {
10344
+ if (safetyTimerRef.current) {
10345
+ clearTimeout(safetyTimerRef.current);
10346
+ }
10347
+ };
10348
+ }, []);
10349
+ const pullProgress = Math.min(pullDistance / threshold, 1);
10350
+ const showIndicator = pullDistance > 0 || isRefreshing;
10351
+ return /* @__PURE__ */ jsxs(Box, { position: "relative", overflow: "hidden", className: cn("w-full", className), children: [
10352
+ /* @__PURE__ */ jsx(
10353
+ Box,
10354
+ {
10355
+ position: "absolute",
10356
+ display: "flex",
10357
+ className: cn(
10358
+ "top-0 left-0 right-0 z-10",
10359
+ "items-center justify-center",
10360
+ "transition-opacity duration-200",
10361
+ showIndicator ? "opacity-100" : "opacity-0"
10362
+ ),
10363
+ style: {
10364
+ height: threshold,
10365
+ transform: `translateY(${showIndicator ? 0 : -threshold}px)`,
10366
+ pointerEvents: "none"
10367
+ },
10368
+ children: /* @__PURE__ */ jsx(
10369
+ Box,
10370
+ {
10371
+ className: "transition-transform duration-150",
10372
+ style: {
10373
+ transform: `scale(${isRefreshing ? 1 : pullProgress})`,
10374
+ opacity: isRefreshing ? 1 : pullProgress
10375
+ },
10376
+ children: /* @__PURE__ */ jsx(
10377
+ Spinner,
10378
+ {
10379
+ size: pullProgress >= 0.5 ? "md" : "sm",
10380
+ className: cn(
10381
+ "text-[var(--color-primary)]",
10382
+ isRefreshing && "animate-spin"
10383
+ )
10384
+ }
10385
+ )
10386
+ }
10387
+ )
10388
+ }
10389
+ ),
10390
+ /* @__PURE__ */ jsx(
10391
+ Box,
10392
+ {
10393
+ fullWidth: true,
10394
+ className: cn(
10395
+ "min-h-0",
10396
+ isPulling && "select-none"
10397
+ ),
10398
+ ...containerProps,
10399
+ children
10400
+ }
10401
+ )
10402
+ ] });
10403
+ };
10404
+ PullToRefresh.displayName = "PullToRefresh";
7943
10405
 
7944
10406
  // components/organisms/types.ts
7945
10407
  var EntityDisplayEvents = {
@@ -8368,6 +10830,7 @@ var StatCard = ({
8368
10830
  // Schema-based props
8369
10831
  entity,
8370
10832
  metrics,
10833
+ sparklineData,
8371
10834
  isLoading: externalLoading,
8372
10835
  error: externalError
8373
10836
  }) => {
@@ -8375,7 +10838,7 @@ var StatCard = ({
8375
10838
  const labelToUse = propLabel ?? propTitle;
8376
10839
  const eventBus = useEventBus();
8377
10840
  const { t } = useTranslate();
8378
- const handleActionClick = React50__default.useCallback(() => {
10841
+ const handleActionClick = React69__default.useCallback(() => {
8379
10842
  if (action?.event) {
8380
10843
  eventBus.emit(`UI:${action.event}`, {});
8381
10844
  }
@@ -8386,7 +10849,7 @@ var StatCard = ({
8386
10849
  const data = Array.isArray(entity) ? entity : entity ? [entity] : [];
8387
10850
  const isLoading = externalLoading ?? false;
8388
10851
  const error = externalError;
8389
- const computeMetricValue = React50__default.useCallback(
10852
+ const computeMetricValue = React69__default.useCallback(
8390
10853
  (metric, items) => {
8391
10854
  if (metric.value !== void 0) {
8392
10855
  return metric.value;
@@ -8425,7 +10888,7 @@ var StatCard = ({
8425
10888
  },
8426
10889
  []
8427
10890
  );
8428
- const schemaStats = React50__default.useMemo(() => {
10891
+ const schemaStats = React69__default.useMemo(() => {
8429
10892
  if (!metrics || metrics.length === 0) return null;
8430
10893
  return metrics.map((metric) => ({
8431
10894
  label: metric.label,
@@ -8433,7 +10896,7 @@ var StatCard = ({
8433
10896
  format: metric.format
8434
10897
  }));
8435
10898
  }, [metrics, data, computeMetricValue]);
8436
- const calculatedTrend = React50__default.useMemo(() => {
10899
+ const calculatedTrend = React69__default.useMemo(() => {
8437
10900
  if (manualTrend !== void 0) return manualTrend;
8438
10901
  if (previousValue === void 0 || currentValue === void 0)
8439
10902
  return void 0;
@@ -8513,7 +10976,35 @@ var StatCard = ({
8513
10976
  ] }),
8514
10977
  subtitle && !calculatedTrend && /* @__PURE__ */ jsx(Typography, { variant: "small", color: "secondary", children: subtitle })
8515
10978
  ] }),
8516
- Icon2 && /* @__PURE__ */ jsx(Box, { className: cn("p-3", iconBg), children: /* @__PURE__ */ jsx(Icon2, { className: cn("h-6 w-6", iconColor) }) })
10979
+ /* @__PURE__ */ jsxs(VStack, { gap: "xs", align: "end", children: [
10980
+ Icon2 && /* @__PURE__ */ jsx(Box, { className: cn("p-3", iconBg), children: /* @__PURE__ */ jsx(Icon2, { className: cn("h-6 w-6", iconColor) }) }),
10981
+ sparklineData && sparklineData.length > 1 && (() => {
10982
+ const w = 80;
10983
+ const h = 32;
10984
+ const pad = 2;
10985
+ const min = Math.min(...sparklineData);
10986
+ const max = Math.max(...sparklineData);
10987
+ const range = max - min || 1;
10988
+ const points = sparklineData.map((v, i) => {
10989
+ const x = pad + i / (sparklineData.length - 1) * (w - pad * 2);
10990
+ const y = pad + (1 - (v - min) / range) * (h - pad * 2);
10991
+ return `${x},${y}`;
10992
+ }).join(" ");
10993
+ const trending = sparklineData[sparklineData.length - 1] >= sparklineData[0];
10994
+ const strokeColor = trending ? "var(--color-success)" : "var(--color-error)";
10995
+ return /* @__PURE__ */ jsx("svg", { width: w, height: h, viewBox: `0 0 ${w} ${h}`, className: "flex-shrink-0", children: /* @__PURE__ */ jsx(
10996
+ "polyline",
10997
+ {
10998
+ fill: "none",
10999
+ stroke: strokeColor,
11000
+ strokeWidth: "2",
11001
+ strokeLinecap: "round",
11002
+ strokeLinejoin: "round",
11003
+ points
11004
+ }
11005
+ ) });
11006
+ })()
11007
+ ] })
8517
11008
  ] }),
8518
11009
  action && /* @__PURE__ */ jsxs(
8519
11010
  Button,
@@ -8561,7 +11052,7 @@ var PageHeader = ({
8561
11052
  action.onClick();
8562
11053
  }
8563
11054
  };
8564
- const statusColors = {
11055
+ const statusColors2 = {
8565
11056
  default: "bg-[var(--color-muted)] text-[var(--color-foreground)]",
8566
11057
  success: "bg-[var(--color-success)]/10 text-[var(--color-success)]",
8567
11058
  warning: "bg-[var(--color-warning)]/10 text-[var(--color-warning)]",
@@ -8569,7 +11060,7 @@ var PageHeader = ({
8569
11060
  info: "bg-[var(--color-info)]/10 text-[var(--color-info)]"
8570
11061
  };
8571
11062
  return /* @__PURE__ */ jsxs(Box, { className: cn("mb-6", className), children: [
8572
- breadcrumbs && breadcrumbs.length > 0 && /* @__PURE__ */ jsx(Box, { as: "nav", className: "mb-4", children: /* @__PURE__ */ jsx(Box, { as: "ol", className: "flex items-center gap-2 text-sm", children: breadcrumbs.map((crumb, idx) => /* @__PURE__ */ jsxs(React50__default.Fragment, { children: [
11063
+ breadcrumbs && breadcrumbs.length > 0 && /* @__PURE__ */ jsx(Box, { as: "nav", className: "mb-4", children: /* @__PURE__ */ jsx(Box, { as: "ol", className: "flex items-center gap-2 text-sm", children: breadcrumbs.map((crumb, idx) => /* @__PURE__ */ jsxs(React69__default.Fragment, { children: [
8573
11064
  idx > 0 && /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: "/" }),
8574
11065
  crumb.href ? /* @__PURE__ */ jsx(
8575
11066
  "a",
@@ -8600,7 +11091,7 @@ var PageHeader = ({
8600
11091
  variant: "small",
8601
11092
  className: cn(
8602
11093
  "px-2.5 py-1 rounded-[var(--radius-full)] text-xs font-medium",
8603
- statusColors[status.variant || "default"]
11094
+ statusColors2[status.variant || "default"]
8604
11095
  ),
8605
11096
  children: status.label
8606
11097
  }
@@ -8805,7 +11296,7 @@ var DetailPanel = ({
8805
11296
  return;
8806
11297
  }
8807
11298
  if (action.event) {
8808
- eventBus.emit(`UI:${action.event}`, { row: data2 });
11299
+ eventBus.emit(`UI:${action.event}`, { id: data2?.id, row: data2 });
8809
11300
  }
8810
11301
  if (action.onClick) {
8811
11302
  action.onClick();
@@ -9202,7 +11693,7 @@ var Form = ({
9202
11693
  const normalizedInitialData = initialData ?? {};
9203
11694
  const resolvedEntity = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
9204
11695
  const entityName = typeof entity === "string" ? entity : resolvedEntity?.name;
9205
- const entityDerivedFields = React50__default.useMemo(() => {
11696
+ const entityDerivedFields = React69__default.useMemo(() => {
9206
11697
  if (fields && fields.length > 0) return void 0;
9207
11698
  if (!resolvedEntity) return void 0;
9208
11699
  return resolvedEntity.fields.map(
@@ -9221,14 +11712,14 @@ var Form = ({
9221
11712
  const conditionalFields = typeof conditionalFieldsRaw === "boolean" ? {} : conditionalFieldsRaw;
9222
11713
  const hiddenCalculations = typeof hiddenCalculationsRaw === "boolean" ? [] : hiddenCalculationsRaw;
9223
11714
  const violationTriggers = typeof violationTriggersRaw === "boolean" ? [] : violationTriggersRaw;
9224
- const [formData, setFormData] = React50__default.useState(
11715
+ const [formData, setFormData] = React69__default.useState(
9225
11716
  normalizedInitialData
9226
11717
  );
9227
- const [collapsedSections, setCollapsedSections] = React50__default.useState(
11718
+ const [collapsedSections, setCollapsedSections] = React69__default.useState(
9228
11719
  /* @__PURE__ */ new Set()
9229
11720
  );
9230
11721
  const shouldShowCancel = showCancel ?? (fields && fields.length > 0);
9231
- const evalContext = React50__default.useMemo(
11722
+ const evalContext = React69__default.useMemo(
9232
11723
  () => ({
9233
11724
  formValues: formData,
9234
11725
  globalVariables: externalContext?.globalVariables ?? {},
@@ -9237,13 +11728,13 @@ var Form = ({
9237
11728
  }),
9238
11729
  [formData, externalContext]
9239
11730
  );
9240
- React50__default.useEffect(() => {
11731
+ React69__default.useEffect(() => {
9241
11732
  const data = initialData;
9242
11733
  if (data && Object.keys(data).length > 0) {
9243
11734
  setFormData(data);
9244
11735
  }
9245
11736
  }, [initialData]);
9246
- const processCalculations = React50__default.useCallback(
11737
+ const processCalculations = React69__default.useCallback(
9247
11738
  (changedFieldId, newFormData) => {
9248
11739
  if (!hiddenCalculations.length) return;
9249
11740
  const context = {
@@ -9268,7 +11759,7 @@ var Form = ({
9268
11759
  },
9269
11760
  [hiddenCalculations, externalContext, eventBus]
9270
11761
  );
9271
- const checkViolations = React50__default.useCallback(
11762
+ const checkViolations = React69__default.useCallback(
9272
11763
  (changedFieldId, newFormData) => {
9273
11764
  if (!violationTriggers.length) return;
9274
11765
  const context = {
@@ -9305,7 +11796,7 @@ var Form = ({
9305
11796
  processCalculations(name, newFormData);
9306
11797
  checkViolations(name, newFormData);
9307
11798
  };
9308
- const isFieldVisible = React50__default.useCallback(
11799
+ const isFieldVisible = React69__default.useCallback(
9309
11800
  (fieldName) => {
9310
11801
  const condition = conditionalFields[fieldName];
9311
11802
  if (!condition) return true;
@@ -9313,7 +11804,7 @@ var Form = ({
9313
11804
  },
9314
11805
  [conditionalFields, evalContext]
9315
11806
  );
9316
- const isSectionVisible = React50__default.useCallback(
11807
+ const isSectionVisible = React69__default.useCallback(
9317
11808
  (section) => {
9318
11809
  if (!section.condition) return true;
9319
11810
  return Boolean(evaluateFormExpression(section.condition, evalContext));
@@ -9345,7 +11836,7 @@ var Form = ({
9345
11836
  eventBus.emit(`UI:${onCancel}`);
9346
11837
  }
9347
11838
  };
9348
- const renderField = React50__default.useCallback(
11839
+ const renderField = React69__default.useCallback(
9349
11840
  (field) => {
9350
11841
  const fieldName = field.name || field.field;
9351
11842
  if (!fieldName) return null;
@@ -9366,7 +11857,7 @@ var Form = ({
9366
11857
  [formData, isFieldVisible, relationsData, relationsLoading, isLoading]
9367
11858
  );
9368
11859
  const effectiveFields = entityDerivedFields ?? fields;
9369
- const normalizedFields = React50__default.useMemo(() => {
11860
+ const normalizedFields = React69__default.useMemo(() => {
9370
11861
  if (!effectiveFields || effectiveFields.length === 0) return [];
9371
11862
  return effectiveFields.map((field) => {
9372
11863
  if (typeof field === "string") {
@@ -9375,7 +11866,7 @@ var Form = ({
9375
11866
  return field;
9376
11867
  });
9377
11868
  }, [effectiveFields]);
9378
- const schemaFields = React50__default.useMemo(() => {
11869
+ const schemaFields = React69__default.useMemo(() => {
9379
11870
  if (normalizedFields.length === 0) return null;
9380
11871
  if (isDebugEnabled()) {
9381
11872
  debugGroup(`Form: ${entityName || "unknown"}`);
@@ -9385,7 +11876,7 @@ var Form = ({
9385
11876
  }
9386
11877
  return normalizedFields.map(renderField).filter(Boolean);
9387
11878
  }, [normalizedFields, renderField, entityName, conditionalFields]);
9388
- const sectionElements = React50__default.useMemo(() => {
11879
+ const sectionElements = React69__default.useMemo(() => {
9389
11880
  if (!sections || sections.length === 0) return null;
9390
11881
  return sections.map((section) => {
9391
11882
  if (!isSectionVisible(section)) {
@@ -9724,7 +12215,7 @@ var CardGrid = ({
9724
12215
  return;
9725
12216
  }
9726
12217
  if (action.event) {
9727
- eventBus.emit(`UI:${action.event}`, { row: itemData });
12218
+ eventBus.emit(`UI:${action.event}`, { id: itemData.id, row: itemData });
9728
12219
  }
9729
12220
  if (action.onClick) {
9730
12221
  action.onClick(itemData);
@@ -10236,7 +12727,7 @@ function InputPattern({
10236
12727
  className
10237
12728
  }) {
10238
12729
  const { emit } = useEventBus();
10239
- const [localValue, setLocalValue] = React50__default.useState(value);
12730
+ const [localValue, setLocalValue] = React69__default.useState(value);
10240
12731
  const handleChange = (e) => {
10241
12732
  setLocalValue(e.target.value);
10242
12733
  if (onChange) {
@@ -10273,7 +12764,7 @@ function TextareaPattern({
10273
12764
  className
10274
12765
  }) {
10275
12766
  const { emit } = useEventBus();
10276
- const [localValue, setLocalValue] = React50__default.useState(value);
12767
+ const [localValue, setLocalValue] = React69__default.useState(value);
10277
12768
  const handleChange = (e) => {
10278
12769
  setLocalValue(e.target.value);
10279
12770
  if (onChange) {
@@ -10304,7 +12795,7 @@ function SelectPattern({
10304
12795
  className
10305
12796
  }) {
10306
12797
  const { emit } = useEventBus();
10307
- const [localValue, setLocalValue] = React50__default.useState(value);
12798
+ const [localValue, setLocalValue] = React69__default.useState(value);
10308
12799
  const handleChange = (e) => {
10309
12800
  setLocalValue(e.target.value);
10310
12801
  if (onChange) {
@@ -10333,7 +12824,7 @@ function CheckboxPattern({
10333
12824
  className
10334
12825
  }) {
10335
12826
  const { emit } = useEventBus();
10336
- const [localChecked, setLocalChecked] = React50__default.useState(checked);
12827
+ const [localChecked, setLocalChecked] = React69__default.useState(checked);
10337
12828
  const handleChange = (e) => {
10338
12829
  setLocalChecked(e.target.checked);
10339
12830
  if (onChange) {
@@ -10763,7 +13254,7 @@ function SuspenseConfigProvider({
10763
13254
  config,
10764
13255
  children
10765
13256
  }) {
10766
- return React50__default.createElement(
13257
+ return React69__default.createElement(
10767
13258
  SuspenseConfigContext.Provider,
10768
13259
  { value: config },
10769
13260
  children
@@ -11016,7 +13507,7 @@ function CompiledPortal({ slot, className, pattern, sourceTrait, children }) {
11016
13507
  let wrapper;
11017
13508
  switch (slot) {
11018
13509
  case "modal":
11019
- wrapper = /* @__PURE__ */ jsx(Modal, { isOpen: true, onClose: handleDismiss, showCloseButton: true, children: /* @__PURE__ */ jsx(
13510
+ wrapper = /* @__PURE__ */ jsx(Modal, { isOpen: true, onClose: handleDismiss, showCloseButton: true, size: "lg", children: /* @__PURE__ */ jsx(
11020
13511
  Box,
11021
13512
  {
11022
13513
  className: cn("ui-slot", `ui-slot-${slot}`, className),
@@ -11248,4 +13739,4 @@ function UISlotRenderer({
11248
13739
  }
11249
13740
  UISlotRenderer.displayName = "UISlotRenderer";
11250
13741
 
11251
- export { Accordion, ActionButtons, Alert, Avatar, Badge, Box, Breadcrumb, Button, ButtonGroup, CalendarGrid, Card, Card2, CardBody, CardContent, CardFooter, CardGrid, CardHeader, CardTitle, Center, ChartLegend, Checkbox, CodeBlock, ConditionalWrapper, Container, ControlButton, DPad, DataGrid, DataList, DataTable, DateRangeSelector, DayCell, DetailPanel, Divider, Drawer, EmptyState, EntityDisplayEvents, ErrorBoundary, ErrorState, FilterGroup, Flex, FlipCard, FlipContainer, FloatingActionButton, Form, FormField, FormSectionHeader, GraphView, Grid, HStack, Heading, HealthBar, Icon, Input, InputGroup, Label, LawReferenceTooltip, LineChart, LoadingState, MapView, MarkdownContent, MasterDetail, Menu, Modal, Overlay, PageHeader, Pagination, Popover, ProgressBar, ProgressDots, QuizBlock, Radio, RelationSelect, RepeatableFormSection, ScaledDiagram, ScoreDisplay, SearchInput, Select, SidePanel, SimpleGrid, Skeleton, SlotContentRenderer, Spacer, Spinner, Sprite, Stack, StatBadge, StatCard, StateIndicator, SuspenseConfigProvider, Switch, Tabs, Text, TextHighlight, Textarea, ThemeSelector, ThemeToggle, TimeSlotCell, Toast, Tooltip, Typography, UISlotComponent, UISlotRenderer, VStack, ViolationAlert, WizardNavigation, WizardProgress, drawSprite };
13742
+ export { Accordion, ActionButtons, Alert, AnimatedCounter, Avatar, Badge, Box, Breadcrumb, Button, ButtonGroup, CalendarGrid, Card, Card2, CardBody, CardContent, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, Center, ChartLegend, Checkbox, CodeBlock, ConditionalWrapper, ConfettiEffect, Container, ControlButton, DPad, DataGrid, DataList, DataTable, DateRangeSelector, DayCell, DetailPanel, Divider, Drawer, EmptyState, EntityDisplayEvents, ErrorBoundary, ErrorState, FilterGroup, Flex, FlipCard, FlipContainer, FloatingActionButton, Form, FormField, FormSectionHeader, GraphView, Grid, HStack, Heading, HealthBar, Icon, InfiniteScrollSentinel, Input, InputGroup, Label, LawReferenceTooltip, Lightbox, LineChart, LoadingState, MapView, MarkdownContent, MasterDetail, Menu, Meter, Modal, NumberStepper, Overlay, PageHeader, Pagination, Popover, ProgressBar, ProgressDots, PullToRefresh, QuizBlock, Radio, RangeSlider, RelationSelect, RepeatableFormSection, ScaledDiagram, ScoreDisplay, SearchInput, Select, SidePanel, SimpleGrid, Skeleton, SlotContentRenderer, SortableList, Spacer, Spinner, Sprite, Stack, StarRating, StatBadge, StatCard, StatDisplay, StateIndicator, StatusDot, SuspenseConfigProvider, SwipeableRow, Switch, Tabs, Text, TextHighlight, Textarea, ThemeSelector, ThemeToggle, TimeSlotCell, Toast, Tooltip, TrendIndicator, TypewriterText, Typography, UISlotComponent, UISlotRenderer, UploadDropZone, VStack, ViolationAlert, WizardNavigation, WizardProgress, drawSprite };