Skip to content

Commit 9e582ba

Browse files
committed
Auto-flip compare panel orientation based on width
Uses a ResizeObserver to prefer horizontal layout when the panel is wide enough (≥600px) and vertical when narrow, automatically updating the pinned compare panel orientation on resize. (#5171, #5253)
1 parent fc756f4 commit 9e582ba

1 file changed

Lines changed: 35 additions & 17 deletions

File tree

src/webviews/apps/plus/graph/components/gl-graph-details-panel.ts

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,9 @@ declare global {
119119
}
120120
}
121121

122+
type PanelOrientation = 'horizontal' | 'vertical';
123+
const narrowPanelThreshold = 600;
124+
122125
@customElement('gl-graph-details-panel')
123126
export class GlGraphDetailsPanel extends SignalWatcher(LitElement) {
124127
@consume({ context: graphServicesContext, subscribe: true })
@@ -919,9 +922,21 @@ export class GlGraphDetailsPanel extends SignalWatcher(LitElement) {
919922
return this;
920923
}
921924

925+
private _resizeObserver?: ResizeObserver;
926+
@state() private _preferredCompareOrientation: PanelOrientation = 'vertical';
927+
922928
override connectedCallback(): void {
923929
super.connectedCallback?.();
924930
this.addEventListener('switch-model', this.handleSwitchModel);
931+
this._preferredCompareOrientation = this.clientWidth >= narrowPanelThreshold ? 'horizontal' : 'vertical';
932+
this._resizeObserver = new ResizeObserver(entries => {
933+
const preferred: PanelOrientation =
934+
(entries[0]?.contentRect.width ?? this.clientWidth) >= narrowPanelThreshold ? 'horizontal' : 'vertical';
935+
if (this._preferredCompareOrientation !== preferred) {
936+
this._preferredCompareOrientation = preferred;
937+
}
938+
});
939+
this._resizeObserver.observe(this);
925940
}
926941

927942
private handleSwitchModel = (): void => {
@@ -1046,6 +1061,8 @@ export class GlGraphDetailsPanel extends SignalWatcher(LitElement) {
10461061
override disconnectedCallback(): void {
10471062
super.disconnectedCallback?.();
10481063
this.removeEventListener('switch-model', this.handleSwitchModel);
1064+
this._resizeObserver?.disconnect();
1065+
this._resizeObserver = undefined;
10491066
clearTimeout(this._suppressContentOverflowTimer);
10501067
this._suppressContentOverflowTimer = undefined;
10511068
clearTimeout(this._suppressModePanelOverflowTimer);
@@ -1684,19 +1701,19 @@ export class GlGraphDetailsPanel extends SignalWatcher(LitElement) {
16841701

16851702
const compareSheet = compareSheetOpen
16861703
? (() => {
1687-
// Sheet → pinned panel: default click always moves to beside (horizontal),
1688-
// Alt-click moves to below (vertical). Icon + tooltip preview the live action
1689-
// based on the alt-key state so the affordance reads correctly mid-press.
1690-
const labelFor = (o: 'horizontal' | 'vertical') =>
1691-
o === 'horizontal' ? 'Move Beside' : 'Move Below';
1692-
const iconFor = (o: 'horizontal' | 'vertical') =>
1704+
// Click pins to preferred orientation; Alt-click flips it.
1705+
// Icon + tooltip update live with the Alt-key so the affordance previews the actual action.
1706+
const labelFor = (o: PanelOrientation) => (o === 'horizontal' ? 'Move Beside' : 'Move Below');
1707+
const iconFor = (o: PanelOrientation) =>
16931708
o === 'horizontal' ? 'layout-sidebar-right' : 'layout-panel';
1694-
const effective: 'horizontal' | 'vertical' = this._modifiers.altKey ? 'vertical' : 'horizontal';
1709+
const preferred = this._preferredCompareOrientation;
1710+
const alternate = this.flipOrientation(preferred);
1711+
const effective = this._modifiers.altKey ? alternate : preferred;
16951712
const actionLabel = labelFor(effective);
16961713
const actionIcon = iconFor(effective);
16971714
const tooltipContent = this._modifiers.altKey
16981715
? actionLabel
1699-
: `${actionLabel}\n[${getAltKeySymbol()}] ${labelFor('vertical')}`;
1716+
: `${actionLabel}\n[${getAltKeySymbol()}] ${labelFor(alternate)}`;
17001717
return html`<gl-detail-sheet
17011718
aria-label="Compare"
17021719
sheet-title="Comparing References"
@@ -1761,23 +1778,24 @@ export class GlGraphDetailsPanel extends SignalWatcher(LitElement) {
17611778
};
17621779

17631780
private handleOpenCompareAsPanel = (e: MouseEvent): void => {
1764-
// Sheet → pinned panel: default click always moves to beside (horizontal); Alt-click
1765-
// moves to below (vertical). The orientation preview in the sheet header tooltip mirrors
1766-
// this so the affordance reads correctly mid-press.
1767-
// Tell the sheet to skip its focus-restoration step on disconnect — the user is
1768-
// transitioning INTO the new pinned panel, not dismissing the sheet, so returning focus
1769-
// to whatever row was focused before the sheet opened is the wrong direction.
1781+
// Skip the sheet's focus-restoration — the user is transitioning INTO the panel,
1782+
// not dismissing the sheet.
17701783
const sheet = this.querySelector('gl-detail-sheet');
17711784
if (sheet != null) {
17721785
(sheet as { skipFocusRestore: boolean }).skipFocusRestore = true;
17731786
}
1774-
const target: 'horizontal' | 'vertical' = e.altKey ? 'vertical' : 'horizontal';
1787+
1788+
const preferred = this._preferredCompareOrientation;
1789+
const target = e.altKey ? this.flipOrientation(preferred) : preferred;
17751790
this._workflow.openCompareAsPanel(target);
17761791
};
17771792

1793+
private flipOrientation(o: PanelOrientation): PanelOrientation {
1794+
return o === 'horizontal' ? 'vertical' : 'horizontal';
1795+
}
1796+
17781797
private handleFlipCompareOrientation = (): void => {
1779-
const current = this._state.compareSplitOrientation.get();
1780-
this._state.compareSplitOrientation.set(current === 'horizontal' ? 'vertical' : 'horizontal');
1798+
this._state.compareSplitOrientation.set(this.flipOrientation(this._state.compareSplitOrientation.get()));
17811799
};
17821800

17831801
private handleCompareSplitChange = (e: CustomEvent<{ position: number }>): void => {

0 commit comments

Comments
 (0)