fix(mobile): mobile keyboard padding (#15365)

#### PR Dependency Tree


* **PR #15365** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Bug Fixes**
- Improved mobile keyboard and toolbar layout handling when the keyboard
overlays or resizes app content.
- Prevented incorrect extra spacing when Android applies keyboard insets
directly.
- Updated toolbar sizing and visibility states for smoother transitions.

- **Style**
- Adjusted mobile bottom spacing to account for keyboard height, safe
areas, and toolbar height.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
DarkSky
2026-07-28 13:02:38 +08:00
committed by GitHub
parent e7ec8a1032
commit b6fc0a2192
6 changed files with 42 additions and 31 deletions
@@ -41,8 +41,6 @@ export class AffineKeyboardToolbar extends SignalWatcher(
) {
static override styles = keyboardToolbarStyles;
private readonly _expanded$ = signal(false);
get std() {
return this.rootComponent.std;
}
@@ -52,13 +50,17 @@ export class AffineKeyboardToolbar extends SignalWatcher(
}
private get panelHeight() {
return this._expanded$.value
? `${
this.keyboard.staticHeight$.value !== 0
? this.keyboard.staticHeight$.value
: 330
}px`
: this.keyboard.appTabSafeArea$.value;
if (this.keyboard.visible$.value) {
return `${this.keyboard.height$.value}px`;
}
if (this.panelOpened) {
return `${
this.keyboard.staticHeight$.value !== 0
? this.keyboard.staticHeight$.value
: 330
}px`;
}
return this.keyboard.appTabSafeArea$.value;
}
/**
@@ -266,20 +268,13 @@ export class AffineKeyboardToolbar extends SignalWatcher(
super.connectedCallback();
this.setAttribute(RANGE_SYNC_EXCLUDE_ATTR, 'true');
// There are two cases that `_expanded$` will be true:
// 1. when virtual keyboard is opened, the panel need to be expanded and overlapped by the keyboard,
// so that the toolbar will be on the top of the keyboard.
// 2. the panel is opened, whether the keyboard is closed or not exists (e.g. a physical keyboard connected)
//
// There is one case that `_expanded$` will be false:
// 1. the panel is closed, and the keyboard is closed, the toolbar will be rendered at the bottom of the viewport
this._disposables.add(
effect(() => {
if (this.keyboard.visible$.value || this.panelOpened) {
this._expanded$.value = true;
} else {
this._expanded$.value = false;
}
this.toggleAttribute(
'data-keyboard-visible',
this.keyboard.visible$.value
);
this.toggleAttribute('data-panel-open', this.panelOpened);
})
);
@@ -149,6 +149,9 @@ framework.impl(VirtualKeyboardProvider, {
// even though the `keyboardWillShow` event is still triggered.
visible: info.keyboardHeight !== 0,
height: info.keyboardHeight - navBarHeight,
// SystemBars applies the IME inset to the WebView parent, so the
// keyboard no longer overlaps the web content on Android.
overlaysContent: false,
});
})().catch(console.error);
}),
@@ -45,15 +45,19 @@ export function KeyboardToolbarExtension(
override mounted() {
this._disposables.add(
affineVirtualKeyboardProvider.onChange(({ visible, height }) => {
batch(() => {
if (visible && this.staticHeight$.peek() !== height) {
this.staticHeight$.value = height;
}
this.visible$.value = visible;
this.height$.value = height;
});
})
affineVirtualKeyboardProvider.onChange(
({ visible, height, overlaysContent }) => {
const layoutHeight = overlaysContent === false ? 0 : height;
batch(() => {
if (visible && this.staticHeight$.peek() !== height) {
this.staticHeight$.value = height;
}
this.visible$.value = visible;
this.height$.value = layoutHeight;
});
}
)
);
}
@@ -3,6 +3,7 @@ import { createIdentifier } from '@toeverything/infra';
interface VirtualKeyboardInfo {
visible: boolean;
height: number;
overlaysContent?: boolean;
}
type VirtualKeyboardAction = {
@@ -20,8 +20,10 @@ export class VirtualKeyboardService extends Service {
private _observe() {
this.disposables.push(
this.virtualKeyboardProvider.onChange(info => {
const layoutHeight = info.overlaysContent === false ? 0 : info.height;
this.visible$.next(info.visible);
this.height$.next(info.height);
this.height$.next(layoutHeight);
setElementVars(document.body, {
[globalVars.appKeyboardHeight]: `${this.height$.value}px`,
@@ -22,6 +22,12 @@ globalStyle('body:has(>#app-tabs):not(:has(affine-keyboard-toolbar))', {
paddingBottom: globalVars.appTabSafeArea,
});
globalStyle('body:has(affine-keyboard-toolbar)', {
paddingBottom: `calc(${globalVars.appTabSafeArea} + 46px)`,
});
globalStyle('body:has(affine-keyboard-toolbar[data-keyboard-visible])', {
paddingBottom: `calc(${globalVars.appKeyboardHeight} + 46px)`,
});
globalStyle('body:has(affine-keyboard-toolbar[data-panel-open])', {
paddingBottom: `calc(${globalVars.appKeyboardStaticHeight} + 46px)`,
});
globalStyle('body:has(>#app-tabs) edgeless-toolbar-widget', {