Skip to content

Commit 392189d

Browse files
authored
removeAll event timing: clear selected before events fire (#313)
* removeAll event timing: clear selected before events fire (closes #300) * update package.json deps and refactor demo navigation - drop writable store for demo navigation, use `demo_pages` array from newly created (demoos)/index.ts - simplify code fence buttons in +layout.svelte with <CopyButton global /> - update persistent demo page to use sessionStorage directly, not through persisted_store * pnpm remove globals @iconify/svelte 1st not needed, 2nd replaced with SVG symbols in app.html * tweak toc styles
1 parent c051664 commit 392189d

File tree

14 files changed

+94
-90
lines changed

14 files changed

+94
-90
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ repos:
3838
exclude: changelog\.md
3939

4040
- repo: https://github.com/pre-commit/mirrors-eslint
41-
rev: v9.27.0
41+
rev: v9.28.0
4242
hooks:
4343
- id: eslint
4444
types: [file]
@@ -48,7 +48,6 @@ repos:
4848
- 'typescript-eslint'
4949
- eslint
5050
- eslint-plugin-svelte
51-
- globals
5251
- svelte
5352
- typescript
5453
- '@stylistic/eslint-plugin'

package.json

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,16 @@
2626
"svelte": "^5.8.0"
2727
},
2828
"devDependencies": {
29-
"@iconify/svelte": "^5.0.0",
30-
"@playwright/test": "^1.52.0",
31-
"@stylistic/eslint-plugin": "^4.2.0",
29+
"@playwright/test": "^1.53.0",
30+
"@stylistic/eslint-plugin": "^4.4.1",
3231
"@sveltejs/adapter-static": "^3.0.8",
33-
"@sveltejs/kit": "^2.21.1",
32+
"@sveltejs/kit": "^2.21.5",
3433
"@sveltejs/package": "2.3.11",
35-
"@sveltejs/vite-plugin-svelte": "^5.0.3",
36-
"@types/node": "^22.15.20",
37-
"@vitest/coverage-v8": "^3.1.4",
38-
"eslint": "^9.27.0",
39-
"eslint-plugin-svelte": "^3.8.2",
40-
"globals": "^16.1.0",
34+
"@sveltejs/vite-plugin-svelte": "^5.1.0",
35+
"@types/node": "^24.0.1",
36+
"@vitest/coverage-v8": "^3.2.3",
37+
"eslint": "^9.28.0",
38+
"eslint-plugin-svelte": "^3.9.2",
4139
"hastscript": "^9.0.1",
4240
"highlight.js": "^11.11.1",
4341
"jsdom": "^26.1.0",
@@ -47,16 +45,16 @@
4745
"prettier-plugin-svelte": "^3.4.0",
4846
"rehype-autolink-headings": "^7.1.0",
4947
"rehype-slug": "^6.0.0",
50-
"svelte": "^5.32.1",
48+
"svelte": "^5.34.1",
5149
"svelte-check": "^4.2.1",
5250
"svelte-preprocess": "^6.0.3",
5351
"svelte-toc": "^0.6.1",
54-
"svelte-zoo": "^0.4.18",
52+
"svelte-zoo": "^0.4.19",
5553
"svelte2tsx": "^0.7.39",
5654
"typescript": "5.8.3",
57-
"typescript-eslint": "^8.32.1",
55+
"typescript-eslint": "^8.34.0",
5856
"vite": "^6.3.5",
59-
"vitest": "^3.1.4"
57+
"vitest": "^3.2.3"
6058
},
6159
"keywords": [
6260
"svelte",

src/app.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,10 @@ nav:has(+ div.code-example) > a,
149149
section > aside > a.btn {
150150
margin: 0;
151151
}
152+
153+
/* target reusable icons like <svg><use href="#icon-info"></use></svg> */
154+
svg:has(use:only-child) {
155+
width: 1em;
156+
height: 1em;
157+
vertical-align: middle;
158+
}

src/app.html

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,30 @@
4343
d="M7.775 3.275a.75.75 0 0 0 1.06 1.06l1.25-1.25a2 2 0 1 1 2.83 2.83l-2.5 2.5a2 2 0 0 1-2.83 0 .75.75 0 0 0-1.06 1.06 3.5 3.5 0 0 0 4.95 0l2.5-2.5a3.5 3.5 0 0 0-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 0 1 0-2.83l2.5-2.5a2 2 0 0 1 2.83 0 .75.75 0 0 0 1.06-1.06 3.5 3.5 0 0 0-4.95 0l-2.5 2.5a3.5 3.5 0 0 0 4.95 4.95l1.25-1.25a.75.75 0 0 0-1.06-1.06l-1.25 1.25a2 2 0 0 1-2.83 0z"
4444
/>
4545
</symbol>
46+
47+
<symbol viewBox="0 0 16 16" id="octicon-mark-github">
48+
<!-- Icon from Octicons by GitHub - https://github.com/primer/octicons/blob/main/LICENSE -->
49+
<path
50+
fill="currentColor"
51+
d="M8 0c4.42 0 8 3.58 8 8a8.01 8.01 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38c0-.27.01-1.13.01-2.2c0-.75-.25-1.23-.54-1.48c1.78-.2 3.65-.88 3.65-3.95c0-.88-.31-1.59-.82-2.15c.08-.2.36-1.02-.08-2.12c0 0-.67-.22-2.2.82c-.64-.18-1.32-.27-2-.27s-1.36.09-2 .27c-1.53-1.03-2.2-.82-2.2-.82c-.44 1.1-.16 1.92-.08 2.12c-.51.56-.82 1.28-.82 2.15c0 3.06 1.86 3.75 3.64 3.95c-.23.2-.44.55-.51 1.07c-.46.21-1.61.55-2.33-.66c-.15-.24-.6-.83-1.23-.82c-.67.01-.27.38.01.53c.34.19.73.9.82 1.13c.16.45.68 1.31 2.69.94c0 .67.01 1.3.01 1.49c0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8"
52+
></path>
53+
</symbol>
54+
55+
<symbol viewBox="0 0 24 24" id="material-symbols-auto-awesome">
56+
<!-- Icon from Material Symbols by Google - https://github.com/google/material-design-icons/blob/master/LICENSE -->
57+
<path
58+
fill="currentColor"
59+
d="m19 9l-1.25-2.75L15 5l2.75-1.25L19 1l1.25 2.75L23 5l-2.75 1.25L19 9Zm0 14l-1.25-2.75L15 19l2.75-1.25L19 15l1.25 2.75L23 19l-2.75 1.25L19 23ZM9 20l-2.5-5.5L1 12l5.5-2.5L9 4l2.5 5.5L17 12l-5.5 2.5L9 20Z"
60+
></path>
61+
</symbol>
62+
63+
<symbol viewBox="0 0 32 32" id="carbon-edit">
64+
<!-- Icon from Carbon by IBM - undefined -->
65+
<path
66+
fill="currentColor"
67+
d="M2 26h28v2H2zM25.4 9c.8-.8.8-2 0-2.8l-3.6-3.6c-.8-.8-2-.8-2.8 0l-15 15V24h6.4zm-5-5L24 7.6l-3 3L17.4 7zM6 22v-3.6l10-10l3.6 3.6l-10 10z"
68+
></path>
69+
</symbol>
4670
</svg>
4771

4872
<div>%sveltekit.body%</div>

src/lib/MultiSelect.svelte

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -403,10 +403,12 @@
403403
404404
function remove_all(event: Event) {
405405
event.stopPropagation()
406+
selected = [] // Set selected first
407+
searchText = ``
408+
409+
// Now trigger change events
406410
onremoveAll?.({ options: selected })
407411
onchange?.({ options: selected, type: `removeAll` })
408-
selected = []
409-
searchText = ``
410412
}
411413
412414
let is_selected = $derived((label: string | number) =>

src/routes/(demos)/+layout.svelte

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
import { page } from '$app/state'
33
import { name } from '$root/package.json'
44
import { DemoNav } from '$site'
5-
import { demos } from '$site/stores'
65
import type { Snippet } from 'svelte'
76
import { PrevNext } from 'svelte-zoo'
7+
import { demo_pages } from '.'
88
99
interface Props {
1010
children?: Snippet
@@ -21,12 +21,11 @@
2121

2222
{@render children?.()}
2323

24-
<!-- TODO pass onkeyup=null in next version of svelte-zoo -->
2524
<PrevNext
26-
items={$demos}
25+
items={demo_pages}
2726
current={page.url.pathname}
2827
style="max-width: var(--main-max-width); margin: auto;"
29-
on_keyup={() => ({})}
28+
on_keyup={null}
3029
/>
3130
</main>
3231

src/routes/(demos)/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export const routes = Object.keys(
2+
import.meta.glob(`./**/+page.{svx,svelte,md}`),
3+
).map((filename) => {
4+
const parts = filename.split(`/`).filter((part) => !part.startsWith(`(`)) // remove hidden route segments
5+
return { route: `/${parts.slice(1, -1).join(`/`)}`, filename }
6+
})
7+
8+
if (routes.length < 3) {
9+
console.error(`Too few demo routes found: ${routes.length}`)
10+
}
11+
12+
export const demo_pages = routes.map(({ route }) => route)

src/routes/(demos)/persistent/+page.md

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
1-
<script lang="ts">
2-
import hljs from 'highlight.js/lib/common'
3-
import 'highlight.js/styles/vs2015.css'
4-
import store_src from '$site/stores.ts?raw'
5-
</script>
6-
71
## Page-Reload Persistent MultiSelect
82

9-
`language_store` is a Svelte [`writable`](https://svelte.dev/docs/svelte/svelte-store#writable) that's bound to the browser's `sessionStorage`. This example shows how MultiSelect retains its `selected` state on page reload.
3+
This example shows how to combine MultiSelect with `sessionStorage` to persist the `selected` state across page reloads.
104

115
<br />
126

@@ -15,20 +9,27 @@
159
import MultiSelect from '$lib'
1610
import { languages } from '$site/options'
1711
import { LanguageSnippet } from '$site'
18-
import { language_store } from '$site/stores'
12+
import { onMount } from 'svelte'
13+
14+
let selected = $state([])
15+
16+
onMount(() => {
17+
const stored = sessionStorage[`languages`]
18+
selected = stored ? JSON.parse(stored) : `Python TypeScript C Haskell`.split(` `)
19+
})
20+
21+
$effect(() => {
22+
if (sessionStorage) sessionStorage[`languages`] = JSON.stringify(selected)
23+
})
1924
</script>
2025
2126
<MultiSelect
2227
options={languages}
2328
placeholder="What languages do you know?"
24-
bind:selected={$language_store}
29+
bind:selected
2530
>
2631
{#snippet selectedItem({ idx, option })}
2732
<LanguageSnippet {option} {idx} />
2833
{/snippet}
2934
</MultiSelect>
3035
```
31-
32-
`language_store` uses custom initialization logic and a wrapper around `set` method to update `sessionStorage` on new values:
33-
34-
<pre><code>{@html hljs.highlight(store_src, { language: 'typescript' }).value}</code></pre>

src/routes/+error.svelte

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
<script lang="ts">
22
import { page } from '$app/state'
33
import { homepage, name } from '$root/package.json'
4-
import Icon from '@iconify/svelte'
54
65
let online: boolean = $state(false)
76
</script>
@@ -25,8 +24,8 @@
2524
{#if online === false}
2625
Looks like you're offline. If you think your connection is fine, check the
2726
<a href="https://githubstatus.com">GitHub status page</a>
28-
as this site is hosted by &thinsp;<Icon icon="octicon:mark-github" inline />&thinsp;
29-
GitHub Pages.
27+
as this site is hosted by &thinsp;<svg><use href="#octicon-mark-github" /></svg
28+
>&thinsp; GitHub Pages.
3029
{/if}
3130

3231
<p>

src/routes/+layout.svelte

Lines changed: 9 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,29 @@
11
<script lang="ts">
2-
import { afterNavigate, goto } from '$app/navigation'
2+
import { goto } from '$app/navigation'
33
import { page } from '$app/state'
44
import { CmdPalette } from '$lib'
55
import { repository } from '$root/package.json'
66
import { Footer } from '$site'
7-
import { demos } from '$site/stores'
8-
import { mount, type Snippet } from 'svelte'
7+
import { type Snippet } from 'svelte'
98
import Toc from 'svelte-toc'
109
import { CopyButton, GitHubCorner } from 'svelte-zoo'
1110
import '../app.css'
11+
import { routes } from './(demos)'
1212
1313
interface Props {
1414
children?: Snippet
1515
}
1616
let { children }: Props = $props()
1717
18-
afterNavigate(() => {
19-
for (const node of document.querySelectorAll(`pre > code`)) {
20-
// skip if <pre> already contains a button (presumably for copy)
21-
const pre = node.parentElement
22-
if (!pre || pre.querySelector(`button`)) continue
23-
24-
mount(CopyButton, {
25-
target: pre,
26-
props: {
27-
content: node.textContent ?? ``,
28-
style: `position: absolute; top: 1ex; right: 1ex;`,
29-
},
30-
})
31-
}
32-
})
33-
34-
const routes = Object.keys(import.meta.glob(`./**/+page.{svx,svelte,md}`)).map(
35-
(filename) => {
36-
const parts = filename.split(`/`).filter((part) => !part.startsWith(`(`)) // remove hidden route segments
37-
return { route: `/${parts.slice(1, -1).join(`/`)}`, filename }
38-
},
39-
)
40-
41-
if (routes.length < 3) {
42-
console.error(`Too few demo routes found: ${routes.length}`)
43-
}
44-
45-
$demos = routes
46-
.filter(({ filename }) => filename.includes(`/(demos)/`))
47-
.map(({ route }) => route)
48-
4918
const actions = routes.map(({ route }) => ({ label: route, action: () => goto(route) }))
5019
</script>
5120

5221
<CmdPalette {actions} placeholder="Go to..." />
5322

5423
<GitHubCorner href={repository} />
5524

25+
<CopyButton global />
26+
5627
{#if !page.error && page.url.pathname !== `/`}
5728
<a href="." aria-label="Back to index page">&laquo; home</a>
5829
{/if}
@@ -64,11 +35,12 @@
6435
headingSelector="main > :where(h2, h3)"
6536
breakpoint={1250}
6637
--toc-mobile-bg="#1c0e3e"
67-
--toc-font-size="9pt"
6838
--toc-li-padding="3pt 1ex"
6939
--toc-mobile-btn-color="white"
70-
--toc-max-width="20em"
71-
--toc-desktop-nav-margin="0 0 0 17em"
40+
--toc-desktop-nav-margin="0 0 0 14em"
41+
aside_style="max-width: 24em;"
42+
title_element_style="font-size: 16pt; padding: 0 0 0.5em 0.5em;"
43+
ol_style="font-size: 8pt"
7244
/>
7345
{/if}
7446

0 commit comments

Comments
 (0)