add observer to formula and table

This commit is contained in:
Sergey Krylov 2022-06-28 10:20:05 +05:00
parent 3870d7dfd0
commit c74c0490c1
9 changed files with 134 additions and 16 deletions

View File

@ -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());
}
}

View File

@ -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 `
<div class="info">fx</div>
<div class="input" contenteditable spellcheck="false"></div>
<div id="formula-input" class="input" contenteditable spellcheck="false"></div>
`;
}
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');
}
}
}

View File

@ -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 `
<input type="text" class="input" value="Новая таблица">

View File

@ -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);
}
}

View File

@ -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));

View File

@ -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,
});
}

28
src/core/Emitter.ts Normal file
View File

@ -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);
};
}
}

View File

@ -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());
}
}

View File

@ -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('');