Rework slot logic

pull/261/head
Cory LaViska 2020-10-15 16:35:11 -04:00
rodzic 4f23b49163
commit 2bd88b7d92
5 zmienionych plików z 22 dodań i 44 usunięć

Wyświetl plik

@ -189,11 +189,11 @@ To expose custom properties for a component, define them in the `:host` block an
### Conditional Slots
When a component relies on the presence of a slot to do something, don't assume the initial state of the component is its permanent state. Users can add and remove slotted content anytime, and components should be aware of this.
When a component relies on the presence of slotted content to do something, don't assume its initial state is permanent. Slotted content can be added or removed any time and components must be aware of this. A good practice to manage this is:
- Create an `updateSlots` method that uses `hasSlot` (imported from `src/utilities/slot.ts`) to check for the required slot(s)
- Listen on the host element for the `slotchange` event and run `updateSlots`
- Don't conditionally render any slots — always use `hidden` or `display: none` so the slot exists in the DOM
- Create a `handleSlotChange` method that uses `hasSlot` (imported from `src/utilities/slot.ts`) to update state variables for the associated slot(s)
- Add `onSlotchange={this.handleSlotChange}` to the slots you want to watch
- Never conditionally render `<slot>` elements in a component — always use `hidden` so the slot remains in the DOM and the `slotchange` event can be captured
See the source of card, dialog, or drawer for examples.

Wyświetl plik

@ -14,6 +14,7 @@ _During the beta period, these restrictions may be relaxed in the event of a mis
- Fixed a bug where concurrent active modals (i.e. dialog, drawer) would try to steal focus from each other
- Improved `sl-color-picker` grid and slider handles [#246](https://github.com/shoelace-style/shoelace/issues/246)
- Reworked show/hide logic in `sl-alert`, `sl-dialog`, and `sl-drawer` to not use reflow hacks and the `hidden` attribute
- Reworked slot logic in `sl-card`, `sl-dialog`, and `sl-drawer`
- Updated to Popper 2.5.3 to address a fixed position bug in Firefox
## 2.0.0-beta.20

Wyświetl plik

@ -30,19 +30,10 @@ export class Card {
@State() hasHeader = false;
connectedCallback() {
this.updateSlots = this.updateSlots.bind(this);
this.handleSlotChange = this.handleSlotChange.bind(this);
}
componentWillLoad() {
this.updateSlots();
this.host.shadowRoot.addEventListener('slotchange', this.updateSlots);
}
disconnectedCallback() {
this.host.shadowRoot.removeEventListener('slotchange', this.updateSlots);
}
updateSlots() {
handleSlotChange() {
this.hasFooter = hasSlot(this.host, 'footer');
this.hasImage = hasSlot(this.host, 'image');
this.hasHeader = hasSlot(this.host, 'header');
@ -60,11 +51,11 @@ export class Card {
}}
>
<div part="image" class="card__image">
<slot name="image" />
<slot name="image" onSlotchange={this.handleSlotChange} />
</div>
<div part="header" class="card__header">
<slot name="header" />
<slot name="header" onSlotchange={this.handleSlotChange} />
</div>
<div part="body" class="card__body">
@ -72,7 +63,7 @@ export class Card {
</div>
<div part="footer" class="card__footer">
<slot name="footer" />
<slot name="footer" onSlotchange={this.handleSlotChange} />
</div>
</div>
);

Wyświetl plik

@ -78,18 +78,13 @@ export class Dialog {
this.handleTransitionEnd = this.handleTransitionEnd.bind(this);
this.handleKeyDown = this.handleKeyDown.bind(this);
this.handleOverlayClick = this.handleOverlayClick.bind(this);
this.updateSlots = this.updateSlots.bind(this);
this.handleSlotChange = this.handleSlotChange.bind(this);
this.modal = new Modal(this.host, {
onFocusOut: () => this.panel.focus()
});
}
componentWillLoad() {
this.updateSlots();
this.host.shadowRoot.addEventListener('slotchange', this.updateSlots);
}
componentDidLoad() {
// Show on init if open
if (this.open) {
@ -99,8 +94,6 @@ export class Dialog {
disconnectedCallback() {
unlockBodyScrolling(this.host);
this.host.shadowRoot.removeEventListener('slotchange', this.updateSlots);
}
/** Shows the dialog */
@ -162,6 +155,10 @@ export class Dialog {
}
}
handleSlotChange() {
this.hasFooter = hasSlot(this.host, 'footer');
}
handleTransitionEnd(event: TransitionEvent) {
const target = event.target as HTMLElement;
@ -176,10 +173,6 @@ export class Dialog {
}
}
updateSlots() {
this.hasFooter = hasSlot(this.host, 'footer');
}
render() {
return (
<div
@ -222,7 +215,7 @@ export class Dialog {
</div>
<footer part="footer" class="dialog__footer">
<slot name="footer" />
<slot name="footer" onSlotchange={this.handleSlotChange} />
</footer>
</div>
</div>

Wyświetl plik

@ -86,18 +86,13 @@ export class Drawer {
this.handleTransitionEnd = this.handleTransitionEnd.bind(this);
this.handleKeyDown = this.handleKeyDown.bind(this);
this.handleOverlayClick = this.handleOverlayClick.bind(this);
this.updateSlots = this.updateSlots.bind(this);
this.handleSlotChange = this.handleSlotChange.bind(this);
this.modal = new Modal(this.host, {
onFocusOut: () => (this.contained ? null : this.panel.focus())
});
}
componentWillLoad() {
this.updateSlots();
this.host.shadowRoot.addEventListener('slotchange', this.updateSlots);
}
componentDidLoad() {
// Show on init if open
if (this.open) {
@ -107,8 +102,6 @@ export class Drawer {
disconnectedCallback() {
unlockBodyScrolling(this.host);
this.host.shadowRoot.removeEventListener('slotchange', this.updateSlots);
}
/** Shows the drawer */
@ -173,6 +166,10 @@ export class Drawer {
}
}
handleSlotChange() {
this.hasFooter = hasSlot(this.host, 'footer');
}
handleTransitionEnd(event: TransitionEvent) {
const target = event.target as HTMLElement;
@ -187,10 +184,6 @@ export class Drawer {
}
}
updateSlots() {
this.hasFooter = hasSlot(this.host, 'footer');
}
render() {
return (
<div
@ -239,7 +232,7 @@ export class Drawer {
</div>
<footer part="footer" class="drawer__footer">
<slot name="footer" />
<slot name="footer" onSlotchange={this.handleSlotChange} />
</footer>
</div>
</div>