Use `resized-content` in meta tag to standardize behaviour
Previously we were we were setting `overlaysContent = true` for Chrome only, which made it very hard for the same CSS to work well across all three major browsers. With `interactive-widget=resizes-content`, there sems to be better consistency, we can use bottom fixed-positioning in Firefox and Chrome for Android and a CSS workaround for Safari. See also https://developer.chrome.com/blog/viewport-resize-behavior
This commit is contained in:
parent
f2b4baff36
commit
5c702c8429
|
@ -8,19 +8,14 @@ export default class DComposerPosition extends Component {
|
|||
constructor() {
|
||||
super(...arguments);
|
||||
|
||||
if (!window.visualViewport) {
|
||||
return;
|
||||
}
|
||||
|
||||
const html = document.documentElement;
|
||||
|
||||
if (
|
||||
html.classList.contains("ios-device") ||
|
||||
html.classList.contains("mobile-device") ||
|
||||
html.classList.contains("ipados-device")
|
||||
) {
|
||||
window.addEventListener("scroll", this._correctScrollPosition);
|
||||
this._correctScrollPosition();
|
||||
|
||||
const editor = document.querySelector(".d-editor-input");
|
||||
editor?.addEventListener("touchmove", this._textareaTouchMove);
|
||||
}
|
||||
|
@ -29,10 +24,6 @@ export default class DComposerPosition extends Component {
|
|||
willDestroy() {
|
||||
super.willDestroy(...arguments);
|
||||
|
||||
if (!window.visualViewport) {
|
||||
return;
|
||||
}
|
||||
|
||||
const html = document.documentElement;
|
||||
|
||||
if (
|
||||
|
@ -69,7 +60,7 @@ export default class DComposerPosition extends Component {
|
|||
|
||||
_textareaTouchMove(event) {
|
||||
// This is an alternative to locking up the body
|
||||
// It stops scrolls from bubbling up to the body
|
||||
// It stops scrolling in the given element from bubbling up to the body
|
||||
// when the textarea does not have any content to scroll
|
||||
if (event.target) {
|
||||
const notScrollable =
|
||||
|
|
|
@ -5,13 +5,13 @@ import isZoomed from "discourse/lib/zoom-check";
|
|||
import discourseDebounce from "discourse-common/lib/debounce";
|
||||
import { bind } from "discourse-common/utils/decorators";
|
||||
|
||||
const FF_KEYBOARD_DETECT_THRESHOLD = 150;
|
||||
|
||||
export default class DVirtualHeight extends Component {
|
||||
@service site;
|
||||
@service capabilities;
|
||||
@service appEvents;
|
||||
|
||||
MIN_THRESHOLD = 120;
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
|
||||
|
@ -23,7 +23,6 @@ export default class DVirtualHeight extends Component {
|
|||
return;
|
||||
}
|
||||
|
||||
// TODO: Handle device rotation
|
||||
this.windowInnerHeight = window.innerHeight;
|
||||
|
||||
scheduleOnce("afterRender", this, this.debouncedOnViewportResize);
|
||||
|
@ -32,13 +31,6 @@ export default class DVirtualHeight extends Component {
|
|||
"resize",
|
||||
this.debouncedOnViewportResize
|
||||
);
|
||||
if ("virtualKeyboard" in navigator) {
|
||||
navigator.virtualKeyboard.overlaysContent = true;
|
||||
navigator.virtualKeyboard.addEventListener(
|
||||
"geometrychange",
|
||||
this.debouncedOnViewportResize
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
willDestroy() {
|
||||
|
@ -50,13 +42,6 @@ export default class DVirtualHeight extends Component {
|
|||
"resize",
|
||||
this.debouncedOnViewportResize
|
||||
);
|
||||
if ("virtualKeyboard" in navigator) {
|
||||
navigator.virtualKeyboard.overlaysContent = false;
|
||||
navigator.virtualKeyboard.removeEventListener(
|
||||
"geometrychange",
|
||||
this.debouncedOnViewportResize
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
setVH() {
|
||||
|
@ -64,15 +49,7 @@ export default class DVirtualHeight extends Component {
|
|||
return;
|
||||
}
|
||||
|
||||
let height;
|
||||
if ("virtualKeyboard" in navigator) {
|
||||
height =
|
||||
window.visualViewport.height -
|
||||
navigator.virtualKeyboard.boundingRect.height;
|
||||
} else {
|
||||
const activeWindow = window.visualViewport || window;
|
||||
height = activeWindow?.height || window.innerHeight;
|
||||
}
|
||||
const height = Math.round(window.visualViewport.height);
|
||||
|
||||
if (this.previousHeight && Math.abs(this.previousHeight - height) <= 1) {
|
||||
return false;
|
||||
|
@ -84,8 +61,6 @@ export default class DVirtualHeight extends Component {
|
|||
"--composer-vh",
|
||||
`${height / 100}px`
|
||||
);
|
||||
|
||||
document.documentElement.style.setProperty("--vvh", `${height}px`);
|
||||
}
|
||||
|
||||
@bind
|
||||
|
@ -104,26 +79,12 @@ export default class DVirtualHeight extends Component {
|
|||
const docEl = document.documentElement;
|
||||
|
||||
let keyboardVisible = false;
|
||||
if ("virtualKeyboard" in navigator) {
|
||||
if (navigator.virtualKeyboard.boundingRect.height > 0) {
|
||||
keyboardVisible = true;
|
||||
}
|
||||
} else if (this.capabilities.isFirefox && this.capabilities.isAndroid) {
|
||||
if (
|
||||
Math.abs(
|
||||
this.windowInnerHeight -
|
||||
Math.min(window.innerHeight, window.visualViewport.height)
|
||||
) > FF_KEYBOARD_DETECT_THRESHOLD
|
||||
) {
|
||||
keyboardVisible = true;
|
||||
}
|
||||
} else {
|
||||
let viewportWindowDiff =
|
||||
this.windowInnerHeight - window.visualViewport.height;
|
||||
const MIN_THRESHOLD = 20;
|
||||
if (viewportWindowDiff > MIN_THRESHOLD) {
|
||||
keyboardVisible = true;
|
||||
}
|
||||
|
||||
let viewportWindowDiff =
|
||||
this.windowInnerHeight - window.visualViewport.height;
|
||||
|
||||
if (viewportWindowDiff > this.MIN_THRESHOLD) {
|
||||
keyboardVisible = true;
|
||||
}
|
||||
|
||||
this.appEvents.trigger("keyboard-visibility-change", keyboardVisible);
|
||||
|
|
|
@ -1,14 +1,10 @@
|
|||
html.composer-open.not-mobile-device {
|
||||
html.composer-open {
|
||||
#main-outlet {
|
||||
padding-bottom: var(--composer-height);
|
||||
transition: padding-bottom 250ms ease;
|
||||
}
|
||||
}
|
||||
|
||||
html.composer-open {
|
||||
--d-min-composer-reply-height: 35vh;
|
||||
}
|
||||
|
||||
#reply-control {
|
||||
position: fixed;
|
||||
display: flex;
|
||||
|
@ -44,7 +40,7 @@ html.composer-open {
|
|||
}
|
||||
z-index: z("composer", "content");
|
||||
transition: height 0.2s, max-width 0.2s, padding-bottom 0.2s, top 0.2s,
|
||||
min-height 0.2s;
|
||||
transform 0.2s, min-height 0.2s;
|
||||
background-color: var(--secondary);
|
||||
box-shadow: var(--shadow-composer);
|
||||
|
||||
|
@ -71,7 +67,6 @@ html.composer-open {
|
|||
&.open {
|
||||
box-sizing: border-box;
|
||||
height: var(--composer-height);
|
||||
min-height: var(--d-min-composer-reply-height);
|
||||
max-height: calc(100vh - var(--header-offset, 4em));
|
||||
padding-bottom: var(--composer-ipad-padding);
|
||||
}
|
||||
|
@ -632,28 +627,17 @@ div.ac-wrap {
|
|||
}
|
||||
}
|
||||
|
||||
// The composer on mobile is fixed-positioned, same as on desktop because
|
||||
// we are using interactive-widget=resizes-content in the viewport meta tag
|
||||
.ipados-device,
|
||||
.mobile-device {
|
||||
// Let's have the composer be top-anchored for mobile and iPad.
|
||||
// Safari in iOS/iPad and Firefox/Android do not handle well a bottom:0 fixed-positioned element,
|
||||
// especially while the software keyboard is visible.
|
||||
#reply-control {
|
||||
bottom: unset;
|
||||
height: 0;
|
||||
z-index: -1;
|
||||
// these two properties below are equivalent to bottom: 0
|
||||
top: calc(var(--composer-vh, 1vh) * 100);
|
||||
transform: translateY(-100%);
|
||||
|
||||
&.open {
|
||||
z-index: z("mobile-composer");
|
||||
}
|
||||
|
||||
&.composer-action-edit,
|
||||
&.edit-title {
|
||||
height: calc(var(--composer-vh, 1vh) * 100);
|
||||
}
|
||||
|
||||
&.draft,
|
||||
&.saving {
|
||||
z-index: z("ipad-header-nav") + 1;
|
||||
|
@ -671,19 +655,6 @@ div.ac-wrap {
|
|||
// this prevents touch events bubbling up to the browser, i.e. accidental scrolls
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
// When an element (input, textearea) gets focus, iOS Safari tries to put it in the center
|
||||
// by scrolling and zooming. We handle zooming with a meta tag. We used to handle scrolling
|
||||
// using a complicated JS hack.
|
||||
//
|
||||
// However, iOS Safari doesn't scroll when the input has opacity of 0 or is clipped.
|
||||
// We use this quirk and temporarily hide the element on scroll and quickly show it again
|
||||
//
|
||||
// Source https://gist.github.com/kiding/72721a0553fa93198ae2bb6eefaa3299
|
||||
input:focus,
|
||||
textarea:focus {
|
||||
animation: blink_input_opacity_to_prevent_scrolling_when_focus 0.01s;
|
||||
}
|
||||
}
|
||||
|
||||
&.keyboard-visible #reply-control.open {
|
||||
|
@ -691,6 +662,32 @@ div.ac-wrap {
|
|||
}
|
||||
|
||||
&.composer-open .with-topic-progress {
|
||||
bottom: var(--d-min-composer-reply-height);
|
||||
bottom: calc(var(--composer-height));
|
||||
}
|
||||
}
|
||||
|
||||
// Safari in iOS/iPad does not handle well a bottom:0 fixed-positioned element,
|
||||
// especially while the software keyboard is visible, so we top-anchor it here
|
||||
// and shift it using transform
|
||||
.ipados-device,
|
||||
.ios-device {
|
||||
#reply-control {
|
||||
// the two properties below are equivalent to bottom: 0
|
||||
top: calc(var(--composer-vh, 1vh) * 100);
|
||||
transform: translateY(-100%);
|
||||
bottom: unset;
|
||||
}
|
||||
|
||||
// When an element (input, textearea) gets focus, iOS Safari tries to put it in the center
|
||||
// by scrolling and zooming. We handle zooming with a meta tag. We used to handle scrolling
|
||||
// using a complicated JS hack.
|
||||
//
|
||||
// However, iOS Safari doesn't scroll when the input has opacity of 0 or is clipped.
|
||||
// We use this quirk and temporarily hide the element on scroll and quickly show it again
|
||||
//
|
||||
// Source https://gist.github.com/kiding/72721a0553fa93198ae2bb6eefaa3299
|
||||
input:focus,
|
||||
textarea:focus {
|
||||
animation: blink_input_opacity_to_prevent_scrolling_when_focus 0.01s;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<%- end %>
|
||||
<%= discourse_theme_color_meta_tags %>
|
||||
<%= discourse_color_scheme_meta_tag %>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, user-scalable=yes, viewport-fit=cover, interactive-widget=resizes-visual">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, user-scalable=yes, viewport-fit=cover, interactive-widget=resizes-content">
|
||||
<%- if Discourse.base_path.present? %>
|
||||
<meta name="discourse-base-uri" content="<%= Discourse.base_path %>">
|
||||
<% end %>
|
||||
|
|
Loading…
Reference in New Issue