diff --git a/app/assets/stylesheets/popup.css b/app/assets/stylesheets/popup.css index 7ef87d85d..914d3fc9e 100644 --- a/app/assets/stylesheets/popup.css +++ b/app/assets/stylesheets/popup.css @@ -17,6 +17,16 @@ display: flex; } + &.orient-left { + inset-inline: auto 0; + transform: translateX(0); + } + + &.orient-right { + inset-inline: 0 auto; + transform: translateX(0); + } + &:has(.popup__footer) { --panel-padding: var(--block-space) var(--block-space) 0 var(--block-space); } diff --git a/app/javascript/controllers/dialog_controller.js b/app/javascript/controllers/dialog_controller.js index b1d9eee4a..9341f4c25 100644 --- a/app/javascript/controllers/dialog_controller.js +++ b/app/javascript/controllers/dialog_controller.js @@ -1,4 +1,5 @@ import { Controller } from "@hotwired/stimulus" +import { orient } from "helpers/orientation_helpers" export default class extends Controller { static targets = [ "dialog" ] @@ -17,7 +18,9 @@ export default class extends Controller { this.dialogTarget.showModal() } else { this.dialogTarget.show() + orient(this.dialogTarget) } + this.dialogTarget.setAttribute('aria-hidden', 'false') this.dispatch("show") } @@ -34,6 +37,7 @@ export default class extends Controller { this.dialogTarget.close() this.dialogTarget.setAttribute('aria-hidden', 'true') this.dialogTarget.blur() + orient(this.dialogTarget, false) } closeOnClickOutside({ target }) { diff --git a/app/javascript/helpers/orientation_helpers.js b/app/javascript/helpers/orientation_helpers.js new file mode 100644 index 000000000..4e3f1a3a5 --- /dev/null +++ b/app/javascript/helpers/orientation_helpers.js @@ -0,0 +1,25 @@ +const EDGE_THRESHOLD = 16 + +export function orient(el, orient = true) { + const directions = [ + ["orient-left", spaceOnRight], + ["orient-right", spaceOnLeft], + ["orient-top", spaceOnBottom] + ]; + + directions.forEach(([className, fn]) => + el.classList[orient && fn(el) < EDGE_THRESHOLD ? "add" : "remove"](className) + ); +} + +function spaceOnLeft(el) { + return el.getBoundingClientRect().left +} + +function spaceOnRight(el) { + return window.innerWidth - el.getBoundingClientRect().right +} + +function spaceOnBottom(el) { + return window.innerHeight - el.getBoundingClientRect().bottom +}