mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-22 13:32:54 +08:00
refactor(input): split TextInput and ItemInput components
Make it easier to start adding other inputs, like select
This commit is contained in:
61
ionic/components/item-input/item-input.ios.scss
Normal file
61
ionic/components/item-input/item-input.ios.scss
Normal file
@ -0,0 +1,61 @@
|
||||
@import "../../globals.ios";
|
||||
@import "./item-input";
|
||||
|
||||
// iOS Input
|
||||
// --------------------------------------------------
|
||||
|
||||
$text-input-ios-background-color: $list-ios-background-color !default;
|
||||
|
||||
$text-input-ios-input-clear-icon-width: 30px !default;
|
||||
$text-input-ios-input-clear-icon-color: rgba(0, 0, 0, 0.5) !default;
|
||||
$text-input-ios-input-clear-icon-svg: "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'><path fill='" + $text-input-ios-input-clear-icon-color + "' d='M403.1,108.9c-81.2-81.2-212.9-81.2-294.2,0s-81.2,212.9,0,294.2c81.2,81.2,212.9,81.2,294.2,0S484.3,190.1,403.1,108.9z M352,340.2L340.2,352l-84.4-84.2l-84,83.8L160,339.8l84-83.8l-84-83.8l11.8-11.8l84,83.8l84.4-84.2l11.8,11.8L267.6,256L352,340.2z'/></svg>" !default;
|
||||
$text-input-ios-input-clear-icon-size: 18px !default;
|
||||
|
||||
|
||||
// Default Input
|
||||
// --------------------------------------------------
|
||||
|
||||
.item-input {
|
||||
margin: $item-ios-padding-top ($item-ios-padding-right / 2) $item-ios-padding-bottom 0;
|
||||
padding: 0;
|
||||
background-color: $text-input-ios-background-color;
|
||||
}
|
||||
|
||||
|
||||
// Inset Input
|
||||
// --------------------------------------------------
|
||||
|
||||
.inset-input {
|
||||
margin: ($item-ios-padding-top / 2) $item-ios-padding-right ($item-ios-padding-bottom / 2) $item-ios-padding-left;
|
||||
padding: ($item-ios-padding-top / 2) ($item-ios-padding-right / 2) ($item-ios-padding-bottom / 2) ($item-ios-padding-left / 2);
|
||||
}
|
||||
|
||||
|
||||
// Stacked & Floating Inputs
|
||||
// --------------------------------------------------
|
||||
|
||||
.stacked-input,
|
||||
.floating-input {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
// Clear Input Icon
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-input[clearInput] {
|
||||
position: relative;
|
||||
|
||||
.text-input {
|
||||
padding-right: $text-input-ios-input-clear-icon-width;
|
||||
}
|
||||
}
|
||||
|
||||
.text-input-clear-icon {
|
||||
width: $text-input-ios-input-clear-icon-width;
|
||||
|
||||
@include svg-background-image($text-input-ios-input-clear-icon-svg);
|
||||
background-size: $text-input-ios-input-clear-icon-size;
|
||||
right: ($item-ios-padding-right / 2);
|
||||
bottom: 0;
|
||||
}
|
94
ionic/components/item-input/item-input.md.scss
Normal file
94
ionic/components/item-input/item-input.md.scss
Normal file
@ -0,0 +1,94 @@
|
||||
@import "../../globals.md";
|
||||
@import "./item-input";
|
||||
|
||||
// Material Design Input
|
||||
// --------------------------------------------------
|
||||
|
||||
$text-input-md-background-color: $list-md-background-color !default;
|
||||
$text-input-md-highlight-color: map-get($colors-md, primary) !default;
|
||||
$text-input-md-hightlight-color-valid: map-get($colors-md, secondary) !default;
|
||||
$text-input-md-hightlight-color-invalid: map-get($colors-md, danger) !default;
|
||||
|
||||
$text-input-md-input-clear-icon-width: 30px !default;
|
||||
$text-input-md-input-clear-icon-color: #5B5B5B !default;
|
||||
$text-input-md-input-clear-icon-svg: "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'><polygon fill='" + $text-input-md-input-clear-icon-color + "' points='405,136.798 375.202,107 256,226.202 136.798,107 107,136.798 226.202,256 107,375.202 136.798,405 256,285.798 375.202,405 405,375.202 285.798,256'/></svg>" !default;
|
||||
$text-input-md-input-clear-icon-size: 22px !default;
|
||||
|
||||
|
||||
|
||||
// Default Input
|
||||
// --------------------------------------------------
|
||||
|
||||
.item-input {
|
||||
margin: $item-md-padding-top ($item-md-padding-right / 2) $item-md-padding-bottom ($item-md-padding-left / 2);
|
||||
padding: 0;
|
||||
background-color: $text-input-md-background-color;
|
||||
}
|
||||
|
||||
|
||||
// Inset Input
|
||||
// --------------------------------------------------
|
||||
|
||||
.inset-input {
|
||||
margin: ($item-md-padding-top / 2) $item-md-padding-right ($item-md-padding-bottom / 2) $item-md-padding-left;
|
||||
padding: ($item-md-padding-top / 2) ($item-md-padding-right / 2) ($item-md-padding-bottom / 2) ($item-md-padding-left / 2);
|
||||
}
|
||||
|
||||
|
||||
// Highlighted Input
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-input:after {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: $item-md-padding-left;
|
||||
border-bottom-width: 2px;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: transparent;
|
||||
content: '';
|
||||
}
|
||||
|
||||
.input-focused:after {
|
||||
border-bottom-color: $text-input-md-highlight-color;
|
||||
}
|
||||
|
||||
ion-input.ng-valid.input-has-value:after {
|
||||
border-bottom-color: $text-input-md-hightlight-color-valid;
|
||||
}
|
||||
|
||||
ion-input.ng-invalid.ng-touched:after {
|
||||
border-bottom-color: $text-input-md-hightlight-color-invalid;
|
||||
}
|
||||
|
||||
|
||||
// Stacked & Floating Inputs
|
||||
// --------------------------------------------------
|
||||
|
||||
.stacked-input,
|
||||
.floating-input {
|
||||
margin-left: 0;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
|
||||
// Clear Input Icon
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-input[clearInput] {
|
||||
position: relative;
|
||||
|
||||
.text-input {
|
||||
padding-right: $text-input-md-input-clear-icon-width;
|
||||
}
|
||||
}
|
||||
|
||||
.text-input-clear-icon {
|
||||
width: $text-input-md-input-clear-icon-width;
|
||||
|
||||
@include svg-background-image($text-input-md-input-clear-icon-svg);
|
||||
background-size: $text-input-md-input-clear-icon-size;
|
||||
right: ($item-md-padding-right / 2);
|
||||
bottom: 2px;
|
||||
}
|
59
ionic/components/item-input/item-input.scss
Normal file
59
ionic/components/item-input/item-input.scss
Normal file
@ -0,0 +1,59 @@
|
||||
@import "../../globals.core";
|
||||
|
||||
// Input
|
||||
// --------------------------------------------------
|
||||
|
||||
.item-input {
|
||||
display: block;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
flex: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
input.item-input:-webkit-autofill {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
input,
|
||||
textarea {
|
||||
@include placeholder();
|
||||
}
|
||||
|
||||
.platform-mobile textarea {
|
||||
resize: none;
|
||||
}
|
||||
|
||||
|
||||
// Scroll Assist
|
||||
// --------------------------------------------------
|
||||
|
||||
[scroll-assist] {
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
// Input focused
|
||||
// --------------------------------------------------
|
||||
|
||||
.input-focused .item-input {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
|
||||
// Clear Input Icon
|
||||
// --------------------------------------------------
|
||||
|
||||
.text-input-clear-icon {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
position: absolute;
|
||||
}
|
534
ionic/components/item-input/item-input.ts
Normal file
534
ionic/components/item-input/item-input.ts
Normal file
@ -0,0 +1,534 @@
|
||||
import {Component, Directive, Attribute, forwardRef, Host, Optional, ElementRef, Renderer, Input, ContentChild, ContentChildren, HostListener} from 'angular2/core';
|
||||
import {NgIf} from 'angular2/common';
|
||||
|
||||
import {NavController} from '../nav/nav-controller';
|
||||
import {Config} from '../../config/config';
|
||||
import {Form} from '../../util/form';
|
||||
import {Label} from '../label/label';
|
||||
import {TextInput} from '../text-input/text-input';
|
||||
import {IonicApp} from '../app/app';
|
||||
import {Content} from '../content/content';
|
||||
import {pointerCoord, hasPointerMoved} from '../../util/dom';
|
||||
import {Platform} from '../../platform/platform';
|
||||
import {Button} from '../button/button';
|
||||
import {Icon} from '../icon/icon';
|
||||
|
||||
|
||||
/**
|
||||
* @name Input
|
||||
* @module ionic
|
||||
* @description
|
||||
*
|
||||
* `ion-input` is a generic wrapper for both inputs and textareas. You can give `ion-input` attributes to tell it how to handle a child `ion-label` component.
|
||||
*
|
||||
* @property [fixed-label] - a persistant label that sits next the the input
|
||||
* @property [floating-label] - a label that will float about the input if the input is empty of looses focus
|
||||
* @property [stacked-label] - A stacked label will always appear on top of the input
|
||||
* @property [inset] - The input will be inset
|
||||
* @property [clearInput] - A clear icon will appear in the input which clears it
|
||||
*
|
||||
* @usage
|
||||
* ```html
|
||||
* <ion-input>
|
||||
* <ion-label>Username</ion-label>
|
||||
* <input type="text" value="">
|
||||
* </ion-input>
|
||||
*
|
||||
* <ion-input clearInput>
|
||||
* <input type="text" placeholder="Username">
|
||||
* </ion-input>
|
||||
*
|
||||
* <ion-input fixed-label>
|
||||
* <ion-label>Username</ion-label>
|
||||
* <input type="text" value="">
|
||||
* </ion-input>
|
||||
*
|
||||
* <ion-input floating-label>
|
||||
* <ion-label>Username</ion-label>
|
||||
* <input type="text" value="">
|
||||
* </ion-input>
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
|
||||
@Component({
|
||||
selector: 'ion-input',
|
||||
host: {
|
||||
'(touchstart)': 'pointerStart($event)',
|
||||
'(touchend)': 'pointerEnd($event)',
|
||||
'(mouseup)': 'pointerEnd($event)',
|
||||
'class': 'item',
|
||||
'[class.ng-untouched]': 'hasClass("ng-untouched")',
|
||||
'[class.ng-touched]': 'hasClass("ng-touched")',
|
||||
'[class.ng-pristine]': 'hasClass("ng-pristine")',
|
||||
'[class.ng-dirty]': 'hasClass("ng-dirty")',
|
||||
'[class.ng-valid]': 'hasClass("ng-valid")',
|
||||
'[class.ng-invalid]': 'hasClass("ng-invalid")'
|
||||
},
|
||||
template:
|
||||
'<div class="item-inner">' +
|
||||
'<ng-content></ng-content>' +
|
||||
'<input [type]="type" aria-hidden="true" scroll-assist *ngIf="_assist">' +
|
||||
'<button clear *ngIf="clearInput && value" class="text-input-clear-icon" (click)="clearTextInput()" (mousedown)="clearTextInput()"></button>' +
|
||||
'</div>',
|
||||
directives: [NgIf, forwardRef(() => InputScrollAssist), TextInput, Button]
|
||||
})
|
||||
export class ItemInput {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@Input() clearInput: any;
|
||||
value: string = '';
|
||||
|
||||
constructor(
|
||||
config: Config,
|
||||
private _form: Form,
|
||||
private _renderer: Renderer,
|
||||
private _elementRef: ElementRef,
|
||||
private _app: IonicApp,
|
||||
private _platform: Platform,
|
||||
@Optional() @Host() private _scrollView: Content,
|
||||
@Optional() private _nav: NavController,
|
||||
@Attribute('floating-label') isFloating: string,
|
||||
@Attribute('stacked-label') isStacked: string,
|
||||
@Attribute('fixed-label') isFixed: string,
|
||||
@Attribute('inset') isInset: string
|
||||
) {
|
||||
_form.register(this);
|
||||
|
||||
this.type = null;
|
||||
this.lastTouch = 0;
|
||||
|
||||
// make more gud with pending @Attributes API
|
||||
this.displayType = (isFloating === '' ? 'floating' : (isStacked === '' ? 'stacked' : (isFixed === '' ? 'fixed' : (isInset === '' ? 'inset' : null))));
|
||||
|
||||
this._assist = config.get('scrollAssist');
|
||||
this.keyboardHeight = config.get('keyboardHeight');
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@ContentChild(TextInput)
|
||||
set _setInput(textInput) {
|
||||
if (textInput) {
|
||||
textInput.addClass('item-input');
|
||||
if (this.displayType) {
|
||||
textInput.addClass(this.displayType + '-input');
|
||||
}
|
||||
this.input = textInput;
|
||||
this.type = textInput.type;
|
||||
|
||||
this.hasValue(this.input.value);
|
||||
textInput.valueChange.subscribe(inputValue => {
|
||||
this.hasValue(inputValue);
|
||||
});
|
||||
|
||||
this.focusChange(this.hasFocus());
|
||||
textInput.focusChange.subscribe(textInputHasFocus => {
|
||||
this.focusChange(textInputHasFocus);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@ContentChild(Label)
|
||||
set _setLabel(label) {
|
||||
if (label && this.displayType) {
|
||||
label.addClass(this.displayType + '-label');
|
||||
}
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@ContentChildren(Button)
|
||||
set _buttons(buttons) {
|
||||
buttons.toArray().forEach(button => {
|
||||
if (!button.isItem) {
|
||||
button.addClass('item-button');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@ContentChildren(Icon)
|
||||
set _icons(icons) {
|
||||
icons.toArray().forEach(icon => {
|
||||
icon.addClass('item-icon');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* On Initialization check for attributes
|
||||
*/
|
||||
ngOnInit() {
|
||||
let clearInput = this.clearInput;
|
||||
if (typeof clearInput === 'string') {
|
||||
this.clearInput = (clearInput === '' || clearInput === 'true');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ngAfterViewInit() {
|
||||
let self = this;
|
||||
if (self.input && self.label) {
|
||||
// if there is an input and a label
|
||||
// then give the label an ID
|
||||
// and tell the input the ID of who it's labelled by
|
||||
self.input.labelledBy(self.label.id);
|
||||
}
|
||||
|
||||
self.scrollMove = function(ev) {
|
||||
if (!(self._nav && self._nav.isTransitioning())) {
|
||||
self.deregMove();
|
||||
|
||||
if (self.hasFocus()) {
|
||||
self.input.hideFocus(true);
|
||||
self._scrollView.onScrollEnd(function() {
|
||||
self.input.hideFocus(false);
|
||||
|
||||
if (self.hasFocus()) {
|
||||
self.regMove();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
clearTextInput() {
|
||||
console.log("Should clear input");
|
||||
//console.log(this.textInput.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
pointerStart(ev) {
|
||||
if (this._assist && this._app.isEnabled()) {
|
||||
// remember where the touchstart/mousedown started
|
||||
this.startCoord = pointerCoord(ev);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
pointerEnd(ev) {
|
||||
if (!this._app.isEnabled()) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
} else if (this._assist && ev.type === 'touchend') {
|
||||
// get where the touchend/mouseup ended
|
||||
let endCoord = pointerCoord(ev);
|
||||
|
||||
// focus this input if the pointer hasn't moved XX pixels
|
||||
// and the input doesn't already have focus
|
||||
if (!hasPointerMoved(8, this.startCoord, endCoord) && !this.hasFocus()) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
this.initFocus();
|
||||
|
||||
// temporarily prevent mouseup's from focusing
|
||||
this.lastTouch = Date.now();
|
||||
}
|
||||
|
||||
} else if (this.lastTouch + 999 < Date.now()) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
this.setFocus();
|
||||
this.regMove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
initFocus() {
|
||||
// begin the process of setting focus to the inner input element
|
||||
|
||||
let scrollView = this._scrollView;
|
||||
|
||||
if (scrollView && this._assist) {
|
||||
// this input is inside of a scroll view
|
||||
|
||||
// find out if text input should be manually scrolled into view
|
||||
let ele = this._elementRef.nativeElement;
|
||||
|
||||
let scrollData = ItemInput.getScrollData(ele.offsetTop, ele.offsetHeight, scrollView.getDimensions(), this.keyboardHeight, this._platform.height());
|
||||
if (scrollData.scrollAmount > -3 && scrollData.scrollAmount < 3) {
|
||||
// the text input is in a safe position that doesn't require
|
||||
// it to be scrolled into view, just set focus now
|
||||
this.setFocus();
|
||||
this.regMove();
|
||||
return;
|
||||
}
|
||||
|
||||
// add padding to the bottom of the scroll view (if needed)
|
||||
scrollView.addScrollPadding(scrollData.scrollPadding);
|
||||
|
||||
// manually scroll the text input to the top
|
||||
// do not allow any clicks while it's scrolling
|
||||
let scrollDuration = getScrollAssistDuration(scrollData.scrollAmount);
|
||||
this._app.setEnabled(false, scrollDuration);
|
||||
this._nav && this._nav.setTransitioning(true, scrollDuration);
|
||||
|
||||
// temporarily move the focus to the focus holder so the browser
|
||||
// doesn't freak out while it's trying to get the input in place
|
||||
// at this point the native text input still does not have focus
|
||||
this.input.relocate(true, scrollData.inputSafeY);
|
||||
|
||||
// scroll the input into place
|
||||
scrollView.scrollTo(0, scrollData.scrollTo, scrollDuration).then(() => {
|
||||
// the scroll view is in the correct position now
|
||||
// give the native text input focus
|
||||
this.input.relocate(false);
|
||||
|
||||
// all good, allow clicks again
|
||||
this._app.setEnabled(true);
|
||||
this._nav && this._nav.setTransitioning(false);
|
||||
this.regMove();
|
||||
});
|
||||
|
||||
} else {
|
||||
// not inside of a scroll view, just focus it
|
||||
this.setFocus();
|
||||
this.regMove();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
setFocus() {
|
||||
if (this.input) {
|
||||
this._form.setAsFocused(this);
|
||||
|
||||
// set focus on the actual input element
|
||||
this.input.setFocus();
|
||||
|
||||
// ensure the body hasn't scrolled down
|
||||
document.body.scrollTop = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
regMove() {
|
||||
if (this._assist && this._scrollView) {
|
||||
setTimeout(() => {
|
||||
this.deregMove();
|
||||
this.deregScroll = this._scrollView.addScrollEventListener(this.scrollMove);
|
||||
}, 80);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
deregMove() {
|
||||
this.deregScroll && this.deregScroll();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
focusChange(inputHasFocus) {
|
||||
this._renderer.setElementClass(this._elementRef, 'input-focused', inputHasFocus);
|
||||
if (!inputHasFocus) {
|
||||
this.deregMove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
hasFocus() {
|
||||
return !!this.input && this.input.hasFocus();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
hasValue(inputValue) {
|
||||
let inputHasValue = !!(inputValue && inputValue !== '');
|
||||
this._renderer.setElementClass(this._elementRef, 'input-has-value', inputHasValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* This function is used to add the Angular css classes associated with inputs in forms
|
||||
*/
|
||||
hasClass(className) {
|
||||
this.input && this.input.hasClass(className);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ngOnDestroy() {
|
||||
this.deregMove();
|
||||
this._form.deregister(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
static getScrollData(inputOffsetTop, inputOffsetHeight, scrollViewDimensions, keyboardHeight, plaformHeight) {
|
||||
// compute input's Y values relative to the body
|
||||
let inputTop = (inputOffsetTop + scrollViewDimensions.contentTop - scrollViewDimensions.scrollTop);
|
||||
let inputBottom = (inputTop + inputOffsetHeight);
|
||||
|
||||
// compute the safe area which is the viewable content area when the soft keyboard is up
|
||||
let safeAreaTop = scrollViewDimensions.contentTop;
|
||||
let safeAreaHeight = plaformHeight - keyboardHeight - safeAreaTop;
|
||||
safeAreaHeight /= 2;
|
||||
let safeAreaBottom = safeAreaTop + safeAreaHeight;
|
||||
|
||||
let inputTopWithinSafeArea = (inputTop >= safeAreaTop && inputTop <= safeAreaBottom);
|
||||
let inputTopAboveSafeArea = (inputTop < safeAreaTop);
|
||||
let inputTopBelowSafeArea = (inputTop > safeAreaBottom);
|
||||
let inputBottomWithinSafeArea = (inputBottom >= safeAreaTop && inputBottom <= safeAreaBottom);
|
||||
let inputBottomBelowSafeArea = (inputBottom > safeAreaBottom);
|
||||
|
||||
/*
|
||||
Text Input Scroll To Scenarios
|
||||
---------------------------------------
|
||||
1) Input top within safe area, bottom within safe area
|
||||
2) Input top within safe area, bottom below safe area, room to scroll
|
||||
3) Input top above safe area, bottom within safe area, room to scroll
|
||||
4) Input top below safe area, no room to scroll, input smaller than safe area
|
||||
5) Input top within safe area, bottom below safe area, no room to scroll, input smaller than safe area
|
||||
6) Input top within safe area, bottom below safe area, no room to scroll, input larger than safe area
|
||||
7) Input top below safe area, no room to scroll, input larger than safe area
|
||||
*/
|
||||
|
||||
let scrollData = {
|
||||
scrollAmount: 0,
|
||||
scrollTo: 0,
|
||||
scrollPadding: 0,
|
||||
inputSafeY: 0
|
||||
};
|
||||
|
||||
if (inputTopWithinSafeArea && inputBottomWithinSafeArea) {
|
||||
// Input top within safe area, bottom within safe area
|
||||
// no need to scroll to a position, it's good as-is
|
||||
return scrollData;
|
||||
}
|
||||
|
||||
// looks like we'll have to do some auto-scrolling
|
||||
if (inputTopBelowSafeArea || inputBottomBelowSafeArea) {
|
||||
// Input top and bottom below safe area
|
||||
// auto scroll the input up so at least the top of it shows
|
||||
|
||||
if (safeAreaHeight > inputOffsetHeight) {
|
||||
// safe area height is taller than the input height, so we
|
||||
// can bring it up the input just enough to show the input bottom
|
||||
scrollData.scrollAmount = Math.round(safeAreaBottom - inputBottom);
|
||||
|
||||
} else {
|
||||
// safe area height is smaller than the input height, so we can
|
||||
// only scroll it up so the input top is at the top of the safe area
|
||||
// however the input bottom will be below the safe area
|
||||
scrollData.scrollAmount = Math.round(safeAreaTop - inputTop);
|
||||
}
|
||||
|
||||
scrollData.inputSafeY = -(inputTop - safeAreaTop) + 4;
|
||||
|
||||
} else if (inputTopAboveSafeArea) {
|
||||
// Input top above safe area
|
||||
// auto scroll the input down so at least the top of it shows
|
||||
scrollData.scrollAmount = Math.round(safeAreaTop - inputTop);
|
||||
|
||||
scrollData.inputSafeY = (safeAreaTop - inputTop) + 4;
|
||||
}
|
||||
|
||||
// figure out where it should scroll to for the best position to the input
|
||||
scrollData.scrollTo = (scrollViewDimensions.scrollTop - scrollData.scrollAmount);
|
||||
|
||||
if (scrollData.scrollAmount < 0) {
|
||||
// when auto-scrolling up, there also needs to be enough
|
||||
// content padding at the bottom of the scroll view
|
||||
// manually add it if there isn't enough scrollable area
|
||||
|
||||
// figure out how many scrollable area is left to scroll up
|
||||
let availablePadding = (scrollViewDimensions.scrollHeight - scrollViewDimensions.scrollTop) - scrollViewDimensions.contentHeight;
|
||||
|
||||
let paddingSpace = availablePadding + scrollData.scrollAmount;
|
||||
if (paddingSpace < 0) {
|
||||
// there's not enough scrollable area at the bottom, so manually add more
|
||||
scrollData.scrollPadding = (scrollViewDimensions.contentHeight - safeAreaHeight);
|
||||
}
|
||||
}
|
||||
|
||||
// if (!window.safeAreaEle) {
|
||||
// window.safeAreaEle = document.createElement('div');
|
||||
// window.safeAreaEle.style.position = 'absolute';
|
||||
// window.safeAreaEle.style.background = 'rgba(0, 128, 0, 0.7)';
|
||||
// window.safeAreaEle.style.padding = '2px 5px';
|
||||
// window.safeAreaEle.style.textShadow = '1px 1px white';
|
||||
// window.safeAreaEle.style.left = '0px';
|
||||
// window.safeAreaEle.style.right = '0px';
|
||||
// window.safeAreaEle.style.fontWeight = 'bold';
|
||||
// window.safeAreaEle.style.pointerEvents = 'none';
|
||||
// document.body.appendChild(window.safeAreaEle);
|
||||
// }
|
||||
// window.safeAreaEle.style.top = safeAreaTop + 'px';
|
||||
// window.safeAreaEle.style.height = safeAreaHeight + 'px';
|
||||
// window.safeAreaEle.innerHTML = `
|
||||
// <div>scrollTo: ${scrollData.scrollTo}</div>
|
||||
// <div>scrollAmount: ${scrollData.scrollAmount}</div>
|
||||
// <div>scrollPadding: ${scrollData.scrollPadding}</div>
|
||||
// <div>inputSafeY: ${scrollData.inputSafeY}</div>
|
||||
// <div>scrollHeight: ${scrollViewDimensions.scrollHeight}</div>
|
||||
// <div>scrollTop: ${scrollViewDimensions.scrollTop}</div>
|
||||
// <div>contentHeight: ${scrollViewDimensions.contentHeight}</div>
|
||||
// `;
|
||||
|
||||
return scrollData;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[scroll-assist]'
|
||||
})
|
||||
class InputScrollAssist {
|
||||
|
||||
constructor(private _form: Form, private _input: ItemInput) {}
|
||||
|
||||
@HostListener('focus')
|
||||
receivedFocus() {
|
||||
this._form.focusNext(this._input);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const SCROLL_ASSIST_SPEED = 0.4;
|
||||
|
||||
function getScrollAssistDuration(distanceToScroll) {
|
||||
//return 3000;
|
||||
distanceToScroll = Math.abs(distanceToScroll);
|
||||
let duration = distanceToScroll / SCROLL_ASSIST_SPEED;
|
||||
return Math.min(400, Math.max(100, duration));
|
||||
}
|
@ -0,0 +1 @@
|
||||
|
@ -0,0 +1,7 @@
|
||||
import {App} from 'ionic/ionic';
|
||||
|
||||
|
||||
@App({
|
||||
templateUrl: 'main.html'
|
||||
})
|
||||
class E2EApp {}
|
@ -0,0 +1,79 @@
|
||||
|
||||
<ion-toolbar><ion-title>Fixed Inline Label Text Input</ion-title></ion-toolbar>
|
||||
|
||||
|
||||
<ion-content>
|
||||
|
||||
<ion-list>
|
||||
|
||||
<ion-input fixed-label>
|
||||
<ion-label>To</ion-label>
|
||||
<input value="Text 1" type="text">
|
||||
</ion-input>
|
||||
|
||||
<ion-input fixed-label>
|
||||
<ion-label>CC</ion-label>
|
||||
<input value="Text 2" type="text">
|
||||
</ion-input>
|
||||
|
||||
<ion-input fixed-label>
|
||||
<ion-label>From</ion-label>
|
||||
<input value="Text 3" type="text">
|
||||
<button clear item-right>
|
||||
<ion-icon name="power"></ion-icon>
|
||||
</button>
|
||||
</ion-input>
|
||||
|
||||
<ion-input fixed-label>
|
||||
<ion-label>Comments</ion-label>
|
||||
<textarea>Comment value</textarea>
|
||||
</ion-input>
|
||||
|
||||
<ion-input fixed-label>
|
||||
<ion-icon name="globe" item-left></ion-icon>
|
||||
<ion-label>Website</ion-label>
|
||||
<input value="http://ionic.io/" type="url">
|
||||
</ion-input>
|
||||
|
||||
<ion-input fixed-label>
|
||||
<ion-icon name="mail" item-left></ion-icon>
|
||||
<ion-label>Email</ion-label>
|
||||
<input value="email6@email.com" type="email">
|
||||
</ion-input>
|
||||
|
||||
<ion-input fixed-label>
|
||||
<ion-icon name="call" item-left></ion-icon>
|
||||
<ion-label>Phone</ion-label>
|
||||
<input value="867-5309" type="tel">
|
||||
</ion-input>
|
||||
|
||||
<ion-input fixed-label>
|
||||
<ion-icon name="contact" item-left></ion-icon>
|
||||
<input placeholder="Placeholder Text" type="text">
|
||||
</ion-input>
|
||||
|
||||
<ion-input fixed-label>
|
||||
<ion-label>Score</ion-label>
|
||||
<input value="10" type="number">
|
||||
<button outline item-right>Update</button>
|
||||
</ion-input>
|
||||
|
||||
<ion-input fixed-label>
|
||||
<ion-label>First Name</ion-label>
|
||||
<input value="Buzz" type="text">
|
||||
</ion-input>
|
||||
|
||||
<ion-input fixed-label>
|
||||
<ion-label>Last Name</ion-label>
|
||||
<input value="Lightyear" type="text">
|
||||
</ion-input>
|
||||
|
||||
<ion-input fixed-label>
|
||||
<ion-icon name="create" item-left></ion-icon>
|
||||
<ion-label>Message</ion-label>
|
||||
<textarea>To infinity and beyond</textarea>
|
||||
</ion-input>
|
||||
|
||||
</ion-list>
|
||||
|
||||
</ion-content>
|
1
ionic/components/item-input/test/floating-labels/e2e.ts
Normal file
1
ionic/components/item-input/test/floating-labels/e2e.ts
Normal file
@ -0,0 +1 @@
|
||||
|
15
ionic/components/item-input/test/floating-labels/index.ts
Normal file
15
ionic/components/item-input/test/floating-labels/index.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import {App} from 'ionic/ionic';
|
||||
|
||||
|
||||
@App({
|
||||
templateUrl: 'main.html'
|
||||
})
|
||||
class E2EApp {
|
||||
constructor() {
|
||||
this.myValues = {
|
||||
value1: 'Dynamic Input',
|
||||
value2: 'Dynamic Textarea'
|
||||
};
|
||||
}
|
||||
|
||||
}
|
63
ionic/components/item-input/test/floating-labels/main.html
Normal file
63
ionic/components/item-input/test/floating-labels/main.html
Normal file
@ -0,0 +1,63 @@
|
||||
|
||||
<ion-toolbar><ion-title>Floating Label Text Input</ion-title></ion-toolbar>
|
||||
|
||||
|
||||
<ion-content>
|
||||
|
||||
<ion-list inset>
|
||||
|
||||
<ion-input floating-label>
|
||||
<ion-label>Floating Label 1</ion-label>
|
||||
<input [(ngModel)]='myValues.value1' type="text">
|
||||
</ion-input>
|
||||
Value: {{ myValues.value1 }}
|
||||
|
||||
<ion-input floating-label>
|
||||
<ion-label>Floating Label 2</ion-label>
|
||||
<input value="Has Value" type="text">
|
||||
</ion-input>
|
||||
|
||||
<ion-input floating-label>
|
||||
<ion-label>Floating Label 3</ion-label>
|
||||
<input type="number">
|
||||
</ion-input>
|
||||
|
||||
<ion-input floating-label>
|
||||
<ion-label primary>Floating Label 4</ion-label>
|
||||
<textarea [(ngModel)]='myValues.value2'></textarea>
|
||||
</ion-input>
|
||||
Value: {{ myValues.value2 }}
|
||||
|
||||
<ion-input floating-label>
|
||||
<ion-label secondary>Floating Label 5</ion-label>
|
||||
<input type="url">
|
||||
</ion-input>
|
||||
|
||||
<ion-input floating-label>
|
||||
<ion-label danger>Floating Label 6</ion-label>
|
||||
<input type="email">
|
||||
</ion-input>
|
||||
|
||||
<ion-input floating-label>
|
||||
<ion-label>Floating Label 7</ion-label>
|
||||
<textarea></textarea>
|
||||
</ion-input>
|
||||
|
||||
<ion-input floating-label>
|
||||
<ion-label>Floating Label 8</ion-label>
|
||||
<input type="text">
|
||||
</ion-input>
|
||||
|
||||
<ion-input floating-label>
|
||||
<ion-label>Floating Label 9</ion-label>
|
||||
<input type="number">
|
||||
</ion-input>
|
||||
|
||||
<ion-input floating-label>
|
||||
<ion-label>Floating Label 10</ion-label>
|
||||
<textarea></textarea>
|
||||
</ion-input>
|
||||
|
||||
</ion-list>
|
||||
|
||||
</ion-content>
|
0
ionic/components/item-input/test/form-inputs/e2e.ts
Normal file
0
ionic/components/item-input/test/form-inputs/e2e.ts
Normal file
35
ionic/components/item-input/test/form-inputs/index.ts
Normal file
35
ionic/components/item-input/test/form-inputs/index.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import {App} from 'ionic/ionic';
|
||||
import {FormBuilder, Validators} from 'angular2/common';
|
||||
|
||||
|
||||
@App({
|
||||
templateUrl: 'main.html'
|
||||
})
|
||||
class E2EApp {
|
||||
constructor(fb: FormBuilder) {
|
||||
this.loginForm = fb.group({
|
||||
email: ["", Validators.required],
|
||||
username: [""],
|
||||
password: ["", Validators.required],
|
||||
comments: ["", Validators.required]
|
||||
});
|
||||
|
||||
this.login = {
|
||||
email: 'help@ionic.io',
|
||||
username: 'admin'
|
||||
};
|
||||
|
||||
this.user = {
|
||||
username: 'asdf',
|
||||
password: '82'
|
||||
};
|
||||
|
||||
this.submitted = false;
|
||||
}
|
||||
|
||||
submit(ev, value) {
|
||||
console.log("Submitted", value);
|
||||
this.submitted = true;
|
||||
}
|
||||
|
||||
}
|
88
ionic/components/item-input/test/form-inputs/main.html
Normal file
88
ionic/components/item-input/test/form-inputs/main.html
Normal file
@ -0,0 +1,88 @@
|
||||
|
||||
<ion-toolbar><ion-title>Inline Label Text Input</ion-title></ion-toolbar>
|
||||
|
||||
|
||||
<ion-content>
|
||||
|
||||
<form [ngFormModel]="loginForm" #mf="ngForm" novalidate>
|
||||
|
||||
<ion-list>
|
||||
<ion-input floating-label clearInput>
|
||||
<ion-label>Email</ion-label>
|
||||
<input [(ngModel)]="login.email" ngControl="email" type="email" required>
|
||||
</ion-input>
|
||||
|
||||
<ion-input floating-label clearInput>
|
||||
<ion-label>Username</ion-label>
|
||||
<input [(ngModel)]="login.username" ngControl="username" type="text">
|
||||
</ion-input>
|
||||
|
||||
<ion-input floating-label clearInput>
|
||||
<ion-label>Password</ion-label>
|
||||
<input [(ngModel)]="login.password" ngControl="password" type="password" required>
|
||||
</ion-input>
|
||||
|
||||
<ion-input floating-label clearInput>
|
||||
<ion-label>Comments</ion-label>
|
||||
<textarea [(ngModel)]="login.comments" ngControl="comments" required>Comment value</textarea>
|
||||
</ion-input>
|
||||
|
||||
<div padding-left padding-right>
|
||||
<button block (click)="submit($event, login)">Login</button>
|
||||
</div>
|
||||
<div padding-left>
|
||||
<b>Valid form?:</b> {{ mf.form.valid }}<br>
|
||||
<b>Submitted form?:</b> {{ submitted }}<br>
|
||||
<b>Email:</b> {{ login.email }}<br>
|
||||
<b>Username:</b> {{ login.username }}<br>
|
||||
<b>Password:</b> {{ login.password }}<br>
|
||||
<b>Comments:</b> {{ login.comments }}
|
||||
</div>
|
||||
</ion-list>
|
||||
</form>
|
||||
|
||||
<form (ngSubmit)="submit($event, user)" #lf="ngForm">
|
||||
<ion-list>
|
||||
<ion-input floating-label clearInput>
|
||||
<ion-label>Username</ion-label>
|
||||
<input type="text" [(ngModel)]="user.username" ngControl="username" required>
|
||||
</ion-input>
|
||||
<ion-input floating-label clearInput>
|
||||
<ion-label>Password</ion-label>
|
||||
<input type="password" [(ngModel)]="user.password" ngControl="password" required>
|
||||
</ion-input>
|
||||
<div padding-left padding-right clearInput>
|
||||
<button block type="submit">Login</button>
|
||||
</div>
|
||||
<div padding-left>
|
||||
<b>Valid form?:</b> {{ lf.form.valid }}<br>
|
||||
<b>Submitted form?:</b> {{ submitted }}<br>
|
||||
<b>Username:</b> {{ user.username }}<br>
|
||||
<b>Password:</b> {{ user.password }}<br>
|
||||
</div>
|
||||
</ion-list>
|
||||
</form>
|
||||
|
||||
<ion-list>
|
||||
<ion-input clearInput>
|
||||
<ion-label>Email</ion-label>
|
||||
<input type="email" required>
|
||||
</ion-input>
|
||||
|
||||
<ion-input clearInput>
|
||||
<ion-label>Username</ion-label>
|
||||
<input type="text">
|
||||
</ion-input>
|
||||
|
||||
<ion-input clearInput>
|
||||
<ion-label>Password</ion-label>
|
||||
<input type="password" required>
|
||||
</ion-input>
|
||||
|
||||
<ion-input clearInput>
|
||||
<ion-label>Comments</ion-label>
|
||||
<textarea required>Comment value</textarea>
|
||||
</ion-input>
|
||||
</ion-list>
|
||||
|
||||
</ion-content>
|
1
ionic/components/item-input/test/inline-labels/e2e.ts
Normal file
1
ionic/components/item-input/test/inline-labels/e2e.ts
Normal file
@ -0,0 +1 @@
|
||||
|
12
ionic/components/item-input/test/inline-labels/index.ts
Normal file
12
ionic/components/item-input/test/inline-labels/index.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import {App} from 'ionic/ionic';
|
||||
|
||||
|
||||
@App({
|
||||
templateUrl: 'main.html'
|
||||
})
|
||||
class E2EApp {
|
||||
|
||||
submit(ev) {
|
||||
}
|
||||
|
||||
}
|
99
ionic/components/item-input/test/inline-labels/main.html
Normal file
99
ionic/components/item-input/test/inline-labels/main.html
Normal file
@ -0,0 +1,99 @@
|
||||
|
||||
<ion-toolbar><ion-title>Inline Label Text Input</ion-title></ion-toolbar>
|
||||
|
||||
|
||||
<ion-content>
|
||||
|
||||
<form (ng-submit)="submit($event)">
|
||||
|
||||
<ion-list>
|
||||
|
||||
<ion-input>
|
||||
<ion-label>To:</ion-label>
|
||||
<input value="Text 1" type="text">
|
||||
</ion-input>
|
||||
|
||||
<ion-input>
|
||||
<ion-label>CC:</ion-label>
|
||||
<input value="Text 2" type="text">
|
||||
</ion-input>
|
||||
|
||||
<ion-input>
|
||||
<ion-label>From:</ion-label>
|
||||
<input value="Text 3" type="text">
|
||||
<button clear item-right>
|
||||
<ion-icon name="power"></ion-icon>
|
||||
</button>
|
||||
</ion-input>
|
||||
|
||||
<ion-input>
|
||||
<ion-label>Comments:</ion-label>
|
||||
<textarea>Comment value</textarea>
|
||||
</ion-input>
|
||||
|
||||
<ion-input>
|
||||
<ion-icon name="globe" item-left></ion-icon>
|
||||
<ion-label>Website:</ion-label>
|
||||
<input value="http://ionic.io/" type="url">
|
||||
</ion-input>
|
||||
|
||||
<ion-input>
|
||||
<ion-icon name="mail" item-left></ion-icon>
|
||||
<ion-label>Email:</ion-label>
|
||||
<input value="email6@email.com" type="email">
|
||||
</ion-input>
|
||||
|
||||
<ion-input>
|
||||
<ion-icon name="create" item-left></ion-icon>
|
||||
<ion-label>Feedback:</ion-label>
|
||||
<textarea placeholder="Placeholder Text"></textarea>
|
||||
</ion-input>
|
||||
|
||||
<ion-input>
|
||||
<ion-label>More Info:</ion-label>
|
||||
<input placeholder="Placeholder Text" type="text">
|
||||
<ion-icon name="flag" item-right></ion-icon>
|
||||
</ion-input>
|
||||
|
||||
<ion-input>
|
||||
<ion-label>Score:</ion-label>
|
||||
<input value="10" type="number">
|
||||
<button outline item-right>Update</button>
|
||||
</ion-input>
|
||||
|
||||
<ion-input>
|
||||
<ion-label>First Name:</ion-label>
|
||||
<input value="Lightning" type="text">
|
||||
</ion-input>
|
||||
|
||||
<ion-input>
|
||||
<ion-label>Last Name:</ion-label>
|
||||
<input value="McQueen" type="text">
|
||||
</ion-input>
|
||||
|
||||
<ion-input>
|
||||
<ion-label>Message:</ion-label>
|
||||
<textarea>KA-CHOW!</textarea>
|
||||
</ion-input>
|
||||
|
||||
<ion-input>
|
||||
<ion-label>Beginning:</ion-label>
|
||||
<input value="2005-07-30" type="date">
|
||||
</ion-input>
|
||||
|
||||
<ion-input>
|
||||
<ion-label>Time:</ion-label>
|
||||
<input value="12:00pm" type="time">
|
||||
</ion-input>
|
||||
|
||||
</ion-list>
|
||||
|
||||
<ion-input>
|
||||
<ion-label>No List</ion-label>
|
||||
<input value="ListFreeeee" type="text">
|
||||
</ion-input>
|
||||
<textarea></textarea>
|
||||
|
||||
</form>
|
||||
|
||||
</ion-content>
|
14
ionic/components/item-input/test/input-focus/index.ts
Normal file
14
ionic/components/item-input/test/input-focus/index.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import {App} from 'ionic/ionic';
|
||||
|
||||
|
||||
@App({
|
||||
templateUrl: 'main.html',
|
||||
config: {
|
||||
scrollAssist: true
|
||||
}
|
||||
})
|
||||
class E2EApp {
|
||||
reload() {
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
129
ionic/components/item-input/test/input-focus/main.html
Normal file
129
ionic/components/item-input/test/input-focus/main.html
Normal file
@ -0,0 +1,129 @@
|
||||
|
||||
<ion-toolbar>
|
||||
<ion-title>Inset Focus</ion-title>
|
||||
<ion-buttons end>
|
||||
<button (click)="reload()">
|
||||
Reload
|
||||
</button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
|
||||
|
||||
<ion-content>
|
||||
|
||||
<p>Paragraph text with a <a href="#">link</a>.</p>
|
||||
|
||||
<ion-list>
|
||||
|
||||
<ion-input>
|
||||
<ion-label>Text 1:</ion-label>
|
||||
<input type="text">
|
||||
</ion-input>
|
||||
|
||||
<ion-item>
|
||||
Item with button right
|
||||
<button item-right>Button 1</button>
|
||||
</ion-item>
|
||||
|
||||
<ion-input>
|
||||
<ion-label id="my-label1">Text 2:</ion-label>
|
||||
<input value="value" type="text">
|
||||
</ion-input>
|
||||
|
||||
<button ion-item>
|
||||
Button Item
|
||||
</button>
|
||||
|
||||
<ion-input>
|
||||
<ion-label>Text 3:</ion-label>
|
||||
<input type="text">
|
||||
<button clear item-right>
|
||||
<ion-icon name="power"></ion-icon>
|
||||
</button>
|
||||
</ion-input>
|
||||
|
||||
<ion-input>
|
||||
<ion-label>Comments:</ion-label>
|
||||
<textarea>Comment value</textarea>
|
||||
</ion-input>
|
||||
|
||||
<ion-input>
|
||||
<ion-icon name="globe" item-left></ion-icon>
|
||||
<ion-label>Website:</ion-label>
|
||||
<input value="http://ionic.io/" type="url">
|
||||
</ion-input>
|
||||
|
||||
<ion-input>
|
||||
<ion-icon name="mail" item-left></ion-icon>
|
||||
<ion-label>Email:</ion-label>
|
||||
<input value="email6@email.com" type="email">
|
||||
</ion-input>
|
||||
|
||||
<ion-input>
|
||||
<ion-icon name="create" item-left></ion-icon>
|
||||
<ion-label>Feedback:</ion-label>
|
||||
<textarea placeholder="Placeholder Text"></textarea>
|
||||
</ion-input>
|
||||
|
||||
<ion-toggle>
|
||||
Toggle
|
||||
</ion-toggle>
|
||||
|
||||
<ion-input>
|
||||
<ion-label>More Info:</ion-label>
|
||||
<input placeholder="Placeholder Text" type="text">
|
||||
<ion-icon name="flag" item-right></ion-icon>
|
||||
</ion-input>
|
||||
|
||||
<ion-checkbox>
|
||||
Checkbox
|
||||
</ion-checkbox>
|
||||
|
||||
<ion-input>
|
||||
<ion-label>Score:</ion-label>
|
||||
<input value="10" type="number">
|
||||
<button outline item-right>Update</button>
|
||||
</ion-input>
|
||||
|
||||
<ion-input>
|
||||
<ion-label>First Name:</ion-label>
|
||||
<input value="Lightning" type="text">
|
||||
</ion-input>
|
||||
|
||||
<ion-input>
|
||||
<ion-label>Last Name:</ion-label>
|
||||
<input value="McQueen" type="text">
|
||||
</ion-input>
|
||||
|
||||
<ion-input>
|
||||
<ion-label>Message:</ion-label>
|
||||
<textarea>KA-CHOW!</textarea>
|
||||
</ion-input>
|
||||
|
||||
<ion-item>
|
||||
Item
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
Item
|
||||
</ion-item>
|
||||
|
||||
</ion-list>
|
||||
|
||||
<ion-list radio-group>
|
||||
|
||||
<ion-list-header>
|
||||
Radios
|
||||
</ion-list-header>
|
||||
|
||||
<ion-radio value="1">
|
||||
Radio 1
|
||||
</ion-radio>
|
||||
|
||||
<ion-radio value="2">
|
||||
Radio 2
|
||||
</ion-radio>
|
||||
|
||||
</ion-list>
|
||||
|
||||
</ion-content>
|
1
ionic/components/item-input/test/inset-inputs/e2e.ts
Normal file
1
ionic/components/item-input/test/inset-inputs/e2e.ts
Normal file
@ -0,0 +1 @@
|
||||
|
7
ionic/components/item-input/test/inset-inputs/index.ts
Normal file
7
ionic/components/item-input/test/inset-inputs/index.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import {App} from 'ionic/ionic';
|
||||
|
||||
|
||||
@App({
|
||||
templateUrl: 'main.html'
|
||||
})
|
||||
class E2EApp {}
|
71
ionic/components/item-input/test/inset-inputs/main.html
Normal file
71
ionic/components/item-input/test/inset-inputs/main.html
Normal file
@ -0,0 +1,71 @@
|
||||
|
||||
<ion-toolbar>
|
||||
<ion-title>Inset Inputs</ion-title>
|
||||
</ion-toolbar>
|
||||
|
||||
|
||||
<ion-content class="outer-content">
|
||||
|
||||
|
||||
<ion-list inset>
|
||||
|
||||
<ion-input>
|
||||
<ion-icon name="search" item-left></ion-icon>
|
||||
<input value="Input inside an inset list" type="text">
|
||||
</ion-input>
|
||||
|
||||
<ion-input>
|
||||
<ion-icon name="call" item-left></ion-icon>
|
||||
<input type="tel">
|
||||
</ion-input>
|
||||
|
||||
<ion-input>
|
||||
<ion-icon name="mail" item-left></ion-icon>
|
||||
<input placeholder="Placeholder text" type="text">
|
||||
</ion-input>
|
||||
|
||||
</ion-list>
|
||||
|
||||
|
||||
<ion-list inset>
|
||||
|
||||
<ion-input inset>
|
||||
<ion-icon name="search" item-left></ion-icon>
|
||||
<input value="Inset input inside an inset list" type="text">
|
||||
</ion-input>
|
||||
|
||||
<ion-input inset>
|
||||
<input value="Inset input inside an inset list" type="text">
|
||||
<ion-icon name="mic" item-right></ion-icon>
|
||||
</ion-input>
|
||||
|
||||
</ion-list>
|
||||
|
||||
|
||||
<ion-list>
|
||||
|
||||
<ion-input inset>
|
||||
<ion-icon name="search" item-left></ion-icon>
|
||||
<input value="Inset input inside a list" type="text">
|
||||
<button outline item-right>Search</button>
|
||||
</ion-input>
|
||||
|
||||
</ion-list>
|
||||
|
||||
|
||||
<ion-card>
|
||||
|
||||
<ion-input>
|
||||
<ion-icon name="search" item-left></ion-icon>
|
||||
<input value="Input inside a card" type="text">
|
||||
</ion-input>
|
||||
|
||||
<ion-input>
|
||||
<input value="Input inside a card" type="text">
|
||||
<ion-icon name="mic" item-right></ion-icon>
|
||||
</ion-input>
|
||||
|
||||
</ion-card>
|
||||
|
||||
|
||||
</ion-content>
|
@ -0,0 +1 @@
|
||||
|
@ -0,0 +1,7 @@
|
||||
import {App} from 'ionic/ionic';
|
||||
|
||||
|
||||
@App({
|
||||
templateUrl: 'main.html'
|
||||
})
|
||||
class E2EApp {}
|
@ -0,0 +1,59 @@
|
||||
|
||||
<ion-toolbar><ion-title>Placeholder Label Text Input</ion-title></ion-toolbar>
|
||||
|
||||
|
||||
<ion-content class="outer-content">
|
||||
|
||||
<ion-list>
|
||||
|
||||
<ion-input>
|
||||
<input placeholder="Text Input Placeholder" type="text">
|
||||
</ion-input>
|
||||
|
||||
<ion-input>
|
||||
<input placeholder="Text Input Placeholder" value="Text Input Value" type="text">
|
||||
</ion-input>
|
||||
|
||||
<ion-input>
|
||||
<ion-icon name="call" item-left></ion-icon>
|
||||
<input placeholder="Text Input Placeholder" type="text">
|
||||
</ion-input>
|
||||
|
||||
<ion-input>
|
||||
<ion-icon name="call" item-left></ion-icon>
|
||||
<input placeholder="Text Input Placeholder" value="Text Input Value" type="text">
|
||||
</ion-input>
|
||||
|
||||
<ion-input>
|
||||
<textarea placeholder="Textarea Placeholder"></textarea>
|
||||
</ion-input>
|
||||
|
||||
<ion-input>
|
||||
<textarea placeholder="Textarea Placeholder">Textarea value</textarea>
|
||||
</ion-input>
|
||||
|
||||
</ion-list>
|
||||
|
||||
<ion-list inset>
|
||||
|
||||
<ion-input>
|
||||
<input placeholder="Inset List: Text Input Placeholder" type="text">
|
||||
</ion-input>
|
||||
|
||||
<ion-input>
|
||||
<input placeholder="Inset List: Text Input Placeholder" value="Inset List: Text Input Value" type="text">
|
||||
</ion-input>
|
||||
|
||||
<ion-input>
|
||||
<ion-icon name="call" item-left></ion-icon>
|
||||
<input placeholder="Inset List: Text Input Placeholder" type="text">
|
||||
</ion-input>
|
||||
|
||||
<ion-input>
|
||||
<ion-icon name="call" item-left></ion-icon>
|
||||
<input placeholder="Inset List: Text Input Placeholder" value="Inset List: Text Input Value" type="text">
|
||||
</ion-input>
|
||||
|
||||
</ion-list>
|
||||
|
||||
</ion-content>
|
1
ionic/components/item-input/test/stacked-labels/e2e.ts
Normal file
1
ionic/components/item-input/test/stacked-labels/e2e.ts
Normal file
@ -0,0 +1 @@
|
||||
|
7
ionic/components/item-input/test/stacked-labels/index.ts
Normal file
7
ionic/components/item-input/test/stacked-labels/index.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import {App} from 'ionic/ionic';
|
||||
|
||||
|
||||
@App({
|
||||
templateUrl: 'main.html'
|
||||
})
|
||||
class E2EApp {}
|
61
ionic/components/item-input/test/stacked-labels/main.html
Normal file
61
ionic/components/item-input/test/stacked-labels/main.html
Normal file
@ -0,0 +1,61 @@
|
||||
|
||||
<ion-toolbar><ion-title>Stacked Label Text Input</ion-title></ion-toolbar>
|
||||
|
||||
|
||||
<ion-content>
|
||||
|
||||
<ion-list>
|
||||
|
||||
<ion-input stacked-label>
|
||||
<ion-label>Label 1</ion-label>
|
||||
<input value="Text 1" type="text">
|
||||
</ion-input>
|
||||
|
||||
<ion-input stacked-label>
|
||||
<ion-label>Label 2</ion-label>
|
||||
<input value="Text 2" type="text">
|
||||
</ion-input>
|
||||
|
||||
<ion-input stacked-label>
|
||||
<ion-label>Label 3</ion-label>
|
||||
<input value="3" type="number">
|
||||
</ion-input>
|
||||
|
||||
<ion-input stacked-label>
|
||||
<ion-label>Label 4</ion-label>
|
||||
<textarea>Textarea 4</textarea>
|
||||
</ion-input>
|
||||
|
||||
<ion-input stacked-label>
|
||||
<ion-label>Label 5</ion-label>
|
||||
<input value="http://url5.com/" type="url">
|
||||
</ion-input>
|
||||
|
||||
<ion-input stacked-label>
|
||||
<ion-label>Label 6</ion-label>
|
||||
<input value="email6@email.com" type="email">
|
||||
</ion-input>
|
||||
|
||||
<ion-input stacked-label>
|
||||
<ion-label>Label 7</ion-label>
|
||||
<textarea>Textarea 7</textarea>
|
||||
</ion-input>
|
||||
|
||||
<ion-input stacked-label>
|
||||
<ion-label>Label 8</ion-label>
|
||||
<input value="Text 8" type="text">
|
||||
</ion-input>
|
||||
|
||||
<ion-input stacked-label>
|
||||
<ion-label>Label 9</ion-label>
|
||||
<input value="9" type="number">
|
||||
</ion-input>
|
||||
|
||||
<ion-input stacked-label>
|
||||
<ion-label>Label 10</ion-label>
|
||||
<textarea>Textarea 10</textarea>
|
||||
</ion-input>
|
||||
|
||||
</ion-list>
|
||||
|
||||
</ion-content>
|
135
ionic/components/item-input/test/text-input.spec.ts
Normal file
135
ionic/components/item-input/test/text-input.spec.ts
Normal file
@ -0,0 +1,135 @@
|
||||
import {ItemInput} from 'ionic/ionic';
|
||||
|
||||
export function run() {
|
||||
|
||||
it('should scroll, top and bottom below safe area, no room to scroll', () => {
|
||||
let inputOffsetTop = 350;
|
||||
let inputOffsetHeight = 35;
|
||||
let scrollViewDimensions = {
|
||||
contentTop: 100,
|
||||
contentHeight: 700,
|
||||
scrollTop: 30,
|
||||
scrollHeight: 700
|
||||
};
|
||||
let keyboardHeight = 400;
|
||||
let platformHeight = 800;
|
||||
|
||||
let scrollData = ItemInput.getScrollData(inputOffsetTop, inputOffsetHeight, scrollViewDimensions, keyboardHeight, platformHeight);
|
||||
|
||||
expect(scrollData.scrollAmount).toBe(-205);
|
||||
expect(scrollData.scrollTo).toBe(235);
|
||||
expect(scrollData.scrollPadding).toBe(550);
|
||||
});
|
||||
|
||||
it('should scroll, top and bottom below safe area, room to scroll', () => {
|
||||
let inputOffsetTop = 350;
|
||||
let inputOffsetHeight = 35;
|
||||
let scrollViewDimensions = {
|
||||
contentTop: 100,
|
||||
contentHeight: 700,
|
||||
scrollTop: 30,
|
||||
scrollHeight: 1000
|
||||
};
|
||||
let keyboardHeight = 400;
|
||||
let platformHeight = 800;
|
||||
|
||||
let scrollData = ItemInput.getScrollData(inputOffsetTop, inputOffsetHeight, scrollViewDimensions, keyboardHeight, platformHeight);
|
||||
|
||||
expect(scrollData.scrollAmount).toBe(-205);
|
||||
expect(scrollData.scrollTo).toBe(235);
|
||||
expect(scrollData.scrollPadding).toBe(0);
|
||||
});
|
||||
|
||||
it('should scroll, top above safe', () => {
|
||||
// Input top within safe area, bottom below safe area, room to scroll
|
||||
let inputOffsetTop = 100;
|
||||
let inputOffsetHeight = 33;
|
||||
let scrollViewDimensions = {
|
||||
contentTop: 100,
|
||||
contentHeight: 700,
|
||||
scrollTop: 250,
|
||||
scrollHeight: 700
|
||||
};
|
||||
let keyboardHeight = 400;
|
||||
let platformHeight = 800;
|
||||
|
||||
let scrollData = ItemInput.getScrollData(inputOffsetTop, inputOffsetHeight, scrollViewDimensions, keyboardHeight, platformHeight);
|
||||
|
||||
expect(scrollData.scrollAmount).toBe(150);
|
||||
expect(scrollData.scrollTo).toBe(100);
|
||||
expect(scrollData.scrollPadding).toBe(0);
|
||||
});
|
||||
|
||||
it('should scroll, top in safe, bottom below safe, below more than top in, not enough padding', () => {
|
||||
// Input top within safe area, bottom below safe area, room to scroll
|
||||
let inputOffsetTop = 100;
|
||||
let inputOffsetHeight = 320;
|
||||
let scrollViewDimensions = {
|
||||
contentTop: 100,
|
||||
contentHeight: 700,
|
||||
scrollTop: 20,
|
||||
scrollHeight: 700
|
||||
};
|
||||
let keyboardHeight = 400;
|
||||
let platformHeight = 800;
|
||||
|
||||
let scrollData = ItemInput.getScrollData(inputOffsetTop, inputOffsetHeight, scrollViewDimensions, keyboardHeight, platformHeight);
|
||||
|
||||
expect(scrollData.scrollAmount).toBe(-80);
|
||||
expect(scrollData.scrollTo).toBe(100);
|
||||
expect(scrollData.scrollPadding).toBe(550);
|
||||
});
|
||||
|
||||
it('should scroll, top in safe, bottom below safe, below more than top in, enough padding', () => {
|
||||
// Input top within safe area, bottom below safe area, room to scroll
|
||||
let inputOffsetTop = 20;
|
||||
let inputOffsetHeight = 330;
|
||||
let scrollViewDimensions = {
|
||||
contentTop: 100,
|
||||
scrollTop: 0,
|
||||
};
|
||||
let keyboardHeight = 400;
|
||||
let platformHeight = 800;
|
||||
|
||||
let scrollData = ItemInput.getScrollData(inputOffsetTop, inputOffsetHeight, scrollViewDimensions, keyboardHeight, platformHeight);
|
||||
|
||||
expect(scrollData.scrollAmount).toBe(-20);
|
||||
expect(scrollData.scrollTo).toBe(20);
|
||||
expect(scrollData.scrollPadding).toBe(0);
|
||||
});
|
||||
|
||||
it('should scroll, top in safe, bottom below safe, below less than top in, enough padding', () => {
|
||||
// Input top within safe area, bottom below safe area, room to scroll
|
||||
let inputOffsetTop = 250;
|
||||
let inputOffsetHeight = 80; // goes 30px below safe area
|
||||
let scrollViewDimensions = {
|
||||
contentTop: 100,
|
||||
scrollTop: 0,
|
||||
};
|
||||
let keyboardHeight = 400;
|
||||
let platformHeight = 800;
|
||||
|
||||
let scrollData = ItemInput.getScrollData(inputOffsetTop, inputOffsetHeight, scrollViewDimensions, keyboardHeight, platformHeight);
|
||||
|
||||
expect(scrollData.scrollAmount).toBe(-180);
|
||||
expect(scrollData.scrollTo).toBe(180);
|
||||
expect(scrollData.scrollPadding).toBe(0);
|
||||
});
|
||||
|
||||
it('should not scroll, top in safe, bottom in safe', () => {
|
||||
// Input top within safe area, bottom within safe area
|
||||
let inputOffsetTop = 100;
|
||||
let inputOffsetHeight = 50;
|
||||
let scrollViewDimensions = {
|
||||
contentTop: 100,
|
||||
scrollTop: 0,
|
||||
keyboardTop: 400
|
||||
};
|
||||
let keyboardHeight = 400;
|
||||
let platformHeight = 800;
|
||||
|
||||
let scrollData = ItemInput.getScrollData(inputOffsetTop, inputOffsetHeight, scrollViewDimensions, keyboardHeight, platformHeight);
|
||||
expect(scrollData.scrollAmount).toBe(0);
|
||||
});
|
||||
|
||||
}
|
Reference in New Issue
Block a user