10
10
import { get_app_context } from ' ../../app-context' ;
11
11
import type { Gist , User } from ' $lib/db/types' ;
12
12
import type { File } from ' @sveltejs/repl' ;
13
+ import { browser } from ' $app/environment' ;
13
14
14
15
interface Props {
15
16
examples: Array <{ title: string ; examples: any [] }>;
16
17
user: User | null ;
17
18
repl: Repl ;
18
19
gist: Gist ;
19
20
name: string ;
20
- zen_mode: boolean ;
21
- modified_count: number ;
21
+ modified: boolean ;
22
22
forked: (value : { gist: Gist }) => void ;
23
23
saved: () => void ;
24
24
}
25
25
26
26
let {
27
27
name = $bindable (),
28
- zen_mode = $bindable (),
29
- modified_count = $bindable (),
28
+ modified = $bindable (),
30
29
user,
31
30
repl,
32
31
gist,
41
40
let downloading = $state (false );
42
41
let justSaved = $state (false );
43
42
let justForked = $state (false );
43
+ let select: HTMLSelectElement ;
44
44
45
45
function wait(ms : number ) {
46
46
return new Promise ((f ) => setTimeout (f , ms ));
84
84
const gist = await r .json ();
85
85
forked ({ gist });
86
86
87
- modified_count = 0 ;
87
+ modified = false ;
88
88
repl .markSaved ();
89
89
90
90
if (intentWasSave ) {
146
146
throw new Error (` Received an HTTP ${r .status } response: ${error } ` );
147
147
}
148
148
149
- modified_count = 0 ;
149
+ modified = false ;
150
150
repl .markSaved ();
151
151
saved ();
152
152
justSaved = true ;
@@ -209,6 +209,14 @@ export default app;`
209
209
210
210
downloading = false ;
211
211
}
212
+
213
+ // modifying an app should reset the `<select>`, so that
214
+ // the example can be reselected
215
+ $effect (() => {
216
+ if (modified ) {
217
+ select .value = ' ' ;
218
+ }
219
+ });
212
220
</script >
213
221
214
222
<svelte:window on:keydown ={handleKeydown } />
@@ -217,10 +225,11 @@ export default app;`
217
225
<div class =" examples-select" >
218
226
<span class =" raised icon" ><Icon name =" menu" /></span >
219
227
<select
228
+ bind:this ={select }
220
229
title =" examples"
221
230
value ={gist .id }
222
- onchange ={e => {
223
- goto (` /playground/${( e . target as HTMLSelectElement ) .value } ` );
231
+ onchange ={( e ) => {
232
+ goto (` /playground/${e . currentTarget .value } ` );
224
233
}}
225
234
>
226
235
<option value =" untitled" >Create new</option >
@@ -237,52 +246,55 @@ export default app;`
237
246
238
247
<input
239
248
bind:value ={name }
249
+ onchange ={() => (modified = true )}
240
250
onfocus ={(e ) => e .currentTarget .select ()}
241
251
use:enter ={(e ) => (e .currentTarget as HTMLInputElement ).blur ()}
242
252
/>
243
253
244
254
<div class =" buttons" >
245
- <button class ="raised icon" onclick ={() => (zen_mode = ! zen_mode )} title =" fullscreen editor" >
246
- {#if zen_mode }
247
- <Icon size ={18 } name =" close" />
248
- {:else }
249
- <Icon size ={18 } name =" maximize" />
250
- {/if }
251
- </button >
252
-
253
- <button class ="raised icon" disabled ={downloading } onclick ={download } title =" download zip file" >
254
- <Icon size ={18 } name =" download" />
255
- </button >
256
-
257
- <button class ="raised icon" disabled ={saving || ! user } onclick ={() => fork (false )} title =" fork" >
255
+ <button
256
+ class =" raised icon"
257
+ disabled ={saving || ! user }
258
+ onclick ={() => fork (false )}
259
+ aria-label ={user ? ' fork' : ' log in to fork' }
260
+ >
258
261
{#if justForked }
259
262
<Icon size ={18 } name =" check" />
260
263
{:else }
261
264
<Icon size ={18 } name =" git-branch" />
262
265
{/if }
263
266
</button >
264
267
265
- <button class ="raised icon" disabled ={saving || ! user } onclick ={save } title =" save" >
268
+ <button
269
+ class =" raised icon"
270
+ disabled ={saving || ! user }
271
+ onclick ={save }
272
+ aria-label ={user
273
+ ? ` save (${browser && navigator .platform === ' MacIntel' ? ' ⌘' : ' Ctrl' }+S) `
274
+ : ' log in to save' }
275
+ >
266
276
{#if justSaved }
267
277
<Icon size ={18 } name =" check" />
268
278
{:else }
269
279
<Icon size ={18 } name =" save" />
270
- {#if modified_count }
271
- <div class ="badge" >{ modified_count }</ div >
280
+ {#if modified }
281
+ <span class =" badge" ></ span >
272
282
{/if }
273
283
{/if }
274
284
</button >
275
285
286
+ <button
287
+ class =" raised icon download"
288
+ disabled ={downloading }
289
+ onclick ={download }
290
+ aria-label =" download zip file"
291
+ ></button >
292
+
276
293
{#if user }
277
294
<UserMenu {user } />
278
295
{:else }
279
- <button
280
- class =" raised icon"
281
- onclick ={(e ) => (e .preventDefault (), login ())}
282
- style =" width: auto; padding: 0 0.4rem"
283
- >
284
- <Icon name =" log-in" />
285
- <span > Log in to save</span >
296
+ <button class ="raised icon login" onclick ={login }>
297
+ <span >log in</span >
286
298
</button >
287
299
{/if }
288
300
</div >
@@ -319,11 +331,15 @@ export default app;`
319
331
320
332
.examples-select {
321
333
position : relative ;
322
- }
323
334
324
- .examples-select :has (select :focus-visible ) .raised.icon {
325
- outline : 2px solid hsla (var (--sk-theme-1-hsl ), 0.6 );
326
- border-radius : var (--sk-border-radius );
335
+ &:has(select:focus-visible) .raised .icon {
336
+ outline : 2px solid hsla (var (--sk-theme-1-hsl ), 0.6 );
337
+ border-radius : var (--sk-border-radius );
338
+ }
339
+
340
+ span {
341
+ pointer-events : none ;
342
+ }
327
343
}
328
344
329
345
select {
@@ -343,6 +359,7 @@ export default app;`
343
359
justify-content : center ;
344
360
width : 3.2rem ;
345
361
height : 3.2rem ;
362
+ user-select : none ;
346
363
}
347
364
348
365
.icon {
@@ -351,18 +368,62 @@ export default app;`
351
368
font-size : var (--sk-font-size-ui-small );
352
369
color : var (--sk-text-3 );
353
370
line-height : 1 ;
371
+ background : 50% 50% no-repeat ;
372
+ background-size : 1.8rem ;
373
+ z-index : 999 ;
374
+
375
+ &[aria-label ]:hover::before {
376
+ content : ' ' ;
377
+ width : 1rem ;
378
+ height : 1rem ;
379
+ position : absolute ;
380
+ background : var (--sk-text-3 );
381
+ top : calc (100% + 0.5rem );
382
+ rotate : 45deg ;
383
+ }
384
+
385
+ &[aria-label ]:hover ::after {
386
+ content : attr (aria-label );
387
+ position : absolute ;
388
+ top : calc (100% + 1rem );
389
+ background : var (--sk-text-3 );
390
+ color : var (--sk-back-4 );
391
+ padding : 0.5em 0.5em ;
392
+ border-radius : var (--sk-border-radius );
393
+ }
394
+
395
+ &.login {
396
+ width : auto ;
397
+ background-image : url ($lib/icons/user-light.svg );
398
+ background-position : 0.4rem 50% ;
399
+ padding : 0 0.4rem 0 2.8rem ;
400
+
401
+ :root.dark & {
402
+ background-image: url ($lib/icons/user-dark.svg );
403
+ }
404
+ }
405
+
406
+ &.download {
407
+ background-image : url ($lib/icons/download-light.svg );
408
+
409
+ :root.dark & {
410
+ background-image: url ($lib/icons/download-dark.svg );
411
+ }
412
+ }
354
413
}
355
414
356
415
.icon :hover ,
357
416
.icon :focus-visible {
358
417
opacity : 1 ;
359
418
}
419
+
420
+ /* TODO use lucide-svelte, so we don't need all this customisation? */
360
421
.icon :disabled {
361
- opacity : 0.3 ;
362
- }
422
+ color : #ccc ;
363
423
364
- .icon [title ^= ' fullscreen' ] {
365
- display : none ;
424
+ :root.dark & {
425
+ color : #555 ;
426
+ }
366
427
}
367
428
368
429
input {
@@ -378,30 +439,13 @@ export default app;`
378
439
font-size : var (--sk-font-size-ui-medium );
379
440
}
380
441
381
- button span {
382
- display : none ;
383
- }
384
-
385
442
.badge {
386
- background : #ff3e00 ;
387
- border-radius : 100% ;
388
- font-size : 10px ;
389
- padding : 0 ;
390
- width : 15px ;
391
- height : 15px ;
392
- line-height : 15px ;
393
443
position : absolute ;
394
- top : 10px ;
395
- right : 0px ;
396
- }
397
-
398
- @media (min-width : 600px ) {
399
- .icon [title ^= ' fullscreen' ] {
400
- display : inline ;
401
- }
402
-
403
- button span {
404
- display : inline-block ;
405
- }
444
+ background : var (--sk-theme-1 );
445
+ border-radius : 50% ;
446
+ width : 1rem ;
447
+ height : 1rem ;
448
+ top : -0.2rem ;
449
+ right : -0.2rem ;
406
450
}
407
451
</style >
0 commit comments