laif-ds
Version:
Design System di Laif con componenti React basati su principi di Atomic Design
586 lines (585 loc) • 19.7 kB
JavaScript
"use client";
import { jsx as d, jsxs as se } from "react/jsx-runtime";
import { useReactTable as ct } from "../../../../node_modules/@tanstack/react-table/build/lib/index.js";
import { useRef as f, useState as l, useEffect as O, useMemo as C } from "react";
import { cn as dt } from "../../../../lib/utils.js";
import { Button as ut } from "../../button.js";
import { Checkbox as Ie } from "../../checkbox.js";
import { DataTableActionsComponent as gt } from "./components/data-table-actions.js";
import { DataTableBody as mt } from "./components/data-table-body.js";
import { DataTableColumnVisibility as pt } from "./components/data-table-column-visibility.js";
import { DataTableFilters as ft } from "./components/data-table-filters.js";
import { DataTablePaginationComponent as ht } from "./components/data-table-pagination.js";
import { DataTableSearchbar as St } from "./components/data-table-searchbar.js";
import { DataTableSortingComponent as bt } from "./components/data-table-sorting.js";
import { applySearchFilterToRow as Te, debounce as Ft, buildSearchFilter as vt, updatePageSizeFromContainer as Ct, createDefaultAdvancedFilter as xt, createBadgeFilterFromHeader as yt } from "./data-table.service.js";
import { defaultDataTableI18n as wt } from "./data-table-i18n.js";
import { getPaginationRowModel as Rt, getFilteredRowModel as It, getSortedRowModel as Tt, getCoreRowModel as zt } from "../../../../node_modules/@tanstack/table-core/build/lib/index.js";
function Wt({
columns: Z,
data: V,
loading: z = !1,
emptyComponent: ze,
className: De,
rowSelection: Be = {},
onRowSelectionChange: le,
checkable: j = !1,
onCheckedRowsChange: $,
actions: _e = [],
customComponentsLeft: Me,
customComponentsRight: Oe,
hidePagination: He = !1,
hideActionsRow: Ne = !1,
i18n: h = wt,
maxSortedColumns: ce = 2,
initialState: u,
// New mode-based API
serverMode: Le = !1,
serverConfig: de,
disableAutoPageSize: b = !1,
// Test & Accessibility
id: Ge,
"data-testid": Pe,
rowClassName: ke
}) {
const q = f(null), i = Le === !0, g = i ? de : void 0, ue = (e) => !e || !e.sort_by || !e.sort_order ? [] : e.sort_by.map((t, n) => ({
id: t,
desc: e.sort_order[n] === "desc"
})), Ae = (e) => {
const t = ue(e?.sorting), n = {
sort_by: t.map((a) => a.id),
sort_order: t.map((a) => a.desc ? "desc" : "asc")
};
return {
computedSorting: t.length > 0 ? n : void 0,
computedFilter: e?.computedFilter
// Use provided value or undefined
};
}, [H, Ve] = l(() => ue(u?.sorting)), [je] = l([]), [qe, Ke] = l(
() => u?.columnVisibility ?? {}
), [m, Ee] = l(() => u?.filters?.searchbarFilter), [K, N] = l(() => u?.pagination ?? { pageIndex: 0, pageSize: 10 }), ge = f(g);
ge.current = g;
const E = f(g?.onStateChange);
E.current = g?.onStateChange;
const W = f(null), D = f(null), We = f(0), me = f(!1), pe = f(null), J = f({}), fe = f(null), U = f(z);
U.current = z;
const L = (e) => {
e.computedFilter && (J.current.computedFilter = e.computedFilter), e.computedSorting && (J.current.computedSorting = e.computedSorting);
const t = {
...e,
// Use new computed values if available, otherwise use last known values
computedFilter: e.computedFilter || J.current.computedFilter,
computedSorting: e.computedSorting || J.current.computedSorting
};
W.current = t, D.current && clearTimeout(D.current), D.current = setTimeout(() => {
W.current && E.current && (We.current = Date.now(), E.current(W.current), W.current = null, D.current = null);
}, 400);
};
O(() => {
const e = setTimeout(() => {
me.current = !0;
}, 100);
return () => {
clearTimeout(e), D.current && clearTimeout(D.current);
};
}, []);
const [r, G] = l(() => {
const { computedSorting: e, computedFilter: t } = Ae(u);
return {
pagination: u?.pagination ?? { pageIndex: 0, pageSize: 10 },
sorting: u?.sorting,
filters: u?.filters,
computedFilter: u?.computedFilter ?? t,
computedSorting: u?.computedSorting ?? e
};
});
i && !g && console.warn("DataTable: Server mode requires serverConfig");
const [Je, he] = l(0), [Ue, Se] = l(0), [Xe, Qe] = l({}), be = le ? Be : Xe, Ye = le || Qe, [F, ee] = l(
() => u?.filters?.filterBadges ?? []
), [x, te] = l(() => u?.filters?.advancedFilterBadge), [X, Ze] = l(""), [$e, re] = l(void 0), [Fe, ve] = l([]), Ce = C(() => j ? [
{
id: "data-table-integrated-checkbox-column",
size: 24,
minSize: 24,
maxSize: 24,
header: ({ table: e }) => /* @__PURE__ */ d(
"div",
{
className: "flex w-6 max-w-6 min-w-6 items-center justify-center",
style: { width: "24px", minWidth: "24px", maxWidth: "24px" },
children: /* @__PURE__ */ d(
Ie,
{
className: "cursor-pointer",
checked: e.getIsAllPageRowsSelected(),
onCheckedChange: (t) => e.toggleAllPageRowsSelected(!!t),
"aria-label": h.selectAll
}
)
}
),
cell: ({ row: e }) => /* @__PURE__ */ d(
"div",
{
className: "flex w-6 max-w-6 min-w-6 items-center justify-center",
style: { width: "24px", minWidth: "24px", maxWidth: "24px" },
children: /* @__PURE__ */ d(
Ie,
{
className: "cursor-pointer",
checked: e.getIsSelected(),
onCheckedChange: (t) => e.toggleSelected(!!t),
"aria-label": h.selectRow
}
)
}
),
enableSorting: !1,
enableHiding: !1,
enableResizing: !1,
meta: {
pinned: "left",
sortable: !1,
searchable: !1,
filterable: !1,
type: "other",
listOptions: []
}
},
...Z
] : Z, [Z, j]), I = C(() => Ce.map(
(e) => e
), [Ce]), Q = (e) => {
if (e.id) return e.id;
if ("accessorKey" in e && typeof e.accessorKey == "string")
return e.accessorKey;
}, et = (e) => {
const t = e.meta?.headerLabel;
return t || (typeof e.header == "string" ? e.header : Q(e) ?? "-");
};
O(() => {
const e = (I ?? []).map(Q).filter((c) => !!c), t = new Set(Fe), n = new Set(e);
t.size === n.size && [...t].every((c) => n.has(c)) || ve(e);
}, [I]);
const B = C(() => I.filter((e) => !!e.meta?.searchable), [I]), T = C(() => B.map((e) => "accessorKey" in e && typeof e.accessorKey == "string" ? e.accessorKey : e.id).filter((e) => !!e), [B]), [Y, tt] = l(() => u?.computedFilter ?? {}), rt = {
data: C(() => i || Object.keys(Y).length === 0 ? V : V.filter((e) => Te({
original: e,
getValue: (n) => e[n]
}, Y)), [V, Y, i]),
columns: I,
getCoreRowModel: zt(),
onSortingChange: (e) => {
const t = typeof e == "function" ? e(H) : e;
Ve(t);
},
getSortedRowModel: Tt(),
manualSorting: i,
onColumnVisibilityChange: Ke,
onRowSelectionChange: Ye,
onColumnOrderChange: ve,
getFilteredRowModel: It(),
manualFiltering: i,
globalFilterFn: (e, t, n) => i || !n ? !0 : (T ?? []).some((a) => {
const c = e.getValue(a);
return Array.isArray(c) ? c.some(
(p) => String(p).toLowerCase().includes(n.toLowerCase())
) : String(c).toLowerCase().includes(n.toLowerCase());
}) ?? !1,
filterFns: {
advancedFilter: (e) => i ? !0 : Te(e, Y)
},
onPaginationChange: (e) => {
if (i && g) {
if (typeof e == "object") {
const t = b ? {
...r.pagination,
pageIndex: e.pageIndex
} : {
...r.pagination,
pageIndex: e.pageIndex,
pageSize: e.pageSize
}, n = {
...r,
pagination: t,
// Preserve computed values if they exist
computedFilter: r.computedFilter,
computedSorting: r.computedSorting
};
G(n), L(n);
}
} else
N(
typeof e == "object" ? (t) => b ? { ...t, pageIndex: e.pageIndex } : {
pageIndex: e.pageIndex,
pageSize: e.pageSize
} : e
);
},
getPaginationRowModel: Rt(),
manualPagination: i,
pageCount: i && z ? Ue : Je,
state: {
sorting: H,
columnFilters: je,
columnVisibility: qe,
rowSelection: be,
globalFilter: m,
pagination: i ? r.pagination : K,
columnOrder: Fe
},
// Prevent implicit resets on data/columns changes so filters/sorting persist
autoResetPageIndex: !1
}, s = ct(rt), ne = s.getFilteredRowModel();
O(() => {
if (i && g) {
const e = Math.ceil(
g.totalItems / (r.pagination.pageSize || 10)
);
he(e), !z && e > 0 && Se(e);
} else {
const e = ne.rows.length, t = Math.ceil(e / K.pageSize);
he(t), Se(t);
}
}, [
i,
g?.totalItems,
r.pagination.pageSize,
r.pagination.pageIndex,
s,
ne,
K.pageSize,
z
]), O(() => {
if (b) return;
const e = () => {
if (U.current) return;
const y = q.current;
if (!y) return;
const _ = y.querySelector(
"thead tr"
), o = y.querySelector(
'tbody[data-table-body="data"] tr'
), w = _?.getBoundingClientRect().height ?? 40;
if (!!!o?.querySelector("td")) return;
const k = o?.getBoundingClientRect().height ?? 32.5;
Ct(y, {
rowHeight: k,
headerHeight: w,
onPageChange: i ? (R, v) => {
if (g && me.current) {
const S = b ? {
...r.pagination,
pageIndex: R
} : {
...r.pagination,
pageIndex: R,
pageSize: v
}, ie = S.pageIndex === r.pagination.pageIndex, ae = b || S.pageSize === r.pagination.pageSize;
if (ie && ae)
return;
const A = {
...r,
pagination: S,
// Preserve computed values if they exist
computedFilter: r.computedFilter,
computedSorting: r.computedSorting
};
G(A), U.current || L(A);
}
} : (R, v) => {
N(
(S) => b ? { ...S, pageIndex: R } : { pageIndex: R, pageSize: v }
);
},
setPagination: (R) => {
const v = typeof R == "function" ? R(r.pagination) : R;
if (i && g) {
const S = b ? {
...r.pagination,
pageIndex: v.pageIndex
} : {
...r.pagination,
pageIndex: v.pageIndex,
pageSize: v.pageSize
}, ie = S.pageIndex === r.pagination.pageIndex, ae = b || S.pageSize === r.pagination.pageSize;
if (ie && ae)
return;
const A = {
...r,
pagination: S,
// Preserve computed values if they exist
computedFilter: r.computedFilter,
computedSorting: r.computedSorting
};
G(A), U.current || L(A);
} else
N(
(S) => b ? { ...S, pageIndex: v.pageIndex } : {
pageIndex: v.pageIndex,
pageSize: v.pageSize
}
);
}
});
}, t = Ft(() => {
requestAnimationFrame(() => {
e();
});
}, 200);
requestAnimationFrame(() => {
requestAnimationFrame(() => {
e();
});
});
const n = setTimeout(e, 200), a = document.fonts;
a && typeof a.ready?.then == "function" && a.ready.then(() => e()), window.addEventListener("resize", t);
const c = q.current, p = typeof ResizeObserver < "u" ? new ResizeObserver((y) => {
const _ = y[0];
if (!_) return;
const o = _.contentRect.height, w = fe.current;
(w === null || Math.abs(o - w) > 0.5) && (fe.current = o, t());
}) : null;
return c && p && p.observe(c), () => {
window.removeEventListener("resize", t), clearTimeout(n), p && p.disconnect(), clearTimeout(n);
};
}, [
i,
q,
b,
// Recalculate when the rendered rows count changes; helps when data arrives async
ne.rows.length
]), O(() => {
if ($ && j) {
const e = s.getFilteredSelectedRowModel().rows.map((t) => t.original);
$(e);
}
}, [s, $, j, be]);
const xe = s.getState().sorting, nt = C(() => {
const e = s.getHeaderGroups()[0], t = e ? e.headers : [];
return (xe || []).map((c) => t.find((p) => p.column.id === c.id)).filter(Boolean);
}, [s.getHeaderGroups, xe, I]), oe = C(() => s.getHeaderGroups()[0].headers.filter((e) => e.column.columnDef.meta?.sortable), [s.getHeaderGroups, I]), P = C(() => s.getHeaderGroups()[0].headers.filter((e) => e.column.columnDef.meta?.filterable), [s.getHeaderGroups, I]), ot = C(() => X ? P.filter((e) => (e.column.columnDef.meta?.headerLabel ?? (typeof e.column.columnDef.header == "string" ? e.column.columnDef.header : void 0) ?? e.column.id).toLowerCase().includes(X.toLowerCase())) : P, [P, X]), ye = (e) => {
const t = F.find(
(a) => a.columnId === (e?.id ?? "")
);
if (t)
return re(t.id), t.id;
const n = yt(e);
return ee((a) => [...a, n]), re(n.id), n.id;
}, it = () => {
te(xt());
}, at = C(() => F.sort((e, t) => e.value === void 0 || e.value === "" ? 1 : t.value === void 0 || t.value === "" ? -1 : 1), [F]), we = f({
filterBadges: [],
advancedFilterBadge: void 0,
searchbarGlobalFilter: void 0
});
O(() => {
const e = vt(
F,
x ?? void 0
), t = m && T.length > 0 ? T.length === 1 ? (() => {
const o = T[0], M = B.find(
(k) => Q(k) === o
)?.meta?.type;
return M === "list_multi_select" || M === "list_single_select" ? {
[o]: {
operator: "array_overlap",
value: [m]
}
} : {
[o]: {
operator: "like",
value: m
}
};
})() : {
_or: T.map((o) => {
const M = B.find(
(k) => Q(k) === o
)?.meta?.type;
return M === "list_multi_select" || M === "list_single_select" ? {
[o]: {
operator: "array_overlap",
value: [m]
}
} : {
[o]: {
operator: "like",
value: m
}
};
})
} : null, n = [
...e && Object.keys(e).length > 0 ? [e] : [],
...t ? [t] : []
], a = n.length === 0 ? {} : n.length === 1 ? n[0] : { _and: n }, c = {
sort_by: H.map((o) => o.id),
sort_order: H.map((o) => o.desc ? "desc" : "asc")
}, p = we.current, y = JSON.stringify(F) !== JSON.stringify(p.filterBadges) || JSON.stringify(x) !== JSON.stringify(p.advancedFilterBadge) || m !== p.searchbarGlobalFilter;
we.current = {
filterBadges: [...F],
advancedFilterBadge: x ? { ...x } : void 0,
searchbarGlobalFilter: m
};
const _ = ge.current;
if (tt(a), i && E.current && _) {
const o = {
...r,
computedFilter: a,
// Emit only computedSorting. Use empty arrays to signal "no sorting" when cleared.
computedSorting: c,
filters: {
filterBadges: F,
advancedFilterBadge: x,
searchbarFilter: m
},
pagination: {
...r.pagination,
// Only reset pageIndex to 0 when filters actually changed, not when just sorting changed
pageIndex: y ? 0 : r.pagination.pageIndex
}
};
G(o);
const w = JSON.stringify({
computedFilter: o.computedFilter,
computedSorting: o.computedSorting,
filters: o.filters,
pagination: o.pagination
});
pe.current !== w && (pe.current = w, L(o));
} else
y && N((o) => ({
...o,
pageIndex: 0
}));
}, [
F,
x,
m,
T,
B,
H,
i
]);
const Re = s.getAllLeafColumns(), st = Re.filter((e) => e.getIsVisible()), lt = Re.filter((e) => !e.getIsVisible());
return /* @__PURE__ */ se(
"div",
{
id: Ge,
"data-testid": Pe,
className: dt(
"flex h-full max-h-full min-h-[250px] w-full max-w-full min-w-0 flex-col gap-2",
De
),
children: [
!Ne && /* @__PURE__ */ se("div", { className: "flex w-full items-center justify-between gap-1", children: [
/* @__PURE__ */ se("div", { className: "flex min-w-0 flex-1 items-center gap-1 overflow-x-auto", children: [
oe.length > 0 && /* @__PURE__ */ d(
bt,
{
table: s,
sortedColumns: nt,
sortableColumns: oe,
i18n: h,
maxSortedColumns: ce
}
),
oe.length > 0 && P.length > 0 && /* @__PURE__ */ d("div", { className: "border-d-border h-4 min-h-4 w-[1px] border-r" }),
Me,
/* @__PURE__ */ d(
ft,
{
advancedFilterBadge: x,
setAdvancedFilterBadge: te,
filterableColumns: P,
sortedFilterBadges: at,
setFilterBadges: ee,
filterSearch: X,
setFilterSearch: Ze,
filteredColumns: ot,
handleAddFilter: ye,
handleAddAdvancedFilter: it,
pendingOpenFilterId: $e,
onPendingOpenFilterHandled: () => re(void 0),
i18n: h
}
)
] }),
/* @__PURE__ */ d("div", { className: "border-d-border h-4 min-h-4 w-[1px] border-r" }),
Oe,
(F.length || x) && /* @__PURE__ */ d(
ut,
{
className: "h-6 text-xs",
iconLeft: "FunnelX",
size: "sm",
variant: "ghost-destructive",
onClick: () => {
ee([]), te(void 0);
},
children: h.reset
}
),
T.length > 0 && /* @__PURE__ */ d(
St,
{
debounceMs: 300,
onSearch: Ee,
i18n: h,
initialValue: m ?? "",
searchableColumnsHeaders: B.map(
(e) => et(e)
)
}
),
/* @__PURE__ */ d(
pt,
{
table: s,
visibleColumns: st,
hiddenColumns: lt,
i18n: h
}
),
/* @__PURE__ */ d(gt, { actions: _e, i18n: h })
] }),
/* @__PURE__ */ d(
mt,
{
table: s,
tableContainerRef: q,
loading: z,
data: V,
emptyComponent: ze,
notFoundMessage: h?.notFoundMessage ?? "N/A",
onAddFilter: ye,
maxSortedColumns: ce,
i18n: h,
isServerSide: i,
filterBadges: F,
advancedFilterBadge: x,
searchbarGlobalFilter: m,
serverDebounceTime: de?.serverDebounceTime,
rowClassName: ke
}
),
!He && /* @__PURE__ */ d(
ht,
{
table: s,
isServerSide: i,
clientPagination: K,
serverState: i ? r : void 0,
onServerStateChange: i && g ? (e) => {
G(e), L(e);
} : void 0,
totalItems: g?.totalItems,
i18n: h
}
)
]
}
);
}
export {
Wt as DataTable
};