Skip to content

Commit 9985efa

Browse files
authored
Panel UI (#1032)
* better panel UI * rename slots * convert to svelte 5 * tweaks * lint * use modern spring
1 parent 45926cc commit 9985efa

File tree

3 files changed

+104
-49
lines changed

3 files changed

+104
-49
lines changed

packages/repl/src/lib/Output/Output.svelte

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,14 +112,14 @@
112112
{#if embedded}
113113
<Editor workspace={js_workspace} />
114114
{:else}
115-
<PaneWithPanel pos="50%" max="-4.2rem" panel="Compiler options">
116-
<div slot="main">
115+
<PaneWithPanel min="-18rem" pos="-18rem" panel="Compiler options">
116+
{#snippet main()}
117117
<Editor workspace={js_workspace} />
118-
</div>
118+
{/snippet}
119119

120-
<div slot="panel-body">
120+
{#snippet body()}
121121
<CompilerOptions {workspace} />
122-
</div>
122+
{/snippet}
123123
</PaneWithPanel>
124124
{/if}
125125
</div>

packages/repl/src/lib/Output/PaneWithPanel.svelte

Lines changed: 94 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,117 @@
11
<script lang="ts">
2-
import { spring } from 'svelte/motion';
2+
import { Spring } from 'svelte/motion';
33
import { SplitPane, type Length } from '@rich_harris/svelte-split-pane';
44
55
const UNIT_REGEX = /(\d+)(?:(px|rem|%|em))/i;
66
7-
export let panel: string;
7+
interface Props {
8+
panel: string;
9+
pos?: Length;
10+
min?: Length;
11+
max?: Length;
12+
main?: import('svelte').Snippet;
13+
header?: import('svelte').Snippet;
14+
body?: import('svelte').Snippet;
15+
}
816
9-
export let pos: Length = '90%';
17+
let {
18+
panel,
19+
pos = $bindable('90%'),
20+
min = '4.2rem',
21+
max = '-4.2rem',
22+
main,
23+
header,
24+
body
25+
}: Props = $props();
1026
11-
$: previous_pos = Math.min(+pos.replace(UNIT_REGEX, '$1'), 70);
27+
let previous_pos = Math.min(normalize(pos), 70);
1228
13-
export let max: Length = '90%';
29+
let container: HTMLElement;
1430
1531
// we can't bind to the spring itself, but we
1632
// can still use the spring to drive `pos`
17-
const driver = spring(+pos.replace(UNIT_REGEX, '$1'), {
33+
const driver = new Spring(normalize(pos), {
1834
stiffness: 0.2,
1935
damping: 0.5
2036
});
2137
22-
// @ts-ignore
23-
$: pos = $driver + '%';
38+
$effect(() => {
39+
pos = driver.current + '%';
40+
});
2441
2542
const toggle = () => {
26-
const numeric_pos = +pos.replace(UNIT_REGEX, '$1');
43+
const pc = normalize(pos);
44+
const px = pc * 0.01 * container.clientHeight;
2745
28-
driver.set(numeric_pos, { hard: true });
46+
const open = container.clientHeight - px > 42;
2947
30-
if (numeric_pos > 80) {
31-
driver.set(previous_pos);
32-
} else {
33-
previous_pos = numeric_pos;
48+
driver.set(pc, { hard: true });
49+
50+
if (open) {
51+
previous_pos = pc;
3452
driver.set(100);
53+
} else {
54+
driver.set(previous_pos);
3555
}
3656
};
57+
58+
function normalize(pos: string) {
59+
let normalized = +pos.replace(UNIT_REGEX, '$1');
60+
61+
if (normalized < 0) {
62+
normalized += 100;
63+
}
64+
65+
return normalized;
66+
}
3767
</script>
3868

39-
<SplitPane {max} min="10%" type="vertical" bind:pos>
40-
{#snippet a()}
41-
<section>
42-
<slot name="main" />
43-
</section>
44-
{/snippet}
45-
46-
{#snippet b()}
47-
<section>
48-
<div class="panel-header">
49-
<button class="panel-heading" on:click={toggle}>{panel}</button>
50-
<slot name="panel-header" />
51-
</div>
52-
53-
<div class="panel-body">
54-
<slot name="panel-body" />
55-
</div>
56-
</section>
57-
{/snippet}
58-
</SplitPane>
69+
<div class="container" bind:this={container}>
70+
<SplitPane {min} {max} type="vertical" bind:pos>
71+
{#snippet a()}
72+
<section>
73+
{@render main?.()}
74+
</section>
75+
{/snippet}
76+
77+
{#snippet b()}
78+
<section>
79+
<div class="panel-header">
80+
<button class="panel-heading raised" onclick={toggle}>
81+
<svg
82+
width="1.8rem"
83+
height="1.8rem"
84+
viewBox="0 0 24 24"
85+
fill="none"
86+
stroke="currentColor"
87+
stroke-width="2"
88+
stroke-linecap="round"
89+
stroke-linejoin="round"
90+
>
91+
<path d="m7 15 5 5 5-5" />
92+
<path d="m7 9 5-5 5 5" />
93+
</svg>
94+
95+
{panel}
96+
</button>
97+
98+
{@render header?.()}
99+
</div>
100+
101+
<div class="panel-body">
102+
{@render body?.()}
103+
</div>
104+
</section>
105+
{/snippet}
106+
</SplitPane>
107+
</div>
59108

60109
<style>
110+
.container {
111+
width: 100%;
112+
height: 100%;
113+
}
114+
61115
.panel-header {
62116
height: var(--sk-pane-controls-height);
63117
display: flex;
@@ -74,8 +128,13 @@
74128
.panel-heading {
75129
font: var(--sk-font-ui-small);
76130
text-transform: uppercase;
77-
flex: 1;
131+
height: 3.2rem;
132+
padding: 0 0.8rem;
133+
/* flex: 1; */
78134
text-align: left;
135+
display: flex;
136+
align-items: center;
137+
gap: 0.4rem;
79138
}
80139
81140
section {

packages/repl/src/lib/Output/Viewer.svelte

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -295,23 +295,19 @@
295295

296296
<div class="iframe-container">
297297
{#if !onLog}
298-
<PaneWithPanel pos="100%" max="-4.2rem" panel="Console">
299-
<div slot="main">
300-
{@render main()}
301-
</div>
302-
303-
<div slot="panel-header">
298+
<PaneWithPanel pos="100%" panel="Console" {main}>
299+
{#snippet header()}
304300
<button class="raised" disabled={logs.length === 0} on:click|stopPropagation={clear_logs}>
305301
{#if logs.length > 0}
306302
({logs.length})
307303
{/if}
308304
Clear
309305
</button>
310-
</div>
306+
{/snippet}
311307

312-
<section slot="panel-body">
308+
{#snippet body()}
313309
<Console {logs} />
314-
</section>
310+
{/snippet}
315311
</PaneWithPanel>
316312
{:else}
317313
{@render main()}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy