diff --git a/ionic/components/picker/picker.ios.scss b/ionic/components/picker/picker.ios.scss
index b8b68cef65..6e58e3811e 100644
--- a/ionic/components/picker/picker.ios.scss
+++ b/ionic/components/picker/picker.ios.scss
@@ -5,45 +5,59 @@
// --------------------------------------------------
$picker-ios-height: 260px !default;
-$picker-ios-background-color: #cfd5da !default;
+$picker-ios-border-color: $list-ios-border-color !default;
+$picker-ios-background-color: $list-ios-background-color !default;
$picker-ios-toolbar-height: 44px !default;
-$picker-ios-toolbar-background-color: #f7f7f8 !default;
+$picker-ios-toolbar-background-color: $picker-ios-background-color !default;
$picker-ios-button-height: $picker-ios-toolbar-height !default;
-$picker-ios-button-text-color: color($colors-ios, primary) !default;
+$picker-ios-button-text-color: $link-ios-color !default;
$picker-ios-button-background-color: transparent !default;
-$picker-ios-option-offset-y: 90px !default;
-$picker-ios-option-font-size: 18px !default;
-$picker-ios-option-line-height: 24px !default;
+$picker-ios-column-padding: 0 12px !default;
+
+$picker-ios-option-padding: 0 10px !default;
+$picker-ios-option-text-color: $list-ios-text-color !default;
+$picker-ios-option-font-size: 22px !default;
+$picker-ios-option-height: 42px !default;
+$picker-ios-option-offset-y: (($picker-ios-height - $picker-ios-toolbar-height) / 2) - ($picker-ios-option-height / 2) - 10 !default;
+
+$picker-highlight-opacity: .8 !default;
.picker-wrapper {
height: $picker-ios-height;
- border-top: 1px solid #929499;
+ border-top: 1px solid $picker-ios-border-color;
background: $picker-ios-background-color;
}
-.hairlines .picker-wrapper {
- border-width: $hairlines-width;
-}
-
.picker-toolbar {
display: flex;
height: $picker-ios-toolbar-height;
+
+ border-bottom: 1px solid $picker-ios-border-color;
+
background: $picker-ios-toolbar-background-color;
}
+.hairlines .picker-wrapper,
+.hairlines .picker-toolbar {
+ border-width: $hairlines-width;
+}
+
.picker-toolbar-button {
flex: 1;
+
text-align: right;
}
.picker-toolbar-cancel {
+ font-weight: normal;
+
text-align: left;
}
@@ -57,23 +71,85 @@ $picker-ios-option-line-height: 24px !default;
background: $picker-ios-button-background-color;
}
-.picker-offset {
- transform: translateY($picker-ios-option-offset-y);
+.picker-columns {
+ height: $picker-ios-height - $picker-ios-toolbar-height;
+
+ perspective: 1800px;
}
-.picker-column {
- padding: 0 10px;
+.picker-col {
+ padding: $picker-ios-column-padding;
+
+ transform-style: preserve-3d;
}
.picker-prefix,
.picker-suffix,
-.picker-options {
- padding: 0 8px;
+.picker-opts {
+ top: $picker-ios-option-offset-y;
+
font-size: $picker-ios-option-font-size;
- line-height: $picker-ios-option-line-height;
+ line-height: $picker-ios-option-height;
+ color: $picker-ios-option-text-color;
+
+ transform-style: preserve-3d;
+
+ pointer-events: none;
}
-.picker-prefix,
-.picker-suffix {
- padding: 0
+.picker-opt {
+ margin: 0;
+ padding: $picker-ios-option-padding;
+
+ width: calc(100% - 24px);
+
+ font-size: $picker-ios-option-font-size;
+ line-height: $picker-ios-option-height;
+
+ background: transparent;
+ transform-origin: center center;
+ transform-style: preserve-3d;
+ transition-timing-function: ease-out;
+
+ backface-visibility: hidden;
+
+ pointer-events: auto;
+}
+
+.picker-above-highlight {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 10;
+ display: block;
+
+ width: 100%;
+ height: $picker-ios-option-offset-y + 4px;
+
+ border-bottom: 1px solid $picker-ios-border-color;
+
+ background: linear-gradient(to bottom,
+ rgba($picker-ios-background-color, 1) 20%,
+ rgba($picker-ios-background-color, .7) 100%);
+
+ transform: translate3d(0, 0, 90px);
+}
+
+.picker-below-highlight {
+ position: absolute;
+ top: $picker-ios-option-offset-y + $picker-ios-option-height - 4;
+ left: 0;
+ z-index: 11;
+ display: block;
+
+ width: 100%;
+ height: $picker-ios-option-offset-y + $picker-ios-option-height;
+
+ border-top: 1px solid $picker-ios-border-color;
+
+ background: linear-gradient(to top,
+ rgba($picker-ios-background-color, 1) 30%,
+ rgba($picker-ios-background-color, .7) 100%);
+
+ transform: translate3d(0, 0, 90px);
}
diff --git a/ionic/components/picker/picker.md.scss b/ionic/components/picker/picker.md.scss
index 6e999c22ce..825c72d3ca 100644
--- a/ionic/components/picker/picker.md.scss
+++ b/ionic/components/picker/picker.md.scss
@@ -3,3 +3,159 @@
// Material Design Picker
// --------------------------------------------------
+
+$picker-md-height: 260px !default;
+$picker-md-border-color: $list-md-border-color !default;
+$picker-md-background-color: $list-md-background-color !default;
+
+$picker-md-toolbar-height: 44px !default;
+$picker-md-toolbar-background-color: $picker-md-background-color !default;
+
+$picker-md-button-height: $picker-md-toolbar-height !default;
+$picker-md-button-text-color: $link-md-color !default;
+$picker-md-button-background-color: transparent !default;
+
+$picker-md-column-padding: 0 12px !default;
+
+$picker-md-option-padding: 0 10px !default;
+$picker-md-option-text-color: $list-md-text-color !default;
+$picker-md-option-font-size: 18px !default;
+$picker-md-option-height: 42px !default;
+$picker-md-option-offset-y: (($picker-md-height - $picker-md-toolbar-height) / 2) - ($picker-md-option-height / 2) - 10 !default;
+
+$picker-md-option-selected-font-size: 24px !default;
+$picker-md-option-selected-color: $link-md-color !default;
+
+$picker-highlight-opacity: .8 !default;
+
+
+.picker-wrapper {
+ height: $picker-md-height;
+
+ border-top: 1px solid $picker-md-border-color;
+
+ background: $picker-md-background-color;
+}
+
+.picker-toolbar {
+ display: flex;
+
+ justify-content: flex-end;
+
+ height: $picker-md-toolbar-height;
+
+ background: $picker-md-toolbar-background-color;
+}
+
+.hairlines .picker-wrapper,
+.hairlines .picker-toolbar {
+ border-width: $hairlines-width;
+}
+
+.picker-button,
+.picker-button.activated {
+ margin: 0;
+
+ height: $picker-md-button-height;
+
+ color: $picker-md-button-text-color;
+ background: $picker-md-button-background-color;
+
+ box-shadow: none;
+}
+
+.picker-columns {
+ height: $picker-md-height - $picker-md-toolbar-height;
+
+ perspective: 1800px;
+}
+
+.picker-col {
+ padding: $picker-md-column-padding;
+
+ transform-style: preserve-3d;
+}
+
+.picker-prefix,
+.picker-suffix,
+.picker-opts {
+ top: $picker-md-option-offset-y;
+
+ font-size: $picker-md-option-font-size;
+ line-height: $picker-md-option-height;
+ color: $picker-md-option-text-color;
+
+ transform-style: preserve-3d;
+
+ pointer-events: none;
+}
+
+.picker-opts ion-button-effect {
+ display: none;
+}
+
+.picker-opt {
+ margin: 0;
+ padding: $picker-md-option-padding;
+
+ width: calc(100% - 24px);
+
+ font-size: $picker-md-option-font-size;
+ line-height: $picker-md-option-height;
+
+ background: transparent;
+
+ transition-timing-function: ease-out;
+
+ backface-visibility: hidden;
+
+ pointer-events: auto;
+}
+
+.picker-opt .button-inner {
+ transition: 200ms;
+}
+
+.picker-prefix,
+.picker-suffix,
+.picker-opt-selected {
+ font-size: $picker-md-option-selected-font-size;
+
+ color: $picker-md-option-selected-color;
+}
+
+.picker-above-highlight {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 10;
+
+ width: 100%;
+ height: $picker-md-option-offset-y + 4px;
+
+ border-bottom: 1px solid $picker-md-border-color;
+
+ background: linear-gradient(to bottom,
+ rgba($picker-md-background-color, 1) 20%,
+ rgba($picker-md-background-color, .7) 100%);
+
+ transform: translate3d(0, 0, 90px);
+}
+
+.picker-below-highlight {
+ position: absolute;
+ top: $picker-md-option-offset-y + $picker-md-option-height - 4;
+ left: 0;
+ z-index: 11;
+
+ width: 100%;
+ height: $picker-md-option-offset-y + $picker-md-option-height;
+
+ border-top: 1px solid $picker-md-border-color;
+
+ background: linear-gradient(to top,
+ rgba($picker-md-background-color, 1) 30%,
+ rgba($picker-md-background-color, .7) 100%);
+
+ transform: translate3d(0, 0, 90px);
+}
diff --git a/ionic/components/picker/picker.scss b/ionic/components/picker/picker.scss
index 910b3af9b1..792f3698fe 100644
--- a/ionic/components/picker/picker.scss
+++ b/ionic/components/picker/picker.scss
@@ -29,9 +29,11 @@ ion-picker-cmp {
left: 0;
z-index: $z-index-overlay-wrapper;
display: flex;
- flex-direction: column;
+
overflow: hidden;
+ flex-direction: column;
+
margin: auto;
width: $picker-width;
@@ -41,32 +43,79 @@ ion-picker-cmp {
}
.picker-columns {
+ position: relative;
display: flex;
- flex: 1;
+
overflow: hidden;
+
+ justify-content: center;
}
-.picker-offset {
+.picker-col {
+ position: relative;
display: flex;
+
+ max-height: 100%;
}
-.picker-column {
- flex: 1;
+.picker-opts {
+ position: relative;
+
+ width: 100%;
+ min-width: 50px;
+ max-width: 100%;
}
.picker-prefix {
+ position: relative;
+
flex: 1;
+
+ min-width: 50px;
+ min-width: 45%;
+
text-align: right;
+ white-space: nowrap;
}
.picker-suffix {
+ position: relative;
+
flex: 1;
+
+ min-width: 50px;
+ min-width: 45%;
+
text-align: left;
+ white-space: nowrap;
}
-.picker-option {
- flex: 1;
+.picker-opt {
+ position: absolute;
+ top: 0;
+ left: 0;
overflow: hidden;
- white-space: nowrap;
+
+ flex: 1;
+
+ width: 100%;
+
+ text-align: center;
text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.picker-opts-left .button-inner {
+ justify-content: flex-start;
+}
+
+.picker-opts-right .button-inner {
+ justify-content: flex-end;
+}
+
+.picker-above-highlight,
+.picker-below-highlight {
+ display: none;
+
+ pointer-events: none;
}
diff --git a/ionic/components/picker/picker.ts b/ionic/components/picker/picker.ts
index 92f1350e38..0c0f2c9758 100644
--- a/ionic/components/picker/picker.ts
+++ b/ionic/components/picker/picker.ts
@@ -1,38 +1,24 @@
-import {Component, ElementRef, Input, ViewChild, Renderer, HostListener, ChangeDetectionStrategy, ViewEncapsulation} from 'angular2/core';
-import {NgClass, NgIf, NgFor} from 'angular2/common';
+import {Component, ElementRef, Input, ViewChild, Renderer, HostListener, ViewEncapsulation} from 'angular2/core';
import {Animation} from '../../animations/animation';
import {Transition, TransitionOptions} from '../../transitions/transition';
import {Config} from '../../config/config';
-import {isPresent} from '../../util/util';
+import {isPresent, isString, isNumber} from '../../util/util';
import {NavParams} from '../nav/nav-params';
import {ViewController} from '../nav/view-controller';
-import {raf, CSS, pointerCoord} from '../../util/dom';
-
+import {nativeRaf, cancelRaf, CSS, pointerCoord} from '../../util/dom';
/**
* @name Picker
* @description
*
- * @usage
- * ```ts
- * constructor(private nav: NavController) {}
- *
- * presentSelector() {
- * let picker = Picker.create({
- *
- * });
- * this.nav.present(picker);
- * }
- *
- * ```
- *
*/
export class Picker extends ViewController {
constructor(opts: PickerOptions = {}) {
opts.columns = opts.columns || [];
+ opts.buttons = opts.buttons || [];
opts.enableBackdropDismiss = isPresent(opts.enableBackdropDismiss) ? !!opts.enableBackdropDismiss : true;
super(PickerDisplayCmp, opts);
@@ -54,6 +40,20 @@ export class Picker extends ViewController {
return this._nav && this._nav.config.get(key);
}
+ /**
+ * @param {any} button Picker toolbar button
+ */
+ addButton(button: any) {
+ this.data.buttons.push(button);
+ }
+
+ /**
+ * @param {any} button Picker toolbar button
+ */
+ addColumn(column: PickerColumn) {
+ this.data.columns.push(column);
+ }
+
/**
* @param {string} cssClass CSS class name to add to the picker's outer wrapper.
*/
@@ -68,95 +68,140 @@ export class Picker extends ViewController {
}
-
/**
* @private
*/
@Component({
- selector: '.picker-column',
+ selector: '.picker-col',
template:
- '
' +
- '
{{col.prefix}}
' +
- '
' +
- '
' +
- '{{o.text}}' +
- '
' +
- '
' +
- '
{{col.suffix}}
' +
- '
',
+ '{{col.prefix}}
' +
+ '' +
+ '' +
+ '
' +
+ '{{col.suffix}}
',
host: {
- '[style.flex]': 'col.flex',
+ '[style.min-width]': 'col.columnWidth',
+ '[class.picker-opts-left]': 'col.align=="left"',
+ '[class.picker-opts-right]': 'col.align=="right"',
'(touchstart)': 'pointerStart($event)',
'(touchmove)': 'pointerMove($event)',
'(touchend)': 'pointerEnd($event)',
'(mousedown)': 'pointerStart($event)',
'(mousemove)': 'pointerMove($event)',
- '(mouseup)': 'pointerEnd($event)',
+ '(body:mouseup)': 'pointerEnd($event)',
+ '(body:mouseout)': 'mouseOut($event)',
}
})
class PickerColumnCmp {
@ViewChild('colEle') colEle: ElementRef;
@Input() col: PickerColumn;
- y: number;
+ y: number = 0;
colHeight: number;
optHeight: number;
velocity: number;
pos: number[] = [];
- scrollingDown: boolean;
msPrv: number = 0;
startY: number = null;
+ rafId: number;
+ bounceFrom: number;
+ maxY: number;
+ rotateFactor: number;
+
+ constructor(config: Config) {
+ this.rotateFactor = config.getNumber('pickerRotateFactor', 0);
+ }
ngAfterViewInit() {
+ // get the scrollable element within the column
let colEle: HTMLElement = this.colEle.nativeElement;
this.colHeight = colEle.clientHeight;
+
+ // get the height of one option
this.optHeight = (colEle.firstElementChild ? colEle.firstElementChild.clientHeight : 0);
- this.setY(0, true);
+ // set the scroll position for the selected option
+ let selectedIndex = this.col.options.indexOf(this.col.selected);
+ this.setSelected(selectedIndex, 0);
}
pointerStart(ev) {
+ console.debug('picker, pointerStart', ev.type, this.startY);
+
if (this.isPrevented(ev)) {
+ // do not both with mouse events if a touch event already fired
return;
}
+ // cancel any previous raf's that haven't fired yet
+ cancelRaf(this.rafId);
+
+ // remember where the pointer started from`
this.startY = pointerCoord(ev).y;
+ // reset everything
this.velocity = 0;
this.pos.length = 0;
this.pos.push(this.startY, Date.now());
-
- console.debug('picker, pointerStart', ev.type, this.startY);
+ this.maxY = (this.optHeight * (this.col.options.length - 1)) * -1;
}
pointerMove(ev) {
+ ev.preventDefault();
+ ev.stopPropagation();
+
if (this.startY !== null) {
if (this.isPrevented(ev)) {
return;
}
- let currentY = pointerCoord(ev).y;
- console.debug('picker, pointerMove', ev.type, currentY);
-
+ var currentY = pointerCoord(ev).y;
this.pos.push(currentY, Date.now());
- this.setY(this.startY + currentY, false);
+
+ // update the scroll position relative to pointer start position
+ var y = this.y + (currentY - this.startY);
+
+ if (y > 0) {
+ // scrolling up higher than scroll area
+ y = Math.pow(y, 0.8);
+ this.bounceFrom = y;
+
+ } else if (y < this.maxY) {
+ // scrolling down below scroll area
+ y = y + Math.pow(this.maxY - y, 0.9);
+ this.bounceFrom = y;
+
+ } else {
+ this.bounceFrom = 0;
+ }
+
+ this.update(y, 0, false);
}
}
pointerEnd(ev) {
- if (this.startY !== null) {
+ if (this.isPrevented(ev)) {
+ return;
+ }
- if (this.isPrevented(ev)) {
- return;
- }
+ this.velocity = 0;
+ if (this.bounceFrom > 0) {
+ // bounce back up
+ this.update(0, 100, true);
+
+ } else if (this.bounceFrom < 0) {
+ // bounce back down
+ this.update(this.maxY, 100, true);
+
+ } else if (this.startY !== null) {
var endY = pointerCoord(ev).y;
console.debug('picker, pointerEnd', ev.type, endY);
this.pos.push(endY, Date.now());
- this.velocity = 0;
- this.scrollingDown = (endY < this.startY);
var endPos = (this.pos.length - 1);
var startPos = endPos;
@@ -176,45 +221,135 @@ class PickerColumnCmp {
this.velocity = ((movedTop / timeOffset) * FRAME_MS);
}
- this.setY(this.startY + endY, true);
+ if (Math.abs(endY - this.startY) > 3) {
+ ev.preventDefault();
+ ev.stopPropagation();
- this.decelerate();
+ var y = this.y + (endY - this.startY);
+ this.update(y, 0, true);
+ }
- this.startY = null;
+ }
+
+ this.startY = null;
+ this.decelerate();
+ }
+
+ mouseOut(ev) {
+ if (ev.target.classList.contains('picker-col')) {
+ this.pointerEnd(ev);
}
}
decelerate() {
- var self = this;
+ var y = 0;
+ cancelRaf(this.rafId);
- if (self.velocity) {
- self.velocity *= DECELERATION_FRICTION;
- console.log(`decelerate velocity ${self.velocity}`);
+ if (isNaN(this.y) || !this.optHeight) {
+ // fallback in case numbers get outta wack
+ this.update(y, 0, true);
- var y = self.y + self.velocity;
- self.setY(y, true);
+ } else if (Math.abs(this.velocity) > 0) {
+ // still decelerating
+ this.velocity *= DECELERATION_FRICTION;
- raf(self.decelerate.bind(self));
+ // do not let it go slower than a velocity of 1
+ this.velocity = (this.velocity > 0 ? Math.max(this.velocity, 1) : Math.min(this.velocity, -1));
- } else if (self.y % this.optHeight !== 0) {
+ y = Math.round(this.y - this.velocity);
- self.y = self.y + (this.scrollingDown ? -1 : 1);
+ if (y > 0) {
+ // whoops, it's trying to scroll up farther than the options we have!
+ y = 0;
+ this.velocity = 0;
- console.log(`lock in ${self.y}`);
+ } else if (y < this.maxY) {
+ // gahh, it's trying to scroll down farther than we can!
+ y = this.maxY;
+ this.velocity = 0;
+ }
- self.setY(self.y, true);
+ console.log(`decelerate y: ${y}, velocity: ${this.velocity}, optHeight: ${this.optHeight}`);
- raf(self.decelerate.bind(self));
+ this.update(y, 0, true);
+
+ if (y % this.optHeight !== 0 || Math.abs(this.velocity) > 1) {
+ // isn't locked in yet, keep decelerating until it is
+ this.rafId = nativeRaf(this.decelerate.bind(this));
+ }
+
+ } else if (this.y % this.optHeight !== 0) {
+ // needs to still get locked into a position so options line up
+ var currentPos = Math.abs(this.y % this.optHeight);
+
+ // create a velocity in the direction it needs to scroll
+ this.velocity = (currentPos > (this.optHeight / 2) ? 1 : -1);
+
+ this.decelerate();
}
}
- setY(yOffset: number, saveY: boolean) {
- let y = yOffset + this.y;
+ optClick(ev, index: number) {
+ if (!this.velocity) {
+ ev.preventDefault();
+ ev.stopPropagation();
- console.log(`y: ${y}, yOffset: ${yOffset}, colHeight: ${this.colHeight}, optHeight: ${this.optHeight}`);
+ this.setSelected(index, 150);
+ }
+ }
- let colEleStyle = this.colEle.nativeElement.style;
- colEleStyle[CSS.transform] = `translate3d(0px,${y}px,0px)`;
+ setSelected(selectedIndex: number, duration: number) {
+ // if there is a selected index, then figure out it's y position
+ // if there isn't a selected index, then just use the top y position
+ let y = (selectedIndex > -1) ? ((selectedIndex * this.optHeight) * -1) : 0;
+
+ cancelRaf(this.rafId);
+ this.velocity = 0;
+
+ // so what y position we're at
+ this.update(y, duration, true);
+ }
+
+ update(y: number, duration: number, saveY: boolean) {
+ // ensure we've got a good round number :)
+ y = Math.round(y);
+
+ let selectedIndex = Math.abs(Math.round(y / this.optHeight));
+
+ this.col.selected = this.col.options[selectedIndex];
+
+ let colEle: HTMLElement = this.colEle.nativeElement;
+ let optElements: any = colEle.querySelectorAll('.picker-opt');
+
+ for (var i = 0; i < optElements.length; i++) {
+ var optEle: HTMLElement = optElements[i];
+ var optTop = (i * this.optHeight);
+ var optOffset = (optTop + y);
+
+ var rotateX = (optOffset * this.rotateFactor);
+ var translateX = 0;
+ var translateY = 0;
+ var translateZ = 0;
+
+ if (this.rotateFactor !== 0) {
+ translateX = 10;
+ translateZ = 90;
+ if (rotateX > 90 || rotateX < -90) {
+ translateX = -9999;
+ rotateX = 0;
+ }
+
+ } else {
+ translateY = optOffset;
+ }
+
+ optEle.style[CSS.transform] = `rotateX(${rotateX}deg) translate3d(${translateX}px,${translateY}px,${translateZ}px)`;
+
+ optEle.style[CSS.transitionDuration] = (duration > 0 ? duration + 'ms' : '');
+
+ optEle.classList[i === selectedIndex ? 'add' : 'remove']('picker-opt-selected');
+
+ }
if (saveY) {
this.y = y;
@@ -223,9 +358,12 @@ class PickerColumnCmp {
isPrevented(ev) {
if (ev.type.indexOf('touch') > -1) {
+ // this is a touch event, so prevent mouse events for a while
this.msPrv = Date.now() + 2000;
} else if (this.msPrv > Date.now() && ev.type.indexOf('mouse') > -1) {
+ // this is a mouse event, and a touch event already happend recently
+ // prevent the calling method from continuing
ev.preventDefault();
ev.stopPropagation();
return true;
@@ -245,21 +383,21 @@ class PickerColumnCmp {
'' +
'
' +
'
' +
- '
' +
+ '
' +
+ '
' +
+ '
' +
'
' +
'
',
host: {
'role': 'dialog'
},
- directives: [NgClass, NgIf, NgFor, PickerColumnCmp],
- changeDetection: ChangeDetectionStrategy.OnPush,
+ directives: [PickerColumnCmp],
encapsulation: ViewEncapsulation.None,
})
class PickerDisplayCmp {
@@ -293,7 +431,7 @@ class PickerDisplayCmp {
let data = this.d;
data.buttons = data.buttons.map(button => {
- if (typeof button === 'string') {
+ if (isString(button)) {
return { text: button };
}
if (button.role) {
@@ -302,10 +440,34 @@ class PickerDisplayCmp {
return button;
});
+ // clean up dat data
data.columns = data.columns.map(column => {
- if (!column.flex) {
- column.flex = 1;
+ if (!isPresent(column.columnWidth)) {
+ column.columnWidth = (100 / data.columns.length) + '%';
}
+ if (!isPresent(column.options)) {
+ column.options = [];
+ }
+
+ column.options = column.options.map(inputOpt => {
+ let opt: PickerColumnOption = {
+ text: '',
+ value: ''
+ };
+
+ if (isPresent(inputOpt)) {
+ if (isString(inputOpt) || isNumber(inputOpt)) {
+ opt.text = inputOpt;
+ opt.value = inputOpt;
+
+ } else {
+ opt.text = isPresent(inputOpt.text) ? inputOpt.text : inputOpt.value;
+ opt.value = isPresent(inputOpt.value) ? inputOpt.value : inputOpt.text;
+ }
+ }
+
+ return opt;
+ });
return column;
});
}
@@ -384,7 +546,7 @@ class PickerDisplayCmp {
// return an object of all the values with the input name as the key
let values = {};
this.d.columns.forEach(col => {
- values[col.name] = col.value;
+ values[col.name] = col.selected ? col.selected.value : null;
});
return values;
}
@@ -404,19 +566,20 @@ export interface PickerOptions {
export interface PickerColumn {
name?: string;
- value?: string;
+ selected?: PickerColumnOption;
prefix?: string;
suffix?: string;
options: PickerColumnOption[];
- flex?: number;
cssClass?: string;
+ columnWidth?: string;
+ prefixWidth?: string;
+ suffixWidth?: string;
+ optionsWidth?: string;
}
export interface PickerColumnOption {
- value?: string;
- text?: string;
- checked?: boolean;
- id?: string;
+ value?: any;
+ text?: any;
}
@@ -458,7 +621,5 @@ Transition.register('picker-slide-out', PickerSlideOut);
let pickerIds = -1;
-const MIN_VELOCITY_START_DECELERATION = 4;
-const MIN_VELOCITY_CONTINUE_DECELERATION = 0.12;
const DECELERATION_FRICTION = 0.97;
const FRAME_MS = (1000 / 60);
diff --git a/ionic/components/picker/picker.wp.scss b/ionic/components/picker/picker.wp.scss
index 466cd79ef2..85fdb18cf4 100644
--- a/ionic/components/picker/picker.wp.scss
+++ b/ionic/components/picker/picker.wp.scss
@@ -3,3 +3,171 @@
// Windows Picker
// --------------------------------------------------
+
+$picker-wp-height: 260px !default;
+$picker-wp-border-color: $list-wp-border-color !default;
+$picker-wp-background-color: $list-wp-background-color !default;
+
+$picker-wp-toolbar-height: 44px !default;
+$picker-wp-toolbar-background-color: $picker-wp-background-color !default;
+
+$picker-wp-button-height: $picker-wp-toolbar-height !default;
+$picker-wp-button-text-color: $link-wp-color !default;
+$picker-wp-button-background-color: transparent !default;
+
+$picker-wp-column-padding: 0 12px !default;
+
+$picker-wp-option-padding: 0 10px !default;
+$picker-wp-option-text-color: $list-wp-text-color !default;
+$picker-wp-option-font-size: 18px !default;
+$picker-wp-option-height: 42px !default;
+$picker-wp-option-offset-y: (($picker-wp-height - $picker-wp-toolbar-height) / 2) - ($picker-wp-option-height / 2) - 10 !default;
+
+$picker-wp-option-selected-font-size: 24px !default;
+$picker-wp-option-selected-color: $link-wp-color !default;
+
+$picker-highlight-opacity: .8 !default;
+
+
+.picker-wrapper {
+ height: $picker-wp-height;
+
+ border-top: 1px solid $picker-wp-border-color;
+
+ background: $picker-wp-background-color;
+}
+
+.picker-toolbar {
+ display: flex;
+
+ justify-content: flex-end;
+
+ height: $picker-wp-toolbar-height;
+
+ background: $picker-wp-toolbar-background-color;
+}
+
+.hairlines .picker-wrapper,
+.hairlines .picker-toolbar {
+ border-width: $hairlines-width;
+}
+
+.picker-toolbar-button {
+ flex: 1;
+
+ text-align: right;
+}
+
+.picker-toolbar-cancel {
+ font-weight: normal;
+
+ text-align: left;
+}
+
+.picker-button,
+.picker-button.activated {
+ margin: 0;
+
+ height: $picker-wp-button-height;
+
+ color: $picker-wp-button-text-color;
+ background: $picker-wp-button-background-color;
+
+ box-shadow: none;
+}
+
+.picker-columns {
+ height: $picker-wp-height - $picker-wp-toolbar-height;
+
+ perspective: 1800px;
+}
+
+.picker-col {
+ padding: $picker-wp-column-padding;
+
+ transform-style: preserve-3d;
+}
+
+.picker-prefix,
+.picker-suffix,
+.picker-opts {
+ top: $picker-wp-option-offset-y;
+
+ font-size: $picker-wp-option-font-size;
+ line-height: $picker-wp-option-height;
+ color: $picker-wp-option-text-color;
+
+ transform-style: preserve-3d;
+
+ pointer-events: none;
+}
+
+.picker-opts ion-button-effect {
+ display: none;
+}
+
+.picker-opt {
+ margin: 0;
+ padding: $picker-wp-option-padding;
+
+ width: calc(100% - 24px);
+
+ font-size: $picker-wp-option-font-size;
+ line-height: $picker-wp-option-height;
+
+ background: transparent;
+
+ transition-timing-function: ease-out;
+
+ backface-visibility: hidden;
+
+ pointer-events: auto;
+}
+
+.picker-opt .button-inner {
+ transition: 200ms;
+}
+
+.picker-prefix,
+.picker-suffix,
+.picker-opt-selected {
+ font-size: $picker-wp-option-selected-font-size;
+
+ color: $picker-wp-option-selected-color;
+}
+
+.picker-above-highlight {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 10;
+
+ width: 100%;
+ height: $picker-wp-option-offset-y + 4px;
+
+ border-bottom: 1px solid $picker-wp-border-color;
+
+ background: linear-gradient(to bottom,
+ rgba($picker-wp-background-color, 1) 20%,
+ rgba($picker-wp-background-color, .7) 100%);
+
+ transform: translate3d(0, 0, 90px);
+}
+
+.picker-below-highlight {
+ position: absolute;
+ top: $picker-wp-option-offset-y + $picker-wp-option-height - 4;
+ left: 0;
+ z-index: 11;
+
+ width: 100%;
+ height: $picker-wp-option-offset-y + $picker-wp-option-height;
+
+ border-top: 1px solid $picker-wp-border-color;
+
+ background: linear-gradient(to top,
+ rgba($picker-wp-background-color, 1) 30%,
+ rgba($picker-wp-background-color, .7) 100%);
+
+ transform: translate3d(0, 0, 90px);
+}
diff --git a/ionic/components/picker/test/basic/index.ts b/ionic/components/picker/test/basic/index.ts
index ac9e51ac1f..1d05985f57 100644
--- a/ionic/components/picker/test/basic/index.ts
+++ b/ionic/components/picker/test/basic/index.ts
@@ -1,68 +1,212 @@
+import {ViewEncapsulation} from 'angular2/core';
import {App, Page, Picker, NavController} from 'ionic-angular';
@Page({
- templateUrl: 'main.html'
+ templateUrl: 'main.html',
+ encapsulation: ViewEncapsulation.None,
})
class E2EPage {
+ smoothie: string;
+ timer: string;
- constructor(private nav: NavController) {
- setTimeout(() => {
- this.presentPicker()
- }, 250);
- }
+ constructor(private nav: NavController) {}
- presentPicker() {
+ twoColumns() {
let picker = Picker.create({
buttons: [
{
text: 'Cancel',
role: 'cancel'
},
- 'Done'
+ {
+ text: 'Done',
+ handler: (data) => {
+ this.smoothie = `${data.flavor1} ${data.flavor2}`;
+ }
+ }
],
columns: [
{
- prefix: 'prefix',
- suffix: 'suffix',
+ name: 'flavor1',
+ align: 'right',
options: [
- { text: 'Jan' },
- { text: 'Feb' },
- { text: 'Mar' },
- { text: 'Apr' },
- { text: 'May' },
- { text: 'Jun' },
- { text: 'Jul' },
- { text: 'Aug' },
- { text: 'Sep' },
- { text: 'Oct' },
- { text: 'Nov' },
- { text: 'Dec' },
+ { text: 'Mango' },
+ { text: 'Banana' },
+ { text: 'Cherry' },
+ { text: 'Strawberry' },
+ { text: 'Raspberry' },
+ { text: 'Blueberry' },
+ { text: 'Peach' },
+ { text: 'Coconut' },
+ { text: 'Pineapple' },
+ { text: 'Honeydew' },
+ { text: 'Watermelon' },
+ { text: 'Grape' },
+ { text: 'Avocado' },
+ { text: 'Kiwi' },
+ { text: 'Orange' },
+ { text: 'Papaya' },
+ ]
+ },
+ {
+ name: 'flavor2',
+ align: 'left',
+ options: [
+ { text: 'Banana' },
+ { text: 'Orange' },
+ { text: 'Grape' },
+ { text: 'Watermelon' },
+ { text: 'Strawberry' },
+ { text: 'Papaya' },
+ { text: 'Kiwi' },
+ { text: 'Cherry' },
+ { text: 'Raspberry' },
+ { text: 'Mango' },
+ { text: 'Pineapple' },
+ { text: 'Peach' },
+ { text: 'Avocado' },
+ { text: 'Honeydew' },
+ { text: 'Blueberry' },
+ { text: 'Coconut' },
]
},
- // {
- // prefix: 'prefix',
- // suffix: 'suffix',
- // options: [
- // { text: 'Jan' },
- // { text: 'Feb' },
- // { text: 'Mar' },
- // { text: 'Apr' },
- // { text: 'May' },
- // { text: 'Jun' },
- // { text: 'Jul' },
- // { text: 'Aug' },
- // { text: 'Sep' },
- // { text: 'Oct' },
- // { text: 'Nov' },
- // { text: 'Dec' },
- // ]
- // },
]
});
this.nav.present(picker);
}
+
+ prefixLabel() {
+ let picker = Picker.create({
+ buttons: [
+ {
+ text: 'Nerp',
+ role: 'cancel'
+ },
+ {
+ text: 'Woot!',
+ handler: (data) => {
+ this.smoothie = `${data.flavor1}`;
+ }
+ }
+ ],
+ columns: [
+ {
+ name: 'flavor1',
+ align: 'left',
+ prefix: 'Flavor',
+ options: [
+ { text: 'Mango' },
+ { text: 'Banana' },
+ { text: 'Cherry' },
+ { text: 'Strawberry' },
+ { text: 'Raspberry' },
+ { text: 'Blueberry' },
+ { text: 'Peach' },
+ { text: 'Coconut' },
+ { text: 'Pineapple' },
+ { text: 'Honeydew' },
+ { text: 'Watermelon' },
+ { text: 'Grape' },
+ { text: 'Avocado' },
+ { text: 'Kiwi' },
+ { text: 'Orange' },
+ { text: 'Papaya' },
+ ]
+ }
+ ]
+ });
+
+ this.nav.present(picker);
+ }
+
+ suffixLabel() {
+ let picker = Picker.create({
+ buttons: [
+ {
+ text: 'No',
+ role: 'cancel'
+ },
+ {
+ text: 'Si',
+ handler: (data) => {
+ this.smoothie = `${data.flavor1}`;
+ }
+ }
+ ],
+ columns: [
+ {
+ name: 'flavor1',
+ align: 'right',
+ suffix: 'flavor',
+ options: [
+ { text: 'Mango' },
+ { text: 'Banana' },
+ { text: 'Cherry' },
+ { text: 'Strawberry' },
+ { text: 'Raspberry' },
+ { text: 'Blueberry' },
+ { text: 'Peach' },
+ { text: 'Coconut' },
+ { text: 'Pineapple' },
+ { text: 'Honeydew' },
+ { text: 'Watermelon' },
+ { text: 'Grape' },
+ { text: 'Avocado' },
+ { text: 'Kiwi' },
+ { text: 'Orange' },
+ { text: 'Papaya' },
+ ]
+ }
+ ]
+ });
+
+ this.nav.present(picker);
+ }
+
+ columnSizes() {
+ let picker = Picker.create();
+
+ picker.addButton({
+ text: 'Cancel',
+ role: 'cancel'
+ });
+
+ picker.addButton({
+ text: 'Set Timer',
+ handler: (data) => {
+ this.timer = `${data.hour}:${data.min}`;
+ }
+ });
+
+ picker.addColumn({
+ name: 'hour',
+ suffix: 'hour',
+ columnWidth: '30%',
+ optionsWidth: '50px',
+ options: Array.apply(null, {length: 23}).map(Number.call, Number)
+ });
+
+ var minuteOptions = [];
+
+ for (var i = 0; i < 60; i++) {
+ minuteOptions.push({
+ text: i,
+ value: ('0' + i).slice(-2)
+ });
+ }
+
+ picker.addColumn({
+ name: 'min',
+ suffix: 'min',
+ columnWidth: '40%',
+ optionsWidth: '80px',
+ options: minuteOptions
+ });
+
+ this.nav.present(picker);
+ }
}
diff --git a/ionic/components/picker/test/basic/main.html b/ionic/components/picker/test/basic/main.html
index 90bd279550..fa80234cb4 100644
--- a/ionic/components/picker/test/basic/main.html
+++ b/ionic/components/picker/test/basic/main.html
@@ -4,6 +4,30 @@
-
+
+
+
+
+
+
+
+
+
+
+ Smoothie: {{ smoothie }}
+ Timer: {{ timer }}
+
+
+
\ No newline at end of file
diff --git a/ionic/config/modes.ts b/ionic/config/modes.ts
index 60142a2ebb..9d6d1dffca 100644
--- a/ionic/config/modes.ts
+++ b/ionic/config/modes.ts
@@ -33,6 +33,7 @@ Config.setModeConfig('ios', {
pickerEnter: 'picker-slide-in',
pickerLeave: 'picker-slide-out',
+ pickerRotateFactor: -0.46,
spinner: 'ios',