DEV: Address composer mobile positioning issues
This commit is contained in:
parent
7f72e602a9
commit
f67f4b6dfc
|
@ -3,7 +3,6 @@ import { cancel, schedule, throttle } from "@ember/runloop";
|
||||||
import { classNameBindings } from "@ember-decorators/component";
|
import { classNameBindings } from "@ember-decorators/component";
|
||||||
import { observes } from "@ember-decorators/object";
|
import { observes } from "@ember-decorators/object";
|
||||||
import { headerOffset } from "discourse/lib/offset-calculator";
|
import { headerOffset } from "discourse/lib/offset-calculator";
|
||||||
import positioningWorkaround from "discourse/lib/safari-hacks";
|
|
||||||
import { isiPad } from "discourse/lib/utilities";
|
import { isiPad } from "discourse/lib/utilities";
|
||||||
import Composer from "discourse/models/composer";
|
import Composer from "discourse/models/composer";
|
||||||
import discourseDebounce from "discourse-common/lib/debounce";
|
import discourseDebounce from "discourse-common/lib/debounce";
|
||||||
|
@ -68,13 +67,6 @@ export default class ComposerBody extends Component {
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@observes("composeState")
|
|
||||||
disableFullscreen() {
|
|
||||||
if (this.composeState !== Composer.OPEN && positioningWorkaround.blur) {
|
|
||||||
positioningWorkaround.blur();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setupComposerResizeEvents() {
|
setupComposerResizeEvents() {
|
||||||
this.origComposerSize = 0;
|
this.origComposerSize = 0;
|
||||||
this.lastMousePos = 0;
|
this.lastMousePos = 0;
|
||||||
|
@ -184,8 +176,6 @@ export default class ComposerBody extends Component {
|
||||||
triggerOpen();
|
triggerOpen();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
positioningWorkaround(this.element);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
willDestroyElement() {
|
willDestroyElement() {
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import Component from "@glimmer/component";
|
import Component from "@glimmer/component";
|
||||||
import { cancel, scheduleOnce } from "@ember/runloop";
|
import { cancel, scheduleOnce } from "@ember/runloop";
|
||||||
import { service } from "@ember/service";
|
import { service } from "@ember/service";
|
||||||
import { clearAllBodyScrollLocks } from "discourse/lib/body-scroll-lock";
|
import { clearAllBodyScrollLocks, disableBodyScroll } from "discourse/lib/body-scroll-lock";
|
||||||
import isZoomed from "discourse/lib/zoom-check";
|
import isZoomed from "discourse/lib/zoom-check";
|
||||||
import discourseDebounce from "discourse-common/lib/debounce";
|
import discourseDebounce from "discourse-common/lib/debounce";
|
||||||
import { bind } from "discourse-common/utils/decorators";
|
import { bind } from "discourse-common/utils/decorators";
|
||||||
|
|
||||||
const KEYBOARD_DETECT_THRESHOLD = 150;
|
const FF_KEYBOARD_DETECT_THRESHOLD = 150;
|
||||||
|
|
||||||
export default class DVirtualHeight extends Component {
|
export default class DVirtualHeight extends Component {
|
||||||
@service site;
|
@service site;
|
||||||
|
@ -95,6 +95,7 @@ export default class DVirtualHeight extends Component {
|
||||||
@bind
|
@bind
|
||||||
onViewportResize() {
|
onViewportResize() {
|
||||||
this.setVH();
|
this.setVH();
|
||||||
|
const docEl = document.documentElement;
|
||||||
|
|
||||||
let keyboardVisible = false;
|
let keyboardVisible = false;
|
||||||
if ("virtualKeyboard" in navigator) {
|
if ("virtualKeyboard" in navigator) {
|
||||||
|
@ -106,7 +107,7 @@ export default class DVirtualHeight extends Component {
|
||||||
Math.abs(
|
Math.abs(
|
||||||
this.windowInnerHeight -
|
this.windowInnerHeight -
|
||||||
Math.min(window.innerHeight, window.visualViewport.height)
|
Math.min(window.innerHeight, window.visualViewport.height)
|
||||||
) > KEYBOARD_DETECT_THRESHOLD
|
) > FF_KEYBOARD_DETECT_THRESHOLD
|
||||||
) {
|
) {
|
||||||
keyboardVisible = true;
|
keyboardVisible = true;
|
||||||
}
|
}
|
||||||
|
@ -121,7 +122,7 @@ export default class DVirtualHeight extends Component {
|
||||||
// adds bottom padding when using a hardware keyboard and the accessory bar is visible
|
// adds bottom padding when using a hardware keyboard and the accessory bar is visible
|
||||||
// accessory bar height is 55px, using 75 allows a small buffer
|
// accessory bar height is 55px, using 75 allows a small buffer
|
||||||
if (this.capabilities.isIpadOS) {
|
if (this.capabilities.isIpadOS) {
|
||||||
document.documentElement.style.setProperty(
|
docEl.style.setProperty(
|
||||||
"--composer-ipad-padding",
|
"--composer-ipad-padding",
|
||||||
`${viewportWindowDiff < 75 ? viewportWindowDiff : 0}px`
|
`${viewportWindowDiff < 75 ? viewportWindowDiff : 0}px`
|
||||||
);
|
);
|
||||||
|
@ -130,11 +131,20 @@ export default class DVirtualHeight extends Component {
|
||||||
|
|
||||||
this.appEvents.trigger("keyboard-visibility-change", keyboardVisible);
|
this.appEvents.trigger("keyboard-visibility-change", keyboardVisible);
|
||||||
|
|
||||||
keyboardVisible
|
if (keyboardVisible) {
|
||||||
? document.documentElement.classList.add("keyboard-visible")
|
docEl.classList.add("keyboard-visible");
|
||||||
: document.documentElement.classList.remove("keyboard-visible");
|
|
||||||
|
// disable body scroll in mobile composer
|
||||||
|
// we have to do this because we're positioning the composer with
|
||||||
|
// position: fixed and top: 0 and scrolling would move the composer halfway out of the viewport
|
||||||
|
// we can't use bottom: 0, it is very unreliable with keyboard visible
|
||||||
|
if (docEl.classList.contains("composer-open")) {
|
||||||
|
disableBodyScroll(document.querySelector("#reply-control"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!keyboardVisible) {
|
if (!keyboardVisible) {
|
||||||
|
docEl.classList.remove("keyboard-visible");
|
||||||
clearAllBodyScrollLocks();
|
clearAllBodyScrollLocks();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ import { schedule, scheduleOnce } from "@ember/runloop";
|
||||||
import { service } from "@ember/service";
|
import { service } from "@ember/service";
|
||||||
import MountWidget from "discourse/components/mount-widget";
|
import MountWidget from "discourse/components/mount-widget";
|
||||||
import offsetCalculator from "discourse/lib/offset-calculator";
|
import offsetCalculator from "discourse/lib/offset-calculator";
|
||||||
import { isWorkaroundActive } from "discourse/lib/safari-hacks";
|
|
||||||
import DiscourseURL from "discourse/lib/url";
|
import DiscourseURL from "discourse/lib/url";
|
||||||
import { cloak, uncloak } from "discourse/widgets/post-stream";
|
import { cloak, uncloak } from "discourse/widgets/post-stream";
|
||||||
import discourseDebounce from "discourse-common/lib/debounce";
|
import discourseDebounce from "discourse-common/lib/debounce";
|
||||||
|
@ -64,11 +63,7 @@ export default class ScrollingPostStream extends MountWidget {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (document.webkitFullscreenElement || document.fullscreenElement) {
|
||||||
isWorkaroundActive() ||
|
|
||||||
document.webkitFullscreenElement ||
|
|
||||||
document.fullscreenElement
|
|
||||||
) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,17 +3,12 @@ import { action } from "@ember/object";
|
||||||
import { alias } from "@ember/object/computed";
|
import { alias } from "@ember/object/computed";
|
||||||
import { scheduleOnce } from "@ember/runloop";
|
import { scheduleOnce } from "@ember/runloop";
|
||||||
import { classNameBindings } from "@ember-decorators/component";
|
import { classNameBindings } from "@ember-decorators/component";
|
||||||
import { isTesting } from "discourse-common/config/environment";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
import discourseLater from "discourse-common/lib/later";
|
|
||||||
import discourseComputed, { bind } from "discourse-common/utils/decorators";
|
|
||||||
|
|
||||||
const CSS_TRANSITION_DELAY = isTesting() ? 0 : 500;
|
@classNameBindings("docked")
|
||||||
|
|
||||||
@classNameBindings("docked", "withTransitions")
|
|
||||||
export default class TopicProgress extends Component {
|
export default class TopicProgress extends Component {
|
||||||
elementId = "topic-progress-wrapper";
|
elementId = "topic-progress-wrapper";
|
||||||
docked = false;
|
docked = false;
|
||||||
withTransitions = null;
|
|
||||||
progressPosition = null;
|
progressPosition = null;
|
||||||
|
|
||||||
@alias("topic.postStream") postStream;
|
@alias("topic.postStream") postStream;
|
||||||
|
@ -70,106 +65,20 @@ export default class TopicProgress extends Component {
|
||||||
didInsertElement() {
|
didInsertElement() {
|
||||||
super.didInsertElement(...arguments);
|
super.didInsertElement(...arguments);
|
||||||
|
|
||||||
this.appEvents
|
this.appEvents.on("topic:current-post-scrolled", this, this._topicScrolled);
|
||||||
.on("composer:resized", this, this._composerEvent)
|
|
||||||
.on("topic:current-post-scrolled", this, this._topicScrolled);
|
|
||||||
|
|
||||||
if (this.prevEvent) {
|
if (this.prevEvent) {
|
||||||
scheduleOnce("afterRender", this, this._topicScrolled, this.prevEvent);
|
scheduleOnce("afterRender", this, this._topicScrolled, this.prevEvent);
|
||||||
}
|
}
|
||||||
scheduleOnce("afterRender", this, this._startObserver);
|
|
||||||
|
|
||||||
// start CSS transitions a tiny bit later
|
|
||||||
// to avoid jumpiness on initial topic load
|
|
||||||
discourseLater(this._addCssTransitions, CSS_TRANSITION_DELAY);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
willDestroyElement() {
|
willDestroyElement() {
|
||||||
super.willDestroyElement(...arguments);
|
super.willDestroyElement(...arguments);
|
||||||
this._topicBottomObserver?.disconnect();
|
this.appEvents.off(
|
||||||
this.appEvents
|
"topic:current-post-scrolled",
|
||||||
.off("composer:resized", this, this._composerEvent)
|
this,
|
||||||
.off("topic:current-post-scrolled", this, this._topicScrolled);
|
this._topicScrolled
|
||||||
}
|
);
|
||||||
|
|
||||||
@bind
|
|
||||||
_addCssTransitions() {
|
|
||||||
if (this.isDestroying || this.isDestroyed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.set("withTransitions", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
_startObserver() {
|
|
||||||
if ("IntersectionObserver" in window) {
|
|
||||||
this._topicBottomObserver = this._setupObserver();
|
|
||||||
this._topicBottomObserver.observe(
|
|
||||||
document.querySelector("#topic-bottom")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_setupObserver() {
|
|
||||||
// minimum 50px here ensures element is not docked when
|
|
||||||
// scrolling down quickly, it causes post stream refresh loop
|
|
||||||
// on Android
|
|
||||||
const bottomIntersectionMargin =
|
|
||||||
document.querySelector("#reply-control")?.clientHeight || 50;
|
|
||||||
|
|
||||||
return new IntersectionObserver(this._intersectionHandler, {
|
|
||||||
threshold: 1,
|
|
||||||
rootMargin: `0px 0px -${bottomIntersectionMargin}px 0px`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_composerEvent() {
|
|
||||||
// reinitializing needed to account for composer height
|
|
||||||
// might be no longer necessary if IntersectionObserver API supports dynamic rootMargin
|
|
||||||
// see https://github.com/w3c/IntersectionObserver/issues/428
|
|
||||||
if ("IntersectionObserver" in window) {
|
|
||||||
this._topicBottomObserver?.disconnect();
|
|
||||||
this._startObserver();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@bind
|
|
||||||
_intersectionHandler(entries) {
|
|
||||||
if (!this.element || this.isDestroying || this.isDestroyed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const composerH =
|
|
||||||
document.querySelector("#reply-control")?.clientHeight || 0;
|
|
||||||
|
|
||||||
// on desktop, pin this element to the composer
|
|
||||||
// otherwise the grid layout will change too much when toggling the composer
|
|
||||||
// and jitter when the viewport is near the topic bottom
|
|
||||||
if (this.site.desktopView && composerH) {
|
|
||||||
this.set("docked", false);
|
|
||||||
this.element.style.setProperty("bottom", `${composerH}px`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entries[0].isIntersecting === true) {
|
|
||||||
this.set("docked", true);
|
|
||||||
this.element.style.removeProperty("bottom");
|
|
||||||
} else {
|
|
||||||
if (entries[0].boundingClientRect.top > 0) {
|
|
||||||
this.set("docked", false);
|
|
||||||
if (composerH === 0) {
|
|
||||||
const filteredPostsHeight =
|
|
||||||
document.querySelector(".posts-filtered-notice")?.clientHeight || 0;
|
|
||||||
filteredPostsHeight === 0
|
|
||||||
? this.element.style.removeProperty("bottom")
|
|
||||||
: this.element.style.setProperty(
|
|
||||||
"bottom",
|
|
||||||
`${filteredPostsHeight}px`
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
this.element.style.setProperty("bottom", `${composerH}px`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
click(e) {
|
click(e) {
|
||||||
|
|
|
@ -1,14 +1,5 @@
|
||||||
import positioningWorkaround from "discourse/lib/safari-hacks";
|
|
||||||
import { helperContext } from "discourse-common/lib/helpers";
|
|
||||||
|
|
||||||
export default function (element) {
|
export default function (element) {
|
||||||
const caps = helperContext().capabilities;
|
element.focus();
|
||||||
|
|
||||||
if (caps.isApple && positioningWorkaround.touchstartEvent) {
|
|
||||||
positioningWorkaround.touchstartEvent(element);
|
|
||||||
} else {
|
|
||||||
element.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
const len = element.value.length;
|
const len = element.value.length;
|
||||||
element.setSelectionRange(len, len);
|
element.setSelectionRange(len, len);
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
html.composer-open {
|
html.composer-open.not-mobile-device {
|
||||||
#main-outlet {
|
#main-outlet {
|
||||||
padding-bottom: var(--composer-height);
|
padding-bottom: var(--composer-height);
|
||||||
transition: padding-bottom 250ms ease;
|
transition: padding-bottom 250ms ease;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#reply-control {
|
#reply-control {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -71,10 +72,6 @@ html.composer-open {
|
||||||
height: var(--composer-height);
|
height: var(--composer-height);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.closed {
|
|
||||||
height: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.draft,
|
&.draft,
|
||||||
&.saving {
|
&.saving {
|
||||||
height: 40px !important;
|
height: 40px !important;
|
||||||
|
@ -613,36 +610,6 @@ div.ac-wrap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
body.ios-safari-composer-hacks {
|
|
||||||
#main-outlet,
|
|
||||||
header,
|
|
||||||
.grippie,
|
|
||||||
html:not(.fullscreen-composer) & .toggle-fullscreen {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#reply-control {
|
|
||||||
top: 0px;
|
|
||||||
&.open {
|
|
||||||
height: calc(var(--composer-vh, 1vh) * 100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
body:not(.ios-safari-composer-hacks) {
|
|
||||||
#reply-control.open {
|
|
||||||
--min-height: 255px;
|
|
||||||
min-height: var(--min-height);
|
|
||||||
max-height: calc(100vh - var(--header-offset, 4em));
|
|
||||||
&.composer-action-reply {
|
|
||||||
// we can let the reply composer get a little shorter
|
|
||||||
min-height: calc(var(--min-height) - 4em);
|
|
||||||
}
|
|
||||||
padding-bottom: var(--composer-ipad-padding);
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.toggle-preview {
|
.toggle-preview {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
transition: all 0.33s ease-out;
|
transition: all 0.33s ease-out;
|
||||||
|
@ -659,3 +626,28 @@ body:not(.ios-safari-composer-hacks) {
|
||||||
.draft-error {
|
.draft-error {
|
||||||
color: var(--danger);
|
color: var(--danger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes blink_input_opacity_to_prevent_scrolling_when_focus {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
.ios-device #reply-control {
|
||||||
|
input:focus,
|
||||||
|
textarea:focus {
|
||||||
|
animation: blink_input_opacity_to_prevent_scrolling_when_focus 0.01s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -16,10 +16,6 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
html.keyboard-visible body.ios-safari-composer-hacks & {
|
|
||||||
height: calc(var(--composer-vh, 1vh) * 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
&__container {
|
&__container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
|
@ -41,20 +41,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
#topic-progress-wrapper {
|
#topic-progress-wrapper {
|
||||||
position: fixed;
|
|
||||||
&.docked {
|
&.docked {
|
||||||
position: initial;
|
|
||||||
|
|
||||||
.toggle-admin-menu {
|
.toggle-admin-menu {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bottom: 0;
|
|
||||||
html:not(.footer-nav-visible) & {
|
|
||||||
bottom: env(safe-area-inset-bottom);
|
|
||||||
}
|
|
||||||
|
|
||||||
right: 10px;
|
right: 10px;
|
||||||
z-index: z("timeline");
|
z-index: z("timeline");
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -67,14 +59,6 @@
|
||||||
border: 0;
|
border: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.with-transitions {
|
|
||||||
transition: bottom 0.2s, margin-bottom 0.2s;
|
|
||||||
|
|
||||||
#topic-progress .bg {
|
|
||||||
transition: width 0.5s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(.docked) {
|
&:not(.docked) {
|
||||||
@media screen and (min-width: $reply-area-max-width) {
|
@media screen and (min-width: $reply-area-max-width) {
|
||||||
right: calc(50%); // right side of composer
|
right: calc(50%); // right side of composer
|
||||||
|
|
|
@ -117,6 +117,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.with-topic-progress {
|
||||||
|
position: sticky;
|
||||||
|
bottom: 0px;
|
||||||
|
z-index: z("timeline");
|
||||||
|
}
|
||||||
|
|
||||||
.topic-status-info,
|
.topic-status-info,
|
||||||
.topic-timer-info {
|
.topic-timer-info {
|
||||||
border-top: 1px solid var(--primary-low);
|
border-top: 1px solid var(--primary-low);
|
||||||
|
|
|
@ -19,9 +19,10 @@ html.footer-nav-visible {
|
||||||
padding-bottom: 0px;
|
padding-bottom: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#topic-progress-wrapper:not(.docked) {
|
.with-topic-progress {
|
||||||
margin-bottom: calc(var(--footer-nav-height) + env(safe-area-inset-bottom));
|
bottom: var(--footer-nav-height);
|
||||||
}
|
}
|
||||||
|
|
||||||
.posts-filtered-notice {
|
.posts-filtered-notice {
|
||||||
transition: all linear 0.1s;
|
transition: all linear 0.1s;
|
||||||
bottom: calc(var(--footer-nav-height) + env(safe-area-inset-bottom));
|
bottom: calc(var(--footer-nav-height) + env(safe-area-inset-bottom));
|
||||||
|
|
|
@ -7,7 +7,16 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
#reply-control {
|
#reply-control {
|
||||||
z-index: z("mobile-composer");
|
// We override values set in base/compose.scss so that on mobile, the composer is top-anchored.
|
||||||
|
// This is not ideal, but it is much better than bottom-anchoring.
|
||||||
|
// Mobile browsers (especially Safari and Firefox/Android) do not handle well
|
||||||
|
// a bottom/fixed-positioned element, especially while the software keyboard is visible.
|
||||||
|
top: 0;
|
||||||
|
// With height and z-index: -1, the composer jitters a lot less
|
||||||
|
height: 100%;
|
||||||
|
z-index: -1;
|
||||||
|
max-height: unset;
|
||||||
|
min-height: unset;
|
||||||
|
|
||||||
.reply-area {
|
.reply-area {
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
|
@ -16,15 +25,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&.open {
|
&.open {
|
||||||
height: 250px;
|
display: flex;
|
||||||
&.edit-title {
|
z-index: z("mobile-composer");
|
||||||
height: 100%;
|
height: calc(var(--composer-vh, 1vh) * 100);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.keyboard-visible &.open {
|
.keyboard-visible &.open {
|
||||||
top: 0px;
|
|
||||||
height: calc(var(--composer-vh, 1vh) * 100);
|
|
||||||
.reply-area {
|
.reply-area {
|
||||||
padding-bottom: 6px;
|
padding-bottom: 6px;
|
||||||
}
|
}
|
||||||
|
@ -64,6 +70,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&.draft {
|
&.draft {
|
||||||
|
top: calc(100% - 40px);
|
||||||
z-index: z("footer-nav") + 1;
|
z-index: z("footer-nav") + 1;
|
||||||
padding-bottom: env(safe-area-inset-bottom);
|
padding-bottom: env(safe-area-inset-bottom);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue