kopia lustrzana https://github.com/shoelace-style/shoelace
use reactive controller for slot detection
rodzic
1e3bac6031
commit
46f05224ab
|
@ -19,6 +19,7 @@ _During the beta period, these restrictions may be relaxed in the event of a mis
|
||||||
- Improved `<sl-spinner>` track color when used on various backgrounds
|
- Improved `<sl-spinner>` track color when used on various backgrounds
|
||||||
- Improved a11y in `<sl-radio>` so VoiceOver announces radios properly in a radio group
|
- Improved a11y in `<sl-radio>` so VoiceOver announces radios properly in a radio group
|
||||||
- Improved the API for the experimental `<sl-split-panel>` component by making `position` accept a percentage and adding the `position-in-pixels` attribute
|
- Improved the API for the experimental `<sl-split-panel>` component by making `position` accept a percentage and adding the `position-in-pixels` attribute
|
||||||
|
- Refactored `<sl-breadcrumb-item>`, `<sl-button>`, `<sl-card>`, `<sl-dialog>`, `<sl-drawer>`, `<sl-input>`, `<sl-range>`, `<sl-select>`, and `<sl-textarea>` to use a Reactive Controller for slot detection
|
||||||
- Refactored internal id usage in `<sl-details>`, `<sl-dialog>`, `<sl-drawer>`, and `<sl-dropdown>`
|
- Refactored internal id usage in `<sl-details>`, `<sl-dialog>`, `<sl-drawer>`, and `<sl-dropdown>`
|
||||||
- Removed `position: relative` from the common component stylesheet
|
- Removed `position: relative` from the common component stylesheet
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { LitElement, html } from 'lit';
|
import { LitElement, html } from 'lit';
|
||||||
import { customElement, property, state } from 'lit/decorators.js';
|
import { customElement, property } from 'lit/decorators.js';
|
||||||
import { classMap } from 'lit/directives/class-map.js';
|
import { classMap } from 'lit/directives/class-map.js';
|
||||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||||
import { hasSlot } from '../../internal/slot';
|
import { HasSlotController } from '../../internal/slot';
|
||||||
import styles from './breadcrumb-item.styles';
|
import styles from './breadcrumb-item.styles';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,8 +25,7 @@ import styles from './breadcrumb-item.styles';
|
||||||
export default class SlBreadcrumbItem extends LitElement {
|
export default class SlBreadcrumbItem extends LitElement {
|
||||||
static styles = styles;
|
static styles = styles;
|
||||||
|
|
||||||
@state() hasPrefix = false;
|
private hasSlotController = new HasSlotController(this, ['prefix', 'suffix']);
|
||||||
@state() hasSuffix = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional URL to direct the user to when the breadcrumb item is activated. When set, a link will be rendered
|
* Optional URL to direct the user to when the breadcrumb item is activated. When set, a link will be rendered
|
||||||
|
@ -40,11 +39,6 @@ export default class SlBreadcrumbItem extends LitElement {
|
||||||
/** The `rel` attribute to use on the link. Only used when `href` is set. */
|
/** The `rel` attribute to use on the link. Only used when `href` is set. */
|
||||||
@property() rel: string = 'noreferrer noopener';
|
@property() rel: string = 'noreferrer noopener';
|
||||||
|
|
||||||
handleSlotChange() {
|
|
||||||
this.hasPrefix = hasSlot(this, 'prefix');
|
|
||||||
this.hasSuffix = hasSlot(this, 'suffix');
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const isLink = this.href ? true : false;
|
const isLink = this.href ? true : false;
|
||||||
|
|
||||||
|
@ -53,12 +47,12 @@ export default class SlBreadcrumbItem extends LitElement {
|
||||||
part="base"
|
part="base"
|
||||||
class=${classMap({
|
class=${classMap({
|
||||||
'breadcrumb-item': true,
|
'breadcrumb-item': true,
|
||||||
'breadcrumb-item--has-prefix': this.hasPrefix,
|
'breadcrumb-item--has-prefix': this.hasSlotController.test('prefix'),
|
||||||
'breadcrumb-item--has-suffix': this.hasSuffix
|
'breadcrumb-item--has-suffix': this.hasSlotController.test('suffix')
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<span part="prefix" class="breadcrumb-item__prefix">
|
<span part="prefix" class="breadcrumb-item__prefix">
|
||||||
<slot name="prefix" @slotchange=${this.handleSlotChange}></slot>
|
<slot name="prefix"></slot>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
${isLink
|
${isLink
|
||||||
|
@ -80,7 +74,7 @@ export default class SlBreadcrumbItem extends LitElement {
|
||||||
`}
|
`}
|
||||||
|
|
||||||
<span part="suffix" class="breadcrumb-item__suffix">
|
<span part="suffix" class="breadcrumb-item__suffix">
|
||||||
<slot name="suffix" @slotchange=${this.handleSlotChange}></slot>
|
<slot name="suffix"></slot>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span part="separator" class="breadcrumb-item__separator" aria-hidden="true">
|
<span part="separator" class="breadcrumb-item__separator" aria-hidden="true">
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { html, literal } from 'lit/static-html.js';
|
||||||
import { classMap } from 'lit/directives/class-map.js';
|
import { classMap } from 'lit/directives/class-map.js';
|
||||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||||
import { emit } from '../../internal/event';
|
import { emit } from '../../internal/event';
|
||||||
import { hasSlot } from '../../internal/slot';
|
import { HasSlotController } from '../../internal/slot';
|
||||||
import styles from './button.styles';
|
import styles from './button.styles';
|
||||||
|
|
||||||
import '../spinner/spinner';
|
import '../spinner/spinner';
|
||||||
|
@ -34,10 +34,9 @@ export default class SlButton extends LitElement {
|
||||||
|
|
||||||
@query('.button') button: HTMLButtonElement | HTMLLinkElement;
|
@query('.button') button: HTMLButtonElement | HTMLLinkElement;
|
||||||
|
|
||||||
|
private hasSlotController = new HasSlotController(this, ['[default]', 'prefix', 'suffix']);
|
||||||
|
|
||||||
@state() private hasFocus = false;
|
@state() private hasFocus = false;
|
||||||
@state() private hasLabel = false;
|
|
||||||
@state() private hasPrefix = false;
|
|
||||||
@state() private hasSuffix = false;
|
|
||||||
|
|
||||||
/** The button's variant. */
|
/** The button's variant. */
|
||||||
@property({ reflect: true }) variant: 'default' | 'primary' | 'success' | 'neutral' | 'warning' | 'danger' | 'text' =
|
@property({ reflect: true }) variant: 'default' | 'primary' | 'success' | 'neutral' | 'warning' | 'danger' | 'text' =
|
||||||
|
@ -82,11 +81,6 @@ export default class SlButton extends LitElement {
|
||||||
/** Tells the browser to download the linked file as this filename. Only used when `href` is set. */
|
/** Tells the browser to download the linked file as this filename. Only used when `href` is set. */
|
||||||
@property() download: string;
|
@property() download: string;
|
||||||
|
|
||||||
connectedCallback() {
|
|
||||||
super.connectedCallback();
|
|
||||||
this.handleSlotChange();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Simulates a click on the button. */
|
/** Simulates a click on the button. */
|
||||||
click() {
|
click() {
|
||||||
this.button.click();
|
this.button.click();
|
||||||
|
@ -102,12 +96,6 @@ export default class SlButton extends LitElement {
|
||||||
this.button.blur();
|
this.button.blur();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSlotChange() {
|
|
||||||
this.hasLabel = hasSlot(this);
|
|
||||||
this.hasPrefix = hasSlot(this, 'prefix');
|
|
||||||
this.hasSuffix = hasSlot(this, 'suffix');
|
|
||||||
}
|
|
||||||
|
|
||||||
handleBlur() {
|
handleBlur() {
|
||||||
this.hasFocus = false;
|
this.hasFocus = false;
|
||||||
emit(this, 'sl-blur');
|
emit(this, 'sl-blur');
|
||||||
|
@ -152,9 +140,9 @@ export default class SlButton extends LitElement {
|
||||||
'button--standard': !this.outline,
|
'button--standard': !this.outline,
|
||||||
'button--outline': this.outline,
|
'button--outline': this.outline,
|
||||||
'button--pill': this.pill,
|
'button--pill': this.pill,
|
||||||
'button--has-label': this.hasLabel,
|
'button--has-label': this.hasSlotController.test('[default]'),
|
||||||
'button--has-prefix': this.hasPrefix,
|
'button--has-prefix': this.hasSlotController.test('prefix'),
|
||||||
'button--has-suffix': this.hasSuffix
|
'button--has-suffix': this.hasSlotController.test('suffix')
|
||||||
})}
|
})}
|
||||||
?disabled=${ifDefined(isLink ? undefined : this.disabled)}
|
?disabled=${ifDefined(isLink ? undefined : this.disabled)}
|
||||||
type=${ifDefined(isLink ? undefined : this.submit ? 'submit' : 'button')}
|
type=${ifDefined(isLink ? undefined : this.submit ? 'submit' : 'button')}
|
||||||
|
@ -172,13 +160,13 @@ export default class SlButton extends LitElement {
|
||||||
@click=${this.handleClick}
|
@click=${this.handleClick}
|
||||||
>
|
>
|
||||||
<span part="prefix" class="button__prefix">
|
<span part="prefix" class="button__prefix">
|
||||||
<slot @slotchange=${this.handleSlotChange} name="prefix"></slot>
|
<slot name="prefix"></slot>
|
||||||
</span>
|
</span>
|
||||||
<span part="label" class="button__label">
|
<span part="label" class="button__label">
|
||||||
<slot @slotchange=${this.handleSlotChange}></slot>
|
<slot></slot>
|
||||||
</span>
|
</span>
|
||||||
<span part="suffix" class="button__suffix">
|
<span part="suffix" class="button__suffix">
|
||||||
<slot @slotchange=${this.handleSlotChange} name="suffix"></slot>
|
<slot name="suffix"></slot>
|
||||||
</span>
|
</span>
|
||||||
${
|
${
|
||||||
this.caret
|
this.caret
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { LitElement, html } from 'lit';
|
import { LitElement, html } from 'lit';
|
||||||
import { customElement, state } from 'lit/decorators.js';
|
import { customElement } from 'lit/decorators.js';
|
||||||
import { classMap } from 'lit/directives/class-map.js';
|
import { classMap } from 'lit/directives/class-map.js';
|
||||||
import { hasSlot } from '../../internal/slot';
|
import { HasSlotController } from '../../internal/slot';
|
||||||
import styles from './card.styles';
|
import styles from './card.styles';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -28,15 +28,7 @@ import styles from './card.styles';
|
||||||
export default class SlCard extends LitElement {
|
export default class SlCard extends LitElement {
|
||||||
static styles = styles;
|
static styles = styles;
|
||||||
|
|
||||||
@state() private hasFooter = false;
|
private hasSlotController = new HasSlotController(this, ['footer', 'header', 'image']);
|
||||||
@state() private hasImage = false;
|
|
||||||
@state() private hasHeader = false;
|
|
||||||
|
|
||||||
handleSlotChange() {
|
|
||||||
this.hasFooter = hasSlot(this, 'footer');
|
|
||||||
this.hasImage = hasSlot(this, 'image');
|
|
||||||
this.hasHeader = hasSlot(this, 'header');
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
|
@ -44,17 +36,17 @@ export default class SlCard extends LitElement {
|
||||||
part="base"
|
part="base"
|
||||||
class=${classMap({
|
class=${classMap({
|
||||||
card: true,
|
card: true,
|
||||||
'card--has-footer': this.hasFooter,
|
'card--has-footer': this.hasSlotController.test('footer'),
|
||||||
'card--has-image': this.hasImage,
|
'card--has-image': this.hasSlotController.test('image'),
|
||||||
'card--has-header': this.hasHeader
|
'card--has-header': this.hasSlotController.test('header')
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<div part="image" class="card__image">
|
<div part="image" class="card__image">
|
||||||
<slot name="image" @slotchange=${this.handleSlotChange}></slot>
|
<slot name="image"></slot>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div part="header" class="card__header">
|
<div part="header" class="card__header">
|
||||||
<slot name="header" @slotchange=${this.handleSlotChange}></slot>
|
<slot name="header"></slot>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div part="body" class="card__body">
|
<div part="body" class="card__body">
|
||||||
|
@ -62,7 +54,7 @@ export default class SlCard extends LitElement {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div part="footer" class="card__footer">
|
<div part="footer" class="card__footer">
|
||||||
<slot name="footer" @slotchange=${this.handleSlotChange}></slot>
|
<slot name="footer"></slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { LitElement, html } from 'lit';
|
import { LitElement, html } from 'lit';
|
||||||
import { customElement, property, query, state } from 'lit/decorators.js';
|
import { customElement, property, query } from 'lit/decorators.js';
|
||||||
import { classMap } from 'lit/directives/class-map.js';
|
import { classMap } from 'lit/directives/class-map.js';
|
||||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||||
import { animateTo, stopAnimations } from '../../internal/animate';
|
import { animateTo, stopAnimations } from '../../internal/animate';
|
||||||
|
@ -7,7 +7,7 @@ import { emit } from '../../internal/event';
|
||||||
import { watch } from '../../internal/watch';
|
import { watch } from '../../internal/watch';
|
||||||
import { waitForEvent } from '../../internal/event';
|
import { waitForEvent } from '../../internal/event';
|
||||||
import { lockBodyScrolling, unlockBodyScrolling } from '../../internal/scroll';
|
import { lockBodyScrolling, unlockBodyScrolling } from '../../internal/scroll';
|
||||||
import { hasSlot } from '../../internal/slot';
|
import { HasSlotController } from '../../internal/slot';
|
||||||
import { isPreventScrollSupported } from '../../internal/support';
|
import { isPreventScrollSupported } from '../../internal/support';
|
||||||
import Modal from '../../internal/modal';
|
import Modal from '../../internal/modal';
|
||||||
import { setDefaultAnimation, getAnimation } from '../../utilities/animation-registry';
|
import { setDefaultAnimation, getAnimation } from '../../utilities/animation-registry';
|
||||||
|
@ -65,11 +65,10 @@ export default class SlDialog extends LitElement {
|
||||||
@query('.dialog__panel') panel: HTMLElement;
|
@query('.dialog__panel') panel: HTMLElement;
|
||||||
@query('.dialog__overlay') overlay: HTMLElement;
|
@query('.dialog__overlay') overlay: HTMLElement;
|
||||||
|
|
||||||
|
private hasSlotController = new HasSlotController(this, ['footer']);
|
||||||
private modal: Modal;
|
private modal: Modal;
|
||||||
private originalTrigger: HTMLElement | null;
|
private originalTrigger: HTMLElement | null;
|
||||||
|
|
||||||
@state() private hasFooter = false;
|
|
||||||
|
|
||||||
/** Indicates whether or not the dialog is open. You can use this in lieu of the show/hide methods. */
|
/** Indicates whether or not the dialog is open. You can use this in lieu of the show/hide methods. */
|
||||||
@property({ type: Boolean, reflect: true }) open = false;
|
@property({ type: Boolean, reflect: true }) open = false;
|
||||||
|
|
||||||
|
@ -87,9 +86,7 @@ export default class SlDialog extends LitElement {
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
|
|
||||||
this.modal = new Modal(this);
|
this.modal = new Modal(this);
|
||||||
this.handleSlotChange();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
firstUpdated() {
|
firstUpdated() {
|
||||||
|
@ -208,10 +205,6 @@ export default class SlDialog extends LitElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSlotChange() {
|
|
||||||
this.hasFooter = hasSlot(this, 'footer');
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
<div
|
<div
|
||||||
|
@ -219,7 +212,7 @@ export default class SlDialog extends LitElement {
|
||||||
class=${classMap({
|
class=${classMap({
|
||||||
dialog: true,
|
dialog: true,
|
||||||
'dialog--open': this.open,
|
'dialog--open': this.open,
|
||||||
'dialog--has-footer': this.hasFooter
|
'dialog--has-footer': this.hasSlotController.test('footer')
|
||||||
})}
|
})}
|
||||||
@keydown=${this.handleKeyDown}
|
@keydown=${this.handleKeyDown}
|
||||||
>
|
>
|
||||||
|
@ -257,7 +250,7 @@ export default class SlDialog extends LitElement {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer part="footer" class="dialog__footer">
|
<footer part="footer" class="dialog__footer">
|
||||||
<slot name="footer" @slotchange=${this.handleSlotChange}></slot>
|
<slot name="footer"></slot>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { LitElement, html } from 'lit';
|
import { LitElement, html } from 'lit';
|
||||||
import { customElement, property, query, state } from 'lit/decorators.js';
|
import { customElement, property, query } from 'lit/decorators.js';
|
||||||
import { classMap } from 'lit/directives/class-map.js';
|
import { classMap } from 'lit/directives/class-map.js';
|
||||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||||
import { animateTo, stopAnimations } from '../../internal/animate';
|
import { animateTo, stopAnimations } from '../../internal/animate';
|
||||||
|
@ -7,7 +7,7 @@ import { emit } from '../../internal/event';
|
||||||
import { watch } from '../../internal/watch';
|
import { watch } from '../../internal/watch';
|
||||||
import { waitForEvent } from '../../internal/event';
|
import { waitForEvent } from '../../internal/event';
|
||||||
import { lockBodyScrolling, unlockBodyScrolling } from '../../internal/scroll';
|
import { lockBodyScrolling, unlockBodyScrolling } from '../../internal/scroll';
|
||||||
import { hasSlot } from '../../internal/slot';
|
import { HasSlotController } from '../../internal/slot';
|
||||||
import { uppercaseFirstLetter } from '../../internal/string';
|
import { uppercaseFirstLetter } from '../../internal/string';
|
||||||
import { isPreventScrollSupported } from '../../internal/support';
|
import { isPreventScrollSupported } from '../../internal/support';
|
||||||
import Modal from '../../internal/modal';
|
import Modal from '../../internal/modal';
|
||||||
|
@ -73,11 +73,10 @@ export default class SlDrawer extends LitElement {
|
||||||
@query('.drawer__panel') panel: HTMLElement;
|
@query('.drawer__panel') panel: HTMLElement;
|
||||||
@query('.drawer__overlay') overlay: HTMLElement;
|
@query('.drawer__overlay') overlay: HTMLElement;
|
||||||
|
|
||||||
|
private hasSlotController = new HasSlotController(this, ['footer']);
|
||||||
private modal: Modal;
|
private modal: Modal;
|
||||||
private originalTrigger: HTMLElement | null;
|
private originalTrigger: HTMLElement | null;
|
||||||
|
|
||||||
@state() private hasFooter = false;
|
|
||||||
|
|
||||||
/** Indicates whether or not the drawer is open. You can use this in lieu of the show/hide methods. */
|
/** Indicates whether or not the drawer is open. You can use this in lieu of the show/hide methods. */
|
||||||
@property({ type: Boolean, reflect: true }) open = false;
|
@property({ type: Boolean, reflect: true }) open = false;
|
||||||
|
|
||||||
|
@ -104,9 +103,7 @@ export default class SlDrawer extends LitElement {
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
|
|
||||||
this.modal = new Modal(this);
|
this.modal = new Modal(this);
|
||||||
this.handleSlotChange();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
firstUpdated() {
|
firstUpdated() {
|
||||||
|
@ -228,10 +225,6 @@ export default class SlDrawer extends LitElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSlotChange() {
|
|
||||||
this.hasFooter = hasSlot(this, 'footer');
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
<div
|
<div
|
||||||
|
@ -245,7 +238,7 @@ export default class SlDrawer extends LitElement {
|
||||||
'drawer--start': this.placement === 'start',
|
'drawer--start': this.placement === 'start',
|
||||||
'drawer--contained': this.contained,
|
'drawer--contained': this.contained,
|
||||||
'drawer--fixed': !this.contained,
|
'drawer--fixed': !this.contained,
|
||||||
'drawer--has-footer': this.hasFooter
|
'drawer--has-footer': this.hasSlotController.test('footer')
|
||||||
})}
|
})}
|
||||||
@keydown=${this.handleKeyDown}
|
@keydown=${this.handleKeyDown}
|
||||||
>
|
>
|
||||||
|
@ -284,7 +277,7 @@ export default class SlDrawer extends LitElement {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer part="footer" class="drawer__footer">
|
<footer part="footer" class="drawer__footer">
|
||||||
<slot name="footer" @slotchange=${this.handleSlotChange}></slot>
|
<slot name="footer"></slot>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { live } from 'lit/directives/live.js';
|
||||||
import { emit } from '../../internal/event';
|
import { emit } from '../../internal/event';
|
||||||
import { watch } from '../../internal/watch';
|
import { watch } from '../../internal/watch';
|
||||||
import { getLabelledBy, renderFormControl } from '../../internal/form-control';
|
import { getLabelledBy, renderFormControl } from '../../internal/form-control';
|
||||||
import { hasSlot } from '../../internal/slot';
|
import { HasSlotController } from '../../internal/slot';
|
||||||
import styles from './input.styles';
|
import styles from './input.styles';
|
||||||
|
|
||||||
import '../icon/icon';
|
import '../icon/icon';
|
||||||
|
@ -49,13 +49,12 @@ export default class SlInput extends LitElement {
|
||||||
|
|
||||||
@query('.input__control') input: HTMLInputElement;
|
@query('.input__control') input: HTMLInputElement;
|
||||||
|
|
||||||
|
private hasSlotController = new HasSlotController(this, ['help-text', 'label']);
|
||||||
private inputId = `input-${++id}`;
|
private inputId = `input-${++id}`;
|
||||||
private helpTextId = `input-help-text-${id}`;
|
private helpTextId = `input-help-text-${id}`;
|
||||||
private labelId = `input-label-${id}`;
|
private labelId = `input-label-${id}`;
|
||||||
|
|
||||||
@state() private hasFocus = false;
|
@state() private hasFocus = false;
|
||||||
@state() private hasHelpTextSlot = false;
|
|
||||||
@state() private hasLabelSlot = false;
|
|
||||||
@state() private isPasswordVisible = false;
|
@state() private isPasswordVisible = false;
|
||||||
|
|
||||||
/** The input's type. */
|
/** The input's type. */
|
||||||
|
@ -163,21 +162,10 @@ export default class SlInput extends LitElement {
|
||||||
this.value = this.input.value;
|
this.value = this.input.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
|
||||||
super.connectedCallback();
|
|
||||||
this.handleSlotChange = this.handleSlotChange.bind(this);
|
|
||||||
this.shadowRoot!.addEventListener('slotchange', this.handleSlotChange);
|
|
||||||
}
|
|
||||||
|
|
||||||
firstUpdated() {
|
firstUpdated() {
|
||||||
this.invalid = !this.input.checkValidity();
|
this.invalid = !this.input.checkValidity();
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback() {
|
|
||||||
super.disconnectedCallback();
|
|
||||||
this.shadowRoot!.removeEventListener('slotchange', this.handleSlotChange);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Sets focus on the input. */
|
/** Sets focus on the input. */
|
||||||
focus(options?: FocusOptions) {
|
focus(options?: FocusOptions) {
|
||||||
this.input.focus(options);
|
this.input.focus(options);
|
||||||
|
@ -276,13 +264,6 @@ export default class SlInput extends LitElement {
|
||||||
this.isPasswordVisible = !this.isPasswordVisible;
|
this.isPasswordVisible = !this.isPasswordVisible;
|
||||||
}
|
}
|
||||||
|
|
||||||
@watch('helpText')
|
|
||||||
@watch('label')
|
|
||||||
handleSlotChange() {
|
|
||||||
this.hasHelpTextSlot = hasSlot(this, 'help-text');
|
|
||||||
this.hasLabelSlot = hasSlot(this, 'label');
|
|
||||||
}
|
|
||||||
|
|
||||||
@watch('value')
|
@watch('value')
|
||||||
handleValueChange() {
|
handleValueChange() {
|
||||||
if (this.input) {
|
if (this.input) {
|
||||||
|
@ -291,16 +272,19 @@ export default class SlInput extends LitElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const hasLabelSlot = this.hasSlotController.test('label');
|
||||||
|
const hasHelpTextSlot = this.hasSlotController.test('help-text');
|
||||||
|
|
||||||
// NOTE - always bind value after min/max, otherwise it will be clamped
|
// NOTE - always bind value after min/max, otherwise it will be clamped
|
||||||
return renderFormControl(
|
return renderFormControl(
|
||||||
{
|
{
|
||||||
inputId: this.inputId,
|
inputId: this.inputId,
|
||||||
label: this.label,
|
label: this.label,
|
||||||
labelId: this.labelId,
|
labelId: this.labelId,
|
||||||
hasLabelSlot: this.hasLabelSlot,
|
hasLabelSlot,
|
||||||
helpTextId: this.helpTextId,
|
helpTextId: this.helpTextId,
|
||||||
helpText: this.helpText,
|
helpText: this.helpText,
|
||||||
hasHelpTextSlot: this.hasHelpTextSlot,
|
hasHelpTextSlot,
|
||||||
size: this.size
|
size: this.size
|
||||||
},
|
},
|
||||||
html`
|
html`
|
||||||
|
@ -355,10 +339,10 @@ export default class SlInput extends LitElement {
|
||||||
getLabelledBy({
|
getLabelledBy({
|
||||||
label: this.label,
|
label: this.label,
|
||||||
labelId: this.labelId,
|
labelId: this.labelId,
|
||||||
hasLabelSlot: this.hasLabelSlot,
|
hasLabelSlot,
|
||||||
helpText: this.helpText,
|
helpText: this.helpText,
|
||||||
helpTextId: this.helpTextId,
|
helpTextId: this.helpTextId,
|
||||||
hasHelpTextSlot: this.hasHelpTextSlot
|
hasHelpTextSlot
|
||||||
})
|
})
|
||||||
)}
|
)}
|
||||||
aria-invalid=${this.invalid ? 'true' : 'false'}
|
aria-invalid=${this.invalid ? 'true' : 'false'}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { emit } from '../../internal/event';
|
||||||
import { live } from 'lit/directives/live.js';
|
import { live } from 'lit/directives/live.js';
|
||||||
import { watch } from '../../internal/watch';
|
import { watch } from '../../internal/watch';
|
||||||
import { getLabelledBy, renderFormControl } from '../../internal/form-control';
|
import { getLabelledBy, renderFormControl } from '../../internal/form-control';
|
||||||
import { hasSlot } from '../../internal/slot';
|
import { HasSlotController } from '../../internal/slot';
|
||||||
import styles from './range.styles';
|
import styles from './range.styles';
|
||||||
|
|
||||||
let id = 0;
|
let id = 0;
|
||||||
|
@ -39,14 +39,13 @@ export default class SlRange extends LitElement {
|
||||||
@query('.range__control') input: HTMLInputElement;
|
@query('.range__control') input: HTMLInputElement;
|
||||||
@query('.range__tooltip') output: HTMLOutputElement;
|
@query('.range__tooltip') output: HTMLOutputElement;
|
||||||
|
|
||||||
|
private hasSlotController = new HasSlotController(this, ['help-text', 'label']);
|
||||||
private inputId = `input-${++id}`;
|
private inputId = `input-${++id}`;
|
||||||
private helpTextId = `input-help-text-${id}`;
|
private helpTextId = `input-help-text-${id}`;
|
||||||
private labelId = `input-label-${id}`;
|
private labelId = `input-label-${id}`;
|
||||||
private resizeObserver: ResizeObserver;
|
private resizeObserver: ResizeObserver;
|
||||||
|
|
||||||
@state() private hasFocus = false;
|
@state() private hasFocus = false;
|
||||||
@state() private hasHelpTextSlot = false;
|
|
||||||
@state() private hasLabelSlot = false;
|
|
||||||
@state() private hasTooltip = false;
|
@state() private hasTooltip = false;
|
||||||
|
|
||||||
/** The input's name attribute. */
|
/** The input's name attribute. */
|
||||||
|
@ -88,14 +87,11 @@ export default class SlRange extends LitElement {
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
this.resizeObserver = new ResizeObserver(() => this.syncRange());
|
this.resizeObserver = new ResizeObserver(() => this.syncRange());
|
||||||
this.shadowRoot!.addEventListener('slotchange', this.handleSlotChange);
|
|
||||||
|
|
||||||
if (this.value === undefined || this.value === null) this.value = this.min;
|
if (this.value === undefined || this.value === null) this.value = this.min;
|
||||||
if (this.value < this.min) this.value = this.min;
|
if (this.value < this.min) this.value = this.min;
|
||||||
if (this.value > this.max) this.value = this.max;
|
if (this.value > this.max) this.value = this.max;
|
||||||
|
|
||||||
this.handleSlotChange();
|
|
||||||
|
|
||||||
this.updateComplete.then(() => {
|
this.updateComplete.then(() => {
|
||||||
this.syncRange();
|
this.syncRange();
|
||||||
this.resizeObserver.observe(this.input);
|
this.resizeObserver.observe(this.input);
|
||||||
|
@ -105,7 +101,6 @@ export default class SlRange extends LitElement {
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
this.resizeObserver.unobserve(this.input);
|
this.resizeObserver.unobserve(this.input);
|
||||||
this.shadowRoot!.removeEventListener('slotchange', this.handleSlotChange);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets focus on the input. */
|
/** Sets focus on the input. */
|
||||||
|
@ -163,13 +158,6 @@ export default class SlRange extends LitElement {
|
||||||
emit(this, 'sl-focus');
|
emit(this, 'sl-focus');
|
||||||
}
|
}
|
||||||
|
|
||||||
@watch('label')
|
|
||||||
@watch('helpText')
|
|
||||||
handleSlotChange() {
|
|
||||||
this.hasHelpTextSlot = hasSlot(this, 'help-text');
|
|
||||||
this.hasLabelSlot = hasSlot(this, 'label');
|
|
||||||
}
|
|
||||||
|
|
||||||
handleThumbDragStart() {
|
handleThumbDragStart() {
|
||||||
this.hasTooltip = true;
|
this.hasTooltip = true;
|
||||||
}
|
}
|
||||||
|
@ -207,16 +195,19 @@ export default class SlRange extends LitElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const hasLabelSlot = this.hasSlotController.test('label');
|
||||||
|
const hasHelpTextSlot = this.hasSlotController.test('help-text');
|
||||||
|
|
||||||
// NOTE - always bind value after min/max, otherwise it will be clamped
|
// NOTE - always bind value after min/max, otherwise it will be clamped
|
||||||
return renderFormControl(
|
return renderFormControl(
|
||||||
{
|
{
|
||||||
inputId: this.inputId,
|
inputId: this.inputId,
|
||||||
label: this.label,
|
label: this.label,
|
||||||
labelId: this.labelId,
|
labelId: this.labelId,
|
||||||
hasLabelSlot: this.hasLabelSlot,
|
hasLabelSlot,
|
||||||
helpTextId: this.helpTextId,
|
helpTextId: this.helpTextId,
|
||||||
helpText: this.helpText,
|
helpText: this.helpText,
|
||||||
hasHelpTextSlot: this.hasHelpTextSlot,
|
hasHelpTextSlot,
|
||||||
size: 'medium'
|
size: 'medium'
|
||||||
},
|
},
|
||||||
html`
|
html`
|
||||||
|
@ -249,10 +240,10 @@ export default class SlRange extends LitElement {
|
||||||
getLabelledBy({
|
getLabelledBy({
|
||||||
label: this.label,
|
label: this.label,
|
||||||
labelId: this.labelId,
|
labelId: this.labelId,
|
||||||
hasLabelSlot: this.hasLabelSlot,
|
hasLabelSlot,
|
||||||
helpText: this.helpText,
|
helpText: this.helpText,
|
||||||
helpTextId: this.helpTextId,
|
helpTextId: this.helpTextId,
|
||||||
hasHelpTextSlot: this.hasHelpTextSlot
|
hasHelpTextSlot
|
||||||
})
|
})
|
||||||
)}
|
)}
|
||||||
@input=${this.handleInput}
|
@input=${this.handleInput}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { emit } from '../../internal/event';
|
||||||
import { watch } from '../../internal/watch';
|
import { watch } from '../../internal/watch';
|
||||||
import { getLabelledBy, renderFormControl } from '../../internal/form-control';
|
import { getLabelledBy, renderFormControl } from '../../internal/form-control';
|
||||||
import { getTextContent } from '../../internal/slot';
|
import { getTextContent } from '../../internal/slot';
|
||||||
import { hasSlot } from '../../internal/slot';
|
import { HasSlotController } from '../../internal/slot';
|
||||||
import type SlDropdown from '../dropdown/dropdown';
|
import type SlDropdown from '../dropdown/dropdown';
|
||||||
import type SlIconButton from '../icon-button/icon-button';
|
import type SlIconButton from '../icon-button/icon-button';
|
||||||
import type SlMenu from '../menu/menu';
|
import type SlMenu from '../menu/menu';
|
||||||
|
@ -65,14 +65,13 @@ export default class SlSelect extends LitElement {
|
||||||
@query('.select__hidden-select') input: HTMLInputElement;
|
@query('.select__hidden-select') input: HTMLInputElement;
|
||||||
@query('.select__menu') menu: SlMenu;
|
@query('.select__menu') menu: SlMenu;
|
||||||
|
|
||||||
|
private hasSlotController = new HasSlotController(this, ['help-text', 'label']);
|
||||||
private inputId = `select-${++id}`;
|
private inputId = `select-${++id}`;
|
||||||
private helpTextId = `select-help-text-${id}`;
|
private helpTextId = `select-help-text-${id}`;
|
||||||
private labelId = `select-label-${id}`;
|
private labelId = `select-label-${id}`;
|
||||||
private resizeObserver: ResizeObserver;
|
private resizeObserver: ResizeObserver;
|
||||||
|
|
||||||
@state() private hasFocus = false;
|
@state() private hasFocus = false;
|
||||||
@state() private hasHelpTextSlot = false;
|
|
||||||
@state() private hasLabelSlot = false;
|
|
||||||
@state() private isOpen = false;
|
@state() private isOpen = false;
|
||||||
@state() private displayLabel = '';
|
@state() private displayLabel = '';
|
||||||
@state() private displayTags: TemplateResult[] = [];
|
@state() private displayTags: TemplateResult[] = [];
|
||||||
|
@ -130,12 +129,11 @@ export default class SlSelect extends LitElement {
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
this.handleSlotChange = this.handleSlotChange.bind(this);
|
this.handleMenuSlotChange = this.handleMenuSlotChange.bind(this);
|
||||||
this.resizeObserver = new ResizeObserver(() => this.resizeMenu());
|
this.resizeObserver = new ResizeObserver(() => this.resizeMenu());
|
||||||
|
|
||||||
this.updateComplete.then(() => {
|
this.updateComplete.then(() => {
|
||||||
this.resizeObserver.observe(this);
|
this.resizeObserver.observe(this);
|
||||||
this.shadowRoot!.addEventListener('slotchange', this.handleSlotChange);
|
|
||||||
this.syncItemsFromValue();
|
this.syncItemsFromValue();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -147,7 +145,6 @@ export default class SlSelect extends LitElement {
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
this.resizeObserver.unobserve(this);
|
this.resizeObserver.unobserve(this);
|
||||||
this.shadowRoot!.removeEventListener('slotchange', this.handleSlotChange);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Checks for validity and shows the browser's validation message if the control is invalid. */
|
/** Checks for validity and shows the browser's validation message if the control is invalid. */
|
||||||
|
@ -318,12 +315,7 @@ export default class SlSelect extends LitElement {
|
||||||
this.syncItemsFromValue();
|
this.syncItemsFromValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@watch('helpText')
|
async handleMenuSlotChange() {
|
||||||
@watch('label')
|
|
||||||
async handleSlotChange() {
|
|
||||||
this.hasHelpTextSlot = hasSlot(this, 'help-text');
|
|
||||||
this.hasLabelSlot = hasSlot(this, 'label');
|
|
||||||
|
|
||||||
// Wait for items to render before gathering labels otherwise the slot won't exist
|
// Wait for items to render before gathering labels otherwise the slot won't exist
|
||||||
const items = this.getItems();
|
const items = this.getItems();
|
||||||
|
|
||||||
|
@ -437,6 +429,8 @@ export default class SlSelect extends LitElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const hasLabelSlot = this.hasSlotController.test('label');
|
||||||
|
const hasHelpTextSlot = this.hasSlotController.test('help-text');
|
||||||
const hasSelection = this.multiple ? this.value?.length > 0 : this.value !== '';
|
const hasSelection = this.multiple ? this.value?.length > 0 : this.value !== '';
|
||||||
|
|
||||||
return renderFormControl(
|
return renderFormControl(
|
||||||
|
@ -444,10 +438,10 @@ export default class SlSelect extends LitElement {
|
||||||
inputId: this.inputId,
|
inputId: this.inputId,
|
||||||
label: this.label,
|
label: this.label,
|
||||||
labelId: this.labelId,
|
labelId: this.labelId,
|
||||||
hasLabelSlot: this.hasLabelSlot,
|
hasLabelSlot,
|
||||||
helpTextId: this.helpTextId,
|
helpTextId: this.helpTextId,
|
||||||
helpText: this.helpText,
|
helpText: this.helpText,
|
||||||
hasHelpTextSlot: this.hasHelpTextSlot,
|
hasHelpTextSlot,
|
||||||
size: this.size,
|
size: this.size,
|
||||||
onLabelClick: () => this.handleLabelClick()
|
onLabelClick: () => this.handleLabelClick()
|
||||||
},
|
},
|
||||||
|
@ -489,10 +483,10 @@ export default class SlSelect extends LitElement {
|
||||||
getLabelledBy({
|
getLabelledBy({
|
||||||
label: this.label,
|
label: this.label,
|
||||||
labelId: this.labelId,
|
labelId: this.labelId,
|
||||||
hasLabelSlot: this.hasLabelSlot,
|
hasLabelSlot,
|
||||||
helpText: this.helpText,
|
helpText: this.helpText,
|
||||||
helpTextId: this.helpTextId,
|
helpTextId: this.helpTextId,
|
||||||
hasHelpTextSlot: this.hasHelpTextSlot
|
hasHelpTextSlot
|
||||||
})
|
})
|
||||||
)}
|
)}
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
|
@ -548,7 +542,7 @@ export default class SlSelect extends LitElement {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<sl-menu part="menu" class="select__menu" @sl-select=${this.handleMenuSelect}>
|
<sl-menu part="menu" class="select__menu" @sl-select=${this.handleMenuSelect}>
|
||||||
<slot @slotchange=${this.handleSlotChange}></slot>
|
<slot @slotchange=${this.handleMenuSlotChange}></slot>
|
||||||
</sl-menu>
|
</sl-menu>
|
||||||
</sl-dropdown>
|
</sl-dropdown>
|
||||||
`
|
`
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { emit } from '../../internal/event';
|
||||||
import { live } from 'lit/directives/live.js';
|
import { live } from 'lit/directives/live.js';
|
||||||
import { watch } from '../../internal/watch';
|
import { watch } from '../../internal/watch';
|
||||||
import { getLabelledBy, renderFormControl } from '../../internal/form-control';
|
import { getLabelledBy, renderFormControl } from '../../internal/form-control';
|
||||||
import { hasSlot } from '../../internal/slot';
|
import { HasSlotController } from '../../internal/slot';
|
||||||
import styles from './textarea.styles';
|
import styles from './textarea.styles';
|
||||||
|
|
||||||
let id = 0;
|
let id = 0;
|
||||||
|
@ -35,14 +35,13 @@ export default class SlTextarea extends LitElement {
|
||||||
|
|
||||||
@query('.textarea__control') input: HTMLTextAreaElement;
|
@query('.textarea__control') input: HTMLTextAreaElement;
|
||||||
|
|
||||||
|
private hasSlotController = new HasSlotController(this, ['help-text', 'label']);
|
||||||
private inputId = `textarea-${++id}`;
|
private inputId = `textarea-${++id}`;
|
||||||
private helpTextId = `textarea-help-text-${id}`;
|
private helpTextId = `textarea-help-text-${id}`;
|
||||||
private labelId = `textarea-label-${id}`;
|
private labelId = `textarea-label-${id}`;
|
||||||
private resizeObserver: ResizeObserver;
|
private resizeObserver: ResizeObserver;
|
||||||
|
|
||||||
@state() private hasFocus = false;
|
@state() private hasFocus = false;
|
||||||
@state() private hasHelpTextSlot = false;
|
|
||||||
@state() private hasLabelSlot = false;
|
|
||||||
|
|
||||||
/** The textarea's size. */
|
/** The textarea's size. */
|
||||||
@property({ reflect: true }) size: 'small' | 'medium' | 'large' = 'medium';
|
@property({ reflect: true }) size: 'small' | 'medium' | 'large' = 'medium';
|
||||||
|
@ -115,10 +114,7 @@ export default class SlTextarea extends LitElement {
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
this.handleSlotChange = this.handleSlotChange.bind(this);
|
|
||||||
this.resizeObserver = new ResizeObserver(() => this.setTextareaHeight());
|
this.resizeObserver = new ResizeObserver(() => this.setTextareaHeight());
|
||||||
this.shadowRoot!.addEventListener('slotchange', this.handleSlotChange);
|
|
||||||
this.handleSlotChange();
|
|
||||||
|
|
||||||
this.updateComplete.then(() => {
|
this.updateComplete.then(() => {
|
||||||
this.setTextareaHeight();
|
this.setTextareaHeight();
|
||||||
|
@ -133,7 +129,6 @@ export default class SlTextarea extends LitElement {
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
this.resizeObserver.unobserve(this.input);
|
this.resizeObserver.unobserve(this.input);
|
||||||
this.shadowRoot!.removeEventListener('slotchange', this.handleSlotChange);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets focus on the textarea. */
|
/** Sets focus on the textarea. */
|
||||||
|
@ -243,13 +238,6 @@ export default class SlTextarea extends LitElement {
|
||||||
this.setTextareaHeight();
|
this.setTextareaHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
@watch('helpText')
|
|
||||||
@watch('label')
|
|
||||||
handleSlotChange() {
|
|
||||||
this.hasHelpTextSlot = hasSlot(this, 'help-text');
|
|
||||||
this.hasLabelSlot = hasSlot(this, 'label');
|
|
||||||
}
|
|
||||||
|
|
||||||
@watch('value')
|
@watch('value')
|
||||||
handleValueChange() {
|
handleValueChange() {
|
||||||
if (this.input) {
|
if (this.input) {
|
||||||
|
@ -269,15 +257,18 @@ export default class SlTextarea extends LitElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const hasLabelSlot = this.hasSlotController.test('label');
|
||||||
|
const hasHelpTextSlot = this.hasSlotController.test('help-text');
|
||||||
|
|
||||||
return renderFormControl(
|
return renderFormControl(
|
||||||
{
|
{
|
||||||
inputId: this.inputId,
|
inputId: this.inputId,
|
||||||
label: this.label,
|
label: this.label,
|
||||||
labelId: this.labelId,
|
labelId: this.labelId,
|
||||||
hasLabelSlot: this.hasLabelSlot,
|
hasLabelSlot,
|
||||||
helpTextId: this.helpTextId,
|
helpTextId: this.helpTextId,
|
||||||
helpText: this.helpText,
|
helpText: this.helpText,
|
||||||
hasHelpTextSlot: this.hasHelpTextSlot,
|
hasHelpTextSlot,
|
||||||
size: this.size
|
size: this.size
|
||||||
},
|
},
|
||||||
html`
|
html`
|
||||||
|
@ -321,10 +312,10 @@ export default class SlTextarea extends LitElement {
|
||||||
getLabelledBy({
|
getLabelledBy({
|
||||||
label: this.label,
|
label: this.label,
|
||||||
labelId: this.labelId,
|
labelId: this.labelId,
|
||||||
hasLabelSlot: this.hasLabelSlot,
|
hasLabelSlot,
|
||||||
helpText: this.helpText,
|
helpText: this.helpText,
|
||||||
helpTextId: this.helpTextId,
|
helpTextId: this.helpTextId,
|
||||||
hasHelpTextSlot: this.hasHelpTextSlot
|
hasHelpTextSlot
|
||||||
})
|
})
|
||||||
)}
|
)}
|
||||||
@change=${this.handleChange}
|
@change=${this.handleChange}
|
||||||
|
|
|
@ -1,3 +1,57 @@
|
||||||
|
import { ReactiveController, ReactiveControllerHost } from 'lit';
|
||||||
|
|
||||||
|
export class HasSlotController implements ReactiveController {
|
||||||
|
host: ReactiveControllerHost & Element;
|
||||||
|
slotNames: string[] = [];
|
||||||
|
|
||||||
|
constructor(host: ReactiveControllerHost & Element, slotNames: string[] = []) {
|
||||||
|
(this.host = host).addController(this);
|
||||||
|
this.slotNames = slotNames;
|
||||||
|
this.handleSlotChange = this.handleSlotChange.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private hasDefaultSlot() {
|
||||||
|
return [...this.host.childNodes].some(node => {
|
||||||
|
if (node.nodeType === node.TEXT_NODE && node.textContent!.trim() !== '') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.nodeType === node.ELEMENT_NODE) {
|
||||||
|
const el = node as HTMLElement;
|
||||||
|
if (!el.hasAttribute('slot')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private hasNamedSlot(name: string) {
|
||||||
|
return this.host.querySelector(`:scope > [slot="${name}"]`) !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
test(slotName: string) {
|
||||||
|
return slotName === '[default]' ? this.hasDefaultSlot() : this.hasNamedSlot(slotName);
|
||||||
|
}
|
||||||
|
|
||||||
|
hostConnected() {
|
||||||
|
this.host.shadowRoot!.addEventListener('slotchange', this.handleSlotChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
hostDisconnected() {
|
||||||
|
this.host.shadowRoot!.removeEventListener('slotchange', this.handleSlotChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSlotChange(event: Event) {
|
||||||
|
const slot = event.target as HTMLSlotElement;
|
||||||
|
|
||||||
|
if ((this.slotNames.includes('[default]') && !slot.name) || (slot.name && this.slotNames?.includes(slot.name))) {
|
||||||
|
this.host.requestUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Given a slot, this function iterates over all of its assigned element and text nodes and returns the concatenated
|
// Given a slot, this function iterates over all of its assigned element and text nodes and returns the concatenated
|
||||||
// HTML as a string. This is useful because we can't use slot.innerHTML as an alternative.
|
// HTML as a string. This is useful because we can't use slot.innerHTML as an alternative.
|
||||||
|
@ -35,30 +89,3 @@ export function getTextContent(slot: HTMLSlotElement): string {
|
||||||
|
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Determines whether an element has a slot. If name is specified, the function will look for a corresponding named
|
|
||||||
// slot, otherwise it will look for a "default" slot (e.g. a non-empty text node or an element with no slot attribute).
|
|
||||||
//
|
|
||||||
export function hasSlot(el: HTMLElement, name?: string) {
|
|
||||||
// Look for a named slot
|
|
||||||
if (name) {
|
|
||||||
return el.querySelector(`:scope > [slot="${name}"]`) !== null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look for a default slot
|
|
||||||
return [...el.childNodes].some(node => {
|
|
||||||
if (node.nodeType === node.TEXT_NODE && node.textContent!.trim() !== '') {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.nodeType === node.ELEMENT_NODE) {
|
|
||||||
const el = node as HTMLElement;
|
|
||||||
if (!el.hasAttribute('slot')) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
Ładowanie…
Reference in New Issue