Skip to content

Commit

Permalink
Refactored sheetTable, pulling interactions out.
Browse files Browse the repository at this point in the history
  • Loading branch information
erikh2000 authored and erikh2000 committed Jan 1, 2025
1 parent 9f930a6 commit 021cafd
Show file tree
Hide file tree
Showing 13 changed files with 104 additions and 86 deletions.
4 changes: 2 additions & 2 deletions src/components/sheetTable/SheetRow.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Row from "@/sheets/types/Row"
import styles from './SheetRow.module.css';
import styles from '@/components/sheetTable/SheetRow.module.css';
import GeneratedText from "../generatedText/GeneratedText";
import { cellValueToText } from "./sheetTableUtil";
import { cellValueToText } from "./interactions/row";

type Props = {
row:Row,
Expand Down
90 changes: 12 additions & 78 deletions src/components/sheetTable/SheetTable.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,17 @@
import { useState, useRef, useEffect, RefObject, CSSProperties } from 'react';
import { useState, useRef, useEffect } from 'react';

import styles from './SheetTable.module.css';
import rowStyles from './SheetRow.module.css';
import SheetRow from "./SheetRow";
import SheetHeader from './SheetHeader';
import HoneSheet from '@/sheets/types/HoneSheet';
import SheetFooter from './SheetFooter';
import DOMTextMeasurer from './DOMTextMeasurer';
import { plural } from '@/common/englishGrammarUtil';

export enum GeneratedFooterText {
ROW_COUNT = 0,
}

export enum HorizontalScroll {
CLEAR = 0,
LEFT,
RIGHT
}

export enum VerticalScroll {
CLEAR = 0,
TOP,
BOTTOM
}
import HorizontalScroll from './types/HorizontalScroll';
import VerticalScroll from './types/VerticalScroll';
import { GeneratedFooterText } from './types/GeneratedFooterText';
import { getFooterText, getRowScrollContainerStyle, measureColumnWidths, setHorizontalScroll, setVerticalScroll, syncScrollableElements } from './interactions/table';

type Props = {
columnNames:string[],
sheet:HoneSheet,
displayRowCount?:number,
selectedColNo?:number,
Expand All @@ -37,58 +23,6 @@ type Props = {
footerText?:string|GeneratedFooterText
}

type DivRef = RefObject<HTMLDivElement>;

function _measureColumnWidths(sheetTableElement:HTMLDivElement, sheet:HoneSheet):number[] {
const measurer = new DOMTextMeasurer(sheetTableElement, rowStyles.measureCellText);
const widths = sheet.columns.map(column => measurer.measureTextWidth(column.name));
for(let rowI = 0; rowI < sheet.rows.length; rowI++) {
const row = sheet.rows[rowI];
for(let cellI = 0; cellI < row.length; cellI++) {
const cell = '' + row[cellI];
widths[cellI] = Math.max(widths[cellI], measurer.measureTextWidth(cell));
}
}
return widths;
}

function _getFooterText(footerText:string|GeneratedFooterText|undefined, sheet:HoneSheet):string {
if (footerText === undefined) return '';
if (footerText === GeneratedFooterText.ROW_COUNT) return `${sheet.rows.length} ${plural('row', sheet.rows.length)}`;
return footerText;
}

function _syncScrollableElements(headerInnerElement:DivRef, rowsScrollElement:DivRef) {
if (!headerInnerElement.current || !rowsScrollElement.current) return;
const scrollLeft = rowsScrollElement.current.scrollLeft;
headerInnerElement.current.style.transform = `translateX(-${scrollLeft}px)`;
}

function _getRowScrollContainerStyle(displayRowCount:number|undefined, parentElement:HTMLDivElement|null):CSSProperties {
if (!displayRowCount || !parentElement) return {};
const measurer = new DOMTextMeasurer(parentElement, rowStyles.measureCellText);
const lineHeight = measurer.getLineHeight();
return {maxHeight:displayRowCount * lineHeight + 'px'};
}

function _setHorizontalScroll(rowsScrollElement:DivRef, horizontalScroll?:HorizontalScroll) {
if (horizontalScroll === undefined || horizontalScroll === HorizontalScroll.CLEAR || rowsScrollElement.current === null) return;
if (horizontalScroll === HorizontalScroll.LEFT) {
rowsScrollElement.current.scrollLeft = 0;
} else if (horizontalScroll === HorizontalScroll.RIGHT) {
rowsScrollElement.current.scrollLeft = rowsScrollElement.current.scrollWidth;
}
}

function _setVerticalScroll(rowsScrollElement:DivRef, verticalScroll?:VerticalScroll) {
if (verticalScroll === undefined || verticalScroll === VerticalScroll.CLEAR || rowsScrollElement.current === null) return;
if (verticalScroll === VerticalScroll.TOP) {
rowsScrollElement.current.scrollTop = 0;
} else if (verticalScroll === VerticalScroll.BOTTOM) {
rowsScrollElement.current.scrollTop = rowsScrollElement.current.scrollHeight;
}
}

function SheetTable({sheet, footerText, displayRowCount, selectedRowNo, onSelectCell, generatedColNo, horizontalScroll, verticalScroll}:Props) {
const sheetTableElement = useRef<HTMLDivElement>(null);
const headerInnerElement = useRef<HTMLDivElement>(null);
Expand All @@ -97,16 +31,16 @@ function SheetTable({sheet, footerText, displayRowCount, selectedRowNo, onSelect

useEffect(() => {
if (!sheetTableElement.current) return;
const nextColumnWidths:number[] = _measureColumnWidths(sheetTableElement.current, sheet);
const nextColumnWidths:number[] = measureColumnWidths(sheetTableElement.current, sheet);
setColumnWidths(nextColumnWidths);
}, [sheet, sheet.rows]);

useEffect(() => {
_setHorizontalScroll(rowsScrollElement, horizontalScroll);
setHorizontalScroll(rowsScrollElement, horizontalScroll);
}, [horizontalScroll]);

useEffect(() => {
_setVerticalScroll(rowsScrollElement, verticalScroll);
setVerticalScroll(rowsScrollElement, verticalScroll);
}, [verticalScroll]);

const rowCount = sheet.rows.length;
Expand All @@ -115,15 +49,15 @@ function SheetTable({sheet, footerText, displayRowCount, selectedRowNo, onSelect
isSelected={rowI+1===selectedRowNo} onSelectCell={onSelectCell} generatedColNo={generatedColNo}/>
);

const rowScrollContainerStyle = _getRowScrollContainerStyle(displayRowCount, rowsScrollElement.current);
const displayFooterText = _getFooterText(footerText, sheet);
const rowScrollContainerStyle = getRowScrollContainerStyle(displayRowCount, rowsScrollElement.current);
const displayFooterText = getFooterText(footerText, sheet);
return (
<div className={styles.sheetTable} ref={sheetTableElement}>
<div className={styles.headerScrollContainer}>
<SheetHeader columns={sheet.columns} columnWidths={columnWidths} ref={headerInnerElement}/>
</div>
<div className={styles.rowsScrollContainer} style={rowScrollContainerStyle} ref={rowsScrollElement}
onScroll={() => _syncScrollableElements(headerInnerElement, rowsScrollElement)}>
onScroll={() => syncScrollableElements(headerInnerElement, rowsScrollElement)}>
<div className={styles.rowsInnerContainer}>
{rowsContent}
</div>
Expand Down
File renamed without changes.
60 changes: 60 additions & 0 deletions src/components/sheetTable/interactions/table.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { CSSProperties, RefObject } from 'react';
import HoneSheet from '@/sheets/types/HoneSheet';
import DOMTextMeasurer from '@/components/sheetTable/DOMTextMeasurer';
import { plural } from '@/common/englishGrammarUtil';
import rowStyles from '@/components/sheetTable/SheetRow.module.css';
import { GeneratedFooterText } from '@/components/sheetTable/types/GeneratedFooterText';
import HorizontalScroll from '@/components/sheetTable/types/HorizontalScroll';
import VerticalScroll from '@/components/sheetTable/types/VerticalScroll';

type DivRef = RefObject<HTMLDivElement>;

export function measureColumnWidths(sheetTableElement:HTMLDivElement, sheet:HoneSheet):number[] {
const measurer = new DOMTextMeasurer(sheetTableElement, rowStyles.measureCellText);
const widths = sheet.columns.map(column => measurer.measureTextWidth(column.name));
for(let rowI = 0; rowI < sheet.rows.length; rowI++) {
const row = sheet.rows[rowI];
for(let cellI = 0; cellI < row.length; cellI++) {
const cell = '' + row[cellI];
widths[cellI] = Math.max(widths[cellI], measurer.measureTextWidth(cell));
}
}
return widths;
}

export function getFooterText(footerText:string|GeneratedFooterText|undefined, sheet:HoneSheet):string {
if (footerText === undefined) return '';
if (footerText === GeneratedFooterText.ROW_COUNT) return `${sheet.rows.length} ${plural('row', sheet.rows.length)}`;
return footerText;
}

export function syncScrollableElements(headerInnerElement:DivRef, rowsScrollElement:DivRef) {
if (!headerInnerElement.current || !rowsScrollElement.current) return;
const scrollLeft = rowsScrollElement.current.scrollLeft;
headerInnerElement.current.style.transform = `translateX(-${scrollLeft}px)`;
}

export function getRowScrollContainerStyle(displayRowCount:number|undefined, parentElement:HTMLDivElement|null):CSSProperties {
if (!displayRowCount || !parentElement) return {};
const measurer = new DOMTextMeasurer(parentElement, rowStyles.measureCellText);
const lineHeight = measurer.getLineHeight();
return {maxHeight:displayRowCount * lineHeight + 'px'};
}

export function setHorizontalScroll(rowsScrollElement:DivRef, horizontalScroll?:HorizontalScroll) {
if (horizontalScroll === undefined || horizontalScroll === HorizontalScroll.CLEAR || rowsScrollElement.current === null) return;
if (horizontalScroll === HorizontalScroll.LEFT) {
rowsScrollElement.current.scrollLeft = 0;
} else if (horizontalScroll === HorizontalScroll.RIGHT) {
rowsScrollElement.current.scrollLeft = rowsScrollElement.current.scrollWidth;
}
}

export function setVerticalScroll(rowsScrollElement:DivRef, verticalScroll?:VerticalScroll) {
if (verticalScroll === undefined || verticalScroll === VerticalScroll.CLEAR || rowsScrollElement.current === null) return;
if (verticalScroll === VerticalScroll.TOP) {
rowsScrollElement.current.scrollTop = 0;
} else if (verticalScroll === VerticalScroll.BOTTOM) {
rowsScrollElement.current.scrollTop = rowsScrollElement.current.scrollHeight;
}
}
4 changes: 4 additions & 0 deletions src/components/sheetTable/types/GeneratedFooterText.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum GeneratedFooterText {
ROW_COUNT = 0,
}

7 changes: 7 additions & 0 deletions src/components/sheetTable/types/HorizontalScroll.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
enum HorizontalScroll {
CLEAR = 0,
LEFT,
RIGHT
}

export default HorizontalScroll;
7 changes: 7 additions & 0 deletions src/components/sheetTable/types/VerticalScroll.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
enum VerticalScroll {
CLEAR = 0,
TOP,
BOTTOM
}

export default VerticalScroll;
2 changes: 1 addition & 1 deletion src/homeScreen/HomeScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ import ConfirmSheetPasteDialog from "./dialogs/ConfirmSheetPasteDialog";
import ImportExampleDialog from "./dialogs/ImportExampleDialog";
import LLMDevPauseDialog from "@/homeScreen/dialogs/LLMDevPauseDialog";
import { LOAD_URL } from "@/common/urlUtil";
import { HorizontalScroll } from "@/components/sheetTable/SheetTable";
import { doesSheetHaveWritableColumns } from "@/sheets/sheetUtil";
import HorizontalScroll from "@/components/sheetTable/types/HorizontalScroll";

function HomeScreen() {
const [sheet, setSheet] = useState<HoneSheet|null>(null);
Expand Down
3 changes: 2 additions & 1 deletion src/homeScreen/PromptOutputRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { useMemo, useState, useEffect } from "react";

import HoneSheet from "@/sheets/types/HoneSheet";
import styles from './PromptOutputRow.module.css';
import SheetTable, { HorizontalScroll } from "@/components/sheetTable/SheetTable";
import SheetTable from "@/components/sheetTable/SheetTable";
import HorizontalScroll from "@/components/sheetTable/types/HorizontalScroll";

type Props = {
sheet:HoneSheet;
Expand Down
4 changes: 3 additions & 1 deletion src/homeScreen/SheetPane.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import Pane, { ButtonDefinition } from "@/components/pane/Pane";
import { getComment } from "./interactions/comment";
import HoneSheet from "@/sheets/types/HoneSheet";
import SheetTable, { GeneratedFooterText, HorizontalScroll } from "@/components/sheetTable/SheetTable";
import SheetTable from "@/components/sheetTable/SheetTable";
import HorizontalScroll from "@/components/sheetTable/types/HorizontalScroll";
import { GeneratedFooterText } from "@/components/sheetTable/types/GeneratedFooterText";

type Props = {
sheet: HoneSheet|null,
Expand Down
3 changes: 2 additions & 1 deletion src/homeScreen/dialogs/ConfirmSheetPasteDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import DialogFooter from '@/components/modalDialogs/DialogFooter';
import DialogButton from '@/components/modalDialogs/DialogButton';
import HoneSheet from '@/sheets/types/HoneSheet';
import { doesSheetHaveWritableColumns } from '@/sheets/sheetUtil';
import SheetTable, { GeneratedFooterText } from '@/components/sheetTable/SheetTable';
import SheetTable from '@/components/sheetTable/SheetTable';
import { GeneratedFooterText } from '@/components/sheetTable/types/GeneratedFooterText';

type Props = {
pastedSheet:HoneSheet|null,
Expand Down
3 changes: 2 additions & 1 deletion src/homeScreen/dialogs/ImportExampleDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import DialogFooter from '@/components/modalDialogs/DialogFooter';
import DialogButton from '@/components/modalDialogs/DialogButton';
import SheetSelector from './SheetSelector';
import HoneSheet from '@/sheets/types/HoneSheet';
import SheetTable, { GeneratedFooterText } from '@/components/sheetTable/SheetTable';
import SheetTable from '@/components/sheetTable/SheetTable';
import StringMap from '@/common/types/StringMap';
import styles from './ImportExampleDialog.module.css';
import { GeneratedFooterText } from '@/components/sheetTable/types/GeneratedFooterText';

type Props = {
availableSheets:HoneSheet[],
Expand Down
3 changes: 2 additions & 1 deletion src/homeScreen/dialogs/ImportSheetDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import DialogFooter from '@/components/modalDialogs/DialogFooter';
import DialogButton from '@/components/modalDialogs/DialogButton';
import SheetSelector from './SheetSelector';
import HoneSheet from '@/sheets/types/HoneSheet';
import SheetTable, { GeneratedFooterText } from '@/components/sheetTable/SheetTable';
import SheetTable from '@/components/sheetTable/SheetTable';
import { GeneratedFooterText } from '@/components/sheetTable/types/GeneratedFooterText';

type Props = {
availableSheets:HoneSheet[],
Expand Down

0 comments on commit 021cafd

Please sign in to comment.