@pos-360/horizon 0.24.0 → 0.26.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.
@@ -138,6 +138,159 @@ var Dashboard = React.forwardRef(
138
138
  }
139
139
  );
140
140
  Dashboard.displayName = "Dashboard";
141
+ var TrendBadge = ({ trend, size }) => {
142
+ const Icon = trend.direction === "up" ? lucideReact.TrendingUp : trend.direction === "down" ? lucideReact.TrendingDown : lucideReact.Minus;
143
+ const iconSize = size === "sm" ? "w-3 h-3" : "w-3.5 h-3.5";
144
+ return /* @__PURE__ */ jsxRuntime.jsxs(
145
+ "div",
146
+ {
147
+ className: chunk5XF7Y25B_js.cn(
148
+ "inline-flex items-center gap-1 px-2 py-0.5 rounded-hz-full font-medium",
149
+ size === "sm" && "text-xs",
150
+ size === "md" && "text-xs",
151
+ size === "lg" && "text-sm",
152
+ trend.direction === "up" && "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400",
153
+ trend.direction === "down" && "bg-rose-100 text-rose-700 dark:bg-rose-900/30 dark:text-rose-400",
154
+ trend.direction === "neutral" && "bg-gray-100 text-gray-600 dark:bg-neutral-800 dark:text-gray-400"
155
+ ),
156
+ children: [
157
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: iconSize }),
158
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: trend.value === null ? "N/A" : `${Math.abs(trend.value).toFixed(1)}%` })
159
+ ]
160
+ }
161
+ );
162
+ };
163
+ var StatDisplay = React.forwardRef(
164
+ ({ data, size = "md", className }, ref) => {
165
+ const { value, label, trend, prefix = "", suffix = "" } = data;
166
+ const formattedValue = React.useMemo(() => {
167
+ if (typeof value === "number") {
168
+ return value.toLocaleString("en-US", {
169
+ minimumFractionDigits: 0,
170
+ maximumFractionDigits: 2
171
+ });
172
+ }
173
+ return value;
174
+ }, [value]);
175
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref, className: chunk5XF7Y25B_js.cn("flex flex-col", className), children: [
176
+ /* @__PURE__ */ jsxRuntime.jsx(
177
+ chunk5XF7Y25B_js.Text,
178
+ {
179
+ as: "span",
180
+ size: "xs",
181
+ color: "muted",
182
+ className: "uppercase tracking-wide",
183
+ children: label
184
+ }
185
+ ),
186
+ /* @__PURE__ */ jsxRuntime.jsxs(
187
+ "span",
188
+ {
189
+ className: chunk5XF7Y25B_js.cn(
190
+ "font-bold text-gray-900 dark:text-gray-100 mt-0.5 font-mono",
191
+ size === "sm" && "text-lg",
192
+ size === "md" && "text-xl",
193
+ size === "lg" && "text-2xl"
194
+ ),
195
+ children: [
196
+ prefix,
197
+ formattedValue,
198
+ suffix
199
+ ]
200
+ }
201
+ ),
202
+ trend && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mt-1", children: [
203
+ /* @__PURE__ */ jsxRuntime.jsx(TrendBadge, { trend, size }),
204
+ trend.label && /* @__PURE__ */ jsxRuntime.jsx(chunk5XF7Y25B_js.Text, { as: "span", size: "xs", color: "muted", children: trend.label })
205
+ ] })
206
+ ] });
207
+ }
208
+ );
209
+ StatDisplay.displayName = "StatDisplay";
210
+ var KPI_CARD_ROWS = {
211
+ /** A single standard KpiCard (~80px) */
212
+ standard: 2,
213
+ /** The featured/hero variant (~110px) */
214
+ featured: 3,
215
+ /**
216
+ * A KpiCardGroup with one featured card + one row of supporting cards (~250px).
217
+ * Add 2 for each additional row of supporting cards.
218
+ */
219
+ group: 6
220
+ };
221
+ var LARGE_PANEL_HEADER_ROWS = 1;
222
+ function KpiCardGroup({
223
+ cards,
224
+ featured,
225
+ columns = 4,
226
+ gap = 4,
227
+ className
228
+ }) {
229
+ const colClass = {
230
+ 2: "grid-cols-2",
231
+ 3: "grid-cols-3",
232
+ 4: "grid-cols-2 md:grid-cols-4",
233
+ 5: "grid-cols-2 md:grid-cols-5"
234
+ };
235
+ const gapClass = `gap-${gap}`;
236
+ const featuredCard = featured ? cards.find((c) => c.id === featured) : null;
237
+ const supportingCards = featured ? cards.filter((c) => c.id !== featured) : cards;
238
+ const supportingCols = Math.min(supportingCards.length, columns);
239
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: chunk5XF7Y25B_js.cn("flex flex-col", gapClass, className), children: [
240
+ featuredCard && /* @__PURE__ */ jsxRuntime.jsx(KpiCard, { ...featuredCard, featured: true }),
241
+ supportingCards.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: chunk5XF7Y25B_js.cn("grid", colClass[supportingCols], gapClass), children: supportingCards.map((card) => /* @__PURE__ */ jsxRuntime.jsx(KpiCard, { ...card, featured: false }, card.id)) })
242
+ ] });
243
+ }
244
+ function KpiCard({
245
+ label,
246
+ value,
247
+ prefix,
248
+ suffix,
249
+ trend,
250
+ size = "md",
251
+ featured = false,
252
+ className
253
+ }) {
254
+ const formattedValue = React.useMemo(() => {
255
+ if (typeof value === "number") {
256
+ return value.toLocaleString("en-US", { minimumFractionDigits: 0, maximumFractionDigits: 2 });
257
+ }
258
+ return value;
259
+ }, [value]);
260
+ if (featured) {
261
+ const Icon = trend?.direction === "up" ? lucideReact.TrendingUp : trend?.direction === "down" ? lucideReact.TrendingDown : lucideReact.Minus;
262
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: chunk5XF7Y25B_js.cn("p-4", className), children: [
263
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-semibold uppercase tracking-wide text-blue-500 dark:text-blue-400", children: label }),
264
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-4xl font-bold text-gray-900 dark:text-gray-100 mt-1 font-mono leading-none", children: [
265
+ prefix,
266
+ formattedValue,
267
+ suffix
268
+ ] }),
269
+ trend && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mt-2", children: [
270
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: chunk5XF7Y25B_js.cn(
271
+ "inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium",
272
+ trend.direction === "up" && "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400",
273
+ trend.direction === "down" && "bg-rose-100 text-rose-700 dark:bg-rose-900/30 dark:text-rose-400",
274
+ trend.direction === "neutral" && "bg-gray-100 text-gray-600 dark:bg-neutral-800 dark:text-gray-400"
275
+ ), children: [
276
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "w-3 h-3" }),
277
+ trend.value === null ? "N/A" : `${Math.abs(trend.value).toFixed(1)}%`
278
+ ] }),
279
+ trend.label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-500 dark:text-gray-400", children: trend.label })
280
+ ] })
281
+ ] });
282
+ }
283
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: chunk5XF7Y25B_js.cn(
284
+ "bg-white dark:bg-neutral-900 border border-gray-100 dark:border-neutral-800 rounded-lg p-4",
285
+ className
286
+ ), children: /* @__PURE__ */ jsxRuntime.jsx(
287
+ StatDisplay,
288
+ {
289
+ data: { label, value, prefix, suffix, trend },
290
+ size
291
+ }
292
+ ) });
293
+ }
141
294
  var breakpoints2 = {
142
295
  sm: 640,
143
296
  md: 768,
@@ -212,78 +365,6 @@ var DashboardPanel = React.forwardRef(
212
365
  }
213
366
  );
214
367
  DashboardPanel.displayName = "DashboardPanel";
215
- var TrendBadge = ({ trend, size }) => {
216
- const Icon = trend.direction === "up" ? lucideReact.TrendingUp : trend.direction === "down" ? lucideReact.TrendingDown : lucideReact.Minus;
217
- const iconSize = size === "sm" ? "w-3 h-3" : "w-3.5 h-3.5";
218
- return /* @__PURE__ */ jsxRuntime.jsxs(
219
- "div",
220
- {
221
- className: chunk5XF7Y25B_js.cn(
222
- "inline-flex items-center gap-1 px-2 py-0.5 rounded-hz-full font-medium",
223
- size === "sm" && "text-xs",
224
- size === "md" && "text-xs",
225
- size === "lg" && "text-sm",
226
- trend.direction === "up" && "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400",
227
- trend.direction === "down" && "bg-rose-100 text-rose-700 dark:bg-rose-900/30 dark:text-rose-400",
228
- trend.direction === "neutral" && "bg-gray-100 text-gray-600 dark:bg-neutral-800 dark:text-gray-400"
229
- ),
230
- children: [
231
- /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: iconSize }),
232
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
233
- Math.abs(trend.value).toFixed(1),
234
- "%"
235
- ] })
236
- ]
237
- }
238
- );
239
- };
240
- var StatDisplay = React.forwardRef(
241
- ({ data, size = "md", className }, ref) => {
242
- const { value, label, trend, prefix = "", suffix = "" } = data;
243
- const formattedValue = React.useMemo(() => {
244
- if (typeof value === "number") {
245
- return value.toLocaleString("en-US", {
246
- minimumFractionDigits: 0,
247
- maximumFractionDigits: 2
248
- });
249
- }
250
- return value;
251
- }, [value]);
252
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref, className: chunk5XF7Y25B_js.cn("flex flex-col", className), children: [
253
- /* @__PURE__ */ jsxRuntime.jsx(
254
- chunk5XF7Y25B_js.Text,
255
- {
256
- as: "span",
257
- size: "xs",
258
- color: "muted",
259
- className: "uppercase tracking-wide",
260
- children: label
261
- }
262
- ),
263
- /* @__PURE__ */ jsxRuntime.jsxs(
264
- "span",
265
- {
266
- className: chunk5XF7Y25B_js.cn(
267
- "font-bold text-gray-900 dark:text-gray-100 mt-0.5",
268
- size === "sm" && "text-lg",
269
- size === "md" && "text-xl",
270
- size === "lg" && "text-2xl"
271
- ),
272
- children: [
273
- prefix,
274
- formattedValue,
275
- suffix
276
- ]
277
- }
278
- ),
279
- trend && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mt-1", children: [
280
- /* @__PURE__ */ jsxRuntime.jsx(TrendBadge, { trend, size }),
281
- trend.label && /* @__PURE__ */ jsxRuntime.jsx(chunk5XF7Y25B_js.Text, { as: "span", size: "xs", color: "muted", children: trend.label })
282
- ] })
283
- ] });
284
- }
285
- );
286
- StatDisplay.displayName = "StatDisplay";
287
368
  var defaultColors = [
288
369
  "#3b82f6",
289
370
  // blue-500
@@ -912,10 +993,7 @@ var TrendIndicator = ({ trend }) => {
912
993
  ),
913
994
  children: [
914
995
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "w-4 h-4" }),
915
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm font-semibold", children: [
916
- Math.abs(trend.value).toFixed(1),
917
- "%"
918
- ] })
996
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold", children: trend.value === null ? "N/A" : `${Math.abs(trend.value).toFixed(1)}%` })
919
997
  ]
920
998
  }
921
999
  );
@@ -2670,6 +2748,10 @@ exports.CompactPanel = CompactPanel;
2670
2748
  exports.Dashboard = Dashboard;
2671
2749
  exports.DashboardPanel = DashboardPanel;
2672
2750
  exports.Input = Input;
2751
+ exports.KPI_CARD_ROWS = KPI_CARD_ROWS;
2752
+ exports.KpiCard = KpiCard;
2753
+ exports.KpiCardGroup = KpiCardGroup;
2754
+ exports.LARGE_PANEL_HEADER_ROWS = LARGE_PANEL_HEADER_ROWS;
2673
2755
  exports.LargePanel = LargePanel;
2674
2756
  exports.MediumPanel = MediumPanel;
2675
2757
  exports.SideNav = SideNav;
@@ -2684,5 +2766,5 @@ exports.TextButton = TextButton;
2684
2766
  exports.Toast = Toast;
2685
2767
  exports.useDashboardContext = useDashboardContext;
2686
2768
  exports.useSideNavContext = useSideNavContext;
2687
- //# sourceMappingURL=chunk-VELIN34G.js.map
2688
- //# sourceMappingURL=chunk-VELIN34G.js.map
2769
+ //# sourceMappingURL=chunk-3LLPK34Q.js.map
2770
+ //# sourceMappingURL=chunk-3LLPK34Q.js.map