import { DOMContext } from '../../../treb-base-types/src/index'; import type { DataModel, ViewModel, Annotation } from '../../../treb-data-model/src/index'; import type { Tile } from '../types/tile'; import type { Theme, Point, Extent, Size, Position, ICellAddress, Table, IArea } from '../../../treb-base-types/src/index'; import { Area, Rectangle } from '../../../treb-base-types/src/index'; import type { GridEvent } from '../types/grid_events'; import type { CellValue, AnnotationLayout, Corner } from '../../../treb-base-types/src/index'; import { Area as TileRange } from '../../../treb-base-types/src/index'; export { Area as TileRange } from '../../../treb-base-types/src/index'; export interface TooltipOptions { up?: true; left?: true; text?: string; x?: number; y?: number; } /** * layout structure and management functions */ export declare abstract class BaseLayout { protected model: DataModel; protected view: ViewModel; DOM: DOMContext; column_header: HTMLDivElement; row_header: HTMLDivElement; contents: HTMLDivElement; buffer_canvas: HTMLCanvasElement; corner: HTMLDivElement; corner_canvas: HTMLCanvasElement; grid_selection: SVGElement; grid_cover: HTMLDivElement; column_header_cover: HTMLDivElement; row_header_cover: HTMLDivElement; annotation_container: HTMLDivElement; mask: HTMLDivElement; mock_selection: HTMLDivElement; container?: HTMLElement; grid_tiles: Tile[][]; column_header_tiles: Tile[]; row_header_tiles: Tile[]; corner_selection: SVGElement; row_header_selection: SVGElement; column_header_selection: SVGElement; corner_annotations: HTMLDivElement; row_header_annotations: HTMLDivElement; column_header_annotations: HTMLDivElement; frozen_row_tiles: Tile[]; frozen_column_tiles: Tile[]; header_size: Size; applied_theme_colors: string[]; /** * last rendered column. this is used to calculate the limits of * cell overflows, which may exceed actual data in the sheet. */ last_column: number; total_height: number; total_width: number; default_row_height: number; default_column_width: number; header_offset: { x: number; y: number; }; /** freeze rows/columns */ /** * NOTE: dpr can probably change, on zoom; but I'm not sure there's * an event we can trap for that. it might be necessary to test this * periodically. */ dpr: number; /** separate scale, user-controlled (testing...) */ scale: number; /** * this is a reference to the node that handles scrolling. it needs * to be different for legacy renderer. */ scroll_reference_node: HTMLElement; get scroll_offset(): { x: number; y: number; }; set scroll_offset(offset: { x: number; y: number; }); protected dropdown_caret: SVGSVGElement; protected spill_border: SVGSVGElement; /** we have to disable mock selection for IE or it breaks key handling */ private trident; private default_tile_size; private tooltip_state?; private tooltip; private dropdown_list; private dropdown_caret_visible; private dropdown_callback?; private dropdown_selected?; private note_node; private sort_button; private title_node; private row_cache; private column_cache; /** * flag so we don't try to paint before we have tiles */ private initialized; constructor(model: DataModel, view: ViewModel, mock?: boolean, DOM?: DOMContext); FocusInLayout(target?: EventTarget): boolean; /** * if the DPR has changed, update it and return true. otherwise return * false. this is used on resize events: if the scale has changed, we * probably want to repaint (and we need to update scale). */ UpdateDPR(): boolean; /** wrapper around sheet method, incorporating scale */ ColumnWidth(column: number): number; /** wrapper around sheet method, incorporating scale */ RowHeight(row: number): number; /** * wrapper around sheet method, incorporating scale * * NOTE: this does not update total size, so unless there's a subsequent call * to a layout update, total size will be out of sync */ SetRowHeight(row: number, height: number): void; /** * wrapper around sheet method, incorporating scale * * NOTE: this does not update total size, so unless there's a subsequent call * to a layout update, total size will be out of sync */ SetColumnWidth(column: number, width: number): void; /** * show/hide grid selections. used when selecting annotations. */ ShowSelections(show?: boolean): void; HideTitle(): void; ShowTitle(text: string, address: ICellAddress): void; HideNote(): void; HideTableSortButton(): void; AreaToRect(area: IArea, offset?: { x: number; y: number; }): Rectangle; Screenshot({ type, quality, download, selection, target, nub, }: { type?: 'png' | 'jpeg' | 'webp'; quality?: number; download?: boolean; selection?: Area; target?: Area; nub?: boolean; }): Promise; ShowTableSortButton(table: Table, column: number, address: ICellAddress): void; /** * adding html parameter at the end, so we can keep the function * signature otherwise the same. this is for markdown formatting. */ ShowNote(note: string, address: ICellAddress, event?: MouseEvent, markdown?: string): void; /** * raise or lower annotation in z-order (implicit) * * returns true if we've made changes, so you can trigger any necessary * events or side-effects */ AnnotationLayoutOrder(annotation: Annotation, delta: number): boolean; /** * */ PointToAnnotationCorner(point: Point): Corner; /** * utility for managing (new) annotation layout. we offset the {top, left} * by {1, 1} pixel so that the alignment snaps to cell boundaries. */ RectToAnnotationLayout(rect: Partial): AnnotationLayout; AddressToAnnotationLayout(tl: ICellAddress, br: ICellAddress): AnnotationLayout; /** * @see RectToAnnotationLayout regarding the 1 pixel shift */ AnnotationLayoutToRect(layout: AnnotationLayout): Rectangle; UpdateAnnotation(elements: Annotation | Annotation[], theme: Theme): void; /** returns a list of copies painted to frozen panes, for move/size */ GetFrozenAnnotations(annotation: Annotation): HTMLElement[]; /** * clone all annotations into freeze panes */ CloneFrozenAnnotations(): void; /** * remove all annotations from freeze panes * */ ClearFrozenAnnotations(): void; /** * remove a frozen annotation * @param annotation */ RemoveFrozenAnnotation(annotation: Annotation): void; /** * clone a single annotation. usually this will be used on create, but * we batch them when we freeze panes (from unfrozen) */ CloneFrozenAnnotation(annotation: Annotation): void; RemoveAnnotation(annotation: Annotation): void; /** * remove annotation nodes from the container, without impacting * the underlying data. annotations will still retain nodes, they * just won't be attached to anything. * * NOTE: IE destroys nodes if you do this? (...) * patch in legacy... actually we'll do it here */ RemoveAnnotationNodes(): void; AddAnnotation(annotation: Annotation, theme: Theme): void; AnnotationMouseDown(annotation: Annotation, node: HTMLElement, event: MouseEvent, move_target: HTMLElement, resize_target: HTMLElement): Promise; /** * this used to be an abstract method for initializing. we're taking it * over to do some additional work post init, and renaming the subclass-specific * method (@see InitializeInternal). */ Initialize(container: HTMLElement, callbacks: { scroll: () => void; dropdown: (value: CellValue) => void; sort: (table: string, column: number, asc: boolean) => void; focus: () => void; }, scroll?: boolean): void; /** * create a selection so that this node (and parents) receive * a copy event on ctrl+c (or any other system copy event). * seems to break IE, so split. */ MockSelection(): void; /** * FIXME: this is public for now but tiles should move into * this class, then this method can become private */ CreateTile(classes: string, size: Size, position: Position, first_cell: Position, cell_extent: Extent, pixel_start: Point, parent: HTMLElement, mark_dirty?: boolean): Tile; ApplyThemeColors(): void; /** * applies theme to nodes, as necessary */ ApplyTheme(theme: Theme): void; UpdateTotalSize(): void; UpdateContentsSize(): void; /** hides column/row resize tooltip and removes any specific classes */ HideTooltip(): void; /** show column/row resize tooltip */ ShowTooltip(options?: TooltipOptions): void; ShowDropdownCaret(area: Area, list: CellValue[], current: CellValue): void; ShowSpillBorder(area?: IArea): void; HideDropdownCaret(): void; /** * this method returns the scroll offset adjusted for headers. * if you just want the raw scroll offset, use the accessor. * * @param offset_headers * @returns */ GetScrollOffset(): Point; ScrollTo(address: ICellAddress, x?: boolean, y?: boolean, smooth?: boolean): void; /** * scroll address into view, at top-left or bottom-right depending on * target and current position. also offsets for frozen rows, columns. */ ScrollIntoView(address: ICellAddress, smooth?: boolean): void; UpdateTooltip(options?: TooltipOptions): void; /** * y coordinate to row header. for consistency we return an address. */ CoordinateToRowHeader(y: number): ICellAddress; /** * x coordinate to colum header. for consistency we return an address. */ CoordinateToColumnHeader(x: number): ICellAddress; /** * point to cell address (grid only) * * FIXME: implement cap_maximum parameter (not sure where we would need it) */ PointToAddress_Grid(point: Point, offset_freeze?: boolean): ICellAddress; /** * get an adjacent tile. this is used by the renderer when a merge or * overflow runs out of the painted tile, and we need to paint it. */ AdjacentTile(tile: Tile, row_offset?: number, column_offset?: number): Tile | undefined; UpdateTiles(): void; ClearLayoutCaches(): void; /** * returns the tile index for a given column. this is used to map * a column to a tile in either the header or the grid. * FIXME: speed up w/ lookup cache */ TileIndexForColumn(column: number): number; /** * returns the tile index for a given row. this is used to map * a column to a tile in either the header or the grid. * FIXME: speed up w/ lookup cache */ TileIndexForRow(row: number): number; /** * marks header tiles as dirty, for next repaint call * * UPDATE fix for entire column/row/sheet (the Intersects() method * doesn't support infinities, for some reason: FIX THAT) */ DirtyHeaders(area?: Area): void; DirtyAll(): void; DirtyArea(areas: IArea | IArea[]): void; /** calculate first visible tile based on scroll position */ VisibleTiles(): TileRange; UpdateTileHeights(mark_dirty?: boolean, start_row?: number): void; /** * update all widths. start_column is a hint that can save some work * by skipping unaffected tiles */ UpdateTileWidths(mark_dirty?: boolean, start_column?: number): void; ClampToGrid(point: Point): Point; /** * wrapper method for CellAddressToRectangle allows us to offset for * frozen rows/columns. in some cases we may not want to do this, so * the underlying method is still visible (and cache contains the raw * rectangles, not offset). */ OffsetCellAddressToRectangle(address: ICellAddress): Rectangle; /** * finds the rectangle, in the coordinate space of the grid node, * of the cell with the given address. uses a cache since we wind * up looking up the same rectangles a lot. * * UPDATE dropping rectangle cache in favor of holding row and * column edges. I realized we were holding a lot of redundant * information, and this should be resonably fast. * * TODO could probably be slightly more efficient by holding the * left edge of the column/row at the index; then we don't have to * have special behavior for column/row 0. */ CellAddressToRectangle(address: ICellAddress): Rectangle; /** * resizes the tile at this index, rebuilds structure of subsequent tiles. * this is necessary because tiles keep track of pixel position: so if a * tile is resized, subsequent tiles have to change. */ ResizeTileWidth(index: number, width: number, mark_dirty?: boolean): void; /** * resizes the tile at this index, rebuilds structure of subsequent tiles */ ResizeTileHeight(index: number, height: number, mark_dirty?: boolean): void; /** * do subclass-specific initialization */ abstract InitializeInternal(container: HTMLElement, scroll_callback: () => void): void; /** * set resize cursor. this is subclass-specific because it's set on * different nodes depending on layout. */ abstract ResizeCursor(resize?: 'row' | 'column'): void; protected abstract UpdateTileGridPosition(tile: Tile): void; protected abstract UpdateContainingGrid(): void; protected abstract UpdateGridTemplates(columns: boolean, rows: boolean): void; }