refactor: migrate side panel over mode logic to signals

- Move isCustomOver logic from right-panel component to layout service
- Implement using signals and computed properties for better performance
- Add isRightPanelOverCustom computed signal in layout service
- Maintain behavior: work-view routes use responsive mode, non-work-view routes force over mode unless on very big screens (≥1270px)
- Update right-panel component to use the new signal-based API
This commit is contained in:
Johannes Millan 2025-07-25 17:02:17 +02:00
parent b99a1654cb
commit 22fce24bca
3 changed files with 59 additions and 5 deletions

View file

@ -1,4 +1,4 @@
import { inject, Injectable } from '@angular/core';
import { inject, Injectable, computed } from '@angular/core';
import {
hideAddTaskBar,
hideIssuePanel,
@ -22,16 +22,18 @@ import {
selectIsShowSideNav,
selectIsShowTaskViewCustomizerPanel,
} from './store/layout.reducer';
import { filter, map, switchMap, withLatestFrom } from 'rxjs/operators';
import { filter, map, switchMap, withLatestFrom, startWith } from 'rxjs/operators';
import { BreakpointObserver } from '@angular/cdk/layout';
import { NavigationStart, Router } from '@angular/router';
import { NavigationStart, NavigationEnd, Router } from '@angular/router';
import { WorkContextService } from '../../features/work-context/work-context.service';
import { selectMiscConfig } from '../../features/config/store/global-config.reducer';
import { toSignal } from '@angular/core/rxjs-interop';
const NAV_ALWAYS_VISIBLE = 1200;
const NAV_OVER_RIGHT_PANEL_NEXT = 800;
const BOTH_OVER = 720;
const XS_MAX = 599;
const VERY_BIG_SCREEN = 1270;
@Injectable({
providedIn: 'root',
@ -62,6 +64,46 @@ export class LayoutService {
isRightPanelOver$: Observable<boolean> = this._breakPointObserver
.observe([`(min-width: ${BOTH_OVER}px)`])
.pipe(map((result) => !result.matches));
// Signals for custom right panel behavior
private readonly _isVeryBigScreen = toSignal(
this._breakPointObserver
.observe([`(min-width: ${VERY_BIG_SCREEN}px)`])
.pipe(map((result) => result.matches)),
{ initialValue: false },
);
private readonly _isWorkViewRoute = toSignal(
this._router.events.pipe(
filter((event): event is NavigationEnd => event instanceof NavigationEnd),
map((event) => this._isWorkViewUrl(event.urlAfterRedirects)),
startWith(this._isWorkViewUrl(this._router.url)),
),
{ initialValue: this._isWorkViewUrl(this._router.url) },
);
private readonly _isRightPanelOverDefault = toSignal(this.isRightPanelOver$, {
initialValue: false,
});
// Computed signal for custom right panel over behavior
readonly isRightPanelOverCustom = computed(() => {
const isWorkView = this._isWorkViewRoute();
const isVeryBigScreen = this._isVeryBigScreen();
const defaultIsOver = this._isRightPanelOverDefault();
// Use default behavior if on work view
if (isWorkView) {
return defaultIsOver;
}
// For non-work-view routes: use "over" mode unless on very big screen
return !isVeryBigScreen;
});
private _isWorkViewUrl(url: string): boolean {
return url.includes('/active/') || url.includes('/tag/') || url.includes('/project/');
}
isNavOver$: Observable<boolean> = this._store$.select(selectMiscConfig).pipe(
switchMap((miscCfg) => {
if (miscCfg.isUseMinimalNav) {

View file

@ -1,7 +1,7 @@
<better-drawer-container
(wasClosed)="close()"
[isOpen]="isOpen$ | async"
[isOver]="isAlwaysOver() || (layoutService.isRightPanelOver$ | async)"
[isOver]="isCustomOver()"
[sideWidth]="40"
>
<ng-container better-drawer-content>
@ -25,7 +25,7 @@
} @else if (selectedTaskWithDelayForNone$ | async) {
@let selectedTaskWithDelayForNone = selectedTaskWithDelayForNone$ | async;
<task-detail-panel
[isOver]="isAlwaysOver() || (layoutService.isRightPanelOver$ | async)"
[isOver]="isCustomOver()"
[@taskDetailPanelTaskChange]="selectedTaskWithDelayForNone?.id"
[@.disabled]="isDisableTaskPanelAni"
[task]="selectedTaskWithDelayForNone"

View file

@ -4,6 +4,7 @@ import {
inject,
input,
OnDestroy,
computed,
} from '@angular/core';
import { combineLatest, Observable, of, Subscription } from 'rxjs';
import { TaskDetailTargetPanel, TaskWithSubTasks } from '../tasks/task.model';
@ -69,6 +70,17 @@ export class RightPanelComponent implements OnDestroy {
// NOTE: used for debugging
readonly isAlwaysOver = input<boolean>(false);
// Use the computed signal from layout service
readonly isCustomOver = computed(() => {
// Always use "over" mode for debugging
if (this.isAlwaysOver()) {
return true;
}
// Use the layout service's computed signal
return this.layoutService.isRightPanelOverCustom();
});
// to still display its data when panel is closing
selectedTaskWithDelayForNone$: Observable<TaskWithSubTasks | null> =
this.taskService.selectedTask$.pipe(