@erudit-js/prose 4.0.1 → 4.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app/composables/context.d.ts +6 -1
- package/dist/app/language/element.d.ts +1 -1
- package/dist/elements/accent/Accent.vue +1 -0
- package/dist/elements/diagram/Diagram.vue +6 -10
- package/dist/elements/link/BlockLink.vue +117 -66
- package/dist/elements/link/Link.vue +38 -11
- package/dist/elements/link/core.d.ts +2 -1
- package/dist/elements/link/core.js +1 -1
- package/dist/elements/link/dependency/app.d.ts +2 -2
- package/dist/elements/link/dependency/core.d.ts +18 -24
- package/dist/elements/link/dependency/core.js +1 -17
- package/dist/elements/link/reference/languages/en.d.ts +2 -3
- package/dist/elements/link/reference/languages/en.js +5 -1
- package/dist/elements/link/reference/languages/ru.d.ts +2 -3
- package/dist/elements/link/reference/languages/ru.js +5 -1
- package/dist/elements/link/reference/phrases.d.ts +5 -0
- package/dist/elements/link/reference/phrases.js +1 -0
- package/dist/elements/link/step.d.ts +16 -0
- package/dist/elements/link/step.js +36 -0
- package/dist/elements/link/storage.d.ts +9 -5
- package/dist/elements/link/storage.js +4 -4
- package/dist/elements/math/_global.d.ts +7 -0
- package/dist/elements/math/block.d.ts +2 -0
- package/dist/elements/math/block.js +42 -29
- package/dist/elements/math/components/MathGroup.vue +20 -3
- package/dist/elements/problem/_global.d.ts +174 -29
- package/dist/elements/problem/app.d.ts +7 -7
- package/dist/elements/problem/app.js +13 -12
- package/dist/elements/problem/components/ProblemButtonGenerate.vue +96 -0
- package/dist/elements/problem/components/ProblemContent.vue +14 -104
- package/dist/elements/problem/components/expanders/Check.vue +58 -11
- package/dist/elements/problem/components/expanders/Checks.vue +5 -5
- package/dist/elements/problem/components/expanders/DefaultPlusSections.vue +0 -2
- package/dist/elements/problem/core.d.ts +55 -28
- package/dist/elements/problem/core.js +2 -1
- package/dist/elements/problem/languages/{en.d.ts → problem/en.d.ts} +1 -1
- package/dist/elements/problem/languages/problem/en.js +6 -0
- package/dist/elements/problem/languages/{ru.d.ts → problem/ru.d.ts} +1 -1
- package/dist/elements/problem/languages/problem/ru.js +6 -0
- package/dist/elements/problem/languages/problems/en.d.ts +3 -0
- package/dist/elements/problem/languages/problems/en.js +6 -0
- package/dist/elements/problem/languages/problems/ru.d.ts +3 -0
- package/dist/elements/problem/languages/problems/ru.js +6 -0
- package/dist/elements/problem/languages/shared/en.d.ts +3 -0
- package/dist/elements/problem/languages/{en.js → shared/en.js} +12 -3
- package/dist/elements/problem/languages/shared/ru.d.ts +3 -0
- package/dist/elements/problem/languages/{ru.js → shared/ru.js} +12 -3
- package/dist/elements/problem/phrases.d.ts +4 -0
- package/dist/elements/problem/problemCheck.d.ts +166 -0
- package/dist/elements/problem/problemCheck.js +203 -0
- package/dist/elements/problem/problemContent.d.ts +2 -120
- package/dist/elements/problem/problemContent.js +2 -127
- package/dist/elements/problem/problemScript.d.ts +6 -1
- package/dist/resolve.d.ts +2 -1
- package/dist/resolve.js +8 -5
- package/package.json +2 -2
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { ref, useTemplateRef } from 'vue';
|
|
3
|
+
import { autoUpdate } from '@floating-ui/vue';
|
|
4
|
+
|
|
5
|
+
import generateIcon from '../assets/actions/generate.svg?raw';
|
|
6
|
+
import plusIcon from '../../../app/shared/assets/plus.svg?raw';
|
|
7
|
+
import { useProseContext } from '../../../app/composables/context.js';
|
|
8
|
+
import { useProblemPhrase } from '../composables/phrase.js';
|
|
9
|
+
import { DEFAULT_SEED, type ProblemSeed } from '../rng.js';
|
|
10
|
+
import ProblemButton from './ProblemButton.vue';
|
|
11
|
+
|
|
12
|
+
const emits = defineEmits<{ (e: 'generate', seed: ProblemSeed): void }>();
|
|
13
|
+
|
|
14
|
+
const { EruditTransition, EruditIcon, usePopup } = useProseContext();
|
|
15
|
+
|
|
16
|
+
const seed = ref<ProblemSeed>(DEFAULT_SEED);
|
|
17
|
+
const cogRotation = ref(0);
|
|
18
|
+
const usingCustomSeed = ref(false);
|
|
19
|
+
|
|
20
|
+
function generate() {
|
|
21
|
+
cogRotation.value += 180;
|
|
22
|
+
|
|
23
|
+
if (usingCustomSeed.value) {
|
|
24
|
+
usingCustomSeed.value = false;
|
|
25
|
+
} else {
|
|
26
|
+
seed.value = Math.floor(Math.random() * 1000000) + 1;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
emits('generate', seed.value);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const phrase = await useProblemPhrase();
|
|
33
|
+
|
|
34
|
+
const containerElement = useTemplateRef('container');
|
|
35
|
+
const toggleElement = useTemplateRef('toggle');
|
|
36
|
+
const popupElement = useTemplateRef('popup');
|
|
37
|
+
const { popupVisible, popupStyles } = usePopup(
|
|
38
|
+
containerElement,
|
|
39
|
+
toggleElement,
|
|
40
|
+
popupElement,
|
|
41
|
+
{
|
|
42
|
+
whileElementsMounted: autoUpdate,
|
|
43
|
+
placement: 'top',
|
|
44
|
+
},
|
|
45
|
+
);
|
|
46
|
+
</script>
|
|
47
|
+
|
|
48
|
+
<template>
|
|
49
|
+
<div ref="container">
|
|
50
|
+
<div ref="toggle" class="select-none">
|
|
51
|
+
<ProblemButton @click="generate" class="flex items-center gap-[7px]">
|
|
52
|
+
<EruditIcon
|
|
53
|
+
:name="generateIcon"
|
|
54
|
+
:style="{ transform: `rotate(${cogRotation}deg)` }"
|
|
55
|
+
class="text-[1.3em] transition-[transform] backface-hidden"
|
|
56
|
+
/>
|
|
57
|
+
<span>{{ phrase.action_generate }}</span>
|
|
58
|
+
</ProblemButton>
|
|
59
|
+
</div>
|
|
60
|
+
<EruditTransition>
|
|
61
|
+
<div v-if="popupVisible" ref="popup" :style="popupStyles" class="pb-2.5">
|
|
62
|
+
<form
|
|
63
|
+
class="shadow-border text-main-xs flex rounded bg-neutral-900
|
|
64
|
+
text-white shadow-lg dark:bg-neutral-200 dark:text-black"
|
|
65
|
+
@submit.prevent="generate"
|
|
66
|
+
>
|
|
67
|
+
<input
|
|
68
|
+
type="text"
|
|
69
|
+
v-model="seed"
|
|
70
|
+
@input="usingCustomSeed = true"
|
|
71
|
+
:title="phrase.seed_explain"
|
|
72
|
+
@focus="($event as any).target.select()"
|
|
73
|
+
class="max-w-[100px] flex-1 p-[5px] text-center outline-none"
|
|
74
|
+
/>
|
|
75
|
+
<button
|
|
76
|
+
v-if="seed !== DEFAULT_SEED"
|
|
77
|
+
type="button"
|
|
78
|
+
@click="
|
|
79
|
+
seed = DEFAULT_SEED;
|
|
80
|
+
usingCustomSeed = true;
|
|
81
|
+
generate();
|
|
82
|
+
"
|
|
83
|
+
class="cursor-pointer pr-[3px]"
|
|
84
|
+
>
|
|
85
|
+
<EruditIcon
|
|
86
|
+
:name="plusIcon"
|
|
87
|
+
class="hocus:text-white dark:hocus:text-black rotate-45
|
|
88
|
+
text-[1.3em] text-neutral-400 transition-[color]
|
|
89
|
+
dark:text-neutral-600"
|
|
90
|
+
/>
|
|
91
|
+
</button>
|
|
92
|
+
</form>
|
|
93
|
+
</div>
|
|
94
|
+
</EruditTransition>
|
|
95
|
+
</div>
|
|
96
|
+
</template>
|
|
@@ -4,11 +4,9 @@ import {
|
|
|
4
4
|
onMounted,
|
|
5
5
|
ref,
|
|
6
6
|
shallowRef,
|
|
7
|
-
useTemplateRef,
|
|
8
7
|
watchEffect,
|
|
9
8
|
type Component,
|
|
10
9
|
} from 'vue';
|
|
11
|
-
import { autoUpdate, offset, useFloating } from '@floating-ui/vue';
|
|
12
10
|
import {
|
|
13
11
|
isProseElement,
|
|
14
12
|
resolveRawElement,
|
|
@@ -17,14 +15,14 @@ import {
|
|
|
17
15
|
|
|
18
16
|
import {
|
|
19
17
|
problemAnswer,
|
|
20
|
-
problemCheckSchema,
|
|
21
18
|
problemDescriptionSchema,
|
|
22
19
|
problemHintSchema,
|
|
23
20
|
problemNote,
|
|
24
21
|
problemSolution,
|
|
25
|
-
type CheckFunction,
|
|
26
22
|
type ProblemContentChild,
|
|
27
23
|
} from '../problemContent.js';
|
|
24
|
+
import { problemCheckSchema } from '../problemCheck.js';
|
|
25
|
+
import type { CheckFunction } from '../problemScript.js';
|
|
28
26
|
import { useProseContext } from '../../../app/composables/context.js';
|
|
29
27
|
import { useProblemPhrase } from '../composables/phrase.js';
|
|
30
28
|
import type { ProblemAction } from '../shared.js';
|
|
@@ -36,12 +34,12 @@ import { useElementStorage } from '../../../app/composables/storage.js';
|
|
|
36
34
|
import type { ProblemScriptStorage } from '../storage.js';
|
|
37
35
|
import { createProblemScriptInstance } from '../composables/problemScript.js';
|
|
38
36
|
import { DEFAULT_SEED, type ProblemSeed } from '../rng.js';
|
|
39
|
-
import plusIcon from '../../../app/shared/assets/plus.svg?raw';
|
|
40
37
|
import Render from '../../../app/shared/Render.vue';
|
|
41
38
|
import Hint from './expanders/Hint.vue';
|
|
42
39
|
import DefaultPlusSections from './expanders/DefaultPlusSections.vue';
|
|
43
40
|
import ProblemButton from './ProblemButton.vue';
|
|
44
41
|
import Checks from './expanders/Checks.vue';
|
|
42
|
+
import ProblemButtonGenerate from './ProblemButtonGenerate.vue';
|
|
45
43
|
|
|
46
44
|
const { element, initialElements } = defineProps<{
|
|
47
45
|
element:
|
|
@@ -50,7 +48,7 @@ const { element, initialElements } = defineProps<{
|
|
|
50
48
|
initialElements: ProseElement<ProblemContentChild>[];
|
|
51
49
|
}>();
|
|
52
50
|
|
|
53
|
-
const { EruditIcon
|
|
51
|
+
const { EruditIcon } = useProseContext();
|
|
54
52
|
const phrase = await useProblemPhrase();
|
|
55
53
|
|
|
56
54
|
const actionIcons: Record<ProblemAction, string> = Object.fromEntries(
|
|
@@ -194,31 +192,15 @@ onMounted(async () => {
|
|
|
194
192
|
}
|
|
195
193
|
});
|
|
196
194
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const seed = ref<ProblemSeed>(DEFAULT_SEED);
|
|
200
|
-
const usingCustomSeed = ref(false);
|
|
201
|
-
|
|
202
|
-
async function doGenerate() {
|
|
195
|
+
let currentSeed: ProblemSeed = DEFAULT_SEED;
|
|
196
|
+
async function doGenerate(seed: ProblemSeed) {
|
|
203
197
|
if (!scriptInstance.value) {
|
|
204
198
|
return;
|
|
205
199
|
}
|
|
206
200
|
|
|
207
|
-
|
|
201
|
+
currentSeed = seed;
|
|
208
202
|
|
|
209
|
-
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
generating.value = true;
|
|
214
|
-
|
|
215
|
-
if (usingCustomSeed.value) {
|
|
216
|
-
usingCustomSeed.value = false;
|
|
217
|
-
} else {
|
|
218
|
-
seed.value = Math.floor(Math.random() * 1000000000) + 1;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
const generateResult = scriptInstance.value.generate(seed.value);
|
|
203
|
+
const generateResult = scriptInstance.value.generate(seed);
|
|
222
204
|
|
|
223
205
|
if (generateResult.check) {
|
|
224
206
|
scriptCheck.value = generateResult.check;
|
|
@@ -234,28 +216,14 @@ async function doGenerate() {
|
|
|
234
216
|
proseElements.push(resolveResult.proseElement as any);
|
|
235
217
|
}
|
|
236
218
|
|
|
237
|
-
|
|
219
|
+
if (currentSeed !== seed) {
|
|
220
|
+
// A new generation has been requested while we were async generating.
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
238
223
|
|
|
224
|
+
elements.value = proseElements;
|
|
239
225
|
key.value++;
|
|
240
|
-
generating.value = false;
|
|
241
226
|
}
|
|
242
|
-
|
|
243
|
-
//
|
|
244
|
-
// Seed Popup
|
|
245
|
-
//
|
|
246
|
-
|
|
247
|
-
const seedPopupVisible = ref(false);
|
|
248
|
-
const seedReferenceElement = useTemplateRef('seedReference');
|
|
249
|
-
const seedPopupElement = useTemplateRef('seedPopup');
|
|
250
|
-
|
|
251
|
-
const { floatingStyles: seedFloatingStyles } = useFloating(
|
|
252
|
-
seedReferenceElement,
|
|
253
|
-
seedPopupElement,
|
|
254
|
-
{
|
|
255
|
-
whileElementsMounted: autoUpdate,
|
|
256
|
-
placement: 'top',
|
|
257
|
-
},
|
|
258
|
-
);
|
|
259
227
|
</script>
|
|
260
228
|
|
|
261
229
|
<template>
|
|
@@ -283,65 +251,7 @@ const { floatingStyles: seedFloatingStyles } = useFloating(
|
|
|
283
251
|
<EruditIcon :name="actionIcons[actionKey]" class="text-[1.3em]" />
|
|
284
252
|
<span>{{ phrase[`action_${actionKey}`] }}</span>
|
|
285
253
|
</ProblemButton>
|
|
286
|
-
<
|
|
287
|
-
v-if="isGenerator"
|
|
288
|
-
ref="seedReference"
|
|
289
|
-
@mouseenter="seedPopupVisible = true"
|
|
290
|
-
@mouseleave="seedPopupVisible = false"
|
|
291
|
-
>
|
|
292
|
-
<ProblemButton
|
|
293
|
-
@touchstart="seedPopupVisible = seedPopupVisible ? false : true"
|
|
294
|
-
@click="doGenerate"
|
|
295
|
-
class="flex items-center gap-[7px]"
|
|
296
|
-
>
|
|
297
|
-
<EruditIcon
|
|
298
|
-
:name="actionIcons.generate"
|
|
299
|
-
:style="{ transform: `rotate(${generateRotation}deg)` }"
|
|
300
|
-
class="text-[1.3em] transition-[transform] backface-hidden"
|
|
301
|
-
/>
|
|
302
|
-
<span>{{ phrase.action_generate }}</span>
|
|
303
|
-
</ProblemButton>
|
|
304
|
-
<EruditTransition>
|
|
305
|
-
<div
|
|
306
|
-
v-if="seedPopupVisible"
|
|
307
|
-
ref="seedPopup"
|
|
308
|
-
:style="seedFloatingStyles"
|
|
309
|
-
class="pb-2.5"
|
|
310
|
-
>
|
|
311
|
-
<form
|
|
312
|
-
class="shadow-border text-main-xs flex rounded bg-neutral-900
|
|
313
|
-
text-white shadow-lg dark:bg-neutral-200 dark:text-black"
|
|
314
|
-
@submit.prevent="doGenerate"
|
|
315
|
-
>
|
|
316
|
-
<input
|
|
317
|
-
type="text"
|
|
318
|
-
v-model="seed"
|
|
319
|
-
@input="usingCustomSeed = true"
|
|
320
|
-
:title="phrase.seed_explain"
|
|
321
|
-
@focus="($event as any).target.select()"
|
|
322
|
-
class="max-w-[100px] flex-1 p-[5px] text-center outline-none"
|
|
323
|
-
/>
|
|
324
|
-
<button
|
|
325
|
-
v-if="seed !== DEFAULT_SEED"
|
|
326
|
-
type="button"
|
|
327
|
-
@click="
|
|
328
|
-
seed = DEFAULT_SEED;
|
|
329
|
-
usingCustomSeed = true;
|
|
330
|
-
doGenerate();
|
|
331
|
-
"
|
|
332
|
-
class="cursor-pointer pr-[3px]"
|
|
333
|
-
>
|
|
334
|
-
<EruditIcon
|
|
335
|
-
:name="plusIcon"
|
|
336
|
-
class="hocus:text-white dark:hocus:text-black rotate-45
|
|
337
|
-
text-[1.3em] text-neutral-400 transition-[color]
|
|
338
|
-
dark:text-neutral-600"
|
|
339
|
-
/>
|
|
340
|
-
</button>
|
|
341
|
-
</form>
|
|
342
|
-
</div>
|
|
343
|
-
</EruditTransition>
|
|
344
|
-
</div>
|
|
254
|
+
<ProblemButtonGenerate v-if="isGenerator" @generate="doGenerate" />
|
|
345
255
|
</div>
|
|
346
256
|
|
|
347
257
|
<Suspense suspensible>
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { useTemplateRef, ref, watch } from 'vue';
|
|
2
|
+
import { useTemplateRef, ref, watch, computed } from 'vue';
|
|
3
3
|
import type { ProseElement } from '@jsprose/core';
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
checkProblemAnswer,
|
|
7
|
+
fromSerializableValidator,
|
|
8
|
+
type problemCheckSchema,
|
|
9
|
+
} from '../../problemCheck.js';
|
|
6
10
|
import checkIcon from '../../assets/actions/check.svg?raw';
|
|
7
11
|
import plusIcon from '../../../../app/shared/assets/plus.svg?raw';
|
|
8
12
|
import successIcon from '../../../../app/shared/assets/check.svg?raw';
|
|
@@ -28,6 +32,10 @@ const { check, script } = defineProps<{
|
|
|
28
32
|
};
|
|
29
33
|
}>();
|
|
30
34
|
|
|
35
|
+
const validator = computed(() => {
|
|
36
|
+
return fromSerializableValidator(check.data.serializedValidator);
|
|
37
|
+
});
|
|
38
|
+
|
|
31
39
|
const emit = defineEmits<{
|
|
32
40
|
(event: 'status-change', status: CheckStatus): void;
|
|
33
41
|
}>();
|
|
@@ -61,6 +69,35 @@ const formatText = useFormatText();
|
|
|
61
69
|
const { EruditIcon, EruditTransition } = useProseContext();
|
|
62
70
|
const phrase = await useProblemPhrase();
|
|
63
71
|
|
|
72
|
+
const hint = computed(() => {
|
|
73
|
+
if (check.data.hint) {
|
|
74
|
+
return check.data.hint;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (check.data.serializedValidator.type === 'boolean') {
|
|
78
|
+
return phrase.boolean_check_hint;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (check.data.serializedValidator.type === 'array') {
|
|
82
|
+
return phrase.array_check_hint(
|
|
83
|
+
check.data.serializedValidator.ordered,
|
|
84
|
+
check.data.serializedValidator.separator,
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const labelText = computed(() => {
|
|
90
|
+
const rawLabel = formatText(check.data.label ?? phrase.action_answer);
|
|
91
|
+
const trimmed = rawLabel.trimEnd();
|
|
92
|
+
|
|
93
|
+
if (!trimmed) {
|
|
94
|
+
return '';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const endsWithAlphaNum = /[\p{L}\p{N}]$/u.test(trimmed);
|
|
98
|
+
return endsWithAlphaNum ? `${trimmed}:` : trimmed;
|
|
99
|
+
});
|
|
100
|
+
|
|
64
101
|
const answerInputElement = useTemplateRef<HTMLInputElement>('answer');
|
|
65
102
|
const answerInput = ref('');
|
|
66
103
|
const lastCheckedInput = ref<string | undefined | null>(null);
|
|
@@ -73,20 +110,29 @@ watch(answerInput, () => {
|
|
|
73
110
|
});
|
|
74
111
|
|
|
75
112
|
function doCheck() {
|
|
76
|
-
const newInput = answerInput.value.
|
|
113
|
+
const newInput = answerInput.value.replace(/\s+/g, ' ').trim();
|
|
77
114
|
|
|
78
|
-
if (newInput === lastCheckedInput.value)
|
|
115
|
+
if (newInput === lastCheckedInput.value) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
79
118
|
|
|
80
119
|
lastCheckedInput.value = newInput;
|
|
81
120
|
|
|
82
121
|
if (script) {
|
|
83
|
-
const ok = script.check(newInput);
|
|
122
|
+
const ok = script.check(newInput || undefined);
|
|
84
123
|
state.value = ok ? 'correct' : 'wrong';
|
|
85
124
|
emit('status-change', state.value);
|
|
86
125
|
return;
|
|
87
126
|
}
|
|
88
127
|
|
|
89
|
-
state.value =
|
|
128
|
+
state.value = checkProblemAnswer(
|
|
129
|
+
newInput,
|
|
130
|
+
phrase.yes_regexp,
|
|
131
|
+
phrase.no_regexp,
|
|
132
|
+
validator.value,
|
|
133
|
+
)
|
|
134
|
+
? 'correct'
|
|
135
|
+
: 'wrong';
|
|
90
136
|
|
|
91
137
|
emit('status-change', state.value);
|
|
92
138
|
}
|
|
@@ -101,7 +147,7 @@ function doCheck() {
|
|
|
101
147
|
]"
|
|
102
148
|
>
|
|
103
149
|
<span @click="answerInputElement?.focus()">
|
|
104
|
-
{{
|
|
150
|
+
{{ labelText }}
|
|
105
151
|
</span>
|
|
106
152
|
</div>
|
|
107
153
|
|
|
@@ -113,9 +159,10 @@ function doCheck() {
|
|
|
113
159
|
autocomplete="off"
|
|
114
160
|
:placeholder="check.data.placeholder"
|
|
115
161
|
:class="[
|
|
116
|
-
`bg-bg-main text-main-sm relative z-10 min-w-0 flex-1
|
|
117
|
-
rounded-tr-none rounded-br-none border border-r-0 px-2.5 py-1
|
|
118
|
-
|
|
162
|
+
`bg-bg-main text-main-sm focus:ring-brand relative z-10 min-w-0 flex-1
|
|
163
|
+
rounded rounded-tr-none rounded-br-none border border-r-0 px-2.5 py-1
|
|
164
|
+
ring-2 ring-transparent outline-0
|
|
165
|
+
transition-[border,color,background,box-shadow]`,
|
|
119
166
|
states[state].inputClass,
|
|
120
167
|
]"
|
|
121
168
|
/>
|
|
@@ -147,7 +194,7 @@ function doCheck() {
|
|
|
147
194
|
</form>
|
|
148
195
|
|
|
149
196
|
<div class="text-text-dimmed text-main-xs italic">
|
|
150
|
-
{{
|
|
197
|
+
{{ hint ? formatText(hint) : '' }}
|
|
151
198
|
</div>
|
|
152
199
|
</div>
|
|
153
200
|
</template>
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import { computed, ref } from 'vue';
|
|
3
3
|
import { isProseElement, type ProseElement } from '@jsprose/core';
|
|
4
4
|
|
|
5
|
-
import type { CheckFunction } from '../../
|
|
6
|
-
import { problemCheckSchema } from '../../
|
|
5
|
+
import type { CheckFunction } from '../../problemScript.js';
|
|
6
|
+
import { problemCheckSchema } from '../../problemCheck.js';
|
|
7
7
|
import ProblemExpander from '../ProblemExpander.vue';
|
|
8
8
|
import Check from './Check.vue';
|
|
9
9
|
|
|
@@ -81,8 +81,8 @@ const scriptAnswers = ref<Record<string, string | undefined>>({});
|
|
|
81
81
|
const scriptCheckNameMap = computed(() => {
|
|
82
82
|
const map = new Map<CheckProseElement, string>();
|
|
83
83
|
for (const check of allChecks.value) {
|
|
84
|
-
if (check.data.script) {
|
|
85
|
-
map.set(check, check.data.
|
|
84
|
+
if (check.data.serializedValidator.type === 'script') {
|
|
85
|
+
map.set(check, check.data.serializedValidator.name);
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
return map;
|
|
@@ -132,7 +132,7 @@ function scriptClearFunction(check: CheckProseElement) {
|
|
|
132
132
|
:check="check"
|
|
133
133
|
v-show="isVisible(check)"
|
|
134
134
|
:script="
|
|
135
|
-
check.data.script
|
|
135
|
+
check.data.serializedValidator.type === 'script'
|
|
136
136
|
? {
|
|
137
137
|
check: (answer) => scriptCheckFunction(check, answer),
|
|
138
138
|
clear: () => scriptClearFunction(check),
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import { watchEffect } from 'vue';
|
|
3
2
|
import {
|
|
4
3
|
isProseElement,
|
|
5
4
|
type AnySchema,
|
|
@@ -7,7 +6,6 @@ import {
|
|
|
7
6
|
} from '@jsprose/core';
|
|
8
7
|
|
|
9
8
|
import { problemSectionSchema } from '../../problemContent.js';
|
|
10
|
-
import { useArrayContainsAnchor } from '../../../../app/composables/anchor.js';
|
|
11
9
|
import ProblemExpander from '../ProblemExpander.vue';
|
|
12
10
|
import ProblemExpanderSection from '../ProblemExpanderSection.vue';
|
|
13
11
|
import Render from '../../../../app/shared/Render.vue';
|
|
@@ -93,68 +93,95 @@ declare const _default: [{
|
|
|
93
93
|
name: "problemCheck";
|
|
94
94
|
type: "block";
|
|
95
95
|
linkable: false;
|
|
96
|
-
Data: import("./
|
|
96
|
+
Data: import("./problemCheck.js").ProblemCheckData;
|
|
97
97
|
Storage: undefined;
|
|
98
|
-
Children: import("
|
|
98
|
+
Children: import("./problemCheck.js").ProblemCheckSchema[] | undefined;
|
|
99
99
|
}, {
|
|
100
100
|
ProblemCheck: import("@jsprose/core").Tag<"ProblemCheck", {
|
|
101
101
|
name: "problemCheck";
|
|
102
102
|
type: "block";
|
|
103
103
|
linkable: false;
|
|
104
|
-
Data: import("./
|
|
104
|
+
Data: import("./problemCheck.js").ProblemCheckData;
|
|
105
105
|
Storage: undefined;
|
|
106
|
-
Children: import("
|
|
106
|
+
Children: import("./problemCheck.js").ProblemCheckSchema[] | undefined;
|
|
107
107
|
}, ({
|
|
108
108
|
label?: string;
|
|
109
109
|
hint?: string;
|
|
110
110
|
placeholder?: string;
|
|
111
111
|
} & (((Pick<{
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
112
|
+
yes: true;
|
|
113
|
+
no: true;
|
|
114
|
+
answer: import("./problemCheck.js").ProblemCheckValue;
|
|
115
|
+
answers: (import("./problemCheck.js").ProblemCheckValueDefined | import("./problemCheck.js").ProblemCheckValueDefined[])[] | {
|
|
116
|
+
ordered?: boolean;
|
|
117
|
+
separator?: string;
|
|
118
|
+
values: (import("./problemCheck.js").ProblemCheckValueDefined | import("./problemCheck.js").ProblemCheckValueDefined[])[];
|
|
117
119
|
};
|
|
118
120
|
script: string;
|
|
119
121
|
}, "answer"> & {
|
|
120
|
-
|
|
122
|
+
yes?: undefined;
|
|
123
|
+
no?: undefined;
|
|
121
124
|
answers?: undefined;
|
|
122
125
|
script?: undefined;
|
|
123
126
|
}) | (Pick<{
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
127
|
+
yes: true;
|
|
128
|
+
no: true;
|
|
129
|
+
answer: import("./problemCheck.js").ProblemCheckValue;
|
|
130
|
+
answers: (import("./problemCheck.js").ProblemCheckValueDefined | import("./problemCheck.js").ProblemCheckValueDefined[])[] | {
|
|
131
|
+
ordered?: boolean;
|
|
132
|
+
separator?: string;
|
|
133
|
+
values: (import("./problemCheck.js").ProblemCheckValueDefined | import("./problemCheck.js").ProblemCheckValueDefined[])[];
|
|
129
134
|
};
|
|
130
135
|
script: string;
|
|
131
|
-
}, "
|
|
136
|
+
}, "yes"> & {
|
|
132
137
|
answer?: undefined;
|
|
138
|
+
no?: undefined;
|
|
133
139
|
answers?: undefined;
|
|
134
140
|
script?: undefined;
|
|
135
141
|
}) | (Pick<{
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
142
|
+
yes: true;
|
|
143
|
+
no: true;
|
|
144
|
+
answer: import("./problemCheck.js").ProblemCheckValue;
|
|
145
|
+
answers: (import("./problemCheck.js").ProblemCheckValueDefined | import("./problemCheck.js").ProblemCheckValueDefined[])[] | {
|
|
146
|
+
ordered?: boolean;
|
|
147
|
+
separator?: string;
|
|
148
|
+
values: (import("./problemCheck.js").ProblemCheckValueDefined | import("./problemCheck.js").ProblemCheckValueDefined[])[];
|
|
149
|
+
};
|
|
150
|
+
script: string;
|
|
151
|
+
}, "no"> & {
|
|
152
|
+
answer?: undefined;
|
|
153
|
+
yes?: undefined;
|
|
154
|
+
answers?: undefined;
|
|
155
|
+
script?: undefined;
|
|
156
|
+
}) | (Pick<{
|
|
157
|
+
yes: true;
|
|
158
|
+
no: true;
|
|
159
|
+
answer: import("./problemCheck.js").ProblemCheckValue;
|
|
160
|
+
answers: (import("./problemCheck.js").ProblemCheckValueDefined | import("./problemCheck.js").ProblemCheckValueDefined[])[] | {
|
|
161
|
+
ordered?: boolean;
|
|
162
|
+
separator?: string;
|
|
163
|
+
values: (import("./problemCheck.js").ProblemCheckValueDefined | import("./problemCheck.js").ProblemCheckValueDefined[])[];
|
|
141
164
|
};
|
|
142
165
|
script: string;
|
|
143
166
|
}, "answers"> & {
|
|
144
167
|
answer?: undefined;
|
|
145
|
-
|
|
168
|
+
yes?: undefined;
|
|
169
|
+
no?: undefined;
|
|
146
170
|
script?: undefined;
|
|
147
171
|
}) | (Pick<{
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
172
|
+
yes: true;
|
|
173
|
+
no: true;
|
|
174
|
+
answer: import("./problemCheck.js").ProblemCheckValue;
|
|
175
|
+
answers: (import("./problemCheck.js").ProblemCheckValueDefined | import("./problemCheck.js").ProblemCheckValueDefined[])[] | {
|
|
176
|
+
ordered?: boolean;
|
|
177
|
+
separator?: string;
|
|
178
|
+
values: (import("./problemCheck.js").ProblemCheckValueDefined | import("./problemCheck.js").ProblemCheckValueDefined[])[];
|
|
153
179
|
};
|
|
154
180
|
script: string;
|
|
155
181
|
}, "script"> & {
|
|
156
182
|
answer?: undefined;
|
|
157
|
-
|
|
183
|
+
yes?: undefined;
|
|
184
|
+
no?: undefined;
|
|
158
185
|
answers?: undefined;
|
|
159
186
|
})) & (import("@jsprose/core").TagChildren | import("@jsprose/core").NoTagChildren))) & {}>;
|
|
160
187
|
}, undefined>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { defineEruditProseCoreElements } from "../../coreElement.js";
|
|
2
2
|
import { problemCoreElement } from "./problem.js";
|
|
3
|
-
import {
|
|
3
|
+
import { problemCheckCoreElement } from "./problemCheck.js";
|
|
4
|
+
import { problemAnswer, problemDescriptionCoreElement, problemHintCoreElement, problemNote, problemSectionCoreElement, problemSolution } from "./problemContent.js";
|
|
4
5
|
import { problemsCoreElement, subProblemCoreElement } from "./problems.js";
|
|
5
6
|
export default defineEruditProseCoreElements(
|
|
6
7
|
problemDescriptionCoreElement,
|