shoelace/src/components/option/option.ts

114 wiersze
3.8 KiB
TypeScript
Czysty Zwykły widok Historia

2022-12-17 16:27:30 +00:00
import { html } from 'lit';
2022-12-28 16:42:08 +00:00
import { customElement, property, query, state } from 'lit/decorators.js';
2022-12-19 22:46:31 +00:00
import { classMap } from 'lit/directives/class-map.js';
2022-12-17 16:27:30 +00:00
import ShoelaceElement from '../../internal/shoelace-element';
2022-12-20 17:13:39 +00:00
import { getTextContent } from '../../internal/slot';
2022-12-19 22:46:31 +00:00
import { watch } from '../../internal/watch';
2022-12-17 16:27:30 +00:00
import { LocalizeController } from '../../utilities/localize';
2022-12-19 22:46:31 +00:00
import '../icon/icon';
2022-12-17 16:27:30 +00:00
import styles from './option.styles';
import type { CSSResultGroup } from 'lit';
/**
2022-12-20 18:36:53 +00:00
* @summary Options define the selectable items within various form controls such as [select](/components/select).
2022-12-17 16:27:30 +00:00
*
* @since 2.0
2022-12-20 18:36:53 +00:00
* @status stable
2022-12-17 16:27:30 +00:00
*
* @dependency sl-icon
*
2022-12-20 17:13:39 +00:00
* @event sl-label-change - Emitted when the option's label changes. For performance reasons, this event is only emitted
2022-12-20 18:36:53 +00:00
* when the default slot's `slotchange` event is triggered. It will not fire when the label is first set. Useful for
* parent controls that want to observe label changes without attaching an expensive mutation observer.
2022-12-17 16:27:30 +00:00
*
2022-12-20 18:36:53 +00:00
* @slot - The option's label.
* @slot prefix - Used to prepend an icon or similar element to the menu item.
* @slot suffix - Used to append an icon or similar element to the menu item.
2022-12-17 16:27:30 +00:00
*
2022-12-20 18:36:53 +00:00
* @csspart checked-icon - The checked icon, an `<sl-icon>` element.
2022-12-17 16:27:30 +00:00
* @csspart base - The component's base wrapper.
2022-12-20 18:36:53 +00:00
* @csspart label - The option's label.
* @csspart prefix - The container that wraps the prefix.
* @csspart suffix - The container that wraps the suffix.
2022-12-17 16:27:30 +00:00
*/
@customElement('sl-option')
export default class SlOption extends ShoelaceElement {
static styles: CSSResultGroup = styles;
2022-12-20 17:13:39 +00:00
private cachedTextLabel: string;
2022-12-20 18:36:53 +00:00
// @ts-expect-error -- Controller is currently unused
2022-12-17 16:27:30 +00:00
private readonly localize = new LocalizeController(this);
2022-12-28 16:42:08 +00:00
@state() current = false; // the user has keyed into the option, but hasn't selected it yet (shows a highlight)
@state() selected = false; // the option is selected and has aria-selected="true"
2022-12-20 17:13:39 +00:00
@query('.option__label') defaultSlot: HTMLSlotElement;
2022-12-17 16:27:30 +00:00
/** The option's value. When selected, the containing form control will receive this value. */
@property() value = '';
/** Draws the option in a disabled state, preventing selection. */
@property({ type: Boolean, reflect: true }) disabled = false;
connectedCallback() {
super.connectedCallback();
this.setAttribute('role', 'option');
this.setAttribute('aria-selected', 'false');
}
2022-12-28 16:42:08 +00:00
/** Returns a plain text label based on the option's content. */
getTextLabel() {
return this.textContent ?? '';
}
2022-12-19 22:46:31 +00:00
@watch('disabled')
handleDisabledChange() {
this.setAttribute('aria-disabled', this.disabled ? 'true' : 'false');
}
@watch('selected')
handleSelectedChange() {
this.setAttribute('aria-selected', this.selected ? 'true' : 'false');
}
2022-12-20 17:13:39 +00:00
handleDefaultSlotChange() {
const textLabel = getTextContent(this.defaultSlot);
// Ignore the first time the label is set
if (typeof this.cachedTextLabel === 'undefined') {
this.cachedTextLabel = textLabel;
return;
}
if (textLabel !== this.cachedTextLabel) {
this.cachedTextLabel = textLabel;
this.emit('sl-label-change');
}
}
2022-12-17 16:27:30 +00:00
render() {
return html`
2022-12-19 22:46:31 +00:00
<div
2022-12-20 18:36:53 +00:00
part="base"
2022-12-19 22:46:31 +00:00
class=${classMap({
option: true,
'option--current': this.current,
2022-12-20 18:36:53 +00:00
'option--disabled': this.disabled,
2022-12-19 22:46:31 +00:00
'option--selected': this.selected
})}
>
2022-12-20 18:36:53 +00:00
<sl-icon part="checked-icon" class="option__check" name="check" library="system" aria-hidden="true"></sl-icon>
<slot part="prefix" name="prefix" class="option__prefix"></slot>
<slot part="label" class="option__label" @slotchange=${this.handleDefaultSlotChange}></slot>
<slot part="suffix" name="suffix" class="option__suffix"></slot>
2022-12-17 16:27:30 +00:00
</div>
`;
}
}
declare global {
interface HTMLElementTagNameMap {
'sl-option': SlOption;
}
}