little refactor
This commit is contained in:
parent
f7e04814a5
commit
3bd194816e
@ -1,4 +1,4 @@
|
|||||||
import { $, Dom } from 'core/dom';
|
import { $, Dom } from 'core/Dom';
|
||||||
|
|
||||||
export function Loader(): Dom {
|
export function Loader(): Dom {
|
||||||
return $.create('div', 'loader')
|
return $.create('div', 'loader')
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { $ } from 'core/dom';
|
import { $ } from 'core/Dom';
|
||||||
import { Emitter } from 'core/Emitter';
|
import { Observer } from 'core/Observer';
|
||||||
import { ExcelComponent } from 'core/ExcelComponent';
|
import { ExcelComponent } from 'core/ExcelComponent';
|
||||||
import { Store } from 'core/store/Store';
|
import { Store } from 'core/store/Store';
|
||||||
import { StoreSubscriber } from 'core/StoreSubscriber';
|
import { StoreSubscriber } from 'core/StoreSubscriber';
|
||||||
@ -10,15 +10,20 @@ interface ExcelOptionsType {
|
|||||||
store: any,
|
store: any,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BaseComponentOption = {
|
||||||
|
observer: Observer;
|
||||||
|
store: Store;
|
||||||
|
};
|
||||||
|
|
||||||
export class Excel {
|
export class Excel {
|
||||||
components: any[];
|
components: any[];
|
||||||
emitter: Emitter;
|
observer: Observer;
|
||||||
store: Store;
|
store: Store;
|
||||||
subscriber: StoreSubscriber;
|
subscriber: StoreSubscriber;
|
||||||
|
|
||||||
constructor(options: ExcelOptionsType) {
|
constructor(options: ExcelOptionsType) {
|
||||||
this.components = options.components;
|
this.components = options.components;
|
||||||
this.emitter = new Emitter();
|
this.observer = new Observer();
|
||||||
this.store = options.store;
|
this.store = options.store;
|
||||||
this.subscriber = new StoreSubscriber(this.store);
|
this.subscriber = new StoreSubscriber(this.store);
|
||||||
}
|
}
|
||||||
@ -26,8 +31,8 @@ export class Excel {
|
|||||||
getRoot() {
|
getRoot() {
|
||||||
const $root = $.create('div', 'excel');
|
const $root = $.create('div', 'excel');
|
||||||
|
|
||||||
const componentOptions = {
|
const componentOptions: BaseComponentOption = {
|
||||||
emitter: this.emitter,
|
observer: this.observer,
|
||||||
store: this.store,
|
store: this.store,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Dom } from 'core/dom';
|
import { Dom } from 'core/Dom';
|
||||||
import { ExcelComponent, ComponentOptionsType } from 'core/ExcelComponent';
|
import { ExcelComponent, ComponentOptionsType } from 'core/ExcelComponent';
|
||||||
|
|
||||||
export class Formula extends ExcelComponent {
|
export class Formula extends ExcelComponent {
|
||||||
@ -8,13 +8,10 @@ export class Formula extends ExcelComponent {
|
|||||||
|
|
||||||
constructor($root: Dom, options: ComponentOptionsType) {
|
constructor($root: Dom, options: ComponentOptionsType) {
|
||||||
super($root, {
|
super($root, {
|
||||||
// @ts-ignore next-line
|
|
||||||
eventListeners: ['input', 'keydown'],
|
|
||||||
// @ts-ignore next-line
|
|
||||||
name: 'Formula',
|
|
||||||
// @ts-ignore next-line
|
|
||||||
subscribe: ['currentText'],
|
|
||||||
...options,
|
...options,
|
||||||
|
eventListeners: ['input', 'keydown'],
|
||||||
|
name: 'Formula',
|
||||||
|
subscribe: ['currentText'],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,7 +27,7 @@ export class Formula extends ExcelComponent {
|
|||||||
|
|
||||||
this.formulaInput = this.$root.find('#formula-input');
|
this.formulaInput = this.$root.find('#formula-input');
|
||||||
|
|
||||||
this.$on('table:select-cell', (cell: Dom) => {
|
this.$onEventFromObserver('table:select-cell', (cell: Dom) => {
|
||||||
this.formulaInput.text = cell.data.value || '';
|
this.formulaInput.text = cell.data.value || '';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -44,7 +41,8 @@ export class Formula extends ExcelComponent {
|
|||||||
if (!target) return;
|
if (!target) return;
|
||||||
|
|
||||||
const text = (target as HTMLElement).innerText.trim();
|
const text = (target as HTMLElement).innerText.trim();
|
||||||
this.$emit('formula:input', text);
|
|
||||||
|
this.$emitEventToObserver('formula:input', text);
|
||||||
}
|
}
|
||||||
|
|
||||||
onKeydown(event: KeyboardEvent) {
|
onKeydown(event: KeyboardEvent) {
|
||||||
@ -53,7 +51,7 @@ export class Formula extends ExcelComponent {
|
|||||||
if (preventedKeys.includes(event.key)) event.preventDefault();
|
if (preventedKeys.includes(event.key)) event.preventDefault();
|
||||||
|
|
||||||
if (event.key === 'Enter') {
|
if (event.key === 'Enter') {
|
||||||
this.$emit('formula:enter-press');
|
this.$emitEventToObserver('formula:enter-press');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,18 +1,18 @@
|
|||||||
|
import { ComponentOptionsType, ExcelComponent } from 'core/ExcelComponent';
|
||||||
import * as actions from 'redux/action-creators';
|
import * as actions from 'redux/action-creators';
|
||||||
import { $, Dom } from 'core/dom';
|
import { $, Dom } from 'core/Dom';
|
||||||
import { ActiveRoute } from 'core/routes/ActiveRoute';
|
import { ActiveRoute } from 'core/routes/ActiveRoute';
|
||||||
import { ExcelComponentState } from 'core/ExcelComponentState';
|
|
||||||
import { deleteTable } from 'redux/action-creators';
|
import { deleteTable } from 'redux/action-creators';
|
||||||
|
|
||||||
export class Header extends ExcelComponentState {
|
export class Header extends ExcelComponent {
|
||||||
static className = 'excel__header';
|
static className = 'excel__header';
|
||||||
|
|
||||||
constructor($root: Dom, options: any) {
|
constructor($root: Dom, options: ComponentOptionsType) {
|
||||||
super($root, {
|
super($root, {
|
||||||
|
...options,
|
||||||
name: 'Header',
|
name: 'Header',
|
||||||
eventListeners: ['input', 'click'],
|
eventListeners: ['input', 'click'],
|
||||||
subscribe: ['title'],
|
subscribe: ['title'],
|
||||||
...options,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { startCellId } from 'components/table/table.functions';
|
import { startCellId } from 'components/table/table.functions';
|
||||||
import * as actions from 'redux/action-creators';
|
import * as actions from 'redux/action-creators';
|
||||||
import { $, Dom } from 'core/dom';
|
import { $, Dom } from 'core/Dom';
|
||||||
import { ExcelComponent } from 'core/ExcelComponent';
|
import { ComponentOptionsType, ExcelComponent } from 'core/ExcelComponent';
|
||||||
import { TableSelection } from 'components/table/TableSelection';
|
import { TableSelection } from 'components/table/TableSelection';
|
||||||
import { changeCurrentStyles } from 'redux/action-creators';
|
import { changeCurrentStyles } from 'redux/action-creators';
|
||||||
import { createTable } from 'components/table/table.template';
|
import { createTable } from 'components/table/table.template';
|
||||||
@ -15,11 +15,11 @@ export class Table extends ExcelComponent {
|
|||||||
|
|
||||||
private selection: TableSelection;
|
private selection: TableSelection;
|
||||||
|
|
||||||
constructor($root: Dom, options: any) {
|
constructor($root: Dom, options: ComponentOptionsType) {
|
||||||
super($root, {
|
super($root, {
|
||||||
|
...options,
|
||||||
name: 'Table',
|
name: 'Table',
|
||||||
eventListeners: ['mousedown', 'keydown', 'input'],
|
eventListeners: ['mousedown', 'keydown', 'input'],
|
||||||
...options,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,9 +36,9 @@ export class Table extends ExcelComponent {
|
|||||||
|
|
||||||
this.initTable();
|
this.initTable();
|
||||||
|
|
||||||
this.$on('formula:input', this.updateCurrentText);
|
this.$onEventFromObserver('formula:input', this.updateCurrentText);
|
||||||
this.$on('formula:enter-press', () => this.selection.current.focus());
|
this.$onEventFromObserver('formula:enter-press', () => this.selection.current.focus());
|
||||||
this.$on('toolbar:applyStyle', this.updateCurrentStyles);
|
this.$onEventFromObserver('toolbar:applyStyle', this.updateCurrentStyles);
|
||||||
}
|
}
|
||||||
|
|
||||||
initTable() {
|
initTable() {
|
||||||
@ -55,12 +55,12 @@ export class Table extends ExcelComponent {
|
|||||||
|
|
||||||
Object.keys(size.col).forEach(key => {
|
Object.keys(size.col).forEach(key => {
|
||||||
const cols = this.$root.findAll(`[data-col="${key}"]`);
|
const cols = this.$root.findAll(`[data-col="${key}"]`);
|
||||||
cols.forEach(el => $(el as HTMLElement).css({ width: `${size.col[key]}px` }));
|
cols.forEach(el => $(el as HTMLElement).css({ width: `${size.col[+key]}px` }));
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.keys(size.row).forEach(key => {
|
Object.keys(size.row).forEach(key => {
|
||||||
const rows = this.$root.findAll(`[data-row="${key}"]`);
|
const rows = this.$root.findAll(`[data-row="${key}"]`);
|
||||||
rows.forEach(el => $(el as HTMLElement).css({ height: `${size.row[key]}px` }));
|
rows.forEach(el => $(el as HTMLElement).css({ height: `${size.row[+key]}px` }));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,11 +83,11 @@ export class Table extends ExcelComponent {
|
|||||||
const $cell = this.$root.find(`[data-id="${startCellId}"]`);
|
const $cell = this.$root.find(`[data-id="${startCellId}"]`);
|
||||||
this.selection.select($cell);
|
this.selection.select($cell);
|
||||||
|
|
||||||
this.$emit('table:select-cell', $cell);
|
this.$emitEventToObserver('table:select-cell', $cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
emitSelectCallback() {
|
emitSelectCallback() {
|
||||||
this.$emit('table:select-cell', this.selection.current);
|
this.$emitEventToObserver('table:select-cell', this.selection.current);
|
||||||
|
|
||||||
const styles = this.selection.current?.getStyles(Object.keys(initialStyleState));
|
const styles = this.selection.current?.getStyles(Object.keys(initialStyleState));
|
||||||
this.dispatchToStore(changeCurrentStyles(styles));
|
this.dispatchToStore(changeCurrentStyles(styles));
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
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';
|
||||||
|
|
||||||
export class TableSelection {
|
export class TableSelection {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { startCellId } from 'components/table/table.functions';
|
import { startCellId } from 'components/table/table.functions';
|
||||||
import { $, Dom } from 'core/dom';
|
import { $, Dom } from 'core/Dom';
|
||||||
|
|
||||||
type CustomElementType = Element & { css: any };
|
type CustomElementType = Element & { css: any };
|
||||||
type ResizeReturnDataType = { value: number, id: string, type: string };
|
type ResizeReturnDataType = { value: number, id: string, type: string };
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { $ } from 'core/dom';
|
import { $ } from 'core/Dom';
|
||||||
import { TableSelection } from 'components/table/TableSelection';
|
import { TableSelection } from 'components/table/TableSelection';
|
||||||
import { getParamsFromCellId, isCell, startCellId } from 'components/table/table.functions';
|
import { getParamsFromCellId, isCell, startCellId } from 'components/table/table.functions';
|
||||||
|
|
||||||
@ -68,7 +68,10 @@ export function selectHandler(event: MouseEvent | KeyboardEvent, selection: Tabl
|
|||||||
}
|
}
|
||||||
case 'Tab': {
|
case 'Tab': {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
col++;
|
|
||||||
|
if (event.shiftKey) col--;
|
||||||
|
else col++;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: break;
|
default: break;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { $ } from 'core/dom';
|
import { $ } from 'core/Dom';
|
||||||
|
|
||||||
export function isCell(event: Event): boolean {
|
export function isCell(event: Event): boolean {
|
||||||
return $(event.target as HTMLElement).data.type === 'cell';
|
return $(event.target as HTMLElement).data.type === 'cell';
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { startCellId } from 'components/table/table.functions';
|
import { startCellId } from 'components/table/table.functions';
|
||||||
import { $, Dom } from 'core/dom';
|
import { $, Dom } from 'core/Dom';
|
||||||
import { ExcelComponentState } from 'core/ExcelComponentState';
|
import { ExcelComponentState } from 'core/ExcelComponentState';
|
||||||
import { ComponentOptionsType } from 'core/ExcelComponent';
|
import { ComponentOptionsType } from 'core/ExcelComponent';
|
||||||
import { createToolbar } from 'components/toolbar/toolbar.template';
|
import { createToolbar } from 'components/toolbar/toolbar.template';
|
||||||
@ -50,14 +50,14 @@ export class Toolbar extends ExcelComponentState {
|
|||||||
const value = JSON.parse(stringValue);
|
const value = JSON.parse(stringValue);
|
||||||
const key = Object.keys(value)[0];
|
const key = Object.keys(value)[0];
|
||||||
|
|
||||||
this.$emit('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 value = `${e.target.value.toString()}px`;
|
const value = `${e.target.value.toString()}px`;
|
||||||
|
|
||||||
this.$emit('toolbar:applyStyle', { fontSize: value });
|
this.$emitEventToObserver('toolbar:applyStyle', { fontSize: value });
|
||||||
this.setComponentState({ fontSize: value });
|
this.setComponentState({ fontSize: value });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,5 +4,5 @@ export type ToolbarStateType = {
|
|||||||
textDecoration?: 'none' | 'underline';
|
textDecoration?: 'none' | 'underline';
|
||||||
justifyContent?: 'start' | 'center' | 'end';
|
justifyContent?: 'start' | 'center' | 'end';
|
||||||
alignItems?: 'start' | 'center' | 'end';
|
alignItems?: 'start' | 'center' | 'end';
|
||||||
fontSize?: any;
|
fontSize?: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -85,7 +85,7 @@ export function createToolbar(state: ToolbarStateType): string {
|
|||||||
return buttons.join(' ');
|
return buttons.join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
function createFontSizeButton(currentSize: string) {
|
function createFontSizeButton(currentSize = '12px') {
|
||||||
const fontSizeInPixels = +currentSize.slice(0, -2);
|
const fontSizeInPixels = +currentSize.slice(0, -2);
|
||||||
const options = [];
|
const options = [];
|
||||||
|
|
||||||
@ -95,9 +95,11 @@ function createFontSizeButton(currentSize: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return `
|
return `
|
||||||
|
<div class="button">
|
||||||
<select class="button__size" id="button-size">
|
<select class="button__size" id="button-size">
|
||||||
${options.join('')}
|
${options.join('')}
|
||||||
</select>
|
</select>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -31,7 +31,7 @@ export class LocalStorageClient implements ClientDataType {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
norm(state) {
|
norm(state: StateType) {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
currentStyles: { ...state.stylesState?.[startCellId] },
|
currentStyles: { ...state.stylesState?.[startCellId] },
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Dom } from 'core/dom';
|
import { Dom } from 'core/Dom';
|
||||||
import { capitalize } from 'core/utils';
|
import { capitalize } from 'core/utils';
|
||||||
|
|
||||||
export class DomListener {
|
export class DomListener {
|
||||||
|
|||||||
@ -1,34 +1,28 @@
|
|||||||
import { ActionType } from 'redux/types';
|
import { ActionType, StateType } from 'redux/types';
|
||||||
import { Dom } from 'core/dom';
|
import { Dom } from 'core/Dom';
|
||||||
import { DomListener } from 'core/DomListener';
|
import { DomListener } from 'core/DomListener';
|
||||||
import { Emitter } from 'core/Emitter';
|
import { Observer } from 'core/Observer';
|
||||||
import { Store } from 'core/store/Store';
|
import { Store } from 'core/store/Store';
|
||||||
|
|
||||||
interface ExcelComponentClass {
|
|
||||||
toHTML: () => string;
|
|
||||||
prepare: () => void;
|
|
||||||
storeChanged?: (args: any) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ComponentOptionsType = {
|
export type ComponentOptionsType = {
|
||||||
eventListeners: string[];
|
eventListeners: string[];
|
||||||
name: string;
|
name: string;
|
||||||
emitter: Emitter;
|
observer: Observer;
|
||||||
store: Store;
|
store: Store;
|
||||||
subscribe: string[],
|
subscribe: (keyof StateType)[],
|
||||||
};
|
};
|
||||||
|
|
||||||
export abstract class ExcelComponent extends DomListener implements ExcelComponentClass {
|
export abstract class ExcelComponent extends DomListener {
|
||||||
private name: string;
|
private name: string;
|
||||||
private emitter: Emitter;
|
private observer: Observer;
|
||||||
public store: Store;
|
public store: Store;
|
||||||
private subscribe: string[];
|
private subscribe: (keyof StateType)[];
|
||||||
private unsubscribers: ((args?: any) => any)[];
|
private unsubscribers: ((args?: any) => any)[];
|
||||||
|
|
||||||
constructor($root: Dom, options: ComponentOptionsType) {
|
constructor($root: Dom, options: ComponentOptionsType) {
|
||||||
super($root, options.eventListeners);
|
super($root, options.eventListeners);
|
||||||
this.name = options.name;
|
this.name = options.name;
|
||||||
this.emitter = options.emitter;
|
this.observer = options.observer;
|
||||||
this.store = options.store;
|
this.store = options.store;
|
||||||
this.subscribe = options.subscribe;
|
this.subscribe = options.subscribe;
|
||||||
|
|
||||||
@ -44,12 +38,12 @@ export abstract class ExcelComponent extends DomListener implements ExcelCompone
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$emit(event: string, args?: any): void {
|
$emitEventToObserver(event: string, args?: any): void {
|
||||||
this.emitter?.emit(event, args);
|
this.observer?.emit(event, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
$on(event: string, callback: (args: any) => any) {
|
$onEventFromObserver(event: string, callback: (args: any) => any) {
|
||||||
const unsub = this.emitter?.subscribe(event, callback);
|
const unsub = this.observer?.subscribe(event, callback);
|
||||||
unsub && this.unsubscribers.push(unsub);
|
unsub && this.unsubscribers.push(unsub);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,10 +52,10 @@ export abstract class ExcelComponent extends DomListener implements ExcelCompone
|
|||||||
}
|
}
|
||||||
|
|
||||||
storeChanged(args?: any) {
|
storeChanged(args?: any) {
|
||||||
console.log('CHANGE STORE: ', args);
|
console.log('CHANGE STORE: ', args, ' in component ', this.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
isWatching(key: string) {
|
isWatching(key: keyof StateType) {
|
||||||
return this.subscribe?.includes(key);
|
return this.subscribe?.includes(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Dom } from 'core/dom';
|
import { Dom } from 'core/Dom';
|
||||||
import { ExcelComponent, ComponentOptionsType } from 'core/ExcelComponent';
|
import { ExcelComponent, ComponentOptionsType } from 'core/ExcelComponent';
|
||||||
|
|
||||||
type ExcelComponentStateType = {
|
type ExcelComponentStateType = {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
export class Emitter {
|
export class Observer {
|
||||||
private listeners: {
|
private listeners: {
|
||||||
[k: string]: Array<(args?: any) => any>
|
[k: string]: Array<(args?: any) => any>
|
||||||
};
|
};
|
||||||
@ -7,16 +7,6 @@ export class Emitter {
|
|||||||
this.listeners = {};
|
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) {
|
subscribe(eventName: string, callback: (args?: any) => any) {
|
||||||
this.listeners[eventName] = this.listeners[eventName] || [];
|
this.listeners[eventName] = this.listeners[eventName] || [];
|
||||||
this.listeners[eventName].push(callback);
|
this.listeners[eventName].push(callback);
|
||||||
@ -25,4 +15,12 @@ export class Emitter {
|
|||||||
this.listeners[eventName] = this.listeners[eventName].filter(listener => listener !== callback);
|
this.listeners[eventName] = this.listeners[eventName].filter(listener => listener !== callback);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emit(eventName: string, args: any[]) {
|
||||||
|
if (!Array.isArray(this.listeners[eventName])) return false;
|
||||||
|
|
||||||
|
this.listeners[eventName].forEach(listener => listener(args));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -5,9 +5,9 @@ import { StateType } from 'redux/types';
|
|||||||
export class StateProcessor {
|
export class StateProcessor {
|
||||||
private client: ClientDataType;
|
private client: ClientDataType;
|
||||||
|
|
||||||
constructor(client: ClientDataType, dalay = 300) {
|
constructor(client: ClientDataType, delay = 300) {
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.listen = debounce(this.listen.bind(this), dalay);
|
this.listen = debounce(this.listen.bind(this), delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
listen(state: StateType) {
|
listen(state: StateType) {
|
||||||
|
|||||||
@ -4,30 +4,29 @@ import { isEqual } from 'core/utils';
|
|||||||
|
|
||||||
export class StoreSubscriber {
|
export class StoreSubscriber {
|
||||||
sub: any;
|
sub: any;
|
||||||
prevState: StateType;
|
currentState: StateType;
|
||||||
|
|
||||||
constructor(private store: Store) {
|
constructor(private store: Store) {
|
||||||
this.sub = null;
|
this.sub = null;
|
||||||
this.prevState = {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
subscribeComponents(components: any[]) {
|
subscribeComponents(components: any[]) {
|
||||||
this.prevState = this.store.getState();
|
this.currentState = this.store.getState();
|
||||||
|
|
||||||
this.sub = this.store.subscribeFromStore((state: StateType) => {
|
this.sub = this.store.subscribeToStore((newState: StateType) => {
|
||||||
if (!state) return;
|
if (!newState) return;
|
||||||
|
|
||||||
Object.keys(state).forEach(key => {
|
Object.keys(newState).forEach((key) => {
|
||||||
if (!isEqual(this.prevState[key], state[key])) {
|
if (!isEqual(this.currentState[key as keyof StateType], newState[key as keyof StateType])) {
|
||||||
components.forEach(component => {
|
components.forEach(component => {
|
||||||
if (component.isWatching(key)) {
|
if (component.isWatching(key)) {
|
||||||
const changes = { [key]: state[key] };
|
component.storeChanged({ [key]: newState[key as keyof StateType] });
|
||||||
component.storeChanged(changes);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.prevState = this.store.getState();
|
|
||||||
|
this.currentState = this.store.getState();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { $, Dom, SelectorType } from 'core/dom';
|
import { $, Dom, SelectorType } from 'core/Dom';
|
||||||
import { ActiveRoute } from 'core/routes/ActiveRoute';
|
import { ActiveRoute } from 'core/routes/ActiveRoute';
|
||||||
import { DashboardPage } from 'pages/DashboardPage';
|
import { DashboardPage } from 'pages/DashboardPage';
|
||||||
import { ExcelPage } from 'pages/ExcelPage';
|
import { ExcelPage } from 'pages/ExcelPage';
|
||||||
@ -34,8 +34,8 @@ export class Router {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async changePageHandler() {
|
async changePageHandler() {
|
||||||
this.$placeholder.clear().append(this.loader);
|
|
||||||
this.page?.destroy();
|
this.page?.destroy();
|
||||||
|
this.$placeholder.clear().append(this.loader);
|
||||||
|
|
||||||
let Page;
|
let Page;
|
||||||
|
|
||||||
@ -9,8 +9,9 @@ export class Store {
|
|||||||
this.listeners = [];
|
this.listeners = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
subscribeFromStore(fn: (state: StateType) => void): SubscribeType {
|
subscribeToStore(fn: (state: StateType) => void): SubscribeType {
|
||||||
this.listeners.push(fn);
|
this.listeners.push(fn);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
unsubscribe: () => {
|
unsubscribe: () => {
|
||||||
this.listeners = this.listeners.filter((l: any) => l !== fn);
|
this.listeners = this.listeners.filter((l: any) => l !== fn);
|
||||||
@ -23,7 +24,7 @@ export class Store {
|
|||||||
this.listeners.forEach(listener => listener(this.state));
|
this.listeners.forEach(listener => listener(this.state));
|
||||||
}
|
}
|
||||||
|
|
||||||
getState() {
|
getState(): StateType {
|
||||||
return JSON.parse(JSON.stringify(this.state));
|
return JSON.parse(JSON.stringify(this.state));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import './styles/index.scss';
|
import './styles/index.scss';
|
||||||
import { Router } from 'core/routes/router';
|
import { Router } from 'core/routes/Router';
|
||||||
import { DashboardPage } from 'pages/DashboardPage';
|
import { DashboardPage } from 'pages/DashboardPage';
|
||||||
import { ExcelPage } from 'pages/ExcelPage';
|
import { ExcelPage } from 'pages/ExcelPage';
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
export abstract class AbstractPage {
|
export abstract class AbstractPage {
|
||||||
params: any;
|
params: string[];
|
||||||
|
|
||||||
constructor(params: any) {
|
constructor(params: string[]) {
|
||||||
this.params = params || Date.now().toString();
|
this.params = params || Date.now().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { $ } from 'core/dom';
|
import { $ } from 'core/Dom';
|
||||||
import { AbstractPage } from 'pages/AbstractPage';
|
import { AbstractPage } from 'pages/AbstractPage';
|
||||||
import { storage } from 'core/utils';
|
import { storage } from 'core/utils';
|
||||||
|
|
||||||
|
|||||||
@ -32,7 +32,7 @@ export class ExcelPage extends AbstractPage {
|
|||||||
const state = await this.processor.get();
|
const state = await this.processor.get();
|
||||||
const store = new Store(rootReducer, state);
|
const store = new Store(rootReducer, state);
|
||||||
|
|
||||||
this.storeSub = store.subscribeFromStore(this.processor.listen);
|
this.storeSub = store.subscribeToStore(this.processor.listen);
|
||||||
|
|
||||||
this.excel = new Excel({
|
this.excel = new Excel({
|
||||||
components: [Header, Toolbar, Formula, Table],
|
components: [Header, Toolbar, Formula, Table],
|
||||||
|
|||||||
@ -17,13 +17,13 @@ export function rootReducer(state: StateType, action: ActionType) {
|
|||||||
const newState: StateType = { ...state };
|
const newState: StateType = { ...state };
|
||||||
const fieldName = `${action.resizeData?.type}State`;
|
const fieldName = `${action.resizeData?.type}State`;
|
||||||
|
|
||||||
newState[fieldName][action.resizeData?.id] = action.resizeData?.value;
|
newState[fieldName as 'colState' | 'rowState'][action.resizeData?.id] = action.resizeData?.value;
|
||||||
|
|
||||||
return { ...state, ...newState };
|
return { ...state, ...newState };
|
||||||
}
|
}
|
||||||
|
|
||||||
case CHANGE_TEXT: {
|
case CHANGE_TEXT: {
|
||||||
const newState: StateType = state.dataState || {};
|
const newState: { [k: string]: string } = state.dataState || {};
|
||||||
const fieldName = action.data.id;
|
const fieldName = action.data.id;
|
||||||
|
|
||||||
newState[fieldName] = action.data.text;
|
newState[fieldName] = action.data.text;
|
||||||
|
|||||||
12
src/redux/types.d.ts
vendored
12
src/redux/types.d.ts
vendored
@ -1,10 +1,20 @@
|
|||||||
|
import { ToolbarStateType } from 'components/toolbar/toolbar-types';
|
||||||
|
|
||||||
export type ActionType = {
|
export type ActionType = {
|
||||||
type: string
|
type: string
|
||||||
[k: string]: any
|
[k: string]: any
|
||||||
};
|
};
|
||||||
|
|
||||||
export type StateType = {
|
export type StateType = {
|
||||||
[k: string]: any
|
colState: { [k: number]: number };
|
||||||
|
rowState: { [k: number]: number };
|
||||||
|
currentStyles: ToolbarStateType;
|
||||||
|
dataState: { [k: string]: string };
|
||||||
|
id: string;
|
||||||
|
openDate: string;
|
||||||
|
stylesState: { [k: string]: ToolbarStateType };
|
||||||
|
title: string;
|
||||||
|
currentText: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ReducerType = (state: StateType, action: ActionType) => StateType;
|
export type ReducerType = (state: StateType, action: ActionType) => StateType;
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
* @jest-environment jsdom
|
* @jest-environment jsdom
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Router } from '../src/core/routes/router';
|
import { Router } from '../src/core/routes/Router';
|
||||||
import { AbstractPage } from '../src/pages/AbstractPage';
|
import { AbstractPage } from '../src/pages/AbstractPage';
|
||||||
|
|
||||||
class DashboardPage extends AbstractPage {
|
class DashboardPage extends AbstractPage {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Store } from '../src/core/store/createStore';
|
import { Store } from '../src/core/store/Store';
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
count: 0,
|
count: 0,
|
||||||
@ -24,7 +24,7 @@ describe('Create store', () => {
|
|||||||
test('should return store object', () => {
|
test('should return store object', () => {
|
||||||
expect(store).toBeDefined();
|
expect(store).toBeDefined();
|
||||||
expect(store.dispatchToStore).toBeDefined();
|
expect(store.dispatchToStore).toBeDefined();
|
||||||
expect(store.subscribeFromStore).toBeDefined();
|
expect(store.subscribeToStore).toBeDefined();
|
||||||
expect(store.getState).not.toBeUndefined();
|
expect(store.getState).not.toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ describe('Create store', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should call subscriber', () => {
|
test('should call subscriber', () => {
|
||||||
store.subscribeFromStore(handler);
|
store.subscribeToStore(handler);
|
||||||
store.dispatchToStore({ type: 'ADD' });
|
store.dispatchToStore({ type: 'ADD' });
|
||||||
|
|
||||||
expect(handler).toHaveBeenCalled();
|
expect(handler).toHaveBeenCalled();
|
||||||
@ -55,7 +55,7 @@ describe('Create store', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should NOT call sub if unsubscribe', () => {
|
test('should NOT call sub if unsubscribe', () => {
|
||||||
const unsub = store.subscribeFromStore(handler);
|
const unsub = store.subscribeToStore(handler);
|
||||||
unsub.unsubscribe();
|
unsub.unsubscribe();
|
||||||
store.dispatchToStore({ type: 'ADD' });
|
store.dispatchToStore({ type: 'ADD' });
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user