wip mwc-textfield

This commit is contained in:
Frankie Fu 2019-07-11 10:04:14 -07:00 committed by Elliott Marquez
parent a02e735528
commit 5038e4f253
9 changed files with 529 additions and 51 deletions

View File

@ -196,6 +196,13 @@ limitations under the License.
<span class="mdc-list-item__secondary-text">Tabs with support for icon and text labels</span>
</span>
</a>
<a role="listitem" class="mdc-list-item" href="textfield.html">
<span class="demo-catalog-list-icon mdc-list-item__graphic"><img src="https://material-components-web.appspot.com/images/ic_text_field_24px.svg"></span>
<span class="mdc-list-item__text">
Text field
<span class="mdc-list-item__secondary-text">Single and multiline text fields</span>
</span>
</a>
<a role="listitem" class="mdc-list-item" href="top-app-bar.html">
<span class="demo-catalog-list-icon mdc-list-item__graphic"><img src="https://material-components-web.appspot.com/images/ic_component_24px.svg"></span>
<span class="mdc-list-item__text">

View File

@ -1,7 +1,7 @@
<!doctype html>
<!--
@license
Copyright 2018 Google Inc. All Rights Reserved.
Copyright 2019 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -21,8 +21,22 @@ limitations under the License.
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="../node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js"></script>
<script type="module" src="../node_modules/@material/mwc-textfield/mwc-textfield.js"></script>
<script type="module" src="../node_modules/@material/mwc-textfield/mwc-textarea.js"></script>
<script type="module" src="../node_modules/@material/mwc-icon/mwc-icon.js"></script>
<link rel="stylesheet" href="demo-component.css">
<style>
mwc-textfield {
min-width: 240px;
}
.shaped-filled {
--mwc-textfield-filled-border-radius: 16px 16px 0 0;
}
.shaped-outlined {
--mwc-textfield-outlined-leading-width: 28px;
--mwc-textfield-outlined-leading-border-radius: 28px 0 0 28px;
--mwc-textfield-outlined-trailing-border-radius: 0 28px 28px 0;
}
</style>
</head>
<body class="unresolved">
<header>
@ -31,74 +45,63 @@ limitations under the License.
</header>
<main>
<h4>Filled</h4>
<div class="demo-group-spaced">
<mwc-textfield></mwc-textfield>
<mwc-textfield label="Say something..."></mwc-textfield>
<mwc-textfield label="Say something..." value="hi"></mwc-textfield>
<mwc-textfield icon="event" label="Say something..." value="hi"></mwc-textfield>
<mwc-textfield icon="mail" iconTrailing label="Say something..." value="hi"></mwc-textfield>
<mwc-textfield label="Standard"></mwc-textfield>
<mwc-textfield label="Standard" icon="event"></mwc-textfield>
<mwc-textfield label="Standard" iconTrailing="delete"></mwc-textfield>
</div>
<h4>box</h4>
<div class="demo-group-spaced">
<mwc-textfield box></mwc-textfield>
<mwc-textfield box label="Say something..."></mwc-textfield>
<mwc-textfield box label="Say something..." value="hi"></mwc-textfield>
<mwc-textfield box icon="event" label="Say something..." value="hi"></mwc-textfield>
<mwc-textfield box icon="event" iconTrailing label="Say something..." value="hi"></mwc-textfield>
<h4>Shaped Filled</h4>
<div class="demo-group-spaced shaped-filled">
<mwc-textfield label="Standard"></mwc-textfield>
<mwc-textfield label="Standard" icon="event"></mwc-textfield>
<mwc-textfield label="Standard" iconTrailing="delete"></mwc-textfield>
</div>
<h4>box - required</h4>
<h4>Outlined</h4>
<div class="demo-group-spaced">
<mwc-textfield required box></mwc-textfield>
<mwc-textfield required box label="Say something..."></mwc-textfield>
<mwc-textfield required box label="Say something..." value="hi"></mwc-textfield>
<mwc-textfield required box icon="event" label="Say something..." value="hi"></mwc-textfield>
<mwc-textfield required box iconTrailing icon="event" label="Say something..." value="hi"></mwc-textfield>
<mwc-textfield outlined label="Standard"></mwc-textfield>
<mwc-textfield outlined label="Standard" icon="event"></mwc-textfield>
<mwc-textfield outlined label="Standard" iconTrailing="delete"></mwc-textfield>
</div>
<h4>outlined</h4>
<div class="demo-group-spaced">
<mwc-textfield outlined></mwc-textfield>
<mwc-textfield outlined label="Say something..."></mwc-textfield>
<mwc-textfield outlined label="Say something..." value="hi"></mwc-textfield>
<mwc-textfield outlined icon="event" label="Say something..." value="hi"></mwc-textfield>
<mwc-textfield outlined iconTrailing icon="event" label="Say something..." value="hi"></mwc-textfield>
<h4>Shaped Outlined</h4>
<div class="demo-group-spaced shaped-outlined">
<mwc-textfield outlined label="Email" type="email"></mwc-textfield>
<mwc-textfield outlined label="Standard" icon="event"></mwc-textfield>
<mwc-textfield outlined label="Standard" iconTrailing="delete"></mwc-textfield>
</div>
<h4>outlined - required - email - helperText</h4>
<h4>Text Field without label</h4>
<div class="demo-group-spaced">
<mwc-textfield outlined required type="email" helperText="Make sure to include an @"></mwc-textfield>
<mwc-textfield outlined required type="email" label="Enter email..." helperText="Make sure to include an @"></mwc-textfield>
<mwc-textfield outlined required type="email" label="Enter email..." value="hi" helperText="Make sure to include an @"></mwc-textfield>
<mwc-textfield outlined required type="email" icon="event" label="Say something..." value="hi" helperText="Make sure to include an @"></mwc-textfield>
<mwc-textfield outlined iconTrailing required type="email" icon="event" label="Say something..." value="hi" helperText="Make sure to include an @"></mwc-textfield>
<mwc-textfield helper="Helper Text"></mwc-textfield>
<mwc-textfield outlined helper="Helper Text"></mwc-textfield>
<mwc-textfield outlined helper="Helper Text" class="shaped-outlined"></mwc-textfield>
</div>
<h4>box disabled</h4>
<h4>Text Field with Character Counter</h4>
<div class="demo-group-spaced">
<mwc-textfield box disabled></mwc-textfield>
<mwc-textfield box disabled label="Say something..."></mwc-textfield>
<mwc-textfield box disabled label="Say something..." value="hi"></mwc-textfield>
<mwc-textfield box disabled icon="event" label="Say something..." value="hi"></mwc-textfield>
<mwc-textfield box disabled iconTrailing icon="event" label="Say something..." value="hi"></mwc-textfield>
<mwc-textfield label="Standard" helper="Helper Text" helperPersistent maxlength="18" charCounter></mwc-textfield>
<mwc-textfield outlined label="Standard" helper="Helper Text" helperPersistent maxlength="18" charCounter></mwc-textfield>
<mwc-textfield outlined label="Standard" helper="Helper Text" helperPersistent maxlength="18" charCounter class="shaped-outlined"></mwc-textfield>
</div>
<h4>fullWidth</h4>
<h4>Textarea</h4>
<div class="demo-group-spaced">
<mwc-textarea outlined label="Standard"></mwc-textarea>
</div>
<mwc-textfield fullwidth></mwc-textfield>
<mwc-textfield fullwidth placeholder="Say something.." ></mwc-textfield>
<mwc-textfield fullwidth label="Say something..."></mwc-textfield>
<mwc-textfield fullwidth label="Say something..." value="hi"></mwc-textfield>
<h4>Textarea with Character Counter</h4>
<div class="demo-group-spaced">
<mwc-textarea outlined label="Standard" maxlength="18" charCounter></mwc-textarea>
</div>
<h4>Full Width</h4>
<mwc-textfield fullwidth placeholder="Standard" helper="Helper Text" helperpersistent></mwc-textfield>
<h4>Full Width Textarea</h4>
<mwc-textarea outlined fullwidth label="Standard" helper="Helper Text" helperpersistent></mwc-textarea>
</main>
<script>

View File

@ -0,0 +1,23 @@
{
"name": "@material/mwc-textfield",
"version": "0.6.0",
"description": "",
"main": "mwc-textfield.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "Apache-2.0",
"dependencies": {
"@material/floating-label": "^1.0.0",
"@material/line-ripple": "^1.0.0",
"@material/mwc-base": "^0.6.0",
"@material/mwc-icon": "^0.6.0",
"@material/notched-outline": "1.0.0",
"@material/textfield": "^1.0.0"
},
"publishConfig": {
"access": "public"
},
"private": true
}

View File

@ -0,0 +1,61 @@
/**
@license
Copyright 2019 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import {html, property, query, classMap} from '@material/mwc-base/form-element.js';
import {TextFieldBase} from './mwc-textfield-base.js';
export abstract class TextAreaBase extends TextFieldBase {
@query('textarea')
protected formElement!: HTMLInputElement;
@property({type: Number})
rows = 2;
@property({type: Number})
cols = 20;
render() {
const classes = {
'mdc-text-field--disabled': this.disabled,
'mdc-text-field--no-label': !this.label,
'mdc-text-field--outlined': this.outlined,
'mdc-text-field--fullwidth': this.fullWidth,
};
return html`
<div class="mdc-text-field mdc-text-field--textarea ${classMap(classes)}">
${this.charCounter ? html`<div class="mdc-text-field-character-counter"></div>` : ''}
${this.renderInput()}
${this.outlined ? this.renderOutlined() : this.renderLabel()}
</div>
${this.helper ? this.renderHelperText() : ''}
`;
}
protected renderInput() {
return html`
<textarea id="text-field"
class="mdc-text-field__input"
.value="${this.value}"
rows="${this.rows}"
cols="${this.cols}"
?disabled="${this.disabled}"
placeholder="${this.placeholder}"
?required="${this.required}"
maxlength="${this.maxlength}"
@change="${this.handleInputChange}"></textarea>`;
}
}

View File

@ -0,0 +1,31 @@
/**
@license
Copyright 2019 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import {TextAreaBase} from './mwc-textarea-base.js';
import {customElement} from '@material/mwc-base/form-element.js';
import {style} from './mwc-textfield-css.js';
declare global {
interface HTMLElementTagNameMap {
'mwc-textarea': TextArea;
}
}
@customElement('mwc-textarea' as any)
export class TextArea extends TextAreaBase {
static styles = style;
}

View File

@ -0,0 +1,259 @@
/**
@license
Copyright 2019 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import {FormElement, html, query, property, classMap, addHasRemoveClass} from '@material/mwc-base/form-element.js';
import MDCTextFieldFoundation from '@material/textfield/foundation.js';
import {MDCTextFieldAdapter} from '@material/textfield/adapter.js';
import {MDCFloatingLabel} from '@material/floating-label';
import {MDCLineRipple} from '@material/line-ripple';
import {MDCNotchedOutline} from '@material/notched-outline';
import {MDCTextFieldCharacterCounter} from '@material/textfield/character-counter';
export abstract class TextFieldBase extends FormElement {
protected mdcFoundation!: MDCTextFieldFoundation;
protected readonly mdcFoundationClass = MDCTextFieldFoundation;
@query('.mdc-text-field')
protected mdcRoot!: HTMLElement;
@query('input')
protected formElement!: HTMLInputElement;
@query('label')
protected labelElement!: HTMLLabelElement;
@query('.mdc-line-ripple')
protected lineRippleElement!: HTMLElement;
@query('.mdc-notched-outline')
protected outlineLement!: HTMLElement;
@query('.mdc-text-field-character-counter')
protected charCounterElement!: HTMLElement;
@property()
value = '';
@property()
type = 'text';
@property()
placeholder = '';
@property()
label = '';
@property()
icon = '';
@property()
iconTrailing = '';
@property({type: Boolean, reflect: true})
disabled = false;
@property({type: Boolean})
required = false;
@property({type: Number})
maxlength = -1;
@property({type: Boolean, reflect: true})
outlined = false;
@property({type: Boolean, reflect: true})
fullWidth = false;
@property()
helper = '';
@property({type: Boolean})
helperPersistent = false;
@property({type: Boolean})
charCounter = false;
private _floatingLabel!: MDCFloatingLabel | null;
private _lineRipple!: MDCLineRipple | null;
private _outline!: MDCNotchedOutline | null;
private _characterCounter!: MDCTextFieldCharacterCounter | null;
render() {
const classes = {
'mdc-text-field--disabled': this.disabled,
'mdc-text-field--no-label': !this.label,
'mdc-text-field--outlined': this.outlined,
'mdc-text-field--fullwidth': this.fullWidth,
'mdc-text-field--with-leading-icon': this.icon,
'mdc-text-field--with-trailing-icon': this.iconTrailing,
};
return html`
<div class="mdc-text-field ${classMap(classes)}">
${this.icon ? this.renderIcon(this.icon) : ''}
${this.renderInput()}
${this.iconTrailing ? this.renderIcon(this.iconTrailing) : ''}
${this.outlined ? this.renderOutlined() : this.renderLabel()}
</div>
${(this.helper || this.charCounter) ? this.renderHelperText() : ''}
`;
}
protected renderInput() {
return html`
<input id="text-field"
class="mdc-text-field__input"
type="${this.type}"
.value="${this.value}"
?disabled="${this.disabled}"
placeholder="${this.placeholder}"
?required="${this.required}"
maxlength="${this.maxlength}"
@change="${this.handleInputChange}">`;
}
protected renderIcon(icon: String) {
return html`<i class="material-icons mdc-text-field__icon">${icon}</i>`;
}
protected renderOutlined() {
return html`
<div class="mdc-notched-outline">
<div class="mdc-notched-outline__leading"></div>
${this.label ? html`<div class="mdc-notched-outline__notch">
<label for="text-field" class="mdc-floating-label">${this.label}</label>
</div>` : ''}
<div class="mdc-notched-outline__trailing"></div>
</div>`;
}
protected renderLabel() {
return html`
${this.label && !this.fullWidth ? html`<label class="mdc-floating-label" for="text-field">${this.label}</label>` : ''}
<div class="mdc-line-ripple"></div>
`;
}
protected renderHelperText() {
const classes = {
'mdc-text-field-helper-text--persistent': this.helperPersistent,
};
return html`
<div class="mdc-text-field-helper-line">
<div class="mdc-text-field-helper-text ${classMap(classes)}">${this.helper}</div>
${this.charCounter ? html`<div class="mdc-text-field-character-counter"></div>` : ''}
</div>
`;
}
protected handleInputChange() {
this.value = this.formElement.value;
}
protected createFoundation() {
if (this.mdcFoundation !== undefined) {
this.mdcFoundation.destroy();
}
this._characterCounter = this.charCounterElement ? new MDCTextFieldCharacterCounter(this.charCounterElement) : null;
this.mdcFoundation = new this.mdcFoundationClass(this.createAdapter(), {
characterCounter: this._characterCounter ? this._characterCounter.foundation : undefined
});
this.mdcFoundation.init();
}
protected createAdapter(): MDCTextFieldAdapter {
this._floatingLabel = this.labelElement ? new MDCFloatingLabel(this.labelElement) : null;
this._lineRipple = this.lineRippleElement ? new MDCLineRipple(this.lineRippleElement) : null;
this._outline = this.outlineLement ? new MDCNotchedOutline(this.outlineLement) : null;
return {
...addHasRemoveClass(this.mdcRoot),
...this.getRootAdapterMethods(),
...this.getInputAdapterMethods(),
...this.getLabelAdapterMethods(),
...this.getLineRippleAdapterMethods(),
...this.getOutlineAdapterMethods(),
};
}
private getRootAdapterMethods() {
return {
registerTextFieldInteractionHandler: (evtType: string,
handler: any) => this.addEventListener(evtType, handler),
deregisterTextFieldInteractionHandler: (evtType: string,
handler: any) => this.removeEventListener(evtType, handler),
registerValidationAttributeChangeHandler: (handler: any) => {
const getAttributesList = (mutationsList: MutationRecord[]): string[] => {
return mutationsList
.map((mutation) => mutation.attributeName)
.filter((attributeName) => attributeName) as string[];
};
const observer = new MutationObserver((mutationsList) => handler(getAttributesList(mutationsList)));
const config = {attributes: true};
observer.observe(this.formElement, config);
return observer;
},
deregisterValidationAttributeChangeHandler: (observer: MutationObserver) => observer.disconnect(),
};
}
private getInputAdapterMethods() {
return {
getNativeInput: () => this.formElement,
isFocused: () => this.shadowRoot!.activeElement === this.formElement,
registerInputInteractionHandler: (evtType: string,
handler: any) => this.formElement.addEventListener(evtType, handler),
deregisterInputInteractionHandler: (evtType: string,
handler: any) => this.formElement.removeEventListener(evtType, handler),
};
}
private getLabelAdapterMethods() {
return {
floatLabel: (shouldFloat: boolean) => this._floatingLabel && this._floatingLabel.float(shouldFloat),
getLabelWidth: () => this._floatingLabel ? this._floatingLabel.getWidth() : 0,
hasLabel: () => Boolean(this._floatingLabel),
shakeLabel: (shouldShake: boolean) => this._floatingLabel && this._floatingLabel.shake(shouldShake),
};
}
private getLineRippleAdapterMethods() {
return {
activateLineRipple: () => {
if (this._lineRipple) {
this._lineRipple.activate();
}
},
deactivateLineRipple: () => {
if (this._lineRipple) {
this._lineRipple.deactivate();
}
},
setLineRippleTransformOrigin: (normalizedX: number) => {
if (this._lineRipple) {
this._lineRipple.setRippleCenter(normalizedX);
}
},
};
}
private getOutlineAdapterMethods() {
return {
closeOutline: () => this._outline && this._outline.closeNotch(),
hasOutline: () => Boolean(this._outline),
notchOutline: (labelWidth: number) => this._outline && this._outline.notch(labelWidth),
};
}
}

View File

@ -0,0 +1,48 @@
/**
@license
Copyright 2019 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
@import '@material/textfield/mdc-text-field.scss';
@import "@material/mwc-icon/src/_mwc-icon.scss";
.material-icons {
@extend %material-icons;
}
:host {
display: inline-block;
outline: none;
}
:host([fullwidth]) {
display: block;
}
.mdc-text-field {
width: 100%;
border-radius: var(--mwc-textfield-filled-border-radius);
.mdc-notched-outline {
& > .mdc-notched-outline__leading {
width: var(--mwc-textfield-outlined-leading-width, 12px);
border-radius: var(--mwc-textfield-outlined-leading-border-radius, 4px 0px 0px 4px);
}
& > .mdc-notched-outline__trailing {
border-radius: var(--mwc-textfield-outlined-trailing-border-radius, 0px 4px 4px 0);
}
}
}

View File

@ -0,0 +1,31 @@
/**
@license
Copyright 2019 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import {TextFieldBase} from './mwc-textfield-base.js';
import {customElement} from '@material/mwc-base/form-element.js';
import {style} from './mwc-textfield-css.js';
declare global {
interface HTMLElementTagNameMap {
'mwc-textfield': TextField;
}
}
@customElement('mwc-textfield' as any)
export class TextField extends TextFieldBase {
static styles = style;
}

View File

@ -0,0 +1,15 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "."
},
"include": [
"src/*.ts"
],
"references": [
{"path": "../base"},
{"path": "../ripple"},
{"path": "../icon"}
]
}