UNPKG

@abaktiar/datagrid

Version:

A powerful, feature-rich React DataGrid component with TypeScript support, built on TanStack Table. Features customizable floating action dock, Excel/CSV export, and advanced row selection.

1,286 lines (1,285 loc) 112 kB
"use strict"; Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); const react = require("react"); const reactTable = require("@tanstack/react-table"); const XLSX = require("xlsx"); function _interopNamespaceDefault(e) { const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } }); if (e) { for (const k in e) { if (k !== "default") { const d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: () => e[k] }); } } } n.default = e; return Object.freeze(n); } const XLSX__namespace = /* @__PURE__ */ _interopNamespaceDefault(XLSX); var jsxRuntime = { exports: {} }; var reactJsxRuntime_production = {}; /** * @license React * react-jsx-runtime.production.js * * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ var hasRequiredReactJsxRuntime_production; function requireReactJsxRuntime_production() { if (hasRequiredReactJsxRuntime_production) return reactJsxRuntime_production; hasRequiredReactJsxRuntime_production = 1; var REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element"), REACT_FRAGMENT_TYPE = Symbol.for("react.fragment"); function jsxProd(type, config, maybeKey) { var key = null; void 0 !== maybeKey && (key = "" + maybeKey); void 0 !== config.key && (key = "" + config.key); if ("key" in config) { maybeKey = {}; for (var propName in config) "key" !== propName && (maybeKey[propName] = config[propName]); } else maybeKey = config; config = maybeKey.ref; return { $$typeof: REACT_ELEMENT_TYPE, type, key, ref: void 0 !== config ? config : null, props: maybeKey }; } reactJsxRuntime_production.Fragment = REACT_FRAGMENT_TYPE; reactJsxRuntime_production.jsx = jsxProd; reactJsxRuntime_production.jsxs = jsxProd; return reactJsxRuntime_production; } var hasRequiredJsxRuntime; function requireJsxRuntime() { if (hasRequiredJsxRuntime) return jsxRuntime.exports; hasRequiredJsxRuntime = 1; { jsxRuntime.exports = requireReactJsxRuntime_production(); } return jsxRuntime.exports; } var jsxRuntimeExports = requireJsxRuntime(); function TableHeader({ table, className = "", customRenderer }) { const [resizeState, setResizeState] = react.useState({ isResizing: false, startX: 0, startWidth: 0, header: null, currentWidth: 0 }); const tableRef = react.useRef(null); const resizeTimeoutRef = react.useRef(void 0); const handleResize = react.useCallback((header, newWidth) => { var _a; if (resizeTimeoutRef.current) { clearTimeout(resizeTimeoutRef.current); } const headerElement = (_a = tableRef.current) == null ? void 0 : _a.querySelector(`[data-column-id="${header.id}"]`); if (headerElement) { headerElement.style.width = `${newWidth}px`; } resizeTimeoutRef.current = window.setTimeout(() => { table.setColumnSizing((prev) => ({ ...prev, [header.column.id]: newWidth })); }, 16); }, [table]); const handleMouseMove = react.useCallback((e) => { if (!resizeState.isResizing || !resizeState.header) return; e.preventDefault(); e.stopPropagation(); const deltaX = e.clientX - resizeState.startX; const newWidth = Math.max( resizeState.header.column.columnDef.minSize ?? 50, Math.min( resizeState.header.column.columnDef.maxSize ?? 1e3, resizeState.startWidth + deltaX ) ); setResizeState((prev) => ({ ...prev, currentWidth: newWidth })); handleResize(resizeState.header, newWidth); }, [resizeState.isResizing, resizeState.header, resizeState.startX, resizeState.startWidth, handleResize]); const handleMouseUp = react.useCallback(() => { if (resizeState.isResizing) { if (resizeState.header && resizeState.currentWidth > 0) { table.setColumnSizing((prev) => ({ ...prev, [resizeState.header.column.id]: resizeState.currentWidth })); } setResizeState((prev) => ({ ...prev, isResizing: false, header: null, currentWidth: 0 })); document.body.style.userSelect = ""; document.body.style.cursor = ""; if (resizeTimeoutRef.current) { clearTimeout(resizeTimeoutRef.current); resizeTimeoutRef.current = void 0; } if (tableRef.current) { const table2 = tableRef.current.closest(".datagrid-table"); if (table2) { table2.removeAttribute("data-resizing"); } } } }, [resizeState.isResizing, resizeState.header, resizeState.currentWidth, table]); const handleTouchMove = react.useCallback((e) => { if (!resizeState.isResizing || !resizeState.header) return; e.preventDefault(); const touch = e.touches[0]; if (touch) { const deltaX = touch.clientX - resizeState.startX; const newWidth = Math.max( resizeState.header.column.columnDef.minSize ?? 50, Math.min( resizeState.header.column.columnDef.maxSize ?? 1e3, resizeState.startWidth + deltaX ) ); setResizeState((prev) => ({ ...prev, currentWidth: newWidth })); handleResize(resizeState.header, newWidth); } }, [resizeState.isResizing, resizeState.header, resizeState.startX, resizeState.startWidth, handleResize]); const handleTouchEnd = react.useCallback(() => { handleMouseUp(); }, [handleMouseUp]); react.useEffect(() => { if (resizeState.isResizing) { document.addEventListener("mousemove", handleMouseMove, { passive: false }); document.addEventListener("mouseup", handleMouseUp, { passive: false }); document.addEventListener("touchmove", handleTouchMove, { passive: false }); document.addEventListener("touchend", handleTouchEnd, { passive: false }); return () => { document.removeEventListener("mousemove", handleMouseMove); document.removeEventListener("mouseup", handleMouseUp); document.removeEventListener("touchmove", handleTouchMove); document.removeEventListener("touchend", handleTouchEnd); }; } }, [resizeState.isResizing, handleMouseMove, handleMouseUp, handleTouchMove, handleTouchEnd]); react.useEffect(() => { return () => { if (resizeTimeoutRef.current) { clearTimeout(resizeTimeoutRef.current); } }; }, []); const handleResizeStart = react.useCallback((e, header) => { var _a; e.preventDefault(); e.stopPropagation(); const clientX = "touches" in e ? (_a = e.touches[0]) == null ? void 0 : _a.clientX : e.clientX; if (typeof clientX !== "number") return; const currentWidth = header.getSize(); setResizeState({ isResizing: true, startX: clientX, startWidth: currentWidth, header, currentWidth }); document.body.style.userSelect = "none"; document.body.style.cursor = "col-resize"; if (tableRef.current) { const table2 = tableRef.current.closest(".datagrid-table"); if (table2) { table2.setAttribute("data-resizing", "true"); } } }, []); return /* @__PURE__ */ jsxRuntimeExports.jsx("thead", { ref: tableRef, className: `datagrid-header ${className}`, children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsxRuntimeExports.jsx("tr", { className: "datagrid-header-row", children: headerGroup.headers.map((header) => { var _a, _b, _c, _d, _e; const column = header.column.columnDef; const canSort = header.column.getCanSort(); const sortDirection = header.column.getIsSorted(); const isColumnResizing = resizeState.isResizing && ((_a = resizeState.header) == null ? void 0 : _a.id) === header.id; const isAnyColumnResizing = resizeState.isResizing; const headerConfig = column.headerConfig; const headerClassName = `datagrid-header-cell ${canSort ? "sortable" : ""} ${sortDirection ? `sorted-${sortDirection}` : ""} ${isColumnResizing ? "resizing" : ""} ${isAnyColumnResizing ? "table-resizing" : ""} ${(headerConfig == null ? void 0 : headerConfig.className) || ""}`; const displayWidth = isColumnResizing && resizeState.currentWidth > 0 ? resizeState.currentWidth : header.getSize(); const headerStyle = { width: displayWidth, minWidth: header.column.columnDef.minSize, maxWidth: header.column.columnDef.maxSize, textAlign: (headerConfig == null ? void 0 : headerConfig.align) || "left", ...headerConfig == null ? void 0 : headerConfig.style }; return /* @__PURE__ */ jsxRuntimeExports.jsxs( "th", { "data-column-id": header.id, className: headerClassName, style: headerStyle, onClick: canSort && !isAnyColumnResizing ? header.column.getToggleSortingHandler() : void 0, title: headerConfig == null ? void 0 : headerConfig.tooltip, "data-resizing": isColumnResizing ? "true" : "false", children: [ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "datagrid-header-content", children: customRenderer && customRenderer(column) || /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ header.isPlaceholder ? null : /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "datagrid-header-text", children: [ (headerConfig == null ? void 0 : headerConfig.icon) && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "datagrid-header-icon", style: { marginRight: "6px" }, children: headerConfig.icon }), reactTable.flexRender(header.column.columnDef.header, header.getContext()) ] }), canSort && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "datagrid-sort-indicator", children: [ sortDirection === "asc" && /* @__PURE__ */ jsxRuntimeExports.jsx( "svg", { className: "sort-asc", width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "m7 14 5-5 5 5" }) } ), sortDirection === "desc" && /* @__PURE__ */ jsxRuntimeExports.jsx( "svg", { className: "sort-desc", width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "m17 10-5 5-5-5" }) } ), !sortDirection && /* @__PURE__ */ jsxRuntimeExports.jsxs( "svg", { className: "sort-none", width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "m7 14 5-5 5 5" }), /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "m7 10 5 5 5-5" }) ] } ) ] }) ] }) }), header.column.getCanFilter() && ((_b = column.filterConfig) == null ? void 0 : _b.enabled) === true && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `datagrid-column-filter ${((_c = column.filterConfig) == null ? void 0 : _c.className) || ""}`, children: /* @__PURE__ */ jsxRuntimeExports.jsx( "input", { type: ((_d = column.filterConfig) == null ? void 0 : _d.type) || "text", value: header.column.getFilterValue() ?? "", onChange: (e) => header.column.setFilterValue(e.target.value), placeholder: ((_e = column.filterConfig) == null ? void 0 : _e.placeholder) || `Filter ${header.id}...`, className: "datagrid-filter-input", onClick: (e) => e.stopPropagation() } ) }), header.column.getCanResize() && /* @__PURE__ */ jsxRuntimeExports.jsx( "div", { className: `datagrid-column-resizer ${isColumnResizing ? "active" : ""}`, onMouseDown: (e) => handleResizeStart(e, header), onTouchStart: (e) => handleResizeStart(e, header), "aria-label": `Resize ${header.id} column`, role: "separator", tabIndex: -1, children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "datagrid-column-resizer-handle" }) } ) ] }, header.id ); }) }, headerGroup.id)) }); } function ContextMenu({ items, position, onClose, cellValue, rowData, className = "" }) { const menuRef = react.useRef(null); const [adjustedPosition, setAdjustedPosition] = react.useState(position); react.useEffect(() => { const handleClickOutside = (event) => { if (menuRef.current && !menuRef.current.contains(event.target)) { onClose(); } }; const handleEscape = (event) => { if (event.key === "Escape") { onClose(); } }; document.addEventListener("mousedown", handleClickOutside); document.addEventListener("keydown", handleEscape); return () => { document.removeEventListener("mousedown", handleClickOutside); document.removeEventListener("keydown", handleEscape); }; }, [onClose]); react.useEffect(() => { if (menuRef.current) { const menu = menuRef.current; const rect = menu.getBoundingClientRect(); const viewportWidth = window.innerWidth; const viewportHeight = window.innerHeight; let { x, y } = position; if (x + rect.width > viewportWidth) { x = viewportWidth - rect.width - 10; } if (y + rect.height > viewportHeight) { y = viewportHeight - rect.height - 10; } setAdjustedPosition({ x, y }); } }, [position]); const handleItemClick = (item) => { if (typeof item.disabled === "function" ? item.disabled(cellValue, rowData) : item.disabled) { return; } item.onClick(cellValue, rowData); onClose(); }; return /* @__PURE__ */ jsxRuntimeExports.jsx( "div", { ref: menuRef, className: `datagrid-context-menu ${className}`, style: { position: "fixed", left: adjustedPosition.x, top: adjustedPosition.y, zIndex: 9999 }, children: items.map((item, index) => { if (item.separator) { return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "datagrid-context-menu-separator" }, index); } const isDisabled = typeof item.disabled === "function" ? item.disabled(cellValue, rowData) : item.disabled; return /* @__PURE__ */ jsxRuntimeExports.jsxs( "div", { className: `datagrid-context-menu-item ${isDisabled ? "disabled" : ""} ${item.className || ""}`, onClick: () => handleItemClick(item), children: [ item.icon && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "datagrid-context-menu-icon", children: item.icon }), /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "datagrid-context-menu-label", children: item.label }) ] }, index ); }) } ); } function TableBody({ table, className = "", rowClassName = "", cellClassName = "", customCellRenderer, onRowClick, onRowDoubleClick, onCellClick }) { var _a; const rows = table.getRowModel().rows; const [contextMenu, setContextMenu] = react.useState(null); const getRowClassName = (row) => { if (typeof rowClassName === "function") { return `datagrid-body-row ${rowClassName(row)}`; } return `datagrid-body-row ${rowClassName}`; }; const getCellClassName = (cell, row, column) => { if (typeof cellClassName === "function") { return `datagrid-body-cell ${cellClassName(cell, row, column)}`; } return `datagrid-body-cell ${cellClassName}`; }; return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ /* @__PURE__ */ jsxRuntimeExports.jsxs("tbody", { className: `datagrid-body ${className}`, children: [ rows.map((row) => /* @__PURE__ */ jsxRuntimeExports.jsx( "tr", { className: getRowClassName(row.original), onClick: () => onRowClick == null ? void 0 : onRowClick(row.original), onDoubleClick: () => onRowDoubleClick == null ? void 0 : onRowDoubleClick(row.original), children: row.getVisibleCells().map((cell) => { const column = cell.column.columnDef; const cellConfig = column.cellConfig; const cellValue = cell.getValue(); let cellClassNames = getCellClassName(cellValue, row.original, column); if (cellConfig == null ? void 0 : cellConfig.className) { if (typeof cellConfig.className === "function") { cellClassNames += ` ${cellConfig.className(cellValue, row.original)}`; } else { cellClassNames += ` ${cellConfig.className}`; } } let cellStyle = { width: cell.column.getSize(), minWidth: cell.column.columnDef.minSize, maxWidth: cell.column.columnDef.maxSize, textAlign: (cellConfig == null ? void 0 : cellConfig.align) || "left" }; if (cellConfig == null ? void 0 : cellConfig.style) { if (typeof cellConfig.style === "function") { cellStyle = { ...cellStyle, ...cellConfig.style(cellValue, row.original) }; } else { cellStyle = { ...cellStyle, ...cellConfig.style }; } } let cellContent = customCellRenderer ? customCellRenderer(cellValue, row.original, column) : (cellConfig == null ? void 0 : cellConfig.format) ? cellConfig.format(cellValue, row.original) : reactTable.flexRender(cell.column.columnDef.cell, cell.getContext()); let cellTitle; if (cellConfig == null ? void 0 : cellConfig.tooltip) { if (typeof cellConfig.tooltip === "function") { cellTitle = cellConfig.tooltip(cellValue, row.original); } else if (cellConfig.tooltip === true) { cellTitle = String(cellValue); } } return /* @__PURE__ */ jsxRuntimeExports.jsx( "td", { className: cellClassNames, style: cellStyle, title: cellTitle, onClick: (e) => { e.stopPropagation(); if (cellConfig == null ? void 0 : cellConfig.onClick) { cellConfig.onClick(cellValue, row.original); } else { onCellClick == null ? void 0 : onCellClick(cellValue, row.original); } }, onDoubleClick: (e) => { e.stopPropagation(); if (cellConfig == null ? void 0 : cellConfig.onDoubleClick) { cellConfig.onDoubleClick(cellValue, row.original); } }, onContextMenu: (e) => { var _a2; e.preventDefault(); e.stopPropagation(); if (cellConfig == null ? void 0 : cellConfig.onRightClick) { cellConfig.onRightClick(cellValue, row.original, e); } if (((_a2 = cellConfig == null ? void 0 : cellConfig.contextMenu) == null ? void 0 : _a2.items) && cellConfig.contextMenu.items.length > 0) { setContextMenu({ visible: true, position: { x: e.clientX, y: e.clientY }, cellValue, rowData: row.original, column }); } }, children: /* @__PURE__ */ jsxRuntimeExports.jsx( "div", { className: `datagrid-cell-content ${(cellConfig == null ? void 0 : cellConfig.wrap) ? "wrap" : ""} ${(cellConfig == null ? void 0 : cellConfig.truncate) ? "truncate" : ""}`, children: cellContent } ) }, cell.id ); }) }, row.id )), rows.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("tr", { className: "datagrid-empty-row", children: /* @__PURE__ */ jsxRuntimeExports.jsx("td", { colSpan: table.getAllColumns().length, className: "datagrid-empty-cell", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "datagrid-empty-content", children: "No data available" }) }) }) ] }), (contextMenu == null ? void 0 : contextMenu.visible) && ((_a = contextMenu.column.cellConfig) == null ? void 0 : _a.contextMenu) && /* @__PURE__ */ jsxRuntimeExports.jsx( ContextMenu, { items: contextMenu.column.cellConfig.contextMenu.items, position: contextMenu.position, onClose: () => setContextMenu(null), cellValue: contextMenu.cellValue, rowData: contextMenu.rowData, className: contextMenu.column.cellConfig.contextMenu.className } ) ] }); } function TablePagination({ table, pageSizeOptions = [5, 10, 20, 50, 100] }) { const pageIndex = table.getState().pagination.pageIndex; const pageSize = table.getState().pagination.pageSize; const pageCount = table.getPageCount(); const isManualPagination = table.options.manualPagination; const totalRows = isManualPagination ? table.options.rowCount ?? 0 : table.getFilteredRowModel().rows.length; const startRow = totalRows > 0 ? pageIndex * pageSize + 1 : 0; const endRow = totalRows > 0 ? Math.min((pageIndex + 1) * pageSize, totalRows) : 0; const canPreviousPage = table.getCanPreviousPage(); const canNextPage = table.getCanNextPage(); return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "datagrid-pagination", children: [ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "datagrid-pagination-info", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "datagrid-pagination-text", children: [ "Showing ", startRow, " to ", endRow, " of ", totalRows, " entries" ] }) }), /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "datagrid-pagination-controls", children: [ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "datagrid-page-size", children: [ /* @__PURE__ */ jsxRuntimeExports.jsx("label", { htmlFor: "page-size-select", className: "datagrid-page-size-label", children: "Show:" }), /* @__PURE__ */ jsxRuntimeExports.jsx( "select", { id: "page-size-select", value: pageSize, onChange: (e) => table.setPageSize(Number(e.target.value)), className: "datagrid-page-size-select", children: pageSizeOptions.map((size) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: size, children: size }, size)) } ) ] }), /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "datagrid-page-navigation", children: [ /* @__PURE__ */ jsxRuntimeExports.jsx( "button", { onClick: () => table.setPageIndex(0), disabled: !canPreviousPage, className: "datagrid-page-button", title: "First page", children: /* @__PURE__ */ jsxRuntimeExports.jsxs( "svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "m11 17-5-5 5-5" }), /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "m18 17-5-5 5-5" }) ] } ) } ), /* @__PURE__ */ jsxRuntimeExports.jsx( "button", { onClick: () => table.previousPage(), disabled: !canPreviousPage, className: "datagrid-page-button", title: "Previous page", children: /* @__PURE__ */ jsxRuntimeExports.jsx( "svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "m15 18-6-6 6-6" }) } ) } ), /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "datagrid-page-info", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "datagrid-page-current", children: [ "Page ", pageIndex + 1, " of ", pageCount ] }) }), /* @__PURE__ */ jsxRuntimeExports.jsx( "button", { onClick: () => table.nextPage(), disabled: !canNextPage, className: "datagrid-page-button", title: "Next page", children: /* @__PURE__ */ jsxRuntimeExports.jsx( "svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "m9 18 6-6-6-6" }) } ) } ), /* @__PURE__ */ jsxRuntimeExports.jsx( "button", { onClick: () => table.setPageIndex(pageCount - 1), disabled: !canNextPage, className: "datagrid-page-button", title: "Last page", children: /* @__PURE__ */ jsxRuntimeExports.jsxs( "svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "m6 17 5-5-5-5" }), /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "m13 17 5-5-5-5" }) ] } ) } ) ] }), /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "datagrid-page-jump", children: [ /* @__PURE__ */ jsxRuntimeExports.jsx("label", { htmlFor: "page-jump-input", className: "datagrid-page-jump-label", children: "Go to:" }), /* @__PURE__ */ jsxRuntimeExports.jsx( "input", { id: "page-jump-input", type: "number", min: 1, max: pageCount, defaultValue: pageIndex + 1, onChange: (e) => { const page = e.target.value ? Number(e.target.value) - 1 : 0; table.setPageIndex(page); }, className: "datagrid-page-jump-input" } ) ] }) ] }) ] }); } function GlobalFilter({ value, onChange, placeholder = "Search all columns...", className = "", showIcon = true, debounceMs = 300 }) { const [localValue, setLocalValue] = react.useState(value); react.useEffect(() => { const timer = setTimeout(() => { onChange(localValue); }, debounceMs); return () => clearTimeout(timer); }, [localValue, onChange, debounceMs]); react.useEffect(() => { setLocalValue(value); }, [value]); return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `datagrid-global-filter ${className}`, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "datagrid-search-container", children: [ showIcon && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "datagrid-search-icon", children: /* @__PURE__ */ jsxRuntimeExports.jsxs( "svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "11", cy: "11", r: "8" }), /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "m21 21-4.35-4.35" }) ] } ) }), /* @__PURE__ */ jsxRuntimeExports.jsx( "input", { type: "text", value: localValue, onChange: (e) => setLocalValue(e.target.value), placeholder, className: "datagrid-search-input", style: { paddingLeft: showIcon ? "36px" : "12px" } } ) ] }) }); } function TableContextMenu({ items, position, onClose, data, className = "" }) { const menuRef = react.useRef(null); const [adjustedPosition, setAdjustedPosition] = react.useState(position); react.useEffect(() => { const handleClickOutside = (event) => { if (menuRef.current && !menuRef.current.contains(event.target)) { onClose(); } }; const handleEscape = (event) => { if (event.key === "Escape") { onClose(); } }; document.addEventListener("mousedown", handleClickOutside); document.addEventListener("keydown", handleEscape); return () => { document.removeEventListener("mousedown", handleClickOutside); document.removeEventListener("keydown", handleEscape); }; }, [onClose]); react.useEffect(() => { if (menuRef.current) { const menu = menuRef.current; const rect = menu.getBoundingClientRect(); const viewportWidth = window.innerWidth; const viewportHeight = window.innerHeight; let { x, y } = position; if (x + rect.width > viewportWidth) { x = viewportWidth - rect.width - 10; } if (y + rect.height > viewportHeight) { y = viewportHeight - rect.height - 10; } setAdjustedPosition({ x, y }); } }, [position]); const handleItemClick = (item) => { if (typeof item.disabled === "function" ? item.disabled(data) : item.disabled) { return; } item.onClick(data); onClose(); }; return /* @__PURE__ */ jsxRuntimeExports.jsx( "div", { ref: menuRef, className: `datagrid-context-menu ${className}`, style: { position: "fixed", left: adjustedPosition.x, top: adjustedPosition.y, zIndex: 9999 }, children: items.map((item, index) => { if (item.separator) { return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "datagrid-context-menu-separator" }, index); } const isDisabled = typeof item.disabled === "function" ? item.disabled(data) : item.disabled; return /* @__PURE__ */ jsxRuntimeExports.jsxs( "div", { className: `datagrid-context-menu-item ${isDisabled ? "disabled" : ""} ${item.className || ""}`, onClick: () => handleItemClick(item), children: [ item.icon && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "datagrid-context-menu-icon", children: item.icon }), /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "datagrid-context-menu-label", children: item.label }) ] }, index ); }) } ); } const FloatingActionDock = ({ selectedData, selectedRowIndices, items, className = "", position = "bottom-center", showCount = true, hideDelay = 0, onClose }) => { const [isVisible, setIsVisible] = react.useState(false); const [isLeaving, setIsLeaving] = react.useState(false); const hasSelectionRef = react.useRef(false); react.useEffect(() => { if (selectedData.length > 0) { if (!hasSelectionRef.current) { hasSelectionRef.current = true; setIsVisible(true); } setIsLeaving(false); } else if (hasSelectionRef.current && !isLeaving) { setIsLeaving(true); const timer = setTimeout(() => { setIsVisible(false); setIsLeaving(false); hasSelectionRef.current = false; if (onClose) onClose(); }, 220); return () => clearTimeout(timer); } }, [selectedData.length, onClose, isLeaving]); if (!hasSelectionRef.current && !isVisible) return null; const getPositionClasses = () => { switch (position) { case "bottom-left": return "dock-position-bottom-left"; case "bottom-right": return "dock-position-bottom-right"; case "bottom-center": return "dock-position-bottom-center"; case "top-left": return "dock-position-top-left"; case "top-right": return "dock-position-top-right"; case "top-center": return "dock-position-top-center"; default: return "dock-position-bottom-center"; } }; const handleItemClick = (item) => { if (item.disabled) { const isDisabled = typeof item.disabled === "function" ? item.disabled(selectedData, selectedRowIndices) : item.disabled; if (isDisabled) return; } item.onClick(selectedData, selectedRowIndices); }; const getVariantClasses = (variant) => { switch (variant) { case "primary": return "variant-primary"; case "danger": return "variant-danger"; case "success": return "variant-success"; default: return "variant-default"; } }; return /* @__PURE__ */ jsxRuntimeExports.jsx( "div", { className: `datagrid-floating-dock ${getPositionClasses()} ${className}`, "data-entering": selectedData.length > 0 && !hasSelectionRef.current ? "true" : "false", "data-leaving": isLeaving ? "true" : "false", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "datagrid-floating-dock-container", children: [ showCount && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "datagrid-floating-dock-badge", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [ selectedData.length, " selected" ] }) }), /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "datagrid-floating-dock-actions", children: items.map((item, index) => { if (item.separator) { return /* @__PURE__ */ jsxRuntimeExports.jsx( "div", { className: "datagrid-floating-dock-separator" }, `separator-${index}` ); } const isDisabled = item.disabled ? typeof item.disabled === "function" ? item.disabled(selectedData, selectedRowIndices) : item.disabled : false; if (item.customButton) { const CustomButton = item.customButton; return /* @__PURE__ */ jsxRuntimeExports.jsxs( CustomButton, { onClick: () => handleItemClick(item), disabled: isDisabled, className: `datagrid-floating-dock-button ${item.className || ""}`, children: [ item.icon && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "datagrid-floating-dock-button-icon", children: item.icon }), /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "datagrid-floating-dock-button-label", children: item.label }) ] }, index ); } return /* @__PURE__ */ jsxRuntimeExports.jsxs( "button", { onClick: () => handleItemClick(item), disabled: isDisabled, className: `datagrid-floating-dock-button ${getVariantClasses(item.variant)} ${item.className || ""} ${isDisabled ? "datagrid-floating-dock-button-disabled" : ""}`, title: item.label, children: [ item.icon && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "datagrid-floating-dock-button-icon", children: item.icon }), /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "datagrid-floating-dock-button-label", children: item.label }) ] }, index ); }) }) ] }) } ); }; const DataGridComponent = function DataGrid2({ data, columns, enableSorting = true, enableFiltering = true, enablePagination = true, enableRowSelection = false, enableColumnResizing = true, enableGlobalSearch = true, pageSize = 10, pageSizeOptions = [5, 10, 20, 50, 100], rowCount, enableMultiRowSelection = true, density = "comfortable", theme = "light", sorting: controlledSorting, onSortingChange, columnFilters: controlledColumnFilters, onColumnFiltersChange, pagination: controlledPagination, onPaginationChange, rowSelection: controlledRowSelection, onRowSelectionChange, globalFilter: controlledGlobalFilter, onGlobalFilterChange, searchConfig = { enabled: true, placeholder: "Search all columns...", position: "top", showIcon: true, debounceMs: 300 }, tableContextMenu, onTableRightClick, floatingActionDock, onRowClick, onRowDoubleClick, onCellClick, loading = false, loadingComponent: LoadingComponent, emptyComponent: EmptyComponent, errorComponent: _ErrorComponent, customCellRenderer, customHeaderRenderer, className = "", tableClassName = "", headerClassName = "", bodyClassName = "", rowClassName = "", cellClassName = "", // Advanced features enableInlineEditing: _enableInlineEditing = false, enableExpandableRows: _enableExpandableRows = false, enableGrouping: _enableGrouping = false, enableAggregation: _enableAggregation = false, enableExport: _enableExport = false, // Server-side operations manualSorting = false, manualFiltering = false, manualPagination = false }) { const [internalSorting, setInternalSorting] = react.useState([]); const [internalColumnFilters, setInternalColumnFilters] = react.useState([]); const [internalPagination, setInternalPagination] = react.useState({ pageIndex: 0, pageSize }); const [internalRowSelection, setInternalRowSelection] = react.useState({}); const [internalGlobalFilter, setInternalGlobalFilter] = react.useState(""); const [tableContextMenuState, setTableContextMenuState] = react.useState(null); const tableContainerRef = react.useRef(null); const [hasHorizontalOverflow, setHasHorizontalOverflow] = react.useState(false); const sorting = controlledSorting ?? internalSorting; const columnFilters = controlledColumnFilters ?? internalColumnFilters; const pagination = controlledPagination ?? internalPagination; const rowSelection = controlledRowSelection ?? internalRowSelection; const globalFilter = controlledGlobalFilter ?? internalGlobalFilter; const processedColumns = react.useMemo(() => { return columns.map((col) => ({ ...col, enableSorting: col.enableSorting ?? enableSorting, enableColumnFilter: col.enableFiltering ?? enableFiltering, enableResizing: col.enableResizing ?? enableColumnResizing, enableHiding: col.enableHiding ?? true, size: col.size ?? 150, minSize: col.minSize ?? 50, maxSize: col.maxSize ?? 500 })); }, [columns, enableSorting, enableFiltering, enableColumnResizing]); const finalColumns = react.useMemo(() => { if (!enableRowSelection) return processedColumns; const selectionColumn = { id: "select", accessorKey: "select", header: ({ table: table2 }) => /* @__PURE__ */ jsxRuntimeExports.jsx( "input", { type: "checkbox", checked: table2.getIsAllRowsSelected(), onChange: table2.getToggleAllRowsSelectedHandler(), className: "datagrid-checkbox" } ), cell: ({ row }) => /* @__PURE__ */ jsxRuntimeExports.jsx( "input", { type: "checkbox", checked: row.getIsSelected(), onChange: row.getToggleSelectedHandler(), className: "datagrid-checkbox" } ), size: 50, enableSorting: false, enableFiltering: false, enableResizing: false, enableHiding: false }; return [selectionColumn, ...processedColumns]; }, [processedColumns, enableRowSelection]); const table = reactTable.useReactTable({ data, columns: finalColumns, // Cast to ColumnDef to match TanStack Table requirements state: { sorting, columnFilters, pagination, rowSelection, globalFilter }, enableRowSelection, enableColumnResizing, columnResizeMode: "onEnd", // Use onEnd mode to prevent excessive re-renders during drag columnResizeDirection: "ltr", onSortingChange: onSortingChange || setInternalSorting, onColumnFiltersChange: onColumnFiltersChange || setInternalColumnFilters, onPaginationChange: onPaginationChange || setInternalPagination, onRowSelectionChange: onRowSelectionChange || setInternalRowSelection, onGlobalFilterChange: onGlobalFilterChange || setInternalGlobalFilter, getCoreRowModel: reactTable.getCoreRowModel(), getSortedRowModel: reactTable.getSortedRowModel(), getFilteredRowModel: reactTable.getFilteredRowModel(), getPaginationRowModel: enablePagination ? reactTable.getPaginationRowModel() : void 0, manualSorting, manualFiltering, manualPagination, autoResetPageIndex: false, rowCount: manualPagination ? rowCount : void 0 }); const containerClasses = `datagrid-container datagrid-density-${density} datagrid-theme-${theme} ${className}`; const tableClasses = `datagrid-table ${tableClassName}`; const handleTableRightClick = react.useCallback( (event) => { event.preventDefault(); if (onTableRightClick) { onTableRightClick(event); } if ((tableContextMenu == null ? void 0 : tableContextMenu.items) && tableContextMenu.items.length > 0) { setTableContextMenuState({ visible: true, position: { x: event.clientX, y: event.clientY } }); } }, [onTableRightClick, tableContextMenu] ); react.useEffect(() => { const checkOverflow = () => { if (tableContainerRef.current) { const container = tableContainerRef.current; const hasOverflow = container.scrollWidth > container.clientWidth + 5; setHasHorizontalOverflow(hasOverflow); } }; const timeoutId = setTimeout(checkOverflow, 100); window.addEventListener("resize", checkOverflow); const observer = new ResizeObserver(() => { setTimeout(checkOverflow, 50); }); if (tableContainerRef.current) { observer.observe(tableContainerRef.current); } return () => { clearTimeout(timeoutId); window.removeEventListener("resize", checkOverflow); observer.disconnect(); }; }, [data, finalColumns]); const tableContent = /* @__PURE__ */ jsxRuntimeExports.jsx( "div", { ref: tableContainerRef, className: `datagrid-table-container ${hasHorizontalOverflow ? "overflow-content" : "fits-content"}`, onContextMenu: handleTableRightClick, children: /* @__PURE__ */ jsxRuntimeExports.jsxs( "table", { className: tableClasses, "data-resizing": table.getState().columnSizingInfo.isResizingColumn ? "true" : "false", children: [ /* @__PURE__ */ jsxRuntimeExports.jsx(TableHeader, { table, className: headerClassName, customRenderer: customHeaderRenderer }), /* @__PURE__ */ jsxRuntimeExports.jsx( TableBody, { table, className: bodyClassName, rowClassName, cellClassName, customCellRenderer, onRowClick, onRowDoubleClick, onCellClick } ) ] } ) } ); const renderSearch = () => { if (!enableGlobalSearch || !(searchConfig == null ? void 0 : searchConfig.enabled) || (searchConfig == null ? void 0 : searchConfig.position) === "none") { return null; } return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `datagrid-toolbar ${(searchConfig == null ? void 0 : searchConfig.className) || ""}`, children: /* @__PURE__ */ jsxRuntimeExports.jsx( GlobalFilter, { value: globalFilter, onChange: setInternalGlobalFilter, placeholder: (searchConfig == null ? void 0 : searchConfig.placeholder) || "Search all columns...", showIcon: searchConfig == null ? void 0 : searchConfig.showIcon, debounceMs: searchConfig == null ? void 0 : searchConfig.debounceMs } ) }); }; const selectedRowIndices = Object.keys(rowSelection).filter((key) => rowSelection[key]).map(Number); const selectedData = selectedRowIndices.map((index) => data[index]).filter(Boolean); return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: containerClasses, children: [ (searchConfig == null ? void 0 : searchConfig.position) === "top" && renderSearch(), /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { position: "relative" }, children: [ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { position: "absolute", top: 0, left: 0, right: 0, height: "3px", backgroundColor: loading ? "rgba(229, 231, 235, 0.3)" : "transparent", zIndex: 1e3, overflow: "hidden", borderRadius: "0px", transition: "backgroundColor 0.2s ease-in-out", opacity: loading ? 1 : 0 }, children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { position: "absolute", top: 0, left: loading ? "-100%" : "100%", width: "100%", height: "100%", background: "linear-gradient(90deg, transparent 0%, #3b82f6 50%, transparent 100%)", animation: loading ? "line-bar-loader-slide 1.5s ease-in-out infinite" : "none", transition: "left 0.3s ease-in-out" } }) }), tableContent ] }), (searchConfig == null ? void 0 : searchConfig.position) === "bottom" && renderSearch(), enablePagination && /* @__PURE__ */ jsxRuntimeExports.jsx(TablePagination, { table, pageSizeOptions }), (tableContextMenuState == null ? void 0 : tableContextMenuState.visible) && tableContextMenu && /* @__PURE__ */ jsxRuntimeExports.jsx( TableContextMenu, { items: tableContextMenu.items, position: tableContextMenuState.position, onClose: () => setTableContextMenuState(null), data, className: tableContextMenu.className } ), (floatingActionDock == null ? void 0 : floatingActionDock.enabled) && floatingActionDock.items.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx( FloatingActionDock, { selectedData, selectedRowIndices, items: floatingActionDock.items, position: floatingActionDock.position, showCount: floatingActionDock.showCount, hideDelay: floatingActionDock.hideDelay, className: floatingActionDock.className } ) ] }); }; const DataGrid = react.memo(DataGridComponent); const createCopyValueAction = (label = "Copy Value", icon = "📋") => ({ label, icon, onClick: async (value) => { try { await navigator.clipboard.writeText(String(value)); console.log("Value copied to clipboard:", value); } catch (err) { console.error("Failed to copy value:", err); const textArea = document.createElement("textarea"); textArea.value = String(value); document.body.appendChild(textArea); textArea.select(); document.execCommand("copy"); document.body.removeChild(textArea); } } }); const createCopyRowAction = (label = "Copy Row", icon = "📄") => ({ label, icon, onClick: async (value, row) => { try { const jsonString = JSON.stringify(row, null, 2); await navigator.clipboard.writeText(jsonString); console.log("Row copied to clipboard:", row); } catch (err) { console.error("Failed to copy row:", err); } } }); const createCopyFormattedAction = (formatter, label = "Copy Formatted", icon = "📋") => ({ label, icon, onClick: async (value, row) => { try {