refactor(all): use ts strict mode

This commit is contained in:
Manu Mtz.-Almeida
2018-02-05 18:34:37 +01:00
parent 513b0216dd
commit 2f8a027e2f
35 changed files with 3630 additions and 293 deletions

File diff suppressed because it is too large Load Diff

View File

@ -32,6 +32,9 @@ export class ActionSheetController {
dismiss(data?: any, role?: any, actionSheetId = -1) {
actionSheetId = actionSheetId >= 0 ? actionSheetId : getHighestId();
const actionSheet = actionSheets.get(actionSheetId);
if (!actionSheet) {
return Promise.reject('action-sheet does not exist');
}
return actionSheet.dismiss(data, role);
}

View File

@ -33,7 +33,7 @@ export class ActionSheet {
color: string;
actionSheetId: number;
private animation: Animation;
private animation: Animation | null = null;
@Element() private el: HTMLElement;
@ -232,22 +232,17 @@ export class ActionSheet {
}
render() {
let cancelButton: ActionSheetButton;
const buttons = this.buttons
.map(b => {
if (typeof b === 'string') {
b = { text: b };
}
if (!b.cssClass) {
b.cssClass = '';
}
if (b.role === 'cancel') {
cancelButton = b;
return null;
}
return b;
})
.filter(b => b !== null);
const allButtons = this.buttons.map(b => {
if (typeof b === 'string') {
b = { text: b };
}
if (!b.cssClass) {
b.cssClass = '';
}
return b;
});
const cancelButton = allButtons.find(b => b.role === 'cancel');
const buttons = allButtons.filter(b => b.role !== 'cancel');
return [
<ion-backdrop

View File

@ -32,6 +32,9 @@ export class AlertController {
dismiss(data?: any, role?: any, alertId = -1) {
alertId = alertId >= 0 ? alertId : getHighestId();
const alert = alerts.get(alertId);
if (!alert) {
return Promise.reject('alert does not exist');
}
return alert.dismiss(data, role);
}

View File

@ -26,9 +26,9 @@ export class Alert {
color: string;
alertId: number;
private animation: Animation;
private animation: Animation | null = null;
private activeId: string;
private inputType: string;
private inputType: string | null = null;
private hdrId: string;
@Element() private el: HTMLElement;
@ -376,39 +376,34 @@ export class Alert {
'alert-button-group-vertical': this.buttons.length > 2
};
const buttons = this.buttons
.map(b => {
if (typeof b === 'string') {
b = { text: b };
}
return b;
})
.filter(b => b !== null);
const buttons = this.buttons.map(b => {
if (typeof b === 'string') {
return { text: b } as AlertButton;
}
return b;
})
.filter(b => b !== null);
this.inputs = this.inputs.map((i, index) => {
return {
type: i.type || 'text',
name: i.name ? i.name : index + '',
placeholder: i.placeholder ? i.placeholder : '',
value: i.value ? i.value : '',
label: i.label,
checked: !!i.checked,
disabled: !!i.disabled,
id: i.id ? i.id : `alert-input-${this.alertId}-${index}`,
handler: i.handler ? i.handler : null,
min: i.min ? i.min : null,
max: i.max ? i.max : null
} as AlertInput;
}).filter(i => i !== null);
// An alert can be created with several different inputs. Radios,
// checkboxes and inputs are all accepted, but they cannot be mixed.
const inputTypes: string[] = [];
this.inputs = this.inputs
.map((i, index) => {
const r: AlertInput = {
type: i.type || 'text',
name: i.name ? i.name : index + '',
placeholder: i.placeholder ? i.placeholder : '',
value: i.value ? i.value : '',
label: i.label,
checked: !!i.checked,
disabled: !!i.disabled,
id: i.id ? i.id : `alert-input-${this.alertId}-${index}`,
handler: i.handler ? i.handler : null,
min: i.min ? i.min : null,
max: i.max ? i.max : null
};
return r;
})
.filter(i => i !== null);
this.inputs.forEach(i => {
this.inputs.forEach(i => {
if (inputTypes.indexOf(i.type) < 0) {
inputTypes.push(i.type);
}
@ -418,7 +413,7 @@ export class Alert {
console.warn(`Alert cannot mix input types: ${(inputTypes.join('/'))}. Please see alert docs for more info.`);
}
this.inputType = inputTypes.length ? inputTypes[0] : null;
this.inputType = inputTypes.length > 0 ? inputTypes[0] : null;
return [
<ion-backdrop
@ -428,24 +423,19 @@ export class Alert {
<div class='alert-wrapper'>
<div class='alert-head'>
{this.title
? <h2 id={hdrId} class='alert-title'>{this.title}</h2>
: null}
? <h2 id={hdrId} class='alert-title'>{this.title}</h2>
: null}
{this.subTitle
? <h2 id={subHdrId} class='alert-sub-title'>{this.subTitle}</h2>
: null}
? <h2 id={subHdrId} class='alert-sub-title'>{this.subTitle}</h2>
: null}
</div>
<div id={msgId} class='alert-message' innerHTML={this.message}></div>
<div id={msgId} class='alert-message' innerHTML={this.message}></div>
{(() => {
switch (this.inputType) {
case 'checkbox':
return this.renderCheckbox(this.inputs);
case 'radio':
return this.renderRadio(this.inputs);
default:
return this.renderInput(this.inputs);
case 'checkbox': return this.renderCheckbox(this.inputs);
case 'radio': return this.renderRadio(this.inputs);
default: return this.renderInput(this.inputs);
}
})()}
@ -457,7 +447,7 @@ export class Alert {
</span>
</button>
)}
</div>
</div>
</div>
];
}
@ -483,8 +473,8 @@ export interface AlertOptions {
}
export interface AlertInput {
type?: string;
name?: string | number;
type: string;
name: string | number;
placeholder?: string;
value?: string;
label?: string;
@ -497,7 +487,7 @@ export interface AlertInput {
}
export interface AlertButton {
text?: string;
text: string;
role?: string;
cssClass?: string;
handler?: (value: any) => boolean|void;

View File

@ -74,6 +74,6 @@ export interface EffectProperty {
export interface EffectState {
val: any;
num: number;
num: number|null;
effectUnit: string;
}

View File

@ -14,25 +14,25 @@ export class Animator {
private _beforeStyles: { [property: string]: any; };
private _childAnimations: Animator[];
private _childAnimationTotal: number;
private _duration: number = null;
private _easingName: string = null;
private _elements: HTMLElement[] = null;
private _duration: number|null = null;
private _easingName: string|null = null;
private _elements: HTMLElement[]|null = null;
private _elementTotal: number;
private _fxProperties: EffectProperty[];
private _hasDur: boolean;
private _hasTweenEffect: boolean;
private _isAsync: boolean;
private _isReverse: boolean;
private _onFinishCallbacks: Function[];
private _onFinishOneTimeCallbacks: Function[];
private _onFinishCallbacks: Function[] | undefined;
private _onFinishOneTimeCallbacks: Function[] | undefined;
private _readCallbacks: Function[];
private _reversedEasingName: string = null;
private _reversedEasingName: string|undefined;
private _timerId: any;
private _unregisterTrnsEnd: Function;
private _unregisterTrnsEnd: Function | undefined;
private _writeCallbacks: Function[];
private _destroyed = false;
parent: Animator;
parent: Animator|undefined;
opts: AnimationOptions;
hasChildren = false;
isPlaying = false;
@ -106,7 +106,7 @@ export class Animator {
* Get the easing of this animation. If this animation does
* not have an easing, then it'll get the easing from its parent.
*/
getEasing(): string {
getEasing(): string|null {
if (this._isReverse && this._reversedEasingName) {
return this._reversedEasingName;
}
@ -163,14 +163,14 @@ export class Animator {
* NO DOM
*/
private _getProp(name: string): EffectProperty {
private _getProp(name: string): EffectProperty | undefined {
if (this._fxProperties) {
return this._fxProperties.find(function(prop) { return prop.effectName === name; });
return this._fxProperties.find(prop => prop.effectName === name);
} else {
this._fxProperties = [];
}
return null;
return undefined;
}
private _addProp(state: string, prop: string, val: any): EffectProperty {
@ -361,7 +361,7 @@ export class Animator {
* DOM WRITE
* RECURSION
*/
_playInit(opts: PlayOptions) {
_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
@ -393,7 +393,7 @@ export class Animator {
* NO RECURSION
* ROOT ANIMATION
*/
_playDomInspect(opts: PlayOptions) {
_playDomInspect(opts: PlayOptions|undefined) {
const self = this;
// fire off all the "before" function that have DOM READS in them
// elements will be in the DOM, however visibily hidden
@ -426,7 +426,7 @@ export class Animator {
* DOM WRITE
* RECURSION
*/
_playProgress(opts: PlayOptions) {
_playProgress(opts: PlayOptions|undefined) {
const children = this._childAnimations;
for (let i = 0; i < this._childAnimationTotal; i++) {
// ******** DOM WRITE ****************
@ -558,7 +558,7 @@ export class Animator {
* NO DOM
* RECURSION
*/
_hasDuration(opts: PlayOptions) {
_hasDuration(opts: PlayOptions|undefined) {
if (this.getDuration(opts) > DURATION_MIN) {
return true;
}
@ -620,10 +620,11 @@ export class Animator {
_progress(stepValue: number) {
// bread 'n butter
let val: any;
const elements = this._elements;
const effects = this._fxProperties;
const nuElements = this._elementTotal;
if (!effects || !nuElements || this._destroyed) {
if (!elements || !effects || !nuElements || this._destroyed) {
return;
}
@ -634,7 +635,6 @@ export class Animator {
let i = 0;
let j = 0;
let finalTransform = '';
const elements = this._elements;
let fx: EffectProperty;
for (i = 0; i < effects.length; i++) {
@ -701,12 +701,13 @@ export class Animator {
*/
_setTrans(dur: number, forcedLinearEasing: boolean) {
// Transition is not enabled if there are not effects
if (!this._fxProperties) {
const elements = this._elements;
const nuElements = this._elementTotal;
if (!elements || !this._fxProperties || nuElements === 0) {
return;
}
// set the TRANSITION properties inline on the element
const elements = this._elements;
const easing = (forcedLinearEasing ? 'linear' : this.getEasing());
const durString = dur + 'ms';
const cssTransform = CSS_PROP.transitionProp;
@ -714,7 +715,7 @@ export class Animator {
const cssTransitionTimingFn = CSS_PROP.transitionTimingFnProp;
let eleStyle: any;
for (let i = 0; i < this._elementTotal; i++) {
for (let i = 0; i < nuElements; i++) {
eleStyle = elements[i].style;
if (dur > 0) {
// ******** DOM WRITE ****************
@ -760,26 +761,27 @@ export class Animator {
* RECURSION
*/
_setBeforeStyles() {
let i: number, j: number;
let j: number;
const children = this._childAnimations;
for (i = 0; i < this._childAnimationTotal; i++) {
for (let i = 0; i < this._childAnimationTotal; i++) {
children[i]._setBeforeStyles();
}
const elements = this._elements;
const nuElements = this._elementTotal;
// before the animations have started
// only set before styles if animation is not reversed
if (this._isReverse) {
if (!elements || nuElements === 0 || this._isReverse) {
return;
}
const addClasses = this._beforeAddClasses;
const removeClasses = this._beforeRemoveClasses;
let el: HTMLElement;
let elementClassList: DOMTokenList;
let prop: string;
for (i = 0; i < this._elementTotal; i++) {
el = this._elements[i];
elementClassList = el.classList;
for (let i = 0; i < nuElements; i++) {
const el = elements[i];
const elementClassList = el.classList;
// css classes to add before the animation
if (addClasses) {
@ -859,6 +861,9 @@ export class Animator {
let el: HTMLElement;
let elementClassList: DOMTokenList;
const elements = this._elements;
if (!elements) {
return;
}
let prop: string;
for (i = 0; i < this._elementTotal; i++) {
@ -1012,13 +1017,8 @@ export class Animator {
/**
* End the progress animation.
*/
progressEnd(shouldComplete: boolean, currentStepValue: number, dur: number) {
const self = this;
if (dur === undefined) {
dur = -1;
}
if (self._isReverse) {
progressEnd(shouldComplete: boolean, currentStepValue: number, dur?: number) {
if (this._isReverse) {
// if the animation is going in reverse then
// flip the step value: 0 becomes 1, 1 becomes 0
currentStepValue = ((currentStepValue * -1) + 1);
@ -1026,28 +1026,31 @@ export class Animator {
const stepValue = shouldComplete ? 1 : 0;
const diff = Math.abs(currentStepValue - stepValue);
if (dur === undefined) {
dur = -1;
}
if (diff < 0.05) {
dur = 0;
} else if (dur < 0) {
dur = self._duration;
dur = this._duration;
}
self._isAsync = (dur > 30);
this._isAsync = (dur > 30);
self._progressEnd(shouldComplete, stepValue, dur, self._isAsync);
this._progressEnd(shouldComplete, stepValue, dur, this._isAsync);
if (self._isAsync) {
if (this._isAsync) {
// for the root animation only
// set the async TRANSITION END event
// and run onFinishes when the transition ends
// ******** DOM WRITE ****************
self._asyncEnd(dur, shouldComplete);
this._asyncEnd(dur, shouldComplete);
// this animation has a duration so we need another RAF
// for the CSS TRANSITION properties to kick in
if (!self._destroyed) {
window.requestAnimationFrame(function() {
self._playToStep(stepValue);
if (!this._destroyed) {
window.requestAnimationFrame(() => {
this._playToStep(stepValue);
});
}
}
@ -1182,7 +1185,7 @@ export class Animator {
this._writeCallbacks.length = 0;
}
this.parent = null;
this.parent = undefined;
if (this._childAnimations) {
this._childAnimations.length = this._childAnimationTotal = 0;
@ -1198,18 +1201,16 @@ export class Animator {
/**
* NO DOM
*/
_transEl(): HTMLElement {
_transEl(): HTMLElement|null {
// get the lowest level element that has an Animation
let targetEl: HTMLElement;
for (let i = 0; i < this._childAnimationTotal; i++) {
targetEl = this._childAnimations[i]._transEl();
const targetEl = this._childAnimations[i]._transEl();
if (targetEl) {
return targetEl;
}
}
return (this._hasTweenEffect && this._hasDur && this._elementTotal ? this._elements[0] : null);
return (this._hasTweenEffect && this._hasDur && this._elements && this._elementTotal > 0 ? this._elements[0] : null);
}
create() {

View File

@ -1,42 +1,27 @@
export let CSS_PROP = function(docEle: HTMLElement) {
const css: {
transformProp?: string;
transitionProp?: string;
transitionDurationProp?: string;
transitionTimingFnProp?: string;
} = {};
// transform
let i: number;
let keys = ['webkitTransform', '-webkit-transform', 'webkit-transform', 'transform'];
const transformProp = [
'webkitTransform',
'-webkit-transform',
'webkit-transform',
'transform'
].find(key => (docEle.style as any)[key] !== undefined) || 'transform';
for (i = 0; i < keys.length; i++) {
if ((docEle.style as any)[keys[i]] !== undefined) {
css.transformProp = keys[i];
break;
}
}
// transition
keys = ['webkitTransition', 'transition'];
for (i = 0; i < keys.length; i++) {
if ((docEle.style as any)[keys[i]] !== undefined) {
css.transitionProp = keys[i];
break;
}
}
const transitionProp = [
'webkitTransition',
'transition'
].find(key => (docEle.style as any)[key] !== undefined) || 'transition';
// The only prefix we care about is webkit for transitions.
const prefix = css.transitionProp.indexOf('webkit') > -1 ? '-webkit-' : '';
const prefix = transitionProp.indexOf('webkit') > -1 ? '-webkit-' : '';
// transition duration
css.transitionDurationProp = prefix + 'transition-duration';
// transition timing function
css.transitionTimingFnProp = prefix + 'transition-timing-function';
return css;
return {
transitionDurationProp: prefix + 'transition-duration',
transitionTimingFnProp: prefix + 'transition-timing-function',
transformProp,
transitionProp
};
}(document.documentElement);

View File

@ -148,7 +148,7 @@ export class Button {
* Get the classes based on the type
* e.g. block, full, round, large
*/
function getButtonTypeClassMap(buttonType: string, type: string, mode: string): CssClassMap {
function getButtonTypeClassMap(buttonType: string, type: string|null, mode: string): CssClassMap {
if (!type) {
return {};
}

View File

@ -15,7 +15,7 @@ export class Content {
private cTop = 0;
private cBottom = 0;
private dirty = false;
private scrollEl: HTMLIonScrollElement;
private scrollEl: HTMLIonScrollElement|null;
mode: string;
color: string;

View File

@ -1,11 +1,10 @@
export class GestureController {
private gestureId = 0;
private requestedStart = new Map<number, number>();
private disabledGestures = new Map<string, Set<number>>();
private disabledScroll = new Set<number>();
private capturedId: number = null;
private capturedId: number|null = null;
createGesture(gestureName: string, gesturePriority: number, disableScroll: boolean): GestureDelegate {
return new GestureDelegate(this, this.newID(), gestureName, gesturePriority, disableScroll);

View File

@ -21,7 +21,7 @@ export class InfiniteScroll {
private thrPx = 0;
private thrPc = 0;
private scrollEl: HTMLIonScrollElement;
private scrollEl: HTMLIonScrollElement|null = null;
private didFire = false;
private isBusy = false;
private init = false;
@ -219,7 +219,7 @@ export class InfiniteScroll {
return (
!this.disabled &&
!this.isBusy &&
this.scrollEl &&
!!this.scrollEl &&
!this.isLoading);
}

View File

@ -32,6 +32,9 @@ export class ModalController {
dismiss(data?: any, role?: any, modalId = -1) {
modalId = modalId >= 0 ? modalId : getHighestId();
const modal = modals.get(modalId);
if (!modal) {
return Promise.reject('modal does not exist');
}
return modal.dismiss(data, role);
}

View File

@ -32,6 +32,9 @@ export class PickerController {
dismiss(data?: any, role?: any, pickerId = -1) {
pickerId = pickerId >= 0 ? pickerId : getHighestId();
const picker = pickers.get(pickerId);
if (!picker) {
return Promise.reject('picker does not exist');
}
return picker.dismiss(data, role);
}

View File

@ -32,6 +32,9 @@ export class PopoverController {
dismiss(data?: any, role?: any, popoverId = -1) {
popoverId = popoverId >= 0 ? popoverId : getHighestId();
const popover = popovers.get(popoverId);
if (!popover) {
return Promise.reject('popover does not exist');
}
return popover.dismiss(data, role);
}

View File

@ -4,6 +4,7 @@ import { Component, Event, EventEmitter, Listen, Prop } from '@stencil/core';
tag: `ion-range-knob`
})
export class RangeKnob {
@Prop() pressed: boolean;
@Prop() pin: boolean;
@Prop() min: number;

View File

@ -28,8 +28,7 @@ export class Refresher {
private didStart = false;
private gestureConfig: any;
private progress = 0;
scrollEl: HTMLElement;
private scrollEl: HTMLElement | null = null;
@Prop({ context: 'dom' }) dom: DomController;
@ -116,7 +115,12 @@ export class Refresher {
console.error('Make sure you use: <ion-refresher slot="fixed">');
return;
}
this.scrollEl = this.el.parentElement.querySelector('ion-scroll') as HTMLElement;
const parentElement = this.el.parentElement;
if (!parentElement) {
console.error('ion-refresher is not attached');
return;
}
this.scrollEl = parentElement.querySelector('ion-scroll') as HTMLElement;
if (!this.scrollEl) {
console.error('ion-refresher didn\'t attached, make sure if parent is a ion-content');
}
@ -183,6 +187,9 @@ export class Refresher {
}
private onMove(detail: GestureDetail) {
if (!this.scrollEl) {
return 0;
}
// this method can get called like a bazillion times per second,
// so it's built to be as efficient as possible, and does its
// best to do any DOM read/writes only when absolutely necessary
@ -309,26 +316,14 @@ export class Refresher {
}
private close(state: RefresherState, delay: string) {
let timer: number;
function close(ev: TransitionEvent) {
// closing is done, return to inactive state
if (ev) {
clearTimeout(timer);
}
// create fallback timer incase something goes wrong with transitionEnd event
setTimeout(() => {
this.state = RefresherState.Inactive;
this.progress = 0;
this.didStart = false;
this.setCss(0, '0ms', false, '');
}
// create fallback timer incase something goes wrong with transitionEnd event
timer = setTimeout(close.bind(this), 600);
// create transition end event on the content's scroll element
// TODO: what is this?
// this.scrollEl.onScrollElementTransitionEnd(close.bind(this));
}, 600);
// reset set the styles on the scroll element
// set that the refresh is actively cancelling/completing
@ -336,9 +331,6 @@ export class Refresher {
this.setCss(0, '', true, delay);
// TODO: stop gesture
// if (this._pointerEvents) {
// this._pointerEvents.stop();
// }
}
private setCss(y: number, duration: string, overflowVisible: boolean, delay: string) {

View File

@ -25,7 +25,7 @@ export class ReorderIndexes {
}
})
export class ReorderGroup {
private selectedItemEl: HTMLElement = null;
private selectedItemEl: HTMLElement|null = null;
private selectedItemHeight: number;
private lastToIndex: number;
private cachedHeights: number[] = [];
@ -179,7 +179,7 @@ export class ReorderGroup {
? children[toIndex + 1]
: children[toIndex];
this.containerEl.insertBefore(this.selectedItemEl, ref);
this.containerEl.insertBefore(selectedItem, ref);
const len = children.length;
const transform = CSS_PROP.transformProp;
@ -188,9 +188,11 @@ export class ReorderGroup {
}
const reorderInactive = () => {
this.selectedItemEl.style.transition = '';
this.selectedItemEl.classList.remove(ITEM_REORDER_SELECTED);
this.selectedItemEl = null;
if (this.selectedItemEl) {
this.selectedItemEl.style.transition = '';
this.selectedItemEl.classList.remove(ITEM_REORDER_SELECTED);
this.selectedItemEl = null;
}
};
if (toIndex === fromIndex) {
selectedItem.style.transition = 'transform 200ms ease-in-out';
@ -287,7 +289,7 @@ function indexForItem(element: any): number {
return element['$ionIndex'];
}
function findReorderItem(node: HTMLElement, container: HTMLElement): HTMLElement {
function findReorderItem(node: HTMLElement, container: HTMLElement): HTMLElement|null {
let nested = 0;
let parent;
while (node && nested < 6) {

View File

@ -21,7 +21,7 @@ export class RouterController {
const base = document.querySelector('head > base');
if (base) {
const baseURL = base.getAttribute('href');
if (baseURL.length > 0) {
if (baseURL && baseURL.length > 0) {
this.basePrefix = baseURL;
}
}

View File

@ -11,7 +11,7 @@ export interface NavElement extends HTMLElement {
export interface RouterEntry {
path: string;
id: any;
segments?: string[];
segments: string[];
props?: any;
}
@ -29,7 +29,7 @@ export class RouterSegments {
next(): string {
if (this.segments.length > 0) {
return this.segments.shift();
return this.segments.shift() as string;
}
return '';
}
@ -55,7 +55,7 @@ export function writeNavState(root: HTMLElement, segments: RouterSegments): Prom
export function readNavState(node: HTMLElement) {
const stack = [];
let pivot: NavElement;
let pivot: NavElement | null = null;
let state: NavState;
while (true) {
pivot = breadthFirstSearch(node);
@ -85,13 +85,13 @@ function mustMatchRoute(segments: RouterSegments, routes: RouterEntries) {
return r;
}
export function matchRoute(segments: RouterSegments, routes: RouterEntries): RouterEntry {
export function matchRoute(segments: RouterSegments, routes: RouterEntries): RouterEntry | null {
if (!routes) {
return null;
}
let index = 0;
routes = routes.map(initRoute);
let selectedRoute: RouterEntry = null;
let selectedRoute: RouterEntry|null = null;
let ambiguous = false;
let segment: string;
let l: number;
@ -154,7 +154,7 @@ export function parseURL(url: string): string[] {
}
const navs = ['ION-NAV', 'ION-TABS'];
export function breadthFirstSearch(root: HTMLElement): NavElement {
export function breadthFirstSearch(root: HTMLElement): NavElement | null {
if (!root) {
console.error('search root is null');
return null;
@ -166,8 +166,8 @@ export function breadthFirstSearch(root: HTMLElement): NavElement {
// first, before moving to the next level neighbours.
const queue = [root];
while (queue.length > 0) {
const node = queue.shift();
let node: HTMLElement | undefined;
while (node = queue.shift()) {
// visit node
if (navs.indexOf(node.tagName) >= 0) {
return node as NavElement;

View File

@ -345,14 +345,14 @@ export class Select {
openActionSheet() {
const interfaceOptions = {...this.interfaceOptions};
const actionSheetButtons: ActionSheetButton[] = this.childOpts.map(option => {
const actionSheetButtons = this.childOpts.map(option => {
return {
role: (option.selected ? 'selected' : ''),
text: option.textContent,
handler: () => {
this.value = option.value;
}
};
} as ActionSheetButton;
});
actionSheetButtons.push({
@ -382,10 +382,7 @@ export class Select {
const interfaceOptions = {...this.interfaceOptions};
const label = this.getLabel();
let labelText: string = null;
if (label) {
labelText = label.textContent;
}
const labelText = (label) ? label.textContent : null;
const alertOpts: AlertOptions = Object.assign(interfaceOptions, {
title: interfaceOptions.title ? interfaceOptions.title : labelText,

View File

@ -62,11 +62,11 @@ export class Tabbar {
}
protected analyzeTabs() {
const tabs: HTMLIonTabButtonElement[] = Array.from(document.querySelectorAll('ion-tab-button')),
scrollLeft: number = this.scrollEl.scrollLeft,
tabsWidth: number = this.scrollEl.clientWidth;
let previous: {tab: HTMLIonTabButtonElement, amount: number},
next: {tab: HTMLIonTabButtonElement, amount: number};
const tabs: HTMLIonTabButtonElement[] = Array.from(document.querySelectorAll('ion-tab-button'));
const scrollLeft = this.scrollEl.scrollLeft;
const tabsWidth = this.scrollEl.clientWidth;
let previous: {tab: HTMLIonTabButtonElement, amount: number};
let next: {tab: HTMLIonTabButtonElement, amount: number};
tabs.forEach((tab: HTMLIonTabButtonElement) => {
const left: number = tab.offsetLeft,
@ -85,11 +85,70 @@ export class Tabbar {
return {previous, next};
}
private getSelectedButton(): HTMLIonTabButtonElement {
private getSelectedButton(): HTMLIonTabButtonElement | undefined {
return Array.from(this.el.querySelectorAll('ion-tab-button'))
.find(btn => btn.selected);
}
protected scrollToSelectedButton() {
this.dom.read(() => {
const activeTabButton = this.getSelectedButton();
if (activeTabButton) {
const scrollLeft: number = this.scrollEl.scrollLeft,
tabsWidth: number = this.scrollEl.clientWidth,
left: number = activeTabButton.offsetLeft,
right: number = left + activeTabButton.offsetWidth;
let amount;
if (right > (tabsWidth + scrollLeft)) {
amount = right - tabsWidth;
} else if (left < scrollLeft) {
amount = left;
}
if (amount !== undefined) {
this.scrollEl.scrollToPoint(amount, 0, 250).then(() => {
this.updateBoundaries();
});
}
}
});
}
private scrollByTab(direction: 'left' | 'right') {
this.dom.read(() => {
const {previous, next} = this.analyzeTabs(),
info = direction === 'right' ? next : previous,
amount = info && info.amount;
if (info) {
this.scrollEl.scrollToPoint(amount, 0, 250).then(() => {
this.updateBoundaries();
});
}
});
}
private updateBoundaries() {
this.canScrollLeft = this.scrollEl.scrollLeft !== 0;
this.canScrollRight = this.scrollEl.scrollLeft < (this.scrollEl.scrollWidth - this.scrollEl.offsetWidth);
}
private updateHighlight() {
if (!this.highlight) {
return;
}
this.dom.read(() => {
const btn = this.getSelectedButton();
const highlight = this.el.querySelector('div.tabbar-highlight') as HTMLElement;
if (btn && highlight) {
highlight.style.transform = `translate3d(${btn.offsetLeft}px,0,0) scaleX(${btn.offsetWidth})`;
}
});
}
hostData() {
const themedClasses = this.translucent ? createThemedClasses(this.mode, this.color, 'tabbar-translucent') : {};
@ -139,61 +198,4 @@ export class Tabbar {
];
}
}
protected scrollToSelectedButton() {
this.dom.read(() => {
const activeTabButton: HTMLIonTabButtonElement = this.getSelectedButton();
if (activeTabButton) {
const scrollLeft: number = this.scrollEl.scrollLeft,
tabsWidth: number = this.scrollEl.clientWidth,
left: number = activeTabButton.offsetLeft,
right: number = left + activeTabButton.offsetWidth;
let amount;
if (right > (tabsWidth + scrollLeft)) {
amount = right - tabsWidth;
} else if (left < scrollLeft) {
amount = left;
}
if (amount !== undefined) {
this.scrollEl.scrollToPoint(amount, 0, 250).then(() => {
this.updateBoundaries();
});
}
}
});
}
scrollByTab(direction: 'left' | 'right') {
this.dom.read(() => {
const {previous, next} = this.analyzeTabs(),
info = direction === 'right' ? next : previous,
amount = info && info.amount;
if (info) {
this.scrollEl.scrollToPoint(amount, 0, 250).then(() => {
this.updateBoundaries();
});
}
});
}
updateBoundaries() {
this.canScrollLeft = this.scrollEl.scrollLeft !== 0;
this.canScrollRight = this.scrollEl.scrollLeft < (this.scrollEl.scrollWidth - this.scrollEl.offsetWidth);
}
updateHighlight() {
this.dom.read(() => {
const btn = this.getSelectedButton(),
ionTabbarHighlight: HTMLElement = this.highlight && this.el.querySelector('div.tabbar-highlight');
if (btn && ionTabbarHighlight) {
ionTabbarHighlight.style.transform = `translate3d(${btn.offsetLeft}px,0,0) scaleX(${btn.offsetWidth})`;
}
});
}
}

View File

@ -16,7 +16,7 @@ export class Tabs {
@Element() el: HTMLElement;
@State() tabs: HTMLIonTabElement[] = [];
@State() selectedTab: HTMLIonTabElement;
@State() selectedTab: HTMLIonTabElement | undefined;
@Prop({ context: 'config' }) config: Config;
@ -77,7 +77,8 @@ export class Tabs {
}
componentDidUnload() {
this.tabs = this.selectedTab = null;
this.tabs.length = 0;
this.selectedTab = undefined;
}
@Listen('ionTabbarClick')
@ -132,7 +133,7 @@ export class Tabs {
* @return {HTMLIonTabElement} Returns the currently selected tab
*/
@Method()
getSelected(): HTMLIonTabElement {
getSelected(): HTMLIonTabElement | undefined {
return this.tabs.find((tab) => tab.selected);
}
@ -190,7 +191,8 @@ export class Tabs {
private initSelect() {
// find pre-selected tabs
const selectedTab = this.tabs.find(t => t.selected) || this.tabs.find(t => t.show && !t.disabled);
const selectedTab = this.tabs.find(t => t.selected) ||
this.tabs.find(t => t.show && !t.disabled);
// reset all tabs none is selected
for (const tab of this.tabs) {
@ -198,8 +200,9 @@ export class Tabs {
tab.selected = false;
}
}
selectedTab.setActive(true);
if (selectedTab) {
selectedTab.setActive(true);
}
this.selectedTab = selectedTab;
}

View File

@ -17,7 +17,7 @@ export class TapClick {
private gestureCtrl: GestureController;
private activatableEle: HTMLElement;
private activatableEle: HTMLElement | null;
private activeDefer: any;
private clearDefers = new WeakMap<HTMLElement, any>();
@ -104,7 +104,7 @@ export class TapClick {
this.setActivatedElement(null, ev);
}
private setActivatedElement(el: HTMLElement, ev: UIEvent) {
private setActivatedElement(el: HTMLElement | null, ev: UIEvent) {
// do nothing
const activatableEle = this.activatableEle;
if (el && el === activatableEle) {
@ -157,7 +157,9 @@ export class TapClick {
private removeActivated(smooth: boolean) {
const activatableEle = this.activatableEle;
if (!activatableEle) {
return;
}
const time = CLEAR_STATE_DEFERS - Date.now() + this.lastActivated;
if (smooth && time > 0) {
const deferId = setTimeout(() => {

View File

@ -32,6 +32,9 @@ export class ToastController {
dismiss(data?: any, role?: any, toastId = -1) {
toastId = toastId >= 0 ? toastId : getHighestId();
const toast = toasts.get(toastId);
if (!toast) {
return Promise.reject('toast does not exist');
}
return toast.dismiss(data, role);
}

View File

@ -24,7 +24,7 @@ export class Toast {
mode: string;
color: string;
private animation: Animation;
private animation: Animation | null;
@Element() private el: HTMLElement;

View File

@ -15,10 +15,17 @@
<ion-header>
<ion-toolbar>
<ion-title>Ionic CDN demo</ion-title>
<ion-buttons slot="end">
<ion-button onclick="addItems()">Add Items</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<p>
<ion-button onclick="addItems()">Add Items</ion-button>
</p>
<ion-virtual-scroll id="virtual"></ion-virtual-scroll>
</ion-content>
@ -27,6 +34,10 @@
</ion-app>
<script>
function addItems() {
const virtual = document.getElementById('virtual');
virtual.addItems()
}
async function init() {
const virtual = await document.getElementById('virtual').componentOnReady();
virtual.itemHeight = () => 45;

View File

@ -11,7 +11,7 @@ import { Cell, DomRenderFn, HeaderFn, ItemHeightFn, ItemRenderFn, NodeHeightFn,
})
export class VirtualScroll {
private scrollEl: HTMLElement;
private scrollEl: HTMLElement | null;
private range: Range = {offset: 0, length: 0};
private timerUpdate: any;
private heightIndex: Uint32Array;
@ -145,7 +145,7 @@ export class VirtualScroll {
private updateVirtualScroll() {
// do nothing if there is a scheduled update
if (!this.isEnabled) {
if (!this.isEnabled || !this.scrollEl) {
return;
}
if (this.timerUpdate) {
@ -155,13 +155,15 @@ export class VirtualScroll {
this.dom.read(() => {
let topOffset = 0;
let node = this.el;
while (node !== this.scrollEl) {
let node: HTMLElement | null = this.el;
while (node && node !== this.scrollEl) {
topOffset += node.offsetTop;
node = node.parentElement;
}
this.viewportOffset = topOffset;
this.currentScrollTop = this.scrollEl.scrollTop;
if (this.scrollEl) {
this.currentScrollTop = this.scrollEl.scrollTop;
}
});
this.dom.write(() => {
@ -276,11 +278,15 @@ export class VirtualScroll {
}
private calcDimensions() {
this.viewportHeight = this.scrollEl.offsetHeight;
if (this.scrollEl) {
this.viewportHeight = this.scrollEl.offsetHeight;
}
}
private enableScrollEvents(shouldListen: boolean) {
this.isEnabled = shouldListen;
this.enableListener(this, 'scroll', shouldListen, this.scrollEl);
if (this.scrollEl) {
this.isEnabled = shouldListen;
this.enableListener(this, 'scroll', shouldListen, this.scrollEl);
}
}
}

View File

@ -28,7 +28,7 @@ export function createConfigController(configObj: any, platforms: PlatformConfig
return fallback !== undefined ? fallback : null;
}
function getBoolean(key: string, fallback: boolean): boolean {
function getBoolean(key: string, fallback?: boolean): boolean {
const val = get(key);
if (val === null) {
return fallback !== undefined ? fallback : false;
@ -39,7 +39,7 @@ export function createConfigController(configObj: any, platforms: PlatformConfig
return !!val;
}
function getNumber(key: string, fallback: number): number {
function getNumber(key: string, fallback?: number): number {
const val = parseFloat(get(key));
return isNaN(val) ? (fallback !== undefined ? fallback : NaN) : val;
}

View File

@ -10,7 +10,7 @@ export interface DomController {
}
export interface RafCallback {
(timeStamp?: number): void;
(timeStamp: number): void;
}
export interface DomControllerCallback {

View File

@ -1,4 +1,5 @@
export function isCordova(): boolean {
return !!(window['cordova'] || window['PhoneGap'] || window['phonegap']);
const win = window as any;
return !!(win['cordova'] || win['PhoneGap'] || win['phonegap']);
}

View File

@ -151,7 +151,7 @@ export interface OverlayController {
export interface FrameworkDelegate {
attachViewToDom(elementOrContainerToMountTo: any, elementOrComponentToMount: any, propsOrDataObj?: any, classesToAdd?: string[], escapeHatch?: any): Promise<FrameworkMountingData>;
removeViewFromDom(elementOrContainerToUnmountFrom: any, elementOrComponentToUnmount: any, escapeHatch?: any): Promise<FrameworkMountingData>;
removeViewFromDom(elementOrContainerToUnmountFrom: any, elementOrComponentToUnmount: any, escapeHatch?: any): Promise<void>;
}
export interface FrameworkMountingData {

View File

@ -24,13 +24,9 @@ export class DomFrameworkDelegate implements FrameworkDelegate {
});
}
removeViewFromDom(parentElement: HTMLElement, childElement: HTMLElement): Promise<FrameworkMountingData> {
removeViewFromDom(parentElement: HTMLElement, childElement: HTMLElement): Promise<void> {
parentElement.removeChild(childElement);
return Promise.resolve({
element: null,
data: null,
component: null
});
return Promise.resolve();
}
shouldDeferToRouter(_elementOrComponentToMount: any): Promise<boolean> {

View File

@ -253,7 +253,7 @@ export const NON_TEXT_INPUT_REGEX = /^(radio|checkbox|range|file|submit|reset|co
export function hasFocusedTextInput() {
const activeElement = getActiveElement();
if (isTextInput(activeElement)) {
if (isTextInput(activeElement) && activeElement.parentElement) {
return activeElement.parentElement.querySelector(':focus') === activeElement;
}
return false;
@ -271,7 +271,7 @@ export function reorderArray(array: any[], indexes: {from: number, to: number}):
export function playAnimationAsync(animation: Animation): Promise<Animation> {
return new Promise((resolve) => {
animation.onFinish((ani: Animation) => {
animation.onFinish((ani) => {
resolve(ani);
});
animation.play();
@ -299,7 +299,7 @@ export function debounce(func: Function, wait = 0) {
};
}
export function getNavAsChildIfExists(element: HTMLElement): HTMLIonNavElement {
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 HTMLIonNavElement;

View File

@ -51,7 +51,7 @@ export function getButtonClassMap(buttonType: string, mode: string): CssClassMap
}
export function getClassMap(classes: string): CssClassMap {
export function getClassMap(classes: string | undefined): CssClassMap {
const map: CssClassMap = {};
if (classes) {
classes