chore(packages): move the packages to root

This commit is contained in:
Brandy Carney
2018-03-12 16:02:25 -04:00
parent 097f1a2cd3
commit d37623a2ca
1255 changed files with 38 additions and 38 deletions

View File

@ -0,0 +1,318 @@
# ion-textarea
The textarea component is used for multi-line text input. A native textarea element is rendered inside of the component. The user experience and interactivity of the textarea component is improved by having control over the native textarea.
Unlike the native textarea element, the Ionic textarea does not support loading its value from the inner content. The textarea value should be set in the `value` attribute.
The textarea component accepts the [native textarea attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea) in addition to the Ionic properties.
```html
<!-- Default textarea -->
<ion-textarea></ion-textarea>
<!-- Textarea in an item with a placeholder -->
<ion-item>
<ion-textarea placeholder="Enter more information here..."></ion-textarea>
</ion-item>
<!-- Textarea in an item with a floating label -->
<ion-item>
<ion-label floating>Description</ion-label>
<ion-textarea></ion-textarea>
</ion-item>
<!-- Disabled and readonly textarea in an item with a stacked label -->
<ion-item>
<ion-label stacked>Summary</ion-label>
<ion-textarea
disabled
readonly
value="Ionic enables developers to build performant, high-quality mobile apps.">
</ion-textarea>
</ion-item>
<!-- Textarea that clears the value on edit -->
<ion-item>
<ion-label>Comment</ion-label>
<ion-textarea clear-on-edit="true"></ion-textarea>
</ion-item>
<!-- Textarea with custom number of rows and cols -->
<ion-item>
<ion-label>Notes</ion-label>
<ion-textarea rows="6" cols="20" placeholder="Enter any notes here..."></ion-textarea>
</ion-item>
```
<!-- Auto Generated Below -->
## Properties
#### autocapitalize
string
Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. Defaults to `"none"`.
#### autocomplete
string
Indicates whether the value of the control can be automatically completed by the browser. Defaults to `"off"`.
#### autofocus
boolean
This Boolean attribute lets you specify that a form control should have input focus when the page loads. Defaults to `false`.
#### clearOnEdit
boolean
If true, the value will be cleared after focus upon edit. Defaults to `true` when `type` is `"password"`, `false` for all other types.
#### cols
number
The visible width of the text control, in average character widths. If it is specified, it must be a positive integer.
#### debounce
number
Set the amount of time, in milliseconds, to wait to trigger the `ionInput` event after each keystroke. Default `0`.
#### disabled
boolean
If true, the user cannot interact with the textarea. Defaults to `false`.
#### maxlength
number
If the value of the type attribute is `text`, `email`, `search`, `password`, `tel`, or `url`, this attribute specifies the maximum number of characters that the user can enter.
#### minlength
number
If the value of the type attribute is `text`, `email`, `search`, `password`, `tel`, or `url`, this attribute specifies the minimum number of characters that the user can enter.
#### name
string
The name of the control, which is submitted with the form data.
#### placeholder
string
Instructional text that shows before the input has a value.
#### readonly
boolean
If true, the user cannot modify the value. Defaults to `false`.
#### required
boolean
If true, the user must fill in a value before submitting a form.
#### rows
number
The number of visible text lines for the control.
#### spellcheck
boolean
If true, the element will have its spelling and grammar checked. Defaults to `false`.
#### value
string
The value of the textarea.
#### wrap
string
Indicates how the control wraps text. Possible values are: `"hard"`, `"soft"`, `"off"`.
## Attributes
#### autocapitalize
string
Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. Defaults to `"none"`.
#### autocomplete
string
Indicates whether the value of the control can be automatically completed by the browser. Defaults to `"off"`.
#### autofocus
boolean
This Boolean attribute lets you specify that a form control should have input focus when the page loads. Defaults to `false`.
#### clear-on-edit
boolean
If true, the value will be cleared after focus upon edit. Defaults to `true` when `type` is `"password"`, `false` for all other types.
#### cols
number
The visible width of the text control, in average character widths. If it is specified, it must be a positive integer.
#### debounce
number
Set the amount of time, in milliseconds, to wait to trigger the `ionInput` event after each keystroke. Default `0`.
#### disabled
boolean
If true, the user cannot interact with the textarea. Defaults to `false`.
#### maxlength
number
If the value of the type attribute is `text`, `email`, `search`, `password`, `tel`, or `url`, this attribute specifies the maximum number of characters that the user can enter.
#### minlength
number
If the value of the type attribute is `text`, `email`, `search`, `password`, `tel`, or `url`, this attribute specifies the minimum number of characters that the user can enter.
#### name
string
The name of the control, which is submitted with the form data.
#### placeholder
string
Instructional text that shows before the input has a value.
#### readonly
boolean
If true, the user cannot modify the value. Defaults to `false`.
#### required
boolean
If true, the user must fill in a value before submitting a form.
#### rows
number
The number of visible text lines for the control.
#### spellcheck
boolean
If true, the element will have its spelling and grammar checked. Defaults to `false`.
#### value
string
The value of the textarea.
#### wrap
string
Indicates how the control wraps text. Possible values are: `"hard"`, `"soft"`, `"off"`.
## Events
#### ionBlur
Emitted when the input loses focus.
#### ionFocus
Emitted when the input has focus.
#### ionInput
Emitted when the input value has changed.
#### ionStyle
Emitted when the styles change.
----------------------------------------------
*Built with [StencilJS](https://stenciljs.com/)*

View File

@ -0,0 +1,19 @@
'use strict';
const { By, until } = require('selenium-webdriver');
const { register, Page, platforms } = require('../../../../../scripts/e2e');
class E2ETestPage extends Page {
constructor(driver, platform) {
super(driver, `http://localhost:3333/src/components/textarea/test/basic?ionicplatform=${platform}`);
}
}
platforms.forEach(platform => {
describe('textarea/basic', () => {
register('should init', driver => {
const page = new E2ETestPage(driver, platform);
return page.navigate('#content');
});
});
});

View File

@ -0,0 +1,125 @@
<!DOCTYPE html>
<html dir="ltr">
<head>
<meta charset="UTF-8">
<title>Input - Textarea</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>Input - Textarea</ion-title>
</ion-toolbar>
</ion-header>
<ion-content id="content">
<ion-list>
<ion-item>
<ion-label color="primary">Inline Label</ion-label>
<ion-textarea placeholder="Textarea"></ion-textarea>
</ion-item>
<ion-item>
<ion-label color="primary" fixed>Fixed Label</ion-label>
<ion-textarea placeholder="Textarea"></ion-textarea>
</ion-item>
<ion-item>
<ion-textarea placeholder="Textarea with no label"></ion-textarea>
</ion-item>
<ion-item>
<ion-label color="primary" stacked>Stacked Label</ion-label>
<ion-textarea placeholder="Textarea"></ion-textarea>
</ion-item>
<ion-item>
<ion-label color="primary" floating>Floating Label</ion-label>
<ion-textarea></ion-textarea>
</ion-item>
<ion-item>
<ion-label>Disabled</ion-label>
<ion-textarea id="dynamicDisabled" value="Disabled" disabled></ion-textarea>
</ion-item>
<ion-item>
<ion-label>Readonly</ion-label>
<ion-textarea id="dynamicReadonly" value="Readonly" readonly></ion-textarea>
</ion-item>
<ion-item>
<ion-label>Dynamic Value</ion-label>
<ion-input id="dynamicValue" value="dynamic"></ion-input>
</ion-item>
<ion-item>
<ion-label>Dynamic Value</ion-label>
<ion-textarea id="dynamicTextareaValue" value="dynamic"></ion-textarea>
</ion-item>
<ion-item>
<ion-label color="primary">Clear on Edit</ion-label>
<ion-textarea clear-on-edit="true"></ion-textarea>
</ion-item>
<ion-item>
<ion-label color="primary">Clear on Edit</ion-label>
<ion-input clear-on-edit="true"></ion-input>
</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>
<ion-button color="light" onclick="toggleString('dynamicValue', 'value'); toggleString('dynamicTextareaValue', 'value')">
Toggle Value
</ion-button>
<ion-button color="danger" onclick="clearString('dynamicValue', 'value'); clearString('dynamicTextareaValue', 'value')">
Clear Value
</ion-button>
</div>
</ion-content>
<script>
function toggleBoolean(id, prop) {
var el = document.getElementById(id);
var isTrue = el[prop] ? false : true;
el[prop] = isTrue;
console.log('in toggleBoolean, setting', prop, 'to', isTrue);
}
function toggleString(id, prop) {
var el = document.getElementById(id);
console.log('INPUT ELE', el);
var newString = el[prop] === 'dynamic' ? 'changed' : 'dynamic';
el[prop] = newString;
console.log('in toggleString, setting', prop, 'to', newString);
}
function clearString(id, prop) {
var el = document.getElementById(id);
el[prop] = '';
console.log('in toggleString, setting', prop, 'to', '');
}
</script>
</ion-app>
</body>
</html>

View File

@ -0,0 +1,19 @@
'use strict';
const { By, until } = require('selenium-webdriver');
const { register, Page, platforms } = require('../../../../../scripts/e2e');
class E2ETestPage extends Page {
constructor(driver, platform) {
super(driver, `http://localhost:3333/src/components/textarea/test/standalone?ionicplatform=${platform}`);
}
}
platforms.forEach(platform => {
describe('textarea/standalone', () => {
register('should init', driver => {
const page = new E2ETestPage(driver, platform);
return page.navigate();
});
});
});

View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html dir="ltr">
<head>
<meta charset="UTF-8">
<title>Textarea - Standalone</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-textarea placeholder="Textarea"></ion-textarea>
<ion-textarea value="value"></ion-textarea>
<ion-textarea value="44"></ion-textarea>
</body>
</html>

View File

@ -0,0 +1,43 @@
@import "./textarea";
@import "./textarea.ios.vars";
// iOS Textarea
// --------------------------------------------------
.native-textarea-ios {
@include placeholder($textarea-ios-placeholder-color);
@include margin($textarea-ios-margin-top, $textarea-ios-margin-end, $textarea-ios-margin-bottom, $textarea-ios-margin-start);
@include padding(0);
width: calc(100% - #{($textarea-ios-margin-end + $textarea-ios-margin-start)});
font-family: $textarea-ios-font-family;
font-size: $textarea-ios-font-size;
}
// iOS Stacked & Floating Textarea
// --------------------------------------------------
.item-ios.item-label-stacked .native-textarea,
.item-ios.item-label-floating .native-textarea {
@include margin(8px, null, 8px, 0);
width: calc(100% - #{$textarea-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);
}
// iOS Textarea After Label
// --------------------------------------------------
.label-ios + ion-textarea .native-textarea,
.label-ios + .input + .cloned-input {
@include margin-horizontal($textarea-ios-by-label-margin-start, null);
width: calc(100% - (#{$item-ios-padding-end} / 2) - #{$item-ios-padding-start});
}

View File

@ -0,0 +1,30 @@
@import "../../themes/ionic.globals.ios";
@import "../item/item.ios.vars";
// iOS Textarea
// --------------------------------------------------
/// @prop - Margin start of the textarea when it is after a label
$textarea-ios-by-label-margin-start: $item-ios-padding-start !default;
/// @prop - Font family of the textarea
$textarea-ios-font-family: $font-family-ios-base !default;
/// @prop - Font size of the textarea
$textarea-ios-font-size: inherit !default;
/// @prop - Margin top of the textarea
$textarea-ios-margin-top: $item-ios-padding-top !default;
/// @prop - Margin end of the textarea
$textarea-ios-margin-end: ($item-ios-padding-end / 2) !default;
/// @prop - Margin bottom of the textarea
$textarea-ios-margin-bottom: $item-ios-padding-bottom !default;
/// @prop - Margin start of the textarea
$textarea-ios-margin-start: 0 !default;
/// @prop - Placeholder text color of the textarea
$textarea-ios-placeholder-color: $placeholder-text-ios-color !default;

View File

@ -0,0 +1,27 @@
@import "./textarea";
@import "./textarea.md.vars";
// Material Design Textarea
// --------------------------------------------------
.native-textarea-md {
@include placeholder($textarea-md-placeholder-color);
@include margin($textarea-md-margin-top, $textarea-md-margin-end, $textarea-md-margin-bottom, $textarea-md-margin-start);
@include padding(0);
width: calc(100% - #{$textarea-md-margin-end} - #{$textarea-md-margin-start});
font-family: $textarea-md-font-family;
font-size: $textarea-md-font-size;
}
// Material Design Stacked & Floating Textarea
// --------------------------------------------------
.item-label-stacked .native-textarea-md,
.item-label-floating .native-textarea-md {
@include margin(8px, null, 8px, 0);
width: calc(100% - #{$textarea-md-margin-end});
}

View File

@ -0,0 +1,27 @@
@import "../../themes/ionic.globals.md";
@import "../item/item.md.vars";
// Material Design Textarea
// --------------------------------------------------
/// @prop - Font family of the textarea
$textarea-md-font-family: $font-family-md-base !default;
/// @prop - Font size of the textarea
$textarea-md-font-size: inherit !default;
/// @prop - Margin top of the textarea
$textarea-md-margin-top: $item-md-padding-top !default;
/// @prop - Margin end of the textarea
$textarea-md-margin-end: ($item-md-padding-end / 2) !default;
/// @prop - Margin bottom of the textarea
$textarea-md-margin-bottom: $item-md-padding-bottom !default;
/// @prop - Margin start of the textarea
$textarea-md-margin-start: ($item-md-padding-start / 2) !default;
/// @prop - Placeholder text color of the textarea
$textarea-md-placeholder-color: $placeholder-text-md-color !default;

View File

@ -0,0 +1,86 @@
@import "./textarea.vars";
// Textarea
// --------------------------------------------------
ion-textarea {
position: relative;
display: block;
flex: 1;
width: 100%;
}
.item-input ion-textarea {
position: static;
}
// Textarea Within An Item
// --------------------------------------------------
.item.item-textarea {
align-items: stretch;
}
// Native Textarea
// --------------------------------------------------
.native-textarea {
@include appearance(none);
@include border-radius(0);
display: block;
flex: 1;
width: 92%;
width: calc(100% - 10px);
border: 0;
font-size: inherit;
background: transparent;
&:active,
&:focus {
outline: none;
}
}
.native-textarea[disabled] {
opacity: .4;
}
.platform-mobile .native-textarea {
resize: none;
}
.item-input-has-focus a,
.item-input-has-focus button,
.item-input-has-focus textarea {
pointer-events: auto;
}
// 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
.textarea-cover {
@include position(0, null, null, 0);
position: absolute;
width: 100%;
height: 100%;
}
.textarea[disabled] .textarea-cover {
pointer-events: none;
}

View File

@ -0,0 +1,272 @@
import { Component, Element, Event, EventEmitter, Prop, Watch } from '@stencil/core';
import { debounceEvent } from '../../utils/helpers';
import { createThemedClasses } from '../../utils/theme';
import { TextareaComponent } from '../input/input-base';
@Component({
tag: 'ion-textarea',
// TODO: separate textarea from input scss
// right now we're cheating by knowing ion-input
// css is bundled with ion-textarea
styleUrls: {
ios: 'textarea.ios.scss',
md: 'textarea.md.scss'
},
host: {
theme: 'textarea'
}
})
export class Textarea implements TextareaComponent {
mode: string;
color: string;
didBlurAfterEdit: boolean;
styleTmr: number;
@Element() private el: HTMLElement;
/**
* Emitted when the input value has changed.
*/
@Event() ionInput: EventEmitter;
/**
* Emitted when the styles change.
*/
@Event() ionStyle: EventEmitter;
/**
* Emitted when the input loses focus.
*/
@Event() ionBlur: EventEmitter;
/**
* Emitted when the input has focus.
*/
@Event() ionFocus: EventEmitter;
/**
* Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. Defaults to `"none"`.
*/
@Prop() autocapitalize = 'none';
/**
* Indicates whether the value of the control can be automatically completed by the browser. Defaults to `"off"`.
*/
@Prop() autocomplete = 'off';
/**
* This Boolean attribute lets you specify that a form control should have input focus when the page loads. Defaults to `false`.
*/
@Prop() autofocus = false;
/**
* If true, the value will be cleared after focus upon edit. Defaults to `true` when `type` is `"password"`, `false` for all other types.
*/
@Prop({ mutable: true }) clearOnEdit: boolean;
/**
* Set the amount of time, in milliseconds, to wait to trigger the `ionInput` event after each keystroke. Default `0`.
*/
@Prop() debounce = 0;
@Watch('debounce')
protected debounceChanged() {
this.ionInput = debounceEvent(this.ionInput, this.debounce);
}
/**
* If true, the user cannot interact with the textarea. Defaults to `false`.
*/
@Prop() disabled = false;
@Watch('disabled')
protected disabledChanged() {
this.emitStyle();
}
/**
* If the value of the type attribute is `text`, `email`, `search`, `password`, `tel`, or `url`, this attribute specifies the maximum number of characters that the user can enter.
*/
@Prop() maxlength: number;
/**
* If the value of the type attribute is `text`, `email`, `search`, `password`, `tel`, or `url`, this attribute specifies the minimum number of characters that the user can enter.
*/
@Prop() minlength: number;
/**
* The name of the control, which is submitted with the form data.
*/
@Prop() name: string;
/**
* Instructional text that shows before the input has a value.
*/
@Prop() placeholder: string;
/**
* If true, the user cannot modify the value. Defaults to `false`.
*/
@Prop() readonly = false;
/**
* If true, the user must fill in a value before submitting a form.
*/
@Prop() required = false;
/**
* If true, the element will have its spelling and grammar checked. Defaults to `false`.
*/
@Prop() spellcheck = false;
/**
* The visible width of the text control, in average character widths. If it is specified, it must be a positive integer.
*/
@Prop() cols: number;
/**
* The number of visible text lines for the control.
*/
@Prop() rows: number;
/**
* Indicates how the control wraps text. Possible values are: `"hard"`, `"soft"`, `"off"`.
*/
@Prop() wrap: string;
/**
* The value of the textarea.
*/
@Prop({ mutable: true }) value: string;
/**
* Update the native input element when the value changes
*/
@Watch('value')
protected valueChanged() {
const inputEl = this.el.querySelector('textarea');
if (inputEl.value !== this.value) {
inputEl.value = this.value;
}
}
componentDidLoad() {
this.debounceChanged();
this.emitStyle();
}
private emitStyle() {
clearTimeout(this.styleTmr);
const styles = {
'textarea': true,
'input': true,
'input-disabled': this.disabled,
'input-has-value': this.hasValue(),
'input-has-focus': this.hasFocus()
};
this.styleTmr = setTimeout(() => {
this.ionStyle.emit(styles);
});
}
clearTextInput(ev: Event) {
this.value = '';
this.ionInput.emit(ev);
}
inputBlurred(ev: Event) {
this.ionBlur.emit(ev);
this.focusChange(this.hasFocus());
this.emitStyle();
}
inputChanged(ev: Event) {
this.value = ev.target && (ev.target as HTMLInputElement).value;
this.ionInput.emit(ev);
this.emitStyle();
}
inputFocused(ev: Event) {
this.ionFocus.emit(ev);
this.focusChange(this.hasFocus());
this.emitStyle();
}
inputKeydown(ev: Event) {
this.checkClearOnEdit(ev);
}
/**
* Check if we need to clear the text input if clearOnEdit is enabled
*/
checkClearOnEdit(ev: Event) {
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(ev);
}
// Reset the flag
this.didBlurAfterEdit = false;
}
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;
}
}
hasFocus(): boolean {
// check if an input has focus or not
return this.el && (this.el.querySelector(':focus') === this.el.querySelector('textarea'));
}
hasValue(): boolean {
return (this.value !== null && this.value !== undefined && this.value !== '');
}
render() {
const themedClasses = createThemedClasses(this.mode, this.color, 'native-textarea');
// TODO aria-labelledby={this.item.labelId}
return (
<textarea
autoCapitalize={this.autocapitalize}
// autoComplete={this.autocomplete}
autoFocus={this.autofocus}
disabled={this.disabled}
maxLength={this.maxlength}
minLength={this.minlength}
name={this.name}
placeholder={this.placeholder}
readOnly={this.readonly}
required={this.required}
spellCheck={this.spellcheck}
cols={this.cols}
rows={this.rows}
wrap={this.wrap}
class={themedClasses}
onBlur={this.inputBlurred.bind(this)}
onInput={this.inputChanged.bind(this)}
onFocus={this.inputFocused.bind(this)}
onKeyDown={this.inputKeydown.bind(this)}
>
{this.value}
</textarea>
);
}
}

View File

@ -0,0 +1 @@
@import "../../themes/ionic.globals";