@pagamio/frontend-commons-lib 0.8.318 → 0.8.320

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.
@@ -104,29 +104,79 @@ function CustomToolbar({ filters, appliedFilters, onFilterChange, onApply, onCle
104
104
  const PagamioTable = ({ columns, data, isLoading = false, rowCount, sorting, pagination, filtering, search, onRowClick, rowClassName, expandable = false, renderDetailPanel, toolbar, toolbarMode = 'custom', enableColumnResizing, enableColumnPinning, enableColumnOrdering, enableColumnFilters, enableHiding, enableRowSelection, enableRowActions, enableRowVirtualization, enableGrouping, enableEditing, enableDensityToggle, enableFullScreenToggle, enableClickToCopy, enableRowNumbers, enableMultiSort, enableStickyHeader, enableStickyFooter, editDisplayMode, onEditingRowSave, onEditingRowCancel, renderRowActions, renderRowActionMenuItems, positionActionsColumn, renderTopToolbarCustomActions, renderBottomToolbarCustomActions, layoutMode, defaultColumn, mantineTableOptions, }) => {
105
105
  const [expanded, setExpanded] = useState({});
106
106
  const tableRef = useRef(null);
107
+ // ── Internal max-height ─────────────────────────────────────────────
108
+ // The mantine table container is height-capped to `viewportHeight - tableTop`
109
+ // so that the table is the only scrolling element when its parent layout
110
+ // is fixed (page header + sidebar stay put). Without this, large datasets
111
+ // would push the page itself into a scroll, breaking the toolbar/header
112
+ // sticky behaviour that the rest of the app relies on.
113
+ //
114
+ // Bottom margin reserves space for the bottom toolbar (pagination, ~52px)
115
+ // plus a small gap so the table doesn't sit flush against page edges.
116
+ const TABLE_BOTTOM_MARGIN = 68;
117
+ /**
118
+ * Walk up the DOM looking for the closest scrollable ancestor (i.e. an
119
+ * element with overflow-y: auto/scroll AND scrollHeight > clientHeight).
120
+ * If we find one, the *page* is what's scrolling — we should NOT cap the
121
+ * table's height because that would create a confusing nested scroll
122
+ * (page scroll + table scroll). Returns true when the table itself
123
+ * should manage its own scroll.
124
+ */
125
+ const tableShouldOwnScroll = (el) => {
126
+ let node = el.parentElement;
127
+ while (node && node !== document.body) {
128
+ const style = window.getComputedStyle(node);
129
+ const overflowY = style.overflowY;
130
+ const isScrollable = (overflowY === 'auto' || overflowY === 'scroll') && node.scrollHeight > node.clientHeight + 1;
131
+ if (isScrollable)
132
+ return false;
133
+ node = node.parentElement;
134
+ }
135
+ return true;
136
+ };
107
137
  const applyTableHeight = (el) => {
108
138
  const mrtContainer = el.querySelector('.mrt-table-container');
109
139
  if (!mrtContainer)
110
140
  return;
141
+ // If a parent is already handling the scroll, let the table render at
142
+ // its natural height — the user gets one continuous page scroll.
143
+ if (!tableShouldOwnScroll(el)) {
144
+ mrtContainer.style.removeProperty('max-height');
145
+ return;
146
+ }
111
147
  const containerTop = mrtContainer.getBoundingClientRect().top;
112
- const offset = Math.round(containerTop + 68); // 52 MRT pagination + 16 gap
113
- mrtContainer.style.setProperty('max-height', `calc(100vh - ${offset}px)`, 'important');
148
+ const newMax = Math.round(Math.max(240, window.innerHeight - containerTop - TABLE_BOTTOM_MARGIN));
149
+ const newMaxStr = `${newMax}px`;
150
+ // Skip the write if the value hasn't changed — avoids re-layout loops
151
+ // with ResizeObserver where mutating the cell's max-height would refire
152
+ // the observer on the next frame.
153
+ if (mrtContainer.style.maxHeight === newMaxStr)
154
+ return;
155
+ mrtContainer.style.setProperty('max-height', newMaxStr, 'important');
114
156
  };
115
- // Runs after every render (no deps), always after MRT's own layout effect.
157
+ // Runs once on mount + whenever the *window* resizes. We deliberately do
158
+ // NOT run on every render or use a ResizeObserver on the document — both
159
+ // produce loop conditions: applyTableHeight mutates layout, which fires
160
+ // the observer, which calls applyTableHeight again. The window-resize
161
+ // signal is sufficient because tableTop changes during a render get
162
+ // re-applied by useLayoutEffect-on-mount when the table remounts.
116
163
  useLayoutEffect(() => {
117
164
  if (tableRef.current)
118
165
  applyTableHeight(tableRef.current);
119
- });
166
+ }, []);
120
167
  useEffect(() => {
121
168
  const el = tableRef.current;
122
169
  if (!el)
123
170
  return;
124
- const resizeObserver = new ResizeObserver(() => applyTableHeight(el));
125
- resizeObserver.observe(document.documentElement);
126
- window.addEventListener('resize', () => applyTableHeight(el));
171
+ let frame = 0;
172
+ const handler = () => {
173
+ cancelAnimationFrame(frame);
174
+ frame = requestAnimationFrame(() => applyTableHeight(el));
175
+ };
176
+ window.addEventListener('resize', handler);
127
177
  return () => {
128
- resizeObserver.disconnect();
129
- window.removeEventListener('resize', () => applyTableHeight(el));
178
+ cancelAnimationFrame(frame);
179
+ window.removeEventListener('resize', handler);
130
180
  };
131
181
  }, []);
132
182
  // Process columns (handle showHeader: false)
@@ -274,6 +324,6 @@ const PagamioTable = ({ columns, data, isLoading = false, rowCount, sorting, pag
274
324
  });
275
325
  // ── Toolbar rendering ──────────────────────────────────────────────
276
326
  const showCustomToolbar = toolbarMode === 'custom' && (!!toolbar || !!search || !!filtering);
277
- return (_jsxs("div", { children: [showCustomToolbar && (_jsx(CustomToolbar, { filters: toolbar?.filters ?? [], appliedFilters: filtering?.appliedFilters ?? {}, onFilterChange: filtering?.onFilterChange ?? (() => { }), onApply: filtering?.onApply ?? (() => { }), onClearFilters: toolbar?.onClearFilters, showClearFilters: toolbar?.showClearFilters ?? false, showApplyFilterButton: toolbar?.showApplyFilterButton ?? true, searchEnabled: !!search, searchQuery: search?.query ?? '', onSearch: search?.onChange ?? (() => { }), searchPlaceholder: search?.placeholder, exportConfig: toolbar?.export, columns: columns, data: data, addButton: toolbar?.addButton, addText: toolbar?.addText, onAdd: toolbar?.onAdd })), _jsx("div", { ref: tableRef, className: "border border-border rounded-md overflow-hidden", children: _jsx(MantineReactTable, { table: table }) })] }));
327
+ return (_jsxs("div", { children: [showCustomToolbar && (_jsx(CustomToolbar, { filters: toolbar?.filters ?? [], appliedFilters: filtering?.appliedFilters ?? {}, onFilterChange: filtering?.onFilterChange ?? (() => { }), onApply: filtering?.onApply ?? (() => { }), onClearFilters: toolbar?.onClearFilters, showClearFilters: toolbar?.showClearFilters ?? false, showApplyFilterButton: toolbar?.showApplyFilterButton ?? true, searchEnabled: !!search, searchQuery: search?.query ?? '', onSearch: search?.onChange ?? (() => { }), searchPlaceholder: search?.placeholder, exportConfig: toolbar?.export, columns: columns, data: data, addButton: toolbar?.addButton, addText: toolbar?.addText, onAdd: toolbar?.onAdd })), _jsx("div", { ref: tableRef, className: "border border-border rounded-md overflow-y-hidden", children: _jsx(MantineReactTable, { table: table }) })] }));
278
328
  };
279
329
  export default PagamioTable;
package/lib/styles.css CHANGED
@@ -3889,14 +3889,25 @@ video {
3889
3889
  text-overflow: clip !important;
3890
3890
  }
3891
3891
 
3892
- /* MRT semantic mode: the explicit `width: <n>px` on each cell prevents the
3893
- table from filling its container — leftover space becomes dead gap on the
3894
- right. Treat the inline width as a minimum hint instead by overriding it
3895
- to `auto`; the browser's table-layout: auto then distributes width based
3896
- on content + min-width while filling 100% of the table width. */
3892
+ /* MRT semantic mode column sizing.
3893
+ *
3894
+ * MRT inlines `width: calc(--col-X-size * 1px)` and
3895
+ * `min-width: max(calc(--col-X-size * 1px), 50px)` on every cell. With
3896
+ * `table-layout: auto`, those fixed values cause two problems:
3897
+ * 1. Cells can't shrink below their min-width, leaving dead gaps when
3898
+ * content is short.
3899
+ * 2. The combined widths can exceed the container, leaving a horizontal
3900
+ * gap on the right.
3901
+ *
3902
+ * We override both: cells get `width: auto` and `min-width: 0` so the
3903
+ * browser sizes columns by content, then distributes remaining row space
3904
+ * proportionally. `whiteSpace: normal` (set above) lets long content wrap
3905
+ * instead of being crushed.
3906
+ */
3897
3907
  .mantine-Table-table > thead > tr > .mantine-Table-th,
3898
3908
  .mantine-Table-table > tbody > tr > .mantine-Table-td {
3899
3909
  width: auto !important;
3910
+ min-width: 0 !important;
3900
3911
  }
3901
3912
  .file\:-ms-4::file-selector-button {
3902
3913
  margin-inline-start: -1rem;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pagamio/frontend-commons-lib",
3
3
  "description": "Pagamio library for Frontend reusable components like the form engine and table container",
4
- "version": "0.8.318",
4
+ "version": "0.8.320",
5
5
  "publishConfig": {
6
6
  "access": "public",
7
7
  "provenance": false