ui: Almost all interface done with Svelte

This commit is contained in:
nemunaire 2021-08-30 12:46:18 +02:00
commit 7e13cf28bd
54 changed files with 2809 additions and 16 deletions

View file

@ -0,0 +1,161 @@
<script context="module">
import { get_store_value } from 'svelte/internal';
import { themes } from '../../stores/themes.js';
export async function load({ page, fetch, session, context }) {
let exercice = null;
for (let ex in context.theme.exercices) {
if (context.theme.exercices[ex].urlid === page.params.exercice) {
exercice = context.theme.exercices[ex];
exercice.id = ex;
break;
}
}
return {
props: {
theme: context.theme,
exercice: exercice,
}
};
}
</script>
<script>
import {
Alert,
Badge,
Card,
Col,
Icon,
Row,
} from 'sveltestrap';
import ExerciceDownloads from '../../components/ExerciceDownloads.svelte';
import ExerciceFlags from '../../components/ExerciceFlags.svelte';
import ExerciceHints from '../../components/ExerciceHints.svelte';
import ExerciceSolved from '../../components/ExerciceSolved.svelte';
import ExerciceVideo from '../../components/ExerciceVideo.svelte';
import ThemeNav from '../../components/ThemeNav.svelte';
import { my } from '../../stores/my.js';
import { settings } from '../../stores/settings.js';
export let theme;
export let exercice;
</script>
<svelte:head>
<title>{exercice.title} - {$settings.title}</title>
</svelte:head>
{#if exercice}
<ThemeNav {theme} {exercice} />
{/if}
{#if !$my || !$my.exercices[exercice.id]}
<Alert color="warning" class="mt-3" fade={false}>
<Icon name="dash-circle-fill" />
Vous n'avez pas encore accès à ce défi.
</Alert>
{/if}
{#if exercice}
<Card body class="niceborder text-indent my-3">
<h3 class="display-4">{exercice.title}</h3>
<div>
{#each exercice.tags as tag, index}
<Badge href="/tags/{tag}" pill color="secondary" class="mx-1 mb-2" >#{tag}</Badge>
{/each}
</div>
{#if !$my || !$my.exercices[exercice.id]}
<p class="lead text-justify">{@html exercice.headline}</p>
{:else}
<p class="lead text-justify">{@html $my.exercices[exercice.id].statement}</p>
{#if $my.exercices[exercice.id].issue}
<Alert color="{$my.exercices[exercice.id].issuekind}">
{@html $my.exercices[exercice.id].issue}
</Alert>
{/if}
{/if}
<hr class="mt-0 mb-4">
<Row>
<Col>
<ul>
<li>
<strong>Gain&nbsp;:</strong>
{exercice.gain} {exercice.gain==1?"point":"points"}
{#if $settings.firstBlood && exercice.solved < 1}
<em>+{$settings.firstBlood * 100}% (prem's)</em>
{/if}
{#if exercice.curcoeff != 1.0 || $settings.exerciceCurrentCoefficient != 1.0}
<em>{#if exercice.curcoeff * $settings.exerciceCurrentCoefficient > 1}+{Math.round((exercice.curcoeff * $settings.exerciceCurrentCoefficient - 1) * 100)}{:else}-{Math.round((1-(exercice.curcoeff * $settings.exerciceCurrentCoefficient)) * 100)}{/if}% (bonus)</em>
{/if}
</li>
<li>
<strong>Tenté par&nbsp;:</strong>
{#if !exercice.tried}
aucune équipe
{:else}
{exercice.tried} {exercice.tried == 1?"équipe":"équipes"}
{#if $my && $my.exercices[exercice.id].total_tries}
(cumulant {$my.exercices[exercice.id].total_tries} {$my.exercices[exercice.id].total_tries == 1?"tentative":"tentatives"})
{/if}
{/if}
</li>
<li>
<strong>Résolu par&nbsp;:</strong>
{#if !exercice.solved}
aucune équipe
{:else}
{exercice.solved} {exercice.solved == 1?"équipe":"équipes"}
{/if}
</li>
</ul>
</Col>
{#if $my && $my.team_id}
<Col>
{#if $settings.acceptNewIssue}
<a href="/issues/?eid={exercice.id}" class="float-end btn btn-sm btn-warning">
<Icon name="bug" />
Rapporter une anomalie sur ce défi
</a>
{/if}
{#if $settings.QAenabled}
<a href="/qa/exercices/{exercice.id}" class="float-end btn btn-sm btn-info" target="_self">
<Icon name="bug" />
Voir les éléments QA sur ce défi
</a>
{/if}
</Col>
{/if}
</Row>
</Card>
{#if $my && $my.exercices[exercice.id]}
<Row class="mt-4">
{#if $my.exercices[exercice.id].files || $my.exercices[exercice.id].hints}
<Col lg class="mb-5">
{#if $my.exercices[exercice.id].files}
<ExerciceDownloads files={$my.exercices[exercice.id].files} />
{/if}
{#if $my.exercices[exercice.id].hints}
<ExerciceHints hints={$my.exercices[exercice.id].hints} />
{/if}
</Col>
{/if}
<Col lg class="mb-5">
{#if !$my.exercices[exercice.id].solved}
<ExerciceFlags {exercice} flags={$my.exercices[exercice.id].flags} />
{:else}
<ExerciceSolved theme={theme} exercice={$my.exercices[exercice.id]} />
{/if}
{#if $my.exercices[exercice.id].video_uri}
<ExerciceVideo uri={$my.exercices[exercice.id].video_uri} />
{/if}
</Col>
</Row>
{/if}
{/if}

View file

@ -0,0 +1,94 @@
<script context="module">
import { get_store_value } from 'svelte/internal';
import { themes } from '../../stores/themes.js';
export async function load({ page, fetch, session, context }) {
const thms = get_store_value(themes);
let theme = null;
for (let th in thms) {
if (thms[th].urlid === page.params.theme) {
theme = thms[th];
break;
}
}
return {
context: {
...context,
theme: theme,
}, props: {
theme: theme,
}
};
}
</script>
<script>
import {
Container,
} from 'sveltestrap';
import { settings } from '../../stores/settings.js';
export let theme = null;
</script>
<svelte:head>
<title>{theme.name} - {$settings.title}</title>
</svelte:head>
{#if theme}
<div style="background-image: url({theme.image})" class="page-header">
<Container class="text-primary">
<h1 class="display-2">
<a href="/{theme.urlid}">{theme.name}</a>
</h1>
<h2>{@html theme.authors}</h2>
</Container>
<div class="headerfade"></div>
</div>
{/if}
<Container>
<slot></slot>
</Container>
<style>
.page-header {
background-size: cover;
background-position: center;
margin-bottom: -15rem;
}
.page-header h1 {
text-shadow: 0 0 15px rgba(255,255,255,0.95), 0 0 5px rgb(255,255,255)
}
.page-header h1, .page-header h1 a {
color: black;
text-decoration: none;
}
.page-header h2 {
font-size: 100%;
text-shadow: 1px 1px 1px rgba(0,0,0,0.9)
}
.page-header h2, .page-header h2 a {
color: #4eaee6;
}
.page-header h2 a:hover {
text-decoration: underline;
}
.page-header h1 {
padding-top: 4rem;
text-align: center;
}
.page-header h2 {
padding-bottom: 14rem;
text-align: center;
}
.page-header .headerfade {
background: linear-gradient(transparent 0%, rgb(233,236,239) 100%);
height: 3rem;
}
</style>

View file

@ -0,0 +1,103 @@
<script context="module">
export async function load({ page, fetch, session, context }) {
return {
props: {
theme: context.theme,
},
};
}
</script>
<script>
import {
Alert,
Badge,
Icon,
} from 'sveltestrap';
import { goto } from '$app/navigation';
import { my } from '../../stores/my.js';
export let theme = null;
</script>
{#if theme && theme.exercices}
<div class="card niceborder text-indent mt-2 mb-4">
<div class="card-body bg-dark text-light">
<p class="mt-4 mx-3 card-text lead text-justify">{@html theme.headline}</p>
<p class="mb-4 mx-3 card-text text-justify">{@html theme.intro}</p>
</div>
<ul class="list-group">
{#each Object.keys(theme.exercices) as k, index}
<li
class="list-group-item"
class:list-group-item-action={$my && $my.exercices[k]}
on:click={goto(`/${theme.urlid}/${theme.exercices[k].urlid}`)}
>
<div class="row">
<div class="col-1" style="margin-top: -0.5rem; margin-bottom: -0.5rem; text-align: right; border-right: 5px solid #{$my && $my.exercices[k] && $my.exercices[k].solved ? '62c462' : 'aaa'}">
</div>
<div class="col-10">
<div style="position: absolute; margin-left: calc(var(--bs-gutter-x) * -.5 - 15px); margin-top: -0.5rem;">
<svg style="height: 50px; width: 23px;">
<rect
style="fill:#{$my && $my.exercices[k] && (index < 1 || $my.exercices[Object.keys(theme.exercices)[index-1]].solved) ? '62c462' : 'aaa'}"
width="5"
height="30"
x="10"
y="0" />
<path
style="fill:#{$my && $my.exercices[k] ? ($my.exercices[k].solved ? '62c462' : (theme.exercices[k].curcoeff > 1.0 ? 'f89406' : '5bc0de')) : '555'}"
d="m 22,20 a 9.5700617,9.5700617 0 0 1 -9.5690181,9.57006 9.5700617,9.5700617 0 0 1 -9.57110534,-9.56797 9.5700617,9.5700617 0 0 1 9.56692984,-9.57215 9.5700617,9.5700617 0 0 1 9.5731926,9.56588" />
</svg>
</div>
{#each theme.exercices[k].tags as tag, idx}
<Badge href="/tags/{tag}" pill color="secondary" class="mx-1 float-end">#{tag}</Badge>
{/each}
<h5 class="fw-bold">
{#if $my && $my.exercices[k]}
{theme.exercices[k].title}
{:else}
<span style="white-space: nowrap">
<Icon name="lock-fill" aria-hidden="true" title="Vous n'avez pas encore accès à ce défi" />
{theme.exercices[k].title}
</span>
{/if}
{#if theme.exercices[k].curcoeff > 1.0}
<Icon name="gift" aria-hidden="true" title="Un bonus est actuellement appliqué lors de la résolution de ce défi" />
{/if}
</h5>
<p>{@html theme.exercices[k].headline}</p>
</div>
<div class="d-none d-md-block col-1">
{#if $my && $my.exercices[k]}
<a class="float-right" href="{theme.urlid}/{theme.exercices[k].urlid}" style="font-size: 3rem">
<Icon name="chevron-right" aria-hidden="true" />
</a>
{:else}
<span class="float-right" style="font-size: 3rem">
<Icon name="chevron-right" aria-hidden="true" />
</span>
{/if}
</div>
</div>
</li>
{/each}
</ul>
</div>
{:else}
<Alert color="danger" fade={false}>
<Icon name="dash-circle-fill" />
Ce scénario n'existe pas.
</Alert>
{/if}
<style>
.list-group-item-action {
cursor: pointer;
}
</style>