Skip to content

Commit d0dfc65

Browse files
Akos Kittakittaakos
Akos Kitta
authored andcommitted
Improved the scrolling UX in list widgets
- Fixed scrollbar does not reach end of list widget. - Estimated row heights to provide better scroll UX. - Last item's `<select>` must be visible. Closes #1380 Closes #1381 Closes #1387 Signed-off-by: Akos Kitta <[email protected]>
1 parent df3a34e commit d0dfc65

File tree

3 files changed

+38
-34
lines changed

3 files changed

+38
-34
lines changed

Diff for: arduino-ide-extension/src/browser/style/list-widget.css

-4
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,6 @@
4444
height: 100%; /* This has top be 100% down to the `scrollContainer`. */
4545
}
4646

47-
.filterable-list-container .items-container {
48-
padding-bottom: calc(2 * var(--theia-statusBar-height));
49-
}
50-
5147
.filterable-list-container .items-container > div > div:nth-child(odd) {
5248
background-color: var(--theia-sideBar-background);
5349
filter: contrast(105%);

Diff for: arduino-ide-extension/src/browser/widgets/component-list/component-list.tsx

+35-29
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'react-virtualized/styles.css';
12
import * as React from '@theia/core/shared/react';
23
import AutoSizer from 'react-virtualized/dist/commonjs/AutoSizer';
34
import {
@@ -14,7 +15,11 @@ import { Installable } from '../../../common/protocol/installable';
1415
import { ComponentListItem } from './component-list-item';
1516
import { ListItemRenderer } from './list-item-renderer';
1617

17-
function sameAs<T>(left: T[], right: T[], key: (item: T) => string): boolean {
18+
function sameAs<T>(
19+
left: T[],
20+
right: T[],
21+
...compareProps: (keyof T)[]
22+
): boolean {
1823
if (left === right) {
1924
return true;
2025
}
@@ -23,10 +28,12 @@ function sameAs<T>(left: T[], right: T[], key: (item: T) => string): boolean {
2328
return false;
2429
}
2530
for (let i = 0; i < leftLength; i++) {
26-
const leftKey = key(left[i]);
27-
const rightKey = key(right[i]);
28-
if (leftKey !== rightKey) {
29-
return false;
31+
for (const prop of compareProps) {
32+
const leftValue = left[i][prop];
33+
const rightValue = right[i][prop];
34+
if (leftValue !== rightValue) {
35+
return false;
36+
}
3037
}
3138
}
3239
return true;
@@ -43,7 +50,7 @@ export class ComponentList<T extends ArduinoComponent> extends React.Component<
4350
constructor(props: ComponentList.Props<T>) {
4451
super(props);
4552
this.cache = new CellMeasurerCache({
46-
defaultHeight: 300,
53+
defaultHeight: 140,
4754
fixedWidth: true,
4855
});
4956
}
@@ -67,6 +74,11 @@ export class ComponentList<T extends ArduinoComponent> extends React.Component<
6774
rowHeight={this.cache.rowHeight}
6875
deferredMeasurementCache={this.cache}
6976
ref={this.setListRef}
77+
estimatedRowSize={140}
78+
// If default value, then `react-virtualized` will optimize and list item will not receive a `:hover` event.
79+
// Hence, install and version `<select>` won't be visible even if the mouse cursor is over the `<div>`.
80+
// See https://github.com/bvaughn/react-virtualized/blob/005be24a608add0344284053dae7633be86053b2/source/Grid/Grid.js#L38-L42
81+
scrollingResetTimeInterval={0}
7082
/>
7183
);
7284
}}
@@ -77,13 +89,13 @@ export class ComponentList<T extends ArduinoComponent> extends React.Component<
7789
override componentDidUpdate(prevProps: ComponentList.Props<T>): void {
7890
if (
7991
this.resizeAllFlag ||
80-
!sameAs(this.props.items, prevProps.items, this.props.itemLabel)
92+
!sameAs(this.props.items, prevProps.items, 'name', 'installedVersion')
8193
) {
8294
this.clearAll(true);
8395
}
8496
}
8597

86-
private setListRef = (ref: List | null): void => {
98+
private readonly setListRef = (ref: List | null): void => {
8799
this.list = ref || undefined;
88100
};
89101

@@ -98,17 +110,7 @@ export class ComponentList<T extends ArduinoComponent> extends React.Component<
98110
}
99111
}
100112

101-
private clear(index: number): void {
102-
this.cache.clear(index, 0);
103-
this.list?.recomputeRowHeights(index);
104-
// Update the last item if the if the one before was updated
105-
if (index === this.props.items.length - 2) {
106-
this.cache.clear(index + 1, 0);
107-
this.list?.recomputeRowHeights(index + 1);
108-
}
109-
}
110-
111-
private createItem: ListRowRenderer = ({
113+
private readonly createItem: ListRowRenderer = ({
112114
index,
113115
parent,
114116
key,
@@ -123,16 +125,20 @@ export class ComponentList<T extends ArduinoComponent> extends React.Component<
123125
rowIndex={index}
124126
parent={parent}
125127
>
126-
<div style={style}>
127-
<ComponentListItem<T>
128-
key={this.props.itemLabel(item)}
129-
item={item}
130-
itemRenderer={this.props.itemRenderer}
131-
install={this.props.install}
132-
uninstall={this.props.uninstall}
133-
onFocusDidChange={() => this.clear(index)}
134-
/>
135-
</div>
128+
{({ measure, registerChild }) => (
129+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
130+
// @ts-ignore
131+
<div ref={registerChild} style={style}>
132+
<ComponentListItem<T>
133+
key={this.props.itemLabel(item)}
134+
item={item}
135+
itemRenderer={this.props.itemRenderer}
136+
install={this.props.install}
137+
uninstall={this.props.uninstall}
138+
onFocusDidChange={() => measure()}
139+
/>
140+
</div>
141+
)}
136142
</CellMeasurer>
137143
);
138144
};

Diff for: arduino-ide-extension/src/browser/widgets/component-list/filterable-list-container.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ export class FilterableListContainer<
5151
<div className={'filterable-list-container'}>
5252
{this.renderSearchBar()}
5353
{this.renderSearchFilter()}
54-
{this.renderComponentList()}
54+
<div className="filterable-list-container">
55+
{this.renderComponentList()}
56+
</div>
5557
</div>
5658
);
5759
}

0 commit comments

Comments
 (0)