From b2d3e0927613d420b6d9a7e1caf83e43c4c6dd75 Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Wed, 25 Oct 2017 12:02:06 -0400 Subject: [PATCH] refactor(picker): get picker working with gesture and add to config --- .../src/components/datetime/datetime-util.ts | 1 + .../core/src/components/datetime/datetime.tsx | 4 +- .../src/components/picker/picker-column.tsx | 455 +++++++++--------- .../core/src/components/picker/picker.tsx | 5 - packages/core/stencil.config.js | 1 + 5 files changed, 231 insertions(+), 235 deletions(-) diff --git a/packages/core/src/components/datetime/datetime-util.ts b/packages/core/src/components/datetime/datetime-util.ts index 07b0e2cb34..9c7af53c1d 100644 --- a/packages/core/src/components/datetime/datetime-util.ts +++ b/packages/core/src/components/datetime/datetime-util.ts @@ -461,6 +461,7 @@ function fourDigit(val: number): string { export interface DateTimeData { + [key: string]: any; year?: number; month?: number; day?: number; diff --git a/packages/core/src/components/datetime/datetime.tsx b/packages/core/src/components/datetime/datetime.tsx index aa2e3cec63..1e1441aab4 100644 --- a/packages/core/src/components/datetime/datetime.tsx +++ b/packages/core/src/components/datetime/datetime.tsx @@ -255,6 +255,8 @@ import { Picker, PickerColumn, PickerController, PickerOptions } from '../../ind } }) export class DateTime { + [key: string]: any; + private datetimeId: string; private labelId: string; private picker: Picker; @@ -484,8 +486,6 @@ export class DateTime { } open() { - console.debug('datetime, open picker'); - const pickerOptions = {...this.pickerOptions}; // TODO check this.isFocus() || this.disabled diff --git a/packages/core/src/components/picker/picker-column.tsx b/packages/core/src/components/picker/picker-column.tsx index 5f6d2125f1..b9656cb011 100644 --- a/packages/core/src/components/picker/picker-column.tsx +++ b/packages/core/src/components/picker/picker-column.tsx @@ -1,8 +1,6 @@ import { Component, Element, Prop } from '@stencil/core'; -import { PickerColumn, PickerColumnOption } from '../../index'; - -import { PICKER_OPT_SELECTED } from './picker'; +import { GestureDetail, PickerColumn, PickerColumnOption } from '../../index'; import { clamp } from '../../utils/helpers'; @@ -13,15 +11,24 @@ import { clamp } from '../../utils/helpers'; } }) export class PickerColumnCmp { - mode: string; + private mode: string; - colHeight: number; - velocity: number; - optHeight: number; - rotateFactor: number; - scaleFactor: number; - y: number = 0; - lastIndex: number; + private bounceFrom: number; + private colHeight: number; + private lastIndex: number; + private lastTempIndex: number; + private minY: number; + private maxY: number; + private optHeight: number; + private pos: number[] = []; + private rotateFactor: number; + private scaleFactor: number; + private startY: number; + private velocity: number; + private y: number = 0; + + private activeBlock: string; + // private decelerateFunc: Function; @Element() el: HTMLElement; @@ -39,7 +46,6 @@ export class PickerColumnCmp { this.rotateFactor = pickerRotateFactor; this.scaleFactor = pickerScaleFactor; // this.decelerateFunc = this.decelerate.bind(this); - // this.debouncer = domCtrl.debouncer(); } protected ionViewDidLoad() { @@ -50,19 +56,17 @@ export class PickerColumnCmp { // get the height of one option this.optHeight = (colEle.firstElementChild ? colEle.firstElementChild.clientHeight : 0); - // TODO Listening for pointer events - // this.events.pointerEvents({ - // element: this.elementRef.nativeElement, - // pointerDown: this.pointerStart.bind(this), - // pointerMove: this.pointerMove.bind(this), - // pointerUp: this.pointerEnd.bind(this), - // capture: true, - // zone: false - // }); + // TODO block goback-swipe and menu-swipe + // this.activeBlock = 'goback-swipe menu-swipe'; this.refresh(); } + protected ionViewDidUnload() { + // TODO block goback-swipe and menu-swipe + // this.activeBlock = 'goback-swipe menu-swipe'; + } + optClick(ev: Event, index: number) { if (!this.velocity) { ev.preventDefault(); @@ -77,7 +81,6 @@ export class PickerColumnCmp { // if there isn't a selected index, then just use the top y position let y = (selectedIndex > -1) ? ((selectedIndex * this.optHeight) * -1) : 0; - // this._plt.cancelRaf(this.rafId); this.velocity = 0; // set what y position we're at @@ -189,62 +192,192 @@ export class PickerColumnCmp { decelerate() { - // let y = 0; + let y = 0; - // if (isNaN(this.y) || !this.optHeight) { - // // fallback in case numbers get outta wack - // this.update(y, 0, true, true); - // this._haptic.gestureSelectionEnd(); + if (isNaN(this.y) || !this.optHeight) { + // fallback in case numbers get outta wack + this.update(y, 0, true, true); - // } else if (Math.abs(this.velocity) > 0) { - // // still decelerating - // this.velocity *= DECELERATION_FRICTION; + } else if (Math.abs(this.velocity) > 0) { + // still decelerating + this.velocity *= DECELERATION_FRICTION; - // // 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); + // 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); - // y = Math.round(this.y - this.velocity); + y = Math.round(this.y - this.velocity); - // if (y > this.minY) { - // // whoops, it's trying to scroll up farther than the options we have! - // y = this.minY; - // this.velocity = 0; + if (y > this.minY) { + // whoops, it's trying to scroll up farther than the options we have! + y = this.minY; + this.velocity = 0; - // } else if (y < this.maxY) { - // // gahh, it's trying to scroll down farther than we can! - // y = this.maxY; - // this.velocity = 0; - // } + } else if (y < this.maxY) { + // gahh, it's trying to scroll down farther than we can! + y = this.maxY; + this.velocity = 0; + } - // var notLockedIn = (y % this.optHeight !== 0 || Math.abs(this.velocity) > 1); + var notLockedIn = (y % this.optHeight !== 0 || Math.abs(this.velocity) > 1); - // this.update(y, 0, true, !notLockedIn); + this.update(y, 0, true, !notLockedIn); + // TODO + // if (notLockedIn) { + // // isn't locked in yet, keep decelerating until it is + // this.rafId = this._plt.raf(this.decelerateFunc); + // } - // if (notLockedIn) { - // // isn't locked in yet, keep decelerating until it is - // this.rafId = this._plt.raf(this.decelerateFunc); - // } + } 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); - // } 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); - // // create a velocity in the direction it needs to scroll - // this.velocity = (currentPos > (this.optHeight / 2) ? 1 : -1); - // this._haptic.gestureSelectionEnd(); + this.decelerate(); + } - // this.decelerate(); - // } + let currentIndex = Math.max(Math.abs(Math.round(y / this.optHeight)), 0); - // let currentIndex = Math.max(Math.abs(Math.round(y / this.optHeight)), 0); + // TODO // if (currentIndex !== this.lastTempIndex) { // // Trigger a haptic event for physical feedback that the index has changed // this._haptic.gestureSelectionChanged(); // } - // this.lastTempIndex = currentIndex; + this.lastTempIndex = currentIndex; + } + + // TODO should this check enabled? + private canStart() { + return true; + } + + onDragStart(detail: GestureDetail): boolean { + console.debug('picker, onDragStart', detail, this.startY); + + // We have to prevent default in order to block scrolling under the picker + // but we DO NOT have to stop propagation, since we still want + // some "click" events to capture + if (detail.event) { + // TODO this errors + // detail.event.preventDefault(); + } + + // remember where the pointer started from + this.startY = detail.startY; + + // reset everything + this.velocity = 0; + this.pos.length = 0; + this.pos.push(this.startY, Date.now()); + + let options = this.col.options; + let minY = (options.length - 1); + let maxY = 0; + for (var i = 0; i < options.length; i++) { + if (!options[i].disabled) { + minY = Math.min(minY, i); + maxY = Math.max(maxY, i); + } + } + + this.minY = (minY * this.optHeight * -1); + this.maxY = (maxY * this.optHeight * -1); + return true; + } + + onDragMove(detail: GestureDetail) { + if (detail.event) { + detail.event.preventDefault(); + detail.event.stopPropagation(); + } + + console.debug('picker, onDragMove', detail); + + let currentY = detail.currentY; + this.pos.push(currentY, Date.now()); + + if (this.startY === null) { + return; + } + + // update the scroll position relative to pointer start position + let y = this.y + (currentY - this.startY); + + if (y > this.minY) { + // 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 += Math.pow(this.maxY - y, 0.9); + this.bounceFrom = y; + + } else { + this.bounceFrom = 0; + } + + this.update(y, 0, false, false); + + let currentIndex = Math.max(Math.abs(Math.round(y / this.optHeight)), 0); + if (currentIndex !== this.lastTempIndex) { + this.lastTempIndex = currentIndex; + } + } + + onDragEnd(detail: GestureDetail) { + if (this.startY === null) { + return; + } + + console.debug('picker, onDragEnd', detail); + + this.velocity = 0; + + if (this.bounceFrom > 0) { + // bounce back up + this.update(this.minY, 100, true, true); + return; + } else if (this.bounceFrom < 0) { + // bounce back down + this.update(this.maxY, 100, true, true); + return; + } + + let endY = detail.currentY; + + this.pos.push(endY, Date.now()); + + let endPos = (this.pos.length - 1); + let startPos = endPos; + let timeRange = (Date.now() - 100); + + // move pointer to position measured 100ms ago + for (var i = endPos; i > 0 && this.pos[i] > timeRange; i -= 2) { + startPos = i; + } + + if (startPos !== endPos) { + // compute relative movement between these two points + var timeOffset = (this.pos[endPos] - this.pos[startPos]); + var movedTop = (this.pos[startPos - 1] - this.pos[endPos - 1]); + + // based on XXms compute the movement to apply for each render step + var velocity = ((movedTop / timeOffset) * FRAME_MS); + this.velocity = clamp(-MAX_PICKER_SPEED, velocity, MAX_PICKER_SPEED); + } + + if (Math.abs(endY - this.startY) > 3) { + var y = this.y + (endY - this.startY); + this.update(y, 0, true, true); + } + + this.startY = null; + this.decelerate(); } refresh() { @@ -261,14 +394,11 @@ export class PickerColumnCmp { const selectedIndex = clamp(min, this.col.selectedIndex, max); if (this.col.prevSelected !== selectedIndex) { var y = (selectedIndex * this.optHeight) * -1; - // TODO - // this._plt.cancelRaf(this.rafId); this.velocity = 0; this.update(y, 150, true, false); } } - hostData() { return { class: { @@ -278,7 +408,7 @@ export class PickerColumnCmp { style: { 'max-width': this.col.columnWidth } - }; + } } protected render() { @@ -293,28 +423,31 @@ export class PickerColumnCmp { }) .filter(clientInformation => clientInformation !== null); - let pickerPrefix: any[] = []; + let results: any[] = []; if (col.prefix) { - pickerPrefix.push( + results.push(
{col.prefix}
); } - let pickerSuffix: any[] = []; - - if (col.suffix) { - pickerSuffix.push( -
- {col.suffix} -
- ); - } - - return [ - { pickerPrefix }, + results.push( + ,
{options.map((o, index) =>
, - { pickerSuffix } - ]; + + ); + + if (col.suffix) { + results.push( +
+ {col.suffix} +
+ ); + } + + return results; } } -// export class PickerColumnCmp { -// @ViewChild('colEle') colEle: ElementRef; -// @Input() col: PickerColumn; -// pos: number[] = []; -// startY: number = null; -// rafId: number; -// bounceFrom: number; -// minY: number; -// maxY: number; -// lastIndex: number; -// lastTempIndex: number; -// decelerateFunc: Function; -// debouncer: DomDebouncer; -// events: UIEventManager; - -// ngOnDestroy() { -// this._plt.cancelRaf(this.rafId); -// this.events.destroy(); -// } - -// pointerStart(ev: UIEvent): boolean { -// console.debug('picker, pointerStart', ev.type, this.startY); -// this._haptic.gestureSelectionStart(); - -// // We have to prevent default in order to block scrolling under the picker -// // but we DO NOT have to stop propagation, since we still want -// // some "click" events to capture -// ev.preventDefault(); - -// // cancel any previous raf's that haven't fired yet -// this._plt.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()); - -// let options = this.col.options; -// let minY = (options.length - 1); -// let maxY = 0; -// for (var i = 0; i < options.length; i++) { -// if (!options[i].disabled) { -// minY = Math.min(minY, i); -// maxY = Math.max(maxY, i); -// } -// } - -// this.minY = (minY * this.optHeight * -1); -// this.maxY = (maxY * this.optHeight * -1); -// return true; -// } - -// pointerMove(ev: UIEvent) { -// ev.preventDefault(); -// ev.stopPropagation(); - -// let currentY = pointerCoord(ev).y; -// this.pos.push(currentY, Date.now()); - -// this.debouncer.write(() => { -// if (this.startY === null) { -// return; -// } - -// // update the scroll position relative to pointer start position -// let y = this.y + (currentY - this.startY); - -// if (y > this.minY) { -// // 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 += Math.pow(this.maxY - y, 0.9); -// this.bounceFrom = y; - -// } else { -// this.bounceFrom = 0; -// } - -// this.update(y, 0, false, false); - -// let currentIndex = Math.max(Math.abs(Math.round(y / this.optHeight)), 0); -// if (currentIndex !== this.lastTempIndex) { -// // Trigger a haptic event for physical feedback that the index has changed -// this._haptic.gestureSelectionChanged(); -// this.lastTempIndex = currentIndex; -// } -// }); -// } - -// pointerEnd(ev: UIEvent) { -// ev.preventDefault(); -// this.debouncer.cancel(); - -// if (this.startY === null) { -// return; -// } -// console.debug('picker, pointerEnd', ev.type); - -// this.velocity = 0; - -// if (this.bounceFrom > 0) { -// // bounce back up -// this.update(this.minY, 100, true, true); -// return; -// } else if (this.bounceFrom < 0) { -// // bounce back down -// this.update(this.maxY, 100, true, true); -// return; -// } - -// let endY = pointerCoord(ev).y; - -// this.pos.push(endY, Date.now()); - -// let endPos = (this.pos.length - 1); -// let startPos = endPos; -// let timeRange = (Date.now() - 100); - -// // move pointer to position measured 100ms ago -// for (var i = endPos; i > 0 && this.pos[i] > timeRange; i -= 2) { -// startPos = i; -// } - -// if (startPos !== endPos) { -// // compute relative movement between these two points -// var timeOffset = (this.pos[endPos] - this.pos[startPos]); -// var movedTop = (this.pos[startPos - 1] - this.pos[endPos - 1]); - -// // based on XXms compute the movement to apply for each render step -// var velocity = ((movedTop / timeOffset) * FRAME_MS); -// this.velocity = clamp(-MAX_PICKER_SPEED, velocity, MAX_PICKER_SPEED); -// } - -// if (Math.abs(endY - this.startY) > 3) { -// var y = this.y + (endY - this.startY); -// this.update(y, 0, true, true); -// } - -// this.startY = null; -// this.decelerate(); -// } +export const PICKER_OPT_SELECTED = 'picker-opt-selected'; +export const DECELERATION_FRICTION = 0.97; +export const FRAME_MS = (1000 / 60); +export const MAX_PICKER_SPEED = 60; \ No newline at end of file diff --git a/packages/core/src/components/picker/picker.tsx b/packages/core/src/components/picker/picker.tsx index bb76786d91..929036dfa6 100644 --- a/packages/core/src/components/picker/picker.tsx +++ b/packages/core/src/components/picker/picker.tsx @@ -405,11 +405,6 @@ export interface PickerEvent extends Event { }; } -export const PICKER_OPT_SELECTED = 'picker-opt-selected'; -export const DECELERATION_FRICTION = 0.97; -export const FRAME_MS = (1000 / 60); -export const MAX_PICKER_SPEED = 60; - // @ViewChildren(PickerColumnCmp) _cols: QueryList; // d: PickerOptions; diff --git a/packages/core/stencil.config.js b/packages/core/stencil.config.js index fbd9bcb430..ef153b992e 100644 --- a/packages/core/stencil.config.js +++ b/packages/core/stencil.config.js @@ -13,6 +13,7 @@ exports.config = { { components: ['ion-card', 'ion-card-content', 'ion-card-header', 'ion-card-title'] }, { components: ['ion-checkbox'] }, { components: ['ion-chip', 'ion-chip-button'] }, + { components: ['ion-datetime', 'ion-picker', 'ion-picker-column', 'ion-picker-controller'] }, { components: ['ion-fab', 'ion-fab-button', 'ion-fab-list'] }, { components: ['ion-gesture', 'ion-scroll'], priority: 'low' }, { components: ['ion-grid', 'ion-row', 'ion-col'] },