Skip to content

Commit 8c47351

Browse files
committed
feat: introduce vertical resizer in horizontal layout mode (fixes #75)
1 parent d0f7099 commit 8c47351

File tree

4 files changed

+146
-17
lines changed

4 files changed

+146
-17
lines changed

packages/yasgui/src/Tab.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ export class Tab extends EventEmitter {
7878
private settingsModal?: TabSettingsModal;
7979
private currentOrientation: "vertical" | "horizontal";
8080
private orientationToggleButton?: HTMLButtonElement;
81+
private verticalResizerEl?: HTMLDivElement;
82+
private editorWrapperEl?: HTMLDivElement;
8183

8284
constructor(yasgui: Yasgui, conf: PersistedJson) {
8385
super();
@@ -115,6 +117,7 @@ export class Tab extends EventEmitter {
115117
// Useful for adding an infos div that goes alongside the editor without needing to rebuild the whole Yasgui class
116118
const editorWrapper = document.createElement("div");
117119
editorWrapper.className = "editorwrapper";
120+
this.editorWrapperEl = editorWrapper;
118121
const controlbarAndYasqeDiv = document.createElement("div");
119122
//controlbar
120123
this.controlBarEl = document.createElement("div");
@@ -132,6 +135,10 @@ export class Tab extends EventEmitter {
132135

133136
this.initTabSettingsMenu();
134137
this.rootEl.appendChild(editorWrapper);
138+
139+
// Add vertical resizer for horizontal layout
140+
this.drawVerticalResizer();
141+
135142
this.rootEl.appendChild(this.yasrWrapperEl);
136143
this.initControlbar();
137144
this.initYasqe();
@@ -321,6 +328,12 @@ export class Tab extends EventEmitter {
321328
tab.updateOrientationToggleIcon();
322329
}
323330

331+
// Reset editor wrapper width when switching orientations
332+
if (tab.editorWrapperEl) {
333+
tab.editorWrapperEl.style.width = "";
334+
tab.editorWrapperEl.style.flex = "";
335+
}
336+
324337
// Refresh components to adjust to new layout
325338
if (tab.yasqe) {
326339
tab.yasqe.refresh();
@@ -986,7 +999,94 @@ WHERE {
986999
});
9871000
}
9881001

1002+
private drawVerticalResizer() {
1003+
if (this.verticalResizerEl || !this.rootEl) return;
1004+
this.verticalResizerEl = document.createElement("div");
1005+
addClass(this.verticalResizerEl, "verticalResizeWrapper");
1006+
const chip = document.createElement("div");
1007+
addClass(chip, "verticalResizeChip");
1008+
this.verticalResizerEl.appendChild(chip);
1009+
this.verticalResizerEl.addEventListener("mousedown", this.initVerticalDrag, false);
1010+
this.verticalResizerEl.addEventListener("dblclick", this.resetVerticalSplit, false);
1011+
this.rootEl.appendChild(this.verticalResizerEl);
1012+
}
1013+
1014+
private initVerticalDrag = () => {
1015+
document.documentElement.addEventListener("mousemove", this.doVerticalDrag, false);
1016+
document.documentElement.addEventListener("mouseup", this.stopVerticalDrag, false);
1017+
};
1018+
1019+
private calculateVerticalDragOffset(event: MouseEvent): number {
1020+
if (!this.rootEl) return 0;
1021+
1022+
let parentOffset = 0;
1023+
if (this.rootEl.offsetParent) {
1024+
parentOffset = (this.rootEl.offsetParent as HTMLElement).offsetLeft;
1025+
}
1026+
1027+
let scrollOffset = 0;
1028+
let parentElement = this.rootEl.parentElement;
1029+
while (parentElement) {
1030+
scrollOffset += parentElement.scrollLeft;
1031+
parentElement = parentElement.parentElement;
1032+
}
1033+
1034+
return event.clientX - parentOffset - this.rootEl.offsetLeft + scrollOffset;
1035+
}
1036+
1037+
private doVerticalDrag = (event: MouseEvent) => {
1038+
if (!this.editorWrapperEl || !this.rootEl) return;
1039+
1040+
const offset = this.calculateVerticalDragOffset(event);
1041+
const totalWidth = this.rootEl.offsetWidth;
1042+
1043+
// Ensure minimum widths (at least 200px for each panel)
1044+
const minWidth = 200;
1045+
const maxWidth = totalWidth - minWidth - 10; // 10px for resizer
1046+
1047+
const newWidth = Math.max(minWidth, Math.min(maxWidth, offset));
1048+
this.editorWrapperEl.style.width = newWidth + "px";
1049+
this.editorWrapperEl.style.flex = "0 0 " + newWidth + "px";
1050+
};
1051+
1052+
private stopVerticalDrag = () => {
1053+
document.documentElement.removeEventListener("mousemove", this.doVerticalDrag, false);
1054+
document.documentElement.removeEventListener("mouseup", this.stopVerticalDrag, false);
1055+
1056+
// Refresh editors after resizing
1057+
if (this.yasqe) {
1058+
this.yasqe.refresh();
1059+
}
1060+
if (this.yasr) {
1061+
this.yasr.refresh();
1062+
}
1063+
};
1064+
1065+
private resetVerticalSplit = () => {
1066+
if (!this.editorWrapperEl) return;
1067+
1068+
// Reset to 50/50 split
1069+
this.editorWrapperEl.style.width = "";
1070+
this.editorWrapperEl.style.flex = "1 1 50%";
1071+
1072+
// Refresh editors after resizing
1073+
if (this.yasqe) {
1074+
this.yasqe.refresh();
1075+
}
1076+
if (this.yasr) {
1077+
this.yasr.refresh();
1078+
}
1079+
};
1080+
9891081
destroy() {
1082+
// Clean up vertical resizer event listeners
1083+
if (this.verticalResizerEl) {
1084+
this.verticalResizerEl.removeEventListener("mousedown", this.initVerticalDrag, false);
1085+
this.verticalResizerEl.removeEventListener("dblclick", this.resetVerticalSplit, false);
1086+
}
1087+
document.documentElement.removeEventListener("mousemove", this.doVerticalDrag, false);
1088+
document.documentElement.removeEventListener("mouseup", this.stopVerticalDrag, false);
1089+
9901090
this.removeAllListeners();
9911091
this.settingsModal?.destroy();
9921092
this.endpointSelect?.destroy();

packages/yasgui/src/tab.scss

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
flex: 1 1 50%;
5050
display: flex;
5151
flex-direction: column;
52-
min-width: 0;
52+
min-width: 400px;
5353
max-width: 50%;
5454
height: 100%;
5555
overflow: hidden;
@@ -97,11 +97,40 @@
9797
height: 100%;
9898
}
9999

100-
// Hide resize wrapper in horizontal mode (not applicable)
101-
.resizeWrapper {
100+
// Hide horizontal resize wrapper in horizontal mode (vertical resizer is used instead)
101+
.horizontalResizeWrapper {
102102
display: none;
103103
}
104104
}
105+
106+
// Vertical resizer for horizontal layout
107+
.verticalResizeWrapper {
108+
display: none; // Hidden by default (shown only in horizontal mode)
109+
width: 10px;
110+
flex-shrink: 0;
111+
align-items: center;
112+
justify-content: center;
113+
cursor: col-resize;
114+
position: relative;
115+
z-index: 10;
116+
117+
&:hover .verticalResizeChip {
118+
visibility: visible;
119+
}
120+
}
121+
122+
.verticalResizeChip {
123+
width: 4px;
124+
height: 20%;
125+
background-color: #d1d1d1;
126+
visibility: hidden;
127+
border-radius: 2px;
128+
}
129+
130+
// Show vertical resizer only in horizontal mode
131+
&.orientation-horizontal .verticalResizeWrapper {
132+
display: flex;
133+
}
105134
}
106135

107136
.tabContextButton {

packages/yasqe/src/index.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export class Yasqe extends CodeMirror {
5555
private queryBtn: HTMLButtonElement | undefined;
5656
private fullscreenBtn: HTMLButtonElement | undefined;
5757
private isFullscreen: boolean = false;
58-
private resizeWrapper?: HTMLDivElement;
58+
private horizontalResizeWrapper?: HTMLDivElement;
5959
private snippetsBar?: HTMLDivElement;
6060
private snippetsClickHandler?: (e: MouseEvent) => void;
6161
private snippetsResizeHandler?: () => void;
@@ -727,15 +727,15 @@ export class Yasqe extends CodeMirror {
727727
}
728728

729729
private drawResizer() {
730-
if (this.resizeWrapper) return;
731-
this.resizeWrapper = document.createElement("div");
732-
addClass(this.resizeWrapper, "resizeWrapper");
730+
if (this.horizontalResizeWrapper) return;
731+
this.horizontalResizeWrapper = document.createElement("div");
732+
addClass(this.horizontalResizeWrapper, "horizontalResizeWrapper");
733733
const chip = document.createElement("div");
734-
addClass(chip, "resizeChip");
735-
this.resizeWrapper.appendChild(chip);
736-
this.resizeWrapper.addEventListener("mousedown", this.initDrag, false);
737-
this.resizeWrapper.addEventListener("dblclick", this.expandEditor);
738-
this.rootEl.appendChild(this.resizeWrapper);
734+
addClass(chip, "horizontalResizeChip");
735+
this.horizontalResizeWrapper.appendChild(chip);
736+
this.horizontalResizeWrapper.addEventListener("mousedown", this.initDrag, false);
737+
this.horizontalResizeWrapper.addEventListener("dblclick", this.expandEditor);
738+
this.rootEl.appendChild(this.horizontalResizeWrapper);
739739
}
740740
private initDrag() {
741741
document.documentElement.addEventListener("mousemove", this.doDrag, false);
@@ -1407,8 +1407,8 @@ export class Yasqe extends CodeMirror {
14071407
// Abort running query
14081408
this.abortQuery();
14091409
this.unregisterEventListeners();
1410-
this.resizeWrapper?.removeEventListener("mousedown", this.initDrag, false);
1411-
this.resizeWrapper?.removeEventListener("dblclick", this.expandEditor);
1410+
this.horizontalResizeWrapper?.removeEventListener("mousedown", this.initDrag, false);
1411+
this.horizontalResizeWrapper?.removeEventListener("dblclick", this.expandEditor);
14121412
if (this.snippetsClickHandler) {
14131413
document.removeEventListener("click", this.snippetsClickHandler);
14141414
this.snippetsClickHandler = undefined;

packages/yasqe/src/scss/yasqe.scss

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@
8585
vertical-align: middle;
8686
}
8787

88-
.resizeWrapper {
88+
.horizontalResizeWrapper {
8989
width: 100%;
9090
height: 10px;
9191
flex-shrink: 0;
@@ -95,15 +95,15 @@
9595
cursor: row-resize;
9696
}
9797

98-
.resizeChip {
98+
.horizontalResizeChip {
9999
width: 20%;
100100
height: 4px;
101101
background-color: #d1d1d1;
102102
visibility: hidden;
103103
border-radius: 2px;
104104
}
105105

106-
&:hover .resizeChip {
106+
&:hover .horizontalResizeChip {
107107
visibility: visible;
108108
}
109109
}

0 commit comments

Comments
 (0)