@livx.cc/native-kit 0.20.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/core/NativeKit.ts +2 -0
- package/src/core/web-adapter.ts +23 -0
- package/src/index.ts +1 -0
- package/src/modules/app.ts +16 -0
- package/src/modules/keyboard.ts +44 -0
package/package.json
CHANGED
package/src/core/NativeKit.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { DeviceModule } from '../modules/device';
|
|
|
12
12
|
import { GeoModule } from '../modules/geo';
|
|
13
13
|
import { HapticsModule } from '../modules/haptics';
|
|
14
14
|
import { HealthModule } from '../modules/health';
|
|
15
|
+
import { KeyboardModule } from '../modules/keyboard';
|
|
15
16
|
import { LifecycleModule } from '../modules/lifecycle';
|
|
16
17
|
import { MediaModule } from '../modules/media';
|
|
17
18
|
import { MotionModule } from '../modules/motion';
|
|
@@ -70,6 +71,7 @@ export class NativeKit {
|
|
|
70
71
|
public readonly haptics = new HapticsModule(this);
|
|
71
72
|
public readonly share = new ShareModule(this);
|
|
72
73
|
public readonly screen = new ScreenModule(this);
|
|
74
|
+
public readonly keyboard = new KeyboardModule(this);
|
|
73
75
|
public readonly storage = new StorageModule(this);
|
|
74
76
|
public readonly toast = new ToastModule(this);
|
|
75
77
|
public readonly ui = new UiModule(this);
|
package/src/core/web-adapter.ts
CHANGED
|
@@ -49,11 +49,13 @@ export class WebAdapter implements NativeKitAdapter {
|
|
|
49
49
|
device: 'web',
|
|
50
50
|
clipboard: navigator.clipboard ? 'web' : 'none',
|
|
51
51
|
notifications: 'Notification' in window ? 'web' : 'none',
|
|
52
|
+
badge: typeof (navigator as any).setAppBadge === 'function' ? 'web' : 'none', // Badging API (PWA icon badge)
|
|
52
53
|
biometrics: 'none',
|
|
53
54
|
geo: 'geolocation' in navigator ? 'web' : 'none',
|
|
54
55
|
photos: 'web',
|
|
55
56
|
network: 'web',
|
|
56
57
|
screen: 'wakeLock' in n ? 'web' : 'none',
|
|
58
|
+
keyboard: (window as any).visualViewport ? 'web' : 'none', // VisualViewport resize ≈ keyboard show/hide
|
|
57
59
|
dialogs: 'web',
|
|
58
60
|
reviews: 'none',
|
|
59
61
|
themeColor: 'web', // the browser honors <meta name="theme-color"> itself
|
|
@@ -180,6 +182,11 @@ export class WebAdapter implements NativeKitAdapter {
|
|
|
180
182
|
case 'screen.orientation.current':
|
|
181
183
|
return (((screen as any)?.orientation?.type ?? '').startsWith('landscape') ? 'landscape' : 'portrait') as T;
|
|
182
184
|
|
|
185
|
+
case 'keyboard.hide':
|
|
186
|
+
// No programmatic IME on web — blurring the focused field dismisses it.
|
|
187
|
+
(document.activeElement as HTMLElement | null)?.blur?.();
|
|
188
|
+
return undefined as T;
|
|
189
|
+
|
|
183
190
|
case 'reviews.requestReview':
|
|
184
191
|
throw new KitError('UNSUPPORTED', 'No in-app review prompt on web');
|
|
185
192
|
|
|
@@ -371,6 +378,22 @@ export class WebAdapter implements NativeKitAdapter {
|
|
|
371
378
|
so.addEventListener('change', fire);
|
|
372
379
|
return () => so.removeEventListener('change', fire);
|
|
373
380
|
}
|
|
381
|
+
if (event === 'keyboard.show' || event === 'keyboard.hide') {
|
|
382
|
+
const vv = (window as any).visualViewport;
|
|
383
|
+
if (!vv) return () => {};
|
|
384
|
+
let shown = false;
|
|
385
|
+
const onResize = () => {
|
|
386
|
+
// The keyboard shrinks the visual viewport; the hidden band ≈ its height.
|
|
387
|
+
const height = Math.max(0, window.innerHeight - vv.height - vv.offsetTop);
|
|
388
|
+
const nowShown = height > 120; // threshold filters URL-bar/toolbar resizes
|
|
389
|
+
if (nowShown === shown) return;
|
|
390
|
+
shown = nowShown;
|
|
391
|
+
if (event === 'keyboard.show' && nowShown) cb({ height: Math.round(height) });
|
|
392
|
+
if (event === 'keyboard.hide' && !nowShown) cb(undefined);
|
|
393
|
+
};
|
|
394
|
+
vv.addEventListener('resize', onResize);
|
|
395
|
+
return () => vv.removeEventListener('resize', onResize);
|
|
396
|
+
}
|
|
374
397
|
if (event === 'app.pause' || event === 'app.resume') {
|
|
375
398
|
const handler = () => {
|
|
376
399
|
const paused = document.visibilityState === 'hidden';
|
package/src/index.ts
CHANGED
|
@@ -22,6 +22,7 @@ export type {
|
|
|
22
22
|
export type { ImpactStyle, NotifyType } from './modules/haptics';
|
|
23
23
|
export type { SharePayload, ShareFile } from './modules/share';
|
|
24
24
|
export type { Orientation, OrientationLock } from './modules/screen';
|
|
25
|
+
export type { KeyboardInfo } from './modules/keyboard';
|
|
25
26
|
export type { DeviceInfo } from './modules/device';
|
|
26
27
|
export type { ScheduleOptions } from './modules/notifications';
|
|
27
28
|
export type { PushMessage, PushPlatform, PushToken } from './modules/push';
|
package/src/modules/app.ts
CHANGED
|
@@ -31,6 +31,22 @@ export class AppModule {
|
|
|
31
31
|
return this.kit.capability('app');
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
/** 'native' where the OS exposes an app-icon badge (iOS always; Android launcher-dependent →
|
|
35
|
+
* honest no-op) · 'web' where the Badging API exists · else 'none'. Branch on this, not try/catch. */
|
|
36
|
+
get badgeCapability() {
|
|
37
|
+
return this.kit.capability('badge');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Set (or clear, with `0`) the app-icon badge count — the small number on the home-screen icon.
|
|
42
|
+
* Convenience over the shared native badge path: iOS sets the springboard badge; Android is an
|
|
43
|
+
* honest no-op (launchers own badges); web uses the Badging API where present.
|
|
44
|
+
* Branch on {@link badgeCapability}.
|
|
45
|
+
*/
|
|
46
|
+
badge(count: number): Promise<void> {
|
|
47
|
+
return this.kit.invoke('notifications.setBadge', { count });
|
|
48
|
+
}
|
|
49
|
+
|
|
34
50
|
/** Install-level environment facts for analytics/identification (source, install id, timestamps). */
|
|
35
51
|
environment(): Promise<AppEnvironment> {
|
|
36
52
|
return this.kit.invoke('app.environment');
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { NativeKit } from '../core/NativeKit';
|
|
2
|
+
import type { Unsubscribe } from '../core/types';
|
|
3
|
+
|
|
4
|
+
/** Payload of {@link KeyboardModule.onShow}: the on-screen keyboard's height in CSS pixels,
|
|
5
|
+
* so the page can pad/scroll content above it. */
|
|
6
|
+
export interface KeyboardInfo {
|
|
7
|
+
/** Keyboard height in CSS px (0 when unknown). */
|
|
8
|
+
height: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Software-keyboard control + observation — ONE API across platforms.
|
|
13
|
+
*
|
|
14
|
+
* The classic webview pain: the keyboard slides up and covers the focused input.
|
|
15
|
+
* Native emits `show` with the keyboard height (CSS px) and `hide`, so the page can
|
|
16
|
+
* lift content above it; `hide()` dismisses the keyboard programmatically.
|
|
17
|
+
*
|
|
18
|
+
* Native (iOS `UIKeyboardWillShow/Hide`, Android global-layout/IME insets) reports
|
|
19
|
+
* `capability === 'native'`. Web maps to the VisualViewport API where present
|
|
20
|
+
* (`'web'`), else `'none'`. Branch on {@link capability}, not try/catch.
|
|
21
|
+
*/
|
|
22
|
+
export class KeyboardModule {
|
|
23
|
+
constructor(private kit: NativeKit) {}
|
|
24
|
+
|
|
25
|
+
/** 'native' on a shell · 'web' where VisualViewport exists · else 'none'. */
|
|
26
|
+
get capability() {
|
|
27
|
+
return this.kit.capability('keyboard');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Dismiss the software keyboard (resign first responder / hide the IME). */
|
|
31
|
+
hide(): Promise<void> {
|
|
32
|
+
return this.kit.invoke('keyboard.hide');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Keyboard shown — payload carries its height in CSS pixels. */
|
|
36
|
+
onShow(cb: (info: KeyboardInfo) => void): Unsubscribe {
|
|
37
|
+
return this.kit.on('keyboard.show', (p) => cb(p as KeyboardInfo));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Keyboard dismissed. */
|
|
41
|
+
onHide(cb: () => void): Unsubscribe {
|
|
42
|
+
return this.kit.on('keyboard.hide', () => cb());
|
|
43
|
+
}
|
|
44
|
+
}
|