Skip to content

Commit

Permalink
Data Grid UI - Smooth scrolling (#5078)
Browse files Browse the repository at this point in the history
This PR adds smooth scrolling to the Data Table component.
  • Loading branch information
softwarenerd authored Oct 21, 2024
1 parent 0931e85 commit ee60bf5
Show file tree
Hide file tree
Showing 30 changed files with 2,566 additions and 1,205 deletions.
12 changes: 6 additions & 6 deletions src/vs/base/common/positronUtilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ type Mapping = Record<string, unknown>;
type Argument = Value | Mapping;

/**
* Ensures that a given value is within a range.
* Ensures that a given value is within a range of values.
* @param value The value.
* @param min The minimum value, inclusive.
* @param max The maximum value, inclusive.
* @returns The value.
* @param minimumValue The minimum value, inclusive.
* @param maximumValue The maximum value, inclusive.
* @returns The pinned value.
*/
export const pinToRange = (value: number, min: number, max: number) =>
Math.min(Math.max(value, min), max);
export const pinToRange = (value: number, minimumValue: number, maximumValue: number) =>
Math.min(Math.max(value, minimumValue), maximumValue);

/**
* optionalValue function. Returns the value, if it is not undefined; otherwise, returns the default value.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ import { DropDownListBoxSeparator } from 'vs/workbench/browser/positronComponent
import { DataExplorerClientInstance } from 'vs/workbench/services/languageRuntime/common/languageRuntimeDataExplorerClient';
import { DropDownListBox, DropDownListBoxEntry } from 'vs/workbench/browser/positronComponents/dropDownListBox/dropDownListBox';
import { ColumnSchema, ColumnDisplayType, RowFilterCondition } from 'vs/workbench/services/languageRuntime/common/positronDataExplorerComm';
import { dataExplorerExperimentalFeatureEnabled } from 'vs/workbench/services/positronDataExplorer/common/positronDataExplorerExperimentalConfig';
import { RowFilterParameter } from 'vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/addEditRowFilterModalPopup/components/rowFilterParameter';
import { DropDownColumnSelector } from 'vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/addEditRowFilterModalPopup/components/dropDownColumnSelector';
import { RangeRowFilterDescriptor, RowFilterDescriptor, RowFilterDescrType, RowFilterDescriptorComparison, RowFilterDescriptorIsBetween, RowFilterDescriptorIsEmpty, RowFilterDescriptorIsNotBetween, RowFilterDescriptorIsNotEmpty, SingleValueRowFilterDescriptor, RowFilterDescriptorIsNotNull, RowFilterDescriptorIsNull, RowFilterDescriptorSearch, RowFilterDescriptorIsTrue, RowFilterDescriptorIsFalse } from 'vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/addEditRowFilterModalPopup/rowFilterDescriptor';
import { dataExplorerExperimentalFeatureEnabled } from 'vs/workbench/services/positronDataExplorer/common/positronDataExplorerExperimentalConfig';

/**
* Validates a row filter value.
Expand Down Expand Up @@ -594,7 +594,7 @@ export const AddEditRowFilterModalPopup = (props: AddEditRowFilterModalPopupProp
return false;
}

// The the second row filter value is valid.
// The second row filter value is valid.
return true;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import * as React from 'react';
// Other dependencies.
import { Emitter } from 'vs/base/common/event';
import { DataGridInstance } from 'vs/workbench/browser/positronDataGrid/classes/dataGridInstance';
import { ColumnSchema } from 'vs/workbench/services/languageRuntime/common/positronDataExplorerComm';
import { ColumnSchemaCache } from 'vs/workbench/services/positronDataExplorer/common/columnSchemaCache';
import { BackendState, ColumnSchema } from 'vs/workbench/services/languageRuntime/common/positronDataExplorerComm';
import { DataExplorerClientInstance } from 'vs/workbench/services/languageRuntime/common/languageRuntimeDataExplorerClient';
import { ColumnSelectorCell } from 'vs/workbench/browser/positronDataExplorer/components/dataExplorerPanel/components/addEditRowFilterModalPopup/components/columnSelectorCell';

Expand All @@ -25,11 +25,6 @@ const ROW_HEIGHT = 26;
export class ColumnSelectorDataGridInstance extends DataGridInstance {
//#region Private Properties

/**
* Gets the data explorer client instance.
*/
private readonly _dataExplorerClientInstance: DataExplorerClientInstance;

/**
* Gets or sets the search text.
*/
Expand All @@ -47,24 +42,56 @@ export class ColumnSelectorDataGridInstance extends DataGridInstance {

//#endregion Private Properties

//#region Static Methods

/**
* Creates a new column selector data grid instance.
* @param dataExplorerClientInstance The data explorer client instance.
* @returns A Promise<ColumnSelectorDataGridInstance> that resolves when the operation is
* complete.
*/
public static async create(
dataExplorerClientInstance: DataExplorerClientInstance,
): Promise<ColumnSelectorDataGridInstance | undefined> {
try {
// Get the backend state so that we can get the initial number of columns.
const backedState = await dataExplorerClientInstance.getBackendState();

// Return a new instance of the column selector data grid instance.
return new ColumnSelectorDataGridInstance(
backedState.table_shape.num_columns,
dataExplorerClientInstance
);
} catch {
return undefined;
}
}

//#endregion Static Methods

//#region Constructor

/**
* Constructor.
* @param dataExplorerClientInstance The DataExplorerClientInstance.
* @param initialColumns The initial number of columns.
* @param _dataExplorerClientInstance The data explorer client instance.
*/
constructor(dataExplorerClientInstance: DataExplorerClientInstance) {
protected constructor(
initialColumns: number,
private readonly _dataExplorerClientInstance: DataExplorerClientInstance,
) {
// Call the base class's constructor.
super({
columnHeaders: false,
rowHeaders: false,
defaultColumnWidth: 100,
defaultColumnWidth: 0,
defaultRowHeight: ROW_HEIGHT,
columnResize: false,
rowResize: false,
horizontalScrollbar: false,
verticalScrollbar: true,
scrollbarWidth: 8,
scrollbarThickness: 8,
scrollbarOverscroll: 0,
useEditorFont: false,
automaticLayout: true,
rowsMargin: 4,
Expand All @@ -74,19 +101,54 @@ export class ColumnSelectorDataGridInstance extends DataGridInstance {
selection: false
});

// Set the data explorer client instance.
this._dataExplorerClientInstance = dataExplorerClientInstance;
// Create the column schema cache.
this._register(
this._columnSchemaCache = new ColumnSchemaCache(this._dataExplorerClientInstance)
);

// Set the initial layout entries in the row layout manager.
this._rowLayoutManager.setLayoutEntries(initialColumns);

/**
* Updates the data grid instance.
* @param state The state, if known; otherwise, undefined.
*/
const updateDataGridInstance = async (state?: BackendState) => {
// Get the backend state, if it was not supplied.
if (!state) {
state = await this._dataExplorerClientInstance.getBackendState();
}

// Set the layout entries in the row layout manager.
this._rowLayoutManager.setLayoutEntries(state.table_shape.num_columns);

// Scroll to the top.
await this.setScrollOffsets(0, 0);
};

// Add the onDidSchemaUpdate event handler.
this._register(this._dataExplorerClientInstance.onDidSchemaUpdate(async () =>
// Update the data grid instance.
updateDataGridInstance()
));

// Add the onDidDataUpdate event handler.
this._register(this._dataExplorerClientInstance.onDidDataUpdate(async () =>
// Update the data grid instance.
updateDataGridInstance
));

// Add the onDidUpdateBackendState event handler.
this._register(this._dataExplorerClientInstance.onDidUpdateBackendState(async state =>
// Update the data grid instance.
updateDataGridInstance(state)
));

// Allocate and initialize the column schema cache.
this._columnSchemaCache = new ColumnSchemaCache(dataExplorerClientInstance);
// Add the onDidUpdateCache event handler.
this._register(this._columnSchemaCache.onDidUpdateCache(() =>
// Fire the onDidUpdate event.
this._onDidUpdateEmitter.fire()
));

// Add the onDidSchemaUpdate event handler.
this._register(this._dataExplorerClientInstance.onDidSchemaUpdate(async () => {
await this.setScreenPosition(0, 0);
}));
}

//#endregion Constructor
Expand All @@ -107,6 +169,23 @@ export class ColumnSelectorDataGridInstance extends DataGridInstance {
return this._columnSchemaCache.columns;
}

/**
* Gets the scroll width.
*/
override get scrollWidth() {
return 0;
}

/**
* Gets the first column.
*/
override get firstColumn() {
return {
columnIndex: 0,
left: 0
};
}

//#endregion DataGridInstance Properties

//#region DataGridInstance Methods
Expand All @@ -116,29 +195,24 @@ export class ColumnSelectorDataGridInstance extends DataGridInstance {
* @returns A Promise<void> that resolves when the operation is complete.
*/
override async fetchData() {
await this._columnSchemaCache.updateCache({
searchText: this._searchText,
firstColumnIndex: this.firstRowIndex,
visibleColumns: this.screenRows
});
const rowDescriptor = this.firstRow;
if (rowDescriptor) {
await this._columnSchemaCache.update({
searchText: this._searchText,
firstColumnIndex: rowDescriptor.rowIndex,
visibleColumns: this.screenRows
});
}
}

/**
* Gets the the width of a column.
* Gets the width of a column.
* @param columnIndex The column index.
*/
override getColumnWidth(columnIndex: number): number {
return this.layoutWidth - 8;
}

/**
* Gets the the height of a row.
* @param rowIndex The row index.
*/
override getRowHeight(rowIndex: number): number {
return ROW_HEIGHT;
}

selectItem(rowIndex: number): void {
// Get the column schema for the row index.
const columnSchema = this._columnSchemaCache.getColumnSchema(rowIndex);
Expand Down Expand Up @@ -201,7 +275,7 @@ export class ColumnSelectorDataGridInstance extends DataGridInstance {

// select the first available row after fetching so that users cat hit "enter"
// to make an immediate confirmation on what they were searching for
if (this.visibleRows) {
if (this.rows > 0) {
this.showCursor();
this.setCursorRow(0);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@
width: 100%;
height: 100%;
display: grid;
grid-template-rows: [search] 34px [view] 1fr [end];
grid-template-rows: [column-selector-search] 34px [column-selector-data-grid] 1fr [end-column-selector-data-grid];
}

.column-selector
.column-selector-search {
grid-row: search / view;
grid-row: column-selector-search / column-selector-data-grid;
border-bottom: 1px solid var(--vscode-positronDataExplorer-border);
}

.column-selector
.column-selector-data-grid {
grid-row: view / end;
grid-row: column-selector-data-grid / end-column-selector-data-grid;
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ interface ColumnSelectorModalPopupProps {
readonly anchorElement: HTMLElement;
readonly searchInput?: string;
readonly focusInput?: boolean;
readonly onItemHighlighted: (columnSchema: ColumnSchema) => void;
readonly onItemSelected: (columnSchema: ColumnSchema) => void;
}

Expand All @@ -49,7 +48,7 @@ export const ColumnSelectorModalPopup = (props: ColumnSelectorModalPopupProps) =
// Drive focus into the data grid so the user can immediately navigate.
props.columnSelectorDataGridInstance.setCursorPosition(0, 0);
positronDataGridRef.current.focus();
}, []);
}, [props.columnSelectorDataGridInstance, props.focusInput]);

useEffect(() => {
// Create the disposable store for cleanup.
Expand Down
Loading

0 comments on commit ee60bf5

Please sign in to comment.