|
1 | 1 | <script lang="ts"> |
2 | 2 | import { onMount } from 'svelte'; |
3 | 3 | import { base } from '$app/paths'; |
4 | | - import { scale, fade } from 'svelte/transition'; |
| 4 | + import { fade, fly } from 'svelte/transition'; |
5 | 5 | import { cubicOut } from 'svelte/easing'; |
6 | 6 | import Icon from '$lib/components/icons/Icon.svelte'; |
7 | 7 | import { PATHVIEW_VERSION, EXTRACTED_VERSIONS } from '$lib/constants/dependencies'; |
|
61 | 61 | <svelte:window onkeydown={handleKeydown} /> |
62 | 62 |
|
63 | 63 | <!-- 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"> |
67 | 92 | <div class="version-info"> |
68 | 93 | PathView {PATHVIEW_VERSION} · {Object.entries(EXTRACTED_VERSIONS).map(([pkg, ver]) => `${pkg.replace('_', '-')} ${ver}`).join(' · ')} |
69 | 94 | </div> |
| 95 | + |
70 | 96 | <div class="header"> |
71 | 97 | <img src="{base}/pathview_logo.png" alt="PathView" class="logo" /> |
| 98 | + <p class="tagline">Visual block-diagram editor for the PathSim simulation framework</p> |
72 | 99 | </div> |
73 | 100 |
|
74 | 101 | <div class="actions"> |
|
108 | 135 | <div class="examples-section"> |
109 | 136 | <div class="examples-grid"> |
110 | 137 | {#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 | + > |
112 | 144 | <div class="example-info"> |
113 | 145 | <div class="example-name">{example.name}</div> |
114 | 146 | <div class="example-description">{example.description}</div> |
|
125 | 157 | </div> |
126 | 158 | </div> |
127 | 159 | </div> |
128 | | -</div> |
| 160 | +</aside> |
129 | 161 |
|
130 | 162 | <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 | + } |
132 | 170 |
|
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%); |
138 | 183 | display: flex; |
139 | 184 | flex-direction: column; |
140 | | - gap: 16px; |
141 | | - background: var(--surface); |
142 | 185 | overflow: hidden; |
143 | 186 | } |
144 | 187 |
|
| 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 | +
|
145 | 208 | .version-info { |
146 | 209 | position: absolute; |
147 | | - top: 8px; |
148 | | - right: 12px; |
| 210 | + top: 10px; |
| 211 | + left: 16px; |
149 | 212 | font-size: 9px; |
150 | 213 | color: var(--text-disabled); |
151 | 214 | } |
152 | 215 |
|
153 | 216 | .header { |
154 | 217 | text-align: center; |
155 | | - padding: 24px 0; |
| 218 | + padding: 20px 0 8px; |
156 | 219 | } |
157 | 220 |
|
158 | 221 | .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; |
160 | 230 | } |
161 | 231 |
|
162 | 232 | .actions { |
|
170 | 240 | flex-direction: column; |
171 | 241 | align-items: center; |
172 | 242 | gap: 6px; |
173 | | - padding: 6px 12px; |
| 243 | + padding: 6px 8px; |
174 | 244 | background: transparent; |
175 | 245 | border: none; |
176 | 246 | border-radius: var(--radius-md); |
|
197 | 267 | .separator { |
198 | 268 | height: 1px; |
199 | 269 | background: var(--border); |
200 | | - margin: 0 -24px; |
| 270 | + margin: 4px -150px 4px -32px; |
201 | 271 | } |
202 | 272 |
|
203 | 273 | .examples-section { |
204 | 274 | display: flex; |
205 | 275 | flex-direction: column; |
206 | 276 | min-height: 0; |
207 | 277 | flex: 1; |
208 | | - margin: -16px -24px -24px -24px; |
209 | 278 | } |
210 | 279 |
|
211 | 280 | .examples-grid { |
|
214 | 283 | grid-auto-rows: min-content; |
215 | 284 | align-items: start; |
216 | 285 | gap: 10px; |
217 | | - overflow-y: auto; |
218 | | - max-height: 420px; |
219 | | - padding: 16px; |
220 | 286 | } |
221 | 287 |
|
222 | 288 | .example-card { |
|
282 | 348 | .example-preview { |
283 | 349 | background: var(--surface); |
284 | 350 | width: 100%; |
285 | | - padding-top: 28px; /* Space for the header */ |
| 351 | + padding-top: 28px; |
286 | 352 | } |
287 | 353 |
|
288 | 354 | .example-preview img { |
|
291 | 357 | height: auto; |
292 | 358 | } |
293 | 359 |
|
| 360 | + @media (max-width: 900px) { |
| 361 | + .welcome-banner { |
| 362 | + width: 78vw; |
| 363 | + min-width: 0; |
| 364 | + } |
| 365 | + } |
| 366 | +
|
294 | 367 | @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 | +
|
295 | 381 | .examples-grid { |
296 | 382 | grid-template-columns: repeat(2, 1fr); |
297 | 383 | } |
298 | 384 | } |
299 | 385 |
|
300 | | - @media (max-width: 600px) { |
| 386 | + @media (max-width: 500px) { |
301 | 387 | .actions { |
302 | 388 | grid-template-columns: repeat(3, 1fr); |
303 | 389 | } |
304 | | - } |
305 | 390 |
|
306 | | - @media (max-width: 500px) { |
307 | 391 | .examples-grid { |
308 | 392 | grid-template-columns: 1fr; |
309 | 393 | } |
|
0 commit comments