From c74c0490c1838f184053b4007aac69e8cd28ac1c Mon Sep 17 00:00:00 2001 From: Sergey Krylov Date: Tue, 28 Jun 2022 10:20:05 +0500 Subject: [PATCH] add observer to formula and table --- src/components/excel/Excel.ts | 14 ++++++- src/components/formula/Formula.ts | 37 +++++++++++++++---- src/components/header/Header.ts | 8 ++++ src/components/table/Table.ts | 29 +++++++++++++-- .../table/handlers/table.select.handler.ts | 4 +- src/components/toolbar/Toolbar.ts | 5 ++- src/core/Emitter.ts | 28 ++++++++++++++ src/core/ExcelComponent.ts | 17 ++++++++- src/core/dom.ts | 8 ++++ 9 files changed, 134 insertions(+), 16 deletions(-) create mode 100644 src/core/Emitter.ts diff --git a/src/components/excel/Excel.ts b/src/components/excel/Excel.ts index e7761f8..8772e79 100644 --- a/src/components/excel/Excel.ts +++ b/src/components/excel/Excel.ts @@ -1,4 +1,6 @@ import { $, Dom } from '../../core/dom'; +import { Emitter } from '../../core/Emitter'; +import { ExcelComponent } from '../../core/ExcelComponent'; interface ExcelOptionsType { components: any[] @@ -7,10 +9,12 @@ interface ExcelOptionsType { export class Excel { $el: HTMLElement | Dom; components: any[]; + emitter: Emitter; constructor(selector: string, options: ExcelOptionsType) { this.$el = $(selector); this.components = options.components; + this.emitter = new Emitter(); console.log(`Created new Excel class in ${selector} with options: ${options}`); } @@ -18,9 +22,13 @@ export class Excel { getRoot() { const $root = $.create('div', 'excel'); + const componentOptions = { + emitter: this.emitter, + }; + this.components = this.components.map(Component => { const $el = $.create('div', Component.className); - const component: any = new Component($el); + const component: ExcelComponent = new Component($el, componentOptions); $el.html(component.toHTML()); $root.append($el.$el); @@ -35,4 +43,8 @@ export class Excel { this.$el.append(this.getRoot()); this.components.forEach(component => component.init()); } + + destroy() { + this.components.forEach(component => component.destroy()); + } } diff --git a/src/components/formula/Formula.ts b/src/components/formula/Formula.ts index eb224a7..b542119 100644 --- a/src/components/formula/Formula.ts +++ b/src/components/formula/Formula.ts @@ -1,28 +1,51 @@ -import { DomClass } from '../../core/dom'; +import { Dom, DomClass } from '../../core/dom'; import { ExcelComponent } from '../../core/ExcelComponent'; export class Formula extends ExcelComponent { static className = 'excel__formula'; - constructor($root: DomClass) { + private formulaInput: Dom; + + constructor($root: DomClass, options: any) { super($root, { - listeners: ['input', 'click'], + listeners: ['input', 'keydown'], name: 'Formula', + ...options, }); } toHTML(): string { return `
fx
-
+
`; } - onInput(event: Event) { - console.log('Formula on input listeners', event); + init() { + super.init(); + + this.formulaInput = this.$root.find('#formula-input'); + + this.$on('table:input', text => { + this.formulaInput.text = text; + }); + this.$on('table:select-cell', text => { + this.formulaInput.text = text; + }); } - onClick() { + onInput(event: Event) { + const text = (event.target as HTMLElement).textContent.trim(); + this.$emit('formula:input', text); + } + onKeydown(event: KeyboardEvent) { + const preventedKeys = ['Enter', 'Tab']; + + if (preventedKeys.includes(event.key)) event.preventDefault(); + + if (event.key === 'Enter') { + this.$emit('formula:enter-press'); + } } } diff --git a/src/components/header/Header.ts b/src/components/header/Header.ts index e420c30..8632005 100644 --- a/src/components/header/Header.ts +++ b/src/components/header/Header.ts @@ -1,8 +1,16 @@ +import { DomClass } from '../../core/dom'; import { ExcelComponent } from '../../core/ExcelComponent'; export class Header extends ExcelComponent { static className = 'excel__header'; + constructor($root: DomClass, options: any) { + super($root, { + name: 'Header', + ...options, + }); + } + toHTML(): string { return ` diff --git a/src/components/table/Table.ts b/src/components/table/Table.ts index 6e68463..9a434c0 100644 --- a/src/components/table/Table.ts +++ b/src/components/table/Table.ts @@ -1,3 +1,4 @@ +import { DomClass } from '../../core/dom'; import { ExcelComponent } from '../../core/ExcelComponent'; import { selectHandler } from './handlers/table.select.handler'; import { TableSelection } from './TableSelection'; @@ -9,10 +10,11 @@ export class Table extends ExcelComponent { private selection: TableSelection; - constructor($root: any) { + constructor($root: DomClass, options: any) { super($root, { name: 'Table', - listeners: ['mousedown', 'keydown'], + listeners: ['mousedown', 'keydown', 'input'], + ...options, }); } @@ -29,14 +31,33 @@ export class Table extends ExcelComponent { const $cell = this.$root.find('[data-id="0:0"]'); this.selection.select($cell); + + this.$emit('table:select-cell', $cell.text); + + this.$on('formula:input', (data) => { + this.selection.current.text = data; + }); + + this.$on('formula:enter-press', () => { + this.selection.current.focus(); + }); + } + + emitSelectCallback() { + this.$emit('table:select-cell', this.selection.current.text); } onMousedown(event: MouseEvent) { - selectHandler(event, this.selection); + selectHandler(event, this.selection, this.emitSelectCallback.bind(this)); resizeHandler(this.$root, event); } onKeydown(event: KeyboardEvent) { - selectHandler(event, this.selection); + selectHandler(event, this.selection, this.emitSelectCallback.bind(this)); + } + + onInput(event: InputEvent) { + const inputText = (event.target as HTMLElement).textContent.trim(); + this.$emit('table:input', inputText); } } diff --git a/src/components/table/handlers/table.select.handler.ts b/src/components/table/handlers/table.select.handler.ts index 6bdeb71..c54d99b 100644 --- a/src/components/table/handlers/table.select.handler.ts +++ b/src/components/table/handlers/table.select.handler.ts @@ -2,7 +2,7 @@ import { $ } from '../../../core/dom'; import { getParamsFromCellId, isCell } from '../table.functions'; import { TableSelection } from '../TableSelection'; -export function selectHandler(event: MouseEvent | KeyboardEvent, selection: TableSelection) { +export function selectHandler(event: MouseEvent | KeyboardEvent, selection: TableSelection, callback?: () => void) { switch (event.type) { case 'mousedown': { onMouseDownHandler(); @@ -15,6 +15,8 @@ export function selectHandler(event: MouseEvent | KeyboardEvent, selection: Tabl default: break; } + callback(); + function onMouseDownHandler() { if (isCell(event)) { if (event.shiftKey) selection.selectGroup($(event.target as HTMLElement)); diff --git a/src/components/toolbar/Toolbar.ts b/src/components/toolbar/Toolbar.ts index a60f7c4..f4350d2 100644 --- a/src/components/toolbar/Toolbar.ts +++ b/src/components/toolbar/Toolbar.ts @@ -1,13 +1,14 @@ -import { Dom } from '../../core/dom'; +import { DomClass } from '../../core/dom'; import { ExcelComponent } from '../../core/ExcelComponent'; export class Toolbar extends ExcelComponent { static className = 'excel__toolbar'; - constructor($root: Dom) { + constructor($root: DomClass, options: any) { super($root, { name: 'Toolbar', listeners: ['click'], + ...options, }); } diff --git a/src/core/Emitter.ts b/src/core/Emitter.ts new file mode 100644 index 0000000..e38338f --- /dev/null +++ b/src/core/Emitter.ts @@ -0,0 +1,28 @@ +export class Emitter { + private listeners: { + [k: string]: Array<(args?: any) => any> + }; + + constructor() { + this.listeners = {}; + } + + emit(eventName: string, ...args: any[]) { + if (!Array.isArray(this.listeners[eventName])) return false; + + this.listeners[eventName].forEach(listener => { + listener(args); + }); + + return true; + } + + subscribe(eventName: string, callback: (args?: any) => any) { + this.listeners[eventName] = this.listeners[eventName] || []; + this.listeners[eventName].push(callback); + + return () => { + this.listeners[eventName] = this.listeners[eventName].filter(listener => listener !== callback); + }; + } +} diff --git a/src/core/ExcelComponent.ts b/src/core/ExcelComponent.ts index bf56c46..c47552e 100644 --- a/src/core/ExcelComponent.ts +++ b/src/core/ExcelComponent.ts @@ -1,4 +1,5 @@ import { DomListener } from './DomListener'; +import { Emitter } from './Emitter'; interface ExcelComponentClass { toHTML: () => string; @@ -8,15 +9,19 @@ interface ExcelComponentClass { type OptionsType = { listeners?: string[]; name: string; + emitter?: Emitter; }; export class ExcelComponent extends DomListener implements ExcelComponentClass { name: string; + emitter: Emitter; + private unsubscribers: ((args?: any) => any)[]; constructor($root: any, options: OptionsType) { super($root, options?.listeners); this.name = options?.name; - + this.emitter = options?.emitter; + this.unsubscribers = []; this.prepare(); } @@ -28,11 +33,21 @@ export class ExcelComponent extends DomListener implements ExcelComponentClass { return ''; } + $emit(event: string, ...args: any): void { + this.emitter.emit(event, ...args); + } + + $on(event: string, callback: (args: any) => any) { + const unsub = this.emitter.subscribe(event, callback); + this.unsubscribers.push(unsub); + } + init() { this.initDOMListeners(); } destroy() { this.removeDOMListeners(); + this.unsubscribers.forEach(unsub => unsub()); } } diff --git a/src/core/dom.ts b/src/core/dom.ts index 845e202..e046d1d 100644 --- a/src/core/dom.ts +++ b/src/core/dom.ts @@ -24,6 +24,14 @@ export class Dom implements DomClass { return this.$el.outerHTML.trim(); } + set text(text: string) { + this.$el.textContent = text; + } + + get text() { + return this.$el.textContent; + } + clear() { this.html('');