Add label slot to input, select, textarea; closes #248

pull/261/head
Cory LaViska 2020-10-15 11:44:41 -04:00
rodzic 41bbdac940
commit 912b4167d7
4 zmienionych plików z 68 dodań i 8 usunięć

Wyświetl plik

@ -8,6 +8,7 @@ _During the beta period, these restrictions may be relaxed in the event of a mis
## Next
- Added `label` slot to `sl-input`, `sl-select`, and `sl-textarea` [#248](https://github.com/shoelace-style/shoelace/issues/248)
- Fixed a bug where initial transitions didn't show in `sl-dialog` and `sl-drawer` [#247](https://github.com/shoelace-style/shoelace/issues/247)
- Fixed a bug where indeterminate checkboxes would maintain the indeterminate state when toggled
- Improved `sl-color-picker` grid and slider handles [#246](https://github.com/shoelace-style/shoelace/issues/246)

Wyświetl plik

@ -1,4 +1,5 @@
import { Component, Element, Event, EventEmitter, Method, Prop, State, Watch, h } from '@stencil/core';
import { hasSlot } from '../../utilities/slot';
let id = 0;
@ -6,6 +7,7 @@ let id = 0;
* @since 2.0
* @status stable
*
* @slot label - The input's label. Alternatively, you can use the label prop.
* @slot prefix - Used to prepend an icon or similar element to the input.
* @slot suffix - Used to append an icon or similar element to the input.
* @slot clear-icon - An icon to use in lieu of the default clear icon.
@ -38,6 +40,7 @@ export class Input {
@Element() host: HTMLSlInputElement;
@State() hasFocus = false;
@State() hasLabel = false;
@State() isPasswordVisible = false;
/** The input's type. */
@ -118,6 +121,11 @@ export class Input {
/** The input's inputmode attribute. */
@Prop() inputmode: 'none' | 'text' | 'decimal' | 'numeric' | 'tel' | 'search' | 'email' | 'url';
@Watch('label')
handleLabelChange() {
this.detectLabel();
}
@Watch('value')
handleValueChange() {
this.invalid = !this.input.checkValidity();
@ -139,6 +147,7 @@ export class Input {
@Event({ eventName: 'sl-blur' }) slBlur: EventEmitter;
connectedCallback() {
this.detectLabel = this.detectLabel.bind(this);
this.handleChange = this.handleChange.bind(this);
this.handleInput = this.handleInput.bind(this);
this.handleInvalid = this.handleInvalid.bind(this);
@ -148,6 +157,10 @@ export class Input {
this.handlePasswordToggle = this.handlePasswordToggle.bind(this);
}
componentWillLoad() {
this.detectLabel();
}
/** Sets focus on the input. */
@Method()
async setFocus() {
@ -206,6 +219,10 @@ export class Input {
this.invalid = !this.input.checkValidity();
}
detectLabel() {
this.hasLabel = this.label.length > 0 || hasSlot(this.host, 'label');
}
handleChange() {
this.value = this.input.value;
this.slChange.emit();
@ -250,7 +267,7 @@ export class Input {
part="form-control"
class={{
'form-control': true,
'form-control--has-label': this.label.length > 0,
'form-control--has-label': this.hasLabel,
'form-control--invalid': this.invalid
}}
>
@ -265,7 +282,9 @@ export class Input {
}}
htmlFor={this.inputId}
>
{this.label}
<slot name="label" onSlotchange={this.detectLabel}>
{this.label}
</slot>
</label>
<div

Wyświetl plik

@ -1,6 +1,7 @@
import { Component, Element, Event, EventEmitter, Method, Prop, State, Watch, h } from '@stencil/core';
import ResizeObserver from 'resize-observer-polyfill';
import { getTextContent } from '../../utilities/slot';
import { hasSlot } from '../../utilities/slot';
import ResizeObserver from 'resize-observer-polyfill';
let id = 0;
@ -9,6 +10,7 @@ let id = 0;
* @status stable
*
* @slot - The select's options in the form of menu items.
* @slot label - The select's label. Alternatively, you can use the label prop.
* @slot help-text - Help text that describes how to use the select.
*
* @part base - The component's base wrapper.
@ -44,6 +46,7 @@ export class Select {
@Element() host: HTMLSlSelectElement;
@State() hasFocus = false;
@State() hasLabel = false;
@State() isOpen = false;
@State() items = [];
@State() displayLabel = '';
@ -94,6 +97,11 @@ export class Select {
/** This will be true when the control is in an invalid state. Validity is determined by the `required` prop. */
@Prop({ mutable: true }) invalid = false;
@Watch('label')
handleLabelChange() {
this.detectLabel();
}
@Watch('multiple')
handleMultipleChange() {
// Cast to array | string based on `this.multiple`
@ -118,6 +126,7 @@ export class Select {
@Event({ eventName: 'sl-blur' }) slBlur: EventEmitter;
connectedCallback() {
this.detectLabel = this.detectLabel.bind(this);
this.handleBlur = this.handleBlur.bind(this);
this.handleFocus = this.handleFocus.bind(this);
this.handleClear = this.handleClear.bind(this);
@ -134,6 +143,10 @@ export class Select {
this.handleSlotChange = this.handleSlotChange.bind(this);
}
componentWillLoad() {
this.detectLabel();
}
componentDidLoad() {
this.resizeObserver = new ResizeObserver(() => this.resizeMenu());
this.reportDuplicateItemValues();
@ -154,6 +167,10 @@ export class Select {
this.input.setCustomValidity(message);
}
detectLabel() {
this.hasLabel = this.label.length > 0 || hasSlot(this.host, 'label');
}
getItemLabel(item: HTMLSlMenuItemElement) {
const slot = item.shadowRoot.querySelector('slot:not([name])') as HTMLSlotElement;
return getTextContent(slot);
@ -354,7 +371,7 @@ export class Select {
part="form-control"
class={{
'form-control': true,
'form-control--has-label': this.label.length > 0,
'form-control--has-label': this.hasLabel,
'form-control--invalid': this.invalid
}}
>
@ -371,7 +388,9 @@ export class Select {
htmlFor={this.inputId}
onClick={this.handleLabelClick}
>
{this.label}
<slot name="label" onSlotchange={this.detectLabel}>
{this.label}
</slot>
</label>
<sl-dropdown

Wyświetl plik

@ -1,4 +1,5 @@
import { Component, Event, EventEmitter, Method, Prop, State, Watch, h } from '@stencil/core';
import { Component, Element, Event, EventEmitter, Method, Prop, State, Watch, h } from '@stencil/core';
import { hasSlot } from '../../utilities/slot';
import ResizeObserver from 'resize-observer-polyfill';
let id = 0;
@ -7,6 +8,7 @@ let id = 0;
* @since 2.0
* @status stable
*
* @slot label - The textarea's label. Alternatively, you can use the label prop.
* @slot help-text - Help text that describes how to use the input.
*
* @part base - The component's base wrapper.
@ -28,7 +30,10 @@ export class Textarea {
resizeObserver: ResizeObserver;
textarea: HTMLTextAreaElement;
@Element() host: HTMLSlTextareaElement;
@State() hasFocus = false;
@State() hasLabel = false;
/** The textarea's size. */
@Prop({ reflect: true }) size: 'small' | 'medium' | 'large' = 'medium';
@ -102,6 +107,11 @@ export class Textarea {
/** Emitted when the control loses focus. */
@Event({ eventName: 'sl-blur' }) slBlur: EventEmitter;
@Watch('label')
handleLabelChange() {
this.detectLabel();
}
@Watch('rows')
handleRowsChange() {
this.setTextareaHeight();
@ -113,12 +123,17 @@ export class Textarea {
}
connectedCallback() {
this.detectLabel = this.detectLabel.bind(this);
this.handleChange = this.handleChange.bind(this);
this.handleInput = this.handleInput.bind(this);
this.handleBlur = this.handleBlur.bind(this);
this.handleFocus = this.handleFocus.bind(this);
}
componentWillLoad() {
this.detectLabel();
}
componentDidLoad() {
this.setTextareaHeight();
this.resizeObserver = new ResizeObserver(() => this.setTextareaHeight());
@ -188,6 +203,10 @@ export class Textarea {
this.invalid = !this.textarea.checkValidity();
}
detectLabel() {
this.hasLabel = this.label.length > 0 || hasSlot(this.host, 'label');
}
handleChange() {
this.slChange.emit();
}
@ -223,7 +242,7 @@ export class Textarea {
part="form-control"
class={{
'form-control': true,
'form-control--has-label': this.label.length > 0,
'form-control--has-label': this.hasLabel,
'form-control--invalid': this.invalid
}}
>
@ -238,7 +257,9 @@ export class Textarea {
}}
htmlFor={this.textareaId}
>
{this.label}
<slot name="label" onSlotchange={this.detectLabel}>
{this.label}
</slot>
</label>
<div
part="base"