add row button, add row after enter press in last row

This commit is contained in:
Sergey Krylov 2022-07-12 13:27:38 +05:00
parent 09ff3a751d
commit d674203a4d
8 changed files with 84 additions and 23 deletions

View File

@ -4,7 +4,7 @@ import { $, Dom } from 'core/Dom';
import { ComponentOptionsType, ExcelComponent } from 'core/ExcelComponent'; import { ComponentOptionsType, ExcelComponent } from 'core/ExcelComponent';
import { TableSelection } from 'components/table/TableSelection'; import { TableSelection } from 'components/table/TableSelection';
import { changeCurrentStyles, changeCurrentText } from 'redux/action-creators'; import { changeCurrentStyles, changeCurrentText } from 'redux/action-creators';
import { createTable } from 'components/table/table.template'; import { createTable, getNewRowHTML } from 'components/table/table.template';
import { initialStyleState } from 'src/constants'; import { initialStyleState } from 'src/constants';
import { parse } from 'core/utils'; import { parse } from 'core/utils';
import { resizeHandler } from 'components/table/handlers/table.resize'; import { resizeHandler } from 'components/table/handlers/table.resize';
@ -15,6 +15,10 @@ export class Table extends ExcelComponent {
private selection: TableSelection; private selection: TableSelection;
private isMouseDowned: boolean; private isMouseDowned: boolean;
public tableSize = {
row: 50,
col: 24,
};
constructor($root: Dom, options: ComponentOptionsType) { constructor($root: Dom, options: ComponentOptionsType) {
super($root, { super($root, {
@ -27,11 +31,11 @@ export class Table extends ExcelComponent {
} }
toHTML(): string { toHTML(): string {
return createTable(50, 24); return createTable(this.tableSize.row, this.tableSize.col);
} }
prepare() { prepare() {
this.selection = new TableSelection(this.$root); this.selection = new TableSelection(this);
} }
init() { init() {
@ -42,6 +46,7 @@ export class Table extends ExcelComponent {
this.$onEventFromObserver('formula:input', this.updateCurrentText); this.$onEventFromObserver('formula:input', this.updateCurrentText);
this.$onEventFromObserver('formula:enter-press', () => this.selection.current.focus()); this.$onEventFromObserver('formula:enter-press', () => this.selection.current.focus());
this.$onEventFromObserver('toolbar:applyStyle', this.updateCurrentStyles); this.$onEventFromObserver('toolbar:applyStyle', this.updateCurrentStyles);
this.$onEventFromObserver('toolbar:add-row', this.addNewRowHandler);
} }
initTable() { initTable() {
@ -56,14 +61,25 @@ export class Table extends ExcelComponent {
row: this.store.getState()?.rowState, row: this.store.getState()?.rowState,
}; };
Object.keys(size.col).forEach(key => { this.initColSizes(this.$root, size.col);
const cols = this.$root.findAll(`[data-col="${key}"]`); this.initRowSizes(this.$root, size.row);
cols.forEach(el => $(el as HTMLElement).css({ width: `${size.col[+key]}px` })); }
});
Object.keys(size.row).forEach(key => { initRowSizes(rootElem: Dom, rowState = this.store.getState()?.rowState) {
const rows = this.$root.findAll(`[data-row="${key}"]`); if (!rootElem || !rootElem?.$el) return;
rows.forEach(el => $(el as HTMLElement).css({ height: `${size.row[+key]}px` }));
Object.keys(rowState).forEach(key => {
const rows = rootElem.findAll(`[data-row="${key}"]`);
rows.forEach(el => $(el as HTMLElement).css({ height: `${rowState[+key]}px` }));
});
}
initColSizes(rootElem: Dom, colState = this.store.getState()?.colState) {
if (!rootElem || !rootElem?.$el) return;
Object.keys(colState).forEach(key => {
const cols = rootElem.findAll(`[data-col="${key}"]`);
cols.forEach(el => $(el as HTMLElement).css({ width: `${colState[+key]}px` }));
}); });
} }
@ -125,6 +141,13 @@ export class Table extends ExcelComponent {
})); }));
}; };
addNewRowHandler = () => {
this.tableSize.row++;
this.$root.$el.insertAdjacentHTML('beforeend', getNewRowHTML(this.tableSize.row, this.tableSize.col));
const $newRow = $(`[data-row="${this.tableSize.row - 1}"]`);
this.initColSizes($newRow);
};
onMousedown(event: MouseEvent) { onMousedown(event: MouseEvent) {
this.isMouseDowned = true; this.isMouseDowned = true;
selectHandler(event, this.selection, this.emitSelectCallback.bind(this)); selectHandler(event, this.selection, this.emitSelectCallback.bind(this));

View File

@ -1,3 +1,4 @@
import { Table } from 'components/table/Table';
import { $, Dom } from 'core/Dom'; import { $, Dom } from 'core/Dom';
import { getParamsFromCellId, startCellId } from 'components/table/table.functions'; import { getParamsFromCellId, startCellId } from 'components/table/table.functions';
@ -5,9 +6,9 @@ export class TableSelection {
static selectedClassName = 'selected'; static selectedClassName = 'selected';
private group: Dom[]; private group: Dom[];
public current: Dom; public current: Dom;
public rootTable: Dom; public rootTable: Table;
constructor(rootTable: Dom) { constructor(rootTable: Table) {
this.group = []; this.group = [];
this.rootTable = rootTable; this.rootTable = rootTable;
} }
@ -16,12 +17,23 @@ export class TableSelection {
return this.group.map(el => el.data.id); return this.group.map(el => el.data.id);
} }
// TODO make a focus manager
focusToCell($cell: Dom) {
const range = new Range();
const node = $cell.$el;
range.setStartAfter(node.childNodes[0]);
window.getSelection()?.removeAllRanges();
window.getSelection()?.addRange(range);
}
select($el: Dom) { select($el: Dom) {
this.clearSelection(); this.clearSelection();
this.group = [$el]; this.group = [$el];
this.current = $el; this.current = $el;
$el.addClass(TableSelection.selectedClassName); $el.addClass(TableSelection.selectedClassName);
$el.focus(); this.focusToCell($el);
} }
selectByCellId(cellID: { col: number, row: number }) { selectByCellId(cellID: { col: number, row: number }) {
@ -32,7 +44,7 @@ export class TableSelection {
this.clearSelection(); this.clearSelection();
this.current = $(`[data-id="${row}:${col}"]`); this.current = $(`[data-id="${row}:${col}"]`);
this.current.focus(); this.focusToCell(this.current);
this.current.addClass(TableSelection.selectedClassName); this.current.addClass(TableSelection.selectedClassName);
} }

View File

@ -35,7 +35,7 @@ export function selectHandler(event: MouseEvent | KeyboardEvent, selection: Tabl
} else { } else {
const row = target.closest('[data-header="row"]'); const row = target.closest('[data-header="row"]');
const col = target.closest('[data-header="col"]'); const col = target.closest('[data-header="col"]');
const resizer = target.closest('[data-resizer]'); const resizer = target.closest('[data-resize]');
if (row.$el && !resizer.$el) { if (row.$el && !resizer.$el) {
const cells = row.closest('[data-row]').findAll('[data-type="cell"]'); const cells = row.closest('[data-row]').findAll('[data-type="cell"]');
@ -44,7 +44,7 @@ export function selectHandler(event: MouseEvent | KeyboardEvent, selection: Tabl
selection.selectGroupies($cells); selection.selectGroupies($cells);
} else if (col.$el && !resizer.$el) { } else if (col.$el && !resizer.$el) {
const colNumber = col.data.col; const colNumber = col.data.col;
const colls = selection.rootTable.findAll(`[data-col="${colNumber}"]`); const colls = selection.rootTable.$root.findAll(`[data-col="${colNumber}"]`);
const $cells = Array.from(colls).filter(el => el !== col.$el).map(el => $(el as HTMLElement)); const $cells = Array.from(colls).filter(el => el !== col.$el).map(el => $(el as HTMLElement));
selection.selectGroupies($cells); selection.selectGroupies($cells);
@ -88,9 +88,9 @@ export function selectHandler(event: MouseEvent | KeyboardEvent, selection: Tabl
} }
case 'Enter': { case 'Enter': {
if (event.shiftKey) return; if (event.shiftKey) return;
event.preventDefault(); event.preventDefault();
row++; row++;
if (row === selection.rootTable.tableSize.row) selection.rootTable.addNewRowHandler();
break; break;
} }
case 'Tab': { case 'Tab': {

View File

@ -27,7 +27,7 @@ function createCol(columnContent = '', index = 1) {
`; `;
} }
function createRow(dataContent = '', infoContent = '', needResize = true, rowIndex = -1) { function createRow(dataContent = '', infoContent = '', needResize = true, rowIndex = -1): string {
return ` return `
<div class="row" data-type="resizable" data-row="${rowIndex}"> <div class="row" data-type="resizable" data-row="${rowIndex}">
<div class="row-info" data-header="row"> <div class="row-info" data-header="row">
@ -62,3 +62,12 @@ export function createTable(rowsCount = 10, columnCount = 10) {
return rows.join(''); return rows.join('');
} }
export function getNewRowHTML(rowIndex: number, colCount: number): string {
const cells = new Array(colCount)
.fill('')
.map((el, colIndex) => createCell('', colIndex, rowIndex - 1))
.join('');
return createRow(cells, rowIndex.toString(), true, rowIndex - 1);
}

View File

@ -43,8 +43,14 @@ export class Toolbar extends ExcelComponentState {
} }
onClick(event: MouseEvent) { onClick(event: MouseEvent) {
//TODO refactor, make style handler
const target = $(event.target as HTMLElement); const target = $(event.target as HTMLElement);
if (target.closest('[data-addbtn]')) {
this.$emitEventToObserver('toolbar:add-row');
return;
}
let stringValue; let stringValue;
let value; let value;
let key; let key;
@ -80,9 +86,11 @@ export class Toolbar extends ExcelComponentState {
} }
} }
if (value && key) {
this.$emitEventToObserver('toolbar:applyStyle', value); this.$emitEventToObserver('toolbar:applyStyle', value);
this.setComponentState({ [key]: value[key] }); this.setComponentState({ [key]: value[key] });
} }
}
onChange(e: any) { onChange(e: any) {
const target = $(e.target); const target = $(e.target);

View File

@ -88,6 +88,7 @@ export function createToolbar(state: ToolbarStateType): string {
buttons.push(increaseDecreaseFontSize); buttons.push(increaseDecreaseFontSize);
buttons.push(selectGroup); buttons.push(selectGroup);
buttons.push(createAddRowTable());
return buttons.join(''); return buttons.join('');
} }
@ -168,3 +169,11 @@ function createButtonFromConfig(btnConfig: ButtonConfigType) {
</div> </div>
`; `;
} }
function createAddRowTable() {
return `
<div class="button" data-addbtn>
<i class="material-icons" data-addbtn>add_circle</i>
</div>
`;
}

View File

@ -38,8 +38,8 @@ export abstract class ExcelComponent extends DomListener {
return ''; return '';
} }
$emitEventToObserver(event: string, args?: any): void { $emitEventToObserver(event: string, args?: any): boolean {
this.observer?.emit(event, args); return this.observer?.emit(event, args);
} }
$onEventFromObserver(event: string, callback: (args: any) => any) { $onEventFromObserver(event: string, callback: (args: any) => any) {

View File

@ -8,7 +8,7 @@
top: $header-height + $toolbar-height + $formula-height; top: $header-height + $toolbar-height + $formula-height;
overflow: auto; overflow: auto;
font-size: $default-cell-font-size; font-size: $default-cell-font-size;
cursor: url('https://upload.wikimedia.org/wikipedia/commons/thumb/5/5e/Small_arrow_pointing_left.svg/1200px-Small_arrow_pointing_left.svg.png') !important;
.row{ .row{
display: flex; display: flex;
flex-direction: row; flex-direction: row;