@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.
@@ -2972,7 +2972,7 @@ var init_RangeSlider = __esm({
2972
2972
  action,
2973
2973
  actionPayload,
2974
2974
  onChange,
2975
- formatValue: formatValue5,
2975
+ formatValue: formatValue4,
2976
2976
  ...props
2977
2977
  }, ref) => {
2978
2978
  const [isDragging, setIsDragging] = useState(false);
@@ -2981,7 +2981,7 @@ var init_RangeSlider = __esm({
2981
2981
  const eventBus = useSafeEventBus();
2982
2982
  const percentage = max !== min ? (value - min) / (max - min) * 100 : 0;
2983
2983
  const bufferedPercentage = buffered !== void 0 ? Math.min(buffered, 100) : void 0;
2984
- const displayValue = formatValue5 ? formatValue5(value) : String(value);
2984
+ const displayValue = formatValue4 ? formatValue4(value) : String(value);
2985
2985
  const handleChange = useCallback(
2986
2986
  (e) => {
2987
2987
  const newValue = Number(e.target.value);
@@ -15516,6 +15516,7 @@ var init_Tabs = __esm({
15516
15516
  defaultActiveTab,
15517
15517
  activeTab: controlledActiveTab,
15518
15518
  onTabChange,
15519
+ tabChangeEvent,
15519
15520
  variant = "default",
15520
15521
  orientation = "horizontal",
15521
15522
  className
@@ -15538,6 +15539,9 @@ var init_Tabs = __esm({
15538
15539
  setInternalActiveTab(tabId);
15539
15540
  }
15540
15541
  onTabChange?.(tabId);
15542
+ if (tabChangeEvent) {
15543
+ eventBus.emit(`UI:${tabChangeEvent}`, { tabId });
15544
+ }
15541
15545
  if (tabEvent) {
15542
15546
  eventBus.emit(`UI:${tabEvent}`, { tabId });
15543
15547
  }
@@ -15618,7 +15622,7 @@ var init_Tabs = __esm({
15618
15622
  isActive ? variant === "pills" ? "text-primary-foreground font-bold" : "text-foreground font-bold" : "text-muted-foreground hover:text-foreground"
15619
15623
  ),
15620
15624
  children: [
15621
- item.icon && /* @__PURE__ */ jsx(Icon, { icon: item.icon, size: "sm" }),
15625
+ item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: item.icon, size: "sm" }) : /* @__PURE__ */ jsx(Icon, { icon: item.icon, size: "sm" })),
15622
15626
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: isActive ? "semibold" : "normal", className: "!text-inherit", children: item.label }),
15623
15627
  item.badge !== void 0 && /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", children: item.badge })
15624
15628
  ]
@@ -23097,25 +23101,24 @@ var init_Lightbox = __esm({
23097
23101
  Lightbox.displayName = "Lightbox";
23098
23102
  }
23099
23103
  });
23100
- function formatValue3(value, format, max) {
23104
+ function formatNumber(value, format) {
23101
23105
  if (value == null) return "0";
23102
23106
  const v = typeof value === "number" ? value : value;
23103
- let formatted;
23104
23107
  switch (format) {
23105
23108
  case "currency":
23106
- formatted = typeof v === "number" ? `$${v.toFixed(2)}` : String(v);
23107
- break;
23109
+ return typeof v === "number" ? `$${v.toFixed(2)}` : String(v);
23108
23110
  case "percent":
23109
- formatted = typeof v === "number" ? `${Math.round(v)}%` : String(v);
23110
- break;
23111
+ return typeof v === "number" ? `${Math.round(v)}%` : String(v);
23111
23112
  case "number":
23112
- formatted = typeof v === "number" ? v.toLocaleString() : String(v);
23113
- break;
23113
+ return typeof v === "number" ? v.toLocaleString() : String(v);
23114
23114
  default:
23115
- formatted = String(v);
23115
+ return String(v);
23116
23116
  }
23117
- if (max != null) return `${formatted} / ${max}`;
23118
- return formatted;
23117
+ }
23118
+ function composeDisplayValue(value, format, max, prefix, suffix) {
23119
+ const formatted = formatNumber(value, format);
23120
+ const withMax = max != null && max > 0 ? `${formatted} / ${max}` : formatted;
23121
+ return `${prefix ?? ""}${withMax}${suffix ?? ""}`;
23119
23122
  }
23120
23123
  var variantColor, StatDisplay;
23121
23124
  var init_StatDisplay = __esm({
@@ -23139,6 +23142,10 @@ var init_StatDisplay = __esm({
23139
23142
  label,
23140
23143
  value,
23141
23144
  max,
23145
+ target,
23146
+ trend,
23147
+ prefix,
23148
+ suffix,
23142
23149
  icon: iconProp,
23143
23150
  iconBg = "bg-muted",
23144
23151
  iconColor = "text-foreground",
@@ -23154,7 +23161,13 @@ var init_StatDisplay = __esm({
23154
23161
  const iconSizes2 = { sm: "h-4 w-4", md: "h-5 w-5", lg: "h-6 w-6" };
23155
23162
  const valueSizes = { sm: "text-lg", md: "text-2xl", lg: "text-3xl" };
23156
23163
  const padSizes = { sm: "p-3", md: "p-4", lg: "p-6" };
23157
- const displayValue = formatValue3(value, format, max);
23164
+ const displayValue = composeDisplayValue(value, format, max, prefix, suffix);
23165
+ const numericValue = typeof value === "number" ? value : Number(value);
23166
+ const showTarget = typeof target === "number" && target > 0 && Number.isFinite(numericValue);
23167
+ const targetPct = showTarget ? Math.max(0, Math.min(100, numericValue / target * 100)) : 0;
23168
+ const showTrend = typeof trend === "number" && trend !== 0 && Number.isFinite(trend);
23169
+ const trendUp = showTrend && trend > 0;
23170
+ const trendLabel = showTrend ? `${trendUp ? "\u2191" : "\u2193"} ${Math.abs(trend)}` : "";
23158
23171
  if (error) {
23159
23172
  return /* @__PURE__ */ jsx(Card, { className: cn(padSizes[size], className), children: /* @__PURE__ */ jsx(Typography, { variant: "small", color: "error", children: error.message }) });
23160
23173
  }
@@ -23169,13 +23182,31 @@ var init_StatDisplay = __esm({
23169
23182
  ResolvedIcon && /* @__PURE__ */ jsx(ResolvedIcon, { className: cn(iconSizes2[size], iconColor) }),
23170
23183
  typeof iconProp !== "string" && iconProp,
23171
23184
  /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: label }),
23172
- /* @__PURE__ */ jsx(Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue })
23185
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue }),
23186
+ showTrend && /* @__PURE__ */ jsx(Typography, { variant: "caption", className: cn("font-semibold", trendUp ? "text-success" : "text-error"), children: trendLabel })
23173
23187
  ] });
23174
23188
  }
23175
23189
  return /* @__PURE__ */ jsx(Card, { className: cn(padSizes[size], className), children: /* @__PURE__ */ jsxs(HStack, { align: "start", justify: "between", children: [
23176
- /* @__PURE__ */ jsxs(VStack, { gap: "none", className: "space-y-1", children: [
23190
+ /* @__PURE__ */ jsxs(VStack, { gap: "none", className: "space-y-1 flex-1", children: [
23177
23191
  /* @__PURE__ */ jsx(Typography, { variant: "overline", color: "secondary", children: label }),
23178
- /* @__PURE__ */ jsx(Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue })
23192
+ /* @__PURE__ */ jsxs(HStack, { gap: "sm", align: "end", children: [
23193
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue }),
23194
+ showTrend && /* @__PURE__ */ jsx(
23195
+ Typography,
23196
+ {
23197
+ variant: "caption",
23198
+ className: cn("font-semibold pb-1", trendUp ? "text-success" : "text-error"),
23199
+ children: trendLabel
23200
+ }
23201
+ )
23202
+ ] }),
23203
+ showTarget && /* @__PURE__ */ jsx(Box, { className: "mt-2 h-1.5 w-full bg-muted rounded-full overflow-hidden", children: /* @__PURE__ */ jsx(
23204
+ Box,
23205
+ {
23206
+ className: cn("h-full rounded-full transition-all", `bg-${variant === "default" ? "primary" : variant}`),
23207
+ style: { width: `${targetPct}%` }
23208
+ }
23209
+ ) })
23179
23210
  ] }),
23180
23211
  (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 })
23181
23212
  ] }) });
@@ -29431,7 +29462,7 @@ function getStatusStyle(fieldName, value) {
29431
29462
  if (val.includes("low")) return STATUS_STYLES2.low;
29432
29463
  return STATUS_STYLES2.default;
29433
29464
  }
29434
- function formatValue4(value, fieldName) {
29465
+ function formatValue3(value, fieldName) {
29435
29466
  if (typeof value === "number") {
29436
29467
  if (fieldName.toLowerCase().includes("progress") || fieldName.toLowerCase().includes("percent")) {
29437
29468
  return `${value}%`;
@@ -29807,7 +29838,7 @@ var init_List = __esm({
29807
29838
  className: "flex items-center gap-2 text-muted-foreground group-hover:text-foreground transition-colors",
29808
29839
  children: [
29809
29840
  /* @__PURE__ */ jsx(Calendar, { className: "w-3.5 h-3.5" }),
29810
- /* @__PURE__ */ jsx(Typography, { as: "span", children: formatValue4(value, field) })
29841
+ /* @__PURE__ */ jsx(Typography, { as: "span", children: formatValue3(value, field) })
29811
29842
  ]
29812
29843
  },
29813
29844
  field
@@ -29826,7 +29857,7 @@ var init_List = __esm({
29826
29857
  formatFieldLabel2(field),
29827
29858
  ":"
29828
29859
  ] }),
29829
- /* @__PURE__ */ jsx(Typography, { as: "span", className: "text-foreground", children: formatValue4(value, field) })
29860
+ /* @__PURE__ */ jsx(Typography, { as: "span", className: "text-foreground", children: formatValue3(value, field) })
29830
29861
  ]
29831
29862
  },
29832
29863
  field
@@ -11,8 +11,16 @@ export interface StatDisplayProps {
11
11
  label: string;
12
12
  /** Primary value (number or formatted string) */
13
13
  value: number | string;
14
- /** Maximum value (renders as "value / max") */
14
+ /** Optional denominator. >0 renders "value / max"; 0 or omitted hides the divider. */
15
15
  max?: number;
16
+ /** Optional progress target. >0 renders a progress bar at value/target. */
17
+ target?: number;
18
+ /** Signed delta vs previous period. >0 renders ↑ green, <0 renders ↓ red, 0 hides. */
19
+ trend?: number;
20
+ /** Prefix prepended to the formatted value (e.g. "≈ "). */
21
+ prefix?: string;
22
+ /** Suffix appended to the formatted value (e.g. " /mo", " ms"). */
23
+ suffix?: string;
16
24
  /** Lucide icon name or React node */
17
25
  icon?: React.ReactNode;
18
26
  /** Icon background color class */
@@ -13,8 +13,8 @@ export interface TabItem {
13
13
  label: string;
14
14
  /** Tab content - optional for event-driven tabs */
15
15
  content?: React.ReactNode;
16
- /** Tab icon */
17
- icon?: LucideIcon;
16
+ /** Tab icon — pass either a Lucide component or its registry name (e.g. "file-text") */
17
+ icon?: LucideIcon | string;
18
18
  /** Tab badge */
19
19
  badge?: string | number;
20
20
  /** Disable tab */
@@ -35,6 +35,8 @@ export interface TabsProps {
35
35
  activeTab?: string;
36
36
  /** Callback when tab changes */
37
37
  onTabChange?: (tabId: string) => void;
38
+ /** Declarative tab change event — emits UI:{tabChangeEvent} with { tabId } */
39
+ tabChangeEvent?: string;
38
40
  /** Tab variant */
39
41
  variant?: 'default' | 'pills' | 'underline';
40
42
  /** Tab orientation */
@@ -3539,7 +3539,7 @@ var init_RangeSlider = __esm({
3539
3539
  action,
3540
3540
  actionPayload,
3541
3541
  onChange,
3542
- formatValue: formatValue5,
3542
+ formatValue: formatValue4,
3543
3543
  ...props
3544
3544
  }, ref) => {
3545
3545
  const [isDragging, setIsDragging] = React115.useState(false);
@@ -3548,7 +3548,7 @@ var init_RangeSlider = __esm({
3548
3548
  const eventBus = useSafeEventBus();
3549
3549
  const percentage = max !== min ? (value - min) / (max - min) * 100 : 0;
3550
3550
  const bufferedPercentage = buffered !== void 0 ? Math.min(buffered, 100) : void 0;
3551
- const displayValue = formatValue5 ? formatValue5(value) : String(value);
3551
+ const displayValue = formatValue4 ? formatValue4(value) : String(value);
3552
3552
  const handleChange = React115.useCallback(
3553
3553
  (e) => {
3554
3554
  const newValue = Number(e.target.value);
@@ -17069,6 +17069,7 @@ var init_Tabs = __esm({
17069
17069
  defaultActiveTab,
17070
17070
  activeTab: controlledActiveTab,
17071
17071
  onTabChange,
17072
+ tabChangeEvent,
17072
17073
  variant = "default",
17073
17074
  orientation = "horizontal",
17074
17075
  className
@@ -17091,6 +17092,9 @@ var init_Tabs = __esm({
17091
17092
  setInternalActiveTab(tabId);
17092
17093
  }
17093
17094
  onTabChange?.(tabId);
17095
+ if (tabChangeEvent) {
17096
+ eventBus.emit(`UI:${tabChangeEvent}`, { tabId });
17097
+ }
17094
17098
  if (tabEvent) {
17095
17099
  eventBus.emit(`UI:${tabEvent}`, { tabId });
17096
17100
  }
@@ -17171,7 +17175,7 @@ var init_Tabs = __esm({
17171
17175
  isActive ? variant === "pills" ? "text-primary-foreground font-bold" : "text-foreground font-bold" : "text-muted-foreground hover:text-foreground"
17172
17176
  ),
17173
17177
  children: [
17174
- item.icon && /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: item.icon, size: "sm" }),
17178
+ item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: item.icon, size: "sm" }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: item.icon, size: "sm" })),
17175
17179
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: isActive ? "semibold" : "normal", className: "!text-inherit", children: item.label }),
17176
17180
  item.badge !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", size: "sm", children: item.badge })
17177
17181
  ]
@@ -24195,25 +24199,24 @@ var init_Lightbox = __esm({
24195
24199
  Lightbox.displayName = "Lightbox";
24196
24200
  }
24197
24201
  });
24198
- function formatValue3(value, format, max) {
24202
+ function formatNumber(value, format) {
24199
24203
  if (value == null) return "0";
24200
24204
  const v = typeof value === "number" ? value : value;
24201
- let formatted;
24202
24205
  switch (format) {
24203
24206
  case "currency":
24204
- formatted = typeof v === "number" ? `$${v.toFixed(2)}` : String(v);
24205
- break;
24207
+ return typeof v === "number" ? `$${v.toFixed(2)}` : String(v);
24206
24208
  case "percent":
24207
- formatted = typeof v === "number" ? `${Math.round(v)}%` : String(v);
24208
- break;
24209
+ return typeof v === "number" ? `${Math.round(v)}%` : String(v);
24209
24210
  case "number":
24210
- formatted = typeof v === "number" ? v.toLocaleString() : String(v);
24211
- break;
24211
+ return typeof v === "number" ? v.toLocaleString() : String(v);
24212
24212
  default:
24213
- formatted = String(v);
24213
+ return String(v);
24214
24214
  }
24215
- if (max != null) return `${formatted} / ${max}`;
24216
- return formatted;
24215
+ }
24216
+ function composeDisplayValue(value, format, max, prefix, suffix) {
24217
+ const formatted = formatNumber(value, format);
24218
+ const withMax = max != null && max > 0 ? `${formatted} / ${max}` : formatted;
24219
+ return `${prefix ?? ""}${withMax}${suffix ?? ""}`;
24217
24220
  }
24218
24221
  var variantColor, StatDisplay;
24219
24222
  var init_StatDisplay = __esm({
@@ -24237,6 +24240,10 @@ var init_StatDisplay = __esm({
24237
24240
  label,
24238
24241
  value,
24239
24242
  max,
24243
+ target,
24244
+ trend,
24245
+ prefix,
24246
+ suffix,
24240
24247
  icon: iconProp,
24241
24248
  iconBg = "bg-muted",
24242
24249
  iconColor = "text-foreground",
@@ -24252,7 +24259,13 @@ var init_StatDisplay = __esm({
24252
24259
  const iconSizes2 = { sm: "h-4 w-4", md: "h-5 w-5", lg: "h-6 w-6" };
24253
24260
  const valueSizes = { sm: "text-lg", md: "text-2xl", lg: "text-3xl" };
24254
24261
  const padSizes = { sm: "p-3", md: "p-4", lg: "p-6" };
24255
- const displayValue = formatValue3(value, format, max);
24262
+ const displayValue = composeDisplayValue(value, format, max, prefix, suffix);
24263
+ const numericValue = typeof value === "number" ? value : Number(value);
24264
+ const showTarget = typeof target === "number" && target > 0 && Number.isFinite(numericValue);
24265
+ const targetPct = showTarget ? Math.max(0, Math.min(100, numericValue / target * 100)) : 0;
24266
+ const showTrend = typeof trend === "number" && trend !== 0 && Number.isFinite(trend);
24267
+ const trendUp = showTrend && trend > 0;
24268
+ const trendLabel = showTrend ? `${trendUp ? "\u2191" : "\u2193"} ${Math.abs(trend)}` : "";
24256
24269
  if (error) {
24257
24270
  return /* @__PURE__ */ jsxRuntime.jsx(Card, { className: cn(padSizes[size], className), children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "error", children: error.message }) });
24258
24271
  }
@@ -24267,13 +24280,31 @@ var init_StatDisplay = __esm({
24267
24280
  ResolvedIcon && /* @__PURE__ */ jsxRuntime.jsx(ResolvedIcon, { className: cn(iconSizes2[size], iconColor) }),
24268
24281
  typeof iconProp !== "string" && iconProp,
24269
24282
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", color: "secondary", children: label }),
24270
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue })
24283
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue }),
24284
+ showTrend && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: cn("font-semibold", trendUp ? "text-success" : "text-error"), children: trendLabel })
24271
24285
  ] });
24272
24286
  }
24273
24287
  return /* @__PURE__ */ jsxRuntime.jsx(Card, { className: cn(padSizes[size], className), children: /* @__PURE__ */ jsxRuntime.jsxs(HStack, { align: "start", justify: "between", children: [
24274
- /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", className: "space-y-1", children: [
24288
+ /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "none", className: "space-y-1 flex-1", children: [
24275
24289
  /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "overline", color: "secondary", children: label }),
24276
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue })
24290
+ /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", align: "end", children: [
24291
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue }),
24292
+ showTrend && /* @__PURE__ */ jsxRuntime.jsx(
24293
+ Typography,
24294
+ {
24295
+ variant: "caption",
24296
+ className: cn("font-semibold pb-1", trendUp ? "text-success" : "text-error"),
24297
+ children: trendLabel
24298
+ }
24299
+ )
24300
+ ] }),
24301
+ showTarget && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "mt-2 h-1.5 w-full bg-muted rounded-full overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
24302
+ Box,
24303
+ {
24304
+ className: cn("h-full rounded-full transition-all", `bg-${variant === "default" ? "primary" : variant}`),
24305
+ style: { width: `${targetPct}%` }
24306
+ }
24307
+ ) })
24277
24308
  ] }),
24278
24309
  (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 })
24279
24310
  ] }) });
@@ -30434,7 +30465,7 @@ function getStatusStyle(fieldName, value) {
30434
30465
  if (val.includes("low")) return STATUS_STYLES2.low;
30435
30466
  return STATUS_STYLES2.default;
30436
30467
  }
30437
- function formatValue4(value, fieldName) {
30468
+ function formatValue3(value, fieldName) {
30438
30469
  if (typeof value === "number") {
30439
30470
  if (fieldName.toLowerCase().includes("progress") || fieldName.toLowerCase().includes("percent")) {
30440
30471
  return `${value}%`;
@@ -30810,7 +30841,7 @@ var init_List = __esm({
30810
30841
  className: "flex items-center gap-2 text-muted-foreground group-hover:text-foreground transition-colors",
30811
30842
  children: [
30812
30843
  /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Calendar, { className: "w-3.5 h-3.5" }),
30813
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", children: formatValue4(value, field) })
30844
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", children: formatValue3(value, field) })
30814
30845
  ]
30815
30846
  },
30816
30847
  field
@@ -30829,7 +30860,7 @@ var init_List = __esm({
30829
30860
  formatFieldLabel2(field),
30830
30861
  ":"
30831
30862
  ] }),
30832
- /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", className: "text-foreground", children: formatValue4(value, field) })
30863
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { as: "span", className: "text-foreground", children: formatValue3(value, field) })
30833
30864
  ]
30834
30865
  },
30835
30866
  field
@@ -3494,7 +3494,7 @@ var init_RangeSlider = __esm({
3494
3494
  action,
3495
3495
  actionPayload,
3496
3496
  onChange,
3497
- formatValue: formatValue5,
3497
+ formatValue: formatValue4,
3498
3498
  ...props
3499
3499
  }, ref) => {
3500
3500
  const [isDragging, setIsDragging] = useState(false);
@@ -3503,7 +3503,7 @@ var init_RangeSlider = __esm({
3503
3503
  const eventBus = useSafeEventBus();
3504
3504
  const percentage = max !== min ? (value - min) / (max - min) * 100 : 0;
3505
3505
  const bufferedPercentage = buffered !== void 0 ? Math.min(buffered, 100) : void 0;
3506
- const displayValue = formatValue5 ? formatValue5(value) : String(value);
3506
+ const displayValue = formatValue4 ? formatValue4(value) : String(value);
3507
3507
  const handleChange = useCallback(
3508
3508
  (e) => {
3509
3509
  const newValue = Number(e.target.value);
@@ -17024,6 +17024,7 @@ var init_Tabs = __esm({
17024
17024
  defaultActiveTab,
17025
17025
  activeTab: controlledActiveTab,
17026
17026
  onTabChange,
17027
+ tabChangeEvent,
17027
17028
  variant = "default",
17028
17029
  orientation = "horizontal",
17029
17030
  className
@@ -17046,6 +17047,9 @@ var init_Tabs = __esm({
17046
17047
  setInternalActiveTab(tabId);
17047
17048
  }
17048
17049
  onTabChange?.(tabId);
17050
+ if (tabChangeEvent) {
17051
+ eventBus.emit(`UI:${tabChangeEvent}`, { tabId });
17052
+ }
17049
17053
  if (tabEvent) {
17050
17054
  eventBus.emit(`UI:${tabEvent}`, { tabId });
17051
17055
  }
@@ -17126,7 +17130,7 @@ var init_Tabs = __esm({
17126
17130
  isActive ? variant === "pills" ? "text-primary-foreground font-bold" : "text-foreground font-bold" : "text-muted-foreground hover:text-foreground"
17127
17131
  ),
17128
17132
  children: [
17129
- item.icon && /* @__PURE__ */ jsx(Icon, { icon: item.icon, size: "sm" }),
17133
+ item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: item.icon, size: "sm" }) : /* @__PURE__ */ jsx(Icon, { icon: item.icon, size: "sm" })),
17130
17134
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: isActive ? "semibold" : "normal", className: "!text-inherit", children: item.label }),
17131
17135
  item.badge !== void 0 && /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", children: item.badge })
17132
17136
  ]
@@ -24150,25 +24154,24 @@ var init_Lightbox = __esm({
24150
24154
  Lightbox.displayName = "Lightbox";
24151
24155
  }
24152
24156
  });
24153
- function formatValue3(value, format, max) {
24157
+ function formatNumber(value, format) {
24154
24158
  if (value == null) return "0";
24155
24159
  const v = typeof value === "number" ? value : value;
24156
- let formatted;
24157
24160
  switch (format) {
24158
24161
  case "currency":
24159
- formatted = typeof v === "number" ? `$${v.toFixed(2)}` : String(v);
24160
- break;
24162
+ return typeof v === "number" ? `$${v.toFixed(2)}` : String(v);
24161
24163
  case "percent":
24162
- formatted = typeof v === "number" ? `${Math.round(v)}%` : String(v);
24163
- break;
24164
+ return typeof v === "number" ? `${Math.round(v)}%` : String(v);
24164
24165
  case "number":
24165
- formatted = typeof v === "number" ? v.toLocaleString() : String(v);
24166
- break;
24166
+ return typeof v === "number" ? v.toLocaleString() : String(v);
24167
24167
  default:
24168
- formatted = String(v);
24168
+ return String(v);
24169
24169
  }
24170
- if (max != null) return `${formatted} / ${max}`;
24171
- return formatted;
24170
+ }
24171
+ function composeDisplayValue(value, format, max, prefix, suffix) {
24172
+ const formatted = formatNumber(value, format);
24173
+ const withMax = max != null && max > 0 ? `${formatted} / ${max}` : formatted;
24174
+ return `${prefix ?? ""}${withMax}${suffix ?? ""}`;
24172
24175
  }
24173
24176
  var variantColor, StatDisplay;
24174
24177
  var init_StatDisplay = __esm({
@@ -24192,6 +24195,10 @@ var init_StatDisplay = __esm({
24192
24195
  label,
24193
24196
  value,
24194
24197
  max,
24198
+ target,
24199
+ trend,
24200
+ prefix,
24201
+ suffix,
24195
24202
  icon: iconProp,
24196
24203
  iconBg = "bg-muted",
24197
24204
  iconColor = "text-foreground",
@@ -24207,7 +24214,13 @@ var init_StatDisplay = __esm({
24207
24214
  const iconSizes2 = { sm: "h-4 w-4", md: "h-5 w-5", lg: "h-6 w-6" };
24208
24215
  const valueSizes = { sm: "text-lg", md: "text-2xl", lg: "text-3xl" };
24209
24216
  const padSizes = { sm: "p-3", md: "p-4", lg: "p-6" };
24210
- const displayValue = formatValue3(value, format, max);
24217
+ const displayValue = composeDisplayValue(value, format, max, prefix, suffix);
24218
+ const numericValue = typeof value === "number" ? value : Number(value);
24219
+ const showTarget = typeof target === "number" && target > 0 && Number.isFinite(numericValue);
24220
+ const targetPct = showTarget ? Math.max(0, Math.min(100, numericValue / target * 100)) : 0;
24221
+ const showTrend = typeof trend === "number" && trend !== 0 && Number.isFinite(trend);
24222
+ const trendUp = showTrend && trend > 0;
24223
+ const trendLabel = showTrend ? `${trendUp ? "\u2191" : "\u2193"} ${Math.abs(trend)}` : "";
24211
24224
  if (error) {
24212
24225
  return /* @__PURE__ */ jsx(Card, { className: cn(padSizes[size], className), children: /* @__PURE__ */ jsx(Typography, { variant: "small", color: "error", children: error.message }) });
24213
24226
  }
@@ -24222,13 +24235,31 @@ var init_StatDisplay = __esm({
24222
24235
  ResolvedIcon && /* @__PURE__ */ jsx(ResolvedIcon, { className: cn(iconSizes2[size], iconColor) }),
24223
24236
  typeof iconProp !== "string" && iconProp,
24224
24237
  /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: label }),
24225
- /* @__PURE__ */ jsx(Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue })
24238
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue }),
24239
+ showTrend && /* @__PURE__ */ jsx(Typography, { variant: "caption", className: cn("font-semibold", trendUp ? "text-success" : "text-error"), children: trendLabel })
24226
24240
  ] });
24227
24241
  }
24228
24242
  return /* @__PURE__ */ jsx(Card, { className: cn(padSizes[size], className), children: /* @__PURE__ */ jsxs(HStack, { align: "start", justify: "between", children: [
24229
- /* @__PURE__ */ jsxs(VStack, { gap: "none", className: "space-y-1", children: [
24243
+ /* @__PURE__ */ jsxs(VStack, { gap: "none", className: "space-y-1 flex-1", children: [
24230
24244
  /* @__PURE__ */ jsx(Typography, { variant: "overline", color: "secondary", children: label }),
24231
- /* @__PURE__ */ jsx(Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue })
24245
+ /* @__PURE__ */ jsxs(HStack, { gap: "sm", align: "end", children: [
24246
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue }),
24247
+ showTrend && /* @__PURE__ */ jsx(
24248
+ Typography,
24249
+ {
24250
+ variant: "caption",
24251
+ className: cn("font-semibold pb-1", trendUp ? "text-success" : "text-error"),
24252
+ children: trendLabel
24253
+ }
24254
+ )
24255
+ ] }),
24256
+ showTarget && /* @__PURE__ */ jsx(Box, { className: "mt-2 h-1.5 w-full bg-muted rounded-full overflow-hidden", children: /* @__PURE__ */ jsx(
24257
+ Box,
24258
+ {
24259
+ className: cn("h-full rounded-full transition-all", `bg-${variant === "default" ? "primary" : variant}`),
24260
+ style: { width: `${targetPct}%` }
24261
+ }
24262
+ ) })
24232
24263
  ] }),
24233
24264
  (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 })
24234
24265
  ] }) });
@@ -30389,7 +30420,7 @@ function getStatusStyle(fieldName, value) {
30389
30420
  if (val.includes("low")) return STATUS_STYLES2.low;
30390
30421
  return STATUS_STYLES2.default;
30391
30422
  }
30392
- function formatValue4(value, fieldName) {
30423
+ function formatValue3(value, fieldName) {
30393
30424
  if (typeof value === "number") {
30394
30425
  if (fieldName.toLowerCase().includes("progress") || fieldName.toLowerCase().includes("percent")) {
30395
30426
  return `${value}%`;
@@ -30765,7 +30796,7 @@ var init_List = __esm({
30765
30796
  className: "flex items-center gap-2 text-muted-foreground group-hover:text-foreground transition-colors",
30766
30797
  children: [
30767
30798
  /* @__PURE__ */ jsx(Calendar, { className: "w-3.5 h-3.5" }),
30768
- /* @__PURE__ */ jsx(Typography, { as: "span", children: formatValue4(value, field) })
30799
+ /* @__PURE__ */ jsx(Typography, { as: "span", children: formatValue3(value, field) })
30769
30800
  ]
30770
30801
  },
30771
30802
  field
@@ -30784,7 +30815,7 @@ var init_List = __esm({
30784
30815
  formatFieldLabel2(field),
30785
30816
  ":"
30786
30817
  ] }),
30787
- /* @__PURE__ */ jsx(Typography, { as: "span", className: "text-foreground", children: formatValue4(value, field) })
30818
+ /* @__PURE__ */ jsx(Typography, { as: "span", className: "text-foreground", children: formatValue3(value, field) })
30788
30819
  ]
30789
30820
  },
30790
30821
  field