diff --git a/README.md b/README.md index 67ddff606c..4ba33666bb 100644 --- a/README.md +++ b/README.md @@ -14,61 +14,4 @@ To try Ionic 2 today, visit the [Ionic 2 Docs](http://ionicframework.com/docs/v2 There are a few real Ionic 2 apps in the wild. The most complete is the [Ionic Conference App](https://github.com/driftyco/ionic-conference-app), a perfect starting point for building your own conference app. -## Distribution - - [npm: ionic-framework](https://www.npmjs.com/package/ionic-framework) - -## Ionic Framework Package - The ionic-framework package comes with both frontend dependencies, located in 'dist', and a Node API, located in 'tooling'. - -### Bundles: - - - `css/` - - the Ionic CSS stylesheet - - `fonts/` - - Ionicons and Roboto fonts - - `js/` - - `ionic.js` the Ionic module, in System register format - - `ionic.bundle.js` the Ionic bundle, contains: - - es6-module-loader.js - - system.js - - angular2.dev.js - - router.dev.js (angular2 router) - - ionic.js - - web-animations.min.js - - `web-animations.min.js` web animations API polyfill - -### Source files: - - - `src/es5` - Ionic ES5 source files in both CommonJS and System module formats - - `src/es6` - Ionic ES6 source files - - `src/ts` - Ionic TypeScript source files (typings still missing) - - `scss` - Ionic Sass source files - ---------- - -### Tooling - - At the moment, ionic-framework exports one function, `generate`, that can be used to scaffold new pages in an Ionic app. It is used by the [Ionic CLI's](https://github.com/driftyco/ionic-cli) `generate` command. - -#### Methods - -`generate(config)` - -Creates the js, html, and scss file for a new page, based on the supplied [Generator](#generators). - -- **config** (Object) Config object, with the following options: - - `appDirectory` - root directory of the Ionic project - - `generator` - which [generator](#generators) to use, default is `page`. - - `name` - the name of the component to generate. - -Example: - ``` - var ionic = require('ionic-framework'); - ionic.generate({ appDirectory: process.cwd(), generator: 'tabs', name: 'MyTabsPage' }) - ``` - -#### Generators -- `page`, a blank page -- `tabs`, a page with tab navigation - diff --git a/gulpfile.js b/gulpfile.js index 9ea32607a6..bbf4a3e67b 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -401,7 +401,8 @@ gulp.task('package', ['src'], function(){ return gulp.src([ 'scripts/npm/.npmignore', 'scripts/npm/package.json', - 'README.md' + 'scripts/npm/README.md', + '*tooling/**/*' ]) .pipe(gulp.dest('dist')); }); diff --git a/ionic/components/action-sheet/action-sheet.ts b/ionic/components/action-sheet/action-sheet.ts index 1a122600c0..97472f903a 100644 --- a/ionic/components/action-sheet/action-sheet.ts +++ b/ionic/components/action-sheet/action-sheet.ts @@ -130,8 +130,8 @@ export class ActionSheet { * the action sheet, or false to keep it opened. * - `{function=}` `destructiveButtonClicked` Called when the destructive button is clicked. * Return true to close the action sheet, or false to keep it opened. - * @param {String} [opts.enterAnimation='action-sheet-slide-in'] The class used to animate an actionSheet that is entering. - * @param {String} [opts.leaveAnimation='action-sheet-slide-out'] The class used to animate an actionSheet that is leaving. + * - `{String}` `enterAnimation` The class used to animate an actionSheet that is entering. + * - `{String}` `leaveAnimation` The class used to animate an actionSheet that is leaving. * @return {Promise} Promise that resolves when the action sheet is open. */ open(opts={}) { diff --git a/ionic/components/app/id.ts b/ionic/components/app/id.ts index f8bda41715..8efbe00f5e 100644 --- a/ionic/components/app/id.ts +++ b/ionic/components/app/id.ts @@ -42,23 +42,23 @@ import {IonicApp} from './app'; }) export class IdRef { - constructor(private app: IonicApp, private elementRef: ElementRef, private appViewManager: AppViewManager) { + constructor(private _app: IonicApp, private _elementRef: ElementRef, private _appViewManager: AppViewManager) { // Grab the component this directive is attached to - this.component = appViewManager.getComponent(elementRef); + this.component = _appViewManager.getComponent(_elementRef); } /** * @private */ ngOnInit() { - this.app.register(this.id, this.component); + this._app.register(this.id, this.component); } /** * @private */ ngOnDestroy() { - this.app.unregister(this.id); + this._app.unregister(this.id); } } @@ -80,12 +80,12 @@ export class IdRef { }) export class Attr { - constructor(private renderer: Renderer, private elementRef: ElementRef) {} + constructor(private _renderer: Renderer, private _elementRef: ElementRef) {} /** * @private */ ngOnInit() { - this.renderer.setElementAttribute(this.elementRef, this.attr, ''); + this._renderer.setElementAttribute(this._elementRef, this.attr, ''); } } diff --git a/ionic/components/blur/blur.ts b/ionic/components/blur/blur.ts index d70e3cccce..b2c62432f8 100644 --- a/ionic/components/blur/blur.ts +++ b/ionic/components/blur/blur.ts @@ -20,7 +20,7 @@ import {Directive, Renderer, ElementRef} from 'angular2/core'; selector: '[blur]' }) export class Blur { - constructor(private elementRef: ElementRef, private renderer: Renderer) { - renderer.setElementStyle(elementRef, '-webkit-backdrop-filter', 'blur(10px)'); + constructor(private _elementRef: ElementRef, private _renderer: Renderer) { + _renderer.setElementStyle(_elementRef, '-webkit-backdrop-filter', 'blur(10px)'); } } diff --git a/ionic/components/button/button.ts b/ionic/components/button/button.ts index 9c270c49c3..504d54c87e 100644 --- a/ionic/components/button/button.ts +++ b/ionic/components/button/button.ts @@ -21,6 +21,7 @@ import {Toolbar} from '../toolbar/toolbar'; * @property [fab-center] - position a fab button towards the center * @property [fab-top] - position a fab button towards the top * @property [fab-bottom] - position a fab button towards the bottom + * @property [color] - Dynamically set which color attribute this button should use. * @description * Buttons are simple components in Ionic, can consist of text, an icon, or both, and can be enhanced with a wide range of attributes. * @demo /docs/v2/demos/buttons/ @@ -28,28 +29,30 @@ import {Toolbar} from '../toolbar/toolbar'; */ @Directive({ - selector: 'button,[button]' + selector: 'button,[button]', + inputs: ['color'] }) export class Button { constructor( config: Config, - private elementRef: ElementRef, - private renderer: Renderer + private _elementRef: ElementRef, + private _renderer: Renderer ) { this._role = 'button'; // bar-button/item-button this._size = null; // large/small this._style = 'default'; // outline/clear/solid this._shape = null; // round/fab this._display = null; // block/full + this._lastColor = null; this._colors = []; // primary/secondary this._icon = null; // left/right/only this._disabled = false; // disabled - let element = elementRef.nativeElement; + let element = _elementRef.nativeElement; if (config.get('hoverCSS') === false) { - renderer.setElementClass(elementRef, 'disable-hover', true); + _renderer.setElementClass(_elementRef, 'disable-hover', true); } if (element.hasAttribute('ion-item')) { @@ -66,16 +69,32 @@ export class Button { this._readIcon(element); } -/** - * @private - */ + /** + * @private + */ ngAfterContentInit() { + this._lastColor = this.color; + if (this.color) { + this._colors = [this.color]; + } this._assignCss(true); } -/** - * @private - */ + /** + * @private + */ + ngAfterContentChecked() { + if (this._lastColor !== this.color) { + this._assignCss(false); + this._lastColor = this.color; + this._colors = [this.color]; + this._assignCss(true); + } + } + + /** + * @private + */ setRole(val) { this._role = val; } @@ -147,7 +166,7 @@ export class Button { _assignCss(assignCssClass) { let role = this._role; if (role) { - this.renderer.setElementClass(this.elementRef, role, assignCssClass); // button + this._renderer.setElementClass(this._elementRef, role, assignCssClass); // button this._setClass(this._style, assignCssClass); // button-clear this._setClass(this._shape, assignCssClass); // button-round @@ -164,7 +183,7 @@ export class Button { _setClass(type, assignCssClass) { if (type) { - this.renderer.setElementClass(this.elementRef, this._role + '-' + type, assignCssClass); + this._renderer.setElementClass(this._elementRef, this._role + '-' + type, assignCssClass); } } diff --git a/ionic/components/button/test/basic/index.ts b/ionic/components/button/test/basic/index.ts index ff6f299c59..01b30a6221 100644 --- a/ionic/components/button/test/basic/index.ts +++ b/ionic/components/button/test/basic/index.ts @@ -7,5 +7,15 @@ import {App, IonicApp} from 'ionic/ionic'; class E2EApp { constructor(app: IonicApp) { app.setTitle('Basic Buttons'); + + this.testingColors = ['primary', 'secondary', 'danger', 'dark']; + this.testingColorIndex = 0; + this.chgColor(); + } + + chgColor() { + this.btnColor = this.testingColors[this.testingColorIndex]; + console.log('dynamic btnColor', this.btnColor); + this.testingColorIndex = (this.testingColorIndex >= this.testingColors.length - 1 ? 0 : this.testingColorIndex + 1); } } diff --git a/ionic/components/button/test/basic/main.html b/ionic/components/button/test/basic/main.html index 634fabca7b..0a1b3214e1 100644 --- a/ionic/components/button/test/basic/main.html +++ b/ionic/components/button/test/basic/main.html @@ -39,4 +39,9 @@

+

+ + +

+ diff --git a/ionic/components/checkbox/checkbox.ts b/ionic/components/checkbox/checkbox.ts index 46f03486b6..dd7517d012 100644 --- a/ionic/components/checkbox/checkbox.ts +++ b/ionic/components/checkbox/checkbox.ts @@ -54,12 +54,11 @@ import {Form} from '../../util/form'; export class Checkbox { constructor( - private form: Form, + private _form: Form, @Optional() ngControl: NgControl, elementRef: ElementRef ) { - this.form = form; - form.register(this); + _form.register(this); this.onChange = (_) => {}; this.onTouched = (_) => {}; @@ -75,7 +74,7 @@ export class Checkbox { */ ngOnInit() { if (!this.id) { - this.id = 'chk-' + this.form.nextId(); + this.id = 'chk-' + this._form.nextId(); } this.labelId = 'lbl-' + this.id; @@ -132,6 +131,6 @@ export class Checkbox { * @private */ ngOnDestroy() { - this.form.deregister(this); + this._form.deregister(this); } } diff --git a/ionic/components/icon/icon.ts b/ionic/components/icon/icon.ts index d1f1a4abde..31f4962a96 100644 --- a/ionic/components/icon/icon.ts +++ b/ionic/components/icon/icon.ts @@ -40,9 +40,9 @@ import {Config} from '../../config/config'; export class Icon { constructor( - private elementRef: ElementRef, + private _elementRef: ElementRef, config: Config, - private renderer: Renderer + private _renderer: Renderer ) { this.config = config; this.mode = config.get('iconMode'); @@ -52,7 +52,7 @@ export class Icon { * @private */ ngOnInit() { - let ele = this.elementRef.nativeElement; + let ele = this._elementRef.nativeElement; if (this.mode == 'ios' && this.ios) { this.name = this.ios; @@ -113,12 +113,12 @@ export class Icon { if (this._name !== this.name) { if (this._name) { - this.renderer.setElementClass(this.elementRef, this._name, false); + this._renderer.setElementClass(this._elementRef, this._name, false); } this._name = this.name; - this.renderer.setElementClass(this.elementRef, this.name, true); + this._renderer.setElementClass(this._elementRef, this.name, true); - this.renderer.setElementAttribute(this.elementRef, 'aria-label', + this._renderer.setElementAttribute(this._elementRef, 'aria-label', this.name.replace('ion-', '').replace('ios-', '').replace('md-', '').replace('-', ' ')); } } diff --git a/ionic/components/item/item-sliding.ts b/ionic/components/item/item-sliding.ts index 7db35d546a..f653ee6fcf 100644 --- a/ionic/components/item/item-sliding.ts +++ b/ionic/components/item/item-sliding.ts @@ -34,8 +34,8 @@ import {List} from '../list/list'; }) export class ItemSliding { - constructor(@Optional() private list: List, elementRef: ElementRef) { - list.enableSlidingItems(true); + constructor(@Optional() private _list: List, elementRef: ElementRef) { + _list.enableSlidingItems(true); elementRef.nativeElement.$ionSlide = ++slideIds; } @@ -43,7 +43,7 @@ export class ItemSliding { * @private */ close() { - this.list.closeSlidingItems(); + this._list.closeSlidingItems(); } } diff --git a/ionic/components/label/label.ts b/ionic/components/label/label.ts index 668ed560a1..04f877b8b4 100644 --- a/ionic/components/label/label.ts +++ b/ionic/components/label/label.ts @@ -42,9 +42,9 @@ export class Label { constructor( config: Config, @Optional() container: TextInput, - private form: Form, - private elementRef: ElementRef, - private renderer: Renderer + private _form: Form, + private _elementRef: ElementRef, + private _renderer: Renderer ) { this.scrollAssist = config.get('scrollAssist'); this.container = container; @@ -55,7 +55,7 @@ export class Label { */ ngOnInit() { if (!this.id) { - this.id = 'lbl-' + this.form.nextId(); + this.id = 'lbl-' + this._form.nextId(); } this.container && this.container.registerLabel(this); } @@ -94,7 +94,7 @@ export class Label { * @private */ addClass(className) { - this.renderer.setElementClass(this.elementRef, className, true); + this._renderer.setElementClass(this._elementRef, className, true); } } diff --git a/ionic/components/list/list.ts b/ionic/components/list/list.ts index 6c4133cadf..8fd888ff64 100644 --- a/ionic/components/list/list.ts +++ b/ionic/components/list/list.ts @@ -1,10 +1,10 @@ -import {Directive, ElementRef, NgZone} from 'angular2/core'; +import {Directive, ElementRef, Renderer, Attribute, NgZone} from 'angular2/core'; import {Ion} from '../ion'; import {Config} from '../../config/config'; import {ListVirtualScroll} from './virtual'; import {ItemSlidingGesture} from '../item/item-sliding-gesture'; -import * as util from '../../util'; +import {isDefined} from '../../util'; /** * The List is a widely used interface element in almost any mobile app, and can include @@ -42,7 +42,7 @@ export class List extends Ion { ngOnInit() { super.ngOnInit(); - if (util.isDefined(this.virtual)) { + if (isDefined(this.virtual)) { console.log('Content', this.content); console.log('Virtual?', this.virtual); console.log('Items?', this.items.length, 'of \'em'); @@ -138,12 +138,21 @@ export class List extends Ion { * @private */ @Directive({ - selector: 'ion-list-header', - inputs: [ - 'id' - ], - host: { - '[attr.id]': 'id' - } + selector: 'ion-list-header' }) -export class ListHeader {} +export class ListHeader { + + constructor(private _renderer: Renderer, private _elementRef: ElementRef, @Attribute('id') id:string){ + this._id = id; + } + + public get id() { + return this._id; + } + + public set id(val) { + this._id = val; + this._renderer.setElementAttribute(this._elementRef, 'id', val); + } + +} diff --git a/ionic/components/menu/menu-close.ts b/ionic/components/menu/menu-close.ts index 7b3be9e232..e28ca26402 100644 --- a/ionic/components/menu/menu-close.ts +++ b/ionic/components/menu/menu-close.ts @@ -37,13 +37,13 @@ import {Menu} from './menu'; }) export class MenuClose { - constructor(private app: IonicApp) {} + constructor(private _app: IonicApp) {} /** * @private */ close() { - let menu = Menu.getById(this.app, this.menuClose); + let menu = Menu.getById(this._app, this.menuClose); menu && menu.close(); } diff --git a/ionic/components/radio/radio.ts b/ionic/components/radio/radio.ts index 808caa9ca7..b1fbd5642a 100644 --- a/ionic/components/radio/radio.ts +++ b/ionic/components/radio/radio.ts @@ -1,10 +1,84 @@ -import {Component, Directive, ElementRef, Host, Optional, Query, QueryList} from 'angular2/core'; +import {Component, Directive, ElementRef, Renderer, Optional, Input, Output, HostListener, ContentChildren, ContentChild, EventEmitter} from 'angular2/core'; import {NgControl} from 'angular2/common'; -import {Config} from '../../config/config'; -import {Ion} from '../ion'; import {ListHeader} from '../list/list'; import {Form} from '../../util/form'; +import {isDefined} from '../../util/util'; + + +/** + * @description + * A radio button with a unique value. Note that all `` components + * must be wrapped within a ``, and there must be at + * least two `` components within the radio group. + * + * See the [Angular 2 Docs](https://angular.io/docs/js/latest/api/forms/) for more info on forms and input. + * + * @usage + * ```html + * + * Radio Label + * + * ``` + * @demo /docs/v2/demos/radio/ + * @see {@link /docs/v2/components#radio Radio Component Docs} + */ +@Component({ + selector: 'ion-radio', + host: { + '[attr.id]': 'id', + '[attr.aria-disabled]': 'disabled', + '[attr.aria-labelledby]': 'labelId', + 'class': 'item', + 'role': 'radio', + 'tappable': '', + 'tabindex': '0' + }, + template: + '
' + + '' + + '' + + '' + + '
' + + '
' + + '
' + + '
' +}) +export class RadioButton { + @Input() value: string = ''; + @Input() checked: boolean = false; + @Input() disabled: boolean = false; + @Input() id: string; + @Output() select: EventEmitter = new EventEmitter(); + + constructor(private _form: Form, private _renderer: Renderer, private _elementRef: ElementRef) { + this.isChecked = this.checked; + _renderer.setElementAttribute(_elementRef, 'checked', null); + } + + /** + * @private + */ + ngOnInit() { + if (!this.id) { + this.id = 'rb-' + this._form.nextId(); + } + this.labelId = 'lbl-' + this.id; + } + + /** + * @private + */ + @HostListener('click', ['$event']) + private onClick(ev) { + console.debug('RadioButton, select', this.value); + this.select.emit(ev, this.value); + } + + public set isChecked(isChecked) { + this._renderer.setElementAttribute(this._elementRef, 'aria-checked', isChecked); + } +} /** @@ -18,26 +92,34 @@ import {Form} from '../../util/form'; * * @usage * ```html - * + * * * - * Clientside + * Auto Manufacturers * * - * - * Ember + * + * Cord * * - * - * Angular 1 + * + * Duesenberg * * - * - * Angular 2 + * + * Hudson * * - * - * React + * + * Packard + * + * + * + * Studebaker + * + * + * + * Tucker * * * @@ -48,79 +130,26 @@ import {Form} from '../../util/form'; @Directive({ selector: '[radio-group]', host: { - 'role': 'radiogroup', '[attr.aria-activedescendant]': 'activeId', - '[attr.aria-describedby]': 'describedById', + 'role': 'radiogroup' } }) -export class RadioGroup extends Ion { - radios: Array = []; +export class RadioGroup { + @Output() change: EventEmitter = new EventEmitter(); + @ContentChildren(RadioButton) private _buttons; + @ContentChild(ListHeader) private _header; - constructor( - elementRef: ElementRef, - config: Config, - @Optional() ngControl: NgControl, - @Query(ListHeader) private headerQuery: QueryList - ) { - super(elementRef, config); + constructor(@Optional() ngControl: NgControl, private _renderer: Renderer, private _elementRef: ElementRef) { this.ngControl = ngControl; this.id = ++radioGroupIds; - this.radioIds = -1; this.onChange = (_) => {}; this.onTouched = (_) => {}; - if (ngControl) this.ngControl.valueAccessor = this; - } - - /** - * @private - */ - ngOnInit() { - let header = this.headerQuery.first; - if (header) { - if (!header.id) { - header.id = 'radio-header-' + this.id; - } - this.describedById = header.id; + if (ngControl) { + this.ngControl.valueAccessor = this; } } - /** - * @private - * Register the specified radio button with the radio group. - * @param {RadioButton} radio The radio button to register. - */ - registerRadio(radio) { - radio.id = radio.id || ('radio-' + this.id + '-' + (++this.radioIds)); - this.radios.push(radio); - - if (this.value == radio.value) { - radio.check(this.value); - } - - if (radio.checked) { - this.value = radio.value; - this.onChange(this.value); - this.activeId = radio.id; - } - } - - /** - * @private - * Update which radio button in the group is checked, unchecking all others. - * @param {RadioButton} checkedRadio The radio button to check. - */ - update(checkedRadio) { - this.value = checkedRadio.value; - this.activeId = checkedRadio.id; - - for (let radio of this.radios) { - radio.checked = (radio === checkedRadio); - } - - this.onChange(this.value); - } - /** * @private * Angular2 Forms API method called by the model (Control) on change to update @@ -128,9 +157,46 @@ export class RadioGroup extends Ion { * https://github.com/angular/angular/blob/master/modules/angular2/src/forms/directives/shared.ts#L34 */ writeValue(value) { - this.value = value; - for (let radio of this.radios) { - radio.checked = (radio.value == value); + this.value = isDefined(value) ? value : ''; + if (this._buttons) { + let buttons = this._buttons.toArray(); + for (let button of buttons) { + let isChecked = (button.value === this.value); + button.isChecked = isChecked; + if (isChecked) { + this._renderer.setElementAttribute(this._elementRef, 'aria-activedescendant', button.id); + } + } + } + } + + /** + * @private + */ + ngAfterContentInit() { + let header = this._header; + if (header) { + if (!header.id) { + header.id = 'rg-hdr-' + this.id; + } + this._renderer.setElementAttribute(this._elementRef, 'aria-describedby', header.id); + } + + let buttons = this._buttons.toArray(); + for (let button of buttons) { + button.select.subscribe(() => { + this.writeValue(button.value); + this.onChange(button.value); + this.change.emit(this.value); + }); + + if (isDefined(this.value)) { + let isChecked = (button.value === this.value) || button.checked; + button.isChecked = isChecked; + if (isChecked) { + this._renderer.setElementAttribute(this._elementRef, 'aria-activedescendant', button.id); + } + } } } @@ -152,100 +218,4 @@ export class RadioGroup extends Ion { registerOnTouched(fn) { this.onTouched = fn; } } - -/** - * @description - * A single radio component. - * - * See the [Angular 2 Docs](https://angular.io/docs/js/latest/api/forms/) for more info on forms and input. - * - * @usage - * ```html - * - * Radio Label - * - * ``` - * @demo /docs/v2/demos/radio/ - * @see {@link /docs/v2/components#radio Radio Component Docs} - */ -@Component({ - selector: 'ion-radio', - inputs: [ - 'value', - 'checked', - 'disabled', - 'id' - ], - host: { - 'role': 'radio', - 'tappable': 'true', - '[attr.id]': 'id', - '[tabindex]': 'tabIndex', - '[attr.aria-checked]': 'checked', - '[attr.aria-disabled]': 'disabled', - '[attr.aria-labelledby]': 'labelId', - '(click)': 'click($event)', - 'class': 'item' - }, - template: - '
' + - '' + - '' + - '' + - '
' + - '
' + - '
' + - '
' -}) -export class RadioButton extends Ion { - - constructor( - @Host() @Optional() group: RadioGroup, - elementRef: ElementRef, - config: Config, - private form: Form - ) { - super(elementRef, config); - - this.group = group; - this.tabIndex = 0; - } - - /** - * @private - */ - ngOnInit() { - super.ngOnInit(); - if (!this.id) { - this.id = 'rb-' + this.form.nextId(); - } - this.labelId = 'lbl-' + this.id; - - if (this.group) { - this.group.registerRadio(this); - } else { - console.error(' must be within a '); - } - } - - /** - * @private - */ - click(ev) { - ev.preventDefault(); - ev.stopPropagation(); - this.check(); - } - - /** - * Update the checked state of this radio button. - * TODO: Call this toggle? Since unchecks as well - */ - check() { - this.checked = !this.checked; - this.group.update(this); - } - -} - let radioGroupIds = -1; diff --git a/ionic/components/radio/test/basic/main.html b/ionic/components/radio/test/basic/main.html index 0bc6ee37ba..0ec2435ad8 100644 --- a/ionic/components/radio/test/basic/main.html +++ b/ionic/components/radio/test/basic/main.html @@ -45,7 +45,7 @@
- + Currencies {{currency}} diff --git a/ionic/components/segment/segment.scss b/ionic/components/segment/segment.scss index b9e596d6e8..0db5914adf 100644 --- a/ionic/components/segment/segment.scss +++ b/ionic/components/segment/segment.scss @@ -26,4 +26,5 @@ ion-segment { text-align: center; text-overflow: ellipsis; white-space: nowrap; + cursor: pointer; } diff --git a/ionic/components/segment/segment.ts b/ionic/components/segment/segment.ts index e8514e569d..16764c152d 100644 --- a/ionic/components/segment/segment.ts +++ b/ionic/components/segment/segment.ts @@ -1,8 +1,86 @@ -import {Directive, Renderer, ElementRef, Host, Optional, EventEmitter, Output} from 'angular2/core'; +import {Directive, ElementRef, Renderer, Optional, EventEmitter, Input, Output, HostListener, ContentChildren} from 'angular2/core'; import {NgControl} from 'angular2/common'; -import {Ion} from '../ion'; -import {Config} from '../../config/config'; +import {isDefined} from '../../util/util'; + + +/** + * @name SegmentButton + * @description + * The child buttons of the `ion-segment` component. Each `ion-segment-button` must have a value. + * @property {string} [value] - the value of the segment-button. Required. + * @usage + * ```html + * + * + * Friends + * + * + * Enemies + * + * + *``` + * + * Or with `FormBuilder` + * + *```html + * + * + * + * Standard + * + * + * Hybrid + * + * + * Satellite + * + * + * + * ``` + * + * @property {Any} [click] - expression to evaluate when a segment button has been clicked + * + * @demo /docs/v2/demos/segment/ + * @see {@link /docs/v2/components#segment Segment Component Docs} + * @see {@link /docs/v2/api/components/segment/Segment/ Segment API Docs} + */ +@Directive({ + selector: 'ion-segment-button', + host: { + 'tappable': '', + 'class': 'segment-button', + 'role': 'button' + } +}) +export class SegmentButton { + @Input() value: string; + @Output() select: EventEmitter = new EventEmitter(); + + constructor(private _renderer: Renderer, private _elementRef: ElementRef) {} + + /** + * @private + * On click of a SegmentButton + */ + @HostListener('click', ['$event']) + private onClick(ev) { + console.debug('SegmentButton, select', this.value); + this.select.emit(ev, this.value); + } + + ngOnInit() { + if (!isDefined(this.value)) { + console.warn(' requires a "value" attribute'); + } + } + + public set isActive(isActive) { + this._renderer.setElementClass(this._elementRef, 'segment-activated', isActive); + this._renderer.setElementAttribute(this._elementRef, 'aria-pressed', isActive); + } + +} /** @@ -52,27 +130,20 @@ import {Config} from '../../config/config'; @Directive({ selector: 'ion-segment' }) -export class Segment extends Ion { +export class Segment { @Output() change: EventEmitter = new EventEmitter(); - - /** - * @private - * {Array} buttons The children SegmentButton's - */ - buttons: Array = []; + @ContentChildren(SegmentButton) _buttons; value: any; constructor( - @Optional() ngControl: NgControl, - elementRef: ElementRef, - config: Config + @Optional() ngControl: NgControl ) { - super(elementRef, config); - this.onChange = (_) => {}; this.onTouched = (_) => {}; - if (ngControl) ngControl.valueAccessor = this; + if (ngControl) { + ngControl.valueAccessor = this; + } } /** @@ -80,12 +151,34 @@ export class Segment extends Ion { * Write a new value to the element. */ writeValue(value) { - this.value = !value ? '' : value; - for (let button of this.buttons) { - button.isActive = (button.value === value); + this.value = isDefined(value) ? value : ''; + if (this._buttons) { + let buttons = this._buttons.toArray(); + for (let button of buttons) { + button.isActive = (button.value === this.value); + } } } + /** + * @private + */ + ngAfterViewInit() { + let buttons = this._buttons.toArray(); + for (let button of buttons) { + button.select.subscribe(() => { + this.writeValue(button.value); + this.onChange(button.value); + this.change.emit(this.value); + }); + + if (isDefined(this.value)) { + button.isActive = (button.value === this.value); + } + } + } + + /** * @private * Set the function to be called when the control receives a change event. @@ -98,117 +191,4 @@ export class Segment extends Ion { */ registerOnTouched(fn) { this.onTouched = fn; } - /** - * @private - * Called by child SegmentButtons to bind themselves to - * the Segment. - * @param {SegmentButton} segmentButton The child SegmentButton to register. - */ - register(segmentButton) { - this.buttons.push(segmentButton); - - // If this button is registered and matches our value, - // make sure to select it - if (this.value == segmentButton.value) { - this.selected(segmentButton); - } - } - - /** - * @private - * Indicate a button should be selected. - * @param {SegmentButton} segmentButton The button to select. - */ - selected(segmentButton) { - this.buttons.forEach(function(button) { - button.isActive = false; - }); - segmentButton.isActive = true; - - this.value = segmentButton.value; - this.onChange(segmentButton.value); - this.change.emit(this.value); - } -} - - -/** - * @name SegmentButton - * @description - * The child buttons of the `ion-segment` component. Each `ion-segment-button` must have a value. - * @property {string} [value] - the value of the segment-button. - * @usage - * ```html - * - * - * Friends - * - * - * Enemies - * - * - *``` - * - * Or with `FormBuilder` - * - *```html - *
- * - * - * Standard - * - * - * Hybrid - * - * - * Satellite - * - * - *
- * ``` - * - * @property {Any} [click] - expression to evaluate when a segment button has been clicked - * - * @demo /docs/v2/demos/segment/ - * @see {@link /docs/v2/components#segment Segment Component Docs} - * @see {@link /docs/v2/api/components/segment/Segment/ Segment API Docs} - */ -@Directive({ - selector: 'ion-segment-button', - inputs: [ - 'value' - ], - host: { - '(click)': 'click($event)', - '[class.segment-activated]': 'isActive' - } -}) -export class SegmentButton { - constructor( - @Host() segment: Segment, - elementRef: ElementRef, - renderer: Renderer - ) { - this.segment = segment; - - renderer.setElementClass(elementRef, 'segment-button', true); - renderer.setElementAttribute(elementRef, 'tappable', ''); - } - - /** - * @private - * Runs after the first check only - */ - ngOnInit() { - this.segment.register(this); - } - - /** - * @private - * On click of a SegmentButton - * @param {MouseEvent} event The event that happens on click. - */ - click(event) { - this.segment.selected(this, event); - } } diff --git a/ionic/components/segment/test/basic/main.html b/ionic/components/segment/test/basic/main.html index 091d8c2282..5e8442bfbc 100644 --- a/ionic/components/segment/test/basic/main.html +++ b/ionic/components/segment/test/basic/main.html @@ -16,10 +16,10 @@ - + Something - + Else diff --git a/ionic/components/slides/slides.ts b/ionic/components/slides/slides.ts index 4260ee1171..303dd117e9 100644 --- a/ionic/components/slides/slides.ts +++ b/ionic/components/slides/slides.ts @@ -66,6 +66,7 @@ import {Scroll} from '../scroll/scroll'; * @property {Number} [index] - The slide index to start on * @property [pager] - add this property to enable the slide pager * @property {Any} [change] - expression to evaluate when a slide has been changed + * @demo /docs/v2/demos/slides/ * @see {@link /docs/v2/components#slides Slides Component Docs} */ @Component({ diff --git a/ionic/components/toggle/toggle.ts b/ionic/components/toggle/toggle.ts index 900b4731ad..62f892a394 100644 --- a/ionic/components/toggle/toggle.ts +++ b/ionic/components/toggle/toggle.ts @@ -111,7 +111,7 @@ export class Toggle { form: Form, elementRef: ElementRef, config: Config, - @Optional() private ngControl: NgControl + @Optional() private _ngControl: NgControl ) { // deprecated warning if (elementRef.nativeElement.tagName == 'ION-SWITCH') { @@ -129,8 +129,8 @@ export class Toggle { this.onChange = (_) => {}; this.onTouched = (_) => {}; - if (ngControl) { - ngControl.valueAccessor = this; + if (_ngControl) { + _ngControl.valueAccessor = this; } let self = this; diff --git a/scripts/npm/README.md b/scripts/npm/README.md new file mode 100644 index 0000000000..8c0287b45b --- /dev/null +++ b/scripts/npm/README.md @@ -0,0 +1,43 @@ +## Ionic Framework Package + The ionic-framework package comes with both Javascript and Sass frontend dependencies, located in the root of the package, and a Node API, located in `tooling/`. + +### Source files: + +In the root of the package are ES5 sources in the CommonJS module format, their associated Typescript type definition files, and the Ionic Sass entry files. The Javascript sources are meant to be used by a bundler such as Webpack, SystemJS Builder, or Browserify. The type definitions provide support to Typescript tooling for things like type checking and code completion. + +Usually, the only Javascript file required by the user is `ionic.js`, as everything from Ionic can be imported from this file: + +``` + import {App, Page} from 'ionic-framework/ionic'; +``` + +### Bundles: + +Minified and unminified CommonJS and System.register module format bundles, as well as compiled CSS stylesheets for both Ionic iOS and Material Design are located `bundles/`. These can also be used with bundlers to a certain extent, for example, using Webpack's [`externals option`](https://webpack.github.io/docs/configuration.html#externals). The SystemJS bundle is primarily meant to be included in a `