add some type
This commit is contained in:
parent
d674203a4d
commit
d1b83a26a3
@ -5,9 +5,10 @@ import { Store } from 'core/store/Store';
|
|||||||
import { StoreSubscriber } from 'core/StoreSubscriber';
|
import { StoreSubscriber } from 'core/StoreSubscriber';
|
||||||
import { updateOpenDate } from 'redux/action-creators';
|
import { updateOpenDate } from 'redux/action-creators';
|
||||||
|
|
||||||
|
// TODO type for components array
|
||||||
interface ExcelOptionsType {
|
interface ExcelOptionsType {
|
||||||
components: any[],
|
components: any[],
|
||||||
store: any,
|
store: Store,
|
||||||
}
|
}
|
||||||
|
|
||||||
type BaseComponentOption = {
|
type BaseComponentOption = {
|
||||||
|
|||||||
@ -17,10 +17,11 @@ export class Header extends ExcelComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toHTML(): string {
|
toHTML(): string {
|
||||||
const { title } = this.store.getState();
|
const { title, id } = this.store.getState();
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<input type="text" class="input" value="${title}">
|
<input type="text" class="input" value="${title}">
|
||||||
|
<div>ID: <strong>${id}</strong></div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class="button" data-button="delete-table">
|
<div class="button" data-button="delete-table">
|
||||||
@ -33,7 +34,7 @@ export class Header extends ExcelComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onInput(event: InputEvent) {
|
onInput(event: InputEvent) {
|
||||||
const $target = $(event.target as HTMLInputElement);
|
const $target = $(event.target);
|
||||||
|
|
||||||
this.dispatchToStore(actions.changeTitle($target.text));
|
this.dispatchToStore(actions.changeTitle($target.text));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,7 @@ export class Table extends ExcelComponent {
|
|||||||
|
|
||||||
private selection: TableSelection;
|
private selection: TableSelection;
|
||||||
private isMouseDowned: boolean;
|
private isMouseDowned: boolean;
|
||||||
|
private tableResizing = false;
|
||||||
public tableSize = {
|
public tableSize = {
|
||||||
row: 50,
|
row: 50,
|
||||||
col: 24,
|
col: 24,
|
||||||
@ -92,6 +93,8 @@ export class Table extends ExcelComponent {
|
|||||||
const $cell = this.$root.find(`[data-id="${cellId}"]`);
|
const $cell = this.$root.find(`[data-id="${cellId}"]`);
|
||||||
const styles = tableStyles[cellId];
|
const styles = tableStyles[cellId];
|
||||||
|
|
||||||
|
if (!$cell.isExist) return;
|
||||||
|
|
||||||
$cell.text = parse(tableContent[cellId]) || '';
|
$cell.text = parse(tableContent[cellId]) || '';
|
||||||
$cell.setData('value', tableContent[cellId]);
|
$cell.setData('value', tableContent[cellId]);
|
||||||
$cell.css(styles);
|
$cell.css(styles);
|
||||||
@ -116,8 +119,11 @@ export class Table extends ExcelComponent {
|
|||||||
|
|
||||||
async resizeTable(event: MouseEvent) {
|
async resizeTable(event: MouseEvent) {
|
||||||
try {
|
try {
|
||||||
|
if (!$(event.target).closest('[data-resize]').isExist) return;
|
||||||
|
this.tableResizing = true;
|
||||||
const resizeData = await resizeHandler(this.$root, event);
|
const resizeData = await resizeHandler(this.$root, event);
|
||||||
this.dispatchToStore(actions.tableResize({ resizeData }));
|
this.dispatchToStore(actions.tableResize(resizeData));
|
||||||
|
this.tableResizing = false;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('Resize error', e.message);
|
console.warn('Resize error', e.message);
|
||||||
}
|
}
|
||||||
@ -133,7 +139,7 @@ export class Table extends ExcelComponent {
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
updateCurrentStyles = (style: CSSStyleRule) => {
|
updateCurrentStyles = (style: CSSStyleDeclaration) => {
|
||||||
this.selection.applyStyle(style);
|
this.selection.applyStyle(style);
|
||||||
this.dispatchToStore(actions.applyStyle({
|
this.dispatchToStore(actions.applyStyle({
|
||||||
value: style,
|
value: style,
|
||||||
@ -163,7 +169,7 @@ export class Table extends ExcelComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMouseover(event: MouseEvent) {
|
onMouseover(event: MouseEvent) {
|
||||||
this.isMouseDowned && selectHandler(event, this.selection);
|
this.isMouseDowned && !this.tableResizing && selectHandler(event, this.selection);
|
||||||
}
|
}
|
||||||
|
|
||||||
onMouseup() {
|
onMouseup() {
|
||||||
|
|||||||
@ -19,13 +19,17 @@ export class TableSelection {
|
|||||||
|
|
||||||
// TODO make a focus manager
|
// TODO make a focus manager
|
||||||
focusToCell($cell: Dom) {
|
focusToCell($cell: Dom) {
|
||||||
const range = new Range();
|
try {
|
||||||
const node = $cell.$el;
|
const range = new Range();
|
||||||
|
const node = $cell.$el;
|
||||||
|
|
||||||
range.setStartAfter(node.childNodes[0]);
|
range.setStartAfter(node.childNodes[0]);
|
||||||
|
|
||||||
window.getSelection()?.removeAllRanges();
|
window.getSelection()?.removeAllRanges();
|
||||||
window.getSelection()?.addRange(range);
|
window.getSelection()?.addRange(range);
|
||||||
|
} catch (e) {
|
||||||
|
console.log('Error focus', e.message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
select($el: Dom) {
|
select($el: Dom) {
|
||||||
@ -83,7 +87,7 @@ export class TableSelection {
|
|||||||
$cell.addClass(TableSelection.selectedClassName);
|
$cell.addClass(TableSelection.selectedClassName);
|
||||||
}
|
}
|
||||||
|
|
||||||
applyStyle(style: CSSStyleRule) {
|
applyStyle(style: CSSStyleDeclaration) {
|
||||||
this.group.forEach(el => el.css(style));
|
this.group.forEach(el => el.css(style));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
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 };
|
export type ResizeReturnDataType = { value: number, id: string, type: string };
|
||||||
type ResizeReturnDataType = { value: number, id: string, type: string };
|
|
||||||
|
|
||||||
export function resizeHandler($root: Dom, event: MouseEvent) {
|
export function resizeHandler($root: Dom, event: MouseEvent) {
|
||||||
return new Promise<ResizeReturnDataType>(res => {
|
return new Promise<ResizeReturnDataType>(res => {
|
||||||
@ -18,13 +17,7 @@ export function resizeHandler($root: Dom, event: MouseEvent) {
|
|||||||
|
|
||||||
let delta: number;
|
let delta: number;
|
||||||
|
|
||||||
(Element.prototype as CustomElementType).css = function (styles: any) {
|
$resizer.css({ opacity: '1' });
|
||||||
Object.keys(styles).forEach((key: any) => {
|
|
||||||
this.style[key] = styles[key];
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$resizer.css({ opacity: 1 });
|
|
||||||
|
|
||||||
document.onmousemove = e => {
|
document.onmousemove = e => {
|
||||||
document.body.style.userSelect = 'none';
|
document.body.style.userSelect = 'none';
|
||||||
@ -84,7 +77,7 @@ export function resizeHandler($root: Dom, event: MouseEvent) {
|
|||||||
|
|
||||||
res({ value, id, type });
|
res({ value, id, type });
|
||||||
|
|
||||||
$resizer.css({ opacity: 0, bottom: 0, right: 0 });
|
$resizer.css({ opacity: '0', bottom: '0', right: '0' });
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export function selectHandler(event: MouseEvent | KeyboardEvent, selection: Tabl
|
|||||||
callback?.();
|
callback?.();
|
||||||
|
|
||||||
function onMouseDownHandler() {
|
function onMouseDownHandler() {
|
||||||
const target = $(event.target as HTMLElement);
|
const target = $(event.target);
|
||||||
|
|
||||||
if (isCell(event)) {
|
if (isCell(event)) {
|
||||||
if (event.shiftKey) selection.selectTo(target);
|
if (event.shiftKey) selection.selectTo(target);
|
||||||
@ -44,8 +44,8 @@ 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.$root.findAll(`[data-col="${colNumber}"]`);
|
const columns = 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(columns).filter(el => el !== col.$el).map(el => $(el as HTMLElement));
|
||||||
|
|
||||||
selection.selectGroupies($cells);
|
selection.selectGroupies($cells);
|
||||||
}
|
}
|
||||||
@ -109,6 +109,7 @@ export function selectHandler(event: MouseEvent | KeyboardEvent, selection: Tabl
|
|||||||
|
|
||||||
function onMouseOverHandler() {
|
function onMouseOverHandler() {
|
||||||
if (selection.current.$el) {
|
if (selection.current.$el) {
|
||||||
|
// TODO find event type
|
||||||
selection.selectTo($((event as any).toElement));
|
selection.selectTo($((event as any).toElement));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
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).data.type === 'cell';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getParamsFromCellId(cellId: string) {
|
export function getParamsFromCellId(cellId: string) {
|
||||||
|
|||||||
@ -3,6 +3,7 @@ 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';
|
||||||
|
import { StateType } from 'redux/types';
|
||||||
import { fontSizes, initialStyleState } from 'src/constants';
|
import { fontSizes, initialStyleState } from 'src/constants';
|
||||||
|
|
||||||
export class Toolbar extends ExcelComponentState {
|
export class Toolbar extends ExcelComponentState {
|
||||||
@ -38,15 +39,17 @@ export class Toolbar extends ExcelComponentState {
|
|||||||
return this.template;
|
return this.template;
|
||||||
}
|
}
|
||||||
|
|
||||||
storeChanged(args?: any) {
|
storeChanged(args: StateType) {
|
||||||
|
if (!args) return;
|
||||||
|
|
||||||
this.setComponentState(args.currentStyles);
|
this.setComponentState(args.currentStyles);
|
||||||
}
|
}
|
||||||
|
|
||||||
onClick(event: MouseEvent) {
|
onClick(event: MouseEvent) {
|
||||||
//TODO refactor, make style handler
|
// TODO refactor, make style handler
|
||||||
const target = $(event.target as HTMLElement);
|
const target = $(event.target);
|
||||||
|
|
||||||
if (target.closest('[data-addbtn]')) {
|
if (target.closest('[data-addbtn]').isExist) {
|
||||||
this.$emitEventToObserver('toolbar:add-row');
|
this.$emitEventToObserver('toolbar:add-row');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -92,6 +95,7 @@ export class Toolbar extends ExcelComponentState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO find EventType
|
||||||
onChange(e: any) {
|
onChange(e: any) {
|
||||||
const target = $(e.target);
|
const target = $(e.target);
|
||||||
let value = '';
|
let value = '';
|
||||||
|
|||||||
@ -36,7 +36,8 @@ export const initialState: StateType = {
|
|||||||
title: 'New excel table',
|
title: 'New excel table',
|
||||||
openDate: Date.now(),
|
openDate: Date.now(),
|
||||||
currentStyles: initialStyleState,
|
currentStyles: initialStyleState,
|
||||||
currentText: 'huy',
|
currentText: 'initial text',
|
||||||
|
id: '0',
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getNormalizeInitialState(params: string): StateType {
|
export function getNormalizeInitialState(params: string): StateType {
|
||||||
|
|||||||
@ -4,12 +4,12 @@ import { StateType } from 'redux/types';
|
|||||||
import { getNormalizeInitialState } from 'src/constants';
|
import { getNormalizeInitialState } from 'src/constants';
|
||||||
|
|
||||||
export interface ClientDataType {
|
export interface ClientDataType {
|
||||||
save: (state: StateType) => Promise<any>;
|
save: (state: StateType) => Promise<void>;
|
||||||
get: () => Promise<any>;
|
get: () => Promise<StateType>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LocalStorageClient implements ClientDataType {
|
export class LocalStorageClient implements ClientDataType {
|
||||||
private name: string;
|
private readonly name: string;
|
||||||
|
|
||||||
constructor(name: string) {
|
constructor(name: string) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
@ -20,7 +20,7 @@ export class LocalStorageClient implements ClientDataType {
|
|||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
get() {
|
get(): Promise<StateType> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
resolve(getNormalizeInitialState(this.name));
|
resolve(getNormalizeInitialState(this.name));
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { ToolbarStateType } from 'components/toolbar/toolbar-types';
|
import { CallbackType } from 'redux/types';
|
||||||
import { initialStyleState } from 'src/constants';
|
import { initialStyleState } from 'src/constants';
|
||||||
|
|
||||||
export type SelectorType = string | HTMLElement;
|
export type SelectorType = string | HTMLElement | EventTarget | null;
|
||||||
|
|
||||||
export interface DomClass {
|
export interface DomClass {
|
||||||
html(html?: string): string | DomClass;
|
html(html?: string): string | DomClass;
|
||||||
@ -19,7 +19,7 @@ export class Dom implements DomClass {
|
|||||||
if (!elementFromDOM) throw new Error(`Can't find element with "${selector}" selector`);
|
if (!elementFromDOM) throw new Error(`Can't find element with "${selector}" selector`);
|
||||||
else this.$el = elementFromDOM as HTMLElement;
|
else this.$el = elementFromDOM as HTMLElement;
|
||||||
} else {
|
} else {
|
||||||
this.$el = selector;
|
this.$el = selector as HTMLElement;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,6 +30,8 @@ export class Dom implements DomClass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
set text(text: string) {
|
set text(text: string) {
|
||||||
|
if (!this.$el) return;
|
||||||
|
|
||||||
if (!text) this.$el.textContent = '';
|
if (!text) this.$el.textContent = '';
|
||||||
this.$el.textContent = text;
|
this.$el.textContent = text;
|
||||||
}
|
}
|
||||||
@ -46,22 +48,22 @@ export class Dom implements DomClass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: any
|
// FIXME: any
|
||||||
append(node: any) {
|
append(node: HTMLElement | Dom) {
|
||||||
let child = node;
|
let child = node;
|
||||||
|
|
||||||
if (node instanceof Dom) child = node.$el;
|
if (node instanceof Dom) child = node.$el;
|
||||||
|
|
||||||
if (this.$el.append) this.$el.append(child);
|
if (this.$el.append) this.$el.append(child as HTMLElement);
|
||||||
else this.$el.appendChild(child);
|
else this.$el.appendChild(child as HTMLElement);
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
on(eventType: string, callback: any) {
|
on(eventType: string, callback: CallbackType) {
|
||||||
this.$el.addEventListener(eventType, callback);
|
this.$el.addEventListener(eventType, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
off(eventType: string, callback: any) {
|
off(eventType: string, callback: CallbackType) {
|
||||||
this.$el.removeEventListener(eventType, callback);
|
this.$el.removeEventListener(eventType, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,11 +95,11 @@ export class Dom implements DomClass {
|
|||||||
return this.$el.querySelectorAll(selector);
|
return this.$el.querySelectorAll(selector);
|
||||||
}
|
}
|
||||||
|
|
||||||
css(styles: any) {
|
css(styles: Partial<CSSStyleDeclaration>) {
|
||||||
if (!styles) return;
|
if (!styles) return;
|
||||||
|
|
||||||
Object.keys(styles)?.forEach((key: any) => {
|
Object.keys(styles)?.forEach((key: any) => {
|
||||||
this.$el.style[key] = styles[key];
|
this.$el.style[key] = styles[key] as string;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,11 +115,12 @@ export class Dom implements DomClass {
|
|||||||
this.$el?.classList.remove(className);
|
this.$el?.classList.remove(className);
|
||||||
}
|
}
|
||||||
|
|
||||||
getStyles(styles: any[]) {
|
getStyles(styles: string[]): Partial<CSSStyleDeclaration> {
|
||||||
return styles.reduce((res, s) => {
|
return styles.reduce((res, s) => {
|
||||||
// replace all need if case style value have 2 or more word, this.$el.style[s] return ""word value""
|
// replace all need if case style value have 2 or more word, this.$el.style[s] return ""word value""
|
||||||
// for example font-family
|
// for example font-family
|
||||||
res[s] = this.$el.style[s].replaceAll('"', '') || initialStyleState[s as keyof ToolbarStateType];
|
// @ts-ignore
|
||||||
|
res[s] = this.$el.style[s].replaceAll('"', '') || initialStyleState[s];
|
||||||
return res;
|
return res;
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
@ -130,6 +133,10 @@ export class Dom implements DomClass {
|
|||||||
|
|
||||||
return this.$el.getAttribute(name);
|
return this.$el.getAttribute(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isExist(): boolean {
|
||||||
|
return !!this.$el;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function $(selector: SelectorType) {
|
export function $(selector: SelectorType) {
|
||||||
|
|||||||
@ -1,9 +1,12 @@
|
|||||||
import { Dom } from 'core/Dom';
|
import { Dom } from 'core/Dom';
|
||||||
import { capitalize } from 'core/utils';
|
import { getMethodNameByEventName } from 'core/utils';
|
||||||
|
|
||||||
|
// TODO fix types
|
||||||
|
|
||||||
export class DomListener {
|
export class DomListener {
|
||||||
$root: Dom;
|
$root: Dom;
|
||||||
eventListeners: string[];
|
eventListeners: string[];
|
||||||
|
protected name: string;
|
||||||
|
|
||||||
constructor($root: Dom, eventNames: string[]) {
|
constructor($root: Dom, eventNames: string[]) {
|
||||||
if (!$root) throw new Error('Не передали корневой элемент');
|
if (!$root) throw new Error('Не передали корневой элемент');
|
||||||
@ -16,26 +19,22 @@ export class DomListener {
|
|||||||
if (!this.eventListeners) return;
|
if (!this.eventListeners) return;
|
||||||
|
|
||||||
this.eventListeners.forEach((listener: string) => {
|
this.eventListeners.forEach((listener: string) => {
|
||||||
const method: any = getMethodName(listener);
|
const method = getMethodNameByEventName(listener);
|
||||||
// @ts-ignore FIXME:
|
// @ts-ignore
|
||||||
this[method] = this[method]?.bind(this);
|
this[method] = this[method]?.bind(this);
|
||||||
// @ts-ignore FIXME:
|
// @ts-ignore
|
||||||
if (!this[method]) throw new Error(`Отсутствует метод ${method} в компоненте ${this?.name}`);
|
if (!this[method]) throw new Error(`Отсутствует метод ${method} в компоненте ${this?.name}`);
|
||||||
// @ts-ignore FIXME:
|
|
||||||
|
// @ts-ignore
|
||||||
this.$root.on(listener, this[method]);
|
this.$root.on(listener, this[method]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
removeDOMListeners() {
|
removeDOMListeners() {
|
||||||
this.eventListeners.forEach(listener => {
|
this.eventListeners.forEach(listener => {
|
||||||
// @ts-ignore FIXME:
|
const method = getMethodNameByEventName(listener);
|
||||||
const method: any = getMethodName(listener);
|
// @ts-ignore
|
||||||
// @ts-ignore FIXME:
|
|
||||||
this.$root.off(listener, this[method]);
|
this.$root.off(listener, this[method]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMethodName(eventName: string): string {
|
|
||||||
return `on${capitalize(eventName)}`;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { ActionType, StateType } from 'redux/types';
|
import { ActionType, CallbackType, 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 { Observer } from 'core/Observer';
|
import { Observer } from 'core/Observer';
|
||||||
@ -13,13 +13,12 @@ export type ComponentOptionsType = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export abstract class ExcelComponent extends DomListener {
|
export abstract class ExcelComponent extends DomListener {
|
||||||
private name: string;
|
|
||||||
private observer: Observer;
|
private observer: Observer;
|
||||||
public store: Store;
|
public store: Store;
|
||||||
private subscribe: (keyof StateType)[];
|
private subscribe: (keyof StateType)[];
|
||||||
private unsubscribers: ((args?: any) => any)[];
|
private unsubscribers: CallbackType[];
|
||||||
|
|
||||||
constructor($root: Dom, options: ComponentOptionsType) {
|
protected constructor($root: Dom, options: ComponentOptionsType) {
|
||||||
super($root, options.eventListeners);
|
super($root, options.eventListeners);
|
||||||
this.name = options.name;
|
this.name = options.name;
|
||||||
this.observer = options.observer;
|
this.observer = options.observer;
|
||||||
@ -51,7 +50,7 @@ export abstract class ExcelComponent extends DomListener {
|
|||||||
this.store?.dispatchToStore(action);
|
this.store?.dispatchToStore(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
storeChanged(args?: any) {
|
storeChanged(args: StateType) {
|
||||||
console.log('CHANGE STORE: ', args, ' in component ', this.name);
|
console.log('CHANGE STORE: ', args, ' in component ', this.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -21,6 +21,8 @@ export abstract class ExcelComponentState extends ExcelComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setComponentState(newState: ExcelComponentStateType) {
|
setComponentState(newState: ExcelComponentStateType) {
|
||||||
|
if (!newState) return;
|
||||||
|
|
||||||
this.componentState = { ...this.componentState, ...newState };
|
this.componentState = { ...this.componentState, ...newState };
|
||||||
this.$root.html(this.template);
|
this.$root.html(this.template);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,15 @@
|
|||||||
|
import { CallbackType } from 'redux/types';
|
||||||
|
|
||||||
export class Observer {
|
export class Observer {
|
||||||
private listeners: {
|
private readonly listeners: {
|
||||||
[k: string]: Array<(args?: any) => any>
|
[k: string]: Array<CallbackType>
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.listeners = {};
|
this.listeners = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
subscribe(eventName: string, callback: (args?: any) => any) {
|
subscribe(eventName: string, callback: CallbackType) {
|
||||||
this.listeners[eventName] = this.listeners[eventName] || [];
|
this.listeners[eventName] = this.listeners[eventName] || [];
|
||||||
this.listeners[eventName].push(callback);
|
this.listeners[eventName].push(callback);
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { StateType } from 'redux/types';
|
import { StateType, SubscribeType } from 'redux/types';
|
||||||
import { Store } from 'core/store/Store';
|
import { Store } from 'core/store/Store';
|
||||||
import { isEqual } from 'core/utils';
|
import { isEqual } from 'core/utils';
|
||||||
|
|
||||||
export class StoreSubscriber {
|
export class StoreSubscriber {
|
||||||
sub: any;
|
sub: SubscribeType | null;
|
||||||
currentState: StateType;
|
currentState: StateType;
|
||||||
|
|
||||||
constructor(private store: Store) {
|
constructor(private store: Store) {
|
||||||
@ -31,6 +31,6 @@ export class StoreSubscriber {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsubscribeFromStore() {
|
unsubscribeFromStore() {
|
||||||
this.sub.unsubscribe();
|
this.sub?.unsubscribe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@ export class Router {
|
|||||||
private $placeholder: Dom;
|
private $placeholder: Dom;
|
||||||
private routes: RoutesType;
|
private routes: RoutesType;
|
||||||
private page: DashboardPage | ExcelPage;
|
private page: DashboardPage | ExcelPage;
|
||||||
private loader: Dom;
|
private readonly loader: Dom;
|
||||||
|
|
||||||
constructor(selector: SelectorType, routes: RoutesType) {
|
constructor(selector: SelectorType, routes: RoutesType) {
|
||||||
if (!selector) throw new Error('Selector not provided');
|
if (!selector) throw new Error('Selector not provided');
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import { ActionType, ReducerType, StateType, SubscribeType } from 'redux/types';
|
import { ActionType, CallbackType, ReducerType, StateType, SubscribeType } from 'redux/types';
|
||||||
|
|
||||||
export class Store {
|
export class Store {
|
||||||
state: StateType;
|
state: StateType | null;
|
||||||
listeners: ((args?: any) => void)[];
|
listeners: CallbackType[];
|
||||||
|
|
||||||
constructor(private reducer: ReducerType, initialState: StateType) {
|
constructor(private reducer: ReducerType, initialState: StateType) {
|
||||||
this.state = reducer({ ...initialState }, { type: '__INIT__' });
|
this.state = reducer({ ...initialState }, { type: '__INIT__' });
|
||||||
@ -20,6 +20,8 @@ export class Store {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dispatchToStore(action: ActionType) {
|
dispatchToStore(action: ActionType) {
|
||||||
|
if (!this.state || !action.type) return;
|
||||||
|
|
||||||
this.state = this.reducer(this.state, action);
|
this.state = this.reducer(this.state, action);
|
||||||
this.listeners.forEach(listener => listener(this.state));
|
this.listeners.forEach(listener => listener(this.state));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { CallbackType, StateType } from 'redux/types';
|
||||||
import { fontSizes } from 'src/constants';
|
import { fontSizes } from 'src/constants';
|
||||||
|
|
||||||
export function capitalize(string: string): string {
|
export function capitalize(string: string): string {
|
||||||
@ -6,7 +7,7 @@ export function capitalize(string: string): string {
|
|||||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function storage(key: string, data: any = null): any {
|
export function storage(key: string, data: StateType | null = null): any {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
const localData = localStorage.getItem(key);
|
const localData = localStorage.getItem(key);
|
||||||
return localData ? JSON.parse(localData) : false;
|
return localData ? JSON.parse(localData) : false;
|
||||||
@ -25,7 +26,7 @@ export function isEqual(a: any, b: any) {
|
|||||||
return a === b;
|
return a === b;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function debounce(fn: (fnArgs?: any) => void, wait: number) {
|
export function debounce(fn: CallbackType, wait: number) {
|
||||||
let timeout: NodeJS.Timeout;
|
let timeout: NodeJS.Timeout;
|
||||||
|
|
||||||
return function (...args: any) {
|
return function (...args: any) {
|
||||||
@ -61,3 +62,7 @@ export function isSmallestFontSize(fontSize?: string): number | boolean {
|
|||||||
if (!fontSize) return false;
|
if (!fontSize) return false;
|
||||||
return fontSizes.findIndex(el => el === fontSize) === 0;
|
return fontSizes.findIndex(el => el === fontSize) === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getMethodNameByEventName(eventName: string): string {
|
||||||
|
return `on${capitalize(eventName)}`;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { $ } from 'core/Dom';
|
import { $, Dom } from 'core/Dom';
|
||||||
import { AbstractPage } from 'pages/AbstractPage';
|
import { AbstractPage } from 'pages/AbstractPage';
|
||||||
import { storage } from 'core/utils';
|
import { storage } from 'core/utils';
|
||||||
|
|
||||||
export class DashboardPage extends AbstractPage {
|
export class DashboardPage extends AbstractPage {
|
||||||
getRoot() {
|
getRoot(): Dom {
|
||||||
const id = Date.now().toString();
|
const id = Date.now().toString();
|
||||||
|
|
||||||
return $.create('div', 'db').html(
|
return $.create('div', 'db').html(
|
||||||
@ -24,7 +24,7 @@ export class DashboardPage extends AbstractPage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toHtml(key: string) {
|
function toHtml(key: string): string {
|
||||||
const params = +key.split(':')[1];
|
const params = +key.split(':')[1];
|
||||||
const state = storage(key);
|
const state = storage(key);
|
||||||
const link = `#excel/${params}`;
|
const link = `#excel/${params}`;
|
||||||
@ -38,7 +38,7 @@ function toHtml(key: string) {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createRecordsTable() {
|
export function createRecordsTable(): string {
|
||||||
const keys = getAllKeys();
|
const keys = getAllKeys();
|
||||||
if (!keys.length) return '<p>Пока не создали ни одной таблицы</p>';
|
if (!keys.length) return '<p>Пока не создали ни одной таблицы</p>';
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,7 @@ export class ExcelPage extends AbstractPage {
|
|||||||
private storeSub: SubscribeType | null;
|
private storeSub: SubscribeType | null;
|
||||||
private processor: StateProcessor;
|
private processor: StateProcessor;
|
||||||
|
|
||||||
constructor(props: any) {
|
constructor(props: string[]) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.storeSub = null;
|
this.storeSub = null;
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { ResizeReturnDataType } from 'components/table/handlers/table.resize';
|
||||||
import { ActionType } from 'redux/types';
|
import { ActionType } from 'redux/types';
|
||||||
import {
|
import {
|
||||||
CHANGE_TEXT,
|
CHANGE_TEXT,
|
||||||
@ -9,42 +10,42 @@ import {
|
|||||||
UPDATE_DATE, CHANGE_CURRENT_TEXT,
|
UPDATE_DATE, CHANGE_CURRENT_TEXT,
|
||||||
} from 'redux/action-constants';
|
} from 'redux/action-constants';
|
||||||
|
|
||||||
export function tableResize(resizeData: any) {
|
export function tableResize(resizeData: ResizeReturnDataType): ActionType {
|
||||||
return {
|
return {
|
||||||
type: TABLE_RESIZE,
|
type: TABLE_RESIZE,
|
||||||
...resizeData,
|
resizeData,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function changeText(data: { text: string, id: string }) {
|
export function changeText(data: { text: string, id: string }): ActionType {
|
||||||
return {
|
return {
|
||||||
type: CHANGE_TEXT,
|
type: CHANGE_TEXT,
|
||||||
data,
|
data,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function changeCurrentStyles(data: any) {
|
export function changeCurrentStyles(data: Partial<CSSStyleDeclaration>): ActionType {
|
||||||
return {
|
return {
|
||||||
type: CHANGE_STYLES,
|
type: CHANGE_STYLES,
|
||||||
data,
|
data,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function applyStyle(data: { ids: (string | undefined)[], value: CSSStyleRule }) {
|
export function applyStyle(data: { ids: (string | undefined)[], value: CSSStyleDeclaration }): ActionType {
|
||||||
return {
|
return {
|
||||||
type: APPLY_STYLES,
|
type: APPLY_STYLES,
|
||||||
data,
|
data,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function changeTitle(data: string) {
|
export function changeTitle(data: string): ActionType {
|
||||||
return {
|
return {
|
||||||
type: CHANGE_TITLE,
|
type: CHANGE_TITLE,
|
||||||
data,
|
data,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deleteTable(data: string) {
|
export function deleteTable(data: string): ActionType {
|
||||||
return {
|
return {
|
||||||
type: DELETE_TABLE,
|
type: DELETE_TABLE,
|
||||||
data,
|
data,
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import {
|
|||||||
} from 'redux/action-constants';
|
} from 'redux/action-constants';
|
||||||
|
|
||||||
export function rootReducer(state: StateType, action: ActionType) {
|
export function rootReducer(state: StateType, action: ActionType) {
|
||||||
|
// export const rootReducer: ReducerType = function (state: StateType, action: ActionType) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case TABLE_RESIZE: {
|
case TABLE_RESIZE: {
|
||||||
const newState: StateType = { ...state };
|
const newState: StateType = { ...state };
|
||||||
|
|||||||
6
src/redux/types.d.ts
vendored
6
src/redux/types.d.ts
vendored
@ -10,15 +10,17 @@ export type StateType = {
|
|||||||
rowState: { [k: number]: number };
|
rowState: { [k: number]: number };
|
||||||
currentStyles: ToolbarStateType;
|
currentStyles: ToolbarStateType;
|
||||||
dataState: { [k: string]: string };
|
dataState: { [k: string]: string };
|
||||||
id?: string;
|
id: string;
|
||||||
openDate: number;
|
openDate: number;
|
||||||
stylesState: { [k: string]: ToolbarStateType };
|
stylesState: { [k: string]: ToolbarStateType };
|
||||||
title: string;
|
title: string;
|
||||||
currentText: string;
|
currentText: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ReducerType = (state: StateType, action: ActionType) => StateType;
|
export type ReducerType = (state: StateType, action: ActionType) => StateType | null;
|
||||||
|
|
||||||
export type SubscribeType = {
|
export type SubscribeType = {
|
||||||
unsubscribe: () => void
|
unsubscribe: () => void
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type CallbackType = (...args: any[]) => void;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user