Merge branch 'master' into searchbar-refactor

This commit is contained in:
Brandy Carney
2015-12-19 12:46:44 -05:00
22 changed files with 405 additions and 424 deletions

View File

@ -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. 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

View File

@ -401,7 +401,8 @@ gulp.task('package', ['src'], function(){
return gulp.src([ return gulp.src([
'scripts/npm/.npmignore', 'scripts/npm/.npmignore',
'scripts/npm/package.json', 'scripts/npm/package.json',
'README.md' 'scripts/npm/README.md',
'*tooling/**/*'
]) ])
.pipe(gulp.dest('dist')); .pipe(gulp.dest('dist'));
}); });

View File

@ -130,8 +130,8 @@ export class ActionSheet {
* the action sheet, or false to keep it opened. * the action sheet, or false to keep it opened.
* - `{function=}` `destructiveButtonClicked` Called when the destructive button is clicked. * - `{function=}` `destructiveButtonClicked` Called when the destructive button is clicked.
* Return true to close the action sheet, or false to keep it opened. * 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. * - `{String}` `enterAnimation` 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}` `leaveAnimation` The class used to animate an actionSheet that is leaving.
* @return {Promise} Promise that resolves when the action sheet is open. * @return {Promise} Promise that resolves when the action sheet is open.
*/ */
open(opts={}) { open(opts={}) {

View File

@ -42,23 +42,23 @@ import {IonicApp} from './app';
}) })
export class IdRef { 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 // Grab the component this directive is attached to
this.component = appViewManager.getComponent(elementRef); this.component = _appViewManager.getComponent(_elementRef);
} }
/** /**
* @private * @private
*/ */
ngOnInit() { ngOnInit() {
this.app.register(this.id, this.component); this._app.register(this.id, this.component);
} }
/** /**
* @private * @private
*/ */
ngOnDestroy() { ngOnDestroy() {
this.app.unregister(this.id); this._app.unregister(this.id);
} }
} }
@ -80,12 +80,12 @@ export class IdRef {
}) })
export class Attr { export class Attr {
constructor(private renderer: Renderer, private elementRef: ElementRef) {} constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
/** /**
* @private * @private
*/ */
ngOnInit() { ngOnInit() {
this.renderer.setElementAttribute(this.elementRef, this.attr, ''); this._renderer.setElementAttribute(this._elementRef, this.attr, '');
} }
} }

View File

@ -20,7 +20,7 @@ import {Directive, Renderer, ElementRef} from 'angular2/core';
selector: '[blur]' selector: '[blur]'
}) })
export class Blur { export class Blur {
constructor(private elementRef: ElementRef, private renderer: Renderer) { constructor(private _elementRef: ElementRef, private _renderer: Renderer) {
renderer.setElementStyle(elementRef, '-webkit-backdrop-filter', 'blur(10px)'); _renderer.setElementStyle(_elementRef, '-webkit-backdrop-filter', 'blur(10px)');
} }
} }

View File

@ -21,6 +21,7 @@ import {Toolbar} from '../toolbar/toolbar';
* @property [fab-center] - position a fab button towards the center * @property [fab-center] - position a fab button towards the center
* @property [fab-top] - position a fab button towards the top * @property [fab-top] - position a fab button towards the top
* @property [fab-bottom] - position a fab button towards the bottom * @property [fab-bottom] - position a fab button towards the bottom
* @property [color] - Dynamically set which color attribute this button should use.
* @description * @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. * 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/ * @demo /docs/v2/demos/buttons/
@ -28,28 +29,30 @@ import {Toolbar} from '../toolbar/toolbar';
*/ */
@Directive({ @Directive({
selector: 'button,[button]' selector: 'button,[button]',
inputs: ['color']
}) })
export class Button { export class Button {
constructor( constructor(
config: Config, config: Config,
private elementRef: ElementRef, private _elementRef: ElementRef,
private renderer: Renderer private _renderer: Renderer
) { ) {
this._role = 'button'; // bar-button/item-button this._role = 'button'; // bar-button/item-button
this._size = null; // large/small this._size = null; // large/small
this._style = 'default'; // outline/clear/solid this._style = 'default'; // outline/clear/solid
this._shape = null; // round/fab this._shape = null; // round/fab
this._display = null; // block/full this._display = null; // block/full
this._lastColor = null;
this._colors = []; // primary/secondary this._colors = []; // primary/secondary
this._icon = null; // left/right/only this._icon = null; // left/right/only
this._disabled = false; // disabled this._disabled = false; // disabled
let element = elementRef.nativeElement; let element = _elementRef.nativeElement;
if (config.get('hoverCSS') === false) { if (config.get('hoverCSS') === false) {
renderer.setElementClass(elementRef, 'disable-hover', true); _renderer.setElementClass(_elementRef, 'disable-hover', true);
} }
if (element.hasAttribute('ion-item')) { if (element.hasAttribute('ion-item')) {
@ -66,14 +69,30 @@ export class Button {
this._readIcon(element); this._readIcon(element);
} }
/** /**
* @private * @private
*/ */
ngAfterContentInit() { ngAfterContentInit() {
this._lastColor = this.color;
if (this.color) {
this._colors = [this.color];
}
this._assignCss(true); this._assignCss(true);
} }
/** /**
* @private
*/
ngAfterContentChecked() {
if (this._lastColor !== this.color) {
this._assignCss(false);
this._lastColor = this.color;
this._colors = [this.color];
this._assignCss(true);
}
}
/**
* @private * @private
*/ */
setRole(val) { setRole(val) {
@ -147,7 +166,7 @@ export class Button {
_assignCss(assignCssClass) { _assignCss(assignCssClass) {
let role = this._role; let role = this._role;
if (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._style, assignCssClass); // button-clear
this._setClass(this._shape, assignCssClass); // button-round this._setClass(this._shape, assignCssClass); // button-round
@ -164,7 +183,7 @@ export class Button {
_setClass(type, assignCssClass) { _setClass(type, assignCssClass) {
if (type) { if (type) {
this.renderer.setElementClass(this.elementRef, this._role + '-' + type, assignCssClass); this._renderer.setElementClass(this._elementRef, this._role + '-' + type, assignCssClass);
} }
} }

View File

@ -7,5 +7,15 @@ import {App, IonicApp} from 'ionic/ionic';
class E2EApp { class E2EApp {
constructor(app: IonicApp) { constructor(app: IonicApp) {
app.setTitle('Basic Buttons'); 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);
} }
} }

View File

@ -39,4 +39,9 @@
<button disabled>Disabled</button> <button disabled>Disabled</button>
</p> </p>
<p>
<button [color]="btnColor" (click)="chgColor()">Change Color</button>
<button outline [color]="btnColor" (click)="chgColor()">Change Color</button>
</p>
</ion-content> </ion-content>

View File

@ -54,12 +54,11 @@ import {Form} from '../../util/form';
export class Checkbox { export class Checkbox {
constructor( constructor(
private form: Form, private _form: Form,
@Optional() ngControl: NgControl, @Optional() ngControl: NgControl,
elementRef: ElementRef elementRef: ElementRef
) { ) {
this.form = form; _form.register(this);
form.register(this);
this.onChange = (_) => {}; this.onChange = (_) => {};
this.onTouched = (_) => {}; this.onTouched = (_) => {};
@ -75,7 +74,7 @@ export class Checkbox {
*/ */
ngOnInit() { ngOnInit() {
if (!this.id) { if (!this.id) {
this.id = 'chk-' + this.form.nextId(); this.id = 'chk-' + this._form.nextId();
} }
this.labelId = 'lbl-' + this.id; this.labelId = 'lbl-' + this.id;
@ -132,6 +131,6 @@ export class Checkbox {
* @private * @private
*/ */
ngOnDestroy() { ngOnDestroy() {
this.form.deregister(this); this._form.deregister(this);
} }
} }

View File

@ -40,9 +40,9 @@ import {Config} from '../../config/config';
export class Icon { export class Icon {
constructor( constructor(
private elementRef: ElementRef, private _elementRef: ElementRef,
config: Config, config: Config,
private renderer: Renderer private _renderer: Renderer
) { ) {
this.config = config; this.config = config;
this.mode = config.get('iconMode'); this.mode = config.get('iconMode');
@ -52,7 +52,7 @@ export class Icon {
* @private * @private
*/ */
ngOnInit() { ngOnInit() {
let ele = this.elementRef.nativeElement; let ele = this._elementRef.nativeElement;
if (this.mode == 'ios' && this.ios) { if (this.mode == 'ios' && this.ios) {
this.name = this.ios; this.name = this.ios;
@ -113,12 +113,12 @@ export class Icon {
if (this._name !== this.name) { if (this._name !== this.name) {
if (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._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('-', ' ')); this.name.replace('ion-', '').replace('ios-', '').replace('md-', '').replace('-', ' '));
} }
} }

View File

@ -34,8 +34,8 @@ import {List} from '../list/list';
}) })
export class ItemSliding { export class ItemSliding {
constructor(@Optional() private list: List, elementRef: ElementRef) { constructor(@Optional() private _list: List, elementRef: ElementRef) {
list.enableSlidingItems(true); _list.enableSlidingItems(true);
elementRef.nativeElement.$ionSlide = ++slideIds; elementRef.nativeElement.$ionSlide = ++slideIds;
} }
@ -43,7 +43,7 @@ export class ItemSliding {
* @private * @private
*/ */
close() { close() {
this.list.closeSlidingItems(); this._list.closeSlidingItems();
} }
} }

View File

@ -42,9 +42,9 @@ export class Label {
constructor( constructor(
config: Config, config: Config,
@Optional() container: TextInput, @Optional() container: TextInput,
private form: Form, private _form: Form,
private elementRef: ElementRef, private _elementRef: ElementRef,
private renderer: Renderer private _renderer: Renderer
) { ) {
this.scrollAssist = config.get('scrollAssist'); this.scrollAssist = config.get('scrollAssist');
this.container = container; this.container = container;
@ -55,7 +55,7 @@ export class Label {
*/ */
ngOnInit() { ngOnInit() {
if (!this.id) { if (!this.id) {
this.id = 'lbl-' + this.form.nextId(); this.id = 'lbl-' + this._form.nextId();
} }
this.container && this.container.registerLabel(this); this.container && this.container.registerLabel(this);
} }
@ -94,7 +94,7 @@ export class Label {
* @private * @private
*/ */
addClass(className) { addClass(className) {
this.renderer.setElementClass(this.elementRef, className, true); this._renderer.setElementClass(this._elementRef, className, true);
} }
} }

View File

@ -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 {Ion} from '../ion';
import {Config} from '../../config/config'; import {Config} from '../../config/config';
import {ListVirtualScroll} from './virtual'; import {ListVirtualScroll} from './virtual';
import {ItemSlidingGesture} from '../item/item-sliding-gesture'; 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 * 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() { ngOnInit() {
super.ngOnInit(); super.ngOnInit();
if (util.isDefined(this.virtual)) { if (isDefined(this.virtual)) {
console.log('Content', this.content); console.log('Content', this.content);
console.log('Virtual?', this.virtual); console.log('Virtual?', this.virtual);
console.log('Items?', this.items.length, 'of \'em'); console.log('Items?', this.items.length, 'of \'em');
@ -138,12 +138,21 @@ export class List extends Ion {
* @private * @private
*/ */
@Directive({ @Directive({
selector: 'ion-list-header', selector: 'ion-list-header'
inputs: [
'id'
],
host: {
'[attr.id]': 'id'
}
}) })
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);
}
}

View File

@ -37,13 +37,13 @@ import {Menu} from './menu';
}) })
export class MenuClose { export class MenuClose {
constructor(private app: IonicApp) {} constructor(private _app: IonicApp) {}
/** /**
* @private * @private
*/ */
close() { close() {
let menu = Menu.getById(this.app, this.menuClose); let menu = Menu.getById(this._app, this.menuClose);
menu && menu.close(); menu && menu.close();
} }

View File

@ -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 {NgControl} from 'angular2/common';
import {Config} from '../../config/config';
import {Ion} from '../ion';
import {ListHeader} from '../list/list'; import {ListHeader} from '../list/list';
import {Form} from '../../util/form'; import {Form} from '../../util/form';
import {isDefined} from '../../util/util';
/**
* @description
* A radio button with a unique value. Note that all `<ion-radio>` components
* must be wrapped within a `<ion-list radio-group>`, and there must be at
* least two `<ion-radio>` 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
* <ion-radio value="my-value" checked="true">
* Radio Label
* </ion-radio>
* ```
* @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:
'<div class="item-inner">' +
'<ion-item-content id="{{labelId}}">' +
'<ng-content></ng-content>' +
'</ion-item-content>' +
'<div class="radio-media">' +
'<div class="radio-icon"></div>' +
'</div>' +
'</div>'
})
export class RadioButton {
@Input() value: string = '';
@Input() checked: boolean = false;
@Input() disabled: boolean = false;
@Input() id: string;
@Output() select: EventEmitter<any> = 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 * @usage
* ```html * ```html
* <ion-list radio-group ngControl="clientside"> * <ion-list radio-group ngControl="autoManufacturers">
* *
* <ion-list-header> * <ion-list-header>
* Clientside * Auto Manufacturers
* </ion-list-header> * </ion-list-header>
* *
* <ion-radio value="ember"> * <ion-radio value="cord">
* Ember * Cord
* </ion-radio> * </ion-radio>
* *
* <ion-radio value="angular1"> * <ion-radio value="duesenberg" checked="true">
* Angular 1 * Duesenberg
* </ion-radio> * </ion-radio>
* *
* <ion-radio value="angular2" checked="true"> * <ion-radio value="hudson">
* Angular 2 * Hudson
* </ion-radio> * </ion-radio>
* *
* <ion-radio value="react"> * <ion-radio value="packard">
* React * Packard
* </ion-radio>
*
* <ion-radio value="studebaker">
* Studebaker
* </ion-radio>
*
* <ion-radio value="tucker">
* Tucker
* </ion-radio> * </ion-radio>
* *
* </ion-list> * </ion-list>
@ -48,77 +130,24 @@ import {Form} from '../../util/form';
@Directive({ @Directive({
selector: '[radio-group]', selector: '[radio-group]',
host: { host: {
'role': 'radiogroup',
'[attr.aria-activedescendant]': 'activeId', '[attr.aria-activedescendant]': 'activeId',
'[attr.aria-describedby]': 'describedById', 'role': 'radiogroup'
} }
}) })
export class RadioGroup extends Ion { export class RadioGroup {
radios: Array<RadioButton> = []; @Output() change: EventEmitter<any> = new EventEmitter();
@ContentChildren(RadioButton) private _buttons;
@ContentChild(ListHeader) private _header;
constructor( constructor(@Optional() ngControl: NgControl, private _renderer: Renderer, private _elementRef: ElementRef) {
elementRef: ElementRef,
config: Config,
@Optional() ngControl: NgControl,
@Query(ListHeader) private headerQuery: QueryList<ListHeader>
) {
super(elementRef, config);
this.ngControl = ngControl; this.ngControl = ngControl;
this.id = ++radioGroupIds; this.id = ++radioGroupIds;
this.radioIds = -1;
this.onChange = (_) => {}; this.onChange = (_) => {};
this.onTouched = (_) => {}; this.onTouched = (_) => {};
if (ngControl) this.ngControl.valueAccessor = this; 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;
}
}
/**
* @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);
} }
/** /**
@ -128,9 +157,46 @@ export class RadioGroup extends Ion {
* https://github.com/angular/angular/blob/master/modules/angular2/src/forms/directives/shared.ts#L34 * https://github.com/angular/angular/blob/master/modules/angular2/src/forms/directives/shared.ts#L34
*/ */
writeValue(value) { writeValue(value) {
this.value = value; this.value = isDefined(value) ? value : '';
for (let radio of this.radios) { if (this._buttons) {
radio.checked = (radio.value == value); 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; } 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
* <ion-radio value="isChecked" checked="true">
* Radio Label
* </ion-radio>
* ```
* @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:
'<div class="item-inner">' +
'<ion-item-content id="{{labelId}}">' +
'<ng-content></ng-content>' +
'</ion-item-content>' +
'<div class="radio-media">' +
'<div class="radio-icon"></div>' +
'</div>' +
'</div>'
})
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('<ion-radio> must be within a <ion-list radio-group>');
}
}
/**
* @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; let radioGroupIds = -1;

View File

@ -45,7 +45,7 @@
<form (submit)="doSubmit($event)" [ngFormModel]="currencyForm"> <form (submit)="doSubmit($event)" [ngFormModel]="currencyForm">
<ion-list radio-group ngControl="currenciesControl"> <ion-list radio-group ngControl="currenciesControl">
<ion-list-header> <ion-list-header id="currencies">
Currencies Currencies
</ion-list-header> </ion-list-header>
<ion-radio *ngFor="#currency of currencies" [checked]="currency==selectedCurrency" [value]="currency">{{currency}}</ion-radio> <ion-radio *ngFor="#currency of currencies" [checked]="currency==selectedCurrency" [value]="currency">{{currency}}</ion-radio>

View File

@ -26,4 +26,5 @@ ion-segment {
text-align: center; text-align: center;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
cursor: pointer;
} }

View File

@ -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 {NgControl} from 'angular2/common';
import {Ion} from '../ion'; import {isDefined} from '../../util/util';
import {Config} from '../../config/config';
/**
* @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
* <ion-segment [(ngModel)]="relationship" primary>
* <ion-segment-button value="friends" (select)="selectedFriends()">
* Friends
* </ion-segment-button>
* <ion-segment-button value="enemies" (select)="selectedEnemies()">
* Enemies
* </ion-segment-button>
* </ion-segment>
*```
*
* Or with `FormBuilder`
*
*```html
* <form [ngFormModel]="myForm">
* <ion-segment ngControl="mapStyle" danger>
* <ion-segment-button value="standard">
* Standard
* </ion-segment-button>
* <ion-segment-button value="hybrid">
* Hybrid
* </ion-segment-button>
* <ion-segment-button value="sat">
* Satellite
* </ion-segment-button>
* </ion-segment>
* </form>
* ```
*
* @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<any> = 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('<ion-segment-button> 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({ @Directive({
selector: 'ion-segment' selector: 'ion-segment'
}) })
export class Segment extends Ion { export class Segment {
@Output() change: EventEmitter<any> = new EventEmitter(); @Output() change: EventEmitter<any> = new EventEmitter();
@ContentChildren(SegmentButton) _buttons;
/**
* @private
* {Array<SegmentButton>} buttons The children SegmentButton's
*/
buttons: Array<SegmentButton> = [];
value: any; value: any;
constructor( constructor(
@Optional() ngControl: NgControl, @Optional() ngControl: NgControl
elementRef: ElementRef,
config: Config
) { ) {
super(elementRef, config);
this.onChange = (_) => {}; this.onChange = (_) => {};
this.onTouched = (_) => {}; this.onTouched = (_) => {};
if (ngControl) ngControl.valueAccessor = this; if (ngControl) {
ngControl.valueAccessor = this;
}
} }
/** /**
@ -80,11 +151,33 @@ export class Segment extends Ion {
* Write a new value to the element. * Write a new value to the element.
*/ */
writeValue(value) { writeValue(value) {
this.value = !value ? '' : value; this.value = isDefined(value) ? value : '';
for (let button of this.buttons) { if (this._buttons) {
button.isActive = (button.value === value); 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 * @private
@ -98,117 +191,4 @@ export class Segment extends Ion {
*/ */
registerOnTouched(fn) { this.onTouched = fn; } 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
* <ion-segment [(ngModel)]="relationship" primary>
* <ion-segment-button value="friends" (click)="clickedFriends()">
* Friends
* </ion-segment-button>
* <ion-segment-button value="enemies" (click)="clickedEnemies()">
* Enemies
* </ion-segment-button>
* </ion-segment>
*```
*
* Or with `FormBuilder`
*
*```html
* <form [ngFormModel]="myForm">
* <ion-segment ngControl="mapStyle" danger>
* <ion-segment-button value="standard">
* Standard
* </ion-segment-button>
* <ion-segment-button value="hybrid">
* Hybrid
* </ion-segment-button>
* <ion-segment-button value="sat">
* Satellite
* </ion-segment-button>
* </ion-segment>
* </form>
* ```
*
* @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);
}
} }

View File

@ -16,10 +16,10 @@
</button> </button>
</ion-buttons> </ion-buttons>
<ion-segment secondary> <ion-segment secondary>
<ion-segment-button> <ion-segment-button value="something">
Something Something
</ion-segment-button> </ion-segment-button>
<ion-segment-button> <ion-segment-button value="else">
Else Else
</ion-segment-button> </ion-segment-button>
</ion-segment> </ion-segment>

View File

@ -66,6 +66,7 @@ import {Scroll} from '../scroll/scroll';
* @property {Number} [index] - The slide index to start on * @property {Number} [index] - The slide index to start on
* @property [pager] - add this property to enable the slide pager * @property [pager] - add this property to enable the slide pager
* @property {Any} [change] - expression to evaluate when a slide has been changed * @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} * @see {@link /docs/v2/components#slides Slides Component Docs}
*/ */
@Component({ @Component({

View File

@ -111,7 +111,7 @@ export class Toggle {
form: Form, form: Form,
elementRef: ElementRef, elementRef: ElementRef,
config: Config, config: Config,
@Optional() private ngControl: NgControl @Optional() private _ngControl: NgControl
) { ) {
// deprecated warning // deprecated warning
if (elementRef.nativeElement.tagName == 'ION-SWITCH') { if (elementRef.nativeElement.tagName == 'ION-SWITCH') {
@ -129,8 +129,8 @@ export class Toggle {
this.onChange = (_) => {}; this.onChange = (_) => {};
this.onTouched = (_) => {}; this.onTouched = (_) => {};
if (ngControl) { if (_ngControl) {
ngControl.valueAccessor = this; _ngControl.valueAccessor = this;
} }
let self = this; let self = this;

43
scripts/npm/README.md Normal file
View File

@ -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 `<script>` tag for demos, tests and Javascript playgrounds like [Plunker](http://plnkr.co/).
---------
### Tooling
At the moment, the ionic-framework module exports a single 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