refactor(components): add color/mode properties

This commit is contained in:
Brandy Carney
2016-09-13 17:11:33 -05:00
committed by Adam Bradley
parent 52ada1ca6d
commit bc7d328bc0
25 changed files with 1174 additions and 1350 deletions

View File

@@ -1,6 +1,7 @@
import { Directive, ElementRef, Input, Renderer } from '@angular/core';
import { Config } from '../../config/config';
import { Ion } from '../ion';
/**
@@ -13,43 +14,28 @@ import { Config } from '../../config/config';
@Directive({
selector: 'ion-badge'
})
export class Badge {
/** @internal */
_color: string;
export class Badge extends Ion {
/**
* @input {string} The predefined color to use. For example: `"primary"`, `"secondary"`, `"danger"`.
*/
@Input()
get color(): string {
return this._color;
}
set color(value: string) {
this._updateColor(value);
}
constructor(
config: Config,
private _elementRef: ElementRef,
private _renderer: Renderer
) { }
/**
* @internal
*/
_updateColor(newColor: string) {
this._setElementColor(this._color, false);
this._setElementColor(newColor, true);
this._color = newColor;
set color(val: string) {
this._setColor('badge', val);
}
/**
* @internal
* @input {string} The mode to apply to this component.
*/
_setElementColor(color: string, isAdd: boolean) {
if (color !== null && color !== '') {
this._renderer.setElementClass(this._elementRef.nativeElement, `badge-${color}`, isAdd);
}
@Input()
set mode(val: string) {
this._setMode('badge', val);
}
constructor(config: Config, elementRef: ElementRef, renderer: Renderer) {
super(config, elementRef, renderer);
this.mode = config.get('mode');
}
}

View File

@@ -1,6 +1,7 @@
import { Attribute, ChangeDetectionStrategy, Component, ElementRef, Input, Renderer, ViewEncapsulation } from '@angular/core';
import { Config } from '../../config/config';
import { Ion } from '../ion';
import { isTrueProperty } from '../../util/util';
@@ -90,37 +91,34 @@ import { isTrueProperty } from '../../util/util';
*/
@Component({
selector: '[ion-button]',
// NOTE: template must not contain spaces between elements
template: '<span class="button-inner"><ng-content></ng-content></span><ion-button-effect></ion-button-effect>',
template:
'<span class="button-inner">' +
'<ng-content></ng-content>' +
'</span>' +
'<div class="button-effect"></div>',
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
})
export class Button {
/** @internal */
export class Button extends Ion {
/** @private */
_role: string = 'button'; // bar-button
/** @internal */
_mt: boolean = false; // menutoggle
/** @private */
_mt: boolean; // menutoggle
/** @internal */
_size: string = null; // large/small/default
/** @private */
_size: string; // large/small/default
/** @internal */
/** @private */
_style: string = 'default'; // outline/clear/solid
/** @internal */
_shape: string = null; // round/fab
/** @private */
_shape: string; // round/fab
/** @internal */
_display: string = null; // block/full
/** @private */
_display: string; // block/full
/** @internal */
_color: string = null; // primary/secondary
/** @internal */
_disabled: boolean = false; // disabled
/** @internal */
/** @private */
_init: boolean;
/**
@@ -203,9 +201,20 @@ export class Button {
this._attr('_display', 'full', val);
}
/**
* @input {string} A button that fills its parent container without a border-radius or borders on the left/right.
*/
@Input()
set mode(val: string) {
this._assignCss(false);
this._mode = val;
this._assignCss(true);
}
/** @private */
_attr(type: string, attrName: string, attrValue: boolean) {
if (type === '_style') {
this._setColor(this._color, isTrueProperty(attrValue));
this._updateColor(this._color, isTrueProperty(attrValue));
}
this._setClass((<any>this)[type], false);
if (isTrueProperty(attrValue)) {
@@ -224,24 +233,23 @@ export class Button {
*/
@Input()
set color(val: string) {
this._updateColor(val);
this._updateColor(this._color, false);
this._updateColor(val, true);
this._color = val;
}
constructor(
@Attribute('menuToggle') menuToggle: string,
@Attribute('ion-button') ionButton: string,
config: Config,
private _elementRef: ElementRef,
private _renderer: Renderer
elementRef: ElementRef,
renderer: Renderer
) {
let element = _elementRef.nativeElement;
super(config, elementRef, renderer);
this._mode = config.get('mode');
if (config.get('hoverCSS') === false) {
_renderer.setElementClass(element, 'disable-hover', true);
}
if (element.hasAttribute('disabled')) {
this._disabled = true;
this.setElementClass('disable-hover', true);
}
if (ionButton.trim().length > 0) {
@@ -255,30 +263,11 @@ export class Button {
}
}
/**
* @private
*/
ngAfterContentInit() {
this._init = true;
this._assignCss(true);
}
/**
* @internal
*/
_updateColor(newColor: string) {
this._setColor(this._color, false);
this._setColor(newColor, true);
this._color = newColor;
}
/**
* @private
*/
addClass(className: string) {
this._renderer.setElementClass(this._elementRef.nativeElement, className, true);
}
/**
* @private
*/
@@ -289,36 +278,38 @@ export class Button {
}
/**
* @internal
* @private
*/
_assignCss(assignCssClass: boolean) {
let role = this._role;
if (role) {
this._renderer.setElementClass(this._elementRef.nativeElement, role, assignCssClass); // button
this.setElementClass(role, assignCssClass); // button
this.setElementClass(`${role}-${this._mode}`, assignCssClass); // button
this._setClass('menutoggle', this._mt); // menutoggle
this._setClass(this._style, assignCssClass); // button-clear
this._setClass(this._shape, assignCssClass); // button-round
this._setClass(this._display, assignCssClass); // button-full
this._setClass(this._size, assignCssClass); // button-small
this._setColor(this._color, assignCssClass); // button-secondary, bar-button-secondary
this._updateColor(this._color, assignCssClass); // button-secondary, bar-button-secondary
}
}
/**
* @internal
* @private
*/
_setClass(type: string, assignCssClass: boolean) {
if (type && this._init) {
this._renderer.setElementClass(this._elementRef.nativeElement, this._role + '-' + type.toLowerCase(), assignCssClass);
type = type.toLocaleLowerCase();
this.setElementClass(`${this._role}-${type}`, assignCssClass);
this.setElementClass(`${this._role}-${type}-${this._mode}`, assignCssClass);
}
}
/**
* @internal
* @private
*/
_setColor(color: string, isAdd: boolean) {
_updateColor(color: string, isAdd: boolean) {
if (color && this._init) {
// The class should begin with the button role
// button, bar-button
@@ -331,7 +322,7 @@ export class Button {
className += (style !== null && style !== '' && style !== 'default' ? '-' + style.toLowerCase() : '');
if (color !== null && color !== '') {
this._renderer.setElementClass(this._elementRef.nativeElement, `${className}-${color}`, isAdd);
this.setElementClass(`${className}-${this._mode}-${color}`, isAdd);
}
}
}

View File

@@ -1,13 +1,17 @@
import { AfterContentInit, Component, ElementRef, EventEmitter, forwardRef, HostListener, Input, OnDestroy, Optional, Output, Provider, Renderer, ViewEncapsulation } from '@angular/core';
import { AfterContentInit, Component, ElementRef, EventEmitter, forwardRef, HostListener, Input, OnDestroy, Optional, Output, Renderer, ViewEncapsulation } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Config } from '../../config/config';
import { Form } from '../../util/form';
import { Ion } from '../ion';
import { isTrueProperty } from '../../util/util';
import { Item } from '../item/item';
export const CHECKBOX_VALUE_ACCESSOR = new Provider(
NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => Checkbox), multi: true});
export const CHECKBOX_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => Checkbox),
multi: true
};
/**
* @name Checkbox
@@ -49,50 +53,53 @@ export const CHECKBOX_VALUE_ACCESSOR = new Provider(
*/
@Component({
selector: 'ion-checkbox',
template: `
<div class="checkbox-icon" [class.checkbox-checked]="_checked">
<div class="checkbox-inner"></div>
</div>
<button ion-button="item-cover"
role="checkbox"
type="button"
[id]="id"
[attr.aria-checked]="_checked"
[attr.aria-labelledby]="_labelId"
[attr.aria-disabled]="_disabled">
</button>
`,
template:
'<div class="checkbox-icon" [class.checkbox-checked]="_checked">' +
'<div class="checkbox-inner"></div>' +
'</div>' +
'<button role="checkbox" ' +
'type="button" ' +
'ion-button="item-cover" ' +
'[id]="id" ' +
'[attr.aria-checked]="_checked" ' +
'[attr.aria-labelledby]="_labelId" ' +
'[attr.aria-disabled]="_disabled" ' +
'class="item-cover"> ' +
'</button>',
host: {
'[class.checkbox-disabled]': '_disabled'
},
providers: [CHECKBOX_VALUE_ACCESSOR],
encapsulation: ViewEncapsulation.None,
})
export class Checkbox implements AfterContentInit, ControlValueAccessor, OnDestroy {
private _checked: boolean = false;
private _init: boolean;
private _disabled: boolean = false;
private _labelId: string;
private _fn: Function;
/** internal */
private _color: string;
/**
* @private
*/
export class Checkbox extends Ion implements AfterContentInit, ControlValueAccessor, OnDestroy {
/** @private */
_checked: boolean = false;
/** @private */
_init: boolean;
/** @private */
_disabled: boolean = false;
/** @private */
_labelId: string;
/** @private */
_fn: Function;
/** @private */
id: string;
/**
* @input {string} The predefined color to use. For example: `"primary"`, `"secondary"`, `"danger"`.
*/
@Input()
get color(): string {
return this._color;
set color(val: string) {
this._setColor('checkbox', val);
}
set color(value: string) {
this._updateColor(value);
/**
* @input {string} The mode to apply to this component.
*/
@Input()
set mode(val: string) {
this._setMode('checkbox', val);
}
/**
@@ -101,35 +108,22 @@ export class Checkbox implements AfterContentInit, ControlValueAccessor, OnDestr
@Output() ionChange: EventEmitter<Checkbox> = new EventEmitter<Checkbox>();
constructor(
config: Config,
private _form: Form,
@Optional() private _item: Item,
private _elementRef: ElementRef,
private _renderer: Renderer
elementRef: ElementRef,
renderer: Renderer
) {
super(config, elementRef, renderer);
this.mode = config.get('mode');
_form.register(this);
if (_item) {
this.id = 'chk-' + _item.registerInput('checkbox');
this._labelId = 'lbl-' + _item.id;
this._item.setCssClass('item-checkbox', true);
}
}
/**
* @internal
*/
_updateColor(newColor: string) {
this._setElementColor(this._color, false);
this._setElementColor(newColor, true);
this._color = newColor;
}
/**
* @internal
*/
_setElementColor(color: string, isAdd: boolean) {
if (color !== null && color !== '') {
this._renderer.setElementClass(this._elementRef.nativeElement, `checkbox-${color}`, isAdd);
this._item.setElementClass('item-checkbox', true);
}
}
@@ -137,7 +131,7 @@ export class Checkbox implements AfterContentInit, ControlValueAccessor, OnDestr
* @private
*/
@HostListener('click', ['$event'])
private _click(ev: UIEvent) {
_click(ev: UIEvent) {
console.debug('checkbox, checked');
ev.preventDefault();
ev.stopPropagation();
@@ -160,13 +154,13 @@ export class Checkbox implements AfterContentInit, ControlValueAccessor, OnDestr
/**
* @private
*/
private _setChecked(isChecked: boolean) {
if (isChecked !== this._checked) {
_setChecked(isChecked: boolean) {
if (!this._disabled && isChecked !== this._checked) {
this._checked = isChecked;
if (this._init) {
this.ionChange.emit(this);
}
this._item && this._item.setCssClass('item-checkbox-checked', isChecked);
this._item && this._item.setElementClass('item-checkbox-checked', isChecked);
}
}
@@ -205,7 +199,7 @@ export class Checkbox implements AfterContentInit, ControlValueAccessor, OnDestr
set disabled(val: boolean) {
this._disabled = isTrueProperty(val);
this._item && this._item.setCssClass('item-checkbox-disabled', this._disabled);
this._item && this._item.setElementClass('item-checkbox-disabled', this._disabled);
}
/**

View File

@@ -1,5 +1,8 @@
import { Directive, ElementRef, Input, Renderer } from '@angular/core';
import { Config } from '../../config/config';
import { Ion } from '../ion';
/**
* @name Chip
* @module ionic
@@ -90,43 +93,28 @@ import { Directive, ElementRef, Input, Renderer } from '@angular/core';
@Directive({
selector: 'ion-chip'
})
export class Chip {
/** @internal */
_color: string;
export class Chip extends Ion {
/**
* @input {string} The predefined color to use. For example: `"primary"`, `"secondary"`, `"danger"`.
*/
@Input()
get color(): string {
return this._color;
}
set color(value: string) {
this._updateColor(value);
}
constructor(
private _elementRef: ElementRef,
private _renderer: Renderer
) { }
/**
* @internal
*/
_updateColor(newColor: string) {
this._setElementColor(this._color, false);
this._setElementColor(newColor, true);
this._color = newColor;
set color(val: string) {
this._setColor('chip', val);
}
/**
* @internal
* @input {string} The mode to apply to this component.
*/
_setElementColor(color: string, isAdd: boolean) {
if (color !== null && color !== '') {
this._renderer.setElementClass(this._elementRef.nativeElement, `chip-${color}`, isAdd);
}
@Input()
set mode(val: string) {
this._setMode('chip', val);
}
constructor(config: Config, elementRef: ElementRef, renderer: Renderer) {
super(config, elementRef, renderer);
this.mode = config.get('mode');
}
}

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, ElementRef, Input, NgZone, Optional, ViewEncapsulation } from '@angular/core';
import { ChangeDetectionStrategy, Component, ElementRef, Input, NgZone, Optional, Renderer, ViewEncapsulation } from '@angular/core';
import { App } from '../app/app';
import { Ion } from '../ion';
@@ -7,7 +7,7 @@ import { Keyboard } from '../../util/keyboard';
import { nativeRaf, nativeTimeout, transitionEnd} from '../../util/dom';
import { ScrollView } from '../../util/scroll-view';
import { Tabs } from '../tabs/tabs';
import { ViewController } from '../nav/view-controller';
import { ViewController } from '../../navigation/view-controller';
import { isTrueProperty } from '../../util/util';
@@ -103,34 +103,38 @@ import { isTrueProperty } from '../../util/util';
@Component({
selector: 'ion-content',
template:
'<scroll-content>' +
'<div class="scroll-content">' +
'<ng-content></ng-content>' +
'</scroll-content>' +
'</div>' +
'<ng-content select="ion-fixed"></ng-content>' +
'<ng-content select="ion-refresher"></ng-content>',
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
host: {
'[class.statusbar-padding]': '_sbPadding'
}
},
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None
})
export class Content extends Ion {
private _paddingTop: number;
private _paddingRight: number;
private _paddingBottom: number;
private _paddingLeft: number;
private _scrollPadding: number;
private _headerHeight: number;
private _footerHeight: number;
private _tabbarHeight: number;
private _tabsPlacement: string;
private _inputPolling: boolean = false;
private _scroll: ScrollView;
private _scLsn: Function;
private _sbPadding: boolean;
private _fullscreen: boolean;
private _scrollEle: HTMLElement;
private _footerEle: HTMLElement;
_paddingTop: number;
_paddingRight: number;
_paddingBottom: number;
_paddingLeft: number;
_scrollPadding: number;
_headerHeight: number;
_footerHeight: number;
_tabbarHeight: number;
_tabsPlacement: string;
_inputPolling: boolean = false;
_scroll: ScrollView;
_scLsn: Function;
_sbPadding: boolean;
_fullscreen: boolean;
_footerEle: HTMLElement;
/*
* @private
*/
_scrollEle: HTMLElement;
/**
* A number representing how many pixels the top of the content has been
@@ -145,20 +149,25 @@ export class Content extends Ion {
contentBottom: number;
constructor(
private _elementRef: ElementRef,
config: Config,
private _app: App,
private _keyboard: Keyboard,
private _zone: NgZone,
elementRef: ElementRef,
renderer: Renderer,
public _app: App,
public _keyboard: Keyboard,
public _zone: NgZone,
@Optional() viewCtrl: ViewController,
@Optional() private _tabs: Tabs
@Optional() public _tabs: Tabs
) {
super(_elementRef);
super(config, elementRef, renderer);
this._mode = config.get('mode');
this._setMode('content', this._mode);
this._sbPadding = config.getBoolean('statusbarPadding', false);
if (viewCtrl) {
viewCtrl.setContent(this);
viewCtrl.setContentRef(_elementRef);
viewCtrl._setContent(this);
viewCtrl._setContentRef(elementRef);
}
}
@@ -232,7 +241,7 @@ export class Content extends Ion {
return this._addListener('mousemove', handler);
}
private _addListener(type: string, handler: any): Function {
_addListener(type: string, handler: any): Function {
if (!this._scrollEle) { return; }
// ensure we're not creating duplicates
@@ -350,14 +359,6 @@ export class Content extends Ion {
return this._scroll.jsScroll(onScrollCallback);
}
/**
* @private
* DOM WRITE
*/
addCssClass(className: string) {
this.getNativeElement().classList.add(className);
}
/**
* @input {boolean} By default, content is positioned between the headers
* and footers. However, using `fullscreen="true"`, the content will be
@@ -374,20 +375,12 @@ export class Content extends Ion {
this._fullscreen = isTrueProperty(val);
}
/**
* @private
* DOM WRITE
*/
removeCssClass(className: string) {
this.getNativeElement().classList.remove(className);
}
/**
* @private
* DOM WRITE
*/
setScrollElementStyle(prop: string, val: any) {
this._scrollEle.style[prop] = val;
(<any>this._scrollEle.style)[prop] = val;
}
/**

View File

@@ -1,17 +1,20 @@
import { AfterContentInit, Component, EventEmitter, forwardRef, HostListener, Input, OnDestroy, Optional, Output, Provider, ViewEncapsulation } from '@angular/core';
import { AfterContentInit, Component, ElementRef, EventEmitter, forwardRef, HostListener, Input, OnDestroy, Optional, Output, Renderer, ViewEncapsulation } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Config } from '../../config/config';
import { Picker, PickerController } from '../picker/picker';
import { PickerColumn, PickerColumnOption } from '../picker/picker-options';
import { Form } from '../../util/form';
import { Ion } from '../ion';
import { Item } from '../item/item';
import { merge, isBlank, isPresent, isTrueProperty, isArray, isString } from '../../util/util';
import { dateValueRange, renderDateTime, renderTextFormat, convertFormatToKey, getValueFromFormat, parseTemplate, parseDate, updateDate, DateTimeData, convertDataToISO, daysInMonth, dateSortValue, dateDataSortValue, LocaleData } from '../../util/datetime-util';
export const DATETIME_VALUE_ACCESSOR = new Provider(
NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => DateTime), multi: true});
export const DATETIME_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DateTime),
multi: true
};
/**
* @name DateTime
@@ -247,32 +250,32 @@ export const DATETIME_VALUE_ACCESSOR = new Provider(
*/
@Component({
selector: 'ion-datetime',
template: `
<div class="datetime-text">{{_text}}</div>
<button ion-button="item-cover"
aria-haspopup="true"
type="button"
[id]="id"
[attr.aria-labelledby]="_labelId"
[attr.aria-disabled]="_disabled">
</button>
`,
template:
'<div class="datetime-text">{{_text}}</div>' +
'<button aria-haspopup="true" ' +
'type="button" ' +
'[id]="id" ' +
'ion-button="item-cover" ' +
'[attr.aria-labelledby]="_labelId" ' +
'[attr.aria-disabled]="_disabled" ' +
'class="item-cover">' +
'</button>',
host: {
'[class.datetime-disabled]': '_disabled'
},
providers: [DATETIME_VALUE_ACCESSOR],
encapsulation: ViewEncapsulation.None,
})
export class DateTime implements AfterContentInit, ControlValueAccessor, OnDestroy {
private _disabled: any = false;
private _labelId: string;
private _text: string = '';
private _fn: Function;
private _isOpen: boolean = false;
private _min: DateTimeData;
private _max: DateTimeData;
private _value: DateTimeData = {};
private _locale: LocaleData = {};
export class DateTime extends Ion implements AfterContentInit, ControlValueAccessor, OnDestroy {
_disabled: any = false;
_labelId: string;
_text: string = '';
_fn: Function;
_isOpen: boolean = false;
_min: DateTimeData;
_max: DateTimeData;
_value: DateTimeData = {};
_locale: LocaleData = {};
/**
* @private
@@ -404,6 +407,14 @@ export class DateTime implements AfterContentInit, ControlValueAccessor, OnDestr
*/
@Input() pickerOptions: any = {};
/**
* @input {string} The mode to apply to this component.
*/
@Input()
set mode(val: string) {
this._setMode('datetime', val);
}
/**
* @output {any} Any expression to evaluate when the datetime selection has changed.
*/
@@ -416,20 +427,26 @@ export class DateTime implements AfterContentInit, ControlValueAccessor, OnDestr
constructor(
private _form: Form,
private _config: Config,
config: Config,
elementRef: ElementRef,
renderer: Renderer,
@Optional() private _item: Item,
@Optional() private _pickerCtrl: PickerController
) {
this._form.register(this);
super(config, elementRef, renderer);
this.mode = config.get('mode');
_form.register(this);
if (_item) {
this.id = 'dt-' + _item.registerInput('datetime');
this._labelId = 'lbl-' + _item.id;
this._item.setCssClass('item-datetime', true);
this._item.setElementClass('item-datetime', true);
}
}
@HostListener('click', ['$event'])
private _click(ev: UIEvent) {
_click(ev: UIEvent) {
if (ev.detail === 0) {
// do not continue if the click event came from a form submit
return;
@@ -440,7 +457,7 @@ export class DateTime implements AfterContentInit, ControlValueAccessor, OnDestr
}
@HostListener('keyup.space')
private _keyup() {
_keyup() {
if (!this._isOpen) {
this.open();
}
@@ -524,9 +541,9 @@ export class DateTime implements AfterContentInit, ControlValueAccessor, OnDestr
let values: any[];
// first see if they have exact values to use for this input
if (isPresent(this[key + 'Values'])) {
if (isPresent((<any>this)[key + 'Values'])) {
// user provide exact values for this date part
values = convertToArrayOfNumbers(this[key + 'Values'], key);
values = convertToArrayOfNumbers((<any>this)[key + 'Values'], key);
} else {
// use the default date part values
@@ -699,7 +716,7 @@ export class DateTime implements AfterContentInit, ControlValueAccessor, OnDestr
*/
checkHasValue(inputValue: any) {
if (this._item) {
this._item.setCssClass('input-has-value', !!(inputValue && inputValue !== ''));
this._item.setElementClass('input-has-value', !!(inputValue && inputValue !== ''));
}
}
@@ -761,7 +778,7 @@ export class DateTime implements AfterContentInit, ControlValueAccessor, OnDestr
set disabled(val) {
this._disabled = isTrueProperty(val);
this._item && this._item.setCssClass('item-datetime-disabled', this._disabled);
this._item && this._item.setElementClass('item-datetime-disabled', this._disabled);
}
/**

View File

@@ -1,6 +1,7 @@
import { Directive, ElementRef, Renderer, Input } from '@angular/core';
import { Directive, ElementRef, HostBinding, Input, Renderer } from '@angular/core';
import { Config } from '../../config/config';
import { Ion } from '../ion';
/**
@@ -40,15 +41,19 @@ import { Config } from '../../config/config';
'role': 'img'
}
})
export class Icon {
private _isActive: any;
private _name: string = '';
private _ios: string = '';
private _md: string = '';
private _css: string = '';
/** @internal */
_color: string;
export class Icon extends Ion {
/** @private */
_iconMode: string;
/** @private */
_isActive: any;
/** @private */
_name: string = '';
/** @private */
_ios: string = '';
/** @private */
_md: string = '';
/** @private */
_css: string = '';
/**
* @input {string} The predefined color to use. For example: `"primary"`, `"secondary"`, `"danger"`.
@@ -57,22 +62,27 @@ export class Icon {
get color(): string {
return this._color;
}
set color(value: string) {
this._updateColor(value);
this._setColor('icon', value);
}
/**
* @private
* @input {string} The mode to apply to this component.
*/
mode: string;
@Input()
set mode(val: string) {
this._setMode('icon', val);
}
constructor(
config: Config,
private _elementRef: ElementRef,
private _renderer: Renderer
elementRef: ElementRef,
renderer: Renderer
) {
this.mode = config.get('iconMode');
super(config, elementRef, renderer);
this.mode = config.get('mode');
this._iconMode = config.get('iconMode');
}
/**
@@ -80,7 +90,7 @@ export class Icon {
*/
ngOnDestroy() {
if (this._css) {
this._renderer.setElementClass(this._elementRef.nativeElement, this._css, false);
this.setElementClass(this._css, false);
}
}
@@ -96,7 +106,7 @@ export class Icon {
if (!(/^md-|^ios-|^logo-/.test(val))) {
// this does not have one of the defaults
// so lets auto add in the mode prefix for them
val = this.mode + '-' + val;
val = this._iconMode + '-' + val;
}
this._name = val;
this.update();
@@ -142,62 +152,43 @@ export class Icon {
this.update();
}
/**
* @private
*/
@HostBinding('class.hide') _hidden: boolean = false;
/**
* @private
*/
update() {
let css = 'ion-';
if (this._ios && this.mode === 'ios') {
this._hidden = (this._name === null);
if (this._ios && this._iconMode === 'ios') {
css += this._ios;
} else if (this._md && this.mode === 'md') {
} else if (this._md && this._iconMode === 'md') {
css += this._md;
} else {
css += this._name;
}
if (this.mode === 'ios' && !this.isActive && css.indexOf('logo') < 0) {
if (this._iconMode === 'ios' && !this.isActive && css.indexOf('logo') < 0) {
css += '-outline';
}
if (this._css !== css) {
if (this._css) {
this._renderer.setElementClass(this._elementRef.nativeElement, this._css, false);
this.setElementClass(this._css, false);
}
this._css = css;
this._renderer.setElementClass(this._elementRef.nativeElement, css, true);
this.setElementClass(css, true);
this._renderer.setElementAttribute(this._elementRef.nativeElement, 'aria-label',
this.setElementAttribute('aria-label',
css.replace('ion-', '').replace('ios-', '').replace('md-', '').replace('-', ' '));
}
}
/**
* @private
* @param {string} add class name
*/
addClass(className: string) {
this._renderer.setElementClass(this._elementRef.nativeElement, className, true);
}
/**
* @internal
*/
_updateColor(newColor: string) {
this._setElementColor(this._color, false);
this._setElementColor(newColor, true);
this._color = newColor;
}
/**
* @internal
*/
_setElementColor(color: string, isAdd: boolean) {
if (color !== null && color !== '') {
this._renderer.setElementClass(this._elementRef.nativeElement, `icon-${color}`, isAdd);
}
}
}

View File

@@ -83,7 +83,7 @@
</ion-list>
<p>
<button ion-button (click)="updateIcon()">
<button ion-button icon-left (click)="updateIcon()">
<ion-icon [name]="btnIcon"></ion-icon>
Update icon
</button>

View File

@@ -1,54 +1,53 @@
import { ElementRef, EventEmitter, Input, Output, ViewChild } from '@angular/core';
import { ElementRef, Renderer } from '@angular/core';
import { NgControl } from '@angular/forms';
import { App } from '../app/app';
import { closest, copyInputAttributes, Coordinates, hasPointerMoved, pointerCoord } from '../../util/dom';
import { copyInputAttributes, PointerCoordinates, hasPointerMoved, pointerCoord } from '../../util/dom';
import { Config } from '../../config/config';
import { Content } from '../content/content';
import { Form } from '../../util/form';
import { Ion } from '../ion';
import { isTrueProperty } from '../../util/util';
import { Item } from '../item/item';
import { NativeInput, NextInput } from './native-input';
import { NavController } from '../nav/nav-controller';
import { NavControllerBase } from '../nav/nav-controller-base';
import { NavController } from '../../navigation/nav-controller';
import { NavControllerBase } from '../../navigation/nav-controller-base';
import { Platform } from '../../platform/platform';
export class InputBase {
protected _coord: Coordinates;
protected _deregScroll: Function;
protected _disabled: boolean = false;
protected _keyboardHeight: number;
protected _scrollMove: EventListener;
protected _type: string = 'text';
protected _useAssist: boolean;
protected _usePadding: boolean;
protected _value: any = '';
protected _isTouch: boolean;
protected _autoFocusAssist: string;
protected _autoComplete: string;
protected _autoCorrect: string;
protected _nav: NavControllerBase;
export class InputBase extends Ion {
_coord: PointerCoordinates;
_deregScroll: Function;
_disabled: boolean = false;
_keyboardHeight: number;
_scrollMove: EventListener;
_type: string = 'text';
_useAssist: boolean;
_usePadding: boolean;
_value: any = '';
_isTouch: boolean;
_autoFocusAssist: string;
_autoComplete: string;
_autoCorrect: string;
_nav: NavControllerBase;
_native: NativeInput;
inputControl: NgControl;
@Input() clearInput: any;
@Input() placeholder: string = '';
@ViewChild(NativeInput) protected _native: NativeInput;
@Output() blur: EventEmitter<Event> = new EventEmitter<Event>();
@Output() focus: EventEmitter<Event> = new EventEmitter<Event>();
constructor(
config: Config,
protected _form: Form,
protected _item: Item,
protected _app: App,
protected _platform: Platform,
protected _elementRef: ElementRef,
elementRef: ElementRef,
renderer: Renderer,
protected _scrollView: Content,
nav: NavController,
ngControl: NgControl
) {
super(config, elementRef, renderer);
this._nav = <NavControllerBase>nav;
this._useAssist = config.getBoolean('scrollAssist', false);
this._usePadding = config.getBoolean('scrollPadding', this._useAssist);
@@ -66,49 +65,27 @@ export class InputBase {
_form.register(this);
}
ngOnInit() {
if (this._item) {
this._item.setCssClass('item-input', true);
this._item.registerInput(this._type);
}
scrollMove(ev: UIEvent) {
// scroll move event listener this instance can reuse
if (!(this._nav && this._nav.isTransitioning())) {
this.deregScrollMove();
let clearInput = this.clearInput;
if (typeof clearInput === 'string') {
this.clearInput = (clearInput === '' || clearInput === 'true');
}
}
if (this.hasFocus()) {
this._native.hideFocus(true);
ngAfterContentInit() {
let self = this;
this._scrollView.onScrollEnd(() => {
this._native.hideFocus(false);
self._scrollMove = function(ev: UIEvent) {
// scroll move event listener this instance can reuse
if (!(self._nav && self._nav.isTransitioning())) {
self.deregScrollMove();
if (self.hasFocus()) {
self._native.hideFocus(true);
self._scrollView.onScrollEnd(function() {
self._native.hideFocus(false);
if (self.hasFocus()) {
// if it still has focus then keep listening
self.regScrollMove();
}
});
}
if (this.hasFocus()) {
// if it still has focus then keep listening
this.regScrollMove();
}
});
}
};
}
};
this.setItemInputControlCss();
}
ngAfterContentChecked() {
this.setItemInputControlCss();
}
private setItemInputControlCss() {
setItemInputControlCss() {
let item = this._item;
let nativeInput = this._native;
let inputControl = this.inputControl;
@@ -124,35 +101,21 @@ export class InputBase {
}
}
private setControlCss(element: any, control: any) {
element.setCssClass('ng-untouched', control.untouched);
element.setCssClass('ng-touched', control.touched);
element.setCssClass('ng-pristine', control.pristine);
element.setCssClass('ng-dirty', control.dirty);
element.setCssClass('ng-valid', control.valid);
element.setCssClass('ng-invalid', !control.valid);
setControlCss(element: any, control: NgControl) {
element.setElementClass('ng-untouched', control.untouched);
element.setElementClass('ng-touched', control.touched);
element.setElementClass('ng-pristine', control.pristine);
element.setElementClass('ng-dirty', control.dirty);
element.setElementClass('ng-valid', control.valid);
element.setElementClass('ng-invalid', !control.valid);
}
ngOnDestroy() {
this._form.deregister(this);
}
@Input()
get value() {
return this._value;
}
set value(val: any) {
setValue(val: any) {
this._value = val;
this.checkHasValue(val);
}
@Input()
get type() {
return this._type;
}
set type(val) {
setType(val: string) {
this._type = 'text';
if (val) {
@@ -164,22 +127,16 @@ export class InputBase {
}
}
@Input()
get disabled() {
return this._disabled;
}
set disabled(val) {
setDisabled(val: boolean) {
this._disabled = isTrueProperty(val);
this._item && this._item.setCssClass('item-input-disabled', this._disabled);
this._item && this._item.setElementClass('item-input-disabled', this._disabled);
this._native && this._native.isDisabled(this._disabled);
}
/**
* @private
*/
@ViewChild(NativeInput)
private set _nativeInput(nativeInput: NativeInput) {
setNativeInput(nativeInput: NativeInput) {
this._native = nativeInput;
if (this._item && this._item.labelId !== null) {
@@ -200,7 +157,7 @@ export class InputBase {
});
this.checkHasValue(nativeInput.getValue());
this.disabled = this._disabled;
this.setDisabled(this._disabled);
var ionInputEle: HTMLElement = this._elementRef.nativeElement;
let nativeInputEle: HTMLElement = nativeInput.element();
@@ -245,8 +202,7 @@ export class InputBase {
/**
* @private
*/
@ViewChild(NextInput)
private set _nextInput(nextInput: NextInput) {
setNextInput(nextInput: NextInput) {
if (nextInput) {
nextInput.focused.subscribe(() => {
this._form.tabFocus(this);
@@ -290,7 +246,7 @@ export class InputBase {
*/
checkHasValue(inputValue: any) {
if (this._item) {
this._item.setCssClass('input-has-value', !!(inputValue && inputValue !== ''));
this._item.setElementClass('input-has-value', !!(inputValue && inputValue !== ''));
}
}
@@ -299,14 +255,14 @@ export class InputBase {
*/
focusChange(inputHasFocus: boolean) {
if (this._item) {
this._item.setCssClass('input-has-focus', inputHasFocus);
this._item.setElementClass('input-has-focus', inputHasFocus);
}
if (!inputHasFocus) {
this.deregScrollMove();
}
}
private pointerStart(ev: any) {
pointerStart(ev: any) {
// input cover touchstart
console.debug('scroll assist pointerStart', ev.type);
@@ -320,7 +276,7 @@ export class InputBase {
}
}
private pointerEnd(ev: any) {
pointerEnd(ev: any) {
// input cover touchend/mouseup
console.debug('scroll assist pointerEnd', ev.type);
@@ -362,8 +318,8 @@ export class InputBase {
// find out if text input should be manually scrolled into view
// get container of this input, probably an ion-item a few nodes up
var ele = this._elementRef.nativeElement;
ele = closest(ele, 'ion-item,[ion-item]') || ele;
var ele: HTMLElement = this._elementRef.nativeElement;
ele = <HTMLElement>ele.closest('ion-item,[ion-item]') || ele;
var scrollData = InputBase.getScrollData(ele.offsetTop, ele.offsetHeight, scrollView.getContentDimensions(), this._keyboardHeight, this._platform.height());
if (scrollData.scrollAmount > -3 && scrollData.scrollAmount < 3) {
@@ -419,7 +375,7 @@ export class InputBase {
/**
* @private
*/
private setFocus() {
setFocus() {
// immediately set focus
this._form.setAsFocused(this);
@@ -450,12 +406,12 @@ export class InputBase {
/**
* @private
*/
private regScrollMove() {
regScrollMove() {
// register scroll move listener
if (this._useAssist && this._scrollView) {
setTimeout(() => {
this.deregScrollMove();
this._deregScroll = this._scrollView.addScrollListener(this._scrollMove);
this._deregScroll = this._scrollView.addScrollListener(this.scrollMove.bind(this));
}, 80);
}
}
@@ -463,7 +419,7 @@ export class InputBase {
/**
* @private
*/
private deregScrollMove() {
deregScrollMove() {
// deregister the scroll move listener
this._deregScroll && this._deregScroll();
}

View File

@@ -1,16 +1,15 @@
import { Component, Optional, ElementRef, ViewChild, ViewEncapsulation } from '@angular/core';
import { NgIf } from '@angular/common';
import { NgControl, NgModel } from '@angular/forms';
import { Component, Optional, ElementRef, EventEmitter, Input, Output, Renderer, ViewChild, ViewEncapsulation } from '@angular/core';
import { NgControl } from '@angular/forms';
import { App } from '../app/app';
import { Config } from '../../config/config';
import { Content } from '../content/content';
import { Form } from '../../util/form';
import { InputBase } from './input-base';
import { isTrueProperty } from '../../util/util';
import { Item } from '../item/item';
import { Label } from '../label/label';
import { NativeInput, NextInput } from './native-input';
import { NavController } from '../nav/nav-controller';
import { NavController } from '../../navigation/nav-controller';
import { Platform } from '../../platform/platform';
@@ -75,16 +74,15 @@ import { Platform } from '../../platform/platform';
*/
@Component({
selector: 'ion-input',
template: `
<input [type]="type" [(ngModel)]="_value" (blur)="inputBlurred($event)" (focus)="inputFocused($event)" [placeholder]="placeholder" class="text-input">
<input [type]="type" aria-hidden="true" next-input *ngIf="_useAssist">
<button ion-button clear [hidden]="!clearInput" type="button" class="text-input-clear-icon" (click)="clearTextInput()" (mousedown)="clearTextInput()"></button>
<div (touchstart)="pointerStart($event)" (touchend)="pointerEnd($event)" (mousedown)="pointerStart($event)" (mouseup)="pointerEnd($event)" class="input-cover" tappable *ngIf="_useAssist"></div>
`,
directives: [NativeInput, NextInput, NgIf, NgModel],
template:
'<input [type]="type" [(ngModel)]="_value" (blur)="inputBlurred($event)" (focus)="inputFocused($event)" [placeholder]="placeholder" class="text-input" [ngClass]="\'text-input-\' + _mode">' +
'<input [type]="type" aria-hidden="true" next-input *ngIf="_useAssist">' +
'<button ion-button clear [hidden]="!clearInput" type="button" class="text-input-clear-icon" (click)="clearTextInput()" (mousedown)="clearTextInput()"></button>' +
'<div (touchstart)="pointerStart($event)" (touchend)="pointerEnd($event)" (mousedown)="pointerStart($event)" (mouseup)="pointerEnd($event)" class="input-cover" tappable *ngIf="_useAssist"></div>',
encapsulation: ViewEncapsulation.None,
})
export class TextInput extends InputBase {
constructor(
config: Config,
form: Form,
@@ -92,13 +90,104 @@ export class TextInput extends InputBase {
app: App,
platform: Platform,
elementRef: ElementRef,
renderer: Renderer,
@Optional() scrollView: Content,
@Optional() nav: NavController,
@Optional() ngControl: NgControl
) {
super(config, form, item, app, platform, elementRef, scrollView, nav, ngControl);
super(config, form, item, app, platform, elementRef, renderer, scrollView, nav, ngControl);
this.mode = config.get('mode');
}
/**
* @private
*/
_clearInput: boolean = false;
/**
* @private
*/
@Input() placeholder: string = '';
/**
* @private
*/
@Input()
get clearInput() {
return this._clearInput;
}
set clearInput(val: any) {
this._clearInput = isTrueProperty(val);
}
/**
* @private
*/
@Input()
get value() {
return this._value;
}
set value(val: any) {
super.setValue(val);
}
/**
* @private
*/
@Input()
get type() {
return this._type;
}
set type(val: any) {
super.setType(val);
}
/**
* @private
*/
@Input()
get disabled() {
return this._disabled;
}
set disabled(val: any) {
super.setDisabled(val);
}
/**
* @input {string} The mode to apply to this component.
*/
@Input()
set mode(val: string) {
this._setMode('input', val);
}
/**
* @private
*/
@ViewChild(NativeInput)
set _nativeInput(nativeInput: NativeInput) {
super.setNativeInput(nativeInput);
}
/**
* @private
*/
@ViewChild(NextInput)
set _nextInput(nextInput: NextInput) {
super.setNextInput(nextInput);
}
/**
* @private
*/
@Output() blur: EventEmitter<Event> = new EventEmitter<Event>();
/**
* @private
*/
@Output() focus: EventEmitter<Event> = new EventEmitter<Event>();
/**
* @private
*/
@@ -112,6 +201,29 @@ export class TextInput extends InputBase {
inputFocused(ev: UIEvent) {
this.focus.emit(ev);
}
/**
* @private
*/
ngOnInit() {
if (this._item) {
this._item.setElementClass('item-input', true);
this._item.registerInput(this._type);
}
}
/**
* @private
*/
ngAfterContentChecked() {
this.setItemInputControlCss();
}
/**
* @private
*/
ngOnDestroy() {
this._form.deregister(this);
}
/**
* @private
@@ -171,10 +283,9 @@ export class TextInput extends InputBase {
@Component({
selector: 'ion-textarea',
template:
'<textarea [(ngModel)]="_value" (blur)="inputBlurred($event)" (focus)="inputFocused($event)" [placeholder]="placeholder" class="text-input"></textarea>' +
'<textarea [(ngModel)]="_value" (blur)="inputBlurred($event)" (focus)="inputFocused($event)" [placeholder]="placeholder" class="text-input" [ngClass]="\'text-input-\' + _mode"></textarea>' +
'<input type="text" aria-hidden="true" next-input *ngIf="_useAssist">' +
'<div (touchstart)="pointerStart($event)" (touchend)="pointerEnd($event)" (mousedown)="pointerStart($event)" (mouseup)="pointerEnd($event)" class="input-cover" tappable *ngIf="_useAssist"></div>',
directives: [NativeInput, NextInput, NgIf],
encapsulation: ViewEncapsulation.None,
})
export class TextArea extends InputBase {
@@ -185,23 +296,113 @@ export class TextArea extends InputBase {
app: App,
platform: Platform,
elementRef: ElementRef,
renderer: Renderer,
@Optional() scrollView: Content,
@Optional() nav: NavController,
@Optional() ngControl: NgControl
) {
super(config, form, item, app, platform, elementRef, scrollView, nav, ngControl);
super(config, form, item, app, platform, elementRef, renderer, scrollView, nav, ngControl);
this.mode = config.get('mode');
}
/**
* @private
*/
@Input() placeholder: string = '';
/**
* @private
*/
@Input()
get value() {
return this._value;
}
set value(val: any) {
super.setValue(val);
}
/**
* @private
*/
@Input()
get type() {
return this._type;
}
set type(val: any) {
super.setType(val);
}
/**
* @private
*/
@Input()
get disabled() {
return this._disabled;
}
set disabled(val: any) {
super.setDisabled(val);
}
/**
* @input {string} The mode to apply to this component.
*/
@Input()
set mode(val: string) {
this._setMode('input', val);
}
/**
* @private
*/
@ViewChild(NativeInput)
set _nativeInput(nativeInput: NativeInput) {
super.setNativeInput(nativeInput);
}
/**
* @private
*/
@ViewChild(NextInput)
set _nextInput(nextInput: NextInput) {
super.setNextInput(nextInput);
}
/**
* @private
*/
@Output() blur: EventEmitter<Event> = new EventEmitter<Event>();
/**
* @private
*/
@Output() focus: EventEmitter<Event> = new EventEmitter<Event>();
/**
* @private
*/
ngOnInit() {
super.ngOnInit();
if (this._item) {
this._item.setCssClass('item-textarea', true);
this._item.setElementClass('item-textarea', true);
this._item.setElementClass('item-input', true);
this._item.registerInput(this._type);
}
}
/**
* @private
*/
ngAfterContentChecked() {
this.setItemInputControlCss();
}
/**
* @private
*/
ngOnDestroy() {
this._form.deregister(this);
}
/**
* @private
*/

View File

@@ -12,17 +12,17 @@ import { CSS, hasFocus } from '../../util/dom';
selector: '.text-input'
})
export class NativeInput {
private _relocated: boolean;
private _clone: boolean;
private _blurring: boolean;
private _unrefBlur: Function;
_relocated: boolean;
_clone: boolean;
_blurring: boolean;
_unrefBlur: Function;
@Output() focusChange: EventEmitter<boolean> = new EventEmitter<boolean>();
@Output() valueChange: EventEmitter<string> = new EventEmitter<string>();
constructor(
private _elementRef: ElementRef,
private _renderer: Renderer,
public _elementRef: ElementRef,
public _renderer: Renderer,
config: Config,
public ngControl: NgControl
) {
@@ -31,12 +31,12 @@ export class NativeInput {
}
@HostListener('input', ['$event'])
private _change(ev: any) {
_change(ev: any) {
this.valueChange.emit(ev.target.value);
}
@HostListener('focus')
private _focus() {
_focus() {
var self = this;
self.focusChange.emit(true);
@@ -65,7 +65,7 @@ export class NativeInput {
}
@HostListener('blur')
private _blur() {
_blur() {
this.focusChange.emit(false);
this.hideFocus(false);
@@ -111,7 +111,7 @@ export class NativeInput {
// move the native input to a location safe to receive focus
// according to the browser, the native input receives focus in an
// area which doesn't require the browser to scroll the input into place
focusedInputEle.style[CSS.transform] = `translate3d(-9999px,${inputRelativeY}px,0)`;
(<any>focusedInputEle.style)[CSS.transform] = `translate3d(-9999px,${inputRelativeY}px,0)`;
focusedInputEle.style.opacity = '0';
}
@@ -129,7 +129,7 @@ export class NativeInput {
if (this._clone) {
// should remove the cloned node
focusedInputEle.classList.remove('cloned-active');
focusedInputEle.style[CSS.transform] = '';
(<any>focusedInputEle.style)[CSS.transform] = '';
focusedInputEle.style.opacity = '';
removeClone(focusedInputEle, 'cloned-focus');
}
@@ -164,7 +164,7 @@ export class NativeInput {
return this.element().value;
}
setCssClass(cssClass: string, shouldAdd: boolean) {
setElementClass(cssClass: string, shouldAdd: boolean) {
this._renderer.setElementClass(this._elementRef.nativeElement, cssClass, shouldAdd);
}

View File

@@ -1,10 +1,10 @@
import { ChangeDetectionStrategy, Component, ContentChild, ContentChildren, Directive, ElementRef, forwardRef, Input, Renderer, ViewChild, ViewEncapsulation } from '@angular/core';
import { NgIf } from '@angular/common';
import { ChangeDetectionStrategy, Component, ContentChild, ContentChildren, Directive, ElementRef, Input, QueryList, Renderer, ViewChild, ViewEncapsulation } from '@angular/core';
import { Button } from '../button/button';
import { Config } from '../../config/config';
import { Form } from '../../util/form';
import { Icon } from '../icon/icon';
import { Reorder } from '../item/item-reorder';
import { Ion } from '../ion';
import { Label } from '../label/label';
@@ -272,30 +272,31 @@ import { Label } from '../label/label';
*/
@Component({
selector: 'ion-list-header,ion-item,[ion-item],ion-item-divider',
template: `
<ng-content select="[item-left],ion-checkbox:not([item-right])"></ng-content>
<div class="item-inner">
<div class="input-wrapper">
<ng-content select="ion-label"></ng-content>
<ion-label *ngIf="_viewLabel">
<ng-content></ng-content>
</ion-label>
<ng-content select="ion-select,ion-input,ion-textarea,ion-datetime,ion-range,[item-content]"></ng-content>
</div>
<ng-content select="[item-right],ion-radio,ion-toggle"></ng-content>
<ion-reorder></ion-reorder>
</div>
<ion-button-effect></ion-button-effect>
`,
directives: [NgIf, Label, forwardRef(() => Reorder)],
template:
'<ng-content select="[item-left],ion-checkbox:not([item-right])"></ng-content>' +
'<div class="item-inner">' +
'<div class="input-wrapper">' +
'<ng-content select="ion-label"></ng-content>' +
'<ion-label *ngIf="_viewLabel">' +
'<ng-content></ng-content>' +
'</ion-label>' +
'<ng-content select="ion-select,ion-input,ion-textarea,ion-datetime,ion-range,[item-content]"></ng-content>' +
'</div>' +
'<ng-content select="[item-right],ion-radio,ion-toggle"></ng-content>' +
'<ion-reorder></ion-reorder>' +
'</div>' +
'<div class="button-effect"></div>',
host: {
'class': 'item'
},
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
})
export class Item {
private _ids: number = -1;
private _inputs: Array<string> = [];
private _label: Label;
private _viewLabel: boolean = true;
export class Item extends Ion {
_ids: number = -1;
_inputs: Array<string> = [];
_label: Label;
_viewLabel: boolean = true;
/**
* @private
@@ -307,22 +308,26 @@ export class Item {
*/
labelId: string = null;
/** @internal */
_color: string;
/**
* @input {string} The predefined color to use. For example: `"primary"`, `"secondary"`, `"danger"`.
*/
@Input()
get color(): string {
return this._color;
set color(val: string) {
this._updateColor(val);
}
set color(value: string) {
this._updateColor(value);
/**
* @input {string} The mode to apply to this component.
*/
@Input()
set mode(val: string) {
this._setMode('item', val);
}
constructor(form: Form, private _renderer: Renderer, private _elementRef: ElementRef) {
constructor(form: Form, config: Config, elementRef: ElementRef, renderer: Renderer) {
super(config, elementRef, renderer);
this.mode = config.get('mode');
this.id = form.nextId().toString();
}
@@ -344,10 +349,15 @@ export class Item {
}
if (this._inputs.length > 1) {
this.setCssClass('item-multiple-inputs', true);
this.setElementClass('item-multiple-inputs', true);
}
}
_updateColor(newColor: string, colorClass?: string) {
colorClass = colorClass || 'item'; // item-radio
this._setColor(colorClass, newColor);
}
/**
* @private
*/
@@ -359,12 +369,12 @@ export class Item {
* @private
*/
@ContentChild(Label)
private set contentLabel(label: Label) {
set contentLabel(label: Label) {
if (label) {
this._label = label;
this.labelId = label.id = ('lbl-' + this.id);
if (label.type) {
this.setCssClass('item-label-' + label.type, true);
this.setElementClass('item-label-' + label.type, true);
}
this._viewLabel = false;
}
@@ -374,7 +384,7 @@ export class Item {
* @private
*/
@ViewChild(Label)
private set viewLabel(label: Label) {
set viewLabel(label: Label) {
if (!this._label) {
this._label = label;
}
@@ -384,12 +394,10 @@ export class Item {
* @private
*/
@ContentChildren(Button)
private set _buttons(buttons: any) {
buttons.toArray().forEach((button: any) => {
// Don't add the item-button class if the user specifies
// a different size button
if (!button.isItem && !button._size) {
button.addClass('item-button');
set _buttons(buttons: QueryList<Button>) {
buttons.forEach(button => {
if (!button._size) {
button.setElementClass('item-button', true);
}
});
}
@@ -398,52 +406,12 @@ export class Item {
* @private
*/
@ContentChildren(Icon)
private set _icons(icons: any) {
icons.toArray().forEach((icon: any) => {
icon.addClass('item-icon');
set _icons(icons: QueryList<Icon>) {
icons.forEach(icon => {
icon.setElementClass('item-icon', true);
});
}
/**
* @private
*/
setCssClass(cssClass: string, shouldAdd: boolean) {
this._renderer.setElementClass(this._elementRef.nativeElement, cssClass, shouldAdd);
}
/**
* @private
*/
setCssStyle(property: string, value: string) {
this._renderer.setElementStyle(this._elementRef.nativeElement, property, value);
}
/**
* @internal
*/
_updateColor(newColor: string, colorClass?: string) {
this._setElementColor(this._color, false, colorClass);
this._setElementColor(newColor, true, colorClass);
this._color = newColor;
}
/**
* @internal
*/
_setElementColor(color: string, isAdd: boolean, colorClass?: string) {
colorClass = colorClass || 'item'; // item-radio
if (color !== null && color !== '') {
this._renderer.setElementClass(this._elementRef.nativeElement, `${colorClass}-${color}`, isAdd);
}
}
/**
* @private
*/
getNativeElement(): HTMLElement {
return this._elementRef.nativeElement;
}
}
/**
@@ -452,9 +420,16 @@ export class Item {
@Directive({
selector: 'ion-item,[ion-item]',
host: {
'class': 'item'
'class': 'item-block'
}
})
export class ItemContent {
export class ItemContent {}
}
/**
* @private
*/
@Directive({
selector: 'ion-item-group'
})
export class ItemGroup {}

View File

@@ -1,5 +1,8 @@
import { Attribute, Directive, ElementRef, Renderer, Input } from '@angular/core';
import { Config } from '../../config/config';
import { Ion } from '../ion';
/**
* @name Label
@@ -54,22 +57,23 @@ import { Attribute, Directive, ElementRef, Renderer, Input } from '@angular/core
@Directive({
selector: 'ion-label'
})
export class Label {
export class Label extends Ion {
private _id: string;
/** @internal */
_color: string;
/**
* @input {string} The predefined color to use. For example: `"primary"`, `"secondary"`, `"danger"`.
*/
@Input()
get color(): string {
return this._color;
set color(val: string) {
this._setColor('label', val);
}
set color(value: string) {
this._updateColor(value);
/**
* @input {string} The mode to apply to this component.
*/
@Input()
set mode(val: string) {
this._setMode('label', val);
}
/**
@@ -78,13 +82,17 @@ export class Label {
type: string;
constructor(
private _elementRef: ElementRef,
private _renderer: Renderer,
config: Config,
elementRef: ElementRef,
renderer: Renderer,
@Attribute('floating') isFloating: string,
@Attribute('stacked') isStacked: string,
@Attribute('fixed') isFixed: string,
@Attribute('inset') isInset: string
) {
super(config, elementRef, renderer);
this.mode = config.get('mode');
this.type = (isFloating === '' ? 'floating' : (isStacked === '' ? 'stacked' : (isFixed === '' ? 'fixed' : (isInset === '' ? 'inset' : null))));
}
@@ -99,7 +107,7 @@ export class Label {
set id(val: string) {
this._id = val;
if (val) {
this._renderer.setElementAttribute(this._elementRef.nativeElement, 'id', val);
this.setElementAttribute('id', val);
}
}
@@ -107,33 +115,7 @@ export class Label {
* @private
*/
get text(): string {
return this._elementRef.nativeElement.textContent || '';
}
/**
* @private
* @param {string} add class name
*/
addClass(className: string) {
this._renderer.setElementClass(this._elementRef.nativeElement, className, true);
}
/**
* @internal
*/
_updateColor(newColor: string) {
this._setElementColor(this._color, false);
this._setElementColor(newColor, true);
this._color = newColor;
}
/**
* @internal
*/
_setElementColor(color: string, isAdd: boolean) {
if (color !== null && color !== '') {
this._renderer.setElementClass(this._elementRef.nativeElement, `label-${color}`, isAdd);
}
return this.getNativeElement().textContent || '';
}
}

View File

@@ -1,14 +1,11 @@
import { Component, ComponentResolver, HostListener, Renderer, ViewChild, ViewContainerRef } from '@angular/core';
import { Component, ComponentFactoryResolver, HostListener, Renderer, ViewChild, ViewContainerRef } from '@angular/core';
import { addSelector } from '../../config/bootstrap';
import { Animation } from '../../animations/animation';
import { Backdrop } from '../backdrop/backdrop';
import { Key } from '../../util/key';
import { NavParams } from '../nav/nav-params';
import { NavParams } from '../../navigation/nav-params';
import { pascalCaseToDashCase } from '../../util/util';
import { PageTransition } from '../../transitions/page-transition';
import { TransitionOptions } from '../../transitions/transition';
import { ViewController } from '../nav/view-controller';
import { ViewController } from '../../navigation/view-controller';
import { windowDimensions } from '../../util/dom';
@@ -17,60 +14,61 @@ import { windowDimensions } from '../../util/dom';
*/
@Component({
selector: 'ion-modal',
template: `
<ion-backdrop disableScroll="false" (click)="bdClick($event)"></ion-backdrop>
<div class="modal-wrapper">
<div #viewport nav-viewport></div>
</div>
`,
directives: [Backdrop]
template:
'<ion-backdrop disableScroll="false" (click)="_bdClick()"></ion-backdrop>' +
'<div class="modal-wrapper">' +
'<div #viewport nav-viewport></div>' +
'</div>'
})
export class ModalCmp {
@ViewChild('viewport', {read: ViewContainerRef}) viewport: ViewContainerRef;
@ViewChild('viewport', { read: ViewContainerRef }) _viewport: ViewContainerRef;
private d: any;
private enabled: boolean;
/** @private */
_bdDismiss: boolean;
constructor(private _compiler: ComponentResolver, private _renderer: Renderer, private _navParams: NavParams, private _viewCtrl: ViewController) {
this.d = _navParams.data.opts;
}
/** @private */
_enabled: boolean;
loadComponent(done: Function) {
let componentType = this._navParams.data.componentType;
addSelector(componentType, 'ion-page');
this._compiler.resolveComponent(componentType).then((componentFactory) => {
let componentRef = this.viewport.createComponent(componentFactory, this.viewport.length, this.viewport.parentInjector);
this._renderer.setElementClass(componentRef.location.nativeElement, 'show-page', true);
// auto-add page css className created from component JS class name
let cssClassName = pascalCaseToDashCase(componentType.name);
this._renderer.setElementClass(componentRef.location.nativeElement, cssClassName, true);
this._viewCtrl.setInstance(componentRef.instance);
this.enabled = true;
done();
});
constructor(public _cfr: ComponentFactoryResolver, public _renderer: Renderer, public _navParams: NavParams, public _viewCtrl: ViewController) {
this._bdDismiss = _navParams.data.opts.enableBackdropDismiss;
}
ngAfterViewInit() {
// intentionally kept empty
this._load(this._navParams.data.component);
}
dismiss(role: any): Promise<any> {
return this._viewCtrl.dismiss(null, role);
/** @private */
_load(component: any) {
if (component) {
const componentFactory = this._cfr.resolveComponentFactory(component);
// ******** DOM WRITE ****************
const componentRef = this._viewport.createComponent(componentFactory, this._viewport.length, this._viewport.parentInjector, []);
this._viewCtrl._setInstance(componentRef.instance);
this._setCssClass(componentRef, 'ion-page');
this._setCssClass(componentRef, 'show-page');
this._setCssClass(componentRef, pascalCaseToDashCase(component.name));
this._enabled = true;
}
}
bdClick() {
if (this.enabled && this.d.enableBackdropDismiss) {
this.dismiss('backdrop');
/** @private */
_setCssClass(componentRef: any, className: string) {
this._renderer.setElementClass(componentRef.location.nativeElement, className, true);
}
_bdClick() {
if (this._enabled && this._bdDismiss) {
return this._viewCtrl.dismiss(null, 'backdrop');
}
}
@HostListener('body:keyup', ['$event'])
private _keyUp(ev: KeyboardEvent) {
if (this.enabled && this._viewCtrl.isLast() && ev.keyCode === Key.ESCAPE ) {
this.bdClick();
_keyUp(ev: KeyboardEvent) {
if (this._enabled && this._viewCtrl.isLast() && ev.keyCode === Key.ESCAPE) {
this._bdClick();
}
}
}
@@ -79,43 +77,33 @@ export class ModalCmp {
* Animations for modals
*/
class ModalSlideIn extends PageTransition {
constructor(enteringView: ViewController, leavingView: ViewController, opts: TransitionOptions) {
super(enteringView, leavingView, opts);
let ele = enteringView.pageRef().nativeElement;
let backdropEle = ele.querySelector('ion-backdrop');
let backdrop = new Animation(backdropEle);
let wrapper = new Animation(ele.querySelector('.modal-wrapper'));
init() {
super.init();
const ele: HTMLElement = this.enteringView.pageRef().nativeElement;
const backdropEle = ele.querySelector('ion-backdrop');
const backdrop = new Animation(backdropEle);
const wrapper = new Animation(ele.querySelector('.modal-wrapper'));
backdrop.fromTo('opacity', 0.01, 0.4);
wrapper.fromTo('translateY', '100%', '0%');
this
.element(enteringView.pageRef())
.element(this.enteringView.pageRef())
.easing('cubic-bezier(0.36,0.66,0.04,1)')
.duration(400)
.add(backdrop)
.add(wrapper);
if (enteringView.hasNavbar()) {
// entering page has a navbar
let enteringNavBar = new Animation(enteringView.navbarRef());
enteringNavBar.before.addClass('show-navbar');
this.add(enteringNavBar);
}
}
}
PageTransition.register('modal-slide-in', ModalSlideIn);
class ModalSlideOut extends PageTransition {
constructor(enteringView: ViewController, leavingView: ViewController, opts: TransitionOptions) {
super(enteringView, leavingView, opts);
let ele = leavingView.pageRef().nativeElement;
init() {
super.init();
const ele: HTMLElement = this.leavingView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
let wrapperEle = <HTMLElement> ele.querySelector('.modal-wrapper');
let wrapperEle = <HTMLElement>ele.querySelector('.modal-wrapper');
let wrapperEleRect = wrapperEle.getBoundingClientRect();
let wrapper = new Animation(wrapperEle);
@@ -126,7 +114,7 @@ class ModalSlideOut extends PageTransition {
backdrop.fromTo('opacity', 0.4, 0.0);
this
.element(leavingView.pageRef())
.element(this.leavingView.pageRef())
.easing('ease-out')
.duration(250)
.add(backdrop)
@@ -137,12 +125,11 @@ PageTransition.register('modal-slide-out', ModalSlideOut);
class ModalMDSlideIn extends PageTransition {
constructor(enteringView: ViewController, leavingView: ViewController, opts: TransitionOptions) {
super(enteringView, leavingView, opts);
let ele = enteringView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
let wrapper = new Animation(ele.querySelector('.modal-wrapper'));
init() {
super.init();
const ele: HTMLElement = this.enteringView.pageRef().nativeElement;
const backdrop = new Animation(ele.querySelector('ion-backdrop'));
const wrapper = new Animation(ele.querySelector('.modal-wrapper'));
backdrop.fromTo('opacity', 0.01, 0.4);
wrapper.fromTo('translateY', '40px', '0px');
@@ -150,35 +137,27 @@ class ModalMDSlideIn extends PageTransition {
const DURATION = 280;
const EASING = 'cubic-bezier(0.36,0.66,0.04,1)';
this.element(enteringView.pageRef()).easing(EASING).duration(DURATION)
this.element(this.enteringView.pageRef()).easing(EASING).duration(DURATION)
.add(backdrop)
.add(wrapper);
if (enteringView.hasNavbar()) {
// entering page has a navbar
let enteringNavBar = new Animation(enteringView.navbarRef());
enteringNavBar.before.addClass('show-navbar');
this.add(enteringNavBar);
}
}
}
PageTransition.register('modal-md-slide-in', ModalMDSlideIn);
class ModalMDSlideOut extends PageTransition {
constructor(enteringView: ViewController, leavingView: ViewController, opts: TransitionOptions) {
super(enteringView, leavingView, opts);
let ele = leavingView.pageRef().nativeElement;
let backdrop = new Animation(ele.querySelector('ion-backdrop'));
let wrapper = new Animation(ele.querySelector('.modal-wrapper'));
init() {
super.init();
const ele: HTMLElement = this.leavingView.pageRef().nativeElement;
const backdrop = new Animation(ele.querySelector('ion-backdrop'));
const wrapper = new Animation(ele.querySelector('.modal-wrapper'));
backdrop.fromTo('opacity', 0.4, 0.0);
wrapper.fromTo('translateY', '0px', '40px');
wrapper.fromTo('opacity', 0.99, 0);
this
.element(leavingView.pageRef())
.element(this.leavingView.pageRef())
.duration(200)
.easing('cubic-bezier(0.47,0,0.745,0.715)')
.add(wrapper)

View File

@@ -1,64 +1,13 @@
import { Component, Directive, ElementRef, forwardRef, Inject, Input, Optional, Renderer } from '@angular/core';
import { Component, ElementRef, Input, Optional, Renderer, ViewChild } from '@angular/core';
import { App } from '../app/app';
import { Config } from '../../config/config';
import { Icon } from '../icon/icon';
import { Ion } from '../ion';
import { isTrueProperty } from '../../util/util';
import { NavController } from '../nav/nav-controller';
import { NavController } from '../../navigation/nav-controller';
import { ToolbarBase } from '../toolbar/toolbar';
import { ViewController } from '../nav/view-controller';
import { ViewController } from '../../navigation/view-controller';
@Directive({
selector: '.back-button',
host: {
'(click)': 'goBack($event)'
}
})
class BackButton extends Ion {
constructor(
@Optional() private _nav: NavController,
elementRef: ElementRef,
@Optional() @Inject(forwardRef(() => Navbar)) navbar: Navbar
) {
super(elementRef);
navbar && navbar.setBackButtonRef(elementRef);
}
goBack(ev: UIEvent) {
ev.stopPropagation();
ev.preventDefault();
this._nav && this._nav.pop();
}
}
@Directive({
selector: '.back-button-text'
})
class BackButtonText {
constructor(
elementRef: ElementRef,
@Inject(forwardRef(() => Navbar)) navbar: Navbar
) {
navbar.setBackButtonTextRef(elementRef);
}
}
@Directive({
selector: '.toolbar-background'
})
class ToolbarBackground {
constructor(
elementRef: ElementRef,
@Inject(forwardRef(() => Navbar)) navbar: Navbar
) {
navbar.setBackgroundRef(elementRef);
}
}
/**
* @name Navbar
* @description
@@ -97,24 +46,20 @@ class ToolbarBackground {
*/
@Component({
selector: 'ion-navbar',
template: `
<div class="toolbar-background"></div>
<button ion-button="bar-button" class="back-button" [hidden]="_hideBb">
<span class="button-inner">
<ion-icon class="back-button-icon" [name]="_bbIcon"></ion-icon>
<span class="back-button-text">
<span class="back-default">{{_bbText}}</span>
</span>
</span>
</button>
<ng-content select="[menuToggle],ion-buttons[left]"></ng-content>
<ng-content select="ion-buttons[start]"></ng-content>
<ng-content select="ion-buttons[end],ion-buttons[right]"></ng-content>
<div class="toolbar-content">
<ng-content></ng-content>
</div>
`,
directives: [BackButton, BackButtonText, Icon, ToolbarBackground],
template:
'<div class="toolbar-background" [ngClass]="\'toolbar-background-\' + _mode"></div>' +
'<button (click)="backButtonClick($event)" ion-button="bar-button" class="back-button" [ngClass]="\'back-button-\' + _mode" [hidden]="_hideBb">' +
'<span class="button-inner">' +
'<ion-icon class="back-button-icon" [ngClass]="\'back-button-icon-\' + _mode" [name]="_bbIcon"></ion-icon>' +
'<span class="back-button-text" [ngClass]="\'back-button-text-\' + _mode" #bbTxt></span>' +
'</span>' +
'</button>' +
'<ng-content select="[menuToggle],ion-buttons[left]"></ng-content>' +
'<ng-content select="ion-buttons[start]"></ng-content>' +
'<ng-content select="ion-buttons[end],ion-buttons[right]"></ng-content>' +
'<div class="toolbar-content" [ngClass]="\'toolbar-content-\' + _mode">' +
'<ng-content></ng-content>' +
'</div>',
host: {
'[hidden]': '_hidden',
'class': 'toolbar',
@@ -122,29 +67,42 @@ class ToolbarBackground {
}
})
export class Navbar extends ToolbarBase {
private _bbIcon: string;
private _bbText: string;
private _hidden: boolean = false;
private _hideBb: boolean = false;
private _bbRef: ElementRef;
private _bbtRef: ElementRef;
private _bgRef: ElementRef;
private _sbPadding: boolean;
/** @internal */
_color: string;
/**
* @private
*/
@ViewChild('bbTxt') _bbTxt: ElementRef;
/**
* @private
*/
_bbIcon: string;
/**
* @private
*/
_hidden: boolean = false;
/**
* @private
*/
_hideBb: boolean = false;
/**
* @private
*/
_sbPadding: boolean;
/**
* @input {string} The predefined color to use. For example: `"primary"`, `"secondary"`, `"danger"`.
*/
@Input()
get color(): string {
return this._color;
set color(val: string) {
this._setColor('toolbar', val);
}
set color(value: string) {
this._updateColor(value);
}
/**
* @input {string} The mode to apply to this component.
*/
@Input()
set mode(val: string) {
this._setMode('toolbar', val);
}
/**
* @input {boolean} whether the back button should be shown or not
@@ -158,68 +116,39 @@ export class Navbar extends ToolbarBase {
}
constructor(
private _app: App,
public _app: App,
@Optional() viewCtrl: ViewController,
private _elementRef: ElementRef,
private _renderer: Renderer,
config: Config
@Optional() private navCtrl: NavController,
config: Config,
elementRef: ElementRef,
renderer: Renderer
) {
super(_elementRef);
super(config, elementRef, renderer);
viewCtrl && viewCtrl.setNavbar(this);
this.mode = config.get('mode');
viewCtrl && viewCtrl._setNavbar(this);
this._bbIcon = config.get('backButtonIcon');
this._bbText = config.get('backButtonText');
this._sbPadding = config.getBoolean('statusbarPadding', false);
this._sbPadding = config.getBoolean('statusbarPadding');
}
ngAfterViewInit() {
this.setBackButtonText(this._config.get('backButtonText', 'Back'));
}
backButtonClick(ev: UIEvent) {
ev.preventDefault();
ev.stopPropagation();
this.navCtrl && this.navCtrl.pop(null, null);
}
/**
* @private
* Set the text of the Back Button in the Nav Bar. Defaults to "Back".
*/
setBackButtonText(text: string) {
this._bbText = text;
}
/**
* @private
*/
getBackButtonRef() {
return this._bbRef;
}
/**
* @private
*/
setBackButtonRef(backButtonElementRef: ElementRef) {
this._bbRef = backButtonElementRef;
}
/**
* @private
*/
getBackButtonTextRef() {
return this._bbtRef;
}
/**
* @private
*/
setBackButtonTextRef(backButtonTextElementRef: ElementRef) {
this._bbtRef = backButtonTextElementRef;
}
/**
* @private
*/
setBackgroundRef(backgrouneElementRef: ElementRef) {
this._bgRef = backgrouneElementRef;
}
/**
* @private
*/
getBackgroundRef() {
return this._bgRef;
this._renderer.setText(this._bbTxt.nativeElement, text);
}
/**
@@ -241,41 +170,4 @@ export class Navbar extends ToolbarBase {
this._hidden = isHidden;
}
/**
* @internal
*/
_updateColor(newColor: string) {
this._setElementColor(this._color, false);
this._setElementColor(newColor, true);
this._color = newColor;
}
/**
* @internal
*/
_setElementColor(color: string, isAdd: boolean) {
if (color !== null && color !== '') {
this._renderer.setElementClass(this._elementRef.nativeElement, `toolbar-${color}`, isAdd);
}
}
}
/**
* @private
*/
@Directive({
selector: 'template[navbar]'
})
export class NavbarTemplate {
constructor() {
// deprecated warning: added 2016-06-14, beta.10
console.warn('ion-navbar no longer requires *navbar attribute. Please restructure header to:\n' +
'<ion-header>\n' +
' <ion-navbar>\n' +
' ...\n' +
' </ion-navbar>\n' +
'</ion-header>');
}
}

View File

@@ -1,6 +1,8 @@
import { Component, ElementRef, EventEmitter, HostListener, Input, OnInit, OnDestroy, Optional, Output, Renderer, ViewEncapsulation } from '@angular/core';
import { Config } from '../../config/config';
import { Form } from '../../util/form';
import { Ion } from '../ion';
import { isBlank, isCheckedProperty, isPresent, isTrueProperty } from '../../util/util';
import { Item } from '../item/item';
import { RadioGroup } from './radio-group';
@@ -43,49 +45,70 @@ import { RadioGroup } from './radio-group';
*/
@Component({
selector: 'ion-radio',
template: `
<div class="radio-icon" [class.radio-checked]="_checked">
<div class="radio-inner"></div>
</div>
<button ion-button="item-cover"
role="radio"
type="button"
[id]="id"
[attr.aria-checked]="_checked"
[attr.aria-labelledby]="_labelId"
[attr.aria-disabled]="_disabled">
</button>
`,
template:
'<div class="radio-icon" [class.radio-checked]="_checked"> ' +
'<div class="radio-inner"></div> ' +
'</div> ' +
'<button role="radio" ' +
'type="button" ' +
'ion-button="item-cover" ' +
'[id]="id" ' +
'[attr.aria-checked]="_checked" ' +
'[attr.aria-labelledby]="_labelId" ' +
'[attr.aria-disabled]="_disabled" ' +
'class="item-cover"> ' +
'</button>',
host: {
'[class.radio-disabled]': '_disabled'
},
encapsulation: ViewEncapsulation.None,
})
export class RadioButton implements OnDestroy, OnInit {
private _checked: boolean = false;
private _disabled: boolean = false;
private _labelId: string;
private _value: any = null;
export class RadioButton extends Ion implements OnDestroy, OnInit {
/**
* @private
* @internal
*/
_checked: boolean = false;
/**
* @internal
*/
_disabled: boolean = false;
/**
* @internal
*/
_labelId: string;
/**
* @internal
*/
_value: any = null;
/**
* @internal
*/
id: string;
/** @internal */
_color: string;
/**
* @input {string} The predefined color to use. For example: `"primary"`, `"secondary"`, `"danger"`.
*/
@Input()
get color(): string {
return this._color;
set color(val: string) {
this._setColor('radio', val);
if (this._item) {
this._item._updateColor(val, 'item-radio');
}
}
set color(value: string) {
this._updateColor(value);
}
/**
* @input {string} The mode to apply to this component.
*/
@Input()
set mode(val: string) {
this._setMode('radio', val);
}
/**
* @output {any} expression to be evaluated when selected
@@ -94,11 +117,15 @@ export class RadioButton implements OnDestroy, OnInit {
constructor(
private _form: Form,
private _elementRef: ElementRef,
private _renderer: Renderer,
config: Config,
elementRef: ElementRef,
renderer: Renderer,
@Optional() private _item: Item,
@Optional() private _group: RadioGroup
) {
super(config, elementRef, renderer);
this.mode = config.get('mode');
_form.register(this);
if (_group) {
@@ -111,7 +138,7 @@ export class RadioButton implements OnDestroy, OnInit {
// reset to the item's id instead of the radiogroup id
this.id = 'rb-' + _item.registerInput('radio');
this._labelId = 'lbl-' + _item.id;
this._item.setCssClass('item-radio', true);
this._item.setElementClass('item-radio', true);
}
}
@@ -123,7 +150,6 @@ export class RadioButton implements OnDestroy, OnInit {
// if the value is not defined then use it's unique id
return isBlank(this._value) ? this.id : this._value;
}
set value(val: any) {
this._value = val;
}
@@ -140,7 +166,7 @@ export class RadioButton implements OnDestroy, OnInit {
this._checked = isTrueProperty(isChecked);
if (this._item) {
this._item.setCssClass('item-radio-checked', this._checked);
this._item.setElementClass('item-radio-checked', this._checked);
}
}
@@ -151,17 +177,16 @@ export class RadioButton implements OnDestroy, OnInit {
get disabled(): boolean {
return this._disabled;
}
set disabled(val: boolean) {
this._disabled = isTrueProperty(val);
this._item && this._item.setCssClass('item-radio-disabled', this._disabled);
this._item && this._item.setElementClass('item-radio-disabled', this._disabled);
}
/**
* @private
* @internal
*/
@HostListener('click', ['$event'])
private _click(ev: UIEvent) {
_click(ev: UIEvent) {
console.debug('radio, select', this.id);
ev.preventDefault();
ev.stopPropagation();
@@ -171,7 +196,7 @@ export class RadioButton implements OnDestroy, OnInit {
}
/**
* @private
* @internal
*/
ngOnInit() {
if (this._group && isPresent(this._group.value)) {
@@ -180,32 +205,10 @@ export class RadioButton implements OnDestroy, OnInit {
}
/**
* @private
* @internal
*/
ngOnDestroy() {
this._form.deregister(this);
this._group && this._group.remove(this);
}
/**
* @internal
*/
_updateColor(newColor: string) {
this._setElementColor(this._color, false);
this._setElementColor(newColor, true);
this._color = newColor;
}
/**
* @internal
*/
_setElementColor(color: string, isAdd: boolean) {
if (color !== null && color !== '') {
this._renderer.setElementClass(this._elementRef.nativeElement, `radio-${color}`, isAdd);
if (this._item) {
this._item._updateColor(color, 'item-radio');
}
}
}
}

View File

@@ -1,35 +1,34 @@
import { AfterViewInit, Component, ElementRef, EventEmitter, forwardRef, Input, Inject, OnDestroy, OnInit, Optional, Output, Provider, QueryList, Renderer, ViewChild, ViewChildren, ViewEncapsulation } from '@angular/core';
import { NgFor, NgIf } from '@angular/common';
import { AfterViewInit, Component, ElementRef, EventEmitter, forwardRef, Input, Inject, OnDestroy, OnInit, Optional, Output, QueryList, Renderer, ViewChild, ViewChildren, ViewEncapsulation } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { clamp, isNumber, isPresent, isString, isTrueProperty } from '../../util/util';
import { Coordinates, pointerCoord, raf } from '../../util/dom';
import { Config } from '../../config/config';
import { Debouncer } from '../../util/debouncer';
import { Form } from '../../util/form';
import { Ion } from '../ion';
import { Item } from '../item/item';
import { PointerCoordinates, pointerCoord, raf } from '../../util/dom';
import { UIEventManager } from '../../util/ui-event-manager';
export const RANGE_VALUE_ACCESSOR = new Provider(
NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => Range), multi: true});
export const RANGE_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => Range),
multi: true
};
/**
* @private
*/
@Component({
selector: '.range-knob-handle',
template: `
<div class="range-pin" *ngIf="range.pin">{{_val}}</div>
<div class="range-knob"></div>
`,
directives: [NgIf],
template:
'<div class="range-pin" *ngIf="range.pin">{{_val}}</div>' +
'<div class="range-knob"></div>',
host: {
'[class.range-knob-pressed]': 'pressed',
'[class.range-knob-min]': '_val===range.min',
'[class.range-knob-max]': '_val===range.max',
'[style.left]': '_x',
'[style.top]': '_y',
'[style.transform]': '_trns',
'[attr.aria-valuenow]': '_val',
'[attr.aria-valuemin]': 'range.min',
'[attr.aria-valuemax]': 'range.max',
@@ -38,14 +37,14 @@ export const RANGE_VALUE_ACCESSOR = new Provider(
}
})
export class RangeKnob implements OnInit {
private _ratio: number;
private _val: number;
private _x: string;
_ratio: number;
_val: number;
_x: string;
pressed: boolean;
@Input() upper: boolean;
constructor(@Inject(forwardRef(() => Range)) private range: Range) {}
constructor(@Inject(forwardRef(() => Range)) public range: Range) {}
get ratio(): number {
return this._ratio;
@@ -179,18 +178,16 @@ export class RangeKnob implements OnInit {
*/
@Component({
selector: 'ion-range',
template: `
<ng-content select="[range-left]"></ng-content>
<div class="range-slider" #slider>
<div class="range-tick" *ngFor="let t of _ticks" [style.left]="t.left" [class.range-tick-active]="t.active"></div>
<div class="range-bar"></div>
<div class="range-bar range-bar-active" [style.left]="_barL" [style.right]="_barR" #bar></div>
<div class="range-knob-handle"></div>
<div class="range-knob-handle" [upper]="true" *ngIf="_dual"></div>
</div>
<ng-content select="[range-right]"></ng-content>
`,
directives: [NgFor, NgIf, RangeKnob],
template:
'<ng-content select="[range-left]"></ng-content>' +
'<div class="range-slider" #slider>' +
'<div class="range-tick" *ngFor="let t of _ticks" [style.left]="t.left" [class.range-tick-active]="t.active"></div>' +
'<div class="range-bar"></div>' +
'<div class="range-bar range-bar-active" [style.left]="_barL" [style.right]="_barR" #bar></div>' +
'<div class="range-knob-handle"></div>' +
'<div class="range-knob-handle" [upper]="true" *ngIf="_dual"></div>' +
'</div>' +
'<ng-content select="[range-right]"></ng-content>',
host: {
'[class.range-disabled]': '_disabled',
'[class.range-pressed]': '_pressed',
@@ -199,52 +196,52 @@ export class RangeKnob implements OnInit {
providers: [RANGE_VALUE_ACCESSOR],
encapsulation: ViewEncapsulation.None,
})
export class Range implements AfterViewInit, ControlValueAccessor, OnDestroy {
private _dual: boolean = false;
private _pin: boolean;
private _disabled: boolean = false;
private _pressed: boolean;
private _labelId: string;
private _fn: Function;
export class Range extends Ion implements AfterViewInit, ControlValueAccessor, OnDestroy {
_dual: boolean = false;
_pin: boolean;
_disabled: boolean = false;
_pressed: boolean;
_labelId: string;
_fn: Function;
private _active: RangeKnob;
private _start: Coordinates = null;
private _rect: ClientRect;
private _ticks: any[] = [];
private _barL: string;
private _barR: string;
_active: RangeKnob;
_start: PointerCoordinates = null;
_rect: ClientRect;
_ticks: any[];
_barL: string;
_barR: string;
private _min: number = 0;
private _max: number = 100;
private _step: number = 1;
private _snaps: boolean = false;
_min: number = 0;
_max: number = 100;
_step: number = 1;
_snaps: boolean = false;
private _debouncer: Debouncer = new Debouncer(0);
private _events: UIEventManager = new UIEventManager();
_debouncer: Debouncer = new Debouncer(0);
_events: UIEventManager = new UIEventManager();
/**
* @private
*/
value: any;
/** @internal */
_color: string;
/**
* @input {string} The predefined color to use. For example: `"primary"`, `"secondary"`, `"danger"`.
*/
@Input()
get color(): string {
return this._color;
set color(val: string) {
this._setColor('range', val);
}
set color(value: string) {
this._updateColor(value);
/**
* @input {string} The mode to apply to this component.
*/
@Input()
set mode(val: string) {
this._setMode('range', val);
}
@ViewChild('bar') private _bar: ElementRef;
@ViewChild('slider') private _slider: ElementRef;
@ViewChildren(RangeKnob) private _knobs: QueryList<RangeKnob>;
@ViewChild('bar') public _bar: ElementRef;
@ViewChild('slider') public _slider: ElementRef;
@ViewChildren(RangeKnob) public _knobs: QueryList<RangeKnob>;
/**
* @private
@@ -347,15 +344,19 @@ export class Range implements AfterViewInit, ControlValueAccessor, OnDestroy {
constructor(
private _form: Form,
@Optional() private _item: Item,
private _elementRef: ElementRef,
private _renderer: Renderer
config: Config,
elementRef: ElementRef,
renderer: Renderer
) {
super(config, elementRef, renderer);
this.mode = config.get('mode');
_form.register(this);
if (_item) {
this.id = 'rng-' + _item.registerInput('range');
this._labelId = 'lbl-' + _item.id;
_item.setCssClass('item-range', true);
_item.setElementClass('item-range', true);
}
}
@@ -480,7 +481,7 @@ export class Range implements AfterViewInit, ControlValueAccessor, OnDestroy {
/**
* @private
*/
setActiveKnob(current: Coordinates, rect: ClientRect) {
setActiveKnob(current: PointerCoordinates, rect: ClientRect) {
// figure out which knob is the closest one to the pointer
let ratio = (current.x - rect.left) / (rect.width);
@@ -495,7 +496,7 @@ export class Range implements AfterViewInit, ControlValueAccessor, OnDestroy {
/**
* @private
*/
updateKnob(current: Coordinates, rect: ClientRect) {
updateKnob(current: PointerCoordinates, rect: ClientRect) {
// figure out where the pointer is currently at
// update the knob being interacted with
if (this._active) {
@@ -568,7 +569,7 @@ export class Range implements AfterViewInit, ControlValueAccessor, OnDestroy {
* @private
*/
updateTicks() {
if (this._snaps) {
if (this._snaps && this._ticks) {
let ratio = this.ratio;
if (this._dual) {
let upperRatio = this.ratioUpper;
@@ -601,24 +602,6 @@ export class Range implements AfterViewInit, ControlValueAccessor, OnDestroy {
return (value - this._min) / (this._max - this._min);
}
/**
* @internal
*/
_updateColor(newColor: string) {
this._setElementColor(this._color, false);
this._setElementColor(newColor, true);
this._color = newColor;
}
/**
* @internal
*/
_setElementColor(color: string, isAdd: boolean) {
if (color !== null && color !== '') {
this._renderer.setElementClass(this._elementRef.nativeElement, `range-${color}`, isAdd);
}
}
/**
* @private
*/
@@ -667,7 +650,7 @@ export class Range implements AfterViewInit, ControlValueAccessor, OnDestroy {
}
set disabled(val: boolean) {
this._disabled = isTrueProperty(val);
this._item && this._item.setCssClass('item-range-disabled', this._disabled);
this._item && this._item.setElementClass('item-range-disabled', this._disabled);
}
/**

View File

@@ -1,8 +1,8 @@
import { Component, Directive, ElementRef, EventEmitter, HostBinding, Input, Optional, Output, Renderer, ViewChild, ViewEncapsulation } from '@angular/core';
import { NgControl, NgModel } from '@angular/forms';
import { Component, ElementRef, EventEmitter, HostBinding, Input, Optional, Output, Renderer, ViewChild, ViewEncapsulation } from '@angular/core';
import { NgControl } from '@angular/forms';
import { Config } from '../../config/config';
import { Icon } from '../icon/icon';
import { Ion } from '../ion';
import { isPresent } from '../../util/util';
import { Debouncer } from '../../util/debouncer';
@@ -38,7 +38,6 @@ import { Debouncer } from '../../util/debouncer';
'<button ion-button clear class="searchbar-clear-icon" (click)="clearInput($event)" (mousedown)="clearInput($event)"></button>' +
'</div>' +
'<button ion-button #cancelButton [tabindex]="_isActive ? 1 : -1" clear (click)="cancelSearchbar($event)" (mousedown)="cancelSearchbar($event)" class="searchbar-ios-cancel">{{cancelButtonText}}</button>',
directives: [Icon, NgModel],
host: {
'[class.searchbar-has-value]': '_value',
'[class.searchbar-active]': '_isActive',
@@ -47,26 +46,27 @@ import { Debouncer } from '../../util/debouncer';
},
encapsulation: ViewEncapsulation.None
})
export class Searchbar {
private _value: string|number = '';
private _shouldBlur: boolean = true;
private _isActive: boolean = false;
private _searchbarInput: ElementRef;
private _debouncer: Debouncer = new Debouncer(250);
/** @internal */
_color: string;
export class Searchbar extends Ion {
_value: string|number = '';
_shouldBlur: boolean = true;
_isActive: boolean = false;
_searchbarInput: ElementRef;
_debouncer: Debouncer = new Debouncer(250);
/**
* @input {string} The predefined color to use. For example: `"primary"`, `"secondary"`, `"danger"`.
*/
@Input()
get color(): string {
return this._color;
set color(val: string) {
this._setColor('searchbar', val);
}
set color(value: string) {
this._updateColor(value);
/**
* @input {string} The mode to apply to this component.
*/
@Input()
set mode(val: string) {
this._setMode('searchbar', val);
}
/**
@@ -146,11 +146,15 @@ export class Searchbar {
@HostBinding('class.searchbar-has-focus') _sbHasFocus: boolean;
constructor(
private _elementRef: ElementRef,
private _config: Config,
private _renderer: Renderer,
config: Config,
elementRef: ElementRef,
renderer: Renderer,
@Optional() ngControl: NgControl
) {
super(config, elementRef, renderer);
this.mode = config.get('mode');
// If the user passed a ngControl we need to set the valueAccessor
if (ngControl) {
ngControl.valueAccessor = this;
@@ -161,7 +165,7 @@ export class Searchbar {
* @private
*/
@ViewChild('searchbarInput')
private set searchbarInput(searchbarInput: ElementRef) {
set searchbarInput(searchbarInput: ElementRef) {
this._searchbarInput = searchbarInput;
let inputEle = searchbarInput.nativeElement;
@@ -211,9 +215,9 @@ export class Searchbar {
/**
* @private
* After View Initialization position the elements
* After View Checked position the elements
*/
ngAfterViewInit() {
ngAfterViewChecked() {
this.positionElements();
}
@@ -361,24 +365,6 @@ export class Searchbar {
this._isActive = false;
}
/**
* @internal
*/
_updateColor(newColor: string) {
this._setElementColor(this._color, false);
this._setElementColor(newColor, true);
this._color = newColor;
}
/**
* @internal
*/
_setElementColor(color: string, isAdd: boolean) {
if (color !== null && color !== '') {
this._renderer.setElementClass(this._elementRef.nativeElement, `searchbar-${color}`, isAdd);
}
}
/**
* @private
* Write a new value to the element.

View File

@@ -1,6 +1,8 @@
import { Component, ContentChildren, Directive, ElementRef, EventEmitter, HostListener, Input, Output, Optional, QueryList, Renderer, ViewEncapsulation } from '@angular/core';
import { NgControl } from '@angular/forms';
import { Config } from '../../config/config';
import { Ion } from '../ion';
import { isPresent, isTrueProperty } from '../../util/util';
@@ -42,10 +44,9 @@ import { isPresent, isTrueProperty } from '../../util/util';
*/
@Component({
selector: 'ion-segment-button',
template: `
<ng-content></ng-content>
<ion-button-effect></ion-button-effect>
`,
template:
'<ng-content></ng-content>' +
'<div class="button-effect"></div>',
host: {
'tappable': '',
'class': 'segment-button',
@@ -54,7 +55,7 @@ import { isPresent, isTrueProperty } from '../../util/util';
encapsulation: ViewEncapsulation.None,
})
export class SegmentButton {
private _disabled: boolean = false;
_disabled: boolean = false;
/**
* @input {string} the value of the segment button. Required.
@@ -78,13 +79,13 @@ export class SegmentButton {
set disabled(val: boolean) {
this._disabled = isTrueProperty(val);
this.setCssClass('segment-button-disabled', this._disabled);
this._setCssClass('segment-button-disabled', this._disabled);
}
/**
* @private
*/
setCssClass(cssClass: string, shouldAdd: boolean) {
_setCssClass(cssClass: string, shouldAdd: boolean) {
this._renderer.setElementClass(this._elementRef.nativeElement, cssClass, shouldAdd);
}
@@ -93,7 +94,7 @@ export class SegmentButton {
* On click of a SegmentButton
*/
@HostListener('click')
private onClick() {
onClick() {
console.debug('SegmentButton, select', this.value);
this.ionSelect.emit(this);
}
@@ -177,27 +178,27 @@ export class SegmentButton {
@Directive({
selector: 'ion-segment'
})
export class Segment {
private _disabled: boolean = false;
export class Segment extends Ion {
_disabled: boolean = false;
/**
* @private
*/
value: string;
/** @internal */
_color: string;
/**
* @input {string} The predefined color to use. For example: `"primary"`, `"secondary"`, `"danger"`.
*/
@Input()
get color(): string {
return this._color;
set color(val: string) {
this._setColor('segment', val);
}
set color(value: string) {
this._updateColor(value);
/**
* @input {string} The mode to apply to this component.
*/
set mode(val: string) {
this._setMode('segment', val);
}
/**
@@ -212,10 +213,15 @@ export class Segment {
@ContentChildren(SegmentButton) _buttons: QueryList<SegmentButton>;
constructor(
private _elementRef: ElementRef,
private _renderer: Renderer,
config: Config,
elementRef: ElementRef,
renderer: Renderer,
@Optional() ngControl: NgControl
) {
super(config, elementRef, renderer);
this.mode = config.get('mode');
if (ngControl) {
ngControl.valueAccessor = this;
}
@@ -233,28 +239,9 @@ export class Segment {
this._disabled = isTrueProperty(val);
if (this._buttons) {
let buttons = this._buttons.toArray();
for (let button of buttons) {
button.setCssClass('segment-button-disabled', this._disabled);
}
}
}
/**
* @internal
*/
_updateColor(newColor: string) {
this._setElementColor(this._color, false);
this._setElementColor(newColor, true);
this._color = newColor;
}
/**
* @internal
*/
_setElementColor(color: string, isAdd: boolean) {
if (color !== null && color !== '') {
this._renderer.setElementClass(this._elementRef.nativeElement, `segment-${color}`, isAdd);
this._buttons.forEach(button => {
button._setCssClass('segment-button-disabled', this._disabled);
});
}
}
@@ -276,8 +263,7 @@ export class Segment {
* @private
*/
ngAfterViewInit() {
let buttons = this._buttons.toArray();
for (let button of buttons) {
this._buttons.forEach(button => {
button.ionSelect.subscribe((selectedButton: any) => {
this.writeValue(selectedButton.value);
this.onChange(selectedButton.value);
@@ -289,10 +275,9 @@ export class Segment {
}
if (isTrueProperty(this._disabled)) {
button.setCssClass('segment-button-disabled', this._disabled);
button._setCssClass('segment-button-disabled', this._disabled);
}
}
});
}
/**

View File

@@ -1,7 +1,7 @@
import { ChangeDetectionStrategy, Component, ElementRef, Input, Renderer, ViewEncapsulation } from '@angular/core';
import { NgFor, NgStyle } from '@angular/common';
import { Config } from '../../config/config';
import { Ion } from '../ion';
/**
* @name Spinner
@@ -98,31 +98,26 @@ import { Config } from '../../config/config';
*/
@Component({
selector: 'ion-spinner',
template: `
<svg viewBox="0 0 64 64" *ngFor="let i of _c" [ngStyle]="i.style">
<circle [attr.r]="i.r" transform="translate(32,32)"></circle>
</svg>
<svg viewBox="0 0 64 64" *ngFor="let i of _l" [ngStyle]="i.style">
<line [attr.y1]="i.y1" [attr.y2]="i.y2" transform="translate(32,32)"></line>
</svg>
`,
directives: [NgFor, NgStyle],
template:
'<svg viewBox="0 0 64 64" *ngFor="let i of _c" [ngStyle]="i.style">' +
'<circle [attr.r]="i.r" transform="translate(32,32)"></circle>' +
'</svg>' +
'<svg viewBox="0 0 64 64" *ngFor="let i of _l" [ngStyle]="i.style">' +
'<line [attr.y1]="i.y1" [attr.y2]="i.y2" transform="translate(32,32)"></line>' +
'</svg>',
host: {
'[class.spinner-paused]': 'paused'
},
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
})
export class Spinner {
private _c: any[];
private _l: any[];
private _name: string;
private _dur: number = null;
private _init: boolean;
private _applied: string;
/** @internal */
_color: string;
export class Spinner extends Ion {
_c: any[];
_l: any[];
_name: string;
_dur: number = null;
_init: boolean;
_applied: string;
/**
* @input {string} The predefined color to use. For example: `"primary"`, `"secondary"`, `"danger"`.
@@ -131,10 +126,9 @@ export class Spinner {
get color(): string {
return this._color;
}
set color(value: string) {
this._updateColor(value);
}
this._setColor('spinner', value);
}
/**
* @input {string} SVG spinner name.
@@ -167,11 +161,9 @@ export class Spinner {
*/
@Input() paused: boolean = false;
constructor(
private _config: Config,
private _elementRef: ElementRef,
private _renderer: Renderer
) {}
constructor(config: Config, elementRef: ElementRef, renderer: Renderer) {
super(config, elementRef, renderer);
}
/**
* @private
@@ -206,7 +198,7 @@ export class Spinner {
}
}
this._renderer.setElementClass(this._elementRef.nativeElement, this._applied, true);
this.setElementClass(this._applied, true);
}
}
}
@@ -218,24 +210,6 @@ export class Spinner {
return data;
}
/**
* @internal
*/
_updateColor(newColor: string) {
this._setElementColor(this._color, false);
this._setElementColor(newColor, true);
this._color = newColor;
}
/**
* @internal
*/
_setElementColor(color: string, isAdd: boolean) {
if (color !== null && color !== '') {
this._renderer.setElementClass(this._elementRef.nativeElement, `spinner-${color}`, isAdd);
}
}
}
const SPINNERS: any = {

View File

@@ -1,33 +1,29 @@
import { AfterViewInit, Component, ElementRef, Renderer } from '@angular/core';
import { NgIf } from '@angular/common';
import { Animation } from '../../animations/animation';
import { Config } from '../../config/config';
import { isPresent } from '../../util/util';
import { NavParams } from '../nav/nav-params';
import { Transition, TransitionOptions } from '../../transitions/transition';
import { ViewController } from '../nav/view-controller';
import { NavParams } from '../../navigation/nav-params';
import { Transition } from '../../transitions/transition';
import { ViewController } from '../../navigation/view-controller';
/**
* @private
*/
* @private
*/
@Component({
selector: 'ion-toast',
template: `
<div class="toast-wrapper"
[class.toast-bottom]="d.position === 'bottom'"
[class.toast-middle]="d.position === 'middle'"
[class.toast-top]="d.position === 'top'">
<div class="toast-container">
<div class="toast-message" id="{{hdrId}}" *ngIf="d.message">{{d.message}}</div>
<button ion-button clear class="toast-button" *ngIf="d.showCloseButton" (click)="cbClick()">
{{ d.closeButtonText || 'Close' }}
</button>
</div>
</div>
`,
directives: [NgIf],
template:
'<div class="toast-wrapper" ' +
'[class.toast-bottom]="d.position === \'bottom\'" ' +
'[class.toast-middle]="d.position === \'middle\'" ' +
'[class.toast-top]="d.position === \'top\'"> ' +
'<div class="toast-container"> ' +
'<div class="toast-message" id="{{hdrId}}" *ngIf="d.message">{{d.message}}</div> ' +
'<button ion-button clear class="toast-button" *ngIf="d.showCloseButton" (click)="cbClick()"> ' +
'{{ d.closeButtonText || \'Close\' }} ' +
'</button> ' +
'</div> ' +
'</div>',
host: {
'role': 'dialog',
'[attr.aria-labelledby]': 'hdrId',
@@ -35,7 +31,7 @@ import { ViewController } from '../nav/view-controller';
},
})
export class ToastCmp implements AfterViewInit {
private d: {
d: {
message?: string;
cssClass?: string;
duration?: number;
@@ -44,20 +40,20 @@ export class ToastCmp implements AfterViewInit {
dismissOnPageChange?: boolean;
position?: string;
};
private descId: string;
private dismissTimeout: number = undefined;
private enabled: boolean;
private hdrId: string;
private id: number;
descId: string;
dismissTimeout: number = undefined;
enabled: boolean;
hdrId: string;
id: number;
constructor(
private _viewCtrl: ViewController,
private _config: Config,
private _elementRef: ElementRef,
public _viewCtrl: ViewController,
public _config: Config,
public _elementRef: ElementRef,
params: NavParams,
renderer: Renderer
) {
) {
renderer.setElementClass(_elementRef.nativeElement, `toast-${_config.get('mode')}`, true);
this.d = params.data;
if (this.d.cssClass) {
@@ -76,10 +72,9 @@ export class ToastCmp implements AfterViewInit {
ngAfterViewInit() {
// if there's a `duration` set, automatically dismiss.
if (this.d.duration) {
this.dismissTimeout =
setTimeout(() => {
this.dismissTimeout = (<any>setTimeout(() => {
this.dismiss('backdrop');
}, this.d.duration);
}, this.d.duration));
}
this.enabled = true;
}
@@ -113,21 +108,19 @@ export class ToastCmp implements AfterViewInit {
class ToastSlideIn extends Transition {
constructor(enteringView: ViewController, leavingView: ViewController, opts: TransitionOptions) {
super(enteringView, leavingView, opts);
init() {
// DOM READS
let ele = enteringView.pageRef().nativeElement;
let ele = this.enteringView.pageRef().nativeElement;
const wrapperEle = <HTMLElement> ele.querySelector('.toast-wrapper');
let wrapper = new Animation(wrapperEle);
if (enteringView.data && enteringView.data.position === TOAST_POSITION_TOP) {
if (this.enteringView.data && this.enteringView.data.position === TOAST_POSITION_TOP) {
// top
// by default, it is -100% hidden (above the screen)
// so move from that to 10px below top: 0px;
wrapper.fromTo('translateY', '-100%', `${10}px`);
} else if (enteringView.data && enteringView.data.position === TOAST_POSITION_MIDDLE) {
} else if (this.enteringView.data && this.enteringView.data.position === TOAST_POSITION_MIDDLE) {
// Middle
// just center it and fade it in
let topPosition = Math.floor(ele.clientHeight / 2 - wrapperEle.clientHeight / 2);
@@ -147,19 +140,17 @@ class ToastSlideIn extends Transition {
}
class ToastSlideOut extends Transition {
constructor(enteringView: ViewController, leavingView: ViewController, opts: TransitionOptions) {
super(enteringView, leavingView, opts);
let ele = leavingView.pageRef().nativeElement;
init() {
let ele = this.leavingView.pageRef().nativeElement;
const wrapperEle = <HTMLElement> ele.querySelector('.toast-wrapper');
let wrapper = new Animation(wrapperEle);
if (leavingView.data && leavingView.data.position === TOAST_POSITION_TOP) {
if (this.leavingView.data && this.leavingView.data.position === TOAST_POSITION_TOP) {
// top
// reverse arguments from enter transition
wrapper.fromTo('translateY', `${10}px`, '-100%');
} else if (leavingView.data && leavingView.data.position === TOAST_POSITION_MIDDLE) {
} else if (this.leavingView.data && this.leavingView.data.position === TOAST_POSITION_MIDDLE) {
// Middle
// just fade it out
wrapper.fromTo('opacity', 0.99, 0);
@@ -175,21 +166,19 @@ class ToastSlideOut extends Transition {
}
class ToastMdSlideIn extends Transition {
constructor(enteringView: ViewController, leavingView: ViewController, opts: TransitionOptions) {
super(enteringView, leavingView, opts);
init() {
// DOM reads
let ele = enteringView.pageRef().nativeElement;
let ele = this.enteringView.pageRef().nativeElement;
const wrapperEle = ele.querySelector('.toast-wrapper');
let wrapper = new Animation(wrapperEle);
if (enteringView.data && enteringView.data.position === TOAST_POSITION_TOP) {
if (this.enteringView.data && this.enteringView.data.position === TOAST_POSITION_TOP) {
// top
// by default, it is -100% hidden (above the screen)
// so move from that to top: 0px;
wrapper.fromTo('translateY', '-100%', `0%`);
} else if (enteringView.data && enteringView.data.position === TOAST_POSITION_MIDDLE) {
} else if (this.enteringView.data && this.enteringView.data.position === TOAST_POSITION_MIDDLE) {
// Middle
// just center it and fade it in
let topPosition = Math.floor(ele.clientHeight / 2 - wrapperEle.clientHeight / 2);
@@ -209,19 +198,17 @@ class ToastMdSlideIn extends Transition {
}
class ToastMdSlideOut extends Transition {
constructor(enteringView: ViewController, leavingView: ViewController, opts: TransitionOptions) {
super(enteringView, leavingView, opts);
let ele = leavingView.pageRef().nativeElement;
init() {
let ele = this.leavingView.pageRef().nativeElement;
const wrapperEle = ele.querySelector('.toast-wrapper');
let wrapper = new Animation(wrapperEle);
if (leavingView.data && leavingView.data.position === TOAST_POSITION_TOP) {
if (this.leavingView.data && this.leavingView.data.position === TOAST_POSITION_TOP) {
// top
// reverse arguments from enter transition
wrapper.fromTo('translateY', `${0}%`, '-100%');
} else if (leavingView.data && leavingView.data.position === TOAST_POSITION_MIDDLE) {
} else if (this.leavingView.data && this.leavingView.data.position === TOAST_POSITION_MIDDLE) {
// Middle
// just fade it out
wrapper.fromTo('opacity', 0.99, 0);
@@ -237,19 +224,17 @@ class ToastMdSlideOut extends Transition {
}
class ToastWpPopIn extends Transition {
constructor(enteringView: ViewController, leavingView: ViewController, opts: TransitionOptions) {
super(enteringView, leavingView, opts);
let ele = enteringView.pageRef().nativeElement;
init() {
let ele = this.enteringView.pageRef().nativeElement;
const wrapperEle = ele.querySelector('.toast-wrapper');
let wrapper = new Animation(wrapperEle);
if (enteringView.data && enteringView.data.position === TOAST_POSITION_TOP) {
if (this.enteringView.data && this.enteringView.data.position === TOAST_POSITION_TOP) {
// top
wrapper.fromTo('opacity', 0.01, 1);
wrapper.fromTo('scale', 1.3, 1);
} else if (enteringView.data && enteringView.data.position === TOAST_POSITION_MIDDLE) {
} else if (this.enteringView.data && this.enteringView.data.position === TOAST_POSITION_MIDDLE) {
// Middle
// just center it and fade it in
let topPosition = Math.floor(ele.clientHeight / 2 - wrapperEle.clientHeight / 2);
@@ -270,21 +255,19 @@ class ToastWpPopIn extends Transition {
}
class ToastWpPopOut extends Transition {
constructor(enteringView: ViewController, leavingView: ViewController, opts: TransitionOptions) {
super(enteringView, leavingView, opts);
init() {
// DOM reads
let ele = leavingView.pageRef().nativeElement;
let ele = this.leavingView.pageRef().nativeElement;
const wrapperEle = ele.querySelector('.toast-wrapper');
let wrapper = new Animation(wrapperEle);
if (leavingView.data && leavingView.data.position === TOAST_POSITION_TOP) {
if (this.leavingView.data && this.leavingView.data.position === TOAST_POSITION_TOP) {
// top
// reverse arguments from enter transition
wrapper.fromTo('opacity', 0.99, 0);
wrapper.fromTo('scale', 1, 1.3);
} else if (leavingView.data && leavingView.data.position === TOAST_POSITION_MIDDLE) {
} else if (this.leavingView.data && this.leavingView.data.position === TOAST_POSITION_MIDDLE) {
// Middle
// just fade it out
wrapper.fromTo('opacity', 0.99, 0);
@@ -315,4 +298,3 @@ Transition.register('toast-wp-slide-in', ToastWpPopIn);
let toastIds = -1;
const TOAST_POSITION_TOP = 'top';
const TOAST_POSITION_MIDDLE = 'middle';
const TOAST_POSITION_BOTTOM = 'bottom';

View File

@@ -1,16 +1,19 @@
import { AfterContentInit, Component, ElementRef, EventEmitter, forwardRef, Input, OnDestroy, Optional, Output, Provider, Renderer, ViewEncapsulation } from '@angular/core';
import { AfterContentInit, Component, ElementRef, EventEmitter, forwardRef, Input, OnDestroy, Optional, Output, Renderer, ViewEncapsulation } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Config } from '../../config/config';
import { Form } from '../../util/form';
import { isTrueProperty } from '../../util/util';
import { Ion } from '../ion';
import { Item } from '../item/item';
import { pointerCoord } from '../../util/dom';
import { UIEventManager } from '../../util/ui-event-manager';
export const TOGGLE_VALUE_ACCESSOR = new Provider(
NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => Toggle), multi: true});
export const TOGGLE_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => Toggle),
multi: true
};
/**
* @name Toggle
@@ -51,54 +54,65 @@ export const TOGGLE_VALUE_ACCESSOR = new Provider(
*/
@Component({
selector: 'ion-toggle',
template: `
<div class="toggle-icon" [class.toggle-checked]="_checked" [class.toggle-activated]="_activated">
<div class="toggle-inner"></div>
</div>
<button ion-button="item-cover"
role="checkbox"
type="button"
[id]="id"
[attr.aria-checked]="_checked"
[attr.aria-labelledby]="_labelId"
[attr.aria-disabled]="_disabled">
</button>
`,
template:
'<div class="toggle-icon" [class.toggle-checked]="_checked" [class.toggle-activated]="_activated">' +
'<div class="toggle-inner"></div>' +
'</div>' +
'<button role="checkbox" ' +
'type="button" ' +
'ion-button="item-cover" ' +
'[id]="id" ' +
'[attr.aria-checked]="_checked" ' +
'[attr.aria-labelledby]="_labelId" ' +
'[attr.aria-disabled]="_disabled" ' +
'class="item-cover">' +
'</button>',
host: {
'[class.toggle-disabled]': '_disabled'
},
providers: [TOGGLE_VALUE_ACCESSOR],
encapsulation: ViewEncapsulation.None,
})
export class Toggle implements AfterContentInit, ControlValueAccessor, OnDestroy {
private _checked: boolean = false;
private _init: boolean;
private _disabled: boolean = false;
private _labelId: string;
private _activated: boolean = false;
private _startX: number;
private _fn: Function;
private _events: UIEventManager = new UIEventManager();
export class Toggle extends Ion implements AfterContentInit, ControlValueAccessor, OnDestroy {
/** @private */
_checked: boolean = false;
/** @private */
_init: boolean;
/** @private */
_disabled: boolean = false;
/** @private */
_labelId: string;
/** @private */
_activated: boolean = false;
/** @private */
_startX: number;
/** @private */
_msPrv: number = 0;
/** @private */
_fn: Function;
/** @private */
_events: UIEventManager = new UIEventManager();
/**
* @private
*/
id: string;
/** @internal */
_color: string;
/**
* @input {string} The predefined color to use. For example: `"primary"`, `"secondary"`, `"danger"`.
*/
@Input()
get color(): string {
return this._color;
set color(val: string) {
this._setColor('toggle', val);
}
set color(value: string) {
this._updateColor(value);
}
/**
* @input {string} The mode to apply to this component.
*/
@Input()
set mode(val: string) {
this._setMode('toggle', val);
}
/**
* @output {Toggle} expression to evaluate when the toggle value changes
@@ -106,24 +120,28 @@ export class Toggle implements AfterContentInit, ControlValueAccessor, OnDestroy
@Output() ionChange: EventEmitter<Toggle> = new EventEmitter<Toggle>();
constructor(
private _form: Form,
private _elementRef: ElementRef,
private _renderer: Renderer,
@Optional() private _item: Item
public _form: Form,
config: Config,
elementRef: ElementRef,
renderer: Renderer,
@Optional() public _item: Item
) {
this._form.register(this);
super(config, elementRef, renderer);
this.mode = config.get('mode');
_form.register(this);
if (_item) {
this.id = 'tgl-' + _item.registerInput('toggle');
this._labelId = 'lbl-' + _item.id;
this._item.setCssClass('item-toggle', true);
this._item.setElementClass('item-toggle', true);
}
}
/**
* @private
*/
private pointerDown(ev: UIEvent): boolean {
pointerDown(ev: UIEvent): boolean {
this._startX = pointerCoord(ev).x;
this._activated = true;
return true;
@@ -132,7 +150,7 @@ export class Toggle implements AfterContentInit, ControlValueAccessor, OnDestroy
/**
* @private
*/
private pointerMove(ev: UIEvent) {
pointerMove(ev: UIEvent) {
if (this._startX) {
let currentX = pointerCoord(ev).x;
console.debug('toggle, pointerMove', ev.type, currentX);
@@ -155,7 +173,7 @@ export class Toggle implements AfterContentInit, ControlValueAccessor, OnDestroy
/**
* @private
*/
private pointerUp(ev: UIEvent) {
pointerUp(ev: UIEvent) {
if (this._startX) {
let endX = pointerCoord(ev).x;
@@ -186,14 +204,16 @@ export class Toggle implements AfterContentInit, ControlValueAccessor, OnDestroy
this.onChange(this._checked);
}
private _setChecked(isChecked: boolean) {
/**
* @private
*/
_setChecked(isChecked: boolean) {
if (!this._disabled && isChecked !== this._checked) {
this._checked = isChecked;
if (this._init) {
this.ionChange.emit(this);
}
this._item && this._item.setCssClass('item-toggle-checked', isChecked);
this._item && this._item.setElementClass('item-toggle-checked', isChecked);
}
}
@@ -232,26 +252,8 @@ export class Toggle implements AfterContentInit, ControlValueAccessor, OnDestroy
set disabled(val: boolean) {
this._disabled = isTrueProperty(val);
this._item && this._item.setCssClass('item-toggle-disabled', this._disabled);
this._item && this._item.setElementClass('item-toggle-disabled', this._disabled);
}
/**
* @internal
*/
_updateColor(newColor: string) {
this._setElementColor(this._color, false);
this._setElementColor(newColor, true);
this._color = newColor;
}
/**
* @internal
*/
_setElementColor(color: string, isAdd: boolean) {
if (color !== null && color !== '') {
this._renderer.setElementClass(this._elementRef.nativeElement, `toggle-${color}`, isAdd);
}
}
/**
* @private

View File

@@ -1,6 +1,8 @@
import { Directive, ElementRef, Optional, forwardRef, Inject, ContentChildren } from '@angular/core';
import { ContentChildren, Directive, ElementRef, forwardRef, Optional, Inject, Renderer } from '@angular/core';
import { Button } from '../button/button';
import { Config } from '../../config/config';
import { Ion } from '../ion';
import { Navbar } from '../navbar/navbar';
import { Toolbar } from './toolbar';
@@ -11,16 +13,19 @@ import { Toolbar } from './toolbar';
@Directive({
selector: 'ion-buttons,[menuToggle]'
})
export class ToolbarItem {
export class ToolbarItem extends Ion {
inToolbar: boolean;
constructor(
config: Config,
elementRef: ElementRef,
renderer: Renderer,
@Optional() toolbar: Toolbar,
@Optional() @Inject(forwardRef(() => Navbar)) navbar: Navbar
) {
toolbar && toolbar.addItemRef(elementRef);
navbar && navbar.addItemRef(elementRef);
super(config, elementRef, renderer);
this._setMode('bar-buttons', config.get('mode'));
this.inToolbar = !!(toolbar || navbar);
}

View File

@@ -1,5 +1,6 @@
import { Component, ElementRef, Optional, forwardRef, Inject, ChangeDetectionStrategy, ViewEncapsulation } from '@angular/core';
import { ChangeDetectionStrategy, Component, ElementRef, forwardRef, Optional, Inject, Renderer, ViewEncapsulation } from '@angular/core';
import { Config } from '../../config/config';
import { Ion } from '../ion';
import { Navbar } from '../navbar/navbar';
import { Toolbar } from './toolbar';
@@ -43,7 +44,7 @@ import { Toolbar } from './toolbar';
@Component({
selector: 'ion-title',
template:
'<div class="toolbar-title">' +
'<div class="toolbar-title" [ngClass]="\'toolbar-title-\' + _mode">' +
'<ng-content></ng-content>' +
'</div>',
changeDetection: ChangeDetectionStrategy.OnPush,
@@ -51,13 +52,17 @@ import { Toolbar } from './toolbar';
})
export class ToolbarTitle extends Ion {
constructor(
private _elementRef: ElementRef,
config: Config,
elementRef: ElementRef,
renderer: Renderer,
@Optional() toolbar: Toolbar,
@Optional() @Inject(forwardRef(() => Navbar)) navbar: Navbar
) {
super(_elementRef);
toolbar && toolbar.setTitleCmp(this);
navbar && navbar.setTitleCmp(this);
super(config, elementRef, renderer);
this._setMode('title', this._mode = config.get('mode'));
toolbar && toolbar._setTitle(this);
navbar && navbar._setTitle(this);
}
/**

View File

@@ -1,8 +1,9 @@
import { ChangeDetectionStrategy, Component, ContentChild, ContentChildren, Directive, ElementRef, Input, Optional, QueryList, Renderer, ViewEncapsulation } from '@angular/core';
import { ChangeDetectionStrategy, Component, Directive, ElementRef, Input, Optional, Renderer } from '@angular/core';
import { Config } from '../../config/config';
import { Ion } from '../ion';
import { ViewController } from '../nav/view-controller';
import { ToolbarTitle } from './toolbar-title';
import { ViewController } from '../../navigation/view-controller';
/**
@@ -35,10 +36,12 @@ import { ViewController } from '../nav/view-controller';
@Directive({
selector: 'ion-header'
})
export class Header {
export class Header extends Ion {
constructor(@Optional() viewCtrl: ViewController) {
viewCtrl && viewCtrl.setHeader(this);
constructor(config: Config, elementRef: ElementRef, renderer: Renderer, @Optional() viewCtrl: ViewController) {
super(config, elementRef, renderer);
this._setMode('header', config.get('mode'));
viewCtrl && viewCtrl._setHeader(this);
}
}
@@ -69,10 +72,12 @@ export class Header {
@Directive({
selector: 'ion-footer'
})
export class Footer {
export class Footer extends Ion {
constructor(@Optional() viewCtrl: ViewController) {
viewCtrl && viewCtrl.setFooter(this);
constructor(config: Config, elementRef: ElementRef, renderer: Renderer, @Optional() viewCtrl: ViewController) {
super(config, elementRef, renderer);
this._setMode('footer', config.get('mode'));
viewCtrl && viewCtrl._setFooter(this);
}
}
@@ -82,19 +87,17 @@ export class Footer {
* @private
*/
export class ToolbarBase extends Ion {
itemRefs: ElementRef[] = [];
titleRef: any = null;
titleCmp: any;
private _title: ToolbarTitle;
constructor(elementRef: ElementRef) {
super(elementRef);
constructor(config: Config, elementRef: ElementRef, renderer: Renderer) {
super(config, elementRef, renderer);
}
/**
* @private
*/
setTitleCmp(titleCmp: any) {
this.titleCmp = titleCmp;
_setTitle(titleCmp: ToolbarTitle) {
this._title = titleCmp;
}
/**
@@ -102,31 +105,7 @@ export class ToolbarBase extends Ion {
* Returns the toolbar title text if it exists or an empty string
*/
getTitleText() {
return (this.titleCmp && this.titleCmp.getTitleText()) || '';
}
/**
* @private
*/
getTitleRef() {
return this.titleCmp && this.titleCmp.elementRef;
}
/**
* @private
* A toolbar items include the left and right side `ion-buttons`,
* and every `menu-toggle`. It does not include the `ion-title`.
* @returns {TODO} Array of this toolbar's item ElementRefs.
*/
getItemRefs() {
return this.itemRefs;
}
/**
* @private
*/
addItemRef(itemElementRef: ElementRef) {
this.itemRefs.push(itemElementRef);
return (this._title && this._title.getTitleText()) || '';
}
}
@@ -267,15 +246,14 @@ export class ToolbarBase extends Ion {
*/
@Component({
selector: 'ion-toolbar',
template: `
<div class="toolbar-background"></div>
<ng-content select="[menuToggle],ion-buttons[left]"></ng-content>
<ng-content select="ion-buttons[start]"></ng-content>
<ng-content select="ion-buttons[end],ion-buttons[right]"></ng-content>
<div class="toolbar-content">
<ng-content></ng-content>
</div>
`,
template:
'<div class="toolbar-background" [ngClass]="\'toolbar-background-\' + _mode"></div>' +
'<ng-content select="[menuToggle],ion-buttons[left]"></ng-content>' +
'<ng-content select="ion-buttons[start]"></ng-content>' +
'<ng-content select="ion-buttons[end],ion-buttons[right]"></ng-content>' +
'<div class="toolbar-content" [ngClass]="\'toolbar-content-\' + _mode">' +
'<ng-content></ng-content>' +
'</div>',
host: {
'class': 'toolbar',
'[class.statusbar-padding]': '_sbPadding'
@@ -283,59 +261,35 @@ export class ToolbarBase extends Ion {
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class Toolbar extends ToolbarBase {
private _sbPadding: boolean;
/** @internal */
_color: string;
/** @private */
_sbPadding: boolean;
/**
* @input {string} The predefined color to use. For example: `"primary"`, `"secondary"`, `"danger"`.
*/
@Input()
get color(): string {
return this._color;
set color(val: string) {
this._setColor('toolbar', val);
}
set color(value: string) {
this._updateColor(value);
/**
* @input {string} The mode to apply to this component.
*/
@Input()
set mode(val: string) {
this._setMode('toolbar', val);
}
constructor(
@Optional() viewCtrl: ViewController,
@Optional() header: Header,
@Optional() footer: Footer,
config: Config,
private _elementRef: ElementRef,
private _renderer: Renderer
elementRef: ElementRef,
renderer: Renderer
) {
super(_elementRef);
if (viewCtrl && (header || footer)) {
// only toolbars within headers and footer are view toolbars
// toolbars within the content are not view toolbars, since they
// are apart of the content, and could be anywhere within the content
viewCtrl.setToolbarRef(_elementRef);
}
super(config, elementRef, renderer);
this.mode = config.get('mode');
this._sbPadding = config.getBoolean('statusbarPadding');
}
/**
* @internal
*/
_updateColor(newColor: string) {
this._setElementColor(this._color, false);
this._setElementColor(newColor, true);
this._color = newColor;
}
/**
* @internal
*/
_setElementColor(color: string, isAdd: boolean) {
if (color !== null && color !== '') {
this._renderer.setElementClass(this._elementRef.nativeElement, `toolbar-${color}`, isAdd);
}
}
}