site: revise $$slots tutorial chapter

pull/5277/head
Cameron Messinides 5 years ago
parent f20d9554b7
commit 54527f07cc

@ -1,14 +1,57 @@
<script> <script>
import ContactCard from './ContactCard.svelte'; import Project from './Project.svelte'
import Comment from './Comment.svelte'
</script> </script>
<ContactCard> <style>
<span slot="name"> h1 {
P. Sherman font-weight: 300;
</span> margin: 0 1rem;
}
<span slot="address"> ul {
42 Wallaby Way<br> list-style: none;
Sydney padding: 0;
</span> margin: 0.5rem;
</ContactCard> display: flex;
}
@media (max-width: 600px) {
ul {
flex-direction: column;
}
}
li {
padding: 0.5rem;
flex: 1 1 50%;
min-width: 200px;
}
</style>
<h1>
Projects
</h1>
<ul>
<li>
<Project
title="Add Typescript support"
tasksCompleted={25}
totalTasks={57}
>
<div slot="comments">
<Comment name="Ecma Script" postedAt={new Date('2020-08-17T14:12:23')}>
<p>Those interface tests are now passing.</p>
</Comment>
</div>
</Project>
</li>
<li>
<Project
title="Update documentation"
tasksCompleted={18}
totalTasks={21}
/>
</li>
</ul>

@ -0,0 +1,56 @@
<script>
export let name;
export let postedAt;
$: avatar = `https://ui-avatars.com/api/?name=${name.replace(/ /g, '+')}&rounded=true&background=ff3e00&color=fff&bold=true`;
</script>
<style>
article {
background-color: #fff;
border: 1px #ccc solid;
border-radius: 4px;
padding: 1rem;
}
.header {
align-items: center;
display: flex;
}
.details {
flex: 1 1 auto;
margin-left: 0.5rem
}
h4 {
margin: 0;
}
time {
color: #777;
font-size: 0.75rem;
text-decoration: underline;
}
.body {
margin-top: 0.5rem;
}
.body :global(p) {
margin: 0;
}
</style>
<article>
<div class="header">
<img src={avatar} alt="" height="32" width="32">
<div class="details">
<h4>{name}</h4>
<time datetime={postedAt.toISOString()}>{postedAt.toLocaleDateString()}</time>
</div>
</div>
<div class="body">
<slot></slot>
</div>
</article>

@ -1,47 +0,0 @@
<style>
.contact-card {
width: 300px;
border: 1px solid #aaa;
border-radius: 2px;
box-shadow: 2px 2px 8px rgba(0,0,0,0.1);
padding: 1em;
}
h2 {
padding: 0 0 0.2em 0;
margin: 0 0 1em 0;
border-bottom: 1px solid #ff3e00
}
.address, .email {
padding: 0 0 0 1.5em;
background: 0 0 no-repeat;
background-size: 20px 20px;
margin: 0 0 0.5em 0;
line-height: 1.2;
}
.address { background-image: url(tutorial/icons/map-marker.svg) }
.email { background-image: url(tutorial/icons/email.svg) }
.missing { color: #999 }
</style>
<article class="contact-card">
<h2>
<slot name="name">
<span class="missing">Unknown name</span>
</slot>
</h2>
<div class="address">
<slot name="address">
<span class="missing">Unknown address</span>
</slot>
</div>
<div class="email">
<slot name="email">
<span class="missing">Unknown email</span>
</slot>
</div>
</article>

@ -0,0 +1,62 @@
<script>
export let title;
export let tasksCompleted = 0;
export let totalTasks = 0;
</script>
<style>
article {
border: 1px #ccc solid;
border-radius: 4px;
position: relative;
}
article > div {
padding: 1.25rem;
}
article.has-discussion::after {
content: '';
background-color: #ff3e00;
border-radius: 10px;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
height: 20px;
position: absolute;
right: -10px;
top: -10px;
width: 20px;
}
h2,
h3 {
margin: 0 0 0.5rem;
}
h3 {
font-size: 0.875rem;
font-weight: 500;
letter-spacing: 0.08em;
text-transform: uppercase;
}
p {
color: #777;
margin: 0;
}
.discussion {
background-color: #eee;
border-top: 1px #ccc solid;
}
</style>
<article class:has-discussion={true}>
<div>
<h2>{title}</h2>
<p>{tasksCompleted}/{totalTasks} tasks completed</p>
</div>
<div class="discussion">
<h3>Comments</h3>
<slot name="comments"></slot>
</div>
</article>

@ -1,14 +1,57 @@
<script> <script>
import ContactCard from './ContactCard.svelte'; import Project from './Project.svelte'
import Comment from './Comment.svelte'
</script> </script>
<ContactCard> <style>
<span slot="name"> h1 {
P. Sherman font-weight: 300;
</span> margin: 0 1rem;
}
<span slot="address"> ul {
42 Wallaby Way<br> list-style: none;
Sydney padding: 0;
</span> margin: 0.5rem;
</ContactCard> display: flex;
}
@media (max-width: 600px) {
ul {
flex-direction: column;
}
}
li {
padding: 0.5rem;
flex: 1 1 50%;
min-width: 200px;
}
</style>
<h1>
Projects
</h1>
<ul>
<li>
<Project
title="Add Typescript support"
tasksCompleted={25}
totalTasks={57}
>
<div slot="comments">
<Comment name="Ecma Script" postedAt={new Date('2020-08-17T14:12:23')}>
<p>Those interface tests are now passing.</p>
</Comment>
</div>
</Project>
</li>
<li>
<Project
title="Update documentation"
tasksCompleted={18}
totalTasks={21}
/>
</li>
</ul>

@ -0,0 +1,56 @@
<script>
export let name;
export let postedAt;
$: avatar = `https://ui-avatars.com/api/?name=${name.replace(/ /g, '+')}&rounded=true&background=ff3e00&color=fff&bold=true`;
</script>
<style>
article {
background-color: #fff;
border: 1px #ccc solid;
border-radius: 4px;
padding: 1rem;
}
.header {
align-items: center;
display: flex;
}
.details {
flex: 1 1 auto;
margin-left: 0.5rem
}
h4 {
margin: 0;
}
time {
color: #777;
font-size: 0.75rem;
text-decoration: underline;
}
.body {
margin-top: 0.5rem;
}
.body :global(p) {
margin: 0;
}
</style>
<article>
<div class="header">
<img src={avatar} alt="" height="32" width="32">
<div class="details">
<h4>{name}</h4>
<time datetime={postedAt.toISOString()}>{postedAt.toLocaleDateString()}</time>
</div>
</div>
<div class="body">
<slot></slot>
</div>
</article>

@ -1,47 +0,0 @@
<style>
.contact-card {
width: 300px;
border: 1px solid #aaa;
border-radius: 2px;
box-shadow: 2px 2px 8px rgba(0,0,0,0.1);
padding: 1em;
}
h2 {
padding: 0 0 0.2em 0;
margin: 0 0 1em 0;
border-bottom: 1px solid #ff3e00
}
.address, .email {
padding: 0 0 0 1.5em;
background: 0 0 no-repeat;
background-size: 20px 20px;
margin: 0 0 0.5em 0;
line-height: 1.2;
}
.address { background-image: url(tutorial/icons/map-marker.svg) }
.email { background-image: url(tutorial/icons/email.svg) }
.missing { color: #999 }
</style>
<article class="contact-card">
<h2>
<slot name="name">
<span class="missing">Unknown name</span>
</slot>
</h2>
{#if $$slots.address}
<div class="address">
<slot name="address"></slot>
</div>
{/if}
{#if $$slots.email}
<div class="email">
<slot name="email"></slot>
</div>
{/if}
</article>

@ -0,0 +1,64 @@
<script>
export let title;
export let tasksCompleted = 0;
export let totalTasks = 0;
</script>
<style>
article {
border: 1px #ccc solid;
border-radius: 4px;
position: relative;
}
article > div {
padding: 1.25rem;
}
article.has-discussion::after {
content: '';
background-color: #ff3e00;
border-radius: 10px;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
height: 20px;
position: absolute;
right: -10px;
top: -10px;
width: 20px;
}
h2,
h3 {
margin: 0 0 0.5rem;
}
h3 {
font-size: 0.875rem;
font-weight: 500;
letter-spacing: 0.08em;
text-transform: uppercase;
}
p {
color: #777;
margin: 0;
}
.discussion {
background-color: #eee;
border-top: 1px #ccc solid;
}
</style>
<article class:has-discussion={$$slots.comments}>
<div>
<h2>{title}</h2>
<p>{tasksCompleted}/{totalTasks} tasks completed</p>
</div>
{#if $$slots.comments}
<div class="discussion">
<h3>Comments</h3>
<slot name="comments"></slot>
</div>
{/if}
</article>

@ -1,25 +1,28 @@
--- ---
title: Optional slots title: Checking slots
--- ---
In the previous example, the contact card rendered fallback text if a named slot was left empty. But for some slots, perhaps you don't want to render anything at all. We can do this by checking the properties of the special `$$slots` variable. In some cases, you may want to control parts of your component based on whether the parent passes in content for a certain slot. Perhaps you have a wrapper around that slot, and you don't want to render it if the slot is empty. Or perhaps you'd like to apply a class only if the slot is present. You can do this by checking the properties of the special `$$slots` variable.
`$$slots` is an object whose keys are the names of the slots passed in by the parent component. If the parent leaves a slot empty, then `$$slots` will not have an entry for that slot. `$$slots` is an object whose keys are the names of the slots passed in by the parent component. If the parent leaves a slot empty, then `$$slots` will not have an entry for that slot.
In `ContactCard.svelte`, wrap the `address` and `email` slots in `if` blocks that check `$$slots`, and remove the fallbacks from each `<slot>`: Notice that both instances of `<Project>` in this example render a container for comments and a notification dot, even though only one has comments. We want to use `$$slots` to make sure we only render these elements when the parent `<App>` passes in content for the `comments` slot.
In `Project.svelte`, update the `class:has-discussion` directive on the `<article>`:
```html ```html
{#if $$slots.address} <article class:has-discussion={$$slots.comments}>
<div class="address"> ```
<slot name="address"></slot>
</div> Next, wrap the `comments` slot and its wrapping `<div>` in an `if` block that checks `$$slots`:
{/if}
{#if $$slots.email} ```html
<div class="email"> {#if $$slots.comments}
<slot name="email"></slot> <div class="discussion">
<h3>Comments</h3>
<slot name="comments"></slot>
</div> </div>
{/if} {/if}
``` ```
Now the email row won't render at all when the `<App>` leaves that slot empty. Now the comments container and the notification dot won't render when `<App>` leaves the `comments` slot empty.

Loading…
Cancel
Save