Merge branch 'master' into overlay-refactor

This commit is contained in:
Adam Bradley
2015-12-30 15:03:17 -06:00
17 changed files with 311 additions and 69 deletions

View File

@@ -772,8 +772,6 @@ function inlineStyle(ele, effect) {
if (transforms.length) {
transforms.push('translateZ(0px)');
ele.style[CSS.transform] = transforms.join(' ');
} else {
ele.style[CSS.transform] = 'translateZ(0px)';
}
}
}

View File

@@ -70,7 +70,6 @@ ion-searchbar {
border-radius: 5px;
color: $searchbar-ios-input-text-color;
background-color: $searchbar-ios-input-background-color;
background-position: 8px center;
@include placeholder($searchbar-ios-input-placeholder-color);

View File

@@ -46,13 +46,16 @@ export class SearchbarInput {
* @property {boolean} [hideCancelButton=false] - Hides the cancel button
* @property {string} [placeholder=Search] - Sets input placeholder to the value passed in
*
* @property {Any} [input] - Expression to evaluate when the Searchbar input has changed
* @property {Any} [input] - Expression to evaluate when the Searchbar input has changed including cleared
* @property {Any} [keydown] - Expression to evaluate when a key is pushed down in the Searchbar input
* @property {Any} [keypress] - Expression to evaluate when a character is inserted in the Searchbar input
* @property {Any} [keyup] - Expression to evaluate when a key is released in the Searchbar input
* @property {Any} [blur] - Expression to evaluate when the Searchbar input has blurred
* @property {Any} [focus] - Expression to evaluate when the Searchbar input has focused
* @property {Any} [cancel] - Expression to evaluate when the cancel button is clicked.
* @property {Any} [clear] - Expression to evaluate when the clear input button is clicked.
* @property {Any} [cancel] - Expression to evaluate when the cancel button is clicked
* @property {Any} [clear] - Expression to evaluate when the clear input button is clicked
*
* @see {@link /docs/v2/components#search Search Component Docs}
* @see {@link /docs/v2/components#searchbar Searchbar Component Docs}
*/
@Component({
selector: 'ion-searchbar',
@@ -71,35 +74,50 @@ export class SearchbarInput {
export class Searchbar extends Ion {
@ViewChild(SearchbarInput) searchbarInput;
/**
* @private
*/
@Input() cancelButtonText: string;
/**
* @private
*/
@Input() hideCancelButton: any;
/**
* @private
*/
@Input() placeholder: string;
/**
* @private
*/
@Input() ngModel: any;
/**
* @private
*/
@Output() input: EventEmitter<Searchbar> = new EventEmitter();
/**
* @private
*/
@Output() blur: EventEmitter<Searchbar> = new EventEmitter();
/**
* @private
*/
@Output() focus: EventEmitter<Searchbar> = new EventEmitter();
/**
* @private
*/
@Output() cancel: EventEmitter<Searchbar> = new EventEmitter();
/**
* @private
*/
@Output() clear: EventEmitter<Searchbar> = new EventEmitter();
value: string = '';
blurInput: boolean = true;
@HostBinding('class.searchbar-focused') isFocused;
@HostBinding('class.searchbar-left-aligned') shouldLeftAlign;
@HostListener('keyup', ['$event'])
/**
* @private
* Update the Searchbar input value when the input changes
*/
private inputChanged(ev) {
this.value = ev.target.value;
this.onChange(this.value);
this.input.emit(this);
}
constructor(
elementRef: ElementRef,
config: Config,
@@ -130,6 +148,11 @@ export class Searchbar extends Ion {
this.onChange(this.value);
this.shouldLeftAlign = this.value && this.value.trim() != '';
// Using querySelector instead of searchbarInput because at this point it doesn't exist
this.inputElement = this.elementRef.nativeElement.querySelector('.searchbar-input');
this.searchIconElement = this.elementRef.nativeElement.querySelector('.searchbar-search-icon');
this.setElementLeft();
}
/**
@@ -146,6 +169,54 @@ export class Searchbar extends Ion {
}
}
/**
* @private
* Determines whether or not to add style to the element
* to center it properly
*/
setElementLeft() {
if (this.shouldLeftAlign) {
this.inputElement.removeAttribute("style");
this.searchIconElement.removeAttribute("style");
} else {
this.addElementLeft();
}
}
/**
* @private
* Calculates the amount of padding/margin left for the elements
* in order to center them based on the placeholder width
*/
addElementLeft() {
// Create a dummy span to get the placeholder width
let tempSpan = document.createElement('span');
tempSpan.innerHTML = this.placeholder;
document.body.appendChild(tempSpan);
// Get the width of the span then remove it
let textWidth = tempSpan.offsetWidth;
tempSpan.remove();
// Set the input padding left
let inputLeft = "calc(50% - " + (textWidth / 2) + "px)";
this.inputElement.style.paddingLeft = inputLeft;
// Set the icon margin left
let iconLeft = "calc(50% - " + ((textWidth / 2) + this.searchIconElement.offsetWidth + 15) + "px)";
this.searchIconElement.style.marginLeft = iconLeft;
}
/**
* @private
* Update the Searchbar input value when the input changes
*/
inputChanged(ev) {
this.value = ev.target.value;
this.onChange(this.value);
this.input.emit(this);
}
/**
* @private
* Sets the Searchbar to focused and aligned left on input focus.
@@ -155,6 +226,7 @@ export class Searchbar extends Ion {
this.isFocused = true;
this.shouldLeftAlign = true;
this.setElementLeft();
}
/**
@@ -171,9 +243,10 @@ export class Searchbar extends Ion {
return;
}
this.blur.emit(this);
this.isFocused = false;
this.shouldLeftAlign = this.value && this.value.trim() != '';
this.setElementLeft();
}
/**
@@ -185,6 +258,8 @@ export class Searchbar extends Ion {
this.value = '';
this.onChange(this.value);
this.input.emit(this);
this.blurInput = false;
}

View File

@@ -17,22 +17,22 @@ class E2EApp {
}
onClearSearchbar(searchbar) {
console.log("Clicked clear input on", searchbar);
console.log("Clicked clear input on", searchbar.value);
}
onCancelSearchbar(searchbar) {
console.log("Clicked cancel button with", searchbar);
console.log("Clicked cancel button with", searchbar.value);
}
triggerInput(searchbar) {
console.log("Triggered input", searchbar);
console.log("Triggered input", searchbar.value);
}
inputBlurred(searchbar) {
console.log("Blurred input", searchbar);
console.log("Blurred input", searchbar.value);
}
inputFocused(searchbar) {
console.log("Focused input", searchbar);
console.log("Focused input", searchbar.value);
}
}

View File

@@ -55,7 +55,7 @@ import {isDefined} from '../../util/util';
})
export class SegmentButton {
@Input() value: string;
@Output() select: EventEmitter<any> = new EventEmitter();
@Output() select: EventEmitter<SegmentButton> = new EventEmitter();
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
@@ -66,7 +66,7 @@ export class SegmentButton {
@HostListener('click', ['$event'])
private onClick(ev) {
console.debug('SegmentButton, select', this.value);
this.select.emit(ev, this.value);
this.select.emit(this);
}
ngOnInit() {
@@ -131,8 +131,8 @@ export class SegmentButton {
selector: 'ion-segment'
})
export class Segment {
@Output() change: EventEmitter<any> = new EventEmitter();
@ContentChildren(SegmentButton) _buttons;
@Output() change: EventEmitter<SegmentButton> = new EventEmitter();
value: any;
constructor(
@@ -166,10 +166,10 @@ export class Segment {
ngAfterViewInit() {
let buttons = this._buttons.toArray();
for (let button of buttons) {
button.select.subscribe(() => {
this.writeValue(button.value);
this.onChange(button.value);
this.change.emit(this.value);
button.select.subscribe((selectedButton) => {
this.writeValue(selectedButton.value);
this.onChange(selectedButton.value);
this.change.emit(selectedButton);
});
if (isDefined(this.value)) {

View File

@@ -20,12 +20,12 @@ class MyApp {
this.appType = 'free';
}
onSegmentChanged(value) {
console.log("Segment changed to", value);
onSegmentChanged(segmentButton) {
console.log("Segment changed to", segmentButton.value);
}
onSegmentClicked(value) {
console.log("Segment clicked", value);
onSegmentSelected(segmentButton) {
console.log("Segment selected", segmentButton.value);
}
doSubmit(event) {

View File

@@ -1,9 +1,9 @@
<ion-toolbar>
<ion-segment id="segment" [(ngModel)]="relationship" (change)="onSegmentChanged($event)">
<ion-segment-button value="friends" (click)="onSegmentClicked('friends')" class="e2eSegmentFriends">
<ion-segment-button value="friends" (select)="onSegmentSelected($event)" class="e2eSegmentFriends">
Friends
</ion-segment-button>
<ion-segment-button value="enemies" (click)="onSegmentClicked('enemies')">
<ion-segment-button value="enemies" (select)="onSegmentSelected($event)">
Enemies
</ion-segment-button>
</ion-segment>

View File

@@ -7,22 +7,22 @@
<form [ngFormModel]="loginForm" #mf="ngForm" novalidate>
<ion-list>
<ion-input floating-label>
<ion-input floating-label clearInput>
<ion-label>Email</ion-label>
<input [(ngModel)]="login.email" ngControl="email" type="email" required>
</ion-input>
<ion-input floating-label>
<ion-input floating-label clearInput>
<ion-label>Username</ion-label>
<input [(ngModel)]="login.username" ngControl="username" type="text">
</ion-input>
<ion-input floating-label>
<ion-input floating-label clearInput>
<ion-label>Password</ion-label>
<input [(ngModel)]="login.password" ngControl="password" type="password" required>
</ion-input>
<ion-input floating-label>
<ion-input floating-label clearInput>
<ion-label>Comments</ion-label>
<textarea [(ngModel)]="login.comments" ngControl="comments" required>Comment value</textarea>
</ion-input>
@@ -43,15 +43,15 @@
<form (ngSubmit)="submit($event, user)" #lf="ngForm">
<ion-list>
<ion-input floating-label>
<ion-input floating-label clearInput>
<ion-label>Username</ion-label>
<input type="text" [(ngModel)]="user.username" ngControl="username" required>
</ion-input>
<ion-input floating-label>
<ion-input floating-label clearInput>
<ion-label>Password</ion-label>
<input type="password" [(ngModel)]="user.password" ngControl="password" required>
</ion-input>
<div padding-left padding-right>
<div padding-left padding-right clearInput>
<button block type="submit">Login</button>
</div>
<div padding-left>
@@ -63,4 +63,26 @@
</ion-list>
</form>
<ion-list>
<ion-input clearInput>
<ion-label>Email</ion-label>
<input type="email" required>
</ion-input>
<ion-input clearInput>
<ion-label>Username</ion-label>
<input type="text">
</ion-input>
<ion-input clearInput>
<ion-label>Password</ion-label>
<input type="password" required>
</ion-input>
<ion-input clearInput>
<ion-label>Comments</ion-label>
<textarea required>Comment value</textarea>
</ion-input>
</ion-list>
</ion-content>

View File

@@ -4,7 +4,12 @@
// iOS Text Input
// --------------------------------------------------
$text-input-ios-background-color: $list-ios-background-color !default;
$text-input-ios-background-color: $list-ios-background-color !default;
$text-input-ios-input-clear-icon-width: 30px !default;
$text-input-ios-input-clear-icon-color: rgba(0, 0, 0, 0.5) !default;
$text-input-ios-input-clear-icon-svg: "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'><path fill='" + $text-input-ios-input-clear-icon-color + "' d='M403.1,108.9c-81.2-81.2-212.9-81.2-294.2,0s-81.2,212.9,0,294.2c81.2,81.2,212.9,81.2,294.2,0S484.3,190.1,403.1,108.9z M352,340.2L340.2,352l-84.4-84.2l-84,83.8L160,339.8l84-83.8l-84-83.8l11.8-11.8l84,83.8l84.4-84.2l11.8,11.8L267.6,256L352,340.2z'/></svg>" !default;
$text-input-ios-input-clear-icon-size: 18px !default;
// Default Input
@@ -34,3 +39,23 @@ $text-input-ios-background-color: $list-ios-background-color !default;
margin-top: 8px;
margin-bottom: 8px;
}
// Clear Input Icon
// --------------------------------------------------
ion-input[clearInput] {
position: relative;
.text-input {
padding-right: $text-input-ios-input-clear-icon-width;
}
}
.text-input-clear-icon {
width: $text-input-ios-input-clear-icon-width;
@include svg-background-image($text-input-ios-input-clear-icon-svg);
background-size: $text-input-ios-input-clear-icon-size;
right: ($item-ios-padding-right / 2);
bottom: 0;
}

View File

@@ -9,6 +9,12 @@ $text-input-md-highlight-color: map-get($colors-md, primary) !defaul
$text-input-md-hightlight-color-valid: map-get($colors-md, secondary) !default;
$text-input-md-hightlight-color-invalid: map-get($colors-md, danger) !default;
$text-input-md-input-clear-icon-width: 30px !default;
$text-input-md-input-clear-icon-color: #5B5B5B !default;
$text-input-md-input-clear-icon-svg: "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'><polygon fill='" + $text-input-md-input-clear-icon-color + "' points='405,136.798 375.202,107 256,226.202 136.798,107 107,136.798 226.202,256 107,375.202 136.798,405 256,285.798 375.202,405 405,375.202 285.798,256'/></svg>" !default;
$text-input-md-input-clear-icon-size: 22px !default;
// Default Input
// --------------------------------------------------
@@ -65,3 +71,24 @@ ion-input.ng-invalid.ng-touched:after {
margin-top: 8px;
margin-bottom: 8px;
}
// Clear Input Icon
// --------------------------------------------------
ion-input[clearInput] {
position: relative;
.text-input {
padding-right: $text-input-md-input-clear-icon-width;
}
}
.text-input-clear-icon {
width: $text-input-md-input-clear-icon-width;
@include svg-background-image($text-input-md-input-clear-icon-svg);
background-size: $text-input-md-input-clear-icon-size;
right: ($item-md-padding-right / 2);
bottom: 2px;
}

View File

@@ -61,3 +61,15 @@ input,
textarea {
@include placeholder();
}
// Clear Input Icon
// --------------------------------------------------
.text-input-clear-icon {
margin: 0;
padding: 0;
background-repeat: no-repeat;
background-position: center;
position: absolute;
}

View File

@@ -1,24 +1,30 @@
import {Component, Directive, Attribute, forwardRef, Host, Optional, ElementRef, Renderer} from 'angular2/core';
import {Component, Directive, Attribute, forwardRef, Host, Optional, ElementRef, Renderer, Input, ContentChild} from 'angular2/core';
import {NgIf} from 'angular2/common';
import {NavController} from '../nav/nav-controller';
import {Config} from '../../config/config';
import {Form} from '../../util/form';
import {Label} from './label';
import {Label} from '../label/label';
import {IonicApp} from '../app/app';
import {Content} from '../content/content';
import * as dom from '../../util/dom';
import {Platform} from '../../platform/platform';
import {Button} from '../button/button';
/**
* @name Input
* @module ionic
* @description
* `ionInput` is a generic wrapper for both inputs and textareas. You can give `ion-input` to tell it how to handle a chile `ion-label` component
* @property [fixed-labels] - a persistant label that sits next the the input
* @property [floating-labels] - a label that will float about the input if the input is empty of looses focus
* @property [stacked-labels] - A stacked label will always appear on top of the input
*
* `ion-input` is a generic wrapper for both inputs and textareas. You can give `ion-input` attributes to tell it how to handle a child `ion-label` component.
*
* @property [fixed-label] - a persistant label that sits next the the input
* @property [floating-label] - a label that will float about the input if the input is empty of looses focus
* @property [stacked-label] - A stacked label will always appear on top of the input
* @property [inset] - The input will be inset
* @property [clearInput] - A clear icon will appear in the input which clears it
*
* @usage
* ```html
* <ion-input>
@@ -26,7 +32,7 @@ import {Platform} from '../../platform/platform';
* <input type="text" value="">
* </ion-input>
*
* <ion-input>
* <ion-input clearInput>
* <input type="text" placeholder="Username">
* </ion-input>
*
@@ -61,10 +67,19 @@ import {Platform} from '../../platform/platform';
'<div class="item-inner">' +
'<ng-content></ng-content>' +
'<input [type]="type" aria-hidden="true" scroll-assist *ngIf="scrollAssist">' +
'<button clear *ngIf="clearInput && value" class="text-input-clear-icon" (click)="clearTextInput()" (mousedown)="clearTextInput()"></button>' +
'</div>',
directives: [NgIf, forwardRef(() => InputScrollAssist)]
directives: [NgIf, forwardRef(() => InputScrollAssist), forwardRef(() => TextInputElement), Button]
})
export class TextInput {
@ContentChild(forwardRef(() => TextInputElement)) textInputElement;
/**
* @private
*/
@Input() clearInput: any;
value: any = '';
constructor(
form: Form,
@@ -130,12 +145,23 @@ export class TextInput {
this.label = label;
}
/**
* @private
* On Initialization check for attributes
*/
ngOnInit() {
let clearInput = this.clearInput;
if (typeof clearInput === 'string') {
this.clearInput = (clearInput === '' || clearInput === 'true');
}
}
/**
* @private
*/
ngAfterViewInit() {
if (this.input && this.label) {
// if there is an input and an label
// if there is an input and a label
// then give the label an ID
// and tell the input the ID of who it's labelled by
this.input.labelledBy(this.label.id);
@@ -160,6 +186,14 @@ export class TextInput {
};
}
/**
* @private
*/
clearTextInput() {
console.log("Should clear input");
console.log(this.textInputElement.value);
}
/**
* @private
*/
@@ -398,6 +432,7 @@ export class TextInput {
*/
hasValue(inputValue) {
this.renderer.setElementClass(this.elementRef, 'input-has-value', inputValue && inputValue !== '');
this.value = inputValue;
}
/**
@@ -470,7 +505,7 @@ export class TextInputElement {
@Attribute('type') type: string,
elementRef: ElementRef,
renderer: Renderer,
@Optional() wrapper: TextInput,
@Optional() wrapper: TextInput
) {
this.type = type;
this.elementRef = elementRef;

View File

@@ -90,7 +90,7 @@ import {isObject, isDefined, isFunction, isArray, extend} from '../util/util';
* | pageTransitionDelay | 16 | 120 |
* | popupEnter | popup-pop-in | popup-md-pop-in |
* | popupLeave | popup-pop-out | popup-md-pop-out |
* | tabbarPlacement | bottom | true |
* | tabbarPlacement | bottom | top |
* | tabbarHighlight | | top |
* | tabSubPage | | true |
*