@almadar/ui 4.19.3 → 4.21.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.
@@ -7093,7 +7093,7 @@ var init_RangeSlider = __esm({
7093
7093
  action,
7094
7094
  actionPayload,
7095
7095
  onChange,
7096
- formatValue: formatValue5,
7096
+ formatValue: formatValue4,
7097
7097
  ...props
7098
7098
  }, ref) => {
7099
7099
  const [isDragging, setIsDragging] = React127.useState(false);
@@ -7102,7 +7102,7 @@ var init_RangeSlider = __esm({
7102
7102
  const eventBus = useSafeEventBus();
7103
7103
  const percentage = max !== min ? (value - min) / (max - min) * 100 : 0;
7104
7104
  const bufferedPercentage = buffered !== void 0 ? Math.min(buffered, 100) : void 0;
7105
- const displayValue = formatValue5 ? formatValue5(value) : String(value);
7105
+ const displayValue = formatValue4 ? formatValue4(value) : String(value);
7106
7106
  const handleChange = React127.useCallback(
7107
7107
  (e) => {
7108
7108
  const newValue = Number(e.target.value);
@@ -20318,6 +20318,7 @@ var init_Tabs = __esm({
20318
20318
  defaultActiveTab,
20319
20319
  activeTab: controlledActiveTab,
20320
20320
  onTabChange,
20321
+ tabChangeEvent,
20321
20322
  variant = "default",
20322
20323
  orientation = "horizontal",
20323
20324
  className
@@ -20340,6 +20341,9 @@ var init_Tabs = __esm({
20340
20341
  setInternalActiveTab(tabId);
20341
20342
  }
20342
20343
  onTabChange?.(tabId);
20344
+ if (tabChangeEvent) {
20345
+ eventBus.emit(`UI:${tabChangeEvent}`, { tabId });
20346
+ }
20343
20347
  if (tabEvent) {
20344
20348
  eventBus.emit(`UI:${tabEvent}`, { tabId });
20345
20349
  }
@@ -20420,7 +20424,7 @@ var init_Tabs = __esm({
20420
20424
  isActive ? variant === "pills" ? "text-primary-foreground font-bold" : "text-foreground font-bold" : "text-muted-foreground hover:text-foreground"
20421
20425
  ),
20422
20426
  children: [
20423
- item.icon && /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: item.icon, size: "sm" }),
20427
+ item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: item.icon, size: "sm" }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: item.icon, size: "sm" })),
20424
20428
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: isActive ? "semibold" : "normal", className: "!text-inherit", children: item.label }),
20425
20429
  item.badge !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", size: "sm", children: item.badge })
20426
20430
  ]
@@ -27444,25 +27448,24 @@ var init_Lightbox = __esm({
27444
27448
  Lightbox.displayName = "Lightbox";
27445
27449
  }
27446
27450
  });
27447
- function formatValue3(value, format, max) {
27451
+ function formatNumber(value, format) {
27448
27452
  if (value == null) return "0";
27449
27453
  const v = typeof value === "number" ? value : value;
27450
- let formatted;
27451
27454
  switch (format) {
27452
27455
  case "currency":
27453
- formatted = typeof v === "number" ? `$${v.toFixed(2)}` : String(v);
27454
- break;
27456
+ return typeof v === "number" ? `$${v.toFixed(2)}` : String(v);
27455
27457
  case "percent":
27456
- formatted = typeof v === "number" ? `${Math.round(v)}%` : String(v);
27457
- break;
27458
+ return typeof v === "number" ? `${Math.round(v)}%` : String(v);
27458
27459
  case "number":
27459
- formatted = typeof v === "number" ? v.toLocaleString() : String(v);
27460
- break;
27460
+ return typeof v === "number" ? v.toLocaleString() : String(v);
27461
27461
  default:
27462
- formatted = String(v);
27462
+ return String(v);
27463
27463
  }
27464
- if (max != null) return `${formatted} / ${max}`;
27465
- return formatted;
27464
+ }
27465
+ function composeDisplayValue(value, format, max, prefix, suffix) {
27466
+ const formatted = formatNumber(value, format);
27467
+ const withMax = max != null && max > 0 ? `${formatted} / ${max}` : formatted;
27468
+ return `${prefix ?? ""}${withMax}${suffix ?? ""}`;
27466
27469
  }
27467
27470
  var variantColor, StatDisplay;
27468
27471
  var init_StatDisplay = __esm({
@@ -27486,6 +27489,10 @@ var init_StatDisplay = __esm({
27486
27489
  label,
27487
27490
  value,
27488
27491
  max,
27492
+ target,
27493
+ trend,
27494
+ prefix,
27495
+ suffix,
27489
27496
  icon: iconProp,
27490
27497
  iconBg = "bg-muted",
27491
27498
  iconColor = "text-foreground",
@@ -27501,7 +27508,13 @@ var init_StatDisplay = __esm({
27501
27508
  const iconSizes2 = { sm: "h-4 w-4", md: "h-5 w-5", lg: "h-6 w-6" };
27502
27509
  const valueSizes = { sm: "text-lg", md: "text-2xl", lg: "text-3xl" };
27503
27510
  const padSizes = { sm: "p-3", md: "p-4", lg: "p-6" };
27504
- const displayValue = formatValue3(value, format, max);
27511
+ const displayValue = composeDisplayValue(value, format, max, prefix, suffix);
27512
+ const numericValue = typeof value === "number" ? value : Number(value);
27513
+ const showTarget = typeof target === "number" && target > 0 && Number.isFinite(numericValue);
27514
+ const targetPct = showTarget ? Math.max(0, Math.min(100, numericValue / target * 100)) : 0;
27515
+ const showTrend = typeof trend === "number" && trend !== 0 && Number.isFinite(trend);
27516
+ const trendUp = showTrend && trend > 0;
27517
+ const trendLabel = showTrend ? `${trendUp ? "\u2191" : "\u2193"} ${Math.abs(trend)}` : "";
27505
27518
  if (error) {
27506
27519
  return /* @__PURE__ */ jsxRuntime.jsx(Card, { className: cn(padSizes[size], className), children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "error", children: error.message }) });
27507
27520
  }
@@ -27516,13 +27529,31 @@ var init_StatDisplay = __esm({
27516
27529
  ResolvedIcon && /* @__PURE__ */ jsxRuntime.jsx(ResolvedIcon, { className: cn(iconSizes2[size], iconColor) }),
27517
27530
  typeof iconProp !== "string" && iconProp,
27518
27531
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", children: label }),
27519
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue })
27532
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue }),
27533
+ showTrend && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: cn("font-semibold", trendUp ? "text-success" : "text-error"), children: trendLabel })
27520
27534
  ] });
27521
27535
  }
27522
27536
  return /* @__PURE__ */ jsxRuntime.jsx(Card, { className: cn(padSizes[size], className), children: /* @__PURE__ */ jsxRuntime.jsxs(HStack, { align: "start", justify: "between", children: [
27523
- /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", className: "space-y-1", children: [
27537
+ /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", className: "space-y-1 flex-1", children: [
27524
27538
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "overline", color: "secondary", children: label }),
27525
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue })
27539
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", align: "end", children: [
27540
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue }),
27541
+ showTrend && /* @__PURE__ */ jsxRuntime.jsx(
27542
+ Typography,
27543
+ {
27544
+ variant: "caption",
27545
+ className: cn("font-semibold pb-1", trendUp ? "text-success" : "text-error"),
27546
+ children: trendLabel
27547
+ }
27548
+ )
27549
+ ] }),
27550
+ showTarget && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "mt-2 h-1.5 w-full bg-muted rounded-full overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
27551
+ Box,
27552
+ {
27553
+ className: cn("h-full rounded-full transition-all", `bg-${variant === "default" ? "primary" : variant}`),
27554
+ style: { width: `${targetPct}%` }
27555
+ }
27556
+ ) })
27526
27557
  ] }),
27527
27558
  (ResolvedIcon || typeof iconProp !== "string" && iconProp) && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: cn("p-3 rounded-md", iconBg), children: ResolvedIcon ? /* @__PURE__ */ jsxRuntime.jsx(ResolvedIcon, { className: cn(iconSizes2[size], iconColor) }) : iconProp })
27528
27559
  ] }) });
@@ -33683,7 +33714,7 @@ function getStatusStyle(fieldName, value) {
33683
33714
  if (val.includes("low")) return STATUS_STYLES2.low;
33684
33715
  return STATUS_STYLES2.default;
33685
33716
  }
33686
- function formatValue4(value, fieldName) {
33717
+ function formatValue3(value, fieldName) {
33687
33718
  if (typeof value === "number") {
33688
33719
  if (fieldName.toLowerCase().includes("progress") || fieldName.toLowerCase().includes("percent")) {
33689
33720
  return `${value}%`;
@@ -34059,7 +34090,7 @@ var init_List = __esm({
34059
34090
  className: "flex items-center gap-2 text-muted-foreground group-hover:text-foreground transition-colors",
34060
34091
  children: [
34061
34092
  /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Calendar, { className: "w-3.5 h-3.5" }),
34062
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", children: formatValue4(value, field) })
34093
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", children: formatValue3(value, field) })
34063
34094
  ]
34064
34095
  },
34065
34096
  field
@@ -34078,7 +34109,7 @@ var init_List = __esm({
34078
34109
  formatFieldLabel2(field),
34079
34110
  ":"
34080
34111
  ] }),
34081
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", className: "text-foreground", children: formatValue4(value, field) })
34112
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", className: "text-foreground", children: formatValue3(value, field) })
34082
34113
  ]
34083
34114
  },
34084
34115
  field
@@ -51638,7 +51669,6 @@ function getSlotContentRenderer2() {
51638
51669
  }
51639
51670
  function makeLambdaFn(argName, lambdaBody, callerKey) {
51640
51671
  return (item, index) => {
51641
- console.log(`[lambda-triage:${callerKey}#${index}]`, JSON.stringify(item));
51642
51672
  const resolvedBody = resolveLambdaBindings(lambdaBody, argName, item);
51643
51673
  if (resolvedBody === null || typeof resolvedBody !== "object" || Array.isArray(resolvedBody)) {
51644
51674
  return null;
package/dist/avl/index.js CHANGED
@@ -7047,7 +7047,7 @@ var init_RangeSlider = __esm({
7047
7047
  action,
7048
7048
  actionPayload,
7049
7049
  onChange,
7050
- formatValue: formatValue5,
7050
+ formatValue: formatValue4,
7051
7051
  ...props
7052
7052
  }, ref) => {
7053
7053
  const [isDragging, setIsDragging] = useState(false);
@@ -7056,7 +7056,7 @@ var init_RangeSlider = __esm({
7056
7056
  const eventBus = useSafeEventBus();
7057
7057
  const percentage = max !== min ? (value - min) / (max - min) * 100 : 0;
7058
7058
  const bufferedPercentage = buffered !== void 0 ? Math.min(buffered, 100) : void 0;
7059
- const displayValue = formatValue5 ? formatValue5(value) : String(value);
7059
+ const displayValue = formatValue4 ? formatValue4(value) : String(value);
7060
7060
  const handleChange = useCallback(
7061
7061
  (e) => {
7062
7062
  const newValue = Number(e.target.value);
@@ -20272,6 +20272,7 @@ var init_Tabs = __esm({
20272
20272
  defaultActiveTab,
20273
20273
  activeTab: controlledActiveTab,
20274
20274
  onTabChange,
20275
+ tabChangeEvent,
20275
20276
  variant = "default",
20276
20277
  orientation = "horizontal",
20277
20278
  className
@@ -20294,6 +20295,9 @@ var init_Tabs = __esm({
20294
20295
  setInternalActiveTab(tabId);
20295
20296
  }
20296
20297
  onTabChange?.(tabId);
20298
+ if (tabChangeEvent) {
20299
+ eventBus.emit(`UI:${tabChangeEvent}`, { tabId });
20300
+ }
20297
20301
  if (tabEvent) {
20298
20302
  eventBus.emit(`UI:${tabEvent}`, { tabId });
20299
20303
  }
@@ -20374,7 +20378,7 @@ var init_Tabs = __esm({
20374
20378
  isActive ? variant === "pills" ? "text-primary-foreground font-bold" : "text-foreground font-bold" : "text-muted-foreground hover:text-foreground"
20375
20379
  ),
20376
20380
  children: [
20377
- item.icon && /* @__PURE__ */ jsx(Icon, { icon: item.icon, size: "sm" }),
20381
+ item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: item.icon, size: "sm" }) : /* @__PURE__ */ jsx(Icon, { icon: item.icon, size: "sm" })),
20378
20382
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: isActive ? "semibold" : "normal", className: "!text-inherit", children: item.label }),
20379
20383
  item.badge !== void 0 && /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", children: item.badge })
20380
20384
  ]
@@ -27398,25 +27402,24 @@ var init_Lightbox = __esm({
27398
27402
  Lightbox.displayName = "Lightbox";
27399
27403
  }
27400
27404
  });
27401
- function formatValue3(value, format, max) {
27405
+ function formatNumber(value, format) {
27402
27406
  if (value == null) return "0";
27403
27407
  const v = typeof value === "number" ? value : value;
27404
- let formatted;
27405
27408
  switch (format) {
27406
27409
  case "currency":
27407
- formatted = typeof v === "number" ? `$${v.toFixed(2)}` : String(v);
27408
- break;
27410
+ return typeof v === "number" ? `$${v.toFixed(2)}` : String(v);
27409
27411
  case "percent":
27410
- formatted = typeof v === "number" ? `${Math.round(v)}%` : String(v);
27411
- break;
27412
+ return typeof v === "number" ? `${Math.round(v)}%` : String(v);
27412
27413
  case "number":
27413
- formatted = typeof v === "number" ? v.toLocaleString() : String(v);
27414
- break;
27414
+ return typeof v === "number" ? v.toLocaleString() : String(v);
27415
27415
  default:
27416
- formatted = String(v);
27416
+ return String(v);
27417
27417
  }
27418
- if (max != null) return `${formatted} / ${max}`;
27419
- return formatted;
27418
+ }
27419
+ function composeDisplayValue(value, format, max, prefix, suffix) {
27420
+ const formatted = formatNumber(value, format);
27421
+ const withMax = max != null && max > 0 ? `${formatted} / ${max}` : formatted;
27422
+ return `${prefix ?? ""}${withMax}${suffix ?? ""}`;
27420
27423
  }
27421
27424
  var variantColor, StatDisplay;
27422
27425
  var init_StatDisplay = __esm({
@@ -27440,6 +27443,10 @@ var init_StatDisplay = __esm({
27440
27443
  label,
27441
27444
  value,
27442
27445
  max,
27446
+ target,
27447
+ trend,
27448
+ prefix,
27449
+ suffix,
27443
27450
  icon: iconProp,
27444
27451
  iconBg = "bg-muted",
27445
27452
  iconColor = "text-foreground",
@@ -27455,7 +27462,13 @@ var init_StatDisplay = __esm({
27455
27462
  const iconSizes2 = { sm: "h-4 w-4", md: "h-5 w-5", lg: "h-6 w-6" };
27456
27463
  const valueSizes = { sm: "text-lg", md: "text-2xl", lg: "text-3xl" };
27457
27464
  const padSizes = { sm: "p-3", md: "p-4", lg: "p-6" };
27458
- const displayValue = formatValue3(value, format, max);
27465
+ const displayValue = composeDisplayValue(value, format, max, prefix, suffix);
27466
+ const numericValue = typeof value === "number" ? value : Number(value);
27467
+ const showTarget = typeof target === "number" && target > 0 && Number.isFinite(numericValue);
27468
+ const targetPct = showTarget ? Math.max(0, Math.min(100, numericValue / target * 100)) : 0;
27469
+ const showTrend = typeof trend === "number" && trend !== 0 && Number.isFinite(trend);
27470
+ const trendUp = showTrend && trend > 0;
27471
+ const trendLabel = showTrend ? `${trendUp ? "\u2191" : "\u2193"} ${Math.abs(trend)}` : "";
27459
27472
  if (error) {
27460
27473
  return /* @__PURE__ */ jsx(Card, { className: cn(padSizes[size], className), children: /* @__PURE__ */ jsx(Typography, { variant: "small", color: "error", children: error.message }) });
27461
27474
  }
@@ -27470,13 +27483,31 @@ var init_StatDisplay = __esm({
27470
27483
  ResolvedIcon && /* @__PURE__ */ jsx(ResolvedIcon, { className: cn(iconSizes2[size], iconColor) }),
27471
27484
  typeof iconProp !== "string" && iconProp,
27472
27485
  /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: label }),
27473
- /* @__PURE__ */ jsx(Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue })
27486
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue }),
27487
+ showTrend && /* @__PURE__ */ jsx(Typography, { variant: "caption", className: cn("font-semibold", trendUp ? "text-success" : "text-error"), children: trendLabel })
27474
27488
  ] });
27475
27489
  }
27476
27490
  return /* @__PURE__ */ jsx(Card, { className: cn(padSizes[size], className), children: /* @__PURE__ */ jsxs(HStack, { align: "start", justify: "between", children: [
27477
- /* @__PURE__ */ jsxs(VStack, { gap: "none", className: "space-y-1", children: [
27491
+ /* @__PURE__ */ jsxs(VStack, { gap: "none", className: "space-y-1 flex-1", children: [
27478
27492
  /* @__PURE__ */ jsx(Typography, { variant: "overline", color: "secondary", children: label }),
27479
- /* @__PURE__ */ jsx(Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue })
27493
+ /* @__PURE__ */ jsxs(HStack, { gap: "sm", align: "end", children: [
27494
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue }),
27495
+ showTrend && /* @__PURE__ */ jsx(
27496
+ Typography,
27497
+ {
27498
+ variant: "caption",
27499
+ className: cn("font-semibold pb-1", trendUp ? "text-success" : "text-error"),
27500
+ children: trendLabel
27501
+ }
27502
+ )
27503
+ ] }),
27504
+ showTarget && /* @__PURE__ */ jsx(Box, { className: "mt-2 h-1.5 w-full bg-muted rounded-full overflow-hidden", children: /* @__PURE__ */ jsx(
27505
+ Box,
27506
+ {
27507
+ className: cn("h-full rounded-full transition-all", `bg-${variant === "default" ? "primary" : variant}`),
27508
+ style: { width: `${targetPct}%` }
27509
+ }
27510
+ ) })
27480
27511
  ] }),
27481
27512
  (ResolvedIcon || typeof iconProp !== "string" && iconProp) && /* @__PURE__ */ jsx(Box, { className: cn("p-3 rounded-md", iconBg), children: ResolvedIcon ? /* @__PURE__ */ jsx(ResolvedIcon, { className: cn(iconSizes2[size], iconColor) }) : iconProp })
27482
27513
  ] }) });
@@ -33637,7 +33668,7 @@ function getStatusStyle(fieldName, value) {
33637
33668
  if (val.includes("low")) return STATUS_STYLES2.low;
33638
33669
  return STATUS_STYLES2.default;
33639
33670
  }
33640
- function formatValue4(value, fieldName) {
33671
+ function formatValue3(value, fieldName) {
33641
33672
  if (typeof value === "number") {
33642
33673
  if (fieldName.toLowerCase().includes("progress") || fieldName.toLowerCase().includes("percent")) {
33643
33674
  return `${value}%`;
@@ -34013,7 +34044,7 @@ var init_List = __esm({
34013
34044
  className: "flex items-center gap-2 text-muted-foreground group-hover:text-foreground transition-colors",
34014
34045
  children: [
34015
34046
  /* @__PURE__ */ jsx(Calendar, { className: "w-3.5 h-3.5" }),
34016
- /* @__PURE__ */ jsx(Typography, { as: "span", children: formatValue4(value, field) })
34047
+ /* @__PURE__ */ jsx(Typography, { as: "span", children: formatValue3(value, field) })
34017
34048
  ]
34018
34049
  },
34019
34050
  field
@@ -34032,7 +34063,7 @@ var init_List = __esm({
34032
34063
  formatFieldLabel2(field),
34033
34064
  ":"
34034
34065
  ] }),
34035
- /* @__PURE__ */ jsx(Typography, { as: "span", className: "text-foreground", children: formatValue4(value, field) })
34066
+ /* @__PURE__ */ jsx(Typography, { as: "span", className: "text-foreground", children: formatValue3(value, field) })
34036
34067
  ]
34037
34068
  },
34038
34069
  field
@@ -51592,7 +51623,6 @@ function getSlotContentRenderer2() {
51592
51623
  }
51593
51624
  function makeLambdaFn(argName, lambdaBody, callerKey) {
51594
51625
  return (item, index) => {
51595
- console.log(`[lambda-triage:${callerKey}#${index}]`, JSON.stringify(item));
51596
51626
  const resolvedBody = resolveLambdaBindings(lambdaBody, argName, item);
51597
51627
  if (resolvedBody === null || typeof resolvedBody !== "object" || Array.isArray(resolvedBody)) {
51598
51628
  return null;
@@ -3017,7 +3017,7 @@ var init_RangeSlider = __esm({
3017
3017
  action,
3018
3018
  actionPayload,
3019
3019
  onChange,
3020
- formatValue: formatValue5,
3020
+ formatValue: formatValue4,
3021
3021
  ...props
3022
3022
  }, ref) => {
3023
3023
  const [isDragging, setIsDragging] = React110.useState(false);
@@ -3026,7 +3026,7 @@ var init_RangeSlider = __esm({
3026
3026
  const eventBus = useSafeEventBus();
3027
3027
  const percentage = max !== min ? (value - min) / (max - min) * 100 : 0;
3028
3028
  const bufferedPercentage = buffered !== void 0 ? Math.min(buffered, 100) : void 0;
3029
- const displayValue = formatValue5 ? formatValue5(value) : String(value);
3029
+ const displayValue = formatValue4 ? formatValue4(value) : String(value);
3030
3030
  const handleChange = React110.useCallback(
3031
3031
  (e) => {
3032
3032
  const newValue = Number(e.target.value);
@@ -15561,6 +15561,7 @@ var init_Tabs = __esm({
15561
15561
  defaultActiveTab,
15562
15562
  activeTab: controlledActiveTab,
15563
15563
  onTabChange,
15564
+ tabChangeEvent,
15564
15565
  variant = "default",
15565
15566
  orientation = "horizontal",
15566
15567
  className
@@ -15583,6 +15584,9 @@ var init_Tabs = __esm({
15583
15584
  setInternalActiveTab(tabId);
15584
15585
  }
15585
15586
  onTabChange?.(tabId);
15587
+ if (tabChangeEvent) {
15588
+ eventBus.emit(`UI:${tabChangeEvent}`, { tabId });
15589
+ }
15586
15590
  if (tabEvent) {
15587
15591
  eventBus.emit(`UI:${tabEvent}`, { tabId });
15588
15592
  }
@@ -15663,7 +15667,7 @@ var init_Tabs = __esm({
15663
15667
  isActive ? variant === "pills" ? "text-primary-foreground font-bold" : "text-foreground font-bold" : "text-muted-foreground hover:text-foreground"
15664
15668
  ),
15665
15669
  children: [
15666
- item.icon && /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: item.icon, size: "sm" }),
15670
+ item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: item.icon, size: "sm" }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: item.icon, size: "sm" })),
15667
15671
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", weight: isActive ? "semibold" : "normal", className: "!text-inherit", children: item.label }),
15668
15672
  item.badge !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: "default", size: "sm", children: item.badge })
15669
15673
  ]
@@ -23142,25 +23146,24 @@ var init_Lightbox = __esm({
23142
23146
  exports.Lightbox.displayName = "Lightbox";
23143
23147
  }
23144
23148
  });
23145
- function formatValue3(value, format, max) {
23149
+ function formatNumber(value, format) {
23146
23150
  if (value == null) return "0";
23147
23151
  const v = typeof value === "number" ? value : value;
23148
- let formatted;
23149
23152
  switch (format) {
23150
23153
  case "currency":
23151
- formatted = typeof v === "number" ? `$${v.toFixed(2)}` : String(v);
23152
- break;
23154
+ return typeof v === "number" ? `$${v.toFixed(2)}` : String(v);
23153
23155
  case "percent":
23154
- formatted = typeof v === "number" ? `${Math.round(v)}%` : String(v);
23155
- break;
23156
+ return typeof v === "number" ? `${Math.round(v)}%` : String(v);
23156
23157
  case "number":
23157
- formatted = typeof v === "number" ? v.toLocaleString() : String(v);
23158
- break;
23158
+ return typeof v === "number" ? v.toLocaleString() : String(v);
23159
23159
  default:
23160
- formatted = String(v);
23160
+ return String(v);
23161
23161
  }
23162
- if (max != null) return `${formatted} / ${max}`;
23163
- return formatted;
23162
+ }
23163
+ function composeDisplayValue(value, format, max, prefix, suffix) {
23164
+ const formatted = formatNumber(value, format);
23165
+ const withMax = max != null && max > 0 ? `${formatted} / ${max}` : formatted;
23166
+ return `${prefix ?? ""}${withMax}${suffix ?? ""}`;
23164
23167
  }
23165
23168
  var variantColor; exports.StatDisplay = void 0;
23166
23169
  var init_StatDisplay = __esm({
@@ -23184,6 +23187,10 @@ var init_StatDisplay = __esm({
23184
23187
  label,
23185
23188
  value,
23186
23189
  max,
23190
+ target,
23191
+ trend,
23192
+ prefix,
23193
+ suffix,
23187
23194
  icon: iconProp,
23188
23195
  iconBg = "bg-muted",
23189
23196
  iconColor = "text-foreground",
@@ -23199,7 +23206,13 @@ var init_StatDisplay = __esm({
23199
23206
  const iconSizes2 = { sm: "h-4 w-4", md: "h-5 w-5", lg: "h-6 w-6" };
23200
23207
  const valueSizes = { sm: "text-lg", md: "text-2xl", lg: "text-3xl" };
23201
23208
  const padSizes = { sm: "p-3", md: "p-4", lg: "p-6" };
23202
- const displayValue = formatValue3(value, format, max);
23209
+ const displayValue = composeDisplayValue(value, format, max, prefix, suffix);
23210
+ const numericValue = typeof value === "number" ? value : Number(value);
23211
+ const showTarget = typeof target === "number" && target > 0 && Number.isFinite(numericValue);
23212
+ const targetPct = showTarget ? Math.max(0, Math.min(100, numericValue / target * 100)) : 0;
23213
+ const showTrend = typeof trend === "number" && trend !== 0 && Number.isFinite(trend);
23214
+ const trendUp = showTrend && trend > 0;
23215
+ const trendLabel = showTrend ? `${trendUp ? "\u2191" : "\u2193"} ${Math.abs(trend)}` : "";
23203
23216
  if (error) {
23204
23217
  return /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: cn(padSizes[size], className), children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", color: "error", children: error.message }) });
23205
23218
  }
@@ -23214,13 +23227,31 @@ var init_StatDisplay = __esm({
23214
23227
  ResolvedIcon && /* @__PURE__ */ jsxRuntime.jsx(ResolvedIcon, { className: cn(iconSizes2[size], iconColor) }),
23215
23228
  typeof iconProp !== "string" && iconProp,
23216
23229
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "secondary", children: label }),
23217
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue })
23230
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue }),
23231
+ showTrend && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", className: cn("font-semibold", trendUp ? "text-success" : "text-error"), children: trendLabel })
23218
23232
  ] });
23219
23233
  }
23220
23234
  return /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: cn(padSizes[size], className), children: /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { align: "start", justify: "between", children: [
23221
- /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "none", className: "space-y-1", children: [
23235
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "none", className: "space-y-1 flex-1", children: [
23222
23236
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "overline", color: "secondary", children: label }),
23223
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue })
23237
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", align: "end", children: [
23238
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue }),
23239
+ showTrend && /* @__PURE__ */ jsxRuntime.jsx(
23240
+ exports.Typography,
23241
+ {
23242
+ variant: "caption",
23243
+ className: cn("font-semibold pb-1", trendUp ? "text-success" : "text-error"),
23244
+ children: trendLabel
23245
+ }
23246
+ )
23247
+ ] }),
23248
+ showTarget && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "mt-2 h-1.5 w-full bg-muted rounded-full overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
23249
+ exports.Box,
23250
+ {
23251
+ className: cn("h-full rounded-full transition-all", `bg-${variant === "default" ? "primary" : variant}`),
23252
+ style: { width: `${targetPct}%` }
23253
+ }
23254
+ ) })
23224
23255
  ] }),
23225
23256
  (ResolvedIcon || typeof iconProp !== "string" && iconProp) && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: cn("p-3 rounded-md", iconBg), children: ResolvedIcon ? /* @__PURE__ */ jsxRuntime.jsx(ResolvedIcon, { className: cn(iconSizes2[size], iconColor) }) : iconProp })
23226
23257
  ] }) });
@@ -29476,7 +29507,7 @@ function getStatusStyle(fieldName, value) {
29476
29507
  if (val.includes("low")) return STATUS_STYLES2.low;
29477
29508
  return STATUS_STYLES2.default;
29478
29509
  }
29479
- function formatValue4(value, fieldName) {
29510
+ function formatValue3(value, fieldName) {
29480
29511
  if (typeof value === "number") {
29481
29512
  if (fieldName.toLowerCase().includes("progress") || fieldName.toLowerCase().includes("percent")) {
29482
29513
  return `${value}%`;
@@ -29852,7 +29883,7 @@ var init_List = __esm({
29852
29883
  className: "flex items-center gap-2 text-muted-foreground group-hover:text-foreground transition-colors",
29853
29884
  children: [
29854
29885
  /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Calendar, { className: "w-3.5 h-3.5" }),
29855
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { as: "span", children: formatValue4(value, field) })
29886
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { as: "span", children: formatValue3(value, field) })
29856
29887
  ]
29857
29888
  },
29858
29889
  field
@@ -29871,7 +29902,7 @@ var init_List = __esm({
29871
29902
  formatFieldLabel2(field),
29872
29903
  ":"
29873
29904
  ] }),
29874
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { as: "span", className: "text-foreground", children: formatValue4(value, field) })
29905
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { as: "span", className: "text-foreground", children: formatValue3(value, field) })
29875
29906
  ]
29876
29907
  },
29877
29908
  field