mirror of https://github.com/sveltejs/svelte
commit
e7f54679b5
@ -0,0 +1,6 @@
|
||||
<script>
|
||||
import { name, greeting } from './stores.js';
|
||||
</script>
|
||||
|
||||
<h1>{$greeting}</h1>
|
||||
<input value={$name}>
|
@ -0,0 +1,8 @@
|
||||
import { writable, derived } from 'svelte/store';
|
||||
|
||||
export const name = writable('world');
|
||||
|
||||
export const greeting = derived(
|
||||
name,
|
||||
$name => `Hello ${$name}!`
|
||||
);
|
@ -0,0 +1,10 @@
|
||||
<script>
|
||||
import { name, greeting } from './stores.js';
|
||||
</script>
|
||||
|
||||
<h1>{$greeting}</h1>
|
||||
<input bind:value={$name}>
|
||||
|
||||
<button on:click="{() => $name += '!'}">
|
||||
Add exclamation mark!
|
||||
</button>
|
@ -0,0 +1,8 @@
|
||||
import { writable, derived } from 'svelte/store';
|
||||
|
||||
export const name = writable('world');
|
||||
|
||||
export const greeting = derived(
|
||||
name,
|
||||
$name => `Hello ${$name}!`
|
||||
);
|
@ -0,0 +1,23 @@
|
||||
---
|
||||
title: Store bindings
|
||||
---
|
||||
|
||||
If a store is writable — i.e. it has a `set` method — you can bind to its value, just as you can bind to local component state.
|
||||
|
||||
In this example we have a writable store `name` and a derived store `greeting`. Update the `<input>` element:
|
||||
|
||||
```html
|
||||
<input bind:value={$name}>
|
||||
```
|
||||
|
||||
Changing the input value will now update `name` and all its dependents.
|
||||
|
||||
We can also assign directly to store values inside a component. Add a `<button>` element:
|
||||
|
||||
```html
|
||||
<button on:click="{() => $name += '!'}">
|
||||
Add exclamation mark!
|
||||
</button>
|
||||
```
|
||||
|
||||
The `$name += '!'` assignment is equivalent to `name.set($name + '!')`.
|
@ -1,146 +0,0 @@
|
||||
<script>
|
||||
import { quintOut } from 'svelte/easing';
|
||||
import crossfade from './crossfade.js'; // TODO put this in svelte/transition!
|
||||
|
||||
const { send, receive } = crossfade({
|
||||
fallback(node, params) {
|
||||
const style = getComputedStyle(node);
|
||||
const transform = style.transform === 'none' ? '' : style.transform;
|
||||
|
||||
return {
|
||||
duration: 600,
|
||||
easing: quintOut,
|
||||
css: t => `
|
||||
transform: ${transform} scale(${t});
|
||||
opacity: ${t}
|
||||
`
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
let todos = [
|
||||
{ id: 1, done: false, description: 'write some docs' },
|
||||
{ id: 2, done: false, description: 'start writing JSConf talk' },
|
||||
{ id: 3, done: true, description: 'buy some milk' },
|
||||
{ id: 4, done: false, description: 'mow the lawn' },
|
||||
{ id: 5, done: false, description: 'feed the turtle' },
|
||||
{ id: 6, done: false, description: 'fix some bugs' },
|
||||
];
|
||||
|
||||
let uid = todos.length + 1;
|
||||
|
||||
function add(input) {
|
||||
const todo = {
|
||||
id: uid++,
|
||||
done: false,
|
||||
description: input.value
|
||||
};
|
||||
|
||||
todos = [todo, ...todos];
|
||||
input.value = '';
|
||||
}
|
||||
|
||||
function remove(todo) {
|
||||
todos = todos.filter(t => t !== todo);
|
||||
}
|
||||
|
||||
function handleKeydown(event) {
|
||||
if (event.which === 13) {
|
||||
addTodo(event.target);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.new-todo {
|
||||
font-size: 1.4em;
|
||||
width: 100%;
|
||||
margin: 2em 0 1em 0;
|
||||
}
|
||||
|
||||
.board {
|
||||
max-width: 36em;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.left, .right {
|
||||
float: left;
|
||||
width: 50%;
|
||||
padding: 0 1em 0 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 2em;
|
||||
font-weight: 200;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
label {
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: block;
|
||||
font-size: 1em;
|
||||
line-height: 1;
|
||||
padding: 0.5em;
|
||||
margin: 0 auto 0.5em auto;
|
||||
border-radius: 2px;
|
||||
background-color: #eee;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
input { margin: 0 }
|
||||
|
||||
.right label {
|
||||
background-color: rgb(180,240,100);
|
||||
}
|
||||
|
||||
button {
|
||||
float: right;
|
||||
height: 1em;
|
||||
box-sizing: border-box;
|
||||
padding: 0 0.5em;
|
||||
line-height: 1;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: rgb(170,30,30);
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
label:hover button {
|
||||
opacity: 1;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class='board'>
|
||||
<input class="new-todo" placeholder="what needs to be done?" on:enter={add}>
|
||||
|
||||
<div class='left'>
|
||||
<h2>todo</h2>
|
||||
{#each todos.filter(t => !t.done) as todo (todo.id)}
|
||||
<label
|
||||
in:receive="{{key: todo.id}}"
|
||||
out:send="{{key: todo.id}}"
|
||||
>
|
||||
<input type=checkbox bind:checked={todo.done}>
|
||||
{todo.description}
|
||||
<button on:click="{() => remove(todo)}">x</button>
|
||||
</label>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class='right'>
|
||||
<h2>done</h2>
|
||||
{#each todos.filter(t => t.done) as todo (todo.id)}
|
||||
<label
|
||||
in:receive="{{key: todo.id}}"
|
||||
out:send="{{key: todo.id}}"
|
||||
>
|
||||
<input type=checkbox bind:checked={todo.done}>
|
||||
{todo.description}
|
||||
<button on:click="{() => remove(todo)}">x</button>
|
||||
</label>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
@ -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);
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
@ -1,146 +0,0 @@
|
||||
<script>
|
||||
import { quintOut } from 'svelte/easing';
|
||||
import crossfade from './crossfade.js'; // TODO put this in svelte/transition!
|
||||
|
||||
const { send, receive } = crossfade({
|
||||
fallback(node, params) {
|
||||
const style = getComputedStyle(node);
|
||||
const transform = style.transform === 'none' ? '' : style.transform;
|
||||
|
||||
return {
|
||||
duration: 600,
|
||||
easing: quintOut,
|
||||
css: t => `
|
||||
transform: ${transform} scale(${t});
|
||||
opacity: ${t}
|
||||
`
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
let todos = [
|
||||
{ id: 1, done: false, description: 'write some docs' },
|
||||
{ id: 2, done: false, description: 'start writing JSConf talk' },
|
||||
{ id: 3, done: true, description: 'buy some milk' },
|
||||
{ id: 4, done: false, description: 'mow the lawn' },
|
||||
{ id: 5, done: false, description: 'feed the turtle' },
|
||||
{ id: 6, done: false, description: 'fix some bugs' },
|
||||
];
|
||||
|
||||
let uid = todos.length + 1;
|
||||
|
||||
function add(input) {
|
||||
const todo = {
|
||||
id: uid++,
|
||||
done: false,
|
||||
description: input.value
|
||||
};
|
||||
|
||||
todos = [todo, ...todos];
|
||||
input.value = '';
|
||||
}
|
||||
|
||||
function remove(todo) {
|
||||
todos = todos.filter(t => t !== todo);
|
||||
}
|
||||
|
||||
function handleKeydown(event) {
|
||||
if (event.which === 13) {
|
||||
addTodo(event.target);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.new-todo {
|
||||
font-size: 1.4em;
|
||||
width: 100%;
|
||||
margin: 2em 0 1em 0;
|
||||
}
|
||||
|
||||
.board {
|
||||
max-width: 36em;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.left, .right {
|
||||
float: left;
|
||||
width: 50%;
|
||||
padding: 0 1em 0 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 2em;
|
||||
font-weight: 200;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
label {
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: block;
|
||||
font-size: 1em;
|
||||
line-height: 1;
|
||||
padding: 0.5em;
|
||||
margin: 0 auto 0.5em auto;
|
||||
border-radius: 2px;
|
||||
background-color: #eee;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
input { margin: 0 }
|
||||
|
||||
.right label {
|
||||
background-color: rgb(180,240,100);
|
||||
}
|
||||
|
||||
button {
|
||||
float: right;
|
||||
height: 1em;
|
||||
box-sizing: border-box;
|
||||
padding: 0 0.5em;
|
||||
line-height: 1;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: rgb(170,30,30);
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
label:hover button {
|
||||
opacity: 1;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class='board'>
|
||||
<input class="new-todo" placeholder="what needs to be done?" on:enter={add}>
|
||||
|
||||
<div class='left'>
|
||||
<h2>todo</h2>
|
||||
{#each todos.filter(t => !t.done) as todo (todo.id)}
|
||||
<label
|
||||
in:receive="{{key: todo.id}}"
|
||||
out:send="{{key: todo.id}}"
|
||||
>
|
||||
<input type=checkbox bind:checked={todo.done}>
|
||||
{todo.description}
|
||||
<button on:click="{() => remove(todo)}">x</button>
|
||||
</label>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class='right'>
|
||||
<h2>done</h2>
|
||||
{#each todos.filter(t => t.done) as todo (todo.id)}
|
||||
<label
|
||||
in:receive="{{key: todo.id}}"
|
||||
out:send="{{key: todo.id}}"
|
||||
>
|
||||
<input type=checkbox bind:checked={todo.done}>
|
||||
{todo.description}
|
||||
<button on:click="{() => remove(todo)}">x</button>
|
||||
</label>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
@ -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);
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
---
|
||||
title: Deferred transitions
|
||||
---
|
||||
|
||||
A particularly powerful feature of Svelte's transition engine is the ability to *defer* transitions, so that they can be coordinated between multiple elements.
|
||||
|
||||
TODO https://github.com/sveltejs/svelte/issues/2159
|
@ -0,0 +1,32 @@
|
||||
<script>
|
||||
import { slide } from 'svelte/transition';
|
||||
|
||||
let showItems = true;
|
||||
let i = 5;
|
||||
let items = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten'];
|
||||
</script>
|
||||
|
||||
<style>
|
||||
div {
|
||||
padding: 0.5em 0;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
</style>
|
||||
|
||||
<label>
|
||||
<input type="checkbox" bind:checked={showItems}>
|
||||
show list
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<input type="range" bind:value={i} max=10>
|
||||
|
||||
</label>
|
||||
|
||||
{#if showItems}
|
||||
{#each items.slice(0, i) as item}
|
||||
<div transition:slide>
|
||||
{item}
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
@ -0,0 +1,32 @@
|
||||
<script>
|
||||
import { slide } from 'svelte/transition';
|
||||
|
||||
let showItems = true;
|
||||
let i = 5;
|
||||
let items = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten'];
|
||||
</script>
|
||||
|
||||
<style>
|
||||
div {
|
||||
padding: 0.5em 0;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
</style>
|
||||
|
||||
<label>
|
||||
<input type="checkbox" bind:checked={showItems}>
|
||||
show list
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<input type="range" bind:value={i} max=10>
|
||||
|
||||
</label>
|
||||
|
||||
{#if showItems}
|
||||
{#each items.slice(0, i) as item}
|
||||
<div transition:slide|local>
|
||||
{item}
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
@ -0,0 +1,15 @@
|
||||
---
|
||||
title: Local transitions
|
||||
---
|
||||
|
||||
Ordinarily, transitions will play on elements when any container block is added or destroyed. In the example here, toggling the visibility of the entire list also applies transitions to individual list elements.
|
||||
|
||||
Instead, we'd like transitions to play only when individual items are added and removed — on other words, when the user drags the slider.
|
||||
|
||||
We can achieve this with a *local* transition, which only plays when the immediate parent block is added or removed:
|
||||
|
||||
```html
|
||||
<div transition:slide|local>
|
||||
{item}
|
||||
</div>
|
||||
```
|
@ -0,0 +1,148 @@
|
||||
<script>
|
||||
import { quintOut } from 'svelte/easing';
|
||||
import { crossfade } from 'svelte/transition';
|
||||
|
||||
const [send, receive] = crossfade({
|
||||
duration: d => Math.sqrt(d * 200),
|
||||
|
||||
fallback(node, params) {
|
||||
const style = getComputedStyle(node);
|
||||
const transform = style.transform === 'none' ? '' : style.transform;
|
||||
|
||||
return {
|
||||
duration: 600,
|
||||
easing: quintOut,
|
||||
css: t => `
|
||||
transform: ${transform} scale(${t});
|
||||
opacity: ${t}
|
||||
`
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
let uid = 1;
|
||||
|
||||
let todos = [
|
||||
{ id: uid++, done: false, description: 'write some docs' },
|
||||
{ id: uid++, done: false, description: 'start writing blog post' },
|
||||
{ id: uid++, done: true, description: 'buy some milk' },
|
||||
{ id: uid++, done: false, description: 'mow the lawn' },
|
||||
{ id: uid++, done: false, description: 'feed the turtle' },
|
||||
{ id: uid++, done: false, description: 'fix some bugs' },
|
||||
];
|
||||
|
||||
function add(input) {
|
||||
const todo = {
|
||||
id: uid++,
|
||||
done: false,
|
||||
description: input.value
|
||||
};
|
||||
|
||||
todos = [todo, ...todos];
|
||||
input.value = '';
|
||||
}
|
||||
|
||||
function remove(todo) {
|
||||
todos = todos.filter(t => t !== todo);
|
||||
}
|
||||
|
||||
function mark(todo, done) {
|
||||
todo.done = done;
|
||||
remove(todo);
|
||||
todos = todos.concat(todo);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class='board'>
|
||||
<input
|
||||
placeholder="what needs to be done?"
|
||||
on:keydown={e => e.which === 13 && add(e.target)}
|
||||
>
|
||||
|
||||
<div class='left'>
|
||||
<h2>todo</h2>
|
||||
{#each todos.filter(t => !t.done) as todo (todo.id)}
|
||||
<label>
|
||||
<input type=checkbox on:change={() => mark(todo, true)}>
|
||||
{todo.description}
|
||||
<button on:click="{() => remove(todo)}">remove</button>
|
||||
</label>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class='right'>
|
||||
<h2>done</h2>
|
||||
{#each todos.filter(t => t.done) as todo (todo.id)}
|
||||
<label class="done">
|
||||
<input type=checkbox checked on:change={() => mark(todo, false)}>
|
||||
{todo.description}
|
||||
<button on:click="{() => remove(todo)}">remove</button>
|
||||
</label>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.board {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-gap: 1em;
|
||||
max-width: 36em;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.board > input {
|
||||
font-size: 1.4em;
|
||||
grid-column: 1/3;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 2em;
|
||||
font-weight: 200;
|
||||
user-select: none;
|
||||
margin: 0 0 0.5em 0;
|
||||
}
|
||||
|
||||
label {
|
||||
position: relative;
|
||||
line-height: 1.2;
|
||||
padding: 0.5em 2.5em 0.5em 2em;
|
||||
margin: 0 0 0.5em 0;
|
||||
border-radius: 2px;
|
||||
user-select: none;
|
||||
border: 1px solid hsl(240, 8%, 70%);
|
||||
background-color:hsl(240, 8%, 93%);
|
||||
color: #333;
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
position: absolute;
|
||||
left: 0.5em;
|
||||
top: 0.6em;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.done {
|
||||
border: 1px solid hsl(240, 8%, 90%);
|
||||
background-color:hsl(240, 8%, 98%);
|
||||
}
|
||||
|
||||
button {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0.2em;
|
||||
width: 2em;
|
||||
height: 100%;
|
||||
background: no-repeat 50% 50% url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23676778' d='M12,2C17.53,2 22,6.47 22,12C22,17.53 17.53,22 12,22C6.47,22 2,17.53 2,12C2,6.47 6.47,2 12,2M17,7H14.5L13.5,6H10.5L9.5,7H7V9H17V7M9,18H15A1,1 0 0,0 16,17V10H8V17A1,1 0 0,0 9,18Z'%3E%3C/path%3E%3C/svg%3E");
|
||||
background-size: 1.4em 1.4em;
|
||||
border: none;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
text-indent: -9999px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
label:hover button {
|
||||
opacity: 1;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,155 @@
|
||||
<script>
|
||||
import { quintOut } from 'svelte/easing';
|
||||
import { crossfade } from 'svelte/transition';
|
||||
|
||||
const [send, receive] = crossfade({
|
||||
duration: d => Math.sqrt(d * 200),
|
||||
|
||||
fallback(node, params) {
|
||||
const style = getComputedStyle(node);
|
||||
const transform = style.transform === 'none' ? '' : style.transform;
|
||||
|
||||
return {
|
||||
duration: 600,
|
||||
easing: quintOut,
|
||||
css: t => `
|
||||
transform: ${transform} scale(${t});
|
||||
opacity: ${t}
|
||||
`
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
let uid = 1;
|
||||
|
||||
let todos = [
|
||||
{ id: uid++, done: false, description: 'write some docs' },
|
||||
{ id: uid++, done: false, description: 'start writing blog post' },
|
||||
{ id: uid++, done: true, description: 'buy some milk' },
|
||||
{ id: uid++, done: false, description: 'mow the lawn' },
|
||||
{ id: uid++, done: false, description: 'feed the turtle' },
|
||||
{ id: uid++, done: false, description: 'fix some bugs' },
|
||||
];
|
||||
|
||||
function add(input) {
|
||||
const todo = {
|
||||
id: uid++,
|
||||
done: false,
|
||||
description: input.value
|
||||
};
|
||||
|
||||
todos = [todo, ...todos];
|
||||
input.value = '';
|
||||
}
|
||||
|
||||
function remove(todo) {
|
||||
todos = todos.filter(t => t !== todo);
|
||||
}
|
||||
|
||||
function mark(todo, done) {
|
||||
todo.done = done;
|
||||
remove(todo);
|
||||
todos = todos.concat(todo);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class='board'>
|
||||
<input
|
||||
placeholder="what needs to be done?"
|
||||
on:keydown={e => e.which === 13 && add(e.target)}
|
||||
>
|
||||
|
||||
<div class='left'>
|
||||
<h2>todo</h2>
|
||||
{#each todos.filter(t => !t.done) as todo (todo.id)}
|
||||
<label
|
||||
in:receive="{{key: todo.id}}"
|
||||
out:send="{{key: todo.id}}"
|
||||
>
|
||||
<input type=checkbox on:change={() => mark(todo, true)}>
|
||||
{todo.description}
|
||||
<button on:click="{() => remove(todo)}">remove</button>
|
||||
</label>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class='right'>
|
||||
<h2>done</h2>
|
||||
{#each todos.filter(t => t.done) as todo (todo.id)}
|
||||
<label
|
||||
class="done"
|
||||
in:receive="{{key: todo.id}}"
|
||||
out:send="{{key: todo.id}}"
|
||||
>
|
||||
<input type=checkbox checked on:change={() => mark(todo, false)}>
|
||||
{todo.description}
|
||||
<button on:click="{() => remove(todo)}">remove</button>
|
||||
</label>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.board {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-gap: 1em;
|
||||
max-width: 36em;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.board > input {
|
||||
font-size: 1.4em;
|
||||
grid-column: 1/3;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 2em;
|
||||
font-weight: 200;
|
||||
user-select: none;
|
||||
margin: 0 0 0.5em 0;
|
||||
}
|
||||
|
||||
label {
|
||||
position: relative;
|
||||
line-height: 1.2;
|
||||
padding: 0.5em 2.5em 0.5em 2em;
|
||||
margin: 0 0 0.5em 0;
|
||||
border-radius: 2px;
|
||||
user-select: none;
|
||||
border: 1px solid hsl(240, 8%, 70%);
|
||||
background-color:hsl(240, 8%, 93%);
|
||||
color: #333;
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
position: absolute;
|
||||
left: 0.5em;
|
||||
top: 0.6em;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.done {
|
||||
border: 1px solid hsl(240, 8%, 90%);
|
||||
background-color:hsl(240, 8%, 98%);
|
||||
}
|
||||
|
||||
button {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0.2em;
|
||||
width: 2em;
|
||||
height: 100%;
|
||||
background: no-repeat 50% 50% url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23676778' d='M12,2C17.53,2 22,6.47 22,12C22,17.53 17.53,22 12,22C6.47,22 2,17.53 2,12C2,6.47 6.47,2 12,2M17,7H14.5L13.5,6H10.5L9.5,7H7V9H17V7M9,18H15A1,1 0 0,0 16,17V10H8V17A1,1 0 0,0 9,18Z'%3E%3C/path%3E%3C/svg%3E");
|
||||
background-size: 1.4em 1.4em;
|
||||
border: none;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
text-indent: -9999px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
label:hover button {
|
||||
opacity: 1;
|
||||
}
|
||||
</style>
|
@ -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);
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
@ -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);
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
Loading…
Reference in new issue