@juv/codego-react-ui 3.4.8 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +276 -30
- package/dist/index.d.cts +33 -2
- package/dist/index.d.ts +33 -2
- package/dist/index.global.js +276 -30
- package/dist/index.js +276 -30
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -6480,7 +6480,7 @@ var import_react_dom2 = require("react-dom");
|
|
|
6480
6480
|
var import_axios3 = __toESM(require("axios"), 1);
|
|
6481
6481
|
var import_lucide_react17 = require("lucide-react");
|
|
6482
6482
|
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
6483
|
-
function useServerTable({ url, params, encrypt, key, decryptPayloadLog, columnOverrides, debounce = 300, transform, manual = false, refresh: refreshEnabled = false, refreshInterval = 0, hardReload, onSuccess, onError }) {
|
|
6483
|
+
function useServerTable({ url, params, encrypt, key, decryptPayloadLog, columnOverrides, debounce = 300, transform, manual = false, refresh: refreshEnabled = false, refreshInterval = 0, hardReload, onSuccess, onError, filter: filterFields, sort: sortKeys }) {
|
|
6484
6484
|
const [data, setData] = React28.useState([]);
|
|
6485
6485
|
const [columns, setColumns] = React28.useState([]);
|
|
6486
6486
|
const [currentPage, setCurrentPage] = React28.useState(1);
|
|
@@ -6490,6 +6490,15 @@ function useServerTable({ url, params, encrypt, key, decryptPayloadLog, columnOv
|
|
|
6490
6490
|
const [tick, setTick] = React28.useState(0);
|
|
6491
6491
|
const [searchValue, setSearchValue] = React28.useState("");
|
|
6492
6492
|
const debounceTimer = React28.useRef(void 0);
|
|
6493
|
+
const [filterValues, setFilterValues] = React28.useState(() => {
|
|
6494
|
+
const init = {};
|
|
6495
|
+
filterFields?.forEach((f) => {
|
|
6496
|
+
init[f.key] = f.type === "checkbox" || f.type === "toggle" ? false : "";
|
|
6497
|
+
});
|
|
6498
|
+
return init;
|
|
6499
|
+
});
|
|
6500
|
+
const [sortKey, setSortKey] = React28.useState("");
|
|
6501
|
+
const [sortDir, setSortDir] = React28.useState("asc");
|
|
6493
6502
|
React28.useEffect(() => {
|
|
6494
6503
|
if (hardReload) hardReload.current = () => setTick((t) => t + 1);
|
|
6495
6504
|
}, [hardReload]);
|
|
@@ -6498,13 +6507,32 @@ function useServerTable({ url, params, encrypt, key, decryptPayloadLog, columnOv
|
|
|
6498
6507
|
const id = setInterval(() => setTick((t) => t + 1), refreshInterval);
|
|
6499
6508
|
return () => clearInterval(id);
|
|
6500
6509
|
}, [refreshInterval]);
|
|
6510
|
+
const activeFilterParams = React28.useMemo(() => {
|
|
6511
|
+
const out = {};
|
|
6512
|
+
filterFields?.forEach((f) => {
|
|
6513
|
+
const v = filterValues[f.key];
|
|
6514
|
+
if (f.type === "checkbox" || f.type === "toggle") {
|
|
6515
|
+
if (v) out[f.key] = 1;
|
|
6516
|
+
} else if (f.type === "date-range") {
|
|
6517
|
+
if (v?.from) out[`${f.key}_from`] = v.from;
|
|
6518
|
+
if (v?.to) out[`${f.key}_to`] = v.to;
|
|
6519
|
+
} else if (v !== "" && v !== null && v !== void 0) {
|
|
6520
|
+
out[f.key] = v;
|
|
6521
|
+
}
|
|
6522
|
+
});
|
|
6523
|
+
if (sortKey) {
|
|
6524
|
+
out.sort = sortKey;
|
|
6525
|
+
out.direction = sortDir;
|
|
6526
|
+
}
|
|
6527
|
+
return out;
|
|
6528
|
+
}, [filterValues, sortKey, sortDir, filterFields]);
|
|
6501
6529
|
React28.useEffect(() => {
|
|
6502
6530
|
if (manual && tick === 0) return;
|
|
6503
6531
|
let cancelled = false;
|
|
6504
6532
|
setLoading(true);
|
|
6505
6533
|
setError(null);
|
|
6506
6534
|
import_axios3.default.get(url, {
|
|
6507
|
-
params: { ...params, page: currentPage, search: searchValue }
|
|
6535
|
+
params: { ...params, ...activeFilterParams, page: currentPage, search: searchValue }
|
|
6508
6536
|
}).then(({ data: res }) => {
|
|
6509
6537
|
if (cancelled) return;
|
|
6510
6538
|
const payload = encrypt ? decryptLaravelPayload(res, key) : res;
|
|
@@ -6550,15 +6578,184 @@ function useServerTable({ url, params, encrypt, key, decryptPayloadLog, columnOv
|
|
|
6550
6578
|
return () => {
|
|
6551
6579
|
cancelled = true;
|
|
6552
6580
|
};
|
|
6553
|
-
}, [url, currentPage, tick, JSON.stringify(params), encrypt, decryptPayloadLog, JSON.stringify(columnOverrides), searchValue]);
|
|
6581
|
+
}, [url, currentPage, tick, JSON.stringify(params), JSON.stringify(activeFilterParams), encrypt, decryptPayloadLog, JSON.stringify(columnOverrides), searchValue]);
|
|
6554
6582
|
const handleSearchChange = (value) => {
|
|
6555
6583
|
setSearchValue(value);
|
|
6556
6584
|
setCurrentPage(1);
|
|
6557
6585
|
if (debounceTimer.current) clearTimeout(debounceTimer.current);
|
|
6558
|
-
debounceTimer.current = setTimeout(() =>
|
|
6559
|
-
|
|
6560
|
-
|
|
6586
|
+
debounceTimer.current = setTimeout(() => setTick((t) => t + 1), debounce);
|
|
6587
|
+
};
|
|
6588
|
+
const handleFilterChange = (key2, value) => {
|
|
6589
|
+
setFilterValues((prev) => ({ ...prev, [key2]: value }));
|
|
6590
|
+
setCurrentPage(1);
|
|
6591
|
+
setTick((t) => t + 1);
|
|
6592
|
+
};
|
|
6593
|
+
const handleClearFilters = () => {
|
|
6594
|
+
const reset = {};
|
|
6595
|
+
filterFields?.forEach((f) => {
|
|
6596
|
+
reset[f.key] = f.type === "checkbox" || f.type === "toggle" ? false : "";
|
|
6597
|
+
});
|
|
6598
|
+
setFilterValues(reset);
|
|
6599
|
+
setSortKey("");
|
|
6600
|
+
setSortDir("asc");
|
|
6601
|
+
setCurrentPage(1);
|
|
6602
|
+
setTick((t) => t + 1);
|
|
6561
6603
|
};
|
|
6604
|
+
const hasActiveFilters = filterFields?.some((f) => {
|
|
6605
|
+
const v = filterValues[f.key];
|
|
6606
|
+
return f.type === "checkbox" || f.type === "toggle" ? !!v : v !== "" && v !== null && v !== void 0;
|
|
6607
|
+
}) || !!sortKey;
|
|
6608
|
+
const filterBar = filterFields?.length || sortKeys?.length ? /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-wrap items-end gap-3 rounded-xl border border-border bg-muted/30 px-4 py-3", children: [
|
|
6609
|
+
filterFields?.map((f) => {
|
|
6610
|
+
const label = f.label ?? f.key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
6611
|
+
const value = filterValues[f.key];
|
|
6612
|
+
const opts = (f.options ?? []).map(
|
|
6613
|
+
(o) => typeof o === "string" ? { label: o, value: o } : o
|
|
6614
|
+
);
|
|
6615
|
+
if (f.type === "checkbox") return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer select-none", children: [
|
|
6616
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
6617
|
+
"input",
|
|
6618
|
+
{
|
|
6619
|
+
type: "checkbox",
|
|
6620
|
+
checked: !!value,
|
|
6621
|
+
onChange: (e) => handleFilterChange(f.key, e.target.checked),
|
|
6622
|
+
className: "h-4 w-4 rounded accent-primary"
|
|
6623
|
+
}
|
|
6624
|
+
),
|
|
6625
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-xs font-medium text-foreground", children: label })
|
|
6626
|
+
] }, f.key);
|
|
6627
|
+
if (f.type === "toggle") return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer select-none", children: [
|
|
6628
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
6629
|
+
"button",
|
|
6630
|
+
{
|
|
6631
|
+
type: "button",
|
|
6632
|
+
role: "switch",
|
|
6633
|
+
"aria-checked": !!value,
|
|
6634
|
+
onClick: () => handleFilterChange(f.key, !value),
|
|
6635
|
+
className: cn(
|
|
6636
|
+
"relative inline-flex h-5 w-9 shrink-0 rounded-full border-2 border-transparent transition-colors",
|
|
6637
|
+
value ? "bg-primary" : "bg-muted"
|
|
6638
|
+
),
|
|
6639
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: cn(
|
|
6640
|
+
"pointer-events-none inline-block h-4 w-4 rounded-full bg-white shadow-sm transition-transform",
|
|
6641
|
+
value ? "translate-x-4" : "translate-x-0"
|
|
6642
|
+
) })
|
|
6643
|
+
}
|
|
6644
|
+
),
|
|
6645
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-xs font-medium text-foreground", children: label })
|
|
6646
|
+
] }, f.key);
|
|
6647
|
+
if (f.type === "select") return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
6648
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: label }),
|
|
6649
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
|
|
6650
|
+
"select",
|
|
6651
|
+
{
|
|
6652
|
+
value: value ?? "",
|
|
6653
|
+
onChange: (e) => handleFilterChange(f.key, e.target.value),
|
|
6654
|
+
className: "h-8 min-w-[120px] rounded-lg border border-border bg-background px-2 text-xs text-foreground focus:outline-none focus:ring-2 focus:ring-ring transition-colors",
|
|
6655
|
+
children: [
|
|
6656
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("option", { value: "", children: f.placeholder ?? `All ${label}` }),
|
|
6657
|
+
opts.map((o) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("option", { value: o.value, children: o.label }, o.value))
|
|
6658
|
+
]
|
|
6659
|
+
}
|
|
6660
|
+
)
|
|
6661
|
+
] }, f.key);
|
|
6662
|
+
if (f.type === "date" || f.type === "date-time") return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
6663
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: label }),
|
|
6664
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
6665
|
+
"input",
|
|
6666
|
+
{
|
|
6667
|
+
type: f.type === "date-time" ? "datetime-local" : "date",
|
|
6668
|
+
value: value ?? "",
|
|
6669
|
+
onChange: (e) => handleFilterChange(f.key, e.target.value),
|
|
6670
|
+
className: "h-8 rounded-lg border border-border bg-background px-2 text-xs text-foreground focus:outline-none focus:ring-2 focus:ring-ring transition-colors"
|
|
6671
|
+
}
|
|
6672
|
+
)
|
|
6673
|
+
] }, f.key);
|
|
6674
|
+
if (f.type === "date-range") return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
6675
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: label }),
|
|
6676
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex items-center gap-1.5", children: [
|
|
6677
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
6678
|
+
"input",
|
|
6679
|
+
{
|
|
6680
|
+
type: "date",
|
|
6681
|
+
value: value?.from ?? "",
|
|
6682
|
+
onChange: (e) => handleFilterChange(f.key, { ...value, from: e.target.value }),
|
|
6683
|
+
className: "h-8 rounded-lg border border-border bg-background px-2 text-xs text-foreground focus:outline-none focus:ring-2 focus:ring-ring transition-colors"
|
|
6684
|
+
}
|
|
6685
|
+
),
|
|
6686
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-xs text-muted-foreground", children: "\u2013" }),
|
|
6687
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
6688
|
+
"input",
|
|
6689
|
+
{
|
|
6690
|
+
type: "date",
|
|
6691
|
+
value: value?.to ?? "",
|
|
6692
|
+
onChange: (e) => handleFilterChange(f.key, { ...value, to: e.target.value }),
|
|
6693
|
+
className: "h-8 rounded-lg border border-border bg-background px-2 text-xs text-foreground focus:outline-none focus:ring-2 focus:ring-ring transition-colors"
|
|
6694
|
+
}
|
|
6695
|
+
)
|
|
6696
|
+
] })
|
|
6697
|
+
] }, f.key);
|
|
6698
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
6699
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: label }),
|
|
6700
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
6701
|
+
"input",
|
|
6702
|
+
{
|
|
6703
|
+
type: "text",
|
|
6704
|
+
value: value ?? "",
|
|
6705
|
+
placeholder: f.placeholder ?? `Filter ${label}\u2026`,
|
|
6706
|
+
onChange: (e) => handleFilterChange(f.key, e.target.value),
|
|
6707
|
+
className: "h-8 min-w-[140px] rounded-lg border border-border bg-background px-3 text-xs text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring transition-colors"
|
|
6708
|
+
}
|
|
6709
|
+
)
|
|
6710
|
+
] }, f.key);
|
|
6711
|
+
}),
|
|
6712
|
+
sortKeys?.length ? /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
6713
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: "Sort by" }),
|
|
6714
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex items-center gap-1.5", children: [
|
|
6715
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
|
|
6716
|
+
"select",
|
|
6717
|
+
{
|
|
6718
|
+
value: sortKey,
|
|
6719
|
+
onChange: (e) => {
|
|
6720
|
+
setSortKey(e.target.value);
|
|
6721
|
+
setCurrentPage(1);
|
|
6722
|
+
setTick((t) => t + 1);
|
|
6723
|
+
},
|
|
6724
|
+
className: "h-8 min-w-[120px] rounded-lg border border-border bg-background px-2 text-xs text-foreground focus:outline-none focus:ring-2 focus:ring-ring transition-colors",
|
|
6725
|
+
children: [
|
|
6726
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("option", { value: "", children: "Default" }),
|
|
6727
|
+
sortKeys.map((k) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("option", { value: k, children: k.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()) }, k))
|
|
6728
|
+
]
|
|
6729
|
+
}
|
|
6730
|
+
),
|
|
6731
|
+
sortKey && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
6732
|
+
"button",
|
|
6733
|
+
{
|
|
6734
|
+
type: "button",
|
|
6735
|
+
onClick: () => {
|
|
6736
|
+
setSortDir((d) => d === "asc" ? "desc" : "asc");
|
|
6737
|
+
setTick((t) => t + 1);
|
|
6738
|
+
},
|
|
6739
|
+
className: "flex h-8 w-8 items-center justify-center rounded-lg border border-border bg-background text-muted-foreground hover:text-foreground hover:bg-muted transition-colors",
|
|
6740
|
+
title: sortDir === "asc" ? "Ascending" : "Descending",
|
|
6741
|
+
children: sortDir === "asc" ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react17.ChevronUp, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react17.ChevronDown, { className: "h-3.5 w-3.5" })
|
|
6742
|
+
}
|
|
6743
|
+
)
|
|
6744
|
+
] })
|
|
6745
|
+
] }) : null,
|
|
6746
|
+
hasActiveFilters && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
|
|
6747
|
+
"button",
|
|
6748
|
+
{
|
|
6749
|
+
type: "button",
|
|
6750
|
+
onClick: handleClearFilters,
|
|
6751
|
+
className: "flex h-8 items-center gap-1.5 self-end rounded-lg border border-border bg-background px-3 text-xs font-medium text-muted-foreground hover:bg-muted hover:text-foreground transition-colors",
|
|
6752
|
+
children: [
|
|
6753
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react17.X, { className: "h-3 w-3" }),
|
|
6754
|
+
" Clear"
|
|
6755
|
+
]
|
|
6756
|
+
}
|
|
6757
|
+
)
|
|
6758
|
+
] }) : null;
|
|
6562
6759
|
return {
|
|
6563
6760
|
data,
|
|
6564
6761
|
columns,
|
|
@@ -6567,6 +6764,7 @@ function useServerTable({ url, params, encrypt, key, decryptPayloadLog, columnOv
|
|
|
6567
6764
|
serverPagination: pagination ? { pagination, currentPage, goToPage: (page) => setCurrentPage(page) } : null,
|
|
6568
6765
|
loading,
|
|
6569
6766
|
error,
|
|
6767
|
+
filterBar,
|
|
6570
6768
|
goToPage: (page) => setCurrentPage(page),
|
|
6571
6769
|
reload: () => setTick((t) => t + 1),
|
|
6572
6770
|
refresh: () => setTick((t) => t + 1),
|
|
@@ -7276,6 +7474,9 @@ function Table({
|
|
|
7276
7474
|
renderExpanded,
|
|
7277
7475
|
columnVisibility,
|
|
7278
7476
|
onColumnVisibilityChange,
|
|
7477
|
+
columnVisibilityIcon,
|
|
7478
|
+
filterBar,
|
|
7479
|
+
filterableIcon,
|
|
7279
7480
|
exportable = false,
|
|
7280
7481
|
onExport,
|
|
7281
7482
|
virtualized = false,
|
|
@@ -7296,6 +7497,8 @@ function Table({
|
|
|
7296
7497
|
const [sortKey, setSortKey] = React28.useState(null);
|
|
7297
7498
|
const [sortDir, setSortDir] = React28.useState(null);
|
|
7298
7499
|
const [bulkLoading, setBulkLoading] = React28.useState(false);
|
|
7500
|
+
const [bulkConfirm, setBulkConfirm] = React28.useState(null);
|
|
7501
|
+
const [filterBarOpen, setFilterBarOpen] = React28.useState(false);
|
|
7299
7502
|
const [expandedIds, setExpandedIds] = React28.useState(/* @__PURE__ */ new Set());
|
|
7300
7503
|
const [dragOverId, setDragOverId] = React28.useState(null);
|
|
7301
7504
|
const [focusedRowIdx, setFocusedRowIdx] = React28.useState(-1);
|
|
@@ -7432,7 +7635,7 @@ function Table({
|
|
|
7432
7635
|
const unselectedCount = totalRows - selectedIds.length;
|
|
7433
7636
|
const handleSelectAllRecords = () => setSelectedIds(filteredData.map((item) => String(item[idKey])));
|
|
7434
7637
|
const handleUnselectAll = () => setSelectedIds([]);
|
|
7435
|
-
const
|
|
7638
|
+
const execBulkDeleteSelected = async () => {
|
|
7436
7639
|
if (!bulkDeleteBaseUrl || selectedIds.length === 0) {
|
|
7437
7640
|
onBulkDelete?.(selectedIds);
|
|
7438
7641
|
setSelectedIds([]);
|
|
@@ -7454,7 +7657,7 @@ function Table({
|
|
|
7454
7657
|
setBulkLoading(false);
|
|
7455
7658
|
}
|
|
7456
7659
|
};
|
|
7457
|
-
const
|
|
7660
|
+
const execDeleteAll = async () => {
|
|
7458
7661
|
if (!bulkDeleteBaseUrl) return;
|
|
7459
7662
|
setBulkLoading(true);
|
|
7460
7663
|
try {
|
|
@@ -7521,10 +7724,22 @@ function Table({
|
|
|
7521
7724
|
]
|
|
7522
7725
|
}
|
|
7523
7726
|
),
|
|
7727
|
+
unselectedCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
|
|
7728
|
+
"button",
|
|
7729
|
+
{
|
|
7730
|
+
onClick: handleSelectAllRecords,
|
|
7731
|
+
disabled: bulkLoading,
|
|
7732
|
+
className: "inline-flex items-center gap-1.5 rounded-lg border border-border bg-muted/50 px-3 py-1.5 text-xs font-medium text-muted-foreground hover:bg-muted transition-colors disabled:opacity-40",
|
|
7733
|
+
children: [
|
|
7734
|
+
"Select all ",
|
|
7735
|
+
unselectedCount
|
|
7736
|
+
]
|
|
7737
|
+
}
|
|
7738
|
+
),
|
|
7524
7739
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
|
|
7525
7740
|
"button",
|
|
7526
7741
|
{
|
|
7527
|
-
onClick:
|
|
7742
|
+
onClick: () => setBulkConfirm("selected"),
|
|
7528
7743
|
disabled: bulkLoading,
|
|
7529
7744
|
className: "inline-flex items-center gap-1.5 rounded-lg bg-danger/10 border border-danger/20 px-3 py-1.5 text-xs font-medium text-danger hover:bg-danger/20 transition-colors disabled:opacity-40",
|
|
7530
7745
|
children: [
|
|
@@ -7534,30 +7749,29 @@ function Table({
|
|
|
7534
7749
|
" selected"
|
|
7535
7750
|
]
|
|
7536
7751
|
}
|
|
7752
|
+
),
|
|
7753
|
+
bulkDeleteBaseUrl && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
|
|
7754
|
+
"button",
|
|
7755
|
+
{
|
|
7756
|
+
onClick: () => setBulkConfirm("all"),
|
|
7757
|
+
disabled: bulkLoading,
|
|
7758
|
+
className: "inline-flex items-center gap-1.5 rounded-lg bg-danger/10 border border-danger/20 px-3 py-1.5 text-xs font-medium text-danger hover:bg-danger/20 transition-colors disabled:opacity-40",
|
|
7759
|
+
children: [
|
|
7760
|
+
bulkLoading ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react17.Loader2, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react17.Trash2, { className: "h-3.5 w-3.5" }),
|
|
7761
|
+
"Delete all"
|
|
7762
|
+
]
|
|
7763
|
+
}
|
|
7537
7764
|
)
|
|
7538
7765
|
] }),
|
|
7539
|
-
|
|
7540
|
-
"button",
|
|
7541
|
-
{
|
|
7542
|
-
onClick: handleSelectAllRecords,
|
|
7543
|
-
disabled: bulkLoading,
|
|
7544
|
-
className: "inline-flex items-center gap-1.5 rounded-lg border border-border bg-muted/50 px-3 py-1.5 text-xs font-medium text-muted-foreground hover:bg-muted transition-colors disabled:opacity-40",
|
|
7545
|
-
children: [
|
|
7546
|
-
"Select all ",
|
|
7547
|
-
unselectedCount
|
|
7548
|
-
]
|
|
7549
|
-
}
|
|
7550
|
-
),
|
|
7551
|
-
selectable && bulkDeleteBaseUrl && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
|
|
7766
|
+
filterBar && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
7552
7767
|
"button",
|
|
7553
7768
|
{
|
|
7554
|
-
onClick:
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
|
|
7558
|
-
|
|
7559
|
-
|
|
7560
|
-
]
|
|
7769
|
+
onClick: () => setFilterBarOpen((o) => !o),
|
|
7770
|
+
className: cn(
|
|
7771
|
+
"inline-flex items-center gap-1.5 rounded-lg border px-3 py-1.5 text-xs font-medium transition-colors",
|
|
7772
|
+
filterBarOpen ? "border-primary bg-primary/10 text-primary hover:bg-primary/20" : "border-border bg-muted/50 text-muted-foreground hover:bg-muted"
|
|
7773
|
+
),
|
|
7774
|
+
children: filterableIcon ?? "Filter"
|
|
7561
7775
|
}
|
|
7562
7776
|
),
|
|
7563
7777
|
exportable && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "relative group", children: [
|
|
@@ -7573,7 +7787,7 @@ function Table({
|
|
|
7573
7787
|
)) })
|
|
7574
7788
|
] }),
|
|
7575
7789
|
columnVisibility && onColumnVisibilityChange && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "relative group", children: [
|
|
7576
|
-
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("button", { className: "inline-flex items-center gap-1.5 rounded-lg border border-border bg-muted/50 px-3 py-1.5 text-xs font-medium text-muted-foreground hover:bg-muted transition-colors", children: "Columns" }),
|
|
7790
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("button", { className: "inline-flex items-center gap-1.5 rounded-lg border border-border bg-muted/50 px-3 py-1.5 text-xs font-medium text-muted-foreground hover:bg-muted transition-colors", children: columnVisibilityIcon ?? "Columns" }),
|
|
7577
7791
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "absolute right-0 top-full mt-1 z-20 hidden group-hover:flex flex-col min-w-[150px] rounded-xl border border-border bg-card shadow-lg overflow-hidden p-2 gap-1", children: columns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("label", { className: "flex items-center gap-2 px-2 py-1 rounded-lg hover:bg-muted cursor-pointer text-xs", children: [
|
|
7578
7792
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
7579
7793
|
"input",
|
|
@@ -7595,6 +7809,7 @@ function Table({
|
|
|
7595
7809
|
] })
|
|
7596
7810
|
] })
|
|
7597
7811
|
] }),
|
|
7812
|
+
filterBar && filterBarOpen && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { children: filterBar }),
|
|
7598
7813
|
loading && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex items-center justify-center py-12 text-muted-foreground gap-2", children: [
|
|
7599
7814
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react17.Loader2, { className: "h-5 w-5 animate-spin" }),
|
|
7600
7815
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "text-sm", children: "Loading\u2026" })
|
|
@@ -7988,6 +8203,37 @@ function Table({
|
|
|
7988
8203
|
}
|
|
7989
8204
|
}
|
|
7990
8205
|
}
|
|
8206
|
+
),
|
|
8207
|
+
bulkConfirm && (0, import_react_dom2.createPortal)(
|
|
8208
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
8209
|
+
"div",
|
|
8210
|
+
{
|
|
8211
|
+
className: "fixed inset-0 z-50 flex items-center justify-center p-4",
|
|
8212
|
+
style: { background: "rgba(0,0,0,0.5)" },
|
|
8213
|
+
onMouseDown: (e) => {
|
|
8214
|
+
if (e.target === e.currentTarget) setBulkConfirm(null);
|
|
8215
|
+
},
|
|
8216
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "relative w-full max-w-md rounded-2xl border border-border bg-card shadow-2xl flex flex-col", children: [
|
|
8217
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex items-center justify-between px-6 py-4 border-b border-border", children: [
|
|
8218
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("h2", { className: "text-base font-semibold", children: "Confirm Delete" }),
|
|
8219
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("button", { onClick: () => setBulkConfirm(null), className: "text-muted-foreground hover:text-foreground transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react17.X, { className: "h-4 w-4" }) })
|
|
8220
|
+
] }),
|
|
8221
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "px-6 py-4", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { className: "text-sm text-muted-foreground", children: bulkConfirm === "selected" ? `Are you sure you want to delete ${selectedIds.length} selected record${selectedIds.length !== 1 ? "s" : ""}? This action cannot be undone.` : "Are you sure you want to delete all records? This action cannot be undone." }) }),
|
|
8222
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "px-6 py-4 border-t border-border flex justify-end gap-2", children: [
|
|
8223
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Button, { variant: "outline", size: "sm", onClick: () => setBulkConfirm(null), disabled: bulkLoading, children: "Cancel" }),
|
|
8224
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(Button, { variant: "danger", size: "sm", disabled: bulkLoading, onClick: async () => {
|
|
8225
|
+
if (bulkConfirm === "selected") await execBulkDeleteSelected();
|
|
8226
|
+
else await execDeleteAll();
|
|
8227
|
+
setBulkConfirm(null);
|
|
8228
|
+
}, children: [
|
|
8229
|
+
bulkLoading && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react17.Loader2, { className: "h-3.5 w-3.5 mr-1.5 animate-spin" }),
|
|
8230
|
+
bulkLoading ? "Deleting\u2026" : "Delete"
|
|
8231
|
+
] })
|
|
8232
|
+
] })
|
|
8233
|
+
] })
|
|
8234
|
+
}
|
|
8235
|
+
),
|
|
8236
|
+
document.body
|
|
7991
8237
|
)
|
|
7992
8238
|
] });
|
|
7993
8239
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -293,6 +293,17 @@ interface UseServerTableOptions {
|
|
|
293
293
|
onSuccess?: (data: any[]) => void;
|
|
294
294
|
/** Called on fetch error */
|
|
295
295
|
onError?: (error: Error) => void;
|
|
296
|
+
/**
|
|
297
|
+
* Filter fields rendered above the table.
|
|
298
|
+
* Each field appends its value as a query param on every request.
|
|
299
|
+
* Supported types: "input" | "select" | "checkbox" | "toggle" | "date" | "date-time" | "date-range"
|
|
300
|
+
*/
|
|
301
|
+
filter?: ServerTableFilterField[];
|
|
302
|
+
/**
|
|
303
|
+
* Sortable column keys. Renders a sort dropdown above the table.
|
|
304
|
+
* Appends sort=key&direction=asc|desc to every request.
|
|
305
|
+
*/
|
|
306
|
+
sort?: string[];
|
|
296
307
|
}
|
|
297
308
|
/**
|
|
298
309
|
* Return type from the useServerTable hook
|
|
@@ -310,6 +321,8 @@ interface UseServerTableReturn<T> {
|
|
|
310
321
|
goToPage: (page: number) => void;
|
|
311
322
|
reload: () => void;
|
|
312
323
|
refresh: () => void;
|
|
324
|
+
/** Rendered filter + sort bar — place above <Table /> */
|
|
325
|
+
filterBar: React.ReactNode;
|
|
313
326
|
searchValue: string;
|
|
314
327
|
onSearchChange: (value: string) => void;
|
|
315
328
|
page: number;
|
|
@@ -354,6 +367,18 @@ interface ServerPaginationProp {
|
|
|
354
367
|
currentPage: number;
|
|
355
368
|
goToPage: (page: number) => void;
|
|
356
369
|
}
|
|
370
|
+
type ServerTableFilterType = "input" | "select" | "checkbox" | "toggle" | "date" | "date-time" | "date-range";
|
|
371
|
+
interface ServerTableFilterField {
|
|
372
|
+
key: string;
|
|
373
|
+
type: ServerTableFilterType;
|
|
374
|
+
label?: string;
|
|
375
|
+
placeholder?: string;
|
|
376
|
+
/** Options for type="select" */
|
|
377
|
+
options?: string[] | {
|
|
378
|
+
label: string;
|
|
379
|
+
value: string;
|
|
380
|
+
}[];
|
|
381
|
+
}
|
|
357
382
|
/**
|
|
358
383
|
* Custom hook for fetching and managing server-side paginated table data.
|
|
359
384
|
* Supports Laravel encryption, auto-column derivation, and flexible pagination.
|
|
@@ -370,7 +395,7 @@ interface ServerPaginationProp {
|
|
|
370
395
|
* })
|
|
371
396
|
* ```
|
|
372
397
|
*/
|
|
373
|
-
declare function useServerTable<T extends Record<string, any>>({ url, params, encrypt, key, decryptPayloadLog, columnOverrides, debounce, transform, manual, refresh: refreshEnabled, refreshInterval, hardReload, onSuccess, onError }: UseServerTableOptions): UseServerTableReturn<T>;
|
|
398
|
+
declare function useServerTable<T extends Record<string, any>>({ url, params, encrypt, key, decryptPayloadLog, columnOverrides, debounce, transform, manual, refresh: refreshEnabled, refreshInterval, hardReload, onSuccess, onError, filter: filterFields, sort: sortKeys }: UseServerTableOptions): UseServerTableReturn<T>;
|
|
374
399
|
/**
|
|
375
400
|
* Available field types for action forms (edit/view modals)
|
|
376
401
|
* @type {ActionFieldType}
|
|
@@ -713,6 +738,12 @@ interface TableProps<T> {
|
|
|
713
738
|
* - `"soft"` — neumorphic soft shadows, no hard borders
|
|
714
739
|
*/
|
|
715
740
|
variant?: "default" | "zebra" | "card" | "glass" | "soft";
|
|
741
|
+
/** Custom icon for the column visibility toggle button. When provided, hides the "Columns" text label. */
|
|
742
|
+
columnVisibilityIcon?: React.ReactElement;
|
|
743
|
+
/** Filter bar node (e.g. from useServerTable's filterBar). Rendered below the toolbar when visible. */
|
|
744
|
+
filterBar?: React.ReactNode;
|
|
745
|
+
/** Custom icon for the filter toggle button. When provided, hides the "Filter" text label. */
|
|
746
|
+
filterableIcon?: React.ReactElement;
|
|
716
747
|
className?: string;
|
|
717
748
|
}
|
|
718
749
|
/**
|
|
@@ -742,7 +773,7 @@ interface TableProps<T> {
|
|
|
742
773
|
* />
|
|
743
774
|
* ```
|
|
744
775
|
*/
|
|
745
|
-
declare function Table<T extends Record<string, any>>({ data, columns, loading, emptyState, error: errorProp, searchable, searchPlaceholder, searchValue: controlledSearch, onSearchChange, clientPagination, itemsPerPage, selectable, onBulkDelete, idKey, bulkDeleteBaseUrl, defaultActions, serverPagination, variant, className, onRowClick, onRowDoubleClick, rowClassName, expandable, renderExpanded, columnVisibility, onColumnVisibilityChange, exportable, onExport, virtualized, draggable, onRowReorder, keyboardNavigation, }: TableProps<T>): react_jsx_runtime.JSX.Element;
|
|
776
|
+
declare function Table<T extends Record<string, any>>({ data, columns, loading, emptyState, error: errorProp, searchable, searchPlaceholder, searchValue: controlledSearch, onSearchChange, clientPagination, itemsPerPage, selectable, onBulkDelete, idKey, bulkDeleteBaseUrl, defaultActions, serverPagination, variant, className, onRowClick, onRowDoubleClick, rowClassName, expandable, renderExpanded, columnVisibility, onColumnVisibilityChange, columnVisibilityIcon, filterBar, filterableIcon, exportable, onExport, virtualized, draggable, onRowReorder, keyboardNavigation, }: TableProps<T>): react_jsx_runtime.JSX.Element;
|
|
746
777
|
|
|
747
778
|
type BulletinPriority = "low" | "medium" | "high" | "urgent";
|
|
748
779
|
type BulletinLayout = "grid" | "list" | "masonry";
|
package/dist/index.d.ts
CHANGED
|
@@ -293,6 +293,17 @@ interface UseServerTableOptions {
|
|
|
293
293
|
onSuccess?: (data: any[]) => void;
|
|
294
294
|
/** Called on fetch error */
|
|
295
295
|
onError?: (error: Error) => void;
|
|
296
|
+
/**
|
|
297
|
+
* Filter fields rendered above the table.
|
|
298
|
+
* Each field appends its value as a query param on every request.
|
|
299
|
+
* Supported types: "input" | "select" | "checkbox" | "toggle" | "date" | "date-time" | "date-range"
|
|
300
|
+
*/
|
|
301
|
+
filter?: ServerTableFilterField[];
|
|
302
|
+
/**
|
|
303
|
+
* Sortable column keys. Renders a sort dropdown above the table.
|
|
304
|
+
* Appends sort=key&direction=asc|desc to every request.
|
|
305
|
+
*/
|
|
306
|
+
sort?: string[];
|
|
296
307
|
}
|
|
297
308
|
/**
|
|
298
309
|
* Return type from the useServerTable hook
|
|
@@ -310,6 +321,8 @@ interface UseServerTableReturn<T> {
|
|
|
310
321
|
goToPage: (page: number) => void;
|
|
311
322
|
reload: () => void;
|
|
312
323
|
refresh: () => void;
|
|
324
|
+
/** Rendered filter + sort bar — place above <Table /> */
|
|
325
|
+
filterBar: React.ReactNode;
|
|
313
326
|
searchValue: string;
|
|
314
327
|
onSearchChange: (value: string) => void;
|
|
315
328
|
page: number;
|
|
@@ -354,6 +367,18 @@ interface ServerPaginationProp {
|
|
|
354
367
|
currentPage: number;
|
|
355
368
|
goToPage: (page: number) => void;
|
|
356
369
|
}
|
|
370
|
+
type ServerTableFilterType = "input" | "select" | "checkbox" | "toggle" | "date" | "date-time" | "date-range";
|
|
371
|
+
interface ServerTableFilterField {
|
|
372
|
+
key: string;
|
|
373
|
+
type: ServerTableFilterType;
|
|
374
|
+
label?: string;
|
|
375
|
+
placeholder?: string;
|
|
376
|
+
/** Options for type="select" */
|
|
377
|
+
options?: string[] | {
|
|
378
|
+
label: string;
|
|
379
|
+
value: string;
|
|
380
|
+
}[];
|
|
381
|
+
}
|
|
357
382
|
/**
|
|
358
383
|
* Custom hook for fetching and managing server-side paginated table data.
|
|
359
384
|
* Supports Laravel encryption, auto-column derivation, and flexible pagination.
|
|
@@ -370,7 +395,7 @@ interface ServerPaginationProp {
|
|
|
370
395
|
* })
|
|
371
396
|
* ```
|
|
372
397
|
*/
|
|
373
|
-
declare function useServerTable<T extends Record<string, any>>({ url, params, encrypt, key, decryptPayloadLog, columnOverrides, debounce, transform, manual, refresh: refreshEnabled, refreshInterval, hardReload, onSuccess, onError }: UseServerTableOptions): UseServerTableReturn<T>;
|
|
398
|
+
declare function useServerTable<T extends Record<string, any>>({ url, params, encrypt, key, decryptPayloadLog, columnOverrides, debounce, transform, manual, refresh: refreshEnabled, refreshInterval, hardReload, onSuccess, onError, filter: filterFields, sort: sortKeys }: UseServerTableOptions): UseServerTableReturn<T>;
|
|
374
399
|
/**
|
|
375
400
|
* Available field types for action forms (edit/view modals)
|
|
376
401
|
* @type {ActionFieldType}
|
|
@@ -713,6 +738,12 @@ interface TableProps<T> {
|
|
|
713
738
|
* - `"soft"` — neumorphic soft shadows, no hard borders
|
|
714
739
|
*/
|
|
715
740
|
variant?: "default" | "zebra" | "card" | "glass" | "soft";
|
|
741
|
+
/** Custom icon for the column visibility toggle button. When provided, hides the "Columns" text label. */
|
|
742
|
+
columnVisibilityIcon?: React.ReactElement;
|
|
743
|
+
/** Filter bar node (e.g. from useServerTable's filterBar). Rendered below the toolbar when visible. */
|
|
744
|
+
filterBar?: React.ReactNode;
|
|
745
|
+
/** Custom icon for the filter toggle button. When provided, hides the "Filter" text label. */
|
|
746
|
+
filterableIcon?: React.ReactElement;
|
|
716
747
|
className?: string;
|
|
717
748
|
}
|
|
718
749
|
/**
|
|
@@ -742,7 +773,7 @@ interface TableProps<T> {
|
|
|
742
773
|
* />
|
|
743
774
|
* ```
|
|
744
775
|
*/
|
|
745
|
-
declare function Table<T extends Record<string, any>>({ data, columns, loading, emptyState, error: errorProp, searchable, searchPlaceholder, searchValue: controlledSearch, onSearchChange, clientPagination, itemsPerPage, selectable, onBulkDelete, idKey, bulkDeleteBaseUrl, defaultActions, serverPagination, variant, className, onRowClick, onRowDoubleClick, rowClassName, expandable, renderExpanded, columnVisibility, onColumnVisibilityChange, exportable, onExport, virtualized, draggable, onRowReorder, keyboardNavigation, }: TableProps<T>): react_jsx_runtime.JSX.Element;
|
|
776
|
+
declare function Table<T extends Record<string, any>>({ data, columns, loading, emptyState, error: errorProp, searchable, searchPlaceholder, searchValue: controlledSearch, onSearchChange, clientPagination, itemsPerPage, selectable, onBulkDelete, idKey, bulkDeleteBaseUrl, defaultActions, serverPagination, variant, className, onRowClick, onRowDoubleClick, rowClassName, expandable, renderExpanded, columnVisibility, onColumnVisibilityChange, columnVisibilityIcon, filterBar, filterableIcon, exportable, onExport, virtualized, draggable, onRowReorder, keyboardNavigation, }: TableProps<T>): react_jsx_runtime.JSX.Element;
|
|
746
777
|
|
|
747
778
|
type BulletinPriority = "low" | "medium" | "high" | "urgent";
|
|
748
779
|
type BulletinLayout = "grid" | "list" | "masonry";
|