@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.0",
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": "^1",
48
+ "lucide-react": ">=0.363.0",
49
49
  "react": ">=18",
50
50
  "react-calendar": "^6",
51
51
  "react-dom": ">=18",