Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions web/fizzy.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,15 +142,22 @@ export function keyboardInset(win, send, canvas = null, owns = () => true) {
const vv = win.visualViewport;
const viewport = win.document?.querySelector('meta[name="viewport"]')?.content ?? "";
const overlays = /(?:^|[,;])\s*interactive-widget\s*=\s*overlays-content\s*(?:[,;]|$)/i.test(viewport);
// Only phones and tablets have a soft keyboard. On a desktop, a window shrinking after
// focusin would otherwise read as one and pin the canvas height to the stale focusin rect.
const softKeyboard = win.matchMedia?.("(pointer: coarse) and (hover: none)").matches ?? true;
// A resizing viewport and VK geometry can use different origins. Measure only the
// visual viewport unless the page explicitly requests an overlay (or has no VV API).
const vk = overlays || !vv ? win.navigator?.virtualKeyboard : null;
const vk = softKeyboard && (overlays || !vv) ? win.navigator?.virtualKeyboard : null;
const keyboardTop = () => {
if (!softKeyboard) return Infinity;
if (!vk) return vv ? vv.height + vv.offsetTop : win.innerHeight;
const { y, height } = vk.boundingRect;
return height ? y ?? win.innerHeight - height : Infinity;
};
const update = () => {
const current = rect();
const base = rest ?? current;
const edge = vk ? (vk.boundingRect.height ? vk.boundingRect.y ?? win.innerHeight - vk.boundingRect.height : Infinity)
: vv ? vv.height + vv.offsetTop : win.innerHeight;
const next = owns() ? Math.max(0, base.bottom - edge) : 0;
const next = owns() ? Math.max(0, base.bottom - keyboardTop()) : 0;
// resizes-content pages shrink the CSS canvas as well. Keep the focused canvas's layout
// height until the keyboard closes, so both dvui loops see the same backing-store size.
if (canvas && next > 0 && current.height < base.height) {
Expand Down
2 changes: 1 addition & 1 deletion web/tests/embed.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const document = Object.assign(new Target(), {
const window = Object.assign(new Target(), {
visualViewport: Object.assign(new Target(), { height: 100, offsetTop: 0 }),
devicePixelRatio: 1, scrollX: 0, scrollY: 0, localStorage: new Map(), parent: null,
dispatchEvent() {}, matchMedia: () => ({ matches: false, addEventListener() {} }),
dispatchEvent() {}, matchMedia: (q) => ({ matches: q === "(pointer: coarse) and (hover: none)", addEventListener() {} }),
});
window.parent = window;
Object.assign(globalThis, {
Expand Down
14 changes: 14 additions & 0 deletions web/tests/keyboard-inset.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,20 @@ class Target {
fire(type) { this.listeners[type]?.(); }
}

// A desktop has no soft keyboard: a window shrinking after focusin is not an inset.
{
const vv = Object.assign(new Target(), { height: 800, offsetTop: 0 });
const win = Object.assign(new Target(), { navigator: {}, innerHeight: 800, visualViewport: vv, matchMedia: () => ({ matches: false }) });
const canvas = { style: { height: '100%' }, getBoundingClientRect: () => ({ bottom: 800, height: 800 }) };
const sent = [];
keyboardInset(win, (m) => sent.push(m), canvas);
win.fire('focusin');
vv.height = win.innerHeight = 500;
win.fire('resize');
assert.deepEqual(sent, []);
assert.equal(canvas.style.height, '100%');
}

// Chrome on Android: the VirtualKeyboard API, with the browser told not to resize or pan.
{
const vk = Object.assign(new Target(), { overlaysContent: false, boundingRect: { height: 0 } });
Expand Down
Loading