@mmlogic/components 0.2.0 → 0.3.1

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.
@@ -1366,7 +1366,6 @@ const MrdLayoutSection = class {
1366
1366
  this.mrdNavigate = index.createEvent(this, "mrdNavigate");
1367
1367
  this.mrdSearch = index.createEvent(this, "mrdSearch");
1368
1368
  this.mrdDownload = index.createEvent(this, "mrdDownload");
1369
- this.mrdLoadView = index.createEvent(this, "mrdLoadView");
1370
1369
  this.mrdLoadViewPage = index.createEvent(this, "mrdLoadViewPage");
1371
1370
  this.mrdLoadImage = index.createEvent(this, "mrdLoadImage");
1372
1371
  this.mrdViewAction = index.createEvent(this, "mrdViewAction");
@@ -1375,24 +1374,19 @@ const MrdLayoutSection = class {
1375
1374
  this.items = [];
1376
1375
  /** Record data object; keys are field names, _links holds relation and related-view links. */
1377
1376
  this.data = {};
1378
- /** View metadata map (ClientDashboardMetadata.views) for RELATED_VIEW and VIEW items. */
1377
+ /** Legacy: view metadata map (ClientDashboardMetadata.views). Not needed in new flat format. */
1379
1378
  this.views = {};
1380
- /** Top-level _links from ClientDashboardMetadata; used to resolve hrefs for VIEW items. */
1379
+ /** Legacy: top-level _links from ClientDashboardMetadata. Not needed in new flat format. */
1381
1380
  this.links = {};
1382
1381
  this.locale = navigator.language;
1383
1382
  this.searchQueryMap = {};
1384
1383
  this.searchResultsMap = {};
1385
1384
  this.imagePreviewUrl = null;
1386
1385
  this.imagePreviews = {};
1387
- this.activeViewMap = {};
1388
- this.viewLinksMap = {};
1389
- this.activeFiltersMap = {};
1390
1386
  this.searchTimers = {};
1391
1387
  this.handleViewLoadPage = (e, name) => {
1392
- var _a;
1393
1388
  e.stopPropagation();
1394
- const filters = (_a = this.activeFiltersMap[name]) !== null && _a !== void 0 ? _a : [];
1395
- this.mrdLoadViewPage.emit({ name, page: e.detail.page, sort: e.detail.sort, filters });
1389
+ this.mrdLoadViewPage.emit({ name, page: e.detail.page, sort: e.detail.sort, path: e.detail.path, qs: e.detail.qs });
1396
1390
  };
1397
1391
  this.handleSearchInput = (dataClass, query) => {
1398
1392
  this.searchQueryMap = Object.assign(Object.assign({}, this.searchQueryMap), { [dataClass]: query });
@@ -1409,59 +1403,29 @@ const MrdLayoutSection = class {
1409
1403
  }
1410
1404
  componentDidLoad() {
1411
1405
  setTimeout(() => {
1412
- this.emitLoadViews();
1406
+ this.initEmbeddedTables();
1413
1407
  this.emitLoadImages();
1414
1408
  }, 0);
1415
1409
  }
1416
- linksChanged(newVal) {
1417
- if (Object.keys(newVal !== null && newVal !== void 0 ? newVal : {}).length > 0) {
1418
- this.emitLoadViews();
1419
- }
1420
- }
1421
1410
  dataChanged(newVal) {
1422
- var _a;
1423
- if (newVal && Object.keys((_a = newVal === null || newVal === void 0 ? void 0 : newVal._links) !== null && _a !== void 0 ? _a : {}).length > 0) {
1424
- this.emitLoadViews();
1411
+ if (newVal && Object.keys(newVal).length > 0) {
1412
+ setTimeout(() => this.initEmbeddedTables(), 0);
1425
1413
  }
1426
1414
  }
1427
- resolveViewFilters(viewConfig) {
1428
- var _a;
1429
- return ((_a = viewConfig.filter) !== null && _a !== void 0 ? _a : []).map(f => {
1430
- var _a, _b, _c, _d;
1431
- const base = { field: f.name, dataType: 'TEXT' };
1432
- switch (f.operator) {
1433
- case 'FROM': return Object.assign(Object.assign({}, base), { from: (_a = f.value) !== null && _a !== void 0 ? _a : null });
1434
- case 'TO': return Object.assign(Object.assign({}, base), { to: (_b = f.value) !== null && _b !== void 0 ? _b : null });
1435
- case 'STARTS_WITH': return Object.assign(Object.assign({}, base), { operator: 'startsWith', value: (_c = f.value) !== null && _c !== void 0 ? _c : null });
1436
- case 'NOT_EMPTY': return Object.assign(Object.assign({}, base), { operator: 'isNotEmpty' });
1437
- case 'EMPTY': return Object.assign(Object.assign({}, base), { operator: 'isEmpty' });
1438
- default: return Object.assign(Object.assign({}, base), { operator: 'equals', value: (_d = f.value) !== null && _d !== void 0 ? _d : null });
1439
- }
1440
- });
1441
- }
1442
- emitLoadViews() {
1443
- var _a, _b, _c, _d, _e, _f, _g;
1444
- const dataLinks = ((_b = (_a = this.data) === null || _a === void 0 ? void 0 : _a._links) !== null && _b !== void 0 ? _b : {});
1445
- for (const item of this.flattenItems(this.items)) {
1446
- if (item.type === index$1.ClientLayoutItemType.RELATED_VIEW && item.name) {
1447
- const viewConfig = this.views[item.name];
1448
- if (!viewConfig)
1449
- continue;
1450
- const href = (_d = dataLinks[(_c = item.relatedClass) !== null && _c !== void 0 ? _c : '']) === null || _d === void 0 ? void 0 : _d.href;
1451
- this.mrdLoadView.emit({ name: item.name, href, viewConfig, sort: (_e = viewConfig.defaultSort) !== null && _e !== void 0 ? _e : '', filters: this.resolveViewFilters(viewConfig) });
1452
- }
1453
- else if (item.type === index$1.ClientLayoutItemType.VIEW) {
1454
- const viewName = item.name;
1455
- if (!viewName)
1456
- continue;
1457
- const viewConfig = this.views[viewName];
1458
- if (!viewConfig)
1459
- continue;
1460
- const href = (_f = this.links[viewName]) === null || _f === void 0 ? void 0 : _f.href;
1461
- this.mrdLoadView.emit({ name: viewName, href, viewConfig, sort: (_g = viewConfig.defaultSort) !== null && _g !== void 0 ? _g : '', filters: this.resolveViewFilters(viewConfig) });
1415
+ async initEmbeddedTables() {
1416
+ const tables = this.el.querySelectorAll('mrd-table[data-view]');
1417
+ for (const table of Array.from(tables)) {
1418
+ if (typeof table.init === 'function') {
1419
+ await table.init();
1462
1420
  }
1463
1421
  }
1464
1422
  }
1423
+ viewKeyFor(item) {
1424
+ var _a, _b, _c, _d;
1425
+ if (item.type === index$1.ClientLayoutItemType.RELATED_VIEW)
1426
+ return (_b = (_a = item.relatedClass) !== null && _a !== void 0 ? _a : item.name) !== null && _b !== void 0 ? _b : '';
1427
+ return (_d = (_c = item.dataClass) !== null && _c !== void 0 ? _c : item.name) !== null && _d !== void 0 ? _d : '';
1428
+ }
1465
1429
  emitLoadImages() {
1466
1430
  for (const item of this.flattenItems(this.items)) {
1467
1431
  if (item.type === index$1.ClientLayoutItemType.FIELD && item.dataType === index$1.ClientLayoutItemFieldDataType.IMAGE) {
@@ -1491,21 +1455,17 @@ const MrdLayoutSection = class {
1491
1455
  }
1492
1456
  /**
1493
1457
  * Inject data into an embedded mrd-table for a RELATED_VIEW or VIEW item.
1494
- * Pass totalElements on page 0 to initialise the table; omit on subsequent pages.
1495
- * Pass pageLinks (_links from the page response) on page 0 to enable action hrefs in mrdViewAction.
1458
+ * Pass totalElements to update the pagination total (safe to pass on every page).
1459
+ * Pass hasNext (from _links.next presence) so the table can decide whether to emit aggregations.
1496
1460
  */
1497
- async setViewPage(name, page, rows, totalElements, pageLinks) {
1498
- if (pageLinks) {
1499
- this.viewLinksMap = Object.assign(Object.assign({}, this.viewLinksMap), { [name]: pageLinks });
1500
- }
1461
+ async setViewPage(name, page, rows, totalElements, hasNext) {
1501
1462
  const table = this.el.querySelector(`mrd-table[data-view="${name}"]`);
1502
1463
  if (!table)
1503
1464
  return;
1504
1465
  if (totalElements !== undefined) {
1505
1466
  table.totalElements = totalElements;
1506
- await table.init();
1507
1467
  }
1508
- await table.setPage(page, rows);
1468
+ await table.setPage(page, rows, hasNext);
1509
1469
  }
1510
1470
  /** Inject aggregation totals into an embedded mrd-table for a VIEW or RELATED_VIEW item. */
1511
1471
  async setViewAggregations(name, data) {
@@ -1639,63 +1599,36 @@ const MrdLayoutSection = class {
1639
1599
  return (index.h("div", { class: "mrd-layout-section__search", key: `search-${dataClass}` }, index.h("div", { class: "mrd-layout-section__search-wrap" }, index.h("svg", { class: "mrd-layout-section__search-icon", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor", "aria-hidden": "true" }, index.h("path", { "fill-rule": "evenodd", d: "M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z", "clip-rule": "evenodd" })), index.h("input", { class: "mrd-layout-section__search-input", type: "text", value: query, placeholder: (_c = item.label) !== null && _c !== void 0 ? _c : '', onInput: e => this.handleSearchInput(dataClass, e.target.value) })), results.length > 0 && (index.h("ul", { class: "mrd-layout-section__search-results" }, results.map(r => (index.h("li", { key: r.id, class: "mrd-layout-section__search-result" }, index.h("button", { class: "mrd-layout-section__search-result-btn", onClick: () => this.mrdNavigate.emit({ href: r.id, label: r.label }) }, index.h("span", { class: "mrd-layout-section__search-result-label" }, r.label), r.description && index.h("span", { class: "mrd-layout-section__search-result-desc" }, r.description)))))))));
1640
1600
  }
1641
1601
  renderRelatedView(item) {
1642
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
1643
- const isRelated = item.type === index$1.ClientLayoutItemType.RELATED_VIEW;
1644
- const name = item.name;
1645
- if (!name)
1602
+ var _a, _b, _c, _d, _e, _f;
1603
+ const key = this.viewKeyFor(item);
1604
+ if (!key)
1646
1605
  return null;
1647
- const viewConfig = this.views[name];
1648
- if (!viewConfig)
1606
+ if (!item.view)
1649
1607
  return null;
1650
1608
  const showTitle = (_a = item.showTitle) !== null && _a !== void 0 ? _a : false;
1651
- const activeName = (_b = this.activeViewMap[name]) !== null && _b !== void 0 ? _b : name;
1652
- const activeViewConfig = (_c = this.views[activeName]) !== null && _c !== void 0 ? _c : viewConfig;
1653
- // Build the full view list (original + alternatives) so the switcher can always go back.
1654
- const originalLabel = (_f = (_e = (_d = viewConfig.pluralLabel) !== null && _d !== void 0 ? _d : viewConfig.singularLabel) !== null && _e !== void 0 ? _e : item.label) !== null && _f !== void 0 ? _f : name;
1655
- const allViews = [{ name, label: originalLabel }, ...((_g = item.alternativeViews) !== null && _g !== void 0 ? _g : [])];
1656
- const activeEntry = allViews.find(v => v.name === activeName);
1657
- const viewLabel = (_k = (_j = (_h = activeEntry === null || activeEntry === void 0 ? void 0 : activeEntry.label) !== null && _h !== void 0 ? _h : activeViewConfig.pluralLabel) !== null && _j !== void 0 ? _j : activeViewConfig.singularLabel) !== null && _k !== void 0 ? _k : '';
1658
- const altViews = allViews.filter(v => v.name !== activeName);
1659
- const rawActions = (_l = item.actions) !== null && _l !== void 0 ? _l : ['NEW', 'EXPORT'];
1660
- const tableActions = rawActions.reduce((acc, a) => {
1661
- if (a === 'NEW')
1662
- acc.push({ action: 'create', label: t('table_new_record', this.locale), icon: 'assets/sprites.svg#icon-plus', variant: 'primary' });
1663
- if (a === 'EXPORT')
1664
- acc.push({ action: 'export', label: t('table_export_excel', this.locale), icon: 'assets/sprites.svg#icon-file-excel' });
1665
- return acc;
1666
- }, []);
1667
- return (index.h("div", { class: "mrd-layout-section__related-view", key: `view-${name}` }, showTitle && item.label && index.h("h3", { class: "mrd-layout-section__related-view-title" }, item.label), index.h("mrd-table", { "data-view": name, columns: activeViewConfig.values, locale: this.locale, defaultSort: (_m = activeViewConfig.defaultSort) !== null && _m !== void 0 ? _m : '', viewLabel: viewLabel, alternativeViews: altViews, actions: tableActions, onMrdLoadPage: (e) => this.handleViewLoadPage(e, name), onMrdSwitchView: (e) => {
1668
- var _a, _b, _c, _d, _e, _f, _g, _h;
1669
- e.stopPropagation();
1670
- const newViewName = e.detail.name;
1671
- const newViewConfig = this.views[newViewName];
1672
- if (!newViewConfig)
1673
- return;
1674
- this.activeViewMap = Object.assign(Object.assign({}, this.activeViewMap), { [name]: newViewName });
1675
- this.activeFiltersMap = Object.assign(Object.assign({}, this.activeFiltersMap), { [name]: [] });
1676
- const dataLinks = ((_b = (_a = this.data) === null || _a === void 0 ? void 0 : _a._links) !== null && _b !== void 0 ? _b : {});
1677
- const href = isRelated
1678
- ? (_d = dataLinks[(_c = item.relatedClass) !== null && _c !== void 0 ? _c : '']) === null || _d === void 0 ? void 0 : _d.href
1679
- : ((_f = (_e = this.links[newViewName]) === null || _e === void 0 ? void 0 : _e.href) !== null && _f !== void 0 ? _f : (_g = this.links[name]) === null || _g === void 0 ? void 0 : _g.href);
1680
- this.mrdLoadView.emit({ name, href, viewConfig: newViewConfig, sort: (_h = newViewConfig.defaultSort) !== null && _h !== void 0 ? _h : '', filters: this.resolveViewFilters(newViewConfig) });
1681
- }, onMrdFilter: (e) => {
1609
+ // Extract parentId from data._links.self.href for RELATED_VIEW path construction
1610
+ const selfHref = (_e = (_d = (_c = (_b = this.data) === null || _b === void 0 ? void 0 : _b._links) === null || _c === void 0 ? void 0 : _c.self) === null || _d === void 0 ? void 0 : _d.href) !== null && _e !== void 0 ? _e : '';
1611
+ const parentId = (_f = selfHref.split('/').filter(Boolean).pop()) !== null && _f !== void 0 ? _f : '';
1612
+ return (index.h("div", { class: "mrd-layout-section__related-view", key: `view-${key}` }, showTitle && item.label && index.h("h3", { class: "mrd-layout-section__related-view-title" }, item.label), index.h("mrd-table", { "data-view": key, item: item, parentId: parentId, locale: this.locale, onMrdLoadPage: (e) => this.handleViewLoadPage(e, key), onMrdLoadAggregations: (e) => {
1613
+ var _a;
1682
1614
  e.stopPropagation();
1683
- this.activeFiltersMap = Object.assign(Object.assign({}, this.activeFiltersMap), { [name]: e.detail.filters });
1684
- }, onMrdLoadAggregations: (e) => {
1685
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
1615
+ this.mrdLoadViewAggregations.emit(Object.assign({ name: key, dataClass: (_a = item.dataClass) !== null && _a !== void 0 ? _a : key }, e.detail));
1616
+ }, onMrdRowClick: (e) => {
1617
+ var _a, _b, _c;
1686
1618
  e.stopPropagation();
1687
- const dataLinks = ((_b = (_a = this.data) === null || _a === void 0 ? void 0 : _a._links) !== null && _b !== void 0 ? _b : {});
1688
- const href = isRelated
1689
- ? (_d = dataLinks[(_c = item.relatedClass) !== null && _c !== void 0 ? _c : '']) === null || _d === void 0 ? void 0 : _d.href
1690
- : ((_g = (_f = this.links[(_e = this.activeViewMap[name]) !== null && _e !== void 0 ? _e : name]) === null || _f === void 0 ? void 0 : _f.href) !== null && _g !== void 0 ? _g : (_h = this.links[name]) === null || _h === void 0 ? void 0 : _h.href);
1691
- const filters = (_j = this.activeFiltersMap[name]) !== null && _j !== void 0 ? _j : [];
1692
- this.mrdLoadViewAggregations.emit(Object.assign({ name, href, filters }, e.detail));
1619
+ const row = e.detail;
1620
+ this.mrdNavigate.emit({ href: (_b = (_a = row === null || row === void 0 ? void 0 : row._links) === null || _a === void 0 ? void 0 : _a.self) === null || _b === void 0 ? void 0 : _b.href, label: (_c = row === null || row === void 0 ? void 0 : row.name) !== null && _c !== void 0 ? _c : '' });
1693
1621
  }, onMrdAction: (e) => {
1694
- var _a, _b, _c;
1622
+ var _a;
1695
1623
  e.stopPropagation();
1696
- const pl = (_a = this.viewLinksMap[name]) !== null && _a !== void 0 ? _a : {};
1697
- const href = e.detail.action === 'export' ? (_b = pl['excel']) === null || _b === void 0 ? void 0 : _b.href : (_c = pl['self']) === null || _c === void 0 ? void 0 : _c.href;
1698
- this.mrdViewAction.emit({ name, action: e.detail.action, href });
1624
+ this.mrdViewAction.emit({
1625
+ name: key,
1626
+ action: e.detail.action,
1627
+ dataClass: (_a = item.dataClass) !== null && _a !== void 0 ? _a : key,
1628
+ path: e.detail.path,
1629
+ qs: e.detail.qs,
1630
+ parentPath: e.detail.parentPath,
1631
+ });
1699
1632
  } })));
1700
1633
  }
1701
1634
  renderItem(item) {
@@ -1729,13 +1662,10 @@ const MrdLayoutSection = class {
1729
1662
  return (index.h("div", { class: "mrd-layout-section__modal-backdrop", onClick: () => { this.imagePreviewUrl = null; } }, index.h("div", { class: "mrd-layout-section__modal", onClick: (e) => e.stopPropagation() }, index.h("button", { class: "mrd-layout-section__modal-close", onClick: () => { this.imagePreviewUrl = null; } }, "\u2715"), index.h("img", { class: "mrd-layout-section__modal-image", src: this.imagePreviewUrl, alt: "" }))));
1730
1663
  }
1731
1664
  render() {
1732
- return (index.h(index.Host, { key: '7a91a541e056965dc79d74a50827e8c270c33a0d' }, index.h("div", { key: 'bcf4a2e81e704d136fb437cd2cb22acb4a05a8b3', class: "mrd-layout-section" }, this.items.map(item => this.renderItem(item))), this.renderImageModal()));
1665
+ return (index.h(index.Host, { key: '0a3a58f5c80716bc0a7ba1b9468b721706d2ce4a' }, index.h("div", { key: '7cf63580c584811c1bb84b419e8d13026e432fba', class: "mrd-layout-section" }, this.items.map(item => this.renderItem(item))), this.renderImageModal()));
1733
1666
  }
1734
1667
  get el() { return index.getElement(this); }
1735
1668
  static get watchers() { return {
1736
- "links": [{
1737
- "linksChanged": 0
1738
- }],
1739
1669
  "data": [{
1740
1670
  "dataChanged": 0
1741
1671
  }]
@@ -2201,9 +2131,7 @@ const MrdTable = class {
2201
2131
  this.mrdLoadPage = index.createEvent(this, "mrdLoadPage");
2202
2132
  this.mrdRowClick = index.createEvent(this, "mrdRowClick");
2203
2133
  this.mrdAction = index.createEvent(this, "mrdAction");
2204
- this.mrdFilter = index.createEvent(this, "mrdFilter");
2205
2134
  this.mrdDownload = index.createEvent(this, "mrdDownload");
2206
- this.mrdSwitchView = index.createEvent(this, "mrdSwitchView");
2207
2135
  this.mrdLoadAggregations = index.createEvent(this, "mrdLoadAggregations");
2208
2136
  // ── Non-state internals ────────────────────────────────────────────────────
2209
2137
  this.pendingPages = new Set();
@@ -2211,7 +2139,10 @@ const MrdTable = class {
2211
2139
  this.outsideClickHandler = null;
2212
2140
  this.keydownHandler = null;
2213
2141
  // ── Props ──────────────────────────────────────────────────────────────────
2214
- this.columns = [];
2142
+ /** The VIEW or RELATED_VIEW layout item. Contains view config, dataClass, fromClass, actions etc. */
2143
+ this.item = null;
2144
+ /** Parent record id — required for RELATED_VIEW to build /{fromClass}/{parentId}/{dataClass}. */
2145
+ this.parentId = '';
2215
2146
  /** Direct rows (non-paginated mode, used when totalElements === 0). */
2216
2147
  this.rows = [];
2217
2148
  this.locale = navigator.language;
@@ -2223,16 +2154,9 @@ const MrdTable = class {
2223
2154
  this.rowHeight = 36;
2224
2155
  /** Height of the scroll container in px. */
2225
2156
  this.tableHeight = 500;
2226
- /** Initial sort applied on load, e.g. "timestamp,desc" or "name".
2227
- * Parsed by init() into sortField + sortDir. */
2228
- this.defaultSort = '';
2229
- /** Toolbar action buttons rendered above the table. */
2230
- this.actions = [];
2231
- /** Display label of the current view — shown in the toolbar center as a view picker trigger. */
2232
- this.viewLabel = '';
2233
- /** Alternative views available for this table; renders a dropdown when non-empty. */
2234
- this.alternativeViews = [];
2235
2157
  // ── Internal state ─────────────────────────────────────────────────────────
2158
+ /** Index into allViews[] for the currently displayed view. 0 = primary, 1+ = alternatives. */
2159
+ this.activeViewIdx = 0;
2236
2160
  this.loadedPages = new Map();
2237
2161
  this.requestedPages = new Set();
2238
2162
  this.renderStart = 0;
@@ -2258,10 +2182,16 @@ const MrdTable = class {
2258
2182
  this.jsonModal = null;
2259
2183
  /** Aggregation totals received from the host via setAggregations(). Null = not yet loaded. */
2260
2184
  this.aggregations = null;
2185
+ /** Record count received via setAggregations().total; overrides totalElements for display. */
2186
+ this.aggregationsTotal = null;
2187
+ /** True when a fresh aggregations request is needed (set on init / filter change). */
2188
+ this.aggregationsPending = false;
2189
+ /** Lower bound on total derived from setPage() hasNext info; grows as pages load. */
2190
+ this.minKnownTotal = 0;
2261
2191
  this.handleScroll = (e) => {
2262
2192
  const scroller = e.currentTarget;
2263
2193
  const scrollTop = scroller.scrollTop;
2264
- const total = this.totalElements;
2194
+ const total = this.baseTotal;
2265
2195
  const visStart = Math.floor(scrollTop / this.rowHeight);
2266
2196
  const visEnd = Math.min(visStart + this.visibleCount(), total - 1);
2267
2197
  this.scrollTop = scrollTop;
@@ -2275,13 +2205,16 @@ const MrdTable = class {
2275
2205
  totalElementsChanged(newVal) {
2276
2206
  this.renderEnd = Math.min(this.renderEnd, Math.max(0, newVal - 1));
2277
2207
  }
2278
- /** Apply defaultSort when the prop changes (e.g. after a view switch). */
2279
- defaultSortChanged(newVal) {
2280
- this.applyDefaultSort(newVal);
2208
+ /** Reset to primary view when the item prop is replaced from outside. */
2209
+ itemChanged(newVal) {
2210
+ var _a, _b;
2211
+ this.activeViewIdx = 0;
2212
+ this.applyDefaultSort((_b = (_a = newVal === null || newVal === void 0 ? void 0 : newVal.view) === null || _a === void 0 ? void 0 : _a.defaultSort) !== null && _b !== void 0 ? _b : '');
2281
2213
  }
2282
2214
  // ── Lifecycle ──────────────────────────────────────────────────────────────
2283
2215
  componentWillLoad() {
2284
- this.applyDefaultSort(this.defaultSort);
2216
+ var _a, _b, _c;
2217
+ this.applyDefaultSort((_c = (_b = (_a = this.item) === null || _a === void 0 ? void 0 : _a.view) === null || _b === void 0 ? void 0 : _b.defaultSort) !== null && _c !== void 0 ? _c : '');
2285
2218
  }
2286
2219
  // ── Helpers ────────────────────────────────────────────────────────────────
2287
2220
  applyDefaultSort(defaultSort) {
@@ -2313,14 +2246,19 @@ const MrdTable = class {
2313
2246
  this.colWidths = [];
2314
2247
  this.scrollTop = 0;
2315
2248
  this.renderStart = 0;
2316
- // No BUFFER on init — only request what fits the visible area (page 0).
2317
- // BUFFER is applied during scroll to pre-fetch the next page proactively.
2318
- this.renderEnd = Math.max(0, Math.min(this.visibleCount() - 1, this.totalElements - 1));
2249
+ // Always fill the visible viewport on init — totalElements may be stale from a
2250
+ // previous view. setPage() clamps renderEnd when the page is shorter than pageSize.
2251
+ this.renderEnd = this.visibleCount() - 1;
2319
2252
  const scroller = this.el.querySelector('.mrd-table__scroll');
2320
2253
  if (scroller)
2321
2254
  scroller.scrollTop = 0;
2322
2255
  this.aggregations = null;
2323
- this.emitLoadAggregations();
2256
+ this.aggregationsTotal = null;
2257
+ this.aggregationsPending = true;
2258
+ this.minKnownTotal = 0;
2259
+ // Always request page 0 — totalElements may be unknown (0) on first load.
2260
+ this.mrdLoadPage.emit({ page: 0, sort: this.sortParam(), path: this.buildDataPath(), qs: this.buildQueryParams(0) });
2261
+ this.requestedPages = new Set([0]);
2324
2262
  }
2325
2263
  /**
2326
2264
  * Inject the rows for a given page (0-based).
@@ -2329,20 +2267,41 @@ const MrdTable = class {
2329
2267
  * When the page contains fewer rows than pageSize it is the last page.
2330
2268
  * renderEnd is clamped immediately so no loading-placeholder rows appear
2331
2269
  * beyond the actual data — without requiring the host to update totalElements.
2270
+ *
2271
+ * Pass hasNext (from _links.next in the API response) for accurate last-page
2272
+ * detection even when rows.length === pageSize (exact multiple of page size).
2332
2273
  */
2333
- async setPage(pageNumber, rows) {
2334
- if (rows.length < this.pageSize) {
2274
+ async setPage(pageNumber, rows, hasNext) {
2275
+ const isLastPage = hasNext !== undefined ? !hasNext : rows.length < this.pageSize;
2276
+ if (isLastPage) {
2335
2277
  // lastRowIdx is -1 when the page is empty; clamp renderEnd to -1 so the
2336
2278
  // render loop does not execute and no shimmer rows appear.
2337
2279
  const lastRowIdx = pageNumber * this.pageSize + rows.length - 1;
2338
2280
  this.renderEnd = Math.min(this.renderEnd, lastRowIdx);
2281
+ // Exact total is known: update minKnownTotal so the scroll container has the right height.
2282
+ this.minKnownTotal = pageNumber * this.pageSize + rows.length;
2283
+ }
2284
+ else {
2285
+ // There is at least one more page — ensure the scrollbar covers at least that next page.
2286
+ this.minKnownTotal = Math.max(this.minKnownTotal, (pageNumber + 1) * this.pageSize + 1);
2339
2287
  }
2340
2288
  const next = new Map(this.loadedPages);
2341
2289
  next.set(pageNumber, rows);
2342
2290
  this.loadedPages = next;
2291
+ if (pageNumber === 0 && this.aggregationsPending) {
2292
+ this.aggregationsPending = false;
2293
+ const hasAggColumns = this.columns.some(c => c.type === 'FIELD' && c.aggregate);
2294
+ if (!isLastPage || hasAggColumns) {
2295
+ this.emitLoadAggregations();
2296
+ }
2297
+ }
2343
2298
  }
2344
2299
  /** Inject aggregation totals returned by the /aggregations endpoint. */
2345
2300
  async setAggregations(data) {
2301
+ var _a;
2302
+ const total = (_a = data.total) !== null && _a !== void 0 ? _a : (typeof data.count === 'number' ? data.count : undefined);
2303
+ if (total != null)
2304
+ this.aggregationsTotal = total;
2346
2305
  this.aggregations = data;
2347
2306
  }
2348
2307
  // ── Lifecycle ──────────────────────────────────────────────────────────────
@@ -2373,6 +2332,147 @@ const MrdTable = class {
2373
2332
  return '';
2374
2333
  return this.sortDir === 'desc' ? `${this.sortField},desc` : this.sortField;
2375
2334
  }
2335
+ /** Stable ordered list: primary view first, then alternatives (from the item prop). */
2336
+ get allViews() {
2337
+ var _a, _b, _c, _d, _e, _f, _g;
2338
+ if (!this.item)
2339
+ return [];
2340
+ const it = this.item;
2341
+ return [
2342
+ { label: (_e = (_d = (_c = (_a = it.label) !== null && _a !== void 0 ? _a : (_b = it.view) === null || _b === void 0 ? void 0 : _b.pluralLabel) !== null && _c !== void 0 ? _c : it.dataClass) !== null && _d !== void 0 ? _d : it.relatedClass) !== null && _e !== void 0 ? _e : '', dataClass: (_f = it.dataClass) !== null && _f !== void 0 ? _f : it.relatedClass, fromClass: it.fromClass, filterClass: it.filterClass, view: it.view },
2343
+ ...((_g = it.alternativeViews) !== null && _g !== void 0 ? _g : []).map(av => {
2344
+ var _a, _b, _c, _d;
2345
+ return ({
2346
+ label: (_d = (_c = (_a = av.label) !== null && _a !== void 0 ? _a : (_b = av.view) === null || _b === void 0 ? void 0 : _b.pluralLabel) !== null && _c !== void 0 ? _c : av.dataClass) !== null && _d !== void 0 ? _d : '',
2347
+ dataClass: av.dataClass,
2348
+ fromClass: av.fromClass,
2349
+ filterClass: av.filterClass,
2350
+ view: av.view,
2351
+ });
2352
+ }),
2353
+ ];
2354
+ }
2355
+ /** Relative excel export path for the current view.
2356
+ * VIEW: /excel/{dataClass}
2357
+ * RELATED_VIEW: /excel/{fromClass}/{parentId}/{dataClass} */
2358
+ buildExcelPath() {
2359
+ var _a, _b, _c, _d;
2360
+ const v = this.allViews[this.activeViewIdx];
2361
+ if (!v)
2362
+ return '';
2363
+ if (((_a = this.item) === null || _a === void 0 ? void 0 : _a.type) === 'RELATED_VIEW') {
2364
+ return `/excel/${(_b = v.fromClass) !== null && _b !== void 0 ? _b : ''}/${this.parentId}/${(_c = v.dataClass) !== null && _c !== void 0 ? _c : ''}`;
2365
+ }
2366
+ return `/excel/${(_d = v.dataClass) !== null && _d !== void 0 ? _d : ''}`;
2367
+ }
2368
+ buildActionDetail(action) {
2369
+ var _a, _b, _c;
2370
+ if (action === 'export') {
2371
+ return { action, path: this.buildExcelPath(), qs: this.buildQueryParams(0) };
2372
+ }
2373
+ if (action === 'create') {
2374
+ const v = this.allViews[this.activeViewIdx];
2375
+ const parentPath = ((_a = this.item) === null || _a === void 0 ? void 0 : _a.type) === 'RELATED_VIEW'
2376
+ ? `/${(_b = v === null || v === void 0 ? void 0 : v.fromClass) !== null && _b !== void 0 ? _b : ''}/${this.parentId}`
2377
+ : null;
2378
+ return { action, dataClass: (_c = v === null || v === void 0 ? void 0 : v.dataClass) !== null && _c !== void 0 ? _c : '', parentPath };
2379
+ }
2380
+ return { action };
2381
+ }
2382
+ /** Relative data path for the current view, without query string.
2383
+ * VIEW: /{dataClass}
2384
+ * RELATED_VIEW: /{fromClass}/{parentId}/{dataClass} */
2385
+ buildDataPath() {
2386
+ var _a, _b, _c, _d;
2387
+ const v = this.allViews[this.activeViewIdx];
2388
+ if (!v)
2389
+ return '';
2390
+ if (((_a = this.item) === null || _a === void 0 ? void 0 : _a.type) === 'RELATED_VIEW') {
2391
+ return `/${(_b = v.fromClass) !== null && _b !== void 0 ? _b : ''}/${this.parentId}/${(_c = v.dataClass) !== null && _c !== void 0 ? _c : ''}`;
2392
+ }
2393
+ return `/${(_d = v.dataClass) !== null && _d !== void 0 ? _d : ''}`;
2394
+ }
2395
+ /** Build query params for a page request from current sort, view filters, filterClass and active column filters. */
2396
+ buildQueryParams(page) {
2397
+ var _a, _b, _c, _d, _e, _f, _g;
2398
+ const v = this.allViews[this.activeViewIdx];
2399
+ const p = new URLSearchParams();
2400
+ if (page > 0)
2401
+ p.set('page', String(page));
2402
+ const sort = this.sortParam();
2403
+ if (sort)
2404
+ p.set('sort', sort);
2405
+ const filterClass = v === null || v === void 0 ? void 0 : v.filterClass;
2406
+ if (filterClass)
2407
+ p.set('type', filterClass);
2408
+ for (const f of ((_b = (_a = v === null || v === void 0 ? void 0 : v.view) === null || _a === void 0 ? void 0 : _a.filter) !== null && _b !== void 0 ? _b : [])) {
2409
+ if (!f.name)
2410
+ continue;
2411
+ if (f.operator === 'EMPTY') {
2412
+ p.set(f.name, '');
2413
+ continue;
2414
+ }
2415
+ if (f.operator === 'NOT_EMPTY') {
2416
+ p.set(f.name + '_notempty', 'true');
2417
+ continue;
2418
+ }
2419
+ if (f.operator === 'STARTS_WITH') {
2420
+ p.set(f.name + '_startswith', String((_c = f.value) !== null && _c !== void 0 ? _c : ''));
2421
+ continue;
2422
+ }
2423
+ if (f.operator === 'FROM') {
2424
+ p.set(f.name + '_from', String((_d = f.value) !== null && _d !== void 0 ? _d : ''));
2425
+ continue;
2426
+ }
2427
+ if (f.operator === 'TO') {
2428
+ p.set(f.name + '_to', String((_e = f.value) !== null && _e !== void 0 ? _e : ''));
2429
+ continue;
2430
+ }
2431
+ if (f.value != null) {
2432
+ p.set(f.name, String(f.value));
2433
+ }
2434
+ }
2435
+ for (const f of this.activeFilters.values()) {
2436
+ if (f.operator === 'isEmpty') {
2437
+ p.set(f.field, '');
2438
+ continue;
2439
+ }
2440
+ if (f.operator === 'isNotEmpty') {
2441
+ p.set(f.field + '_notempty', 'true');
2442
+ continue;
2443
+ }
2444
+ if (f.operator === 'startsWith') {
2445
+ p.set(f.field + '_startswith', String((_f = f.value) !== null && _f !== void 0 ? _f : ''));
2446
+ continue;
2447
+ }
2448
+ if ((_g = f.values) === null || _g === void 0 ? void 0 : _g.length) {
2449
+ p.set(f.field, f.values.join(','));
2450
+ continue;
2451
+ }
2452
+ if (f.value != null)
2453
+ p.set(f.field, String(f.value));
2454
+ if (f.from != null)
2455
+ p.set(f.field + '_from', String(f.from));
2456
+ if (f.to != null)
2457
+ p.set(f.field + '_to', String(f.to));
2458
+ }
2459
+ return p.toString();
2460
+ }
2461
+ get columns() {
2462
+ var _a, _b, _c;
2463
+ return ((_c = (_b = (_a = this.allViews[this.activeViewIdx]) === null || _a === void 0 ? void 0 : _a.view) === null || _b === void 0 ? void 0 : _b.values) !== null && _c !== void 0 ? _c : []);
2464
+ }
2465
+ get tableActions() {
2466
+ var _a, _b;
2467
+ const raw = (_b = (_a = this.item) === null || _a === void 0 ? void 0 : _a.actions) !== null && _b !== void 0 ? _b : [];
2468
+ return (raw !== null && raw !== void 0 ? raw : []).reduce((acc, a) => {
2469
+ if (a === 'NEW')
2470
+ acc.push({ action: 'create', label: t('table_new_record', this.locale), icon: 'assets/sprites.svg#icon-plus', variant: 'primary' });
2471
+ if (a === 'EXPORT')
2472
+ acc.push({ action: 'export', label: t('table_export_excel', this.locale), icon: 'assets/sprites.svg#icon-file-excel' });
2473
+ return acc;
2474
+ }, []);
2475
+ }
2376
2476
  colName(col) {
2377
2477
  var _a;
2378
2478
  return (_a = col.name) !== null && _a !== void 0 ? _a : '';
@@ -2383,6 +2483,25 @@ const MrdTable = class {
2383
2483
  return 'RELATION';
2384
2484
  return (_a = col.dataType) !== null && _a !== void 0 ? _a : 'TEXT';
2385
2485
  }
2486
+ /** True when we have a reliable total: either from the aggregations response or because
2487
+ * a short page told us it was the last page (exact count from row length). */
2488
+ isTotalKnown() {
2489
+ if (this.aggregationsTotal != null)
2490
+ return true;
2491
+ for (const rows of this.loadedPages.values()) {
2492
+ if (rows.length < this.pageSize)
2493
+ return true;
2494
+ }
2495
+ return false;
2496
+ }
2497
+ /** Effective total: aggregations-response > totalElements prop > minKnownTotal from setPage(). */
2498
+ get baseTotal() {
2499
+ if (this.aggregationsTotal != null)
2500
+ return this.aggregationsTotal;
2501
+ if (this.totalElements > 0)
2502
+ return this.totalElements;
2503
+ return this.minKnownTotal;
2504
+ }
2386
2505
  // ── Aggregation helpers ────────────────────────────────────────────────────
2387
2506
  buildAggregationParams() {
2388
2507
  var _a;
@@ -2403,17 +2522,32 @@ const MrdTable = class {
2403
2522
  params.count = groups.count;
2404
2523
  return Object.keys(params).length > 0 ? params : null;
2405
2524
  }
2525
+ buildAggregationQs() {
2526
+ var _a, _b, _c;
2527
+ const p = new URLSearchParams(this.buildQueryParams(0));
2528
+ p.delete('page');
2529
+ p.delete('sort');
2530
+ const groups = this.buildAggregationParams();
2531
+ if ((_a = groups === null || groups === void 0 ? void 0 : groups.sum) === null || _a === void 0 ? void 0 : _a.length)
2532
+ p.set('sum', groups.sum.join(','));
2533
+ if ((_b = groups === null || groups === void 0 ? void 0 : groups.avg) === null || _b === void 0 ? void 0 : _b.length)
2534
+ p.set('avg', groups.avg.join(','));
2535
+ if ((_c = groups === null || groups === void 0 ? void 0 : groups.count) === null || _c === void 0 ? void 0 : _c.length)
2536
+ p.set('count', groups.count.join(','));
2537
+ return p.toString();
2538
+ }
2406
2539
  emitLoadAggregations() {
2407
- const params = this.buildAggregationParams();
2408
- if (params)
2409
- this.mrdLoadAggregations.emit(params);
2540
+ this.mrdLoadAggregations.emit({ path: this.buildDataPath(), qs: this.buildQueryParams(0), aggQs: this.buildAggregationQs() });
2410
2541
  }
2411
2542
  renderAggregationValue(col) {
2412
- var _a, _b;
2543
+ var _a;
2413
2544
  if (col.type !== 'FIELD' || !col.aggregate || !this.aggregations)
2414
2545
  return '';
2415
2546
  const fn = col.aggregate.toLowerCase();
2416
- const val = (_a = this.aggregations[fn]) === null || _a === void 0 ? void 0 : _a[(_b = col.name) !== null && _b !== void 0 ? _b : ''];
2547
+ const aggData = this.aggregations[fn];
2548
+ const val = typeof aggData === 'object' && aggData !== null
2549
+ ? aggData[(_a = col.name) !== null && _a !== void 0 ? _a : '']
2550
+ : undefined;
2417
2551
  if (val == null)
2418
2552
  return '';
2419
2553
  const dt = col.dataType;
@@ -2438,9 +2572,10 @@ const MrdTable = class {
2438
2572
  this.colWidths = [];
2439
2573
  this.scrollTop = 0;
2440
2574
  this.renderStart = 0;
2441
- // No BUFFER here — totalElements may be stale after a filter change.
2442
- // Only request what is visible; BUFFER kicks in during scroll as usual.
2443
- this.renderEnd = Math.max(0, Math.min(this.visibleCount() - 1, this.totalElements - 1));
2575
+ this.minKnownTotal = 0;
2576
+ // Mirror init(): use visibleCount so the first page is always requested.
2577
+ // setPage() will clamp renderEnd down when the last page is shorter.
2578
+ this.renderEnd = this.visibleCount() - 1;
2444
2579
  const scroller = this.el.querySelector('.mrd-table__scroll');
2445
2580
  if (scroller)
2446
2581
  scroller.scrollTop = 0;
@@ -2471,7 +2606,7 @@ const MrdTable = class {
2471
2606
  for (let p = firstPage; p <= lastPage; p++) {
2472
2607
  if (!this.loadedPages.has(p) && !next.has(p)) {
2473
2608
  next.add(p);
2474
- this.mrdLoadPage.emit({ page: p, sort: this.sortParam() });
2609
+ this.mrdLoadPage.emit({ page: p, sort: this.sortParam(), path: this.buildDataPath(), qs: this.buildQueryParams(p) });
2475
2610
  changed = true;
2476
2611
  }
2477
2612
  }
@@ -2513,7 +2648,7 @@ const MrdTable = class {
2513
2648
  if (pageEnd < this.renderStart || pageStart > this.renderEnd)
2514
2649
  continue;
2515
2650
  next.add(page);
2516
- this.mrdLoadPage.emit({ page, sort: this.sortParam() });
2651
+ this.mrdLoadPage.emit({ page, sort: this.sortParam(), path: this.buildDataPath(), qs: this.buildQueryParams(page) });
2517
2652
  changed = true;
2518
2653
  }
2519
2654
  this.pendingPages.clear();
@@ -2705,9 +2840,9 @@ const MrdTable = class {
2705
2840
  }
2706
2841
  this.activeFilters = next;
2707
2842
  this.closeFilterPopup();
2708
- this.mrdFilter.emit({ filters: Array.from(this.activeFilters.values()) });
2709
2843
  this.aggregations = null;
2710
- this.emitLoadAggregations();
2844
+ this.aggregationsTotal = null;
2845
+ this.aggregationsPending = true;
2711
2846
  if (this.totalElements > 0) {
2712
2847
  this.resetPages();
2713
2848
  this.emitPagesForWindow(this.renderStart, this.renderEnd);
@@ -2720,9 +2855,9 @@ const MrdTable = class {
2720
2855
  next.delete(name);
2721
2856
  this.activeFilters = next;
2722
2857
  this.closeFilterPopup();
2723
- this.mrdFilter.emit({ filters: Array.from(this.activeFilters.values()) });
2724
2858
  this.aggregations = null;
2725
- this.emitLoadAggregations();
2859
+ this.aggregationsTotal = null;
2860
+ this.aggregationsPending = true;
2726
2861
  if (this.totalElements > 0) {
2727
2862
  this.resetPages();
2728
2863
  this.emitPagesForWindow(this.renderStart, this.renderEnd);
@@ -2730,37 +2865,39 @@ const MrdTable = class {
2730
2865
  }
2731
2866
  clearAllFilters() {
2732
2867
  this.activeFilters = new Map();
2733
- this.mrdFilter.emit({ filters: [] });
2734
2868
  this.aggregations = null;
2735
- this.emitLoadAggregations();
2869
+ this.aggregationsTotal = null;
2870
+ this.aggregationsPending = true;
2736
2871
  if (this.totalElements > 0) {
2737
2872
  this.resetPages();
2738
2873
  this.emitPagesForWindow(this.renderStart, this.renderEnd);
2739
2874
  }
2740
2875
  }
2741
2876
  // ── View switcher ──────────────────────────────────────────────────────────
2742
- handleViewSwitch(view) {
2743
- this.mrdSwitchView.emit({ name: view.name, class: view.class });
2877
+ handleViewSwitch(targetIdx) {
2878
+ var _a, _b;
2879
+ const target = this.allViews[targetIdx];
2880
+ if (!(target === null || target === void 0 ? void 0 : target.view))
2881
+ return;
2882
+ this.activeViewIdx = targetIdx;
2883
+ this.applyDefaultSort((_b = (_a = target.view) === null || _a === void 0 ? void 0 : _a.defaultSort) !== null && _b !== void 0 ? _b : '');
2884
+ this.activeFilters = new Map();
2885
+ this.init();
2744
2886
  }
2745
2887
  // ── Render: toolbar ────────────────────────────────────────────────────────
2746
2888
  renderToolbar() {
2747
- var _a, _b;
2748
2889
  const filterCount = this.activeFilters.size;
2749
- const hasActions = ((_a = this.actions) === null || _a === void 0 ? void 0 : _a.length) > 0;
2750
- const hasViewSwitcher = !!this.viewLabel && ((_b = this.alternativeViews) === null || _b === void 0 ? void 0 : _b.length) > 0;
2890
+ const actions = this.tableActions;
2891
+ const allViews = this.allViews;
2892
+ const hasActions = actions.length > 0;
2893
+ const hasViewSwitcher = allViews.length > 1;
2751
2894
  return (index.h("div", { class: "mrd-table__toolbar" }, index.h("div", { class: "mrd-table__toolbar-left" }, index.h("button", { class: `mrd-table__action mrd-table__action--secondary mrd-table__filter-toggle${this.filterMode ? ' mrd-table__filter-toggle--active' : ''}`, onClick: () => this.handleFilterToggle() }, index.h("svg", { class: "mrd-table__action-icon", viewBox: "0 0 24 24", "aria-hidden": "true" }, index.h("path", { fill: "currentColor", d: "M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z" })), filterCount > 0 && index.h("span", { class: "mrd-table__filter-badge" }, filterCount), index.h("span", { class: "mrd-table__action-tooltip" }, this.filterMode ? t('table_filter_hide', this.locale) : t('table_filter', this.locale), filterCount > 0 ? ` (${filterCount} ${t('table_filter_active', this.locale)})` : '')), filterCount > 0 && (index.h("button", { class: "mrd-table__action mrd-table__action--secondary", onClick: () => this.clearAllFilters() }, index.h("svg", { class: "mrd-table__action-icon", viewBox: "0 0 24 24", "aria-hidden": "true" }, index.h("path", { fill: "currentColor", d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" })), index.h("span", { class: "mrd-table__action-tooltip" }, t('table_filter_clear_all', this.locale))))), hasViewSwitcher && (index.h("div", { class: "mrd-table__toolbar-center" }, index.h("select", { class: "mrd-table__view-select", onChange: (e) => {
2752
- const sel = e.target;
2753
- const view = this.alternativeViews.find(v => v.name === sel.value);
2754
- if (view) {
2755
- sel.selectedIndex = 0;
2756
- this.handleViewSwitch(view);
2757
- }
2758
- } }, index.h("option", { value: "" }, this.viewLabel), this.alternativeViews.map(v => {
2759
- var _a;
2760
- return (index.h("option", { value: v.name }, (_a = v.label) !== null && _a !== void 0 ? _a : v.name));
2761
- })))), hasActions && (index.h("div", { class: "mrd-table__toolbar-right" }, this.actions.map(a => {
2895
+ const idx = parseInt(e.target.value, 10);
2896
+ if (!isNaN(idx) && idx !== this.activeViewIdx)
2897
+ this.handleViewSwitch(idx);
2898
+ } }, allViews.map((v, i) => (index.h("option", { value: String(i), selected: i === this.activeViewIdx }, v.label)))))), hasActions && (index.h("div", { class: "mrd-table__toolbar-right" }, actions.map(a => {
2762
2899
  var _a;
2763
- return (index.h("button", { class: `mrd-table__action mrd-table__action--${(_a = a.variant) !== null && _a !== void 0 ? _a : 'secondary'}`, disabled: a.disabled, onClick: () => this.mrdAction.emit({ action: a.action }) }, a.icon
2900
+ return (index.h("button", { class: `mrd-table__action mrd-table__action--${(_a = a.variant) !== null && _a !== void 0 ? _a : 'secondary'}`, disabled: a.disabled, onClick: () => this.mrdAction.emit(this.buildActionDetail(a.action)) }, a.icon
2764
2901
  ? index.h("svg", { class: "mrd-table__action-icon", "aria-hidden": "true" }, index.h("use", { href: a.icon }))
2765
2902
  : a.label, index.h("span", { class: "mrd-table__action-tooltip" }, a.label)));
2766
2903
  })))));
@@ -2854,10 +2991,9 @@ const MrdTable = class {
2854
2991
  return (index.h("div", { class: "mrd-table__filter-popup", style: { top: `${this.popupPos.top}px`, left: `${this.popupPos.left}px` }, onClick: (e) => e.stopPropagation() }, index.h("div", { class: "mrd-table__filter-popup-header" }, index.h("span", { class: "mrd-table__filter-popup-title" }, label), index.h("button", { class: "mrd-table__filter-close", onClick: () => this.closeFilterPopup() }, "\u2715")), index.h("div", { class: "mrd-table__filter-section" }, index.h("div", { class: "mrd-table__filter-section-label" }, t('filter_sorting', this.locale)), index.h("div", { class: "mrd-table__filter-sort-buttons" }, index.h("button", { class: `mrd-table__filter-sort-btn${sortActive && this.sortDir === 'asc' ? ' mrd-table__filter-sort-btn--active' : ''}`, onClick: () => this.applySort(col, 'asc') }, "\u25B2 ", t('filter_ascending', this.locale)), index.h("button", { class: `mrd-table__filter-sort-btn${sortActive && this.sortDir === 'desc' ? ' mrd-table__filter-sort-btn--active' : ''}`, onClick: () => this.applySort(col, 'desc') }, "\u25BC ", t('filter_descending', this.locale)))), index.h("div", { class: "mrd-table__filter-divider" }), index.h("div", { class: "mrd-table__filter-section" }, index.h("div", { class: "mrd-table__filter-section-label" }, t('filter_section', this.locale)), this.renderFilterEditor(col)), index.h("div", { class: "mrd-table__filter-popup-footer" }, index.h("button", { class: "mrd-table__filter-btn mrd-table__filter-btn--clear", onClick: () => this.clearFilter() }, t('filter_clear', this.locale)), index.h("button", { class: "mrd-table__filter-btn mrd-table__filter-btn--apply", onClick: () => this.applyFilter() }, t('filter_apply', this.locale)))));
2855
2992
  }
2856
2993
  // ── Render: footer ────────────────────────────────────────────────────────
2857
- renderFooter(rowCount, effectiveTotal) {
2858
- const total = this.totalElements;
2859
- // Non-paginated mode: show plain row count
2860
- if (total === 0) {
2994
+ renderFooter(rowCount, effectiveTotal, isTotalKnown = true) {
2995
+ // Non-paginated mode: totalElements=0 and no paginated data loaded yet
2996
+ if (this.totalElements === 0 && this.loadedPages.size === 0) {
2861
2997
  const count = rowCount !== null && rowCount !== void 0 ? rowCount : 0;
2862
2998
  if (count === 0)
2863
2999
  return null;
@@ -2866,13 +3002,14 @@ const MrdTable = class {
2866
3002
  // Paginated mode: only show once page 0 has loaded (avoids stale total during filter reset)
2867
3003
  if (!this.loadedPages.has(0))
2868
3004
  return null;
2869
- // Use effectiveTotal (derived from actual page lengths) so the counter
2870
- // is correct even when the host has not yet updated totalElements.
2871
- const displayTotal = effectiveTotal !== null && effectiveTotal !== void 0 ? effectiveTotal : total;
3005
+ // effectiveTotal from render(); fall back to baseTotal when not provided.
3006
+ const displayTotal = effectiveTotal !== null && effectiveTotal !== void 0 ? effectiveTotal : this.baseTotal;
2872
3007
  // Compute from/to independently so partial rows at top/bottom are included.
2873
3008
  const from = Math.min(Math.floor(this.scrollTop / this.rowHeight) + 1, displayTotal);
2874
3009
  const to = Math.min(Math.ceil((this.scrollTop + this.tableHeight) / this.rowHeight), displayTotal);
2875
- return (index.h("div", { class: "mrd-table__footer" }, from, "\u2013", to, " ", t('table_of', this.locale), " ", displayTotal));
3010
+ // Show '…' for the total until we have a reliable count (aggregations or last page loaded).
3011
+ const totalLabel = isTotalKnown ? String(displayTotal) : '…';
3012
+ return (index.h("div", { class: "mrd-table__footer" }, from, "\u2013", to, " ", t('table_of', this.locale), " ", totalLabel));
2876
3013
  }
2877
3014
  // ── Render: cell ──────────────────────────────────────────────────────────
2878
3015
  renderCell(col, row) {
@@ -2931,7 +3068,9 @@ const MrdTable = class {
2931
3068
  if (!((_a = this.columns) === null || _a === void 0 ? void 0 : _a.length))
2932
3069
  return null;
2933
3070
  // ── Non-paginated mode ──────────────────────────────────────────────────
2934
- if (this.totalElements === 0) {
3071
+ // Only enter non-paginated mode when totalElements is 0 AND no paginated data
3072
+ // has been loaded yet — prevents the wrong branch when the host omits totalElements.
3073
+ if (this.totalElements === 0 && this.loadedPages.size === 0) {
2935
3074
  return (index.h(index.Host, null, this.renderToolbar(), index.h("div", { class: "mrd-table" }, index.h("table", { class: "mrd-table__table" }, index.h("thead", null, index.h("tr", null, this.columns.map(col => {
2936
3075
  var _a;
2937
3076
  const name = this.colName(col);
@@ -2948,8 +3087,8 @@ const MrdTable = class {
2948
3087
  // Derive the authoritative row count from loaded pages:
2949
3088
  // if any loaded page is shorter than pageSize it is the last page,
2950
3089
  // so the true total cannot exceed (pageNum * pageSize + pageRows.length).
2951
- // This self-corrects without requiring the host to update totalElements.
2952
- let effectiveTotal = this.totalElements;
3090
+ // aggregationsTotal (from setAggregations) takes priority over the totalElements prop.
3091
+ let effectiveTotal = this.baseTotal;
2953
3092
  for (const [pageNum, pageRows] of this.loadedPages) {
2954
3093
  if (pageRows.length < this.pageSize) {
2955
3094
  effectiveTotal = Math.min(effectiveTotal, pageNum * this.pageSize + pageRows.length);
@@ -2986,7 +3125,7 @@ const MrdTable = class {
2986
3125
  isFiltered ? 'mrd-table__header--filtered' : '',
2987
3126
  ].filter(Boolean).join(' ');
2988
3127
  return (index.h("th", { class: cls, style: this.colWidths[idx] ? { width: `${this.colWidths[idx]}px` } : undefined, onClick: isInteractive ? ((e) => this.filterMode ? this.handleFilterOpen(col, e) : this.handleSortClick(col)) : undefined }, index.h("span", { class: "mrd-table__header-label" }, (_a = col.label) !== null && _a !== void 0 ? _a : ''), isInteractive && isActive && (index.h("span", { class: "mrd-table__sort-icon", "aria-hidden": "true" }, this.sortDir === 'asc' ? '▲' : '▼')), isInteractive && !isActive && !this.filterMode && (index.h("span", { class: "mrd-table__sort-icon", "aria-hidden": "true" }, "\u21C5")), isInteractive && isFiltered && this.renderFilterIcon()));
2989
- }))), index.h("tbody", null, topSpacerHeight > 0 && (index.h("tr", { class: "mrd-table__spacer", style: { height: `${topSpacerHeight}px` } }, index.h("td", { colSpan: colCount }))), renderedRows, bottomSpacerHeight > 0 && (index.h("tr", { class: "mrd-table__spacer", style: { height: `${bottomSpacerHeight}px` } }, index.h("td", { colSpan: colCount })))), this.renderTotalsRow())), effectiveTotal === 0 && this.loadedPages.has(0) && (index.h("p", { class: "mrd-table__empty" }, t('no_results', this.locale))), effectiveTotal > 0 && this.renderFooter(undefined, effectiveTotal), this.renderFilterPopup(), this.renderTextblockModal()));
3128
+ }))), index.h("tbody", null, topSpacerHeight > 0 && (index.h("tr", { class: "mrd-table__spacer", style: { height: `${topSpacerHeight}px` } }, index.h("td", { colSpan: colCount }))), renderedRows, bottomSpacerHeight > 0 && (index.h("tr", { class: "mrd-table__spacer", style: { height: `${bottomSpacerHeight}px` } }, index.h("td", { colSpan: colCount })))), this.renderTotalsRow())), effectiveTotal === 0 && this.loadedPages.has(0) && (index.h("p", { class: "mrd-table__empty" }, t('no_results', this.locale))), effectiveTotal > 0 && this.renderFooter(undefined, effectiveTotal, this.isTotalKnown()), this.renderFilterPopup(), this.renderTextblockModal()));
2990
3129
  }
2991
3130
  renderFilterIcon() {
2992
3131
  return (index.h("span", { class: "mrd-table__filter-icon", "aria-hidden": "true" }, index.h("svg", { viewBox: "0 0 24 24", width: "14", height: "14", fill: "currentColor" }, index.h("path", { d: "M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z" }))));
@@ -3006,8 +3145,8 @@ const MrdTable = class {
3006
3145
  "totalElements": [{
3007
3146
  "totalElementsChanged": 0
3008
3147
  }],
3009
- "defaultSort": [{
3010
- "defaultSortChanged": 0
3148
+ "item": [{
3149
+ "itemChanged": 0
3011
3150
  }]
3012
3151
  }; }
3013
3152
  };