bootstrap refactor wip

This commit is contained in:
Adam Bradley
2015-10-04 00:20:41 -05:00
parent 369ada646f
commit 2b86705f54
9 changed files with 287 additions and 407 deletions

View File

@ -25,6 +25,7 @@ export * from 'ionic/components/nav/view-controller'
export * from 'ionic/components/nav/nav-push'
export * from 'ionic/components/nav/nav-router'
export * from 'ionic/components/nav-bar/nav-bar'
export * from 'ionic/components/overlay/overlay'
export * from 'ionic/components/popup/popup'
export * from 'ionic/components/slides/slides'
export * from 'ionic/components/radio/radio'

View File

@ -1,21 +1,9 @@
import {Component, View, bootstrap, ElementRef, NgZone, bind, DynamicComponentLoader, Injector} from 'angular2/angular2';
import {ROUTER_BINDINGS, HashLocationStrategy, LocationStrategy, Router} from 'angular2/router';
import {Component, View, ElementRef, NgZone, DynamicComponentLoader} from 'angular2/angular2';
import {IonicConfig} from '../../config/config';
import {IonicPlatform, Platform} from '../../platform/platform';
import {ClickBlock} from '../../util/click-block';
import {ScrollTo} from '../../animations/scroll-to';
import * as dom from '../../util/dom';
// injectables
import {TapClick} from '../tap-click/tap-click';
import {ActionSheet} from '../action-sheet/action-sheet';
import {Modal} from '../modal/modal';
import {Popup} from '../popup/popup';
import {FocusHolder} from '../form/focus-holder';
import {Events} from '../../util/events';
import {NavRegistry} from '../nav/nav-registry';
import {Translate} from '../../translation/translate';
/**
* @name IonicApp
@ -51,57 +39,6 @@ export class IonicApp {
this.components = {};
}
/**
* Bind some global events and publish on the 'app' channel
*/
bindEvents(platform, events) {
window.addEventListener('online', (event) => {
events.publish('app:online', event);
}, false);
window.addEventListener('offline', (event) => {
events.publish('app:offline', event);
}, false);
window.addEventListener('orientationchange', (event) => {
events.publish('app:rotated', event);
});
// When that status taps, we respond
window.addEventListener('statusTap', (event) => {
// TODO: Make this more better
var el = document.elementFromPoint(platform.width() / 2, platform.height() / 2);
if(!el) { return; }
var content = dom.closest(el, 'scroll-content');
if(content) {
var scrollTo = new ScrollTo(content);
scrollTo.start(0, 0, 300, 0);
}
});
}
/**
* TODO
* @param {Object} appRef TODO
*/
load(appRef) {
this.ref(appRef);
this._zone = appRef.injector.get(NgZone);
}
/**
* TODO
* @param {TODO=} val TODO
* @return {TODO} TODO
*/
focusHolder(val) {
if (arguments.length) {
this._fcsHldr = val;
}
return this._fcsHldr;
}
/**
* Sets the document title.
* @param {string} val Value to set the document title to.
@ -147,42 +84,6 @@ export class IonicApp {
return (this._trnsTime > Date.now());
}
/**
* TODO
* @param {TODO=} val TODO
* @return TODO
*/
ref(val) {
if (arguments.length) {
this._ref = val;
}
return this._ref;
}
/**
* TODO
* @return TODO
*/
get injector() {
return this._ref.injector;
}
/**
* TODO
* @param {Function} fn TODO
*/
zoneRun(fn) {
this._zone.run(fn);
}
/**
* TODO
* @param {Function} fn TODO
*/
zoneRunOutside(fn) {
this._zone.runOutsideAngular(fn);
}
/**
* Register a known component with a key, for easy lookups later.
* @param {TODO} id The id to use to register the component
@ -232,204 +133,11 @@ export class IonicApp {
* @param {TODO} componentType the component to create and insert
* @return {Promise} Promise that resolves with the ContainerRef created
*/
appendComponent(componentType: Type) {
return this.rootAnchor.append(componentType);
}
/**
* If val is defined, specifies whether app text is RTL. If val is undefined
* returns whether app text is RTL.
*
* @param {boolean=} val Boolean specifying whether text is RTL or not.
* @returns {boolean} true if app text is RTL, false if otherwise.
*/
isRTL(val) {
if (arguments.length) {
this._rtl = val;
appendOverlay(componentType: Type) {
if (!this.overlayAnchor) {
throw ('<ion-overlays></ion-overlays> must be added to your root component\'s template');
}
return this._rtl;
return this.overlayAnchor.append(componentType);
}
}
@Component({
selector: 'root-anchor'
})
@View({
template: ''
})
class RootAnchor {
constructor(app: IonicApp, elementRef: ElementRef, loader: DynamicComponentLoader) {
this.elementRef = elementRef;
this.loader = loader;
app.rootAnchor = this;
}
append(componentType) {
return this.loader.loadNextToLocation(componentType, this.elementRef).catch(err => {
console.error(err)
});
}
}
function initApp(window, document, config, platform) {
// create the base IonicApp
let app = new IonicApp();
app.isRTL(document.dir == 'rtl');
// load all platform data
platform.url(window.location.href);
platform.userAgent(window.navigator.userAgent);
platform.navigatorPlatform(window.navigator.platform);
platform.load();
// copy default platform settings into the user config platform settings
// user config platform settings should override default platform settings
config.setPlatform(platform);
// config and platform settings have been figured out
// apply the correct CSS to the app
applyBodyCss(document, config, platform);
// prepare the ready promise to fire....when ready
platform.prepareReady(config);
setTimeout(function() {
// start listening for resizes XXms after the app starts
window.addEventListener('resize', function() {
platform.windowResize();
});
}, 2500);
return app;
}
/**
* TODO
*
* @param {TODO} rootComponentType TODO
* @param {TODO} config TODO
* @return {Promise} TODO
*/
export function ionicBootstrap(rootComponentType, views, config) {
return new Promise(resolve => {
try {
// get the user config, or create one if wasn't passed in
if (typeof config !== IonicConfig) {
config = new IonicConfig(config);
}
let platform = new IonicPlatform();
// create the base IonicApp
let app = initApp(window, document, config, platform);
// TODO: probs need a better way to inject global injectables
let tapClick = new TapClick(app, config, window, document);
let actionSheet = new ActionSheet(app, config);
let modal = new Modal(app, config);
let popup = new Popup(app, config);
let events = new Events();
let translate = new Translate();
let navRegistry = new NavRegistry(views);
app.bindEvents(platform, events);
// add injectables that will be available to all child components
let appBindings = Injector.resolve([
bind(IonicApp).toValue(app),
bind(IonicConfig).toValue(config),
bind(IonicPlatform).toValue(platform),
bind(TapClick).toValue(tapClick),
bind(ActionSheet).toValue(actionSheet),
bind(Modal).toValue(modal),
bind(Popup).toValue(popup),
bind(Events).toValue(events),
ROUTER_BINDINGS,
bind(LocationStrategy).toClass(HashLocationStrategy),
bind(Translate).toValue(translate),
bind(NavRegistry).toValue(navRegistry)
]);
bootstrap(rootComponentType, appBindings).then(appRef => {
app.load(appRef);
// Adding a anchor to add overlays off of...huh??
let elementRefs = appRef._hostComponent.hostView._view.elementRefs;
let lastElementRef = elementRefs[1];
let injector = lastElementRef.parentView._view.rootElementInjectors[0]._injector;
let loader = injector.get(DynamicComponentLoader);
loader.loadNextToLocation(RootAnchor, lastElementRef).then(() => {
// append the focus holder if its needed
if (config.get('keyboardScrollAssist')) {
app.appendComponent(FocusHolder).then(ref => {
app.focusHolder(ref.instance);
});
}
}).catch(err => {
console.error(err)
});
resolve(app);
}).catch(err => {
console.error('ionicBootstrap', err);
});
} catch (err) {
console.error(err);
}
});
}
function applyBodyCss(document, config, platform) {
let bodyEle = document.body;
if (!bodyEle) {
return dom.ready(function() {
applyBodyCss(document, config, platform);
});
}
let versions = platform.versions();
platform.platforms().forEach(platformName => {
// platform-ios
let platformClass = 'platform-' + platformName;
bodyEle.classList.add(platformClass);
let platformVersion = versions[platformName];
if (platformVersion) {
// platform-ios9
platformClass += platformVersion.major;
bodyEle.classList.add(platformClass);
// platform-ios9_3
bodyEle.classList.add(platformClass + '_' + platformVersion.minor);
}
});
// set the mode class name
// ios
bodyEle.classList.add(config.get('mode'));
// touch devices should not use :hover CSS pseudo
// enable :hover CSS when the "hoverCSS" setting is not false
if (config.get('hoverCSS') !== false) {
bodyEle.classList.add('enable-hover');
}
/**
* Hairline Shim
* Add the "hairline" CSS class name to the body tag
* if the browser supports subpixels.
*/
if (window.devicePixelRatio >= 2) {
var hairlineEle = document.createElement('div');
hairlineEle.style.border = '.5px solid transparent';
bodyEle.appendChild(hairlineEle);
if (hairlineEle.offsetHeight === 1) {
bodyEle.classList.add('hairlines');
}
bodyEle.removeChild(hairlineEle);
}
}

View File

@ -1,4 +1,4 @@
import {Component, DirectiveBinding} from 'angular2/angular2';
import {Component, View, DirectiveBinding} from 'angular2/angular2';
import {IonicApp} from '../app/app';
import {Animation} from '../../animations/animation';
@ -7,10 +7,8 @@ import * as util from 'ionic/util';
export class Overlay {
constructor(app: IonicApp, config: IonicConfig) {
constructor(app: IonicApp) {
this.app = app;
this.config = config;
this.mode = config.get('mode');
}
create(overlayType, componentType: Type, opts={}, context=null) {
@ -21,7 +19,6 @@ export class Overlay {
selector: 'ion-' + overlayType,
host: {
'[style.z-index]': 'zIndex',
'mode': this.mode,
'class': overlayType
}
});
@ -30,14 +27,14 @@ export class Overlay {
// create a unique token that works as a cache key
overlayComponentType.token = overlayType + componentType.name;
app.appendComponent(overlayComponentType).then(ref => {
app.appendOverlay(overlayComponentType).then(ref => {
let overlayRef = new OverlayRef(app, overlayType, opts, ref, context);
overlayRef._open(opts).then(() => {
resolve(overlayRef);
});
}).catch(err => {
console.error('Overlay appendComponent:', err);
console.error('Overlay appendOverlay:', err);
reject(err);
});
@ -181,4 +178,26 @@ export class OverlayRef {
}
@Component({
selector: 'ion-overlays'
})
@View({
template: ''
})
export class OverlaysContainer {
constructor(app: IonicApp, elementRef: ElementRef, loader: DynamicComponentLoader) {
this.elementRef = elementRef;
this.loader = loader;
app.overlayAnchor = this;
}
append(componentType) {
return this.loader.loadNextToLocation(componentType, this.elementRef).catch(err => {
console.error(err);
});
}
}
const ROOT_Z_INDEX = 1000;

View File

@ -1,16 +1,18 @@
import {Injectable} from 'angular2/angular2';
import {IonicApp} from '../app/app';
import {IonicConfig} from '../../config/config';
import {pointerCoord, hasPointerMoved, transitionEnd} from '../../util/dom';
import {Activator} from './activator';
import {RippleActivator} from './ripple';
@Injectable()
export class TapClick {
constructor(app: IonicApp, config: IonicConfig, window, document) {
constructor(app: IonicApp, config: IonicConfig) {
const self = this;
self.app = app;
self.config = config;
self.win = window;
self.doc = document;
self.pointerTolerance = 4;
self.lastTouch = 0;
@ -18,14 +20,13 @@ export class TapClick {
self.disableClick = 0;
self.disableClickLimit = 1000;
self.tapPolyfill = (config.get('tapPolyfill') !== false);
if (config.get('mdRipple')) {
self.activator = new RippleActivator(app, config);
} else {
self.activator = new Activator(app, config);
}
self.enable( config.get('tapPolyfill') !== false );
function bindDom(type, listener, useCapture) {
document.addEventListener(type, listener, useCapture);
@ -80,6 +81,10 @@ export class TapClick {
}
enable(shouldEnable) {
this._enabled = shouldEnable;
}
/**
* TODO
@ -88,7 +93,7 @@ export class TapClick {
touchEnd(ev) {
let self = this;
if (self.tapPolyfill && self.start && self.app.isEnabled()) {
if (self._enabled && self.start && self.app.isEnabled()) {
let endCoord = pointerCoord(ev);
if (!hasPointerMoved(self.pointerTolerance, self.start, endCoord)) {
@ -96,8 +101,8 @@ export class TapClick {
self.disableClick = Date.now();
let clickEvent = self.doc.createEvent('MouseEvents');
clickEvent.initMouseEvent('click', true, true, self.win, 1, 0, 0, endCoord.x, endCoord.y, false, false, false, false, 0, null);
let clickEvent = document.createEvent('MouseEvents');
clickEvent.initMouseEvent('click', true, true, window, 1, 0, 0, endCoord.x, endCoord.y, false, false, false, false, 0, null);
clickEvent.isIonicTap = true;
ev.target.dispatchEvent(clickEvent);
}

146
ionic/config/bootstrap.ts Normal file
View File

@ -0,0 +1,146 @@
import {bootstrap, bind} from 'angular2/angular2';
import {ROUTER_BINDINGS, HashLocationStrategy, LocationStrategy} from 'angular2/router';
import {IonicApp} from '../components/app/app';
import {IonicConfig} from './config';
import {IonicPlatform} from '../platform/platform';
import {ActionSheet} from '../components/action-sheet/action-sheet';
import {Modal} from '../components/modal/modal';
import {Popup} from '../components/popup/popup';
import {Events} from '../util/events';
import {NavRegistry} from '../components/nav/nav-registry';
import {Translate} from '../translation/translate';
import {ClickBlock} from '../util/click-block';
import {TapClick} from '../components/tap-click/tap-click';
import * as dom from '../util/dom';
export function ionicBindings(configSettings) {
let app = new IonicApp();
let platform = new IonicPlatform();
let config = new IonicConfig(configSettings);
let tapClick = new TapClick(app, config, window, document);
// load all platform data
platform.url(window.location.href);
platform.userAgent(window.navigator.userAgent);
platform.navigatorPlatform(window.navigator.platform);
platform.load();
config.setPlatform(platform);
setupDom(window, document, config, platform);
bindEvents(window, document, platform, events);
// prepare the ready promise to fire....when ready
platform.prepareReady(config);
return [
bind(IonicApp).toValue(app),
bind(IonicConfig).toValue(config),
bind(IonicPlatform).toValue(platform),
bind(TapClick).toValue(tapClick),
bind(Events).toValue(events),
ActionSheet,
Modal,
Popup,
Translate,
ROUTER_BINDINGS,
bind(LocationStrategy).toClass(HashLocationStrategy),
];
}
function setupDom(window, document, config, platform) {
let bodyEle = document.body;
if (!bodyEle) {
return dom.ready(function() {
applyBodyCss(document, config, platform);
});
}
let versions = platform.versions();
platform.platforms().forEach(platformName => {
// platform-ios
let platformClass = 'platform-' + platformName;
bodyEle.classList.add(platformClass);
let platformVersion = versions[platformName];
if (platformVersion) {
// platform-ios9
platformClass += platformVersion.major;
bodyEle.classList.add(platformClass);
// platform-ios9_3
bodyEle.classList.add(platformClass + '_' + platformVersion.minor);
}
});
// set the mode class name
// ios/md
bodyEle.classList.add(config.get('mode'));
// touch devices should not use :hover CSS pseudo
// enable :hover CSS when the "hoverCSS" setting is not false
if (config.get('hoverCSS') !== false) {
bodyEle.classList.add('enable-hover');
}
if (config.get('keyboardScrollAssist')) {
// create focus holder
}
/**
* Hairline Shim
* Add the "hairline" CSS class name to the body tag
* if the browser supports subpixels.
*/
if (window.devicePixelRatio >= 2) {
var hairlineEle = document.createElement('div');
hairlineEle.style.border = '.5px solid transparent';
bodyEle.appendChild(hairlineEle);
if (hairlineEle.offsetHeight === 1) {
bodyEle.classList.add('hairlines');
}
bodyEle.removeChild(hairlineEle);
}
}
/**
* Bind some global events and publish on the 'app' channel
*/
function bindEvents(window, document, platform, events) {
window.addEventListener('online', (event) => {
events.publish('app:online', event);
}, false);
window.addEventListener('offline', (event) => {
events.publish('app:offline', event);
}, false);
window.addEventListener('orientationchange', (event) => {
events.publish('app:rotated', event);
});
// When that status taps, we respond
window.addEventListener('statusTap', (event) => {
// TODO: Make this more better
var el = document.elementFromPoint(platform.width() / 2, platform.height() / 2);
if(!el) { return; }
var content = dom.closest(el, 'scroll-content');
if(content) {
var scrollTo = new ScrollTo(content);
scrollTo.start(0, 0, 300, 0);
}
});
// start listening for resizes XXms after the app starts
setTimeout(function() {
window.addEventListener('resize', function() {
platform.windowResize();
});
}, 2000);
}

View File

@ -1,97 +1,8 @@
import {CORE_DIRECTIVES, FORM_DIRECTIVES, NgStyle, Component, Directive, View, forwardRef} from 'angular2/angular2'
import {Component, Directive, View, bootstrap} from 'angular2/angular2'
import * as util from 'ionic/util';
import {IonicConfig} from './config';
import {ionicBootstrap} from '../components/app/app';
import {
Menu, MenuToggle, MenuClose,
Button, Content, Scroll, Refresher,
Slides, Slide, SlideLazy,
Tabs, Tab,
Card, List, ListHeader, Item, ItemGroup, ItemGroupTitle,
Toolbar, ToolbarTitle, ToolbarItem,
Icon,
Checkbox, Switch,
TextInput, TextInputElement, Label,
Segment, SegmentButton, SegmentControlValueAccessor,
RadioGroup, RadioButton, SearchBar,
Nav, NavbarTemplate, Navbar,
NavPush, NavPop, NavRouter,
IdRef,
ShowWhen, HideWhen
} from '../ionic';
/**
* The core Ionic directives. Automatically available in every IonicView
* template.
*/
export const IONIC_DIRECTIVES = [
// Angular
CORE_DIRECTIVES,
FORM_DIRECTIVES,
NgStyle,
// Content
forwardRef(() => Menu),
forwardRef(() => MenuToggle),
forwardRef(() => MenuClose),
forwardRef(() => Button),
forwardRef(() => Content),
forwardRef(() => Scroll),
forwardRef(() => Refresher),
// Lists
forwardRef(() => Card),
forwardRef(() => List),
forwardRef(() => ListHeader),
forwardRef(() => Item),
forwardRef(() => ItemGroup),
forwardRef(() => ItemGroupTitle),
// Slides
forwardRef(() => Slides),
forwardRef(() => Slide),
forwardRef(() => SlideLazy),
// Tabs
forwardRef(() => Tabs),
forwardRef(() => Tab),
// Toolbar
forwardRef(() => Toolbar),
forwardRef(() => ToolbarTitle),
forwardRef(() => ToolbarItem),
// Media
forwardRef(() => Icon),
// Forms
forwardRef(() => Segment),
forwardRef(() => SegmentButton),
forwardRef(() => SegmentControlValueAccessor),
forwardRef(() => Checkbox),
forwardRef(() => RadioGroup),
forwardRef(() => RadioButton),
forwardRef(() => Switch),
forwardRef(() => TextInput),
forwardRef(() => TextInputElement),
forwardRef(() => Label),
// Nav
forwardRef(() => Nav),
forwardRef(() => NavbarTemplate),
forwardRef(() => Navbar),
forwardRef(() => NavPush),
forwardRef(() => NavPop),
forwardRef(() => NavRouter),
forwardRef(() => IdRef),
forwardRef(() => ShowWhen),
forwardRef(() => HideWhen)
];
import {ionicBindings} from './bootstrap';
import {IONIC_DIRECTIVES} from './directives';
/**
* @private
@ -192,7 +103,7 @@ export function App(args={}) {
// redefine with added annotations
Reflect.defineMetadata('annotations', annotations, cls);
ionicBootstrap(cls, args.views, args.config);
bootstrap(cls, ionicBindings(args.config));
return cls;
}

View File

@ -0,0 +1,90 @@
import {CORE_DIRECTIVES, FORM_DIRECTIVES, forwardRef} from 'angular2/angular2'
import {
Menu, MenuToggle, MenuClose,
Button, Content, Scroll, Refresher,
Slides, Slide, SlideLazy,
Tabs, Tab,
Card, List, ListHeader, Item, ItemGroup, ItemGroupTitle,
Toolbar, ToolbarTitle, ToolbarItem,
Icon,
Checkbox, Switch,
TextInput, TextInputElement, Label,
Segment, SegmentButton, SegmentControlValueAccessor,
RadioGroup, RadioButton, SearchBar,
Nav, NavbarTemplate, Navbar,
NavPush, NavPop, NavRouter,
IdRef,
ShowWhen, HideWhen
} from '../ionic';
/**
* The core Ionic directives. Automatically available in every
* IonicView template.
*/
export const IONIC_DIRECTIVES = [
// Angular
CORE_DIRECTIVES,
FORM_DIRECTIVES,
// Content
forwardRef(() => Menu),
forwardRef(() => MenuToggle),
forwardRef(() => MenuClose),
forwardRef(() => Button),
forwardRef(() => Content),
forwardRef(() => Scroll),
forwardRef(() => Refresher),
// Lists
forwardRef(() => Card),
forwardRef(() => List),
forwardRef(() => ListHeader),
forwardRef(() => Item),
forwardRef(() => ItemGroup),
forwardRef(() => ItemGroupTitle),
// Slides
forwardRef(() => Slides),
forwardRef(() => Slide),
forwardRef(() => SlideLazy),
// Tabs
forwardRef(() => Tabs),
forwardRef(() => Tab),
// Toolbar
forwardRef(() => Toolbar),
forwardRef(() => ToolbarTitle),
forwardRef(() => ToolbarItem),
// Media
forwardRef(() => Icon),
// Forms
forwardRef(() => Segment),
forwardRef(() => SegmentButton),
forwardRef(() => SegmentControlValueAccessor),
forwardRef(() => Checkbox),
forwardRef(() => RadioGroup),
forwardRef(() => RadioButton),
forwardRef(() => Switch),
forwardRef(() => TextInput),
forwardRef(() => TextInputElement),
forwardRef(() => Label),
// Nav
forwardRef(() => Nav),
forwardRef(() => NavbarTemplate),
forwardRef(() => Navbar),
forwardRef(() => NavPush),
forwardRef(() => NavPop),
forwardRef(() => NavRouter),
forwardRef(() => IdRef),
forwardRef(() => ShowWhen),
forwardRef(() => HideWhen)
];

View File

@ -1,4 +1,5 @@
export * from './config/bootstrap'
export * from './config/config'
export * from './config/modes'
export * from './config/decorators'

View File

@ -57,7 +57,6 @@
"source-map-support": "^0.2.10",
"systemjs": "0.18.10",
"through2": "^0.6.3",
"traceur-runtime": "0.0.59",
"typescript": "1.5.3",
"vinyl": "^0.4.6",
"yargs": "^3.6.0"