merge master

pull/2684/head
Anton Samoylov 6 years ago
commit 8cca87cb75

@ -1,7 +1,14 @@
src/shared
shared.js
store.js
test/test.js
test/setup.js
**/_actual.js **/_actual.js
**/expected.js **/expected.js
test/*/samples/*/output.js
node_modules
# output files
animate/*.js
esing/*.js
internal/*.js
motion/*.js
store/*.js
transition/*.js
index.js
compiler.js

@ -0,0 +1,99 @@
module.exports = {
root: true,
rules: {
indent: 'off',
'no-unused-vars': 'off',
semi: [2, 'always'],
'keyword-spacing': [2, { before: true, after: true }],
'space-before-blocks': [2, 'always'],
'no-mixed-spaces-and-tabs': [2, 'smart-tabs'],
'no-cond-assign': 0,
'object-shorthand': [2, 'always'],
'no-const-assign': 2,
'no-class-assign': 2,
'no-this-before-super': 2,
'no-var': 2,
'no-unreachable': 2,
'valid-typeof': 2,
'quote-props': [2, 'as-needed'],
'one-var': [2, 'never'],
'prefer-arrow-callback': 2,
'prefer-const': [2, { destructuring: 'all' }],
'arrow-spacing': 2,
'no-inner-declarations': 0,
'require-atomic-updates': 'off',
'@typescript-eslint/indent': [
'error',
'tab',
{
SwitchCase: 1,
ignoredNodes: ['TemplateLiteral']
}
],
'@typescript-eslint/camelcase': 'off',
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/array-type': ['error', 'array-simple'],
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/explicit-member-accessibility': 'off',
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^_'
}
],
'@typescript-eslint/no-object-literal-type-assertion': [
'error',
{
allowAsParameter: true
}
],
'@typescript-eslint/no-unused-vars': 'off'
},
globals: {
globalThis: false
},
env: {
es6: true,
browser: true,
node: true,
mocha: true
},
extends: [
'eslint:recommended',
'plugin:import/errors',
'plugin:import/warnings',
'plugin:import/typescript',
'plugin:@typescript-eslint/recommended'
],
parserOptions: {
ecmaVersion: 9,
sourceType: 'module'
},
plugins: ['svelte3'],
settings: {
'import/core-modules': [
'svelte',
'svelte/internal',
'svelte/store',
'svelte/easing',
'estree'
],
'svelte3/compiler': require('./compiler')
},
overrides: [
{
files: ['*.js'],
rules: {
'@typescript-eslint/no-var-requires': 'off'
}
},
{
files: ['*.svelte'],
processor: 'svelte3/svelte3',
rules: {
'@typescript-eslint/indent': 'off'
}
}
]
};

@ -1,44 +0,0 @@
{
"root": true,
"rules": {
"indent": [2, "tab", { "SwitchCase": 1 }],
"semi": [2, "always"],
"keyword-spacing": [2, { "before": true, "after": true }],
"space-before-blocks": [2, "always"],
"no-mixed-spaces-and-tabs": [2, "smart-tabs"],
"no-cond-assign": 0,
"no-unused-vars": 2,
"object-shorthand": [2, "always"],
"no-const-assign": 2,
"no-class-assign": 2,
"no-this-before-super": 2,
"no-var": 2,
"no-unreachable": 2,
"valid-typeof": 2,
"quote-props": [2, "as-needed"],
"one-var": [2, "never"],
"prefer-arrow-callback": 2,
"prefer-const": [2, { "destructuring": "all" }],
"arrow-spacing": 2,
"no-inner-declarations": 0
},
"env": {
"es6": true,
"browser": true,
"node": true,
"mocha": true
},
"extends": [
"eslint:recommended",
"plugin:import/errors",
"plugin:import/warnings"
],
"parserOptions": {
"ecmaVersion": 9,
"sourceType": "module"
},
"settings": {
"import/core-modules": ["svelte"],
"svelte3/extensions": ["html"]
}
}

@ -1,9 +0,0 @@
[ignore]
<PROJECT_ROOT>/dist/.*
[include]
[libs]
[options]
strip_root=true

1
.gitattributes vendored

@ -1 +0,0 @@
*.svelte linguist-language=HTML

@ -1,21 +1,26 @@
<!-- ------
Thanks for raising an issue! (For *questions*, we recommend instead using https://stackoverflow.com and adding the 'svelte' tag.) Before filing an issue we'd appreciate it if you could take a moment to ensure
there isn't already an open issue or pull-request.
-----
To help us help you, if you've found a bug please consider the following: If there's an existing issue, please add a :+1: reaction to the description of
the issue. One way we prioritize issues is by the number of :+1: reactions on
their descriptions. Please DO NOT add `+1` or :+1: comments.
* If you can demonstrate the bug using https://svelte.dev/repl, please do. ### Feature requests and proposals
* If that's not possible, we recommend creating a small repo that illustrates the problem. We're excited to hear how we can make Svelte better. Please add as much detail
* Make sure you include information about the browser, and which version of Svelte you're using as you can on your use case.
Reproductions should be small, self-contained, correct examples http://sscce.org. ### Bugs
If you're filing an issue about a bug please include as much information
as you can including the following.
Occasionally, this won't be possible, and that's fine we still appreciate you raising the issue. But please understand that Svelte is run by unpaid volunteers in their free time, and issues that follow these instructions will get fixed faster. - Your browser and the version: (e.x. Chrome 52.1, Firefox 48.0, IE 10)
- Your operating system: (e.x. OS X 10, Windows XP, etc)
- Svelte version (Please check you can reproduce the issue with the latest release!)
- Whether your project uses Webpack or Rollup
If you have a stack trace to include, we recommend putting inside a `<details>` block for the sake of the thread's readability: - *Repeatable steps to reproduce the issue*
<details> Thanks for being part of Svelte!
<summary>Stack trace</summary> -------
Stack trace goes here...
</details>
-->

@ -0,0 +1,52 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: 'Bug'
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**Logs**
Please include browser console and server logs around the time this bug occurred.
**To Reproduce**
To help us help you, if you've found a bug please consider the following:
* If you can demonstrate the bug using https://svelte.dev/repl, please do.
* If that's not possible, we recommend creating a small repo that illustrates the problem.
* Reproductions should be small, self-contained, correct examples http://sscce.org.
Occasionally, this won't be possible, and that's fine we still appreciate you raising the issue. But please understand that Svelte is run by unpaid volunteers in their free time, and issues that follow these instructions will get fixed faster.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Stacktraces**
If you have a stack trace to include, we recommend putting inside a `<details>` block for the sake of the thread's readability:
<details>
<summary>Stack trace</summary>
Stack trace goes here...
</details>
**Information about your Svelte project:**
- Your browser and the version: (e.x. Chrome 52.1, Firefox 48.0, IE 10)
- Your operating system: (e.x. OS X 10, Ubuntu Linux 19.10, Windows XP, etc)
- Svelte version (Please check you can reproduce the issue with the latest release!)
- Whether your project uses Webpack or Rollup
**Severity**
How severe an issue is this bug to you? Is this annoying, blocking some users, blocking an upgrade or blocking your usage of Svelte entirely?
Note: the more honest and specific you are here the more we will take you seriously.
**Additional context**
Add any other context about the problem here.

@ -0,0 +1,23 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: 'New Feature'
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. For example: I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**How important is this feature to you?**
Note: the more honest and specific you are here the more we will take you seriously.
**Additional context**
Add any other context or screenshots about the feature request here.

@ -0,0 +1,12 @@
---
name: Questions and help
about: If you think you need help with something related to Svelte
title: ''
labels: 'Question'
assignees: ''
---
This issue tracker is intended to collect bug reports and feature requests.
For help with installation, information on how features work, or questions about specific features of Svelte, please come and join us in the [Svelte Discord](https://svelte.dev/chat), or ask your question on [Stack Overflow](https://stackoverflow.com/questions/tagged/svelte). Any issues open for help requests will be closed to keep from clogging up the issue tracker.

@ -1,7 +1,8 @@
<!--
Thank you for creating a pull request. Before submitting, please note the following:
* If your pull request implements a new feature, please raise an issue to discuss it before sending code. In many cases features are absent for a reason.
* This message body should clearly illustrate what problems it solves. If there are related issues, remember to reference them. ### Before submitting the PR, please make sure you do the following
* Ideally, include a test that fails without this PR but passes with it. PRs will only be merged once they pass CI. (Remember to `npm run lint`!) - [ ] It's really useful if your PR relates to an outstanding issue, so please reference it in your PR, or create an explanatory one for discussion. In many cases features are absent for a reason.
--> - [ ] This message body should clearly illustrate what problems it solves. If there are related issues, remember to reference them.
- [ ] Ideally, include a test that fails without this PR but passes with it. PRs will only be merged once they pass CI. (Remember to `npm run lint`!)
### Tests
- [ ] Run the tests tests with `npm test` or `yarn test`)

22
.gitignore vendored

@ -1,16 +1,18 @@
.idea
.DS_Store .DS_Store
.nyc_output .nyc_output
node_modules node_modules
*.map *.map
/src/compile/internal-exports.ts /src/compiler/compile/internal_exports.ts
/compiler.js /compiler.d.ts
/index.js /compiler.*js
/internal.* /index.*js
/store.js /internal
/easing.js /store
/motion.* /easing
/transition.js /motion
/animate.js /transition
/animate
/scratch/ /scratch/
/coverage/ /coverage/
/coverage.lcov/ /coverage.lcov/
@ -20,6 +22,7 @@ node_modules
/test/sourcemaps/samples/*/output.css.map /test/sourcemaps/samples/*/output.css.map
/yarn-error.log /yarn-error.log
_actual*.* _actual*.*
/types
/site/cypress/screenshots/ /site/cypress/screenshots/
/site/__sapper__/ /site/__sapper__/
@ -27,5 +30,6 @@ _actual*.*
/site/.sessions /site/.sessions
/site/static/svelte-app.json /site/static/svelte-app.json
/site/static/contributors.jpg /site/static/contributors.jpg
/site/static/workers
/site/scripts/svelte-app /site/scripts/svelte-app
/site/src/routes/_contributors.js /site/src/routes/_contributors.js

@ -1,7 +1,8 @@
language: node_js language: node_js
node_js: node_js:
- "node" - "8"
- "10"
- "12"
env: env:
global: global:
- BUILD_TIMEOUT=20000 - BUILD_TIMEOUT=20000

@ -1,5 +1,173 @@
# Svelte changelog # Svelte changelog
## 3.6.9
* Always update derived stores with a derived input whose value does not change ([#3191](https://github.com/sveltejs/svelte/issues/3191))
## 3.6.8
* Preserve global keyframes that don't match local elements ([#3228](https://github.com/sveltejs/svelte/issues/3228))
* Fix spread/`class:` combination ([#3242](https://github.com/sveltejs/svelte/pull/3242))
* Never scope `:root` selector ([#3250](https://github.com/sveltejs/svelte/pull/3250))
* Prevent trailing commas in function arguments ([#3255](https://github.com/sveltejs/svelte/pull/3260))
## 3.6.7
* Prevent corruption of outro callbacks with nested keyed each blocks ([#3209](https://github.com/sveltejs/svelte/pull/3209))
* Prevent cursor jumping in bound input in Safari ([#3199](https://github.com/sveltejs/svelte/issues/3199))
* Make resize listener object unfocusable ([#3206](https://github.com/sveltejs/svelte/issues/3206))
## 3.6.6
* Prevent dynamic components being detached twice ([#3113](https://github.com/sveltejs/svelte/issues/3113), [#2086](https://github.com/sveltejs/svelte/issues/2086))
## 3.6.5
* Handle RxJS-style observables with `get` ([#3153](https://github.com/sveltejs/svelte/issues/3153))
* Pass `let` values to bindings ([#3140](https://github.com/sveltejs/svelte/issues/3140))
* Escape `@` symbols in props ([#3173](https://github.com/sveltejs/svelte/issues/3173))
* Scale crossfaded elements ([#3175](https://github.com/sveltejs/svelte/pull/3175))
## 3.6.4
* Run `onMount` functions in correct order, and before initial `afterUpdate` functions ([#2281](https://github.com/sveltejs/svelte/issues/2281))
* Fix code transformation for shorthand methods ([#2906](https://github.com/sveltejs/svelte/issues/2906))
* Fix assignments in inline functions ([#3038](https://github.com/sveltejs/svelte/issues/3038))
## 3.6.3
* Fix await block mounting inside removed if block ([#1496](https://github.com/sveltejs/svelte/issues/1496))
* Update when element references are removed ([#2034](https://github.com/sveltejs/svelte/issues/2034))
* Don't attempt to serialize non-string values in server-rendered bindings ([#2135](https://github.com/sveltejs/svelte/issues/2135))
* Recognise dependencies in function expressions ([#2693](https://github.com/sveltejs/svelte/issues/2693))
* Scope pseudo-class selectors without class/type ([#1705](https://github.com/sveltejs/svelte/issues/1705))
* Allow nested at-rules ([#3135](https://github.com/sveltejs/svelte/issues/3135))
* Allow attributes to contain `=` characters ([#3149](https://github.com/sveltejs/svelte/pull/3149))
## 3.6.2
* Fix placement of each-else block ([#2917](https://github.com/sveltejs/svelte/issues/2917))
* Make context accessible to `bind:this` ([#2806](https://github.com/sveltejs/svelte/issues/2806))
* Pass hoisted values to slots ([#2586](https://github.com/sveltejs/svelte/issues/2586))
## 3.6.1
* Fix escaping of `@` in dev mode debug filename ([#3114](https://github.com/sveltejs/svelte/pull/3114))
## 3.6.0
* Add `innerHTML` and `textContent` bindings for `contenteditable` elements ([#2996](https://github.com/sveltejs/svelte/pull/2996))
* Fix destructuring assignments where targets are member expressions ([#3092](https://github.com/sveltejs/svelte/issues/3092))
* Deconflict with used globals ([#2963](https://github.com/sveltejs/svelte/pull/2963))
* Always run `onDestroy` functions, not just for detaching components ([#3058](https://github.com/sveltejs/svelte/issues/3058))
* Fix scope analysis around catch clauses ([#3064](https://github.com/sveltejs/svelte/issues/3064))
* Add error constructors to known globals ([#3064](https://github.com/sveltejs/svelte/issues/3064))
* Always bail out of hoisting on encountering local state in function definition ([#3044](https://github.com/sveltejs/svelte/issues/3044))
* Fix incorrect merging of top-level text nodes ([#3027](https://github.com/sveltejs/svelte/issues/3027))
* Handle removal of components in each blocks without props ([#3035](https://github.com/sveltejs/svelte/issues/3035))
* Only call subscriber once when resubscribing to a store ([#3022](https://github.com/sveltejs/svelte/issues/3022))
* Check for existence of dynamic component before introing ([#3054](https://github.com/sveltejs/svelte/issues/3054))
* Sanitize names of bubbled event handlers ([#2923](https://github.com/sveltejs/svelte/issues/2923))
## 3.5.4
* Preserve whitespace at the boundaries of `{#each}` blocks ([#713](https://github.com/sveltejs/svelte/issues/713))
* Fix dynamic `bind:this` on components ([#2333](https://github.com/sveltejs/svelte/issues/2333))
* Fix binding to values in a component when it uses `$$props` ([#2725](https://github.com/sveltejs/svelte/issues/2725))
* Fix parsing ambiguous HTML entities ([#3071](https://github.com/sveltejs/svelte/pull/3071))
## 3.5.3
* Don't double-destroy keyed each blocks with outros ([#3055](https://github.com/sveltejs/svelte/issues/3055))
## 3.5.2
* Prevent duplicated outros causing errors ([#3001](https://github.com/sveltejs/svelte/issues/3001))
* Fix automatic name generation ([#2843](https://github.com/sveltejs/svelte/issues/2843))
* Fix .d.ts stubs ([#3009](https://github.com/sveltejs/svelte/pull/3009))
* Don't strip non-breaking spaces ([#3014](https://github.com/sveltejs/svelte/issues/3014))
* Fix `requestAnimationFrame` context ([#2933](https://github.com/sveltejs/svelte/issues/2933))
* Allow space before attribute value ([#3026](https://github.com/sveltejs/svelte/issues/3026))
* Remove null/undefined attributes ([#1434](https://github.com/sveltejs/svelte/issues/1434))
* Fix whitespace in static markup ([#3030](https://github.com/sveltejs/svelte/pull/3030))
## 3.5.1
* Accommodate webpack idiosyncracies
## 3.5.0
* Update package folder structure ([#2887](https://github.com/sveltejs/svelte/pull/2887))
* Support `once` modifier on component events ([#2654](https://github.com/sveltejs/svelte/issues/2654))
* Allow empty `<title>` tags ([#2980](https://github.com/sveltejs/svelte/issues/2980))
* Render textarea binding values inside element ([#2975](https://github.com/sveltejs/svelte/pull/2975))
* Fix delayed animation glitch ([#2871](https://github.com/sveltejs/svelte/issues/2871))
* Solve diamond dependencies problem with stores ([#2660](https://github.com/sveltejs/svelte/issues/2660))
* Fix missing outros inside each blocks ([#2689](https://github.com/sveltejs/svelte/issues/2689))
* Support animations without transitions ([#2908](https://github.com/sveltejs/svelte/issues/2908))
* Add missing transition events ([#2912](https://github.com/sveltejs/svelte/pull/2912))
## 3.4.4
* Publish type declaration files ([#2874](https://github.com/sveltejs/svelte/issues/2874))
* Don't trigger updates for unreferenced values ([#2865](https://github.com/sveltejs/svelte/pull/2865))
* Omit readonly bindings from SSR output ([#2339](https://github.com/sveltejs/svelte/issues/2339))
* Prevent outdated animation CSS ([#2871](https://github.com/sveltejs/svelte/issues/2871))
* Repair dynamic `{@html ...}` in head ([#2880](https://github.com/sveltejs/svelte/pull/2880))
* Don't create unknown prop warnings for internal props, or if component has `$$props` ([#2881](https://github.com/sveltejs/svelte/pull/2881))
## 3.4.3
* Add type declaration files for everything ([#2842](https://github.com/sveltejs/svelte/pull/2842))
* Prevent `svelte/store` being bundled ([#2786](https://github.com/sveltejs/svelte/issues/2786))
* Warn on unknown props in dev mode ([#2840](https://github.com/sveltejs/svelte/pull/2840))
* Treat `requestAnimationFrame` as a no-op on the server ([#2856](https://github.com/sveltejs/svelte/pull/2856))
* Add `raw` property to AST's `Text` nodes ([#2714](https://github.com/sveltejs/svelte/issues/2714))
* Add `<details bind:open>` ([#2854](https://github.com/sveltejs/svelte/issues/2854))
## 3.4.2
* Use empty string for empty data attributes ([#2804](https://github.com/sveltejs/svelte/pull/2804))
* Support `customElement: true` with no `<svelte:options>` ([#2821](https://github.com/sveltejs/svelte/issues/2821))
* Add docstrings to `svelte/store` ([#2795](https://github.com/sveltejs/svelte/pull/2795))
## 3.4.1
* Handle non-falsy non-function return values from derivers ([#2780](https://github.com/sveltejs/svelte/issues/2780))
* Allow `spring` to work server-side ([#2773](https://github.com/sveltejs/svelte/issues/2773))
## 3.4.0
* Allow custom element to be defined without a `tag` ([#2417](https://github.com/sveltejs/svelte/issues/2417))
* Fix parsing of quote marks inside attribute values ([#2715](https://github.com/sveltejs/svelte/pull/2754))
* Convert `svelte/store` to TypeScript ([#2733](https://github.com/sveltejs/svelte/pull/2733))
* Allow `debug` tags to include hoisted values ([#2764](https://github.com/sveltejs/svelte/issues/2764))
* Parse error if attribute name is missing `=` ([#1513](https://github.com/sveltejs/svelte/pull/2770))
* Allow reactive declarations to depend on mutated `const` values ([#2728](https://github.com/sveltejs/svelte/issues/2728))
## 3.3.0
* Allow multiple event listeners on a single node ([#2688](https://github.com/sveltejs/svelte/issues/2688))
* Allow derivers to return a cleanup function ([#2553](https://github.com/sveltejs/svelte/issues/2553))
* Support namespaced components (`<Foo.Bar/>`) ([#2743](https://github.com/sveltejs/svelte/pull/2743))
## 3.2.2
* Add `window` and `document` to expected globals ([#2722](https://github.com/sveltejs/svelte/pull/2722))
* Prevent hoisting of functions that depend on reactive state ([#2703](https://github.com/sveltejs/svelte/pull/2703))
* Generate correct code when slot has no changes ([#2697](https://github.com/sveltejs/svelte/issues/2697))
* Prevent `Object.prototype`-related bugs ([#2696](https://github.com/sveltejs/svelte/pull/2696))
## 3.2.1
* Use same comparison logic for `derived` as for other stores ([#2644](https://github.com/sveltejs/svelte/issues/2644))
* Invalidate dependencies of reactive declarations ([#2444](https://github.com/sveltejs/svelte/issues/2444))
* Fix instrumentation of auto-subscription self-assignments ([#2681](https://github.com/sveltejs/svelte/issues/2681))
* Warn on non-top-level or module-context statements labeled with `$:` ([#2176](https://github.com/sveltejs/svelte/issues/2176))
## 3.2.0 ## 3.2.0
* Improve `spring` animations, and add `hard`/`soft` options ([#2627](https://github.com/sveltejs/svelte/pull/2627)) * Improve `spring` animations, and add `hard`/`soft` options ([#2627](https://github.com/sveltejs/svelte/pull/2627))

@ -1,7 +1,24 @@
<p align="center"> <p>
<a href="https://svelte.dev"> <a href="https://svelte.dev">
<img alt="Cybernetically enhanced web apps: Svelte" src="https://svelte-assets.surge.sh/banner.png"> <img alt="Cybernetically enhanced web apps: Svelte" src="https://svelte-assets.surge.sh/banner.png">
</a> </a>
<a href="https://www.npmjs.com/package/svelte">
<img src="https://img.shields.io/npm/v/svelte.svg" alt="npm version">
</a>
<a href="https://packagephobia.now.sh/result?p=svelte">
<img src="https://packagephobia.now.sh/badge?p=svelte" alt="install size">
</a>
<a href="https://travis-ci.org/sveltejs/svelte">
<img src="https://api.travis-ci.org/sveltejs/svelte.svg?branch=master"
alt="build status">
</a>
<a href="https://github.com/sveltejs/svelte/blob/master/LICENSE">
<img src="https://img.shields.io/npm/l/svelte.svg" alt="license">
</a>
</p> </p>
@ -9,7 +26,7 @@
Svelte is a new way to build web applications. It's a compiler that takes your declarative components and converts them into efficient JavaScript that surgically updates the DOM. Svelte is a new way to build web applications. It's a compiler that takes your declarative components and converts them into efficient JavaScript that surgically updates the DOM.
Learn more at the [Svelte website](https://svelte.dev), or stop by the [Discord chatroom](https://discord.gg/yy75DKs). Learn more at the [Svelte website](https://svelte.dev), or stop by the [Discord chatroom](https://svelte.dev/chat).
## Development ## Development
@ -19,11 +36,16 @@ Pull requests are encouraged and always welcome. [Pick an issue](https://github.
To install and work on Svelte locally: To install and work on Svelte locally:
```bash ```bash
git clone git@github.com:sveltejs/svelte.git git clone https://github.com/sveltejs/svelte.git
cd svelte cd svelte
npm install npm install
``` ```
> Many tests depend on newlines being preserved as `<LF>`. On Windows, you can ensure this by cloning with:
> ```bash
> git -c core.autocrlf=false clone https://github.com/sveltejs/svelte.git
> ```
To build the compiler, and all the other modules included in the package: To build the compiler, and all the other modules included in the package:
```bash ```bash
@ -36,7 +58,7 @@ To watch for changes and continually rebuild the package (this is useful if you'
npm run dev npm run dev
``` ```
The compiler is written in [TypeScript](https://www.typescriptlang.org/), but don't let that put you off — it's basically just JavaScript with type annotations. You'll pick it up in no time. If you're using an editor other than [VSCode](https://code.visualstudio.com/) you may need to install a plugin in order to get syntax highlighting and code hints etc. The compiler is written in [TypeScript](https://www.typescriptlang.org/), but don't let that put you off — it's basically just JavaScript with type annotations. You'll pick it up in no time. If you're using an editor other than [Visual Studio Code](https://code.visualstudio.com/) you may need to install a plugin in order to get syntax highlighting and code hints etc.
### Running Tests ### Running Tests

@ -1,25 +0,0 @@
import { cubicOut } from './easing';
import { is_function } from './internal';
export function flip(node, animation, params) {
const style = getComputedStyle(node);
const transform = style.transform === 'none' ? '' : style.transform;
const dx = animation.from.left - animation.to.left;
const dy = animation.from.top - animation.to.top;
const d = Math.sqrt(dx * dx + dy * dy);
const {
delay = 0,
duration = d => Math.sqrt(d) * 120,
easing = cubicOut
} = params;
return {
delay,
duration: is_function(duration) ? duration(d) : duration,
easing,
css: (t, u) => `transform: ${transform} translate(${u * dx}px, ${u * dy}px);`
};
}

@ -9,12 +9,13 @@ init:
environment: environment:
matrix: matrix:
# node.js - nodejs_version: 8
- nodejs_version: 10 - nodejs_version: 10
- nodejs_version: 12
install: install:
- ps: Install-Product node $env:nodejs_version - ps: Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version)
- npm install - npm ci || npm install
build: off build: off

3670
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,37 +1,43 @@
{ {
"name": "svelte", "name": "svelte",
"version": "3.2.0", "version": "3.6.9",
"description": "Cybernetically enhanced web apps", "description": "Cybernetically enhanced web apps",
"module": "index.mjs", "module": "index.mjs",
"main": "index", "main": "index",
"files": [ "files": [
"compiler.js", "types",
"compiler.*",
"register.js", "register.js",
"index.*", "index.*",
"internal.*", "internal",
"store.*", "store",
"animate.*", "animate",
"transition.*", "transition",
"easing.*", "easing",
"motion.*", "motion",
"svelte", "svelte",
"README.md" "README.md"
], ],
"engines": {
"node": ">= 8"
},
"types": "types/runtime/index.d.ts",
"scripts": { "scripts": {
"test": "mocha --opts mocha.opts", "test": "mocha --opts mocha.opts",
"test:unit": "mocha --require sucrase/register --recursive ./**/__test__.ts", "test:unit": "mocha --require sucrase/register --recursive src/**/__test__.ts",
"quicktest": "mocha --opts mocha.opts", "quicktest": "mocha --opts mocha.opts",
"precoverage": "c8 mocha --opts mocha.coverage.opts", "precoverage": "c8 mocha --opts mocha.coverage.opts",
"coverage": "c8 report --reporter=text-lcov > coverage.lcov && c8 report --reporter=html", "coverage": "c8 report --reporter=text-lcov > coverage.lcov && c8 report --reporter=html",
"codecov": "codecov", "codecov": "codecov",
"precodecov": "npm run coverage", "precodecov": "npm run coverage",
"lint": "eslint src test/*.js", "build": "rollup -c && npm run tsd",
"build": "rollup -c",
"prepare": "npm run build", "prepare": "npm run build",
"dev": "rollup -cw", "dev": "rollup -cw",
"pretest": "npm run build", "pretest": "npm run build",
"posttest": "agadoo src/internal/index.js", "posttest": "agadoo internal/index.mjs",
"prepublishOnly": "export PUBLISH=true && npm run lint && npm test" "prepublishOnly": "npm run lint && PUBLISH=true npm test",
"tsd": "tsc -p src/compiler --emitDeclarationOnly && tsc -p src/runtime --emitDeclarationOnly",
"lint": "eslint \"{src,test}/**/*.{ts,js}\""
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -50,40 +56,41 @@
}, },
"homepage": "https://github.com/sveltejs/svelte#README", "homepage": "https://github.com/sveltejs/svelte#README",
"devDependencies": { "devDependencies": {
"@sveltejs/svelte-repl": "0.0.5", "@types/mocha": "^5.2.7",
"@types/mocha": "^5.2.0", "@types/node": "=8",
"@types/node": "^10.5.5", "@typescript-eslint/eslint-plugin": "^1.11.0",
"acorn": "^6.1.1", "@typescript-eslint/parser": "^1.11.0",
"acorn": "^6.2.0",
"acorn-dynamic-import": "^4.0.0", "acorn-dynamic-import": "^4.0.0",
"agadoo": "^1.0.1", "agadoo": "^1.0.1",
"c8": "^3.4.0", "c8": "^5.0.1",
"codecov": "^3.0.0", "codecov": "^3.5.0",
"css-tree": "1.0.0-alpha22", "css-tree": "1.0.0-alpha22",
"eslint": "^5.3.0", "eslint": "^6.0.1",
"eslint-plugin-html": "^5.0.0", "eslint-plugin-import": "^2.18.0",
"eslint-plugin-import": "^2.11.0", "eslint-plugin-svelte3": "^2.6.0",
"estree-walker": "^0.6.0", "estree-walker": "^0.6.1",
"is-reference": "^1.1.1", "is-reference": "^1.1.3",
"jsdom": "^12.2.0", "jsdom": "^15.1.1",
"kleur": "^3.0.0", "kleur": "^3.0.3",
"locate-character": "^2.0.5", "locate-character": "^2.0.5",
"magic-string": "^0.25.2", "magic-string": "^0.25.3",
"mocha": "^5.2.0", "mocha": "^6.1.4",
"puppeteer": "^1.13.0", "puppeteer": "^1.18.1",
"rollup": "^1.1.2", "rollup": "^1.16.6",
"rollup-plugin-commonjs": "^9.1.0", "rollup-plugin-commonjs": "^10.0.1",
"rollup-plugin-json": "^3.0.0", "rollup-plugin-json": "^4.0.0",
"rollup-plugin-node-resolve": "^4.0.0", "rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-replace": "^2.0.0", "rollup-plugin-replace": "^2.2.0",
"rollup-plugin-sucrase": "^2.1.0", "rollup-plugin-sucrase": "^2.1.0",
"rollup-plugin-typescript": "^1.0.0", "rollup-plugin-typescript": "^1.0.1",
"rollup-plugin-virtual": "^1.0.1", "rollup-plugin-virtual": "^1.0.1",
"source-map": "0.6", "source-map": "^0.6.1",
"source-map-support": "^0.5.4", "source-map-support": "^0.5.12",
"tiny-glob": "^0.2.1", "tiny-glob": "^0.2.6",
"ts-node": "^8.0.2", "ts-node": "^8.3.0",
"tslib": "^1.8.0", "tslib": "^1.10.0",
"typescript": "^3.0.1" "typescript": "^3.5.2"
}, },
"nyc": { "nyc": {
"include": [ "include": [

@ -2,7 +2,7 @@ const fs = require('fs');
const path = require('path'); const path = require('path');
const { compile } = require('./compiler.js'); const { compile } = require('./compiler.js');
let extensions = ['.svelte', '.html']; const extensions = ['.svelte', '.html'];
let compileOptions = {}; let compileOptions = {};
function capitalise(name) { function capitalise(name) {
@ -25,8 +25,7 @@ function deregisterExtension(extension) {
function registerExtension(extension) { function registerExtension(extension) {
require.extensions[extension] = function(module, filename) { require.extensions[extension] = function(module, filename) {
const name = path.basename(filename) const name = path.parse(filename).name
.slice(0, -path.extname(filename).length)
.replace(/^\d/, '_$&') .replace(/^\d/, '_$&')
.replace(/[^a-zA-Z0-9_$]/g, ''); .replace(/[^a-zA-Z0-9_$]/g, '');

@ -9,36 +9,82 @@ import pkg from './package.json';
const is_publish = !!process.env.PUBLISH; const is_publish = !!process.env.PUBLISH;
const ts_plugin = is_publish
? typescript({
include: 'src/**',
typescript: require('typescript')
})
: sucrase({
transforms: ['typescript']
});
const external = id => id.startsWith('svelte/');
fs.writeFileSync(`./compiler.d.ts`, `export * from './types/compiler/index';`);
export default [ export default [
/* internal.[m]js */ /* runtime */
{
input: `src/runtime/index.ts`,
output: [
{
file: `index.mjs`,
format: 'esm',
paths: id => id.startsWith('svelte/') && `${id.replace('svelte', '.')}`
},
{ {
input: `src/internal/index.js`, file: `index.js`,
format: 'cjs',
paths: id => id.startsWith('svelte/') && `${id.replace('svelte', '.')}`
}
],
external,
plugins: [ts_plugin]
},
...fs.readdirSync('src/runtime')
.filter(dir => fs.statSync(`src/runtime/${dir}`).isDirectory())
.map(dir => ({
input: `src/runtime/${dir}/index.ts`,
output: [ output: [
{ {
file: `internal.mjs`, file: `${dir}/index.mjs`,
format: 'esm', format: 'esm',
paths: id => id.startsWith('svelte/') && id.replace('svelte', '.') paths: id => id.startsWith('svelte/') && `${id.replace('svelte', '..')}`
}, },
{ {
file: `internal.js`, file: `${dir}/index.js`,
format: 'cjs', format: 'cjs',
paths: id => id.startsWith('svelte/') && id.replace('svelte', '.') paths: id => id.startsWith('svelte/') && `${id.replace('svelte', '..')}`
} }
], ],
external: id => id.startsWith('svelte/'), external,
plugins: [{ plugins: [
generateBundle(options, bundle) { ts_plugin,
const mod = bundle['internal.mjs']; {
writeBundle(bundle) {
if (dir === 'internal') {
const mod = bundle['index.mjs'];
if (mod) { if (mod) {
fs.writeFileSync('src/compile/internal-exports.ts', `// This file is automatically generated\nexport default new Set(${JSON.stringify(mod.exports)});`); fs.writeFileSync('src/compiler/compile/internal_exports.ts', `// This file is automatically generated\nexport default new Set(${JSON.stringify(mod.exports)});`);
} }
} }
}]
}, fs.writeFileSync(`${dir}/package.json`, JSON.stringify({
main: './index',
module: './index.mjs',
types: './index.d.ts'
}, null, ' '));
fs.writeFileSync(`${dir}/index.d.ts`, `export * from '../types/runtime/${dir}/index';`);
}
}
]
})),
/* compiler.js */ /* compiler.js */
{ {
input: 'src/index.ts', input: 'src/compiler/index.ts',
plugins: [ plugins: [
replace({ replace({
__VERSION__: pkg.version __VERSION__: pkg.version
@ -48,15 +94,7 @@ export default [
include: ['node_modules/**'] include: ['node_modules/**']
}), }),
json(), json(),
is_publish ts_plugin
? typescript({
include: 'src/**',
exclude: 'src/internal/**',
typescript: require('typescript')
})
: sucrase({
transforms: ['typescript']
})
], ],
output: { output: {
file: 'compiler.js', file: 'compiler.js',
@ -67,34 +105,5 @@ export default [
external: is_publish external: is_publish
? [] ? []
: id => id === 'acorn' || id === 'magic-string' || id.startsWith('css-tree') : id => id === 'acorn' || id === 'magic-string' || id.startsWith('css-tree')
},
/* motion.mjs */
{
input: `src/motion/index.js`,
output: [
{
file: `motion.mjs`,
format: 'esm',
paths: id => id.startsWith('svelte/') && id.replace('svelte', '.')
},
{
file: `motion.js`,
format: 'cjs',
paths: id => id.startsWith('svelte/') && id.replace('svelte', '.')
} }
],
external: id => id.startsWith('svelte/')
},
// everything else
...['index', 'store', 'easing', 'transition', 'animate'].map(name => ({
input: `${name}.mjs`,
output: {
file: `${name}.js`,
format: 'cjs',
paths: id => id.startsWith('svelte/') && id.replace('svelte', '.')
},
external: id => id !== `${name}.mjs`
}))
]; ];

@ -0,0 +1,52 @@
module.exports = {
root: true,
rules: {
indent: [2, 'tab', { SwitchCase: 1 }],
semi: [2, 'always'],
'keyword-spacing': [2, { before: true, after: true }],
'space-before-blocks': [2, 'always'],
'no-mixed-spaces-and-tabs': [2, 'smart-tabs'],
'no-cond-assign': 0,
'no-unused-vars': 2,
'object-shorthand': [2, 'always'],
'no-const-assign': 2,
'no-class-assign': 2,
'no-this-before-super': 2,
'no-var': 2,
'no-unreachable': 2,
'valid-typeof': 2,
'quote-props': [2, 'as-needed'],
'one-var': [2, 'never'],
'prefer-arrow-callback': 2,
'prefer-const': [2, { destructuring: 'all' }],
'arrow-spacing': 2,
'no-inner-declarations': 0,
'require-atomic-updates': 0
},
env: {
es6: true,
browser: true,
node: true,
mocha: true
},
extends: [
'eslint:recommended',
'plugin:import/errors',
'plugin:import/warnings'
],
plugins: ['svelte3'],
overrides: [
{
files: ['*.svelte'],
processor: 'svelte3/svelte3'
}
],
parserOptions: {
ecmaVersion: 9,
sourceType: 'module'
},
settings: {
'import/core-modules': ['svelte'],
'svelte3/compiler': require('svelte/compiler')
}
};

@ -1,44 +0,0 @@
{
"root": true,
"rules": {
"indent": [2, "tab", { "SwitchCase": 1 }],
"semi": [2, "always"],
"keyword-spacing": [2, { "before": true, "after": true }],
"space-before-blocks": [2, "always"],
"no-mixed-spaces-and-tabs": [2, "smart-tabs"],
"no-cond-assign": 0,
"no-unused-vars": 2,
"object-shorthand": [2, "always"],
"no-const-assign": 2,
"no-class-assign": 2,
"no-this-before-super": 2,
"no-var": 2,
"no-unreachable": 2,
"valid-typeof": 2,
"quote-props": [2, "as-needed"],
"one-var": [2, "never"],
"prefer-arrow-callback": 2,
"prefer-const": [2, { "destructuring": "all" }],
"arrow-spacing": 2,
"no-inner-declarations": 0
},
"env": {
"es6": true,
"browser": true,
"node": true,
"mocha": true
},
"extends": [
"eslint:recommended",
"plugin:import/errors",
"plugin:import/warnings"
],
"plugins": ["svelte3"],
"parserOptions": {
"ecmaVersion": 8,
"sourceType": "module"
},
"settings": {
"import/core-modules": ["svelte"]
}
}

@ -1,4 +1,4 @@
FROM mhart/alpine-node:11.14 FROM mhart/alpine-node:12
# install dependencies # install dependencies
WORKDIR /app WORKDIR /app
@ -9,7 +9,7 @@ RUN npm ci --production
# Only copy over the Node pieces we need # Only copy over the Node pieces we need
# ~> Saves 35MB # ~> Saves 35MB
### ###
FROM mhart/alpine-node:base-11.14 FROM mhart/alpine-node:slim-12
WORKDIR /app WORKDIR /app
COPY --from=0 /app . COPY --from=0 /app .

@ -19,4 +19,4 @@ docker:
deploy: sapper docker deploy: sapper docker
@echo "\n~> deploying $(SERVICE) to Cloud Run servers" @echo "\n~> deploying $(SERVICE) to Cloud Run servers"
@gcloud beta run deploy $(SERVICE) --allow-unauthenticated --region us-central1 --image $(IMAGE) --memory=512Mi @gcloud beta run deploy $(SERVICE) --allow-unauthenticated --platform managed --region us-central1 --image $(IMAGE) --memory=512Mi

@ -4,7 +4,10 @@ Set up the project:
```bash ```bash
git clone https://github.com/sveltejs/svelte.git git clone https://github.com/sveltejs/svelte.git
cd svelte/site cd svelte
npm ci
PUBLISH=1 npm run build
cd site
npm ci npm ci
npm run update npm run update
``` ```
@ -13,7 +16,11 @@ Start the server with `npm run dev`, and navigate to [localhost:3000](http://loc
## Using a local copy of Svelte ## Using a local copy of Svelte
By default, the REPL will fetch the most recent version of Svelte from https://unpkg.com/svelte. To use the local copy of the compiler and runtime from this repo, you can navigate to [localhost:3000/repl?version=local](http://localhost:3000/repl?version=local). To produce the proper browser-compatible UMD build, you will need to run `npm run build` with the `PUBLISH` environment variable set (to any non-empty string). By default, the REPL will fetch the most recent version of Svelte from https://unpkg.com/svelte. When running the site locally, you can also use your local copy of Svelte.
To produce the proper browser-compatible UMD build of the compiler, you will need to run `npm run build` (or `npm run dev`) in the root of this repository with the `PUBLISH` environment variable set to any non-empty string.
Then visit the REPL at [localhost:3000/repl?version=local](http://localhost:3000/repl?version=local). Please note that the local REPL only works with `npm run dev` and not when building the site for production usage.
## REPL GitHub integration ## REPL GitHub integration
@ -28,6 +35,13 @@ In order for the REPL's GitHub integration to work properly when running locally
GITHUB_CLIENT_SECRET=[your app's Client Secret] GITHUB_CLIENT_SECRET=[your app's Client Secret]
BASEURL=http://localhost:3000 BASEURL=http://localhost:3000
``` ```
## Building the site
To build the website, run `npm run sapper`. The output can be found in `__sapper__/build`.
## Testing
Tests can be run using `npm run test`.
## Translating the API docs ## Translating the API docs

@ -44,9 +44,11 @@ In the terminal, you can instantly create a new project like so:
npx degit sveltejs/template my-svelte-project npx degit sveltejs/template my-svelte-project
cd my-svelte-project cd my-svelte-project
npm install npm install
npm run dev & open http://localhost:5000 npm run dev
``` ```
This will create a new project in the `my-svelte-project` directory, install its dependencies, and start a server on http://localhost:5000.
Once you've tinkered a bit and understood how everything fits together, you can fork [sveltejs/template](https://github.com/sveltejs/template) and start doing this instead: Once you've tinkered a bit and understood how everything fits together, you can fork [sveltejs/template](https://github.com/sveltejs/template) and start doing this instead:
```bash ```bash
@ -55,4 +57,4 @@ npx degit your-name/template my-new-project
And that's it! Do `npm run build` to create a production-ready version of your app, and check the project template's [README](https://github.com/sveltejs/template/blob/master/README.md) for instructions on how to easily deploy your app to the web with [Now](https://zeit.co/now) or [Surge](http://surge.sh/). And that's it! Do `npm run build` to create a production-ready version of your app, and check the project template's [README](https://github.com/sveltejs/template/blob/master/README.md) for instructions on how to easily deploy your app to the web with [Now](https://zeit.co/now) or [Surge](http://surge.sh/).
You're not restricted to using Rollup — there are also integrations for [webpack](https://github.com/sveltejs/svelte-loader), [Browserify](https://github.com/tehshrike/sveltify) and others, or you can use the [Svelte CLI](https://github.com/sveltejs/svelte-cli) (Update from 2019: with Svelte 3 the CLI was deprecated and we now use [sirv-cli](https://www.npmjs.com/package/sirv-cli) in our template. Feel free to use whatever tool you like!) or the [API](https://github.com/sveltejs/svelte/tree/v2#api) directly. If you make a project template using one of these tools, please share it with the [Svelte Discord chatroom](https://discord.gg/yy75DKs), or via [@sveltejs](https://twitter.com/sveltejs) on Twitter! You're not restricted to using Rollup — there are also integrations for [webpack](https://github.com/sveltejs/svelte-loader), [Browserify](https://github.com/tehshrike/sveltify) and others, or you can use the [Svelte CLI](https://github.com/sveltejs/svelte-cli) (Update from 2019: with Svelte 3 the CLI was deprecated and we now use [sirv-cli](https://www.npmjs.com/package/sirv-cli) in our template. Feel free to use whatever tool you like!) or the [API](https://github.com/sveltejs/svelte/tree/v2#api) directly. If you make a project template using one of these tools, please share it with the [Svelte Discord chatroom](chat), or via [@sveltejs](https://twitter.com/sveltejs) on Twitter!

@ -81,4 +81,4 @@ I believe the next frontier of web performance is 'whole-app optimisation'. Curr
Speaking of Glimmer, the idea of compiling components to bytecode is one that we'll probably steal in 2018. A framework like Sapper could conceivably determine which compilation mode to use based on the characteristics of your app. It could even serve JavaScript for the initial route for the fastest possible startup time, then lazily serve a bytecode interpreter for subsequent routes, resulting in the optimal combination of startup size and total app size. Speaking of Glimmer, the idea of compiling components to bytecode is one that we'll probably steal in 2018. A framework like Sapper could conceivably determine which compilation mode to use based on the characteristics of your app. It could even serve JavaScript for the initial route for the fastest possible startup time, then lazily serve a bytecode interpreter for subsequent routes, resulting in the optimal combination of startup size and total app size.
Mostly, though, we want the direction of Sapper to be determined by its users. If you're the kind of developer who enjoys life on the bleeding edge and would like to help shape the future of how we build web apps, please join us on [GitHub](https://github.com/sveltejs/svelte) and [Discord](https://discord.gg/yy75DKs). Mostly, though, we want the direction of Sapper to be determined by its users. If you're the kind of developer who enjoys life on the bleeding edge and would like to help shape the future of how we build web apps, please join us on [GitHub](https://github.com/sveltejs/svelte) and [Discord](chat).

@ -12,7 +12,7 @@ Almost a year after we first started talking about version 2 on the Svelte issue
## tl;dr ## tl;dr
Each of these items is described in more depth below. If you get stuck, ask for help in our friendly [Discord chatroom](https://discord.gg/yy75DKs). Each of these items is described in more depth below. If you get stuck, ask for help in our friendly [Discord chatroom](chat).
- Install Svelte v2 from npm - Install Svelte v2 from npm
- Upgrade your templates with [svelte-upgrade](https://github.com/sveltejs/svelte-upgrade) - Upgrade your templates with [svelte-upgrade](https://github.com/sveltejs/svelte-upgrade)
@ -201,4 +201,4 @@ Before, there was a `svelte.validate` method which checked your component was va
## My app is broken! Help! ## My app is broken! Help!
Hopefully this covers everything, and the update should be easier for you than it was for us. But if you find bugs, or discover things that aren't mentioned here, swing by [Discord chatroom](https://discord.gg/yy75DKs) or raise an issue on the [tracker](https://github.com/sveltejs/svelte/issues). Hopefully this covers everything, and the update should be easier for you than it was for us. But if you find bugs, or discover things that aren't mentioned here, swing by [Discord chatroom](chat) or raise an issue on the [tracker](https://github.com/sveltejs/svelte/issues).

@ -14,9 +14,8 @@ Earlier this month, I had the privilege of appearing on [The Changelog](https://
...and, most importantly, Svelte 3. ...and, most importantly, Svelte 3.
Unless you hang out in our [Discord server](https://discord.gg/yy75DKs) or follow [@sveltejs](https://twitter.com/sveltejs) on Twitter, you might not know that Svelte 3 is just around the corner, and it's going to be a huge release. We've rethought the developer experience from the ground up, and while it *will* be a nuisance if you need to upgrade a Svelte 2 app (more on that soon) we think you're going to love it. Unless you hang out in our [Discord server](chat) or follow [@sveltejs](https://twitter.com/sveltejs) on Twitter, you might not know that Svelte 3 is just around the corner, and it's going to be a huge release. We've rethought the developer experience from the ground up, and while it *will* be a nuisance if you need to upgrade a Svelte 2 app (more on that soon) we think you're going to love it.
On the podcast [Adam](https://twitter.com/adamstac), [Jerod](https://twitter.com/jerodsanto) and I talk about some of the changes and why we're making them. You can listen here or on the [podcast page](https://changelog.com/podcast/332). On the podcast [Adam](https://twitter.com/adamstac), [Jerod](https://twitter.com/jerodsanto) and I talk about some of the changes and why we're making them. You can listen here or on the [podcast page](https://changelog.com/podcast/332).
<audio data-theme="night" style="width: 100%" data-src="https://changelog.com/podcast/332/embed" src="https://cdn.changelog.com/uploads/podcast/332/the-changelog-332.mp3" preload="none" class="changelog-episode" controls></audio><p><a href="https://changelog.com/podcast/332">The Changelog 332: A UI framework without the framework</a> Listen on <a href="https://changelog.com/">Changelog.com</a></p><script async src="//cdn.changelog.com/embed.js"></script> <audio data-theme="night" style="width: 100%" data-src="https://changelog.com/podcast/332/embed" src="https://cdn.changelog.com/uploads/podcast/332/the-changelog-332.mp3" preload="none" class="changelog-episode" controls></audio><p><a href="https://changelog.com/podcast/332">The Changelog 332: A UI framework without the framework</a> Listen on <a href="https://changelog.com/">Changelog.com</a></p><script async src="//cdn.changelog.com/embed.js"></script>

@ -11,3 +11,23 @@ draft: true
* eslint-plugin-svelte3 * eslint-plugin-svelte3
* svelte-vscode * svelte-vscode
* associating .svelte files with HTML in VSCode, Sublime, Atom, etc etc etc * associating .svelte files with HTML in VSCode, Sublime, Atom, etc etc etc
## Vim/Neovim
To treat all `*.svelte` files as HTML, add the following line to your `init.vim`:
```bash
au! BufNewFile,BufRead *.svelte set ft=html
```
To temporarily turn on HTML syntax highlighting for the current buffer, use:
```bash
:set ft=html
```
To set the filetype for a single file, use a [modeline](https://vim.fandom.com/wiki/Modeline_magic):
```bash
<!-- vim: set ft=html :-->
```

@ -94,4 +94,4 @@ We don't take this lightly: hopefully once you've experienced Svelte 3 you'll un
As grueling as this release has been, we're nowhere near finished. We have a ton of ideas for generating smarter, more compact code, and a long feature wish-list. [Sapper](https://sapper.svelte.dev), our Next.js-style app framework, is still in the middle of being updated to use Svelte 3. The [Svelte Native](https://svelte-native.technology/) community project, which allows you to write Android and iOS apps in Svelte, is making solid progress but deserves more complete support from core. We don't yet have the bounty of editor extensions, syntax highlighters, component kits, devtools and so on that other frameworks have, and we should fix that. We *really* want to add first-class TypeScript support. As grueling as this release has been, we're nowhere near finished. We have a ton of ideas for generating smarter, more compact code, and a long feature wish-list. [Sapper](https://sapper.svelte.dev), our Next.js-style app framework, is still in the middle of being updated to use Svelte 3. The [Svelte Native](https://svelte-native.technology/) community project, which allows you to write Android and iOS apps in Svelte, is making solid progress but deserves more complete support from core. We don't yet have the bounty of editor extensions, syntax highlighters, component kits, devtools and so on that other frameworks have, and we should fix that. We *really* want to add first-class TypeScript support.
But in the meantime we think Svelte 3 is the best way to build web apps yet. Take an hour to go through the [tutorial](tutorial) and we hope to convince you of the same. Either way, we'd love to see you in our [Discord chatroom](https://discord.gg/yy75DKs) and on [GitHub](https://github.com/sveltejs/svelte) — everyone is welcome, especially you. But in the meantime we think Svelte 3 is the best way to build web apps yet. Take an hour to go through the [tutorial](tutorial) and we hope to convince you of the same. Either way, we'd love to see you in our [Discord chatroom](chat) and on [GitHub](https://github.com/sveltejs/svelte) — everyone is welcome, especially you.

@ -2,7 +2,7 @@
title: Before we begin title: Before we begin
--- ---
> Temporary note: This document is a work-in-progress. Please forgive any missing or misleading parts, and don't be shy about asking for help in the [Discord chatroom](https://discord.gg/yy75DKs). The [tutorial](tutorial) is more complete; start there. > Temporary note: This document is a work-in-progress. Please forgive any missing or misleading parts, and don't be shy about asking for help in the [Discord chatroom](chat). The [tutorial](tutorial) is more complete; start there.
This page contains detailed API reference documentation. It's intended to be a resource for people who already have some familiarity with Svelte. This page contains detailed API reference documentation. It's intended to be a resource for people who already have some familiarity with Svelte.

@ -28,26 +28,60 @@ A `<script>` block contains JavaScript that runs when a component instance is cr
--- ---
Svelte uses the `export` keyword to mark a variable declaration as a *property* or *prop*, which means it becomes accessible to consumers of the component: Svelte uses the `export` keyword to mark a variable declaration as a *property* or *prop*, which means it becomes accessible to consumers of the component (see the section on [attributes and props](docs#Attributes_and_props) for more information).
```html ```html
<script> <script>
// these properties can be set externally
export let foo; export let foo;
export let bar = 'optional default value';
// this property is readonly externally
export const buzz = 'buzz';
// Values that are passed in as props // Values that are passed in as props
// are immediately available // are immediately available
console.log(foo, bar); console.log({ foo });
</script>
```
---
// function declarations cannot be set externally, You can specify a default value, which will be used if the component's consumer doesn't specify a prop.
// but can be accessed from outside
export function instanceMethod() { In development mode (see the [compiler options](docs#svelte_compile)), a warning will be printed if no default is provided and the consumer does not specify a value. To squelch this warning, ensure that a default is specified, even if it is `undefined`.
alert(foo);
```html
<script>
export let bar = 'optional default value';
export let baz = undefined;
</script>
```
---
If you export a `const`, `class` or `function`, it is readonly from outside the component. Function *expressions* are valid props, however.
```html
<script>
// these are readonly
export const thisIs = 'readonly';
export function greet(name) {
alert(`hello ${name}!`);
} }
// this is a prop
export let format = n => n.toFixed(2);
</script>
```
---
You can use reserved words as prop names.
```html
<script>
let className;
// creates a `class` property, even
// though it is a reserved word
export { className as class };
</script> </script>
``` ```
@ -66,8 +100,8 @@ Because Svelte's reactivity is based on assignments, using array methods like `.
let count = 0; let count = 0;
function handleClick () { function handleClick () {
// calling this function will trigger a re-render // calling this function will trigger an
// if the markup references `count` // update if the markup references `count`
count = count + 1; count = count + 1;
} }
</script> </script>
@ -77,7 +111,7 @@ Because Svelte's reactivity is based on assignments, using array methods like `.
--- ---
Any top-level statement (i.e. not inside a block or a function) can be made reactive by prefixing it with the `$:` label. Reactive statements run immediately before the component updates, whenever the values that they depend on have changed. Any top-level statement (i.e. not inside a block or a function) can be made reactive by prefixing it with the `$:` [JS label syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/label). Reactive statements run immediately before the component updates, whenever the values that they depend on have changed.
```html ```html
<script> <script>
@ -113,8 +147,29 @@ If a statement consists entirely of an assignment to an undeclared variable, Sve
--- ---
A *store* is any object that allows reactive access to a value via a simple *store contract*.
The [`svelte/store` module](docs#svelte_store) contains minimal store implementations which fulfil this contract. You can use these as the basis for your own stores, or you can implement your stores from scratch.
A store must contain a `.subscribe` method, which must accept as its argument a subscription function. This subscription function must be immediately and synchronously called with the store's current value upon calling `.subscribe`. All of a store's active subscription functions must later be synchronously called whenever the store's value changes. The `.subscribe` method must also return an unsubscription function. Calling an unsubscription function must stop its subscription, and its corresponding subscription function must not be called again by the store.
A store may optionally contain a `.set` method, which must accept as its argument a new value for the store, and which synchronously calls all of the store's active subscription functions. Such a store is called a *writable store*.
```js
const unsubscribe = store.subscribe(value => {
console.log(value);
}); // logs `value`
// later...
unsubscribe();
```
---
Any time you have a reference to a store, you can access its value inside a component by prefixing it with the `$` character. This causes Svelte to declare the prefixed variable, and set up a store subscription that will be unsubscribed when appropriate. Any time you have a reference to a store, you can access its value inside a component by prefixing it with the `$` character. This causes Svelte to declare the prefixed variable, and set up a store subscription that will be unsubscribed when appropriate.
Assignments to `$`-prefixed variables require that the variable be a writable store, and will result in a call to the store's `.set` method.
Note that the store must be declared at the top level of the component — not inside an `if` block or a function, for example. Note that the store must be declared at the top level of the component — not inside an `if` block or a function, for example.
Local variables (that do not represent store values) must *not* have a `$` prefix. Local variables (that do not represent store values) must *not* have a `$` prefix.
@ -128,6 +183,9 @@ Local variables (that do not represent store values) must *not* have a `$` prefi
count.set(1); count.set(1);
console.log($count); // logs 1 console.log($count); // logs 1
$count = 2;
console.log($count); // logs 2
</script> </script>
``` ```
@ -142,6 +200,8 @@ You can `export` bindings from this block, and they will become exports of the c
You cannot `export default`, since the default export is the component itself. You cannot `export default`, since the default export is the component itself.
> Variables defined in `module` scripts are not reactive — reassigning them will not trigger a rerender even though the variable itself will update. For values shared between multiple components, consider using a [store](https://svelte.dev/docs#svelte_store).
```html ```html
<script context="module"> <script context="module">
let totalComponents = 0; let totalComponents = 0;

@ -7,7 +7,7 @@ title: Template syntax
--- ---
A lowercase tag, like `<div>`, denotes a regular HTML element. A capitalised tag, such as `<Widget>`, indicates a *component*. A lowercase tag, like `<div>`, denotes a regular HTML element. A capitalised tag, such as `<Widget>` or `<Namespace.Widget>`, indicates a *component*.
```html ```html
<script> <script>
@ -20,7 +20,7 @@ A lowercase tag, like `<div>`, denotes a regular HTML element. A capitalised tag
``` ```
### Attributes ### Attributes and props
--- ---
@ -76,6 +76,16 @@ When the attribute name and value match (`name={name}`), they can be replaced wi
--- ---
By convention, values passed to components are referred to as *properties* or *props* rather than *attributes*, which are a feature of the DOM.
As with elements, `name={name}` can be replaced with the `{name}` shorthand.
```html
<Widget foo={bar} answer={42} text="hello"/>
```
---
*Spread attributes* allow many attributes or properties to be passed to an element or component at once. *Spread attributes* allow many attributes or properties to be passed to an element or component at once.
An element or component can have multiple spread attributes, interspersed with regular ones. An element or component can have multiple spread attributes, interspersed with regular ones.
@ -101,27 +111,7 @@ Text can also contain JavaScript expressions:
``` ```
### HTML expressions ### {#if ...}
```sv
{@html expression}
```
---
In a text expression, characters like `<` and `>` are escaped. With HTML expressions, they're not.
> Svelte does not sanitize expressions before injecting HTML. If the data comes from an untrusted source, you must sanitize it, or you are exposing your users to an XSS vulnerability.
```html
<div class="blog-post">
<h1>{post.title}</h1>
{@html post.content}
</div>
```
### If blocks
```sv ```sv
{#if expression}...{/if} {#if expression}...{/if}
@ -158,7 +148,7 @@ Additional conditions can be added with `{:else if expression}`, optionally endi
``` ```
### Each blocks ### {#each ...}
```sv ```sv
{#each expression as name}...{/each} {#each expression as name}...{/each}
@ -208,12 +198,20 @@ If a *key* expression is provided — which must uniquely identify each list ite
--- ---
You can freely use destructuring patterns in each blocks. You can freely use destructuring and rest patterns in each blocks.
```html ```html
{#each items as { id, name, qty }, i (id)} {#each items as { id, name, qty }, i (id)}
<li>{i + 1}: {name} x {qty}</li> <li>{i + 1}: {name} x {qty}</li>
{/each} {/each}
{#each objects as { id, ...rest }}
<li><span>{id}</span><MyComponent {...rest}/></li>
{/each}
{#each items as [id, ...rest]}
<li><span>{id}</span><MyComponent values={rest}/></li>
{/each}
``` ```
--- ---
@ -229,7 +227,7 @@ An each block can also have an `{:else}` clause, which is rendered if the list i
``` ```
### Await blocks ### {#await ...}
```sv ```sv
{#await expression}...{:then name}...{:catch name}...{/await} {#await expression}...{:then name}...{:catch name}...{/await}
@ -283,7 +281,80 @@ If you don't care about the pending state, you can also omit the initial block.
``` ```
### DOM events ### {@html ...}
```sv
{@html expression}
```
---
In a text expression, characters like `<` and `>` are escaped. With HTML expressions, they're not.
> Svelte does not sanitize expressions before injecting HTML. If the data comes from an untrusted source, you must sanitize it, or you are exposing your users to an XSS vulnerability.
```html
<div class="blog-post">
<h1>{post.title}</h1>
{@html post.content}
</div>
```
### {@debug ...}
```sv
{@debug}
```
```sv
{@debug var1, var2, ..., varN}
```
---
The `{@debug ...}` tag offers an alternative to `console.log(...)`. It logs the values of specific variables whenever they change, and pauses code execution if you have devtools open.
It accepts a comma-separated list of variable names (not arbitrary expressions).
```html
<script>
let user = {
firstname: 'Ada',
lastname: 'Lovelace'
};
</script>
{@debug user}
<h1>Hello {user.firstname}!</h1>
```
---
`{@debug ...}` accepts a comma-separated list of variable names (not arbitrary expressions).
```html
<!-- Compiles -->
{@debug user}
{@debug user1, user2, user3}
<!-- WON'T compile -->
{@debug user.firstname}
{@debug myArray[0]}
{@debug !isReady}
{@debug typeof user === 'object'}
```
The `{@debug}` tag without any arguments will insert a `debugger` statement that gets triggered when *any* state changes, as opposed to the specified variables.
### Element directives
As well as attributes, elements can have *directives*, which control the element's behaviour in some way.
#### [on:*eventname*](on_element_event)
```sv ```sv
on:eventname={handler} on:eventname={handler}
@ -324,6 +395,13 @@ Handlers can be declared inline with no performance penalty. As with attributes,
Add *modifiers* to DOM events with the `|` character. Add *modifiers* to DOM events with the `|` character.
```html
<form on:submit|preventDefault={handleSubmit}>
<!-- the `submit` event's default is prevented,
so the page won't reload -->
</form>
```
The following modifiers are available: The following modifiers are available:
* `preventDefault` — calls `event.preventDefault()` before running the handler * `preventDefault` — calls `event.preventDefault()` before running the handler
@ -334,13 +412,6 @@ The following modifiers are available:
Modifiers can be chained together, e.g. `on:click|once|capture={...}`. Modifiers can be chained together, e.g. `on:click|once|capture={...}`.
```html
<form on:submit|preventDefault={handleSubmit}>
<!-- the `submit` event's default is prevented,
so the page won't reload -->
</form>
```
--- ---
If the `on:` directive is used without a value, the component will *forward* the event, meaning that a consumer of the component can listen for it. If the `on:` directive is used without a value, the component will *forward* the event, meaning that a consumer of the component can listen for it.
@ -351,40 +422,30 @@ If the `on:` directive is used without a value, the component will *forward* the
</button> </button>
``` ```
### Component events
```sv
on:eventname={handler}
```
--- ---
Components can emit events using [createEventDispatcher](docs#createEventDispatcher), or by forwarding DOM events. Listening for component events looks the same as listening for DOM events: It's possible to have multiple event listeners for the same event:
```html ```html
<SomeComponent on:whatever={handler}/> <script>
``` let counter = 0;
function increment() {
--- counter = counter + 1;
}
As with DOM events, if the `on:` directive is used without a value, the component will *forward* the event, meaning that a consumer of the component can listen for it. function track(event) {
trackEvent(event)
}
</script>
```html <button on:click={increment} on:click={track}>Click me!</button>
<SomeComponent on:whatever/>
``` ```
### Element bindings #### [bind:*property*](bind_element_property)
```sv ```sv
bind:property={variable} bind:property={variable}
``` ```
```sv
bind:group={variable}
```
```sv
bind:this={dom_node}
```
--- ---
@ -418,31 +479,8 @@ Numeric input values are coerced; even though `input.value` is a string as far a
<input type="range" bind:value={num}> <input type="range" bind:value={num}>
``` ```
#### Binding related elements
---
Inputs that work together can use `bind:group`.
```html
<script>
let tortilla = 'Plain';
let fillings = [];
</script>
<!-- grouped radio inputs are mutually exclusive -->
<input type="radio" bind:group={tortilla} value="Plain">
<input type="radio" bind:group={tortilla} value="Whole wheat">
<input type="radio" bind:group={tortilla} value="Spinach">
<!-- grouped checkbox inputs populate an array --> ##### Binding `<select>` value
<input type="checkbox" bind:group={fillings} value="Rice">
<input type="checkbox" bind:group={fillings} value="Beans">
<input type="checkbox" bind:group={fillings} value="Cheese">
<input type="checkbox" bind:group={fillings} value="Guac (extra)">
```
#### Binding `<select>` value
--- ---
@ -482,7 +520,15 @@ When the value of an `<option>` matches its text content, the attribute can be o
</select> </select>
``` ```
#### Media element bindings ---
Elements with the `contenteditable` attribute support `innerHTML` and `textContent` bindings.
```html
<div contenteditable="true" bind:innerHTML={html}></div>
```
##### Media element bindings
--- ---
@ -512,7 +558,7 @@ Media elements (`<audio>` and `<video>`) have their own set of bindings — four
></video> ></video>
``` ```
#### Block-level element bindings ##### Block-level element bindings
--- ---
@ -532,61 +578,61 @@ Block-level elements have 4 readonly bindings, measured using a technique simila
</div> </div>
``` ```
#### Binding a DOM node #### bind:group
```sv
bind:group={variable}
```
--- ---
To get a reference to a DOM node, use `bind:this`. Inputs that work together can use `bind:group`.
```html ```html
<script> <script>
import { onMount } from 'svelte'; let tortilla = 'Plain';
let fillings = [];
let canvasElement;
onMount(() => {
const ctx = canvasElement.getContext('2d');
drawStuff(ctx);
});
</script> </script>
<canvas bind:this={canvasElement}></canvas> <!-- grouped radio inputs are mutually exclusive -->
``` <input type="radio" bind:group={tortilla} value="Plain">
<input type="radio" bind:group={tortilla} value="Whole wheat">
<input type="radio" bind:group={tortilla} value="Spinach">
<!-- grouped checkbox inputs populate an array -->
<input type="checkbox" bind:group={fillings} value="Rice">
<input type="checkbox" bind:group={fillings} value="Beans">
<input type="checkbox" bind:group={fillings} value="Cheese">
<input type="checkbox" bind:group={fillings} value="Guac (extra)">
```
### Component bindings #### [bind:this](bind_element)
```sv ```sv
bind:property={variable} bind:this={dom_node}
```
```sv
bind:this={component_instance}
``` ```
--- ---
You can bind to component props using the same mechanism. To get a reference to a DOM node, use `bind:this`.
```html ```html
<Keypad bind:value={pin}/> <script>
``` import { onMount } from 'svelte';
---
Components also support `bind:this`, allowing you to interact with component instances programmatically.
> Note that we can do `{cart.empty}` rather than `{() => cart.empty()}`, since component methods are closures. You don't need to worry about the value of `this` when calling them. let canvasElement;
```html onMount(() => {
<ShoppingCart bind:this={cart}/> const ctx = canvasElement.getContext('2d');
drawStuff(ctx);
});
</script>
<button on:click={cart.empty}> <canvas bind:this={canvasElement}></canvas>
Empty shopping cart
</button>
``` ```
### Classes #### class:*name*
```sv ```sv
class:name={value} class:name={value}
@ -612,7 +658,7 @@ A `class:` directive provides a shorter way of toggling a class on an element.
``` ```
### Actions #### use:*action*
```sv ```sv
use:action use:action
@ -677,43 +723,19 @@ An action can have parameters. If the returned value has an `update` method, it
``` ```
### Transitions #### transition:*fn*
```sv ```sv
transition:name transition:fn
```
```sv
transition:name={params}
``` ```
```sv ```sv
transition:name|local transition:fn={params}
``` ```
```sv ```sv
transition:name|local={params} transition:fn|local
``` ```
```sv ```sv
in:name transition:fn|local={params}
```
```sv
in:name={params}
```
```sv
in:name|local
```
```sv
in:name|local={params}
```
```sv
out:name
```
```sv
out:name={params}
```
```sv
out:name|local
```
```sv
out:name|local={params}
``` ```
@ -729,7 +751,7 @@ transition = (node: HTMLElement, params: any) => {
--- ---
A transition is triggered by an element entering or leaving the DOM as a result of a state change. Transitions do not run when a component is first mounted, but only on subsequent updates. A transition is triggered by an element entering or leaving the DOM as a result of a state change.
Elements inside an *outroing* block are kept in the DOM until all current transitions have completed. Elements inside an *outroing* block are kept in the DOM until all current transitions have completed.
@ -743,21 +765,9 @@ The `transition:` directive indicates a *bidirectional* transition, which means
{/if} {/if}
``` ```
---
The `in:` and `out:` directives are not bidirectional. An in transition will continue to 'play' alongside the out transition, if the block is outroed while the transition is in progress. If an out transition is aborted, transitions will restart from scratch.
```html
{#if visible}
<div in:fly out:fade>
flies in, fades out
</div>
{/if}
```
> By default intro transitions will not play on first render. You can modify this behaviour by setting `intro: true` when you [create a component](docs#Client-side_component_API). > By default intro transitions will not play on first render. You can modify this behaviour by setting `intro: true` when you [create a component](docs#Client-side_component_API).
#### Transition parameters ##### Transition parameters
--- ---
@ -773,7 +783,7 @@ Like actions, transitions can have parameters.
{/if} {/if}
``` ```
#### Custom transition functions ##### Custom transition functions
--- ---
@ -849,7 +859,7 @@ A custom transition function can also return a `tick` function, which is called
If a transition returns a function instead of a transition object, the function will be called in the next microtask. This allows multiple transitions to coordinate, making [crossfade effects](tutorial/deferred-transitions) possible. If a transition returns a function instead of a transition object, the function will be called in the next microtask. This allows multiple transitions to coordinate, making [crossfade effects](tutorial/deferred-transitions) possible.
#### Transition events ##### Transition events
--- ---
@ -893,12 +903,246 @@ Local transitions only play when the block they belong to is created or destroye
``` ```
### Animations #### in:*fn*/out:*fn*
```sv
in:fn
```
```sv
in:fn={params}
```
```sv
in:fn|local
```
```sv
in:fn|local={params}
```
```sv
out:fn
```
```sv
out:fn={params}
```
```sv
out:fn|local
```
```sv
out:fn|local={params}
```
---
Similar to `transition:`, but only applies to elements entering (`in:`) or leaving (`out:`) the DOM.
Unlike with `transition:`, transitions applied with `in:` and `out:` are not bidirectional — an in transition will continue to 'play' alongside the out transition, rather than reversing, if the block is outroed while the transition is in progress. If an out transition is aborted, transitions will restart from scratch.
```html
{#if visible}
<div in:fly out:fade>
flies in, fades out
</div>
{/if}
```
#### animate:*fn*
```sv
animate:name
```
```sv
animate:name={params}
```
```js
animation = (node: HTMLElement, { from: DOMRect, to: DOMRect } , params: any) => {
delay?: number,
duration?: number,
easing?: (t: number) => number,
css?: (t: number, u: number) => string,
tick?: (t: number, u: number) => void
}
```
```js
DOMRect {
bottom: number,
height: number,
left: number,
right: number,
top: number,
width: number,
x: number,
y:number
}
```
---
An animation is triggered when the contents of a [keyed each block](docs#each) are re-ordered. Animations do not run when an element is removed, only when the each block's data is reordered. Animate directives must be on an element that is an *immediate* child of a keyed each block.
Animations can be used with Svelte's [built-in animation functions](docs#svelte_animate) or [custom animation functions](docs#Custom_animation_functions).
```html
<!-- When `list` is reordered the animation will run-->
{#each list as item, index (item)}
<li animate:flip>{item}</li>
{/each}
```
##### Animation Parameters
---
As with actions and transitions, animations can have parameters.
(The double `{{curlies}}` aren't a special syntax; this is an object literal inside an expression tag.)
```html
{#each list as item, index (item)}
<li animate:flip="{{ delay: 500 }}">{item}</li>
{/each}
```
##### Custom animation functions
---
Animations can use custom functions that provide the `node`, an `animation` object and any `paramaters` as arguments. The `animation` parameter is an object containing `from` and `to` properties each containing a [DOMRect](https://developer.mozilla.org/en-US/docs/Web/API/DOMRect#Properties) describing the geometry of the element in its `start` and `end` positions. The `from` property is the DOMRect of the element in its starting position, the `to` property is the DOMRect of the element in its final position after the list has been reordered and the DOM updated.
If the returned object has a `css` method, Svelte will create a CSS animation that plays on the element.
The `t` argument passed to `css` is a value that goes from `0` and `1` after the `easing` function has been applied. The `u` argument is equal to `1 - t`.
The function is called repeatedly *before* the animation begins, with different `t` and `u` arguments.
```html
<script>
import { cubicOut } from 'svelte/easing';
function whizz(node, { from, to }, params) {
const dx = from.left - to.left;
const dy = from.top - to.top;
const d = Math.sqrt(dx * dx + dy * dy);
return {
delay: 0,
duration: Math.sqrt(d) * 120,
easing: cubicOut,
css: (t, u) =>
`transform: translate(${u * dx}px, ${u * dy}px) rotate(${t*360}deg);`
};
}
</script>
{#each list as item, index (item)}
<div animate:whizz>{item}</div>
{/each}
```
---
A custom animation function can also return a `tick` function, which is called *during* the animation with the same `t` and `u` arguments.
TODO i can't remember how any of this works > If it's possible to use `css` instead of `tick`, do so — CSS animations can run off the main thread, preventing jank on slower devices.
```html
<script>
import { cubicOut } from 'svelte/easing';
### Slots function whizz(node, { from, to }, params) {
const dx = from.left - to.left;
const dy = from.top - to.top;
const d = Math.sqrt(dx * dx + dy * dy);
return {
delay: 0,
duration: Math.sqrt(d) * 120,
easing: cubicOut,
tick: (t, u) =>
Object.assign(node.style, {
color: t > 0.5 ? 'Pink' : 'Blue'
});
};
}
</script>
{#each list as item, index (item)}
<div animate:whizz>{item}</div>
{/each}
```
### Component directives
#### [on:*eventname*](on_component_event)
```sv
on:eventname={handler}
```
---
Components can emit events using [createEventDispatcher](docs#createEventDispatcher), or by forwarding DOM events. Listening for component events looks the same as listening for DOM events:
```html
<SomeComponent on:whatever={handler}/>
```
---
As with DOM events, if the `on:` directive is used without a value, the component will *forward* the event, meaning that a consumer of the component can listen for it.
```html
<SomeComponent on:whatever/>
```
#### [bind:*property*](bind_component_property)
```sv
bind:property={variable}
```
---
You can bind to component props using the same mechanism.
```html
<Keypad bind:value={pin}/>
```
#### [bind:this](bind_component)
```sv
bind:this={component_instance}
```
---
Components also support `bind:this`, allowing you to interact with component instances programmatically.
> Note that we can't do `{cart.empty}` since `cart` is `undefined` when the button is first rendered and throws an error.
```html
<ShoppingCart bind:this={cart}/>
<button on:click={() => cart.empty()}>
Empty shopping cart
</button>
```
### `<slot>`
```sv ```sv
<slot><!-- optional fallback --></slot> <slot><!-- optional fallback --></slot>
@ -930,6 +1174,8 @@ The content is exposed in the child component using the `<slot>` element, which
</div> </div>
``` ```
#### [`<slot name="`*name*`">`](slot_name)
--- ---
Named slots allow consumers to target specific areas. They can also have fallback content. Named slots allow consumers to target specific areas. They can also have fallback content.
@ -949,6 +1195,8 @@ Named slots allow consumers to target specific areas. They can also have fallbac
</div> </div>
``` ```
#### [`<slot let:`*name*`={`*value*`}>`](slot_let)
--- ---
Slots can be rendered zero or more times, and can pass values *back* to the parent using props. The parent exposes the values to the slot template using the `let:` directive. Slots can be rendered zero or more times, and can pass values *back* to the parent using props. The parent exposes the values to the slot template using the `let:` directive.
@ -995,7 +1243,7 @@ Named slots can also expose values. The `let:` directive goes on the element wit
``` ```
### &lt;svelte:self&gt; ### `<svelte:self>`
--- ---
@ -1016,10 +1264,10 @@ It cannot appear at the top level of your markup; it must be inside an if or eac
{/if} {/if}
``` ```
### &lt;svelte:component&gt; ### `<svelte:component>`
```sv ```sv
<svelte:component this={expression}> <svelte:component this={expression}/>
``` ```
--- ---
@ -1033,7 +1281,7 @@ If `this` is falsy, no component is rendered.
``` ```
### &lt;svelte:window&gt; ### `<svelte:window>`
```sv ```sv
<svelte:window on:event={handler}/> <svelte:window on:event={handler}/>
@ -1075,7 +1323,7 @@ All except `scrollX` and `scrollY` are readonly.
``` ```
### &lt;svelte:body&gt; ### `<svelte:body>`
```sv ```sv
<svelte:body on:event={handler}/> <svelte:body on:event={handler}/>
@ -1093,10 +1341,10 @@ As with `<svelte:window>`, this element allows you to add listeners to events on
``` ```
### &lt;svelte:head&gt; ### `<svelte:head>`
```sv ```sv
<svelte:head> <svelte:head>...</svelte:head>
``` ```
--- ---
@ -1110,10 +1358,10 @@ This element makes it possible to insert elements into `document.head`. During s
``` ```
### &lt;svelte:options&gt; ### `<svelte:options>`
```sv ```sv
<svelte:options option={value}> <svelte:options option={value}/>
``` ```
--- ---

@ -156,6 +156,8 @@ Like lifecycle functions, this must be called during component initialisation.
</script> </script>
``` ```
> Context is not inherently reactive. If you need reactive values in context then you can pass a store into context, which *will* be reactive.
#### `getContext` #### `getContext`
```js ```js
@ -212,39 +214,7 @@ Events dispatched from child components can be listened to in their parent. Any
### `svelte/store` ### `svelte/store`
The `svelte/store` module exports functions for creating [stores](tutorial/writable-stores). The `svelte/store` module exports functions for creating [stores](docs#4_Prefix_stores_with_$_to_access_their_values).
---
To be considered a store, an object must have a `subscribe` method that returns an `unsubscribe` function.
```js
const unsubscribe = store.subscribe(value => {
console.log(value);
}); // logs `value`
// later...
unsubscribe();
```
---
Stores have special significance inside Svelte components. Their values can be read by prefixing the store's name with the `$` character, which causes Svelte to set up subscriptions and unsubscriptions automatically during the component's lifecycle.
```html
<script>
import { count } from './stores.js';
function handleClick() {
// this is equivalent to count.update(n => n + 1)
$count += 1;
}
</script>
<button on:click={handleClick}>
Clicks: {$count}
</button>
```
#### `writable` #### `writable`
@ -257,7 +227,11 @@ store = writable(value: any, (set: (value: any) => void) => () => void)
--- ---
Creates a store with additional `set` and `update` methods. Function that creates a store which has values that can be set from 'outside' components. It gets created as an object with additional `set` and `update` methods.
`set` is a method that takes one argument which is the value to be set. The store value gets set to the value of the argument if the store value is not already equal to it.
`update` is a method that takes one argument which is a callback. The callback takes the existing store value as its argument and returns the new value to be set to the store.
```js ```js
import { writable } from 'svelte/store'; import { writable } from 'svelte/store';
@ -324,13 +298,13 @@ const time = readable(new Date(), set => {
store = derived(a, callback: (a: any) => any) store = derived(a, callback: (a: any) => any)
``` ```
```js ```js
store = derived(a, callback: (a: any, set: (value: any) => void) => void, initial_value: any) store = derived(a, callback: (a: any, set: (value: any) => void) => void | () => void, initial_value: any)
``` ```
```js ```js
store = derived([a, ...b], callback: ([a: any, ...b: any[]]) => any) store = derived([a, ...b], callback: ([a: any, ...b: any[]]) => any)
``` ```
```js ```js
store = derived([a, ...b], callback: ([a: any, ...b: any[]], set: (value: any) => void) => void, initial_value: any) store = derived([a, ...b], callback: ([a: any, ...b: any[]], set: (value: any) => void) => void | () => void, initial_value: any)
``` ```
--- ---
@ -361,6 +335,24 @@ const delayed = derived(a, ($a, set) => {
--- ---
If you return a function from the callback, it will be called when a) the callback runs again, or b) the last subscriber unsubscribes.
```js
import { derived } from 'svelte/store';
const tick = derived(frequency, ($frequency, set) => {
const interval = setInterval(() => {
set(Date.now());
}, 1000 / $frequency);
return () => {
clearInterval(interval);
};
}, 'one moment...');
```
---
In both cases, an array of arguments can be passed as the first argument instead of a single store. In both cases, an array of arguments can be passed as the first argument instead of a single store.
```js ```js
@ -407,7 +399,7 @@ Tweened stores update their values over a fixed duration. The following options
* `delay` (`number`, default 0) — milliseconds before starting * `delay` (`number`, default 0) — milliseconds before starting
* `duration` (`number`, default 400) — milliseconds the tween lasts * `duration` (`number`, default 400) — milliseconds the tween lasts
* `easing` (`function`, default `t => t`) — an [easing function](docs#svelte_easing) * `easing` (`function`, default `t => t`) — an [easing function](docs#svelte_easing)
* `interpolator` (`function`) — see below * `interpolate` (`function`) — see below
`store.set` and `store.update` can accept a second `options` argument that will override the options passed in upon instantiation. `store.set` and `store.update` can accept a second `options` argument that will override the options passed in upon instantiation.
@ -441,7 +433,7 @@ Out of the box, Svelte will interpolate between two numbers, two arrays or two o
--- ---
The `interpolator` option allows you to tween between *any* arbitrary values. It must be an `(a, b) => t => value` function, where `a` is the starting value, `b` is the target value, `t` is a number between 0 and 1, and `value` is the result. For example, we can use the [d3-interpolate](https://github.com/d3/d3-interpolate) package to smoothly interpolate between two colours. The `interpolate` option allows you to tween between *any* arbitrary values. It must be an `(a, b) => t => value` function, where `a` is the starting value, `b` is the target value, `t` is a number between 0 and 1, and `value` is the result. For example, we can use the [d3-interpolate](https://github.com/d3/d3-interpolate) package to smoothly interpolate between two colours.
```html ```html
<script> <script>
@ -503,10 +495,204 @@ Both `set` and `update` can take a second argument — an object with `hard` or
### `svelte/transition` ### `svelte/transition`
TODO The `svelte/transition` module exports six functions: `fade`, `fly`, `slide`, `scale`, `draw` and `crossfade`. They are for use with svelte [`transitions`](docs#Transitions).
#### `fade`
```sv
transition:fade={params}
```
```sv
in:fade={params}
```
```sv
out:fade={params}
```
---
Animates the opacity of an element from 0 to the current opacity for `in` transitions and from the current opacity to 0 for `out` transitions.
`fade` accepts the following parameters:
* `delay` (`number`, default 0) — milliseconds before starting
* `duration` (`number`, default 400) — milliseconds the transition lasts
You can see the `fade` transition in action in the [transition tutorial](tutorial/transition).
```html
<script>
import { fade } from 'svelte/transition';
</script>
{#if condition}
<div transition:fade="{{delay: 250, duration: 300}}">
fades in and out
</div>
{/if}
```
#### `fly`
```sv
transition:fly={params}
```
```sv
in:fly={params}
```
```sv
out:fly={params}
```
---
Animates the x and y positions and the opacity of an element. `in` transitions animate from an element's current (default) values to the provided values, passed as parameters. `out` transitions animate from the provided values to an element's default values.
`fly` accepts the following parameters:
* `delay` (`number`, default 0) — milliseconds before starting
* `duration` (`number`, default 400) — milliseconds the transition lasts
* `easing` (`function`, default `cubicOut`) — an [easing function](docs#svelte_easing)
* `x` (`number`, default 0) - the x offset to animate out to and in from
* `y` (`number`, default 0) - the y offset to animate out to and in from
* `opacity` (`number`, default 0) - the opacity value to animate out to and in from
You can see the `fly` transition in action in the [transition tutorial](tutorial/adding-parameters-to-transitions).
```html
<script>
import { fly } from 'svelte/transition';
import { quintOut } from 'svelte/easing';
</script>
{#if condition}
<div transition:fly="{{delay: 250, duration: 300, x: 100, y: 500, opacity: 0.5, easing: quintOut}}">
flies in and out
</div>
{/if}
```
#### `slide`
```sv
transition:slide={params}
```
```sv
in:slide={params}
```
```sv
out:slide={params}
```
---
Slides an element in and out.
`slide` accepts the following parameters:
* `delay` (`number`, default 0) — milliseconds before starting
* `duration` (`number`, default 400) — milliseconds the transition lasts
* `easing` (`function`, default `cubicOut`) — an [easing function](docs#svelte_easing)
```html
<script>
import { slide } from 'svelte/transition';
import { quintOut } from 'svelte/easing';
</script>
{#if condition}
<div transition:slide="{{delay: 250, duration: 300, easing: quintOut }}">
slides in and out
</div>
{/if}
```
#### `scale`
```sv
transition:scale={params}
```
```sv
in:scale={params}
```
```sv
out:scale={params}
```
---
Animates the opacity and scale of an element. `in` transitions animate from an element's current (default) values to the provided values, passed as parameters. `out` transitions animate from the provided values to an element's default values.
`scale` accepts the following parameters:
* `delay` (`number`, default 0) — milliseconds before starting
* `duration` (`number`, default 400) — milliseconds the transition lasts
* `easing` (`function`, default `cubicOut`) — an [easing function](docs#svelte_easing)
* `start` (`number`, default 0) - the scale value to animate out to and in from
* `opacity` (`number`, default 0) - the opacity value to animate out to and in from
```html
<script>
import { scale } from 'svelte/transition';
import { quintOut } from 'svelte/easing';
</script>
{#if condition}
<div transition:scale="{{duration: 500, delay: 500, opacity: 0.5, start: 0.5, easing: quintOut}}">
scales in and out
</div>
{/if}
```
#### `draw`
```sv
transition:draw={params}
```
```sv
in:draw={params}
```
```sv
out:draw={params}
```
---
Animates the stroke of an SVG element, like a snake in a tube. `in` transitions begin with the path invisible and draw the path to the screen over time. `out` transitions start in a visible state and gradually erase the path. `draw` only works with elements that have a `getTotalLength` method, like `<path>` and `<polyline>`.
`scale` accepts the following parameters:
* `delay` (`number`, default 0) — milliseconds before starting
* `speed` (`number`, default undefined) - the speed of the animation, see below.
* `duration` (`number` | `function`, default 800) — milliseconds the transition lasts
* `easing` (`function`, default `cubicInOut`) — an [easing function](docs#svelte_easing)
The `speed` parameter is a means of setting the duration of the transition relative to the path's length. It is modifier that is applied to the length of the path: `duration = length / speed`. A path that is 1000 pixels with a speed of 1 will have a duration of `1000ms`, setting the speed to `0.5` will halve that duration and setting it to `2` will double it.
```html
<script>
import { draw } from 'svelte/transition';
import { quintOut } from 'svelte/easing';
</script>
<svg viewBox="0 0 5 5" xmlns="http://www.w3.org/2000/svg">
{#if condition}
<path transition:draw="{{duration: 5000, delay: 500, easing: quintOut}}"
d="M2 1 h1 v1 h1 v1 h-1 v1 h-1 v-1 h-1 v-1 h1 z"
fill="none"
stroke="cornflowerblue"
stroke-width="0.1px"
stroke-linejoin="round"
/>
{/if}
</svg>
```
<!-- Crossfade is coming soon... -->
* fade, fly, slide, scale, draw
* crossfade...
### `svelte/animate` ### `svelte/animate`
@ -556,7 +742,23 @@ You can see a full example on the [animations tutorial](tutorial/animate)
### `svelte/easing` ### `svelte/easing`
* TODO could have nice little interactive widgets showing the different functions, maybe Easing functions specificy the rate of change over time and are useful when working with Svelte's built-in transitions and animations as well as the tweened and spring utilities. `svelte/easing` contains 31 named exports, a `linear` ease and 3 variants of 10 different easing functions: `in`, `out` and `inOut`.
You can explore the various eases using the [ease visualiser](examples#easing) in the [examples section](examples).
| ease | in | out | inOut |
| --- | --- | --- | --- |
| **back** | `backIn` | `backOut` | `backInOut` |
| **bounce** | `bounceIn` | `bounceOut` | `bounceInOut` |
| **circ** | `circIn` | `circOut` | `circInOut` |
| **cubic** | `cubicIn` | `cubicOut` | `cubicInOut` |
| **elastic** | `elasticIn` | `elasticOut` | `elasticInOut` |
| **expo** | `expoIn` | `expoOut` | `expoInOut` |
| **quad** | `quadIn` | `quadOut` | `quadInOut` |
| **quart** | `quartIn` | `quartOut` | `quartInOut` |
| **quint** | `quintIn` | `quintOut` | `quintInOut` |
| **sine** | `sineIn` | `sineOut` | `sineInOut` |
### `svelte/register` ### `svelte/register`

@ -308,8 +308,8 @@ const { code } = svelte.preprocess(source, [
```js ```js
walk(ast: Node, { walk(ast: Node, {
enter(node: Node, parent: Node)?: void, enter(node: Node, parent: Node, prop: string, index: number)?: void,
leave(node: Node, parent: Node)?: void leave(node: Node, parent: Node, prop: string, index: number)?: void
}) })
``` ```
@ -323,13 +323,13 @@ The walker takes an abstract syntax tree to walk and an object with two optional
```js ```js
const svelte = require('svelte/compiler'); const svelte = require('svelte/compiler');
svelte.walk(ast, { svelte.walk(ast, {
enter(node, parent) { enter(node, parent, prop, index) {
do_something(node); do_something(node);
if (should_skip_children(node)) { if (should_skip_children(node)) {
this.skip(); this.skip();
} }
}, },
leave(node, parent) { leave(node, parent, prop, index) {
do_something_else(node); do_something_else(node);
} }
}); });

@ -1,12 +1,12 @@
<script> <script>
import marked from 'marked'; import marked from 'marked';
let value = `Some words are *italic*, some are **bold**`; let text = `Some words are *italic*, some are **bold**`;
</script> </script>
<style> <style>
textarea { width: 100%; height: 200px; } textarea { width: 100%; height: 200px; }
</style> </style>
<textarea bind:value></textarea> <textarea bind:value={text}></textarea>
{@html marked(value)} {@html marked(text)}

@ -1,5 +1,5 @@
<script> <script>
import Eliza from 'elizanode'; import Eliza from 'elizabot';
import { beforeUpdate, afterUpdate } from 'svelte'; import { beforeUpdate, afterUpdate } from 'svelte';
let div; let div;

@ -1,8 +1,9 @@
<script> <script>
import { quintOut } from 'svelte/easing'; import { quintOut } from 'svelte/easing';
import crossfade from './crossfade.js'; // TODO put this in svelte/transition! import { crossfade } from 'svelte/transition';
import { flip } from 'svelte/animate';
const { send, receive } = crossfade({ const [send, receive] = crossfade({
fallback(node, params) { fallback(node, params) {
const style = getComputedStyle(node); const style = getComputedStyle(node);
const transform = style.transform === 'none' ? '' : style.transform; const transform = style.transform === 'none' ? '' : style.transform;
@ -43,12 +44,6 @@
function remove(todo) { function remove(todo) {
todos = todos.filter(t => t !== todo); todos = todos.filter(t => t !== todo);
} }
function handleKeydown(event) {
if (event.which === 13) {
addTodo(event.target);
}
}
</script> </script>
<style> <style>
@ -114,7 +109,7 @@
</style> </style>
<div class='board'> <div class='board'>
<input class="new-todo" placeholder="what needs to be done?" on:enter={add}> <input class="new-todo" placeholder="what needs to be done?" on:keydown="{event => event.which === 13 && add(this)}">
<div class='left'> <div class='left'>
<h2>todo</h2> <h2>todo</h2>
@ -122,6 +117,7 @@
<label <label
in:receive="{{key: todo.id}}" in:receive="{{key: todo.id}}"
out:send="{{key: todo.id}}" out:send="{{key: todo.id}}"
animate:flip
> >
<input type=checkbox bind:checked={todo.done}> <input type=checkbox bind:checked={todo.done}>
{todo.description} {todo.description}
@ -136,6 +132,7 @@
<label <label
in:receive="{{key: todo.id}}" in:receive="{{key: todo.id}}"
out:send="{{key: todo.id}}" out:send="{{key: todo.id}}"
animate:flip
> >
<input type=checkbox bind:checked={todo.done}> <input type=checkbox bind:checked={todo.done}>
{todo.description} {todo.description}

@ -1,65 +0,0 @@
import { quintOut } from 'svelte/easing';
export default function crossfade({ send, receive, fallback }) {
let requested = new Map();
let provided = new Map();
function crossfade(from, node) {
const to = node.getBoundingClientRect();
const dx = from.left - to.left;
const dy = from.top - to.top;
const style = getComputedStyle(node);
const transform = style.transform === 'none' ? '' : style.transform;
return {
duration: 400,
easing: quintOut,
css: (t, u) => `
opacity: ${t};
transform: ${transform} translate(${u * dx}px,${u * dy}px);
`
};
}
return {
send(node, params) {
provided.set(params.key, {
rect: node.getBoundingClientRect()
});
return () => {
if (requested.has(params.key)) {
const { rect } = requested.get(params.key);
requested.delete(params.key);
return crossfade(rect, node);
}
// if the node is disappearing altogether
// (i.e. wasn't claimed by the other list)
// then we need to supply an outro
provided.delete(params.key);
return fallback(node, params);
};
},
receive(node, params) {
requested.set(params.key, {
rect: node.getBoundingClientRect()
});
return () => {
if (provided.has(params.key)) {
const { rect } = provided.get(params.key);
provided.delete(params.key);
return crossfade(rect, node);
}
requested.delete(params.key);
return fallback(node, params);
};
}
};
}

@ -0,0 +1,106 @@
<script>
import { interpolateString as interpolate } from 'd3-interpolate';
import { tweened } from 'svelte/motion';
import Grid from './Grid.svelte';
import Controls from './Controls.svelte';
import { eases, types } from './eases.js';
let current_type = 'In';
let current_ease = 'sine';
let duration = 2000;
let current = eases.get(current_ease)[current_type];
let playing = false;
let width;
const ease_path = tweened(current.shape, { interpolate });
const time = tweened(0);
const value = tweened(1000);
async function runAnimations() {
playing = true;
value.set(1000, {duration: 0});
time.set(0, {duration: 0});
await ease_path.set(current.shape);
await Promise.all([
time.set(1000, {duration, easing: x => x}),
value.set(0, {duration, easing: current.fn})
]);
playing = false;
}
$: current = eases.get(current_ease)[current_type];
$: current && runAnimations();
</script>
<style>
.easing-vis {
display: flex;
max-height: 95%;
max-width: 800px;
margin: auto;
padding: 10px;
border: 1px solid #333;
border-radius: 2px;
padding: 20px;
}
svg {
width: 100%;
margin: 0 20px 0 0;
}
.graph {
transform: translate(200px,400px)
}
@media (max-width:600px) {
.easing-vis {
flex-direction: column;
max-height: calc(100% - 3rem);
}
}
</style>
<div bind:offsetWidth={width} class="easing-vis">
<svg viewBox="0 0 1400 1802">
<g class="canvas">
<Grid x={$time} y={$value}/>
<g class="graph">
<path
d={$ease_path}
stroke="#333"
stroke-width="2"
fill="none"
/>
<path d="M0,23.647C0,22.41 27.014,0.407 28.496,0.025C29.978,-0.357 69.188,3.744 70.104,4.744C71.02,5.745 71.02,41.499 70.104,42.5C69.188,43.501 29.978,47.601 28.496,47.219C27.014,46.837 0,24.884 0,23.647Z"
fill="#ff3e00"
style="transform: translate(1060px, {($value - 24)}px)"
/>
<circle
cx="{$time}"
cy="{$value}"
r="15"
fill="#ff3e00"
/>
</g>
</g>
</svg>
<Controls
{eases}
{types}
{playing}
{width}
bind:duration
bind:current_ease
bind:current_type
on:play={runAnimations}
/>
</div>

@ -0,0 +1,186 @@
<script>
import { createEventDispatcher } from 'svelte';
export let current_ease;
export let current_type;
export let eases;
export let types;
export let duration;
export let playing;
export let width;
const dispatch = createEventDispatcher();
$: mobile = width && width < 600;
</script>
<style>
.easing-sidebar {
width: 11em;
}
ul {
list-style: none;
padding: 0;
display: flex;
flex-direction: column;
align-items: flex-start;
font-size: 18px;
}
li {
padding: 5px 10px;
background: #eee;
border-radius: 2px;
margin: 3px 0;
cursor:pointer;
}
li:hover {
background: #676778;
color: white;
}
.selected {
background: #ff3e00;
color: white;
}
h3 {
margin: 0 10px 0 0;
}
h4 {
margin-bottom: 0;
}
select {
display: inline;
padding: 0.2em;
margin: 0;
}
.duration {
width: 100%;
display: flex;
align-items: center;
flex-wrap: wrap;
}
.duration span {
display: flex;
}
.duration input {
width: 80px;
margin: 10px 10px 10px 0 ;
}
.duration button {
margin: 10px 5px;
}
.duration .number {
width: 30px;
}
.duration .play {
margin: 0 5px 0 auto;
width: 100%;
}
@media (max-width:600px) {
.easing-types {
display: flex;
align-items: center;
margin-top: 10px;
}
.easing-sidebar {
width: 100%;
}
.duration .play {
margin-left: auto;
width: unset;
}
h3 {
font-size: 0.9em;
display: inline;
}
h3:nth-of-type(2) {
margin-left: auto;
}
ul li {
margin-right: 10px;
}
}
</style>
<div class="easing-sidebar">
<div class="easing-types">
<h3>Ease</h3>
{#if mobile}
<select bind:value={current_ease}>
{#each [...eases] as [name]}
<option
value={name}
class:selected={name === current_ease}
>
{name}
</option>
{/each}
</select>
{:else}
<ul>
{#each [...eases] as [name]}
<li
class:selected={name === current_ease}
on:click={() => current_ease = name}
>
{name}
</li>
{/each}
</ul>
{/if}
<h3>Type</h3>
{#if mobile }
<select bind:value={current_type}>
{#each types as [name, type]}
<option
value={type}
>
{name}
</option>
{/each}
</select>
{:else}
<ul>
{#each types as [name, type]}
<li
class:selected={type === current_type}
on:click={() => current_type = type}
>
{name}
</li>
{/each}
</ul>
{/if}
</div>
<h4>
Duration
</h4>
<div class="duration">
<span>
<input type="number" bind:value={duration} min="0" step="100"/>
<button class="number" on:click={() => duration -= 100}>-</button>
<button class="number" on:click={() => duration += 100}>+</button>
</span>
<button class="play" on:click={() => dispatch('play')}>
{playing ? 'Restart' : 'Play'}
</button>
</div>
</div>

@ -0,0 +1,62 @@
<script>
export let x, y;
</script>
<style>
.grid-line {
stroke:#ccc;
opacity: 0.5;
stroke-width: 2;
}
.grid-line-xy {
stroke: tomato;
stroke-width: 2;
}
</style>
<svelte:options namespace="svg" />
<rect
x=0
y=0
width=1400
height=1800
stroke=#ccc
style="opacity: 0.5"
fill=none
stroke-width=2
/>
{#each { length: 8 } as _, i}
{#if i < 6}
<path
d="M{(i+1) * 200} 0 L{(i+1)*200} 1802"
class="grid-line"
/>
{/if}
<path
d="M0 {(i+1) * 200} L1400 {(i+1)*200} "
class="grid-line"
/>
{/each}
<path
style="transform: translateX({x+200}px)"
d="M0 0 L0 1800"
class="grid-line-xy"
/>
<path
style="transform: translateY({y}px)"
d="M0 400 L1400 400"
class="grid-line-xy"
/>
<rect
x=200
y=400
width=1000
height=1000
stroke=#999
fill=none
stroke-width=2
/>

@ -0,0 +1,43 @@
import * as eases from 'svelte/easing';
const processed_eases = {};
for (const ease in eases) {
if (ease === "linear") {
processed_eases.linear = eases.linear;
} else {
const name = ease.replace(/In$|InOut$|Out$/, '');
const type = ease.match(/In$|InOut$|Out$/)[0];
if (!(name in processed_eases)) processed_eases[name] = {};
processed_eases[name][type] = {};
processed_eases[name][type].fn = eases[ease];
let shape = 'M0 1000';
for (let i = 1; i <= 1000; i++) {
shape = `${shape} L${(i / 1000) * 1000} ${1000 - eases[ease](i / 1000) * 1000} `;
processed_eases[name][type].shape = shape;
}
}
}
const sorted_eases = new Map([
['sine', processed_eases.sine],
['quad', processed_eases.quad],
['cubic', processed_eases.cubic],
['quart', processed_eases.quart],
['quint', processed_eases.quint],
['expo', processed_eases.expo],
['circ', processed_eases.circ],
['back', processed_eases.back],
['elastic', processed_eases.elastic],
['bounce', processed_eases.bounce],
]);
export const types = [
['Ease In', 'In'],
['Ease Out', 'Out'],
['Ease In Out', 'InOut']
];
export { sorted_eases as eases };

@ -0,0 +1,3 @@
{
"title": "Ease Visualiser"
}

@ -1,9 +1,13 @@
<script> <script>
import Modal from './Modal.svelte'; import Modal from './Modal.svelte';
let showModal = true; let showModal = false;
</script> </script>
<button on:click="{() => showModal = true}">
show modal
</button>
{#if showModal} {#if showModal}
<Modal on:close="{() => showModal = false}"> <Modal on:close="{() => showModal = false}">
<h2 slot="header"> <h2 slot="header">
@ -22,8 +26,4 @@
<a href="https://www.merriam-webster.com/dictionary/modal">merriam-webster.com</a> <a href="https://www.merriam-webster.com/dictionary/modal">merriam-webster.com</a>
</Modal> </Modal>
{:else}
<button on:click="{() => showModal = true}">
show modal
</button>
{/if} {/if}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save