FIX: improves position of post toolbar on iOS (#35574)

Since iOS 26 it seems the callout from iOS is following this heuristic:
position the callout where there's the most space. When it used to
favour positioning it above the selection.
This commit is contained in:
Joffrey JAFFEUX
2025-10-27 10:47:21 +01:00
committed by GitHub
parent fc791d2bf7
commit 2cd801bf31
@@ -1,5 +1,4 @@
import Component from "@glimmer/component";
import { cached } from "@glimmer/tracking";
import { action } from "@ember/object";
import { cancel } from "@ember/runloop";
import { service } from "@ember/service";
@@ -146,10 +145,12 @@ export default class PostTextSelection extends Component {
@bind
async showToolbar(cooked) {
const shouldRenderBelow = this.shouldRenderBelow();
const quoteState = this.computeQuoteState(cooked);
let offset = 3;
if (this.shouldRenderUnder) {
if (shouldRenderBelow) {
// on mobile, we ideally want to show the toolbar at the end of the selection
offset = 20;
@@ -171,10 +172,8 @@ export default class PostTextSelection extends Component {
identifier: "post-text-selection-toolbar",
component: PostTextSelectionToolbar,
inline: true,
placement: this.shouldRenderUnder ? "bottom-start" : "top-start",
fallbackPlacements: this.shouldRenderUnder
? ["bottom-end", "top-start"]
: ["bottom-start"],
placement: shouldRenderBelow ? "bottom-start" : "top-start",
fallbackPlacements: shouldRenderBelow ? ["bottom-end"] : ["top-end"],
offset,
trapTab: false,
closeOnScroll: false,
@@ -272,12 +271,28 @@ export default class PostTextSelection extends Component {
// on Desktop, shows the bar at the beginning of the selection
// on Mobile, shows the bar at the end of the selection
@cached
get shouldRenderUnder() {
shouldRenderBelow() {
const { isIOS, isAndroid, isOpera, isFirefox, touch } = this.capabilities;
if (isIOS) {
const rect = virtualElementFromTextRange().rect;
const spaceAbove = rect.top;
const spaceBelow = window.innerHeight - rect.bottom;
const safeAreaInset =
parseInt(
getComputedStyle(document.documentElement)
.getPropertyValue("--safe-area-inset-bottom")
.trim(),
10
) || 0;
// if we have more space above, ios will render its callout above
// so we should render ours, below
return spaceAbove >= spaceBelow - safeAreaInset;
}
return (
this.capabilities.isMobileDevice ||
isIOS ||
isAndroid ||
isOpera ||
(touch && isFirefox)