diff --git a/src/components/table/Table.ts b/src/components/table/Table.ts index 1bb6a0e..43b1a84 100644 --- a/src/components/table/Table.ts +++ b/src/components/table/Table.ts @@ -3,10 +3,10 @@ import * as actions from 'redux/action-creators'; import { $, Dom } from 'core/Dom'; import { ComponentOptionsType, ExcelComponent } from 'core/ExcelComponent'; import { TableSelection } from 'components/table/TableSelection'; -import { changeCurrentStyles, changeCurrentText, changeTableSize } from 'redux/action-creators'; +import { changeCurrentStyles, changeCurrentText, changeTableSize, removeRowFromTable } from 'redux/action-creators'; import { createTable, getNewRowHTML } from 'components/table/table.template'; import { initialState, initialStyleState } from 'src/constants'; -import { parse } from 'core/utils'; +import { getCellId, parse } from 'core/utils'; import { resizeHandler } from 'components/table/handlers/table.resize'; import { selectHandler } from 'components/table/handlers/table.select.handler'; @@ -45,8 +45,8 @@ export class Table extends ExcelComponent { const maxColFromState = Math.max(...Object.keys(colState).map(el => +el)); const normalTableSize = { - col: Math.max(maxRowFromState, row), - row: Math.max(maxColFromState, col), + row: Math.max(maxRowFromState, row), + col: Math.max(maxColFromState, col), }; if ((maxColFromState !== this.tableSize.col) || (maxRowFromState !== this.tableSize.row)) { @@ -65,6 +65,7 @@ export class Table extends ExcelComponent { this.$onEventFromObserver('formula:enter-press', () => this.selection.$currentCell.focus()); this.$onEventFromObserver('toolbar:applyStyle', this.updateCurrentStyles); this.$onEventFromObserver('toolbar:add-row', this.addNewRowHandler); + this.$onEventFromObserver('toolbar:remove-row', this.removeRowHandler); } initTable() { @@ -156,7 +157,7 @@ export class Table extends ExcelComponent { })); }; - updateCurrentStyles = (style: CSSStyleDeclaration) => { + updateCurrentStyles = (style: Partial) => { this.selection.applyStyle(style); this.dispatchToStore(actions.applyStyle({ value: style, @@ -194,4 +195,17 @@ export class Table extends ExcelComponent { onMouseup() { this.isMouseDowned = false; } + + removeRowHandler = () => { + const cell = this.selection.$focusedCell; + const cellId = getCellId(cell); + if (!cellId) return; + + const { row } = cellId; + const nodeToRemove = this.$root.find(`[data-row='${row}']`); + this.$root.removeChild(nodeToRemove); + this.selection.clearSelection(); + + this.dispatchToStore(removeRowFromTable(+row)); + }; } diff --git a/src/components/table/TableSelection.ts b/src/components/table/TableSelection.ts index ecc45c4..58fcba6 100644 --- a/src/components/table/TableSelection.ts +++ b/src/components/table/TableSelection.ts @@ -1,6 +1,7 @@ import { Table } from 'components/table/Table'; import { $, Dom } from 'core/Dom'; import { getParamsFromCellId, startCellId } from 'components/table/table.functions'; +import { initialStyleState } from 'src/constants'; export class TableSelection { static selectedClassName = 'selected'; @@ -60,6 +61,7 @@ export class TableSelection { this.$currentCell?.removeClass(TableSelection.selectedClassName); this.clearHeaderSelection(); this.selectedCellsGroup = []; + this.rootTable.updateCurrentStyles(initialStyleState); } selectFromTo($from: Dom, $cell: Dom) { @@ -110,7 +112,7 @@ export class TableSelection { this.$currentCell = $cell; } - applyStyle(style: CSSStyleDeclaration) { + applyStyle(style: Partial) { this.selectedCellsGroup.forEach(el => el.css(style)); } diff --git a/src/components/table/handlers/table.select.handler.ts b/src/components/table/handlers/table.select.handler.ts index a668c02..01be984 100644 --- a/src/components/table/handlers/table.select.handler.ts +++ b/src/components/table/handlers/table.select.handler.ts @@ -71,6 +71,7 @@ export function selectHandler(event: MouseEvent | KeyboardEvent, selection: Tabl switch (key) { case 'ArrowDown': { + if (selection.rootTable.tableSize.row === row + 1) return; row++; break; } @@ -79,6 +80,7 @@ export function selectHandler(event: MouseEvent | KeyboardEvent, selection: Tabl break; } case 'ArrowRight': { + if (selection.rootTable.tableSize.col === col + 1) return; col++; break; } diff --git a/src/components/toolbar/Toolbar.ts b/src/components/toolbar/Toolbar.ts index 44e6e62..b8d06c8 100644 --- a/src/components/toolbar/Toolbar.ts +++ b/src/components/toolbar/Toolbar.ts @@ -49,11 +49,16 @@ export class Toolbar extends ExcelComponentState { // TODO refactor, make style handler const target = $(event.target); - if (target.closest('[data-addbtn]').isExist) { + if (target.closest('[data-add-row-btn]').isExist) { this.$emitEventToObserver('toolbar:add-row'); return; } + if (target.closest('[data-remove-row-btn]').isExist) { + this.$emitEventToObserver('toolbar:remove-row'); + return; + } + let stringValue; let value; let key; diff --git a/src/components/toolbar/toolbar.template.ts b/src/components/toolbar/toolbar.template.ts index 51a80cd..b684dc3 100644 --- a/src/components/toolbar/toolbar.template.ts +++ b/src/components/toolbar/toolbar.template.ts @@ -88,7 +88,8 @@ export function createToolbar(state: ToolbarStateType): string { buttons.push(increaseDecreaseFontSize); buttons.push(selectGroup); - buttons.push(createAddRowTable()); + buttons.push(createAddRowButton()); + buttons.push(createRemoveRowButton()); return buttons.join(''); } @@ -170,10 +171,18 @@ function createButtonFromConfig(btnConfig: ButtonConfigType) { `; } -function createAddRowTable() { +function createAddRowButton() { return ` -
- add_circle +
+ add_circle +
+ `; +} + +function createRemoveRowButton() { + return ` +
+ remove_circle
`; } diff --git a/src/core/Dom.ts b/src/core/Dom.ts index ab1b9a6..f6969b1 100644 --- a/src/core/Dom.ts +++ b/src/core/Dom.ts @@ -134,6 +134,10 @@ export class Dom implements DomClass { return this.$el.getAttribute(name); } + removeChild($child: Dom) { + this.$el.removeChild($child.$el); + } + get isExist(): boolean { return !!this.$el; } diff --git a/src/core/utils.ts b/src/core/utils.ts index d2b9f0d..ab4f05e 100644 --- a/src/core/utils.ts +++ b/src/core/utils.ts @@ -1,3 +1,4 @@ +import { Dom } from 'core/Dom'; import { CallbackType, StateType } from 'redux/types'; import { fontSizes } from 'src/constants'; @@ -66,3 +67,11 @@ export function isSmallestFontSize(fontSize?: string): number | boolean { export function getMethodNameByEventName(eventName: string): string { return `on${capitalize(eventName)}`; } + +export function getCellId($cell: Dom): { row: string, col:string } | false { + if (!$cell.isExist) return false; + const id = $cell.data.id?.split(':'); + if (!Array.isArray(id)) return false; + const [row, col] = id; + return { row, col }; +} diff --git a/src/redux/action-constants.ts b/src/redux/action-constants.ts index 5dc5bf4..6c853b8 100644 --- a/src/redux/action-constants.ts +++ b/src/redux/action-constants.ts @@ -7,3 +7,4 @@ export const TABLE_RESIZE = 'TABLE_RESIZE'; export const UPDATE_DATE = 'UPDATE_DATE'; export const CHANGE_CURRENT_TEXT = 'CHANGE_CURRENT_TEXT'; export const CHANGE_TABLE_SIZE = 'CHANGE_TABLE_SIZE'; +export const REMOVE_ROW_FROM_TABLE = 'REMOVE_ROW_FROM_TABLE'; diff --git a/src/redux/action-creators.ts b/src/redux/action-creators.ts index ce73679..13a1d7f 100644 --- a/src/redux/action-creators.ts +++ b/src/redux/action-creators.ts @@ -7,7 +7,7 @@ import { APPLY_STYLES, CHANGE_TITLE, DELETE_TABLE, - UPDATE_DATE, CHANGE_CURRENT_TEXT, CHANGE_TABLE_SIZE, + UPDATE_DATE, CHANGE_CURRENT_TEXT, CHANGE_TABLE_SIZE, REMOVE_ROW_FROM_TABLE, } from 'redux/action-constants'; export function tableResize(resizeData: ResizeReturnDataType): ActionType { @@ -31,7 +31,7 @@ export function changeCurrentStyles(data: Partial): ActionT }; } -export function applyStyle(data: { ids: (string | undefined)[], value: CSSStyleDeclaration }): ActionType { +export function applyStyle(data: { ids: (string | undefined)[], value: Partial }): ActionType { return { type: APPLY_STYLES, data, @@ -72,3 +72,10 @@ export function changeTableSize(data: { col: number, row: number }): ActionType data, }; } + +export function removeRowFromTable(removedRowNumber: number) { + return { + type: REMOVE_ROW_FROM_TABLE, + data: removedRowNumber, + }; +} diff --git a/src/redux/rootReducer.ts b/src/redux/rootReducer.ts index 437f739..86564a7 100644 --- a/src/redux/rootReducer.ts +++ b/src/redux/rootReducer.ts @@ -1,3 +1,4 @@ +import { ToolbarStateType } from 'components/toolbar/toolbar-types'; import { ActionType, StateType } from 'redux/types'; import { ActiveRoute } from 'core/routes/ActiveRoute'; import { storageName } from 'pages/ExcelPage'; @@ -8,7 +9,7 @@ import { APPLY_STYLES, CHANGE_TITLE, DELETE_TABLE, - UPDATE_DATE, CHANGE_CURRENT_TEXT, CHANGE_TABLE_SIZE, + UPDATE_DATE, CHANGE_CURRENT_TEXT, CHANGE_TABLE_SIZE, REMOVE_ROW_FROM_TABLE, } from 'redux/action-constants'; export function rootReducer(state: StateType, action: ActionType): StateType { @@ -74,6 +75,70 @@ export function rootReducer(state: StateType, action: ActionType): StateType { return { ...state, tableSize: action.data }; } + case REMOVE_ROW_FROM_TABLE: { + // needs to be redefined next params: + // rowState: { [k: number]: number }; + // dataState: { [k: string]: string }; + // stylesState: { [k: string]: ToolbarStateType }; + // tableSize: { col: number, row: number } + + const newRowStateEntries: [number, number][] = []; + const newDataStateEntries: [string, string][] = []; + const newStylesStateEntries: [string, ToolbarStateType][] = []; + // rowState change + Object.entries(state.rowState).forEach(([key, value]) => { + switch (true) { + case key < action.data: + newRowStateEntries.push([+key, value]); + break; + + case key > action.data: + newRowStateEntries.push([+key - 1, value]); + break; + + default: break; + } + }); + const newRowState = Object.fromEntries(newRowStateEntries); + // dataState change + Object.entries(state.dataState).forEach(([key, value]) => { + const [row, col] = key.split(':'); + switch (true) { + case row < action.data: + newDataStateEntries.push([key, value.toString()]); + break; + + case row > action.data: + newDataStateEntries.push([`${+row - 1}:${col}`, value.toString()]); + break; + + default: break; + } + }); + const newDataState = Object.fromEntries(newDataStateEntries); + // stylesState change + Object.entries(state.stylesState).forEach(([key, value]) => { + const [row, col] = key.split(':'); + switch (true) { + case row < action.data: + newStylesStateEntries.push([key, value]); + break; + + case row > action.data: + newStylesStateEntries.push([`${+row - 1}:${col}`, value]); + break; + + default: break; + } + }); + const newStylesState = Object.fromEntries(newStylesStateEntries); + // tableSize + const newTableSize = { col: state.tableSize.col, row: state.tableSize.row - 1 }; + + // const newRowState = Object.keys(state.rowState).filter(rowIndex => rowIndex.toString() !== action.data.toString()). + return { ...state, rowState: newRowState, dataState: newDataState, stylesState: newStylesState, tableSize: newTableSize }; + } + default: return state; } } diff --git a/src/styles/components/table.scss b/src/styles/components/table.scss index fb281e7..28ed6a2 100644 --- a/src/styles/components/table.scss +++ b/src/styles/components/table.scss @@ -5,6 +5,7 @@ position: absolute; left: 0; right: 0; + max-height: calc(100vh - ($header-height + $toolbar-height + $formula-height)); top: $header-height + $toolbar-height + $formula-height; overflow: auto; font-size: $default-cell-font-size;