Skip to content

Commit 168825d

Browse files
authored
select-icon-component (#373)
1 parent 4a14d3d commit 168825d

File tree

2 files changed

+88
-47
lines changed

2 files changed

+88
-47
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<script lang="ts">
2+
import { Icon } from '@sveltejs/site-kit/components';
3+
import type { Snippet } from 'svelte';
4+
import type { HTMLSelectAttributes } from 'svelte/elements';
5+
6+
let { children, value, ...props }: HTMLSelectAttributes & { children: Snippet } = $props();
7+
8+
export { value }; // allow it to be temporarily overwritten
9+
</script>
10+
11+
<div class="examples-select">
12+
<span class="raised icon"><Icon name="menu" /></span>
13+
<select {...props} {value}>
14+
{@render children()}
15+
</select>
16+
</div>
17+
18+
<style>
19+
.examples-select {
20+
position: relative;
21+
22+
&:has(select:focus-visible) .raised.icon {
23+
outline: 2px solid hsla(var(--sk-theme-1-hsl), 0.6);
24+
border-radius: var(--sk-border-radius);
25+
}
26+
27+
span {
28+
pointer-events: none;
29+
}
30+
}
31+
32+
select {
33+
opacity: 0.0001;
34+
position: absolute;
35+
top: 0;
36+
left: 0;
37+
width: 100%;
38+
height: 100%;
39+
}
40+
41+
span.icon {
42+
display: flex;
43+
align-items: center;
44+
justify-content: center;
45+
user-select: none;
46+
}
47+
48+
.icon {
49+
position: relative;
50+
color: var(--sk-text-3);
51+
line-height: 1;
52+
background-size: 1.8rem;
53+
z-index: 999;
54+
}
55+
56+
.icon:hover,
57+
.icon:focus-visible {
58+
opacity: 1;
59+
}
60+
</style>

apps/svelte.dev/src/routes/(authed)/playground/[id]/AppControls.svelte

Lines changed: 28 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
import type { Gist, User } from '$lib/db/types';
1212
import type { File } from '@sveltejs/repl';
1313
import { browser } from '$app/environment';
14+
import SelectIcon from '$lib/components/SelectIcon.svelte';
15+
import { untrack } from 'svelte';
1416
1517
interface Props {
1618
examples: Array<{ title: string; examples: any[] }>;
@@ -40,7 +42,7 @@
4042
let downloading = $state(false);
4143
let justSaved = $state(false);
4244
let justForked = $state(false);
43-
let select: HTMLSelectElement;
45+
let select: any; // TODO why can't i do `select: SelectIcon`?
4446
4547
function wait(ms: number) {
4648
return new Promise((f) => setTimeout(f, ms));
@@ -214,35 +216,37 @@ export default app;`
214216
// the example can be reselected
215217
$effect(() => {
216218
if (modified) {
217-
select.value = '';
219+
// this is a little tricky, but: we need to wrap this in untrack
220+
// because otherwise we'll read `select.value` and re-run this
221+
// when we navigate, which we don't want
222+
untrack(() => {
223+
select.value = '';
224+
});
218225
}
219226
});
220227
</script>
221228

222229
<svelte:window on:keydown={handleKeydown} />
223230

224231
<div class="app-controls">
225-
<div class="examples-select">
226-
<span class="raised icon"><Icon name="menu" /></span>
227-
<select
228-
bind:this={select}
229-
title="examples"
230-
value={gist.id}
231-
onchange={(e) => {
232-
goto(`/playground/${e.currentTarget.value}`);
233-
}}
234-
>
235-
<option value="untitled">Create new</option>
236-
<option disabled selected value="">or choose an example</option>
237-
{#each examples as section}
238-
<optgroup label={section.title}>
239-
{#each section.examples as example}
240-
<option value={example.slug}>{example.title}</option>
241-
{/each}
242-
</optgroup>
243-
{/each}
244-
</select>
245-
</div>
232+
<SelectIcon
233+
bind:this={select}
234+
title="examples"
235+
value={gist.id}
236+
onchange={async (e) => {
237+
goto(`/playground/${e.currentTarget.value}`);
238+
}}
239+
>
240+
<option value="untitled">Create new</option>
241+
<option disabled selected value="">or choose an example</option>
242+
{#each examples as section}
243+
<optgroup label={section.title}>
244+
{#each section.examples as example}
245+
<option value={example.slug}>{example.title}</option>
246+
{/each}
247+
</optgroup>
248+
{/each}
249+
</SelectIcon>
246250

247251
<input
248252
bind:value={name}
@@ -325,30 +329,7 @@ export default app;`
325329
gap: 0.2rem;
326330
}
327331
328-
.examples-select {
329-
position: relative;
330-
331-
&:has(select:focus-visible) .raised.icon {
332-
outline: 2px solid hsla(var(--sk-theme-1-hsl), 0.6);
333-
border-radius: var(--sk-border-radius);
334-
}
335-
336-
span {
337-
pointer-events: none;
338-
}
339-
}
340-
341-
select {
342-
opacity: 0.0001;
343-
position: absolute;
344-
top: 0;
345-
left: 0;
346-
width: 100%;
347-
height: 100%;
348-
}
349-
350-
button,
351-
span.icon {
332+
button {
352333
display: flex;
353334
align-items: center;
354335
justify-content: center;

0 commit comments

Comments
 (0)