Skip to content

Commit 41ae2a0

Browse files
committed
#381 New Commit Details View File Context Menu, which is an alternative method to run any available actions on a file.
1 parent f025c1a commit 41ae2a0

File tree

6 files changed

+287
-152
lines changed

6 files changed

+287
-152
lines changed

web/contextMenu.ts

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ interface ContextMenuAction {
1010
type ContextMenuActions = ReadonlyArray<ReadonlyArray<ContextMenuAction>>;
1111

1212
type ContextMenuTarget = {
13-
type: TargetType.Commit | TargetType.Ref;
13+
type: TargetType.Commit | TargetType.Ref | TargetType.CommitDetailsView;
1414
elem: HTMLElement;
1515
hash: string;
1616
index: number;
@@ -19,6 +19,7 @@ type ContextMenuTarget = {
1919

2020
class ContextMenu {
2121
private elem: HTMLElement | null = null;
22+
private onClose: (() => void) | null = null;
2223
private target: ContextMenuTarget | null = null;
2324

2425
constructor() {
@@ -27,9 +28,8 @@ class ContextMenu {
2728
document.addEventListener('contextmenu', listener);
2829
}
2930

30-
public show(actions: ContextMenuActions, checked: boolean, target: ContextMenuTarget | null, event: MouseEvent) {
31-
let viewElem = document.getElementById('view'), html = '', handlers: (() => void)[] = [], handlerId = 0;
32-
if (viewElem === null) return;
31+
public show(actions: ContextMenuActions, checked: boolean, target: ContextMenuTarget | null, event: MouseEvent, frameElem: HTMLElement, blockUserInteractionElem: HTMLElement | null = null, onClose: (() => void) | null = null) {
32+
let html = '', handlers: (() => void)[] = [], handlerId = 0;
3333
this.close();
3434

3535
for (let i = 0; i < actions.length; i++) {
@@ -53,22 +53,23 @@ class ContextMenu {
5353
menu.className = 'contextMenu' + (checked ? ' checked' : '');
5454
menu.style.opacity = '0';
5555
menu.innerHTML = html;
56-
viewElem.appendChild(menu);
57-
let bounds = menu.getBoundingClientRect();
58-
let relativeX = event.pageX + bounds.width < viewElem.clientWidth
56+
frameElem.appendChild(menu);
57+
const menuBounds = menu.getBoundingClientRect(), frameBounds = frameElem.getBoundingClientRect();
58+
const relativeX = event.pageX + menuBounds.width < frameBounds.right
5959
? -2 // context menu fits to the right
60-
: event.pageX - bounds.width > 0
61-
? 2 - bounds.width // context menu fits to the left
62-
: -2 - (bounds.width - (viewElem.clientWidth - event.pageX)); // Overlap the context menu horizontally with the cursor
63-
let relativeY = event.pageY + bounds.height < viewElem.clientHeight
60+
: event.pageX - menuBounds.width > 0
61+
? 2 - menuBounds.width // context menu fits to the left
62+
: -2 - (menuBounds.width - (frameBounds.width - event.pageX)); // Overlap the context menu horizontally with the cursor
63+
const relativeY = event.pageY + menuBounds.height < frameBounds.bottom
6464
? -2 // context menu fits below
65-
: event.pageY - bounds.height > 0
66-
? 2 - bounds.height // context menu fits above
67-
: -2 - (bounds.height - (viewElem.clientHeight - event.pageY)); // Overlap the context menu vertically with the cursor
68-
menu.style.left = (viewElem.scrollLeft + Math.max(event.pageX + relativeX, 2)) + 'px';
69-
menu.style.top = (viewElem.scrollTop + Math.max(event.pageY + relativeY, 2)) + 'px';
65+
: event.pageY - menuBounds.height > 0
66+
? 2 - menuBounds.height // context menu fits above
67+
: -2 - (menuBounds.height - (frameBounds.height - event.pageY)); // Overlap the context menu vertically with the cursor
68+
menu.style.left = (frameElem.scrollLeft + Math.max(event.pageX - frameBounds.left + relativeX, 2)) + 'px';
69+
menu.style.top = (frameElem.scrollTop + Math.max(event.pageY - frameBounds.top + relativeY, 2)) + 'px';
7070
menu.style.opacity = '1';
7171
this.elem = menu;
72+
this.onClose = onClose;
7273

7374
addListenerToClass('contextMenuItem', 'click', (e) => {
7475
e.stopPropagation();
@@ -80,15 +81,22 @@ class ContextMenu {
8081
if (this.target !== null && this.target.type !== TargetType.Repo) {
8182
alterClass(this.target.elem, CLASS_CONTEXT_MENU_ACTIVE, true);
8283
}
84+
85+
if (blockUserInteractionElem !== null) {
86+
alterClass(blockUserInteractionElem, CLASS_BLOCK_USER_INTERACTION, true);
87+
}
8388
}
8489

8590
public close() {
8691
if (this.elem !== null) {
8792
this.elem.remove();
8893
this.elem = null;
8994
}
90-
if (this.target !== null && this.target.type !== TargetType.Repo) {
91-
alterClass(this.target.elem, CLASS_CONTEXT_MENU_ACTIVE, false);
95+
alterClassOfCollection(<HTMLCollectionOf<HTMLElement>>document.getElementsByClassName(CLASS_BLOCK_USER_INTERACTION), CLASS_BLOCK_USER_INTERACTION, false);
96+
alterClassOfCollection(<HTMLCollectionOf<HTMLElement>>document.getElementsByClassName(CLASS_CONTEXT_MENU_ACTIVE), CLASS_CONTEXT_MENU_ACTIVE, false);
97+
if (this.onClose !== null) {
98+
this.onClose();
99+
this.onClose = null;
92100
}
93101
this.target = null;
94102
}
@@ -106,8 +114,10 @@ class ContextMenu {
106114
if (commitElem !== null) {
107115
if (typeof this.target.ref === 'undefined') {
108116
// ContextMenu is only dependent on the commit itself
109-
this.target.elem = commitElem;
110-
alterClass(this.target.elem, CLASS_CONTEXT_MENU_ACTIVE, true);
117+
if (this.target.type !== TargetType.CommitDetailsView) {
118+
this.target.elem = commitElem;
119+
alterClass(this.target.elem, CLASS_CONTEXT_MENU_ACTIVE, true);
120+
}
111121
return;
112122
} else {
113123
// ContextMenu is dependent on the commit and ref

web/dialog.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ type DialogInput = DialogTextInput | DialogTextRefInput | DialogSelectInput | Di
7575
type DialogInputValue = string | string[] | boolean;
7676

7777
type DialogTarget = {
78-
type: TargetType.Commit | TargetType.Ref;
78+
type: TargetType.Commit | TargetType.Ref | TargetType.CommitDetailsView;
7979
elem: HTMLElement;
8080
hash: string;
8181
ref?: string;
@@ -271,9 +271,7 @@ class Dialog {
271271
this.elem.remove();
272272
this.elem = null;
273273
}
274-
if (this.target !== null && this.target.type !== TargetType.Repo) {
275-
alterClass(this.target.elem, CLASS_DIALOG_ACTIVE, false);
276-
}
274+
alterClassOfCollection(<HTMLCollectionOf<HTMLElement>>document.getElementsByClassName(CLASS_DIALOG_ACTIVE), CLASS_DIALOG_ACTIVE, false);
277275
this.target = null;
278276
Object.keys(this.customSelects).forEach((index) => this.customSelects[index].remove());
279277
this.customSelects = {};
@@ -303,8 +301,10 @@ class Dialog {
303301
if (commitElem !== null) {
304302
if (typeof this.target.ref === 'undefined') {
305303
// Dialog is only dependent on the commit itself
306-
this.target.elem = commitElem;
307-
alterClass(this.target.elem, CLASS_DIALOG_ACTIVE, true);
304+
if (this.target.type !== TargetType.CommitDetailsView) {
305+
this.target.elem = commitElem;
306+
alterClass(this.target.elem, CLASS_DIALOG_ACTIVE, true);
307+
}
308308
return;
309309
} else {
310310
// Dialog is dependent on the commit and ref

web/global.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ declare global {
3434
lastViewedFile: string | null;
3535
loading: boolean;
3636
fileChangesScrollTop: number;
37+
fileContextMenuOpen: number;
3738
}
3839

3940
interface WebViewState {
@@ -91,12 +92,13 @@ declare global {
9192

9293
const enum TargetType {
9394
Commit = 'commit',
95+
CommitDetailsView = 'cdv',
9496
Ref = 'ref',
9597
Repo = 'repo'
9698
}
9799

98100
interface CommitOrRefTarget {
99-
type: TargetType.Commit | TargetType.Ref;
101+
type: TargetType.Commit | TargetType.Ref | TargetType.CommitDetailsView;
100102
elem: HTMLElement;
101103
}
102104

0 commit comments

Comments
 (0)