mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-19 03:32:21 +08:00
Merge branch 'master' into alpha38
Conflicts: ionic/components/search-bar/search-bar.ts ionic/config/decorators.ts
This commit is contained in:
@ -10,6 +10,7 @@ export * from 'ionic/components/form/input'
|
|||||||
export * from 'ionic/components/icon/icon'
|
export * from 'ionic/components/icon/icon'
|
||||||
export * from 'ionic/components/item/item'
|
export * from 'ionic/components/item/item'
|
||||||
export * from 'ionic/components/item/item-group'
|
export * from 'ionic/components/item/item-group'
|
||||||
|
export * from 'ionic/components/item/item-sliding'
|
||||||
export * from 'ionic/components/menu/menu'
|
export * from 'ionic/components/menu/menu'
|
||||||
export * from 'ionic/components/menu/menu-types'
|
export * from 'ionic/components/menu/menu-types'
|
||||||
export * from 'ionic/components/menu/menu-toggle'
|
export * from 'ionic/components/menu/menu-toggle'
|
||||||
|
@ -22,6 +22,7 @@ $z-index-backdrop: 1;
|
|||||||
$z-index-overlay-wrapper: 10;
|
$z-index-overlay-wrapper: 10;
|
||||||
|
|
||||||
|
|
||||||
|
$z-index-item-options: 1;
|
||||||
|
|
||||||
// Flex Order
|
// Flex Order
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
|
242
ionic/components/item/item-sliding.ts
Normal file
242
ionic/components/item/item-sliding.ts
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
import {Component, Directive, View, ElementRef, NgIf, Host, Optional} from 'angular2/angular2';
|
||||||
|
|
||||||
|
import {Gesture} from 'ionic/gestures/gesture';
|
||||||
|
import {DragGesture} from 'ionic/gestures/drag-gesture';
|
||||||
|
import {Hammer} from 'ionic/gestures/hammer';
|
||||||
|
import {List} from 'ionic/components/list/list';
|
||||||
|
|
||||||
|
import * as util from 'ionic/util';
|
||||||
|
|
||||||
|
import {CSS, raf} from 'ionic/util/dom';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name ionItem
|
||||||
|
* @description
|
||||||
|
* Creates a list-item that can easily be swiped,
|
||||||
|
* deleted, reordered, edited, and more.
|
||||||
|
*
|
||||||
|
* @usage
|
||||||
|
* ```html
|
||||||
|
* <ion-list>
|
||||||
|
* <ion-item-sliding *ng-for="#item of items" (click)="itemTapped($event, item)">
|
||||||
|
* {{item.title}}
|
||||||
|
* <div class="item-note" item-right>
|
||||||
|
* {{item.note}}
|
||||||
|
* </div>
|
||||||
|
* </ion-item>
|
||||||
|
* </ion-list>
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'ion-item-sliding,[ion-item-sliding]',
|
||||||
|
host: {
|
||||||
|
'class': 'item'
|
||||||
|
},
|
||||||
|
properties: [
|
||||||
|
'sliding'
|
||||||
|
]
|
||||||
|
})
|
||||||
|
@View({
|
||||||
|
template:
|
||||||
|
'<ng-content select="[item-left]"></ng-content>' +
|
||||||
|
'<ion-item-sliding-content>' +
|
||||||
|
'<ion-item-content>' +
|
||||||
|
'<ng-content></ng-content>'+
|
||||||
|
'</ion-item-content>' +
|
||||||
|
'<ng-content select="[item-right]"></ng-content>' +
|
||||||
|
'</ion-item-sliding-content>' +
|
||||||
|
'<ng-content select="ion-item-options"></ng-content>',
|
||||||
|
directives: [NgIf]
|
||||||
|
})
|
||||||
|
export class ItemSliding {
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
* @param {ElementRef} elementRef A reference to the component's DOM element.
|
||||||
|
*/
|
||||||
|
constructor(elementRef: ElementRef, @Optional() @Host() list: List) {
|
||||||
|
this._isOpen = false;
|
||||||
|
this._isSlideActive = false;
|
||||||
|
this._isTransitioning = false;
|
||||||
|
this._transform = '';
|
||||||
|
|
||||||
|
this.list = list;
|
||||||
|
|
||||||
|
this.ele = elementRef.nativeElement;
|
||||||
|
this.swipeButtons = {};
|
||||||
|
this.optionButtons = {};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onInit() {
|
||||||
|
this._initSliding();
|
||||||
|
}
|
||||||
|
|
||||||
|
_initSliding() {
|
||||||
|
var itemSlidingContent = this.ele.querySelector('ion-item-sliding-content');
|
||||||
|
var itemOptionsContent = this.ele.querySelector('ion-item-options');
|
||||||
|
|
||||||
|
this.itemSlidingContent = itemSlidingContent;
|
||||||
|
this.itemOptions = itemOptionsContent;
|
||||||
|
|
||||||
|
this.itemWidth = itemSlidingContent.offsetWidth;
|
||||||
|
this.itemOptionsWidth = itemOptionsContent && itemOptionsContent.offsetWidth || 0;
|
||||||
|
|
||||||
|
this.openAmount = 0;
|
||||||
|
|
||||||
|
this.gesture = new ItemSlideGesture(this, itemSlidingContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(andStopDrag) {
|
||||||
|
this.openAmount = 0;
|
||||||
|
|
||||||
|
// Enable it once, it'll get disabled on the next drag
|
||||||
|
raf(() => {
|
||||||
|
this.enableAnimation();
|
||||||
|
if(this.itemSlidingContent) {
|
||||||
|
this.itemSlidingContent.style[CSS.transform] = 'translateX(0)';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
open(amt) {
|
||||||
|
let el = this.itemSlidingContent;
|
||||||
|
this.openAmount = amt || 0;
|
||||||
|
|
||||||
|
if(this.list) {
|
||||||
|
this.list.setOpenItem(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(amt === '') {
|
||||||
|
el.style[CSS.transform] = '';
|
||||||
|
} else {
|
||||||
|
el.style[CSS.transform] = 'translateX(' + -amt + 'px)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isOpen() {
|
||||||
|
return this.openAmount > 0;
|
||||||
|
}
|
||||||
|
getOpenAmt() {
|
||||||
|
return this.openAmount;
|
||||||
|
}
|
||||||
|
getItemWidth() {
|
||||||
|
return this.itemWidth;
|
||||||
|
}
|
||||||
|
disableAnimation() {
|
||||||
|
this.itemSlidingContent.style[CSS.transition] = 'none';
|
||||||
|
}
|
||||||
|
enableAnimation() {
|
||||||
|
// Clear the explicit transition, allow for CSS one to take over
|
||||||
|
this.itemSlidingContent.style[CSS.transition] = '';
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* User did a touchstart
|
||||||
|
*/
|
||||||
|
didTouch() {
|
||||||
|
if(this.isOpen()) {
|
||||||
|
this.close();
|
||||||
|
this.didClose = true;
|
||||||
|
} else {
|
||||||
|
if(this.list) {
|
||||||
|
this.list.closeOpenItem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ItemSlideGesture extends DragGesture {
|
||||||
|
constructor(item: ItemSliding, el: Element) {
|
||||||
|
|
||||||
|
super(el, {
|
||||||
|
direction: 'x',
|
||||||
|
threshold: el.offsetWidth
|
||||||
|
});
|
||||||
|
|
||||||
|
this.el = el;
|
||||||
|
this.item = item;
|
||||||
|
this.canDrag = true;
|
||||||
|
this.listen();
|
||||||
|
|
||||||
|
this.el.addEventListener('touchstart', (e) => {
|
||||||
|
this.item.didTouch();
|
||||||
|
})
|
||||||
|
|
||||||
|
this.el.addEventListener('touchend', (e) => {
|
||||||
|
this.item.didClose = false;
|
||||||
|
});
|
||||||
|
this.el.addEventListener('touchcancel', (e) => {
|
||||||
|
this.item.didClose = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onDragStart(ev) {
|
||||||
|
if(this.item.didClose) { return; }
|
||||||
|
|
||||||
|
this.slide = {};
|
||||||
|
|
||||||
|
this.slide.offsetX = this.item.getOpenAmt();
|
||||||
|
this.slide.startX = ev.center[this.direction];
|
||||||
|
this.slide.started = true;
|
||||||
|
|
||||||
|
this.item.disableAnimation();
|
||||||
|
}
|
||||||
|
|
||||||
|
onDrag(ev) {
|
||||||
|
if (!this.slide || !this.slide.started) return;
|
||||||
|
|
||||||
|
this.slide.x = ev.center[this.direction];
|
||||||
|
this.slide.delta = this.slide.x - this.slide.startX;
|
||||||
|
|
||||||
|
let newX = Math.max(0, this.slide.offsetX - this.slide.delta);
|
||||||
|
|
||||||
|
let buttonsWidth = this.item.itemOptionsWidth;
|
||||||
|
|
||||||
|
if(newX > this.item.itemOptionsWidth) {
|
||||||
|
// Calculate the new X position, capped at the top of the buttons
|
||||||
|
newX = -Math.min(-buttonsWidth, -buttonsWidth + (((this.slide.delta + buttonsWidth) * 0.4)));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.item.open(newX);
|
||||||
|
}
|
||||||
|
|
||||||
|
onDragEnd(ev) {
|
||||||
|
if (!this.slide || !this.slide.started) return;
|
||||||
|
|
||||||
|
let buttonsWidth = this.item.itemOptionsWidth;
|
||||||
|
|
||||||
|
// If we are currently dragging, we want to snap back into place
|
||||||
|
// The final resting point X will be the width of the exposed buttons
|
||||||
|
var restingPoint = this.item.itemOptionsWidth;
|
||||||
|
|
||||||
|
// Check if the drag didn't clear the buttons mid-point
|
||||||
|
// and we aren't moving fast enough to swipe open
|
||||||
|
if (this.item.openAmount < (buttonsWidth / 2)) {
|
||||||
|
|
||||||
|
// If we are going left but too slow, or going right, go back to resting
|
||||||
|
if (ev.direction & Hammer.DIRECTION_RIGHT) {
|
||||||
|
// Left
|
||||||
|
restingPoint = 0;
|
||||||
|
} else if (Math.abs(ev.velocityX) < 0.3) {
|
||||||
|
// Right
|
||||||
|
restingPoint = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
raf(() => {
|
||||||
|
if (restingPoint === 0) {
|
||||||
|
// Reset to zero
|
||||||
|
this.item.open('');
|
||||||
|
var buttons = this.item.itemOptions;
|
||||||
|
clearTimeout(this.hideButtonsTimeout);
|
||||||
|
this.hideButtonsTimeout = setTimeout(() => {
|
||||||
|
buttons && buttons.classList.add('invisible');
|
||||||
|
}, 250);
|
||||||
|
} else {
|
||||||
|
this.item.open(restingPoint);
|
||||||
|
}
|
||||||
|
this.item.enableAnimation();
|
||||||
|
|
||||||
|
this.slide = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -137,3 +137,21 @@ ion-input.item {
|
|||||||
.item.item.item.no-border-bottom + .item {
|
.item.item.item.no-border-bottom + .item {
|
||||||
margin-top: -13px;
|
margin-top: -13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The hidden right-side buttons that can be exposed under a list item
|
||||||
|
* with dragging.
|
||||||
|
*/
|
||||||
|
ion-item-sliding-content {
|
||||||
|
display: block;
|
||||||
|
z-index: $z-index-item-options + 1;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
ion-item-options {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: $z-index-item-options;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {Component, Directive, View, ElementRef, NgIf, ViewQuery, QueryList} from 'angular2/angular2';
|
import {Component, Directive, View, ElementRef, NgIf, ViewQuery, QueryList} from 'angular2/angular2';
|
||||||
|
|
||||||
import {dom} from 'ionic/util';
|
import * as util from 'ionic/util';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -31,6 +31,7 @@ import {dom} from 'ionic/util';
|
|||||||
'<ion-item-content>' +
|
'<ion-item-content>' +
|
||||||
'<ng-content></ng-content>'+
|
'<ng-content></ng-content>'+
|
||||||
'</ion-item-content>' +
|
'</ion-item-content>' +
|
||||||
|
'<ng-content select="ion-item-options"></ng-content>' +
|
||||||
'<ng-content select="[item-right]"></ng-content>',
|
'<ng-content select="[item-right]"></ng-content>',
|
||||||
directives: [NgIf]
|
directives: [NgIf]
|
||||||
})
|
})
|
||||||
@ -48,55 +49,7 @@ export class Item {
|
|||||||
this.ele = elementRef.nativeElement;
|
this.ele = elementRef.nativeElement;
|
||||||
this.swipeButtons = {};
|
this.swipeButtons = {};
|
||||||
this.optionButtons = {};
|
this.optionButtons = {};
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Slideable {
|
|
||||||
constructor(slideElement: Element) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// override
|
|
||||||
onTransform(str: String) {}
|
|
||||||
// override
|
|
||||||
onTransitionActive(active: Boolean) {}
|
|
||||||
//override
|
|
||||||
onSlideActive(active: boolean) {}
|
|
||||||
|
|
||||||
transform(str: String) {
|
|
||||||
if (arguments.length && str !== this._transform) {
|
|
||||||
this.onTransform()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isTransitionActive(active: Boolean) {
|
|
||||||
if (arguments.length && active !== this._isTransitionActive) {
|
|
||||||
this._isTransitionActive = active
|
|
||||||
this.onSetTransitionActive(active)
|
|
||||||
}
|
|
||||||
return this._isTransitioning
|
|
||||||
}
|
|
||||||
|
|
||||||
isSlideActive(active: Boolean) {
|
|
||||||
if (arguments.length && active !== this._isSlideActive) {
|
|
||||||
this._isSlideActive = active
|
|
||||||
this.onSetDragActive(active)
|
|
||||||
}
|
|
||||||
return this._isSlideActive
|
|
||||||
}
|
|
||||||
|
|
||||||
isOpen(open: Boolean) {
|
|
||||||
if (arguments.length && open !== this._isOpen) {
|
|
||||||
this.isTransitionActive(true)
|
|
||||||
dom.raf(() => {
|
|
||||||
this.isOpen = isOpen
|
|
||||||
this.onSetIsOpen(open)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class ItemSlideGesture {
|
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,8 @@ $item-ios-divider-bg: #f5f5f5 !default;
|
|||||||
$item-ios-divider-color: #222 !default;
|
$item-ios-divider-color: #222 !default;
|
||||||
$item-ios-divider-padding: 5px 15px !default;
|
$item-ios-divider-padding: 5px 15px !default;
|
||||||
|
|
||||||
|
$item-ios-sliding-content-bg: $background-color !default;
|
||||||
|
$item-ios-sliding-transition: transform 250ms ease-in-out !default;
|
||||||
|
|
||||||
.item-group-title {
|
.item-group-title {
|
||||||
padding: $item-ios-padding-top $item-ios-padding-right $item-ios-padding-bottom $item-ios-padding-left;
|
padding: $item-ios-padding-top $item-ios-padding-right $item-ios-padding-bottom $item-ios-padding-left;
|
||||||
@ -157,6 +159,41 @@ $item-ios-divider-padding: 5px 15px !default;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ion-item-sliding.item {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
ion-item-sliding-content {
|
||||||
|
background-color: $item-ios-sliding-content-bg;
|
||||||
|
padding-right: ($item-ios-padding-right / 2);
|
||||||
|
padding-left: ($item-ios-padding-left / 2);
|
||||||
|
display: flex;
|
||||||
|
min-height: 42px;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
transition: $item-ios-sliding-transition;
|
||||||
|
|
||||||
|
// To allow the hairlines through
|
||||||
|
margin-top: 1px;
|
||||||
|
margin-bottom: 1px;
|
||||||
|
}
|
||||||
|
ion-item-options {
|
||||||
|
button, [button] {
|
||||||
|
height: calc(100% - 2px);
|
||||||
|
margin: 1px 0 2px 0;
|
||||||
|
|
||||||
|
border: none;
|
||||||
|
border-radius: 0;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
&:before{
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.item.activated,
|
.item.activated,
|
||||||
@ -175,14 +212,25 @@ button.item {
|
|||||||
.list,
|
.list,
|
||||||
.card {
|
.card {
|
||||||
button[ion-item]:not([detail-none]),
|
button[ion-item]:not([detail-none]),
|
||||||
a[ion-item]:not([detail-push]),
|
a[ion-item]:not([detail-none]),
|
||||||
[detail-push] {
|
[detail-push]:not(ion-item-sliding) {
|
||||||
@include ios-detail-push-icon($item-ios-detail-push-color);
|
@include ios-detail-push-icon($item-ios-detail-push-color);
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: right ($item-ios-padding-right - 2) center;
|
background-position: right ($item-ios-padding-right - 2) center;
|
||||||
background-size: 14px 14px;
|
background-size: 14px 14px;
|
||||||
padding-right: 32px;
|
padding-right: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ion-item-sliding[detail-push] {
|
||||||
|
|
||||||
|
ion-item-sliding-content {
|
||||||
|
@include ios-detail-push-icon($item-ios-detail-push-color);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: right ($item-ios-padding-right - 2) center;
|
||||||
|
background-size: 14px 14px;
|
||||||
|
padding-right: 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,6 +29,9 @@ $item-md-divider-bg: #fff !default;
|
|||||||
$item-md-divider-color: #222 !default;
|
$item-md-divider-color: #222 !default;
|
||||||
$item-md-divider-padding: 5px 15px !default;
|
$item-md-divider-padding: 5px 15px !default;
|
||||||
|
|
||||||
|
$item-md-sliding-content-bg: $background-color !default;
|
||||||
|
$item-md-sliding-transition: transform 250ms ease-in-out !default;
|
||||||
|
|
||||||
.list {
|
.list {
|
||||||
|
|
||||||
.item-group-title {
|
.item-group-title {
|
||||||
@ -202,6 +205,41 @@ $item-md-divider-padding: 5px 15px !default;
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ion-item-sliding.item {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
ion-item-sliding-content {
|
||||||
|
background-color: $item-md-sliding-content-bg;
|
||||||
|
padding-right: ($item-md-padding-right / 2);
|
||||||
|
padding-left: ($item-md-padding-left / 2);
|
||||||
|
display: flex;
|
||||||
|
min-height: 42px;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
transition: $item-md-sliding-transition;
|
||||||
|
|
||||||
|
// To allow the hairlines through
|
||||||
|
margin-top: 1px;
|
||||||
|
margin-bottom: 1px;
|
||||||
|
}
|
||||||
|
ion-item-options {
|
||||||
|
button, [button] {
|
||||||
|
height: calc(100% - 2px);
|
||||||
|
margin: 1px 0 1px 0;
|
||||||
|
box-shadow: none;
|
||||||
|
|
||||||
|
border: none;
|
||||||
|
border-radius: 0;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
&:before{
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.item,
|
.item,
|
||||||
|
1
ionic/components/item/test/sliding/e2e.ts
Normal file
1
ionic/components/item/test/sliding/e2e.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
7
ionic/components/item/test/sliding/index.ts
Normal file
7
ionic/components/item/test/sliding/index.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import {App} from 'ionic/ionic';
|
||||||
|
|
||||||
|
|
||||||
|
@App({
|
||||||
|
templateUrl: 'main.html'
|
||||||
|
})
|
||||||
|
class E2EApp {}
|
34
ionic/components/item/test/sliding/main.html
Normal file
34
ionic/components/item/test/sliding/main.html
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<ion-toolbar><ion-title>Sliding Items</ion-title></ion-toolbar>
|
||||||
|
|
||||||
|
<ion-list>
|
||||||
|
|
||||||
|
<ion-item-sliding class="item-text-wrap" detail-push>
|
||||||
|
<h3>Max Lynch</h3>
|
||||||
|
<p>
|
||||||
|
Hey do you want to go to the game tonight?
|
||||||
|
</p>
|
||||||
|
<ion-item-options>
|
||||||
|
<button primary>Archive</button>
|
||||||
|
</ion-item-options>
|
||||||
|
</ion-item-sliding>
|
||||||
|
|
||||||
|
<ion-item-sliding class="item-text-wrap" detail-push>
|
||||||
|
<h3>Adam Bradley</h3>
|
||||||
|
<p>
|
||||||
|
I think I figured out how to get more Mountain Dew
|
||||||
|
</p>
|
||||||
|
<ion-item-options>
|
||||||
|
<button primary>Archive</button>
|
||||||
|
</ion-item-options>
|
||||||
|
</ion-item-sliding>
|
||||||
|
|
||||||
|
<ion-item-sliding class="item-text-wrap" detail-push>
|
||||||
|
<h3>Ben Sperry</h3>
|
||||||
|
<p>
|
||||||
|
I like paper
|
||||||
|
</p>
|
||||||
|
<ion-item-options>
|
||||||
|
<button primary>Archive</button>
|
||||||
|
</ion-item-options>
|
||||||
|
</ion-item-sliding>
|
||||||
|
</ion-list>
|
@ -67,6 +67,18 @@ export class List extends Ion {
|
|||||||
setItemTemplate(item) {
|
setItemTemplate(item) {
|
||||||
this.itemTemplate = item;
|
this.itemTemplate = item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keeps track of any open item (a sliding item, for example), to close it later
|
||||||
|
*/
|
||||||
|
setOpenItem(item) {
|
||||||
|
this.openItem = item;
|
||||||
|
}
|
||||||
|
closeOpenItem() {
|
||||||
|
if(this.openItem) {
|
||||||
|
this.openItem.close(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,7 +66,7 @@ export class MenuType {
|
|||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
onDestory() {
|
onDestroy() {
|
||||||
this.open && this.open.dispose();
|
this.open && this.open.dispose();
|
||||||
this.close && this.close.dispose();
|
this.close && this.close.dispose();
|
||||||
this.seek && this.seek.dispose();
|
this.seek && this.seek.dispose();
|
||||||
|
@ -12,7 +12,8 @@ $search-bar-ios-input-search-icon-svg: "<svg xmlns='http://www.w3.org/2000/sv
|
|||||||
$search-bar-ios-input-search-icon-size: 13px !default;
|
$search-bar-ios-input-search-icon-size: 13px !default;
|
||||||
|
|
||||||
$search-bar-ios-input-height: 28px !default;
|
$search-bar-ios-input-height: 28px !default;
|
||||||
$search-bar-ios-input-text-color: #9D9D9D !default;
|
$search-bar-ios-input-placeholder-color: #9D9D9D !default;
|
||||||
|
$search-bar-ios-input-text-color: #000 !default;
|
||||||
$search-bar-ios-input-background-color: #FFFFFF !default;
|
$search-bar-ios-input-background-color: #FFFFFF !default;
|
||||||
$search-bar-ios-input-transition: all 0.3s cubic-bezier(.25, .45, .05, 1) !default;
|
$search-bar-ios-input-transition: all 0.3s cubic-bezier(.25, .45, .05, 1) !default;
|
||||||
|
|
||||||
@ -32,15 +33,15 @@ $search-bar-ios-input-close-icon-size: 17px !default;
|
|||||||
width: $search-bar-ios-input-search-icon-size + 1;
|
width: $search-bar-ios-input-search-icon-size + 1;
|
||||||
height: $search-bar-ios-input-search-icon-size + 1;
|
height: $search-bar-ios-input-search-icon-size + 1;
|
||||||
|
|
||||||
transform: translateX(calc(50% + 90px));
|
|
||||||
transition: $search-bar-ios-input-transition;
|
|
||||||
|
|
||||||
@include svg-background-image($search-bar-ios-input-search-icon-svg);
|
@include svg-background-image($search-bar-ios-input-search-icon-svg);
|
||||||
background-size: $search-bar-ios-input-search-icon-size;
|
background-size: $search-bar-ios-input-search-icon-size;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 10px;
|
left: 10px;
|
||||||
top: 8px;
|
top: 8px;
|
||||||
|
|
||||||
|
@include calc(margin-left, "50% - 60px");
|
||||||
|
transition: $search-bar-ios-input-transition;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-bar-input {
|
.search-bar-input {
|
||||||
@ -56,9 +57,12 @@ $search-bar-ios-input-close-icon-size: 17px !default;
|
|||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: 8px center;
|
background-position: 8px center;
|
||||||
|
|
||||||
transition: $search-bar-ios-input-transition;
|
&::placeholder {
|
||||||
|
color: $search-bar-ios-input-placeholder-color;
|
||||||
|
}
|
||||||
|
|
||||||
@include calc(padding-left, "50% - 28px");
|
@include calc(padding-left, "50% - 28px");
|
||||||
|
transition: $search-bar-ios-input-transition;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-bar-close-icon {
|
.search-bar-close-icon {
|
||||||
@ -75,7 +79,7 @@ $search-bar-ios-input-close-icon-size: 17px !default;
|
|||||||
|
|
||||||
.search-bar-input-container.left-align {
|
.search-bar-input-container.left-align {
|
||||||
.search-bar-search-icon {
|
.search-bar-search-icon {
|
||||||
transform: translateX(0);
|
margin-left: 0;
|
||||||
transition: $search-bar-ios-input-transition;
|
transition: $search-bar-ios-input-transition;
|
||||||
}
|
}
|
||||||
.search-bar-input {
|
.search-bar-input {
|
||||||
@ -84,6 +88,14 @@ $search-bar-ios-input-close-icon-size: 17px !default;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.search-bar-cancel.left-align {
|
||||||
|
@extend button[clear];
|
||||||
|
display: block;
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 0;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
&.hairlines .search-bar {
|
&.hairlines .search-bar {
|
||||||
border-bottom-width: 0.55px;
|
border-bottom-width: 0.55px;
|
||||||
}
|
}
|
||||||
|
@ -22,8 +22,16 @@ import {IonicComponent} from '../../config/decorators';
|
|||||||
'query'
|
'query'
|
||||||
],
|
],
|
||||||
defaultInputs: {
|
defaultInputs: {
|
||||||
|
|
||||||
|
'showCancel': false,
|
||||||
'cancelText': 'Cancel',
|
'cancelText': 'Cancel',
|
||||||
'placeholder': 'Search'
|
'placeholder': 'Search',
|
||||||
|
'cancelAction': function() {
|
||||||
|
console.log('Default Cancel');
|
||||||
|
this.isFocused = false;
|
||||||
|
this.shouldLeftAlign = this.value.trim() != '';
|
||||||
|
// TODO input blur
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@View({
|
@View({
|
||||||
@ -31,11 +39,12 @@ import {IonicComponent} from '../../config/decorators';
|
|||||||
<div class="search-bar-input-container" [class.left-align]="shouldLeftAlign">
|
<div class="search-bar-input-container" [class.left-align]="shouldLeftAlign">
|
||||||
<div class="search-bar-search-icon"></div>
|
<div class="search-bar-search-icon"></div>
|
||||||
<input (focus)="inputFocused()" (blur)="inputBlurred()"
|
<input (focus)="inputFocused()" (blur)="inputBlurred()"
|
||||||
(input)="inputChanged($event)" class="search-bar-input" type="search" [attr.placeholder]="placeholder">
|
(input)="inputChanged($event)" class="search-bar-input" type="search" [attr.placeholder]="placeholder" [(ng-model)]="value">
|
||||||
<div class="search-bar-close-icon"></div>
|
<div class="search-bar-close-icon" (click)="clearInput()"></div>
|
||||||
</div>
|
</div>
|
||||||
<button class="search-bar-cancel">{{cancelText}}</button>`
|
<button *ng-if="showCancel" (click)="cancelAction()" class="search-bar-cancel" [class.left-align]="shouldLeftAlign">{{cancelText}}</button>`
|
||||||
})
|
})
|
||||||
|
|
||||||
export class SearchBar extends Ion {
|
export class SearchBar extends Ion {
|
||||||
/**
|
/**
|
||||||
* TODO
|
* TODO
|
||||||
@ -69,28 +78,20 @@ export class SearchBar extends Ion {
|
|||||||
*/
|
*/
|
||||||
writeValue(value) {
|
writeValue(value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
console.log('writeValue', value);
|
|
||||||
this.renderer.setElementProperty(this.elementRef, 'value', this.value);
|
this.renderer.setElementProperty(this.elementRef, 'value', this.value);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
registerOnChange(val) {
|
registerOnChange(val) {
|
||||||
console.log('registerONChange', val);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
registerOnTouched(val) {
|
registerOnTouched(val) {
|
||||||
console.log('register on touched', val);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inputChanged(event) {
|
inputChanged(event) {
|
||||||
this.value = event.target.value;
|
this.value = event.target.value;
|
||||||
console.log('Search changed', this.value);
|
|
||||||
this.ngControl.valueAccessor.writeValue(this.value);
|
this.ngControl.valueAccessor.writeValue(this.value);
|
||||||
this.ngControl.control.updateValue(this.value);
|
this.ngControl.control.updateValue(this.value);
|
||||||
// this.ngControl.valueAccessor.updateValue(this.value);
|
|
||||||
// this.ngControl.updateValue(this.value);
|
|
||||||
// TODO: Better way to do this?
|
|
||||||
//this.controlDirective._control().updateValue(event.target.value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inputFocused() {
|
inputFocused() {
|
||||||
@ -101,6 +102,11 @@ export class SearchBar extends Ion {
|
|||||||
this.isFocused = false;
|
this.isFocused = false;
|
||||||
this.shouldLeftAlign = this.value.trim() != '';
|
this.shouldLeftAlign = this.value.trim() != '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearInput() {
|
||||||
|
this.value = '';
|
||||||
|
this.ngControl.control.updateValue('');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -115,13 +121,11 @@ export class SearchPipe extends Pipe {
|
|||||||
}
|
}
|
||||||
|
|
||||||
transform(value, ...args) {
|
transform(value, ...args) {
|
||||||
console.log('Transforming', value, args);
|
|
||||||
return value;
|
return value;
|
||||||
//return `${value} state:${this.state ++}`;
|
//return `${value} state:${this.state ++}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
create(cdRef) {
|
create(cdRef) {
|
||||||
console.log('REF', cdRef);
|
|
||||||
return new SearchPipe(cdRef);
|
return new SearchPipe(cdRef);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
135
ionic/components/search-bar/search-bar.ts.orig
Normal file
135
ionic/components/search-bar/search-bar.ts.orig
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
import {ElementRef, Pipe, NgControl, Renderer, View} from 'angular2/angular2';
|
||||||
|
//import {ControlGroup} from 'angular2/forms'
|
||||||
|
|
||||||
|
import {Ion} from '../ion';
|
||||||
|
import {IonicConfig} from '../../config/config';
|
||||||
|
import {IonicComponent} from '../../config/decorators';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Search Bar
|
||||||
|
* @description
|
||||||
|
* The Search Bar service adds an input field which can be used to search or filter items.
|
||||||
|
*
|
||||||
|
* @usage
|
||||||
|
* ```html
|
||||||
|
* <ion-search-bar ng-control="searchQuery"></ion-search-bar>
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
@IonicComponent({
|
||||||
|
selector: 'ion-search-bar',
|
||||||
|
inputs: [
|
||||||
|
'list',
|
||||||
|
'query'
|
||||||
|
],
|
||||||
|
<<<<<<< HEAD
|
||||||
|
defaultInputs: {
|
||||||
|
=======
|
||||||
|
defaultProperties: {
|
||||||
|
'showCancel': false,
|
||||||
|
>>>>>>> master
|
||||||
|
'cancelText': 'Cancel',
|
||||||
|
'placeholder': 'Search',
|
||||||
|
'cancelAction': function() {
|
||||||
|
console.log('Default Cancel');
|
||||||
|
this.isFocused = false;
|
||||||
|
this.shouldLeftAlign = this.value.trim() != '';
|
||||||
|
// TODO input blur
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
@View({
|
||||||
|
template: `
|
||||||
|
<div class="search-bar-input-container" [class.left-align]="shouldLeftAlign">
|
||||||
|
<div class="search-bar-search-icon"></div>
|
||||||
|
<input (focus)="inputFocused()" (blur)="inputBlurred()"
|
||||||
|
(input)="inputChanged($event)" class="search-bar-input" type="search" [attr.placeholder]="placeholder" [(ng-model)]="value">
|
||||||
|
<div class="search-bar-close-icon" (click)="clearInput()"></div>
|
||||||
|
</div>
|
||||||
|
<button *ng-if="showCancel" (click)="cancelAction()" class="search-bar-cancel" [class.left-align]="shouldLeftAlign">{{cancelText}}</button>`
|
||||||
|
})
|
||||||
|
|
||||||
|
export class SearchBar extends Ion {
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
* @param {ElementRef} elementRef TODO
|
||||||
|
* @param {IonicConfig} config TODO
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
elementRef: ElementRef,
|
||||||
|
config: IonicConfig,
|
||||||
|
ngControl: NgControl,
|
||||||
|
renderer: Renderer
|
||||||
|
) {
|
||||||
|
super(elementRef, config);
|
||||||
|
this.renderer = renderer;
|
||||||
|
this.elementRef = elementRef;
|
||||||
|
if(!ngControl) {
|
||||||
|
// They don't want to do anything that works, so we won't do anything that breaks
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ngControl = ngControl;
|
||||||
|
|
||||||
|
ngControl.valueAccessor = this;
|
||||||
|
|
||||||
|
this.query = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Much like ngModel, this is called from our valueAccessor for the attached
|
||||||
|
* ControlDirective to update the value internally.
|
||||||
|
*/
|
||||||
|
writeValue(value) {
|
||||||
|
this.value = value;
|
||||||
|
this.renderer.setElementProperty(this.elementRef, 'value', this.value);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnChange(val) {
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnTouched(val) {
|
||||||
|
}
|
||||||
|
|
||||||
|
inputChanged(event) {
|
||||||
|
this.value = event.target.value;
|
||||||
|
this.ngControl.valueAccessor.writeValue(this.value);
|
||||||
|
this.ngControl.control.updateValue(this.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
inputFocused() {
|
||||||
|
this.isFocused = true;
|
||||||
|
this.shouldLeftAlign = true;
|
||||||
|
}
|
||||||
|
inputBlurred() {
|
||||||
|
this.isFocused = false;
|
||||||
|
this.shouldLeftAlign = this.value.trim() != '';
|
||||||
|
}
|
||||||
|
|
||||||
|
clearInput() {
|
||||||
|
this.value = '';
|
||||||
|
this.ngControl.control.updateValue('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
export class SearchPipe extends Pipe {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.state = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
supports(newValue) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
transform(value, ...args) {
|
||||||
|
return value;
|
||||||
|
//return `${value} state:${this.state ++}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
create(cdRef) {
|
||||||
|
return new SearchPipe(cdRef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
@ -18,4 +18,7 @@ class IonicApp {
|
|||||||
toolbarSearchQuery: ['', Validators.required]
|
toolbarSearchQuery: ['', Validators.required]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
myCancelAction = function() {
|
||||||
|
console.log('myCancelAction');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,15 @@
|
|||||||
</ion-toolbar> -->
|
</ion-toolbar> -->
|
||||||
<ion-content>
|
<ion-content>
|
||||||
<form [ng-form-model]="form">
|
<form [ng-form-model]="form">
|
||||||
<label> Default Search </label>
|
<label> Search - Default </label>
|
||||||
<ion-search-bar ng-control="searchQuery" class="e2eFloatingSearchBar"></ion-search-bar>
|
<ion-search-bar ng-control="searchQuery" class="e2eFloatingSearchBar"></ion-search-bar>
|
||||||
<label> Placeholder Search </label>
|
<label> Search - Custom Placeholder </label>
|
||||||
<ion-search-bar ng-control="searchQuery" placeholder="Filter"></ion-search-bar>
|
<ion-search-bar ng-control="searchQuery" placeholder="Filter"></ion-search-bar>
|
||||||
|
<label> Search - Cancel Button </label>
|
||||||
|
<ion-search-bar ng-control="searchQuery" show-cancel="true"></ion-search-bar>
|
||||||
|
<label> Search - Custom Cancel Button </label>
|
||||||
|
<ion-search-bar ng-control="searchQuery" show-cancel="true" cancel-text="Done"></ion-search-bar>
|
||||||
|
<label> Search - Custom Cancel Action</label>
|
||||||
|
<ion-search-bar ng-control="searchQuery" show-cancel="true" cancel-text="Done" [cancel-action]="myCancelAction"></ion-search-bar>
|
||||||
</form>
|
</form>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
|
256
ionic/config/decorators.ts.orig
Normal file
256
ionic/config/decorators.ts.orig
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
import {Component, Directive, View, bootstrap} from 'angular2/angular2'
|
||||||
|
|
||||||
|
import * as util from 'ionic/util';
|
||||||
|
<<<<<<< HEAD
|
||||||
|
import {ionicBindings} from './bootstrap';
|
||||||
|
import {IONIC_DIRECTIVES} from './directives';
|
||||||
|
=======
|
||||||
|
import {IonicConfig} from './config';
|
||||||
|
import {ionicBootstrap} from '../components/app/app';
|
||||||
|
import {
|
||||||
|
Menu, MenuToggle, MenuClose,
|
||||||
|
Button, Content, Scroll, Refresher,
|
||||||
|
Slides, Slide, SlideLazy,
|
||||||
|
Tabs, Tab,
|
||||||
|
Card, List, ListHeader, Item, ItemGroup, ItemGroupTitle, ItemSliding,
|
||||||
|
Toolbar, ToolbarTitle, ToolbarItem,
|
||||||
|
Icon,
|
||||||
|
Checkbox, Switch,
|
||||||
|
TextInput, TextInputElement, Label,
|
||||||
|
Segment, SegmentButton, SegmentControlValueAccessor,
|
||||||
|
RadioGroup, RadioButton, SearchBar,
|
||||||
|
Nav, NavbarTemplate, Navbar,
|
||||||
|
NavPush, NavPop, NavRouter,
|
||||||
|
IdRef,
|
||||||
|
ShowWhen, HideWhen
|
||||||
|
} from '../ionic';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The core Ionic directives. Automatically available in every IonicView
|
||||||
|
* template.
|
||||||
|
*/
|
||||||
|
export const IONIC_DIRECTIVES = [
|
||||||
|
// Angular
|
||||||
|
CORE_DIRECTIVES,
|
||||||
|
FORM_DIRECTIVES,
|
||||||
|
|
||||||
|
NgStyle,
|
||||||
|
|
||||||
|
// Content
|
||||||
|
forwardRef(() => Menu),
|
||||||
|
forwardRef(() => MenuToggle),
|
||||||
|
forwardRef(() => MenuClose),
|
||||||
|
|
||||||
|
forwardRef(() => Button),
|
||||||
|
forwardRef(() => Content),
|
||||||
|
forwardRef(() => Scroll),
|
||||||
|
forwardRef(() => Refresher),
|
||||||
|
|
||||||
|
// Lists
|
||||||
|
forwardRef(() => Card),
|
||||||
|
forwardRef(() => List),
|
||||||
|
forwardRef(() => ListHeader),
|
||||||
|
forwardRef(() => Item),
|
||||||
|
forwardRef(() => ItemGroup),
|
||||||
|
forwardRef(() => ItemGroupTitle),
|
||||||
|
forwardRef(() => ItemSliding),
|
||||||
|
|
||||||
|
// Slides
|
||||||
|
forwardRef(() => Slides),
|
||||||
|
forwardRef(() => Slide),
|
||||||
|
forwardRef(() => SlideLazy),
|
||||||
|
|
||||||
|
// Tabs
|
||||||
|
forwardRef(() => Tabs),
|
||||||
|
forwardRef(() => Tab),
|
||||||
|
|
||||||
|
// Toolbar
|
||||||
|
forwardRef(() => Toolbar),
|
||||||
|
forwardRef(() => ToolbarTitle),
|
||||||
|
forwardRef(() => ToolbarItem),
|
||||||
|
|
||||||
|
// Media
|
||||||
|
forwardRef(() => Icon),
|
||||||
|
|
||||||
|
// Forms
|
||||||
|
forwardRef(() => SearchBar),
|
||||||
|
forwardRef(() => Segment),
|
||||||
|
forwardRef(() => SegmentButton),
|
||||||
|
forwardRef(() => SegmentControlValueAccessor),
|
||||||
|
forwardRef(() => Checkbox),
|
||||||
|
forwardRef(() => RadioGroup),
|
||||||
|
forwardRef(() => RadioButton),
|
||||||
|
forwardRef(() => Switch),
|
||||||
|
forwardRef(() => TextInput),
|
||||||
|
forwardRef(() => TextInputElement),
|
||||||
|
forwardRef(() => Label),
|
||||||
|
|
||||||
|
// Nav
|
||||||
|
forwardRef(() => Nav),
|
||||||
|
forwardRef(() => NavbarTemplate),
|
||||||
|
forwardRef(() => Navbar),
|
||||||
|
|
||||||
|
forwardRef(() => NavPush),
|
||||||
|
forwardRef(() => NavPop),
|
||||||
|
forwardRef(() => NavRouter),
|
||||||
|
forwardRef(() => IdRef),
|
||||||
|
|
||||||
|
forwardRef(() => ShowWhen),
|
||||||
|
forwardRef(() => HideWhen)
|
||||||
|
];
|
||||||
|
>>>>>>> master
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
class IonicViewImpl extends View {
|
||||||
|
constructor(args = {}) {
|
||||||
|
args.directives = (args.directives || []).concat(IONIC_DIRECTIVES);
|
||||||
|
super(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The IonicView decorator indicates that the decorated class is an Ionic
|
||||||
|
* navigation view, meaning it can be navigated to using a [NavController](../../Nav/NavController/#creating_views)
|
||||||
|
*
|
||||||
|
* Ionic views have all [IONIC_DIRECTIVES](../IONIC_DIRECTIVES/), which include
|
||||||
|
* all Ionic components, as well as Angular's [CORE_DIRECTIVES](https://angular.io/docs/js/latest/api/core/CORE_DIRECTIVES-const.html)
|
||||||
|
* and [FORM_DIRECTIVES](https://angular.io/docs/js/latest/api/core/FORM_DIRECTIVES-const.html),
|
||||||
|
* already provided to them, so you only need to supply custom directives to
|
||||||
|
* your Ionic views:
|
||||||
|
*
|
||||||
|
* ```ts
|
||||||
|
* @IonicView({
|
||||||
|
* template: '<ion-checkbox my-custom-dir>' +
|
||||||
|
* '</ion-checkbox>'
|
||||||
|
* directives: [MyCustomDirective]
|
||||||
|
* })
|
||||||
|
* class MyPage {}
|
||||||
|
* ```
|
||||||
|
* Here [Checkbox](../../../components/checkbox/Checkbox/) will load because
|
||||||
|
* it is in IONIC_DIRECTIVES, so there is no need to add it to the `directives`
|
||||||
|
* array.
|
||||||
|
*
|
||||||
|
* For custom components that use Ionic components, you will need to include
|
||||||
|
* IONIC_DIRECTIVES in the `directives` array:
|
||||||
|
*
|
||||||
|
* ```ts
|
||||||
|
* import {IONIC_DIRECTIVES} from 'ionic/ionic';
|
||||||
|
* @Component({
|
||||||
|
* template: `<div class="customStyle">
|
||||||
|
* <ion-checkbox></ion-checkbox>
|
||||||
|
* </div>`
|
||||||
|
* })
|
||||||
|
* @View({
|
||||||
|
* directives: [IONIC_DIRECTIVES]
|
||||||
|
* })
|
||||||
|
* class MyCustomCheckbox {}
|
||||||
|
*```
|
||||||
|
* Alternatively, you could:
|
||||||
|
* ```ts
|
||||||
|
* import {Checkbox} from 'ionic/ionic'
|
||||||
|
* ```
|
||||||
|
* along with any other components and add them individually:
|
||||||
|
* ```
|
||||||
|
* @View({
|
||||||
|
* directives: [Checkbox]
|
||||||
|
* })
|
||||||
|
* ```
|
||||||
|
* However, using IONIC_DIRECTIVES will always Just Work :tm: with no
|
||||||
|
* performance overhead, so there is really no reason to not always use it.
|
||||||
|
*
|
||||||
|
* Ionic views are also automatically wrapped in `<ion-view>`, so although you
|
||||||
|
* may see these tags if you inspect your markup, you don't need to include them
|
||||||
|
* in your templates.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export function IonicView(args) {
|
||||||
|
return function(cls) {
|
||||||
|
var annotations = Reflect.getMetadata('annotations', cls) || [];
|
||||||
|
annotations.push(new IonicViewImpl(args));
|
||||||
|
Reflect.defineMetadata('annotations', annotations, cls);
|
||||||
|
return cls;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
*/
|
||||||
|
export function IonicDirective(config) {
|
||||||
|
return function(cls) {
|
||||||
|
var annotations = Reflect.getMetadata('annotations', cls) || [];
|
||||||
|
annotations.push(new Directive(appendConfig(cls, config)));
|
||||||
|
Reflect.defineMetadata('annotations', annotations, cls);
|
||||||
|
return cls;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
*/
|
||||||
|
export function IonicComponent(config) {
|
||||||
|
return function(cls) {
|
||||||
|
return makeComponent(cls, appendConfig(cls, config));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function makeComponent(cls, config) {
|
||||||
|
var annotations = Reflect.getMetadata('annotations', cls) || [];
|
||||||
|
annotations.push(new Component(config));
|
||||||
|
Reflect.defineMetadata('annotations', annotations, cls);
|
||||||
|
return cls;
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendConfig(cls, config) {
|
||||||
|
config.host = config.host || {};
|
||||||
|
|
||||||
|
cls.defaultProperties = config.defaultProperties || {};
|
||||||
|
|
||||||
|
config.properties = config.properties || [];
|
||||||
|
|
||||||
|
for (let prop in cls.defaultProperties) {
|
||||||
|
// add the property to the component "properties"
|
||||||
|
config.properties.push(prop);
|
||||||
|
|
||||||
|
// set the component "hostProperties", so the instance's
|
||||||
|
// property value will be used to set the element's attribute
|
||||||
|
config.host['[attr.' + util.pascalCaseToDashCase(prop) + ']'] = prop;
|
||||||
|
}
|
||||||
|
|
||||||
|
cls.delegates = config.delegates;
|
||||||
|
|
||||||
|
let componentId = config.classId || (config.selector && config.selector.replace('ion-', ''));
|
||||||
|
config.host['class'] = ((config.host['class'] || '') + ' ' + componentId).trim();
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
*/
|
||||||
|
export function App(args={}) {
|
||||||
|
return function(cls) {
|
||||||
|
// get current annotations
|
||||||
|
let annotations = Reflect.getMetadata('annotations', cls) || [];
|
||||||
|
|
||||||
|
// create @Component
|
||||||
|
args.selector = args.selector || 'ion-app';
|
||||||
|
annotations.push(new Component(args));
|
||||||
|
|
||||||
|
// create @View
|
||||||
|
// if no template was provided, default so it has a root ion-nav
|
||||||
|
if (!args.templateUrl && !args.template) {
|
||||||
|
args.template = '<ion-nav></ion-nav>';
|
||||||
|
}
|
||||||
|
|
||||||
|
annotations.push(new IonicViewImpl(args));
|
||||||
|
|
||||||
|
// redefine with added annotations
|
||||||
|
Reflect.defineMetadata('annotations', annotations, cls);
|
||||||
|
|
||||||
|
bootstrap(cls, ionicBindings(cls, args.config));
|
||||||
|
|
||||||
|
return cls;
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,7 @@
|
|||||||
"url": "https://github.com/driftyco/ionic2.git"
|
"url": "https://github.com/driftyco/ionic2.git"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "gulp tests",
|
"test": "gulp karma",
|
||||||
"link": "npm install && gulp src && npm link"
|
"link": "npm install && gulp src && npm link"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
Reference in New Issue
Block a user