Decouple dialog refocus from PanelsService via FocusService
Dialogs only needed refocus(); injecting PanelsService boots a whole workspace, which secondary windows must not do. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { Component, computed, inject, input, signal } from '@angular/core';
|
||||
import { DialogsService, type ChmodRequest } from '../dialogs.service';
|
||||
import { PanelsService } from '../panels.service';
|
||||
import { FocusService } from '../focus.service';
|
||||
|
||||
const ROLES = ['Owner', 'Group', 'Others'] as const;
|
||||
const PERMS = ['r', 'w', 'x'] as const;
|
||||
@@ -81,7 +81,7 @@ export class ChmodDialog {
|
||||
readonly perms = PERMS;
|
||||
|
||||
private readonly dialogs = inject(DialogsService);
|
||||
private readonly panels = inject(PanelsService);
|
||||
private readonly focus = inject(FocusService);
|
||||
|
||||
// mode is initialized lazily from the request on first read
|
||||
private readonly modeOverride = signal<number | null>(null);
|
||||
@@ -120,6 +120,6 @@ export class ChmodDialog {
|
||||
|
||||
private close(): void {
|
||||
this.dialogs.chmod.set(null);
|
||||
this.panels.refocus();
|
||||
this.focus.bump();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Component, ElementRef, afterNextRender, inject, input, viewChild } from '@angular/core';
|
||||
import { DialogsService, type ConfirmRequest } from '../dialogs.service';
|
||||
import { PanelsService } from '../panels.service';
|
||||
import { FocusService } from '../focus.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-confirm-dialog',
|
||||
@@ -41,7 +41,7 @@ export class ConfirmDialog {
|
||||
|
||||
private readonly confirmBtn = viewChild.required<ElementRef<HTMLButtonElement>>('confirmBtn');
|
||||
private readonly dialogs = inject(DialogsService);
|
||||
private readonly panels = inject(PanelsService);
|
||||
private readonly focus = inject(FocusService);
|
||||
|
||||
constructor() {
|
||||
afterNextRender(() => this.confirmBtn().nativeElement.focus());
|
||||
@@ -59,6 +59,6 @@ export class ConfirmDialog {
|
||||
|
||||
private close(): void {
|
||||
this.dialogs.confirm.set(null);
|
||||
this.panels.refocus();
|
||||
this.focus.bump();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
import { NgIcon } from '@ng-icons/core';
|
||||
import { DialogsService, type OverwriteChoice, type OverwriteRequest } from '../dialogs.service';
|
||||
import { formatDate, formatSize } from '../format';
|
||||
import { PanelsService } from '../panels.service';
|
||||
import { FocusService } from '../focus.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-overwrite-dialog',
|
||||
@@ -125,7 +125,7 @@ export class OverwriteDialog {
|
||||
readonly formatDate = formatDate;
|
||||
|
||||
private readonly dialogs = inject(DialogsService);
|
||||
private readonly panels = inject(PanelsService);
|
||||
private readonly focus = inject(FocusService);
|
||||
private readonly primaryBtn = viewChild.required<ElementRef<HTMLButtonElement>>('primaryBtn');
|
||||
|
||||
constructor() {
|
||||
@@ -145,7 +145,7 @@ export class OverwriteDialog {
|
||||
pick(choice: OverwriteChoice): void {
|
||||
const req = this.request();
|
||||
this.dialogs.overwrite.set(null);
|
||||
this.panels.refocus();
|
||||
this.focus.bump();
|
||||
req.resolve(choice);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
viewChild,
|
||||
} from '@angular/core';
|
||||
import { DialogsService, type PromptRequest } from '../dialogs.service';
|
||||
import { PanelsService } from '../panels.service';
|
||||
import { FocusService } from '../focus.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-prompt-dialog',
|
||||
@@ -54,7 +54,7 @@ export class PromptDialog {
|
||||
|
||||
private readonly box = viewChild.required<ElementRef<HTMLInputElement>>('box');
|
||||
private readonly dialogs = inject(DialogsService);
|
||||
private readonly panels = inject(PanelsService);
|
||||
private readonly focus = inject(FocusService);
|
||||
|
||||
constructor() {
|
||||
afterNextRender(() => {
|
||||
@@ -79,6 +79,6 @@ export class PromptDialog {
|
||||
|
||||
private close(): void {
|
||||
this.dialogs.prompt.set(null);
|
||||
this.panels.refocus();
|
||||
this.focus.bump();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { Injectable, signal } from '@angular/core';
|
||||
|
||||
/**
|
||||
* "Give keyboard focus back to the main surface" signal. Dependency-free so
|
||||
* dialogs can bump it from ANY window kind — in the main window the file
|
||||
* panels listen (via PanelsService.focusTick); elsewhere nobody does, which
|
||||
* is exactly right.
|
||||
*/
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class FocusService {
|
||||
readonly tick = signal(0);
|
||||
|
||||
bump(): void {
|
||||
this.tick.update((n) => n + 1);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Injectable, computed, signal, type WritableSignal } from '@angular/core';
|
||||
import { Injectable, computed, inject, signal, type WritableSignal } from '@angular/core';
|
||||
import type { ConnectionConfig, FileEntry, SavedConnection } from '@shared/types';
|
||||
import { FocusService } from './focus.service';
|
||||
import { RpcService } from './rpc.service';
|
||||
import { ToastService } from './toast.service';
|
||||
|
||||
@@ -201,8 +202,9 @@ export class PanelsService {
|
||||
const list = this.workspaces();
|
||||
return list.find((w) => w.id === this.activeWorkspaceId()) ?? list[0];
|
||||
});
|
||||
private readonly focus = inject(FocusService);
|
||||
/** Bumped to tell the active panel to grab keyboard focus again (e.g. after a dialog closes). */
|
||||
readonly focusTick = signal(0);
|
||||
readonly focusTick = this.focus.tick;
|
||||
|
||||
private nextWorkspaceId = 1;
|
||||
|
||||
@@ -265,7 +267,7 @@ export class PanelsService {
|
||||
}
|
||||
|
||||
refocus(): void {
|
||||
this.focusTick.update((n) => n + 1);
|
||||
this.focus.bump();
|
||||
}
|
||||
|
||||
// ---- workspace (tab) management ----
|
||||
|
||||
Reference in New Issue
Block a user