Add labels to textarea and input

This commit is contained in:
Cory LaViska
2020-07-02 07:51:54 -04:00
parent 48aaa624c9
commit e026f33bc0
8 changed files with 245 additions and 105 deletions

View File

@@ -14,6 +14,14 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor i
## Examples
### Labels
```html preview
<sl-input type="text" label="Name"></sl-input>
<br>
<sl-input type="email" label="Email" placeholder="bob@example.com"></sl-input>
```
### Size
```html preview

View File

@@ -14,6 +14,12 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor i
## Examples
### Labels
```html preview
<sl-textarea label="Comments"></sl-textarea>
```
### No Resize
```html preview

20
src/components.d.ts vendored
View File

@@ -184,7 +184,7 @@ export namespace Components {
*/
"show": () => Promise<boolean>;
/**
* The plain-text summary to show in the details header. To show an HTML summary, use the `summary` slot.
* The summary to show in the details header.
*/
"summary": string;
}
@@ -358,6 +358,10 @@ export namespace Components {
* The input's inputmode attribute.
*/
"inputmode": 'none' | 'text' | 'decimal' | 'numeric' | 'tel' | 'search' | 'email' | 'url';
/**
* The input's label.
*/
"label": string;
/**
* The input's max attribute.
*/
@@ -706,6 +710,10 @@ export namespace Components {
* The textarea's inputmode attribute.
*/
"inputmode": 'none' | 'text' | 'decimal' | 'numeric' | 'tel' | 'search' | 'email' | 'url';
/**
* The textarea's label.
*/
"label": string;
/**
* The textarea's maxlength attribute.
*/
@@ -1246,7 +1254,7 @@ declare namespace LocalJSX {
*/
"open"?: boolean;
/**
* The plain-text summary to show in the details header. To show an HTML summary, use the `summary` slot.
* The summary to show in the details header.
*/
"summary"?: string;
}
@@ -1444,6 +1452,10 @@ declare namespace LocalJSX {
* The input's inputmode attribute.
*/
"inputmode"?: 'none' | 'text' | 'decimal' | 'numeric' | 'tel' | 'search' | 'email' | 'url';
/**
* The input's label.
*/
"label"?: string;
/**
* The input's max attribute.
*/
@@ -1816,6 +1828,10 @@ declare namespace LocalJSX {
* The textarea's inputmode attribute.
*/
"inputmode"?: 'none' | 'text' | 'decimal' | 'numeric' | 'tel' | 'search' | 'email' | 'url';
/**
* The textarea's label.
*/
"label"?: string;
/**
* The textarea's maxlength attribute.
*/

View File

@@ -5,6 +5,34 @@
cursor: text;
}
.form-control {
display: flex;
flex-direction: column;
.label {
display: none;
}
}
.form-control--has-label {
.label {
display: inline-block;
margin-bottom: var(--sl-spacing-xx-small);
&.label--small {
font-size: var(--sl-input-font-size-small);
}
&.label--medium {
font-size: var(--sl-input-font-size-medium);
}
&.label--large {
font-size: var(--sl-input-font-size-large);
}
}
}
.input {
flex: 1 1 auto;
display: inline-flex;
@@ -14,8 +42,7 @@
width: 100%;
font-family: var(--sl-input-font-family);
font-weight: var(--sl-input-font-weight);
line-height: var(--sl-line-height-normal);
letter-spacing: var(--sl-letter-spacing-normal);
letter-spacing: var(--sl-input-letter-spacing);
background-color: var(--sl-input-background-color);
border: solid var(--sl-input-border-width) var(--sl-input-border-color);
vertical-align: middle;

View File

@@ -1,5 +1,7 @@
import { Component, Element, Event, EventEmitter, Method, Prop, State, h } from '@stencil/core';
let id = 0;
/**
* @since 1.0
* @status stable
@@ -27,6 +29,8 @@ export class Input {
this.handlePasswordToggle = this.handlePasswordToggle.bind(this);
}
inputId = `input-${++id}`;
labelId = `input-label-${id}`;
input: HTMLInputElement;
@Element() host: HTMLSlInputElement;
@@ -46,6 +50,9 @@ export class Input {
/** The input's value attribute. */
@Prop({ mutable: true }) value: string = '';
/** The input's label. */
@Prop() label = '';
/** The input's placeholder text. */
@Prop() placeholder: string;
@@ -194,77 +201,98 @@ export class Input {
return (
<div
class={{
input: true,
// Sizes
'input--small': this.size === 'small',
'input--medium': this.size === 'medium',
'input--large': this.size === 'large',
// States
'input--disabled': this.disabled,
'input--focused': this.hasFocus,
'input--empty': this.value.length === 0
'form-control': true,
'form-control--has-label': this.label.length > 0
}}
onMouseDown={this.handleMouseDown}
>
<span class="input__prefix">
<slot name="prefix" />
</span>
<label
class={{
label: true,
'label--small': this.size === 'small',
'label--medium': this.size === 'medium',
'label--large': this.size === 'large'
}}
htmlFor={this.inputId}
>
<slot name="label">{this.label}</slot>
</label>
<input
ref={el => (this.input = el)}
class="input__control"
type={this.type === 'password' && this.isPasswordVisible ? 'text' : this.type}
name={this.name}
placeholder={this.placeholder}
disabled={this.disabled}
readonly={this.readonly}
minLength={this.minlength}
maxLength={this.maxlength}
min={this.min}
max={this.max}
step={this.step}
value={this.value}
autoCapitalize={this.autocapitalize}
autoComplete={this.autocomplete}
autoCorrect={this.autocorrect}
autoFocus={this.autofocus}
pattern={this.pattern}
required={this.required}
inputMode={this.inputmode}
onChange={this.handleChange}
onInput={this.handleInput}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
/>
<div
class={{
input: true,
{this.clearable && (
<button class="input__clear" type="button" onClick={this.handleClearClick} tabindex="-1">
<slot name="clear-icon">
<sl-icon name="x-circle" />
</slot>
</button>
)}
// Sizes
'input--small': this.size === 'small',
'input--medium': this.size === 'medium',
'input--large': this.size === 'large',
{this.togglePassword && (
<button class="input__password-toggle" type="button" onClick={this.handlePasswordToggle} tabindex="-1">
{this.isPasswordVisible ? (
<slot name="show-password-icon">
<sl-icon name="eye-slash" />
// States
'input--disabled': this.disabled,
'input--focused': this.hasFocus,
'input--empty': this.value.length === 0
}}
onMouseDown={this.handleMouseDown}
>
<span class="input__prefix">
<slot name="prefix" />
</span>
<input
ref={el => (this.input = el)}
id={this.inputId}
class="input__control"
type={this.type === 'password' && this.isPasswordVisible ? 'text' : this.type}
name={this.name}
placeholder={this.placeholder}
disabled={this.disabled}
readonly={this.readonly}
minLength={this.minlength}
maxLength={this.maxlength}
min={this.min}
max={this.max}
step={this.step}
value={this.value}
autoCapitalize={this.autocapitalize}
autoComplete={this.autocomplete}
autoCorrect={this.autocorrect}
autoFocus={this.autofocus}
pattern={this.pattern}
required={this.required}
inputMode={this.inputmode}
aria-labelledby={this.labelId}
onChange={this.handleChange}
onInput={this.handleInput}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
/>
{this.clearable && (
<button class="input__clear" type="button" onClick={this.handleClearClick} tabindex="-1">
<slot name="clear-icon">
<sl-icon name="x-circle" />
</slot>
) : (
<slot name="hide-password-icon">
{' '}
<sl-icon name="eye" />
</slot>
)}
</button>
)}
</button>
)}
<span class="input__suffix">
<slot name="suffix" />
</span>
{this.togglePassword && (
<button class="input__password-toggle" type="button" onClick={this.handlePasswordToggle} tabindex="-1">
{this.isPasswordVisible ? (
<slot name="show-password-icon">
<sl-icon name="eye-slash" />
</slot>
) : (
<slot name="hide-password-icon">
{' '}
<sl-icon name="eye" />
</slot>
)}
</button>
)}
<span class="input__suffix">
<slot name="suffix" />
</span>
</div>
</div>
);
}

View File

@@ -5,6 +5,34 @@
cursor: text;
}
.form-control {
display: flex;
flex-direction: column;
.label {
display: none;
}
}
.form-control--has-label {
.label {
display: inline-block;
margin-bottom: var(--sl-spacing-xx-small);
&.label--small {
font-size: var(--sl-input-font-size-small);
}
&.label--medium {
font-size: var(--sl-input-font-size-medium);
}
&.label--large {
font-size: var(--sl-input-font-size-large);
}
}
}
.textarea {
display: flex;
align-items: center;
@@ -13,7 +41,7 @@
font-family: var(--sl-input-font-family);
font-weight: var(--sl-input-font-weight);
line-height: var(--sl-line-height-normal);
letter-spacing: var(--sl-letter-spacing-normal);
letter-spacing: var(--sl-input-letter-spacing);
background-color: var(--sl-input-background-color);
border: solid var(--sl-input-border-width) var(--sl-input-border-color);
vertical-align: middle;

View File

@@ -1,6 +1,8 @@
import { Component, Event, EventEmitter, Method, Prop, State, Watch, h } from '@stencil/core';
import ResizeObserver from 'resize-observer-polyfill';
let id = 0;
/**
* @since 1.0
* @status stable
@@ -19,6 +21,8 @@ export class Textarea {
this.handleFocus = this.handleFocus.bind(this);
}
textareaId = `input-${++id}`;
labelId = `input-label-${id}`;
resizeObserver: any;
textarea: HTMLTextAreaElement;
@@ -33,6 +37,9 @@ export class Textarea {
/** The textarea's value attribute. */
@Prop({ mutable: true }) value = '';
/** The textarea's label. */
@Prop() label = '';
/** The textarea's placeholder text. */
@Prop() placeholder: string;
@@ -168,44 +175,63 @@ export class Textarea {
return (
<div
class={{
textarea: true,
// Sizes
'textarea--small': this.size === 'small',
'textarea--medium': this.size === 'medium',
'textarea--large': this.size === 'large',
// States
'textarea--disabled': this.disabled,
'textarea--focused': this.hasFocus,
'textarea--empty': this.value.length === 0,
// Modifiers
'textarea--resize-none': this.resize === 'none',
'textarea--resize-vertical': this.resize === 'vertical',
'textarea--resize-auto': this.resize === 'auto'
'form-control': true,
'form-control--has-label': this.label.length > 0
}}
>
<textarea
ref={el => (this.textarea = el)}
class="textarea__control"
name={this.name}
placeholder={this.placeholder}
disabled={this.disabled}
readOnly={this.readonly}
rows={this.rows}
maxLength={this.maxlength}
value={this.value}
autoCapitalize={this.autocapitalize}
autoCorrect={this.autocorrect}
autoFocus={this.autofocus}
required={this.required}
inputMode={this.inputmode}
onChange={this.handleChange}
onInput={this.handleInput}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
/>
<label
class={{
label: true,
'label--small': this.size === 'small',
'label--medium': this.size === 'medium',
'label--large': this.size === 'large'
}}
htmlFor={this.textareaId}
>
<slot name="label">{this.label}</slot>
</label>
<div
class={{
textarea: true,
// Sizes
'textarea--small': this.size === 'small',
'textarea--medium': this.size === 'medium',
'textarea--large': this.size === 'large',
// States
'textarea--disabled': this.disabled,
'textarea--focused': this.hasFocus,
'textarea--empty': this.value.length === 0,
// Modifiers
'textarea--resize-none': this.resize === 'none',
'textarea--resize-vertical': this.resize === 'vertical',
'textarea--resize-auto': this.resize === 'auto'
}}
>
<textarea
ref={el => (this.textarea = el)}
id={this.textareaId}
class="textarea__control"
name={this.name}
placeholder={this.placeholder}
disabled={this.disabled}
readOnly={this.readonly}
rows={this.rows}
maxLength={this.maxlength}
value={this.value}
autoCapitalize={this.autocapitalize}
autoCorrect={this.autocorrect}
autoFocus={this.autofocus}
required={this.required}
inputMode={this.inputmode}
onChange={this.handleChange}
onInput={this.handleInput}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
/>
</div>
</div>
);
}

View File

@@ -176,6 +176,7 @@
--sl-input-font-size-small: var(--sl-font-size-x-small);
--sl-input-font-size-medium: var(--sl-font-size-small);
--sl-input-font-size-large: var(--sl-font-size-medium);
--sl-input-letter-spacing: var(--sl-letter-spacing-normal);
--sl-input-color: var(--sl-color-gray-30);
--sl-input-color-hover: var(--sl-color-gray-30);