@erudit-js/prose 4.0.0-dev.4 → 4.0.0

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.
Files changed (83) hide show
  1. package/dist/app/default/Inliners.vue +11 -11
  2. package/dist/app/default/Mix.vue +11 -11
  3. package/dist/app/default/Text.vue +25 -25
  4. package/dist/app/shared/Prose.vue +40 -40
  5. package/dist/app/shared/Render.vue +51 -51
  6. package/dist/app/shared/block/AsideMenu.vue +44 -44
  7. package/dist/app/shared/block/AsideMenuButton.vue +51 -53
  8. package/dist/app/shared/block/AsideMenuCopyLink.vue +40 -40
  9. package/dist/app/shared/block/AsideMenuSeparator.vue +3 -3
  10. package/dist/app/shared/block/Block.vue +270 -275
  11. package/dist/app/shared/inliner/Inliner.vue +11 -11
  12. package/dist/app/shared/photoswipe/style.css +22 -26
  13. package/dist/elements/accent/Accent.vue +88 -92
  14. package/dist/elements/accent/AccentColumnSection.vue +61 -61
  15. package/dist/elements/accent/AccentRowSections.vue +64 -65
  16. package/dist/elements/callout/Callout.vue +81 -85
  17. package/dist/elements/callout/_global.d.ts +15 -15
  18. package/dist/elements/caption/Caption.vue +44 -47
  19. package/dist/elements/caption/_global.d.ts +26 -26
  20. package/dist/elements/details/Details.vue +49 -50
  21. package/dist/elements/details/_global.d.ts +27 -27
  22. package/dist/elements/diagram/Diagram.vue +360 -374
  23. package/dist/elements/diagram/_global.d.ts +19 -19
  24. package/dist/elements/diagram/core.d.ts +0 -4
  25. package/dist/elements/diagram/core.js +1 -7
  26. package/dist/elements/emphasis/Emphasis.vue +25 -25
  27. package/dist/elements/emphasis/_global.d.ts +18 -18
  28. package/dist/elements/flex/Flex.vue +36 -38
  29. package/dist/elements/flex/_global.d.ts +23 -23
  30. package/dist/elements/gallery/Gallery.vue +56 -56
  31. package/dist/elements/gallery/_global.d.ts +18 -18
  32. package/dist/elements/heading/Heading.vue +44 -44
  33. package/dist/elements/heading/_global.d.ts +42 -45
  34. package/dist/elements/heading/core.js +7 -14
  35. package/dist/elements/horizontalLine/HorizontalLine.vue +6 -6
  36. package/dist/elements/horizontalLine/_global.d.ts +17 -17
  37. package/dist/elements/image/Image.vue +15 -15
  38. package/dist/elements/image/ImageElement.vue +80 -80
  39. package/dist/elements/image/_global.d.ts +18 -18
  40. package/dist/elements/lineBreak/LineBreak.vue +3 -3
  41. package/dist/elements/lineBreak/_global.d.ts +18 -18
  42. package/dist/elements/link/BlockLink.vue +108 -110
  43. package/dist/elements/link/Link.vue +92 -96
  44. package/dist/elements/link/dependency/_global.d.ts +47 -47
  45. package/dist/elements/link/reference/_global.d.ts +49 -49
  46. package/dist/elements/list/List.vue +58 -65
  47. package/dist/elements/list/_global.d.ts +50 -50
  48. package/dist/elements/math/_global.d.ts +72 -72
  49. package/dist/elements/math/_global.ts +3 -3
  50. package/dist/elements/math/components/BlockMath.vue +30 -30
  51. package/dist/elements/math/components/InlinerMath.vue +65 -65
  52. package/dist/elements/math/components/Katex.vue +88 -89
  53. package/dist/elements/math/components/MathGroup.vue +41 -41
  54. package/dist/elements/math/katex.js +17 -6
  55. package/dist/elements/paragraph/Paragraph.vue +25 -27
  56. package/dist/elements/paragraph/_global.d.ts +27 -27
  57. package/dist/elements/problem/_global.d.ts +112 -112
  58. package/dist/elements/problem/components/Problem.vue +22 -22
  59. package/dist/elements/problem/components/ProblemButton.vue +20 -21
  60. package/dist/elements/problem/components/ProblemContainer.vue +8 -8
  61. package/dist/elements/problem/components/ProblemContent.vue +356 -371
  62. package/dist/elements/problem/components/ProblemExpander.vue +7 -7
  63. package/dist/elements/problem/components/ProblemExpanderSection.vue +57 -57
  64. package/dist/elements/problem/components/ProblemHeader.vue +100 -102
  65. package/dist/elements/problem/components/Problems.vue +83 -87
  66. package/dist/elements/problem/components/SubProblem.vue +14 -14
  67. package/dist/elements/problem/components/expanders/Check.vue +153 -150
  68. package/dist/elements/problem/components/expanders/Checks.vue +146 -83
  69. package/dist/elements/problem/components/expanders/DefaultPlusSections.vue +38 -38
  70. package/dist/elements/problem/components/expanders/Hint.vue +26 -26
  71. package/dist/elements/problem/core.d.ts +23 -23
  72. package/dist/elements/problem/problemContent.d.ts +134 -30
  73. package/dist/elements/problem/problemContent.js +157 -197
  74. package/dist/elements/problem/problemScript.d.ts +2 -2
  75. package/dist/elements/problem/problemScript.js +2 -2
  76. package/dist/elements/problem/rng.d.ts +1 -1
  77. package/dist/elements/problem/rng.js +1 -1
  78. package/dist/elements/table/Table.vue +100 -102
  79. package/dist/elements/table/_global.d.ts +36 -36
  80. package/dist/elements/video/Video.vue +110 -109
  81. package/dist/elements/video/_global.d.ts +18 -18
  82. package/package.json +42 -43
  83. package/types.d.ts +4 -4
@@ -1,150 +1,153 @@
1
- <script lang="ts" setup>
2
- import { useTemplateRef, ref, watch } from 'vue';
3
- import type { ProseElement } from '@jsprose/core';
4
-
5
- import { checkValue, type problemCheckSchema } from '../../problemContent.js';
6
- import checkIcon from '../../assets/actions/check.svg?raw';
7
- import plusIcon from '../../../../app/shared/assets/plus.svg?raw';
8
- import successIcon from '../../../../app/shared/assets/check.svg?raw';
9
- import { useFormatText } from '../../../../app/composables/formatText.js';
10
- import { useProseContext } from '../../../../app/index.js';
11
- import { useProblemPhrase } from '../../composables/phrase.js';
12
-
13
- type CheckStatus = 'default' | 'correct' | 'wrong';
14
- type CheckState = {
15
- icon: string;
16
- iconClass?: string;
17
- labelClass: string;
18
- inputClass: string;
19
- buttonClass: string;
20
- };
21
-
22
- const { check, script } = defineProps<{
23
- check: ProseElement<typeof problemCheckSchema>;
24
- script?: {
25
- check: (answer: string | undefined) => boolean;
26
- clear: () => void;
27
- };
28
- }>();
29
-
30
- const states: Record<CheckStatus, CheckState> = {
31
- default: {
32
- icon: checkIcon,
33
- labelClass: 'text-text-muted',
34
- inputClass: 'border-border text-text',
35
- buttonClass:
36
- 'text-text-muted border-border hocus:border-text-muted hocus:text-text',
37
- },
38
- correct: {
39
- icon: successIcon,
40
- labelClass: 'text-lime-600',
41
- inputClass: 'text-lime-600 border-lime-500',
42
- buttonClass: 'text-lime-600 border-lime-500',
43
- },
44
- wrong: {
45
- icon: plusIcon,
46
- iconClass: 'rotate-45',
47
- labelClass: 'text-red-700 dark:text-red-400',
48
- inputClass: 'text-red-700 dark:text-red-400 border-red-400',
49
- buttonClass: 'text-red-700 dark:text-red-400 border-red-400',
50
- },
51
- };
52
-
53
- const state = ref<CheckStatus>('default');
54
-
55
- const formatText = useFormatText();
56
- const { EruditIcon, EruditTransition } = useProseContext();
57
- const phrase = await useProblemPhrase();
58
-
59
- const answerInputElement = useTemplateRef('answer');
60
- const answerInput = ref<string>('');
61
- const lastCheckedInput = ref<string | undefined | null>(null);
62
-
63
- watch(answerInput, () => {
64
- state.value = 'default';
65
- lastCheckedInput.value = null;
66
- script?.clear();
67
- });
68
-
69
- function doCheck() {
70
- let newInput = answerInput.value.trim().replace(/\s+/g, ' ') || undefined;
71
-
72
- if (newInput === lastCheckedInput.value) {
73
- return;
74
- }
75
-
76
- lastCheckedInput.value = newInput;
77
-
78
- if (script) {
79
- const isCorrect = script.check(newInput);
80
- state.value = isCorrect ? 'correct' : 'wrong';
81
- return;
82
- }
83
-
84
- state.value = checkValue(newInput, check.data) ? 'correct' : 'wrong';
85
- }
86
- </script>
87
-
88
- <template>
89
- <div class="gap-small flex w-full flex-col">
90
- <div
91
- :class="[
92
- 'text-main-sm font-medium transition-[color]',
93
- states[state].labelClass,
94
- ]"
95
- >
96
- <span @click="answerInputElement?.focus()">
97
- {{ formatText(check.data.label ?? phrase.action_answer) + ':' }}
98
- </span>
99
- </div>
100
- <form class="flex" @submit.prevent="doCheck">
101
- <input
102
- ref="answer"
103
- type="text"
104
- name="answer"
105
- v-model="answerInput"
106
- :placeholder="check.data.placeholder"
107
- autocomplete="off"
108
- :class="[
109
- `bg-bg-main text-main-sm relative z-10 min-w-0 flex-1
110
- rounded rounded-tr-none rounded-br-none border border-r-0
111
- px-2.5 py-1 transition-[border,color,background]`,
112
- states[state].inputClass,
113
- ]"
114
- />
115
- <button
116
- type="submit"
117
- :class="[
118
- `bg-bg-main relative w-[50px] cursor-pointer rounded
119
- rounded-tl-none rounded-bl-none border outline-0
120
- transition-[border,color,background]`,
121
- states[state].buttonClass,
122
- ]"
123
- >
124
- <EruditIcon
125
- :key="state"
126
- :name="states[state].icon"
127
- :class="[
128
- 'invisible m-auto text-[1.2em]',
129
- states[state].iconClass,
130
- ]"
131
- />
132
- <EruditTransition>
133
- <EruditIcon
134
- :key="state"
135
- :name="states[state].icon"
136
- :class="[
137
- `absolute top-1/2 left-1/2 -translate-1/2
138
- text-[1.2em]`,
139
- states[state].iconClass,
140
- ]"
141
- />
142
- </EruditTransition>
143
- </button>
144
- </form>
145
-
146
- <div class="text-text-dimmed text-main-xs italic">
147
- {{ check.data.hint ? formatText(check.data.hint) : '' }}
148
- </div>
149
- </div>
150
- </template>
1
+ <script setup lang="ts">
2
+ import { useTemplateRef, ref, watch } from 'vue';
3
+ import type { ProseElement } from '@jsprose/core';
4
+
5
+ import { checkValue, type problemCheckSchema } from '../../problemContent.js';
6
+ import checkIcon from '../../assets/actions/check.svg?raw';
7
+ import plusIcon from '../../../../app/shared/assets/plus.svg?raw';
8
+ import successIcon from '../../../../app/shared/assets/check.svg?raw';
9
+ import { useFormatText } from '../../../../app/composables/formatText.js';
10
+ import { useProseContext } from '../../../../app/index.js';
11
+ import { useProblemPhrase } from '../../composables/phrase.js';
12
+
13
+ type CheckStatus = 'default' | 'correct' | 'wrong';
14
+
15
+ type CheckState = {
16
+ icon: string;
17
+ iconClass?: string;
18
+ labelClass: string;
19
+ inputClass: string;
20
+ buttonClass: string;
21
+ };
22
+
23
+ const { check, script } = defineProps<{
24
+ check: ProseElement<typeof problemCheckSchema>;
25
+ script?: {
26
+ check: (answer: string | undefined) => boolean;
27
+ clear: () => void;
28
+ };
29
+ }>();
30
+
31
+ const emit = defineEmits<{
32
+ (event: 'status-change', status: CheckStatus): void;
33
+ }>();
34
+
35
+ const states: Record<CheckStatus, CheckState> = {
36
+ default: {
37
+ icon: checkIcon,
38
+ labelClass: 'text-text-muted',
39
+ inputClass: 'border-border text-text',
40
+ buttonClass:
41
+ 'text-text-muted border-border hocus:border-text-muted hocus:text-text',
42
+ },
43
+ correct: {
44
+ icon: successIcon,
45
+ labelClass: 'text-lime-600',
46
+ inputClass: 'text-lime-600 border-lime-500',
47
+ buttonClass: 'text-lime-600 border-lime-500',
48
+ },
49
+ wrong: {
50
+ icon: plusIcon,
51
+ iconClass: 'rotate-45',
52
+ labelClass: 'text-red-700 dark:text-red-400',
53
+ inputClass: 'text-red-700 dark:text-red-400 border-red-400',
54
+ buttonClass: 'text-red-700 dark:text-red-400 border-red-400',
55
+ },
56
+ };
57
+
58
+ const state = ref<CheckStatus>('default');
59
+
60
+ const formatText = useFormatText();
61
+ const { EruditIcon, EruditTransition } = useProseContext();
62
+ const phrase = await useProblemPhrase();
63
+
64
+ const answerInputElement = useTemplateRef<HTMLInputElement>('answer');
65
+ const answerInput = ref('');
66
+ const lastCheckedInput = ref<string | undefined | null>(null);
67
+
68
+ watch(answerInput, () => {
69
+ state.value = 'default';
70
+ lastCheckedInput.value = null;
71
+ script?.clear();
72
+ emit('status-change', 'default');
73
+ });
74
+
75
+ function doCheck() {
76
+ const newInput = answerInput.value.trim().replace(/\s+/g, ' ') || undefined;
77
+
78
+ if (newInput === lastCheckedInput.value) return;
79
+
80
+ lastCheckedInput.value = newInput;
81
+
82
+ if (script) {
83
+ const ok = script.check(newInput);
84
+ state.value = ok ? 'correct' : 'wrong';
85
+ emit('status-change', state.value);
86
+ return;
87
+ }
88
+
89
+ state.value = checkValue(newInput, check.data) ? 'correct' : 'wrong';
90
+
91
+ emit('status-change', state.value);
92
+ }
93
+ </script>
94
+
95
+ <template>
96
+ <div class="gap-small flex w-full flex-col">
97
+ <div
98
+ :class="[
99
+ 'text-main-sm font-medium transition-[color]',
100
+ states[state].labelClass,
101
+ ]"
102
+ >
103
+ <span @click="answerInputElement?.focus()">
104
+ {{ formatText(check.data.label ?? phrase.action_answer) + ':' }}
105
+ </span>
106
+ </div>
107
+
108
+ <form class="flex" @submit.prevent="doCheck">
109
+ <input
110
+ ref="answer"
111
+ v-model="answerInput"
112
+ type="text"
113
+ autocomplete="off"
114
+ :placeholder="check.data.placeholder"
115
+ :class="[
116
+ `bg-bg-main text-main-sm relative z-10 min-w-0 flex-1 rounded
117
+ rounded-tr-none rounded-br-none border border-r-0 px-2.5 py-1
118
+ transition-[border,color,background]`,
119
+ states[state].inputClass,
120
+ ]"
121
+ />
122
+
123
+ <button
124
+ type="submit"
125
+ :class="[
126
+ `bg-bg-main relative w-[50px] cursor-pointer rounded rounded-tl-none
127
+ rounded-bl-none border outline-0 transition-[border,color,background]`,
128
+ states[state].buttonClass,
129
+ ]"
130
+ >
131
+ <EruditIcon
132
+ :key="state"
133
+ :name="states[state].icon"
134
+ :class="['invisible m-auto text-[1.2em]', states[state].iconClass]"
135
+ />
136
+ <EruditTransition>
137
+ <EruditIcon
138
+ :key="state"
139
+ :name="states[state].icon"
140
+ :class="[
141
+ 'absolute top-1/2 left-1/2 -translate-1/2 text-[1.2em]',
142
+ states[state].iconClass,
143
+ ]"
144
+ />
145
+ </EruditTransition>
146
+ </button>
147
+ </form>
148
+
149
+ <div class="text-text-dimmed text-main-xs italic">
150
+ {{ check.data.hint ? formatText(check.data.hint) : '' }}
151
+ </div>
152
+ </div>
153
+ </template>
@@ -1,83 +1,146 @@
1
- <script setup lang="ts">
2
- import { ref } from 'vue';
3
- import type { ProseElement } from '@jsprose/core';
4
-
5
- import type {
6
- CheckFunction,
7
- problemCheckSchema,
8
- } from '../../problemContent.js';
9
- import ProblemExpander from '../ProblemExpander.vue';
10
- import Check from './Check.vue';
11
-
12
- const { value } = defineProps<{
13
- value: {
14
- checkElements: ProseElement<typeof problemCheckSchema>[];
15
- checkFunction?: CheckFunction;
16
- };
17
- }>();
18
-
19
- const scriptCheckFunction = (answer: string | undefined, name: string) => {
20
- if (!value.checkFunction) {
21
- console.warn('No check function defined for script checks!');
22
- return false;
23
- }
24
-
25
- const checkResult = value.checkFunction({
26
- answer,
27
- name,
28
- answers: scriptAnswers.value,
29
- });
30
-
31
- if (checkResult === true) {
32
- scriptAnswers.value[name] = answer;
33
- } else {
34
- delete scriptAnswers.value[name];
35
- }
36
-
37
- return checkResult;
38
- };
39
-
40
- const scriptClearFunction = (name: string) => {
41
- delete scriptAnswers.value[name];
42
- };
43
-
44
- const scriptCheckNameMap = new Map<
45
- ProseElement<typeof problemCheckSchema>,
46
- string
47
- >();
48
- for (const checkElement of value.checkElements) {
49
- if (checkElement.data.script) {
50
- scriptCheckNameMap.set(checkElement, checkElement.data.script);
51
- }
52
- }
53
-
54
- const scriptAnswers = ref<Record<string, string | undefined>>({});
55
- </script>
56
-
57
- <template>
58
- <ProblemExpander>
59
- <div
60
- class="micro:grid-cols-2 grid gap-(--proseAsideWidth)
61
- p-(--proseAsideWidth)"
62
- >
63
- <Check
64
- v-for="check of value.checkElements"
65
- :check
66
- :script="
67
- check.data.script
68
- ? {
69
- check: (answer: string | undefined) => {
70
- const name = scriptCheckNameMap.get(check)!;
71
- return scriptCheckFunction(answer, name);
72
- },
73
- clear: () => {
74
- const name = scriptCheckNameMap.get(check)!;
75
- scriptClearFunction(name);
76
- },
77
- }
78
- : undefined
79
- "
80
- />
81
- </div>
82
- </ProblemExpander>
83
- </template>
1
+ <script setup lang="ts">
2
+ import { computed, ref } from 'vue';
3
+ import { isProseElement, type ProseElement } from '@jsprose/core';
4
+
5
+ import type { CheckFunction } from '../../problemContent.js';
6
+ import { problemCheckSchema } from '../../problemContent.js';
7
+ import ProblemExpander from '../ProblemExpander.vue';
8
+ import Check from './Check.vue';
9
+
10
+ type CheckProseElement = ProseElement<typeof problemCheckSchema>;
11
+ type CheckStatus = 'default' | 'correct' | 'wrong';
12
+
13
+ const { value } = defineProps<{
14
+ value: {
15
+ checkElements: CheckProseElement[];
16
+ checkFunction?: CheckFunction;
17
+ };
18
+ }>();
19
+
20
+ let checkIdCounter = 0;
21
+ const checkIdMap = new WeakMap<CheckProseElement, string>();
22
+
23
+ function getCheckId(check: CheckProseElement) {
24
+ if (!checkIdMap.has(check)) {
25
+ checkIdMap.set(check, `check-${++checkIdCounter}`);
26
+ }
27
+ return checkIdMap.get(check)!;
28
+ }
29
+
30
+ function collectAllChecks(
31
+ checks: CheckProseElement[],
32
+ parent?: CheckProseElement,
33
+ ): CheckProseElement[] {
34
+ const result: CheckProseElement[] = [];
35
+
36
+ for (const check of checks) {
37
+ // attach parent reference if not present
38
+ (check as any)._parent = parent ?? null;
39
+ result.push(check);
40
+
41
+ const children = (check.children ?? []).filter((child) =>
42
+ isProseElement(child, problemCheckSchema),
43
+ ) as CheckProseElement[];
44
+
45
+ if (children.length) {
46
+ result.push(...collectAllChecks(children, check));
47
+ }
48
+ }
49
+
50
+ return result;
51
+ }
52
+
53
+ const allChecks = computed(() => collectAllChecks(value.checkElements));
54
+
55
+ const checkStatus = ref<Record<string, CheckStatus>>({});
56
+
57
+ function setStatus(check: CheckProseElement, status: CheckStatus) {
58
+ checkStatus.value[getCheckId(check)] = status;
59
+ }
60
+
61
+ function isVisible(check: CheckProseElement) {
62
+ const parent = (check as any)._parent as CheckProseElement | null;
63
+
64
+ if (!parent) return true;
65
+
66
+ // Check all ancestors in the parent tree
67
+ let current: CheckProseElement | null = parent;
68
+ while (current) {
69
+ const status = checkStatus.value[getCheckId(current)];
70
+ if (status !== 'correct') {
71
+ return false;
72
+ }
73
+ current = (current as any)._parent as CheckProseElement | null;
74
+ }
75
+
76
+ return true;
77
+ }
78
+
79
+ const scriptAnswers = ref<Record<string, string | undefined>>({});
80
+
81
+ const scriptCheckNameMap = computed(() => {
82
+ const map = new Map<CheckProseElement, string>();
83
+ for (const check of allChecks.value) {
84
+ if (check.data.script) {
85
+ map.set(check, check.data.script);
86
+ }
87
+ }
88
+ return map;
89
+ });
90
+
91
+ function scriptCheckFunction(
92
+ check: CheckProseElement,
93
+ answer: string | undefined,
94
+ ) {
95
+ if (!value.checkFunction) {
96
+ console.warn('No check function defined for script checks!');
97
+ return false;
98
+ }
99
+
100
+ const name = scriptCheckNameMap.value.get(check)!;
101
+
102
+ const result = value.checkFunction({
103
+ name,
104
+ answer,
105
+ answers: scriptAnswers.value,
106
+ });
107
+
108
+ if (result === true) {
109
+ scriptAnswers.value[name] = answer;
110
+ } else {
111
+ delete scriptAnswers.value[name];
112
+ }
113
+
114
+ return result;
115
+ }
116
+
117
+ function scriptClearFunction(check: CheckProseElement) {
118
+ const name = scriptCheckNameMap.value.get(check);
119
+ if (name) delete scriptAnswers.value[name];
120
+ }
121
+ </script>
122
+
123
+ <template>
124
+ <ProblemExpander>
125
+ <div
126
+ class="micro:grid-cols-2 grid gap-(--proseAsideWidth)
127
+ p-(--proseAsideWidth)"
128
+ >
129
+ <Check
130
+ v-for="check in allChecks"
131
+ :key="getCheckId(check)"
132
+ :check="check"
133
+ v-show="isVisible(check)"
134
+ :script="
135
+ check.data.script
136
+ ? {
137
+ check: (answer) => scriptCheckFunction(check, answer),
138
+ clear: () => scriptClearFunction(check),
139
+ }
140
+ : undefined
141
+ "
142
+ @status-change="(status) => setStatus(check, status)"
143
+ />
144
+ </div>
145
+ </ProblemExpander>
146
+ </template>
@@ -1,38 +1,38 @@
1
- <script lang="ts" setup>
2
- import { watchEffect } from 'vue';
3
- import {
4
- isProseElement,
5
- type AnySchema,
6
- type ProseElement,
7
- } from '@jsprose/core';
8
-
9
- import { problemSectionSchema } from '../../problemContent.js';
10
- import { useArrayContainsAnchor } from '../../../../app/composables/anchor.js';
11
- import ProblemExpander from '../ProblemExpander.vue';
12
- import ProblemExpanderSection from '../ProblemExpanderSection.vue';
13
- import Render from '../../../../app/shared/Render.vue';
14
-
15
- const { value } = defineProps<{ value: ProseElement<AnySchema> }>();
16
-
17
- const defaultBlocks = value.children!.filter(
18
- (element) => !isProseElement(element, problemSectionSchema),
19
- );
20
-
21
- const sections = value.children!.filter((element) =>
22
- isProseElement(element, problemSectionSchema),
23
- ) as ProseElement<typeof problemSectionSchema>[];
24
- </script>
25
-
26
- <template>
27
- <ProblemExpander>
28
- <div v-if="defaultBlocks.length" class="py-(--proseAsideWidth)">
29
- <Render v-for="child of defaultBlocks" :element="child" />
30
- </div>
31
- <ProblemExpanderSection
32
- v-if="sections.length"
33
- v-for="section of sections"
34
- :title="section.data"
35
- :element="section"
36
- />
37
- </ProblemExpander>
38
- </template>
1
+ <script lang="ts" setup>
2
+ import { watchEffect } from 'vue';
3
+ import {
4
+ isProseElement,
5
+ type AnySchema,
6
+ type ProseElement,
7
+ } from '@jsprose/core';
8
+
9
+ import { problemSectionSchema } from '../../problemContent.js';
10
+ import { useArrayContainsAnchor } from '../../../../app/composables/anchor.js';
11
+ import ProblemExpander from '../ProblemExpander.vue';
12
+ import ProblemExpanderSection from '../ProblemExpanderSection.vue';
13
+ import Render from '../../../../app/shared/Render.vue';
14
+
15
+ const { value } = defineProps<{ value: ProseElement<AnySchema> }>();
16
+
17
+ const defaultBlocks = value.children!.filter(
18
+ (element) => !isProseElement(element, problemSectionSchema),
19
+ );
20
+
21
+ const sections = value.children!.filter((element) =>
22
+ isProseElement(element, problemSectionSchema),
23
+ ) as ProseElement<typeof problemSectionSchema>[];
24
+ </script>
25
+
26
+ <template>
27
+ <ProblemExpander>
28
+ <div v-if="defaultBlocks.length" class="py-(--proseAsideWidth)">
29
+ <Render v-for="child of defaultBlocks" :element="child" />
30
+ </div>
31
+ <ProblemExpanderSection
32
+ v-if="sections.length"
33
+ v-for="section of sections"
34
+ :title="section.data"
35
+ :element="section"
36
+ />
37
+ </ProblemExpander>
38
+ </template>