@diwauhris/ui 1.7.0 → 1.7.1
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 +28 -4
- package/lib/index.cjs +300 -53
- package/lib/index.mjs +298 -54
- package/lib/types/ui-library/gallery/column-manager/ColumnManager.d.ts +66 -0
- package/lib/types/ui-library/gallery/pagination/Pagination.d.ts +17 -1
- package/lib/types/ui-library/gallery/status-dot/MaskedValue.d.ts +54 -0
- package/lib/types/ui-library/gallery/validation-summary/ValidationSummary.d.ts +23 -1
- package/lib/types/ui-library/index.d.ts +4 -0
- package/package.json +1 -1
package/lib/assets/ui.css
CHANGED
|
@@ -482,6 +482,11 @@
|
|
|
482
482
|
margin: auto;
|
|
483
483
|
}
|
|
484
484
|
|
|
485
|
+
.-mx-5 {
|
|
486
|
+
margin-left: -1.25rem;
|
|
487
|
+
margin-right: -1.25rem;
|
|
488
|
+
}
|
|
489
|
+
|
|
485
490
|
.mx-1 {
|
|
486
491
|
margin-left: 0.25rem;
|
|
487
492
|
margin-right: 0.25rem;
|
|
@@ -510,6 +515,10 @@
|
|
|
510
515
|
margin-top: -0.125rem;
|
|
511
516
|
}
|
|
512
517
|
|
|
518
|
+
.-mt-4 {
|
|
519
|
+
margin-top: -1rem;
|
|
520
|
+
}
|
|
521
|
+
|
|
513
522
|
.mb-0\.5 {
|
|
514
523
|
margin-bottom: 0.125rem;
|
|
515
524
|
}
|
|
@@ -610,10 +619,6 @@
|
|
|
610
619
|
margin-top: 2.5rem;
|
|
611
620
|
}
|
|
612
621
|
|
|
613
|
-
.mt-12 {
|
|
614
|
-
margin-top: 3rem;
|
|
615
|
-
}
|
|
616
|
-
|
|
617
622
|
.mt-2 {
|
|
618
623
|
margin-top: 0.5rem;
|
|
619
624
|
}
|
|
@@ -3806,6 +3811,14 @@
|
|
|
3806
3811
|
text-decoration-line: line-through;
|
|
3807
3812
|
}
|
|
3808
3813
|
|
|
3814
|
+
.decoration-amber-500\/60 {
|
|
3815
|
+
text-decoration-color: rgb(245 158 11 / 0.6);
|
|
3816
|
+
}
|
|
3817
|
+
|
|
3818
|
+
.underline-offset-2 {
|
|
3819
|
+
text-underline-offset: 2px;
|
|
3820
|
+
}
|
|
3821
|
+
|
|
3809
3822
|
.antialiased {
|
|
3810
3823
|
-webkit-font-smoothing: antialiased;
|
|
3811
3824
|
-moz-osx-font-smoothing: grayscale;
|
|
@@ -3821,6 +3834,10 @@
|
|
|
3821
3834
|
color: rgb(148 163 184 / var(--tw-placeholder-opacity, 1));
|
|
3822
3835
|
}
|
|
3823
3836
|
|
|
3837
|
+
.accent-brand-blue {
|
|
3838
|
+
accent-color: #034EA2;
|
|
3839
|
+
}
|
|
3840
|
+
|
|
3824
3841
|
.opacity-0 {
|
|
3825
3842
|
opacity: 0;
|
|
3826
3843
|
}
|
|
@@ -4689,6 +4706,9 @@ body {
|
|
|
4689
4706
|
.hover\:underline:hover {
|
|
4690
4707
|
text-decoration-line: underline;
|
|
4691
4708
|
}
|
|
4709
|
+
.hover\:decoration-amber-700:hover {
|
|
4710
|
+
text-decoration-color: #b45309;
|
|
4711
|
+
}
|
|
4692
4712
|
.hover\:opacity-100:hover {
|
|
4693
4713
|
opacity: 1;
|
|
4694
4714
|
}
|
|
@@ -4800,6 +4820,10 @@ body {
|
|
|
4800
4820
|
--tw-ring-opacity: 1;
|
|
4801
4821
|
--tw-ring-color: rgb(253 230 138 / var(--tw-ring-opacity, 1));
|
|
4802
4822
|
}
|
|
4823
|
+
.focus-visible\:ring-amber-600:focus-visible {
|
|
4824
|
+
--tw-ring-opacity: 1;
|
|
4825
|
+
--tw-ring-color: rgb(217 119 6 / var(--tw-ring-opacity, 1));
|
|
4826
|
+
}
|
|
4803
4827
|
.focus-visible\:ring-brand-blue\/15:focus-visible {
|
|
4804
4828
|
--tw-ring-color: rgb(3 78 162 / 0.15);
|
|
4805
4829
|
}
|
package/lib/index.cjs
CHANGED
|
@@ -1475,7 +1475,17 @@ function SegmentedControl({ options, defaultValue, value: controlledValue, onVal
|
|
|
1475
1475
|
}
|
|
1476
1476
|
//#endregion
|
|
1477
1477
|
//#region src/ui-library/gallery/validation-summary/ValidationSummary.tsx
|
|
1478
|
-
function
|
|
1478
|
+
function scrollToField(domId) {
|
|
1479
|
+
const el = document.getElementById(domId);
|
|
1480
|
+
if (!el) return;
|
|
1481
|
+
const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
1482
|
+
el.scrollIntoView({
|
|
1483
|
+
behavior: prefersReducedMotion ? "auto" : "smooth",
|
|
1484
|
+
block: "center"
|
|
1485
|
+
});
|
|
1486
|
+
(el.querySelector("input,select,textarea,[tabindex]") ?? el).focus();
|
|
1487
|
+
}
|
|
1488
|
+
function ValidationSummary({ issues, heading = "Review these fields before saving", fieldIds, fieldLabels, className = "" }) {
|
|
1479
1489
|
if (issues.length === 0) return null;
|
|
1480
1490
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1481
1491
|
role: "alert",
|
|
@@ -1485,14 +1495,24 @@ function ValidationSummary({ issues, heading = "Review these fields before savin
|
|
|
1485
1495
|
children: heading
|
|
1486
1496
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
1487
1497
|
className: "mt-2 list-disc space-y-1 pl-5",
|
|
1488
|
-
children: issues.map((issue) =>
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1498
|
+
children: issues.map((issue) => {
|
|
1499
|
+
const displayLabel = fieldLabels?.[issue.field] ?? issue.field;
|
|
1500
|
+
const domId = fieldIds?.[issue.field];
|
|
1501
|
+
const label = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
1502
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1503
|
+
className: "font-semibold",
|
|
1504
|
+
children: [displayLabel, ":"]
|
|
1505
|
+
}),
|
|
1506
|
+
" ",
|
|
1507
|
+
issue.message
|
|
1508
|
+
] });
|
|
1509
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: domId ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1510
|
+
type: "button",
|
|
1511
|
+
onClick: () => scrollToField(domId),
|
|
1512
|
+
className: "underline decoration-amber-500/60 underline-offset-2 hover:decoration-amber-700 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-amber-600 rounded text-left",
|
|
1513
|
+
children: label
|
|
1514
|
+
}) : label }, `${issue.field}:${issue.message}`);
|
|
1515
|
+
})
|
|
1496
1516
|
})]
|
|
1497
1517
|
});
|
|
1498
1518
|
}
|
|
@@ -2879,53 +2899,72 @@ function getPageNumbers(current, total) {
|
|
|
2879
2899
|
pages.push(total);
|
|
2880
2900
|
return pages;
|
|
2881
2901
|
}
|
|
2882
|
-
function Pagination({ page, totalPages, onPageChange, ariaLabel = "Pagination" }) {
|
|
2883
|
-
if (totalPages <= 1) return null;
|
|
2902
|
+
function Pagination({ page, totalPages, onPageChange, ariaLabel = "Pagination", totalItems, pageSize, itemLabel = "items" }) {
|
|
2903
|
+
if (totalPages <= 1 && !totalItems) return null;
|
|
2904
|
+
const showRecordCount = typeof totalItems === "number" && typeof pageSize === "number";
|
|
2905
|
+
const rangeStart = showRecordCount ? (page - 1) * pageSize + 1 : null;
|
|
2906
|
+
const rangeEnd = showRecordCount ? Math.min(page * pageSize, totalItems) : null;
|
|
2884
2907
|
const pageNumbers = getPageNumbers(page, totalPages);
|
|
2885
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.
|
|
2886
|
-
"
|
|
2887
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.
|
|
2888
|
-
|
|
2889
|
-
|
|
2908
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2909
|
+
className: "flex flex-col items-center gap-1.5",
|
|
2910
|
+
children: [totalPages > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("nav", {
|
|
2911
|
+
"aria-label": ariaLabel,
|
|
2912
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("ol", {
|
|
2913
|
+
className: "list-none flex items-center gap-1",
|
|
2914
|
+
role: "list",
|
|
2915
|
+
children: [
|
|
2916
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2917
|
+
type: "button",
|
|
2918
|
+
onClick: () => onPageChange(page - 1),
|
|
2919
|
+
disabled: page <= 1,
|
|
2920
|
+
"aria-label": "Previous page",
|
|
2921
|
+
className: "flex h-9 w-9 items-center justify-center rounded-md border border-slate-200 bg-white text-slate-500 transition hover:border-slate-300 hover:text-slate-800 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30 disabled:cursor-not-allowed disabled:opacity-40",
|
|
2922
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronLeft, {
|
|
2923
|
+
size: 16,
|
|
2924
|
+
"aria-hidden": "true"
|
|
2925
|
+
})
|
|
2926
|
+
}) }),
|
|
2927
|
+
pageNumbers.map((n, idx) => n === "ellipsis" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
|
|
2928
|
+
"aria-hidden": "true",
|
|
2929
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2930
|
+
className: "flex h-9 w-9 items-center justify-center text-xs font-bold text-slate-300",
|
|
2931
|
+
children: "…"
|
|
2932
|
+
})
|
|
2933
|
+
}, `ellipsis-${idx}`) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2934
|
+
type: "button",
|
|
2935
|
+
onClick: () => onPageChange(n),
|
|
2936
|
+
"aria-label": `Page ${n}`,
|
|
2937
|
+
"aria-current": n === page ? "page" : void 0,
|
|
2938
|
+
className: ["flex h-9 w-9 items-center justify-center rounded-md text-xs font-bold transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30", n === page ? "bg-brand-blue text-white shadow-md shadow-brand-blue/20" : "border border-slate-200 bg-white text-slate-500 hover:border-slate-300 hover:text-slate-800"].filter(Boolean).join(" "),
|
|
2939
|
+
children: n
|
|
2940
|
+
}) }, n)),
|
|
2941
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2942
|
+
type: "button",
|
|
2943
|
+
onClick: () => onPageChange(page + 1),
|
|
2944
|
+
disabled: page >= totalPages,
|
|
2945
|
+
"aria-label": "Next page",
|
|
2946
|
+
className: "flex h-9 w-9 items-center justify-center rounded-md border border-slate-200 bg-white text-slate-500 transition hover:border-slate-300 hover:text-slate-800 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30 disabled:cursor-not-allowed disabled:opacity-40",
|
|
2947
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronRight, {
|
|
2948
|
+
size: 16,
|
|
2949
|
+
"aria-hidden": "true"
|
|
2950
|
+
})
|
|
2951
|
+
}) })
|
|
2952
|
+
]
|
|
2953
|
+
})
|
|
2954
|
+
}), showRecordCount && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
2955
|
+
className: "text-[11px] font-bold tabular-nums text-slate-400",
|
|
2956
|
+
"aria-live": "polite",
|
|
2890
2957
|
children: [
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
"aria-hidden": "true"
|
|
2900
|
-
})
|
|
2901
|
-
}) }),
|
|
2902
|
-
pageNumbers.map((n, idx) => n === "ellipsis" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
|
|
2903
|
-
"aria-hidden": "true",
|
|
2904
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2905
|
-
className: "flex h-9 w-9 items-center justify-center text-xs font-bold text-slate-300",
|
|
2906
|
-
children: "…"
|
|
2907
|
-
})
|
|
2908
|
-
}, `ellipsis-${idx}`) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2909
|
-
type: "button",
|
|
2910
|
-
onClick: () => onPageChange(n),
|
|
2911
|
-
"aria-label": `Page ${n}`,
|
|
2912
|
-
"aria-current": n === page ? "page" : void 0,
|
|
2913
|
-
className: ["flex h-9 w-9 items-center justify-center rounded-md text-xs font-bold transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30", n === page ? "bg-brand-blue text-white shadow-md shadow-brand-blue/20" : "border border-slate-200 bg-white text-slate-500 hover:border-slate-300 hover:text-slate-800"].filter(Boolean).join(" "),
|
|
2914
|
-
children: n
|
|
2915
|
-
}) }, n)),
|
|
2916
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2917
|
-
type: "button",
|
|
2918
|
-
onClick: () => onPageChange(page + 1),
|
|
2919
|
-
disabled: page >= totalPages,
|
|
2920
|
-
"aria-label": "Next page",
|
|
2921
|
-
className: "flex h-9 w-9 items-center justify-center rounded-md border border-slate-200 bg-white text-slate-500 transition hover:border-slate-300 hover:text-slate-800 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30 disabled:cursor-not-allowed disabled:opacity-40",
|
|
2922
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronRight, {
|
|
2923
|
-
size: 16,
|
|
2924
|
-
"aria-hidden": "true"
|
|
2925
|
-
})
|
|
2926
|
-
}) })
|
|
2958
|
+
"Showing ",
|
|
2959
|
+
rangeStart,
|
|
2960
|
+
"–",
|
|
2961
|
+
rangeEnd,
|
|
2962
|
+
" of ",
|
|
2963
|
+
totalItems,
|
|
2964
|
+
" ",
|
|
2965
|
+
itemLabel
|
|
2927
2966
|
]
|
|
2928
|
-
})
|
|
2967
|
+
})]
|
|
2929
2968
|
});
|
|
2930
2969
|
}
|
|
2931
2970
|
//#endregion
|
|
@@ -7528,6 +7567,211 @@ function StatusDot({ status, label, className = "" }) {
|
|
|
7528
7567
|
});
|
|
7529
7568
|
}
|
|
7530
7569
|
//#endregion
|
|
7570
|
+
//#region src/ui-library/gallery/column-manager/ColumnManager.tsx
|
|
7571
|
+
/**
|
|
7572
|
+
* ColumnManager — Design System Component
|
|
7573
|
+
*
|
|
7574
|
+
* A dropdown panel for managing table column visibility and order.
|
|
7575
|
+
* Provides checkboxes to show/hide individual columns and up/down
|
|
7576
|
+
* buttons to reorder them. Includes a reset action.
|
|
7577
|
+
*
|
|
7578
|
+
* Purely presentational — all state is owned by the caller.
|
|
7579
|
+
*
|
|
7580
|
+
* Usage:
|
|
7581
|
+
* const [hidden, setHidden] = useState<string[]>([]);
|
|
7582
|
+
* const [order, setOrder] = useState<string[] | null>(null);
|
|
7583
|
+
*
|
|
7584
|
+
* <ColumnManager
|
|
7585
|
+
* columns={[{ key: 'name', label: 'Name' }, { key: 'dept', label: 'Department' }]}
|
|
7586
|
+
* hiddenColumnKeys={hidden}
|
|
7587
|
+
* columnOrder={order}
|
|
7588
|
+
* onHiddenColumnKeysChange={setHidden}
|
|
7589
|
+
* onColumnOrderChange={setOrder}
|
|
7590
|
+
* onReset={() => { setHidden([]); setOrder(null); }}
|
|
7591
|
+
* />
|
|
7592
|
+
*
|
|
7593
|
+
* Accessibility:
|
|
7594
|
+
* - Trigger button has aria-expanded and aria-controls.
|
|
7595
|
+
* - Panel has role="region" and aria-label.
|
|
7596
|
+
* - Each checkbox is labelled via htmlFor + id.
|
|
7597
|
+
* - Reorder buttons have descriptive aria-labels.
|
|
7598
|
+
* - Panel closes on Escape, focus returns to the trigger.
|
|
7599
|
+
*/
|
|
7600
|
+
/**
|
|
7601
|
+
* Apply a stored column order to the runtime column array.
|
|
7602
|
+
*
|
|
7603
|
+
* Rules:
|
|
7604
|
+
* - Keys in `order` that don't exist in `columns` are silently ignored
|
|
7605
|
+
* (forward compatibility when columns are removed).
|
|
7606
|
+
* - Columns not present in `order` are appended at the end in their
|
|
7607
|
+
* original relative order (forward compatibility when columns are added).
|
|
7608
|
+
* - When `order` is null the original array is returned unchanged.
|
|
7609
|
+
*/
|
|
7610
|
+
function applyColumnOrder(columns, order) {
|
|
7611
|
+
if (!order) return columns;
|
|
7612
|
+
const byKey = new Map(columns.map((c) => [c.key, c]));
|
|
7613
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7614
|
+
const result = [];
|
|
7615
|
+
for (const key of order) {
|
|
7616
|
+
const col = byKey.get(key);
|
|
7617
|
+
if (col) {
|
|
7618
|
+
result.push(col);
|
|
7619
|
+
seen.add(key);
|
|
7620
|
+
}
|
|
7621
|
+
}
|
|
7622
|
+
for (const col of columns) if (!seen.has(col.key)) result.push(col);
|
|
7623
|
+
return result;
|
|
7624
|
+
}
|
|
7625
|
+
function ColumnManager({ columns, hiddenColumnKeys, columnOrder, onHiddenColumnKeysChange, onColumnOrderChange, onReset, triggerLabel = "Columns" }) {
|
|
7626
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
7627
|
+
const triggerRef = (0, react.useRef)(null);
|
|
7628
|
+
const panelRef = (0, react.useRef)(null);
|
|
7629
|
+
const panelId = "cm-panel";
|
|
7630
|
+
const orderedColumns = applyColumnOrder(columns, columnOrder);
|
|
7631
|
+
const hiddenSet = new Set(hiddenColumnKeys);
|
|
7632
|
+
const hiddenCount = hiddenColumnKeys.length;
|
|
7633
|
+
(0, react.useEffect)(() => {
|
|
7634
|
+
if (!open) return;
|
|
7635
|
+
const handlePointerDown = (e) => {
|
|
7636
|
+
if (triggerRef.current?.contains(e.target) || panelRef.current?.contains(e.target)) return;
|
|
7637
|
+
setOpen(false);
|
|
7638
|
+
};
|
|
7639
|
+
document.addEventListener("pointerdown", handlePointerDown);
|
|
7640
|
+
return () => document.removeEventListener("pointerdown", handlePointerDown);
|
|
7641
|
+
}, [open]);
|
|
7642
|
+
(0, react.useEffect)(() => {
|
|
7643
|
+
if (!open) return;
|
|
7644
|
+
const handleKeyDown = (e) => {
|
|
7645
|
+
if (e.key === "Escape") {
|
|
7646
|
+
setOpen(false);
|
|
7647
|
+
triggerRef.current?.focus();
|
|
7648
|
+
}
|
|
7649
|
+
};
|
|
7650
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
7651
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
7652
|
+
}, [open]);
|
|
7653
|
+
function toggleColumn(key) {
|
|
7654
|
+
onHiddenColumnKeysChange(hiddenSet.has(key) ? hiddenColumnKeys.filter((k) => k !== key) : [...hiddenColumnKeys, key]);
|
|
7655
|
+
}
|
|
7656
|
+
function moveUp(index) {
|
|
7657
|
+
if (index === 0) return;
|
|
7658
|
+
const next = [...orderedColumns];
|
|
7659
|
+
[next[index - 1], next[index]] = [next[index], next[index - 1]];
|
|
7660
|
+
onColumnOrderChange(next.map((c) => c.key));
|
|
7661
|
+
}
|
|
7662
|
+
function moveDown(index) {
|
|
7663
|
+
if (index === orderedColumns.length - 1) return;
|
|
7664
|
+
const next = [...orderedColumns];
|
|
7665
|
+
[next[index], next[index + 1]] = [next[index + 1], next[index]];
|
|
7666
|
+
onColumnOrderChange(next.map((c) => c.key));
|
|
7667
|
+
}
|
|
7668
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7669
|
+
className: "relative inline-block",
|
|
7670
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
7671
|
+
ref: triggerRef,
|
|
7672
|
+
type: "button",
|
|
7673
|
+
"aria-expanded": open,
|
|
7674
|
+
"aria-controls": open ? panelId : void 0,
|
|
7675
|
+
onClick: () => setOpen((v) => !v),
|
|
7676
|
+
className: "inline-flex items-center gap-1.5 rounded-lg border border-slate-200 bg-white px-3 py-2 text-xs font-bold text-slate-600 shadow-sm transition hover:border-slate-300 hover:bg-slate-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30",
|
|
7677
|
+
children: [
|
|
7678
|
+
triggerLabel,
|
|
7679
|
+
hiddenCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
7680
|
+
className: "rounded-full bg-brand-blue/10 px-1.5 py-0.5 font-bold text-brand-blue",
|
|
7681
|
+
children: [hiddenCount, " hidden"]
|
|
7682
|
+
}),
|
|
7683
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronDown, {
|
|
7684
|
+
size: 13,
|
|
7685
|
+
className: `transition-transform ${open ? "rotate-180" : ""}`,
|
|
7686
|
+
"aria-hidden": "true"
|
|
7687
|
+
})
|
|
7688
|
+
]
|
|
7689
|
+
}), open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7690
|
+
id: panelId,
|
|
7691
|
+
ref: panelRef,
|
|
7692
|
+
role: "region",
|
|
7693
|
+
"aria-label": "Manage columns",
|
|
7694
|
+
className: "absolute right-0 top-full z-30 mt-1 flex w-64 flex-col overflow-hidden rounded-xl border border-slate-200 bg-white shadow-xl",
|
|
7695
|
+
style: { maxHeight: 400 },
|
|
7696
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7697
|
+
className: "flex shrink-0 items-center justify-between border-b border-slate-100 px-4 py-2.5",
|
|
7698
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7699
|
+
className: "text-xs font-bold text-slate-700",
|
|
7700
|
+
children: "Columns"
|
|
7701
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7702
|
+
type: "button",
|
|
7703
|
+
onClick: () => {
|
|
7704
|
+
onReset();
|
|
7705
|
+
setOpen(false);
|
|
7706
|
+
},
|
|
7707
|
+
className: "rounded-md px-2 py-1 text-[11px] font-bold text-slate-400 hover:bg-slate-100 hover:text-slate-700 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-brand-blue/30 transition",
|
|
7708
|
+
children: "Reset"
|
|
7709
|
+
})]
|
|
7710
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
7711
|
+
className: "overflow-y-auto",
|
|
7712
|
+
role: "list",
|
|
7713
|
+
children: orderedColumns.map((col, idx) => {
|
|
7714
|
+
const visible = !hiddenSet.has(col.key);
|
|
7715
|
+
const checkboxId = `cm-col-${col.key}`;
|
|
7716
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
7717
|
+
className: "flex items-center gap-2 border-b border-slate-50 px-3 py-2 last:border-0",
|
|
7718
|
+
children: [
|
|
7719
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
7720
|
+
type: "checkbox",
|
|
7721
|
+
id: checkboxId,
|
|
7722
|
+
checked: visible,
|
|
7723
|
+
onChange: () => toggleColumn(col.key),
|
|
7724
|
+
className: "h-3.5 w-3.5 rounded accent-brand-blue focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-brand-blue/30"
|
|
7725
|
+
}),
|
|
7726
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
7727
|
+
htmlFor: checkboxId,
|
|
7728
|
+
className: `flex-1 cursor-pointer text-xs font-medium ${visible ? "text-slate-700" : "text-slate-400"}`,
|
|
7729
|
+
children: col.label
|
|
7730
|
+
}),
|
|
7731
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7732
|
+
className: "flex shrink-0 gap-0.5",
|
|
7733
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7734
|
+
type: "button",
|
|
7735
|
+
"aria-label": `Move ${col.label} up`,
|
|
7736
|
+
disabled: idx === 0,
|
|
7737
|
+
onClick: () => moveUp(idx),
|
|
7738
|
+
className: "rounded p-1 text-slate-300 hover:bg-slate-100 hover:text-slate-600 disabled:cursor-not-allowed disabled:opacity-30 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-brand-blue/30 transition",
|
|
7739
|
+
children: "▲"
|
|
7740
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7741
|
+
type: "button",
|
|
7742
|
+
"aria-label": `Move ${col.label} down`,
|
|
7743
|
+
disabled: idx === orderedColumns.length - 1,
|
|
7744
|
+
onClick: () => moveDown(idx),
|
|
7745
|
+
className: "rounded p-1 text-slate-300 hover:bg-slate-100 hover:text-slate-600 disabled:cursor-not-allowed disabled:opacity-30 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-brand-blue/30 transition",
|
|
7746
|
+
children: "▼"
|
|
7747
|
+
})]
|
|
7748
|
+
})
|
|
7749
|
+
]
|
|
7750
|
+
}, col.key);
|
|
7751
|
+
})
|
|
7752
|
+
})]
|
|
7753
|
+
})]
|
|
7754
|
+
});
|
|
7755
|
+
}
|
|
7756
|
+
//#endregion
|
|
7757
|
+
//#region src/ui-library/gallery/status-dot/MaskedValue.tsx
|
|
7758
|
+
function MaskedValue({ value, masked, maskText = "••••• •••••", maskLabel = "Value restricted", className = "" }) {
|
|
7759
|
+
if (!masked) return className ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7760
|
+
className,
|
|
7761
|
+
children: value
|
|
7762
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: value });
|
|
7763
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7764
|
+
"aria-label": maskLabel,
|
|
7765
|
+
title: maskLabel,
|
|
7766
|
+
className: [
|
|
7767
|
+
"inline-flex select-none items-center rounded-md bg-slate-100 px-2 py-0.5",
|
|
7768
|
+
"font-mono text-xs font-medium text-slate-400 tracking-wider",
|
|
7769
|
+
className
|
|
7770
|
+
].filter(Boolean).join(" "),
|
|
7771
|
+
children: maskText
|
|
7772
|
+
});
|
|
7773
|
+
}
|
|
7774
|
+
//#endregion
|
|
7531
7775
|
//#region src/ui-library/gallery/status-actions/statusActions.ts
|
|
7532
7776
|
/**
|
|
7533
7777
|
* statusActions — Gallery-native lifecycle transition utilities.
|
|
@@ -7732,6 +7976,7 @@ exports.CardContent = CardContent;
|
|
|
7732
7976
|
exports.CardFooter = CardFooter;
|
|
7733
7977
|
exports.CardHeader = CardHeader;
|
|
7734
7978
|
exports.Checkbox = Checkbox;
|
|
7979
|
+
exports.ColumnManager = ColumnManager;
|
|
7735
7980
|
exports.Combobox = Combobox;
|
|
7736
7981
|
exports.CommandPalette = CommandPalette;
|
|
7737
7982
|
exports.ConfirmAction = ConfirmAction;
|
|
@@ -7759,6 +8004,7 @@ exports.Input = Input;
|
|
|
7759
8004
|
exports.IsoCubeBlock = IsoCubeBlock;
|
|
7760
8005
|
exports.Kanban = Kanban;
|
|
7761
8006
|
exports.LineChart = LineChart;
|
|
8007
|
+
exports.MaskedValue = MaskedValue;
|
|
7762
8008
|
exports.Menu = Menu;
|
|
7763
8009
|
exports.Modal = Modal;
|
|
7764
8010
|
exports.OrgChart = OrgChart;
|
|
@@ -7818,6 +8064,7 @@ exports.Toolbar = Toolbar;
|
|
|
7818
8064
|
exports.Tooltip = Tooltip;
|
|
7819
8065
|
exports.TreeView = TreeView;
|
|
7820
8066
|
exports.ValidationSummary = ValidationSummary;
|
|
8067
|
+
exports.applyColumnOrder = applyColumnOrder;
|
|
7821
8068
|
Object.defineProperty(exports, "colors", {
|
|
7822
8069
|
enumerable: true,
|
|
7823
8070
|
get: function() {
|
package/lib/index.mjs
CHANGED
|
@@ -1450,7 +1450,17 @@ function SegmentedControl({ options, defaultValue, value: controlledValue, onVal
|
|
|
1450
1450
|
}
|
|
1451
1451
|
//#endregion
|
|
1452
1452
|
//#region src/ui-library/gallery/validation-summary/ValidationSummary.tsx
|
|
1453
|
-
function
|
|
1453
|
+
function scrollToField(domId) {
|
|
1454
|
+
const el = document.getElementById(domId);
|
|
1455
|
+
if (!el) return;
|
|
1456
|
+
const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
1457
|
+
el.scrollIntoView({
|
|
1458
|
+
behavior: prefersReducedMotion ? "auto" : "smooth",
|
|
1459
|
+
block: "center"
|
|
1460
|
+
});
|
|
1461
|
+
(el.querySelector("input,select,textarea,[tabindex]") ?? el).focus();
|
|
1462
|
+
}
|
|
1463
|
+
function ValidationSummary({ issues, heading = "Review these fields before saving", fieldIds, fieldLabels, className = "" }) {
|
|
1454
1464
|
if (issues.length === 0) return null;
|
|
1455
1465
|
return /* @__PURE__ */ jsxs("div", {
|
|
1456
1466
|
role: "alert",
|
|
@@ -1460,14 +1470,24 @@ function ValidationSummary({ issues, heading = "Review these fields before savin
|
|
|
1460
1470
|
children: heading
|
|
1461
1471
|
}), /* @__PURE__ */ jsx("ul", {
|
|
1462
1472
|
className: "mt-2 list-disc space-y-1 pl-5",
|
|
1463
|
-
children: issues.map((issue) =>
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1473
|
+
children: issues.map((issue) => {
|
|
1474
|
+
const displayLabel = fieldLabels?.[issue.field] ?? issue.field;
|
|
1475
|
+
const domId = fieldIds?.[issue.field];
|
|
1476
|
+
const label = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1477
|
+
/* @__PURE__ */ jsxs("span", {
|
|
1478
|
+
className: "font-semibold",
|
|
1479
|
+
children: [displayLabel, ":"]
|
|
1480
|
+
}),
|
|
1481
|
+
" ",
|
|
1482
|
+
issue.message
|
|
1483
|
+
] });
|
|
1484
|
+
return /* @__PURE__ */ jsx("li", { children: domId ? /* @__PURE__ */ jsx("button", {
|
|
1485
|
+
type: "button",
|
|
1486
|
+
onClick: () => scrollToField(domId),
|
|
1487
|
+
className: "underline decoration-amber-500/60 underline-offset-2 hover:decoration-amber-700 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-amber-600 rounded text-left",
|
|
1488
|
+
children: label
|
|
1489
|
+
}) : label }, `${issue.field}:${issue.message}`);
|
|
1490
|
+
})
|
|
1471
1491
|
})]
|
|
1472
1492
|
});
|
|
1473
1493
|
}
|
|
@@ -2854,53 +2874,72 @@ function getPageNumbers(current, total) {
|
|
|
2854
2874
|
pages.push(total);
|
|
2855
2875
|
return pages;
|
|
2856
2876
|
}
|
|
2857
|
-
function Pagination({ page, totalPages, onPageChange, ariaLabel = "Pagination" }) {
|
|
2858
|
-
if (totalPages <= 1) return null;
|
|
2877
|
+
function Pagination({ page, totalPages, onPageChange, ariaLabel = "Pagination", totalItems, pageSize, itemLabel = "items" }) {
|
|
2878
|
+
if (totalPages <= 1 && !totalItems) return null;
|
|
2879
|
+
const showRecordCount = typeof totalItems === "number" && typeof pageSize === "number";
|
|
2880
|
+
const rangeStart = showRecordCount ? (page - 1) * pageSize + 1 : null;
|
|
2881
|
+
const rangeEnd = showRecordCount ? Math.min(page * pageSize, totalItems) : null;
|
|
2859
2882
|
const pageNumbers = getPageNumbers(page, totalPages);
|
|
2860
|
-
return /* @__PURE__ */
|
|
2861
|
-
"
|
|
2862
|
-
children: /* @__PURE__ */
|
|
2863
|
-
|
|
2864
|
-
|
|
2883
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
2884
|
+
className: "flex flex-col items-center gap-1.5",
|
|
2885
|
+
children: [totalPages > 1 && /* @__PURE__ */ jsx("nav", {
|
|
2886
|
+
"aria-label": ariaLabel,
|
|
2887
|
+
children: /* @__PURE__ */ jsxs("ol", {
|
|
2888
|
+
className: "list-none flex items-center gap-1",
|
|
2889
|
+
role: "list",
|
|
2890
|
+
children: [
|
|
2891
|
+
/* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("button", {
|
|
2892
|
+
type: "button",
|
|
2893
|
+
onClick: () => onPageChange(page - 1),
|
|
2894
|
+
disabled: page <= 1,
|
|
2895
|
+
"aria-label": "Previous page",
|
|
2896
|
+
className: "flex h-9 w-9 items-center justify-center rounded-md border border-slate-200 bg-white text-slate-500 transition hover:border-slate-300 hover:text-slate-800 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30 disabled:cursor-not-allowed disabled:opacity-40",
|
|
2897
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, {
|
|
2898
|
+
size: 16,
|
|
2899
|
+
"aria-hidden": "true"
|
|
2900
|
+
})
|
|
2901
|
+
}) }),
|
|
2902
|
+
pageNumbers.map((n, idx) => n === "ellipsis" ? /* @__PURE__ */ jsx("li", {
|
|
2903
|
+
"aria-hidden": "true",
|
|
2904
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
2905
|
+
className: "flex h-9 w-9 items-center justify-center text-xs font-bold text-slate-300",
|
|
2906
|
+
children: "…"
|
|
2907
|
+
})
|
|
2908
|
+
}, `ellipsis-${idx}`) : /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("button", {
|
|
2909
|
+
type: "button",
|
|
2910
|
+
onClick: () => onPageChange(n),
|
|
2911
|
+
"aria-label": `Page ${n}`,
|
|
2912
|
+
"aria-current": n === page ? "page" : void 0,
|
|
2913
|
+
className: ["flex h-9 w-9 items-center justify-center rounded-md text-xs font-bold transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30", n === page ? "bg-brand-blue text-white shadow-md shadow-brand-blue/20" : "border border-slate-200 bg-white text-slate-500 hover:border-slate-300 hover:text-slate-800"].filter(Boolean).join(" "),
|
|
2914
|
+
children: n
|
|
2915
|
+
}) }, n)),
|
|
2916
|
+
/* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("button", {
|
|
2917
|
+
type: "button",
|
|
2918
|
+
onClick: () => onPageChange(page + 1),
|
|
2919
|
+
disabled: page >= totalPages,
|
|
2920
|
+
"aria-label": "Next page",
|
|
2921
|
+
className: "flex h-9 w-9 items-center justify-center rounded-md border border-slate-200 bg-white text-slate-500 transition hover:border-slate-300 hover:text-slate-800 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30 disabled:cursor-not-allowed disabled:opacity-40",
|
|
2922
|
+
children: /* @__PURE__ */ jsx(ChevronRight, {
|
|
2923
|
+
size: 16,
|
|
2924
|
+
"aria-hidden": "true"
|
|
2925
|
+
})
|
|
2926
|
+
}) })
|
|
2927
|
+
]
|
|
2928
|
+
})
|
|
2929
|
+
}), showRecordCount && /* @__PURE__ */ jsxs("p", {
|
|
2930
|
+
className: "text-[11px] font-bold tabular-nums text-slate-400",
|
|
2931
|
+
"aria-live": "polite",
|
|
2865
2932
|
children: [
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
"aria-hidden": "true"
|
|
2875
|
-
})
|
|
2876
|
-
}) }),
|
|
2877
|
-
pageNumbers.map((n, idx) => n === "ellipsis" ? /* @__PURE__ */ jsx("li", {
|
|
2878
|
-
"aria-hidden": "true",
|
|
2879
|
-
children: /* @__PURE__ */ jsx("span", {
|
|
2880
|
-
className: "flex h-9 w-9 items-center justify-center text-xs font-bold text-slate-300",
|
|
2881
|
-
children: "…"
|
|
2882
|
-
})
|
|
2883
|
-
}, `ellipsis-${idx}`) : /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("button", {
|
|
2884
|
-
type: "button",
|
|
2885
|
-
onClick: () => onPageChange(n),
|
|
2886
|
-
"aria-label": `Page ${n}`,
|
|
2887
|
-
"aria-current": n === page ? "page" : void 0,
|
|
2888
|
-
className: ["flex h-9 w-9 items-center justify-center rounded-md text-xs font-bold transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30", n === page ? "bg-brand-blue text-white shadow-md shadow-brand-blue/20" : "border border-slate-200 bg-white text-slate-500 hover:border-slate-300 hover:text-slate-800"].filter(Boolean).join(" "),
|
|
2889
|
-
children: n
|
|
2890
|
-
}) }, n)),
|
|
2891
|
-
/* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("button", {
|
|
2892
|
-
type: "button",
|
|
2893
|
-
onClick: () => onPageChange(page + 1),
|
|
2894
|
-
disabled: page >= totalPages,
|
|
2895
|
-
"aria-label": "Next page",
|
|
2896
|
-
className: "flex h-9 w-9 items-center justify-center rounded-md border border-slate-200 bg-white text-slate-500 transition hover:border-slate-300 hover:text-slate-800 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30 disabled:cursor-not-allowed disabled:opacity-40",
|
|
2897
|
-
children: /* @__PURE__ */ jsx(ChevronRight, {
|
|
2898
|
-
size: 16,
|
|
2899
|
-
"aria-hidden": "true"
|
|
2900
|
-
})
|
|
2901
|
-
}) })
|
|
2933
|
+
"Showing ",
|
|
2934
|
+
rangeStart,
|
|
2935
|
+
"–",
|
|
2936
|
+
rangeEnd,
|
|
2937
|
+
" of ",
|
|
2938
|
+
totalItems,
|
|
2939
|
+
" ",
|
|
2940
|
+
itemLabel
|
|
2902
2941
|
]
|
|
2903
|
-
})
|
|
2942
|
+
})]
|
|
2904
2943
|
});
|
|
2905
2944
|
}
|
|
2906
2945
|
//#endregion
|
|
@@ -7503,6 +7542,211 @@ function StatusDot({ status, label, className = "" }) {
|
|
|
7503
7542
|
});
|
|
7504
7543
|
}
|
|
7505
7544
|
//#endregion
|
|
7545
|
+
//#region src/ui-library/gallery/column-manager/ColumnManager.tsx
|
|
7546
|
+
/**
|
|
7547
|
+
* ColumnManager — Design System Component
|
|
7548
|
+
*
|
|
7549
|
+
* A dropdown panel for managing table column visibility and order.
|
|
7550
|
+
* Provides checkboxes to show/hide individual columns and up/down
|
|
7551
|
+
* buttons to reorder them. Includes a reset action.
|
|
7552
|
+
*
|
|
7553
|
+
* Purely presentational — all state is owned by the caller.
|
|
7554
|
+
*
|
|
7555
|
+
* Usage:
|
|
7556
|
+
* const [hidden, setHidden] = useState<string[]>([]);
|
|
7557
|
+
* const [order, setOrder] = useState<string[] | null>(null);
|
|
7558
|
+
*
|
|
7559
|
+
* <ColumnManager
|
|
7560
|
+
* columns={[{ key: 'name', label: 'Name' }, { key: 'dept', label: 'Department' }]}
|
|
7561
|
+
* hiddenColumnKeys={hidden}
|
|
7562
|
+
* columnOrder={order}
|
|
7563
|
+
* onHiddenColumnKeysChange={setHidden}
|
|
7564
|
+
* onColumnOrderChange={setOrder}
|
|
7565
|
+
* onReset={() => { setHidden([]); setOrder(null); }}
|
|
7566
|
+
* />
|
|
7567
|
+
*
|
|
7568
|
+
* Accessibility:
|
|
7569
|
+
* - Trigger button has aria-expanded and aria-controls.
|
|
7570
|
+
* - Panel has role="region" and aria-label.
|
|
7571
|
+
* - Each checkbox is labelled via htmlFor + id.
|
|
7572
|
+
* - Reorder buttons have descriptive aria-labels.
|
|
7573
|
+
* - Panel closes on Escape, focus returns to the trigger.
|
|
7574
|
+
*/
|
|
7575
|
+
/**
|
|
7576
|
+
* Apply a stored column order to the runtime column array.
|
|
7577
|
+
*
|
|
7578
|
+
* Rules:
|
|
7579
|
+
* - Keys in `order` that don't exist in `columns` are silently ignored
|
|
7580
|
+
* (forward compatibility when columns are removed).
|
|
7581
|
+
* - Columns not present in `order` are appended at the end in their
|
|
7582
|
+
* original relative order (forward compatibility when columns are added).
|
|
7583
|
+
* - When `order` is null the original array is returned unchanged.
|
|
7584
|
+
*/
|
|
7585
|
+
function applyColumnOrder(columns, order) {
|
|
7586
|
+
if (!order) return columns;
|
|
7587
|
+
const byKey = new Map(columns.map((c) => [c.key, c]));
|
|
7588
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7589
|
+
const result = [];
|
|
7590
|
+
for (const key of order) {
|
|
7591
|
+
const col = byKey.get(key);
|
|
7592
|
+
if (col) {
|
|
7593
|
+
result.push(col);
|
|
7594
|
+
seen.add(key);
|
|
7595
|
+
}
|
|
7596
|
+
}
|
|
7597
|
+
for (const col of columns) if (!seen.has(col.key)) result.push(col);
|
|
7598
|
+
return result;
|
|
7599
|
+
}
|
|
7600
|
+
function ColumnManager({ columns, hiddenColumnKeys, columnOrder, onHiddenColumnKeysChange, onColumnOrderChange, onReset, triggerLabel = "Columns" }) {
|
|
7601
|
+
const [open, setOpen] = useState(false);
|
|
7602
|
+
const triggerRef = useRef(null);
|
|
7603
|
+
const panelRef = useRef(null);
|
|
7604
|
+
const panelId = "cm-panel";
|
|
7605
|
+
const orderedColumns = applyColumnOrder(columns, columnOrder);
|
|
7606
|
+
const hiddenSet = new Set(hiddenColumnKeys);
|
|
7607
|
+
const hiddenCount = hiddenColumnKeys.length;
|
|
7608
|
+
useEffect(() => {
|
|
7609
|
+
if (!open) return;
|
|
7610
|
+
const handlePointerDown = (e) => {
|
|
7611
|
+
if (triggerRef.current?.contains(e.target) || panelRef.current?.contains(e.target)) return;
|
|
7612
|
+
setOpen(false);
|
|
7613
|
+
};
|
|
7614
|
+
document.addEventListener("pointerdown", handlePointerDown);
|
|
7615
|
+
return () => document.removeEventListener("pointerdown", handlePointerDown);
|
|
7616
|
+
}, [open]);
|
|
7617
|
+
useEffect(() => {
|
|
7618
|
+
if (!open) return;
|
|
7619
|
+
const handleKeyDown = (e) => {
|
|
7620
|
+
if (e.key === "Escape") {
|
|
7621
|
+
setOpen(false);
|
|
7622
|
+
triggerRef.current?.focus();
|
|
7623
|
+
}
|
|
7624
|
+
};
|
|
7625
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
7626
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
7627
|
+
}, [open]);
|
|
7628
|
+
function toggleColumn(key) {
|
|
7629
|
+
onHiddenColumnKeysChange(hiddenSet.has(key) ? hiddenColumnKeys.filter((k) => k !== key) : [...hiddenColumnKeys, key]);
|
|
7630
|
+
}
|
|
7631
|
+
function moveUp(index) {
|
|
7632
|
+
if (index === 0) return;
|
|
7633
|
+
const next = [...orderedColumns];
|
|
7634
|
+
[next[index - 1], next[index]] = [next[index], next[index - 1]];
|
|
7635
|
+
onColumnOrderChange(next.map((c) => c.key));
|
|
7636
|
+
}
|
|
7637
|
+
function moveDown(index) {
|
|
7638
|
+
if (index === orderedColumns.length - 1) return;
|
|
7639
|
+
const next = [...orderedColumns];
|
|
7640
|
+
[next[index], next[index + 1]] = [next[index + 1], next[index]];
|
|
7641
|
+
onColumnOrderChange(next.map((c) => c.key));
|
|
7642
|
+
}
|
|
7643
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
7644
|
+
className: "relative inline-block",
|
|
7645
|
+
children: [/* @__PURE__ */ jsxs("button", {
|
|
7646
|
+
ref: triggerRef,
|
|
7647
|
+
type: "button",
|
|
7648
|
+
"aria-expanded": open,
|
|
7649
|
+
"aria-controls": open ? panelId : void 0,
|
|
7650
|
+
onClick: () => setOpen((v) => !v),
|
|
7651
|
+
className: "inline-flex items-center gap-1.5 rounded-lg border border-slate-200 bg-white px-3 py-2 text-xs font-bold text-slate-600 shadow-sm transition hover:border-slate-300 hover:bg-slate-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue/30",
|
|
7652
|
+
children: [
|
|
7653
|
+
triggerLabel,
|
|
7654
|
+
hiddenCount > 0 && /* @__PURE__ */ jsxs("span", {
|
|
7655
|
+
className: "rounded-full bg-brand-blue/10 px-1.5 py-0.5 font-bold text-brand-blue",
|
|
7656
|
+
children: [hiddenCount, " hidden"]
|
|
7657
|
+
}),
|
|
7658
|
+
/* @__PURE__ */ jsx(ChevronDown, {
|
|
7659
|
+
size: 13,
|
|
7660
|
+
className: `transition-transform ${open ? "rotate-180" : ""}`,
|
|
7661
|
+
"aria-hidden": "true"
|
|
7662
|
+
})
|
|
7663
|
+
]
|
|
7664
|
+
}), open && /* @__PURE__ */ jsxs("div", {
|
|
7665
|
+
id: panelId,
|
|
7666
|
+
ref: panelRef,
|
|
7667
|
+
role: "region",
|
|
7668
|
+
"aria-label": "Manage columns",
|
|
7669
|
+
className: "absolute right-0 top-full z-30 mt-1 flex w-64 flex-col overflow-hidden rounded-xl border border-slate-200 bg-white shadow-xl",
|
|
7670
|
+
style: { maxHeight: 400 },
|
|
7671
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
7672
|
+
className: "flex shrink-0 items-center justify-between border-b border-slate-100 px-4 py-2.5",
|
|
7673
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
7674
|
+
className: "text-xs font-bold text-slate-700",
|
|
7675
|
+
children: "Columns"
|
|
7676
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
7677
|
+
type: "button",
|
|
7678
|
+
onClick: () => {
|
|
7679
|
+
onReset();
|
|
7680
|
+
setOpen(false);
|
|
7681
|
+
},
|
|
7682
|
+
className: "rounded-md px-2 py-1 text-[11px] font-bold text-slate-400 hover:bg-slate-100 hover:text-slate-700 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-brand-blue/30 transition",
|
|
7683
|
+
children: "Reset"
|
|
7684
|
+
})]
|
|
7685
|
+
}), /* @__PURE__ */ jsx("ul", {
|
|
7686
|
+
className: "overflow-y-auto",
|
|
7687
|
+
role: "list",
|
|
7688
|
+
children: orderedColumns.map((col, idx) => {
|
|
7689
|
+
const visible = !hiddenSet.has(col.key);
|
|
7690
|
+
const checkboxId = `cm-col-${col.key}`;
|
|
7691
|
+
return /* @__PURE__ */ jsxs("li", {
|
|
7692
|
+
className: "flex items-center gap-2 border-b border-slate-50 px-3 py-2 last:border-0",
|
|
7693
|
+
children: [
|
|
7694
|
+
/* @__PURE__ */ jsx("input", {
|
|
7695
|
+
type: "checkbox",
|
|
7696
|
+
id: checkboxId,
|
|
7697
|
+
checked: visible,
|
|
7698
|
+
onChange: () => toggleColumn(col.key),
|
|
7699
|
+
className: "h-3.5 w-3.5 rounded accent-brand-blue focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-brand-blue/30"
|
|
7700
|
+
}),
|
|
7701
|
+
/* @__PURE__ */ jsx("label", {
|
|
7702
|
+
htmlFor: checkboxId,
|
|
7703
|
+
className: `flex-1 cursor-pointer text-xs font-medium ${visible ? "text-slate-700" : "text-slate-400"}`,
|
|
7704
|
+
children: col.label
|
|
7705
|
+
}),
|
|
7706
|
+
/* @__PURE__ */ jsxs("div", {
|
|
7707
|
+
className: "flex shrink-0 gap-0.5",
|
|
7708
|
+
children: [/* @__PURE__ */ jsx("button", {
|
|
7709
|
+
type: "button",
|
|
7710
|
+
"aria-label": `Move ${col.label} up`,
|
|
7711
|
+
disabled: idx === 0,
|
|
7712
|
+
onClick: () => moveUp(idx),
|
|
7713
|
+
className: "rounded p-1 text-slate-300 hover:bg-slate-100 hover:text-slate-600 disabled:cursor-not-allowed disabled:opacity-30 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-brand-blue/30 transition",
|
|
7714
|
+
children: "▲"
|
|
7715
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
7716
|
+
type: "button",
|
|
7717
|
+
"aria-label": `Move ${col.label} down`,
|
|
7718
|
+
disabled: idx === orderedColumns.length - 1,
|
|
7719
|
+
onClick: () => moveDown(idx),
|
|
7720
|
+
className: "rounded p-1 text-slate-300 hover:bg-slate-100 hover:text-slate-600 disabled:cursor-not-allowed disabled:opacity-30 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-brand-blue/30 transition",
|
|
7721
|
+
children: "▼"
|
|
7722
|
+
})]
|
|
7723
|
+
})
|
|
7724
|
+
]
|
|
7725
|
+
}, col.key);
|
|
7726
|
+
})
|
|
7727
|
+
})]
|
|
7728
|
+
})]
|
|
7729
|
+
});
|
|
7730
|
+
}
|
|
7731
|
+
//#endregion
|
|
7732
|
+
//#region src/ui-library/gallery/status-dot/MaskedValue.tsx
|
|
7733
|
+
function MaskedValue({ value, masked, maskText = "••••• •••••", maskLabel = "Value restricted", className = "" }) {
|
|
7734
|
+
if (!masked) return className ? /* @__PURE__ */ jsx("span", {
|
|
7735
|
+
className,
|
|
7736
|
+
children: value
|
|
7737
|
+
}) : /* @__PURE__ */ jsx(Fragment, { children: value });
|
|
7738
|
+
return /* @__PURE__ */ jsx("span", {
|
|
7739
|
+
"aria-label": maskLabel,
|
|
7740
|
+
title: maskLabel,
|
|
7741
|
+
className: [
|
|
7742
|
+
"inline-flex select-none items-center rounded-md bg-slate-100 px-2 py-0.5",
|
|
7743
|
+
"font-mono text-xs font-medium text-slate-400 tracking-wider",
|
|
7744
|
+
className
|
|
7745
|
+
].filter(Boolean).join(" "),
|
|
7746
|
+
children: maskText
|
|
7747
|
+
});
|
|
7748
|
+
}
|
|
7749
|
+
//#endregion
|
|
7506
7750
|
//#region src/ui-library/gallery/status-actions/statusActions.ts
|
|
7507
7751
|
/**
|
|
7508
7752
|
* statusActions — Gallery-native lifecycle transition utilities.
|
|
@@ -7686,4 +7930,4 @@ var shadow = {
|
|
|
7686
7930
|
overlay: "shadow-xl"
|
|
7687
7931
|
};
|
|
7688
7932
|
//#endregion
|
|
7689
|
-
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, ActivityFeed, Alert, ApprovalTimeline, AreaChart, Avatar, Badge, BarChart, BarList, Breadcrumb, Button, CHART_PALETTE, 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, IsoCubeBlock, Kanban, LineChart, Menu, Modal, OrgChart, OrgUnitTree, OtpInput, PafTemplate, PageHeader, Pagination, PayslipTemplate, PermissionMatrix, Popover, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioItem, ReportTemplate, STATUS_PAGE_PRESETS, Section, SegmentedControl, Select, SidebarGroup, SidebarItem, SidebarNav, SidebarSection, Skeleton, SkeletonAvatar, SkeletonCard, SkeletonTableRow, SkeletonText, Spinner, Statistic, StatusBadge, StatusDot, StatusPage, Stepper, Switch, Tab, TabList, TabPanel, TabPanels, Tabs, Textarea, TimekeepingTemplate, 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 };
|
|
7933
|
+
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, ActivityFeed, Alert, ApprovalTimeline, AreaChart, Avatar, Badge, BarChart, BarList, Breadcrumb, Button, CHART_PALETTE, Calendar, Card, CardContent, CardFooter, CardHeader, Checkbox, ColumnManager, Combobox, CommandPalette, ConfirmAction, ConfirmDialog, DatePicker, DescriptionItem, DescriptionList, Dialog, DialogBody, DialogFooter, Divider, Drawer, Dropdown, EmployeeCard, EmptyState, ErrorBanner, Field, FieldContext, FieldInput, FileUpload, FilterChip, Heatmap, IconButton, Input, IsoCubeBlock, Kanban, LineChart, MaskedValue, Menu, Modal, OrgChart, OrgUnitTree, OtpInput, PafTemplate, PageHeader, Pagination, PayslipTemplate, PermissionMatrix, Popover, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioItem, ReportTemplate, STATUS_PAGE_PRESETS, Section, SegmentedControl, Select, SidebarGroup, SidebarItem, SidebarNav, SidebarSection, Skeleton, SkeletonAvatar, SkeletonCard, SkeletonTableRow, SkeletonText, Spinner, Statistic, StatusBadge, StatusDot, StatusPage, Stepper, Switch, Tab, TabList, TabPanel, TabPanels, Tabs, Textarea, TimekeepingTemplate, Timeline, TimelineEmptyState, TimelineFooter, TimelineItem, TimelineValueCard, Toaster, Toolbar, Tooltip, TreeView, ValidationSummary, applyColumnOrder, 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,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ColumnManager — Design System Component
|
|
3
|
+
*
|
|
4
|
+
* A dropdown panel for managing table column visibility and order.
|
|
5
|
+
* Provides checkboxes to show/hide individual columns and up/down
|
|
6
|
+
* buttons to reorder them. Includes a reset action.
|
|
7
|
+
*
|
|
8
|
+
* Purely presentational — all state is owned by the caller.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* const [hidden, setHidden] = useState<string[]>([]);
|
|
12
|
+
* const [order, setOrder] = useState<string[] | null>(null);
|
|
13
|
+
*
|
|
14
|
+
* <ColumnManager
|
|
15
|
+
* columns={[{ key: 'name', label: 'Name' }, { key: 'dept', label: 'Department' }]}
|
|
16
|
+
* hiddenColumnKeys={hidden}
|
|
17
|
+
* columnOrder={order}
|
|
18
|
+
* onHiddenColumnKeysChange={setHidden}
|
|
19
|
+
* onColumnOrderChange={setOrder}
|
|
20
|
+
* onReset={() => { setHidden([]); setOrder(null); }}
|
|
21
|
+
* />
|
|
22
|
+
*
|
|
23
|
+
* Accessibility:
|
|
24
|
+
* - Trigger button has aria-expanded and aria-controls.
|
|
25
|
+
* - Panel has role="region" and aria-label.
|
|
26
|
+
* - Each checkbox is labelled via htmlFor + id.
|
|
27
|
+
* - Reorder buttons have descriptive aria-labels.
|
|
28
|
+
* - Panel closes on Escape, focus returns to the trigger.
|
|
29
|
+
*/
|
|
30
|
+
export interface ColumnManagerColumn {
|
|
31
|
+
/** Unique key matching the data column identifier. */
|
|
32
|
+
key: string;
|
|
33
|
+
/** Human-readable column label shown in the panel. */
|
|
34
|
+
label: string;
|
|
35
|
+
}
|
|
36
|
+
export interface ColumnManagerProps {
|
|
37
|
+
/** All available columns in their default order. */
|
|
38
|
+
columns: ColumnManagerColumn[];
|
|
39
|
+
/** Keys of currently hidden columns. */
|
|
40
|
+
hiddenColumnKeys: string[];
|
|
41
|
+
/**
|
|
42
|
+
* Current column order as an array of keys.
|
|
43
|
+
* Pass null to use the default order from `columns`.
|
|
44
|
+
*/
|
|
45
|
+
columnOrder: string[] | null;
|
|
46
|
+
/** Called with the new hidden-keys array when visibility changes. */
|
|
47
|
+
onHiddenColumnKeysChange: (keys: string[]) => void;
|
|
48
|
+
/** Called with the new key order when a column is moved. */
|
|
49
|
+
onColumnOrderChange: (order: string[]) => void;
|
|
50
|
+
/** Called when the user clicks Reset — caller restores defaults. */
|
|
51
|
+
onReset: () => void;
|
|
52
|
+
/** Label for the trigger button. Default: "Columns" */
|
|
53
|
+
triggerLabel?: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Apply a stored column order to the runtime column array.
|
|
57
|
+
*
|
|
58
|
+
* Rules:
|
|
59
|
+
* - Keys in `order` that don't exist in `columns` are silently ignored
|
|
60
|
+
* (forward compatibility when columns are removed).
|
|
61
|
+
* - Columns not present in `order` are appended at the end in their
|
|
62
|
+
* original relative order (forward compatibility when columns are added).
|
|
63
|
+
* - When `order` is null the original array is returned unchanged.
|
|
64
|
+
*/
|
|
65
|
+
export declare function applyColumnOrder(columns: ColumnManagerColumn[], order: string[] | null): ColumnManagerColumn[];
|
|
66
|
+
export declare function ColumnManager({ columns, hiddenColumnKeys, columnOrder, onHiddenColumnKeysChange, onColumnOrderChange, onReset, triggerLabel, }: ColumnManagerProps): import("react").JSX.Element;
|
|
@@ -23,5 +23,21 @@ export interface PaginationProps {
|
|
|
23
23
|
onPageChange: (page: number) => void;
|
|
24
24
|
/** Accessible label for the navigation landmark (default: "Pagination") */
|
|
25
25
|
ariaLabel?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Total number of records across all pages.
|
|
28
|
+
* When provided alongside `pageSize`, renders a "Showing X–Y of Z {itemLabel}" line.
|
|
29
|
+
*/
|
|
30
|
+
totalItems?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Number of records per page.
|
|
33
|
+
* Required together with `totalItems` to compute the Showing X–Y range.
|
|
34
|
+
*/
|
|
35
|
+
pageSize?: number;
|
|
36
|
+
/**
|
|
37
|
+
* Label for the item type shown in the record count footer.
|
|
38
|
+
* Example: "employees", "records", "results"
|
|
39
|
+
* Default: "items"
|
|
40
|
+
*/
|
|
41
|
+
itemLabel?: string;
|
|
26
42
|
}
|
|
27
|
-
export declare function Pagination({ page, totalPages, onPageChange, ariaLabel, }: PaginationProps): import("react").JSX.Element | null;
|
|
43
|
+
export declare function Pagination({ page, totalPages, onPageChange, ariaLabel, totalItems, pageSize, itemLabel, }: PaginationProps): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MaskedValue — Design System Component
|
|
3
|
+
*
|
|
4
|
+
* Renders a value or a redacted placeholder based on a boolean condition.
|
|
5
|
+
* Used when a value exists but should be hidden from the current viewer
|
|
6
|
+
* (e.g. salary data only visible to users with the appropriate permission).
|
|
7
|
+
*
|
|
8
|
+
* The masking decision is made by the caller — MaskedValue is purely
|
|
9
|
+
* presentational. It does not know about permissions, roles, or data fetching.
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* // Show real value
|
|
13
|
+
* <MaskedValue value={formatCurrency(salary)} masked={false} />
|
|
14
|
+
*
|
|
15
|
+
* // Hide value — consumer decides based on permission/context
|
|
16
|
+
* <MaskedValue
|
|
17
|
+
* value={formatCurrency(salary)}
|
|
18
|
+
* masked={!canViewSalary}
|
|
19
|
+
* maskLabel="Salary is restricted"
|
|
20
|
+
* />
|
|
21
|
+
*
|
|
22
|
+
* Accessibility:
|
|
23
|
+
* - When masked, the placeholder pill has aria-label={maskLabel} so
|
|
24
|
+
* screen readers announce the reason rather than "••••• •••••".
|
|
25
|
+
* - When unmasked, renders children directly — no extra ARIA.
|
|
26
|
+
*/
|
|
27
|
+
import type { ReactNode } from 'react';
|
|
28
|
+
export interface MaskedValueProps {
|
|
29
|
+
/**
|
|
30
|
+
* The real value to display when not masked.
|
|
31
|
+
* Accepts any ReactNode — text, formatted currency, a Badge, etc.
|
|
32
|
+
*/
|
|
33
|
+
value: ReactNode;
|
|
34
|
+
/**
|
|
35
|
+
* When true, renders the mask placeholder instead of value.
|
|
36
|
+
* When false, renders value directly.
|
|
37
|
+
*/
|
|
38
|
+
masked: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Text shown in the masked placeholder pill.
|
|
41
|
+
* Default: '••••• •••••'
|
|
42
|
+
*/
|
|
43
|
+
maskText?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Accessible label for the masked placeholder — announces WHY
|
|
46
|
+
* the value is hidden, not just that it is hidden.
|
|
47
|
+
* Example: "Salary restricted — requires elevated access"
|
|
48
|
+
* Default: "Value restricted"
|
|
49
|
+
*/
|
|
50
|
+
maskLabel?: string;
|
|
51
|
+
/** Additional class on the root element (both masked and unmasked states). */
|
|
52
|
+
className?: string;
|
|
53
|
+
}
|
|
54
|
+
export declare function MaskedValue({ value, masked, maskText, maskLabel, className, }: MaskedValueProps): import("react").JSX.Element;
|
|
@@ -17,6 +17,13 @@
|
|
|
17
17
|
* setIssues([{ field: 'Employee ID', message: 'is required' }]);
|
|
18
18
|
* // Render:
|
|
19
19
|
* <ValidationSummary issues={issues} />
|
|
20
|
+
*
|
|
21
|
+
* // With scroll-to-field (wizard steps):
|
|
22
|
+
* <ValidationSummary
|
|
23
|
+
* issues={issues}
|
|
24
|
+
* fieldIds={{ 'Employee ID': 'employee-id-input', 'Position': 'position-select' }}
|
|
25
|
+
* fieldLabels={{ 'Employee ID': 'Employee ID', 'Position': 'Position Title' }}
|
|
26
|
+
* />
|
|
20
27
|
*/
|
|
21
28
|
export interface ValidationIssue {
|
|
22
29
|
/** Human-readable field label. */
|
|
@@ -32,8 +39,23 @@ export interface ValidationSummaryProps {
|
|
|
32
39
|
* Default: "Review these fields before saving"
|
|
33
40
|
*/
|
|
34
41
|
heading?: string;
|
|
42
|
+
/**
|
|
43
|
+
* Maps issue.field → the DOM element id that the user should be
|
|
44
|
+
* scrolled to when they click the error.
|
|
45
|
+
* When provided, each matching issue renders as a clickable link
|
|
46
|
+
* that scrolls to and focuses the target element.
|
|
47
|
+
* Issues with no matching key render as plain text.
|
|
48
|
+
*/
|
|
49
|
+
fieldIds?: Record<string, string>;
|
|
50
|
+
/**
|
|
51
|
+
* Maps issue.field → a human-friendly display label.
|
|
52
|
+
* When provided, the display label replaces the raw field key in
|
|
53
|
+
* the rendered message. Useful when the field key is a code/ID and
|
|
54
|
+
* the label is the visible form field name.
|
|
55
|
+
*/
|
|
56
|
+
fieldLabels?: Record<string, string>;
|
|
35
57
|
/** Additional class on the root element. */
|
|
36
58
|
className?: string;
|
|
37
59
|
}
|
|
38
|
-
export declare function ValidationSummary({ issues, heading, className, }: ValidationSummaryProps): import("react").JSX.Element | null;
|
|
60
|
+
export declare function ValidationSummary({ issues, heading, fieldIds, fieldLabels, className, }: ValidationSummaryProps): import("react").JSX.Element | null;
|
|
39
61
|
export type { ValidationIssue as ValidationSummaryIssue };
|
|
@@ -144,6 +144,10 @@ export { Statistic } from './gallery/stat-card/Statistic';
|
|
|
144
144
|
export type { StatisticProps, StatisticTrend, StatisticVariant } from './gallery/stat-card/Statistic';
|
|
145
145
|
export { StatusDot } from './gallery/status-dot/StatusDot';
|
|
146
146
|
export type { StatusDotProps, StatusDotStatus } from './gallery/status-dot/StatusDot';
|
|
147
|
+
export { ColumnManager, applyColumnOrder } from './gallery/column-manager/ColumnManager';
|
|
148
|
+
export type { ColumnManagerProps, ColumnManagerColumn } from './gallery/column-manager/ColumnManager';
|
|
149
|
+
export { MaskedValue } from './gallery/status-dot/MaskedValue';
|
|
150
|
+
export type { MaskedValueProps } from './gallery/status-dot/MaskedValue';
|
|
147
151
|
export { Toaster, toast } from 'sonner';
|
|
148
152
|
export { statusMenuItems } from './gallery/status-actions/statusActions';
|
|
149
153
|
export type { ActionMenuItem, Lifecycle, StatusTransitionRequest, StatusMenuOptions } from './gallery/status-actions/statusActions';
|