mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-07-11 13:26:32 +00:00
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 <a.kitta@arduino.cc>
This commit is contained in:
parent
df3a34eec6
commit
d0dfc656e6
@ -44,10 +44,6 @@
|
|||||||
height: 100%; /* This has top be 100% down to the `scrollContainer`. */
|
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) {
|
.filterable-list-container .items-container > div > div:nth-child(odd) {
|
||||||
background-color: var(--theia-sideBar-background);
|
background-color: var(--theia-sideBar-background);
|
||||||
filter: contrast(105%);
|
filter: contrast(105%);
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'react-virtualized/styles.css';
|
||||||
import * as React from '@theia/core/shared/react';
|
import * as React from '@theia/core/shared/react';
|
||||||
import AutoSizer from 'react-virtualized/dist/commonjs/AutoSizer';
|
import AutoSizer from 'react-virtualized/dist/commonjs/AutoSizer';
|
||||||
import {
|
import {
|
||||||
@ -14,7 +15,11 @@ import { Installable } from '../../../common/protocol/installable';
|
|||||||
import { ComponentListItem } from './component-list-item';
|
import { ComponentListItem } from './component-list-item';
|
||||||
import { ListItemRenderer } from './list-item-renderer';
|
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) {
|
if (left === right) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -23,10 +28,12 @@ function sameAs<T>(left: T[], right: T[], key: (item: T) => string): boolean {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (let i = 0; i < leftLength; i++) {
|
for (let i = 0; i < leftLength; i++) {
|
||||||
const leftKey = key(left[i]);
|
for (const prop of compareProps) {
|
||||||
const rightKey = key(right[i]);
|
const leftValue = left[i][prop];
|
||||||
if (leftKey !== rightKey) {
|
const rightValue = right[i][prop];
|
||||||
return false;
|
if (leftValue !== rightValue) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -43,7 +50,7 @@ export class ComponentList<T extends ArduinoComponent> extends React.Component<
|
|||||||
constructor(props: ComponentList.Props<T>) {
|
constructor(props: ComponentList.Props<T>) {
|
||||||
super(props);
|
super(props);
|
||||||
this.cache = new CellMeasurerCache({
|
this.cache = new CellMeasurerCache({
|
||||||
defaultHeight: 300,
|
defaultHeight: 140,
|
||||||
fixedWidth: true,
|
fixedWidth: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -67,6 +74,11 @@ export class ComponentList<T extends ArduinoComponent> extends React.Component<
|
|||||||
rowHeight={this.cache.rowHeight}
|
rowHeight={this.cache.rowHeight}
|
||||||
deferredMeasurementCache={this.cache}
|
deferredMeasurementCache={this.cache}
|
||||||
ref={this.setListRef}
|
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}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
@ -77,13 +89,13 @@ export class ComponentList<T extends ArduinoComponent> extends React.Component<
|
|||||||
override componentDidUpdate(prevProps: ComponentList.Props<T>): void {
|
override componentDidUpdate(prevProps: ComponentList.Props<T>): void {
|
||||||
if (
|
if (
|
||||||
this.resizeAllFlag ||
|
this.resizeAllFlag ||
|
||||||
!sameAs(this.props.items, prevProps.items, this.props.itemLabel)
|
!sameAs(this.props.items, prevProps.items, 'name', 'installedVersion')
|
||||||
) {
|
) {
|
||||||
this.clearAll(true);
|
this.clearAll(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private setListRef = (ref: List | null): void => {
|
private readonly setListRef = (ref: List | null): void => {
|
||||||
this.list = ref || undefined;
|
this.list = ref || undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -98,17 +110,7 @@ export class ComponentList<T extends ArduinoComponent> extends React.Component<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private clear(index: number): void {
|
private readonly createItem: ListRowRenderer = ({
|
||||||
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 = ({
|
|
||||||
index,
|
index,
|
||||||
parent,
|
parent,
|
||||||
key,
|
key,
|
||||||
@ -123,16 +125,20 @@ export class ComponentList<T extends ArduinoComponent> extends React.Component<
|
|||||||
rowIndex={index}
|
rowIndex={index}
|
||||||
parent={parent}
|
parent={parent}
|
||||||
>
|
>
|
||||||
<div style={style}>
|
{({ measure, registerChild }) => (
|
||||||
<ComponentListItem<T>
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
key={this.props.itemLabel(item)}
|
// @ts-ignore
|
||||||
item={item}
|
<div ref={registerChild} style={style}>
|
||||||
itemRenderer={this.props.itemRenderer}
|
<ComponentListItem<T>
|
||||||
install={this.props.install}
|
key={this.props.itemLabel(item)}
|
||||||
uninstall={this.props.uninstall}
|
item={item}
|
||||||
onFocusDidChange={() => this.clear(index)}
|
itemRenderer={this.props.itemRenderer}
|
||||||
/>
|
install={this.props.install}
|
||||||
</div>
|
uninstall={this.props.uninstall}
|
||||||
|
onFocusDidChange={() => measure()}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</CellMeasurer>
|
</CellMeasurer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -51,7 +51,9 @@ export class FilterableListContainer<
|
|||||||
<div className={'filterable-list-container'}>
|
<div className={'filterable-list-container'}>
|
||||||
{this.renderSearchBar()}
|
{this.renderSearchBar()}
|
||||||
{this.renderSearchFilter()}
|
{this.renderSearchFilter()}
|
||||||
{this.renderComponentList()}
|
<div className="filterable-list-container">
|
||||||
|
{this.renderComponentList()}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user