kopia lustrzana https://github.com/shoelace-style/shoelace
Add label slot to input, select, textarea; closes #248
rodzic
41bbdac940
commit
912b4167d7
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
Ładowanie…
Reference in New Issue