Skip to content

Commit

Permalink
Improved the scrolling UX in list widgets
Browse files Browse the repository at this point in the history
 - 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 arduino#1380
Closes arduino#1381
Closes arduino#1387

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
  • Loading branch information
Akos Kitta authored and kittaakos committed Sep 5, 2022
1 parent df3a34e commit d0dfc65
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 34 deletions.
4 changes: 0 additions & 4 deletions arduino-ide-extension/src/browser/style/list-widget.css
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,6 @@
height: 100%; /* This has top be 100% down to the `scrollContainer`. */
}

.filterable-list-container .items-container {
padding-bottom: calc(2 * var(--theia-statusBar-height));
}

.filterable-list-container .items-container > div > div:nth-child(odd) {
background-color: var(--theia-sideBar-background);
filter: contrast(105%);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'react-virtualized/styles.css';
import * as React from '@theia/core/shared/react';
import AutoSizer from 'react-virtualized/dist/commonjs/AutoSizer';
import {
Expand All @@ -14,7 +15,11 @@ import { Installable } from '../../../common/protocol/installable';
import { ComponentListItem } from './component-list-item';
import { ListItemRenderer } from './list-item-renderer';

function sameAs<T>(left: T[], right: T[], key: (item: T) => string): boolean {
function sameAs<T>(
left: T[],
right: T[],
...compareProps: (keyof T)[]
): boolean {
if (left === right) {
return true;
}
Expand All @@ -23,10 +28,12 @@ function sameAs<T>(left: T[], right: T[], key: (item: T) => string): boolean {
return false;
}
for (let i = 0; i < leftLength; i++) {
const leftKey = key(left[i]);
const rightKey = key(right[i]);
if (leftKey !== rightKey) {
return false;
for (const prop of compareProps) {
const leftValue = left[i][prop];
const rightValue = right[i][prop];
if (leftValue !== rightValue) {
return false;
}
}
}
return true;
Expand All @@ -43,7 +50,7 @@ export class ComponentList<T extends ArduinoComponent> extends React.Component<
constructor(props: ComponentList.Props<T>) {
super(props);
this.cache = new CellMeasurerCache({
defaultHeight: 300,
defaultHeight: 140,
fixedWidth: true,
});
}
Expand All @@ -67,6 +74,11 @@ export class ComponentList<T extends ArduinoComponent> extends React.Component<
rowHeight={this.cache.rowHeight}
deferredMeasurementCache={this.cache}
ref={this.setListRef}
estimatedRowSize={140}
// If default value, then `react-virtualized` will optimize and list item will not receive a `:hover` event.
// Hence, install and version `<select>` won't be visible even if the mouse cursor is over the `<div>`.
// See https://github.com/bvaughn/react-virtualized/blob/005be24a608add0344284053dae7633be86053b2/source/Grid/Grid.js#L38-L42
scrollingResetTimeInterval={0}
/>
);
}}
Expand All @@ -77,13 +89,13 @@ export class ComponentList<T extends ArduinoComponent> extends React.Component<
override componentDidUpdate(prevProps: ComponentList.Props<T>): void {
if (
this.resizeAllFlag ||
!sameAs(this.props.items, prevProps.items, this.props.itemLabel)
!sameAs(this.props.items, prevProps.items, 'name', 'installedVersion')
) {
this.clearAll(true);
}
}

private setListRef = (ref: List | null): void => {
private readonly setListRef = (ref: List | null): void => {
this.list = ref || undefined;
};

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

private clear(index: number): void {
this.cache.clear(index, 0);
this.list?.recomputeRowHeights(index);
// Update the last item if the if the one before was updated
if (index === this.props.items.length - 2) {
this.cache.clear(index + 1, 0);
this.list?.recomputeRowHeights(index + 1);
}
}

private createItem: ListRowRenderer = ({
private readonly createItem: ListRowRenderer = ({
index,
parent,
key,
Expand All @@ -123,16 +125,20 @@ export class ComponentList<T extends ArduinoComponent> extends React.Component<
rowIndex={index}
parent={parent}
>
<div style={style}>
<ComponentListItem<T>
key={this.props.itemLabel(item)}
item={item}
itemRenderer={this.props.itemRenderer}
install={this.props.install}
uninstall={this.props.uninstall}
onFocusDidChange={() => this.clear(index)}
/>
</div>
{({ measure, registerChild }) => (
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
<div ref={registerChild} style={style}>
<ComponentListItem<T>
key={this.props.itemLabel(item)}
item={item}
itemRenderer={this.props.itemRenderer}
install={this.props.install}
uninstall={this.props.uninstall}
onFocusDidChange={() => measure()}
/>
</div>
)}
</CellMeasurer>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ export class FilterableListContainer<
<div className={'filterable-list-container'}>
{this.renderSearchBar()}
{this.renderSearchFilter()}
{this.renderComponentList()}
<div className="filterable-list-container">
{this.renderComponentList()}
</div>
</div>
);
}
Expand Down

0 comments on commit d0dfc65

Please sign in to comment.