react-merge-table
Version:
React component for auto-merging rowspan/colspan in HTML tables
255 lines (245 loc) • 7.73 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
MergeTable: () => AutoMergeTable,
TableBody: () => TableBody,
TableHeader: () => TableHeader
});
module.exports = __toCommonJS(src_exports);
// src/components/AutoMergeTable.tsx
var import_jsx_runtime = require("react/jsx-runtime");
var AutoMergeTable = ({ children, className, style, ...rest }) => {
const tableStyle = {
borderCollapse: "collapse",
width: "100%"
};
const mergedStyle = { ...tableStyle, ...style };
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("table", { className, style: mergedStyle, ...rest, children });
};
// src/utils/normalizeHeaderCell.ts
function normalizeHeaderCell(input, index) {
if (typeof input === "string" || typeof input === "number") {
return { key: `$HEADER-${index}`, label: input };
}
return input;
}
// src/components/TableHeader.tsx
var import_jsx_runtime2 = require("react/jsx-runtime");
var TableHeader = ({ headers, defaultStyle = true, style, ...rest }) => {
const thStyle = {
border: "1px solid #ccc",
padding: "8px",
textAlign: "center",
fontWeight: "bold"
};
const mergedStyle = defaultStyle ? { ...thStyle, ...style } : style;
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("thead", { ...rest, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("tr", { children: headers.map((header, index) => {
const normalized = normalizeHeaderCell(header, index);
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("th", { style: mergedStyle, children: normalized.label }, normalized.key);
}) }) });
};
// src/components/TableCell.tsx
var import_react = require("react");
var import_jsx_runtime3 = require("react/jsx-runtime");
var TableCell = ({
cell,
rowIndex,
colIndex,
columnRenderers,
defaultStyle = true,
style,
...rest
}) => {
const renderer = columnRenderers?.[cell.colIndex];
const renderResult = renderer?.(cell);
const isRawElement = (0, import_react.isValidElement)(renderResult) && (renderResult.type === "td" || renderResult.type === "th");
if (isRawElement) {
const element = renderResult;
return (0, import_react.cloneElement)(element, {
rowSpan: cell.rowspan,
colSpan: cell.colspan,
...element.props
});
}
const tdStyle = {
border: "1px solid #ccc",
padding: "8px",
textAlign: "center",
verticalAlign: "middle"
};
const mergedStyle = defaultStyle ? { ...tdStyle, ...style } : style;
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
"td",
{
rowSpan: cell.rowspan,
colSpan: cell.colspan,
style: mergedStyle,
...rest,
children: renderResult ?? renderContent(cell.content)
}
);
};
function renderContent(value) {
if (Array.isArray(value)) {
console.log(value);
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { children: value.map((v) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: v.label }, v.key)) });
}
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: value.label });
}
// src/components/TableRow.tsx
var import_jsx_runtime4 = require("react/jsx-runtime");
var TableRow = ({ row, rowIndex, columnRenderers, defaultStyle = true, ...rest }) => {
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("tr", { ...rest, children: row.map(
(cell, colIndex) => cell.render ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
TableCell,
{
cell,
rowIndex,
colIndex,
defaultStyle,
columnRenderers
},
colIndex
) : null
) });
};
// src/utils/normalizeCellContent.ts
var globalKeyId = 0;
function toKeyed(label) {
return {
key: `$CELL-${globalKeyId++}`,
label
};
}
function isKeyedValueArray(value) {
return value.length > 0 && typeof value[0] === "object" && value[0] !== null && "key" in value[0] && "label" in value[0];
}
function isKeyedValue(value) {
return typeof value === "object" && value !== null && "key" in value && "label" in value;
}
function interpretLabel(label) {
if (label === "$$")
return "$";
if (label === "~~")
return "~";
return label;
}
function normalizeCellContent(input) {
if (Array.isArray(input)) {
if (isKeyedValueArray(input)) {
return input.map(({ key, label }) => ({
key,
label: interpretLabel(label)
}));
} else {
return input.map((v) => toKeyed(interpretLabel(v)));
}
}
if (isKeyedValue(input)) {
return [{
key: input.key,
label: interpretLabel(input.label)
}];
}
return [toKeyed(interpretLabel(input))];
}
// src/utils/parseRowsToMatrix.ts
function parseRowsToMatrix(rows) {
const raw = rows.map(
(row) => row.data.map(normalizeCellContent)
);
const matrix = raw.map(
(row, rowIndex) => row.map((contents, colIndex) => ({
value: rows[rowIndex].data[colIndex],
// 원래의 셀 값
content: contents[0],
// 대표 값
contents,
// 전체 값 배열
hasMultiple: contents.length > 1,
// 다중 값 여부
rowspan: 1,
colspan: 1,
render: true,
rowIndex,
colIndex
}))
);
for (let rowIndex = 0; rowIndex < matrix.length; rowIndex++) {
for (let colIndex = 0; colIndex < matrix[rowIndex].length; colIndex++) {
const cell = matrix[rowIndex][colIndex];
if (!cell.render)
continue;
const first = cell.content.label;
if (first === "$" && rowIndex > 0) {
for (let i = rowIndex - 1; i >= 0; i--) {
const target = matrix[i][colIndex];
const tFirst = target.content.label;
if (target.render && tFirst !== "$") {
target.rowspan++;
cell.render = false;
break;
}
}
} else if (first === "~" && colIndex > 0) {
for (let j = colIndex - 1; j >= 0; j--) {
const target = matrix[rowIndex][j];
const tFirst = target.content.label;
if (target.render && tFirst !== "~") {
target.colspan++;
cell.render = false;
break;
}
}
}
}
}
return matrix;
}
// src/components/TableBody.tsx
var import_jsx_runtime5 = require("react/jsx-runtime");
var TableBody = ({
rows,
columnRenderers,
defaultStyle = true,
...rest
}) => {
const normalizedRows = rows.map(
(row, i) => Array.isArray(row) ? { key: `row-${i}`, data: row } : row
);
const matrix = parseRowsToMatrix(normalizedRows);
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("tbody", { ...rest, children: matrix.map((row, rowIndex) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
TableRow,
{
row,
rowIndex,
defaultStyle,
columnRenderers
},
normalizedRows[rowIndex].key
)) });
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
MergeTable,
TableBody,
TableHeader
});