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 @@ - + + + + + + + + + + + + \ 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',