Skip to content

Instantly share code, notes, and snippets.

@theetrain
Last active July 26, 2024 03:30
Show Gist options
  • Save theetrain/d05bb16ef92e5b09431b964472ff2f47 to your computer and use it in GitHub Desktop.
Save theetrain/d05bb16ef92e5b09431b964472ff2f47 to your computer and use it in GitHub Desktop.

Synchronizing checkbox groups

This showcases how to keep checkboxes in sync between two kinds of views. The views are interchangeable via a radio selection and CSS. Server-side, submitted values would need to be deduplicated by their name.

<!--
"Reacting to checkboxes" by Enrico
https://github.com/theetrain
Synchronizes checkbox state between two different views
of checkboxes.
-->
<script>
import Container from './Container.svelte'
import Check from './Check.svelte'
let items = $state([
{ name: 'earth', checked: false },
{ name: 'fire', checked: false },
{ name: 'wind', checked: false }
])
</script>
<pre>App items: {JSON.stringify(items.map(el => ({ [el.name]: el.checked })))}</pre>
<Container bind:items>
{#snippet tileSnippet(item, index)}
<Check label={item.name} name={item.name} bind:checked={items[index].checked} />
{/snippet}
</Container>
<script>
let { label, checked = $bindable(), ...rest } = $props()
</script>
<label>{label}
<input type="checkbox" {...rest} bind:checked />
</label>
<style>
label {
display: inline-block;
padding: 1rem;
background-color: #eee;
border: 1px solid black;
}
:global(body.dark) label {
background-color: black;
border: 1px solid white;
}
</style>
<script>
let { items = $bindable(), tileSnippet } = $props()
let count = $derived(items.filter(el => el.checked).length)
let container
</script>
<pre>Container: {JSON.stringify(items.map(el => ({ [el.name]: el.checked })))}</pre>
<p>Count {count}</p>
<div bind:this={container} class="container">
<label>tile view<input type="radio" name="toggle" value="tile" checked /></label>
<label>list view<input type="radio" name="toggle" value="list" /></label>
<ul data-view="list">
{#each items as item,i}
<li>
<label>{item.name}<input type="checkbox" name={item.name} bind:checked={items[i].checked} /></label>
</li>
{/each}
</ul>
<div data-view="tile">
{#each items as item, index}
{@render tileSnippet(item, index)}
{/each}
</div>
</div>
<style>
.container {
margin-top: 1rem;
}
.container:has([value="tile"]:checked) [data-view="list"] {
display: none;
}
.container:has([value="list"]:checked) [data-view="tile"] {
display: none;
}
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment