|
2 | 2 | import Prism from 'prismjs';
|
3 | 3 | import Normalizer from 'prismjs/plugins/normalize-whitespace/prism-normalize-whitespace.js';
|
4 | 4 | import questionData from './data/questions';
|
| 5 | + import { onMount } from 'svelte'; |
| 6 | +
|
| 7 | + let storageQuestion; |
| 8 | + let storageStatistics; |
| 9 | + let questions = []; |
| 10 | + let statistics = { |
| 11 | + correct: [], |
| 12 | + wrong: [], |
| 13 | + last: 0, |
| 14 | + }; |
| 15 | + let selected = -1; |
| 16 | + let current = 0; |
| 17 | + let modalVisible = false; |
5 | 18 |
|
6 |
| - const storageQuestion = localStorage.getItem('questions'); |
7 |
| -
|
8 |
| - const questions = storageQuestion ? [...JSON.parse(storageQuestion), ...questionData.slice(JSON.parse(storageQuestion).length)] : questionData; |
9 |
| - const statistics = localStorage.getItem('statistics') ? JSON.parse(localStorage.getItem('statistics')) : { |
10 |
| - correct: [], |
11 |
| - wrong: [], |
12 |
| - last: 0, |
13 |
| - } |
| 19 | + onMount(() => { |
| 20 | + storageQuestion = localStorage.getItem('questions'); |
| 21 | + storageStatistics = localStorage.getItem('statistics'); |
| 22 | + questions = storageQuestion ? [...JSON.parse(storageQuestion), ...questionData.slice(JSON.parse(storageQuestion).length)] : questionData; |
| 23 | + storageStatistics ? statistics = JSON.parse(storageStatistics) : undefined; |
| 24 | + selected = statistics.last === questions.length - 1 ? questions[statistics.last].selected : questions[statistics.last + 1].selected ? questions[statistics.last + 1].selected : undefined; |
| 25 | + current = statistics.last === questions.length - 1 ? statistics.last : statistics.last === 0 ? 0 : statistics.last + 1; |
| 26 | + }); |
14 | 27 |
|
15 |
| - let modalVisible = false; |
16 |
| - let current = statistics.last === questions.length - 1 ? statistics.last : statistics.last === 0 ? 0 : statistics.last + 1; |
17 |
| - let selected = statistics.last === questions.length - 1 ? questions[statistics.last].selected : questions[statistics.last + 1].selected ? questions[statistics.last + 1].selected : undefined; |
18 | 28 | $: done = statistics.correct.length + statistics.wrong.length;
|
19 | 29 | $: score = done ? parseInt((statistics.correct.length / done) * 100, 10) : 0;
|
20 | 30 |
|
|
27 | 37 | 'right-trim': true
|
28 | 38 | });
|
29 | 39 |
|
30 |
| - $: code = Prism.highlight(nw.normalize(questions[current].code), Prism.languages.javascript, 'javascript'); |
| 40 | + $: code = questions.length > 0 ? Prism.highlight(nw.normalize(questions[current].code), Prism.languages.javascript, 'javascript') : undefined; |
31 | 41 |
|
32 | 42 | const handleOnSelection = (correct, index) => {
|
33 | 43 | questions[current].selected = selected = index;
|
|
89 | 99 | .question-panel {
|
90 | 100 | margin-top: -136px;
|
91 | 101 | width: 100%;
|
| 102 | + max-width: calc(100% - 360px); |
92 | 103 | }
|
93 | 104 |
|
94 | 105 | .focus-area {
|
|
237 | 248 | color: #999;
|
238 | 249 | }
|
239 | 250 |
|
| 251 | + .explanation .origin a { |
| 252 | + word-break: break-all; |
| 253 | + } |
| 254 | +
|
| 255 | + :global(.explanation pre) { |
| 256 | + background-color: #f0f0f0; |
| 257 | + border-radius: 5px; |
| 258 | + padding: 20px 24px; |
| 259 | + } |
| 260 | +
|
240 | 261 | .score {
|
241 | 262 | margin-top: 60px;
|
242 | 263 | cursor: pointer;
|
|
405 | 426 | </svelte:head>
|
406 | 427 |
|
407 | 428 | <div class="container-fluid main">
|
408 |
| - <div class="container d-flex justify-between"> |
409 |
| - <div class="question-panel"> |
410 |
| - <div class="focus-area"> |
411 |
| - <h3>{@html questions[current].question}</h3> |
412 |
| - <pre> |
413 |
| - <code class="language-javascript"> |
414 |
| - {@html code} |
415 |
| - </code> |
416 |
| - </pre> |
417 |
| - <div class="selections {questions[current].selected > -1 ? 'selected' : ''}"> |
418 |
| - {#each questions[current].selections as selection, index} |
419 |
| - <div disabled class="selection d-flex justify-between align-center {selected === index ? 'selected' : ''} {selected === index && questions[current].selections[index].correct ? 'right' : 'wrong'}" on:click={() => handleOnSelection(selection.correct, index)}> |
420 |
| - <span>{@html selection.des}</span> |
421 |
| - <div class="check"></div> |
422 |
| - </div> |
423 |
| - {/each} |
| 429 | + {#if questions.length > 0} |
| 430 | + <div class="container d-flex justify-between"> |
| 431 | + <div class="question-panel"> |
| 432 | + <div class="focus-area"> |
| 433 | + <h3>{@html questions[current].question}</h3> |
| 434 | + <pre> |
| 435 | + <code class="language-javascript"> |
| 436 | + {@html code} |
| 437 | + </code> |
| 438 | + </pre> |
| 439 | + <div class="selections {questions[current].selected > -1 ? 'selected' : ''}"> |
| 440 | + {#each questions[current].selections as selection, index} |
| 441 | + <div disabled class="selection d-flex justify-between align-center {selected === index ? 'selected' : ''} {selected === index && questions[current].selections[index].correct ? 'right' : 'wrong'}" on:click={() => handleOnSelection(selection.correct, index)}> |
| 442 | + <span>{@html selection.des}</span> |
| 443 | + <div class="check"></div> |
| 444 | + </div> |
| 445 | + {/each} |
| 446 | + </div> |
424 | 447 | </div>
|
| 448 | + {#if questions[current].selected > -1} |
| 449 | + <div class="explanation"> |
| 450 | + <h4>Explanation</h4> |
| 451 | + {@html questions[current].explanation.html} |
| 452 | + <p class="origin">Origin: <a href={questions[current].explanation.origin} target="_blank" rel="noopener">{questions[current].explanation.origin}</a></p> |
| 453 | + </div> |
| 454 | + {/if} |
425 | 455 | </div>
|
426 |
| - {#if questions[current].selected > -1} |
427 |
| - <div class="explanation"> |
428 |
| - <h4>Explanation</h4> |
429 |
| - {@html questions[current].explanation.html} |
430 |
| - <p class="origin">Origin: <a href={questions[current].explanation.origin} target="_blank" rel="noopener">{questions[current].explanation.origin}</a></p> |
| 456 | + <div class="sider"> |
| 457 | + <div class="progress"> |
| 458 | + <p><span>Current: </span>#{current + 1}</p> |
| 459 | + <div class="progress-bar"> |
| 460 | + <div class="current-progress" style="width: {(statistics.correct.length + statistics.wrong.length) * 100 / questions.length}%"></div> |
| 461 | + </div> |
431 | 462 | </div>
|
432 |
| - {/if} |
433 |
| - </div> |
434 |
| - <div class="sider"> |
435 |
| - <div class="progress"> |
436 |
| - <p><span>Current: </span>#{current + 1}</p> |
437 |
| - <div class="progress-bar"> |
438 |
| - <div class="current-progress" style="width: {(statistics.correct.length + statistics.wrong.length) * 100 / questions.length}%"></div> |
| 463 | + <div class="score d-flex justify-between align-center" on:click={() => handleModalVisible(true)}> |
| 464 | + <div class="current-score d-flex justify-center align-center {score < 60 ? 'poor' : score > 59 & score < 80 ? 'normal' : 'good'}">{score}</div> |
| 465 | + <div class="score-detail"> |
| 466 | + <div class="correct d-flex justify-between align-center"><span>Correct:</span> {statistics.correct.length}</div> |
| 467 | + <div class="wrong d-flex justify-between align-center"><span>Wrong:</span> {statistics.wrong.length}</div> |
| 468 | + </div> |
439 | 469 | </div>
|
440 |
| - </div> |
441 |
| - <div class="score d-flex justify-between align-center" on:click={() => handleModalVisible(true)}> |
442 |
| - <div class="current-score d-flex justify-center align-center {score < 60 ? 'poor' : score > 59 & score < 80 ? 'normal' : 'good'}">{score}</div> |
443 |
| - <div class="score-detail"> |
444 |
| - <div class="correct d-flex justify-between align-center"><span>Correct:</span> {statistics.correct.length}</div> |
445 |
| - <div class="wrong d-flex justify-between align-center"><span>Wrong:</span> {statistics.wrong.length}</div> |
| 470 | + <div class="process-nav d-flex justify-between align-center"> |
| 471 | + <button class="previous" on:click={() => handleNavClick('previous')} disabled={current === 0}>Previous</button> |
| 472 | + <button class="next" on:click={() => handleNavClick('next')} disabled={current + 1 === questions.length}>Next</button> |
446 | 473 | </div>
|
447 |
| - </div> |
448 |
| - <div class="process-nav d-flex justify-between align-center"> |
449 |
| - <button class="previous" on:click={() => handleNavClick('previous')} disabled={current === 0}>Previous</button> |
450 |
| - <button class="next" on:click={() => handleNavClick('next')} disabled={current + 1 === questions.length}>Next</button> |
451 |
| - </div> |
452 |
| - <div class="social-sharing d-flex justify-between align-center"> |
453 |
| - <p>Social Media: </p> |
454 |
| - <div class="social-media"> |
455 |
| - <a class="share-to-facebook" href="https://facebook.com/sharer/sharer.php?u=https%3A%2F%2Fjavascript-hub.dezineleo.com" target="_blank" rel="noopener" aria-label=""> |
456 |
| - <img src="/icon-facebook.svg" alt="FaceBook"> |
457 |
| - </a> |
458 |
| - <a class="share-to-twitter" href="https://twitter.com/intent/tweet/?text=JavaScript%20Hub%20-%20Another%20free%20JavaScript%20learning%20application.&url=https%3A%2F%2Fjavascript-hub.dezineleo.com" target="_blank" rel="noopener" aria-label=""> |
459 |
| - <img src="/icon-twitter.svg" alt="Twitter"> |
460 |
| - </a> |
461 |
| - <a class="share-to-github" href="https://github.com/DezineLeo/javascript-hub/issues" target="_blank" rel="noopener" aria-label=""> |
462 |
| - <img src="/icon-github.svg" alt="Instagram"> |
463 |
| - </a> |
| 474 | + <div class="social-sharing d-flex justify-between align-center"> |
| 475 | + <p>Social Media: </p> |
| 476 | + <div class="social-media"> |
| 477 | + <a class="share-to-facebook" href="https://facebook.com/sharer/sharer.php?u=https%3A%2F%2Fjavascript-hub.dezineleo.com" target="_blank" rel="noopener" aria-label=""> |
| 478 | + <img src="/icon-facebook.svg" alt="FaceBook"> |
| 479 | + </a> |
| 480 | + <a class="share-to-twitter" href="https://twitter.com/intent/tweet/?text=JavaScript%20Hub%20-%20Another%20free%20JavaScript%20learning%20application.&url=https%3A%2F%2Fjavascript-hub.dezineleo.com" target="_blank" rel="noopener" aria-label=""> |
| 481 | + <img src="/icon-twitter.svg" alt="Twitter"> |
| 482 | + </a> |
| 483 | + <a class="share-to-github" href="https://github.com/DezineLeo/javascript-hub/issues" target="_blank" rel="noopener" aria-label=""> |
| 484 | + <img src="/icon-github.svg" alt="Instagram"> |
| 485 | + </a> |
| 486 | + </div> |
464 | 487 | </div>
|
465 | 488 | </div>
|
466 | 489 | </div>
|
467 |
| - </div> |
| 490 | + {/if} |
468 | 491 | <div class="modal-container {modalVisible ? 'visible' : ''}" on:click={() => handleModalVisible(false)}>
|
469 | 492 | <div class="modal-body" on:click={e => e.stopPropagation()}>
|
470 | 493 | <img src="/share.svg" alt="Share JavaScript Hub">
|
|
0 commit comments