@shridey/intelligentable
Version:
Intelligentable is a highly customizable, fully-types, performant, and feature-rich React component library built on top of handpicked industry-level production-grade UI Components for modern web applications.
760 lines (759 loc) • 22.3 kB
JavaScript
import { jsx as f, jsxs as k } from "react/jsx-runtime";
import { useState as V, useMemo as E } from "react";
import { Dropdown as W, Button as P, Input as z, Table as N, ConfigProvider as U } from "antd";
import q from "write-excel-file";
import { jsPDF as G } from "jspdf";
import J from "jspdf-autotable";
import { saveAs as D } from "file-saver";
import { DownloadOutlined as K, FilePdfOutlined as X, FileExcelOutlined as H, FileTextOutlined as O, SearchOutlined as Y } from "@ant-design/icons";
const Z = (a) => {
const e = /* @__PURE__ */ new Map(), s = (n) => "min" in n || "max" in n, t = (n) => "str" in n || "regEx" in n, r = (n) => {
Array.isArray(n.colorConfig) && n.colorConfig.forEach((o) => {
const l = o.color;
let d = "", c = "";
if (s(o)) {
const { min: i, max: u, inclusiveMin: b = !0, inclusiveMax: p = !1 } = o;
d = `threshold-${i ?? "-∞"}-${u ?? "∞"}-${b}-${p}-${l}`;
const m = b ? "≥" : ">", y = p ? "≤" : "<";
i != null && u != null && i === u ? c = `= ${i}` : i != null && u != null ? c = `${m} ${i} & ${y} ${u}` : i != null ? c = `${m} ${i}` : u != null ? c = `${y} ${u}` : c = "All values", e.set(d, { label: c, color: l });
}
t(o) && (o.str ? (d = `str-${o.str}-${o.exactMatch}-${l}`, c = o.exactMatch ? `= "${o.str}"` : `~ "${o.str}"`) : o.regEx && (d = `regex-${o.regEx}-${l}`, c = `= RegEx /${o.regEx}/`), d && e.set(d, { label: c, color: l }));
}), Array.isArray(n.children) && n.children.forEach(r);
};
return a?.forEach(r), Array.from(e.values());
}, _ = ({
columns: a = [],
legendStyle: e = {}
}) => {
const s = Z(a);
return s.length > 0 && /* @__PURE__ */ f(
"div",
{
style: {
display: "flex",
gap: 18,
padding: 6,
border: e?.border || "1px solid #d9d9d9",
borderRadius: 6,
backgroundColor: e?.backgroundColor
},
children: s.map((t, r) => /* @__PURE__ */ k(
"div",
{
style: {
display: "flex",
alignItems: "center",
gap: 6
},
children: [
/* @__PURE__ */ f(
"svg",
{
width: e?.circle?.radius * 2 || 12,
height: e?.circle?.radius * 2 || 12,
children: /* @__PURE__ */ f(
"circle",
{
cx: e?.circle?.radius || 6,
cy: e?.circle?.radius || 6,
r: e?.circle?.radius || 6,
fill: t.color
}
)
}
),
/* @__PURE__ */ f(
"span",
{
style: {
fontSize: e?.label?.fontSize || 14,
fontWeight: e?.label?.fontWeight,
fontFamily: e?.label?.fontFamily,
color: e?.label?.color
},
children: t.label
}
)
]
},
r
))
}
);
}, F = (a, e) => {
const s = /* @__PURE__ */ new Date(), t = (o) => o.toString().padStart(2, "0"), r = `${s.getFullYear()}-${t(s.getMonth() + 1)}-${t(
s.getDate()
)}`, n = `${t(s.getHours())}-${t(s.getMinutes())}`;
return `${a}-${r}_${n}.${e}`;
}, j = (a, e, s) => {
const t = e.filter((l) => l.title), r = t.map((l) => l.title), n = t.map((l) => l.dataIndex), o = a.map(
(l) => n.map((d) => {
const c = l[d];
if (c == null) return "";
const i = String(c);
return typeof c == "string" && (i.includes(s) || i.includes(`
`)) ? `"${i.replace(/"/g, '""')}"` : i;
}).join(s)
);
return [r.join(s), ...o].join(`
`);
}, Q = async (a, e = {}) => {
const {
fontUrl: s,
fontFileName: t = "",
fontName: r = "",
fontStyles: n = [],
fallbackFont: o = "helvetica"
} = e;
if (!s) {
a.setFont(o);
return;
}
try {
const d = await (await fetch(s)).blob(), c = await new Promise((i) => {
const u = new FileReader();
u.onload = () => i(u.result), u.readAsDataURL(d);
}).then((i) => i.split(",")[1]);
a.addFileToVFS(t, c), n.forEach((i) => {
a.addFont(t, r, i);
}), a.setFont(r);
} catch (l) {
console.error(`Failed to load custom font (${t}):`, l), a.setFont(o);
}
}, ee = async (a, e, s, t, r) => {
const n = e.filter((d) => d.title), o = n.map((d) => d.title), l = n.map((d) => d.dataIndex);
switch (s) {
case "csv": {
const d = j(a, e, ","), c = new Blob(["\uFEFF" + d], {
type: "text/csv;charset=utf-8;"
});
D(c, F(t || "table-export", "csv"));
break;
}
case "tsv": {
const d = j(a, e, " "), c = new Blob(["\uFEFF" + d], {
type: "text/tab-separated-values;charset=utf-8;"
});
D(c, F(t || "table-export", "tsv"));
break;
}
case "xlsx": {
const d = a.map((i) => {
const u = {};
return l.forEach((b, p) => {
u[o[p]] = i[b] ?? "";
}), u;
}), c = o.map((i) => ({
column: i,
type: String,
// Force all values to strings
value: (u) => String(u[i] ?? "")
}));
await q(d, {
schema: c,
fileName: F(t || "table-export", "xlsx")
});
break;
}
case "json": {
const d = a.map((i) => {
const u = {};
return l.forEach((b, p) => {
u[o[p]] = i[b];
}), u;
}), c = new Blob([JSON.stringify(d, null, 2)], {
type: "application/json"
});
D(c, F(t || "table-export", "json"));
break;
}
case "pdf": {
const d = e.filter((p) => p.title), c = d.map((p) => p.title), i = d.map((p) => p.dataIndex), u = a.map(
(p) => i.map((m) => {
const y = p[m];
return y != null ? String(y) : "";
})
), b = new G({ orientation: "landscape" });
await Q(b, r), J(b, {
head: [c],
body: u,
styles: {
font: r?.fontName,
fontSize: 8,
cellPadding: 2,
overflow: "linebreak"
},
headStyles: {
fillColor: [70, 130, 180],
textColor: 255,
fontStyle: "bold"
},
margin: { top: 10 }
}), b.save(F(t || "table-export", "pdf"));
break;
}
}
}, te = ({
data: a = [],
columns: e = [],
exportFileName: s = "",
pdfFontOptions: t = {
fontUrl: "",
fontFileName: "",
fontName: "",
fontStyles: [],
fallbackFont: "helvetica"
}
}) => {
const r = (o) => {
ee(
a,
e,
o,
s,
t
);
}, n = [
{
key: "pdf",
label: "PDF",
icon: /* @__PURE__ */ f(X, {}),
onClick: () => r("pdf")
},
{
key: "xlsx",
label: "Excel (XLSX)",
icon: /* @__PURE__ */ f(H, {}),
onClick: () => r("xlsx")
},
{
key: "json",
label: "JSON",
icon: /* @__PURE__ */ f(O, {}),
onClick: () => r("json")
},
{
key: "tsv",
label: "TSV",
icon: /* @__PURE__ */ f(O, {}),
onClick: () => r("tsv")
},
{
key: "csv",
label: "CSV",
icon: /* @__PURE__ */ f(O, {}),
onClick: () => r("csv")
}
].filter(Boolean);
return /* @__PURE__ */ f(W, { menu: { items: n }, trigger: ["hover"], children: /* @__PURE__ */ f(P, { size: "middle", icon: /* @__PURE__ */ f(K, {}), iconPosition: "end", children: "Export" }) });
}, ne = ({
columns: a = [],
data: e = [],
legends: s = {
enable: !1,
style: {}
},
search: t = {
enable: !1,
searchText: "",
setSearchText: (n) => n,
placeholder: "Search table..."
},
exportButton: r = {
enable: !1,
exportFileName: "",
pdfFontOptions: {
fontUrl: "",
fontFileName: "",
fontName: "",
fontStyles: [],
fallbackFont: ""
}
}
}) => (s.enable || t.enable || r.enable) && /* @__PURE__ */ k(
"div",
{
style: {
display: "flex",
alignItems: "center",
justifyContent: "space-between",
flexWrap: "wrap",
rowGap: 12,
columnGap: 12
},
children: [
s.enable && /* @__PURE__ */ f(
_,
{
columns: a,
legendStyle: s.style
}
),
t.enable && t.searchText !== void 0 && t.setSearchText && /* @__PURE__ */ f(
"div",
{
style: {
flex: 1,
minWidth: 300
},
children: /* @__PURE__ */ f(
z,
{
style: {
width: "100%"
},
placeholder: t.placeholder || "Search table...",
value: t.searchText,
onChange: (n) => t.setSearchText(n.target.value),
size: "middle",
suffix: /* @__PURE__ */ f(Y, {})
}
)
}
),
r.enable && /* @__PURE__ */ f(
te,
{
data: e,
columns: a,
exportFileName: r.exportFileName,
pdfFontOptions: r.pdfFontOptions
}
)
]
}
), L = (a, e) => {
if (!Array.isArray(a) || e == null) return;
const s = w(e);
for (const t of a)
if (t.color !== void 0) {
if (s === "string" || s === "date" || s === "dayOfWeek") {
if (t.regEx)
try {
if (new RegExp(t.regEx, "i").test(String(e))) return t.color;
} catch (r) {
console.warn("Invalid regex pattern:", t.regEx), console.error(r);
continue;
}
else if (t.str !== void 0) {
const r = String(e).toLowerCase(), n = t.str.toLowerCase();
if (t.exactMatch) {
if (r === n) return t.color;
} else if (r.includes(n)) return t.color;
}
continue;
}
if (s === "number" || s === "currency" || s === "id" || s === "percentage") {
if (t.min === void 0 && t.max === void 0) continue;
const n = v(e).number;
if (n == null) continue;
const o = t.min ?? -1 / 0, l = t.max ?? 1 / 0;
if (o === l) {
if (n === o) return t.color;
continue;
}
const d = t.inclusiveMin !== !1 ? n >= o : n > o, c = t.inclusiveMax === !0 ? n <= l : n < l;
if (d && c) return t.color;
}
}
}, re = (a, e) => {
const s = L(e, a);
return /* @__PURE__ */ f(
"span",
{
style: {
color: s
},
children: a
}
);
}, se = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"], T = [
"sunday",
"monday",
"tuesday",
"wednesday",
"thursday",
"friday",
"saturday"
], oe = {
sun: "sunday",
mon: "monday",
tue: "tuesday",
wed: "wednesday",
thu: "thursday",
fri: "friday",
sat: "saturday"
}, A = (a) => {
const e = String(a).trim();
if (/^\d{1,2}[/-]\d{1,2}[/-]\d{2,4}$/.test(e)) {
const [t, r, n] = e.split(/[/-]/).map(Number), o = n < 100 ? n > 50 ? 1900 + n : 2e3 + n : n;
return new Date(o, r - 1, t).getTime();
}
return new Date(e).getTime();
}, ae = (a) => {
if (typeof a.sorter == "function" || typeof a.sorter == "boolean")
return a.sorter;
const e = a.dataIndex;
return typeof e != "string" && typeof e != "number" ? !1 : (s, t) => {
const r = s[e], n = t[e], o = w(r);
if (r == null) return -1;
if (n == null) return 1;
switch (o) {
case "number":
return Number(r) - Number(n);
case "percentage":
case "currency":
case "id": {
const l = v(r), d = v(n), c = l.number, i = d.number;
return c === null ? i === null ? 0 : -1 : i === null ? 1 : c - i;
}
case "date":
return A(String(r)) - A(String(n));
case "dayOfWeek": {
const l = (i) => {
const u = String(i).toLowerCase();
return oe[u] || u;
}, d = l(r), c = l(n);
return T.indexOf(d) - T.indexOf(c);
}
case "string":
return String(r).localeCompare(String(n));
default:
return 0;
}
};
}, w = (a) => {
if (typeof a == "number") return "number";
if (a == null) return "string";
const e = String(a).trim().toLowerCase();
if (/^\s*[+-]?\d+(\.\d+)?\s*%$/.test(e)) return "percentage";
if (/^[$€£¥₹]\s*-?\s*\d+(\.\d+)?$/.test(e) || // Plain: $5000
/^-?\s*\d+(\.\d+)?\s*[$€£¥₹]$/.test(e) || // Plain: 5000$
/^[$€£¥₹]\s*-?\s*\d{1,3}(,\d{3})+(\.\d+)?$/.test(e) || // Intl: $1,000,000
/^[$€£¥₹]\s*-?\s*\d{1,3}(,\d{2})+(,\d{3})*(\.\d+)?$/.test(e) || // Indian mixed: ₹3,00,000,000
/^[$€£¥₹]\s*-?\s*\d{1,3}(,\d{2})+(\.\d+)?$/.test(e) || // Pure Indian: ₹3,50,000
/^-?\s*\d{1,3}(,\d{3})+(\.\d+)?\s*[$€£¥₹]$/.test(e) || // Intl: 1,000,000$
/^-?\s*\d{1,3}(,\d{2})+(,\d{3})*(\.\d+)?\s*[$€£¥₹]$/.test(e) || // Indian mixed: 3,00,000,000₹
/^-?\s*\d{1,3}(,\d{2})+(\.\d+)?\s*[$€£¥₹]$/.test(e))
return "currency";
if (/^#\d+$|^id-\d+$/i.test(e)) return "id";
if (T.includes(e) || se.includes(e))
return "dayOfWeek";
const s = /^\s*(\d{1,2})[/-](\d{1,2})[/-](\d{2}|\d{4})\s*$/, t = /^\d{4}-\d{2}-\d{2}(T.*)?$/, r = /^[A-Za-z]{3,9} \d{1,2},? \d{4}$/, n = e.match(s);
if (n) {
const o = Number(n[1]), l = Number(n[2]), d = Number(n[3]), c = d < 100 ? d > 50 ? 1900 + d : 2e3 + d : d, i = new Date(c, l - 1, o);
if (i.getDate() === o && i.getMonth() === l - 1 && i.getFullYear() === c)
return "date";
} else if (t.test(e) || r.test(e)) {
const o = new Date(e);
if (!isNaN(o.getTime()))
return "date";
}
return /^\s*[+-]?(\d+(\.\d+)?|\.\d+)\s*$/.test(e) ? "number" : "string";
}, M = (a, e) => {
const s = Math.pow(10, e);
return (Math.round((a + Number.EPSILON) * s) / s).toFixed(e);
}, B = (a) => a.map((e) => {
const s = Array.isArray(e.children) && e.children.length > 0, t = {
...e,
children: s ? B(e.children) : void 0
};
return s || (t.sorter = ae(e), e.render || (t.render = (r) => {
let n = r;
const o = w(n);
if (e.roundOff !== void 0 && ["number", "percentage", "currency"].includes(o)) {
const l = v(n);
l.number !== null && (n = `${o === "currency" ? l.symbol : ""}${M(
l.number,
e.roundOff
)}${o !== "currency" ? l.symbol : ""}`);
}
return e.colorConfig && (n = re(
n,
e.colorConfig
)), n;
}), e.filters && !e.onFilter && (t.onFilter = (r, n) => {
const o = v(
n[e.dataIndex]
).number;
if (["id", "number", "percentage", "currency"].includes(
w(o)
)) {
if (o == null) return !1;
if (typeof r == "string" && r.includes("-")) {
const [l, d] = r.split("-").map(Number);
return o >= l && o <= d;
}
return o === Number(r);
}
return !1;
})), t;
}), ie = (a) => {
const e = {};
for (const t of a)
e[t] = (e[t] || 0) + 1;
return Object.entries(e).sort((t, r) => r[1] - t[1])[0]?.[0] || "";
}, v = (a) => {
if (a == null) return { number: null, symbol: "" };
const e = String(a).trim().replace(/,/g, ""), s = w(e);
let t = null, r = "";
switch (s) {
case "number":
t = Number(e);
break;
case "percentage":
t = parseFloat(e.replace("%", "")), r = "%";
break;
case "currency": {
r = e.match(/([^0-9.-]*)-?[\d,.]+/)?.[1] || "", t = parseFloat(e.replace(/[^0-9.-]/g, ""));
break;
}
case "id": {
r = e.match(/([^0-9]*)\d+/)?.[1] || "", t = parseInt(e.replace(/[^0-9]/g, ""), 10);
break;
}
default:
t = null;
break;
}
return { number: isNaN(t) ? null : t, symbol: r };
}, R = (a) => a.flatMap(
(e) => e.children ? R(e.children) : [e]
);
function le(a, e) {
return Array.isArray(e) ? e.reduce((s, t) => {
if (s && t in s)
return s[t];
}, a) : a?.[e];
}
const ce = (a, e) => {
const s = {};
return e.forEach((t) => {
const r = t.summaryOperation, n = t.dataIndex;
if (!r) return;
const o = a.map(
(c) => c[n]
), l = w(o.find((c) => c != null));
if (r === "count") {
s[n] = o.length;
return;
}
if (["number", "percentage", "currency", "id"].includes(l)) {
const c = [], i = o.map((m) => {
const { number: y, symbol: h } = v(m);
return y != null && c.push(h), y;
}).filter((m) => typeof m == "number"), u = ie(c), b = (m) => t.roundOff !== void 0 ? M(m, t.roundOff) : m, p = (m) => `${l === "currency" || l === "id" ? u : ""}${m}${l !== "currency" && l !== "id" ? u : ""}`;
switch (r) {
case "sum": {
const m = i.reduce((y, h) => y + h, 0);
s[n] = p(b(m));
break;
}
case "average": {
const m = i.reduce((y, h) => y + h, 0) / (i.length || 1);
s[n] = p(b(m));
break;
}
case "max": {
const m = Math.max(...i);
s[n] = p(b(m));
break;
}
case "min": {
const m = Math.min(...i);
s[n] = p(b(m));
break;
}
}
}
if (l === "date") {
const c = o.map((i) => new Date(String(i))).filter((i) => !isNaN(i.getTime()));
if (c.length === 0) return;
switch (r) {
case "max":
s[n] = c.reduce((i, u) => i > u ? i : u).toLocaleDateString();
break;
case "min":
s[n] = c.reduce((i, u) => i < u ? i : u).toLocaleDateString();
break;
}
}
}), s;
}, de = ({
data: a = [],
columns: e = [],
defaultSummaryRowStyle: s = {}
}) => {
const t = ce(a, e);
return /* @__PURE__ */ f(N.Summary.Row, { style: s, children: e.map((r, n) => {
const o = String(r.dataIndex), l = n === 0 ? `Summary ${t[o] || ""}` : t[o] || "-", d = L(
r.colorConfig,
l
);
return /* @__PURE__ */ f(N.Summary.Cell, { index: n, align: r.align, children: /* @__PURE__ */ k(
"span",
{
style: {
textOverflow: r.ellipsis ? "ellipsis" : void 0,
whiteSpace: r.ellipsis ? "nowrap" : void 0,
overflow: r.ellipsis ? "hidden" : void 0,
maxWidth: r.ellipsis ? r.width : void 0,
display: r.ellipsis ? "block" : void 0,
color: d
},
title: String(l),
children: [
l,
" ",
r.displaySummaryOperationInSummary && /* @__PURE__ */ k("sub", { children: [
"(",
r.summaryOperation,
")"
] })
]
}
) }, n);
}) });
};
function ue(a, e) {
return e.reduce((s, t) => t(s), a);
}
const Se = ({
columns: a = [],
dataSource: e = [],
dataTransform: s = ({ pipeline: c }) => c([]),
tableThemeConfig: t = {
defaultSummaryRow: {},
legends: {},
searchBox: {},
exportButton: {},
exportButtonDropdown: {}
},
defaultSummary: r = {
enable: !1,
fixed: !1
},
enableLegends: n = !1,
search: o = {
enable: !1,
placeholder: "Search table...",
onSearch: () => !1
},
tableExport: l = {
enable: !1,
exportFileName: "",
pdfFontOptions: {
fontUrl: "",
fontFileName: "",
fontName: "",
fontStyles: [],
fallbackFont: ""
}
},
...d
}) => {
const c = B(a), i = R(c), [u, b] = V(""), p = E(() => {
const y = e ?? [];
return s ? s({
pipeline: (h) => ue([...y], h)
}) : y;
}, [e, s]), m = E(() => {
const y = u.trim().toLowerCase();
return y ? p?.filter((h) => o.onSearch ? o.onSearch(y, h, i) : i.some((C) => {
const I = C.dataIndex;
if (I === void 0) return !1;
const g = le(h, I);
if (g == null) return !1;
let x = null;
const S = w(g);
if (S === "string" || typeof g == "boolean")
x = String(g);
else if (S === "number" || S === "currency" || S === "id" || S === "percentage" || typeof g == "number") {
const $ = v(g);
$.number !== null && (x = `${S === "currency" ? $.symbol : ""}${C.roundOff ? M($.number, C.roundOff) : $.number}${S !== "currency" ? $.symbol : ""}`);
} else if (Array.isArray(g))
x = g.map(($) => String($)).join(", ");
else if (g instanceof Date)
x = g.toLocaleString();
else if (typeof g == "object" && g !== null)
try {
x = JSON.stringify(g);
} catch {
x = null;
}
return x?.toLowerCase().includes(y) ?? !1;
})) : p;
}, [o, u, p, i]);
return /* @__PURE__ */ f(
U,
{
theme: {
components: {
Table: t,
Input: t?.searchBox,
Button: t?.exportButton,
Dropdown: t?.exportButtonDropdown
}
},
children: /* @__PURE__ */ k(
"div",
{
style: {
display: "flex",
flexDirection: "column",
gap: 12
},
children: [
/* @__PURE__ */ f(
ne,
{
columns: i,
data: m,
legends: {
enable: n,
style: t?.legends || {}
},
search: {
enable: o.enable,
searchText: u,
setSearchText: b,
placeholder: o.placeholder || "Search table..."
},
exportButton: {
enable: l.enable,
exportFileName: l.exportFileName,
pdfFontOptions: l.pdfFontOptions
}
}
),
/* @__PURE__ */ f(
N,
{
columns: c,
dataSource: m,
summary: d.summary ? d.summary : r.enable ? (y) => /* @__PURE__ */ f(
N.Summary,
{
fixed: r.fixed,
children: /* @__PURE__ */ f(
de,
{
data: y,
columns: i,
defaultSummaryRowStyle: t?.defaultSummaryRow || {}
}
)
}
) : void 0,
...d
}
)
]
}
)
}
);
};
export {
Se as IntelligentTable
};