@schneideress/dashboardframework 20.0.29 → 20.0.31

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.
@@ -4977,29 +4977,38 @@ class RADashboardArea {
4977
4977
  this.initialWidgetCount++ : null;
4978
4978
  }
4979
4979
  /**
4980
- * Whether expanded view is active. Uses bulk action as source of truth (works in both normal browser and kiosk).
4981
- * Handles bulkActionData as string (from API) or object (from event). Falls back to URL param for edge cases (e.g. shared link).
4980
+ * Whether expanded view is active. URL `?expanded=true` is checked first; then bulk action (works in normal browser and kiosk).
4981
+ * Handles bulkActionData as string (from API) or object (from event).
4982
4982
  */
4983
4983
  isExpandedView() {
4984
- if (this.bulkActionData) {
4985
- try {
4986
- const data = typeof this.bulkActionData === 'string'
4987
- ? JSON.parse(this.bulkActionData)
4988
- : this.bulkActionData;
4989
- if (data?.allSelections?.expandedView === true)
4984
+ try {
4985
+ if (typeof window !== 'undefined' && window.location?.search) {
4986
+ const fromUrl = new URLSearchParams(window.location.search).get('expanded') === 'true';
4987
+ if (fromUrl) {
4990
4988
  return true;
4989
+ }
4991
4990
  }
4992
- catch (_) {
4993
- // ignore parse errors
4994
- }
4995
4991
  }
4996
- return typeof location !== 'undefined' && new URLSearchParams(window.location.search).get('expanded') === 'true';
4992
+ catch (_) {
4993
+ // ignore URL parse errors
4994
+ }
4995
+ if (!this.bulkActionData)
4996
+ return false;
4997
+ try {
4998
+ const data = typeof this.bulkActionData === 'string'
4999
+ ? JSON.parse(this.bulkActionData)
5000
+ : this.bulkActionData;
5001
+ return data?.allSelections?.expandedView === true;
5002
+ }
5003
+ catch (_) {
5004
+ return false;
5005
+ }
4997
5006
  }
4998
5007
  dataLoaded(widgetInfo) {
4999
5008
  setTimeout(() => {
5000
5009
  this.currentLoadingCount--;
5001
5010
  }, 100);
5002
- // For expanded view, override widget dimensions after data loads (from bulk action or URL)
5011
+ // For expanded view, override widget dimensions after data loads (from bulk action)
5003
5012
  if (this.isExpandedView() && widgetInfo && widgetInfo.widgetInstanceId) {
5004
5013
  this.overrideWidgetDimensions(widgetInfo);
5005
5014
  }
@@ -5040,49 +5049,59 @@ class RADashboardArea {
5040
5049
  }
5041
5050
  return;
5042
5051
  }
5043
- // Find the table body or grid content that contains the actual data rows
5044
- const tbody = element.querySelector('tbody');
5045
- const table = element.querySelector('table');
5052
+ // Prefer the real data table (ra-grid) so we do not pick unrelated tables when present
5053
+ const dataTable = element.querySelector('table.ra-grid')
5054
+ ?? element.querySelector('table');
5055
+ const tbody = dataTable?.querySelector('tbody')
5056
+ ?? element.querySelector('tbody');
5057
+ const table = dataTable;
5046
5058
  const wcBody = element.querySelector('.wcBody');
5047
5059
  const wc = element.querySelector('.wc');
5048
5060
  const kendoGridContent = element.querySelector('.k-grid-content');
5049
5061
  const kendoGridTable = element.querySelector('.k-grid-table');
5062
+ const addVisibleFilterHeights = (base) => {
5063
+ let h = base;
5064
+ element.querySelectorAll('.ra-widget-filter').forEach((node) => {
5065
+ const fe = node;
5066
+ if (fe.offsetHeight > 0) {
5067
+ h += fe.offsetHeight;
5068
+ }
5069
+ });
5070
+ return h;
5071
+ };
5050
5072
  // Measure the actual content height - try multiple strategies to get accurate measurement
5051
5073
  let contentHeight = 0;
5052
5074
  let measuredElement = null;
5053
- // Strategy 1: Measure table body (most accurate for row count)
5054
- if (tbody) {
5075
+ // Strategy 1: HTML table / ra-grid — use full table box (thead + tbody), not tbody only (avoids clipping header row)
5076
+ if (table && table.offsetHeight > 0) {
5077
+ measuredElement = table;
5078
+ contentHeight = addVisibleFilterHeights(table.offsetHeight);
5079
+ }
5080
+ // Strategy 2: tbody only (fallback if table not measurable)
5081
+ else if (tbody) {
5055
5082
  measuredElement = tbody;
5056
- // Count actual rows and calculate height
5057
5083
  const rows = tbody.querySelectorAll('tr');
5058
5084
  if (rows.length > 0) {
5059
- // Get height of first row to estimate
5060
- const firstRow = rows[0];
5061
- const rowHeight = firstRow.offsetHeight || 30; // Default row height if not available
5062
- // Calculate total height based on row count
5063
- const estimatedHeight = rows.length * rowHeight;
5064
- // Use scrollHeight if available, otherwise use estimated
5065
- contentHeight = Math.max(tbody.scrollHeight, estimatedHeight);
5085
+ let totalRowHeight = 0;
5086
+ rows.forEach((row) => {
5087
+ totalRowHeight += row.offsetHeight || 30;
5088
+ });
5089
+ contentHeight = addVisibleFilterHeights(totalRowHeight);
5066
5090
  }
5067
5091
  else {
5068
- contentHeight = tbody.scrollHeight;
5092
+ contentHeight = addVisibleFilterHeights(tbody.scrollHeight);
5069
5093
  }
5070
5094
  }
5071
- // Strategy 2: Measure Kendo grid content
5095
+ // Strategy 3: Measure Kendo grid content
5072
5096
  else if (kendoGridContent) {
5073
5097
  measuredElement = kendoGridContent;
5074
- contentHeight = kendoGridContent.scrollHeight;
5098
+ contentHeight = addVisibleFilterHeights(kendoGridContent.scrollHeight);
5075
5099
  // Also check the table inside
5076
5100
  if (kendoGridTable) {
5077
5101
  const tableHeight = kendoGridTable.scrollHeight;
5078
- contentHeight = Math.max(contentHeight, tableHeight);
5102
+ contentHeight = Math.max(contentHeight, addVisibleFilterHeights(tableHeight));
5079
5103
  }
5080
5104
  }
5081
- // Strategy 3: Measure table
5082
- else if (table) {
5083
- measuredElement = table;
5084
- contentHeight = table.scrollHeight;
5085
- }
5086
5105
  // Strategy 4: Measure widget content
5087
5106
  else if (wc) {
5088
5107
  measuredElement = wc;
@@ -5109,16 +5128,22 @@ class RADashboardArea {
5109
5128
  // Try offsetHeight
5110
5129
  contentHeight = Math.max(contentHeight, offsetH);
5111
5130
  }
5112
- // For tables, also try measuring all rows directly
5113
- if (tbody || table) {
5114
- const allRows = (tbody || table)?.querySelectorAll('tr') || [];
5115
- if (allRows.length > 0) {
5116
- let totalRowHeight = 0;
5117
- allRows.forEach((row) => {
5118
- totalRowHeight += row.offsetHeight || 30;
5119
- });
5120
- // Use the maximum of scrollHeight and calculated row heights
5121
- contentHeight = Math.max(contentHeight, totalRowHeight);
5131
+ // Sum of tr heights often exceeds table.offsetHeight for many rows (borders/spacing), which grows extra whitespace.
5132
+ // When the table already laid out with a real box height, trust that measurement only.
5133
+ const tableBoxHeightTrusted = !!(table && table.offsetHeight > 0);
5134
+ if (!tableBoxHeightTrusted) {
5135
+ const rowHost = table || tbody;
5136
+ if (rowHost) {
5137
+ const hostEl = rowHost;
5138
+ const allRows = hostEl.querySelectorAll('tr');
5139
+ if (allRows.length > 0) {
5140
+ let totalRowHeight = 0;
5141
+ allRows.forEach((row) => {
5142
+ totalRowHeight += row.offsetHeight || 30;
5143
+ });
5144
+ const withFilters = addVisibleFilterHeights(totalRowHeight);
5145
+ contentHeight = Math.max(contentHeight, withFilters);
5146
+ }
5122
5147
  }
5123
5148
  }
5124
5149
  }
@@ -5126,8 +5151,8 @@ class RADashboardArea {
5126
5151
  const header = element.querySelector('.wcheader');
5127
5152
  const headerHeight = header ? header.offsetHeight : 46;
5128
5153
  // Calculate total height needed (content + header + padding)
5129
- // Add sufficient padding to ensure no scrolling
5130
- const padding = 40; // Padding for margins and spacing
5154
+ // Slightly less vertical slack for tall tables (grid ceil already adds a partial row).
5155
+ const padding = contentHeight > 380 ? 28 : 36;
5131
5156
  const totalHeight = contentHeight + headerHeight + padding;
5132
5157
  // Check if content is loaded (reasonable height)
5133
5158
  // For expanded view, we expect significant content
@@ -5137,13 +5162,12 @@ class RADashboardArea {
5137
5162
  this.measureAndUpdateWidgetDimensions(gridsterItem, attempt + 1, maxAttempts);
5138
5163
  return;
5139
5164
  }
5140
- // Calculate rows needed based on grid cell height
5141
- // Use Math.ceil to ensure we have enough space (round up)
5142
- const calculatedRows = Math.ceil(totalHeight / this.gridcellHeight);
5143
- // Add a small buffer (0.5 rows) to ensure no scrolling, then round up
5144
- // This accounts for any rounding errors or minor spacing issues
5145
- const bufferedRows = calculatedRows + 0.5;
5146
- const newRows = Math.max(Math.ceil(bufferedRows), 4);
5165
+ // Calculate rows needed based on grid cell height.
5166
+ // Blend pixel + fractional cell buffer; keep a bit more for short content to avoid last-row clipping.
5167
+ const bufferPx = contentHeight > 380
5168
+ ? Math.max(12, this.gridcellHeight * 0.35)
5169
+ : Math.max(16, this.gridcellHeight * 0.5);
5170
+ const newRows = Math.max(Math.ceil((totalHeight + bufferPx) / this.gridcellHeight), 2);
5147
5171
  // For expanded view, use full available width (no spacing needed since widgets stack vertically)
5148
5172
  const maxCols = this.responsiveService.maxColsDesktop || 12;
5149
5173
  const newCols = maxCols; // Full width
@@ -5184,8 +5208,12 @@ class RADashboardArea {
5184
5208
  }
5185
5209
  if (el) {
5186
5210
  const wcBody = el.querySelector('.wcBody');
5211
+ const tableContainer = el.querySelector('.table-container');
5212
+ const verifyTable = el.querySelector('table.ra-grid')
5213
+ ?? el.querySelector('table');
5187
5214
  const tbody = el.querySelector('tbody');
5188
- const contentEl = tbody || wcBody || el;
5215
+ // ra-grid scrolls inside .table-container; measuring tbody alone can miss real overflow
5216
+ const contentEl = tableContainer || verifyTable || tbody || wcBody || el;
5189
5217
  // Check if there's scrolling (scrollHeight > clientHeight)
5190
5218
  if (contentEl && contentEl.scrollHeight > contentEl.clientHeight + 10) {
5191
5219
  // Still has scrolling, need more height
@@ -5203,6 +5231,23 @@ class RADashboardArea {
5203
5231
  setTimeout(() => this.setAreaHeight(), 100);
5204
5232
  }
5205
5233
  }
5234
+ else if (contentEl) {
5235
+ // If there is clear spare space, shrink one row to avoid oversized blank area.
5236
+ // Keep this conservative to prevent oscillation/flapping.
5237
+ const spareHeight = contentEl.clientHeight - contentEl.scrollHeight;
5238
+ const canShrink = spareHeight > (this.gridcellHeight * 0.8) && gridsterItem.rows > 2;
5239
+ if (canShrink) {
5240
+ gridsterItem.rows = gridsterItem.rows - 1;
5241
+ if (gridsterItem.widgetInfo) {
5242
+ gridsterItem.widgetInfo.height = gridsterItem.rows;
5243
+ }
5244
+ this.widgetList = [...this.widgetList];
5245
+ if (this.options && this.options.api) {
5246
+ this.options.api?.optionsChanged();
5247
+ }
5248
+ setTimeout(() => this.setAreaHeight(), 100);
5249
+ }
5250
+ }
5206
5251
  }
5207
5252
  // Continue with normal retry logic
5208
5253
  this.measureAndUpdateWidgetDimensions(gridsterItem, attempt + 1, maxAttempts);