Mobile fixes (#8832)
Some checks failed
Checks / main (push) Waiting to run
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
Dev / Test development (push) Waiting to run
Dev / Build Docker image (push) Blocked by required conditions
Dev / Check Docker build (Dockerfile) (push) Blocked by required conditions
Dev / Check Docker build (Dockerfile.alpine) (push) Blocked by required conditions
/ Check Docker build (Dockerfile) (push) Waiting to run
/ Check Docker build (Dockerfile.alpine) (push) Waiting to run
/ Build Docker images (Dockerfile, ubuntu-24.04-arm, linux/arm64) (push) Blocked by required conditions
/ Build Docker images (Dockerfile.alpine, ubuntu-latest, linux/amd64) (push) Blocked by required conditions
/ Build Docker images (Dockerfile.legacy, ubuntu-24.04-arm, linux/arm/v7) (push) Blocked by required conditions
/ Build Docker images (Dockerfile.legacy, ubuntu-24.04-arm, linux/arm/v8) (push) Blocked by required conditions
/ Merge manifest lists (push) Blocked by required conditions
playwright / E2E tests on linux-arm64 (push) Waiting to run
playwright / E2E tests on linux-x64 (push) Waiting to run
Deploy Documentation / Build and Deploy Documentation (push) Has been cancelled
Deploy web clipper extension / Build web clipper extension (push) Has been cancelled

This commit is contained in:
Elian Doran 2026-02-25 23:17:50 +02:00 committed by GitHub
commit 6a3c4fec98
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 48 additions and 31 deletions

View File

@ -2630,7 +2630,7 @@ iframe.print-iframe {
}
}
#root-widget.virtual-keyboard-opened .note-split:not(.active) {
body:not(.ios) #root-widget.virtual-keyboard-opened .note-split:not(.active) {
max-height: 80px;
opacity: 0.4;
}

View File

@ -1,7 +1,6 @@
import "./NoteDetail.css";
import clsx from "clsx";
import { note } from "mermaid/dist/rendering-util/rendering-elements/shapes/note.js";
import { isValidElement, VNode } from "preact";
import { useEffect, useRef, useState } from "preact/hooks";

View File

@ -5,7 +5,7 @@
align-items: center;
position: absolute;
width: 100%;
top: 20px;
top: calc(env(safe-area-inset-top) + 20px);
pointer-events: none;
contain: none;
}

View File

@ -3,7 +3,7 @@ import { LOCALES } from "@triliumnext/commons";
import { EventData } from "../../components/app_context.js";
import { getEnabledExperimentalFeatureIds } from "../../services/experimental_features.js";
import options from "../../services/options.js";
import utils, { isMobile } from "../../services/utils.js";
import utils, { isIOS, isMobile } from "../../services/utils.js";
import { readCssVar } from "../../utils/css-var.js";
import type BasicWidget from "../basic_widget.js";
import FlexContainer from "./flex_container.js";
@ -19,9 +19,12 @@ import FlexContainer from "./flex_container.js";
*/
export default class RootContainer extends FlexContainer<BasicWidget> {
private originalWindowHeight: number;
constructor(isHorizontalLayout: boolean) {
super(isHorizontalLayout ? "column" : "row");
this.originalWindowHeight = window.innerHeight ?? 0;
this.id("root-widget");
this.css("height", "100dvh");
}
@ -31,6 +34,7 @@ export default class RootContainer extends FlexContainer<BasicWidget> {
window.visualViewport?.addEventListener("resize", () => this.#onMobileResize());
}
this.#setDeviceSpecificClasses();
this.#setMaxContentWidth();
this.#setMotion();
this.#setShadows();
@ -65,7 +69,7 @@ export default class RootContainer extends FlexContainer<BasicWidget> {
#onMobileResize() {
const viewportHeight = window.visualViewport?.height ?? window.innerHeight;
const windowHeight = window.innerHeight;
const windowHeight = Math.max(window.innerHeight, this.originalWindowHeight); // inner height changes when keyboard is opened, we need to compare with the original height to detect it.
// If viewport is significantly smaller, keyboard is likely open
const isKeyboardOpened = windowHeight - viewportHeight > 150;
@ -117,6 +121,12 @@ export default class RootContainer extends FlexContainer<BasicWidget> {
document.body.dir = correspondingLocale?.rtl ? "rtl" : "ltr";
}
#setDeviceSpecificClasses() {
if (isIOS()) {
document.body.classList.add("ios");
}
}
#initPWATopbarColor() {
if (!utils.isPWA()) return;
const tracker = $("#background-color-tracker");

View File

@ -1,11 +1,11 @@
import FlexContainer from "./containers/flex_container.js";
import utils from "../services/utils.js";
import attributeService from "../services/attributes.js";
import type BasicWidget from "./basic_widget.js";
import type { EventData } from "../components/app_context.js";
import type NoteContext from "../components/note_context.js";
import type FNote from "../entities/fnote.js";
import attributeService from "../services/attributes.js";
import { getLocaleById } from "../services/i18n.js";
import utils from "../services/utils.js";
import type BasicWidget from "./basic_widget.js";
import FlexContainer from "./containers/flex_container.js";
export default class NoteWrapperWidget extends FlexContainer<BasicWidget> {
@ -43,11 +43,16 @@ export default class NoteWrapperWidget extends FlexContainer<BasicWidget> {
refresh() {
const isHiddenExt = this.isHiddenExt(); // preserve through class reset
const isActive = this.$widget.hasClass("active");
this.$widget.removeClass();
this.toggleExt(!isHiddenExt);
if (isActive) {
this.$widget.addClass("active");
}
this.$widget.addClass("component note-split");
const note = this.noteContext?.note;
@ -92,7 +97,7 @@ export default class NoteWrapperWidget extends FlexContainer<BasicWidget> {
#hasBackgroundEffects(note: FNote): boolean {
const MIME_TYPES_WITH_BACKGROUND_EFFECTS = [
"application/pdf"
]
];
const COLLECTIONS_WITH_BACKGROUND_EFFECTS = [
"grid",

View File

@ -2,22 +2,14 @@ body.mobile {
.classic-toolbar-outer-container {
contain: none !important;
}
.classic-toolbar-outer-container.visible {
height: 38px;
background-color: var(--main-background-color);
position: relative;
overflow: visible;
flex-shrink: 0;
}
#root-widget.virtual-keyboard-opened .classic-toolbar-outer-container.ios {
position: absolute;
inset-inline-start: 0;
inset-inline-end: 0;
bottom: 0;
}
.classic-toolbar-widget {
position: absolute;
bottom: 0;
@ -28,27 +20,27 @@ body.mobile {
display: flex;
align-items: flex-end;
user-select: none;
touch-action: pan-x;
scrollbar-width: 0 !important;
}
.classic-toolbar-widget::-webkit-scrollbar:horizontal {
height: 0 !important;
}
.classic-toolbar-widget.dropdown-active {
height: 50vh;
}
.classic-toolbar-widget .ck.ck-toolbar {
--ck-color-toolbar-background: transparent;
--ck-color-toolbar-background: var(--main-background-color);
--ck-color-button-default-background: transparent;
--ck-color-button-default-disabled-background: transparent;
position: absolute;
background-color: transparent;
border: none;
}
.classic-toolbar-widget .ck.ck-button.ck-disabled {
opacity: 0.3;
}
}
}

View File

@ -66,11 +66,22 @@ export default function MobileEditorToolbar({ inPopupEditor }: MobileEditorToolb
}
function usePositioningOniOS(enabled: boolean, wrapperRef: MutableRef<HTMLDivElement | null>) {
// Capture the baseline offset (Safari nav bar height) before the keyboard opens.
const baselineOffset = useRef(window.innerHeight - (window.visualViewport?.height ?? window.innerHeight));
const adjustPosition = useCallback(() => {
if (!wrapperRef.current) return;
const bottom = window.innerHeight - (window.visualViewport?.height || 0);
wrapperRef.current.style.bottom = `${bottom}px`;
}, []);
const viewport = window.visualViewport;
if (!viewport) return;
// Subtract the baseline so only the keyboard's contribution remains.
const bottom = window.innerHeight - viewport.height - viewport.offsetTop;
if (bottom - baselineOffset.current <= 0) {
// Keyboard is hidden — clear the inline style so CSS controls positioning.
wrapperRef.current.style.removeProperty("bottom");
} else {
wrapperRef.current.style.bottom = `${bottom}px`;
}
}, [ wrapperRef ]);
useEffect(() => {
if (!isIOS() || !enabled) return;
@ -82,7 +93,7 @@ function usePositioningOniOS(enabled: boolean, wrapperRef: MutableRef<HTMLDivEle
window.visualViewport?.removeEventListener("resize", adjustPosition);
window.removeEventListener("scroll", adjustPosition);
};
}, [ enabled ]);
}, [ enabled, adjustPosition ]);
}
/**