Skip to content

Commit 6143bd3

Browse files
Convert RawTable to a functional component
1 parent 0451dd8 commit 6143bd3

File tree

1 file changed

+136
-138
lines changed

1 file changed

+136
-138
lines changed
Lines changed: 136 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import * as React from "react";
22
import {
3-
ResultTableProps,
43
className,
54
emptyQueryResultsMessage,
65
jumpToLocation,
@@ -19,158 +18,157 @@ import { onNavigation } from "./results";
1918
import { tryGetResolvableLocation } from "../../common/bqrs-utils";
2019
import { ScrollIntoViewHelper } from "./scroll-into-view-helper";
2120
import { sendTelemetry } from "../common/telemetry";
21+
import { assertNever } from "../../common/helpers-pure";
2222

23-
export type RawTableProps = ResultTableProps & {
23+
export type RawTableProps = {
24+
databaseUri: string;
2425
resultSet: RawTableResultSet;
2526
sortState?: RawResultsSortState;
2627
offset: number;
2728
};
2829

29-
interface RawTableState {
30-
selectedItem?: { row: number; column: number };
30+
interface TableItem {
31+
readonly row: number;
32+
readonly column: number;
3133
}
3234

33-
export class RawTable extends React.Component<RawTableProps, RawTableState> {
34-
private scroller = new ScrollIntoViewHelper();
35-
36-
constructor(props: RawTableProps) {
37-
super(props);
38-
this.setSelection = this.setSelection.bind(this);
39-
this.handleNavigationEvent = this.handleNavigationEvent.bind(this);
40-
this.state = {};
41-
}
42-
43-
private setSelection(row: number, column: number) {
44-
this.setState((prev) => ({
45-
...prev,
46-
selectedItem: { row, column },
47-
}));
48-
sendTelemetry("local-results-raw-results-table-selected");
49-
}
50-
51-
render(): React.ReactNode {
52-
const { resultSet, databaseUri } = this.props;
53-
54-
let dataRows = resultSet.rows;
55-
if (dataRows.length === 0) {
56-
return emptyQueryResultsMessage();
57-
}
58-
59-
let numTruncatedResults = 0;
60-
if (dataRows.length > RAW_RESULTS_LIMIT) {
61-
numTruncatedResults = dataRows.length - RAW_RESULTS_LIMIT;
62-
dataRows = dataRows.slice(0, RAW_RESULTS_LIMIT);
63-
}
64-
65-
const tableRows = dataRows.map((row: ResultRow, rowIndex: number) => (
66-
<RawTableRow
67-
key={rowIndex}
68-
rowIndex={rowIndex + this.props.offset}
69-
row={row}
70-
databaseUri={databaseUri}
71-
selectedColumn={
72-
this.state.selectedItem?.row === rowIndex
73-
? this.state.selectedItem?.column
74-
: undefined
35+
export function RawTable({
36+
databaseUri,
37+
resultSet,
38+
sortState,
39+
offset,
40+
}: RawTableProps) {
41+
const [selectedItem, setSelectedItem] = React.useState<
42+
TableItem | undefined
43+
>();
44+
45+
const scroller = React.useMemo(() => new ScrollIntoViewHelper(), []);
46+
scroller.update();
47+
48+
const setSelection = React.useCallback(
49+
(row: number, column: number): void => {
50+
setSelectedItem({ row, column });
51+
sendTelemetry("local-results-raw-results-table-selected");
52+
},
53+
[],
54+
);
55+
56+
const navigateWithDelta = React.useCallback(
57+
(rowDelta: number, columnDelta: number): void => {
58+
setSelectedItem((prevSelectedItem) => {
59+
const numberOfAlerts = resultSet.rows.length;
60+
if (numberOfAlerts === 0) {
61+
return prevSelectedItem;
7562
}
76-
onSelected={this.setSelection}
77-
scroller={this.scroller}
78-
/>
79-
));
80-
81-
if (numTruncatedResults > 0) {
82-
const colSpan = dataRows[0].length + 1; // one row for each data column, plus index column
83-
tableRows.push(
84-
<tr>
85-
<td
86-
key={"message"}
87-
colSpan={colSpan}
88-
style={{ textAlign: "center", fontStyle: "italic" }}
89-
>
90-
Too many results to show at once. {numTruncatedResults} result(s)
91-
omitted.
92-
</td>
93-
</tr>,
94-
);
95-
}
96-
97-
return (
98-
<table className={className}>
99-
<RawTableHeader
100-
columns={resultSet.schema.columns}
101-
schemaName={resultSet.schema.name}
102-
sortState={this.props.sortState}
103-
/>
104-
<tbody>{tableRows}</tbody>
105-
</table>
106-
);
107-
}
108-
109-
private handleNavigationEvent(event: NavigateMsg) {
110-
switch (event.direction) {
111-
case NavigationDirection.up: {
112-
this.navigateWithDelta(-1, 0);
113-
break;
114-
}
115-
case NavigationDirection.down: {
116-
this.navigateWithDelta(1, 0);
117-
break;
118-
}
119-
case NavigationDirection.left: {
120-
this.navigateWithDelta(0, -1);
121-
break;
122-
}
123-
case NavigationDirection.right: {
124-
this.navigateWithDelta(0, 1);
125-
break;
126-
}
127-
}
128-
}
129-
130-
private navigateWithDelta(rowDelta: number, columnDelta: number) {
131-
this.setState((prevState) => {
132-
const numberOfAlerts = this.props.resultSet.rows.length;
133-
if (numberOfAlerts === 0) {
134-
return prevState;
135-
}
136-
const currentRow = prevState.selectedItem?.row;
137-
const nextRow = currentRow === undefined ? 0 : currentRow + rowDelta;
138-
if (nextRow < 0 || nextRow >= numberOfAlerts) {
139-
return prevState;
140-
}
141-
const currentColumn = prevState.selectedItem?.column;
142-
const nextColumn =
143-
currentColumn === undefined ? 0 : currentColumn + columnDelta;
144-
// Jump to the location of the new cell
145-
const rowData = this.props.resultSet.rows[nextRow];
146-
if (nextColumn < 0 || nextColumn >= rowData.length) {
147-
return prevState;
148-
}
149-
const cellData = rowData[nextColumn];
150-
if (cellData != null && typeof cellData === "object") {
151-
const location = tryGetResolvableLocation(cellData.url);
152-
if (location !== undefined) {
153-
jumpToLocation(location, this.props.databaseUri);
63+
const currentRow = prevSelectedItem?.row;
64+
const nextRow = currentRow === undefined ? 0 : currentRow + rowDelta;
65+
if (nextRow < 0 || nextRow >= numberOfAlerts) {
66+
return prevSelectedItem;
67+
}
68+
const currentColumn = prevSelectedItem?.column;
69+
const nextColumn =
70+
currentColumn === undefined ? 0 : currentColumn + columnDelta;
71+
// Jump to the location of the new cell
72+
const rowData = resultSet.rows[nextRow];
73+
if (nextColumn < 0 || nextColumn >= rowData.length) {
74+
return prevSelectedItem;
75+
}
76+
const cellData = rowData[nextColumn];
77+
if (cellData != null && typeof cellData === "object") {
78+
const location = tryGetResolvableLocation(cellData.url);
79+
if (location !== undefined) {
80+
jumpToLocation(location, databaseUri);
81+
}
15482
}
83+
scroller.scrollIntoViewOnNextUpdate();
84+
return { row: nextRow, column: nextColumn };
85+
});
86+
},
87+
[databaseUri, resultSet, scroller],
88+
);
89+
90+
const handleNavigationEvent = React.useCallback(
91+
(event: NavigateMsg) => {
92+
switch (event.direction) {
93+
case NavigationDirection.up: {
94+
navigateWithDelta(-1, 0);
95+
break;
96+
}
97+
case NavigationDirection.down: {
98+
navigateWithDelta(1, 0);
99+
break;
100+
}
101+
case NavigationDirection.left: {
102+
navigateWithDelta(0, -1);
103+
break;
104+
}
105+
case NavigationDirection.right: {
106+
navigateWithDelta(0, 1);
107+
break;
108+
}
109+
default:
110+
assertNever(event.direction);
155111
}
156-
this.scroller.scrollIntoViewOnNextUpdate();
157-
return {
158-
...prevState,
159-
selectedItem: { row: nextRow, column: nextColumn },
160-
};
161-
});
112+
},
113+
[navigateWithDelta],
114+
);
115+
116+
React.useEffect(() => {
117+
onNavigation.addListener(handleNavigationEvent);
118+
return () => {
119+
onNavigation.removeListener(handleNavigationEvent);
120+
};
121+
}, [handleNavigationEvent]);
122+
123+
let dataRows = resultSet.rows;
124+
if (dataRows.length === 0) {
125+
return emptyQueryResultsMessage();
162126
}
163127

164-
componentDidUpdate() {
165-
this.scroller.update();
128+
let numTruncatedResults = 0;
129+
if (dataRows.length > RAW_RESULTS_LIMIT) {
130+
numTruncatedResults = dataRows.length - RAW_RESULTS_LIMIT;
131+
dataRows = dataRows.slice(0, RAW_RESULTS_LIMIT);
166132
}
167133

168-
componentDidMount() {
169-
this.scroller.update();
170-
onNavigation.addListener(this.handleNavigationEvent);
134+
const tableRows = dataRows.map((row: ResultRow, rowIndex: number) => (
135+
<RawTableRow
136+
key={rowIndex}
137+
rowIndex={rowIndex + offset}
138+
row={row}
139+
databaseUri={databaseUri}
140+
selectedColumn={
141+
selectedItem?.row === rowIndex ? selectedItem?.column : undefined
142+
}
143+
onSelected={setSelection}
144+
scroller={scroller}
145+
/>
146+
));
147+
148+
if (numTruncatedResults > 0) {
149+
const colSpan = dataRows[0].length + 1; // one row for each data column, plus index column
150+
tableRows.push(
151+
<tr>
152+
<td
153+
key={"message"}
154+
colSpan={colSpan}
155+
style={{ textAlign: "center", fontStyle: "italic" }}
156+
>
157+
Too many results to show at once. {numTruncatedResults} result(s)
158+
omitted.
159+
</td>
160+
</tr>,
161+
);
171162
}
172163

173-
componentWillUnmount() {
174-
onNavigation.removeListener(this.handleNavigationEvent);
175-
}
164+
return (
165+
<table className={className}>
166+
<RawTableHeader
167+
columns={resultSet.schema.columns}
168+
schemaName={resultSet.schema.name}
169+
sortState={sortState}
170+
/>
171+
<tbody>{tableRows}</tbody>
172+
</table>
173+
);
176174
}

0 commit comments

Comments
 (0)