mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
chore(build): rename ionic directory to src and update all references in the build process.
This commit is contained in:
585
src/components/input/input-base.ts
Normal file
585
src/components/input/input-base.ts
Normal file
@@ -0,0 +1,585 @@
|
||||
import {Directive, Input, Output, EventEmitter, HostListener, ViewChild, ElementRef} from '@angular/core';
|
||||
import {NgControl} from '@angular/common';
|
||||
|
||||
import {Config} from '../../config/config';
|
||||
import {Content} from '../content/content';
|
||||
import {Form} from '../../util/form';
|
||||
import {Item} from '../item/item';
|
||||
import {IonicApp} from '../app/app';
|
||||
import {isTrueProperty} from '../../util/util';
|
||||
import {Label} from '../label/label';
|
||||
import {pointerCoord, hasPointerMoved, closest, copyInputAttributes} from '../../util/dom';
|
||||
import {NavController} from '../nav/nav-controller';
|
||||
import {NativeInput, NextInput} from './native-input';
|
||||
import {Platform} from '../../platform/platform';
|
||||
|
||||
|
||||
export class InputBase {
|
||||
protected _coord;
|
||||
protected _deregScroll;
|
||||
protected _disabled: boolean = false;
|
||||
protected _keyboardHeight;
|
||||
protected _scrollMove: EventListener;
|
||||
protected _type: string = 'text';
|
||||
protected _useAssist: boolean;
|
||||
protected _usePadding: boolean;
|
||||
protected _value = '';
|
||||
protected _isTouch: boolean;
|
||||
protected _autoFocusAssist: string;
|
||||
protected _autoComplete: string;
|
||||
protected _autoCorrect: string;
|
||||
|
||||
inputControl: NgControl;
|
||||
|
||||
@Input() clearInput;
|
||||
@Input() placeholder: string = '';
|
||||
@ViewChild(NativeInput) protected _native: NativeInput;
|
||||
@Output() blur: EventEmitter<Event> = new EventEmitter;
|
||||
@Output() focus: EventEmitter<Event> = new EventEmitter;
|
||||
|
||||
constructor(
|
||||
config: Config,
|
||||
protected _form: Form,
|
||||
protected _item: Item,
|
||||
protected _app: IonicApp,
|
||||
protected _platform: Platform,
|
||||
protected _elementRef: ElementRef,
|
||||
protected _scrollView: Content,
|
||||
protected _nav: NavController,
|
||||
ngControl: NgControl
|
||||
) {
|
||||
this._useAssist = config.getBoolean('scrollAssist', false);
|
||||
this._usePadding = config.getBoolean('scrollPadding', this._useAssist);
|
||||
this._keyboardHeight = config.getNumber('keyboardHeight');
|
||||
|
||||
this._autoFocusAssist = config.get('autoFocusAssist', 'delay');
|
||||
this._autoComplete = config.get('autocomplete', 'off');
|
||||
this._autoCorrect = config.get('autocorrect', 'off');
|
||||
|
||||
if (ngControl) {
|
||||
ngControl.valueAccessor = this;
|
||||
}
|
||||
|
||||
_form.register(this);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
if (this._item) {
|
||||
this._item.setCssClass('item-input', true);
|
||||
this._item.registerInput(this._type);
|
||||
}
|
||||
|
||||
let clearInput = this.clearInput;
|
||||
if (typeof clearInput === 'string') {
|
||||
this.clearInput = (clearInput === '' || clearInput === 'true');
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterContentInit() {
|
||||
let self = this;
|
||||
|
||||
self._scrollMove = function(ev: UIEvent) {
|
||||
// scroll move event listener this instance can reuse
|
||||
if (!(self._nav && self._nav.isTransitioning())) {
|
||||
self.deregScrollMove();
|
||||
|
||||
if (self.hasFocus()) {
|
||||
self._native.hideFocus(true);
|
||||
|
||||
self._scrollView.onScrollEnd(function() {
|
||||
self._native.hideFocus(false);
|
||||
|
||||
if (self.hasFocus()) {
|
||||
// if it still has focus then keep listening
|
||||
self.regScrollMove();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.setItemControlCss();
|
||||
}
|
||||
|
||||
ngAfterContentChecked() {
|
||||
this.setItemControlCss();
|
||||
}
|
||||
|
||||
private setItemControlCss() {
|
||||
let item = this._item;
|
||||
let nativeControl = this._native && this._native.ngControl;
|
||||
|
||||
if (item && nativeControl) {
|
||||
item.setCssClass('ng-untouched', nativeControl.untouched);
|
||||
item.setCssClass('ng-touched', nativeControl.touched);
|
||||
item.setCssClass('ng-pristine', nativeControl.pristine);
|
||||
item.setCssClass('ng-dirty', nativeControl.dirty);
|
||||
item.setCssClass('ng-valid', nativeControl.valid);
|
||||
item.setCssClass('ng-invalid', !nativeControl.valid);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this._form.deregister(this);
|
||||
}
|
||||
|
||||
@Input()
|
||||
get value() {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
set value(val) {
|
||||
this._value = val;
|
||||
}
|
||||
|
||||
@Input()
|
||||
get type() {
|
||||
return this._type;
|
||||
}
|
||||
|
||||
set type(val) {
|
||||
this._type = 'text';
|
||||
|
||||
if (val) {
|
||||
val = val.toLowerCase();
|
||||
|
||||
if (/password|email|number|search|tel|url|date|month|time|week/.test(val)) {
|
||||
this._type = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Input()
|
||||
get disabled() {
|
||||
return this._disabled;
|
||||
}
|
||||
|
||||
set disabled(val) {
|
||||
this._disabled = isTrueProperty(val);
|
||||
this._item && this._item.setCssClass('item-input-disabled', this._disabled);
|
||||
this._native && this._native.isDisabled(this._disabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@ViewChild(NativeInput)
|
||||
private set _nativeInput(nativeInput: NativeInput) {
|
||||
this._native = nativeInput;
|
||||
|
||||
if (this._item && this._item.labelId !== null) {
|
||||
nativeInput.labelledBy(this._item.labelId);
|
||||
}
|
||||
|
||||
nativeInput.valueChange.subscribe(inputValue => {
|
||||
this.onChange(inputValue);
|
||||
});
|
||||
|
||||
this.focusChange(this.hasFocus());
|
||||
nativeInput.focusChange.subscribe(textInputHasFocus => {
|
||||
this.focusChange(textInputHasFocus);
|
||||
this.checkHasValue(nativeInput.getValue());
|
||||
if (!textInputHasFocus) {
|
||||
this.onTouched(textInputHasFocus);
|
||||
}
|
||||
});
|
||||
|
||||
this.checkHasValue(nativeInput.getValue());
|
||||
this.disabled = this._disabled;
|
||||
|
||||
var ionInputEle: HTMLElement = this._elementRef.nativeElement;
|
||||
let nativeInputEle: HTMLElement = nativeInput.element();
|
||||
|
||||
// copy ion-input attributes to the native input element
|
||||
copyInputAttributes(ionInputEle, nativeInputEle);
|
||||
|
||||
if (ionInputEle.hasAttribute('autofocus')) {
|
||||
// the ion-input element has the autofocus attributes
|
||||
ionInputEle.removeAttribute('autofocus');
|
||||
|
||||
if (this._autoFocusAssist === 'immediate') {
|
||||
// config says to immediate focus on the input
|
||||
// works best on android devices
|
||||
nativeInputEle.focus();
|
||||
|
||||
} else if (this._autoFocusAssist === 'delay') {
|
||||
// config says to chill out a bit and focus on the input after transitions
|
||||
// works best on desktop
|
||||
setTimeout(() => {
|
||||
nativeInputEle.focus();
|
||||
}, 650);
|
||||
}
|
||||
|
||||
// traditionally iOS has big issues with autofocus on actual devices
|
||||
// autoFocus is disabled by default with the iOS mode config
|
||||
}
|
||||
|
||||
// by default set autocomplete="off" unless specified by the input
|
||||
if (ionInputEle.hasAttribute('autocomplete')) {
|
||||
this._autoComplete = ionInputEle.getAttribute('autocomplete');
|
||||
}
|
||||
nativeInputEle.setAttribute('autocomplete', this._autoComplete);
|
||||
|
||||
// by default set autocomplete="off" unless specified by the input
|
||||
if (ionInputEle.hasAttribute('autocorrect')) {
|
||||
this._autoCorrect = ionInputEle.getAttribute('autocorrect');
|
||||
}
|
||||
nativeInputEle.setAttribute('autocorrect', this._autoCorrect);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@ViewChild(NextInput)
|
||||
private set _nextInput(nextInput: NextInput) {
|
||||
if (nextInput) {
|
||||
nextInput.focused.subscribe(() => {
|
||||
this._form.tabFocus(this);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Angular2 Forms API method called by the model (Control) on change to update
|
||||
* the checked value.
|
||||
* https://github.com/angular/angular/blob/master/modules/angular2/src/forms/directives/shared.ts#L34
|
||||
*/
|
||||
writeValue(val) {
|
||||
this._value = val;
|
||||
this.checkHasValue(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
onChange(val) {
|
||||
this.checkHasValue(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
onTouched(val) {}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
hasFocus(): boolean {
|
||||
// check if an input has focus or not
|
||||
return this._native.hasFocus();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
checkHasValue(inputValue) {
|
||||
if (this._item) {
|
||||
this._item.setCssClass('input-has-value', !!(inputValue && inputValue !== ''));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
focusChange(inputHasFocus: boolean) {
|
||||
if (this._item) {
|
||||
this._item.setCssClass('input-has-focus', inputHasFocus);
|
||||
}
|
||||
if (!inputHasFocus) {
|
||||
this.deregScrollMove();
|
||||
}
|
||||
}
|
||||
|
||||
private pointerStart(ev) {
|
||||
// input cover touchstart
|
||||
console.debug('scroll assist pointerStart', ev.type);
|
||||
|
||||
if (ev.type === 'touchstart') {
|
||||
this._isTouch = true;
|
||||
}
|
||||
|
||||
if ((this._isTouch || (!this._isTouch && ev.type === 'mousedown')) && this._app.isEnabled()) {
|
||||
// remember where the touchstart/mousedown started
|
||||
this._coord = pointerCoord(ev);
|
||||
}
|
||||
}
|
||||
|
||||
private pointerEnd(ev) {
|
||||
// input cover touchend/mouseup
|
||||
console.debug('scroll assist pointerEnd', ev.type);
|
||||
|
||||
if ((this._isTouch && ev.type === 'mouseup') || !this._app.isEnabled()) {
|
||||
// the app is actively doing something right now
|
||||
// don't try to scroll in the input
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
} else if (this._coord) {
|
||||
// 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._coord, endCoord) && !this.hasFocus()) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
// begin the input focus process
|
||||
console.debug('initFocus', ev.type);
|
||||
this.initFocus();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this._coord = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
initFocus() {
|
||||
// begin the process of setting focus to the inner input element
|
||||
var scrollView = this._scrollView;
|
||||
|
||||
if (scrollView) {
|
||||
// this input is inside of a scroll view
|
||||
// find out if text input should be manually scrolled into view
|
||||
|
||||
// get container of this input, probably an ion-item a few nodes up
|
||||
var ele = this._elementRef.nativeElement;
|
||||
ele = closest(ele, 'ion-item,[ion-item]') || ele;
|
||||
|
||||
var scrollData = InputBase.getScrollData(ele.offsetTop, ele.offsetHeight, scrollView.getContentDimensions(), 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.regScrollMove();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._usePadding) {
|
||||
// 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
|
||||
var 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._native.beginFocus(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._native.beginFocus(false, 0);
|
||||
|
||||
// ensure this is the focused input
|
||||
this.setFocus();
|
||||
|
||||
// all good, allow clicks again
|
||||
this._app.setEnabled(true);
|
||||
this._nav && this._nav.setTransitioning(false);
|
||||
this.regScrollMove();
|
||||
|
||||
if (this._usePadding) {
|
||||
this._scrollView.clearScrollPaddingFocusOut();
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
// not inside of a scroll view, just focus it
|
||||
this.setFocus();
|
||||
this.regScrollMove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private setFocus() {
|
||||
// immediately set focus
|
||||
this._form.setAsFocused(this);
|
||||
|
||||
// set focus on the actual input element
|
||||
console.debug(`input-base, setFocus ${this._native.element().value}`);
|
||||
this._native.setFocus();
|
||||
|
||||
// ensure the body hasn't scrolled down
|
||||
document.body.scrollTop = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Angular2 Forms API method called by the view (NgControl) to register the
|
||||
* onChange event handler that updates the model (Control).
|
||||
* https://github.com/angular/angular/blob/master/modules/angular2/src/forms/directives/shared.ts#L27
|
||||
* @param {Function} fn the onChange event handler.
|
||||
*/
|
||||
registerOnChange(fn) { this.onChange = fn; }
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Angular2 Forms API method called by the view (NgControl) to register
|
||||
* the onTouched event handler that marks model (Control) as touched.
|
||||
* @param {Function} fn onTouched event handler.
|
||||
*/
|
||||
registerOnTouched(fn) { this.onTouched = fn; }
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private regScrollMove() {
|
||||
// register scroll move listener
|
||||
if (this._useAssist && this._scrollView) {
|
||||
setTimeout(() => {
|
||||
this.deregScrollMove();
|
||||
this._deregScroll = this._scrollView.addScrollListener(this._scrollMove);
|
||||
}, 80);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private deregScrollMove() {
|
||||
// deregister the scroll move listener
|
||||
this._deregScroll && this._deregScroll();
|
||||
}
|
||||
|
||||
focusNext() {
|
||||
this._form.tabFocus(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;
|
||||
}
|
||||
}
|
||||
|
||||
const SCROLL_ASSIST_SPEED = 0.3;
|
||||
|
||||
function getScrollAssistDuration(distanceToScroll) {
|
||||
distanceToScroll = Math.abs(distanceToScroll);
|
||||
let duration = distanceToScroll / SCROLL_ASSIST_SPEED;
|
||||
return Math.min(400, Math.max(150, duration));
|
||||
}
|
||||
86
src/components/input/input.ios.scss
Normal file
86
src/components/input/input.ios.scss
Normal file
@@ -0,0 +1,86 @@
|
||||
@import "../../globals.ios";
|
||||
@import "./input";
|
||||
|
||||
// iOS Input
|
||||
// --------------------------------------------------
|
||||
|
||||
$text-input-ios-background-color: $list-ios-background-color !default;
|
||||
|
||||
$text-input-ios-margin-top: $item-ios-padding-top !default;
|
||||
$text-input-ios-margin-right: ($item-ios-padding-right / 2) !default;
|
||||
$text-input-ios-margin-bottom: $item-ios-padding-bottom !default;
|
||||
$text-input-ios-margin-left: 0 !default;
|
||||
|
||||
$text-input-ios-input-clear-icon-width: 30px !default;
|
||||
$text-input-ios-input-clear-icon-color: rgba(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;
|
||||
|
||||
|
||||
// iOS Default Input
|
||||
// --------------------------------------------------
|
||||
|
||||
.text-input {
|
||||
margin: $text-input-ios-margin-top $text-input-ios-margin-right $text-input-ios-margin-bottom $text-input-ios-margin-left;
|
||||
padding: 0;
|
||||
|
||||
width: calc(100% - #{$text-input-ios-margin-right} - #{$text-input-ios-margin-left});
|
||||
}
|
||||
|
||||
|
||||
// iOS Inset Input
|
||||
// --------------------------------------------------
|
||||
|
||||
.inset-input {
|
||||
margin: ($item-ios-padding-top / 2) $item-ios-padding-right ($item-ios-padding-bottom / 2) 0;
|
||||
padding: ($item-ios-padding-top / 2) ($item-ios-padding-right / 2) ($item-ios-padding-bottom / 2) ($item-ios-padding-left / 2);
|
||||
}
|
||||
|
||||
|
||||
// iOS Stacked & Floating Inputs
|
||||
// --------------------------------------------------
|
||||
|
||||
.item-label-stacked .text-input,
|
||||
.item-label-floating .text-input {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
margin-left: 0;
|
||||
|
||||
width: calc(100% - #{$text-input-ios-margin-right});
|
||||
}
|
||||
|
||||
.item-label-stacked ion-select,
|
||||
.item-label-floating ion-select {
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.item-label-floating .text-input.cloned-input,
|
||||
.item-label-stacked .text-input.cloned-input {
|
||||
top: 30px;
|
||||
}
|
||||
|
||||
|
||||
// iOS Clear Input Icon
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-input[clearInput] {
|
||||
position: relative;
|
||||
|
||||
.text-input {
|
||||
padding-right: $text-input-ios-input-clear-icon-width;
|
||||
}
|
||||
}
|
||||
|
||||
.text-input-clear-icon {
|
||||
@include svg-background-image($text-input-ios-input-clear-icon-svg);
|
||||
|
||||
right: ($item-ios-padding-right / 2);
|
||||
bottom: 0;
|
||||
|
||||
width: $text-input-ios-input-clear-icon-width;
|
||||
height: 34px;
|
||||
|
||||
background-size: $text-input-ios-input-clear-icon-size;
|
||||
}
|
||||
120
src/components/input/input.md.scss
Normal file
120
src/components/input/input.md.scss
Normal file
@@ -0,0 +1,120 @@
|
||||
@import "../../globals.md";
|
||||
@import "./input";
|
||||
|
||||
// Material Design Input
|
||||
// --------------------------------------------------
|
||||
|
||||
$text-input-md-background-color: $list-md-background-color !default;
|
||||
$text-input-md-highlight-color: color($colors-md, primary) !default;
|
||||
$text-input-md-hightlight-color-valid: color($colors-md, secondary) !default;
|
||||
$text-input-md-hightlight-color-invalid: color($colors-md, danger) !default;
|
||||
|
||||
$text-input-md-margin-top: $item-md-padding-top !default;
|
||||
$text-input-md-margin-right: ($item-md-padding-right / 2) !default;
|
||||
$text-input-md-margin-bottom: $item-md-padding-bottom !default;
|
||||
$text-input-md-margin-left: ($item-md-padding-left / 2) !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;
|
||||
|
||||
|
||||
|
||||
// Material Design Default Input
|
||||
// --------------------------------------------------
|
||||
|
||||
.text-input {
|
||||
margin: $text-input-md-margin-top $text-input-md-margin-right $text-input-md-margin-bottom $text-input-md-margin-left;
|
||||
padding: 0;
|
||||
|
||||
width: calc(100% - #{$text-input-md-margin-right} - #{$text-input-md-margin-left});
|
||||
}
|
||||
|
||||
|
||||
// Material Design 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);
|
||||
}
|
||||
|
||||
|
||||
// Material Design Highlighted Input
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-input::after {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: $item-md-padding-left;
|
||||
|
||||
border-bottom-width: 2px;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: transparent;
|
||||
content: "";
|
||||
}
|
||||
|
||||
.input-has-focus::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;
|
||||
}
|
||||
|
||||
|
||||
// Material Design Stacked & Floating Inputs
|
||||
// --------------------------------------------------
|
||||
|
||||
.item-label-stacked .text-input,
|
||||
.item-label-floating .text-input {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
margin-left: 0;
|
||||
|
||||
width: calc(100% - #{$text-input-md-margin-right});
|
||||
}
|
||||
|
||||
.item-label-stacked ion-select,
|
||||
.item-label-floating ion-select {
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.item-label-floating .text-input.cloned-input {
|
||||
top: 32px;
|
||||
}
|
||||
|
||||
.item-label-stacked .text-input.cloned-input {
|
||||
top: 27px;
|
||||
}
|
||||
|
||||
|
||||
// Material Design Clear Input Icon
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-input[clearInput] {
|
||||
position: relative;
|
||||
|
||||
.text-input {
|
||||
padding-right: $text-input-md-input-clear-icon-width;
|
||||
}
|
||||
}
|
||||
|
||||
.text-input-clear-icon {
|
||||
@include svg-background-image($text-input-md-input-clear-icon-svg);
|
||||
|
||||
right: ($item-md-padding-right / 2);
|
||||
bottom: 4px;
|
||||
|
||||
width: $text-input-md-input-clear-icon-width;
|
||||
|
||||
background-size: $text-input-md-input-clear-icon-size;
|
||||
}
|
||||
156
src/components/input/input.scss
Normal file
156
src/components/input/input.scss
Normal file
@@ -0,0 +1,156 @@
|
||||
@import "../../globals.core";
|
||||
|
||||
// Input/Textarea Wrapper
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-input,
|
||||
ion-textarea {
|
||||
position: relative;
|
||||
display: block;
|
||||
|
||||
flex: 1;
|
||||
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
.item-input ion-input,
|
||||
.item-input ion-textarea {
|
||||
position: static;
|
||||
}
|
||||
|
||||
// Textarea Within An Item
|
||||
// --------------------------------------------------
|
||||
|
||||
.item.item-textarea {
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
|
||||
// Native Text Input
|
||||
// --------------------------------------------------
|
||||
|
||||
.text-input {
|
||||
@include placeholder();
|
||||
|
||||
display: inline-block;
|
||||
|
||||
flex: 1;
|
||||
|
||||
width: 92%;
|
||||
width: calc(100% - 10px);
|
||||
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
textarea.text-input {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.text-input[disabled] {
|
||||
opacity: .4;
|
||||
}
|
||||
|
||||
|
||||
input.text-input:-webkit-autofill {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.platform-mobile textarea.text-input {
|
||||
resize: none;
|
||||
}
|
||||
|
||||
|
||||
// Input Cover: Unfocused
|
||||
// --------------------------------------------------
|
||||
// The input cover is the div that actually receives the
|
||||
// tap/click event when scroll assist is configured to true.
|
||||
// This make it so the native input element is not clickable.
|
||||
// This will only show when the scroll assist is configured
|
||||
// otherwise the .input-cover will not be rendered at all
|
||||
|
||||
.input-cover {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
||||
// Input Cover: Focused
|
||||
// --------------------------------------------------
|
||||
// When the input has focus, then the input cover should be hidden
|
||||
|
||||
.input-has-focus .input-cover {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.input-has-focus {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.input-has-focus input,
|
||||
.input-has-focus textarea {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
|
||||
// Scroll Assist Input
|
||||
// --------------------------------------------------
|
||||
// This input is used to help the app handle
|
||||
// Next and Previous input tabbing
|
||||
|
||||
[next-input] {
|
||||
position: absolute;
|
||||
bottom: 1px;
|
||||
|
||||
padding: 0;
|
||||
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
|
||||
border: 0;
|
||||
background: transparent;
|
||||
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
// Clear Input Icon
|
||||
// --------------------------------------------------
|
||||
|
||||
.text-input-clear-icon {
|
||||
position: absolute;
|
||||
display: none;
|
||||
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.input-has-value .text-input-clear-icon {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
// Cloned Input
|
||||
// --------------------------------------------------
|
||||
|
||||
.text-input.cloned-input {
|
||||
position: relative;
|
||||
top: 0;
|
||||
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.item-input:not(.item-label-floating) .text-input.cloned-active {
|
||||
display: none;
|
||||
}
|
||||
206
src/components/input/input.ts
Normal file
206
src/components/input/input.ts
Normal file
@@ -0,0 +1,206 @@
|
||||
import {Component, Optional, ElementRef, ViewChild, ViewEncapsulation} from '@angular/core';
|
||||
import {NgControl} from '@angular/common';
|
||||
|
||||
import {Config} from '../../config/config';
|
||||
import {Content} from '../content/content';
|
||||
import {Form} from '../../util/form';
|
||||
import {InputBase} from './input-base';
|
||||
import {IonicApp} from '../app/app';
|
||||
import {Item} from '../item/item';
|
||||
import {Label} from '../label/label';
|
||||
import {NativeInput, NextInput} from './native-input';
|
||||
import {NavController} from '../nav/nav-controller';
|
||||
import {Platform} from '../../platform/platform';
|
||||
|
||||
|
||||
/**
|
||||
* @name Input
|
||||
* @description
|
||||
*
|
||||
* `ion-input` is meant for text type inputs only, such as `text`,
|
||||
* `password`, `email`, `number`, `search`, `tel`, and `url`. Ionic
|
||||
* still uses an actual `<input type="text">` HTML element within the
|
||||
* component, however, with Ionic wrapping the native HTML input
|
||||
* element it's able to better handle the user experience and
|
||||
* interactivity.
|
||||
*
|
||||
* Similarily, `<ion-textarea>` should be used in place of `<textarea>`.
|
||||
*
|
||||
* An `ion-input` is **not** used for non-text type inputs, such as a
|
||||
* `checkbox`, `radio`, `toggle`, `range`, `select`, etc.
|
||||
*
|
||||
* @property [type] - The HTML input type (text, password, email, number, search, tel, or url)
|
||||
* @property [clearInput] - A clear icon will appear in the input which clears it
|
||||
*
|
||||
* @usage
|
||||
* ```html
|
||||
* <ion-item>
|
||||
* <ion-label>Username</ion-label>
|
||||
* <ion-input></ion-input>
|
||||
* </ion-item>
|
||||
*
|
||||
* <ion-item>
|
||||
* <ion-label fixed>Website</ion-label>
|
||||
* <ion-input type="url"></ion-input>
|
||||
* </ion-item>
|
||||
*
|
||||
* <ion-item>
|
||||
* <ion-label floating>Email</ion-label>
|
||||
* <ion-input type="email"></ion-input>
|
||||
* </ion-item>
|
||||
*
|
||||
* <ion-item>
|
||||
* <ion-label stacked>Phone</ion-label>
|
||||
* <ion-input type="tel"></ion-input>
|
||||
* </ion-item>
|
||||
*
|
||||
* <ion-item>
|
||||
* <ion-input placeholder="Username" clearInput></ion-input>
|
||||
* </ion-item>
|
||||
* ```
|
||||
*
|
||||
* @demo /docs/v2/demos/input/
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ion-input',
|
||||
template:
|
||||
'<input [type]="type" [(ngModel)]="_value" (blur)="inputBlurred($event)" (focus)="inputFocused($event)" [placeholder]="placeholder" class="text-input">' +
|
||||
'<input [type]="type" aria-hidden="true" next-input *ngIf="_useAssist">' +
|
||||
'<button clear [hidden]="!clearInput" type="button" class="text-input-clear-icon" (click)="clearTextInput()" (mousedown)="clearTextInput()"></button>' +
|
||||
'<div (touchstart)="pointerStart($event)" (touchend)="pointerEnd($event)" (mousedown)="pointerStart($event)" (mouseup)="pointerEnd($event)" class="input-cover" tappable *ngIf="_useAssist"></div>',
|
||||
directives: [
|
||||
NextInput,
|
||||
NativeInput,
|
||||
],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class TextInput extends InputBase {
|
||||
constructor(
|
||||
config: Config,
|
||||
form: Form,
|
||||
@Optional() item: Item,
|
||||
app: IonicApp,
|
||||
platform: Platform,
|
||||
elementRef: ElementRef,
|
||||
@Optional() scrollView: Content,
|
||||
@Optional() nav: NavController,
|
||||
@Optional() ngControl: NgControl
|
||||
) {
|
||||
super(config, form, item, app, platform, elementRef, scrollView, nav, ngControl);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
inputBlurred(event) {
|
||||
this.blur.emit(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
inputFocused(event) {
|
||||
this.focus.emit(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
clearTextInput() {
|
||||
console.debug('Should clear input');
|
||||
this._value = '';
|
||||
this.onChange(this._value);
|
||||
this.writeValue(this._value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @name TextArea
|
||||
* @description
|
||||
*
|
||||
* `ion-textarea` is is used for multi-line text inputs. Ionic still
|
||||
* uses an actual `<textarea>` HTML element within the component;
|
||||
* however, with Ionic wrapping the native HTML text area element, Ionic
|
||||
* is able to better handle the user experience and interactivity.
|
||||
*
|
||||
* Not that `<ion-textarea>` must load its value from the `value` or
|
||||
* `[(ngModel)]` attribute. Unlike the native `<textarea>` element,
|
||||
* `<ion-textarea>` does not support loading its value from the
|
||||
* textarea's inner content.
|
||||
*
|
||||
* When requiring only a single-line text input, we recommend using
|
||||
* `<ion-input>` instead.
|
||||
*
|
||||
* @usage
|
||||
* ```html
|
||||
* <ion-item>
|
||||
* <ion-label>Comments</ion-label>
|
||||
* <ion-textarea></ion-textarea>
|
||||
* </ion-item>
|
||||
*
|
||||
* <ion-item>
|
||||
* <ion-label stacked>Message</ion-label>
|
||||
* <ion-textarea [(ngModel)]="msg"></ion-textarea>
|
||||
* </ion-item>
|
||||
*
|
||||
* <ion-item>
|
||||
* <ion-label floating>Description</ion-label>
|
||||
* <ion-textarea></ion-textarea>
|
||||
* </ion-item>
|
||||
* ```
|
||||
*
|
||||
* @demo /docs/v2/demos/textarea/
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ion-textarea',
|
||||
template:
|
||||
'<textarea [(ngModel)]="_value" (blur)="inputBlurred($event)" (focus)="inputFocused($event)" [placeholder]="placeholder" class="text-input"></textarea>' +
|
||||
'<input type="text" aria-hidden="true" next-input *ngIf="_useAssist">' +
|
||||
'<div (touchstart)="pointerStart($event)" (touchend)="pointerEnd($event)" (mousedown)="pointerStart($event)" (mouseup)="pointerEnd($event)" class="input-cover" tappable *ngIf="_useAssist"></div>',
|
||||
directives: [
|
||||
NextInput,
|
||||
NativeInput
|
||||
],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class TextArea extends InputBase {
|
||||
constructor(
|
||||
config: Config,
|
||||
form: Form,
|
||||
@Optional() item: Item,
|
||||
app: IonicApp,
|
||||
platform: Platform,
|
||||
elementRef: ElementRef,
|
||||
@Optional() scrollView: Content,
|
||||
@Optional() nav: NavController,
|
||||
@Optional() ngControl: NgControl
|
||||
) {
|
||||
super(config, form, item, app, platform, elementRef, scrollView, nav, ngControl);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
ngOnInit() {
|
||||
super.ngOnInit();
|
||||
if (this._item) {
|
||||
this._item.setCssClass('item-textarea', true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
inputBlurred(event) {
|
||||
this.blur.emit(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
inputFocused(event) {
|
||||
this.focus.emit(event);
|
||||
}
|
||||
}
|
||||
116
src/components/input/input.wp.scss
Normal file
116
src/components/input/input.wp.scss
Normal file
@@ -0,0 +1,116 @@
|
||||
@import "../../globals.wp";
|
||||
@import "./input";
|
||||
|
||||
// Windows Input
|
||||
// --------------------------------------------------
|
||||
|
||||
$text-input-wp-background-color: $list-wp-background-color !default;
|
||||
$text-input-wp-border-color: $input-wp-border-color !default;
|
||||
$text-input-wp-border-width: 2px !default;
|
||||
|
||||
$text-input-wp-margin-top: $item-wp-padding-top !default;
|
||||
$text-input-wp-margin-right: ($item-wp-padding-right / 2) !default;
|
||||
$text-input-wp-margin-bottom: $item-wp-padding-bottom !default;
|
||||
$text-input-wp-margin-left: ($item-wp-padding-left / 2) !default;
|
||||
$text-input-wp-padding-vertical: 0 !default;
|
||||
$text-input-wp-padding-horizontal: 8px !default;
|
||||
$text-input-wp-line-height: 3rem !default;
|
||||
|
||||
$text-input-wp-highlight-color: color($colors-wp, primary) !default;
|
||||
$text-input-wp-hightlight-color-valid: color($colors-wp, secondary) !default;
|
||||
$text-input-wp-hightlight-color-invalid: color($colors-wp, danger) !default;
|
||||
|
||||
$text-input-wp-input-clear-icon-width: 30px !default;
|
||||
$text-input-wp-input-clear-icon-color: $input-wp-border-color !default;
|
||||
$text-input-wp-input-clear-icon-svg: "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'><polygon fill='" + $text-input-wp-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-wp-input-clear-icon-size: 22px !default;
|
||||
|
||||
|
||||
|
||||
// Windows Default Input
|
||||
// --------------------------------------------------
|
||||
|
||||
.text-input {
|
||||
margin: $text-input-wp-margin-top $text-input-wp-margin-right $text-input-wp-margin-bottom $text-input-wp-margin-left;
|
||||
padding: $text-input-wp-padding-vertical $text-input-wp-padding-horizontal;
|
||||
|
||||
width: calc(100% - #{$text-input-wp-margin-right} - #{$text-input-wp-margin-left});
|
||||
|
||||
border: $text-input-wp-border-width solid $text-input-wp-border-color;
|
||||
line-height: $text-input-wp-line-height;
|
||||
}
|
||||
|
||||
|
||||
// Windows Inset Input
|
||||
// --------------------------------------------------
|
||||
|
||||
.inset-input {
|
||||
margin: ($item-wp-padding-top / 2) $item-wp-padding-right ($item-wp-padding-bottom / 2) $item-wp-padding-left;
|
||||
padding: ($item-wp-padding-top / 2) ($item-wp-padding-right / 2) ($item-wp-padding-bottom / 2) ($item-wp-padding-left / 2);
|
||||
}
|
||||
|
||||
|
||||
// Windows Highlighted Input
|
||||
// --------------------------------------------------
|
||||
|
||||
.input-has-focus .text-input {
|
||||
border-color: $text-input-wp-highlight-color;
|
||||
}
|
||||
|
||||
ion-input.ng-valid.input-has-value .text-input {
|
||||
border-color: $text-input-wp-hightlight-color-valid;
|
||||
}
|
||||
|
||||
ion-input.ng-invalid.ng-touched .text-input {
|
||||
border-color: $text-input-wp-hightlight-color-invalid;
|
||||
}
|
||||
|
||||
|
||||
// Windows Stacked & Floating Inputs
|
||||
// --------------------------------------------------
|
||||
|
||||
.item-label-stacked .text-input,
|
||||
.item-label-floating .text-input,
|
||||
.item-label-stacked ion-select,
|
||||
.item-label-floating ion-select {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
margin-left: 0;
|
||||
|
||||
width: calc(100% - #{$text-input-wp-margin-right});
|
||||
}
|
||||
|
||||
.item-label-floating .text-input.cloned-input {
|
||||
top: 32px;
|
||||
}
|
||||
|
||||
.item-label-stacked .text-input.cloned-input {
|
||||
top: 27px;
|
||||
}
|
||||
|
||||
.item-label-stacked [item-right],
|
||||
.item-label-floating [item-right] {
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
// Windows Clear Input Icon
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-input[clearInput] {
|
||||
position: relative;
|
||||
|
||||
.text-input {
|
||||
padding-right: $text-input-wp-input-clear-icon-width;
|
||||
}
|
||||
}
|
||||
|
||||
.text-input-clear-icon {
|
||||
@include svg-background-image($text-input-wp-input-clear-icon-svg);
|
||||
|
||||
right: ($item-wp-padding-right / 2);
|
||||
bottom: 7px;
|
||||
|
||||
width: $text-input-wp-input-clear-icon-width;
|
||||
|
||||
background-size: $text-input-wp-input-clear-icon-size;
|
||||
}
|
||||
214
src/components/input/native-input.ts
Normal file
214
src/components/input/native-input.ts
Normal file
@@ -0,0 +1,214 @@
|
||||
import {Directive, Attribute, ElementRef, Renderer, Input, Output, EventEmitter, HostListener} from '@angular/core';
|
||||
import {NgControl} from '@angular/common';
|
||||
|
||||
import {Config} from '../../config/config';
|
||||
import {CSS, hasFocus} from '../../util/dom';
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@Directive({
|
||||
selector: '.text-input'
|
||||
})
|
||||
export class NativeInput {
|
||||
private _relocated: boolean;
|
||||
private _clone: boolean;
|
||||
private _blurring: boolean;
|
||||
private _unrefBlur: Function;
|
||||
|
||||
@Output() focusChange: EventEmitter<boolean> = new EventEmitter();
|
||||
@Output() valueChange: EventEmitter<string> = new EventEmitter();
|
||||
|
||||
constructor(
|
||||
private _elementRef: ElementRef,
|
||||
private _renderer: Renderer,
|
||||
config: Config,
|
||||
public ngControl: NgControl
|
||||
) {
|
||||
this._clone = config.getBoolean('inputCloning', false);
|
||||
this._blurring = config.getBoolean('inputBlurring', false);
|
||||
}
|
||||
|
||||
@HostListener('input', ['$event'])
|
||||
private _change(ev) {
|
||||
this.valueChange.emit(ev.target.value);
|
||||
}
|
||||
|
||||
@HostListener('focus')
|
||||
private _focus() {
|
||||
var self = this;
|
||||
|
||||
self.focusChange.emit(true);
|
||||
|
||||
function docTouchEnd(ev) {
|
||||
var tapped: HTMLElement = ev.target;
|
||||
if (tapped && self.element()) {
|
||||
if (tapped.tagName !== 'INPUT' && tapped.tagName !== 'TEXTAREA' && !tapped.classList.contains('input-cover')) {
|
||||
self.element().blur();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (self._blurring) {
|
||||
// automatically blur input if:
|
||||
// 1) this input has focus
|
||||
// 2) the newly tapped document element is not an input
|
||||
console.debug('input blurring enabled');
|
||||
|
||||
document.addEventListener('touchend', docTouchEnd, true);
|
||||
self._unrefBlur = function() {
|
||||
console.debug('input blurring disabled');
|
||||
document.removeEventListener('touchend', docTouchEnd, true);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('blur')
|
||||
private _blur() {
|
||||
this.focusChange.emit(false);
|
||||
this.hideFocus(false);
|
||||
|
||||
this._unrefBlur && this._unrefBlur();
|
||||
this._unrefBlur = null;
|
||||
}
|
||||
|
||||
labelledBy(val: string) {
|
||||
this._renderer.setElementAttribute(this._elementRef.nativeElement, 'aria-labelledby', val);
|
||||
}
|
||||
|
||||
isDisabled(val: boolean) {
|
||||
this._renderer.setElementAttribute(this._elementRef.nativeElement, 'disabled', val ? '' : null);
|
||||
}
|
||||
|
||||
setFocus() {
|
||||
// let's set focus to the element
|
||||
// but only if it does not already have focus
|
||||
if (document.activeElement !== this.element()) {
|
||||
this.element().focus();
|
||||
}
|
||||
}
|
||||
|
||||
beginFocus(shouldFocus: boolean, inputRelativeY: number) {
|
||||
if (this._relocated !== shouldFocus) {
|
||||
var focusedInputEle = this.element();
|
||||
if (shouldFocus) {
|
||||
// we should focus into this element
|
||||
|
||||
if (this._clone) {
|
||||
// this platform needs the input to be cloned
|
||||
// this allows for the actual input to receive the focus from
|
||||
// the user's touch event, but before it receives focus, it
|
||||
// moves the actual input to a location that will not screw
|
||||
// up the app's layout, and does not allow the native browser
|
||||
// to attempt to scroll the input into place (messing up headers/footers)
|
||||
// the cloned input fills the area of where native input should be
|
||||
// while the native input fakes out the browser by relocating itself
|
||||
// before it receives the actual focus event
|
||||
var clonedInputEle = cloneInput(focusedInputEle, 'cloned-focus');
|
||||
focusedInputEle.parentNode.insertBefore(clonedInputEle, focusedInputEle);
|
||||
|
||||
// move the native input to a location safe to receive focus
|
||||
// according to the browser, the native input receives focus in an
|
||||
// area which doesn't require the browser to scroll the input into place
|
||||
focusedInputEle.style[CSS.transform] = `translate3d(-9999px,${inputRelativeY}px,0)`;
|
||||
focusedInputEle.style.opacity = '0';
|
||||
}
|
||||
|
||||
// let's now set focus to the actual native element
|
||||
// at this point it is safe to assume the browser will not attempt
|
||||
// to scroll the input into view itself (screwing up headers/footers)
|
||||
this.setFocus();
|
||||
|
||||
if (this._clone) {
|
||||
focusedInputEle.classList.add('cloned-active');
|
||||
}
|
||||
|
||||
} else {
|
||||
// should remove the focus
|
||||
if (this._clone) {
|
||||
// should remove the cloned node
|
||||
focusedInputEle.classList.remove('cloned-active');
|
||||
focusedInputEle.style[CSS.transform] = '';
|
||||
focusedInputEle.style.opacity = '';
|
||||
removeClone(focusedInputEle, 'cloned-focus');
|
||||
}
|
||||
}
|
||||
|
||||
this._relocated = shouldFocus;
|
||||
}
|
||||
}
|
||||
|
||||
hideFocus(shouldHideFocus: boolean) {
|
||||
let focusedInputEle = this.element();
|
||||
|
||||
console.debug(`native input hideFocus, shouldHideFocus: ${shouldHideFocus}, input value: ${focusedInputEle.value}`);
|
||||
|
||||
if (shouldHideFocus) {
|
||||
let clonedInputEle = cloneInput(focusedInputEle, 'cloned-move');
|
||||
|
||||
focusedInputEle.classList.add('cloned-active');
|
||||
focusedInputEle.parentNode.insertBefore(clonedInputEle, focusedInputEle);
|
||||
|
||||
} else {
|
||||
focusedInputEle.classList.remove('cloned-active');
|
||||
removeClone(focusedInputEle, 'cloned-move');
|
||||
}
|
||||
}
|
||||
|
||||
hasFocus(): boolean {
|
||||
return hasFocus(this.element());
|
||||
}
|
||||
|
||||
getValue(): string {
|
||||
return this.element().value;
|
||||
}
|
||||
|
||||
element(): HTMLInputElement {
|
||||
return this._elementRef.nativeElement;
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this._unrefBlur && this._unrefBlur();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function cloneInput(focusedInputEle, addCssClass) {
|
||||
let clonedInputEle = focusedInputEle.cloneNode(true);
|
||||
clonedInputEle.classList.add('cloned-input');
|
||||
clonedInputEle.classList.add(addCssClass);
|
||||
clonedInputEle.setAttribute('aria-hidden', true);
|
||||
clonedInputEle.removeAttribute('aria-labelledby');
|
||||
clonedInputEle.tabIndex = -1;
|
||||
clonedInputEle.style.width = (focusedInputEle.offsetWidth + 10) + 'px';
|
||||
clonedInputEle.style.height = focusedInputEle.offsetHeight + 'px';
|
||||
clonedInputEle.value = focusedInputEle.value;
|
||||
return clonedInputEle;
|
||||
}
|
||||
|
||||
function removeClone(focusedInputEle, queryCssClass) {
|
||||
let clonedInputEle = focusedInputEle.parentElement.querySelector('.' + queryCssClass);
|
||||
if (clonedInputEle) {
|
||||
clonedInputEle.parentNode.removeChild(clonedInputEle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[next-input]'
|
||||
})
|
||||
export class NextInput {
|
||||
@Output() focused: EventEmitter<boolean> = new EventEmitter();
|
||||
|
||||
@HostListener('focus')
|
||||
receivedFocus() {
|
||||
console.debug('native-input, next-input received focus');
|
||||
this.focused.emit(true);
|
||||
}
|
||||
|
||||
}
|
||||
4
src/components/input/test/clear-input/e2e.ts
Normal file
4
src/components/input/test/clear-input/e2e.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
it('should clear input', function() {
|
||||
element(by.css('.e2eClearInput .text-input-clear-icon')).click();
|
||||
});
|
||||
11
src/components/input/test/clear-input/index.ts
Normal file
11
src/components/input/test/clear-input/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import {App} from 'ionic-angular';
|
||||
|
||||
|
||||
@App({
|
||||
templateUrl: 'main.html'
|
||||
})
|
||||
class E2EApp {
|
||||
constructor() {
|
||||
this.myValue = 'value';
|
||||
}
|
||||
}
|
||||
20
src/components/input/test/clear-input/main.html
Normal file
20
src/components/input/test/clear-input/main.html
Normal file
@@ -0,0 +1,20 @@
|
||||
|
||||
<ion-toolbar>
|
||||
<ion-title>Clear Input</ion-title>
|
||||
</ion-toolbar>
|
||||
|
||||
|
||||
<ion-content>
|
||||
|
||||
|
||||
<ion-list>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Text 1:</ion-label>
|
||||
<ion-input class="e2eClearInput" [(ngModel)]="myValue" clearInput></ion-input>
|
||||
</ion-item>
|
||||
|
||||
|
||||
</ion-list>
|
||||
|
||||
</ion-content>
|
||||
1
src/components/input/test/fixed-inline-labels/e2e.ts
Normal file
1
src/components/input/test/fixed-inline-labels/e2e.ts
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
21
src/components/input/test/fixed-inline-labels/index.ts
Normal file
21
src/components/input/test/fixed-inline-labels/index.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import {App, Page} from '../../../../../ionic';
|
||||
|
||||
|
||||
@Page({
|
||||
templateUrl: 'main.html'
|
||||
})
|
||||
class PageOne {
|
||||
url;
|
||||
input1: string = 'Text 1';
|
||||
|
||||
onEvent(event) {
|
||||
console.log("Did Event:", event.type);
|
||||
}
|
||||
}
|
||||
|
||||
@App({
|
||||
template: '<ion-nav [root]="root"></ion-nav>'
|
||||
})
|
||||
class E2EApp {
|
||||
root = PageOne;
|
||||
}
|
||||
92
src/components/input/test/fixed-inline-labels/main.html
Normal file
92
src/components/input/test/fixed-inline-labels/main.html
Normal file
@@ -0,0 +1,92 @@
|
||||
|
||||
<ion-toolbar>
|
||||
<ion-title>Fixed Inline Label Text Input</ion-title>
|
||||
</ion-toolbar>
|
||||
|
||||
<ion-content>
|
||||
|
||||
<ion-list>
|
||||
|
||||
<ion-item>
|
||||
<ion-label fixed>To</ion-label>
|
||||
<ion-input [(ngModel)]="input1"
|
||||
(blur)="onEvent($event)"
|
||||
(input)="onEvent($event)"
|
||||
(focus)="onEvent($event)">
|
||||
</ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label fixed>CC</ion-label>
|
||||
<ion-select>
|
||||
<ion-option checked>Admin</ion-option>
|
||||
</ion-select>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label fixed>From</ion-label>
|
||||
<ion-input value="Text 3"></ion-input>
|
||||
<button clear item-right>
|
||||
<ion-icon name="power"></ion-icon>
|
||||
</button>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label fixed>Comments</ion-label>
|
||||
<ion-textarea value="Comment value"
|
||||
(blur)="onEvent($event)"
|
||||
(input)="onEvent($event)"
|
||||
(focus)="onEvent($event)">
|
||||
</ion-textarea>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-icon name="globe" item-left></ion-icon>
|
||||
<ion-label fixed>Website</ion-label>
|
||||
<ion-input value="http://ionic.io/" type="url"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-icon name="mail" item-left></ion-icon>
|
||||
<ion-label fixed>Email</ion-label>
|
||||
<ion-input value="email6@email.com" type="email"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-icon name="call" item-left></ion-icon>
|
||||
<ion-label fixed>Phone</ion-label>
|
||||
<ion-input value="867-5309" type="tel"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-icon name="contact" item-left></ion-icon>
|
||||
<ion-input placeholder="Placeholder Text"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label fixed>Score</ion-label>
|
||||
<ion-input value="10" type="number"></ion-input>
|
||||
<button outline item-right>Update</button>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label fixed>First Name</ion-label>
|
||||
<ion-input value="Buzz" type="text"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label fixed>Last Name</ion-label>
|
||||
<ion-input value="Lightyear" type="text"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-icon name="create" item-left></ion-icon>
|
||||
<ion-label fixed>Message</ion-label>
|
||||
<ion-textarea value="To infinity and beyond"></ion-textarea>
|
||||
</ion-item>
|
||||
|
||||
</ion-list>
|
||||
|
||||
<p>{{url}}</p>
|
||||
|
||||
</ion-content>
|
||||
1
src/components/input/test/floating-labels/e2e.ts
Normal file
1
src/components/input/test/floating-labels/e2e.ts
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
17
src/components/input/test/floating-labels/index.ts
Normal file
17
src/components/input/test/floating-labels/index.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import {App} from '../../../../../ionic';
|
||||
|
||||
|
||||
@App({
|
||||
templateUrl: 'main.html'
|
||||
})
|
||||
class E2EApp {
|
||||
constructor() {
|
||||
this.myParam = '';
|
||||
|
||||
this.myValues = {
|
||||
value1: 'Dynamic Input',
|
||||
value2: 'Dynamic Textarea'
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
83
src/components/input/test/floating-labels/main.html
Normal file
83
src/components/input/test/floating-labels/main.html
Normal file
@@ -0,0 +1,83 @@
|
||||
|
||||
<ion-toolbar><ion-title>Floating Label Text Input</ion-title></ion-toolbar>
|
||||
|
||||
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
|
||||
<ion-item>
|
||||
<ion-label floating>Params: {{ myParam }}</ion-label>
|
||||
<ion-input [(ngModel)]="myParam" type="text"></ion-input>
|
||||
<button clear item-right>
|
||||
<ion-icon name="settings"></ion-icon>
|
||||
</button>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label floating>Floating Label 1: {{ myValues.value1 }}</ion-label>
|
||||
<ion-input [(ngModel)]='myValues.value1'></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label floating>Floating Label 2</ion-label>
|
||||
<ion-input value="Has Value"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label floating>Floating Label 3</ion-label>
|
||||
<ion-input type="number"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label floating primary>Floating Label 4: {{ myValues.value2 }}</ion-label>
|
||||
<ion-textarea [(ngModel)]='myValues.value2'></ion-textarea>
|
||||
<button outline item-right>
|
||||
View
|
||||
</button>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label floating secondary>Floating Label 5</ion-label>
|
||||
<ion-input type="url"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label floating danger>Floating Label 6</ion-label>
|
||||
<ion-input type="email"></ion-input>
|
||||
<ion-avatar item-right>
|
||||
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAAAAACH5BAAAAAAALAAAAAABAAEAAAICTAEAOw==">
|
||||
</ion-avatar>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label floating>Floating Label 7</ion-label>
|
||||
<ion-textarea></ion-textarea>
|
||||
<ion-thumbnail item-right>
|
||||
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAAAAACH5BAAAAAAALAAAAAABAAEAAAICTAEAOw==">
|
||||
</ion-thumbnail>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label floating>Floating Label 8</ion-label>
|
||||
<ion-input></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label floating>Floating Label 9</ion-label>
|
||||
<ion-input type="number"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label floating>Floating Label 10</ion-label>
|
||||
<ion-textarea></ion-textarea>
|
||||
</ion-item>
|
||||
|
||||
</ion-list>
|
||||
|
||||
</ion-content>
|
||||
|
||||
<style>
|
||||
img {
|
||||
height: 100px;
|
||||
}
|
||||
</style>
|
||||
0
src/components/input/test/form-inputs/e2e.ts
Normal file
0
src/components/input/test/form-inputs/e2e.ts
Normal file
39
src/components/input/test/form-inputs/index.ts
Normal file
39
src/components/input/test/form-inputs/index.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import {App} from '../../../../../ionic';
|
||||
import {FormBuilder, Validators} from '@angular/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;
|
||||
}
|
||||
|
||||
disable() {
|
||||
this.isTextAreaDisabled = !this.isTextAreaDisabled;
|
||||
}
|
||||
|
||||
}
|
||||
117
src/components/input/test/form-inputs/main.html
Normal file
117
src/components/input/test/form-inputs/main.html
Normal file
@@ -0,0 +1,117 @@
|
||||
|
||||
<ion-toolbar><ion-title>Form Inputs</ion-title></ion-toolbar>
|
||||
|
||||
|
||||
<ion-content>
|
||||
|
||||
<form [ngFormModel]="loginForm" #mf="ngForm" novalidate>
|
||||
|
||||
<ion-list>
|
||||
<ion-item>
|
||||
<ion-label floating>Email</ion-label>
|
||||
<ion-input clearInput [(ngModel)]="login.email" ngControl="email" type="email" required></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label floating>Username</ion-label>
|
||||
<ion-input clearInput [(ngModel)]="login.username" ngControl="username"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Password</ion-label>
|
||||
<ion-input clearInput [(ngModel)]="login.password" ngControl="password" type="password" required></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Comments</ion-label>
|
||||
<ion-textarea clearInput [(ngModel)]="login.comments" ngControl="comments" required>Comment value</ion-textarea>
|
||||
</ion-item>
|
||||
|
||||
<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-item>
|
||||
<ion-label floating>Username</ion-label>
|
||||
<ion-input [(ngModel)]="user.username" ngControl="username" required></ion-input>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label floating>Password</ion-label>
|
||||
<ion-input type="password" [(ngModel)]="user.password" ngControl="password" required></ion-input>
|
||||
</ion-item>
|
||||
<div padding-left padding-right>
|
||||
<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-item>
|
||||
<ion-label>Email</ion-label>
|
||||
<ion-input type="email" required></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Username</ion-label>
|
||||
<ion-input></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Password</ion-label>
|
||||
<ion-input type="password" required></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Comments</ion-label>
|
||||
<ion-textarea required>Comment value</ion-textarea>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
|
||||
<ion-input placeholder="Stand-alone ion-input"></ion-input>
|
||||
|
||||
<ion-input placeholder="Stand-alone textarea"></ion-input>
|
||||
|
||||
<ion-list>
|
||||
<ion-item>
|
||||
<ion-label>Custom Attrs</ion-label>
|
||||
<ion-input autocomplete="off"
|
||||
spellcheck="no"
|
||||
required
|
||||
some-weird-attr="value"
|
||||
accept="sure"
|
||||
class="no-copy" id="no-copy" checked=checked
|
||||
value="copy custom attributes"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Disabled Input</ion-label>
|
||||
<ion-input disabled value="Value"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Disabled TextArea</ion-label>
|
||||
<ion-textarea [disabled]="isTextAreaDisabled" value="Value"></ion-textarea>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
|
||||
<button (click)="disable()">Disable</button>
|
||||
|
||||
</ion-content>
|
||||
1
src/components/input/test/inline-labels/e2e.ts
Normal file
1
src/components/input/test/inline-labels/e2e.ts
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
12
src/components/input/test/inline-labels/index.ts
Normal file
12
src/components/input/test/inline-labels/index.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import {App} from '../../../../../ionic';
|
||||
|
||||
|
||||
@App({
|
||||
templateUrl: 'main.html'
|
||||
})
|
||||
class E2EApp {
|
||||
|
||||
submit(ev) {
|
||||
}
|
||||
|
||||
}
|
||||
98
src/components/input/test/inline-labels/main.html
Normal file
98
src/components/input/test/inline-labels/main.html
Normal file
@@ -0,0 +1,98 @@
|
||||
|
||||
<ion-toolbar><ion-title>Inline Label Text Input</ion-title></ion-toolbar>
|
||||
|
||||
|
||||
<ion-content>
|
||||
|
||||
<form (ng-submit)="submit($event)">
|
||||
|
||||
<ion-list>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>To:</ion-label>
|
||||
<ion-input value="Text 1"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>CC:</ion-label>
|
||||
<ion-input value="Text 2"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>From:</ion-label>
|
||||
<ion-input value="Text 3"></ion-input>
|
||||
<button clear item-right>
|
||||
<ion-icon name="power"></ion-icon>
|
||||
</button>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Comments:</ion-label>
|
||||
<ion-textarea value="Comment value"></ion-textarea>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-icon name="globe" item-left></ion-icon>
|
||||
<ion-label>Website:</ion-label>
|
||||
<ion-input value="http://ionic.io/" type="url"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-icon name="mail" item-left></ion-icon>
|
||||
<ion-label>Email:</ion-label>
|
||||
<ion-input value="email6@email.com" type="email"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-icon name="create" item-left></ion-icon>
|
||||
<ion-label>Feedback:</ion-label>
|
||||
<ion-textarea placeholder="Placeholder Text"></ion-textarea>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>More Info:</ion-label>
|
||||
<ion-input placeholder="Placeholder Text"></ion-input>
|
||||
<ion-icon name="flag" item-right></ion-icon>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Score:</ion-label>
|
||||
<ion-input value="10" type="number"></ion-input>
|
||||
<button outline item-right>Update</button>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>First Name:</ion-label>
|
||||
<ion-input value="Lightning"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Last Name:</ion-label>
|
||||
<ion-input value="McQueen"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Message:</ion-label>
|
||||
<ion-textarea value="KA-CHOW!"></ion-textarea>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Beginning:</ion-label>
|
||||
<ion-input value="2005-07-30" type="date"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Time:</ion-label>
|
||||
<ion-input value="12:00pm" type="time"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
</ion-list>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>No List</ion-label>
|
||||
<ion-input value="ListFreeeee"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
</form>
|
||||
|
||||
</ion-content>
|
||||
34
src/components/input/test/input-focus/index.ts
Normal file
34
src/components/input/test/input-focus/index.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import {App} from '../../../../../ionic';
|
||||
|
||||
|
||||
@App({
|
||||
templateUrl: 'main.html',
|
||||
config: {
|
||||
//scrollAssist: true
|
||||
}
|
||||
})
|
||||
class E2EApp {
|
||||
reload() {
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('click', function(ev) {
|
||||
console.log(`CLICK, ${ev.target.localName}.${ev.target.className}, time: ${Date.now()}`);
|
||||
});
|
||||
|
||||
document.addEventListener('touchstart', function(ev) {
|
||||
console.log(`TOUCH START, ${ev.target.localName}.${ev.target.className}, time: ${Date.now()}`);
|
||||
});
|
||||
|
||||
document.addEventListener('touchend', function(ev) {
|
||||
console.log(`TOUCH END, ${ev.target.localName}.${ev.target.className}, time: ${Date.now()}`);
|
||||
});
|
||||
|
||||
document.addEventListener('focusin', function(ev) {console.log(`CLICK, ${ev.target.localName}.${ev.target.className}, time: ${Date.now()}`);
|
||||
console.log(`FOCUS IN, ${ev.target.localName}.${ev.target.className}, time: ${Date.now()}`);
|
||||
});
|
||||
|
||||
document.addEventListener('focusout', function(ev) {console.log(`CLICK, ${ev.target.localName}.${ev.target.className}, time: ${Date.now()}`);
|
||||
console.log(`FOCUS OUT, ${ev.target.localName}.${ev.target.className}, time: ${Date.now()}`);
|
||||
});
|
||||
183
src/components/input/test/input-focus/main.html
Normal file
183
src/components/input/test/input-focus/main.html
Normal file
@@ -0,0 +1,183 @@
|
||||
|
||||
<ion-toolbar primary>
|
||||
<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-item>
|
||||
<ion-label>Text 1:</ion-label>
|
||||
<ion-input></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
Item with button right
|
||||
<button item-right>Button 1</button>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label id="my-label1">Text 2:</ion-label>
|
||||
<ion-input value="value"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<button ion-item>
|
||||
Button Item
|
||||
</button>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Text 3:</ion-label>
|
||||
<ion-input></ion-input>
|
||||
<button clear item-right>
|
||||
<ion-icon name="power"></ion-icon>
|
||||
</button>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Comments:</ion-label>
|
||||
<ion-textarea>Comment value</ion-textarea>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-icon name="globe" item-left></ion-icon>
|
||||
<ion-label>Website:</ion-label>
|
||||
<ion-input value="http://ionic.io/" type="url"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-icon name="mail" item-left></ion-icon>
|
||||
<ion-label>Email:</ion-label>
|
||||
<ion-input value="email6@email.com" type="email"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-icon name="create" item-left></ion-icon>
|
||||
<ion-label>Feedback:</ion-label>
|
||||
<ion-textarea placeholder="Placeholder Text"></ion-textarea>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Toggle</ion-label>
|
||||
<ion-toggle></ion-toggle>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>More Info:</ion-label>
|
||||
<ion-input placeholder="Placeholder Text"></ion-input>
|
||||
<ion-icon name="flag" item-right></ion-icon>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Checkbox</ion-label>
|
||||
<ion-checkbox></ion-checkbox>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Score:</ion-label>
|
||||
<ion-input value="10" type="number"></ion-input>
|
||||
<button outline item-right>Update</button>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>First Name:</ion-label>
|
||||
<ion-input value="Lightning"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Last Name:</ion-label>
|
||||
<ion-input value="McQueen"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Message:</ion-label>
|
||||
<ion-textarea value="KA-CHOW!"></ion-textarea>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
Item
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
Item
|
||||
</ion-item>
|
||||
|
||||
</ion-list>
|
||||
|
||||
<ion-list radio-group>
|
||||
|
||||
<ion-list-header>
|
||||
Radios
|
||||
</ion-list-header>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Radio 1</ion-label>
|
||||
<ion-radio value="1"></ion-radio>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Radio 2</ion-label>
|
||||
<ion-radio value="2"></ion-radio>
|
||||
</ion-item>
|
||||
|
||||
</ion-list>
|
||||
|
||||
<ion-list>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Bottom input 9:</ion-label>
|
||||
<ion-input value="bottom input 9"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Bottom input 8:</ion-label>
|
||||
<ion-input value="bottom input 8"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Bottom input 7:</ion-label>
|
||||
<ion-input value="bottom input 7"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Bottom input 6:</ion-label>
|
||||
<ion-input value="bottom input 6"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Bottom input 5:</ion-label>
|
||||
<ion-input value="bottom input 5"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Bottom input 4:</ion-label>
|
||||
<ion-input value="bottom input 4"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Bottom input 3:</ion-label>
|
||||
<ion-input value="bottom input 3"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Bottom input 2:</ion-label>
|
||||
<ion-input value="bottom input 2"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Bottom input 1:</ion-label>
|
||||
<ion-input value="bottom input 1"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
</ion-list>
|
||||
|
||||
|
||||
</ion-content>
|
||||
1
src/components/input/test/inset-inputs/e2e.ts
Normal file
1
src/components/input/test/inset-inputs/e2e.ts
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
7
src/components/input/test/inset-inputs/index.ts
Normal file
7
src/components/input/test/inset-inputs/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import {App} from '../../../../../ionic';
|
||||
|
||||
|
||||
@App({
|
||||
templateUrl: 'main.html'
|
||||
})
|
||||
class E2EApp {}
|
||||
71
src/components/input/test/inset-inputs/main.html
Normal file
71
src/components/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-item>
|
||||
<ion-icon name="search" item-left></ion-icon>
|
||||
<ion-input value="Input inside an inset list"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-icon name="call" item-left></ion-icon>
|
||||
<ion-input type="tel"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-icon name="mail" item-left></ion-icon>
|
||||
<ion-input placeholder="Placeholder text"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
</ion-list>
|
||||
|
||||
|
||||
<ion-list inset>
|
||||
|
||||
<ion-item inset>
|
||||
<ion-icon name="search" item-left></ion-icon>
|
||||
<ion-input value="Inset input inside an inset list"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item inset>
|
||||
<ion-input value="Inset input inside an inset list"></ion-input>
|
||||
<ion-icon name="mic" item-right></ion-icon>
|
||||
</ion-item>
|
||||
|
||||
</ion-list>
|
||||
|
||||
|
||||
<ion-list>
|
||||
|
||||
<ion-item inset>
|
||||
<ion-icon name="search" item-left></ion-icon>
|
||||
<ion-input value="Inset input inside a list"></ion-input>
|
||||
<button outline item-right>Search</button>
|
||||
</ion-item>
|
||||
|
||||
</ion-list>
|
||||
|
||||
|
||||
<ion-card>
|
||||
|
||||
<ion-item>
|
||||
<ion-icon name="search" item-left></ion-icon>
|
||||
<ion-input value="Input inside a card"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-input value="Input inside a card"></ion-input>
|
||||
<ion-icon name="mic" item-right></ion-icon>
|
||||
</ion-item>
|
||||
|
||||
</ion-card>
|
||||
|
||||
|
||||
</ion-content>
|
||||
1
src/components/input/test/placeholder-labels/e2e.ts
Normal file
1
src/components/input/test/placeholder-labels/e2e.ts
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
7
src/components/input/test/placeholder-labels/index.ts
Normal file
7
src/components/input/test/placeholder-labels/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import {App} from '../../../../../ionic';
|
||||
|
||||
|
||||
@App({
|
||||
templateUrl: 'main.html'
|
||||
})
|
||||
class E2EApp {}
|
||||
61
src/components/input/test/placeholder-labels/main.html
Normal file
61
src/components/input/test/placeholder-labels/main.html
Normal file
@@ -0,0 +1,61 @@
|
||||
|
||||
<ion-toolbar>
|
||||
<ion-title>Placeholder Label Text Input</ion-title>
|
||||
</ion-toolbar>
|
||||
|
||||
|
||||
<ion-content class="outer-content">
|
||||
|
||||
<ion-list>
|
||||
|
||||
<ion-item>
|
||||
<ion-input placeholder="Text Input Placeholder"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-input placeholder="Text Input Placeholder" value="Text Input Value"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-icon name="call" item-left></ion-icon>
|
||||
<ion-input placeholder="Text Input Placeholder"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-icon name="call" item-left></ion-icon>
|
||||
<ion-input placeholder="Text Input Placeholder" value="Text Input Value"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-textarea placeholder="Textarea Placeholder"></ion-textarea>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-textarea placeholder="ion-textarea Placeholder" value="Textarea value"></ion-textarea>
|
||||
</ion-item>
|
||||
|
||||
</ion-list>
|
||||
|
||||
<ion-list inset>
|
||||
|
||||
<ion-item>
|
||||
<ion-input placeholder="Inset List: Text Input Placeholder"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-input placeholder="Inset List: Text Input Placeholder" value="Inset List: Text Input Value"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-icon name="call" item-left></ion-icon>
|
||||
<ion-input placeholder="Inset List: Text Input Placeholder"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-icon name="call" item-left></ion-icon>
|
||||
<ion-input placeholder="Inset List: Text Input Placeholder" value="Inset List: Text Input Value"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
</ion-list>
|
||||
|
||||
</ion-content>
|
||||
1
src/components/input/test/stacked-labels/e2e.ts
Normal file
1
src/components/input/test/stacked-labels/e2e.ts
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
14
src/components/input/test/stacked-labels/index.ts
Normal file
14
src/components/input/test/stacked-labels/index.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import {App, Page} from '../../../../../ionic';
|
||||
|
||||
|
||||
@App({
|
||||
template: '<ion-nav [root]="rootPage"></ion-nav>'
|
||||
})
|
||||
class E2EApp {
|
||||
rootPage = PageOne;
|
||||
}
|
||||
|
||||
@Page({
|
||||
templateUrl: 'main.html'
|
||||
})
|
||||
class PageOne {}
|
||||
68
src/components/input/test/stacked-labels/main.html
Normal file
68
src/components/input/test/stacked-labels/main.html
Normal file
@@ -0,0 +1,68 @@
|
||||
|
||||
<ion-toolbar>
|
||||
<ion-title>Stacked Label Text Input</ion-title>
|
||||
</ion-toolbar>
|
||||
|
||||
|
||||
<ion-content>
|
||||
|
||||
<ion-list>
|
||||
|
||||
<ion-item>
|
||||
<ion-label stacked>Label 1</ion-label>
|
||||
<ion-input value="Text 1"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label stacked>Label 2</ion-label>
|
||||
<ion-input value="Text 2"></ion-input>
|
||||
<button item-right clear><ion-icon name="heart-outline"></ion-icon></button>
|
||||
<button item-right clear><ion-icon ios="ios-call-outline" md="md-call"></ion-icon></button>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label stacked>Label 3</ion-label>
|
||||
<ion-input value="3" type="number"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label stacked>Label 4</ion-label>
|
||||
<ion-textarea value="Textarea 4"></ion-textarea>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label stacked>Label 5</ion-label>
|
||||
<ion-input value="http://url5.com/" type="url"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label stacked>Label 6</ion-label>
|
||||
<ion-select [(ngModel)]="gender">
|
||||
<ion-option value="f" checked="true">Female</ion-option>
|
||||
<ion-option value="m">Male</ion-option>
|
||||
</ion-select>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label stacked>Label 7</ion-label>
|
||||
<ion-textarea value="Textarea 7"></ion-textarea>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label stacked>Label 8</ion-label>
|
||||
<ion-input value="Text 8"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label stacked>Label 9</ion-label>
|
||||
<ion-input value="9" type="number"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label stacked>Label 10</ion-label>
|
||||
<ion-textarea value="Textarea 10"></ion-textarea>
|
||||
</ion-item>
|
||||
|
||||
</ion-list>
|
||||
|
||||
</ion-content>
|
||||
135
src/components/input/test/text-input.spec.ts
Normal file
135
src/components/input/test/text-input.spec.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
import {TextInput} from '../../../../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 = TextInput.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 = TextInput.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', () => {
|
||||
// TextInput 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 = TextInput.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', () => {
|
||||
// TextInput 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 = TextInput.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', () => {
|
||||
// TextInput 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 = TextInput.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', () => {
|
||||
// TextInput 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 = TextInput.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', () => {
|
||||
// TextInput 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 = TextInput.getScrollData(inputOffsetTop, inputOffsetHeight, scrollViewDimensions, keyboardHeight, platformHeight);
|
||||
expect(scrollData.scrollAmount).toBe(0);
|
||||
});
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user