feat(input): initial checkin of input component

This commit is contained in:
Brandy Carney
2017-07-28 18:28:30 -04:00
parent 7736045342
commit 2edcbab5cd
8 changed files with 1235 additions and 0 deletions

View File

@ -0,0 +1,182 @@
@import "../../themes/ionic.globals.ios";
@import "./input";
// iOS Input
// --------------------------------------------------
/// @prop - Background color of the input
$text-input-ios-background-color: $list-ios-background-color !default;
/// @prop - Margin top of the input
$text-input-ios-margin-top: $item-ios-padding-top !default;
// deprecated
$text-input-ios-margin-right: ($item-ios-padding-end / 2) !default;
/// @prop - Margin end of the input
$text-input-ios-margin-end: $text-input-ios-margin-right !default;
/// @prop - Margin bottom of the input
$text-input-ios-margin-bottom: $item-ios-padding-bottom !default;
// deprecated
$text-input-ios-margin-left: 0 !default;
/// @prop - Margin start of the input
$text-input-ios-margin-start: $text-input-ios-margin-left !default;
/// @prop - Width of the icon used to clear the input
$text-input-ios-input-clear-icon-width: 30px !default;
/// @prop - Color of the icon used to clear the input
$text-input-ios-input-clear-icon-color: rgba(0, 0, 0, .5) !default;
/// @prop - Icon used to clear the input
$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;
/// @prop - Size of the icon used to clear the input
$text-input-ios-input-clear-icon-size: 18px !default;
/// @prop - Show the focus highlight when the input has focus
$text-input-ios-show-focus-highlight: false !default;
/// @prop - Show the valid highlight when it is valid and has a value
$text-input-ios-show-valid-highlight: $text-input-ios-show-focus-highlight !default;
/// @prop - Show the invalid highlight when it is invalid and has value
$text-input-ios-show-invalid-highlight: $text-input-ios-show-focus-highlight !default;
/// @prop - Color of the input highlight
$text-input-ios-highlight-color: color($colors-ios, primary) !default;
/// @prop - Color of the input highlight when valid
$text-input-ios-highlight-color-valid: $text-input-highlight-color-valid !default;
/// @prop - Color of the input highlight when invalid
$text-input-ios-highlight-color-invalid: $text-input-highlight-color-invalid !default;
// iOS Default Input
// --------------------------------------------------
.text-input-ios {
@include margin($text-input-ios-margin-top, $text-input-ios-margin-end, $text-input-ios-margin-bottom, $text-input-ios-margin-start);
@include padding(0);
width: calc(100% - #{($text-input-ios-margin-end + $text-input-ios-margin-start)});
}
// iOS Inset Input
// --------------------------------------------------
.input-ios .inset-input {
@include padding(($item-ios-padding-top / 2), ($item-ios-padding-end / 2), ($item-ios-padding-bottom / 2), ($item-ios-padding-start / 2));
@include margin(($item-ios-padding-top / 2), $item-ios-padding-end, ($item-ios-padding-bottom / 2), 0);
}
// iOS Highlighted Input
// --------------------------------------------------
// Input highlight mixin for focus, valid, and invalid states
@mixin ios-input-highlight($highlight-color) {
border-bottom-color: $highlight-color;
}
// Show the focus highlight when the input has focus
@if ($text-input-ios-show-focus-highlight) {
// In order to get a 2px border we need to add an inset
// box-shadow 1px (this is to avoid the div resizing)
// TODO remove all uses of input-has-focus in v4
// TODO remove all uses of input-has-value in v4
.item-ios.item-input.item-input-has-focus .item-inner,
.item-ios.item-input.input-has-focus .item-inner {
@include ios-input-highlight($text-input-ios-highlight-color);
}
// The last item in a list has a border on the item, not the
// inner item, so add it to the item itself
.list-ios .item-input.item-input-has-focus:last-child,
.list-ios .item-input.input-has-focus:last-child {
@include ios-input-highlight($text-input-ios-highlight-color);
.item-inner {
box-shadow: none;
}
}
}
// Show the valid highlight when it has the .ng-valid class and a value
@if ($text-input-ios-show-valid-highlight) {
.item-ios.item-input.ng-valid.item-input-has-value:not(.input-has-focus):not(.item-input-has-focus) .item-inner,
.item-ios.item-input.ng-valid.input-has-value:not(.input-has-focus):not(.item-input-has-focus) .item-inner {
@include ios-input-highlight($text-input-ios-highlight-color-valid);
}
.list-ios .item-input.ng-valid.item-input-has-value:not(.input-has-focus):not(.item-input-has-focus):last-child,
.list-ios .item-input.ng-valid.input-has-value:not(.input-has-focus):not(.item-input-has-focus):last-child {
@include ios-input-highlight($text-input-ios-highlight-color-valid);
.item-inner {
box-shadow: none;
}
}
}
// Show the invalid highlight when it has the invalid class and has been touched
@if ($text-input-ios-show-invalid-highlight) {
.item-ios.item-input.ng-invalid.ng-touched:not(.input-has-focus):not(.item-input-has-focus) .item-inner {
@include ios-input-highlight($text-input-ios-highlight-color-invalid);
}
.list-ios .item-input.ng-invalid.ng-touched:not(.input-has-focus):not(.item-input-has-focus):last-child {
@include ios-input-highlight($text-input-ios-highlight-color-invalid);
.item-inner {
box-shadow: none;
}
}
}
// iOS Stacked & Floating Inputs
// --------------------------------------------------
.item-ios.item-label-stacked .text-input,
.item-ios.item-label-floating .text-input {
@include margin(8px, null, 8px, 0);
width: calc(100% - #{$text-input-ios-margin-end});
}
.item-ios.item-label-stacked .label-ios + .input + .cloned-input,
.item-ios.item-label-floating .label-ios + .input + .cloned-input {
@include margin-horizontal(0, null);
}
.item-label-stacked .select-ios,
.item-label-floating .select-ios {
@include padding(8px, null, 8px, 0);
}
// iOS Clear Input Icon
// --------------------------------------------------
.input-ios[clearInput] {
position: relative;
}
.input-ios[clearInput] .text-input {
@include padding-horizontal(null, $text-input-ios-input-clear-icon-width);
}
.input-ios .text-input-clear-icon {
@include position-horizontal(null, ($item-ios-padding-end / 2));
@include svg-background-image($text-input-ios-input-clear-icon-svg);
width: $text-input-ios-input-clear-icon-width;
background-size: $text-input-ios-input-clear-icon-size;
}

View File

@ -0,0 +1,179 @@
@import "../../themes/ionic.globals.md";
@import "./input";
// Material Design Input
// --------------------------------------------------
/// @prop - Background color of the input
$text-input-md-background-color: $list-md-background-color !default;
/// @prop - Margin top of the input
$text-input-md-margin-top: $item-md-padding-top !default;
// deprecated
$text-input-md-margin-right: ($item-md-padding-end / 2) !default;
/// @prop - Margin end of the input
$text-input-md-margin-end: $text-input-md-margin-right !default;
/// @prop - Margin bottom of the input
$text-input-md-margin-bottom: $item-md-padding-bottom !default;
// deprecated
$text-input-md-margin-left: ($item-md-padding-start / 2) !default;
/// @prop - Margin start of the input
$text-input-md-margin-start: $text-input-md-margin-left !default;
/// @prop - Width of the icon used to clear the input
$text-input-md-input-clear-icon-width: 30px !default;
/// @prop - Color of the icon used to clear the input
$text-input-md-input-clear-icon-color: #5b5b5b !default;
/// @prop - Icon used to clear the input
$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;
/// @prop - Size of the icon used to clear the input
$text-input-md-input-clear-icon-size: 22px !default;
/// @prop - Show the focus highlight when the input has focus
$text-input-md-show-focus-highlight: true !default;
/// @prop - Show the valid highlight when it is valid and has a value
$text-input-md-show-valid-highlight: $text-input-md-show-focus-highlight !default;
/// @prop - Show the invalid highlight when it is invalid and has value
$text-input-md-show-invalid-highlight: $text-input-md-show-focus-highlight !default;
/// @prop - Color of the input highlight
$text-input-md-highlight-color: color($colors-md, primary) !default;
/// @prop - Color of the input highlight when valid
$text-input-md-highlight-color-valid: $text-input-highlight-color-valid !default;
/// @prop - Color of the input highlight when invalid
$text-input-md-highlight-color-invalid: $text-input-highlight-color-invalid !default;
// Material Design Default Input
// --------------------------------------------------
.text-input-md {
@include margin($text-input-md-margin-top, $text-input-md-margin-end, $text-input-md-margin-bottom, $text-input-md-margin-start);
@include padding(0);
width: calc(100% - #{$text-input-md-margin-end} - #{$text-input-md-margin-start});
}
// Material Design Inset Input
// --------------------------------------------------
.input-md .inset-input {
@include padding(($item-md-padding-top / 2), ($item-md-padding-end / 2), ($item-md-padding-bottom / 2), ($item-md-padding-start / 2));
@include margin(($item-md-padding-top / 2), $item-md-padding-end, ($item-md-padding-bottom / 2), $item-md-padding-start);
}
// Material Design Highlighted Input
// --------------------------------------------------
// Input highlight mixin for focus, valid, and invalid states
@mixin md-input-highlight($highlight-color) {
border-bottom-color: $highlight-color;
box-shadow: inset 0 -1px 0 0 $highlight-color;
}
// Show the focus highlight when the input has focus
@if ($text-input-md-show-focus-highlight) {
// In order to get a 2px border we need to add an inset
// box-shadow 1px (this is to avoid the div resizing)
// TODO remove all uses of input-has-focus in v4
.item-md.item-input.item-input-has-focus .item-inner,
.item-md.item-input.input-has-focus .item-inner {
@include md-input-highlight($text-input-md-highlight-color);
}
// The last item in a list has a border on the item, not the
// inner item, so add it to the item itself
.list-md .item-input.item-input-has-focus:last-child,
.list-md .item-input.input-has-focus:last-child {
@include md-input-highlight($text-input-md-highlight-color);
.item-inner {
box-shadow: none;
}
}
}
// Show the valid highlight when it has the .ng-valid class and a value
@if ($text-input-md-show-valid-highlight) {
// TODO remove all uses of input-has-focus in v4
// TODO remove all uses of input-has-value in v4
.item-md.item-input.ng-valid.item-input-has-value:not(.input-has-focus):not(.item-input-has-focus) .item-inner,
.item-md.item-input.ng-valid.input-has-value:not(.input-has-focus):not(.item-input-has-focus) .item-inner {
@include md-input-highlight($text-input-md-highlight-color-valid);
}
.list-md .item-input.ng-valid.item-input-has-value:not(.input-has-focus):not(.item-input-has-focus):last-child,
.list-md .item-input.ng-valid.input-has-value:not(.input-has-focus):not(.item-input-has-focus):last-child {
@include md-input-highlight($text-input-md-highlight-color-valid);
.item-inner {
box-shadow: none;
}
}
}
// Show the invalid highlight when it has the invalid class and has been touched
@if ($text-input-md-show-invalid-highlight) {
.item-md.item-input.ng-invalid.ng-touched:not(.input-has-focus):not(.item-input-has-focus) .item-inner {
@include md-input-highlight($text-input-md-highlight-color-invalid);
}
.list-md .item-input.ng-invalid.ng-touched:not(.input-has-focus):not(.item-input-has-focus):last-child {
@include md-input-highlight($text-input-md-highlight-color-invalid);
.item-inner {
box-shadow: none;
}
}
}
// Material Design Stacked & Floating Inputs
// --------------------------------------------------
.item-label-stacked .text-input-md,
.item-label-floating .text-input-md {
@include margin(8px, null, 8px, 0);
width: calc(100% - #{$text-input-md-margin-end});
}
.item-label-stacked .select-md,
.item-label-floating .select-md {
@include padding(8px, null, 8px, 0);
}
// Material Design Clear Input Icon
// --------------------------------------------------
.input-md[clearInput] {
position: relative;
}
.input-md[clearInput] .text-input {
@include padding-horizontal(null, $text-input-md-input-clear-icon-width);
}
.input-md .text-input-clear-icon {
@include position-horizontal(null, ($item-md-padding-end / 2));
@include svg-background-image($text-input-md-input-clear-icon-svg);
width: $text-input-md-input-clear-icon-width;
background-size: $text-input-md-input-clear-icon-size;
}

View File

@ -0,0 +1,165 @@
@import "../../themes/ionic.globals";
// Input
// --------------------------------------------------
/// @prop - Color of the input highlight when valid
$text-input-highlight-color-valid: #32db64 !default;
/// @prop - Color of the input highlight when invalid
$text-input-highlight-color-invalid: #f53d3d !default;
/// @prop - Color of the input placeholder
$text-input-placeholder-color: #999 !default;
// 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($text-input-placeholder-color);
@include appearance(none);
@include border-radius(0);
display: inline-block;
flex: 1;
width: 92%;
width: calc(100% - 10px);
border: 0;
background: transparent;
}
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
// The input cover is not clickable when the input is disabled
.input-cover {
@include position(0, null, null, 0);
position: absolute;
width: 100%;
height: 100%;
}
.input[disabled] .input-cover {
pointer-events: none;
}
// Input Cover: Focused
// --------------------------------------------------
// When the input has focus, then the input cover should be hidden
.item-input-has-focus .input-cover {
display: none;
}
.item-input-has-focus {
pointer-events: none;
}
.item-input-has-focus input,
.item-input-has-focus textarea,
.item-input-has-focus a,
.item-input-has-focus button {
pointer-events: auto;
}
// Scroll Assist Input
// --------------------------------------------------
// This input is used to help the app handle
// Next and Previous input tabbing
[next-input] {
@include padding(0);
position: absolute;
bottom: 20px;
width: 1px;
height: 1px;
border: 0;
background: transparent;
pointer-events: none;
}
// Clear Input Icon
// --------------------------------------------------
.text-input-clear-icon {
@include margin(0);
@include padding(0);
@include background-position(center);
position: absolute;
top: 0;
display: none;
height: 100%;
background-repeat: no-repeat;
}
// TODO remove all uses of input-has-focus in v4
// TODO remove all uses of input-has-value in v4
.item-input-has-focus.item-input-has-value .text-input-clear-icon {
display: block;
}

View File

@ -0,0 +1,289 @@
import { Component, Element, Event, EventEmitter, Prop, PropDidChange } from '@stencil/core';
import { createThemedClasses } from '../../utils/theme';
@Component({
tag: 'ion-input',
styleUrls: {
ios: 'input.ios.scss',
md: 'input.md.scss',
wp: 'input.wp.scss'
},
host: {
theme: 'input'
}
})
export class Input {
mode: any;
color: any;
styleTmr: any;
didBlurAfterEdit: boolean;
@Element() el: HTMLElement;
/**
* @output {event} Emitted when the styles change.
*/
@Event() ionStyle: EventEmitter;
/**
* @output {event} Emitted when the input no longer has focus.
*/
@Event() ionBlur: EventEmitter;
/**
* @output {event} Emitted when the input has focus.
*/
@Event() ionFocus: EventEmitter;
/**
* @input {string} Indicates whether the value of the control can be automatically completed by the browser. Defaults to `"off"`.
*/
@Prop() autocomplete: string = 'off';
/**
* @input {string} Whether autocorrection should be enabled when the user is entering/editing the text value. Defaults to `"off"`.
*/
@Prop() autocorrect: string = 'off';
/**
* @input {string} This Boolean attribute lets you specify that a form control should have input focus when the page loads. Defaults to `false`.
*/
@Prop() autofocus: boolean;
/**
* @input {boolean} If true and the type is `checkbox` or `radio`, the control is selected by default. Defaults to `false`.
*/
@Prop() checked: boolean = false;
/**
* @hidden
*/
@PropDidChange('checked')
setChecked() {
this.emitStyle();
}
/**
* @input {boolean} If true, a clear icon will appear in the input when there is a value. Clicking it clears the input. Defaults to `false`.
*/
@Prop() clearInput: boolean = false;
/**
* @input {boolean} If true, the value will be cleared after focus upon edit. Defaults to `true` when `type` is `"password"`, `false` for all other types. Defaults to `false`.
*/
@Prop({state: true}) clearOnEdit: boolean;
/**
* @input {boolean} If true, the user cannot interact with this element. Defaults to `false`.
*/
@Prop() disabled: boolean = false;
/**
* @hidden
*/
@PropDidChange('disabled')
setDisabled() {
this.emitStyle();
}
/**
* @input {any} The minimum value, which must not be greater than its maximum (max attribute) value.
*/
@Prop() min: string;
/**
* @input {any} The maximum value, which must not be less than its minimum (min attribute) value.
*/
@Prop() max: string;
/**
* @input {string} Instructional text that shows before the input has a value.
*/
@Prop() placeholder: string;
/**
* @input {boolean} If true, the user cannot modify the value. Defaults to `false`.
*/
@Prop() readonly: boolean = false;
/**
* @input {string} If true, the element will have its spelling and grammar checked. Defaults to `false`.
*/
@Prop() spellcheck: boolean = false;
/**
* @input {any} Works with the min and max attributes to limit the increments at which a value can be set.
*/
@Prop() step: string;
/**
* @input {string} The type of control to display. The default type is text. Possible values are: `"text"`, `"password"`, `"email"`, `"number"`, `"search"`, `"tel"`, or `"url"`.
*/
@Prop() type: string = 'text';
/**
* @input {string} The text value of the input.
*/
@Prop({ state: true }) value: string;
ionViewDidLoad() {
this.emitStyle();
// By default, password inputs clear after focus when they have content
if (this.type === 'password' && this.clearOnEdit !== false) {
this.clearOnEdit = true;
}
}
private emitStyle() {
clearTimeout(this.styleTmr);
let styles = {
'input': true,
'input-checked': this.checked,
'input-disabled': this.disabled,
'input-has-value': this.hasValue(),
'input-has-focus': this.hasFocus()
};
this.styleTmr = setTimeout(() => {
this.ionStyle.emit(styles);
});
}
/**
* @hidden
*/
hasValue(): boolean {
return (this.value !== null && this.value !== undefined && this.value !== '');
}
/**
* @hidden
*/
inputBlurred(ev: any) {
this.ionBlur.emit(ev);
this.focusChange(this.hasFocus());
this.emitStyle();
}
/**
* @hidden
*/
inputChanged(ev: any) {
this.value = ev.target && ev.target.value;
this.emitStyle();
}
/**
* @hidden
*/
inputFocused(ev: any) {
this.ionFocus.emit(ev);
this.focusChange(this.hasFocus());
this.emitStyle();
}
/**
* @hidden
*/
hasFocus(): boolean {
// check if an input has focus or not
return this.el && (this.el.querySelector(':focus') === this.el.querySelector('input'));
}
/**
* @hidden
*/
focusChange(inputHasFocus: boolean) {
// If clearOnEdit is enabled and the input blurred but has a value, set a flag
if (this.clearOnEdit && !inputHasFocus && this.hasValue()) {
this.didBlurAfterEdit = true;
}
}
/**
* @hidden
*/
inputKeydown() {
this.checkClearOnEdit();
}
/**
* Check if we need to clear the text input if clearOnEdit is enabled
* @hidden
*/
checkClearOnEdit() {
if (!this.clearOnEdit) {
return;
}
// Did the input value change after it was blurred and edited?
if (this.didBlurAfterEdit && this.hasValue()) {
// Clear the input
this.clearTextInput();
}
// Reset the flag
this.didBlurAfterEdit = false;
}
/**
* @hidden
*/
clearTextInput() {
console.debug('Should clear input', this.el);
this.value = '';
}
render() {
const themedClasses = createThemedClasses(this.mode, this.color, 'text-input');
// TODO aria-labelledby={this.item.labelId}
// OLD RENDER
// '<input [(ngModel)]="_value" [type]="type" (blur)="inputBlurred($event)" (focus)="inputFocused($event)" [placeholder]="placeholder" [disabled]="disabled" [readonly]="readonly" class="text-input" [ngClass]="\'text-input-\' + _mode" *ngIf="_type!==\'textarea\'" #input>' +
// '<textarea [(ngModel)]="_value" (blur)="inputBlurred($event)" (focus)="inputFocused($event)" [placeholder]="placeholder" [disabled]="disabled" [readonly]="readonly" class="text-input" [ngClass]="\'text-input-\' + _mode" *ngIf="_type===\'textarea\'" #textarea></textarea>' +
// '<input [type]="type" aria-hidden="true" next-input *ngIf="_useAssist">' +
// '<ion-button clear [hidden]="!clearInput" type="button" class="text-input-clear-icon" (click)="clearTextInput()" (mousedown)="clearTextInput()"></ion-button>' +
// '<div (touchstart)="pointerStart($event)" (touchend)="pointerEnd($event)" (mousedown)="pointerStart($event)" (mouseup)="pointerEnd($event)" class="input-cover" tappable *ngIf="_useAssist"></div>',
return (
<input
aria-disabled={this.disabled ? 'true' : false}
autoComplete={this.autocomplete}
autoCorrect={this.autocorrect}
autoFocus={this.autofocus}
checked={this.checked}
disabled={this.disabled}
min={this.min}
max={this.max}
placeholder={this.placeholder}
readOnly={this.readonly}
spellCheck={this.spellcheck}
step={this.step}
type={this.type}
value={this.value}
class={themedClasses}
onBlur={this.inputBlurred.bind(this)}
onInput={this.inputChanged.bind(this)}
onFocus={this.inputFocused.bind(this)}
onKeyDown={this.inputKeydown.bind(this)}
/>
)
}
}

View File

@ -0,0 +1,162 @@
@import "../../themes/ionic.globals.wp";
@import "./input";
// Windows Input
// --------------------------------------------------
/// @prop - Background color of the input
$text-input-wp-background-color: $list-wp-background-color !default;
/// @prop - Border color of the input
$text-input-wp-border-color: $input-wp-border-color !default;
/// @prop - Border width of the input
$text-input-wp-border-width: 2px !default;
/// @prop - Margin top of the input
$text-input-wp-margin-top: $item-wp-padding-top !default;
// deprecated
$text-input-wp-margin-right: ($item-wp-padding-end / 2) !default;
/// @prop - Margin end of the input
$text-input-wp-margin-end: $text-input-wp-margin-right !default;
/// @prop - Margin bottom of the input
$text-input-wp-margin-bottom: $item-wp-padding-bottom !default;
// deprecated
$text-input-wp-margin-left: ($item-wp-padding-start / 2) !default;
/// @prop - Margin start of the input
$text-input-wp-margin-start: $text-input-wp-margin-left !default;
/// @prop - Vertical padding of the input
$text-input-wp-padding-vertical: 0 !default;
/// @prop - Horizontal padding of the input
$text-input-wp-padding-horizontal: 8px !default;
/// @prop - Line height of the input
$text-input-wp-line-height: 3rem !default;
/// @prop - Width of the icon used to clear the input
$text-input-wp-input-clear-icon-width: 30px !default;
/// @prop - Color of the icon used to clear the input
$text-input-wp-input-clear-icon-color: $input-wp-border-color !default;
/// @prop - Icon used to clear the input
$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;
/// @prop - Size of the icon used to clear the input
$text-input-wp-input-clear-icon-size: 22px !default;
/// @prop - Show the focus highlight when the input has focus
$text-input-wp-show-focus-highlight: true !default;
/// @prop - Show the valid highlight when it is valid and has a value
$text-input-wp-show-valid-highlight: $text-input-wp-show-focus-highlight !default;
/// @prop - Show the invalid highlight when it is invalid and has value
$text-input-wp-show-invalid-highlight: $text-input-wp-show-focus-highlight !default;
/// @prop - Color of the input highlight
$text-input-wp-highlight-color: color($colors-wp, primary) !default;
/// @prop - Color of the input highlight when valid
$text-input-wp-highlight-color-valid: $text-input-highlight-color-valid !default;
/// @prop - Color of the input highlight when invalid
$text-input-wp-highlight-color-invalid: $text-input-highlight-color-invalid !default;
// Windows Default Input
// --------------------------------------------------
.text-input-wp {
@include margin($text-input-wp-margin-top, $text-input-wp-margin-end, $text-input-wp-margin-bottom, $text-input-wp-margin-start);
@include padding($text-input-wp-padding-vertical, $text-input-wp-padding-horizontal);
width: calc(100% - #{$text-input-wp-margin-end} - #{$text-input-wp-margin-start});
border: $text-input-wp-border-width solid $text-input-wp-border-color;
line-height: $text-input-wp-line-height;
}
// Windows Inset Input
// --------------------------------------------------
.item-wp .inset-input {
@include padding(($item-wp-padding-top / 2), ($item-wp-padding-end / 2), ($item-wp-padding-bottom / 2), ($item-wp-padding-start / 2));
@include margin(($item-wp-padding-top / 2), $item-wp-padding-end, ($item-wp-padding-bottom / 2), $item-wp-padding-start);
}
// Windows Highlighted Input
// --------------------------------------------------
// Show the focus highlight when the input has focus
@if ($text-input-wp-show-focus-highlight) {
// TODO remove all uses of input-has-focus in v4
// TODO remove all uses of input-has-value in v4
.item-wp.item-input.item-input-has-focus .text-input,
.item-wp.item-input.input-has-focus .text-input {
border-color: $text-input-wp-highlight-color;
}
}
// Show the valid highlight when it has the .ng-valid class and a value
@if ($text-input-wp-show-valid-highlight) {
.item-wp.item-input.ng-valid.item-input-has-value:not(.input-has-focus):not(.item-input-has-focus) .text-input,
.item-wp.item-input.ng-valid.input-has-value:not(.input-has-focus):not(.item-input-has-focus) .text-input {
border-color: $text-input-wp-highlight-color-valid;
}
}
// Show the invalid highlight when it has the invalid class and has been touched
@if ($text-input-wp-show-invalid-highlight) {
.item-wp.item-input.ng-invalid.ng-touched:not(.input-has-focus):not(.item-input-has-focus) .text-input {
border-color: $text-input-wp-highlight-color-invalid;
}
}
// Windows Stacked & Floating Inputs
// --------------------------------------------------
.item-label-stacked .text-input-wp,
.item-label-floating .text-input-wp,
.item-label-stacked .select-wp,
.item-label-floating .select-wp {
@include margin(8px, null, 8px, 0);
width: calc(100% - #{$text-input-wp-margin-end});
}
.item-wp.item-label-stacked [item-right], // deprecated
.item-wp.item-label-floating [item-right], // deprecated
.item-wp.item-label-stacked [item-end],
.item-wp.item-label-floating [item-end] {
align-self: flex-end;
}
// Windows Clear Input Icon
// --------------------------------------------------
.input-wp[clearInput] {
position: relative;
}
.input-wp[clearInput] .text-input {
@include padding-horizontal(null, $text-input-wp-input-clear-icon-width);
}
.input-wp .text-input-clear-icon {
@include position-horizontal(null, ($item-wp-padding-end / 2));
@include svg-background-image($text-input-wp-input-clear-icon-svg);
width: $text-input-wp-input-clear-icon-width;
background-size: $text-input-wp-input-clear-icon-size;
}

View File

@ -0,0 +1,208 @@
<!DOCTYPE html>
<html dir="ltr">
<head>
<meta charset="UTF-8">
<title>Ionic Inputs</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<script src="/dist/ionic.js"></script>
</head>
<body>
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Basic Form</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<ion-item>
<ion-label>Default</ion-label>
<ion-input></ion-input>
</ion-item>
<ion-item>
<ion-label floating>Floating</ion-label>
<ion-input checked></ion-input>
</ion-item>
<ion-item>
<ion-label fixed>Type #</ion-label>
<ion-input type="number" value="333"></ion-input>
</ion-item>
<ion-item>
<ion-label stacked>Password</ion-label>
<ion-input type="password"></ion-input>
</ion-item>
<ion-item>
<ion-label stacked>Placeholder</ion-label>
<ion-input placeholder="Enter Something"></ion-input>
</ion-item>
<ion-item>
<ion-label>Disabled</ion-label>
<ion-input id="dynamicDisabled" value="Disabled" disabled></ion-input>
</ion-item>
<ion-item>
<ion-label>Readonly</ion-label>
<ion-input id="dynamicReadonly" value="Readonly" readonly></ion-input>
</ion-item>
<ion-item>
<ion-label>Toggle</ion-label>
<ion-toggle checked slot="end"></ion-toggle>
</ion-item>
</ion-list>
<div text-center>
<ion-button onclick="toggleBoolean('dynamicDisabled', 'disabled')">
Toggle Disabled
</ion-button>
<ion-button color="secondary" onclick="toggleBoolean('dynamicReadonly', 'readonly')">
Toggle Readonly
</ion-button>
</div>
<!--
<form>
<ion-list>
<ion-item>
<ion-label floating>Email</ion-label>
<ion-input clearInput id="login.email" name="email" type="email" required></ion-input>
</ion-item>
<ion-item>
<ion-label floating>Username</ion-label>
<ion-input clearInput id="login.username" name="username"></ion-input>
</ion-item>
<ion-item>
<ion-label>Password</ion-label>
<ion-input clearInput id="login.password" name="password" type="password" required></ion-input>
</ion-item>
<ion-item>
<ion-label>Comments</ion-label>
<ion-textarea clearInput id="login.comments" name="comments" required>Comment value</ion-textarea>
</ion-item>
<div padding-left padding-right>
<ion-button block onclick="submit($event, login)">Login</ion-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>
<ion-list>
<ion-list-header>
Form w/ disabled inputs
</ion-list-header>
<ion-item>
<ion-label floating>Email</ion-label>
<ion-input type="email" name="email"></ion-input>
</ion-item>
<ion-item>
<ion-label floating>Username</ion-label>
<ion-input name="username"></ion-input>
</ion-item>
<ion-item>
<ion-label floating>Password</ion-label>
<ion-input type="password" name="password"></ion-input>
</ion-item>
<ion-item>
<ion-label floating>Comments</ion-label>
<ion-textarea name="comments"></ion-textarea>
</ion-item>
<div padding-left padding-right>
<ion-button block type="submit">Login</ion-button>
<ion-button block type="button" color="secondary" onclick="toggleDisable()">Disable (toggle)</ion-button>
</div>
<div padding-left>
<b>Valid form?:</b> {{ lf.form.valid }}<br>
<b>Submitted form?:</b> {{ submitted }}<br>
<b>Email:</b> {{ userForm.controls.email.value }}<br>
<b>Username:</b> {{ userForm.controls.username.value }}<br>
<b>Password:</b> {{ userForm.controls.password.value }}<br>
<b>Comments:</b> {{ userForm.controls.comments.value }}<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="false"
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 id="dynamicTextAreaDisabled" value="Value"></ion-textarea>
</ion-item>
</ion-list>
<ion-button onclick="disable()">Disable</ion-button> -->
</ion-content>
<script>
function toggleBoolean(id, prop) {
var ele = document.getElementById(id);
var isTrue = ele[prop] ? false : true;
ele[prop] = isTrue;
console.log('in toggleBoolean, setting to', isTrue);
}
</script>
</ion-app>
</body>
</html>

View File

@ -0,0 +1,49 @@
// /**
// * @name TextArea
// * @description
// *
// * `ion-textarea` 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.
// *
// * Note 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>
// *
// * <ion-item>
// * <ion-label>Long Description</ion-label>
// * <ion-textarea rows="6" placeholder="enter long description here..."></ion-textarea>
// * </ion-item>
// * ```
// *
// * @demo /docs/demos/src/textarea/
// */
// TODO textarea
// if (this.type === TEXTAREA) {
// item.setElementClass('item-textarea', true);
// }

View File

@ -13,6 +13,7 @@ exports.config = {
{ components: ['ion-gesture', 'ion-scroll'], priority: 'low' }, { components: ['ion-gesture', 'ion-scroll'], priority: 'low' },
{ components: ['ion-grid', 'ion-row', 'ion-col'] }, { components: ['ion-grid', 'ion-row', 'ion-col'] },
{ components: ['ion-item', 'ion-item-divider', 'ion-item-sliding', 'ion-item-options', 'ion-item-option', 'ion-label', 'ion-list', 'ion-list-header', 'ion-skeleton-text'] }, { components: ['ion-item', 'ion-item-divider', 'ion-item-sliding', 'ion-item-options', 'ion-item-option', 'ion-label', 'ion-list', 'ion-list-header', 'ion-skeleton-text'] },
{ components: ['ion-input'] },
{ components: ['ion-loading', 'ion-loading-controller'] }, { components: ['ion-loading', 'ion-loading-controller'] },
{ components: ['ion-menu'], priority: 'low' }, { components: ['ion-menu'], priority: 'low' },
{ components: ['ion-modal', 'ion-modal-controller'] }, { components: ['ion-modal', 'ion-modal-controller'] },