mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-11-08 15:51:16 +08:00
refactor: use capacitor types for native plugins (#27755)
Issue number: Internal
---------
<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->
<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->
## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->
Ionic currently detects and uses Capacitor APIs for different plugins
(haptics, status bar and keyboard). This implementation does not have
type safety and can result in unexpected behaviors.
## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->
- Adds `@capacitor/core`, `@capacitor/keyboard`, `@capacitor/haptics`
and `@capacitor/status-bar` as dev dependencies. These should _only_ be
used with `import type { }`.
- Refactors the plugin usages to be typed against the plugin packages,
while using a duplicate enum when needing a value. This allows us to not
bundle the capacitor plugins with Ionic Framework.
- Introduces a `getCapacitor()` function for interacting with the
`window.Capacitor` object through a typed object.
**How does it work?**
The idea is we want the type safety from the Capacitor packages, without
directly bundling that source code within Ionic Framework. This means we
use the Capacitor deps where a type is needed, but clone any enums where
a value is referenced. If a Capacitor dep changes the supported values,
Typescript will fail to compile and that will signal to use to update
our enum values to match any changes.
## Does this introduce a breaking change?
- [ ] Yes
- [x] No
<!-- If this introduces a breaking change, please describe the impact
and migration path for existing applications below. -->
## Other information
<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->
Dev-build: `7.1.2-dev.11688696027.1c4d4ad1`
Tested against a demo app for some of the core behavior:
https://github.com/sean-perkins/capacitor-ionic-plugins-demo
This commit is contained in:
4
.github/dependabot.yml
vendored
4
.github/dependabot.yml
vendored
@ -14,3 +14,7 @@ updates:
|
|||||||
- dependency-name: "@stencil/sass"
|
- dependency-name: "@stencil/sass"
|
||||||
- dependency-name: "@stencil/vue-output-target"
|
- dependency-name: "@stencil/vue-output-target"
|
||||||
- dependency-name: "ionicons"
|
- dependency-name: "ionicons"
|
||||||
|
- dependency-name: "@capacitor/core"
|
||||||
|
- dependency-name: "@capacitor/keyboard"
|
||||||
|
- dependency-name: "@capacitor/haptics"
|
||||||
|
- dependency-name: "@capacitor/status-bar"
|
||||||
|
|||||||
70
core/package-lock.json
generated
70
core/package-lock.json
generated
@ -15,6 +15,10 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@axe-core/playwright": "^4.7.3",
|
"@axe-core/playwright": "^4.7.3",
|
||||||
|
"@capacitor/core": "^5.1.1",
|
||||||
|
"@capacitor/haptics": "^5.0.5",
|
||||||
|
"@capacitor/keyboard": "^5.0.5",
|
||||||
|
"@capacitor/status-bar": "^5.0.5",
|
||||||
"@ionic/eslint-config": "^0.3.0",
|
"@ionic/eslint-config": "^0.3.0",
|
||||||
"@ionic/prettier-config": "^2.0.0",
|
"@ionic/prettier-config": "^2.0.0",
|
||||||
"@jest/core": "^27.5.1",
|
"@jest/core": "^27.5.1",
|
||||||
@ -602,6 +606,42 @@
|
|||||||
"integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
|
"integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@capacitor/core": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-17rZMGpZYQgBAmUS1uAS1GrRkPAQiUfpTfvd2wWS9z87GoTL/5vtpPAtI0/XLtr4eve2TsATY2r8BaW2NfEO6Q==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@capacitor/haptics": {
|
||||||
|
"version": "5.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@capacitor/haptics/-/haptics-5.0.5.tgz",
|
||||||
|
"integrity": "sha512-iDgmCehrdc0Sdob/y3KjAX0sll/y9PTP6hJubwTpIpRt3kOKGDhMMNkUGRKm6myTIRPpa2Mm8jWCSYaA4pPq4g==",
|
||||||
|
"dev": true,
|
||||||
|
"peerDependencies": {
|
||||||
|
"@capacitor/core": "^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@capacitor/keyboard": {
|
||||||
|
"version": "5.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@capacitor/keyboard/-/keyboard-5.0.5.tgz",
|
||||||
|
"integrity": "sha512-pB2o15C8Cz3QqDcToU4H0B/i+LLXYPQVtShukywMlVEJZ6UUy+KSK8XCg/YPDZBY9E0lSprHw4+NBqH0HTYRKQ==",
|
||||||
|
"dev": true,
|
||||||
|
"peerDependencies": {
|
||||||
|
"@capacitor/core": "^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@capacitor/status-bar": {
|
||||||
|
"version": "5.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@capacitor/status-bar/-/status-bar-5.0.5.tgz",
|
||||||
|
"integrity": "sha512-8ykkIbndeAaATrAYcr4CLSplTeR6CU15h8trXV3DgLXlFQAC6E/WJnoMy1QL61n5rHh725nixqwCTepcgGx/rw==",
|
||||||
|
"dev": true,
|
||||||
|
"peerDependencies": {
|
||||||
|
"@capacitor/core": "^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@eslint/eslintrc": {
|
"node_modules/@eslint/eslintrc": {
|
||||||
"version": "0.4.3",
|
"version": "0.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz",
|
||||||
@ -10743,6 +10783,36 @@
|
|||||||
"integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
|
"integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@capacitor/core": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-17rZMGpZYQgBAmUS1uAS1GrRkPAQiUfpTfvd2wWS9z87GoTL/5vtpPAtI0/XLtr4eve2TsATY2r8BaW2NfEO6Q==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@capacitor/haptics": {
|
||||||
|
"version": "5.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@capacitor/haptics/-/haptics-5.0.5.tgz",
|
||||||
|
"integrity": "sha512-iDgmCehrdc0Sdob/y3KjAX0sll/y9PTP6hJubwTpIpRt3kOKGDhMMNkUGRKm6myTIRPpa2Mm8jWCSYaA4pPq4g==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
|
"@capacitor/keyboard": {
|
||||||
|
"version": "5.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@capacitor/keyboard/-/keyboard-5.0.5.tgz",
|
||||||
|
"integrity": "sha512-pB2o15C8Cz3QqDcToU4H0B/i+LLXYPQVtShukywMlVEJZ6UUy+KSK8XCg/YPDZBY9E0lSprHw4+NBqH0HTYRKQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
|
"@capacitor/status-bar": {
|
||||||
|
"version": "5.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@capacitor/status-bar/-/status-bar-5.0.5.tgz",
|
||||||
|
"integrity": "sha512-8ykkIbndeAaATrAYcr4CLSplTeR6CU15h8trXV3DgLXlFQAC6E/WJnoMy1QL61n5rHh725nixqwCTepcgGx/rw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"@eslint/eslintrc": {
|
"@eslint/eslintrc": {
|
||||||
"version": "0.4.3",
|
"version": "0.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz",
|
||||||
|
|||||||
@ -37,6 +37,10 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@axe-core/playwright": "^4.7.3",
|
"@axe-core/playwright": "^4.7.3",
|
||||||
|
"@capacitor/core": "^5.1.1",
|
||||||
|
"@capacitor/haptics": "^5.0.5",
|
||||||
|
"@capacitor/keyboard": "^5.0.5",
|
||||||
|
"@capacitor/status-bar": "^5.0.5",
|
||||||
"@ionic/eslint-config": "^0.3.0",
|
"@ionic/eslint-config": "^0.3.0",
|
||||||
"@ionic/prettier-config": "^2.0.0",
|
"@ionic/prettier-config": "^2.0.0",
|
||||||
"@jest/core": "^27.5.1",
|
"@jest/core": "^27.5.1",
|
||||||
|
|||||||
@ -74,6 +74,7 @@ export const getBackdropValueForSheet = (x: number, backdropBreakpoint: number)
|
|||||||
* support for Style.Default.
|
* support for Style.Default.
|
||||||
*/
|
*/
|
||||||
export const setCardStatusBarDark = () => {
|
export const setCardStatusBarDark = () => {
|
||||||
|
// TODO FW-4696 Remove supportDefaultStatusBarStyle in Ionic v8
|
||||||
if (!win || win.innerWidth >= 768 || !StatusBar.supportsDefaultStatusBarStyle()) {
|
if (!win || win.innerWidth >= 768 || !StatusBar.supportsDefaultStatusBarStyle()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -82,6 +83,7 @@ export const setCardStatusBarDark = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const setCardStatusBarDefault = (defaultStyle = Style.Default) => {
|
export const setCardStatusBarDefault = (defaultStyle = Style.Default) => {
|
||||||
|
// TODO FW-4696 Remove supportDefaultStatusBarStyle in Ionic v8
|
||||||
if (!win || win.innerWidth >= 768 || !StatusBar.supportsDefaultStatusBarStyle()) {
|
if (!win || win.innerWidth >= 768 || !StatusBar.supportsDefaultStatusBarStyle()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import {
|
|||||||
printIonContentErrorMsg,
|
printIonContentErrorMsg,
|
||||||
} from '@utils/content';
|
} from '@utils/content';
|
||||||
import { clamp, componentOnReady, getElementRoot, raf, transitionEndAsync } from '@utils/helpers';
|
import { clamp, componentOnReady, getElementRoot, raf, transitionEndAsync } from '@utils/helpers';
|
||||||
import { hapticImpact } from '@utils/native/haptic';
|
import { ImpactStyle, hapticImpact } from '@utils/native/haptic';
|
||||||
|
|
||||||
import { getIonMode } from '../../global/ionic-global';
|
import { getIonMode } from '../../global/ionic-global';
|
||||||
import type { Animation, Gesture, GestureDetail } from '../../interface';
|
import type { Animation, Gesture, GestureDetail } from '../../interface';
|
||||||
@ -246,7 +246,7 @@ export class Refresher implements ComponentInterface {
|
|||||||
if (!this.didRefresh) {
|
if (!this.didRefresh) {
|
||||||
this.beginRefresh();
|
this.beginRefresh();
|
||||||
this.didRefresh = true;
|
this.didRefresh = true;
|
||||||
hapticImpact({ style: 'light' });
|
hapticImpact({ style: ImpactStyle.Light });
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translate the content element otherwise when pointer is removed
|
* Translate the content element otherwise when pointer is removed
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
|
import type { KeyboardResizeOptions } from '@capacitor/keyboard';
|
||||||
|
|
||||||
import { getScrollElement, scrollByPoint } from '../../content';
|
import { getScrollElement, scrollByPoint } from '../../content';
|
||||||
import { raf } from '../../helpers';
|
import { raf } from '../../helpers';
|
||||||
import type { KeyboardResizeOptions } from '../../native/keyboard';
|
|
||||||
import { KeyboardResize } from '../../native/keyboard';
|
import { KeyboardResize } from '../../native/keyboard';
|
||||||
|
|
||||||
import { relocateInput, SCROLL_AMOUNT_PADDING } from './common';
|
import { relocateInput, SCROLL_AMOUNT_PADDING } from './common';
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { doc, win } from '@utils/browser';
|
import { doc, win } from '@utils/browser';
|
||||||
|
|
||||||
import { KeyboardResize, Keyboard } from '../native/keyboard';
|
import { Keyboard, KeyboardResize } from '../native/keyboard';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The element that resizes when the keyboard opens
|
* The element that resizes when the keyboard opens
|
||||||
|
|||||||
9
core/src/utils/native/capacitor.ts
Normal file
9
core/src/utils/native/capacitor.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import type { CapacitorGlobal } from '@capacitor/core';
|
||||||
|
import { win } from '@utils/browser';
|
||||||
|
|
||||||
|
export const getCapacitor = () => {
|
||||||
|
if (win !== undefined) {
|
||||||
|
return (win as any).Capacitor as CapacitorGlobal;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
@ -1,24 +1,91 @@
|
|||||||
// Main types for this API
|
import type {
|
||||||
|
HapticsPlugin,
|
||||||
|
NotificationType as CapacitorNotificationType,
|
||||||
|
ImpactStyle as CapacitorImpactStyle,
|
||||||
|
} from '@capacitor/haptics';
|
||||||
|
|
||||||
|
import { getCapacitor } from './capacitor';
|
||||||
|
|
||||||
|
export enum ImpactStyle {
|
||||||
|
/**
|
||||||
|
* A collision between large, heavy user interface elements
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
Heavy = 'HEAVY',
|
||||||
|
/**
|
||||||
|
* A collision between moderately sized user interface elements
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
Medium = 'MEDIUM',
|
||||||
|
/**
|
||||||
|
* A collision between small, light user interface elements
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
Light = 'LIGHT',
|
||||||
|
}
|
||||||
|
|
||||||
interface HapticImpactOptions {
|
interface HapticImpactOptions {
|
||||||
style: 'light' | 'medium' | 'heavy';
|
style: CapacitorImpactStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum NotificationType {
|
||||||
|
/**
|
||||||
|
* A notification feedback type indicating that a task has completed successfully
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
Success = 'SUCCESS',
|
||||||
|
/**
|
||||||
|
* A notification feedback type indicating that a task has produced a warning
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
Warning = 'WARNING',
|
||||||
|
/**
|
||||||
|
* A notification feedback type indicating that a task has failed
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
Error = 'ERROR',
|
||||||
}
|
}
|
||||||
|
|
||||||
interface HapticNotificationOptions {
|
interface HapticNotificationOptions {
|
||||||
style: 'success' | 'warning' | 'error';
|
type: CapacitorNotificationType;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TapticEngine {
|
||||||
|
gestureSelectionStart: () => void;
|
||||||
|
gestureSelectionChanged: () => void;
|
||||||
|
gestureSelectionEnd: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const HapticEngine = {
|
const HapticEngine = {
|
||||||
getEngine() {
|
getEngine(): HapticsPlugin | undefined {
|
||||||
const win = window as any;
|
const tapticEngine = (window as any).TapticEngine;
|
||||||
return win.TapticEngine || (win.Capacitor?.isPluginAvailable('Haptics') && win.Capacitor.Plugins.Haptics);
|
if (tapticEngine) {
|
||||||
|
// Cordova
|
||||||
|
// TODO FW-4707 - Remove this in Ionic 8
|
||||||
|
return tapticEngine;
|
||||||
|
}
|
||||||
|
const capacitor = getCapacitor();
|
||||||
|
|
||||||
|
if (capacitor?.isPluginAvailable('Haptics')) {
|
||||||
|
// Capacitor
|
||||||
|
return capacitor.Plugins.Haptics as HapticsPlugin;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
},
|
},
|
||||||
available() {
|
available() {
|
||||||
const win = window as any;
|
|
||||||
const engine = this.getEngine();
|
const engine = this.getEngine();
|
||||||
if (!engine) {
|
if (!engine) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const capacitor = getCapacitor();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Developers can manually import the
|
* Developers can manually import the
|
||||||
* Haptics plugin in their app which will cause
|
* Haptics plugin in their app which will cause
|
||||||
@ -28,25 +95,30 @@ const HapticEngine = {
|
|||||||
* the Vibrate API. This check avoids that error
|
* the Vibrate API. This check avoids that error
|
||||||
* if the browser does not support the Vibrate API.
|
* if the browser does not support the Vibrate API.
|
||||||
*/
|
*/
|
||||||
if (win.Capacitor?.getPlatform() === 'web') {
|
if (capacitor?.getPlatform() === 'web') {
|
||||||
return typeof navigator !== 'undefined' && navigator.vibrate !== undefined;
|
return typeof navigator !== 'undefined' && navigator.vibrate !== undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
isCordova() {
|
isCordova() {
|
||||||
return !!(window as any).TapticEngine;
|
return (window as any).TapticEngine !== undefined;
|
||||||
},
|
},
|
||||||
isCapacitor() {
|
isCapacitor() {
|
||||||
const win = window as any;
|
return getCapacitor() !== undefined;
|
||||||
return !!win.Capacitor;
|
|
||||||
},
|
},
|
||||||
impact(options: HapticImpactOptions) {
|
impact(options: HapticImpactOptions) {
|
||||||
const engine = this.getEngine();
|
const engine = this.getEngine();
|
||||||
if (!engine) {
|
if (!engine) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const style = this.isCapacitor() ? options.style.toUpperCase() : options.style;
|
/**
|
||||||
|
* To provide backwards compatibility with Cordova apps,
|
||||||
|
* we convert the style to lowercase.
|
||||||
|
*
|
||||||
|
* TODO: FW-4707 - Remove this in Ionic 8
|
||||||
|
*/
|
||||||
|
const style = this.isCapacitor() ? options.style : (options.style.toLowerCase() as ImpactStyle);
|
||||||
engine.impact({ style });
|
engine.impact({ style });
|
||||||
},
|
},
|
||||||
notification(options: HapticNotificationOptions) {
|
notification(options: HapticNotificationOptions) {
|
||||||
@ -54,11 +126,24 @@ const HapticEngine = {
|
|||||||
if (!engine) {
|
if (!engine) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const style = this.isCapacitor() ? options.style.toUpperCase() : options.style;
|
/**
|
||||||
engine.notification({ style });
|
* To provide backwards compatibility with Cordova apps,
|
||||||
|
* we convert the style to lowercase.
|
||||||
|
*
|
||||||
|
* TODO: FW-4707 - Remove this in Ionic 8
|
||||||
|
*/
|
||||||
|
const type = this.isCapacitor() ? options.type : (options.type.toLowerCase() as NotificationType);
|
||||||
|
engine.notification({ type });
|
||||||
},
|
},
|
||||||
selection() {
|
selection() {
|
||||||
this.impact({ style: 'light' });
|
/**
|
||||||
|
* To provide backwards compatibility with Cordova apps,
|
||||||
|
* we convert the style to lowercase.
|
||||||
|
*
|
||||||
|
* TODO: FW-4707 - Remove this in Ionic 8
|
||||||
|
*/
|
||||||
|
const style = this.isCapacitor() ? ImpactStyle.Light : ('light' as ImpactStyle);
|
||||||
|
this.impact({ style });
|
||||||
},
|
},
|
||||||
selectionStart() {
|
selectionStart() {
|
||||||
const engine = this.getEngine();
|
const engine = this.getEngine();
|
||||||
@ -68,7 +153,7 @@ const HapticEngine = {
|
|||||||
if (this.isCapacitor()) {
|
if (this.isCapacitor()) {
|
||||||
engine.selectionStart();
|
engine.selectionStart();
|
||||||
} else {
|
} else {
|
||||||
engine.gestureSelectionStart();
|
(engine as unknown as TapticEngine).gestureSelectionStart();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
selectionChanged() {
|
selectionChanged() {
|
||||||
@ -79,7 +164,7 @@ const HapticEngine = {
|
|||||||
if (this.isCapacitor()) {
|
if (this.isCapacitor()) {
|
||||||
engine.selectionChanged();
|
engine.selectionChanged();
|
||||||
} else {
|
} else {
|
||||||
engine.gestureSelectionChanged();
|
(engine as unknown as TapticEngine).gestureSelectionChanged();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
selectionEnd() {
|
selectionEnd() {
|
||||||
@ -90,7 +175,7 @@ const HapticEngine = {
|
|||||||
if (this.isCapacitor()) {
|
if (this.isCapacitor()) {
|
||||||
engine.selectionEnd();
|
engine.selectionEnd();
|
||||||
} else {
|
} else {
|
||||||
engine.gestureSelectionEnd();
|
(engine as unknown as TapticEngine).gestureSelectionEnd();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -135,7 +220,7 @@ export const hapticSelectionEnd = () => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Use this to indicate success/failure/warning to the user.
|
* Use this to indicate success/failure/warning to the user.
|
||||||
* options should be of the type `{ type: 'success' }` (or `warning`/`error`)
|
* options should be of the type `{ type: NotificationType.SUCCESS }` (or `WARNING`/`ERROR`)
|
||||||
*/
|
*/
|
||||||
export const hapticNotification = (options: HapticNotificationOptions) => {
|
export const hapticNotification = (options: HapticNotificationOptions) => {
|
||||||
hapticAvailable() && HapticEngine.notification(options);
|
hapticAvailable() && HapticEngine.notification(options);
|
||||||
@ -143,7 +228,7 @@ export const hapticNotification = (options: HapticNotificationOptions) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Use this to indicate success/failure/warning to the user.
|
* Use this to indicate success/failure/warning to the user.
|
||||||
* options should be of the type `{ style: 'light' }` (or `medium`/`heavy`)
|
* options should be of the type `{ style: ImpactStyle.LIGHT }` (or `MEDIUM`/`HEAVY`)
|
||||||
*/
|
*/
|
||||||
export const hapticImpact = (options: HapticImpactOptions) => {
|
export const hapticImpact = (options: HapticImpactOptions) => {
|
||||||
hapticAvailable() && HapticEngine.impact(options);
|
hapticAvailable() && HapticEngine.impact(options);
|
||||||
|
|||||||
@ -1,30 +1,55 @@
|
|||||||
import { win } from '../browser';
|
import type { CapacitorException } from '@capacitor/core';
|
||||||
|
import type { KeyboardPlugin, KeyboardResizeOptions } from '@capacitor/keyboard';
|
||||||
|
|
||||||
import type { NativePluginError } from './native-interface';
|
import { getCapacitor } from './capacitor';
|
||||||
|
import { ExceptionCode } from './native-interface';
|
||||||
// Interfaces source: https://capacitorjs.com/docs/apis/keyboard#interfaces
|
|
||||||
export interface KeyboardResizeOptions {
|
|
||||||
mode: KeyboardResize;
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum KeyboardResize {
|
export enum KeyboardResize {
|
||||||
|
/**
|
||||||
|
* Only the `body` HTML element will be resized.
|
||||||
|
* Relative units are not affected, because the viewport does not change.
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
Body = 'body',
|
Body = 'body',
|
||||||
|
/**
|
||||||
|
* Only the `ion-app` HTML element will be resized.
|
||||||
|
* Use it only for Ionic Framework apps.
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
Ionic = 'ionic',
|
Ionic = 'ionic',
|
||||||
|
/**
|
||||||
|
* The whole native Web View will be resized when the keyboard shows/hides.
|
||||||
|
* This affects the `vh` relative unit.
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
Native = 'native',
|
Native = 'native',
|
||||||
|
/**
|
||||||
|
* Neither the app nor the Web View are resized.
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
None = 'none',
|
None = 'none',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Keyboard = {
|
export const Keyboard = {
|
||||||
getEngine() {
|
getEngine(): KeyboardPlugin | undefined {
|
||||||
return (win as any)?.Capacitor?.isPluginAvailable('Keyboard') && (win as any)?.Capacitor.Plugins.Keyboard;
|
const capacitor = getCapacitor();
|
||||||
|
|
||||||
|
if (capacitor?.isPluginAvailable('Keyboard')) {
|
||||||
|
return capacitor.Plugins.Keyboard as KeyboardPlugin;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
},
|
},
|
||||||
getResizeMode(): Promise<KeyboardResizeOptions | undefined> {
|
getResizeMode(): Promise<KeyboardResizeOptions | undefined> {
|
||||||
const engine = this.getEngine();
|
const engine = this.getEngine();
|
||||||
if (!engine?.getResizeMode) {
|
if (!engine?.getResizeMode) {
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
}
|
}
|
||||||
return engine.getResizeMode().catch((e: NativePluginError) => {
|
return engine.getResizeMode().catch((e: CapacitorException) => {
|
||||||
if (e.code === 'UNIMPLEMENTED') {
|
if (e.code === ExceptionCode.Unimplemented) {
|
||||||
// If the native implementation is not available
|
// If the native implementation is not available
|
||||||
// we treat it the same as if the plugin is not available.
|
// we treat it the same as if the plugin is not available.
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|||||||
@ -1,13 +1,17 @@
|
|||||||
/**
|
export enum ExceptionCode {
|
||||||
* Used to represent a generic error from a native plugin call.
|
|
||||||
*/
|
|
||||||
export interface NativePluginError {
|
|
||||||
/**
|
/**
|
||||||
* The error code.
|
* API is not implemented.
|
||||||
|
*
|
||||||
|
* This usually means the API can't be used because it is not implemented for
|
||||||
|
* the current platform.
|
||||||
*/
|
*/
|
||||||
code?: string;
|
Unimplemented = 'UNIMPLEMENTED',
|
||||||
/**
|
/**
|
||||||
* The error message.
|
* API is not available.
|
||||||
|
*
|
||||||
|
* This means the API can't be used right now because:
|
||||||
|
* - it is currently missing a prerequisite, such as network connectivity
|
||||||
|
* - it requires a particular platform or browser version
|
||||||
*/
|
*/
|
||||||
message?: string;
|
Unavailable = 'UNAVAILABLE',
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
import { win } from '../browser';
|
import type { StatusBarPlugin, Style as StatusBarStyle } from '@capacitor/status-bar';
|
||||||
|
|
||||||
|
import { getCapacitor } from './capacitor';
|
||||||
|
|
||||||
interface StyleOptions {
|
interface StyleOptions {
|
||||||
style: Style;
|
style: StatusBarStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum Style {
|
export enum Style {
|
||||||
@ -11,17 +13,24 @@ export enum Style {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const StatusBar = {
|
export const StatusBar = {
|
||||||
getEngine() {
|
getEngine(): StatusBarPlugin | undefined {
|
||||||
return (win as any)?.Capacitor?.isPluginAvailable('StatusBar') && (win as any)?.Capacitor.Plugins.StatusBar;
|
const capacitor = getCapacitor();
|
||||||
|
|
||||||
|
if (capacitor?.isPluginAvailable('StatusBar')) {
|
||||||
|
return capacitor.Plugins.StatusBar as StatusBarPlugin;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
},
|
},
|
||||||
|
// TODO FW-4696 Remove supportDefaultStatusBarStyle in Ionic v8
|
||||||
supportsDefaultStatusBarStyle() {
|
supportsDefaultStatusBarStyle() {
|
||||||
|
const capacitor = getCapacitor() as any;
|
||||||
/**
|
/**
|
||||||
* The 'DEFAULT' status bar style was added
|
* The 'DEFAULT' status bar style was added
|
||||||
* to the @capacitor/status-bar plugin in Capacitor 3.
|
* to the @capacitor/status-bar plugin in Capacitor 3.
|
||||||
* PluginHeaders is only supported in Capacitor 3+,
|
* PluginHeaders is only supported in Capacitor 3+,
|
||||||
* so we can use this to detect Capacitor 3.
|
* so we can use this to detect Capacitor 3.
|
||||||
*/
|
*/
|
||||||
return !!(win as any)?.Capacitor?.PluginHeaders;
|
return !!capacitor?.PluginHeaders;
|
||||||
},
|
},
|
||||||
setStyle(options: StyleOptions) {
|
setStyle(options: StyleOptions) {
|
||||||
const engine = this.getEngine();
|
const engine = this.getEngine();
|
||||||
@ -31,7 +40,7 @@ export const StatusBar = {
|
|||||||
|
|
||||||
engine.setStyle(options);
|
engine.setStyle(options);
|
||||||
},
|
},
|
||||||
getStyle: async function (): Promise<Style> {
|
getStyle: async function (): Promise<StatusBarStyle> {
|
||||||
const engine = this.getEngine();
|
const engine = this.getEngine();
|
||||||
if (!engine) {
|
if (!engine) {
|
||||||
return Style.Default;
|
return Style.Default;
|
||||||
|
|||||||
Reference in New Issue
Block a user