server/frontend/fic/src/lib/components/FlagKey.svelte

253 lines
8.5 KiB
Svelte
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script>
import {
Badge,
Button,
Icon,
Spinner,
} from '@sveltestrap/sveltestrap';
import { tick } from 'svelte';
import { my } from '$lib/stores/my.js';
import { settings } from '$lib/stores/settings.js';
export { className as class };
let className = '';
export let exercice_id = 0;
export let flag = { };
export let no_label = false;
export let value = "";
let values = [""];
function waitChoices(i) {
my.refresh((my) => {
let haveChoices = false;
if (my && my.exercices[exercice_id].flags) {
my.exercices[exercice_id].flags.forEach((f) => {
if (f.id == flag.id && f.choices) {
haveChoices = true;
}
})
}
if (haveChoices) {
wcsubmitted = false;
} else if (i > 0) {
setTimeout(waitChoices, 450, i-1);
}
})
}
let wcsubmitted = false;
async function wantchoices() {
if (!confirm("Êtes-vous sûr de vouloir utiliser " + (flag.choices_cost * $settings.wchoiceCurrentCoefficient) + " points pour avoir une liste de propositions à la place de ce champ de texte à compléter ?")) {
return;
}
wcsubmitted = true;
const response = await fetch(
"wantchoices/" + exercice_id,
{
method: "POST",
headers: {'Accept': 'application/json'},
body: JSON.stringify({ id: Number(flag.id) }),
}
)
if (response.status < 300) {
waitChoices(15);
} else {
wcsubmitted = false;
}
}
function addItem() {
values.push("");
values = values;
}
$: {
let v = values.slice();
// Remove empty cells
if (!flag.nb_lines) {
for (let i = v.length - 1; i > 0; i--) {
if (v[i] === null || !String(v[i]).length) {
v.splice(i, 1);
}
}
}
// Sort cells (case doesn't count in sort)
if (flag.ignore_order) {
v = v.sort((a,b) => {
const aUC = String(a).toUpperCase();
const bUC = String(b).toUpperCase();
if (aUC < bUC) {
return -1;
}
if (aUC > bUC) {
return 1;
}
return 0;
});
}
value = v.join(flag.separator ? flag.separator : ',');
if (flag.separator) {
value += flag.separator;
}
}
$: {
if (flag.nb_lines) {
while (values.length != flag.nb_lines) {
if (values.length > flag.nb_lines) {
values.pop();
} else {
values.push("");
}
}
}
}
</script>
<div class={className + " form-group"}>
{#if flag.bonus_gain}
<div class={'float-end badge bg-' + (flag.found?'success':'danger')} title={'Ce flag est optionnel, si vous le complétez il vous rapportera ' + flag.bonus_gain + ' points supplémentaires'}>
optionnel | {#if flag.bonus_gain > 0}+{/if}{flag.bonus_gain}&nbsp;pts
</div>
{/if}
{#if !no_label}
<label for="sol_{flag.type}{flag.id}_0">{flag.label}&nbsp;:</label>
{/if}
{#if !flag.found && !$settings.hideCaseSensitivity && !flag.ignore_case}
<Badge
class="float-end"
color="danger"
title="Ce flag est sensible à la casse!"
>
aA
</Badge>
{/if}
{#if !flag.found}
{#each values as v, index}
{#if !flag.choices}
<div class="input-group" class:mt-1={index != 0}>
{#if flag.type == 'number'}
<input
type="number"
class="form-control flag"
id="sol_{flag.type}{flag.id}_{index}"
autocomplete="off"
bind:value={values[index]}
placeholder={flag.placeholder}
title={flag.placeholder}
min={flag.min}
max={flag.max}
step={flag.step}
>
{:else if !flag.multiline}
<input
type="text"
class="form-control flag"
id="sol_{flag.type}{flag.id}_{index}"
autocomplete="off"
bind:value={values[index]}
placeholder={flag.placeholder}
title={flag.placeholder}
on:keydown={(e) => {if (flag.separator && e.keyCode === 13) { e.preventDefault(); addItem(); tick().then(() => { document.getElementById('sol_' + flag.type + '' + flag.id + '_' + (values.length - 1)).focus(); }); return false;}}}
>
{:else}
<textarea
class="form-control flag"
id="sol_{flag.type}{flag.id}_{index}"
autocomplete="off"
bind:value={values[index]}
placeholder="{flag.placeholder}"
title="{flag.placeholder}"
></textarea>
{/if}
{#if flag.unit}
<span class="input-group-text">{flag.unit}</span>
{/if}
{#if flag.choices_cost > 0}
<Button
color="success"
type="button"
on:click={wantchoices}
disabled={wcsubmitted || $settings.disablesubmitbutton}
title="Cliquez pour échanger ce champ de texte par une liste de choix. L'opération vous coûtera {flag.choices_cost * $settings.wchoiceCurrentCoefficient} points."
>
{#if wcsubmitted}
<Spinner size="sm" class="me-2" />
{/if}
<Icon name="list-task" />
Liste de propositions ({flag.choices_cost * $settings.wchoiceCurrentCoefficient} {flag.choices_cost * $settings.wchoiceCurrentCoefficient===1?"point":"points"})
</Button>
{:else if flag.separator && !flag.nb_lines && index == values.length - 1}
<Button
color="success"
type="button"
title="Ajouter un élément."
on:click={addItem}
>
<Icon name="plus" />
</Button>
{/if}
</div>
{:else if flag.type == 'radio'}
{#each Object.keys(flag.choices) as l, i}
<div class="form-check">
<input
id="sol_{flag.type}{flag.id}_{index}_{i}"
type="radio"
value={l}
bind:group={values[index]}
class="form-check-input"
>
<label
class="form-check-label"
for="sol_{flag.type}{flag.id}_{index}_{i}"
>
{flag.choices[l]}
</label>
</div>
{/each}
{:else}
<select
class="form-select"
id="sol_{flag.type}{flag.id}_{index}"
bind:value={values[index]}
>
{#each Object.keys(flag.choices) as l}
<option value={l}>{flag.choices[l]}</option>
{/each}
</select>
{/if}
{/each}
{#if flag.help}
<small class="form-text text-muted">{@html flag.help}</small>
{/if}
{:else if value}
<input
class="form-control is-valid"
disabled
id="sol_{flag.type}{flag.id}_0"
type="text"
title="Flag trouvé à {flag.found}"
value={value}
>
{:else}
<Icon
name="check"
class="form-control-feedback text-success"
aria-hidden="true"
title="Flag trouvé à {flag.found}"
/>
{/if}
</div>