diff --git a/src/components/sheetTable/SheetRow.tsx b/src/components/sheetTable/SheetRow.tsx index 6a529d4..2ee8f64 100644 --- a/src/components/sheetTable/SheetRow.tsx +++ b/src/components/sheetTable/SheetRow.tsx @@ -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, diff --git a/src/components/sheetTable/SheetTable.tsx b/src/components/sheetTable/SheetTable.tsx index c81dc02..5c0ece5 100644 --- a/src/components/sheetTable/SheetTable.tsx +++ b/src/components/sheetTable/SheetTable.tsx @@ -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, @@ -37,58 +23,6 @@ type Props = { footerText?:string|GeneratedFooterText } -type DivRef = RefObject; - -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(null); const headerInnerElement = useRef(null); @@ -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; @@ -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 (
_syncScrollableElements(headerInnerElement, rowsScrollElement)}> + onScroll={() => syncScrollableElements(headerInnerElement, rowsScrollElement)}>
{rowsContent}
diff --git a/src/components/sheetTable/sheetTableUtil.ts b/src/components/sheetTable/interactions/row.ts similarity index 100% rename from src/components/sheetTable/sheetTableUtil.ts rename to src/components/sheetTable/interactions/row.ts diff --git a/src/components/sheetTable/interactions/table.ts b/src/components/sheetTable/interactions/table.ts new file mode 100644 index 0000000..2ac35b9 --- /dev/null +++ b/src/components/sheetTable/interactions/table.ts @@ -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; + +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; + } +} \ No newline at end of file diff --git a/src/components/sheetTable/types/GeneratedFooterText.ts b/src/components/sheetTable/types/GeneratedFooterText.ts new file mode 100644 index 0000000..98b0c54 --- /dev/null +++ b/src/components/sheetTable/types/GeneratedFooterText.ts @@ -0,0 +1,4 @@ +export enum GeneratedFooterText { + ROW_COUNT = 0, +} + diff --git a/src/components/sheetTable/types/HorizontalScroll.ts b/src/components/sheetTable/types/HorizontalScroll.ts new file mode 100644 index 0000000..7d84fe2 --- /dev/null +++ b/src/components/sheetTable/types/HorizontalScroll.ts @@ -0,0 +1,7 @@ +enum HorizontalScroll { + CLEAR = 0, + LEFT, + RIGHT +} + +export default HorizontalScroll; \ No newline at end of file diff --git a/src/components/sheetTable/types/VerticalScroll.ts b/src/components/sheetTable/types/VerticalScroll.ts new file mode 100644 index 0000000..fd7e77e --- /dev/null +++ b/src/components/sheetTable/types/VerticalScroll.ts @@ -0,0 +1,7 @@ +enum VerticalScroll { + CLEAR = 0, + TOP, + BOTTOM +} + +export default VerticalScroll; \ No newline at end of file diff --git a/src/homeScreen/HomeScreen.tsx b/src/homeScreen/HomeScreen.tsx index c21d263..b555608 100644 --- a/src/homeScreen/HomeScreen.tsx +++ b/src/homeScreen/HomeScreen.tsx @@ -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(null); diff --git a/src/homeScreen/PromptOutputRow.tsx b/src/homeScreen/PromptOutputRow.tsx index ffea206..2e179b5 100644 --- a/src/homeScreen/PromptOutputRow.tsx +++ b/src/homeScreen/PromptOutputRow.tsx @@ -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; diff --git a/src/homeScreen/SheetPane.tsx b/src/homeScreen/SheetPane.tsx index 87e6fad..1ec8899 100644 --- a/src/homeScreen/SheetPane.tsx +++ b/src/homeScreen/SheetPane.tsx @@ -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, diff --git a/src/homeScreen/dialogs/ConfirmSheetPasteDialog.tsx b/src/homeScreen/dialogs/ConfirmSheetPasteDialog.tsx index 2f25257..539bbc3 100644 --- a/src/homeScreen/dialogs/ConfirmSheetPasteDialog.tsx +++ b/src/homeScreen/dialogs/ConfirmSheetPasteDialog.tsx @@ -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, diff --git a/src/homeScreen/dialogs/ImportExampleDialog.tsx b/src/homeScreen/dialogs/ImportExampleDialog.tsx index 24d5136..061cb33 100644 --- a/src/homeScreen/dialogs/ImportExampleDialog.tsx +++ b/src/homeScreen/dialogs/ImportExampleDialog.tsx @@ -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[], diff --git a/src/homeScreen/dialogs/ImportSheetDialog.tsx b/src/homeScreen/dialogs/ImportSheetDialog.tsx index 183b832..8bf17b1 100644 --- a/src/homeScreen/dialogs/ImportSheetDialog.tsx +++ b/src/homeScreen/dialogs/ImportSheetDialog.tsx @@ -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[],