From 1e3f305ba32ff7c064ed34e8091eb335ce517b62 Mon Sep 17 00:00:00 2001 From: Mathias Jakobsen Date: Mon, 11 Sep 2023 10:59:48 +0100 Subject: [PATCH] Merge pull request #14763 from overleaf/mj-extend-col-row-selectors [visual] Allow shift clicking column and row selectors GitOrigin-RevId: 2e6191741681196e4af462f92dc60268f22d137e --- .../contexts/selection-context.tsx | 11 ++++- .../components/table-generator/selectors.tsx | 45 ++++++++++++++----- ...codemirror-editor-table-generator.spec.tsx | 44 ++++++++++++++++++ 3 files changed, 86 insertions(+), 14 deletions(-) diff --git a/services/web/frontend/js/features/source-editor/components/table-generator/contexts/selection-context.tsx b/services/web/frontend/js/features/source-editor/components/table-generator/contexts/selection-context.tsx index 1d9e11a98b..c9487dbad5 100644 --- a/services/web/frontend/js/features/source-editor/components/table-generator/contexts/selection-context.tsx +++ b/services/web/frontend/js/features/source-editor/components/table-generator/contexts/selection-context.tsx @@ -30,13 +30,20 @@ export class TableSelection { ) } - static selectRow(row: number, table: TableData) { + selectRow(row: number, extend: boolean, table: TableData) { return new TableSelection( - { row, cell: 0 }, + { row: extend ? this.from.row : row, cell: 0 }, { row, cell: table.columns.length - 1 } ) } + selectColumn(column: number, extend: boolean, table: TableData) { + return new TableSelection( + { row: 0, cell: extend ? this.from.cell : column }, + { row: table.rows.length - 1, cell: column } + ) + } + normalized() { const minX = Math.min(this.from.cell, this.to.cell) const maxX = Math.max(this.from.cell, this.to.cell) diff --git a/services/web/frontend/js/features/source-editor/components/table-generator/selectors.tsx b/services/web/frontend/js/features/source-editor/components/table-generator/selectors.tsx index 9090281733..2339254280 100644 --- a/services/web/frontend/js/features/source-editor/components/table-generator/selectors.tsx +++ b/services/web/frontend/js/features/source-editor/components/table-generator/selectors.tsx @@ -1,4 +1,4 @@ -import { useCallback } from 'react' +import { MouseEventHandler, useCallback } from 'react' import { TableSelection, useSelectionContext, @@ -9,14 +9,22 @@ import { useTableContext } from './contexts/table-context' export const ColumnSelector = ({ index }: { index: number }) => { const { selection, setSelection } = useSelectionContext() const { table } = useTableContext() - const onColumnSelect = useCallback(() => { - setSelection( - new TableSelection( - { row: 0, cell: index }, - { row: table.rows.length - 1, cell: index } - ) - ) - }, [table.rows.length, index, setSelection]) + const onColumnSelect: MouseEventHandler = useCallback( + event => { + event.preventDefault() + if (!selection) { + setSelection( + new TableSelection( + { row: 0, cell: index }, + { row: table.rows.length - 1, cell: index } + ) + ) + return + } + setSelection(selection.selectColumn(index, event.shiftKey, table)) + }, + [index, setSelection, selection, table] + ) const fullySelected = selection?.isColumnSelected(index, table) return ( // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions @@ -32,9 +40,22 @@ export const ColumnSelector = ({ index }: { index: number }) => { export const RowSelector = ({ index }: { index: number }) => { const { table } = useTableContext() const { selection, setSelection } = useSelectionContext() - const onSelect = useCallback(() => { - setSelection(TableSelection.selectRow(index, table)) - }, [index, setSelection, table]) + const onSelect: MouseEventHandler = useCallback( + event => { + event.preventDefault() + if (!selection) { + setSelection( + new TableSelection( + { row: index, cell: 0 }, + { row: index, cell: table.columns.length - 1 } + ) + ) + return + } + setSelection(selection.selectRow(index, event.shiftKey, table)) + }, + [index, setSelection, table, selection] + ) const fullySelected = selection?.isRowSelected(index, table) return ( // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions diff --git a/services/web/test/frontend/features/source-editor/components/codemirror-editor-table-generator.spec.tsx b/services/web/test/frontend/features/source-editor/components/codemirror-editor-table-generator.spec.tsx index 20ad0fa9c6..3aa902d69d 100644 --- a/services/web/test/frontend/features/source-editor/components/codemirror-editor-table-generator.spec.tsx +++ b/services/web/test/frontend/features/source-editor/components/codemirror-editor-table-generator.spec.tsx @@ -588,5 +588,49 @@ cell 3 & cell 4 \\\\ cy.get('@cell-2').tab({ shift: true }) cy.get('@cell-1').should('have.focus').should('have.class', 'selected') }) + + it('Can select rows and columns with selectors', function () { + mountEditor(` +\\begin{tabular}{cc } + cell 1 & cell 2\\\\ + cell 3 & cell 4 \\\\ +\\end{tabular}`) + cy.get('.table-generator-cell').eq(0).as('cell-1') + cy.get('.table-generator-cell').eq(1).as('cell-2') + cy.get('.table-generator-cell').eq(2).as('cell-3') + cy.get('.table-generator-cell').eq(3).as('cell-4') + + cy.get('.column-selector').eq(0).click() + cy.get('@cell-1').should('have.class', 'selected') + cy.get('@cell-2').should('not.have.class', 'selected') + cy.get('@cell-3').should('have.class', 'selected') + cy.get('@cell-4').should('not.have.class', 'selected') + cy.get('.column-selector').eq(1).click() + cy.get('@cell-1').should('not.have.class', 'selected') + cy.get('@cell-2').should('have.class', 'selected') + cy.get('@cell-3').should('not.have.class', 'selected') + cy.get('@cell-4').should('have.class', 'selected') + cy.get('.column-selector').eq(0).click({ shiftKey: true }) + cy.get('@cell-1').should('have.class', 'selected') + cy.get('@cell-2').should('have.class', 'selected') + cy.get('@cell-3').should('have.class', 'selected') + cy.get('@cell-4').should('have.class', 'selected') + + cy.get('.row-selector').eq(0).click() + cy.get('@cell-1').should('have.class', 'selected') + cy.get('@cell-2').should('have.class', 'selected') + cy.get('@cell-3').should('not.have.class', 'selected') + cy.get('@cell-4').should('not.have.class', 'selected') + cy.get('.row-selector').eq(1).click() + cy.get('@cell-1').should('not.have.class', 'selected') + cy.get('@cell-2').should('not.have.class', 'selected') + cy.get('@cell-3').should('have.class', 'selected') + cy.get('@cell-4').should('have.class', 'selected') + cy.get('.row-selector').eq(0).click({ shiftKey: true }) + cy.get('@cell-1').should('have.class', 'selected') + cy.get('@cell-2').should('have.class', 'selected') + cy.get('@cell-3').should('have.class', 'selected') + cy.get('@cell-4').should('have.class', 'selected') + }) }) })