refactor(Context): remove global Context

This commit is contained in:
Adam Bradley
2017-12-18 13:21:17 -06:00
parent 3324e7a9b9
commit c415bbe1d7
19 changed files with 98 additions and 65 deletions

View File

@ -14,9 +14,9 @@
}
},
"@stencil/core": {
"version": "0.0.9-3",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-0.0.9-3.tgz",
"integrity": "sha512-r56SaikZKceTlX0IGAKWKyuVtLI7XW/dUyfFYdRlPOto4Ifq63Pbs4s47Lmh2/NutfGMW1o7l6CWkhmO01tTPg==",
"version": "0.0.9-4",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-0.0.9-4.tgz",
"integrity": "sha512-P4eUb6UI1hHSsLrRf8rPRwdL/hWXDh/b0Cv6gkPK/iQPD27ARLy/M1mqjptEORpGkVodAE36JogLz+3zN3DG+Q==",
"dev": true,
"requires": {
"chokidar": "1.7.0",
@ -3428,9 +3428,9 @@
"dev": true
},
"ionicons": {
"version": "4.0.0-10",
"resolved": "https://registry.npmjs.org/ionicons/-/ionicons-4.0.0-10.tgz",
"integrity": "sha512-Ucr/nsio5YEIawva/Wcdu8zIJX/rs2otf7LiXmGgME09aE4N3id35fAko0LloTNpSxg0Lw0oX/KJEAI4prqVvA==",
"version": "4.0.0-11",
"resolved": "https://registry.npmjs.org/ionicons/-/ionicons-4.0.0-11.tgz",
"integrity": "sha512-AoK+gnAE9ABLI0/swX5By8SXxU8AMpX2vpGMr7/lbc6emPhSV/ESfYMMa88a65tsMp8yG+zGOJI/LSwY6YeAQA==",
"dev": true
},
"is-arrayish": {

View File

@ -9,12 +9,12 @@
"dist/"
],
"devDependencies": {
"@stencil/core": "0.0.9-3",
"@stencil/core": "0.0.9-4",
"@stencil/dev-server": "0.0.18-0",
"@stencil/utils": "latest",
"@types/jest": "^21.1.6",
"chromedriver": "^2.33.2",
"ionicons": "4.0.0-10",
"ionicons": "4.0.0-11",
"jest": "^21.2.1",
"mocha": "^4.0.1",
"np": "^2.17.0",

View File

@ -4,11 +4,12 @@ import {
AnimationBuilder,
AnimationController,
Config,
DomController,
OverlayDismissEvent,
OverlayDismissEventDetail
} from '../../index';
import { domControllerAsync, playAnimationAsync, isDef } from '../../utils/helpers';
import { domControllerAsync, isDef, playAnimationAsync } from '../../utils/helpers';
import { createThemedClasses } from '../../utils/theme';
import iosEnterAnimation from './animations/ios.enter';
@ -68,6 +69,7 @@ export class ActionSheet {
@Prop({ connect: 'ion-animation-controller' }) animationCtrl: AnimationController;
@Prop({ context: 'config' }) config: Config;
@Prop({ context: 'dom' }) dom: DomController;
/**
* Additional class or classes to apply to the action-sheet
@ -173,7 +175,7 @@ export class ActionSheet {
return playAnimationAsync(animation);
}).then((animation) => {
animation.destroy();
return domControllerAsync(Context.dom.write, () => {
return domControllerAsync(this.dom.write, () => {
this.el.parentNode.removeChild(this.el);
});
}).then(() => {

View File

@ -1,5 +1,5 @@
import { Component, CssClassMap, Element, Event, EventEmitter, Method, Prop } from '@stencil/core';
import { Animation, AnimationBuilder, AnimationController, Config, OverlayDismissEvent, OverlayDismissEventDetail } from '../../index';
import { Animation, AnimationBuilder, AnimationController, Config, DomController, OverlayDismissEvent, OverlayDismissEventDetail } from '../../index';
import { domControllerAsync, playAnimationAsync } from '../../utils/helpers';
import { BACKDROP } from '../../utils/overlay-constants';
@ -65,6 +65,7 @@ export class Alert {
@Prop({ connect: 'ion-animation-controller' }) animationCtrl: AnimationController;
@Prop({ context: 'config' }) config: Config;
@Prop({ context: 'dom' }) dom: DomController;
/**
* Additional class or classes to apply to the alert
@ -178,7 +179,7 @@ export class Alert {
}).then((animation) => {
animation.destroy();
return domControllerAsync(Context.dom.write, () => {
return domControllerAsync(this.dom.write, () => {
this.el.parentNode.removeChild(this.el);
});
}).then(() => {

View File

@ -1,5 +1,5 @@
import { Component, Element, Listen, Method, Prop } from '@stencil/core';
import { Config } from '../../index';
import { Config, DomController } from '../../index';
import { createThemedClasses, getElementClassObject } from '../../utils/theme';
import { getPageElement } from '../../utils/helpers';
@ -23,6 +23,7 @@ export class Content {
@Element() private el: HTMLElement;
@Prop({ context: 'config' }) config: Config;
@Prop({ context: 'dom' }) dom: DomController;
/**
* @output {ScrollEvent} Emitted when the scrolling first starts.
@ -100,13 +101,13 @@ export class Content {
return;
}
if (this.fullscreen) {
Context.dom.raf(() => {
Context.dom.read(this.readDimensions.bind(this));
Context.dom.write(this.writeDimensions.bind(this));
this.dom.raf(() => {
this.dom.read(this.readDimensions.bind(this));
this.dom.write(this.writeDimensions.bind(this));
});
} else {
this.cTop = this.cBottom = null;
Context.dom.write(() => this.scrollEl.removeAttribute('style'));
this.dom.write(() => this.scrollEl.removeAttribute('style'));
}
}

View File

@ -1,8 +1,12 @@
import { ElementRef, applyStyles, assert, getElementReference, updateDetail } from '../../utils/helpers';
import { BLOCK_ALL, BlockerDelegate, GestureController, GestureDelegate } from '../gesture-controller/gesture-controller';
import { Component, Element, Event, EventEmitter, Listen, Prop, PropDidChange } from '@stencil/core';
import { DomController } from '../../index';
import { PanRecognizer } from './recognizers';
declare const Ionic: { gesture: GestureController };
@Component({
tag: 'ion-gesture'
})
@ -23,6 +27,9 @@ export class Gesture {
@Element() private el: HTMLElement;
@Prop({ context: 'dom' }) dom: DomController;
@Prop({ context: 'enableListener' }) enableListener: any;
@Prop() enabled: boolean = true;
@Prop() attachTo: ElementRef = 'child';
@Prop() autoBlockAll: boolean = false;
@ -73,7 +80,7 @@ export class Gesture {
// in this case, we already know the GestureController and Gesture are already
// apart of the same bundle, so it's safe to load it this way
// only create one instance of GestureController, and reuse the same one later
this.ctrl = Context.gesture = Context.gesture || new GestureController;
this.ctrl = Ionic.gesture = Ionic.gesture || new GestureController();
this.gesture = this.ctrl.createGesture(this.gestureName, this.gesturePriority, this.disableScroll);
const types = this.type.replace(/\s/g, '').toLowerCase().split(',');
@ -84,7 +91,7 @@ export class Gesture {
this.enabledChanged(this.enabled);
if (this.pan || this.hasPress) {
Context.dom.write(() => {
this.dom.write(() => {
applyStyles(getElementReference(this.el, this.attachTo), GESTURE_INLINE_STYLES);
});
}
@ -98,8 +105,8 @@ export class Gesture {
@PropDidChange('enabled')
protected enabledChanged(isEnabled: boolean) {
if (this.pan || this.hasPress) {
Context.enableListener(this, 'touchstart', isEnabled, this.attachTo);
Context.enableListener(this, 'mousedown', isEnabled, this.attachTo);
this.enableListener(this, 'touchstart', isEnabled, this.attachTo);
this.enableListener(this, 'mousedown', isEnabled, this.attachTo);
if (!isEnabled) {
this.abortGesture();
}
@ -216,7 +223,7 @@ export class Gesture {
if (!this.isMoveQueued && this.hasFiredStart) {
this.isMoveQueued = true;
this.calcGestureData(ev);
Context.dom.write(this.fireOnMove.bind(this));
this.dom.write(this.fireOnMove.bind(this));
}
return;
}
@ -424,18 +431,18 @@ export class Gesture {
private enableMouse(shouldEnable: boolean) {
if (this.pan) {
Context.enableListener(this, 'document:mousemove', shouldEnable);
this.enableListener(this, 'document:mousemove', shouldEnable);
}
Context.enableListener(this, 'document:mouseup', shouldEnable);
this.enableListener(this, 'document:mouseup', shouldEnable);
}
private enableTouch(shouldEnable: boolean) {
if (this.pan) {
Context.enableListener(this, 'touchmove', shouldEnable, this.attachTo);
this.enableListener(this, 'touchmove', shouldEnable, this.attachTo);
}
Context.enableListener(this, 'touchcancel', shouldEnable, this.attachTo);
Context.enableListener(this, 'touchend', shouldEnable, this.attachTo);
this.enableListener(this, 'touchcancel', shouldEnable, this.attachTo);
this.enableListener(this, 'touchend', shouldEnable, this.attachTo);
}

View File

@ -1,5 +1,5 @@
import { Component, Element, Event, EventEmitter, Listen, Method, Prop, PropDidChange, State } from '@stencil/core';
import { ScrollDetail, StencilElement } from '../../index';
import { DomController, ScrollDetail, StencilElement } from '../../index';
const enum Position {
Top = 'top',
@ -23,6 +23,9 @@ export class InfiniteScroll {
@Element() private el: HTMLElement;
@State() isLoading: boolean = false;
@Prop({ context: 'dom' }) dom: DomController;
@Prop({ context: 'enableListener' }) enableListener: any;
/**
* @input {string} The threshold distance from the bottom
* of the content to call the `infinite` output event when scrolled.
@ -97,7 +100,7 @@ export class InfiniteScroll {
this.thresholdChanged(this.threshold);
this.enableScrollEvents(this.enabled);
if (this.position === Position.Top) {
Context.dom.write(() => this.scrollEl.scrollToBottom(0));
this.dom.write(() => this.scrollEl.scrollToBottom(0));
}
}
@ -196,14 +199,14 @@ export class InfiniteScroll {
const prev = this.scrollEl.scrollHeight - this.scrollEl.scrollTop;
// ******** DOM READ ****************
Context.dom.read(() => {
this.dom.read(() => {
// UI has updated, save the new content dimensions
const scrollHeight = this.scrollEl.scrollHeight;
// New content was added on top, so the scroll position should be changed immediately to prevent it from jumping around
const newScrollTop = scrollHeight - prev;
// ******** DOM WRITE ****************
Context.dom.write(() => {
this.dom.write(() => {
this.scrollEl.scrollTop = newScrollTop;
this.isBusy = false;
});
@ -225,7 +228,7 @@ export class InfiniteScroll {
* @hidden
*/
private enableScrollEvents(shouldListen: boolean) {
Context.enableListener(this, 'ionScroll', shouldListen, this.scrollEl);
this.enableListener(this, 'ionScroll', shouldListen, this.scrollEl);
}
hostData() {
@ -237,7 +240,6 @@ export class InfiniteScroll {
};
}
render() {
return <slot></slot>;
}

View File

@ -1,10 +1,10 @@
import { Component, Element, Event, EventEmitter, Listen, Method, Prop, State } from '@stencil/core';
import { Component, Element, Event, EventEmitter, Listen, Method, Prop } from '@stencil/core';
import {
Animation,
AnimationBuilder,
AnimationController,
Config,
DomController,
OverlayDismissEvent,
OverlayDismissEventDetail
} from '../../index';
@ -71,6 +71,7 @@ export class Loading {
@Prop({ connect: 'ion-animation-controller' }) animationCtrl: AnimationController;
@Prop({ context: 'config' }) config: Config;
@Prop({ context: 'dom' }) dom: DomController;
/**
* Additional classes to apply for custom CSS
@ -173,7 +174,7 @@ export class Loading {
return playAnimationAsync(animation);
}).then((animation) => {
animation.destroy();
return domControllerAsync(Context.dom.write, () => {
return domControllerAsync(this.dom.write, () => {
this.el.parentNode.removeChild(this.el);
});
}).then(() => {

View File

@ -35,6 +35,7 @@ export class Menu {
@Prop({ context: 'config' }) config: Config;
@Prop({ connect: 'ion-menu-controller' }) lazyMenuCtrl: StencilElement;
@Prop({ context: 'enableListener' }) enableListener: any;
/**
* @input {string} The content's id the menu should use.
@ -374,7 +375,7 @@ export class Menu {
this.isAnimating = false;
// add/remove backdrop click listeners
Context.enableListener(this, 'body:click', isOpen);
this.enableListener(this, 'body:click', isOpen);
if (isOpen) {
// disable swipe to go back gesture

View File

@ -4,6 +4,7 @@ import {
AnimationBuilder,
AnimationController,
Config,
DomController,
FrameworkDelegate,
OverlayDismissEvent,
OverlayDismissEventDetail
@ -63,6 +64,8 @@ export class Modal {
@Prop({ connect: 'ion-animation-controller' }) animationCtrl: AnimationController;
@Prop({ context: 'config' }) config: Config;
@Prop({ context: 'dom' }) dom: DomController;
@Prop() mode: string;
@Prop() color: string;
@Prop() component: any;
@ -144,7 +147,7 @@ export class Modal {
this.animation.destroy();
await domControllerAsync(Context.dom.write, () => {});
await domControllerAsync(this.dom.write, () => {});
// TODO - Figure out how to make DOM controller work with callbacks that return a promise or are async
const userComponentParent = this.el.querySelector(`.${USER_COMPONENT_MODAL_CONTAINER_CLASS}`);

View File

@ -1,9 +1,8 @@
import { Component, Element, Prop } from '@stencil/core';
import { GestureDetail, PickerColumn, PickerColumnOption } from '../../index';
import { DomController, GestureDetail, PickerColumn, PickerColumnOption } from '../../index';
import { clamp } from '../../utils/helpers';
@Component({
tag: 'ion-picker-column',
host: {
@ -14,7 +13,6 @@ export class PickerColumnCmp {
private mode: string;
private bounceFrom: number;
private colHeight: number;
private lastIndex: number;
private lastTempIndex: number;
private minY: number;
@ -31,6 +29,8 @@ export class PickerColumnCmp {
@Element() private el: HTMLElement;
@Prop({ context: 'dom' }) dom: DomController;
@Prop() col: PickerColumn;
componentWillLoad() {
@ -49,7 +49,6 @@ export class PickerColumnCmp {
componentDidLoad() {
// get the scrollable element within the column
let colEle = this.el.querySelector('.picker-opts');
this.colHeight = colEle.clientHeight;
// get the height of one option
this.optHeight = (colEle.firstElementChild ? colEle.firstElementChild.clientHeight : 0);
@ -94,7 +93,6 @@ export class PickerColumnCmp {
let opt: PickerColumnOption;
let optOffset: number;
let visible: boolean;
let translateX: number;
let translateY: number;
let translateZ: number;
let rotateX: number;
@ -121,13 +119,11 @@ export class PickerColumnCmp {
if (Math.abs(rotateX) > 90) {
visible = false;
} else {
translateX = 0;
translateY = 0;
translateZ = 90;
transform = `rotateX(${rotateX}deg) `;
}
} else {
translateX = 0;
translateZ = 0;
translateY = optOffset;
if (Math.abs(translateY) > 170) {
@ -224,7 +220,7 @@ export class PickerColumnCmp {
if (notLockedIn) {
// isn't locked in yet, keep decelerating until it is
Context.dom.raf(() => this.decelerate());
this.dom.raf(() => this.decelerate());
}
} else if (this.y % this.optHeight !== 0) {

View File

@ -5,6 +5,7 @@ import {
AnimationBuilder,
AnimationController,
Config,
DomController,
OverlayDismissEvent,
OverlayDismissEventDetail
} from '../../index';
@ -66,6 +67,8 @@ export class Picker {
@Prop({ connect: 'ion-animation-controller' }) animationCtrl: AnimationController;
@Prop({ context: 'config' }) config: Config;
@Prop({ context: 'dom' }) dom: DomController;
@Prop() cssClass: string;
@Prop() content: string;
@Prop() dismissOnPageChange: boolean = false;
@ -130,7 +133,7 @@ export class Picker {
return playAnimationAsync(animation);
}).then((animation) => {
animation.destroy();
return domControllerAsync(Context.dom.write, () => {
return domControllerAsync(this.dom.write, () => {
this.el.parentNode.removeChild(this.el);
});
}).then(() => {

View File

@ -4,6 +4,7 @@ import {
AnimationBuilder,
AnimationController,
Config,
DomController,
FrameworkDelegate,
OverlayDismissEvent,
OverlayDismissEventDetail
@ -64,6 +65,7 @@ export class Popover {
@Prop({ connect: 'ion-animation-controller' }) animationCtrl: AnimationController;
@Prop({ context: 'config' }) config: Config;
@Prop({ context: 'dom' }) dom: DomController;
@Prop() mode: string;
@Prop() color: string;
@ -151,7 +153,7 @@ export class Popover {
return playAnimationAsync(animation);
}).then((animation) => {
animation.destroy();
return domControllerAsync(Context.dom.write, () => {});
return domControllerAsync(this.dom.write, () => {});
}).then(() => {
// TODO - Figure out how to make DOM controller work with callbacks that return a promise or are async
const userComponentParent = this.el.querySelector(`.${USER_COMPONENT_POPOVER_CONTAINER_CLASS}`);

View File

@ -1,5 +1,5 @@
import { Component, Element, Prop, PropDidChange, State } from '@stencil/core';
import { GestureDetail } from '../../index';
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';
@ -45,6 +45,8 @@ export class ReorderGroup {
@Element() private el: HTMLElement;
@Prop({ context: 'dom' }) dom: DomController;
@Prop() enabled: boolean = false;
/**
@ -54,7 +56,7 @@ export class ReorderGroup {
protected enabledChanged(enabled: boolean) {
if (enabled) {
this._enabled = true;
Context.dom.raf(() => {
this.dom.raf(() => {
this._iconVisible = true;
});
} else {

View File

@ -1,6 +1,6 @@
import { Component, Listen, Prop } from '@stencil/core';
import { RouterSegments, generateURL, parseURL, readNavState, writeNavState } from './router-utils';
import { Config } from '../../index';
import { Config, DomController } from '../../index';
@Component({
@ -13,6 +13,7 @@ export class RouterController {
private basePrefix: string = '#';
@Prop({ context: 'config' }) config: Config;
@Prop({ context: 'dom' }) dom: DomController;
componentDidLoad() {
const enabled = this.enabled = this.config.getBoolean('useRouter', false);
@ -25,7 +26,7 @@ export class RouterController {
}
}
Context.dom.raf(() => {
this.dom.raf(() => {
console.debug('[OUT] page load -> write nav state');
this.writeNavStateRoot();
});
@ -45,7 +46,7 @@ export class RouterController {
if (this.isBlocked()) {
return;
}
debugger;
console.debug('[IN] nav changed -> update URL');
const { stack, pivot } = this.readNavState();
if (pivot) {

View File

@ -1,7 +1,9 @@
import { Component, Element, Event, EventEmitter, Listen, Method, Prop, PropDidChange } from '@stencil/core';
import { Config, GestureDetail } from '../../index';
import { Config, DomController, GestureDetail } from '../../index';
import { GestureController, GestureDelegate } from '../gesture-controller/gesture-controller';
declare const Ionic: { gesture: GestureController };
@Component({
tag: 'ion-scroll'
@ -21,6 +23,9 @@ export class Scroll {
@Element() private el: HTMLElement;
@Prop({ context: 'config'}) config: Config;
@Prop({ context: 'dom' }) dom: DomController;
@Prop({ context: 'isServer' }) isServer: boolean;
@Prop() enabled: boolean = true;
@Prop() jsScroll: boolean = false;
@PropDidChange('jsScroll')
@ -50,11 +55,11 @@ export class Scroll {
@Event() ionScrollEnd: EventEmitter;
componentDidLoad() {
if (Context.isServer) {
if (this.isServer) {
return;
}
const gestureCtrl = Context.gesture = Context.gesture || new GestureController;
const gestureCtrl = Ionic.gesture = Ionic.gesture || new GestureController();
this.gesture = gestureCtrl.createGesture('scroll', 100, false);
}
@ -143,7 +148,7 @@ export class Scroll {
if (easedT < 1) {
// do not use DomController here
// must use nativeRaf in order to fire in the next frame
Context.dom.raf(step);
this.dom.raf(step);
} else {
stopScroll = true;
@ -157,8 +162,8 @@ export class Scroll {
self.isScrolling = true;
// chill out for a frame first
Context.dom.write(() => {
Context.dom.write(timeStamp => {
this.dom.write(() => {
this.dom.write(timeStamp => {
startTime = timeStamp;
step(timeStamp);
});
@ -174,7 +179,7 @@ export class Scroll {
if (!this.queued) {
this.queued = true;
Context.dom.read((timeStamp) => {
this.dom.read(timeStamp => {
this.queued = false;
this.onScroll(timeStamp);
});
@ -249,7 +254,7 @@ export class Scroll {
// haven't scrolled in a while, so it's a scrollend
this.isScrolling = false;
Context.dom.read((timeStamp) => {
this.dom.read(timeStamp => {
if (!this.isScrolling) {
this.onEnd(timeStamp);
}

View File

@ -1,4 +1,5 @@
import { Component, Element, Listen, Prop, PropDidChange, State } from '@stencil/core';
import { DomController } from '../../index';
import { getParentElement } from '../../utils/helpers';
@ -12,6 +13,8 @@ export class TabHighlight {
@State() animated = false;
@State() transform = '';
@Prop({ context: 'dom' }) dom: DomController;
@Prop() selectedTab: HTMLIonTabElement;
@PropDidChange('selectedTab')
selectedTabChanged() {
@ -28,7 +31,7 @@ export class TabHighlight {
}
protected updateTransform() {
Context.dom.read(() => {
this.dom.read(() => {
const btn = this.getSelectedButton();
this.transform = (btn)
? `translate3d(${btn.offsetLeft}px,0,0) scaleX(${btn.offsetWidth})`

View File

@ -1,5 +1,5 @@
import { Component, Element, Event, EventEmitter, Listen, Method, Prop } from '@stencil/core';
import { Animation, AnimationBuilder, AnimationController, Config, CssClassMap, OverlayDismissEvent, OverlayDismissEventDetail } from '../../index';
import { Animation, AnimationBuilder, AnimationController, Config, CssClassMap, DomController, OverlayDismissEvent, OverlayDismissEventDetail } from '../../index';
import { domControllerAsync, playAnimationAsync } from '../../utils/helpers';
import { createThemedClasses } from '../../utils/theme';
@ -60,6 +60,7 @@ export class Toast {
@Prop({ connect: 'ion-animation-controller' }) animationCtrl: AnimationController;
@Prop({ context: 'config' }) config: Config;
@Prop({ context: 'dom' }) dom: DomController;
@Prop() message: string;
@Prop() cssClass: string;
@ -120,7 +121,7 @@ export class Toast {
return playAnimationAsync(animation);
}).then((animation) => {
animation.destroy();
return domControllerAsync(Context.dom.write, () => {
return domControllerAsync(this.dom.write, () => {
this.el.parentNode.removeChild(this.el);
});
}).then(() => {

View File

@ -5,6 +5,8 @@ import { createDomControllerClient } from './dom-controller';
const Ionic = (window as any).Ionic = (window as any).Ionic || {};
declare const Context: any;
// add dom controller, used to coordinate DOM reads and write in order to avoid
// layout thrashing
if (!Context.dom) {