mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-16 01:26:37 +08:00
165 lines
4.7 KiB
TypeScript
165 lines
4.7 KiB
TypeScript
import { IS_IOS } from '@blocksuite/global/env';
|
|
import type * as GlobalTypes from '@blocksuite/global/types';
|
|
import { DisposableGroup } from '@blocksuite/global/utils';
|
|
import { signal } from '@preact/signals-core';
|
|
import type { ReactiveController, ReactiveControllerHost } from 'lit';
|
|
|
|
declare type _GLOBAL_ = typeof GlobalTypes;
|
|
|
|
function notSupportedWarning() {
|
|
console.warn('VirtualKeyboard API and VisualViewport API are not supported');
|
|
}
|
|
|
|
export type VirtualKeyboardControllerConfig = {
|
|
useScreenHeight: boolean;
|
|
inputElement: HTMLElement;
|
|
};
|
|
|
|
export class VirtualKeyboardController implements ReactiveController {
|
|
private readonly _disposables = new DisposableGroup();
|
|
|
|
private readonly _keyboardHeight$ = signal(0);
|
|
|
|
private readonly _keyboardOpened$ = signal(false);
|
|
|
|
private readonly _storeInitialInputElementAttributes = () => {
|
|
const { inputElement } = this.config;
|
|
if (navigator.virtualKeyboard) {
|
|
const { overlaysContent } = navigator.virtualKeyboard;
|
|
const { virtualKeyboardPolicy } = inputElement;
|
|
|
|
this._disposables.add(() => {
|
|
if (!navigator.virtualKeyboard) return;
|
|
navigator.virtualKeyboard.overlaysContent = overlaysContent;
|
|
inputElement.virtualKeyboardPolicy = virtualKeyboardPolicy;
|
|
});
|
|
} else if (visualViewport) {
|
|
const { inputMode } = inputElement;
|
|
this._disposables.add(() => {
|
|
inputElement.inputMode = inputMode;
|
|
});
|
|
}
|
|
};
|
|
|
|
private readonly _updateKeyboardHeight = () => {
|
|
const { virtualKeyboard } = navigator;
|
|
if (virtualKeyboard) {
|
|
this._keyboardOpened$.value = virtualKeyboard.boundingRect.height > 0;
|
|
this._keyboardHeight$.value = virtualKeyboard.boundingRect.height;
|
|
} else if (visualViewport) {
|
|
const windowHeight = this.config.useScreenHeight
|
|
? window.screen.height
|
|
: window.innerHeight;
|
|
|
|
/**
|
|
* ┌───────────────┐ - window top
|
|
* │ │
|
|
* │ │
|
|
* │ │
|
|
* │ │
|
|
* │ │
|
|
* └───────────────┘ - keyboard top --
|
|
* │ │ │ keyboard height in layout viewport
|
|
* └───────────────┘ - page(html) bottom --
|
|
* │ │ │ visualViewport.offsetTop
|
|
* └───────────────┘ - window bottom --
|
|
*/
|
|
this._keyboardOpened$.value = windowHeight - visualViewport.height > 0;
|
|
this._keyboardHeight$.value =
|
|
windowHeight -
|
|
visualViewport.height -
|
|
(IS_IOS ? 0 : visualViewport.offsetTop);
|
|
} else {
|
|
notSupportedWarning();
|
|
}
|
|
};
|
|
|
|
hide = () => {
|
|
if (navigator.virtualKeyboard) {
|
|
navigator.virtualKeyboard.hide();
|
|
} else {
|
|
this.config.inputElement.inputMode = 'none';
|
|
}
|
|
};
|
|
|
|
host: ReactiveControllerHost & {
|
|
virtualKeyboardControllerConfig: VirtualKeyboardControllerConfig;
|
|
hasUpdated: boolean;
|
|
};
|
|
|
|
show = () => {
|
|
if (navigator.virtualKeyboard) {
|
|
navigator.virtualKeyboard.show();
|
|
} else {
|
|
this.config.inputElement.inputMode = '';
|
|
}
|
|
};
|
|
|
|
toggle = () => {
|
|
if (this.opened) {
|
|
this.hide();
|
|
} else {
|
|
this.show();
|
|
}
|
|
};
|
|
|
|
get config() {
|
|
return this.host.virtualKeyboardControllerConfig;
|
|
}
|
|
|
|
/**
|
|
* Return the height of keyboard in layout viewport
|
|
* see comment in the `_updateKeyboardHeight` method
|
|
*/
|
|
get keyboardHeight() {
|
|
return this._keyboardHeight$.value;
|
|
}
|
|
|
|
get opened() {
|
|
return this._keyboardOpened$.value;
|
|
}
|
|
|
|
constructor(host: VirtualKeyboardController['host']) {
|
|
(this.host = host).addController(this);
|
|
}
|
|
|
|
hostConnected() {
|
|
this._storeInitialInputElementAttributes();
|
|
|
|
const { inputElement } = this.config;
|
|
|
|
if (navigator.virtualKeyboard) {
|
|
navigator.virtualKeyboard.overlaysContent = true;
|
|
this.config.inputElement.virtualKeyboardPolicy = 'manual';
|
|
|
|
this._disposables.addFromEvent(
|
|
navigator.virtualKeyboard,
|
|
'geometrychange',
|
|
this._updateKeyboardHeight
|
|
);
|
|
} else if (visualViewport) {
|
|
this._disposables.addFromEvent(
|
|
visualViewport,
|
|
'resize',
|
|
this._updateKeyboardHeight
|
|
);
|
|
this._disposables.addFromEvent(
|
|
visualViewport,
|
|
'scroll',
|
|
this._updateKeyboardHeight
|
|
);
|
|
} else {
|
|
notSupportedWarning();
|
|
}
|
|
|
|
this._disposables.addFromEvent(inputElement, 'focus', this.show);
|
|
this._disposables.addFromEvent(inputElement, 'blur', this.hide);
|
|
|
|
this._updateKeyboardHeight();
|
|
}
|
|
|
|
hostDisconnected() {
|
|
this._disposables.dispose();
|
|
}
|
|
}
|