@diwauhris/ui 1.4.1 → 1.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/assets/ui.css +8 -0
- package/lib/index.cjs +333 -6
- package/lib/index.mjs +321 -9
- package/lib/types/ui-library/gallery/heatmap/Heatmap.d.ts +40 -0
- package/lib/types/ui-library/gallery/stat-card/Statistic.d.ts +29 -0
- package/lib/types/ui-library/gallery/status-dot/StatusDot.d.ts +17 -0
- package/lib/types/ui-library/index.d.ts +7 -0
- package/package.json +1 -1
package/lib/assets/ui.css
CHANGED
|
@@ -2490,6 +2490,10 @@
|
|
|
2490
2490
|
background-color: rgb(45 138 202 / 0.3);
|
|
2491
2491
|
}
|
|
2492
2492
|
|
|
2493
|
+
.bg-brand-sky\/40 {
|
|
2494
|
+
background-color: rgb(45 138 202 / 0.4);
|
|
2495
|
+
}
|
|
2496
|
+
|
|
2493
2497
|
.bg-brand-sky\/5 {
|
|
2494
2498
|
background-color: rgb(45 138 202 / 0.05);
|
|
2495
2499
|
}
|
|
@@ -2502,6 +2506,10 @@
|
|
|
2502
2506
|
background-color: rgb(45 138 202 / 0.6);
|
|
2503
2507
|
}
|
|
2504
2508
|
|
|
2509
|
+
.bg-brand-sky\/70 {
|
|
2510
|
+
background-color: rgb(45 138 202 / 0.7);
|
|
2511
|
+
}
|
|
2512
|
+
|
|
2505
2513
|
.bg-brand-sky\/\[0\.04\] {
|
|
2506
2514
|
background-color: rgb(45 138 202 / 0.04);
|
|
2507
2515
|
}
|
package/lib/index.cjs
CHANGED
|
@@ -46,6 +46,7 @@ _radix_ui_react_popover = __toESM(_radix_ui_react_popover, 1);
|
|
|
46
46
|
let _radix_ui_react_tooltip = require("@radix-ui/react-tooltip");
|
|
47
47
|
_radix_ui_react_tooltip = __toESM(_radix_ui_react_tooltip, 1);
|
|
48
48
|
let react_router_dom = require("react-router-dom");
|
|
49
|
+
let sonner = require("sonner");
|
|
49
50
|
//#region src/ui-library/gallery/button/Button.tsx
|
|
50
51
|
var VARIANT_STYLES$1 = {
|
|
51
52
|
primary: "font-bold bg-brand-blue text-white shadow-lg shadow-brand-blue/25 hover:bg-brand-navy hover:shadow-xl hover:shadow-brand-blue/30 active:bg-brand-navy focus-visible:ring-brand-blue/30 disabled:bg-slate-200 disabled:text-slate-400 disabled:shadow-none",
|
|
@@ -4225,7 +4226,7 @@ function PermissionMatrix({ roles, permissions, caption = "Permission matrix", c
|
|
|
4225
4226
|
* - Keyboard: ArrowKeys navigate days, Enter/Space select
|
|
4226
4227
|
* - Previous/Next month buttons have aria-label
|
|
4227
4228
|
*/
|
|
4228
|
-
var MONTHS = [
|
|
4229
|
+
var MONTHS$1 = [
|
|
4229
4230
|
"January",
|
|
4230
4231
|
"February",
|
|
4231
4232
|
"March",
|
|
@@ -4239,7 +4240,7 @@ var MONTHS = [
|
|
|
4239
4240
|
"November",
|
|
4240
4241
|
"December"
|
|
4241
4242
|
];
|
|
4242
|
-
var DAYS = [
|
|
4243
|
+
var DAYS$1 = [
|
|
4243
4244
|
"Su",
|
|
4244
4245
|
"Mo",
|
|
4245
4246
|
"Tu",
|
|
@@ -4301,7 +4302,7 @@ function Calendar({ value, defaultValue, onChange, minDate, maxDate, className =
|
|
|
4301
4302
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4302
4303
|
className: "text-sm font-bold text-slate-800",
|
|
4303
4304
|
children: [
|
|
4304
|
-
MONTHS[viewMonth],
|
|
4305
|
+
MONTHS$1[viewMonth],
|
|
4305
4306
|
" ",
|
|
4306
4307
|
viewYear
|
|
4307
4308
|
]
|
|
@@ -4319,9 +4320,9 @@ function Calendar({ value, defaultValue, onChange, minDate, maxDate, className =
|
|
|
4319
4320
|
]
|
|
4320
4321
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("table", {
|
|
4321
4322
|
role: "grid",
|
|
4322
|
-
"aria-label": `${MONTHS[viewMonth]} ${viewYear}`,
|
|
4323
|
+
"aria-label": `${MONTHS$1[viewMonth]} ${viewYear}`,
|
|
4323
4324
|
className: "w-full border-collapse",
|
|
4324
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tr", { children: DAYS.map((d) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", {
|
|
4325
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tr", { children: DAYS$1.map((d) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", {
|
|
4325
4326
|
scope: "col",
|
|
4326
4327
|
className: "pb-2 text-center text-[10px] font-bold uppercase tracking-widest text-slate-400",
|
|
4327
4328
|
children: d
|
|
@@ -4334,7 +4335,7 @@ function Calendar({ value, defaultValue, onChange, minDate, maxDate, className =
|
|
|
4334
4335
|
const isToday = isSameDay(date, today);
|
|
4335
4336
|
const isSelected = !!selected && isSameDay(date, selected);
|
|
4336
4337
|
const isDisabled = (minDate ? date < minDate : false) || (maxDate ? date > maxDate : false);
|
|
4337
|
-
const label = `${day} ${MONTHS[viewMonth]} ${viewYear}${isToday ? ", Today" : ""}${isSelected ? ", Selected" : ""}`;
|
|
4338
|
+
const label = `${day} ${MONTHS$1[viewMonth]} ${viewYear}${isToday ? ", Today" : ""}${isSelected ? ", Selected" : ""}`;
|
|
4338
4339
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
|
|
4339
4340
|
role: "gridcell",
|
|
4340
4341
|
"aria-label": label,
|
|
@@ -4461,6 +4462,317 @@ function LineChart({ data, height = 120, lineColor = "#6366f1", filled = true, "
|
|
|
4461
4462
|
});
|
|
4462
4463
|
}
|
|
4463
4464
|
//#endregion
|
|
4465
|
+
//#region src/ui-library/gallery/heatmap/Heatmap.tsx
|
|
4466
|
+
/**
|
|
4467
|
+
* Heatmap — Design System Component
|
|
4468
|
+
*
|
|
4469
|
+
* A GitHub contribution-style calendar heatmap.
|
|
4470
|
+
* HRIS use cases: attendance patterns, leave frequency, approval volume.
|
|
4471
|
+
*
|
|
4472
|
+
* Usage:
|
|
4473
|
+
* const data = [
|
|
4474
|
+
* { date: '2025-01-01', value: 3 },
|
|
4475
|
+
* { date: '2025-01-02', value: 0 },
|
|
4476
|
+
* ...
|
|
4477
|
+
* ];
|
|
4478
|
+
* <Heatmap data={data} aria-label="Attendance heatmap" />
|
|
4479
|
+
*/
|
|
4480
|
+
var SCALES = {
|
|
4481
|
+
blue: { cells: [
|
|
4482
|
+
"bg-slate-100",
|
|
4483
|
+
"bg-brand-sky/20",
|
|
4484
|
+
"bg-brand-sky/40",
|
|
4485
|
+
"bg-brand-sky/70",
|
|
4486
|
+
"bg-brand-sky"
|
|
4487
|
+
] },
|
|
4488
|
+
navy: { cells: [
|
|
4489
|
+
"bg-slate-100",
|
|
4490
|
+
"bg-brand-navy/15",
|
|
4491
|
+
"bg-brand-navy/35",
|
|
4492
|
+
"bg-brand-navy/65",
|
|
4493
|
+
"bg-brand-navy"
|
|
4494
|
+
] },
|
|
4495
|
+
teal: { cells: [
|
|
4496
|
+
"bg-slate-100",
|
|
4497
|
+
"bg-cyan-100",
|
|
4498
|
+
"bg-cyan-300",
|
|
4499
|
+
"bg-cyan-500",
|
|
4500
|
+
"bg-cyan-700"
|
|
4501
|
+
] },
|
|
4502
|
+
orange: { cells: [
|
|
4503
|
+
"bg-slate-100",
|
|
4504
|
+
"bg-orange-100",
|
|
4505
|
+
"bg-orange-300",
|
|
4506
|
+
"bg-orange-400",
|
|
4507
|
+
"bg-orange-600"
|
|
4508
|
+
] }
|
|
4509
|
+
};
|
|
4510
|
+
var MONTHS = [
|
|
4511
|
+
"Jan",
|
|
4512
|
+
"Feb",
|
|
4513
|
+
"Mar",
|
|
4514
|
+
"Apr",
|
|
4515
|
+
"May",
|
|
4516
|
+
"Jun",
|
|
4517
|
+
"Jul",
|
|
4518
|
+
"Aug",
|
|
4519
|
+
"Sep",
|
|
4520
|
+
"Oct",
|
|
4521
|
+
"Nov",
|
|
4522
|
+
"Dec"
|
|
4523
|
+
];
|
|
4524
|
+
var DAYS = [
|
|
4525
|
+
"Sun",
|
|
4526
|
+
"Mon",
|
|
4527
|
+
"Tue",
|
|
4528
|
+
"Wed",
|
|
4529
|
+
"Thu",
|
|
4530
|
+
"Fri",
|
|
4531
|
+
"Sat"
|
|
4532
|
+
];
|
|
4533
|
+
function toIntensity(value) {
|
|
4534
|
+
if (value === 0) return 0;
|
|
4535
|
+
if (value <= 2) return 1;
|
|
4536
|
+
if (value <= 5) return 2;
|
|
4537
|
+
if (value <= 7) return 3;
|
|
4538
|
+
return 4;
|
|
4539
|
+
}
|
|
4540
|
+
function Heatmap({ data, scale = "blue", showLegend = true, showMonthLabels = true, showDayLabels = true, size = "md", onCellHover, "aria-label": ariaLabel = "Activity heatmap" }) {
|
|
4541
|
+
const colors = SCALES[scale].cells;
|
|
4542
|
+
const cellSize = size === "sm" ? "w-2.5 h-2.5" : "w-3.5 h-3.5";
|
|
4543
|
+
const gap = size === "sm" ? "gap-[3px]" : "gap-[4px]";
|
|
4544
|
+
const cells = (0, react.useMemo)(() => data.map((d) => ({
|
|
4545
|
+
...d,
|
|
4546
|
+
intensity: toIntensity(d.value)
|
|
4547
|
+
})), [data]);
|
|
4548
|
+
const weeks = (0, react.useMemo)(() => {
|
|
4549
|
+
const cols = [];
|
|
4550
|
+
let week = [];
|
|
4551
|
+
cells.forEach((cell, i) => {
|
|
4552
|
+
week.push(cell);
|
|
4553
|
+
if (week.length === 7 || i === cells.length - 1) {
|
|
4554
|
+
cols.push(week);
|
|
4555
|
+
week = [];
|
|
4556
|
+
}
|
|
4557
|
+
});
|
|
4558
|
+
return cols;
|
|
4559
|
+
}, [cells]);
|
|
4560
|
+
const monthLabels = (0, react.useMemo)(() => {
|
|
4561
|
+
const labels = [];
|
|
4562
|
+
let lastMonth = -1;
|
|
4563
|
+
weeks.forEach((week, colIdx) => {
|
|
4564
|
+
const firstDay = week[0];
|
|
4565
|
+
if (!firstDay) return;
|
|
4566
|
+
const m = new Date(firstDay.date).getMonth();
|
|
4567
|
+
if (m !== lastMonth) {
|
|
4568
|
+
labels.push({
|
|
4569
|
+
label: MONTHS[m],
|
|
4570
|
+
col: colIdx
|
|
4571
|
+
});
|
|
4572
|
+
lastMonth = m;
|
|
4573
|
+
}
|
|
4574
|
+
});
|
|
4575
|
+
return labels;
|
|
4576
|
+
}, [weeks]);
|
|
4577
|
+
const colW = size === "sm" ? 13 : 19;
|
|
4578
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4579
|
+
className: "w-full overflow-x-auto",
|
|
4580
|
+
"aria-label": ariaLabel,
|
|
4581
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4582
|
+
className: "inline-flex flex-col gap-1.5",
|
|
4583
|
+
children: [
|
|
4584
|
+
showMonthLabels && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4585
|
+
className: "relative h-4",
|
|
4586
|
+
style: { width: weeks.length * colW },
|
|
4587
|
+
children: monthLabels.map(({ label, col }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4588
|
+
className: "absolute text-[10px] font-bold text-slate-400",
|
|
4589
|
+
style: { left: col * colW },
|
|
4590
|
+
children: label
|
|
4591
|
+
}, `${label}-${col}`))
|
|
4592
|
+
}),
|
|
4593
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4594
|
+
className: "flex items-start gap-1",
|
|
4595
|
+
children: [showDayLabels && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4596
|
+
className: "flex flex-col gap-[4px] mr-1",
|
|
4597
|
+
children: DAYS.map((d, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4598
|
+
className: `text-[10px] font-bold text-slate-400 leading-none ${size === "sm" ? "h-2.5" : "h-3.5"} flex items-center ${i % 2 === 0 ? "opacity-0" : ""}`,
|
|
4599
|
+
"aria-hidden": i % 2 === 0,
|
|
4600
|
+
children: d
|
|
4601
|
+
}, d))
|
|
4602
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4603
|
+
className: `flex ${gap}`,
|
|
4604
|
+
children: weeks.map((week, colIdx) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4605
|
+
className: `flex flex-col ${gap}`,
|
|
4606
|
+
children: week.map((cell) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4607
|
+
type: "button",
|
|
4608
|
+
title: `${cell.date}: ${cell.value}`,
|
|
4609
|
+
"aria-label": `${cell.date}: ${cell.value} activities`,
|
|
4610
|
+
onMouseEnter: () => onCellHover?.(cell),
|
|
4611
|
+
onMouseLeave: () => onCellHover?.(null),
|
|
4612
|
+
onFocus: () => onCellHover?.(cell),
|
|
4613
|
+
onBlur: () => onCellHover?.(null),
|
|
4614
|
+
className: [
|
|
4615
|
+
cellSize,
|
|
4616
|
+
"rounded-sm transition-transform hover:scale-125 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-brand-blue/50",
|
|
4617
|
+
colors[cell.intensity]
|
|
4618
|
+
].join(" ")
|
|
4619
|
+
}, cell.date))
|
|
4620
|
+
}, colIdx))
|
|
4621
|
+
})]
|
|
4622
|
+
}),
|
|
4623
|
+
showLegend && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4624
|
+
className: "flex items-center gap-2 mt-1",
|
|
4625
|
+
children: [
|
|
4626
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4627
|
+
className: "text-[10px] font-bold text-slate-400",
|
|
4628
|
+
children: "Less"
|
|
4629
|
+
}),
|
|
4630
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4631
|
+
className: "flex gap-1",
|
|
4632
|
+
children: colors.map((c, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4633
|
+
className: `w-3 h-3 rounded-sm ${c}`,
|
|
4634
|
+
"aria-hidden": "true"
|
|
4635
|
+
}, i))
|
|
4636
|
+
}),
|
|
4637
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4638
|
+
className: "text-[10px] font-bold text-slate-400",
|
|
4639
|
+
children: "More"
|
|
4640
|
+
})
|
|
4641
|
+
]
|
|
4642
|
+
})
|
|
4643
|
+
]
|
|
4644
|
+
})
|
|
4645
|
+
});
|
|
4646
|
+
}
|
|
4647
|
+
//#endregion
|
|
4648
|
+
//#region src/ui-library/gallery/stat-card/Statistic.tsx
|
|
4649
|
+
/**
|
|
4650
|
+
* Statistic — Design System Component
|
|
4651
|
+
*
|
|
4652
|
+
* A KPI card. Four variants: default, accent, minimal, compact.
|
|
4653
|
+
* Signature element: brand-navy left border strip + font-heading number.
|
|
4654
|
+
*
|
|
4655
|
+
* Usage:
|
|
4656
|
+
* <Statistic label="Total Employees" value={142} icon={Users} />
|
|
4657
|
+
* <Statistic label="Fill Rate" value="83%" variant="accent" trend="up" trendLabel="+2%" />
|
|
4658
|
+
*/
|
|
4659
|
+
var TREND_CONFIG = {
|
|
4660
|
+
up: {
|
|
4661
|
+
icon: lucide_react.TrendingUp,
|
|
4662
|
+
color: "text-emerald-600",
|
|
4663
|
+
label: "Up"
|
|
4664
|
+
},
|
|
4665
|
+
down: {
|
|
4666
|
+
icon: lucide_react.TrendingDown,
|
|
4667
|
+
color: "text-rose-500",
|
|
4668
|
+
label: "Down"
|
|
4669
|
+
},
|
|
4670
|
+
neutral: {
|
|
4671
|
+
icon: lucide_react.Minus,
|
|
4672
|
+
color: "text-slate-400",
|
|
4673
|
+
label: "No change"
|
|
4674
|
+
}
|
|
4675
|
+
};
|
|
4676
|
+
function Statistic({ label, value, helper, icon: Icon, trend, trendLabel, variant = "default" }) {
|
|
4677
|
+
if (variant === "minimal") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
|
|
4678
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4679
|
+
className: "text-[11px] font-bold uppercase tracking-[0.18em] text-brand-sky",
|
|
4680
|
+
children: label
|
|
4681
|
+
}),
|
|
4682
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4683
|
+
className: "mt-1 font-heading text-4xl font-bold tabular-nums text-brand-navy",
|
|
4684
|
+
children: value
|
|
4685
|
+
}),
|
|
4686
|
+
helper && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4687
|
+
className: "mt-1 text-xs font-medium text-slate-400",
|
|
4688
|
+
children: helper
|
|
4689
|
+
})
|
|
4690
|
+
] });
|
|
4691
|
+
if (variant === "compact") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4692
|
+
className: "flex items-center gap-4 rounded-2xl border border-slate-200 bg-white px-5 py-4 shadow-sm",
|
|
4693
|
+
children: [Icon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4694
|
+
className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-brand-navy/[0.06] text-brand-navy",
|
|
4695
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {
|
|
4696
|
+
size: 18,
|
|
4697
|
+
"aria-hidden": "true"
|
|
4698
|
+
})
|
|
4699
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4700
|
+
className: "min-w-0",
|
|
4701
|
+
children: [
|
|
4702
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4703
|
+
className: "text-[11px] font-bold uppercase tracking-[0.18em] text-slate-400 truncate",
|
|
4704
|
+
children: label
|
|
4705
|
+
}),
|
|
4706
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4707
|
+
className: "font-heading text-2xl font-bold tabular-nums text-brand-navy leading-none mt-0.5",
|
|
4708
|
+
children: value
|
|
4709
|
+
}),
|
|
4710
|
+
helper && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4711
|
+
className: "mt-0.5 text-xs font-medium text-slate-400 truncate",
|
|
4712
|
+
children: helper
|
|
4713
|
+
})
|
|
4714
|
+
]
|
|
4715
|
+
})]
|
|
4716
|
+
});
|
|
4717
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4718
|
+
className: "relative overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm",
|
|
4719
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4720
|
+
className: `absolute inset-y-0 left-0 w-1 ${variant === "accent" ? "bg-brand-blue" : "bg-brand-navy"}`,
|
|
4721
|
+
"aria-hidden": "true"
|
|
4722
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4723
|
+
className: "px-6 py-5",
|
|
4724
|
+
children: [
|
|
4725
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4726
|
+
className: "flex items-start justify-between gap-3",
|
|
4727
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4728
|
+
className: "text-[11px] font-bold uppercase tracking-[0.18em] text-brand-sky",
|
|
4729
|
+
children: label
|
|
4730
|
+
}), Icon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4731
|
+
className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-brand-navy/[0.06] text-brand-navy",
|
|
4732
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {
|
|
4733
|
+
size: 16,
|
|
4734
|
+
"aria-hidden": "true"
|
|
4735
|
+
})
|
|
4736
|
+
})]
|
|
4737
|
+
}),
|
|
4738
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4739
|
+
className: "mt-3 font-heading text-4xl font-bold tabular-nums leading-none text-brand-navy",
|
|
4740
|
+
children: value
|
|
4741
|
+
}),
|
|
4742
|
+
trend ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4743
|
+
className: "mt-2.5 flex items-center gap-1.5",
|
|
4744
|
+
children: (() => {
|
|
4745
|
+
const cfg = TREND_CONFIG[trend];
|
|
4746
|
+
const TrendIcon = cfg.icon;
|
|
4747
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TrendIcon, {
|
|
4748
|
+
size: 13,
|
|
4749
|
+
className: cfg.color,
|
|
4750
|
+
"aria-hidden": "true"
|
|
4751
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4752
|
+
className: `text-xs font-semibold ${cfg.color}`,
|
|
4753
|
+
children: trendLabel ?? cfg.label
|
|
4754
|
+
})] });
|
|
4755
|
+
})()
|
|
4756
|
+
}) : helper ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4757
|
+
className: "mt-2 text-xs font-medium text-slate-400",
|
|
4758
|
+
children: helper
|
|
4759
|
+
}) : null
|
|
4760
|
+
]
|
|
4761
|
+
})]
|
|
4762
|
+
});
|
|
4763
|
+
}
|
|
4764
|
+
//#endregion
|
|
4765
|
+
//#region src/ui-library/gallery/status-dot/StatusDot.tsx
|
|
4766
|
+
function StatusDot({ status, label, className = "" }) {
|
|
4767
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4768
|
+
className: `status-dot-cell ${className}`,
|
|
4769
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4770
|
+
className: `status-dot ${status}`,
|
|
4771
|
+
"aria-hidden": "true"
|
|
4772
|
+
}), label && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label })]
|
|
4773
|
+
});
|
|
4774
|
+
}
|
|
4775
|
+
//#endregion
|
|
4464
4776
|
//#region src/ui-library/gallery/status-actions/statusActions.ts
|
|
4465
4777
|
/**
|
|
4466
4778
|
* statusActions — Gallery-native lifecycle transition utilities.
|
|
@@ -4683,6 +4995,7 @@ exports.FieldContext = FieldContext;
|
|
|
4683
4995
|
exports.FieldInput = FieldInput;
|
|
4684
4996
|
exports.FileUpload = FileUpload;
|
|
4685
4997
|
exports.FilterChip = FilterChip;
|
|
4998
|
+
exports.Heatmap = Heatmap;
|
|
4686
4999
|
exports.IconButton = IconButton;
|
|
4687
5000
|
exports.Input = Input;
|
|
4688
5001
|
exports.Kanban = Kanban;
|
|
@@ -4712,7 +5025,9 @@ exports.SkeletonCard = SkeletonCard;
|
|
|
4712
5025
|
exports.SkeletonTableRow = SkeletonTableRow;
|
|
4713
5026
|
exports.SkeletonText = SkeletonText;
|
|
4714
5027
|
exports.Spinner = Spinner;
|
|
5028
|
+
exports.Statistic = Statistic;
|
|
4715
5029
|
exports.StatusBadge = StatusBadge;
|
|
5030
|
+
exports.StatusDot = StatusDot;
|
|
4716
5031
|
exports.Stepper = Stepper;
|
|
4717
5032
|
exports.Switch = Switch;
|
|
4718
5033
|
exports.Tab = Tab;
|
|
@@ -4726,6 +5041,12 @@ exports.TimelineEmptyState = TimelineEmptyState;
|
|
|
4726
5041
|
exports.TimelineFooter = TimelineFooter;
|
|
4727
5042
|
exports.TimelineItem = TimelineItem;
|
|
4728
5043
|
exports.TimelineValueCard = TimelineValueCard;
|
|
5044
|
+
Object.defineProperty(exports, "Toaster", {
|
|
5045
|
+
enumerable: true,
|
|
5046
|
+
get: function() {
|
|
5047
|
+
return sonner.Toaster;
|
|
5048
|
+
}
|
|
5049
|
+
});
|
|
4729
5050
|
exports.Toolbar = Toolbar;
|
|
4730
5051
|
exports.Tooltip = Tooltip;
|
|
4731
5052
|
exports.TreeView = TreeView;
|
|
@@ -4755,6 +5076,12 @@ Object.defineProperty(exports, "shadow", {
|
|
|
4755
5076
|
}
|
|
4756
5077
|
});
|
|
4757
5078
|
exports.statusMenuItems = statusMenuItems;
|
|
5079
|
+
Object.defineProperty(exports, "toast", {
|
|
5080
|
+
enumerable: true,
|
|
5081
|
+
get: function() {
|
|
5082
|
+
return sonner.toast;
|
|
5083
|
+
}
|
|
5084
|
+
});
|
|
4758
5085
|
Object.defineProperty(exports, "typography", {
|
|
4759
5086
|
enumerable: true,
|
|
4760
5087
|
get: function() {
|
package/lib/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { createContext, createElement, use, useCallback, useContext, useEffect, useId, useRef, useState } from "react";
|
|
2
|
+
import { createContext, createElement, use, useCallback, useContext, useEffect, useId, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { clsx } from "clsx";
|
|
4
4
|
import { twMerge } from "tailwind-merge";
|
|
5
|
-
import { AlertCircle, AlertTriangle, Archive, ArchiveRestore, ArrowDown, Ban, CalendarDays, Check, CheckCircle2, ChevronDown, ChevronLeft, ChevronRight, Clock, Command, File, Home, Info, Minus, Search, ShieldCheck, UploadCloud, X } from "lucide-react";
|
|
5
|
+
import { AlertCircle, AlertTriangle, Archive, ArchiveRestore, ArrowDown, Ban, CalendarDays, Check, CheckCircle2, ChevronDown, ChevronLeft, ChevronRight, Clock, Command, File, Home, Info, Minus, Search, ShieldCheck, TrendingDown, TrendingUp, UploadCloud, X } from "lucide-react";
|
|
6
6
|
import Calendar$1 from "react-calendar";
|
|
7
7
|
import { format, isValid, parse } from "date-fns";
|
|
8
8
|
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
@@ -10,6 +10,7 @@ import { createPortal } from "react-dom";
|
|
|
10
10
|
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
11
11
|
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
12
12
|
import { Link } from "react-router-dom";
|
|
13
|
+
import { Toaster, toast } from "sonner";
|
|
13
14
|
export * from "lucide-react";
|
|
14
15
|
//#region \0rolldown/runtime.js
|
|
15
16
|
var __defProp = Object.defineProperty;
|
|
@@ -4202,7 +4203,7 @@ function PermissionMatrix({ roles, permissions, caption = "Permission matrix", c
|
|
|
4202
4203
|
* - Keyboard: ArrowKeys navigate days, Enter/Space select
|
|
4203
4204
|
* - Previous/Next month buttons have aria-label
|
|
4204
4205
|
*/
|
|
4205
|
-
var MONTHS = [
|
|
4206
|
+
var MONTHS$1 = [
|
|
4206
4207
|
"January",
|
|
4207
4208
|
"February",
|
|
4208
4209
|
"March",
|
|
@@ -4216,7 +4217,7 @@ var MONTHS = [
|
|
|
4216
4217
|
"November",
|
|
4217
4218
|
"December"
|
|
4218
4219
|
];
|
|
4219
|
-
var DAYS = [
|
|
4220
|
+
var DAYS$1 = [
|
|
4220
4221
|
"Su",
|
|
4221
4222
|
"Mo",
|
|
4222
4223
|
"Tu",
|
|
@@ -4278,7 +4279,7 @@ function Calendar({ value, defaultValue, onChange, minDate, maxDate, className =
|
|
|
4278
4279
|
/* @__PURE__ */ jsxs("span", {
|
|
4279
4280
|
className: "text-sm font-bold text-slate-800",
|
|
4280
4281
|
children: [
|
|
4281
|
-
MONTHS[viewMonth],
|
|
4282
|
+
MONTHS$1[viewMonth],
|
|
4282
4283
|
" ",
|
|
4283
4284
|
viewYear
|
|
4284
4285
|
]
|
|
@@ -4296,9 +4297,9 @@ function Calendar({ value, defaultValue, onChange, minDate, maxDate, className =
|
|
|
4296
4297
|
]
|
|
4297
4298
|
}), /* @__PURE__ */ jsxs("table", {
|
|
4298
4299
|
role: "grid",
|
|
4299
|
-
"aria-label": `${MONTHS[viewMonth]} ${viewYear}`,
|
|
4300
|
+
"aria-label": `${MONTHS$1[viewMonth]} ${viewYear}`,
|
|
4300
4301
|
className: "w-full border-collapse",
|
|
4301
|
-
children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: DAYS.map((d) => /* @__PURE__ */ jsx("th", {
|
|
4302
|
+
children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: DAYS$1.map((d) => /* @__PURE__ */ jsx("th", {
|
|
4302
4303
|
scope: "col",
|
|
4303
4304
|
className: "pb-2 text-center text-[10px] font-bold uppercase tracking-widest text-slate-400",
|
|
4304
4305
|
children: d
|
|
@@ -4311,7 +4312,7 @@ function Calendar({ value, defaultValue, onChange, minDate, maxDate, className =
|
|
|
4311
4312
|
const isToday = isSameDay(date, today);
|
|
4312
4313
|
const isSelected = !!selected && isSameDay(date, selected);
|
|
4313
4314
|
const isDisabled = (minDate ? date < minDate : false) || (maxDate ? date > maxDate : false);
|
|
4314
|
-
const label = `${day} ${MONTHS[viewMonth]} ${viewYear}${isToday ? ", Today" : ""}${isSelected ? ", Selected" : ""}`;
|
|
4315
|
+
const label = `${day} ${MONTHS$1[viewMonth]} ${viewYear}${isToday ? ", Today" : ""}${isSelected ? ", Selected" : ""}`;
|
|
4315
4316
|
return /* @__PURE__ */ jsx("td", {
|
|
4316
4317
|
role: "gridcell",
|
|
4317
4318
|
"aria-label": label,
|
|
@@ -4438,6 +4439,317 @@ function LineChart({ data, height = 120, lineColor = "#6366f1", filled = true, "
|
|
|
4438
4439
|
});
|
|
4439
4440
|
}
|
|
4440
4441
|
//#endregion
|
|
4442
|
+
//#region src/ui-library/gallery/heatmap/Heatmap.tsx
|
|
4443
|
+
/**
|
|
4444
|
+
* Heatmap — Design System Component
|
|
4445
|
+
*
|
|
4446
|
+
* A GitHub contribution-style calendar heatmap.
|
|
4447
|
+
* HRIS use cases: attendance patterns, leave frequency, approval volume.
|
|
4448
|
+
*
|
|
4449
|
+
* Usage:
|
|
4450
|
+
* const data = [
|
|
4451
|
+
* { date: '2025-01-01', value: 3 },
|
|
4452
|
+
* { date: '2025-01-02', value: 0 },
|
|
4453
|
+
* ...
|
|
4454
|
+
* ];
|
|
4455
|
+
* <Heatmap data={data} aria-label="Attendance heatmap" />
|
|
4456
|
+
*/
|
|
4457
|
+
var SCALES = {
|
|
4458
|
+
blue: { cells: [
|
|
4459
|
+
"bg-slate-100",
|
|
4460
|
+
"bg-brand-sky/20",
|
|
4461
|
+
"bg-brand-sky/40",
|
|
4462
|
+
"bg-brand-sky/70",
|
|
4463
|
+
"bg-brand-sky"
|
|
4464
|
+
] },
|
|
4465
|
+
navy: { cells: [
|
|
4466
|
+
"bg-slate-100",
|
|
4467
|
+
"bg-brand-navy/15",
|
|
4468
|
+
"bg-brand-navy/35",
|
|
4469
|
+
"bg-brand-navy/65",
|
|
4470
|
+
"bg-brand-navy"
|
|
4471
|
+
] },
|
|
4472
|
+
teal: { cells: [
|
|
4473
|
+
"bg-slate-100",
|
|
4474
|
+
"bg-cyan-100",
|
|
4475
|
+
"bg-cyan-300",
|
|
4476
|
+
"bg-cyan-500",
|
|
4477
|
+
"bg-cyan-700"
|
|
4478
|
+
] },
|
|
4479
|
+
orange: { cells: [
|
|
4480
|
+
"bg-slate-100",
|
|
4481
|
+
"bg-orange-100",
|
|
4482
|
+
"bg-orange-300",
|
|
4483
|
+
"bg-orange-400",
|
|
4484
|
+
"bg-orange-600"
|
|
4485
|
+
] }
|
|
4486
|
+
};
|
|
4487
|
+
var MONTHS = [
|
|
4488
|
+
"Jan",
|
|
4489
|
+
"Feb",
|
|
4490
|
+
"Mar",
|
|
4491
|
+
"Apr",
|
|
4492
|
+
"May",
|
|
4493
|
+
"Jun",
|
|
4494
|
+
"Jul",
|
|
4495
|
+
"Aug",
|
|
4496
|
+
"Sep",
|
|
4497
|
+
"Oct",
|
|
4498
|
+
"Nov",
|
|
4499
|
+
"Dec"
|
|
4500
|
+
];
|
|
4501
|
+
var DAYS = [
|
|
4502
|
+
"Sun",
|
|
4503
|
+
"Mon",
|
|
4504
|
+
"Tue",
|
|
4505
|
+
"Wed",
|
|
4506
|
+
"Thu",
|
|
4507
|
+
"Fri",
|
|
4508
|
+
"Sat"
|
|
4509
|
+
];
|
|
4510
|
+
function toIntensity(value) {
|
|
4511
|
+
if (value === 0) return 0;
|
|
4512
|
+
if (value <= 2) return 1;
|
|
4513
|
+
if (value <= 5) return 2;
|
|
4514
|
+
if (value <= 7) return 3;
|
|
4515
|
+
return 4;
|
|
4516
|
+
}
|
|
4517
|
+
function Heatmap({ data, scale = "blue", showLegend = true, showMonthLabels = true, showDayLabels = true, size = "md", onCellHover, "aria-label": ariaLabel = "Activity heatmap" }) {
|
|
4518
|
+
const colors = SCALES[scale].cells;
|
|
4519
|
+
const cellSize = size === "sm" ? "w-2.5 h-2.5" : "w-3.5 h-3.5";
|
|
4520
|
+
const gap = size === "sm" ? "gap-[3px]" : "gap-[4px]";
|
|
4521
|
+
const cells = useMemo(() => data.map((d) => ({
|
|
4522
|
+
...d,
|
|
4523
|
+
intensity: toIntensity(d.value)
|
|
4524
|
+
})), [data]);
|
|
4525
|
+
const weeks = useMemo(() => {
|
|
4526
|
+
const cols = [];
|
|
4527
|
+
let week = [];
|
|
4528
|
+
cells.forEach((cell, i) => {
|
|
4529
|
+
week.push(cell);
|
|
4530
|
+
if (week.length === 7 || i === cells.length - 1) {
|
|
4531
|
+
cols.push(week);
|
|
4532
|
+
week = [];
|
|
4533
|
+
}
|
|
4534
|
+
});
|
|
4535
|
+
return cols;
|
|
4536
|
+
}, [cells]);
|
|
4537
|
+
const monthLabels = useMemo(() => {
|
|
4538
|
+
const labels = [];
|
|
4539
|
+
let lastMonth = -1;
|
|
4540
|
+
weeks.forEach((week, colIdx) => {
|
|
4541
|
+
const firstDay = week[0];
|
|
4542
|
+
if (!firstDay) return;
|
|
4543
|
+
const m = new Date(firstDay.date).getMonth();
|
|
4544
|
+
if (m !== lastMonth) {
|
|
4545
|
+
labels.push({
|
|
4546
|
+
label: MONTHS[m],
|
|
4547
|
+
col: colIdx
|
|
4548
|
+
});
|
|
4549
|
+
lastMonth = m;
|
|
4550
|
+
}
|
|
4551
|
+
});
|
|
4552
|
+
return labels;
|
|
4553
|
+
}, [weeks]);
|
|
4554
|
+
const colW = size === "sm" ? 13 : 19;
|
|
4555
|
+
return /* @__PURE__ */ jsx("div", {
|
|
4556
|
+
className: "w-full overflow-x-auto",
|
|
4557
|
+
"aria-label": ariaLabel,
|
|
4558
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
4559
|
+
className: "inline-flex flex-col gap-1.5",
|
|
4560
|
+
children: [
|
|
4561
|
+
showMonthLabels && /* @__PURE__ */ jsx("div", {
|
|
4562
|
+
className: "relative h-4",
|
|
4563
|
+
style: { width: weeks.length * colW },
|
|
4564
|
+
children: monthLabels.map(({ label, col }) => /* @__PURE__ */ jsx("span", {
|
|
4565
|
+
className: "absolute text-[10px] font-bold text-slate-400",
|
|
4566
|
+
style: { left: col * colW },
|
|
4567
|
+
children: label
|
|
4568
|
+
}, `${label}-${col}`))
|
|
4569
|
+
}),
|
|
4570
|
+
/* @__PURE__ */ jsxs("div", {
|
|
4571
|
+
className: "flex items-start gap-1",
|
|
4572
|
+
children: [showDayLabels && /* @__PURE__ */ jsx("div", {
|
|
4573
|
+
className: "flex flex-col gap-[4px] mr-1",
|
|
4574
|
+
children: DAYS.map((d, i) => /* @__PURE__ */ jsx("span", {
|
|
4575
|
+
className: `text-[10px] font-bold text-slate-400 leading-none ${size === "sm" ? "h-2.5" : "h-3.5"} flex items-center ${i % 2 === 0 ? "opacity-0" : ""}`,
|
|
4576
|
+
"aria-hidden": i % 2 === 0,
|
|
4577
|
+
children: d
|
|
4578
|
+
}, d))
|
|
4579
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
4580
|
+
className: `flex ${gap}`,
|
|
4581
|
+
children: weeks.map((week, colIdx) => /* @__PURE__ */ jsx("div", {
|
|
4582
|
+
className: `flex flex-col ${gap}`,
|
|
4583
|
+
children: week.map((cell) => /* @__PURE__ */ jsx("button", {
|
|
4584
|
+
type: "button",
|
|
4585
|
+
title: `${cell.date}: ${cell.value}`,
|
|
4586
|
+
"aria-label": `${cell.date}: ${cell.value} activities`,
|
|
4587
|
+
onMouseEnter: () => onCellHover?.(cell),
|
|
4588
|
+
onMouseLeave: () => onCellHover?.(null),
|
|
4589
|
+
onFocus: () => onCellHover?.(cell),
|
|
4590
|
+
onBlur: () => onCellHover?.(null),
|
|
4591
|
+
className: [
|
|
4592
|
+
cellSize,
|
|
4593
|
+
"rounded-sm transition-transform hover:scale-125 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-brand-blue/50",
|
|
4594
|
+
colors[cell.intensity]
|
|
4595
|
+
].join(" ")
|
|
4596
|
+
}, cell.date))
|
|
4597
|
+
}, colIdx))
|
|
4598
|
+
})]
|
|
4599
|
+
}),
|
|
4600
|
+
showLegend && /* @__PURE__ */ jsxs("div", {
|
|
4601
|
+
className: "flex items-center gap-2 mt-1",
|
|
4602
|
+
children: [
|
|
4603
|
+
/* @__PURE__ */ jsx("span", {
|
|
4604
|
+
className: "text-[10px] font-bold text-slate-400",
|
|
4605
|
+
children: "Less"
|
|
4606
|
+
}),
|
|
4607
|
+
/* @__PURE__ */ jsx("div", {
|
|
4608
|
+
className: "flex gap-1",
|
|
4609
|
+
children: colors.map((c, i) => /* @__PURE__ */ jsx("div", {
|
|
4610
|
+
className: `w-3 h-3 rounded-sm ${c}`,
|
|
4611
|
+
"aria-hidden": "true"
|
|
4612
|
+
}, i))
|
|
4613
|
+
}),
|
|
4614
|
+
/* @__PURE__ */ jsx("span", {
|
|
4615
|
+
className: "text-[10px] font-bold text-slate-400",
|
|
4616
|
+
children: "More"
|
|
4617
|
+
})
|
|
4618
|
+
]
|
|
4619
|
+
})
|
|
4620
|
+
]
|
|
4621
|
+
})
|
|
4622
|
+
});
|
|
4623
|
+
}
|
|
4624
|
+
//#endregion
|
|
4625
|
+
//#region src/ui-library/gallery/stat-card/Statistic.tsx
|
|
4626
|
+
/**
|
|
4627
|
+
* Statistic — Design System Component
|
|
4628
|
+
*
|
|
4629
|
+
* A KPI card. Four variants: default, accent, minimal, compact.
|
|
4630
|
+
* Signature element: brand-navy left border strip + font-heading number.
|
|
4631
|
+
*
|
|
4632
|
+
* Usage:
|
|
4633
|
+
* <Statistic label="Total Employees" value={142} icon={Users} />
|
|
4634
|
+
* <Statistic label="Fill Rate" value="83%" variant="accent" trend="up" trendLabel="+2%" />
|
|
4635
|
+
*/
|
|
4636
|
+
var TREND_CONFIG = {
|
|
4637
|
+
up: {
|
|
4638
|
+
icon: TrendingUp,
|
|
4639
|
+
color: "text-emerald-600",
|
|
4640
|
+
label: "Up"
|
|
4641
|
+
},
|
|
4642
|
+
down: {
|
|
4643
|
+
icon: TrendingDown,
|
|
4644
|
+
color: "text-rose-500",
|
|
4645
|
+
label: "Down"
|
|
4646
|
+
},
|
|
4647
|
+
neutral: {
|
|
4648
|
+
icon: Minus,
|
|
4649
|
+
color: "text-slate-400",
|
|
4650
|
+
label: "No change"
|
|
4651
|
+
}
|
|
4652
|
+
};
|
|
4653
|
+
function Statistic({ label, value, helper, icon: Icon, trend, trendLabel, variant = "default" }) {
|
|
4654
|
+
if (variant === "minimal") return /* @__PURE__ */ jsxs("div", { children: [
|
|
4655
|
+
/* @__PURE__ */ jsx("p", {
|
|
4656
|
+
className: "text-[11px] font-bold uppercase tracking-[0.18em] text-brand-sky",
|
|
4657
|
+
children: label
|
|
4658
|
+
}),
|
|
4659
|
+
/* @__PURE__ */ jsx("p", {
|
|
4660
|
+
className: "mt-1 font-heading text-4xl font-bold tabular-nums text-brand-navy",
|
|
4661
|
+
children: value
|
|
4662
|
+
}),
|
|
4663
|
+
helper && /* @__PURE__ */ jsx("p", {
|
|
4664
|
+
className: "mt-1 text-xs font-medium text-slate-400",
|
|
4665
|
+
children: helper
|
|
4666
|
+
})
|
|
4667
|
+
] });
|
|
4668
|
+
if (variant === "compact") return /* @__PURE__ */ jsxs("div", {
|
|
4669
|
+
className: "flex items-center gap-4 rounded-2xl border border-slate-200 bg-white px-5 py-4 shadow-sm",
|
|
4670
|
+
children: [Icon && /* @__PURE__ */ jsx("div", {
|
|
4671
|
+
className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-brand-navy/[0.06] text-brand-navy",
|
|
4672
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
4673
|
+
size: 18,
|
|
4674
|
+
"aria-hidden": "true"
|
|
4675
|
+
})
|
|
4676
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
4677
|
+
className: "min-w-0",
|
|
4678
|
+
children: [
|
|
4679
|
+
/* @__PURE__ */ jsx("p", {
|
|
4680
|
+
className: "text-[11px] font-bold uppercase tracking-[0.18em] text-slate-400 truncate",
|
|
4681
|
+
children: label
|
|
4682
|
+
}),
|
|
4683
|
+
/* @__PURE__ */ jsx("p", {
|
|
4684
|
+
className: "font-heading text-2xl font-bold tabular-nums text-brand-navy leading-none mt-0.5",
|
|
4685
|
+
children: value
|
|
4686
|
+
}),
|
|
4687
|
+
helper && /* @__PURE__ */ jsx("p", {
|
|
4688
|
+
className: "mt-0.5 text-xs font-medium text-slate-400 truncate",
|
|
4689
|
+
children: helper
|
|
4690
|
+
})
|
|
4691
|
+
]
|
|
4692
|
+
})]
|
|
4693
|
+
});
|
|
4694
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
4695
|
+
className: "relative overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm",
|
|
4696
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
4697
|
+
className: `absolute inset-y-0 left-0 w-1 ${variant === "accent" ? "bg-brand-blue" : "bg-brand-navy"}`,
|
|
4698
|
+
"aria-hidden": "true"
|
|
4699
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
4700
|
+
className: "px-6 py-5",
|
|
4701
|
+
children: [
|
|
4702
|
+
/* @__PURE__ */ jsxs("div", {
|
|
4703
|
+
className: "flex items-start justify-between gap-3",
|
|
4704
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
4705
|
+
className: "text-[11px] font-bold uppercase tracking-[0.18em] text-brand-sky",
|
|
4706
|
+
children: label
|
|
4707
|
+
}), Icon && /* @__PURE__ */ jsx("div", {
|
|
4708
|
+
className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-brand-navy/[0.06] text-brand-navy",
|
|
4709
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
4710
|
+
size: 16,
|
|
4711
|
+
"aria-hidden": "true"
|
|
4712
|
+
})
|
|
4713
|
+
})]
|
|
4714
|
+
}),
|
|
4715
|
+
/* @__PURE__ */ jsx("p", {
|
|
4716
|
+
className: "mt-3 font-heading text-4xl font-bold tabular-nums leading-none text-brand-navy",
|
|
4717
|
+
children: value
|
|
4718
|
+
}),
|
|
4719
|
+
trend ? /* @__PURE__ */ jsx("div", {
|
|
4720
|
+
className: "mt-2.5 flex items-center gap-1.5",
|
|
4721
|
+
children: (() => {
|
|
4722
|
+
const cfg = TREND_CONFIG[trend];
|
|
4723
|
+
const TrendIcon = cfg.icon;
|
|
4724
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(TrendIcon, {
|
|
4725
|
+
size: 13,
|
|
4726
|
+
className: cfg.color,
|
|
4727
|
+
"aria-hidden": "true"
|
|
4728
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
4729
|
+
className: `text-xs font-semibold ${cfg.color}`,
|
|
4730
|
+
children: trendLabel ?? cfg.label
|
|
4731
|
+
})] });
|
|
4732
|
+
})()
|
|
4733
|
+
}) : helper ? /* @__PURE__ */ jsx("p", {
|
|
4734
|
+
className: "mt-2 text-xs font-medium text-slate-400",
|
|
4735
|
+
children: helper
|
|
4736
|
+
}) : null
|
|
4737
|
+
]
|
|
4738
|
+
})]
|
|
4739
|
+
});
|
|
4740
|
+
}
|
|
4741
|
+
//#endregion
|
|
4742
|
+
//#region src/ui-library/gallery/status-dot/StatusDot.tsx
|
|
4743
|
+
function StatusDot({ status, label, className = "" }) {
|
|
4744
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
4745
|
+
className: `status-dot-cell ${className}`,
|
|
4746
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
4747
|
+
className: `status-dot ${status}`,
|
|
4748
|
+
"aria-hidden": "true"
|
|
4749
|
+
}), label && /* @__PURE__ */ jsx("span", { children: label })]
|
|
4750
|
+
});
|
|
4751
|
+
}
|
|
4752
|
+
//#endregion
|
|
4441
4753
|
//#region src/ui-library/gallery/status-actions/statusActions.ts
|
|
4442
4754
|
/**
|
|
4443
4755
|
* statusActions — Gallery-native lifecycle transition utilities.
|
|
@@ -4621,4 +4933,4 @@ var shadow = {
|
|
|
4621
4933
|
overlay: "shadow-xl"
|
|
4622
4934
|
};
|
|
4623
4935
|
//#endregion
|
|
4624
|
-
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, ActivityFeed, Alert, ApprovalTimeline, Avatar, Badge, BarChart, Breadcrumb, Button, Calendar, Card, CardContent, CardFooter, CardHeader, Checkbox, Combobox, CommandPalette, ConfirmAction, ConfirmDialog, DatePicker, DescriptionItem, DescriptionList, Dialog, DialogBody, DialogFooter, Divider, Drawer, Dropdown, EmployeeCard, EmptyState, ErrorBanner, Field, FieldContext, FieldInput, FileUpload, FilterChip, IconButton, Input, Kanban, LineChart, Menu, Modal, OtpInput, PageHeader, Pagination, PermissionMatrix, Popover, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioItem, Section, SegmentedControl, Select, SidebarGroup, SidebarItem, SidebarNav, SidebarSection, Skeleton, SkeletonAvatar, SkeletonCard, SkeletonTableRow, SkeletonText, Spinner, StatusBadge, Stepper, Switch, Tab, TabList, TabPanel, TabPanels, Tabs, Textarea, Timeline, TimelineEmptyState, TimelineFooter, TimelineItem, TimelineValueCard, Toolbar, Tooltip, TreeView, ValidationSummary, colors_exports as colors, dimension_exports as dimension, radius_exports as radius, shadow_exports as shadow, statusMenuItems, typography_exports as typography, useFieldContext };
|
|
4936
|
+
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, ActivityFeed, Alert, ApprovalTimeline, Avatar, Badge, BarChart, Breadcrumb, Button, Calendar, Card, CardContent, CardFooter, CardHeader, Checkbox, Combobox, CommandPalette, ConfirmAction, ConfirmDialog, DatePicker, DescriptionItem, DescriptionList, Dialog, DialogBody, DialogFooter, Divider, Drawer, Dropdown, EmployeeCard, EmptyState, ErrorBanner, Field, FieldContext, FieldInput, FileUpload, FilterChip, Heatmap, IconButton, Input, Kanban, LineChart, Menu, Modal, OtpInput, PageHeader, Pagination, PermissionMatrix, Popover, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioItem, Section, SegmentedControl, Select, SidebarGroup, SidebarItem, SidebarNav, SidebarSection, Skeleton, SkeletonAvatar, SkeletonCard, SkeletonTableRow, SkeletonText, Spinner, Statistic, StatusBadge, StatusDot, Stepper, Switch, Tab, TabList, TabPanel, TabPanels, Tabs, Textarea, Timeline, TimelineEmptyState, TimelineFooter, TimelineItem, TimelineValueCard, Toaster, Toolbar, Tooltip, TreeView, ValidationSummary, colors_exports as colors, dimension_exports as dimension, radius_exports as radius, shadow_exports as shadow, statusMenuItems, toast, typography_exports as typography, useFieldContext };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Heatmap — Design System Component
|
|
3
|
+
*
|
|
4
|
+
* A GitHub contribution-style calendar heatmap.
|
|
5
|
+
* HRIS use cases: attendance patterns, leave frequency, approval volume.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const data = [
|
|
9
|
+
* { date: '2025-01-01', value: 3 },
|
|
10
|
+
* { date: '2025-01-02', value: 0 },
|
|
11
|
+
* ...
|
|
12
|
+
* ];
|
|
13
|
+
* <Heatmap data={data} aria-label="Attendance heatmap" />
|
|
14
|
+
*/
|
|
15
|
+
export type HeatmapScale = 'blue' | 'navy' | 'teal' | 'orange';
|
|
16
|
+
export interface HeatmapDataPoint {
|
|
17
|
+
/** Date string in yyyy-MM-dd format */
|
|
18
|
+
date: string;
|
|
19
|
+
/** Raw value — automatically bucketed into 0–4 intensity levels */
|
|
20
|
+
value: number;
|
|
21
|
+
}
|
|
22
|
+
export interface HeatmapProps {
|
|
23
|
+
/** Array of date + value data points */
|
|
24
|
+
data: HeatmapDataPoint[];
|
|
25
|
+
/** Color scale — defaults to 'blue' */
|
|
26
|
+
scale?: HeatmapScale;
|
|
27
|
+
/** Show the Less → More legend strip — default true */
|
|
28
|
+
showLegend?: boolean;
|
|
29
|
+
/** Show month labels above columns — default true */
|
|
30
|
+
showMonthLabels?: boolean;
|
|
31
|
+
/** Show day labels (Mon/Wed/Fri) — default true */
|
|
32
|
+
showDayLabels?: boolean;
|
|
33
|
+
/** Cell size — default 'md' */
|
|
34
|
+
size?: 'sm' | 'md';
|
|
35
|
+
/** Called when a cell is hovered, with the cell data or null on leave */
|
|
36
|
+
onCellHover?: (cell: HeatmapDataPoint | null) => void;
|
|
37
|
+
/** Accessible label for the heatmap container */
|
|
38
|
+
'aria-label'?: string;
|
|
39
|
+
}
|
|
40
|
+
export declare function Heatmap({ data, scale, showLegend, showMonthLabels, showDayLabels, size, onCellHover, 'aria-label': ariaLabel, }: HeatmapProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Statistic — Design System Component
|
|
3
|
+
*
|
|
4
|
+
* A KPI card. Four variants: default, accent, minimal, compact.
|
|
5
|
+
* Signature element: brand-navy left border strip + font-heading number.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* <Statistic label="Total Employees" value={142} icon={Users} />
|
|
9
|
+
* <Statistic label="Fill Rate" value="83%" variant="accent" trend="up" trendLabel="+2%" />
|
|
10
|
+
*/
|
|
11
|
+
export type StatisticTrend = 'up' | 'down' | 'neutral';
|
|
12
|
+
export type StatisticVariant = 'default' | 'accent' | 'minimal' | 'compact';
|
|
13
|
+
export interface StatisticProps {
|
|
14
|
+
/** KPI label — shown above the number */
|
|
15
|
+
label: string;
|
|
16
|
+
/** The headline number or string */
|
|
17
|
+
value: string | number;
|
|
18
|
+
/** Secondary text below the value (not shown when trend is set) */
|
|
19
|
+
helper?: string;
|
|
20
|
+
/** Icon component — e.g. from lucide-react */
|
|
21
|
+
icon?: React.ElementType;
|
|
22
|
+
/** Trend direction — renders a TrendingUp/Down/Minus icon */
|
|
23
|
+
trend?: StatisticTrend;
|
|
24
|
+
/** Override the default trend label */
|
|
25
|
+
trendLabel?: string;
|
|
26
|
+
/** Visual variant */
|
|
27
|
+
variant?: StatisticVariant;
|
|
28
|
+
}
|
|
29
|
+
export declare function Statistic({ label, value, helper, icon: Icon, trend, trendLabel, variant, }: StatisticProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StatusDot — Design System Component
|
|
3
|
+
*
|
|
4
|
+
* A coloured glowing dot paired with a label. Used in tables to indicate
|
|
5
|
+
* account or entity status at a glance.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* <StatusDot status="active" label="Active" />
|
|
9
|
+
* <StatusDot status="locked" label="Locked" />
|
|
10
|
+
*/
|
|
11
|
+
export type StatusDotStatus = 'active' | 'locked' | 'inactive' | 'pending';
|
|
12
|
+
export interface StatusDotProps {
|
|
13
|
+
status: StatusDotStatus;
|
|
14
|
+
label?: string;
|
|
15
|
+
className?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function StatusDot({ status, label, className }: StatusDotProps): import("react").JSX.Element;
|
|
@@ -118,6 +118,13 @@ export { Calendar } from './gallery/calendar/Calendar';
|
|
|
118
118
|
export type { CalendarProps } from './gallery/calendar/Calendar';
|
|
119
119
|
export { BarChart, LineChart } from './gallery/charts/Charts';
|
|
120
120
|
export type { BarChartProps, LineChartProps, ChartDataPoint } from './gallery/charts/Charts';
|
|
121
|
+
export { Heatmap } from './gallery/heatmap/Heatmap';
|
|
122
|
+
export type { HeatmapProps, HeatmapDataPoint, HeatmapScale } from './gallery/heatmap/Heatmap';
|
|
123
|
+
export { Statistic } from './gallery/stat-card/Statistic';
|
|
124
|
+
export type { StatisticProps, StatisticTrend, StatisticVariant } from './gallery/stat-card/Statistic';
|
|
125
|
+
export { StatusDot } from './gallery/status-dot/StatusDot';
|
|
126
|
+
export type { StatusDotProps, StatusDotStatus } from './gallery/status-dot/StatusDot';
|
|
127
|
+
export { Toaster, toast } from 'sonner';
|
|
121
128
|
export { statusMenuItems } from './gallery/status-actions/statusActions';
|
|
122
129
|
export type { ActionMenuItem, Lifecycle, StatusTransitionRequest, StatusMenuOptions } from './gallery/status-actions/statusActions';
|
|
123
130
|
export * from './tokens/index';
|