Skip to content

Commit 1f285ef

Browse files
authored
docs(ui5-table): test page for the virtualization (#10380)
* feat(ui5-table): Table row actions * docs(ui5-table): test page for the virtualization
1 parent 37af390 commit 1f285ef

File tree

7 files changed

+216
-90
lines changed

7 files changed

+216
-90
lines changed

packages/main/src/Table.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,11 @@ interface ITableFeature extends UI5Element {
4242
* Called when the table is activated.
4343
* @param table table instance
4444
*/
45-
onTableActivate(table: Table): void;
45+
onTableActivate?(table: Table): void;
4646
/**
4747
* Called when the table finished rendering.
4848
*/
49-
onTableAfterRendering?(): void;
49+
onTableAfterRendering?(table?: Table): void;
5050
}
5151

5252
/**
@@ -363,7 +363,7 @@ class Table extends UI5Element {
363363
ResizeHandler.register(this, this._onResizeBound);
364364
}
365365
this._events.forEach(eventType => this.addEventListener(eventType, this._onEventBound));
366-
this.features.forEach(feature => feature.onTableActivate(this));
366+
this.features.forEach(feature => feature.onTableActivate?.(this));
367367
this._tableNavigation = new TableNavigation(this);
368368
this._tableDragAndDrop = new TableDragAndDrop(this);
369369
}
@@ -391,7 +391,7 @@ class Table extends UI5Element {
391391
}
392392

393393
onAfterRendering(): void {
394-
this.features.forEach(feature => feature.onTableAfterRendering?.());
394+
this.features.forEach(feature => feature.onTableAfterRendering?.(this));
395395
}
396396

397397
_getSelection(): TableSelection | undefined {
@@ -504,7 +504,7 @@ class Table extends UI5Element {
504504
}
505505

506506
_isFeature(feature: any) {
507-
return Boolean(feature.onTableActivate && feature.onTableAfterRendering);
507+
return Boolean(feature.onTableActivate || feature.onTableAfterRendering);
508508
}
509509

510510
_isGrowingFeature(feature: any) {

packages/main/src/TableVirtualizer.ts

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -125,22 +125,20 @@ class TableVirtualizer extends UI5Element implements ITableFeature {
125125
this._onRowInvalidateBound = this._onRowInvalidate.bind(this);
126126
}
127127

128-
onTableActivate(table: Table): void {
129-
this._table = table;
130-
this._scrollContainer.addEventListener("scroll", this._onScrollBound, { passive: true });
131-
this._onScroll();
132-
}
133-
134128
onAfterRendering(): void {
135129
this._table && this._table._invalidate++;
136130
}
137131

138-
onTableAfterRendering(): void {
132+
onTableAfterRendering(table: Table): void {
139133
if (!this._table) {
140-
return;
134+
this._table = table;
135+
this._scrollContainer.addEventListener("scroll", this._onScrollBound, { passive: true });
136+
this._updateRowsHeight();
137+
this._onScroll();
138+
} else {
139+
this._updateRowsHeight();
141140
}
142141

143-
this._updateRowsHeight();
144142
if (this._tabBlockingState & TabBlocking.Released) {
145143
const tabBlockingRow = this._table.rows.at(this._tabBlockingState & TabBlocking.Next ? -1 : 0) as HTMLElement;
146144
const tabForwardingElement = getTabbableElements(tabBlockingRow).at(this._tabBlockingState & TabBlocking.Next ? 0 : -1);
@@ -161,10 +159,12 @@ class TableVirtualizer extends UI5Element implements ITableFeature {
161159
reset(): void {
162160
this._lastRowPosition = -1;
163161
this._firstRowPosition = -1;
164-
if (this._scrollContainer.scrollTop > 0) {
165-
this._scrollContainer.scrollTop = 0;
166-
} else {
167-
this._onScroll();
162+
if (this._table) {
163+
if (this._scrollContainer.scrollTop > 0) {
164+
this._scrollContainer.scrollTop = 0;
165+
} else {
166+
this._onScroll();
167+
}
168168
}
169169
}
170170

@@ -177,11 +177,7 @@ class TableVirtualizer extends UI5Element implements ITableFeature {
177177
}
178178

179179
_onScroll(): void {
180-
if (!this._table) {
181-
return;
182-
}
183-
184-
const headerRow = this._table.headerRow[0];
180+
const headerRow = this._table!.headerRow[0];
185181
const headerHeight = headerRow.offsetHeight;
186182
let scrollTop = this._scrollContainer.scrollTop;
187183
let scrollableHeight = this._scrollContainer.clientHeight;

packages/main/test/pages/TableVirtualizer.html

Lines changed: 66 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -15,85 +15,84 @@
1515
</style>
1616
</head>
1717

18-
<body>
19-
<!-- toolbar with ui5-bar -->
20-
<ui5-bar design="Header" accessible-name-ref="title" style="position: sticky; top: 0; z-index: 2; height: 50px;">
21-
<ui5-title tabindex="0" level="H3" id="title" slot="startContent">My Selectable Products (1000)</ui5-title>
22-
<ui5-slider id="slider" min="0" max="100" step="1" value="100"
23-
label-interval="0"></ui5-slider>
24-
</ui5-bar>
18+
<body style="background-color: var(--sapBackgroundColor)" class="ui5-content-density-compact">
19+
<div class="section">
20+
<ui5-table id="table" loading-delay="100" style="height: 150px;">
21+
<ui5-table-virtualizer id="virtualizer" slot="features" row-count="1000" row-height="32"></ui5-table-virtualizer>
22+
<ui5-table-selection slot="features"></ui5-table-selection>
23+
<ui5-table-header-row slot="headerRow" sticky>
24+
<ui5-table-header-cell width="300px">Product Name</ui5-table-header-cell>
25+
<ui5-table-header-cell>Dimensions</ui5-table-header-cell>
26+
<ui5-table-header-cell>Weight</ui5-table-header-cell>
27+
<ui5-table-header-cell horizontal-align="Right">Price</ui5-table-header-cell>
28+
</ui5-table-header-row>
29+
</ui5-table>
30+
<template id="rowTemplate">
31+
<ui5-table-row position="-1" row-key="-1">
32+
<ui5-table-cell data="name"></ui5-table-cell>
33+
<ui5-table-cell data="height"></ui5-table-cell>
34+
<ui5-table-cell data="weight"></ui5-table-cell>
35+
<ui5-table-cell data="price"></ui5-table-cell>
36+
</ui5-table-row>
37+
</template>
38+
</div>
2539

26-
<ui5-table id="table" loading-delay="0" accessible-name-ref="title" no-data-text="No data found" overflow-mode="Scroll" style="height: 375px;">
27-
<ui5-table-virtualizer id="virtualizer" slot="features" row-height="51" row-count="10"></ui5-table-virtualizer>
28-
<ui5-table-selection id="selection" selected="2 5" slot="features"></ui5-table-selection>
29-
<ui5-table-header-row slot="headerRow" sticky>
30-
<ui5-table-header-cell width="200px" id="produtCol">Product</ui5-table-header-cell>
31-
<ui5-table-header-cell width="max-content" id="supplierCol">Supplier</ui5-table-header-cell>
32-
<ui5-table-header-cell width="max-content" id="dimensionsCol">Dimensions</ui5-table-header-cell>
33-
<ui5-table-header-cell width="max-content" id="weightCol">Weight</ui5-table-header-cell>
34-
<ui5-table-header-cell width="100px" id="priceCol">Price</ui5-table-header-cell>
35-
</ui5-table-heπader-row>
36-
</ui5-table>
37-
38-
<ui5-input value="after table" data-sap-ui-fastnavgroup="true"></ui5-input>
3940
<script>
40-
const slider = document.getElementById("slider");
41-
const table = document.getElementById("table");
42-
let timer = 0;
43-
slider.addEventListener("input", (e) => {
44-
table.style.width = `${e.target.value}%`;
45-
});
46-
table.addEventListener("row-click", (e) => {
47-
console.log(`${Date.now()}: Row with the row-key ${e.detail.row.row-key} is clicked`);
48-
});
4941

50-
const virtualizerData = [];
51-
for (let i = 0; i <= 1000; i++) {
52-
virtualizerData.push(Math.random() * 200 + 32);
53-
}
42+
class ProductStore {
43+
constructor() {
44+
this.products = [];
45+
}
5446

55-
const virtualizer = document.getElementById("virtualizer");
56-
virtualizer.addEventListener("range-change", (e) => {
47+
async fetchProducts(first, last) {
48+
const products = [];
49+
for (let i = first; i < last; i++) {
50+
this.products[i] ??= await this.fetchProduct(i);
51+
products.push(this.products[i]);
52+
}
53+
return products;
54+
}
5755

58-
if (table.rows.length) {
59-
clearTimeout(timer);
60-
table.loading = true;
61-
timer = setTimeout(() => {
62-
for (let i = e.detail.first; i < e.detail.last; i++) {
63-
const index = i - e.detail.first;
64-
const content = table.rows[index].cells[0];
65-
table.rows[index].rowKey = i + "";
66-
table.rows[index].position = i;
67-
content.querySelector("b").firstChild.nodeValue = `Notebook Basic ${i}`;
68-
content.querySelector("a").firstChild.nodeValue = `HT-100${i}`;
69-
}
70-
table.loading = false;
71-
}, 500);
72-
return;
56+
async fetchProduct(index) {
57+
return new Promise(resolve => {
58+
setTimeout(() => {
59+
resolve({
60+
key: `P${index}`,
61+
name: `Product ${index}`,
62+
height: `${(Math.random() * 100).toFixed(0)} cm`,
63+
weight: `${(Math.random() * 100).toFixed(1)} KG`,
64+
price: `${(Math.random() * 1000).toFixed(2)} EUR`
65+
});
66+
}, Math.random() * 10); // Simulate network delay
67+
});
7368
}
69+
}
7470

75-
table.rows.forEach(row => row.remove());
71+
const productStore = new ProductStore();
72+
const table = document.getElementById("table");
73+
const rowTemplate = document.getElementById("rowTemplate");
74+
const virtualizer = document.getElementById("virtualizer");
7675

77-
for (let i = e.detail.first; i < e.detail.last; i++) {
78-
const newRow = document.createElement("ui5-table-row");
79-
newRow.setAttribute("row-key", i.toString());
80-
newRow.position = i;
81-
newRow.innerHTML = `
82-
<ui5-table-cell><ui5-label><b>Notebook Basic ${i}</b><br><a href="#">HT-100${i}</a></ui5-label></ui5-table-cell>
83-
<ui5-table-cell><ui5-label>Technocom</ui5-label></ui5-table-cell>
84-
<ui5-table-cell><ui5-input></ui5-input></ui5-table-cell>
85-
<ui5-table-cell><ui5-label style="color: #2b7c2b"><b>3.7</b> KG</ui5-label></ui5-table-cell>
86-
<ui5-table-cell><ui5-label><b>29</b> EUR</ui5-label></ui5-table-cell>
87-
`;
88-
table.appendChild(newRow);
76+
virtualizer.addEventListener("range-change", async (e) => {
77+
const { first, last } = e.detail;
78+
const products = await productStore.fetchProducts(first, last);
79+
for (let i = first; i < last; i++) {
80+
const rowIndex = i - first;
81+
const product = products[rowIndex];
82+
const row = table.rows[rowIndex] || table.appendChild(rowTemplate.content.firstElementChild.cloneNode(true));
83+
row.setAttribute("position", i);
84+
row.setAttribute("row-key", product.key);
85+
row.querySelectorAll("[data]").forEach(el => {
86+
el.textContent = product[el.getAttribute("data")];
87+
});
88+
}
89+
for (let i = last; i < table.rows.length; i++) {
90+
table.rows[i].remove();
8991
}
9092
});
9193

92-
const selection = document.getElementById("selection");
93-
selection.addEventListener("change", (e) => {
94-
console.log(e.target.selected);
95-
});
9694
</script>
95+
9796
</body>
9897

9998
</html>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
slug: ../../TableVirtualizer
3+
sidebar_class_name: newComponentBadge expComponentBadge
4+
---
5+
6+
import Virtualizer from "../../../_samples/main/Table/Virtualizer/Virtualizer.md";
7+
8+
<%COMPONENT_OVERVIEW%>
9+
10+
<%COMPONENT_METADATA%>
11+
12+
## Basic Sample
13+
14+
<Virtualizer/>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import html from '!!raw-loader!./sample.html';
2+
import js from '!!raw-loader!./main.js';
3+
4+
Enhance your table with virtualization capabilities by incorporating the **Virtualizer** feature.
5+
6+
For effective table virtualization, the `range-change` event with its `first` and `last` parameters determines which rows are currently visible and need to be rendered. To ensure proper virtualization, you must set the following attributes:
7+
8+
- `row-count` for the `Table`: This attribute specifies the total number of rows in the table. It helps the virtualizer determine the number of rows to manage.
9+
10+
- `row-height` for the `Table`: This attribute defines the height of each row in the table. Consistent row height allows the virtualizer to calculate which rows are currently visible and need to be rendered.
11+
12+
- `position` for the `TableRow`: This attribute determines the position of each row within the table. Proper positioning ensures that rows are rendered in the correct location when the user scrolls.
13+
14+
By setting these attributes and handling the `range-change` event properly, the `TableVirtualizer` can efficiently manage and render only the rows that are visible when the user scrolls.
15+
16+
<Editor html={html} js={js} />
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import "@ui5/webcomponents/dist/Table.js";
2+
import "@ui5/webcomponents/dist/TableRow.js";
3+
import "@ui5/webcomponents/dist/TableCell.js";
4+
import "@ui5/webcomponents/dist/TableHeaderRow.js";
5+
import "@ui5/webcomponents/dist/TableHeaderCell.js";
6+
import "@ui5/webcomponents/dist/TableVirtualizer.js";
7+
import "@ui5/webcomponents/dist/TableSelection.js";
8+
9+
class ProductStore {
10+
constructor() {
11+
this.products = [];
12+
}
13+
14+
async fetchProducts(first, last) {
15+
const products = [];
16+
for (let i = first; i < last; i++) {
17+
this.products[i] ??= await this.fetchProduct(i);
18+
products.push(this.products[i]);
19+
}
20+
return products;
21+
}
22+
23+
async fetchProduct(index) {
24+
return new Promise(resolve => {
25+
setTimeout(() => {
26+
resolve({
27+
key: `P${index}`,
28+
name: `Product ${index}`,
29+
height: `${(Math.random() * 100).toFixed(0)} cm`,
30+
weight: `${(Math.random() * 100).toFixed(1)} KG`,
31+
price: `${(Math.random() * 1000).toFixed(2)} EUR`
32+
});
33+
}, Math.random() * 10); // Simulate network delay
34+
});
35+
}
36+
}
37+
38+
const productStore = new ProductStore();
39+
const table = document.getElementById("table");
40+
const rowTemplate = document.getElementById("rowTemplate");
41+
const virtualizer = document.getElementById("virtualizer");
42+
43+
virtualizer.addEventListener("range-change", async (e) => {
44+
const { first, last } = e.detail;
45+
const products = await productStore.fetchProducts(first, last);
46+
for (let i = first; i < last; i++) {
47+
const rowIndex = i - first;
48+
const product = products[rowIndex];
49+
const row = table.rows[rowIndex] || table.appendChild(rowTemplate.content.firstElementChild.cloneNode(true));
50+
row.setAttribute("position", i);
51+
row.setAttribute("row-key", product.key);
52+
row.querySelectorAll("[data]").forEach(el => {
53+
el.textContent = product[el.getAttribute("data")];
54+
});
55+
}
56+
for (let i = last; i < table.rows.length; i++) {
57+
table.rows[i].remove();
58+
}
59+
});
60+
61+
requestAnimationFrame(() => virtualizer.reset());
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<!-- playground-fold -->
2+
<!DOCTYPE html>
3+
<html lang="en">
4+
5+
<head>
6+
<meta charset="UTF-8">
7+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
8+
<title>Sample</title>
9+
</head>
10+
11+
<body style="background-color: var(--sapBackgroundColor)">
12+
<div class="section">
13+
<template id="rowTemplate">
14+
<ui5-table-row position="-1" row-key="-1">
15+
<ui5-table-cell data="name"></ui5-table-cell>
16+
<ui5-table-cell data="height"></ui5-table-cell>
17+
<ui5-table-cell data="weight"></ui5-table-cell>
18+
<ui5-table-cell data="price"></ui5-table-cell>
19+
</ui5-table-row>
20+
</template>
21+
<!-- playground-fold-end -->
22+
<ui5-table id="table" loading-delay="100" style="height: 150px;" class="ui5-content-density-compact">
23+
<ui5-table-virtualizer id="virtualizer" slot="features" row-count="1000" row-height="32"></ui5-table-virtualizer>
24+
<!-- playground-fold -->
25+
<ui5-table-selection slot="features"></ui5-table-selection>
26+
<ui5-table-header-row slot="headerRow" sticky>
27+
<ui5-table-header-cell min-width="150px">Product Name</ui5-table-header-cell>
28+
<ui5-table-header-cell>Dimensions</ui5-table-header-cell>
29+
<ui5-table-header-cell>Weight</ui5-table-header-cell>
30+
<ui5-table-header-cell horizontal-align="Right">Price</ui5-table-header-cell>
31+
</ui5-table-header-row>
32+
<!-- playground-fold-end -->
33+
</ui5-table>
34+
<!-- playground-fold -->
35+
</div>
36+
<script type="module" src="main.js"></script>
37+
</body>
38+
39+
</html>
40+
<!-- playground-fold-end -->

0 commit comments

Comments
 (0)