@diwauhris/ui 1.4.0 → 1.4.2
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
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,306 @@ 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
|
|
4464
4765
|
//#region src/ui-library/gallery/status-actions/statusActions.ts
|
|
4465
4766
|
/**
|
|
4466
4767
|
* statusActions — Gallery-native lifecycle transition utilities.
|
|
@@ -4683,6 +4984,7 @@ exports.FieldContext = FieldContext;
|
|
|
4683
4984
|
exports.FieldInput = FieldInput;
|
|
4684
4985
|
exports.FileUpload = FileUpload;
|
|
4685
4986
|
exports.FilterChip = FilterChip;
|
|
4987
|
+
exports.Heatmap = Heatmap;
|
|
4686
4988
|
exports.IconButton = IconButton;
|
|
4687
4989
|
exports.Input = Input;
|
|
4688
4990
|
exports.Kanban = Kanban;
|
|
@@ -4712,6 +5014,7 @@ exports.SkeletonCard = SkeletonCard;
|
|
|
4712
5014
|
exports.SkeletonTableRow = SkeletonTableRow;
|
|
4713
5015
|
exports.SkeletonText = SkeletonText;
|
|
4714
5016
|
exports.Spinner = Spinner;
|
|
5017
|
+
exports.Statistic = Statistic;
|
|
4715
5018
|
exports.StatusBadge = StatusBadge;
|
|
4716
5019
|
exports.Stepper = Stepper;
|
|
4717
5020
|
exports.Switch = Switch;
|
|
@@ -4726,6 +5029,12 @@ exports.TimelineEmptyState = TimelineEmptyState;
|
|
|
4726
5029
|
exports.TimelineFooter = TimelineFooter;
|
|
4727
5030
|
exports.TimelineItem = TimelineItem;
|
|
4728
5031
|
exports.TimelineValueCard = TimelineValueCard;
|
|
5032
|
+
Object.defineProperty(exports, "Toaster", {
|
|
5033
|
+
enumerable: true,
|
|
5034
|
+
get: function() {
|
|
5035
|
+
return sonner.Toaster;
|
|
5036
|
+
}
|
|
5037
|
+
});
|
|
4729
5038
|
exports.Toolbar = Toolbar;
|
|
4730
5039
|
exports.Tooltip = Tooltip;
|
|
4731
5040
|
exports.TreeView = TreeView;
|
|
@@ -4755,6 +5064,12 @@ Object.defineProperty(exports, "shadow", {
|
|
|
4755
5064
|
}
|
|
4756
5065
|
});
|
|
4757
5066
|
exports.statusMenuItems = statusMenuItems;
|
|
5067
|
+
Object.defineProperty(exports, "toast", {
|
|
5068
|
+
enumerable: true,
|
|
5069
|
+
get: function() {
|
|
5070
|
+
return sonner.toast;
|
|
5071
|
+
}
|
|
5072
|
+
});
|
|
4758
5073
|
Object.defineProperty(exports, "typography", {
|
|
4759
5074
|
enumerable: true,
|
|
4760
5075
|
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,306 @@ 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
|
|
4441
4742
|
//#region src/ui-library/gallery/status-actions/statusActions.ts
|
|
4442
4743
|
/**
|
|
4443
4744
|
* statusActions — Gallery-native lifecycle transition utilities.
|
|
@@ -4621,4 +4922,4 @@ var shadow = {
|
|
|
4621
4922
|
overlay: "shadow-xl"
|
|
4622
4923
|
};
|
|
4623
4924
|
//#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 };
|
|
4925
|
+
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, 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;
|
|
@@ -118,6 +118,11 @@ 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 { Toaster, toast } from 'sonner';
|
|
121
126
|
export { statusMenuItems } from './gallery/status-actions/statusActions';
|
|
122
127
|
export type { ActionMenuItem, Lifecycle, StatusTransitionRequest, StatusMenuOptions } from './gallery/status-actions/statusActions';
|
|
123
128
|
export * from './tokens/index';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diwauhris/ui",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "1.4.
|
|
4
|
+
"version": "1.4.2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "DIWA UHRIS UI component library — React + Tailwind CSS",
|
|
7
7
|
"keywords": [
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"clsx": "^2",
|
|
46
46
|
"d3": "^7",
|
|
47
47
|
"date-fns": "^4",
|
|
48
|
-
"lucide-react": "
|
|
48
|
+
"lucide-react": ">=0.363.0",
|
|
49
49
|
"react": ">=18",
|
|
50
50
|
"react-calendar": "^6",
|
|
51
51
|
"react-dom": ">=18",
|