Updated context menu to swap position if outside window

refs https://github.com/TryGhost/Team/issues/2677

When opening a context menu close to the border of the window, the menu went outside the window. This PR updates the position mirror horizontally or vertically if the menu would go outside the window.
This commit is contained in:
Simon Backx 2023-04-14 11:04:11 +02:00
parent 352d7ee5e2
commit 20d2722f04
4 changed files with 58 additions and 4 deletions

View File

@ -564,3 +564,5 @@ add|ember-template-lint|no-action|271|58|271|58|5124558b018d5e90a3d203fd54c5e4ca
add|ember-template-lint|no-action|289|68|289|68|eaa96ff81a7c4b4743ca191655c017bd90549e96|1680566400000|1690934400000|1696118400000|app/components/modal-portal-settings.hbs
add|ember-template-lint|no-invalid-interactive|1|103|1|103|f5a46b2538fbf79a40f2683ff1151ca60e0fa0ca|1680652800000|1691020800000|1696204800000|app/components/gh-context-menu.hbs
add|ember-template-lint|no-invalid-interactive|5|53|5|53|9647ef6afba919b2af04fe551b0fdf0fb63be849|1680652800000|1691020800000|1696204800000|app/components/gh-context-menu.hbs
remove|ember-template-lint|no-invalid-interactive|1|103|1|103|f5a46b2538fbf79a40f2683ff1151ca60e0fa0ca|1680652800000|1691020800000|1696204800000|app/components/gh-context-menu.hbs
add|ember-template-lint|no-invalid-interactive|1|103|1|103|534029ab0ba1b74eff4a2f31c8b4dd9f1460316a|1681430400000|1691798400000|1696982400000|app/components/gh-context-menu.hbs

View File

@ -1,5 +1,5 @@
<div role="menu" class="gh-context-menu-container" {{did-insert this.setup}} data-open={{this.isOpen}} {{on "click" this.stopClicks}}>
<div class="gh-context-menu" style={{this.style}}>
<div class="gh-context-menu {{this.class}}" style={{this.style}}>
{{yield this this.selectionList}}
</div>
<div role="none" class="gh-context-menu-overlay" {{on "click" this.close}} {{on "contextmenu" this.onContextMenuOutside}}></div>

View File

@ -12,7 +12,10 @@ export default class GhContextMenu extends Component {
@tracked isOpen = false;
@tracked left = 0;
@tracked top = 0;
@tracked yPlacement = 'bottom';
@tracked xPlacement = 'right';
@tracked selectionList = new SelectionList();
element = null;
/**
* The current state of the context menu
@ -73,8 +76,13 @@ export default class GhContextMenu extends Component {
return `left: ${this.left}px; top: ${this.top}px;`;
}
get class() {
return `gh-placement-${this.yPlacement} gh-placement-${this.xPlacement}`;
}
@action
setup() {
setup(element) {
this.element = element;
const dropdownService = this.dropdown;
dropdownService.on('close', this, this.close);
dropdownService.on('toggle', this, this.toggle);
@ -82,6 +90,7 @@ export default class GhContextMenu extends Component {
willDestroy() {
super.willDestroy(...arguments);
this.element = null;
const dropdownService = this.dropdown;
dropdownService.off('close', this, this.close);
dropdownService.off('toggle', this, this.toggle);
@ -119,12 +128,43 @@ export default class GhContextMenu extends Component {
this.selectionList = options.selectionList;
}
this.calculatePlacement();
this.open();
} else {
this.close();
}
}
get listElement() {
return this.element?.firstElementChild?.firstElementChild;
}
calculatePlacement() {
if (!this.element || !this.listElement) {
return;
}
const windowHeight = window.innerHeight;
const windowWidth = window.innerWidth;
const menuHeight = this.listElement.offsetHeight;
const menuWidth = this.listElement.offsetWidth;
const padding = 10;
// Do we have enough place to place the menu below?
if (this.top + menuHeight + padding < windowHeight) {
this.yPlacement = 'bottom';
} else {
this.yPlacement = 'top';
}
// Do we have enough place to place the menu to the right?
if (this.left + menuWidth + padding < windowWidth) {
this.xPlacement = 'right';
} else {
this.xPlacement = 'left';
}
}
@action
stopClicks(event) {
event.stopPropagation();

View File

@ -351,13 +351,25 @@ Post context menu
.gh-posts-context-menu {
max-width: 160px !important;
border-radius: var(--border-radius);
box-shadow:
box-shadow:
0 0 2.3px rgba(0, 0, 0, 0.028),
0 3.8px 7.8px rgba(0, 0, 0, 0.042),
0 17px 35px -7px rgba(0, 0, 0, 0.11)
;
}
.gh-placement-top .gh-posts-context-menu {
transform: translateY(-100%);
}
.gh-placement-left .gh-posts-context-menu {
transform: translateX(-100%);
}
.gh-placement-top.gh-placement-left .gh-posts-context-menu {
transform: translate(-100%, -100%);
}
.gh-posts-context-menu li > button {
padding: 8px 16px;
}
@ -375,4 +387,4 @@ Post context menu
.gh-posts-context-menu li > button span svg path {
stroke-width: 2px;
}
}