Skip to content

Commit 1f7dcce

Browse files
committed
feat: yasqe buttons are now more responsive, increases mobile friendlyness (related to #122)
1 parent 46df854 commit 1f7dcce

2 files changed

Lines changed: 233 additions & 16 deletions

File tree

packages/yasqe/src/index.ts

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ export class Yasqe extends CodeMirror {
6161
private queryBtn: HTMLButtonElement | undefined;
6262
private saveBtn: HTMLButtonElement | undefined;
6363
private fullscreenBtn: HTMLButtonElement | undefined;
64+
private hamburgerBtn: HTMLButtonElement | undefined;
65+
private hamburgerMenu: HTMLDivElement | undefined;
66+
private shareBtn: HTMLButtonElement | undefined;
6467
private isFullscreen: boolean = false;
6568
private horizontalResizeWrapper?: HTMLDivElement;
6669
private snippetsBar?: HTMLDivElement;
@@ -318,6 +321,7 @@ export class Yasqe extends CodeMirror {
318321
shareLinkWrapper.setAttribute("aria-label", "Share query");
319322
shareLinkWrapper.appendChild(shareIcon);
320323
buttons.appendChild(shareLinkWrapper);
324+
this.shareBtn = shareLinkWrapper;
321325
shareLinkWrapper.addEventListener("click", (event: MouseEvent) => showSharePopup(event));
322326
shareLinkWrapper.addEventListener("keydown", (event: KeyboardEvent) => {
323327
if (event.code === "Enter") {
@@ -609,6 +613,130 @@ export class Yasqe extends CodeMirror {
609613
this.fullscreenBtn.title = "Toggle fullscreen (F11)";
610614
this.fullscreenBtn.setAttribute("aria-label", "Toggle fullscreen");
611615
buttons.appendChild(this.fullscreenBtn);
616+
617+
/**
618+
* Draw hamburger menu button and dropdown (for mobile)
619+
*/
620+
this.hamburgerBtn = document.createElement("button");
621+
addClass(this.hamburgerBtn, "yasqe_hamburgerButton");
622+
const hamburgerIcon = document.createElement("i");
623+
addClass(hamburgerIcon, "fas");
624+
addClass(hamburgerIcon, "fa-bars");
625+
hamburgerIcon.setAttribute("aria-hidden", "true");
626+
this.hamburgerBtn.appendChild(hamburgerIcon);
627+
this.hamburgerBtn.title = "More options";
628+
this.hamburgerBtn.setAttribute("aria-label", "More options");
629+
this.hamburgerBtn.setAttribute("aria-expanded", "false");
630+
buttons.appendChild(this.hamburgerBtn);
631+
632+
// Create hamburger menu
633+
this.hamburgerMenu = document.createElement("div");
634+
this.hamburgerMenu.className = "yasqe_hamburgerMenu";
635+
buttons.appendChild(this.hamburgerMenu);
636+
637+
// Add menu items
638+
if (this.config.createShareableLink) {
639+
const shareItem = document.createElement("button");
640+
shareItem.className = "yasqe_hamburgerMenuItem";
641+
const shareIconMenu = document.createElement("i");
642+
addClass(shareIconMenu, "fas");
643+
addClass(shareIconMenu, "fa-share-nodes");
644+
shareIconMenu.setAttribute("aria-hidden", "true");
645+
shareItem.appendChild(shareIconMenu);
646+
const shareLabel = document.createElement("span");
647+
shareLabel.textContent = "Share";
648+
shareItem.appendChild(shareLabel);
649+
shareItem.onclick = () => {
650+
this.closeHamburgerMenu();
651+
this.shareBtn?.click();
652+
};
653+
this.hamburgerMenu.appendChild(shareItem);
654+
}
655+
656+
const saveItem = document.createElement("button");
657+
saveItem.className = "yasqe_hamburgerMenuItem";
658+
const saveIconMenu = document.createElement("i");
659+
addClass(saveIconMenu, "fas");
660+
addClass(saveIconMenu, "fa-save");
661+
saveIconMenu.setAttribute("aria-hidden", "true");
662+
saveItem.appendChild(saveIconMenu);
663+
const saveLabel = document.createElement("span");
664+
saveLabel.textContent = "Save";
665+
saveItem.appendChild(saveLabel);
666+
saveItem.onclick = () => {
667+
this.closeHamburgerMenu();
668+
this.emit("saveManagedQuery");
669+
};
670+
this.hamburgerMenu.appendChild(saveItem);
671+
672+
if (this.config.showFormatButton) {
673+
const formatItem = document.createElement("button");
674+
formatItem.className = "yasqe_hamburgerMenuItem";
675+
const formatIconMenu = document.createElement("i");
676+
addClass(formatIconMenu, "fas");
677+
addClass(formatIconMenu, "fa-align-left");
678+
formatIconMenu.setAttribute("aria-hidden", "true");
679+
formatItem.appendChild(formatIconMenu);
680+
const formatLabel = document.createElement("span");
681+
formatLabel.textContent = "Format";
682+
formatItem.appendChild(formatLabel);
683+
formatItem.onclick = () => {
684+
this.closeHamburgerMenu();
685+
this.format();
686+
};
687+
this.hamburgerMenu.appendChild(formatItem);
688+
}
689+
690+
const fullscreenItem = document.createElement("button");
691+
fullscreenItem.className = "yasqe_hamburgerMenuItem";
692+
const fullscreenIconMenu = document.createElement("i");
693+
addClass(fullscreenIconMenu, "fas");
694+
addClass(fullscreenIconMenu, "fa-expand");
695+
fullscreenIconMenu.setAttribute("aria-hidden", "true");
696+
fullscreenItem.appendChild(fullscreenIconMenu);
697+
const fullscreenLabel = document.createElement("span");
698+
fullscreenLabel.textContent = "Fullscreen";
699+
fullscreenItem.appendChild(fullscreenLabel);
700+
fullscreenItem.onclick = () => {
701+
this.closeHamburgerMenu();
702+
this.toggleFullscreen();
703+
};
704+
this.hamburgerMenu.appendChild(fullscreenItem);
705+
706+
// Toggle hamburger menu
707+
this.hamburgerBtn.onclick = (e) => {
708+
e.stopPropagation();
709+
this.toggleHamburgerMenu();
710+
};
711+
712+
// Close hamburger menu when clicking outside
713+
document.addEventListener("click", (e) => {
714+
if (
715+
this.hamburgerMenu &&
716+
this.hamburgerBtn &&
717+
!this.hamburgerMenu.contains(e.target as Node) &&
718+
!this.hamburgerBtn.contains(e.target as Node)
719+
) {
720+
this.closeHamburgerMenu();
721+
}
722+
});
723+
}
724+
725+
private toggleHamburgerMenu() {
726+
if (!this.hamburgerMenu || !this.hamburgerBtn) return;
727+
const isActive = this.hamburgerMenu.classList.contains("active");
728+
if (isActive) {
729+
this.closeHamburgerMenu();
730+
} else {
731+
addClass(this.hamburgerMenu, "active");
732+
this.hamburgerBtn.setAttribute("aria-expanded", "true");
733+
}
734+
}
735+
736+
private closeHamburgerMenu() {
737+
if (!this.hamburgerMenu || !this.hamburgerBtn) return;
738+
removeClass(this.hamburgerMenu, "active");
739+
this.hamburgerBtn.setAttribute("aria-expanded", "false");
612740
}
613741
public toggleFullscreen() {
614742
this.isFullscreen = !this.isFullscreen;

packages/yasqe/src/scss/buttons.scss

Lines changed: 105 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
$queryButtonHeight: 40px;
44

55
.yasqe_btn {
6-
color: #333;
6+
color: var(--yasgui-text-primary, #333);
77
border: 1px solid transparent;
8-
background-color: #fff;
9-
border-color: #ccc;
8+
background-color: var(--yasgui-bg-primary, #fff);
9+
border-color: var(--yasgui-border-color, #ccc);
1010
border-width: 1px;
1111
display: inline-block;
1212
text-align: center;
@@ -33,8 +33,8 @@
3333

3434
&:hover {
3535
outline: 0;
36-
background-color: #ebebeb;
37-
border-color: #adadad;
36+
background-color: var(--yasgui-bg-secondary, #ebebeb);
37+
border-color: var(--yasgui-text-muted, #adadad);
3838
}
3939

4040
&:focus,
@@ -46,10 +46,10 @@
4646
}
4747

4848
&.btn_icon:focus {
49-
color: #333;
49+
color: var(--yasgui-text-primary, #333);
5050
border: 1px solid transparent;
51-
background-color: #fff;
52-
border-color: #ccc;
51+
background-color: var(--yasgui-bg-primary, #fff);
52+
border-color: var(--yasgui-border-color, #ccc);
5353
}
5454

5555
&.yasqe_btn-sm {
@@ -77,7 +77,7 @@
7777
margin-left: 8px;
7878
height: 36px;
7979
width: 36px;
80-
color: #505050;
80+
color: var(--yasgui-text-secondary, #505050);
8181

8282
i {
8383
font-size: 20px;
@@ -96,8 +96,8 @@
9696
position: absolute;
9797
padding: 12px;
9898
margin-left: 0px;
99-
background-color: #fff;
100-
border: 1px solid #e3e3e3;
99+
background-color: var(--yasgui-bg-primary, #fff);
100+
border: 1px solid var(--yasgui-border-color, #e3e3e3);
101101
border-radius: 4px;
102102
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
103103
width: auto;
@@ -111,7 +111,7 @@
111111
.yasqe_sharePopup_title {
112112
font-weight: 600;
113113
font-size: 14px;
114-
color: #333;
114+
color: var(--yasgui-text-primary, #333);
115115
margin-bottom: 4px;
116116
}
117117

@@ -301,7 +301,7 @@
301301
margin-left: 8px;
302302
height: 36px;
303303
width: 36px;
304-
color: #505050;
304+
color: var(--yasgui-text-secondary, #505050);
305305

306306
i {
307307
font-size: 20px;
@@ -323,7 +323,7 @@
323323
margin-left: 8px;
324324
height: 36px;
325325
width: 36px;
326-
color: #505050;
326+
color: var(--yasgui-text-secondary, #505050);
327327

328328
i {
329329
font-size: 20px;
@@ -345,7 +345,7 @@
345345
margin-left: 8px;
346346
height: 36px;
347347
width: 36px;
348-
color: #505050;
348+
color: var(--yasgui-text-secondary, #505050);
349349

350350
i {
351351
font-size: 20px;
@@ -359,6 +359,95 @@
359359
color: #337ab7;
360360
}
361361
}
362+
363+
.yasqe_hamburgerButton {
364+
display: none;
365+
align-items: center;
366+
justify-content: center;
367+
border: none;
368+
background: none;
369+
cursor: pointer;
370+
padding: 6px;
371+
margin-left: 8px;
372+
height: 36px;
373+
width: 36px;
374+
color: var(--yasgui-text-secondary, #505050);
375+
376+
i {
377+
font-size: 20px;
378+
}
379+
380+
&:hover {
381+
color: #337ab7;
382+
}
383+
384+
&[aria-expanded="true"] {
385+
color: #337ab7;
386+
}
387+
}
388+
389+
.yasqe_hamburgerMenu {
390+
display: none;
391+
position: absolute;
392+
bottom: 46px;
393+
right: 0;
394+
background-color: var(--yasgui-bg-primary, #fff);
395+
border: 1px solid var(--yasgui-border-color, #e3e3e3);
396+
border-radius: 4px;
397+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
398+
padding: 4px;
399+
min-width: 180px;
400+
z-index: 10000;
401+
402+
&.active {
403+
display: block;
404+
}
405+
406+
.yasqe_hamburgerMenuItem {
407+
display: flex;
408+
align-items: center;
409+
gap: 10px;
410+
width: 100%;
411+
padding: 10px 12px;
412+
border: none;
413+
background: none;
414+
text-align: left;
415+
cursor: pointer;
416+
font-size: 14px;
417+
color: var(--yasgui-text-primary, #333);
418+
border-radius: 2px;
419+
420+
i {
421+
font-size: 16px;
422+
width: 20px;
423+
color: var(--yasgui-text-secondary, #505050);
424+
}
425+
426+
&:hover {
427+
background-color: var(--yasgui-bg-secondary, #f0f0f0);
428+
}
429+
430+
&:active {
431+
background-color: var(--yasgui-bg-tertiary, #e0e0e0);
432+
}
433+
}
434+
}
435+
}
436+
437+
// Responsive: Show hamburger menu on smaller screens
438+
@media (max-width: 768px) {
439+
.yasqe_buttons {
440+
.yasqe_share,
441+
.yasqe_saveButton,
442+
.yasqe_formatButton,
443+
.yasqe_fullscreenButton {
444+
display: none !important;
445+
}
446+
447+
.yasqe_hamburgerButton {
448+
display: inline-flex;
449+
}
450+
}
362451
}
363452

364453
&.fullscreen {
@@ -368,7 +457,7 @@
368457
right: 0;
369458
bottom: 0;
370459
z-index: 9998;
371-
background: white;
460+
background: var(--yasgui-bg-primary, white);
372461
margin: 0;
373462
padding-top: 100px;
374463

0 commit comments

Comments
 (0)