mirror of https://github.com/sveltejs/svelte
commit
2a7a71426e
@ -1,15 +1,14 @@
|
||||
module.exports = {
|
||||
file: [
|
||||
'test/test.js'
|
||||
'test/test.ts'
|
||||
],
|
||||
require: [
|
||||
'sucrase/register'
|
||||
]
|
||||
};
|
||||
|
||||
// add coverage options when running 'npx c8 mocha'
|
||||
if (process.env.NODE_V8_COVERAGE) {
|
||||
Object.assign(module.exports, {
|
||||
fullTrace: true,
|
||||
require: [
|
||||
'source-map-support/register'
|
||||
]
|
||||
});
|
||||
module.exports.fullTrace = true;
|
||||
module.exports.require.push('source-map-support/register');
|
||||
}
|
||||
|
@ -0,0 +1,85 @@
|
||||
---
|
||||
title: What's new in Svelte: October 2020
|
||||
description: New object methods, in-depth learning resources and tons of integration examples!
|
||||
author: Daniel Sandoval
|
||||
authorURL: https://desandoval.net
|
||||
---
|
||||
|
||||
Welcome to the first edition of our "What's new in Svelte" series! We'll try to make this a monthly blog post in which you'll find out about new features, bug fixes, and a showcase of Svelte projects from around the community.
|
||||
|
||||
## New features
|
||||
1. `use:obj.method` allows functions defined within objects to be used within actions ([Example](https://svelte.dev/repl/c305722adb4a4545b27b198ea8ff9bde?version=3.27.0), **3.26.0**, warning removed in **3.27.0**)
|
||||
2. `_` is now supported as a "numerical separator", similar to a `.` or `,` ([Example](https://svelte.dev/repl/844c39e91d1248649fe54af839fab570?version=3.26.0), **3.26.0**)
|
||||
3. `import.meta` now works in template expressions ([Example](https://svelte.dev/repl/9630de41957a4c80a4fce264360a6bc7?version=3.26.0), **3.26.0**)
|
||||
4. CSS Selectors with `~` and `+` combinators are now supported ([Example](https://svelte.dev/repl/91ad9257d2d1430185a504a18cc60172?version=3.29.0), **3.27.0**, with a compiler fix in **3.29.0**)
|
||||
5. The `{#key}` block is now available to key arbitrary content on an expression. Whever the expression changes, the contents inside the `{#key}` block will be destroyed and recreated. For an in-depth explanation and to find out how it's implemented, check out the [new blog post](https://lihautan.com/contributing-to-svelte-implement-key-block/) of Svelte Team member Tan Li Hau. ([More info](https://github.com/sveltejs/svelte/issues/1469), **3.29.0**)
|
||||
6. Slots can now be forwarded through child components! This used to only be possible by adding extra wrapper `<div>`s ([More info](https://github.com/sveltejs/svelte/issues/2079), **3.29.0**)
|
||||
7. When using TypeScript, you can now type the `createEventDispatcher` method:
|
||||
```html
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
/**
|
||||
* you can also add docs
|
||||
*/
|
||||
checked: boolean; // Will translate to `CustomEvent<boolean>`
|
||||
hello: string;
|
||||
}>();
|
||||
|
||||
// ...
|
||||
</script>
|
||||
```
|
||||
This will make sure that you can invoke dispatch only with the specified event names and its types. The Svelte for VS Code extension was also updated to deal with this new feature. It will provide strong typings for these events as well as autocompletion and hover information.
|
||||
|
||||
**New from Sapper!**
|
||||
Sapper 0.28.9 just came out. The highlights from it include much better support for CSP nonces, asset preload support for exported pages, and error details are now available in the `$page` store on error pages.
|
||||
|
||||
In addition, Sapper's CSS handling has been rewritten over the course of recent releases in order to fix existing CSS handling bugs, refactor the CSS handling to occur entirely within a Rollup plugin, and remove the need internally to register CSS in the routing system. Congrats and thank you to the folks working on Sapper for all their solid work!
|
||||
|
||||
|
||||
## Impactful bug fixes
|
||||
- CSS compilation will no longer remove rules for the `open` attribute on `<details>` elements ([Example](https://svelte.dev/repl/ab4c0c177d1f4fab92f46eb8539cea9a?version=3.26.0), **3.26.0**)
|
||||
- `prettier-plugin-svelte` will do a better job now at dealing with whitespaces, especially around inline elements. It will also preserve formatting inside `<pre>` tags and will no longer format languages which are not supported by Prettier, like SASS, Pug or Stylus.
|
||||
|
||||
|
||||
## Coming up
|
||||
- [Svelte Summit](https://sveltesummit.com/), Svelte's second global online conference, is taking place on October 18! Sign up for free to get reminders and talk updates!
|
||||
|
||||
For all the features and bugfixes see the CHANGELOG for [Svelte](https://github.com/sveltejs/svelte/blob/master/CHANGELOG.md) and [Sapper](https://github.com/sveltejs/sapper/blob/master/CHANGELOG.md).
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Svelte Showcase
|
||||
- [This CustomMenu example](https://svelte.dev/repl/3a33725c3adb4f57b46b597f9dade0c1?version=3.25.0) demos how to replace the OS right-click menu
|
||||
- [Github Tetris](https://svelte.dev/repl/cc1eaa7c66964fedb5e70e3ecbbaa0e1?version=3.25.1) lets you play a Tetris-like game in a git commit history
|
||||
- [Who are my representatives?](https://whoaremyrepresentatives.us/) is a website built with Svelte to help US residents get more info on their congressional representatives
|
||||
- [Pick Palette](https://github.com/bluwy/pick-palette) is a color palette manager made with Svelte!
|
||||
|
||||
#### In-depth learning:
|
||||
- [Svelte 3 Up and Running](https://www.amazon.com/dp/B08D6T6BKS/ref=cm_sw_r_tw_dp_x_OQMtFb3GPQCB2) is a new book about building production-ready static web apps with Svelte 3
|
||||
- [Sapper Tutorial (Crash Course)](https://www.youtube.com/playlist?list=PL4cUxeGkcC9gdr4Qhx83gBBcID-KMe-PQ) walks through the ins-and-outs of Sapper, the Svelte-powered application framework
|
||||
- [Svelte Society Day France](https://france.sveltesociety.dev/) happened September 27th featuring a wide variety of topics all in French! You can find the full recording [here](https://www.youtube.com/watch?v=aS1TQ155JK4).
|
||||
|
||||
#### Plug-and-play components:
|
||||
- [svelte-zoom](https://github.com/vaheqelyan/svelte-zoom) brings "nearly native" pan-and-zoom to images on desktop and mobile
|
||||
- [svelte-materialify](https://github.com/TheComputerM/svelte-materialify) is a Material component library for Svelte with over 50 components
|
||||
- [svelte-undoable](https://github.com/macfja/svelte-undoable) makes it easy to introduce undo and redo functionality using `bind:`
|
||||
- [This Tilt component](https://svelte.dev/repl/7b23ad9d2693424482cd411b0378b55b?version=3.24.1) implements a common UX pattern where the hovered element tilts to follow the mouse
|
||||
|
||||
#### Lots of examples of how use JS tech came out this month:
|
||||
- [Sapper with PostCSS and Tailwind](https://codechips.me/sapper-with-postcss-and-tailwind/)
|
||||
- [PrismJS (Code block syntax highlighting)](https://github.com/phptuts/Svelte-PrismJS)
|
||||
- [Filepond (Drag-and-drop file upload)](https://github.com/pqina/svelte-filepond)
|
||||
- [Ionic (UI Components)](https://github.com/Tommertom/svelte-ionic-app)
|
||||
- [Pell (WYSIWYG Editor)](https://github.com/Demonicious/svelte-pell/)
|
||||
- [Leaflet (Mapping)](https://github.com/anoram/leaflet-svelte)
|
||||
|
||||
**Reminder**: There's a [Svelte integrations repo](https://github.com/sveltejs/integrations) that demonstrates ways to incorporate Svelte into your stack (and vice versa). If you've got questions on how to use a particular piece of tech with Svelte, you may find your answer there... and if you've gotten something to work with Svelte, consider contributing!
|
||||
|
||||
For more amazing Svelte projects, check out the [Svelte Society](https://sveltesociety.dev/), [Reddit](https://www.reddit.com/r/sveltejs/) and [Discord](https://discord.com/invite/yy75DKs)… and be sure to post your own!
|
||||
|
||||
## See you next month!
|
||||
|
||||
By the way, Svelte now has an [OpenCollective](https://opencollective.com/svelte)! All contributions and all expenses are published in our transparent public ledger. Learn who is donating, how much, where that money is going, submit expenses, get reimbursed and more!
|
@ -0,0 +1,46 @@
|
||||
---
|
||||
title: What's new in Svelte: November 2020
|
||||
description: Slot forwarding fixes, SvelteKit for faster local development, and more from Svelte Summit
|
||||
author: Daniel Sandoval
|
||||
authorURL: https://desandoval.net
|
||||
---
|
||||
|
||||
Welcome back to the "What's new in Svelte" series! This month, we're covering new features & bug fixes, last month's Svelte Summit and some stand-out sites and libraries...
|
||||
|
||||
## New features & impactful bug fixes
|
||||
|
||||
1. Destructuring Promises now works as expected by using the `{#await}` syntax
|
||||
(**3.29.3**, [Example](https://svelte.dev/repl/3fd4e2cecfa14d629961478f1dac2445?version=3.29.3))
|
||||
2. Slot forwarding (released in 3.29.0) should no longer hang during compilation (**3.29.3**, [Example](https://svelte.dev/repl/29959e70103f4868a6525c0734934936?version=3.29.3))
|
||||
3. Better typings for the `get` function in `svelte/store` and on lifecycle hooks (**3.29.1**)
|
||||
|
||||
**What's going on in Sapper?**
|
||||
|
||||
Sapper got some new types in its `preload` function, which will make typing easier if you are using TypeScript. See the [Sapper docs](https://sapper.svelte.dev/docs#Typing_the_function) on how to use them. There also were fixes to `preload` links in exported sites. Route layouts got a few fixes too - including ensuring CSS is applied to nested route layouts. You can also better organize your files now that extensions with multiple dots are supported. (**0.28.10**)
|
||||
|
||||
|
||||
For all the features and bugfixes see the CHANGELOGs for [Svelte](https://github.com/sveltejs/svelte/blob/master/CHANGELOG.md) and [Sapper](https://github.com/sveltejs/sapper/blob/master/CHANGELOG.md).
|
||||
|
||||
|
||||
## [Svelte Summit](https://sveltesummit.com/) was Svelte-tacular!
|
||||
- Rich Harris demoed the possible future of Svelte development in a talk titled "Futuristic Web Development". The not-yet-public project is called SvelteKit (name may change) and will bring a first-class developer experience and more flexibility for build outputs. If you want to get the full sneak-peek, [check out the video](https://www.youtube.com/watch?v=qSfdtmcZ4d0).
|
||||
- 17 speakers made the best of the conference's virtual format... From floating heads to seamless demos, Svelte developers from every skill level will find something of interest in this year's [YouTube playlist](https://www.youtube.com/playlist?list=PL8bMgX1kyZThM1sbYCoWdTcpiYysJsSeu)
|
||||
|
||||
---
|
||||
|
||||
## Community Showcase
|
||||
- [Svelte Lab](https://sveltelab.app/) showcases a variety of components, visualizations and interactions that can be achieved in Svelte. You can click into any component to see its source or edit it, using the site's built-in REPL
|
||||
- [svelte-electron-boilerplate](https://github.com/hjalmar/svelte-electron-boilerplate) is a fast way to get up and running with a Svelte app built in the desktop javascript framework, Electron
|
||||
- [React Hooks in Svelte](https://github.com/joshnuss/react-hooks-in-svelte) showcases examples of common React Hooks ported to Svelte.
|
||||
- [gurlic](https://gurlic.com/) is a social network and internet experiment that is super snappy thanks to Svelte
|
||||
- [Interference 2020](https://interference2020.org/) visualizes reported foreign interference in the 2020 U.S. elections. You can learn more about how it was built in [YYY's talk at Svelte Summit]()
|
||||
- [jitsi-svelte](https://github.com/relm-us/jitsi-svelte) lets you easily create your own custom Jitsi client by providing out-of-the-box components built with Svelte
|
||||
- [Ellx](https://ellx.io/) is part spreadsheet, part notebook and part IDE. It's super smooth thanks to Svelte 😎
|
||||
- [This New Zealand news site](https://www.nzherald.co.nz/nz/election-2020-latest-results-party-vote-electorate-vote-and-full-data/5CFVO4ENKNQDE3SICRRNPU5GZM/) breaks down the results of the 2020 Parliamentary elections using Svelte
|
||||
- [Budibase](https://github.com/Budibase/budibase) is a no-code app builder, powered by Svelte
|
||||
- [Svelt-yjs](https://github.com/relm-us/svelt-yjs) combines the collaborative, local-first technology of Yjs with the power of Svelte to enable multiple users across the internet to stay in sync.
|
||||
- [tabler-icons-svelte](https://github.com/benflap/tabler-icons-svelte) is a Svelte wrapper for over 850 free MIT-licensed high-quality SVG icons for you to use in your web projects.
|
||||
|
||||
## See you next month!
|
||||
|
||||
Got an idea for something to add to the Showcase? Want to get involved more with Svelte? We're always looking for maintainers, contributors and fanatics... Check out the [Svelte Society](https://sveltesociety.dev/), [Reddit](https://www.reddit.com/r/sveltejs/) and [Discord](https://discord.com/invite/yy75DKs) to get involved!
|
@ -0,0 +1,103 @@
|
||||
---
|
||||
title: What's the deal with SvelteKit?
|
||||
description: We're rethinking how to build Svelte apps. Here's what you need to know
|
||||
author: Rich Harris
|
||||
authorURL: https://twitter.com/rich_harris
|
||||
---
|
||||
|
||||
<aside><p>If you <em>didn't</em> attend Svelte Summit, you can catch up on the <a href="https://www.youtube.com/c/SvelteSociety/videos">Svelte Society YouTube page</a></p></aside>
|
||||
|
||||
If you attended [Svelte Summit](https://sveltesummit.com/) last month you may have seen my talk, Futuristic Web Development, in which I finally tackled one of the most frequently asked questions about Svelte: when will Sapper reach version 1.0?
|
||||
|
||||
The answer: never.
|
||||
|
||||
This was slightly tongue-in-cheek — as the talk explains, it's really more of a rewrite of Sapper coupled with a rebrand — but it raised a lot of new questions from the community, and it's time we offered a bit more clarity on what you can expect from Sapper's successor, SvelteKit.
|
||||
|
||||
<div class="max">
|
||||
<figure style="max-width: 960px; margin: 0 auto">
|
||||
<div style="height: 0; padding: 0 0 57.1% 0; position: relative; margin: 0 auto;">
|
||||
<iframe style="position: absolute; width: 100%; height: 100%; left: 0; top: 0; margin: 0;" src="https://www.youtube-nocookie.com/embed/qSfdtmcZ4d0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
</div>
|
||||
|
||||
<figcaption>'Futuristic Web Development' from <a href="https://sveltesummit.com/">Svelte Summit</a></figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
|
||||
## What's Sapper?
|
||||
|
||||
[Sapper](https://sapper.svelte.dev) is an *app framework* (or 'metaframework') built on top of Svelte (which is a *component* framework). Its job is to make it easy to build Svelte apps with all the modern best practices like server-side rendering (SSR) and code-splitting, and to provide a project structure that makes development productive and fun. It uses *filesystem-based routing* (as popularised by [Next](https://nextjs.org/) and adopted by many other frameworks, albeit with some enhancements) — your project's file structure mirrors the structure of the app itself.
|
||||
|
||||
While the Svelte homepage and documentation encourages you to [degit](https://github.com/Rich-Harris/degit) the [sveltejs/template](https://github.com/sveltejs/template) repo to start building an app, Sapper has long been our recommended way to build apps; this very blog post is (at the time of writing!) rendered with Sapper.
|
||||
|
||||
|
||||
## Why are we migrating to something new?
|
||||
|
||||
Firstly, the distinction between [sveltejs/template](https://github.com/sveltejs/template) and [sveltejs/sapper-template](https://github.com/sveltejs/sapper-template) is confusing, particularly to newcomers to Svelte. Having a single recommended way to start building apps with Svelte will bring enormous benefits: we simplify onboarding, reduce the maintenance and support burden, and can potentially begin to explore the new possibilities that are unlocked by having a predictable project structure. (This last part is deliberately vague because it will take time to fully understand what those possibilities are.)
|
||||
|
||||
Aside from all that, we've been tempted by the thought of rewriting Sapper for a while. This is partly because the codebase has become a little unkempt over the years ([Sapper started in 2017](/blog/sapper-towards-the-ideal-web-app-framework)), but mostly because the web has changed a lot recently, and it's time to rethink some of our foundational assumptions.
|
||||
|
||||
|
||||
## How is this new thing different?
|
||||
|
||||
The first of those foundational assumptions is that you need to use a module bundler like [webpack](https://webpack.js.org/) or [Rollup](http://rollupjs.org/) to build apps. These tools trace the dependency graph of your application, analysing and transforming code along the way (turning Svelte components to JS modules, for example), in order to create bundles of code that can run anywhere. As the original creator of Rollup, I can attest that it is a surprisingly complex problem with fiendish edge cases.
|
||||
|
||||
You certainly needed a bundler several years ago, because browsers didn't natively support the `import` keyword, but it's much less true today. Right now, we're seeing the rise of the *unbundled development* workflow, which is radically simpler: instead of eagerly bundling your app, a dev server can serve modules (converted to JavaScript, if necessary) *on-demand*, meaning startup is essentially instantaneous however large your app becomes.
|
||||
|
||||
[Snowpack](https://www.snowpack.dev/) is at the vanguard of this movement, and it's what powers SvelteKit. It's astonishingly fast, and has a beautiful development experience (hot module reloading, error overlays and so on), and we've been working closely with the Snowpack team on features like SSR. The hot module reloading is particularly revelatory if you're used to using Sapper with Rollup (which has never had first-class HMR support owing to its architecture, which prioritises the most efficient output).
|
||||
|
||||
That's not to say we're abandoning bundlers altogether. It's still essential to optimise your app for production, and SvelteKit uses Rollup to make your apps as fast and lean as they possibly can be (which includes things like extracting styles into static `.css` files).
|
||||
|
||||
The other foundational assumption is that a server-rendered app needs, well, a server. Sapper effectively has two modes — `sapper build`, which creates a standalone app that has to run on a Node server, and `sapper export` which bakes your app out as a collection of static files suitable for hosting on services like GitHub Pages.
|
||||
|
||||
Static files can go pretty much anywhere, but running a Node server (and monitoring/scaling it etc) is less straightforward. Nowadays we're witnessing a shift towards *serverless platforms*, in which you as the app author don't need to think about the server your code is running on, with all the attendant complexity. You can get Sapper apps running on serverless platforms, thanks to things like [vercel-sapper](https://github.com/thgh/vercel-sapper), but it's certainly not what you'd call idiomatic.
|
||||
|
||||
<aside><p>It'll still be possible to create both Node apps and fully pre-rendered (aka exported) sites</a></p></aside>
|
||||
|
||||
SvelteKit fully embraces the serverless paradigm, and will launch with support for all the major serverless providers, with an 'adapter' API for targeting any platforms that we don't officially cater to. In addition, we'll be able to do partial pre-rendering, which means that static pages can be generated at build time but dynamic ones get rendered on-demand.
|
||||
|
||||
|
||||
## When can I start using it?
|
||||
|
||||
If you're feeling brave, you can start right now:
|
||||
|
||||
```bash
|
||||
npm init svelte@next
|
||||
```
|
||||
|
||||
This will scaffold a new project and install the `@sveltejs/kit` CLI, which provides the tools for developing and building an app.
|
||||
|
||||
We don't recommend it though! There are no docs, and we won't be able to offer any form of support. It's also likely to break often.
|
||||
|
||||
The work is being done in a private monorepo while we're still in exploration mode. Our plan is to get a public beta ready and announce it here once we've closed a few issues — the repo itself will remain private at that time, but we'll create a place to collect feedback from the YOLO crowd. After that, we'll work towards a 1.0 release which will involve opening the repo up.
|
||||
|
||||
I'm not going to make any firm promises about timings, because I don't like to break promises. But I *think* we're talking about weeks rather than months.
|
||||
|
||||
|
||||
## What if I don't want to use SvelteKit?
|
||||
|
||||
You won't have to — it will always be possible to use Svelte as a standalone package or via a bundler integration like [rollup-plugin-svelte](https://github.com/sveltejs/rollup-plugin-svelte). We think it's essential that you can bend Svelte to fit your workflow, however esoteric, and use third-party app frameworks like [Elder.js](https://github.com/Elderjs/elderjs), [Routify](https://routify.dev/), [Plenti](https://plenti.co/), [Crown](https://crownframework.com/), [JungleJS](https://www.junglejs.org/) and others.
|
||||
|
||||
|
||||
## TypeScript?
|
||||
|
||||
Don't worry, we won't launch without full TypeScript support.
|
||||
|
||||
|
||||
## How can I migrate my existing Sapper apps?
|
||||
|
||||
For the most part, it should be relatively straightforward to migrate a Sapper codebase.
|
||||
|
||||
There are some unavoidable changes (being able to run on serverless platforms means we need to replace custom `server.js` files and `(req, res) => {...}` functions with more portable equivalents), and we're taking the opportunity to fix a few design flaws, but on the whole a SvelteKit app will feel very familiar to Sapper users.
|
||||
|
||||
Detailed migration guides will accompany the 1.0 launch.
|
||||
|
||||
|
||||
## How can I contribute?
|
||||
|
||||
Keep your eyes peeled for announcements about when we'll launch the public beta and open up the repo. (Also, blog post TODO but I would be remiss if I didn't mention that we now have an [OpenCollective](https://opencollective.com/svelte) where you can contribute financially to the project if it's been valuable to you. Many, many thanks to those of you who already have.)
|
||||
|
||||
|
||||
## Where can I learn more?
|
||||
|
||||
Follow [@sveltejs](https://twitter.com/sveltejs) and [@SvelteSociety](https://twitter.com/SvelteSociety) on Twitter, and visit [svelte.dev/chat](https://svelte.dev/chat). You should also subscribe to [Svelte Radio](https://www.svelteradio.com/), where Kevin and his co-hosts will grill me about this project on an upcoming episode (and between now and next week when we record it, [reply to this Twitter thread](https://twitter.com/Rich_Harris/status/1323376048571121665) with your additional questions).
|
@ -0,0 +1,13 @@
|
||||
<script>
|
||||
import Profile from "./Profile.svelte";
|
||||
</script>
|
||||
|
||||
<Profile>
|
||||
<span slot="name">Bob</span>
|
||||
<span slot="email">bob@email.com</span>
|
||||
</Profile>
|
||||
|
||||
<Profile>
|
||||
<span slot="name">Alice</span>
|
||||
<span slot="phone">12345678</span>
|
||||
</Profile>
|
@ -0,0 +1,24 @@
|
||||
<style>
|
||||
section {
|
||||
width: 200px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
padding: 16px;
|
||||
box-shadow: 2px 2px 4px #dedede;
|
||||
border: 1px solid #888;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<section>
|
||||
<div>Name</div>
|
||||
<slot name="name" />
|
||||
{#if $$slots.email}
|
||||
<div>Email</div>
|
||||
<slot name="email" />
|
||||
{/if}
|
||||
{#if $$slots.phone}
|
||||
<div>Phone</div>
|
||||
<slot name="phone" />
|
||||
{/if}
|
||||
</section>
|
@ -0,0 +1,3 @@
|
||||
{
|
||||
"title": "Conditional Slots"
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
<script>
|
||||
import Project from './Project.svelte'
|
||||
import Comment from './Comment.svelte'
|
||||
</script>
|
||||
|
||||
<style>
|
||||
h1 {
|
||||
font-weight: 300;
|
||||
margin: 0 1rem;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0.5rem;
|
||||
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>
|
@ -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>
|
@ -0,0 +1,57 @@
|
||||
<script>
|
||||
import Project from './Project.svelte'
|
||||
import Comment from './Comment.svelte'
|
||||
</script>
|
||||
|
||||
<style>
|
||||
h1 {
|
||||
font-weight: 300;
|
||||
margin: 0 1rem;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0.5rem;
|
||||
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>
|
@ -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>
|
@ -0,0 +1,28 @@
|
||||
---
|
||||
title: Checking for slot content
|
||||
---
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
<article class:has-discussion={$$slots.comments}>
|
||||
```
|
||||
|
||||
Next, wrap the `comments` slot and its wrapping `<div>` in an `if` block that checks `$$slots`:
|
||||
|
||||
```html
|
||||
{#if $$slots.comments}
|
||||
<div class="discussion">
|
||||
<h3>Comments</h3>
|
||||
<slot name="comments"></slot>
|
||||
</div>
|
||||
{/if}
|
||||
```
|
||||
|
||||
Now the comments container and the notification dot won't render when `<App>` leaves the `comments` slot empty.
|
@ -0,0 +1,19 @@
|
||||
import Expression from './shared/Expression';
|
||||
import map_children from './shared/map_children';
|
||||
import AbstractBlock from './shared/AbstractBlock';
|
||||
|
||||
export default class KeyBlock extends AbstractBlock {
|
||||
type: 'KeyBlock';
|
||||
|
||||
expression: Expression;
|
||||
|
||||
constructor(component, parent, scope, info) {
|
||||
super(component, parent, scope, info);
|
||||
|
||||
this.expression = new Expression(component, this, scope, info.expression);
|
||||
|
||||
this.children = map_children(component, this, scope, info.children);
|
||||
|
||||
this.warn_if_empty_block();
|
||||
}
|
||||
}
|
@ -1,3 +1,3 @@
|
||||
import { BaseAttributeWrapper } from "./Attribute";
|
||||
import { BaseAttributeWrapper } from './Attribute';
|
||||
|
||||
export default class SpreadAttributeWrapper extends BaseAttributeWrapper {}
|
||||
|
@ -0,0 +1,61 @@
|
||||
import ElementWrapper from './index';
|
||||
import SlotWrapper from '../Slot';
|
||||
import Block from '../../Block';
|
||||
import { sanitize } from '../../../../utils/names';
|
||||
import InlineComponentWrapper from '../InlineComponent';
|
||||
import create_debugging_comment from '../shared/create_debugging_comment';
|
||||
import { get_slot_definition } from '../shared/get_slot_definition';
|
||||
|
||||
export default function create_slot_block(attribute, element: ElementWrapper | SlotWrapper, block: Block) {
|
||||
const owner = find_slot_owner(element.parent);
|
||||
|
||||
if (owner && owner.node.type === 'InlineComponent') {
|
||||
const name = attribute.get_static_value() as string;
|
||||
|
||||
if (!((owner as unknown) as InlineComponentWrapper).slots.has(name)) {
|
||||
const child_block = block.child({
|
||||
comment: create_debugging_comment(element.node, element.renderer.component),
|
||||
name: element.renderer.component.get_unique_name(
|
||||
`create_${sanitize(name)}_slot`
|
||||
),
|
||||
type: 'slot'
|
||||
});
|
||||
|
||||
const { scope, lets } = element.node;
|
||||
const seen = new Set(lets.map(l => l.name.name));
|
||||
|
||||
((owner as unknown) as InlineComponentWrapper).node.lets.forEach(l => {
|
||||
if (!seen.has(l.name.name)) lets.push(l);
|
||||
});
|
||||
|
||||
((owner as unknown) as InlineComponentWrapper).slots.set(
|
||||
name,
|
||||
get_slot_definition(child_block, scope, lets)
|
||||
);
|
||||
element.renderer.blocks.push(child_block);
|
||||
}
|
||||
|
||||
element.slot_block = ((owner as unknown) as InlineComponentWrapper).slots.get(
|
||||
name
|
||||
).block;
|
||||
|
||||
return element.slot_block;
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
function find_slot_owner(owner) {
|
||||
while (owner) {
|
||||
if (owner.node.type === 'InlineComponent') {
|
||||
break;
|
||||
}
|
||||
|
||||
if (owner.node.type === 'Element' && /-/.test(owner.node.name)) {
|
||||
break;
|
||||
}
|
||||
|
||||
owner = owner.parent;
|
||||
}
|
||||
return owner;
|
||||
}
|
@ -0,0 +1,136 @@
|
||||
import Wrapper from './shared/Wrapper';
|
||||
import Renderer from '../Renderer';
|
||||
import Block from '../Block';
|
||||
import EachBlock from '../../nodes/EachBlock';
|
||||
import KeyBlock from '../../nodes/KeyBlock';
|
||||
import create_debugging_comment from './shared/create_debugging_comment';
|
||||
import FragmentWrapper from './Fragment';
|
||||
import { b, x } from 'code-red';
|
||||
import { Identifier } from 'estree';
|
||||
|
||||
export default class KeyBlockWrapper extends Wrapper {
|
||||
node: KeyBlock;
|
||||
fragment: FragmentWrapper;
|
||||
block: Block;
|
||||
dependencies: string[];
|
||||
var: Identifier = { type: 'Identifier', name: 'key_block' };
|
||||
|
||||
constructor(
|
||||
renderer: Renderer,
|
||||
block: Block,
|
||||
parent: Wrapper,
|
||||
node: EachBlock,
|
||||
strip_whitespace: boolean,
|
||||
next_sibling: Wrapper
|
||||
) {
|
||||
super(renderer, block, parent, node);
|
||||
|
||||
this.cannot_use_innerhtml();
|
||||
this.not_static_content();
|
||||
|
||||
this.dependencies = node.expression.dynamic_dependencies();
|
||||
|
||||
if (this.dependencies.length) {
|
||||
block = block.child({
|
||||
comment: create_debugging_comment(node, renderer.component),
|
||||
name: renderer.component.get_unique_name('create_key_block'),
|
||||
type: 'key'
|
||||
});
|
||||
renderer.blocks.push(block);
|
||||
}
|
||||
|
||||
this.block = block;
|
||||
this.fragment = new FragmentWrapper(
|
||||
renderer,
|
||||
this.block,
|
||||
node.children,
|
||||
this,
|
||||
strip_whitespace,
|
||||
next_sibling
|
||||
);
|
||||
}
|
||||
|
||||
render(block: Block, parent_node: Identifier, parent_nodes: Identifier) {
|
||||
if (this.dependencies.length === 0) {
|
||||
this.render_static_key(block, parent_node, parent_nodes);
|
||||
} else {
|
||||
this.render_dynamic_key(block, parent_node, parent_nodes);
|
||||
}
|
||||
}
|
||||
|
||||
render_static_key(_block: Block, parent_node: Identifier, parent_nodes: Identifier) {
|
||||
this.fragment.render(this.block, parent_node, parent_nodes);
|
||||
}
|
||||
|
||||
render_dynamic_key(block: Block, parent_node: Identifier, parent_nodes: Identifier) {
|
||||
this.fragment.render(
|
||||
this.block,
|
||||
null,
|
||||
(x`#nodes` as unknown) as Identifier
|
||||
);
|
||||
|
||||
const has_transitions = !!(
|
||||
this.block.has_intro_method || this.block.has_outro_method
|
||||
);
|
||||
const dynamic = this.block.has_update_method;
|
||||
|
||||
const previous_key = block.get_unique_name('previous_key');
|
||||
const snippet = this.node.expression.manipulate(block);
|
||||
block.add_variable(previous_key, snippet);
|
||||
|
||||
const not_equal = this.renderer.component.component_options.immutable ? x`@not_equal` : x`@safe_not_equal`;
|
||||
const condition = x`${this.renderer.dirty(this.dependencies)} && ${not_equal}(${previous_key}, ${previous_key} = ${snippet})`;
|
||||
|
||||
block.chunks.init.push(b`
|
||||
let ${this.var} = ${this.block.name}(#ctx);
|
||||
`);
|
||||
block.chunks.create.push(b`${this.var}.c();`);
|
||||
if (this.renderer.options.hydratable) {
|
||||
block.chunks.claim.push(b`${this.var}.l(${parent_nodes});`);
|
||||
}
|
||||
block.chunks.mount.push(
|
||||
b`${this.var}.m(${parent_node || '#target'}, ${
|
||||
parent_node ? 'null' : '#anchor'
|
||||
});`
|
||||
);
|
||||
const anchor = this.get_or_create_anchor(block, parent_node, parent_nodes);
|
||||
const body = b`
|
||||
${
|
||||
has_transitions
|
||||
? b`
|
||||
@group_outros();
|
||||
@transition_out(${this.var}, 1, 1, @noop);
|
||||
@check_outros();
|
||||
`
|
||||
: b`${this.var}.d(1);`
|
||||
}
|
||||
${this.var} = ${this.block.name}(#ctx);
|
||||
${this.var}.c();
|
||||
${has_transitions && b`@transition_in(${this.var})`}
|
||||
${this.var}.m(${this.get_update_mount_node(anchor)}, ${anchor});
|
||||
`;
|
||||
|
||||
if (dynamic) {
|
||||
block.chunks.update.push(b`
|
||||
if (${condition}) {
|
||||
${body}
|
||||
} else {
|
||||
${this.var}.p(#ctx, #dirty);
|
||||
}
|
||||
`);
|
||||
} else {
|
||||
block.chunks.update.push(b`
|
||||
if (${condition}) {
|
||||
${body}
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
if (has_transitions) {
|
||||
block.chunks.intro.push(b`@transition_in(${this.var})`);
|
||||
block.chunks.outro.push(b`@transition_out(${this.var})`);
|
||||
}
|
||||
|
||||
block.chunks.destroy.push(b`${this.var}.d(detaching)`);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue