diff --git a/packages/core/src/components/reorder/reorder-group.tsx b/packages/core/src/components/reorder/reorder-group.tsx index e2de232271..e246a11aa0 100644 --- a/packages/core/src/components/reorder/reorder-group.tsx +++ b/packages/core/src/components/reorder/reorder-group.tsx @@ -1,6 +1,7 @@ import { Component, Element, Prop, PropDidChange, State } from '@stencil/core'; import { GestureDetail } from '../../index'; import { clamp, reorderArray } from '../../utils/helpers'; +import { hapticSelectionChanged, hapticSelectionEnd, hapticSelectionStart} from '../../utils/haptic'; import { CSS_PROP } from '../animation-controller/constants'; const AUTO_SCROLL_MARGIN = 60; @@ -252,9 +253,7 @@ export class ReorderGroup { item.classList.add(ITEM_REORDER_SELECTED); - if ((window as any).TapticEngine) { - (window as any).TapticEngine.gestureSelectionStart(); - } + hapticSelectionStart(); } private onDragMove(ev: GestureDetail) { @@ -276,9 +275,7 @@ export class ReorderGroup { let fromIndex = indexForItem(selectedItem); this.lastToIndex = toIndex; - if ((window as any).TapticEngine) { - (window as any).TapticEngine.gestureSelectionChanged(); - } + hapticSelectionChanged(); this._reorderMove(fromIndex, toIndex); } @@ -321,9 +318,7 @@ export class ReorderGroup { reorderInactive(); } - if ((window as any).TapticEngine) { - (window as any).TapticEngine.gestureSelectionEnd(); - } + hapticSelectionEnd(); } private itemIndexForTop(deltaY: number): number { diff --git a/packages/core/src/components/toggle/toggle.tsx b/packages/core/src/components/toggle/toggle.tsx index 4a14d34879..e0cfaa5f1e 100644 --- a/packages/core/src/components/toggle/toggle.tsx +++ b/packages/core/src/components/toggle/toggle.tsx @@ -1,5 +1,6 @@ import { Component, Event, EventEmitter, Listen, Method, Prop, PropDidChange, State } from '@stencil/core'; import { BooleanInputComponent, GestureDetail } from '../../index'; +import { hapticSelection } from '../../utils/haptic'; @Component({ @@ -19,7 +20,7 @@ export class Toggle implements BooleanInputComponent { private labelId: string; private styleTmr: any; private gestureConfig: any; - private startX: number; + private pivotX: number; hasFocus: boolean = false; @@ -85,39 +86,30 @@ export class Toggle implements BooleanInputComponent { } private onDragStart(detail: GestureDetail) { - this.startX = detail.startX; + this.pivotX = detail.currentX; + this.activated = true; this.fireFocus(); } private onDragMove(detail: GestureDetail) { const currentX = detail.currentX; - if (this.checked) { - if (currentX + 15 < this.startX) { - this.checked = false; - this.activated = true; - this.startX = currentX; - } - - } else if (currentX - 15 > this.startX) { - this.checked = true; - this.activated = (currentX < this.startX + 5); - this.startX = currentX; + const checked = this.checked; + if (shouldToggle(checked, currentX - this.pivotX, -15)) { + this.checked = !checked; + this.pivotX = currentX; + hapticSelection(); } } private onDragEnd(detail: GestureDetail) { - const delta = detail.deltaX; - if (this.checked) { - if (delta < -4) { - this.checked = false; - } - - } else if (delta > 4) { - this.checked = true; + const delta = detail.currentX - this.pivotX; + const checked = this.checked; + if (shouldToggle(checked, delta, 4)) { + this.checked = !checked; + hapticSelection(); } this.activated = false; - this.startX = null; this.fireBlur(); } @@ -182,3 +174,15 @@ export class Toggle implements BooleanInputComponent { ); } } + +function shouldToggle(checked: boolean, deltaX: number, margin: number): boolean { + const isRTL = document.dir === 'rtl'; + + if (checked) { + return (!isRTL && (margin > deltaX)) || + (isRTL && (- margin < deltaX)); + } else { + return (!isRTL && (- margin < deltaX)) || + (isRTL && (margin > deltaX)); + } +} diff --git a/packages/core/src/utils/haptic.ts b/packages/core/src/utils/haptic.ts new file mode 100644 index 0000000000..f1bb324c38 --- /dev/null +++ b/packages/core/src/utils/haptic.ts @@ -0,0 +1,56 @@ +const _engine = (window as any).TapticEngine; + +/** + * Check to see if the Haptic Plugin is available + * @return {boolean} Returns true or false if the plugin is available + * + */ +export function hapticAvailable() { + return !!_engine; +} + +/** + * Trigger a selection changed haptic event. Good for one-time events + * (not for gestures) + */ +export function hapticSelection() { + _engine && _engine.selection(); +} + +/** + * Tell the haptic engine that a gesture for a selection change is starting. + */ +export function hapticSelectionStart() { + _engine && _engine.gestureSelectionStart(); +} + +/** + * Tell the haptic engine that a selection changed during a gesture. + */ +export function hapticSelectionChanged() { + _engine && _engine.gestureSelectionChanged(); +} + +/** + * Tell the haptic engine we are done with a gesture. This needs to be + * called lest resources are not properly recycled. + */ +export function hapticSelectionEnd() { + _engine && _engine.gestureSelectionEnd(); +} + +/** + * Use this to indicate success/failure/warning to the user. + * options should be of the type `{ type: 'success' }` (or `warning`/`error`) + */ +export function hapticNotification(options: { type: string }) { + _engine && _engine.notification(options); +} + +/** + * Use this to indicate success/failure/warning to the user. + * options should be of the type `{ style: 'light' }` (or `medium`/`heavy`) + */ +export function hapticImpact(options: { style: string }) { + _engine && _engine.impact(options); +}