react-smart-data-table
Version:
A smart data table component for React.js meant to be configuration free
756 lines (755 loc) • 20.7 kB
JavaScript
import { Component as e, createContext as t, memo as n, use as r, useCallback as i, useEffect as a, useImperativeHandle as o, useMemo as s, useReducer as c, useRef as l, useState as u } from "react";
import d from "clsx";
import { find as f } from "linkifyjs";
import { flatten as p } from "flat";
import m from "escape-string-regexp";
import { snakeCase as h } from "change-case";
import { jsx as g, jsxs as _ } from "react/jsx-runtime";
//#region lib/helpers/file-extensions.ts
var v = /* @__PURE__ */ "ase.art.bmp.blp.cd5.cit.cpt.cr2.cut.dds.dib.djvu.egt.exif.gif.gpl.grf.icns.ico.iff.jng.jpeg.jpg.jfif.jp2.jps.lbm.max.miff.mng.msp.nitf.ota.pbm.pc1.pc2.pc3.pcf.pcx.pdn.pgm.PI1.PI2.PI3.pict.pct.pnm.pns.ppm.psb.psd.pdd.psp.px.pxm.pxr.qfx.raw.rle.sct.sgi.rgb.int.bw.tga.tiff.tif.vtf.xbm.xcf.xpm.3dv.amf.ai.awg.cgm.cdr.cmx.dxf.e2d.egt.eps.fs.gbr.odg.svg.stl.vrml.x3d.sxd.v2d.vnd.wmf.emf.art.xar.png.webp.jxr.hdp.wdp.cur.ecw.iff.lbm.liff.nrrd.pam.pcx.pgf.sgi.rgb.rgba.bw.int.inta.sid.ras.sun.tga".split("."), y = "data", ee = "URL detected by the renderer", b = "Select All", x = "Unselect All", S = "Data type is invalid, expected string or array !", te = "Response type is invalid, expected JSON !", ne = "Sampling percentage should be withing the [0, 100] range.", C = "Something went wrong !", re = "DESC", w = "false", T = {
key: "default",
text: "",
invisible: !1,
sortable: !1,
filterable: !1,
isImg: !1
}, E = ([e]) => e, D = (e) => typeof e == "string" || e instanceof String, O = (e) => Array.isArray(e), k = (e) => e && typeof e == "object" && e.constructor === Object || !1, A = (e) => Array.isArray(e) && "length" in e ? !e.length : k(e) ? !Object.keys(e).length : !1, j = (e) => typeof e == "function", M = (e) => typeof e == "number" && Number.isFinite(e), N = (e) => e === void 0, P = (e) => {
if (D(e)) {
let t = e.search(/[a-z]/);
return t === -1 ? e.toUpperCase() : e.substring(0, t + 1).toUpperCase() + e.substring(t + 1);
}
return "";
}, F = (e, t, n) => {
let r = typeof n == "function" ? n : (e, n) => e[t] > n[t] ? 1 : n[t] > e[t] ? -1 : 0;
return [...e].sort(r);
}, ie = (e) => !(e && /^\d+$/.test(e)), ae = (...e) => {
console.error(...e);
};
function I(e = 1, t = 1, n = 1) {
let r = e - 1 > 0 ? e - 1 : 1, i = e + 1 > t ? t : e + 1, a = 1 + 2 * n, o = 2 + a, s = [{
active: !1,
value: 1,
text: "<<"
}, {
active: !1,
value: r,
text: "<"
}];
if (t > o) {
e >= a && (s.push({
active: !1,
value: 1,
text: "1"
}), e > a && s.push({
active: !1,
value: void 0,
text: "..."
}));
for (let r = -n; r < 2 * n; r += 1) {
let n = e + r;
n > 0 && n <= t && s.push({
active: e === n,
value: n,
text: `${n}`
});
}
t - e > n && (t - e - 1 > n && s.push({
active: !1,
value: void 0,
text: "..."
}), s.push({
active: !1,
value: t,
text: `${t}`
}));
} else for (let n = 1; n <= t; n += 1) s.push({
active: e === n,
value: n,
text: `${n}`
});
return s.push({
active: !1,
value: i,
text: ">"
}), s.push({
active: !1,
value: t,
text: ">>"
}), s;
}
async function L(e, { dataKey: t = y, dataKeyResolver: n, options: r = {} } = {}) {
if (O(e)) return e;
if (D(e)) {
let i = await fetch(e, r), { headers: a, ok: o, status: s, statusText: c } = i;
if (o) {
let e = a.get("content-type");
if (e && e.includes("application/json")) {
let e = await i.json();
return typeof n == "function" ? n(e) : t ? e[t] : e;
}
throw Error(te);
}
throw Error(`${s} - ${c}`);
} else throw Error(S);
}
function R(e) {
return e.map(P).join(" ").trim();
}
function z(e) {
return D(e) ? R(h(e).split("_").filter(ie)) : "";
}
function B(e, t) {
return N(e) ? t : e;
}
function V(e, t = {}) {
let { text: n, invisible: r, sortable: i, filterable: a } = { ...t[e] };
return {
key: e,
text: B(n, z(e)),
invisible: B(r, !1),
sortable: B(i, !0),
filterable: B(a, !0),
isImg: B(r, !1)
};
}
function oe(e = [], t = 0) {
if (!t) return p(E(e));
if (t < 0 || t > 100) throw Error(ne);
return p(e.slice(0, Math.ceil(t / 100 * e.length)).reduce((e, t) => ({
...e,
...t
}), {}));
}
function H(e = [], t = {}, n = [], r = !1, i = 0) {
let a = [], o = [];
if (e && O(e) && !A(e)) {
let s = oe(e.filter((e) => !!e), i);
if (A(n) || n.forEach((e) => {
a.includes(e) || (o.push(V(e, t)), a.push(e));
}), !r && k(s)) {
let e = [...Object.keys(s), ...Object.keys(t)];
for (let n of e) a.includes(n) || (o.push(V(n, t)), a.push(n));
}
}
return o;
}
function se(e = []) {
let t = [];
return e && O(e) && !A(e) && (t = e.filter((e) => k(e) && !A(e)).map((e) => p(e))), t;
}
function U(e, t, n) {
let r = e.toLowerCase();
return t.filter((e) => {
let t = Object.keys(e);
for (let i = 0, a = t.length; i < a; i += 1) {
let a = t[i];
if (n[a]?.filterable !== !1 && String(e[a]).toLowerCase().includes(r)) return !0;
}
return !1;
});
}
function W(e, t, n) {
return e ? U(e, t, n) : t;
}
function G(e, t, n) {
if (M(n) && Math.sign(n)) {
let r = n * (t - 1), i = n * t;
return e.slice(r, i);
}
return e;
}
function K(e) {
let t = /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/;
if (typeof e == "string") {
let [n, r] = e.split(":");
if (n && n === "data" && r) {
let [e, n] = r.split(";");
if (e) {
let [r, i] = e.split("/"), [a] = i.split("+");
if (r === "image" && typeof a == "string" && n) {
let [e, r] = n.split(",");
if (e === "base64" && t.test(r)) return v.includes(a.toLowerCase());
}
}
}
}
return !1;
}
function ce(e) {
let t = K(e);
if (t) return t;
let n = document.createElement("a");
n.href = String(e);
let { pathname: r } = n, i = r.search(/[:?&#]/);
i !== -1 && (r = r.substring(0, i));
let a = r.split(".").pop().toLowerCase();
return v.includes(a);
}
function q(e, t) {
let n = {
first: void 0,
highlight: void 0,
last: void 0,
value: e
};
if (!t) return n;
if (RegExp(`.*?${m(t)}.*?`, "i").test(e)) {
let n = E(e.toLowerCase().split(t.toLowerCase())).length, r = t.length;
return {
first: e.substring(0, n),
highlight: e.substring(n, n + r),
last: e.substring(n + r),
value: e
};
}
return n;
}
function le({ children: e, content: t, parseBool: n } = {}) {
if (n) {
let { noWord: r = "No", yesWord: i = "Yes" } = typeof n == "object" ? n : {};
if (t === !0 || e === !0) return i;
if (t === !1 || e === !1) return r;
}
if (t === 0 || e === 0) return "0";
if (t === !1 || e === !1) return w;
let r = "";
return t ? r = t : e && (r = e), `${r}`;
}
//#endregion
//#region lib/components/HighlightValue.tsx
var J = ({ children: e, filterValue: t = "", "data-testid": n = "highlight-value" }) => {
let { first: r, highlight: i, last: a } = s(() => q(String(e), t), [e, t]);
return !r && !i && !a ? e : /* @__PURE__ */ _("span", {
"data-testid": n,
children: [
r,
/* @__PURE__ */ g("span", {
className: "rsdt rsdt-highlight",
children: i
}),
a
]
});
}, Y = n(({ children: e, content: t = "", filterable: n = !0, filterValue: r = "", isImg: a = !1, parseBool: o = !1, parseImg: c = !1, withLinks: l = !1 }) => {
let u = s(() => le({
children: e,
content: t,
parseBool: o
}), [
e,
t,
o
]), d = i(() => n ? /* @__PURE__ */ g(J, {
filterValue: r,
"data-testid": "cell-value",
children: u
}) : u, [
r,
n,
u
]), p = i(({ bypass: e = !1 } = {}) => {
if (a || e || ce(u)) {
let { style: e, className: t } = typeof c == "boolean" ? {
style: void 0,
className: void 0
} : c;
return /* @__PURE__ */ g("img", {
src: u,
style: e,
className: t,
alt: ee,
"data-testid": "cell-value"
});
}
return null;
}, [
a,
c,
u
]), m = i(() => {
let e = f(u), t = d();
if (A(e)) return K(u) ? p({ bypass: !0 }) : t;
let n = E(e), r = null;
return c && n.type === "url" && (r = p()), /* @__PURE__ */ g("a", {
href: n.href,
onClick: (e) => e.stopPropagation(),
children: r || t
});
}, [
d,
c,
p,
u
]);
return l ? m() : c && p() || d();
}), X = ({ children: e, ...t }) => /* @__PURE__ */ g("table", {
...t,
children: e
}), ue = n(({ children: e, ...t }) => /* @__PURE__ */ g("tbody", {
...t,
children: e
})), de = n(({ children: e, ...t }) => /* @__PURE__ */ g("td", {
...t,
children: e
})), fe = n(({ children: e, ...t }) => /* @__PURE__ */ g("tfoot", {
...t,
children: e
})), pe = n(({ children: e, ...t }) => /* @__PURE__ */ g("thead", {
...t,
children: e
})), me = n(({ children: e, ...t }) => /* @__PURE__ */ g("th", {
...t,
children: e
})), he = n(({ children: e, ...t }) => /* @__PURE__ */ g("tr", {
...t,
children: e
}));
X.Body = ue, X.Cell = de, X.Footer = fe, X.Header = pe, X.HeaderCell = me, X.Row = he;
//#endregion
//#region lib/helpers/typed-memo.ts
var ge = n;
//#endregion
//#region lib/components/DataRow.tsx
function _e({ row: e, rowIndex: t, columns: n, colProperties: r, withLinks: a, filterValue: o, parseBool: s, parseImg: c, onRowClick: l, rowClassName: u, tableData: f }) {
let p = i((n) => {
l(n, e, t, f);
}, [
l,
e,
t,
f
]);
return /* @__PURE__ */ g(X.Row, {
onClick: p,
className: d(u?.(e, t, f)),
children: n.map((n) => {
let i = r[n.key], l = !i?.invisible, u = i?.transform;
return l ? /* @__PURE__ */ g(X.Cell, {
"data-column-name": n.key,
children: j(u) ? u(e[n.key], t, e) : /* @__PURE__ */ g(Y, {
withLinks: a,
filterValue: o,
parseBool: s,
parseImg: c,
filterable: i?.filterable,
isImg: i?.isImg,
children: e[n.key]
})
}, `row-${t}-${n.key}`) : null;
})
});
}
var ve = ge(_e), Z = class extends e {
static {
this.defaultProps = {
logError: () => null,
fbComponent: null
};
}
constructor(e) {
super(e), this.state = {
hasError: !1,
error: null,
errorInfo: null
};
}
componentDidCatch(e, t) {
let { logError: n } = this.props;
this.setState({
hasError: !0,
error: e,
errorInfo: t
}), typeof n == "function" && n(e, t);
}
renderDefaultFB() {
let { error: e, errorInfo: t } = this.state;
return /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("h2", { children: C }), /* @__PURE__ */ _("details", {
style: { whiteSpace: "pre-wrap" },
children: [/* @__PURE__ */ g("summary", { children: e && e.toString() }), /* @__PURE__ */ g("p", { children: t.componentStack })]
})] });
}
render() {
let { hasError: e } = this.state, { children: t, fbComponent: n } = this.props;
return e ? n || this.renderDefaultFB() : t;
}
}, ye = n(({ active: e, value: t, text: n, onPageChange: r }) => {
let a = i((e) => {
r(e, { activePage: t });
}, [r, t]);
return /* @__PURE__ */ g("button", {
className: d("rsdt", "rsdt-paginate", { active: e }, "button"),
type: "button",
onClick: a,
disabled: N(t),
"data-testid": "paginator-item",
children: n
});
}, ({ active: e, value: t, text: n }, { active: r, value: i, text: a }) => e === r && t === i && n === a), be = n(({ activePage: e, totalPages: t, onPageChange: n }) => {
let r = s(() => I(e, t), [e, t]), i = s(() => typeof n == "function" ? n : () => null, [n]);
return /* @__PURE__ */ g("div", {
className: "rsdt rsdt-paginate",
children: r.map(({ active: e, value: t, text: n }, r) => /* @__PURE__ */ g(ye, {
active: e,
value: t,
text: n,
onPageChange: i
}, N(t) ? `ellipsis-${r}` : n))
});
}, ({ activePage: e, totalPages: t }, { activePage: n, totalPages: r }) => e === n && t === r), Q = ({ locale: { selectAllWord: e = b, unSelectAllWord: t = x } = {}, handleToggleAll: n, ref: r }) => {
let [a, s] = u(!1), c = i(() => {
typeof n == "function" && n(a), s(!a);
}, [n, a]);
return o(r, () => ({ setUnchecked: () => {
a && s(!1);
} })), /* @__PURE__ */ g("span", {
className: "rsdt rsdt-column-toggles toggle",
children: /* @__PURE__ */ _("label", {
htmlFor: "select-all",
children: [/* @__PURE__ */ g("input", {
type: "checkbox",
id: "select-all",
value: "select-all",
name: "select-all",
checked: a,
onChange: c,
"data-testid": "select-all"
}), a ? t : e]
})
});
};
Q.displayName = "SelectAll";
//#endregion
//#region lib/components/Toggles.tsx
var xe = ({ columns: e, colProperties: t, handleColumnToggle: n, handleColumnToggleAll: r, selectAll: a = !1 }) => {
let o = typeof a == "object" ? a : {}, s = l(null), c = i(({ target: { value: e } }) => {
n(String(e)), s?.current && e && s.current.setUnchecked();
}, [n]), u = i((e) => {
let n = t[e];
return !n || !n.invisible;
}, [t]);
return /* @__PURE__ */ _("nav", {
className: "rsdt rsdt-column-toggles",
children: [a && /* @__PURE__ */ g(Q, {
locale: o?.locale,
handleToggleAll: typeof o?.handleToggleAll == "function" ? o?.handleToggleAll : r,
ref: s
}), e.map(({ key: e, text: t } = T) => /* @__PURE__ */ g("span", {
className: "rsdt rsdt-column-toggles toggle",
children: /* @__PURE__ */ _("label", {
htmlFor: e,
children: [/* @__PURE__ */ g("input", {
type: "checkbox",
id: e,
value: e,
name: t,
checked: u(e),
onChange: c
}), t]
})
}, e))]
});
}, Se = {
activePage: 1,
asyncData: [],
colProperties: {},
columns: [],
isLoading: !1,
prevFilterValue: "",
sorting: {
key: "",
dir: ""
}
}, Ce = t(Se), we = () => r(Ce), $ = /* @__PURE__ */ function(e) {
return e.FetchStart = "FETCH_START", e.FetchSuccess = "FETCH_SUCCESS", e.FetchError = "FETCH_ERROR", e.SetSorting = "SET_SORTING", e.SetActivePage = "SET_ACTIVE_PAGE", e.ToggleColumn = "TOGGLE_COLUMN", e.ToggleAllColumns = "TOGGLE_ALL_COLUMNS", e.ResetPageForFilter = "RESET_PAGE_FOR_FILTER", e;
}($ || {});
//#endregion
//#region lib/hooks/useTableReducer.ts
function Te(e, t) {
switch (t.type) {
case $.FetchStart: return {
...e,
isLoading: !0
};
case $.FetchSuccess: return {
...e,
asyncData: t.asyncData,
columns: t.columns,
isLoading: !1
};
case $.FetchError: return {
...e,
isLoading: !1
};
case $.SetSorting: {
let { key: n } = t.column, r = e.sorting.key === n ? e.sorting.dir : "", i;
return i = r ? r === "ASC" ? re : "" : "ASC", {
...e,
sorting: {
key: n,
dir: i
}
};
}
case $.SetActivePage: return {
...e,
activePage: t.activePage
};
case $.ToggleColumn: {
let { key: n } = t, r = e.colProperties[n] ?? {
...T,
key: n
};
return {
...e,
colProperties: {
...e.colProperties,
[n]: {
...r,
invisible: !r.invisible
}
}
};
}
case $.ToggleAllColumns: {
let n = { ...e.colProperties };
for (let { key: e } of t.columns) n[e] = {
...n[e] ?? {
...T,
key: e
},
invisible: t.isChecked
};
return {
...e,
colProperties: n
};
}
case $.ResetPageForFilter: return {
...e,
activePage: 1,
prevFilterValue: t.filterValue
};
default: return e;
}
}
function Ee(e = {}) {
return c(Te, {
...Se,
colProperties: e
});
}
//#endregion
//#region lib/hooks/useAsyncData.ts
function De({ data: e, dataKey: t, dataKeyResolver: n, dataRequestOptions: r, headers: i, orderedHeaders: o, hideUnordered: s, dataSampling: c, dispatch: u }) {
let d = l({
headers: i,
orderedHeaders: o,
hideUnordered: s,
dataSampling: c
});
d.current = {
headers: i,
orderedHeaders: o,
hideUnordered: s,
dataSampling: c
}, a(() => {
D(e) && (async () => {
u({ type: $.FetchStart });
try {
let i = await L(e, {
dataKey: t,
dataKeyResolver: n,
options: r
}), a = d.current, o = H(i, a.headers, a.orderedHeaders, a.hideUnordered, a.dataSampling);
u({
type: $.FetchSuccess,
asyncData: i,
columns: o
});
} catch (e) {
u({ type: $.FetchError }), ae(e);
}
})();
}, [
e,
t,
n,
r,
u
]);
}
//#endregion
//#region lib/hooks/useColumns.ts
function Oe({ resolvedData: e, stateColumns: t, headers: n, orderedHeaders: r, hideUnordered: i, dataSampling: a, dynamic: o }) {
return s(() => !o && !A(t) ? t : H(e, n, r, i, a), [
e,
t,
n,
r,
i,
a,
o
]);
}
//#endregion
//#region lib/hooks/useRows.ts
function ke({ resolvedData: e, sorting: t, colProperties: n, filterValue: r, activePage: i, perPage: a }) {
let o = s(() => se(e), [e]), c = s(() => {
let { dir: e, key: r } = t;
if (!e) return o;
let i = F(o, r, typeof n[r]?.sortable == "function" && n[r].sortable);
return e === "ASC" ? i : i.reverse();
}, [
o,
t,
n
]), l = s(() => W(r, c, n), [
r,
c,
n
]);
return {
filteredRows: l,
visibleRows: s(() => G(l, i, a), [
l,
i,
a
])
};
}
//#endregion
//#region lib/SmartDataTable.tsx
var Ae = {}, je = [], Me = {}, Ne = () => null;
function Pe({ className: e = "", data: t, dataKey: n = y, dataKeyResolver: r = null, dataRequestOptions: o = Me, dataSampling: c = 0, dynamic: u = !1, emptyTable: f = null, filterValue: p = "", headers: m = Ae, hideUnordered: h = !1, loader: v = null, name: ee = "reactsmartdatatable", onRowClick: b = Ne, orderedHeaders: x = je, paginator: S = be, parseBool: te = !1, parseImg: ne = !1, perPage: C = 0, rowClassName: re, sortable: w = !1, withFooter: T = !1, withHeader: E = !0, withLinks: O = !1, withToggles: k = !1 }) {
let [j, M] = Ee(m), { activePage: N, asyncData: P, colProperties: F, columns: ie, isLoading: ae, sorting: I } = j, L = l(p);
a(() => {
p !== L.current && (L.current = p, M({
type: $.ResetPageForFilter,
filterValue: p
}));
}, [p, M]), De({
data: t,
dataKey: n,
dataKeyResolver: r,
dataRequestOptions: o,
headers: m,
orderedHeaders: x,
hideUnordered: h,
dataSampling: c,
dispatch: M
});
let R = s(() => D(t) ? P : t, [t, P]), z = Oe({
resolvedData: R,
stateColumns: ie,
headers: m,
orderedHeaders: x,
hideUnordered: h,
dataSampling: c,
dynamic: u
}), { filteredRows: B, visibleRows: V } = ke({
resolvedData: R,
sorting: I,
colProperties: F,
filterValue: p,
activePage: N,
perPage: C
}), oe = i((e, { activePage: t }) => {
M({
type: $.SetActivePage,
activePage: t
});
}, [M]), H = i((e) => {
M({
type: $.SetSorting,
column: e
});
}, [M]), se = i((e) => {
M({
type: $.ToggleColumn,
key: e
});
}, [M]), U = i((e) => (t) => {
M({
type: $.ToggleAllColumns,
columns: e,
isChecked: t
});
}, [M]), W = i((e, t, n, r) => {
typeof b == "function" && b(e, {
rowData: t,
rowIndex: n,
tableData: r
});
}, [b]), G = s(() => C > 0 ? Math.ceil(B.length / C) : 0, [B.length, C]), K = s(() => U(z), [U, z]), ce = (e) => {
let { key: t, dir: n } = I, r = "rsdt-sortable-icon";
return t === e.key && n && (r = n === "ASC" ? "rsdt-sortable-asc" : "rsdt-sortable-desc"), /* @__PURE__ */ g("i", {
className: d("rsdt", r),
onClick: () => H(e),
onKeyDown: () => H(e),
role: "button",
tabIndex: 0,
"aria-label": "sorting column"
});
}, q = () => {
let e = z.map((e) => {
let t = F[e.key];
return !t || !t.invisible ? /* @__PURE__ */ _(X.HeaderCell, {
"data-column-name": e.key,
children: [/* @__PURE__ */ g("span", { children: e.text }), /* @__PURE__ */ g("span", {
className: "rsdt rsdt-sortable",
children: w && e.sortable ? ce(e) : null
})]
}, e.key) : null;
});
return /* @__PURE__ */ g(X.Row, { children: e });
}, le = () => {
let e = V.map((e, t) => /* @__PURE__ */ g(Z, { children: /* @__PURE__ */ g(ve, {
row: e,
rowIndex: t,
columns: z,
colProperties: F,
withLinks: O,
filterValue: p,
parseBool: te,
parseImg: ne,
onRowClick: W,
rowClassName: re,
tableData: B
}) }, `row-${t}`));
return /* @__PURE__ */ g(X.Body, { children: e });
}, J = () => k ? /* @__PURE__ */ g(Z, { children: /* @__PURE__ */ g(xe, {
columns: z,
colProperties: F,
handleColumnToggle: se,
handleColumnToggleAll: K,
selectAll: (typeof k == "object" ? k : {})?.selectAll
}) }) : null, Y = () => !C || C <= 0 || !G ? null : /* @__PURE__ */ g(Z, { children: /* @__PURE__ */ g(S, {
totalPages: G,
activePage: N,
onPageChange: oe
}) });
return ae ? v : A(B) ? f : /* @__PURE__ */ g(Ce, {
value: j,
children: /* @__PURE__ */ _("section", {
className: "rsdt rsdt-container",
children: [
J(),
/* @__PURE__ */ _(X, {
"data-table-name": ee,
className: e,
children: [
E && /* @__PURE__ */ g(X.Header, { children: q() }),
le(),
T && /* @__PURE__ */ g(X.Footer, { children: q() })
]
}),
Y()
]
})
});
}
//#endregion
//#region lib/index.ts
var Fe = Pe;
//#endregion
export { Y as CellValue, Y as TableCell, Z as ErrorBoundary, J as HighlightValue, be as Paginator, Q as SelectAll, xe as Toggles, Fe as default, we as useSmartDataTableContext };