mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-01-23 02:36:05 +00:00
refactor: improve typing
This commit is contained in:
parent
fbc45de335
commit
f077a6f719
15 changed files with 73 additions and 65 deletions
|
|
@ -350,6 +350,8 @@ git clone https://github.com/johannesjo/super-productivity.git
|
||||||
cd super-productivity
|
cd super-productivity
|
||||||
npm i -g @angular/cli
|
npm i -g @angular/cli
|
||||||
npm i
|
npm i
|
||||||
|
# prepare the env file once
|
||||||
|
npm run env
|
||||||
```
|
```
|
||||||
|
|
||||||
**Run the dev server**
|
**Run the dev server**
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,12 @@ export const initDebug = (
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.devToolsMode !== 'previous' && opts.devToolsMode) {
|
if (opts.devToolsMode !== 'previous' && opts.devToolsMode) {
|
||||||
devToolsOptions.mode = opts.devToolsMode as 'bottom' | 'left' | 'right' | 'undocked' | 'detach';
|
devToolsOptions.mode = opts.devToolsMode as
|
||||||
|
| 'bottom'
|
||||||
|
| 'left'
|
||||||
|
| 'right'
|
||||||
|
| 'undocked'
|
||||||
|
| 'detach';
|
||||||
}
|
}
|
||||||
|
|
||||||
app.on('browser-window-created', (event, win) => {
|
app.on('browser-window-created', (event, win) => {
|
||||||
|
|
|
||||||
|
|
@ -121,21 +121,21 @@ export class MainHeaderComponent implements OnDestroy {
|
||||||
currentTaskContext = toSignal(this._currentTaskContext$);
|
currentTaskContext = toSignal(this._currentTaskContext$);
|
||||||
|
|
||||||
private _isRouteWithSidePanel$ = this._router.events.pipe(
|
private _isRouteWithSidePanel$ = this._router.events.pipe(
|
||||||
filter((event: any) => event instanceof NavigationEnd),
|
filter((event): event is NavigationEnd => event instanceof NavigationEnd),
|
||||||
map((event) => true), // Always true since right-panel is now global
|
map((event) => true), // Always true since right-panel is now global
|
||||||
startWith(true), // Always true since right-panel is now global
|
startWith(true), // Always true since right-panel is now global
|
||||||
);
|
);
|
||||||
isRouteWithSidePanel = toSignal(this._isRouteWithSidePanel$, { initialValue: true });
|
isRouteWithSidePanel = toSignal(this._isRouteWithSidePanel$, { initialValue: true });
|
||||||
|
|
||||||
private _isScheduleSection$ = this._router.events.pipe(
|
private _isScheduleSection$ = this._router.events.pipe(
|
||||||
filter((event: any) => event instanceof NavigationEnd),
|
filter((event): event is NavigationEnd => event instanceof NavigationEnd),
|
||||||
map((event) => !!event.urlAfterRedirects.match(/(schedule)$/)),
|
map((event) => !!event.urlAfterRedirects.match(/(schedule)$/)),
|
||||||
startWith(!!this._router.url.match(/(schedule)$/)),
|
startWith(!!this._router.url.match(/(schedule)$/)),
|
||||||
);
|
);
|
||||||
isScheduleSection = toSignal(this._isScheduleSection$, { initialValue: false });
|
isScheduleSection = toSignal(this._isScheduleSection$, { initialValue: false });
|
||||||
|
|
||||||
private _isWorkViewPage$ = this._router.events.pipe(
|
private _isWorkViewPage$ = this._router.events.pipe(
|
||||||
filter((event: any) => event instanceof NavigationEnd),
|
filter((event): event is NavigationEnd => event instanceof NavigationEnd),
|
||||||
map((event) => !!event.urlAfterRedirects.match(/tasks$/)),
|
map((event) => !!event.urlAfterRedirects.match(/tasks$/)),
|
||||||
startWith(!!this._router.url.match(/tasks$/)),
|
startWith(!!this._router.url.match(/tasks$/)),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,7 @@ export class MobileSidePanelMenuComponent {
|
||||||
// Convert observables to signals
|
// Convert observables to signals
|
||||||
readonly isRouteWithSidePanel = toSignal(
|
readonly isRouteWithSidePanel = toSignal(
|
||||||
this._router.events.pipe(
|
this._router.events.pipe(
|
||||||
filter((event: any) => event instanceof NavigationEnd),
|
filter((event): event is NavigationEnd => event instanceof NavigationEnd),
|
||||||
map((event) => true), // Always true since right-panel is now global
|
map((event) => true), // Always true since right-panel is now global
|
||||||
startWith(true), // Always true since right-panel is now global
|
startWith(true), // Always true since right-panel is now global
|
||||||
),
|
),
|
||||||
|
|
@ -134,7 +134,7 @@ export class MobileSidePanelMenuComponent {
|
||||||
|
|
||||||
readonly isWorkViewPage = toSignal(
|
readonly isWorkViewPage = toSignal(
|
||||||
this._router.events.pipe(
|
this._router.events.pipe(
|
||||||
filter((event: any) => event instanceof NavigationEnd),
|
filter((event): event is NavigationEnd => event instanceof NavigationEnd),
|
||||||
map((event) => !!event.urlAfterRedirects.match(/tasks$/)),
|
map((event) => !!event.urlAfterRedirects.match(/tasks$/)),
|
||||||
startWith(!!this._router.url.match(/tasks$/)),
|
startWith(!!this._router.url.match(/tasks$/)),
|
||||||
),
|
),
|
||||||
|
|
@ -185,7 +185,12 @@ export class MobileSidePanelMenuComponent {
|
||||||
this.isShowMobileMenu.update((v) => !v);
|
this.isShowMobileMenu.update((v) => !v);
|
||||||
}
|
}
|
||||||
|
|
||||||
onPluginButtonClick(button: any): void {
|
onPluginButtonClick(button: {
|
||||||
|
pluginId: string;
|
||||||
|
onClick?: () => void;
|
||||||
|
label?: string;
|
||||||
|
icon?: string;
|
||||||
|
}): void {
|
||||||
this._store.dispatch(togglePluginPanel(button.pluginId));
|
this._store.dispatch(togglePluginPanel(button.pluginId));
|
||||||
|
|
||||||
if (button.onClick) {
|
if (button.onClick) {
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ export class DatabaseService {
|
||||||
e: Error | unknown,
|
e: Error | unknown,
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
fn: Function,
|
fn: Function,
|
||||||
args: any[],
|
args: unknown[],
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
devError(e);
|
devError(e);
|
||||||
if (confirm(this._translateService.instant(T.CONFIRM.RELOAD_AFTER_IDB_ERROR))) {
|
if (confirm(this._translateService.instant(T.CONFIRM.RELOAD_AFTER_IDB_ERROR))) {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,10 @@ const FIELDS_TO_COMPARE: (keyof SimpleCounterCfgFields)[] = [
|
||||||
'countdownDuration',
|
'countdownDuration',
|
||||||
];
|
];
|
||||||
|
|
||||||
export const isEqualSimpleCounterCfg = (a: any, b: any): boolean => {
|
export const isEqualSimpleCounterCfg = (
|
||||||
|
a: SimpleCounterCfgFields[] | unknown,
|
||||||
|
b: SimpleCounterCfgFields[] | unknown,
|
||||||
|
): boolean => {
|
||||||
if (Array.isArray(a) && Array.isArray(b)) {
|
if (Array.isArray(a) && Array.isArray(b)) {
|
||||||
if (a.length !== b.length) {
|
if (a.length !== b.length) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -18,7 +21,7 @@ export const isEqualSimpleCounterCfg = (a: any, b: any): boolean => {
|
||||||
if (a[i] !== b[i]) {
|
if (a[i] !== b[i]) {
|
||||||
// eslint-disable-next-line @typescript-eslint/prefer-for-of
|
// eslint-disable-next-line @typescript-eslint/prefer-for-of
|
||||||
for (let j = 0; j < FIELDS_TO_COMPARE.length; j++) {
|
for (let j = 0; j < FIELDS_TO_COMPARE.length; j++) {
|
||||||
const field: any = FIELDS_TO_COMPARE[j];
|
const field = FIELDS_TO_COMPARE[j];
|
||||||
if (a[field] !== b[field]) {
|
if (a[field] !== b[field]) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Injectable, inject } from '@angular/core';
|
import { inject, Injectable } from '@angular/core';
|
||||||
import { select, Store } from '@ngrx/store';
|
import { select, Store } from '@ngrx/store';
|
||||||
import {
|
import {
|
||||||
selectAllSimpleCounters,
|
selectAllSimpleCounters,
|
||||||
|
|
@ -12,8 +12,8 @@ import {
|
||||||
deleteSimpleCounter,
|
deleteSimpleCounter,
|
||||||
deleteSimpleCounters,
|
deleteSimpleCounters,
|
||||||
increaseSimpleCounterCounterToday,
|
increaseSimpleCounterCounterToday,
|
||||||
setSimpleCounterCounterToday,
|
|
||||||
setSimpleCounterCounterForDate,
|
setSimpleCounterCounterForDate,
|
||||||
|
setSimpleCounterCounterToday,
|
||||||
toggleSimpleCounterCounter,
|
toggleSimpleCounterCounter,
|
||||||
turnOffAllSimpleCounterCounters,
|
turnOffAllSimpleCounterCounters,
|
||||||
updateAllSimpleCounters,
|
updateAllSimpleCounters,
|
||||||
|
|
@ -38,7 +38,9 @@ export class SimpleCounterService {
|
||||||
select(selectAllSimpleCounters),
|
select(selectAllSimpleCounters),
|
||||||
);
|
);
|
||||||
simpleCountersUpdatedOnCfgChange$: Observable<SimpleCounter[]> =
|
simpleCountersUpdatedOnCfgChange$: Observable<SimpleCounter[]> =
|
||||||
this.simpleCounters$.pipe(distinctUntilChanged(isEqualSimpleCounterCfg));
|
this.simpleCounters$.pipe(
|
||||||
|
distinctUntilChanged((a, b) => isEqualSimpleCounterCfg(a, b)),
|
||||||
|
);
|
||||||
|
|
||||||
enabledSimpleCounters$: Observable<SimpleCounter[]> = this._store$.select(
|
enabledSimpleCounters$: Observable<SimpleCounter[]> = this._store$.select(
|
||||||
selectEnabledSimpleCounters,
|
selectEnabledSimpleCounters,
|
||||||
|
|
@ -47,9 +49,6 @@ export class SimpleCounterService {
|
||||||
selectEnabledSimpleStopWatchCounters,
|
selectEnabledSimpleStopWatchCounters,
|
||||||
);
|
);
|
||||||
|
|
||||||
enabledSimpleCountersUpdatedOnCfgChange$: Observable<SimpleCounter[]> =
|
|
||||||
this.enabledSimpleCounters$.pipe(distinctUntilChanged(isEqualSimpleCounterCfg));
|
|
||||||
|
|
||||||
enabledAndToggledSimpleCounters$: Observable<SimpleCounter[]> = this._store$.select(
|
enabledAndToggledSimpleCounters$: Observable<SimpleCounter[]> = this._store$.select(
|
||||||
selectEnabledAndToggledSimpleCounters,
|
selectEnabledAndToggledSimpleCounters,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { Task } from '../task.model';
|
||||||
|
|
||||||
@Pipe({ name: 'subTaskTotalTimeSpent' })
|
@Pipe({ name: 'subTaskTotalTimeSpent' })
|
||||||
export class SubTaskTotalTimeSpentPipe implements PipeTransform {
|
export class SubTaskTotalTimeSpentPipe implements PipeTransform {
|
||||||
transform: (value: any, ...args: any[]) => any = getSubTasksTotalTimeSpent;
|
transform: (value: Task[]) => number = getSubTasksTotalTimeSpent;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getSubTasksTotalTimeSpent = (subTasks: Task[]): number => {
|
export const getSubTasksTotalTimeSpent = (subTasks: Task[]): number => {
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ export class TaskRelatedModelEffects {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
autoAddTodayTagOnMarkAsDone: any = createEffect(() =>
|
autoAddTodayTagOnMarkAsDone = createEffect(() =>
|
||||||
this.ifAutoAddTodayEnabled$(
|
this.ifAutoAddTodayEnabled$(
|
||||||
this._actions$.pipe(
|
this._actions$.pipe(
|
||||||
ofType(TaskSharedActions.updateTask),
|
ofType(TaskSharedActions.updateTask),
|
||||||
|
|
@ -76,7 +76,7 @@ export class TaskRelatedModelEffects {
|
||||||
// EXTERNAL ===> TASKS
|
// EXTERNAL ===> TASKS
|
||||||
// -------------------
|
// -------------------
|
||||||
|
|
||||||
moveTaskToUnDone$: any = createEffect(() =>
|
moveTaskToUnDone$ = createEffect(() =>
|
||||||
this._actions$.pipe(
|
this._actions$.pipe(
|
||||||
ofType(moveTaskInTodayList, moveProjectTaskToRegularList),
|
ofType(moveTaskInTodayList, moveProjectTaskToRegularList),
|
||||||
filter(
|
filter(
|
||||||
|
|
@ -95,7 +95,7 @@ export class TaskRelatedModelEffects {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
moveTaskToDone$: any = createEffect(() =>
|
moveTaskToDone$ = createEffect(() =>
|
||||||
this._actions$.pipe(
|
this._actions$.pipe(
|
||||||
ofType(moveTaskInTodayList, moveProjectTaskToRegularList),
|
ofType(moveTaskInTodayList, moveProjectTaskToRegularList),
|
||||||
filter(
|
filter(
|
||||||
|
|
|
||||||
|
|
@ -149,15 +149,15 @@ export class WorklogComponent implements AfterViewInit, OnDestroy {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
sortWorklogItems(a: any, b: any): number {
|
sortWorklogItems(a: { key: number }, b: { key: number }): number {
|
||||||
return b.key - a.key;
|
return b.key - a.key;
|
||||||
}
|
}
|
||||||
|
|
||||||
sortWorklogItemsReverse(a: any, b: any): number {
|
sortWorklogItemsReverse(a: { key: number }, b: { key: number }): number {
|
||||||
return a.key - b.key;
|
return a.key - b.key;
|
||||||
}
|
}
|
||||||
|
|
||||||
trackByKey(i: number, val: { key: any; val: any }): number {
|
trackByKey(i: number, val: { key: number; val: unknown }): number {
|
||||||
return val.key;
|
return val.key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -89,9 +89,9 @@ export class FileImexComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: after promise done the file is NOT yet read
|
// NOTE: after promise done the file is NOT yet read
|
||||||
async handleFileInput(ev: any): Promise<void> {
|
async handleFileInput(ev: Event): Promise<void> {
|
||||||
const files = ev.target.files;
|
const files = (ev.target as HTMLInputElement).files;
|
||||||
const file = files.item(0);
|
const file = files?.item(0);
|
||||||
|
|
||||||
if (!file) {
|
if (!file) {
|
||||||
// No file selected or selection cancelled
|
// No file selected or selection cancelled
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Action } from '@ngrx/store';
|
import { Action, ActionReducer } from '@ngrx/store';
|
||||||
import { DEFAULT_TASK, Task, TaskCopy } from '../../../features/tasks/task.model';
|
import { DEFAULT_TASK, Task, TaskCopy } from '../../../features/tasks/task.model';
|
||||||
import { TaskSharedActions } from '../task-shared.actions';
|
import { TaskSharedActions } from '../task-shared.actions';
|
||||||
|
|
||||||
|
|
@ -21,28 +21,33 @@ import { validateAndFixDataConsistencyAfterBatchUpdate } from './validate-and-fi
|
||||||
* Meta reducer for handling batch updates to tasks within a project
|
* Meta reducer for handling batch updates to tasks within a project
|
||||||
* This reducer processes all operations in a single state update for efficiency
|
* This reducer processes all operations in a single state update for efficiency
|
||||||
*/
|
*/
|
||||||
export const taskBatchUpdateMetaReducer = (
|
export const taskBatchUpdateMetaReducer = <T extends Partial<RootState> = RootState>(
|
||||||
reducer: any,
|
reducer: ActionReducer<T>,
|
||||||
): ((state: any, action: any) => any) => {
|
): ActionReducer<T> => {
|
||||||
return (state: any, action: Action) => {
|
return (state: T | undefined, action: Action) => {
|
||||||
if (action.type === TaskSharedActions.batchUpdateForProject.type) {
|
if (action.type === TaskSharedActions.batchUpdateForProject.type) {
|
||||||
const { projectId, operations, createdTaskIds } = action as ReturnType<
|
const { projectId, operations, createdTaskIds } = action as ReturnType<
|
||||||
typeof TaskSharedActions.batchUpdateForProject
|
typeof TaskSharedActions.batchUpdateForProject
|
||||||
>;
|
>;
|
||||||
|
|
||||||
// Ensure state has required properties
|
// Ensure state has required properties
|
||||||
if (!state[TASK_FEATURE_NAME] || !state[PROJECT_FEATURE_NAME]) {
|
const rootState = state as unknown as RootState;
|
||||||
|
if (
|
||||||
|
!rootState ||
|
||||||
|
!rootState[TASK_FEATURE_NAME] ||
|
||||||
|
!rootState[PROJECT_FEATURE_NAME]
|
||||||
|
) {
|
||||||
Log.error('taskBatchUpdateMetaReducer: Missing required state properties');
|
Log.error('taskBatchUpdateMetaReducer: Missing required state properties');
|
||||||
return reducer(state, action);
|
return reducer(state, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate project exists
|
// Validate project exists
|
||||||
if (!state[PROJECT_FEATURE_NAME].entities[projectId]) {
|
if (!rootState[PROJECT_FEATURE_NAME].entities[projectId]) {
|
||||||
Log.error(`taskBatchUpdateMetaReducer: Project ${projectId} not found`);
|
Log.error(`taskBatchUpdateMetaReducer: Project ${projectId} not found`);
|
||||||
return reducer(state, action);
|
return reducer(state, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
let newState = { ...state } as RootState;
|
let newState = { ...rootState } as RootState;
|
||||||
const tasksToAdd: Task[] = [];
|
const tasksToAdd: Task[] = [];
|
||||||
const tasksToUpdate: { id: string; changes: Partial<Task> }[] = [];
|
const tasksToUpdate: { id: string; changes: Partial<Task> }[] = [];
|
||||||
const taskIdsToDelete: string[] = [];
|
const taskIdsToDelete: string[] = [];
|
||||||
|
|
@ -346,7 +351,7 @@ export const taskBatchUpdateMetaReducer = (
|
||||||
newTaskOrder,
|
newTaskOrder,
|
||||||
);
|
);
|
||||||
|
|
||||||
return reducer(newState, action);
|
return reducer(newState as T, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
return reducer(state, action);
|
return reducer(state, action);
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ export class InputDurationSliderComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
// Convert to signals
|
// Convert to signals
|
||||||
readonly minutesBefore = signal(0);
|
readonly minutesBefore = signal(0);
|
||||||
readonly dots = signal<any[]>([]);
|
readonly dots = signal<number[]>([]);
|
||||||
readonly uid = 'duration-input-slider' + nanoid();
|
readonly uid = 'duration-input-slider' + nanoid();
|
||||||
readonly el: HTMLElement;
|
readonly el: HTMLElement;
|
||||||
|
|
||||||
|
|
@ -47,9 +47,9 @@ export class InputDurationSliderComponent implements OnInit, OnDestroy {
|
||||||
// Internal model signal
|
// Internal model signal
|
||||||
readonly _model = signal(0);
|
readonly _model = signal(0);
|
||||||
|
|
||||||
startHandler?: (ev: any) => void;
|
startHandler?: (ev: MouseEvent | TouchEvent) => void;
|
||||||
endHandler?: () => void;
|
endHandler?: () => void;
|
||||||
moveHandler?: (ev: any) => void;
|
moveHandler?: (ev: MouseEvent | TouchEvent) => void;
|
||||||
|
|
||||||
readonly circleEl = viewChild<ElementRef>('circleEl');
|
readonly circleEl = viewChild<ElementRef>('circleEl');
|
||||||
|
|
||||||
|
|
@ -74,7 +74,10 @@ export class InputDurationSliderComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't execute when clicked on label or input
|
// don't execute when clicked on label or input
|
||||||
if (ev.target.tagName === 'LABEL' || ev.target.tagName === 'INPUT') {
|
if (
|
||||||
|
(ev.target as HTMLElement)?.tagName === 'LABEL' ||
|
||||||
|
(ev.target as HTMLElement)?.tagName === 'INPUT'
|
||||||
|
) {
|
||||||
this.endHandler();
|
this.endHandler();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -91,7 +94,8 @@ export class InputDurationSliderComponent implements OnInit, OnDestroy {
|
||||||
this.moveHandler = (ev) => {
|
this.moveHandler = (ev) => {
|
||||||
if (
|
if (
|
||||||
ev.type === 'click' &&
|
ev.type === 'click' &&
|
||||||
(ev.target.tagName === 'LABEL' || ev.target.tagName === 'INPUT')
|
((ev.target as HTMLElement)?.tagName === 'LABEL' ||
|
||||||
|
(ev.target as HTMLElement)?.tagName === 'INPUT')
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -108,16 +112,18 @@ export class InputDurationSliderComponent implements OnInit, OnDestroy {
|
||||||
const centerX = circleEl.nativeElement.offsetWidth / 2;
|
const centerX = circleEl.nativeElement.offsetWidth / 2;
|
||||||
const centerY = circleEl.nativeElement.offsetHeight / 2;
|
const centerY = circleEl.nativeElement.offsetHeight / 2;
|
||||||
|
|
||||||
let offsetX;
|
let offsetX: number;
|
||||||
|
let offsetY: number;
|
||||||
|
|
||||||
let offsetY;
|
|
||||||
if (ev.type === 'touchmove') {
|
if (ev.type === 'touchmove') {
|
||||||
const rect = ev.target.getBoundingClientRect();
|
const touchEv = ev as TouchEvent;
|
||||||
offsetX = ev.targetTouches[0].pageX - rect.left;
|
const rect = (ev.target as Element).getBoundingClientRect();
|
||||||
offsetY = ev.targetTouches[0].pageY - rect.top;
|
offsetX = touchEv.targetTouches[0].pageX - rect.left;
|
||||||
|
offsetY = touchEv.targetTouches[0].pageY - rect.top;
|
||||||
} else {
|
} else {
|
||||||
offsetX = ev.offsetX;
|
const mouseEv = ev as MouseEvent;
|
||||||
offsetY = ev.offsetY;
|
offsetX = mouseEv.offsetX;
|
||||||
|
offsetY = mouseEv.offsetY;
|
||||||
}
|
}
|
||||||
|
|
||||||
const x = offsetX - centerX;
|
const x = offsetX - centerX;
|
||||||
|
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
import { Pipe, PipeTransform } from '@angular/core';
|
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { map } from 'rxjs/operators';
|
|
||||||
import { msToString } from './ms-to-string.pipe';
|
|
||||||
|
|
||||||
@Pipe({ name: 'msToString$' })
|
|
||||||
export class MsToStringPipe$ implements PipeTransform {
|
|
||||||
transform(value$: Observable<any> | undefined, showSeconds?: boolean): any {
|
|
||||||
if (value$) {
|
|
||||||
value$.pipe(
|
|
||||||
map((value) => {
|
|
||||||
return msToString(value, showSeconds);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -41,7 +41,7 @@ export class PanDirective implements OnDestroy {
|
||||||
private _isPanning = false;
|
private _isPanning = false;
|
||||||
private _touchIdentifier: number | null = null;
|
private _touchIdentifier: number | null = null;
|
||||||
private _isScrolling = false;
|
private _isScrolling = false;
|
||||||
private _scrollTimeout: any = null;
|
private _scrollTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||||
private _scrollListener: (() => void) | null = null;
|
private _scrollListener: (() => void) | null = null;
|
||||||
private _scrollableParent: HTMLElement | null = null;
|
private _scrollableParent: HTMLElement | null = null;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue