From 7dcefe345caf69cd104077c554c35ab67435f5b0 Mon Sep 17 00:00:00 2001 From: "Manu Mtz.-Almeida" Date: Sat, 10 Mar 2018 01:41:09 +0100 Subject: [PATCH] refactor(helper): move functions --- packages/core/src/components.d.ts | 9 +- .../animation-interface.tsx | 6 +- .../animation-controller/animator.tsx | 45 +++- .../animation-controller/constants.ts | 29 +-- .../core/src/components/content/content.tsx | 25 +- .../src/components/datetime/datetime-util.ts | 14 +- .../core/src/components/datetime/datetime.tsx | 5 +- .../gesture-controller-utils.ts | 5 - .../core/src/components/gesture/gesture.tsx | 33 ++- .../components/item-sliding/item-sliding.tsx | 20 +- .../keyboard-controller.tsx | 33 ++- packages/core/src/components/menu/menu.tsx | 24 +- packages/core/src/components/nav/nav.tsx | 6 +- .../reorder-group/reorder-group.tsx | 20 +- .../src/components/status-tap/status-tap.tsx | 6 +- packages/core/src/global/config-controller.ts | 6 +- packages/core/src/global/events.ts | 15 -- packages/core/src/global/ionic-global.ts | 3 - packages/core/src/index.d.ts | 19 +- .../core/src/utils/dom-framework-delegate.ts | 16 +- .../core/src/utils/dom-router-delegate.ts | 16 -- packages/core/src/utils/helpers.ts | 245 ------------------ packages/core/src/utils/overlays.ts | 3 +- 23 files changed, 205 insertions(+), 398 deletions(-) delete mode 100644 packages/core/src/global/events.ts delete mode 100644 packages/core/src/utils/dom-router-delegate.ts diff --git a/packages/core/src/components.d.ts b/packages/core/src/components.d.ts index c400a6120e..b0d1d41367 100644 --- a/packages/core/src/components.d.ts +++ b/packages/core/src/components.d.ts @@ -33,14 +33,13 @@ import { AlertButton, AlertInput, } from './components/alert/alert'; -import { - ElementRef, - Side, -} from './utils/helpers'; import { GestureCallback, GestureDetail, } from './components/gesture/gesture'; +import { + Side, +} from './utils/helpers'; import { PickerButton, PickerColumn as PickerColumn2, @@ -1062,7 +1061,7 @@ declare global { } namespace JSXElements { export interface IonGestureAttributes extends HTMLAttributes { - attachTo?: ElementRef; + attachTo?: string|HTMLElement; autoBlockAll?: boolean; canStart?: GestureCallback; direction?: string; diff --git a/packages/core/src/components/animation-controller/animation-interface.tsx b/packages/core/src/components/animation-controller/animation-interface.tsx index b21c9230c3..bca2143f5e 100644 --- a/packages/core/src/components/animation-controller/animation-interface.tsx +++ b/packages/core/src/components/animation-controller/animation-interface.tsx @@ -6,7 +6,7 @@ export interface AnimationController { export interface Animation { new (): any; - parent: Animation; + parent: Animation|undefined; hasChildren: boolean; addElement(el: Node|Node[]|NodeList): Animation; add(childAnimation: Animation): Animation; @@ -30,7 +30,8 @@ export interface Animation { afterStyles(styles: { [property: string]: any; }): Animation; afterClearStyles(propertyNames: string[]): Animation; play(opts?: PlayOptions): void; - syncPlay(): void; + playSync(): void; + playAsync(opts?: PlayOptions): Promise; reverse(shouldReverse?: boolean): Animation; stop(stepValue?: number): void; progressStart(): void; @@ -39,7 +40,6 @@ export interface Animation { onFinish(callback: (animation?: Animation) => void, opts?: {oneTimeCallback?: boolean, clearExistingCallacks?: boolean}): Animation; destroy(): void; isRoot(): boolean; - create(): Animation; hasCompleted: boolean; } diff --git a/packages/core/src/components/animation-controller/animator.tsx b/packages/core/src/components/animation-controller/animator.tsx index b0bd579ac2..432777c684 100644 --- a/packages/core/src/components/animation-controller/animator.tsx +++ b/packages/core/src/components/animation-controller/animator.tsx @@ -1,8 +1,28 @@ import { AnimationOptions, EffectProperty, EffectState, PlayOptions } from './animation-interface'; -import { CSS_PROP, CSS_VALUE_REGEX, DURATION_MIN, TRANSFORM_PROPS, TRANSITION_END_FALLBACK_PADDING_MS } from './constants'; +import { CSS_PROP, CSS_VALUE_REGEX, DURATION_MIN, TRANSITION_END_FALLBACK_PADDING_MS } from './constants'; import { transitionEnd } from './transition-end'; +export const TRANSFORM_PROPS: {[key: string]: number} = { + 'translateX': 1, + 'translateY': 1, + 'translateZ': 1, + + 'scale': 1, + 'scaleX': 1, + 'scaleY': 1, + 'scaleZ': 1, + + 'rotate': 1, + 'rotateX': 1, + 'rotateY': 1, + 'rotateZ': 1, + + 'skewX': 1, + 'skewY': 1, + 'perspective': 1 +}; + const raf = window.requestAnimationFrame || ((f: Function) => f()); export class Animator { @@ -50,7 +70,6 @@ export class Animator { this._addEl(el); } } - return this; } @@ -117,7 +136,7 @@ export class Animator { /** * Set the easing for this animation. */ - easing(name: string) { + easing(name: string): Animator { this._easingName = name; return this; } @@ -125,7 +144,7 @@ export class Animator { /** * Set the easing for this reversed animation. */ - easingReverse(name: string) { + easingReverse(name: string): Animator { this._reversedEasingName = name; return this; } @@ -356,7 +375,15 @@ export class Animator { }); } - syncPlay() { + playAsync(opts?: PlayOptions): Promise { + return new Promise(resolve => { + this.onFinish(resolve, {oneTimeCallback: true, clearExistingCallacks: true }); + this.play(opts); + return this; + }); + } + + playSync() { // If the animation was already invalidated (it did finish), do nothing if (!this._destroyed) { const opts = { duration: 0 }; @@ -371,7 +398,7 @@ export class Animator { * DOM WRITE * RECURSION */ - _playInit(opts: PlayOptions|undefined) { + private _playInit(opts: PlayOptions|undefined) { // always default that an animation does not tween // a tween requires that an Animation class has an element // and that it has at least one FROM/TO effect @@ -1212,7 +1239,7 @@ export class Animator { * NO DOM */ _transEl(): HTMLElement|null { - // get the lowest level element that has an Animation + // get the lowest level element that has an Animator for (let i = 0; i < this._childAnimationTotal; i++) { const targetEl = this._childAnimations[i]._transEl(); if (targetEl) { @@ -1222,8 +1249,4 @@ export class Animator { return (this._hasTweenEffect && this._hasDur && this._elements && this._elementTotal > 0 ? this._elements[0] : null); } - - create() { - return new Animator(); - } } diff --git a/packages/core/src/components/animation-controller/constants.ts b/packages/core/src/components/animation-controller/constants.ts index 6780fecf21..ddafc093a7 100644 --- a/packages/core/src/components/animation-controller/constants.ts +++ b/packages/core/src/components/animation-controller/constants.ts @@ -1,5 +1,5 @@ -export let CSS_PROP = function(docEle: HTMLElement) { +export const CSS_PROP = function(docEle: HTMLElement) { // transform const transformProp = [ 'webkitTransform', @@ -25,27 +25,6 @@ export let CSS_PROP = function(docEle: HTMLElement) { }(document.documentElement); - -export let TRANSFORM_PROPS: {[key: string]: number} = { - 'translateX': 1, - 'translateY': 1, - 'translateZ': 1, - - 'scale': 1, - 'scaleX': 1, - 'scaleY': 1, - 'scaleZ': 1, - - 'rotate': 1, - 'rotateX': 1, - 'rotateY': 1, - 'rotateZ': 1, - - 'skewX': 1, - 'skewY': 1, - 'perspective': 1 -}; - -export let CSS_VALUE_REGEX = /(^-?\d*\.?\d*)(.*)/; -export let DURATION_MIN = 32; -export let TRANSITION_END_FALLBACK_PADDING_MS = 400; +export const CSS_VALUE_REGEX = /(^-?\d*\.?\d*)(.*)/; +export const DURATION_MIN = 32; +export const TRANSITION_END_FALLBACK_PADDING_MS = 400; diff --git a/packages/core/src/components/content/content.tsx b/packages/core/src/components/content/content.tsx index 04976acc5b..d8a6f676af 100644 --- a/packages/core/src/components/content/content.tsx +++ b/packages/core/src/components/content/content.tsx @@ -1,6 +1,5 @@ import { Component, Element, Listen, Method, Prop } from '@stencil/core'; import { Config, DomController } from '../../index'; -import { getPageElement } from '../../utils/helpers'; @Component({ tag: 'ion-content', @@ -141,3 +140,27 @@ export class Content { ]; } } + +function getParentElement(el: any) { + if (el.parentElement ) { + // normal element with a parent element + return el.parentElement; + } + if (el.parentNode && el.parentNode.host) { + // shadow dom's document fragment + return el.parentNode.host; + } + return null; +} + +function getPageElement(el: HTMLElement) { + const tabs = el.closest('ion-tabs'); + if (tabs) { + return tabs; + } + const page = el.closest('ion-app,ion-page,.ion-page,page-inner'); + if (page) { + return page; + } + return getParentElement(el); +} diff --git a/packages/core/src/components/datetime/datetime-util.ts b/packages/core/src/components/datetime/datetime-util.ts index 136c2a5e8c..f2437eed47 100644 --- a/packages/core/src/components/datetime/datetime-util.ts +++ b/packages/core/src/components/datetime/datetime-util.ts @@ -1,8 +1,8 @@ -import { isArray, isBlank, isString } from '../../utils/helpers'; +export function isBlank(val: any): val is null { return val === undefined || val === null; } export function renderDatetime(template: string, value: DatetimeData, locale: LocaleData) { - if (isBlank(value)) { + if (value === undefined) { return ''; } @@ -234,7 +234,7 @@ export function parseDate(val: any): DatetimeData { export function updateDate(existingData: DatetimeData, newData: any): boolean { if (newData && newData !== '') { - if (isString(newData)) { + if (typeof newData === 'string') { // new date is a string, and hopefully in the ISO format // convert it to our DatetimeData if a valid ISO newData = parseDate(newData); @@ -399,14 +399,14 @@ export function convertToArrayOfStrings(input: string | string[] | undefined | n return null; } - if (isString(input)) { + if (typeof input === 'string') { // convert the string to an array of strings // auto remove any [] characters input = input.replace(/\[|\]/g, '').split(','); } let values: string[]; - if (isArray(input)) { + if (Array.isArray(input)) { // trim up each string value values = input.map(val => val.toString().trim()); } @@ -424,14 +424,14 @@ export function convertToArrayOfStrings(input: string | string[] | undefined | n * an array of numbers, and clean up any user input */ export function convertToArrayOfNumbers(input: any[] | string | number, type: string): number[] { - if (isString(input)) { + if (typeof input === 'string') { // convert the string to an array of strings // auto remove any whitespace and [] characters input = input.replace(/\[|\]|\s/g, '').split(','); } let values: number[]; - if (isArray(input)) { + if (Array.isArray(input)) { // ensure each value is an actual number in the returned array values = input .map((num: any) => parseInt(num, 10)) diff --git a/packages/core/src/components/datetime/datetime.tsx b/packages/core/src/components/datetime/datetime.tsx index a949176351..bb567f358c 100644 --- a/packages/core/src/components/datetime/datetime.tsx +++ b/packages/core/src/components/datetime/datetime.tsx @@ -12,13 +12,14 @@ import { dateValueRange, daysInMonth, getValueFromFormat, + isBlank, parseDate, parseTemplate, renderDatetime, renderTextFormat, updateDate } from './datetime-util'; -import { clamp, isBlank, isObject } from '../../utils/helpers'; +import { clamp } from '../../utils/helpers'; import { Picker, PickerColumn, PickerController, PickerOptions } from '../../index'; @@ -590,7 +591,7 @@ export class Datetime { hasValue(): boolean { const val = this.datetimeValue; return val - && isObject(val) + && typeof val === 'object' && Object.keys(val).length > 0; } diff --git a/packages/core/src/components/gesture-controller/gesture-controller-utils.ts b/packages/core/src/components/gesture-controller/gesture-controller-utils.ts index 90d41b125e..4e3185d5f8 100644 --- a/packages/core/src/components/gesture-controller/gesture-controller-utils.ts +++ b/packages/core/src/components/gesture-controller/gesture-controller-utils.ts @@ -117,8 +117,3 @@ export interface BlockerConfig { disable?: string[]; disableScroll?: boolean; } - -export const BLOCK_ALL: BlockerConfig = { - disable: ['menu-swipe', 'goback-swipe'], - disableScroll: true -}; diff --git a/packages/core/src/components/gesture/gesture.tsx b/packages/core/src/components/gesture/gesture.tsx index 15c125540e..f0676b83b8 100644 --- a/packages/core/src/components/gesture/gesture.tsx +++ b/packages/core/src/components/gesture/gesture.tsx @@ -1,9 +1,13 @@ import { Component, Event, EventEmitter, EventListenerEnable, Listen, Prop, Watch } from '@stencil/core'; -import { ElementRef, assert, now, updateDetail } from '../../utils/helpers'; -import { BlockerDelegate, DomController, GestureDelegate } from '../../index'; -import { BLOCK_ALL } from '../gesture-controller/gesture-controller-utils'; +import { assert, now } from '../../utils/helpers'; +import { BlockerConfig, BlockerDelegate, DomController, GestureDelegate } from '../../index'; import { PanRecognizer } from './recognizers'; +export const BLOCK_ALL: BlockerConfig = { + disable: ['menu-swipe', 'goback-swipe'], + disableScroll: true +}; + @Component({ tag: 'ion-gesture' @@ -27,7 +31,7 @@ export class Gesture { @Prop({ context: 'enableListener' }) enableListener: EventListenerEnable; @Prop() disabled = false; - @Prop() attachTo: ElementRef = 'child'; + @Prop() attachTo: string|HTMLElement = 'child'; @Prop() autoBlockAll = false; @Prop() disableScroll = false; @Prop() direction = 'x'; @@ -478,7 +482,26 @@ export interface GestureDetail { data?: any; } - export interface GestureCallback { (detail?: GestureDetail): boolean|void; } + +function updateDetail(ev: any, detail: any) { + // get X coordinates for either a mouse click + // or a touch depending on the given event + let x = 0; + let y = 0; + if (ev) { + const changedTouches = ev.changedTouches; + if (changedTouches && changedTouches.length > 0) { + const touch = changedTouches[0]; + x = touch.clientX; + y = touch.clientY; + } else if (ev.pageX !== undefined) { + x = ev.pageX; + y = ev.pageY; + } + } + detail.currentX = x; + detail.currentY = y; +} diff --git a/packages/core/src/components/item-sliding/item-sliding.tsx b/packages/core/src/components/item-sliding/item-sliding.tsx index de0a190900..0b0004c50d 100644 --- a/packages/core/src/components/item-sliding/item-sliding.tsx +++ b/packages/core/src/components/item-sliding/item-sliding.tsx @@ -1,7 +1,6 @@ import { Component, Element, Event, EventEmitter, Method, State } from '@stencil/core'; import { GestureDetail } from '../../index'; -import { swipeShouldReset } from '../../utils/helpers'; import { ItemOptions } from '../item-options/item-options'; @@ -288,3 +287,22 @@ export class ItemSliding { ); } } + +/** @hidden */ +export function swipeShouldReset(isResetDirection: boolean, isMovingFast: boolean, isOnResetZone: boolean): boolean { + // The logic required to know when the sliding item should close (openAmount=0) + // depends on three booleans (isCloseDirection, isMovingFast, isOnCloseZone) + // and it ended up being too complicated to be written manually without errors + // so the truth table is attached below: (0=false, 1=true) + // isCloseDirection | isMovingFast | isOnCloseZone || shouldClose + // 0 | 0 | 0 || 0 + // 0 | 0 | 1 || 1 + // 0 | 1 | 0 || 0 + // 0 | 1 | 1 || 0 + // 1 | 0 | 0 || 0 + // 1 | 0 | 1 || 1 + // 1 | 1 | 0 || 1 + // 1 | 1 | 1 || 1 + // The resulting expression was generated by resolving the K-map (Karnaugh map): + return (!isMovingFast && isOnResetZone) || (isResetDirection && isMovingFast); +} diff --git a/packages/core/src/components/keyboard-controller/keyboard-controller.tsx b/packages/core/src/components/keyboard-controller/keyboard-controller.tsx index c72ed8323c..8f332f4503 100644 --- a/packages/core/src/components/keyboard-controller/keyboard-controller.tsx +++ b/packages/core/src/components/keyboard-controller/keyboard-controller.tsx @@ -1,6 +1,5 @@ import { Component, Event, EventEmitter, Prop} from '@stencil/core'; import { Config } from '../..'; -import { focusOutActiveElement, getDocument, getWindow, hasFocusedTextInput } from '../../utils/helpers'; import { KEY_TAB } from './keys'; let v2KeyboardWillShowHandler: () => void = null; @@ -75,11 +74,11 @@ export function onCloseImpl(keyboardController: KeyboardController, callback: Fu } export function componentDidLoadImpl(keyboardController: KeyboardController) { - focusOutline(getDocument(), keyboardController.config.get('focusOutline')); + focusOutline(document, keyboardController.config.get('focusOutline')); if (keyboardController.config.getBoolean('keyboardResizes', false)) { - listenV2(getWindow(), keyboardController); + listenV2(window, keyboardController); } else { - listenV1(getWindow(), keyboardController); + listenV1(window, keyboardController); } } @@ -183,5 +182,31 @@ export function focusOutline(doc: Document, value: boolean) { doc.addEventListener('keydown', keyDownHandler); } + + +function hasFocusedTextInput() { + const activeElement = document.activeElement; + if (isTextInput(activeElement) && activeElement.parentElement) { + return activeElement.parentElement.querySelector(':focus') === activeElement; + } + return false; +} + +const NON_TEXT_INPUT_REGEX = /^(radio|checkbox|range|file|submit|reset|color|image|button)$/i; + +function isTextInput(el: any) { + return !!el && + (el.tagName === 'TEXTAREA' + || el.contentEditable === 'true' + || (el.tagName === 'INPUT' && !(NON_TEXT_INPUT_REGEX.test(el.type)))); +} + + +function focusOutActiveElement() { + const activeElement = document.activeElement as HTMLElement; + activeElement && activeElement.blur && activeElement.blur(); +} + + const KEYBOARD_CLOSE_POLLING = 150; const KEYBOARD_POLLING_CHECKS_MAX = 100; diff --git a/packages/core/src/components/menu/menu.tsx b/packages/core/src/components/menu/menu.tsx index 6d1040932d..811dfed921 100644 --- a/packages/core/src/components/menu/menu.tsx +++ b/packages/core/src/components/menu/menu.tsx @@ -1,6 +1,6 @@ import { Component, Element, Event, EventEmitter, EventListenerEnable, Listen, Method, Prop, State, Watch } from '@stencil/core'; import { Animation, Config, GestureDetail } from '../../index'; -import { Side, assert, checkEdgeSide, isRightSide } from '../../utils/helpers'; +import { Side, assert, isRightSide } from '../../utils/helpers'; @Component({ tag: 'ion-menu', @@ -248,19 +248,13 @@ export class Menu { } private startAnimation(shouldOpen: boolean, animated: boolean): Promise { - let done; - const promise = new Promise(resolve => done = resolve); - const ani = this.animation - .onFinish(done, {oneTimeCallback: true, clearExistingCallacks: true }) - .reverse(!shouldOpen); - + const ani = this.animation.reverse(!shouldOpen); if (animated) { - ani.play(); + return ani.playAsync(); } else { - ani.syncPlay(); + ani.playSync(); + return Promise.resolve(ani); } - - return promise; } private canSwipe(): boolean { @@ -477,6 +471,14 @@ function computeDelta(deltaX: number, isOpen: boolean, isRightSide: boolean): nu return Math.max(0, (isOpen !== isRightSide) ? -deltaX : deltaX); } +function checkEdgeSide(posX: number, isRightSide: boolean, maxEdgeStart: number): boolean { + if (isRightSide) { + return posX >= window.innerWidth - maxEdgeStart; + } else { + return posX <= maxEdgeStart; + } +} + const SHOW_MENU = 'show-menu'; const SHOW_BACKDROP = 'show-backdrop'; const MENU_CONTENT_OPEN = 'menu-content-open'; diff --git a/packages/core/src/components/nav/nav.tsx b/packages/core/src/components/nav/nav.tsx index 734683fbbc..6e50c16645 100644 --- a/packages/core/src/components/nav/nav.tsx +++ b/packages/core/src/components/nav/nav.tsx @@ -19,7 +19,7 @@ import { import { ViewController, isViewController } from './view-controller'; import { AnimationOptions, Config, DomController, GestureDetail, NavOutlet } from '../..'; -import { assert, isBlank, isNumber } from '../../utils/helpers'; +import { assert } from '../../utils/helpers'; import { TransitionController } from './transition-controller'; import { Transition } from './transition'; @@ -136,7 +136,7 @@ export class NavControllerBase implements NavOutlet { if (isViewController(indexOrViewCtrl)) { config.removeView = indexOrViewCtrl; config.removeStart = 1; - } else if (isNumber(indexOrViewCtrl)) { + } else if (typeof indexOrViewCtrl === 'number') { config.removeStart = indexOrViewCtrl + 1; } return this._queueTrns(config, done); @@ -186,7 +186,7 @@ export class NavControllerBase implements NavOutlet { @Method() setPages(pages: any[], opts?: NavOptions, done?: TransitionDoneFn): Promise { - if (isBlank(opts)) { + if (!opts) { opts = {}; } // if animation wasn't set to true then default it to NOT animate diff --git a/packages/core/src/components/reorder-group/reorder-group.tsx b/packages/core/src/components/reorder-group/reorder-group.tsx index e264d6be1f..3c8341a0b4 100644 --- a/packages/core/src/components/reorder-group/reorder-group.tsx +++ b/packages/core/src/components/reorder-group/reorder-group.tsx @@ -1,8 +1,6 @@ import { Component, Element, Prop, State, Watch } from '@stencil/core'; import { DomController, 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; const SCROLL_JUMP = 10; @@ -148,7 +146,7 @@ export class ReorderGroup { // // Get coordinate const top = this.containerTop - scroll; const bottom = this.containerBottom - scroll; - const currentY = clamp(top, ev.currentY, bottom); + const currentY = Math.max(top, Math.min(ev.currentY, bottom)); const deltaY = scroll + currentY - ev.startY; const normalizedY = currentY - top; const toIndex = this.itemIndexForTop(normalizedY); @@ -161,7 +159,7 @@ export class ReorderGroup { } // Update selected item position - (selectedItem.style as any)[CSS_PROP.transformProp] = `translateY(${deltaY}px)`; + selectedItem.style.transform = `translateY(${deltaY}px)`; } private onDragEnd() { @@ -182,9 +180,8 @@ export class ReorderGroup { this.containerEl.insertBefore(selectedItem, ref); const len = children.length; - const transform = CSS_PROP.transformProp; for (let i = 0; i < len; i++) { - children[i].style[transform] = ''; + children[i].style['transform'] = ''; } const reorderInactive = () => { @@ -223,7 +220,6 @@ export class ReorderGroup { private reorderMove(fromIndex: number, toIndex: number) { const itemHeight = this.selectedItemHeight; const children = this.containerEl.children; - const transform = CSS_PROP.transformProp; for (let i = 0; i < children.length; i++) { const style = (children[i] as any).style; let value = ''; @@ -232,7 +228,7 @@ export class ReorderGroup { } else if (i < fromIndex && i >= toIndex) { value = `translateY(${itemHeight}px)`; } - style[transform] = value; + style['transform'] = value; } } @@ -302,3 +298,11 @@ function findReorderItem(node: HTMLElement, container: HTMLElement): HTMLElement } return null; } + +export function reorderArray(array: any[], indexes: {from: number, to: number}): any[] { + const element = array[indexes.from]; + array.splice(indexes.from, 1); + array.splice(indexes.to, 0, element); + return array; +} + diff --git a/packages/core/src/components/status-tap/status-tap.tsx b/packages/core/src/components/status-tap/status-tap.tsx index 4c6b81c4bd..2596c0f3ba 100644 --- a/packages/core/src/components/status-tap/status-tap.tsx +++ b/packages/core/src/components/status-tap/status-tap.tsx @@ -30,9 +30,9 @@ export class StatusTap { return null; } return el.closest('ion-scroll'); - }).then(([scroll]: HTMLIonScrollElement[]) => { - return scroll.componentOnReady(); - }).then((scroll: HTMLIonScrollElement) => { + }) + .then(scroll => scroll.componentOnReady()) + .then(scroll => { return domControllerAsync(this.dom.write, () => { return scroll.scrollToTop(this.duration); }); diff --git a/packages/core/src/global/config-controller.ts b/packages/core/src/global/config-controller.ts index ead33fdad1..d9a988e6ce 100644 --- a/packages/core/src/global/config-controller.ts +++ b/packages/core/src/global/config-controller.ts @@ -45,8 +45,8 @@ export function createConfigController(configObj: any, platforms: PlatformConfig } return { - get: get, - getBoolean: getBoolean, - getNumber: getNumber + get, + getBoolean, + getNumber }; } diff --git a/packages/core/src/global/events.ts b/packages/core/src/global/events.ts deleted file mode 100644 index a30184a3ee..0000000000 --- a/packages/core/src/global/events.ts +++ /dev/null @@ -1,15 +0,0 @@ - - -export function setupEvents(win: Window, doc: Document) { - - win.addEventListener('statusTap', () => { - const centerElm = doc.elementFromPoint(win.innerWidth / 2, win.innerHeight / 2); - if (centerElm) { - const scrollElm = centerElm.closest('ion-scroll'); - if (scrollElm) { - scrollElm.componentOnReady(() => scrollElm.scrollToTop(300)); - } - } - }); - -} diff --git a/packages/core/src/global/ionic-global.ts b/packages/core/src/global/ionic-global.ts index 2e1c9e89c9..9f71ea511f 100644 --- a/packages/core/src/global/ionic-global.ts +++ b/packages/core/src/global/ionic-global.ts @@ -2,7 +2,6 @@ import 'ionicons'; import { createConfigController } from './config-controller'; import { createDomControllerClient } from './dom-controller'; import { PLATFORM_CONFIGS, detectPlatforms, readQueryParam } from './platform-configs'; -import { setupEvents } from './events'; const Ionic = (window as any).Ionic = (window as any).Ionic || {}; @@ -31,8 +30,6 @@ Ionic.config = Context.config = createConfigController( Context.platforms ); -setupEvents(window, document); - // first see if the mode was set as an attribute on // which could have been set by the user, or by prerendering // otherwise get the mode via config settings, and fallback to md diff --git a/packages/core/src/index.d.ts b/packages/core/src/index.d.ts index fc1b63d894..1757068bba 100644 --- a/packages/core/src/index.d.ts +++ b/packages/core/src/index.d.ts @@ -72,7 +72,7 @@ export { RadioGroup } from './components/radio-group/radio-group'; export { Radio, HTMLIonRadioElementEvent } from './components/radio/radio'; export { Range, RangeEvent } from './components/range/range'; export { RangeKnob } from './components/range-knob/range-knob'; -export { ReorderGroup } from './components/reorder-group/reorder-group'; +export { ReorderGroup, reorderArray } from './components/reorder-group/reorder-group'; export { RouteNode, RouteTree, @@ -109,6 +109,7 @@ export { PlatformConfig } from './global/platform-configs'; export * from './components'; export { DomController, RafCallback } from './global/dom-controller'; +export { FrameworkDelegate, FrameworkMountingData } from './utils/dom-framework-delegate'; export interface Config { get: (key: string, fallback?: any) => any; @@ -136,22 +137,6 @@ export interface OverlayDismissEventDetail { role?: string; } -export interface FrameworkDelegate { - attachViewToDom(elementOrContainerToMountTo: any, elementOrComponentToMount: any, propsOrDataObj?: any, classesToAdd?: string[], escapeHatch?: any): Promise; - removeViewFromDom(elementOrContainerToUnmountFrom: any, elementOrComponentToUnmount: any, escapeHatch?: any): Promise; -} - -export interface RouterDelegate { - pushUrlState(urlSegment: string, stateObject?: any, title?: string): Promise; - popUrlState(): Promise; -} - -export interface FrameworkMountingData { - element: HTMLElement; - component: any; - data: any; -} - declare global { namespace JSXElements { diff --git a/packages/core/src/utils/dom-framework-delegate.ts b/packages/core/src/utils/dom-framework-delegate.ts index 1ce3e98272..c1a454f683 100644 --- a/packages/core/src/utils/dom-framework-delegate.ts +++ b/packages/core/src/utils/dom-framework-delegate.ts @@ -1,11 +1,21 @@ -import { FrameworkDelegate, FrameworkMountingData, } from '../index'; -import { isString } from './helpers'; + +export interface FrameworkDelegate { + attachViewToDom(elementOrContainerToMountTo: any, elementOrComponentToMount: any, propsOrDataObj?: any, classesToAdd?: string[], escapeHatch?: any): Promise; + removeViewFromDom(elementOrContainerToUnmountFrom: any, elementOrComponentToUnmount: any, escapeHatch?: any): Promise; +} + + +export interface FrameworkMountingData { + element: HTMLElement; + component: any; + data: any; +} export class DomFrameworkDelegate implements FrameworkDelegate { attachViewToDom(parentElement: HTMLElement, tagOrElement: string | HTMLElement, data: any = {}, classesToAdd: string[] = []): Promise { return new Promise((resolve) => { - const usersElement = (isString(tagOrElement) ? document.createElement(tagOrElement) : tagOrElement); + const usersElement = (typeof tagOrElement === 'string' ? document.createElement(tagOrElement) : tagOrElement); Object.assign(usersElement, data); if (classesToAdd.length) { diff --git a/packages/core/src/utils/dom-router-delegate.ts b/packages/core/src/utils/dom-router-delegate.ts deleted file mode 100644 index 815c356656..0000000000 --- a/packages/core/src/utils/dom-router-delegate.ts +++ /dev/null @@ -1,16 +0,0 @@ - - -import { RouterDelegate } from '../index'; - -export class DomRouterDelegate implements RouterDelegate { - - pushUrlState(urlSegment: string, stateObject: any = null, title = ''): Promise { - history.pushState(stateObject, title, urlSegment); - return Promise.resolve(); - } - - popUrlState() { - history.back(); - return Promise.resolve(); - } -} diff --git a/packages/core/src/utils/helpers.ts b/packages/core/src/utils/helpers.ts index 8cc49c51a8..b76b884dde 100644 --- a/packages/core/src/utils/helpers.ts +++ b/packages/core/src/utils/helpers.ts @@ -1,4 +1,3 @@ -import { Animation } from '../index'; import { EventEmitter } from '@stencil/core'; export function clamp(min: number, n: number, max: number) { @@ -7,43 +6,6 @@ export function clamp(min: number, n: number, max: number) { export function isDef(v: any): boolean { return v !== undefined && v !== null; } -export function isUndef(v: any): boolean { return v === undefined || v === null; } - -export function isArray(v: any): v is Array { return Array.isArray(v); } - -export function isObject(v: any): v is Object { return v !== null && typeof v === 'object'; } - -export function isBoolean(v: any): v is (boolean) { return typeof v === 'boolean'; } - -export function isString(v: any): v is (string) { return typeof v === 'string'; } - -export function isNumber(v: any): v is (number) { return typeof v === 'number'; } - -export function isFunction(v: any): v is (Function) { return typeof v === 'function'; } - -export function isStringOrNumber(v: any): v is (string | number) { return isString(v) || isNumber(v); } - -export function isBlank(val: any): val is null { return val === undefined || val === null; } - -/** @hidden */ -export function isCheckedProperty(a: any, b: any): boolean { - if (a === undefined || a === null || a === '') { - return (b === undefined || b === null || b === ''); - - } else if (a === true || a === 'true') { - return (b === true || b === 'true'); - - } else if (a === false || a === 'false') { - return (b === false || b === 'false'); - - } else if (a === 0 || a === '0') { - return (b === 0 || b === '0'); - } - - // not using strict comparison on purpose - return (a == b); // tslint:disable-line -} - export function assert(actual: any, reason: string) { if (!actual) { const message = 'ASSERT: ' + reason; @@ -63,11 +25,6 @@ export function autoFocus(containerEl: HTMLElement): HTMLElement|null { return null; } -export function toDashCase(str: string) { - return str.replace(/([A-Z])/g, (g) => '-' + g[0].toLowerCase()); -} - - export function now(ev: UIEvent) { return ev.timeStamp || Date.now(); } @@ -87,93 +44,8 @@ export function pointerCoord(ev: any): {x: number, y: number} { } return {x: 0, y: 0}; } - -export function updateDetail(ev: any, detail: any) { - // get X coordinates for either a mouse click - // or a touch depending on the given event - let x = 0; - let y = 0; - if (ev) { - const changedTouches = ev.changedTouches; - if (changedTouches && changedTouches.length > 0) { - const touch = changedTouches[0]; - x = touch.clientX; - y = touch.clientY; - } else if (ev.pageX !== undefined) { - x = ev.pageX; - y = ev.pageY; - } - } - detail.currentX = x; - detail.currentY = y; -} - -export type ElementRef = 'child' | 'parent' | 'body' | 'document' | 'window'; - -export function getElementReference(el: any, ref: ElementRef) { - if (ref === 'child') { - return el.firstElementChild; - } - if (ref === 'parent') { - return getParentElement(el) || el; - } - if (ref === 'body') { - return el.ownerDocument.body; - } - if (ref === 'document') { - return el.ownerDocument; - } - if (ref === 'window') { - return el.ownerDocument.defaultView; - } - return el; -} - -export function getParentElement(el: any) { - if (el.parentElement ) { - // normal element with a parent element - return el.parentElement; - } - if (el.parentNode && el.parentNode.host) { - // shadow dom's document fragment - return el.parentNode.host; - } - return null; -} - -export function getPageElement(el: HTMLElement) { - const tabs = el.closest('ion-tabs'); - if (tabs) { - return tabs; - } - const page = el.closest('ion-app,ion-page,.ion-page,page-inner'); - if (page) { - return page; - } - return getParentElement(el); -} - -export function applyStyles(el: HTMLElement, styles: {[styleProp: string]: string|number}) { - const styleProps = Object.keys(styles); - - if (el) { - for (let i = 0; i < styleProps.length; i++) { - (el.style as any)[styleProps[i]] = styles[styleProps[i]]; - } - } -} - -/** @hidden */ export type Side = 'left' | 'right' | 'start' | 'end'; -export function checkEdgeSide(posX: number, isRightSide: boolean, maxEdgeStart: number): boolean { - if (isRightSide) { - return posX >= window.innerWidth - maxEdgeStart; - } else { - return posX <= maxEdgeStart; - } -} - /** * @hidden * Given a side, return if it should be on the right @@ -193,85 +65,6 @@ export function isRightSide(side: Side, defaultRight = false): boolean { } } -/** @hidden */ -export function swipeShouldReset(isResetDirection: boolean, isMovingFast: boolean, isOnResetZone: boolean): boolean { - // The logic required to know when the sliding item should close (openAmount=0) - // depends on three booleans (isCloseDirection, isMovingFast, isOnCloseZone) - // and it ended up being too complicated to be written manually without errors - // so the truth table is attached below: (0=false, 1=true) - // isCloseDirection | isMovingFast | isOnCloseZone || shouldClose - // 0 | 0 | 0 || 0 - // 0 | 0 | 1 || 1 - // 0 | 1 | 0 || 0 - // 0 | 1 | 1 || 0 - // 1 | 0 | 0 || 0 - // 1 | 0 | 1 || 1 - // 1 | 1 | 0 || 1 - // 1 | 1 | 1 || 1 - // The resulting expression was generated by resolving the K-map (Karnaugh map): - return (!isMovingFast && isOnResetZone) || (isResetDirection && isMovingFast); -} - -export function getOrAppendElement(tagName: string): Element { - const element = document.querySelector(tagName); - if (element) { - return element; - } - const tmp = document.createElement(tagName); - document.body.appendChild(tmp); - return tmp; -} - -export function getWindow() { - return window; -} - -export function getDocument() { - return document; -} - -export function getActiveElement(): HTMLElement { - return getDocument()['activeElement'] as HTMLElement; -} - -export function focusOutActiveElement() { - const activeElement = getActiveElement(); - activeElement && activeElement.blur && activeElement.blur(); -} - -export function isTextInput(el: any) { - return !!el && - (el.tagName === 'TEXTAREA' - || el.contentEditable === 'true' - || (el.tagName === 'INPUT' && !(NON_TEXT_INPUT_REGEX.test(el.type)))); -} -export const NON_TEXT_INPUT_REGEX = /^(radio|checkbox|range|file|submit|reset|color|image|button)$/i; - -export function hasFocusedTextInput() { - const activeElement = getActiveElement(); - if (isTextInput(activeElement) && activeElement.parentElement) { - return activeElement.parentElement.querySelector(':focus') === activeElement; - } - return false; -} - -/** - * @private - */ -export function reorderArray(array: any[], indexes: {from: number, to: number}): any[] { - const element = array[indexes.from]; - array.splice(indexes.from, 1); - array.splice(indexes.to, 0, element); - return array; -} - -export function playAnimationAsync(animation: Animation): Promise { - return new Promise(resolve => { - animation.onFinish(resolve); - animation.play(); - }); -} - export function domControllerAsync(domControllerFunction: Function, callback?: Function): Promise { return new Promise((resolve) => { domControllerFunction(() => { @@ -304,41 +97,3 @@ export function debounce(func: Function, wait = 0) { timer = setTimeout(func, wait, ...args); }; } - -export function asyncRaf(): Promise { - return new Promise(resolve => requestAnimationFrame(resolve)); -} - -export function getNavAsChildIfExists(element: HTMLElement): HTMLIonNavElement|null { - for (let i = 0; i < element.children.length; i++) { - if (element.children[i].tagName.toLowerCase() === 'ion-nav') { - return element.children[i] as any as HTMLIonNavElement; - } - } - return null; -} - -export function normalizeUrl(url: string): string { - url = url.trim(); - if (url.charAt(0) !== '/') { - // ensure first char is a / - url = '/' + url; - } - if (url.length > 1 && url.charAt(url.length - 1) === '/') { - // ensure last char is not a / - url = url.substr(0, url.length - 1); - } - return url; -} - -export function isParentTab(element: HTMLElement) { - return element.parentElement.tagName.toLowerCase() === 'ion-tab'; -} - -export function getIonApp(): Promise { - const appElement = document.querySelector('ion-app'); - if (!appElement) { - return Promise.resolve(null); - } - return appElement.componentOnReady(); -} diff --git a/packages/core/src/utils/overlays.ts b/packages/core/src/utils/overlays.ts index 24720cfad7..ac801583ae 100644 --- a/packages/core/src/utils/overlays.ts +++ b/packages/core/src/utils/overlays.ts @@ -1,5 +1,4 @@ import { Animation, AnimationBuilder } from '..'; -import { playAnimationAsync } from './helpers'; let lastId = 1; @@ -70,7 +69,7 @@ export function overlayAnimation( if (!animate) { animation.duration(0); } - return playAnimationAsync(animation); + return animation.playAsync(); }).then((animation) => { animation.destroy(); overlay.animation = undefined;