@rufous/ui 0.2.105 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.cjs +52 -17
- package/dist/main.css +27 -2
- package/dist/main.d.cts +28 -1
- package/dist/main.d.ts +28 -1
- package/dist/main.js +52 -17
- package/package.json +2 -2
package/dist/main.cjs
CHANGED
|
@@ -3616,15 +3616,27 @@ var DateField = ({
|
|
|
3616
3616
|
const spaceBelow = window.innerHeight - rect.bottom - GAP2;
|
|
3617
3617
|
const spaceAbove = rect.top - GAP2;
|
|
3618
3618
|
const useDropUp = spaceBelow < PICKER_H && spaceAbove > spaceBelow;
|
|
3619
|
-
|
|
3619
|
+
if (useDropUp) {
|
|
3620
|
+
return {
|
|
3621
|
+
position: "fixed",
|
|
3622
|
+
left: rect.left,
|
|
3623
|
+
// bottom anchors picker's bottom edge exactly to field top — no gap regardless of actual height
|
|
3624
|
+
bottom: window.innerHeight - rect.top + GAP2,
|
|
3625
|
+
// prevent going above viewport
|
|
3626
|
+
maxHeight: rect.top - GAP2 - 4,
|
|
3627
|
+
overflowY: "auto",
|
|
3628
|
+
zIndex: 99999,
|
|
3629
|
+
animationName: "rf-date-picker-appear-up",
|
|
3630
|
+
transformOrigin: "bottom left"
|
|
3631
|
+
};
|
|
3632
|
+
}
|
|
3620
3633
|
return {
|
|
3621
3634
|
position: "fixed",
|
|
3622
3635
|
left: rect.left,
|
|
3623
|
-
top,
|
|
3636
|
+
top: rect.bottom + GAP2,
|
|
3624
3637
|
zIndex: 99999,
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
transformOrigin: useDropUp ? "bottom left" : "top left"
|
|
3638
|
+
animationName: "rf-date-picker-appear",
|
|
3639
|
+
transformOrigin: "top left"
|
|
3628
3640
|
};
|
|
3629
3641
|
})(),
|
|
3630
3642
|
onMouseDown: (e) => e.preventDefault()
|
|
@@ -4427,6 +4439,12 @@ function DataGrid({
|
|
|
4427
4439
|
columns: initialColumnsProp,
|
|
4428
4440
|
data,
|
|
4429
4441
|
actions,
|
|
4442
|
+
loading = false,
|
|
4443
|
+
pagination = true,
|
|
4444
|
+
paginationMode = "client",
|
|
4445
|
+
rowCount,
|
|
4446
|
+
paginationModel,
|
|
4447
|
+
onPaginationModelChange,
|
|
4430
4448
|
pageSize: initialPageSize = 10,
|
|
4431
4449
|
pageSizeOptions = [5, 10, 25, 50],
|
|
4432
4450
|
title,
|
|
@@ -4467,6 +4485,23 @@ function DataGrid({
|
|
|
4467
4485
|
const [sortDirection, setSortDirection] = (0, import_react23.useState)(null);
|
|
4468
4486
|
const [filterText, setFilterText] = (0, import_react23.useState)("");
|
|
4469
4487
|
const [currentPage, setCurrentPage] = (0, import_react23.useState)(1);
|
|
4488
|
+
const activePage = paginationModel ? paginationModel.page + 1 : currentPage;
|
|
4489
|
+
const activePageSize = paginationModel ? paginationModel.pageSize : pageSize;
|
|
4490
|
+
const handlePageChange = (newPage) => {
|
|
4491
|
+
if (onPaginationModelChange) {
|
|
4492
|
+
onPaginationModelChange({ page: newPage - 1, pageSize: activePageSize });
|
|
4493
|
+
} else {
|
|
4494
|
+
setCurrentPage(newPage);
|
|
4495
|
+
}
|
|
4496
|
+
};
|
|
4497
|
+
const handlePageSizeChange = (newSize) => {
|
|
4498
|
+
if (onPaginationModelChange) {
|
|
4499
|
+
onPaginationModelChange({ page: 0, pageSize: newSize });
|
|
4500
|
+
} else {
|
|
4501
|
+
setPageSize(newSize);
|
|
4502
|
+
setCurrentPage(1);
|
|
4503
|
+
}
|
|
4504
|
+
};
|
|
4470
4505
|
const [resizingColumn, setResizingColumn] = (0, import_react23.useState)(null);
|
|
4471
4506
|
const [startX, setStartX] = (0, import_react23.useState)(0);
|
|
4472
4507
|
const [startWidth, setStartWidth] = (0, import_react23.useState)(0);
|
|
@@ -4699,11 +4734,14 @@ function DataGrid({
|
|
|
4699
4734
|
return 0;
|
|
4700
4735
|
});
|
|
4701
4736
|
}, [filteredData, sortField, sortDirection, resolvedColumns]);
|
|
4702
|
-
const
|
|
4737
|
+
const isServer = paginationMode === "server";
|
|
4738
|
+
const totalRows = isServer ? rowCount ?? data.length : filteredData.length;
|
|
4739
|
+
const totalPages = Math.max(1, Math.ceil(totalRows / activePageSize));
|
|
4703
4740
|
const paginatedData = (0, import_react23.useMemo)(() => {
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4741
|
+
if (isServer) return data;
|
|
4742
|
+
const start = (activePage - 1) * activePageSize;
|
|
4743
|
+
return sortedData.slice(start, start + activePageSize);
|
|
4744
|
+
}, [isServer, data, sortedData, activePage, activePageSize]);
|
|
4707
4745
|
const handleExport = () => {
|
|
4708
4746
|
const exportableCols = resolvedColumns.filter((c) => !c.hidden && c.isExportable !== false);
|
|
4709
4747
|
const headers = exportableCols.map((c) => c.headerName).join(",");
|
|
@@ -4798,7 +4836,7 @@ function DataGrid({
|
|
|
4798
4836
|
onClick: () => setShowManageColumns(true)
|
|
4799
4837
|
},
|
|
4800
4838
|
/* @__PURE__ */ import_react23.default.createElement(import_lucide_react2.Columns, { size: 16 })
|
|
4801
|
-
)), /* @__PURE__ */ import_react23.default.createElement("button", { className: "dg-action-btn", onClick: handleExport }, /* @__PURE__ */ import_react23.default.createElement(import_lucide_react2.Download, { size: 14 }), " Export CSV"), headerActions && /* @__PURE__ */ import_react23.default.createElement("div", { className: `dg-header-slot ${alignClass(headerActions.align)}` }, headerActions.content))), /* @__PURE__ */ import_react23.default.createElement("div", { className: `dg-toolbar ${alignClass(toolbarContent?.align)}` }, toolbarContent?.content || ""), /* @__PURE__ */ import_react23.default.createElement("div", { className: `dg-table-wrap${paginatedData.length === 0 ? " dg-table-wrap--empty" : ""}` }, /* @__PURE__ */ import_react23.default.createElement("table", { className: "dg-table" }, /* @__PURE__ */ import_react23.default.createElement("thead", null, /* @__PURE__ */ import_react23.default.createElement("tr", null, visibleColumns.map((col, idx) => {
|
|
4839
|
+
)), /* @__PURE__ */ import_react23.default.createElement("button", { className: "dg-action-btn", onClick: handleExport }, /* @__PURE__ */ import_react23.default.createElement(import_lucide_react2.Download, { size: 14 }), " Export CSV"), headerActions && /* @__PURE__ */ import_react23.default.createElement("div", { className: `dg-header-slot ${alignClass(headerActions.align)}` }, headerActions.content))), /* @__PURE__ */ import_react23.default.createElement("div", { className: `dg-toolbar ${alignClass(toolbarContent?.align)}` }, toolbarContent?.content || ""), /* @__PURE__ */ import_react23.default.createElement("div", { className: `dg-table-wrap${paginatedData.length === 0 && !loading ? " dg-table-wrap--empty" : ""}` }, loading && /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-loading-overlay" }, /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-loading-spinner" })), /* @__PURE__ */ import_react23.default.createElement("table", { className: "dg-table" }, /* @__PURE__ */ import_react23.default.createElement("thead", null, /* @__PURE__ */ import_react23.default.createElement("tr", null, visibleColumns.map((col, idx) => {
|
|
4802
4840
|
const colField = String(col.field);
|
|
4803
4841
|
const width = columnWidths[colField] || 200;
|
|
4804
4842
|
const leftOffset = getLeftOffset(col, idx);
|
|
@@ -4912,17 +4950,14 @@ function DataGrid({
|
|
|
4912
4950
|
},
|
|
4913
4951
|
action.icon
|
|
4914
4952
|
)))));
|
|
4915
|
-
})()))))), paginatedData.length === 0 && /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-empty-state" }, /* @__PURE__ */ import_react23.default.createElement("svg", { className: "dg-empty-icon", viewBox: "0 0 200 160", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ import_react23.default.createElement("rect", { x: "20", y: "30", width: "160", height: "100", rx: "8", fill: "var(--hover-color)", stroke: "var(--border-color)", strokeWidth: "1.5" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "20", y: "30", width: "160", height: "28", rx: "8", fill: "var(--border-color)", opacity: "0.5" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "20", y: "50", width: "160", height: "8", rx: "0", fill: "var(--border-color)", opacity: "0.5" }), /* @__PURE__ */ import_react23.default.createElement("line", { x1: "72", y1: "30", x2: "72", y2: "130", stroke: "var(--border-color)", strokeWidth: "1" }), /* @__PURE__ */ import_react23.default.createElement("line", { x1: "128", y1: "30", x2: "128", y2: "130", stroke: "var(--border-color)", strokeWidth: "1" }), /* @__PURE__ */ import_react23.default.createElement("line", { x1: "20", y1: "78", x2: "180", y2: "78", stroke: "var(--border-color)", strokeWidth: "1" }), /* @__PURE__ */ import_react23.default.createElement("line", { x1: "20", y1: "104", x2: "180", y2: "104", stroke: "var(--border-color)", strokeWidth: "1" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "32", y: "87", width: "28", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.4" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "84", y: "87", width: "28", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.4" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "140", y: "87", width: "28", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.4" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "32", y: "113", width: "20", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.3" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "84", y: "113", width: "32", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.3" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "140", y: "113", width: "20", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.3" }), /* @__PURE__ */ import_react23.default.createElement("circle", { cx: "148", cy: "108", r: "26", fill: "var(--surface-color)", stroke: "var(--border-color)", strokeWidth: "1.5" }), /* @__PURE__ */ import_react23.default.createElement("circle", { cx: "145", cy: "105", r: "10", stroke: "var(--text-secondary)", strokeWidth: "2.5", opacity: "0.5" }), /* @__PURE__ */ import_react23.default.createElement("line", { x1: "152", y1: "113", x2: "161", y2: "122", stroke: "var(--text-secondary)", strokeWidth: "2.5", strokeLinecap: "round", opacity: "0.5" }), /* @__PURE__ */ import_react23.default.createElement("line", { x1: "141", y1: "101", x2: "149", y2: "109", stroke: "var(--text-secondary)", strokeWidth: "2", strokeLinecap: "round", opacity: "0.5" }), /* @__PURE__ */ import_react23.default.createElement("line", { x1: "149", y1: "101", x2: "141", y2: "109", stroke: "var(--text-secondary)", strokeWidth: "2", strokeLinecap: "round", opacity: "0.5" })), /* @__PURE__ */ import_react23.default.createElement("p", { className: "dg-empty-title" }, "No data found"), /* @__PURE__ */ import_react23.default.createElement("p", { className: "dg-empty-subtitle" }, filterText || hasActiveFilters ? "Try adjusting your search or filters" : "No records to display"))), /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-pagination" }, /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-page-info" }, /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-per-page" }, /* @__PURE__ */ import_react23.default.createElement("span", null, "Rows per page:"), /* @__PURE__ */ import_react23.default.createElement(
|
|
4953
|
+
})()))))), paginatedData.length === 0 && /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-empty-state" }, /* @__PURE__ */ import_react23.default.createElement("svg", { className: "dg-empty-icon", viewBox: "0 0 200 160", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ import_react23.default.createElement("rect", { x: "20", y: "30", width: "160", height: "100", rx: "8", fill: "var(--hover-color)", stroke: "var(--border-color)", strokeWidth: "1.5" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "20", y: "30", width: "160", height: "28", rx: "8", fill: "var(--border-color)", opacity: "0.5" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "20", y: "50", width: "160", height: "8", rx: "0", fill: "var(--border-color)", opacity: "0.5" }), /* @__PURE__ */ import_react23.default.createElement("line", { x1: "72", y1: "30", x2: "72", y2: "130", stroke: "var(--border-color)", strokeWidth: "1" }), /* @__PURE__ */ import_react23.default.createElement("line", { x1: "128", y1: "30", x2: "128", y2: "130", stroke: "var(--border-color)", strokeWidth: "1" }), /* @__PURE__ */ import_react23.default.createElement("line", { x1: "20", y1: "78", x2: "180", y2: "78", stroke: "var(--border-color)", strokeWidth: "1" }), /* @__PURE__ */ import_react23.default.createElement("line", { x1: "20", y1: "104", x2: "180", y2: "104", stroke: "var(--border-color)", strokeWidth: "1" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "32", y: "87", width: "28", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.4" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "84", y: "87", width: "28", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.4" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "140", y: "87", width: "28", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.4" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "32", y: "113", width: "20", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.3" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "84", y: "113", width: "32", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.3" }), /* @__PURE__ */ import_react23.default.createElement("rect", { x: "140", y: "113", width: "20", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.3" }), /* @__PURE__ */ import_react23.default.createElement("circle", { cx: "148", cy: "108", r: "26", fill: "var(--surface-color)", stroke: "var(--border-color)", strokeWidth: "1.5" }), /* @__PURE__ */ import_react23.default.createElement("circle", { cx: "145", cy: "105", r: "10", stroke: "var(--text-secondary)", strokeWidth: "2.5", opacity: "0.5" }), /* @__PURE__ */ import_react23.default.createElement("line", { x1: "152", y1: "113", x2: "161", y2: "122", stroke: "var(--text-secondary)", strokeWidth: "2.5", strokeLinecap: "round", opacity: "0.5" }), /* @__PURE__ */ import_react23.default.createElement("line", { x1: "141", y1: "101", x2: "149", y2: "109", stroke: "var(--text-secondary)", strokeWidth: "2", strokeLinecap: "round", opacity: "0.5" }), /* @__PURE__ */ import_react23.default.createElement("line", { x1: "149", y1: "101", x2: "141", y2: "109", stroke: "var(--text-secondary)", strokeWidth: "2", strokeLinecap: "round", opacity: "0.5" })), /* @__PURE__ */ import_react23.default.createElement("p", { className: "dg-empty-title" }, "No data found"), /* @__PURE__ */ import_react23.default.createElement("p", { className: "dg-empty-subtitle" }, filterText || hasActiveFilters ? "Try adjusting your search or filters" : "No records to display"))), pagination && /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-pagination" }, /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-page-info" }, /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-per-page" }, /* @__PURE__ */ import_react23.default.createElement("span", null, "Rows per page:"), /* @__PURE__ */ import_react23.default.createElement(
|
|
4916
4954
|
"select",
|
|
4917
4955
|
{
|
|
4918
|
-
value:
|
|
4919
|
-
onChange: (e) =>
|
|
4920
|
-
setPageSize(Number(e.target.value));
|
|
4921
|
-
setCurrentPage(1);
|
|
4922
|
-
}
|
|
4956
|
+
value: activePageSize,
|
|
4957
|
+
onChange: (e) => handlePageSizeChange(Number(e.target.value))
|
|
4923
4958
|
},
|
|
4924
4959
|
pageSizeOptions.map((o) => /* @__PURE__ */ import_react23.default.createElement("option", { key: o, value: o }, o))
|
|
4925
|
-
)), /* @__PURE__ */ import_react23.default.createElement("span", null, (
|
|
4960
|
+
)), /* @__PURE__ */ import_react23.default.createElement("span", null, (activePage - 1) * activePageSize + 1, "\u2013", Math.min(activePage * activePageSize, totalRows), " of ", totalRows)), /* @__PURE__ */ import_react23.default.createElement("div", { className: "dg-page-nav" }, /* @__PURE__ */ import_react23.default.createElement("button", { className: "dg-page-btn", disabled: activePage === 1, onClick: () => handlePageChange(activePage - 1) }, /* @__PURE__ */ import_react23.default.createElement(import_lucide_react2.ChevronLeft, { size: 15 })), /* @__PURE__ */ import_react23.default.createElement("span", { className: "dg-page-fraction" }, activePage, " / ", totalPages), /* @__PURE__ */ import_react23.default.createElement("button", { className: "dg-page-btn", disabled: activePage === totalPages, onClick: () => handlePageChange(activePage + 1) }, /* @__PURE__ */ import_react23.default.createElement(import_lucide_react2.ChevronRight, { size: 15 })))), activeMenu && /* @__PURE__ */ import_react23.default.createElement(
|
|
4926
4961
|
"div",
|
|
4927
4962
|
{
|
|
4928
4963
|
ref: menuRef,
|
package/dist/main.css
CHANGED
|
@@ -443,6 +443,7 @@
|
|
|
443
443
|
overflow-x: auto;
|
|
444
444
|
overflow-y: auto;
|
|
445
445
|
flex: 1;
|
|
446
|
+
position: relative;
|
|
446
447
|
}
|
|
447
448
|
.dg-table-wrap--empty {
|
|
448
449
|
display: flex;
|
|
@@ -936,6 +937,30 @@
|
|
|
936
937
|
--tf-hover-border-color: var(--text-secondary);
|
|
937
938
|
--tf-primary-color: var(--primary-color);
|
|
938
939
|
}
|
|
940
|
+
.dg-loading-overlay {
|
|
941
|
+
position: absolute;
|
|
942
|
+
inset: 0;
|
|
943
|
+
top: 41px;
|
|
944
|
+
background: rgba(255, 255, 255, 0.65);
|
|
945
|
+
display: flex;
|
|
946
|
+
align-items: center;
|
|
947
|
+
justify-content: center;
|
|
948
|
+
z-index: 20;
|
|
949
|
+
pointer-events: all;
|
|
950
|
+
}
|
|
951
|
+
.dg-loading-spinner {
|
|
952
|
+
width: 32px;
|
|
953
|
+
height: 32px;
|
|
954
|
+
border: 3px solid rgba(0, 0, 0, 0.1);
|
|
955
|
+
border-top-color: var(--primary-color, #f15b24);
|
|
956
|
+
border-radius: 50%;
|
|
957
|
+
animation: dg-spin 0.7s linear infinite;
|
|
958
|
+
}
|
|
959
|
+
@keyframes dg-spin {
|
|
960
|
+
to {
|
|
961
|
+
transform: rotate(360deg);
|
|
962
|
+
}
|
|
963
|
+
}
|
|
939
964
|
.dg-empty-state {
|
|
940
965
|
flex: 1;
|
|
941
966
|
display: flex;
|
|
@@ -6321,11 +6346,11 @@ pre {
|
|
|
6321
6346
|
z-index: 2;
|
|
6322
6347
|
}
|
|
6323
6348
|
.rf-text-field__adornment--start {
|
|
6324
|
-
margin-left:
|
|
6349
|
+
margin-left: 0px;
|
|
6325
6350
|
margin-right: -6px;
|
|
6326
6351
|
}
|
|
6327
6352
|
.rf-text-field__adornment--end {
|
|
6328
|
-
margin-right:
|
|
6353
|
+
margin-right: 0px;
|
|
6329
6354
|
margin-left: -6px;
|
|
6330
6355
|
}
|
|
6331
6356
|
.rf-text-field--standard .rf-text-field__adornment--start {
|
package/dist/main.d.cts
CHANGED
|
@@ -863,10 +863,37 @@ interface DataGridToolbarSlot {
|
|
|
863
863
|
content: React__default.ReactNode;
|
|
864
864
|
align?: 'left' | 'center' | 'right';
|
|
865
865
|
}
|
|
866
|
+
interface PaginationModel {
|
|
867
|
+
/** 0-indexed current page */
|
|
868
|
+
page: number;
|
|
869
|
+
pageSize: number;
|
|
870
|
+
}
|
|
866
871
|
interface DataGridProps<T> {
|
|
867
872
|
columns: Column<T>[];
|
|
868
873
|
data: T[];
|
|
869
874
|
actions?: Action<T>[] | ((item: T) => Action<T>[]);
|
|
875
|
+
/** Show a loading overlay over the table body */
|
|
876
|
+
loading?: boolean;
|
|
877
|
+
/** Show the pagination bar. Defaults to true. */
|
|
878
|
+
pagination?: boolean;
|
|
879
|
+
/**
|
|
880
|
+
* 'client' (default) — DataGrid filters, sorts, and paginates data internally.
|
|
881
|
+
* 'server' — data is already the current page; DataGrid skips client-side
|
|
882
|
+
* filtering/sorting/slicing and delegates everything to the server.
|
|
883
|
+
*/
|
|
884
|
+
paginationMode?: 'client' | 'server';
|
|
885
|
+
/**
|
|
886
|
+
* Total number of rows across all pages (server mode).
|
|
887
|
+
* Used to compute total page count when paginationMode="server".
|
|
888
|
+
*/
|
|
889
|
+
rowCount?: number;
|
|
890
|
+
/**
|
|
891
|
+
* Controlled pagination state. page is 0-indexed.
|
|
892
|
+
* When provided, the DataGrid uses these values instead of internal state.
|
|
893
|
+
*/
|
|
894
|
+
paginationModel?: PaginationModel;
|
|
895
|
+
/** Called whenever the page or pageSize changes. page is 0-indexed. */
|
|
896
|
+
onPaginationModelChange?: (model: PaginationModel) => void;
|
|
870
897
|
pageSize?: number;
|
|
871
898
|
pageSizeOptions?: number[];
|
|
872
899
|
title?: string;
|
|
@@ -886,7 +913,7 @@ interface DataGridProps<T> {
|
|
|
886
913
|
|
|
887
914
|
declare function DataGrid<T extends {
|
|
888
915
|
id: string | number;
|
|
889
|
-
}>({ columns: initialColumnsProp, data, actions, pageSize: initialPageSize, pageSizeOptions, title, className, sx, onRowDoubleClick, onCellDoubleClick, headerActions, toolbarContent, }: DataGridProps<T>): React__default.JSX.Element;
|
|
916
|
+
}>({ columns: initialColumnsProp, data, actions, loading, pagination, paginationMode, rowCount, paginationModel, onPaginationModelChange, pageSize: initialPageSize, pageSizeOptions, title, className, sx, onRowDoubleClick, onCellDoubleClick, headerActions, toolbarContent, }: DataGridProps<T>): React__default.JSX.Element;
|
|
890
917
|
|
|
891
918
|
type SelectOption = {
|
|
892
919
|
value: string | number;
|
package/dist/main.d.ts
CHANGED
|
@@ -863,10 +863,37 @@ interface DataGridToolbarSlot {
|
|
|
863
863
|
content: React__default.ReactNode;
|
|
864
864
|
align?: 'left' | 'center' | 'right';
|
|
865
865
|
}
|
|
866
|
+
interface PaginationModel {
|
|
867
|
+
/** 0-indexed current page */
|
|
868
|
+
page: number;
|
|
869
|
+
pageSize: number;
|
|
870
|
+
}
|
|
866
871
|
interface DataGridProps<T> {
|
|
867
872
|
columns: Column<T>[];
|
|
868
873
|
data: T[];
|
|
869
874
|
actions?: Action<T>[] | ((item: T) => Action<T>[]);
|
|
875
|
+
/** Show a loading overlay over the table body */
|
|
876
|
+
loading?: boolean;
|
|
877
|
+
/** Show the pagination bar. Defaults to true. */
|
|
878
|
+
pagination?: boolean;
|
|
879
|
+
/**
|
|
880
|
+
* 'client' (default) — DataGrid filters, sorts, and paginates data internally.
|
|
881
|
+
* 'server' — data is already the current page; DataGrid skips client-side
|
|
882
|
+
* filtering/sorting/slicing and delegates everything to the server.
|
|
883
|
+
*/
|
|
884
|
+
paginationMode?: 'client' | 'server';
|
|
885
|
+
/**
|
|
886
|
+
* Total number of rows across all pages (server mode).
|
|
887
|
+
* Used to compute total page count when paginationMode="server".
|
|
888
|
+
*/
|
|
889
|
+
rowCount?: number;
|
|
890
|
+
/**
|
|
891
|
+
* Controlled pagination state. page is 0-indexed.
|
|
892
|
+
* When provided, the DataGrid uses these values instead of internal state.
|
|
893
|
+
*/
|
|
894
|
+
paginationModel?: PaginationModel;
|
|
895
|
+
/** Called whenever the page or pageSize changes. page is 0-indexed. */
|
|
896
|
+
onPaginationModelChange?: (model: PaginationModel) => void;
|
|
870
897
|
pageSize?: number;
|
|
871
898
|
pageSizeOptions?: number[];
|
|
872
899
|
title?: string;
|
|
@@ -886,7 +913,7 @@ interface DataGridProps<T> {
|
|
|
886
913
|
|
|
887
914
|
declare function DataGrid<T extends {
|
|
888
915
|
id: string | number;
|
|
889
|
-
}>({ columns: initialColumnsProp, data, actions, pageSize: initialPageSize, pageSizeOptions, title, className, sx, onRowDoubleClick, onCellDoubleClick, headerActions, toolbarContent, }: DataGridProps<T>): React__default.JSX.Element;
|
|
916
|
+
}>({ columns: initialColumnsProp, data, actions, loading, pagination, paginationMode, rowCount, paginationModel, onPaginationModelChange, pageSize: initialPageSize, pageSizeOptions, title, className, sx, onRowDoubleClick, onCellDoubleClick, headerActions, toolbarContent, }: DataGridProps<T>): React__default.JSX.Element;
|
|
890
917
|
|
|
891
918
|
type SelectOption = {
|
|
892
919
|
value: string | number;
|
package/dist/main.js
CHANGED
|
@@ -3467,15 +3467,27 @@ var DateField = ({
|
|
|
3467
3467
|
const spaceBelow = window.innerHeight - rect.bottom - GAP2;
|
|
3468
3468
|
const spaceAbove = rect.top - GAP2;
|
|
3469
3469
|
const useDropUp = spaceBelow < PICKER_H && spaceAbove > spaceBelow;
|
|
3470
|
-
|
|
3470
|
+
if (useDropUp) {
|
|
3471
|
+
return {
|
|
3472
|
+
position: "fixed",
|
|
3473
|
+
left: rect.left,
|
|
3474
|
+
// bottom anchors picker's bottom edge exactly to field top — no gap regardless of actual height
|
|
3475
|
+
bottom: window.innerHeight - rect.top + GAP2,
|
|
3476
|
+
// prevent going above viewport
|
|
3477
|
+
maxHeight: rect.top - GAP2 - 4,
|
|
3478
|
+
overflowY: "auto",
|
|
3479
|
+
zIndex: 99999,
|
|
3480
|
+
animationName: "rf-date-picker-appear-up",
|
|
3481
|
+
transformOrigin: "bottom left"
|
|
3482
|
+
};
|
|
3483
|
+
}
|
|
3471
3484
|
return {
|
|
3472
3485
|
position: "fixed",
|
|
3473
3486
|
left: rect.left,
|
|
3474
|
-
top,
|
|
3487
|
+
top: rect.bottom + GAP2,
|
|
3475
3488
|
zIndex: 99999,
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
transformOrigin: useDropUp ? "bottom left" : "top left"
|
|
3489
|
+
animationName: "rf-date-picker-appear",
|
|
3490
|
+
transformOrigin: "top left"
|
|
3479
3491
|
};
|
|
3480
3492
|
})(),
|
|
3481
3493
|
onMouseDown: (e) => e.preventDefault()
|
|
@@ -4299,6 +4311,12 @@ function DataGrid({
|
|
|
4299
4311
|
columns: initialColumnsProp,
|
|
4300
4312
|
data,
|
|
4301
4313
|
actions,
|
|
4314
|
+
loading = false,
|
|
4315
|
+
pagination = true,
|
|
4316
|
+
paginationMode = "client",
|
|
4317
|
+
rowCount,
|
|
4318
|
+
paginationModel,
|
|
4319
|
+
onPaginationModelChange,
|
|
4302
4320
|
pageSize: initialPageSize = 10,
|
|
4303
4321
|
pageSizeOptions = [5, 10, 25, 50],
|
|
4304
4322
|
title,
|
|
@@ -4339,6 +4357,23 @@ function DataGrid({
|
|
|
4339
4357
|
const [sortDirection, setSortDirection] = useState9(null);
|
|
4340
4358
|
const [filterText, setFilterText] = useState9("");
|
|
4341
4359
|
const [currentPage, setCurrentPage] = useState9(1);
|
|
4360
|
+
const activePage = paginationModel ? paginationModel.page + 1 : currentPage;
|
|
4361
|
+
const activePageSize = paginationModel ? paginationModel.pageSize : pageSize;
|
|
4362
|
+
const handlePageChange = (newPage) => {
|
|
4363
|
+
if (onPaginationModelChange) {
|
|
4364
|
+
onPaginationModelChange({ page: newPage - 1, pageSize: activePageSize });
|
|
4365
|
+
} else {
|
|
4366
|
+
setCurrentPage(newPage);
|
|
4367
|
+
}
|
|
4368
|
+
};
|
|
4369
|
+
const handlePageSizeChange = (newSize) => {
|
|
4370
|
+
if (onPaginationModelChange) {
|
|
4371
|
+
onPaginationModelChange({ page: 0, pageSize: newSize });
|
|
4372
|
+
} else {
|
|
4373
|
+
setPageSize(newSize);
|
|
4374
|
+
setCurrentPage(1);
|
|
4375
|
+
}
|
|
4376
|
+
};
|
|
4342
4377
|
const [resizingColumn, setResizingColumn] = useState9(null);
|
|
4343
4378
|
const [startX, setStartX] = useState9(0);
|
|
4344
4379
|
const [startWidth, setStartWidth] = useState9(0);
|
|
@@ -4571,11 +4606,14 @@ function DataGrid({
|
|
|
4571
4606
|
return 0;
|
|
4572
4607
|
});
|
|
4573
4608
|
}, [filteredData, sortField, sortDirection, resolvedColumns]);
|
|
4574
|
-
const
|
|
4609
|
+
const isServer = paginationMode === "server";
|
|
4610
|
+
const totalRows = isServer ? rowCount ?? data.length : filteredData.length;
|
|
4611
|
+
const totalPages = Math.max(1, Math.ceil(totalRows / activePageSize));
|
|
4575
4612
|
const paginatedData = useMemo2(() => {
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4613
|
+
if (isServer) return data;
|
|
4614
|
+
const start = (activePage - 1) * activePageSize;
|
|
4615
|
+
return sortedData.slice(start, start + activePageSize);
|
|
4616
|
+
}, [isServer, data, sortedData, activePage, activePageSize]);
|
|
4579
4617
|
const handleExport = () => {
|
|
4580
4618
|
const exportableCols = resolvedColumns.filter((c) => !c.hidden && c.isExportable !== false);
|
|
4581
4619
|
const headers = exportableCols.map((c) => c.headerName).join(",");
|
|
@@ -4670,7 +4708,7 @@ function DataGrid({
|
|
|
4670
4708
|
onClick: () => setShowManageColumns(true)
|
|
4671
4709
|
},
|
|
4672
4710
|
/* @__PURE__ */ React75.createElement(Columns, { size: 16 })
|
|
4673
|
-
)), /* @__PURE__ */ React75.createElement("button", { className: "dg-action-btn", onClick: handleExport }, /* @__PURE__ */ React75.createElement(Download, { size: 14 }), " Export CSV"), headerActions && /* @__PURE__ */ React75.createElement("div", { className: `dg-header-slot ${alignClass(headerActions.align)}` }, headerActions.content))), /* @__PURE__ */ React75.createElement("div", { className: `dg-toolbar ${alignClass(toolbarContent?.align)}` }, toolbarContent?.content || ""), /* @__PURE__ */ React75.createElement("div", { className: `dg-table-wrap${paginatedData.length === 0 ? " dg-table-wrap--empty" : ""}` }, /* @__PURE__ */ React75.createElement("table", { className: "dg-table" }, /* @__PURE__ */ React75.createElement("thead", null, /* @__PURE__ */ React75.createElement("tr", null, visibleColumns.map((col, idx) => {
|
|
4711
|
+
)), /* @__PURE__ */ React75.createElement("button", { className: "dg-action-btn", onClick: handleExport }, /* @__PURE__ */ React75.createElement(Download, { size: 14 }), " Export CSV"), headerActions && /* @__PURE__ */ React75.createElement("div", { className: `dg-header-slot ${alignClass(headerActions.align)}` }, headerActions.content))), /* @__PURE__ */ React75.createElement("div", { className: `dg-toolbar ${alignClass(toolbarContent?.align)}` }, toolbarContent?.content || ""), /* @__PURE__ */ React75.createElement("div", { className: `dg-table-wrap${paginatedData.length === 0 && !loading ? " dg-table-wrap--empty" : ""}` }, loading && /* @__PURE__ */ React75.createElement("div", { className: "dg-loading-overlay" }, /* @__PURE__ */ React75.createElement("div", { className: "dg-loading-spinner" })), /* @__PURE__ */ React75.createElement("table", { className: "dg-table" }, /* @__PURE__ */ React75.createElement("thead", null, /* @__PURE__ */ React75.createElement("tr", null, visibleColumns.map((col, idx) => {
|
|
4674
4712
|
const colField = String(col.field);
|
|
4675
4713
|
const width = columnWidths[colField] || 200;
|
|
4676
4714
|
const leftOffset = getLeftOffset(col, idx);
|
|
@@ -4784,17 +4822,14 @@ function DataGrid({
|
|
|
4784
4822
|
},
|
|
4785
4823
|
action.icon
|
|
4786
4824
|
)))));
|
|
4787
|
-
})()))))), paginatedData.length === 0 && /* @__PURE__ */ React75.createElement("div", { className: "dg-empty-state" }, /* @__PURE__ */ React75.createElement("svg", { className: "dg-empty-icon", viewBox: "0 0 200 160", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React75.createElement("rect", { x: "20", y: "30", width: "160", height: "100", rx: "8", fill: "var(--hover-color)", stroke: "var(--border-color)", strokeWidth: "1.5" }), /* @__PURE__ */ React75.createElement("rect", { x: "20", y: "30", width: "160", height: "28", rx: "8", fill: "var(--border-color)", opacity: "0.5" }), /* @__PURE__ */ React75.createElement("rect", { x: "20", y: "50", width: "160", height: "8", rx: "0", fill: "var(--border-color)", opacity: "0.5" }), /* @__PURE__ */ React75.createElement("line", { x1: "72", y1: "30", x2: "72", y2: "130", stroke: "var(--border-color)", strokeWidth: "1" }), /* @__PURE__ */ React75.createElement("line", { x1: "128", y1: "30", x2: "128", y2: "130", stroke: "var(--border-color)", strokeWidth: "1" }), /* @__PURE__ */ React75.createElement("line", { x1: "20", y1: "78", x2: "180", y2: "78", stroke: "var(--border-color)", strokeWidth: "1" }), /* @__PURE__ */ React75.createElement("line", { x1: "20", y1: "104", x2: "180", y2: "104", stroke: "var(--border-color)", strokeWidth: "1" }), /* @__PURE__ */ React75.createElement("rect", { x: "32", y: "87", width: "28", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.4" }), /* @__PURE__ */ React75.createElement("rect", { x: "84", y: "87", width: "28", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.4" }), /* @__PURE__ */ React75.createElement("rect", { x: "140", y: "87", width: "28", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.4" }), /* @__PURE__ */ React75.createElement("rect", { x: "32", y: "113", width: "20", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.3" }), /* @__PURE__ */ React75.createElement("rect", { x: "84", y: "113", width: "32", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.3" }), /* @__PURE__ */ React75.createElement("rect", { x: "140", y: "113", width: "20", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.3" }), /* @__PURE__ */ React75.createElement("circle", { cx: "148", cy: "108", r: "26", fill: "var(--surface-color)", stroke: "var(--border-color)", strokeWidth: "1.5" }), /* @__PURE__ */ React75.createElement("circle", { cx: "145", cy: "105", r: "10", stroke: "var(--text-secondary)", strokeWidth: "2.5", opacity: "0.5" }), /* @__PURE__ */ React75.createElement("line", { x1: "152", y1: "113", x2: "161", y2: "122", stroke: "var(--text-secondary)", strokeWidth: "2.5", strokeLinecap: "round", opacity: "0.5" }), /* @__PURE__ */ React75.createElement("line", { x1: "141", y1: "101", x2: "149", y2: "109", stroke: "var(--text-secondary)", strokeWidth: "2", strokeLinecap: "round", opacity: "0.5" }), /* @__PURE__ */ React75.createElement("line", { x1: "149", y1: "101", x2: "141", y2: "109", stroke: "var(--text-secondary)", strokeWidth: "2", strokeLinecap: "round", opacity: "0.5" })), /* @__PURE__ */ React75.createElement("p", { className: "dg-empty-title" }, "No data found"), /* @__PURE__ */ React75.createElement("p", { className: "dg-empty-subtitle" }, filterText || hasActiveFilters ? "Try adjusting your search or filters" : "No records to display"))), /* @__PURE__ */ React75.createElement("div", { className: "dg-pagination" }, /* @__PURE__ */ React75.createElement("div", { className: "dg-page-info" }, /* @__PURE__ */ React75.createElement("div", { className: "dg-per-page" }, /* @__PURE__ */ React75.createElement("span", null, "Rows per page:"), /* @__PURE__ */ React75.createElement(
|
|
4825
|
+
})()))))), paginatedData.length === 0 && /* @__PURE__ */ React75.createElement("div", { className: "dg-empty-state" }, /* @__PURE__ */ React75.createElement("svg", { className: "dg-empty-icon", viewBox: "0 0 200 160", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, /* @__PURE__ */ React75.createElement("rect", { x: "20", y: "30", width: "160", height: "100", rx: "8", fill: "var(--hover-color)", stroke: "var(--border-color)", strokeWidth: "1.5" }), /* @__PURE__ */ React75.createElement("rect", { x: "20", y: "30", width: "160", height: "28", rx: "8", fill: "var(--border-color)", opacity: "0.5" }), /* @__PURE__ */ React75.createElement("rect", { x: "20", y: "50", width: "160", height: "8", rx: "0", fill: "var(--border-color)", opacity: "0.5" }), /* @__PURE__ */ React75.createElement("line", { x1: "72", y1: "30", x2: "72", y2: "130", stroke: "var(--border-color)", strokeWidth: "1" }), /* @__PURE__ */ React75.createElement("line", { x1: "128", y1: "30", x2: "128", y2: "130", stroke: "var(--border-color)", strokeWidth: "1" }), /* @__PURE__ */ React75.createElement("line", { x1: "20", y1: "78", x2: "180", y2: "78", stroke: "var(--border-color)", strokeWidth: "1" }), /* @__PURE__ */ React75.createElement("line", { x1: "20", y1: "104", x2: "180", y2: "104", stroke: "var(--border-color)", strokeWidth: "1" }), /* @__PURE__ */ React75.createElement("rect", { x: "32", y: "87", width: "28", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.4" }), /* @__PURE__ */ React75.createElement("rect", { x: "84", y: "87", width: "28", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.4" }), /* @__PURE__ */ React75.createElement("rect", { x: "140", y: "87", width: "28", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.4" }), /* @__PURE__ */ React75.createElement("rect", { x: "32", y: "113", width: "20", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.3" }), /* @__PURE__ */ React75.createElement("rect", { x: "84", y: "113", width: "32", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.3" }), /* @__PURE__ */ React75.createElement("rect", { x: "140", y: "113", width: "20", height: "6", rx: "3", fill: "var(--border-color)", opacity: "0.3" }), /* @__PURE__ */ React75.createElement("circle", { cx: "148", cy: "108", r: "26", fill: "var(--surface-color)", stroke: "var(--border-color)", strokeWidth: "1.5" }), /* @__PURE__ */ React75.createElement("circle", { cx: "145", cy: "105", r: "10", stroke: "var(--text-secondary)", strokeWidth: "2.5", opacity: "0.5" }), /* @__PURE__ */ React75.createElement("line", { x1: "152", y1: "113", x2: "161", y2: "122", stroke: "var(--text-secondary)", strokeWidth: "2.5", strokeLinecap: "round", opacity: "0.5" }), /* @__PURE__ */ React75.createElement("line", { x1: "141", y1: "101", x2: "149", y2: "109", stroke: "var(--text-secondary)", strokeWidth: "2", strokeLinecap: "round", opacity: "0.5" }), /* @__PURE__ */ React75.createElement("line", { x1: "149", y1: "101", x2: "141", y2: "109", stroke: "var(--text-secondary)", strokeWidth: "2", strokeLinecap: "round", opacity: "0.5" })), /* @__PURE__ */ React75.createElement("p", { className: "dg-empty-title" }, "No data found"), /* @__PURE__ */ React75.createElement("p", { className: "dg-empty-subtitle" }, filterText || hasActiveFilters ? "Try adjusting your search or filters" : "No records to display"))), pagination && /* @__PURE__ */ React75.createElement("div", { className: "dg-pagination" }, /* @__PURE__ */ React75.createElement("div", { className: "dg-page-info" }, /* @__PURE__ */ React75.createElement("div", { className: "dg-per-page" }, /* @__PURE__ */ React75.createElement("span", null, "Rows per page:"), /* @__PURE__ */ React75.createElement(
|
|
4788
4826
|
"select",
|
|
4789
4827
|
{
|
|
4790
|
-
value:
|
|
4791
|
-
onChange: (e) =>
|
|
4792
|
-
setPageSize(Number(e.target.value));
|
|
4793
|
-
setCurrentPage(1);
|
|
4794
|
-
}
|
|
4828
|
+
value: activePageSize,
|
|
4829
|
+
onChange: (e) => handlePageSizeChange(Number(e.target.value))
|
|
4795
4830
|
},
|
|
4796
4831
|
pageSizeOptions.map((o) => /* @__PURE__ */ React75.createElement("option", { key: o, value: o }, o))
|
|
4797
|
-
)), /* @__PURE__ */ React75.createElement("span", null, (
|
|
4832
|
+
)), /* @__PURE__ */ React75.createElement("span", null, (activePage - 1) * activePageSize + 1, "\u2013", Math.min(activePage * activePageSize, totalRows), " of ", totalRows)), /* @__PURE__ */ React75.createElement("div", { className: "dg-page-nav" }, /* @__PURE__ */ React75.createElement("button", { className: "dg-page-btn", disabled: activePage === 1, onClick: () => handlePageChange(activePage - 1) }, /* @__PURE__ */ React75.createElement(ChevronLeft, { size: 15 })), /* @__PURE__ */ React75.createElement("span", { className: "dg-page-fraction" }, activePage, " / ", totalPages), /* @__PURE__ */ React75.createElement("button", { className: "dg-page-btn", disabled: activePage === totalPages, onClick: () => handlePageChange(activePage + 1) }, /* @__PURE__ */ React75.createElement(ChevronRight, { size: 15 })))), activeMenu && /* @__PURE__ */ React75.createElement(
|
|
4798
4833
|
"div",
|
|
4799
4834
|
{
|
|
4800
4835
|
ref: menuRef,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rufous/ui",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Experimental: A lightweight React UI component library (Beta)",
|
|
7
7
|
"style": "./dist/main.css",
|
|
@@ -93,4 +93,4 @@
|
|
|
93
93
|
"react": "^18.0.0 || ^19.0.0",
|
|
94
94
|
"react-dom": "^18.0.0 || ^19.0.0"
|
|
95
95
|
}
|
|
96
|
-
}
|
|
96
|
+
}
|