Skip to content

Commit cb1901c

Browse files
authored
Merge pull request #279 from pathsim/feature/welcome-banner
Welcome modal as left-docked banner with angled edge
2 parents eec3a47 + e4f68b2 commit cb1901c

1 file changed

Lines changed: 112 additions & 28 deletions

File tree

src/lib/components/WelcomeModal.svelte

Lines changed: 112 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script lang="ts">
22
import { onMount } from 'svelte';
33
import { base } from '$app/paths';
4-
import { scale, fade } from 'svelte/transition';
4+
import { fade, fly } from 'svelte/transition';
55
import { cubicOut } from 'svelte/easing';
66
import Icon from '$lib/components/icons/Icon.svelte';
77
import { PATHVIEW_VERSION, EXTRACTED_VERSIONS } from '$lib/constants/dependencies';
@@ -61,14 +61,41 @@
6161
<svelte:window onkeydown={handleKeydown} />
6262

6363
<!-- svelte-ignore a11y_no_static_element_interactions, a11y_click_events_have_key_events -->
64-
<div class="dialog-backdrop" transition:fade={{ duration: 150 }} onclick={onClose} onkeydown={(e) => e.key === 'Escape' && onClose()} role="presentation">
65-
<!-- svelte-ignore a11y_no_static_element_interactions, a11y_click_events_have_key_events -->
66-
<div class="modal glass-panel" transition:scale={{ start: 0.95, duration: 200, easing: cubicOut }} onclick={(e) => e.stopPropagation()} role="dialog" tabindex="-1">
64+
<div
65+
class="welcome-backdrop"
66+
transition:fade={{ duration: 200 }}
67+
onclick={onClose}
68+
role="presentation"
69+
></div>
70+
71+
<!-- svelte-ignore a11y_no_noninteractive_element_to_interactive_role -->
72+
<aside
73+
class="welcome-banner"
74+
transition:fly={{ x: -900, duration: 320, easing: cubicOut, opacity: 1 }}
75+
role="dialog"
76+
aria-label="Welcome"
77+
tabindex="-1"
78+
>
79+
<svg class="banner-edge" preserveAspectRatio="none" viewBox="0 0 110 100" aria-hidden="true">
80+
<line
81+
x1="0"
82+
y1="0"
83+
x2="110"
84+
y2="100"
85+
stroke="currentColor"
86+
stroke-width="2.5"
87+
vector-effect="non-scaling-stroke"
88+
/>
89+
</svg>
90+
91+
<div class="banner-content">
6792
<div class="version-info">
6893
PathView {PATHVIEW_VERSION} · {Object.entries(EXTRACTED_VERSIONS).map(([pkg, ver]) => `${pkg.replace('_', '-')} ${ver}`).join(' · ')}
6994
</div>
95+
7096
<div class="header">
7197
<img src="{base}/pathview_logo.png" alt="PathView" class="logo" />
98+
<p class="tagline">Visual block-diagram editor for the PathSim simulation framework</p>
7299
</div>
73100

74101
<div class="actions">
@@ -108,7 +135,12 @@
108135
<div class="examples-section">
109136
<div class="examples-grid">
110137
{#each examples as example}
111-
<a class="example-card" href="?model={base}/examples/{example.filename}">
138+
<a
139+
class="example-card"
140+
href="?model={base}/examples/{example.filename}"
141+
data-sveltekit-reload
142+
onclick={onClose}
143+
>
112144
<div class="example-info">
113145
<div class="example-name">{example.name}</div>
114146
<div class="example-description">{example.description}</div>
@@ -125,38 +157,76 @@
125157
</div>
126158
</div>
127159
</div>
128-
</div>
160+
</aside>
129161

130162
<style>
131-
/* Uses global .dialog-backdrop from app.css */
163+
.welcome-backdrop {
164+
position: fixed;
165+
inset: 0;
166+
background: rgba(0, 0, 0, 0.18);
167+
backdrop-filter: blur(5px);
168+
z-index: var(--z-modal);
169+
}
132170
133-
.modal {
134-
position: relative;
135-
width: 90%;
136-
max-width: 780px;
137-
padding: 24px;
171+
.welcome-banner {
172+
position: fixed;
173+
top: 0;
174+
left: 0;
175+
bottom: 0;
176+
width: 58vw;
177+
max-width: 760px;
178+
min-width: 460px;
179+
background: var(--surface);
180+
box-shadow: 4px 0 24px rgba(0, 0, 0, 0.25);
181+
z-index: calc(var(--z-modal) + 1);
182+
clip-path: polygon(0 0, calc(100% - 110px) 0, 100% 100%, 0 100%);
138183
display: flex;
139184
flex-direction: column;
140-
gap: 16px;
141-
background: var(--surface);
142185
overflow: hidden;
143186
}
144187
188+
.banner-edge {
189+
position: absolute;
190+
top: 0;
191+
right: 0;
192+
width: 110px;
193+
height: 100%;
194+
color: var(--border);
195+
pointer-events: none;
196+
}
197+
198+
.banner-content {
199+
flex: 1;
200+
min-height: 0;
201+
padding: 28px 150px 28px 32px;
202+
display: flex;
203+
flex-direction: column;
204+
gap: 16px;
205+
overflow-y: auto;
206+
}
207+
145208
.version-info {
146209
position: absolute;
147-
top: 8px;
148-
right: 12px;
210+
top: 10px;
211+
left: 16px;
149212
font-size: 9px;
150213
color: var(--text-disabled);
151214
}
152215
153216
.header {
154217
text-align: center;
155-
padding: 24px 0;
218+
padding: 20px 0 8px;
156219
}
157220
158221
.logo {
159-
height: 100px;
222+
height: 92px;
223+
}
224+
225+
.tagline {
226+
margin: 10px 0 0;
227+
font-size: 13px;
228+
color: var(--text-muted);
229+
letter-spacing: 0.2px;
160230
}
161231
162232
.actions {
@@ -170,7 +240,7 @@
170240
flex-direction: column;
171241
align-items: center;
172242
gap: 6px;
173-
padding: 6px 12px;
243+
padding: 6px 8px;
174244
background: transparent;
175245
border: none;
176246
border-radius: var(--radius-md);
@@ -197,15 +267,14 @@
197267
.separator {
198268
height: 1px;
199269
background: var(--border);
200-
margin: 0 -24px;
270+
margin: 4px -150px 4px -32px;
201271
}
202272
203273
.examples-section {
204274
display: flex;
205275
flex-direction: column;
206276
min-height: 0;
207277
flex: 1;
208-
margin: -16px -24px -24px -24px;
209278
}
210279
211280
.examples-grid {
@@ -214,9 +283,6 @@
214283
grid-auto-rows: min-content;
215284
align-items: start;
216285
gap: 10px;
217-
overflow-y: auto;
218-
max-height: 420px;
219-
padding: 16px;
220286
}
221287
222288
.example-card {
@@ -282,7 +348,7 @@
282348
.example-preview {
283349
background: var(--surface);
284350
width: 100%;
285-
padding-top: 28px; /* Space for the header */
351+
padding-top: 28px;
286352
}
287353
288354
.example-preview img {
@@ -291,19 +357,37 @@
291357
height: auto;
292358
}
293359
360+
@media (max-width: 900px) {
361+
.welcome-banner {
362+
width: 78vw;
363+
min-width: 0;
364+
}
365+
}
366+
294367
@media (max-width: 700px) {
368+
.welcome-banner {
369+
width: 95vw;
370+
clip-path: polygon(0 0, calc(100% - 60px) 0, 100% 100%, 0 100%);
371+
}
372+
373+
.banner-edge {
374+
width: 60px;
375+
}
376+
377+
.banner-content {
378+
padding-right: 90px;
379+
}
380+
295381
.examples-grid {
296382
grid-template-columns: repeat(2, 1fr);
297383
}
298384
}
299385
300-
@media (max-width: 600px) {
386+
@media (max-width: 500px) {
301387
.actions {
302388
grid-template-columns: repeat(3, 1fr);
303389
}
304-
}
305390
306-
@media (max-width: 500px) {
307391
.examples-grid {
308392
grid-template-columns: 1fr;
309393
}

0 commit comments

Comments
 (0)