@optionfactory/fml 9.0.0-rc31 → 9.0.0-rc32
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/custom-elements.json +7 -0
- package/dist/fml.css +1 -1
- package/dist/fml.css.map +1 -1
- package/dist/fml.iife.js +676 -28
- package/dist/fml.iife.js.map +1 -1
- package/dist/fml.iife.min.js +1 -1
- package/dist/fml.iife.min.js.map +1 -1
- package/dist/ful.css +1 -1
- package/dist/ful.css.map +1 -1
- package/dist/ful.d.mts +68 -0
- package/dist/ful.iife.js +676 -28
- package/dist/ful.iife.js.map +1 -1
- package/dist/ful.iife.min.js +1 -1
- package/dist/ful.iife.min.js.map +1 -1
- package/dist/ful.min.mjs +1 -1
- package/dist/ful.min.mjs.map +1 -1
- package/dist/ful.mjs +676 -28
- package/dist/ful.mjs.map +1 -1
- package/dist/vscode.html-custom-data.json +4 -0
- package/dist/web-types.json +9 -1
- package/package.json +1 -1
package/dist/ful.iife.js
CHANGED
|
@@ -4928,6 +4928,10 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
4928
4928
|
* @typedef {{ data: any[], size: number }} TablePageResponse the rows of the
|
|
4929
4929
|
* requested page and the total number of rows across every page
|
|
4930
4930
|
*/
|
|
4931
|
+
/**
|
|
4932
|
+
* @typedef {{ count: Element, appending: Element, failure: Element, error: Element }} TableStatus the pieces
|
|
4933
|
+
* of the status bar `paging="infinite"` and `paging="virtual"` show in place of the pager
|
|
4934
|
+
*/
|
|
4931
4935
|
/**
|
|
4932
4936
|
* @typedef {{ load(pageRequest: TablePageRequest, sortRequest: TableSortRequest|null, filterRequest: Record<string, any>): Promise<TablePageResponse> }} TableRowLoader
|
|
4933
4937
|
*/
|
|
@@ -5400,6 +5404,21 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
5400
5404
|
* for it. Without it the table shows an initial panel until `load`,
|
|
5401
5405
|
* `reload` or `resetWithFilter` is called.
|
|
5402
5406
|
*
|
|
5407
|
+
* `paging="infinite"`, read once at the upgrade, replaces the pager with a
|
|
5408
|
+
* status bar saying how many rows of how many are shown, with a reload
|
|
5409
|
+
* button. A load still replaces the rows with one page, scrolling the rows
|
|
5410
|
+
* back to the top; from then on, whenever a row of the last half page
|
|
5411
|
+
* comes into view, the next page is asked with the same size, sort and
|
|
5412
|
+
* filters and appended (see `load`). `reload`, a sort, a filter submit and a
|
|
5413
|
+
* size change start over from the first page.
|
|
5414
|
+
*
|
|
5415
|
+
* `paging="virtual"` shows the same status bar, saying which rows of how many
|
|
5416
|
+
* are in view, and keeps in the page only the page in view and one on either
|
|
5417
|
+
* side of it, the others standing in as empty space sized on the height of
|
|
5418
|
+
* the rows. The rows must scroll inside the table, as `ful-fill` makes them:
|
|
5419
|
+
* a table whose rows the page scrolls appends as `paging="infinite"` does,
|
|
5420
|
+
* and warns. See `load` and `reload`.
|
|
5421
|
+
*
|
|
5403
5422
|
* Inside the table, and not inside a `ful-table` nested in it, a
|
|
5404
5423
|
* `page:requested` loads that page with the current size,
|
|
5405
5424
|
* sort and filters; a `sort:requested` loads the current page with the new
|
|
@@ -5415,13 +5434,14 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
5415
5434
|
*/
|
|
5416
5435
|
class Table extends index_mjs.ParsedElement {
|
|
5417
5436
|
/** Read once at the upgrade. */
|
|
5418
|
-
static attributes = ['loader', 'autoload:presence'];
|
|
5437
|
+
static attributes = ['loader', 'autoload:presence', 'paging'];
|
|
5419
5438
|
/** These stay live after the upgrade: see `pageSize`, `src` and `method`. */
|
|
5420
5439
|
static observed = ['page-size:number', 'src', 'method'];
|
|
5421
5440
|
static slots = true;
|
|
5422
5441
|
static config = {
|
|
5423
5442
|
searchIcon: 'search',
|
|
5424
5443
|
emptyIcon: 'inbox',
|
|
5444
|
+
reloadIcon: 'arrow-clockwise',
|
|
5425
5445
|
};
|
|
5426
5446
|
static template = `
|
|
5427
5447
|
<ful-form data-tpl-if="slots.filters">
|
|
@@ -5431,7 +5451,9 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
5431
5451
|
<table>
|
|
5432
5452
|
<caption data-tpl-if="slots.caption">{{{{ slots.caption }}}}</caption>
|
|
5433
5453
|
<thead></thead>
|
|
5454
|
+
<tbody data-tpl-if="virtual" data-ref="before" aria-hidden="true"><tr><td data-tpl-colspan="schema.length"></td></tr></tbody>
|
|
5434
5455
|
<tbody></tbody>
|
|
5456
|
+
<tbody data-tpl-if="virtual" data-ref="after" aria-hidden="true"><tr><td data-tpl-colspan="schema.length"></td></tr></tbody>
|
|
5435
5457
|
<tbody data-ref="initial">
|
|
5436
5458
|
<tr>
|
|
5437
5459
|
<td data-tpl-colspan="schema.length">
|
|
@@ -5475,7 +5497,16 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
5475
5497
|
</tfoot>
|
|
5476
5498
|
</table>
|
|
5477
5499
|
</ful-table-wrapper>
|
|
5478
|
-
<ful-pagination current="0" total="1"></ful-pagination>
|
|
5500
|
+
<ful-pagination data-tpl-if="!scrolled" current="0" total="1"></ful-pagination>
|
|
5501
|
+
<ful-table-status data-tpl-if="scrolled">
|
|
5502
|
+
<span data-ref="count"></span>
|
|
5503
|
+
<ful-spinner data-ref="appending" role="status" hidden><span class="ful-sr-only">{{ #l10n:t('spinner.loading') }}</span></ful-spinner>
|
|
5504
|
+
<div data-ref="append-failure" role="alert" hidden>
|
|
5505
|
+
<span>{{ #l10n:t('table.error') }} <span data-ref="append-error"></span></span>
|
|
5506
|
+
<button type="button" data-ref="retry">{{ #l10n:t('table.retry') }}</button>
|
|
5507
|
+
</div>
|
|
5508
|
+
<button type="button" data-ref="reload" data-tpl-aria-label="#l10n:t('pagination.reload')"><ful-icon data-tpl-name="config.reloadIcon" aria-hidden="true"></ful-icon></button>
|
|
5509
|
+
</ful-table-status>
|
|
5479
5510
|
`;
|
|
5480
5511
|
static templates = {
|
|
5481
5512
|
row: `
|
|
@@ -5493,6 +5524,31 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
5493
5524
|
#feedback;
|
|
5494
5525
|
#paginator;
|
|
5495
5526
|
#sorters;
|
|
5527
|
+
#wrapper;
|
|
5528
|
+
/** @type {TableStatus|null} null under the pager */
|
|
5529
|
+
#status = null;
|
|
5530
|
+
/** @type {IntersectionObserver|null} */
|
|
5531
|
+
#endWatch = null;
|
|
5532
|
+
/** @type {Element[]} the rows whose coming into view appends the next page, none while no append is awaited */
|
|
5533
|
+
#watched = [];
|
|
5534
|
+
/** the page the next append asks for, the rows shown, the total the last answer reported and the rows it carried */
|
|
5535
|
+
#appended = { next: 1, shown: 0, total: 0, last: 0 };
|
|
5536
|
+
/** whether the table keeps a window of pages, `paging="virtual"` over rows that scroll inside it */
|
|
5537
|
+
#virtual = false;
|
|
5538
|
+
/** @type {{ before: HTMLElement, after: HTMLElement } | null} the cells standing in for the rows above and below the window */
|
|
5539
|
+
#spacers = null;
|
|
5540
|
+
/** @type {{ first: number, last: number, pages: Map<number, Element[]> } | null} the pages in the page, in order, null while no rows show */
|
|
5541
|
+
#window = null;
|
|
5542
|
+
/** the total number of rows the last answer reported */
|
|
5543
|
+
#total = 0;
|
|
5544
|
+
/** the height of a row the spacers are sized on, measured on the first rows a load shows; 0 until measured */
|
|
5545
|
+
#rowHeight = 0;
|
|
5546
|
+
/** @type {{ readonly stale: boolean } | null} the claim of the window shift in flight */
|
|
5547
|
+
#shifting = null;
|
|
5548
|
+
/** whether the last window shift failed: scrolling asks for nothing more until a retry or a load */
|
|
5549
|
+
#windowFailed = false;
|
|
5550
|
+
/** @type {ReturnType<typeof setTimeout>|undefined} */
|
|
5551
|
+
#jump = undefined;
|
|
5496
5552
|
/** @type {{ pageRequest: { page: number, size: number }, sortRequest: any, filterRequest: any }} */
|
|
5497
5553
|
#latestRequest = { pageRequest: { page: 0, size: 10 }, sortRequest: null, filterRequest: {} };
|
|
5498
5554
|
/** whether a load has been asked for, by autoload or by a caller */
|
|
@@ -5620,6 +5676,7 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
5620
5676
|
}
|
|
5621
5677
|
this.#buildLoader();
|
|
5622
5678
|
this.#latestRequest = { ...this.#latestRequest, pageRequest: { ...this.#latestRequest.pageRequest, page: 0 } };
|
|
5679
|
+
this.#window = null;
|
|
5623
5680
|
return true;
|
|
5624
5681
|
}
|
|
5625
5682
|
/**
|
|
@@ -5630,14 +5687,24 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
5630
5687
|
async render({ slots }) {
|
|
5631
5688
|
const template = this.template();
|
|
5632
5689
|
const schema = TableSchemaParser.parse(slots.schema, template);
|
|
5633
|
-
const
|
|
5690
|
+
const paging = this.declared('paging');
|
|
5691
|
+
const fragment = template
|
|
5692
|
+
.withOverlay({
|
|
5693
|
+
slots,
|
|
5694
|
+
schema,
|
|
5695
|
+
scrolled: paging === 'infinite' || paging === 'virtual',
|
|
5696
|
+
virtual: paging === 'virtual',
|
|
5697
|
+
})
|
|
5698
|
+
.render();
|
|
5634
5699
|
const tableWrapper = /** @type HTMLTableElement */ (index_mjs.Nodes.queryChildren(fragment, 'ful-table-wrapper'));
|
|
5635
5700
|
const table = /** @type HTMLTableElement */ (tableWrapper.querySelector('table'));
|
|
5636
5701
|
index_mjs.Attributes.forward('table-', this, table);
|
|
5637
5702
|
this.#buildLoader();
|
|
5638
5703
|
|
|
5704
|
+
this.#wrapper = tableWrapper;
|
|
5705
|
+
const status = index_mjs.Nodes.queryChildren(fragment, 'ful-table-status');
|
|
5639
5706
|
this.#schema = schema;
|
|
5640
|
-
this.#body = table.querySelector(':scope > tbody');
|
|
5707
|
+
this.#body = table.querySelector(':scope > tbody:not([data-ref])');
|
|
5641
5708
|
this.#loading = table.querySelector(':scope > tbody[data-ref=loading]');
|
|
5642
5709
|
this.#noAutoload = table.querySelector(':scope > tbody[data-ref=initial]');
|
|
5643
5710
|
this.#empty = table.querySelector(':scope > tbody[data-ref=empty]');
|
|
@@ -5647,6 +5714,56 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
5647
5714
|
this.#feedback = table.querySelector(':scope > tbody[data-ref=feedback]');
|
|
5648
5715
|
this.#paginator = index_mjs.Nodes.queryChildren(fragment, 'ful-pagination');
|
|
5649
5716
|
this.replaceChildren(fragment);
|
|
5717
|
+
if (paging === 'virtual') {
|
|
5718
|
+
const overflow = getComputedStyle(tableWrapper).overflowY;
|
|
5719
|
+
this.#virtual = overflow === 'auto' || overflow === 'scroll';
|
|
5720
|
+
if (!this.#virtual) {
|
|
5721
|
+
console.warn(
|
|
5722
|
+
'ful-table: paging="virtual" needs rows that scroll inside the table, as ful-fill makes them; this one appends its pages as paging="infinite" does',
|
|
5723
|
+
);
|
|
5724
|
+
}
|
|
5725
|
+
}
|
|
5726
|
+
if (status) {
|
|
5727
|
+
const piece = (/** @type string */ ref) =>
|
|
5728
|
+
/** @type Element */ (status.querySelector(`[data-ref=${ref}]`));
|
|
5729
|
+
this.#status = {
|
|
5730
|
+
count: piece('count'),
|
|
5731
|
+
appending: piece('appending'),
|
|
5732
|
+
failure: piece('append-failure'),
|
|
5733
|
+
error: piece('append-error'),
|
|
5734
|
+
};
|
|
5735
|
+
piece('reload').addEventListener('click', () => this.reload());
|
|
5736
|
+
piece('retry').addEventListener('click', () => {
|
|
5737
|
+
if (!this.#virtual) {
|
|
5738
|
+
this.#appendNext();
|
|
5739
|
+
return;
|
|
5740
|
+
}
|
|
5741
|
+
this.#windowFailed = false;
|
|
5742
|
+
this.#reconcile(true);
|
|
5743
|
+
});
|
|
5744
|
+
}
|
|
5745
|
+
if (this.#virtual) {
|
|
5746
|
+
this.#spacers = {
|
|
5747
|
+
before: /** @type HTMLElement */ (table.querySelector(':scope > tbody[data-ref=before] td')),
|
|
5748
|
+
after: /** @type HTMLElement */ (table.querySelector(':scope > tbody[data-ref=after] td')),
|
|
5749
|
+
};
|
|
5750
|
+
// scroll fires once per frame at most, so it needs no throttle of its own
|
|
5751
|
+
tableWrapper.addEventListener(
|
|
5752
|
+
'scroll',
|
|
5753
|
+
() => {
|
|
5754
|
+
this.#countInView();
|
|
5755
|
+
this.#reconcile(false);
|
|
5756
|
+
},
|
|
5757
|
+
{ passive: true },
|
|
5758
|
+
);
|
|
5759
|
+
} else if (status) {
|
|
5760
|
+
this.#endWatch = new IntersectionObserver((entries) => {
|
|
5761
|
+
// an entry queued before the watch moved on to another row, or stopped, asks for nothing
|
|
5762
|
+
if (entries.some((e) => e.isIntersecting && this.#watched.includes(e.target))) {
|
|
5763
|
+
this.#appendNext();
|
|
5764
|
+
}
|
|
5765
|
+
});
|
|
5766
|
+
}
|
|
5650
5767
|
const thead = /** @type HTMLTableSectionElement */ (this.querySelector('thead'));
|
|
5651
5768
|
schema.headersTemplate.renderTo(thead);
|
|
5652
5769
|
this.#sorters = thead.querySelectorAll('ful-sorter');
|
|
@@ -5703,6 +5820,14 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
5703
5820
|
* `submit` listeners as the later ones, and the load its `submit:success`
|
|
5704
5821
|
* starts is the one awaited. A filter submit that is cancelled, fails or
|
|
5705
5822
|
* is dropped for one in flight loads nothing, and the promise resolves.
|
|
5823
|
+
*
|
|
5824
|
+
* Under `paging="virtual"`, once rows show, it asks again for the pages in
|
|
5825
|
+
* the page, with the same size, sort and filters, and shows them where
|
|
5826
|
+
* they are, the scroll position and the widths of the columns kept; the
|
|
5827
|
+
* table is busy and settles, fails and dispatches as a load does, one
|
|
5828
|
+
* `load:success` per page. When the answers report fewer rows than the
|
|
5829
|
+
* window starts at, it loads the first page instead. A `src` or `method`
|
|
5830
|
+
* write still starts over from the first page.
|
|
5706
5831
|
* @returns {Promise<void>} as `load`
|
|
5707
5832
|
*/
|
|
5708
5833
|
async reload() {
|
|
@@ -5712,6 +5837,9 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
5712
5837
|
}
|
|
5713
5838
|
return;
|
|
5714
5839
|
}
|
|
5840
|
+
if (this.#window) {
|
|
5841
|
+
return await this.#reloadWindow();
|
|
5842
|
+
}
|
|
5715
5843
|
return await this.load(
|
|
5716
5844
|
this.#latestRequest.pageRequest,
|
|
5717
5845
|
this.#latestRequest.sortRequest,
|
|
@@ -5750,6 +5878,40 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
5750
5878
|
* failure arrives it changes nothing, dispatches nothing and resolves;
|
|
5751
5879
|
* superseded while its rows render, it leaves them to the newer load and
|
|
5752
5880
|
* dispatches nothing.
|
|
5881
|
+
*
|
|
5882
|
+
* Under `paging="infinite"` the status bar takes the pager's place, and
|
|
5883
|
+
* once the load has dispatched `load:success` the next page is awaited:
|
|
5884
|
+
* when a row of the last half page comes into view, through
|
|
5885
|
+
* whatever scrolls it, the following page is asked for with the same
|
|
5886
|
+
* size, sort and filters and its rows are appended. An append runs
|
|
5887
|
+
* without `aria-busy`, a spinner in the status bar standing in, and
|
|
5888
|
+
* dispatches `load:success` or `load:failure` as a load does, its
|
|
5889
|
+
* `pageRequest` naming the appended page. A failed append keeps the
|
|
5890
|
+
* rows, shows the error in the status bar with a retry button and
|
|
5891
|
+
* rejects to nobody, nothing awaiting it. Appending stops once the rows
|
|
5892
|
+
* shown reach the total the last answer reported, or an answer carries
|
|
5893
|
+
* no rows. A load supersedes an append in flight; `reload` repeats this
|
|
5894
|
+
* load, not the pages appended to it.
|
|
5895
|
+
*
|
|
5896
|
+
* Under `paging="virtual"` the load shows its page as the window, scrolled
|
|
5897
|
+
* to where it sits among the total, and measures two things on its rows:
|
|
5898
|
+
* the height the empty space standing in for the other rows is sized on,
|
|
5899
|
+
* and the width of the columns, which then stay as they are, in
|
|
5900
|
+
* proportion, until the next load, so that the pages coming and going do
|
|
5901
|
+
* not move them. From then on the window follows the scroll: the page in
|
|
5902
|
+
* view and one on either side stay in the page and the others are dropped,
|
|
5903
|
+
* the page holding the focus kept while it is within two pages of them. A
|
|
5904
|
+
* page next to the window is asked for at once; a scroll landing farther
|
|
5905
|
+
* waits for the scroll to rest. Window pages are asked for without
|
|
5906
|
+
* `aria-busy`, a spinner in the status bar standing in, and dispatch
|
|
5907
|
+
* `load:success` or `load:failure` per page as appends do; a failure keeps
|
|
5908
|
+
* the rows and offers a retry. The row in view stays where it is as pages
|
|
5909
|
+
* come and go above it: the table measures it before and after and
|
|
5910
|
+
* corrects the scroll by whatever the browser's own scroll anchoring has
|
|
5911
|
+
* not kept, which is all of it where the browser anchors no table rows
|
|
5912
|
+
* (Firefox), has no scroll anchoring or the rows' container declares
|
|
5913
|
+
* `overflow-anchor: none`, and nothing where it has. The table carries
|
|
5914
|
+
* `aria-rowcount` and each row its `aria-rowindex`.
|
|
5753
5915
|
* @param {TablePageRequest} pageRequest
|
|
5754
5916
|
* @param {TableSortRequest|null} sortRequest
|
|
5755
5917
|
* @param {Record<string, any>} filterRequest
|
|
@@ -5760,6 +5922,13 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
5760
5922
|
this.#loadRequested = true;
|
|
5761
5923
|
this.#requestedFilters = filterRequest;
|
|
5762
5924
|
const claim = this.#loads.take();
|
|
5925
|
+
this.#watched = [];
|
|
5926
|
+
this.#endWatch?.disconnect();
|
|
5927
|
+
clearTimeout(this.#jump);
|
|
5928
|
+
this.#shifting = null;
|
|
5929
|
+
this.#windowFailed = false;
|
|
5930
|
+
this.#status?.appending.setAttribute('hidden', '');
|
|
5931
|
+
this.#status?.failure.setAttribute('hidden', '');
|
|
5763
5932
|
this.#loading.toggleAttribute('hidden', this.#body.childElementCount > 0);
|
|
5764
5933
|
this.#feedback.setAttribute('hidden', '');
|
|
5765
5934
|
this.#noAutoload.setAttribute('hidden', '');
|
|
@@ -5789,6 +5958,9 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
5789
5958
|
if (claim.stale) {
|
|
5790
5959
|
return;
|
|
5791
5960
|
}
|
|
5961
|
+
if (this.#virtual) {
|
|
5962
|
+
this.#settleWindow();
|
|
5963
|
+
}
|
|
5792
5964
|
this.removeAttribute('aria-busy');
|
|
5793
5965
|
this.dispatchEvent(
|
|
5794
5966
|
new CustomEvent('load:success', {
|
|
@@ -5797,24 +5969,15 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
5797
5969
|
detail: { pageRequest, sortRequest, filterRequest, response: pageResponse },
|
|
5798
5970
|
}),
|
|
5799
5971
|
);
|
|
5972
|
+
this.#watchEnd(pageRequest.size);
|
|
5973
|
+
this.#reconcile(false);
|
|
5800
5974
|
return;
|
|
5801
5975
|
}
|
|
5802
5976
|
} catch (/** @type any */ error) {
|
|
5803
5977
|
if (claim.stale) {
|
|
5804
5978
|
return;
|
|
5805
5979
|
}
|
|
5806
|
-
this.#
|
|
5807
|
-
this.#body.replaceChildren();
|
|
5808
|
-
this.#feedback.removeAttribute('hidden');
|
|
5809
|
-
this.#feedback.querySelector('[data-ref=feedback-error]').textContent = index_mjs.Localization.of().failure(error);
|
|
5810
|
-
this.removeAttribute('aria-busy');
|
|
5811
|
-
this.dispatchEvent(
|
|
5812
|
-
new CustomEvent('load:failure', {
|
|
5813
|
-
bubbles: true,
|
|
5814
|
-
cancelable: false,
|
|
5815
|
-
detail: { pageRequest, sortRequest, filterRequest, exception: error },
|
|
5816
|
-
}),
|
|
5817
|
-
);
|
|
5980
|
+
this.#fail(error, { pageRequest, sortRequest, filterRequest });
|
|
5818
5981
|
throw error;
|
|
5819
5982
|
}
|
|
5820
5983
|
return await this.load({ page: lastPage, size: pageRequest.size }, sortRequest, filterRequest);
|
|
@@ -5845,19 +6008,471 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
5845
6008
|
filterRequest,
|
|
5846
6009
|
);
|
|
5847
6010
|
}
|
|
5848
|
-
|
|
5849
|
-
|
|
6011
|
+
/**
|
|
6012
|
+
* Watches the last half page of rows, so that any of them coming into
|
|
6013
|
+
* view appends the next page, unless the rows shown already reach the
|
|
6014
|
+
* total or the last answer carried none. A single row would be missed by
|
|
6015
|
+
* a scroll jumping past it, to the end for instance.
|
|
6016
|
+
* @param {number} size the rows a page asks for
|
|
6017
|
+
*/
|
|
6018
|
+
#watchEnd(size) {
|
|
6019
|
+
const { shown, total, last } = this.#appended;
|
|
6020
|
+
if (!this.#endWatch || !(shown < total) || last === 0) {
|
|
6021
|
+
return;
|
|
6022
|
+
}
|
|
6023
|
+
const rows = [...this.#body.children];
|
|
6024
|
+
this.#watched = rows.slice(Math.max(0, rows.length - Math.ceil(size / 2)));
|
|
6025
|
+
for (const row of this.#watched) {
|
|
6026
|
+
this.#endWatch.observe(row);
|
|
6027
|
+
}
|
|
6028
|
+
}
|
|
6029
|
+
async #appendNext() {
|
|
6030
|
+
const status = /** @type TableStatus */ (this.#status);
|
|
6031
|
+
this.#watched = [];
|
|
6032
|
+
this.#endWatch?.disconnect();
|
|
6033
|
+
const { sortRequest, filterRequest } = this.#latestRequest;
|
|
6034
|
+
const pageRequest = { page: this.#appended.next, size: this.#latestRequest.pageRequest.size };
|
|
6035
|
+
const claim = this.#loads.take();
|
|
6036
|
+
status.failure.setAttribute('hidden', '');
|
|
6037
|
+
status.appending.removeAttribute('hidden');
|
|
6038
|
+
try {
|
|
6039
|
+
const pageResponse = await this.#loader.load(pageRequest, sortRequest, filterRequest);
|
|
6040
|
+
if (claim.stale) {
|
|
6041
|
+
return;
|
|
6042
|
+
}
|
|
6043
|
+
this.#body.append(
|
|
6044
|
+
this.template('row')
|
|
6045
|
+
.withOverlay({ schema: this.#schema, pageRequest, filterRequest, pageResponse })
|
|
6046
|
+
.render(),
|
|
6047
|
+
);
|
|
6048
|
+
this.#appended = {
|
|
6049
|
+
next: pageRequest.page + 1,
|
|
6050
|
+
shown: this.#appended.shown + pageResponse.data.length,
|
|
6051
|
+
total: pageResponse.size,
|
|
6052
|
+
last: pageResponse.data.length,
|
|
6053
|
+
};
|
|
6054
|
+
status.count.textContent = index_mjs.Localization.of().t('table.loaded', {
|
|
6055
|
+
loaded: this.#appended.shown,
|
|
6056
|
+
total: this.#appended.total,
|
|
6057
|
+
});
|
|
6058
|
+
await index_mjs.Rendering.waitForChildren(this.#body).catch(() => undefined);
|
|
6059
|
+
if (claim.stale) {
|
|
6060
|
+
return;
|
|
6061
|
+
}
|
|
6062
|
+
status.appending.setAttribute('hidden', '');
|
|
6063
|
+
this.dispatchEvent(
|
|
6064
|
+
new CustomEvent('load:success', {
|
|
6065
|
+
bubbles: true,
|
|
6066
|
+
cancelable: false,
|
|
6067
|
+
detail: { pageRequest, sortRequest, filterRequest, response: pageResponse },
|
|
6068
|
+
}),
|
|
6069
|
+
);
|
|
6070
|
+
this.#watchEnd(pageRequest.size);
|
|
6071
|
+
} catch (/** @type any */ error) {
|
|
6072
|
+
if (claim.stale) {
|
|
6073
|
+
return;
|
|
6074
|
+
}
|
|
6075
|
+
status.appending.setAttribute('hidden', '');
|
|
6076
|
+
status.error.textContent = index_mjs.Localization.of().failure(error);
|
|
6077
|
+
status.failure.removeAttribute('hidden');
|
|
6078
|
+
this.dispatchEvent(
|
|
6079
|
+
new CustomEvent('load:failure', {
|
|
6080
|
+
bubbles: true,
|
|
6081
|
+
cancelable: false,
|
|
6082
|
+
detail: { pageRequest, sortRequest, filterRequest, exception: error },
|
|
6083
|
+
}),
|
|
6084
|
+
);
|
|
6085
|
+
}
|
|
6086
|
+
}
|
|
6087
|
+
/**
|
|
6088
|
+
* Renders the rows of a page, numbering them with `aria-rowindex` under
|
|
6089
|
+
* `paging="virtual"`, the header row being the first.
|
|
6090
|
+
* @param {TablePageRequest} pageRequest
|
|
6091
|
+
* @param {Record<string, any>} filterRequest
|
|
6092
|
+
* @param {TablePageResponse} pageResponse
|
|
6093
|
+
* @returns {Element[]}
|
|
6094
|
+
*/
|
|
6095
|
+
#pageRows(pageRequest, filterRequest, pageResponse) {
|
|
6096
|
+
const rows = [
|
|
6097
|
+
...this.template('row')
|
|
6098
|
+
.withOverlay({ schema: this.#schema, pageRequest, filterRequest, pageResponse })
|
|
6099
|
+
.render().children,
|
|
6100
|
+
];
|
|
6101
|
+
if (this.#virtual) {
|
|
6102
|
+
rows.forEach((row, i) => {
|
|
6103
|
+
row.setAttribute('aria-rowindex', String(pageRequest.page * pageRequest.size + i + 2));
|
|
6104
|
+
});
|
|
6105
|
+
}
|
|
6106
|
+
return rows;
|
|
6107
|
+
}
|
|
6108
|
+
/**
|
|
6109
|
+
* Shows a failed load: the rows go, the error panel shows and
|
|
6110
|
+
* `load:failure` is dispatched.
|
|
6111
|
+
* @param {any} error
|
|
6112
|
+
* @param {{ pageRequest: TablePageRequest, sortRequest: TableSortRequest|null, filterRequest: Record<string, any> }} request
|
|
6113
|
+
*/
|
|
6114
|
+
#fail(error, request) {
|
|
5850
6115
|
this.#loading.setAttribute('hidden', '');
|
|
5851
|
-
this.#body.replaceChildren(
|
|
5852
|
-
|
|
5853
|
-
|
|
5854
|
-
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
6116
|
+
this.#body.replaceChildren();
|
|
6117
|
+
this.#window = null;
|
|
6118
|
+
this.#fitSpacers();
|
|
6119
|
+
this.#status?.count.replaceChildren();
|
|
6120
|
+
this.#feedback.removeAttribute('hidden');
|
|
6121
|
+
this.#feedback.querySelector('[data-ref=feedback-error]').textContent = index_mjs.Localization.of().failure(error);
|
|
6122
|
+
this.removeAttribute('aria-busy');
|
|
6123
|
+
this.dispatchEvent(
|
|
6124
|
+
new CustomEvent('load:failure', {
|
|
6125
|
+
bubbles: true,
|
|
6126
|
+
cancelable: false,
|
|
6127
|
+
detail: { ...request, exception: error },
|
|
6128
|
+
}),
|
|
5860
6129
|
);
|
|
6130
|
+
}
|
|
6131
|
+
/** Counts the rows of a load just shown and moves the window where its page sits. */
|
|
6132
|
+
#settleWindow() {
|
|
6133
|
+
const table = /** @type HTMLTableElement */ (this.#body.parentElement);
|
|
6134
|
+
table.setAttribute('aria-rowcount', String(this.#total + 1));
|
|
6135
|
+
table.tHead?.rows[0]?.setAttribute('aria-rowindex', '1');
|
|
6136
|
+
const status = /** @type TableStatus */ (this.#status);
|
|
6137
|
+
const window = this.#window;
|
|
6138
|
+
if (!window) {
|
|
6139
|
+
this.#fitSpacers();
|
|
6140
|
+
status.count.textContent = index_mjs.Localization.of().t('table.loaded', { loaded: 0, total: this.#total });
|
|
6141
|
+
return;
|
|
6142
|
+
}
|
|
6143
|
+
const size = this.#latestRequest.pageRequest.size;
|
|
6144
|
+
const from = window.first * size;
|
|
6145
|
+
status.count.textContent = index_mjs.Localization.of().t('table.range', {
|
|
6146
|
+
from: from + 1,
|
|
6147
|
+
to: from + /** @type Element[] */ (window.pages.get(window.first)).length,
|
|
6148
|
+
total: this.#total,
|
|
6149
|
+
});
|
|
6150
|
+
if (this.#measureRows()) {
|
|
6151
|
+
this.#wrapper.scrollTop = from * this.#rowHeight;
|
|
6152
|
+
}
|
|
6153
|
+
}
|
|
6154
|
+
/**
|
|
6155
|
+
* Measures the height of a row and locks the widths of the columns, once
|
|
6156
|
+
* per load, on the rows it shows, then sizes the spacers. A table that is not laid out, inside a
|
|
6157
|
+
* closed disclosure for instance, measures nothing and is measured when
|
|
6158
|
+
* its rows next scroll.
|
|
6159
|
+
* @returns {boolean} whether the rows are measured
|
|
6160
|
+
*/
|
|
6161
|
+
#measureRows() {
|
|
6162
|
+
if (this.#rowHeight > 0) {
|
|
6163
|
+
return true;
|
|
6164
|
+
}
|
|
6165
|
+
const count = this.#body.childElementCount;
|
|
6166
|
+
const height = this.#body.getBoundingClientRect().height;
|
|
6167
|
+
if (count === 0 || height === 0) {
|
|
6168
|
+
return false;
|
|
6169
|
+
}
|
|
6170
|
+
this.#rowHeight = height / count;
|
|
6171
|
+
this.#lockColumns();
|
|
6172
|
+
this.#fitSpacers();
|
|
6173
|
+
return true;
|
|
6174
|
+
}
|
|
6175
|
+
/**
|
|
6176
|
+
* Fixes the widths of the columns, in proportion to the table, at those
|
|
6177
|
+
* the rows in the page give them.
|
|
6178
|
+
*/
|
|
6179
|
+
#lockColumns() {
|
|
6180
|
+
const table = /** @type HTMLTableElement */ (this.#body.parentElement);
|
|
6181
|
+
const widths = [...(table.tHead?.rows[0]?.cells ?? [])].map((cell) => cell.getBoundingClientRect().width);
|
|
6182
|
+
const sum = widths.reduce((a, b) => a + b, 0);
|
|
6183
|
+
const columns = document.createElement('colgroup');
|
|
6184
|
+
columns.setAttribute('data-ref', 'widths');
|
|
6185
|
+
for (const width of widths) {
|
|
6186
|
+
const column = document.createElement('col');
|
|
6187
|
+
column.style.width = `${(width / sum) * 100}%`;
|
|
6188
|
+
columns.append(column);
|
|
6189
|
+
}
|
|
6190
|
+
table.insertBefore(columns, table.tHead);
|
|
6191
|
+
}
|
|
6192
|
+
#unlockColumns() {
|
|
6193
|
+
this.#body.parentElement?.querySelector(':scope > colgroup[data-ref=widths]')?.remove();
|
|
6194
|
+
}
|
|
6195
|
+
/** Sizes the space standing in for the rows above and below the window. */
|
|
6196
|
+
#fitSpacers() {
|
|
6197
|
+
if (!this.#spacers) {
|
|
6198
|
+
return;
|
|
6199
|
+
}
|
|
6200
|
+
const window = this.#window;
|
|
6201
|
+
const size = this.#latestRequest.pageRequest.size;
|
|
6202
|
+
const above = window ? window.first * size : 0;
|
|
6203
|
+
const below = window ? Math.max(0, this.#total - (window.last + 1) * size) : 0;
|
|
6204
|
+
this.#spacers.before.style.height = `${above * this.#rowHeight}px`;
|
|
6205
|
+
this.#spacers.after.style.height = `${below * this.#rowHeight}px`;
|
|
6206
|
+
}
|
|
6207
|
+
/** Says in the status bar which rows are in view; nothing changes while none is. */
|
|
6208
|
+
#countInView() {
|
|
6209
|
+
const status = /** @type TableStatus */ (this.#status);
|
|
6210
|
+
const size = this.#latestRequest.pageRequest.size;
|
|
6211
|
+
const view = this.#wrapper.getBoundingClientRect();
|
|
6212
|
+
const top = view.top + (this.#body.parentElement?.querySelector(':scope > thead')?.getBoundingClientRect().height ?? 0);
|
|
6213
|
+
let from = 0;
|
|
6214
|
+
let to = 0;
|
|
6215
|
+
for (const [page, rows] of this.#window?.pages ?? []) {
|
|
6216
|
+
rows.forEach((row, i) => {
|
|
6217
|
+
const rect = row.getBoundingClientRect();
|
|
6218
|
+
if (rect.bottom > top && rect.top < view.bottom) {
|
|
6219
|
+
from ||= page * size + i + 1;
|
|
6220
|
+
to = page * size + i + 1;
|
|
6221
|
+
}
|
|
6222
|
+
});
|
|
6223
|
+
}
|
|
6224
|
+
if (from > 0) {
|
|
6225
|
+
status.count.textContent = index_mjs.Localization.of().t('table.range', { from, to, total: this.#total });
|
|
6226
|
+
}
|
|
6227
|
+
}
|
|
6228
|
+
/**
|
|
6229
|
+
* The page at the middle of the view: found among the rows in the page,
|
|
6230
|
+
* or estimated on the height of a row when the middle falls in the space
|
|
6231
|
+
* standing in for others.
|
|
6232
|
+
* @param {{ first: number, last: number, pages: Map<number, Element[]> }} window
|
|
6233
|
+
* @returns {number}
|
|
6234
|
+
*/
|
|
6235
|
+
#pageInView(window) {
|
|
6236
|
+
const pageHeight = this.#latestRequest.pageRequest.size * this.#rowHeight;
|
|
6237
|
+
const view = this.#wrapper.getBoundingClientRect();
|
|
6238
|
+
const middle = view.top + view.height / 2;
|
|
6239
|
+
const rows = this.#body.getBoundingClientRect();
|
|
6240
|
+
if (middle < rows.top) {
|
|
6241
|
+
return Math.max(0, window.first - Math.ceil((rows.top - middle) / pageHeight));
|
|
6242
|
+
}
|
|
6243
|
+
if (middle >= rows.bottom) {
|
|
6244
|
+
return window.last + 1 + Math.floor((middle - rows.bottom) / pageHeight);
|
|
6245
|
+
}
|
|
6246
|
+
let found = window.first;
|
|
6247
|
+
for (const [page, pageRows] of window.pages) {
|
|
6248
|
+
if (pageRows.length > 0 && pageRows[0].getBoundingClientRect().top <= middle) {
|
|
6249
|
+
found = page;
|
|
6250
|
+
}
|
|
6251
|
+
}
|
|
6252
|
+
return found;
|
|
6253
|
+
}
|
|
6254
|
+
/**
|
|
6255
|
+
* Moves the window to the page in view and its neighbours. A window next
|
|
6256
|
+
* to the current one moves at once; one farther away waits for the scroll
|
|
6257
|
+
* to rest, unless `settled` says it has.
|
|
6258
|
+
* @param {boolean} settled
|
|
6259
|
+
*/
|
|
6260
|
+
#reconcile(settled) {
|
|
6261
|
+
const window = this.#window;
|
|
6262
|
+
const pageCount = Math.ceil(this.#total / this.#latestRequest.pageRequest.size);
|
|
6263
|
+
if (
|
|
6264
|
+
!window ||
|
|
6265
|
+
this.#shifting ||
|
|
6266
|
+
this.#windowFailed ||
|
|
6267
|
+
this.hasAttribute('aria-busy') ||
|
|
6268
|
+
!this.#wrapper.isConnected ||
|
|
6269
|
+
!(pageCount > 0) ||
|
|
6270
|
+
!this.#measureRows()
|
|
6271
|
+
) {
|
|
6272
|
+
return;
|
|
6273
|
+
}
|
|
6274
|
+
const target = Math.min(pageCount - 1, this.#pageInView(window));
|
|
6275
|
+
let first = Math.max(0, target - 1);
|
|
6276
|
+
let last = Math.min(pageCount - 1, target + 1);
|
|
6277
|
+
const focused = this.#body.contains(document.activeElement)
|
|
6278
|
+
? /** @type Element */ (document.activeElement).closest('tr')
|
|
6279
|
+
: null;
|
|
6280
|
+
const focusedPage = [...window.pages].find(([, rows]) => focused && rows.includes(focused))?.[0];
|
|
6281
|
+
if (focusedPage !== undefined && Math.max(last, focusedPage) - Math.min(first, focusedPage) <= 4) {
|
|
6282
|
+
first = Math.min(first, focusedPage);
|
|
6283
|
+
last = Math.max(last, focusedPage);
|
|
6284
|
+
}
|
|
6285
|
+
if (first === window.first && last === window.last) {
|
|
6286
|
+
return;
|
|
6287
|
+
}
|
|
6288
|
+
clearTimeout(this.#jump);
|
|
6289
|
+
if (!settled && (last < window.first - 1 || first > window.last + 1)) {
|
|
6290
|
+
this.#jump = setTimeout(() => this.#reconcile(true), 150);
|
|
6291
|
+
return;
|
|
6292
|
+
}
|
|
6293
|
+
this.#shiftWindow(window, first, last);
|
|
6294
|
+
}
|
|
6295
|
+
/**
|
|
6296
|
+
* Asks for the pages of the new window that are not in the page, drops
|
|
6297
|
+
* the ones outside it and keeps the row in view where it is.
|
|
6298
|
+
* @param {{ first: number, last: number, pages: Map<number, Element[]> }} window
|
|
6299
|
+
* @param {number} first
|
|
6300
|
+
* @param {number} last
|
|
6301
|
+
*/
|
|
6302
|
+
async #shiftWindow(window, first, last) {
|
|
6303
|
+
const status = /** @type TableStatus */ (this.#status);
|
|
6304
|
+
const claim = this.#loads.hold();
|
|
6305
|
+
this.#shifting = claim;
|
|
6306
|
+
const { sortRequest, filterRequest } = this.#latestRequest;
|
|
6307
|
+
const size = this.#latestRequest.pageRequest.size;
|
|
6308
|
+
const wanted = Array.from({ length: last - first + 1 }, (_, i) => first + i);
|
|
6309
|
+
const missing = wanted.filter((page) => !window.pages.has(page));
|
|
6310
|
+
status.failure.setAttribute('hidden', '');
|
|
6311
|
+
status.appending.removeAttribute('hidden');
|
|
6312
|
+
try {
|
|
6313
|
+
const answers = await Promise.all(
|
|
6314
|
+
missing.map(async (page) => {
|
|
6315
|
+
const pageRequest = { page, size };
|
|
6316
|
+
return { pageRequest, response: await this.#loader.load(pageRequest, sortRequest, filterRequest) };
|
|
6317
|
+
}),
|
|
6318
|
+
);
|
|
6319
|
+
if (claim.stale) {
|
|
6320
|
+
return;
|
|
6321
|
+
}
|
|
6322
|
+
const kept = wanted.filter((page) => window.pages.has(page));
|
|
6323
|
+
const top = this.#wrapper.getBoundingClientRect().top;
|
|
6324
|
+
const anchor = kept
|
|
6325
|
+
.flatMap((page) => window.pages.get(page) ?? [])
|
|
6326
|
+
.find((row) => row.getBoundingClientRect().bottom > top);
|
|
6327
|
+
const anchorTop = anchor?.getBoundingClientRect().top ?? 0;
|
|
6328
|
+
for (const [page, rows] of window.pages) {
|
|
6329
|
+
if (page < first || page > last) {
|
|
6330
|
+
for (const row of rows) {
|
|
6331
|
+
row.remove();
|
|
6332
|
+
}
|
|
6333
|
+
}
|
|
6334
|
+
}
|
|
6335
|
+
/** @type {Map<number, Element[]>} */
|
|
6336
|
+
const pages = new Map();
|
|
6337
|
+
for (const page of wanted) {
|
|
6338
|
+
const answer = answers.find((a) => a.pageRequest.page === page);
|
|
6339
|
+
pages.set(
|
|
6340
|
+
page,
|
|
6341
|
+
window.pages.get(page) ??
|
|
6342
|
+
(answer ? this.#pageRows(answer.pageRequest, filterRequest, answer.response) : []),
|
|
6343
|
+
);
|
|
6344
|
+
}
|
|
6345
|
+
if (kept.length === 0) {
|
|
6346
|
+
this.#body.replaceChildren(...[...pages.values()].flat());
|
|
6347
|
+
} else {
|
|
6348
|
+
this.#body.prepend(...wanted.filter((page) => page < kept[0]).flatMap((page) => pages.get(page) ?? []));
|
|
6349
|
+
this.#body.append(
|
|
6350
|
+
...wanted.filter((page) => page > /** @type number */ (kept.at(-1))).flatMap((page) => pages.get(page) ?? []),
|
|
6351
|
+
);
|
|
6352
|
+
}
|
|
6353
|
+
this.#window = { first, last, pages };
|
|
6354
|
+
this.#total = answers.at(-1)?.response.size ?? this.#total;
|
|
6355
|
+
this.#body.parentElement?.setAttribute('aria-rowcount', String(this.#total + 1));
|
|
6356
|
+
this.#fitSpacers();
|
|
6357
|
+
if (anchor) {
|
|
6358
|
+
this.#wrapper.scrollTop += anchor.getBoundingClientRect().top - anchorTop;
|
|
6359
|
+
}
|
|
6360
|
+
await index_mjs.Rendering.waitForChildren(this.#body).catch(() => undefined);
|
|
6361
|
+
if (claim.stale) {
|
|
6362
|
+
return;
|
|
6363
|
+
}
|
|
6364
|
+
if (anchor) {
|
|
6365
|
+
// the elements in the new rows have rendered since, which can change their height
|
|
6366
|
+
this.#wrapper.scrollTop += anchor.getBoundingClientRect().top - anchorTop;
|
|
6367
|
+
}
|
|
6368
|
+
status.appending.setAttribute('hidden', '');
|
|
6369
|
+
this.#countInView();
|
|
6370
|
+
for (const { pageRequest, response } of answers) {
|
|
6371
|
+
this.dispatchEvent(
|
|
6372
|
+
new CustomEvent('load:success', {
|
|
6373
|
+
bubbles: true,
|
|
6374
|
+
cancelable: false,
|
|
6375
|
+
detail: { pageRequest, sortRequest, filterRequest, response },
|
|
6376
|
+
}),
|
|
6377
|
+
);
|
|
6378
|
+
}
|
|
6379
|
+
} catch (/** @type any */ error) {
|
|
6380
|
+
if (claim.stale) {
|
|
6381
|
+
return;
|
|
6382
|
+
}
|
|
6383
|
+
this.#windowFailed = true;
|
|
6384
|
+
status.appending.setAttribute('hidden', '');
|
|
6385
|
+
status.error.textContent = index_mjs.Localization.of().failure(error);
|
|
6386
|
+
status.failure.removeAttribute('hidden');
|
|
6387
|
+
this.dispatchEvent(
|
|
6388
|
+
new CustomEvent('load:failure', {
|
|
6389
|
+
bubbles: true,
|
|
6390
|
+
cancelable: false,
|
|
6391
|
+
detail: { pageRequest: { page: missing[0], size }, sortRequest, filterRequest, exception: error },
|
|
6392
|
+
}),
|
|
6393
|
+
);
|
|
6394
|
+
return;
|
|
6395
|
+
} finally {
|
|
6396
|
+
if (this.#shifting === claim) {
|
|
6397
|
+
this.#shifting = null;
|
|
6398
|
+
}
|
|
6399
|
+
}
|
|
6400
|
+
this.#reconcile(false);
|
|
6401
|
+
}
|
|
6402
|
+
/**
|
|
6403
|
+
* Asks again for the pages of the window and shows them where they are.
|
|
6404
|
+
* @returns {Promise<void>}
|
|
6405
|
+
*/
|
|
6406
|
+
async #reloadWindow() {
|
|
6407
|
+
const window = /** @type {{ first: number, last: number, pages: Map<number, Element[]> }} */ (this.#window);
|
|
6408
|
+
const status = /** @type TableStatus */ (this.#status);
|
|
6409
|
+
const { sortRequest, filterRequest } = this.#latestRequest;
|
|
6410
|
+
const size = this.#latestRequest.pageRequest.size;
|
|
6411
|
+
this.#requestedFilters = filterRequest;
|
|
6412
|
+
const claim = this.#loads.take();
|
|
6413
|
+
clearTimeout(this.#jump);
|
|
6414
|
+
this.#shifting = null;
|
|
6415
|
+
this.#windowFailed = false;
|
|
6416
|
+
status.appending.setAttribute('hidden', '');
|
|
6417
|
+
status.failure.setAttribute('hidden', '');
|
|
6418
|
+
this.setAttribute('aria-busy', 'true');
|
|
6419
|
+
const wanted = Array.from({ length: window.last - window.first + 1 }, (_, i) => window.first + i);
|
|
6420
|
+
try {
|
|
6421
|
+
const answers = await Promise.all(
|
|
6422
|
+
wanted.map(async (page) => {
|
|
6423
|
+
const pageRequest = { page, size };
|
|
6424
|
+
return { pageRequest, response: await this.#loader.load(pageRequest, sortRequest, filterRequest) };
|
|
6425
|
+
}),
|
|
6426
|
+
);
|
|
6427
|
+
if (claim.stale) {
|
|
6428
|
+
return;
|
|
6429
|
+
}
|
|
6430
|
+
const total = /** @type {{ response: TablePageResponse }} */ (answers.at(-1)).response.size;
|
|
6431
|
+
const pageCount = Math.ceil(total / size);
|
|
6432
|
+
if (!(window.first < pageCount)) {
|
|
6433
|
+
return await this.load({ page: 0, size }, sortRequest, filterRequest);
|
|
6434
|
+
}
|
|
6435
|
+
const shown = answers.filter((a) => a.pageRequest.page < pageCount);
|
|
6436
|
+
const pages = new Map(
|
|
6437
|
+
shown.map((a) => [a.pageRequest.page, this.#pageRows(a.pageRequest, filterRequest, a.response)]),
|
|
6438
|
+
);
|
|
6439
|
+
this.#body.replaceChildren(...[...pages.values()].flat());
|
|
6440
|
+
this.#window = { first: window.first, last: Math.min(window.last, pageCount - 1), pages };
|
|
6441
|
+
this.#total = total;
|
|
6442
|
+
this.#body.parentElement?.setAttribute('aria-rowcount', String(total + 1));
|
|
6443
|
+
this.#fitSpacers();
|
|
6444
|
+
await index_mjs.Rendering.waitForChildren(this.#body).catch(() => undefined);
|
|
6445
|
+
if (claim.stale) {
|
|
6446
|
+
return;
|
|
6447
|
+
}
|
|
6448
|
+
this.removeAttribute('aria-busy');
|
|
6449
|
+
this.#countInView();
|
|
6450
|
+
for (const { pageRequest, response } of shown) {
|
|
6451
|
+
this.dispatchEvent(
|
|
6452
|
+
new CustomEvent('load:success', {
|
|
6453
|
+
bubbles: true,
|
|
6454
|
+
cancelable: false,
|
|
6455
|
+
detail: { pageRequest, sortRequest, filterRequest, response },
|
|
6456
|
+
}),
|
|
6457
|
+
);
|
|
6458
|
+
}
|
|
6459
|
+
this.#reconcile(false);
|
|
6460
|
+
} catch (/** @type any */ error) {
|
|
6461
|
+
if (claim.stale) {
|
|
6462
|
+
return;
|
|
6463
|
+
}
|
|
6464
|
+
this.#fail(error, { pageRequest: { page: window.first, size }, sortRequest, filterRequest });
|
|
6465
|
+
throw error;
|
|
6466
|
+
}
|
|
6467
|
+
}
|
|
6468
|
+
#update(pageRequest, sortRequest, filterRequest, pageResponse) {
|
|
6469
|
+
this.#loading.setAttribute('hidden', '');
|
|
6470
|
+
if (this.#virtual) {
|
|
6471
|
+
this.#rowHeight = 0;
|
|
6472
|
+
this.#unlockColumns();
|
|
6473
|
+
}
|
|
6474
|
+
const rows = this.#pageRows(pageRequest, filterRequest, pageResponse);
|
|
6475
|
+
this.#body.replaceChildren(...rows);
|
|
5861
6476
|
this.#empty.toggleAttribute('hidden', pageResponse.data.length !== 0);
|
|
5862
6477
|
if (this.#emptyTemplate && pageResponse.data.length === 0) {
|
|
5863
6478
|
this.#emptyAuthors.replaceChildren(
|
|
@@ -5866,7 +6481,28 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
5866
6481
|
.render(),
|
|
5867
6482
|
);
|
|
5868
6483
|
}
|
|
5869
|
-
this.#
|
|
6484
|
+
if (this.#virtual) {
|
|
6485
|
+
this.#wrapper.scrollTop = 0;
|
|
6486
|
+
this.#total = pageResponse.size;
|
|
6487
|
+
this.#window =
|
|
6488
|
+
rows.length > 0 ? { first: pageRequest.page, last: pageRequest.page, pages: new Map([[pageRequest.page, rows]]) } : null;
|
|
6489
|
+
return;
|
|
6490
|
+
}
|
|
6491
|
+
if (this.#status) {
|
|
6492
|
+
this.#wrapper.scrollTop = 0;
|
|
6493
|
+
this.#appended = {
|
|
6494
|
+
next: pageRequest.page + 1,
|
|
6495
|
+
shown: pageResponse.data.length,
|
|
6496
|
+
total: pageResponse.size,
|
|
6497
|
+
last: pageResponse.data.length,
|
|
6498
|
+
};
|
|
6499
|
+
this.#status.count.textContent = index_mjs.Localization.of().t('table.loaded', {
|
|
6500
|
+
loaded: this.#appended.shown,
|
|
6501
|
+
total: this.#appended.total,
|
|
6502
|
+
});
|
|
6503
|
+
return;
|
|
6504
|
+
}
|
|
6505
|
+
this.#paginator.update({ current: pageRequest.page, total: Math.ceil(pageResponse.size / pageRequest.size) });
|
|
5870
6506
|
}
|
|
5871
6507
|
}
|
|
5872
6508
|
|
|
@@ -8833,6 +9469,9 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
8833
9469
|
'table.initial': 'Start searching to see results.',
|
|
8834
9470
|
'table.error': 'Error while loading data:',
|
|
8835
9471
|
'table.no-data': 'No elements found.',
|
|
9472
|
+
'table.loaded': 'Showing {loaded} of {total}',
|
|
9473
|
+
'table.range': 'Rows {from}-{to} of {total}',
|
|
9474
|
+
'table.retry': 'Retry',
|
|
8836
9475
|
'dropdown.empty': 'No results',
|
|
8837
9476
|
'select.remove': 'Remove',
|
|
8838
9477
|
'files.dropzone-label': 'Click or drop your files here',
|
|
@@ -8884,6 +9523,9 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
8884
9523
|
'table.initial': 'Avvia la ricerca per visualizzare i risultati.',
|
|
8885
9524
|
'table.error': 'Errore nel caricamento dei dati:',
|
|
8886
9525
|
'table.no-data': 'Nessun elemento trovato.',
|
|
9526
|
+
'table.loaded': 'Mostrati {loaded} di {total}',
|
|
9527
|
+
'table.range': 'Righe {from}-{to} di {total}',
|
|
9528
|
+
'table.retry': 'Riprova',
|
|
8887
9529
|
'dropdown.empty': 'Nessun risultato',
|
|
8888
9530
|
'select.remove': 'Rimuovi',
|
|
8889
9531
|
'files.dropzone-label': 'Clicca o trascina i file qui',
|
|
@@ -8935,6 +9577,9 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
8935
9577
|
'table.initial': 'Inicia la búsqueda para ver los resultados.',
|
|
8936
9578
|
'table.error': 'Error al cargar los datos:',
|
|
8937
9579
|
'table.no-data': 'No se encontraron elementos.',
|
|
9580
|
+
'table.loaded': 'Mostrando {loaded} de {total}',
|
|
9581
|
+
'table.range': 'Filas {from}-{to} de {total}',
|
|
9582
|
+
'table.retry': 'Reintentar',
|
|
8938
9583
|
'dropdown.empty': 'Sin resultados',
|
|
8939
9584
|
'select.remove': 'Eliminar',
|
|
8940
9585
|
'files.dropzone-label': 'Haz clic o arrastra tus archivos aquí',
|
|
@@ -8989,6 +9634,9 @@ var ful = (function (exports, index_mjs, index_mjs$1) {
|
|
|
8989
9634
|
'table.initial': 'Lancez la recherche pour voir les résultats.',
|
|
8990
9635
|
'table.error': 'Erreur lors du chargement des données :',
|
|
8991
9636
|
'table.no-data': 'Aucun élément trouvé.',
|
|
9637
|
+
'table.loaded': 'Affichage de {loaded} sur {total}',
|
|
9638
|
+
'table.range': 'Lignes {from} à {to} sur {total}',
|
|
9639
|
+
'table.retry': 'Réessayer',
|
|
8992
9640
|
'dropdown.empty': 'Aucun résultat',
|
|
8993
9641
|
'select.remove': 'Retirer',
|
|
8994
9642
|
'files.dropzone-label': 'Cliquez ou déposez vos fichiers ici',
|