@webwriter/quiz 1.0.9 → 1.0.10

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 (112) hide show
  1. package/.env +1 -0
  2. package/LICENSE +6 -6
  3. package/custom.d.ts +175 -175
  4. package/dist/widgets/webwriter-choice-item.js +174 -29
  5. package/dist/widgets/webwriter-choice.js +174 -29
  6. package/dist/widgets/webwriter-cloze-gap.js +174 -29
  7. package/dist/widgets/webwriter-cloze.js +174 -29
  8. package/dist/widgets/webwriter-mark.js +174 -29
  9. package/dist/widgets/webwriter-order-item.js +174 -29
  10. package/dist/widgets/webwriter-order.js +174 -29
  11. package/dist/widgets/webwriter-pairing-item.js +174 -29
  12. package/dist/widgets/webwriter-pairing.js +174 -29
  13. package/dist/widgets/webwriter-quiz.js +174 -29
  14. package/dist/widgets/webwriter-speech.js +174 -29
  15. package/dist/widgets/webwriter-task-explainer.js +174 -29
  16. package/dist/widgets/webwriter-task-hint.js +174 -29
  17. package/dist/widgets/webwriter-task-prompt.js +174 -29
  18. package/dist/widgets/webwriter-task.js +176 -31
  19. package/dist/widgets/webwriter-text.js +174 -29
  20. package/lit-localize.json +14 -14
  21. package/localization/bg.xlf +69 -54
  22. package/localization/cs.xlf +69 -54
  23. package/localization/da.xlf +69 -54
  24. package/localization/de.xlf +74 -54
  25. package/localization/el.xlf +69 -54
  26. package/localization/es.xlf +69 -54
  27. package/localization/et.xlf +69 -54
  28. package/localization/fi.xlf +69 -54
  29. package/localization/fr.xlf +69 -54
  30. package/localization/generated/bg.ts +29 -24
  31. package/localization/generated/cs.ts +29 -24
  32. package/localization/generated/da.ts +29 -24
  33. package/localization/generated/de.ts +29 -24
  34. package/localization/generated/el.ts +29 -24
  35. package/localization/generated/es.ts +29 -24
  36. package/localization/generated/et.ts +29 -24
  37. package/localization/generated/fi.ts +29 -24
  38. package/localization/generated/fr.ts +29 -24
  39. package/localization/generated/hu.ts +29 -24
  40. package/localization/generated/id.ts +29 -24
  41. package/localization/generated/index.js +2 -2
  42. package/localization/generated/it.ts +29 -24
  43. package/localization/generated/ja.ts +29 -24
  44. package/localization/generated/ko.ts +29 -24
  45. package/localization/generated/locale-codes.js +79 -79
  46. package/localization/generated/lt.ts +29 -24
  47. package/localization/generated/lv.ts +29 -24
  48. package/localization/generated/nb.ts +29 -24
  49. package/localization/generated/nl.ts +29 -24
  50. package/localization/generated/pl.ts +29 -24
  51. package/localization/generated/pt-BR.ts +29 -24
  52. package/localization/generated/pt-PT.ts +29 -24
  53. package/localization/generated/ro.ts +29 -24
  54. package/localization/generated/ru.ts +29 -24
  55. package/localization/generated/sk.ts +29 -24
  56. package/localization/generated/sl.ts +29 -24
  57. package/localization/generated/sv.ts +29 -24
  58. package/localization/generated/tr.ts +29 -24
  59. package/localization/generated/uk.ts +29 -24
  60. package/localization/generated/zh-hans.ts +29 -24
  61. package/localization/hu.xlf +69 -54
  62. package/localization/id.xlf +69 -54
  63. package/localization/it.xlf +69 -54
  64. package/localization/ja.xlf +69 -54
  65. package/localization/ko.xlf +69 -54
  66. package/localization/lt.xlf +69 -54
  67. package/localization/lv.xlf +69 -54
  68. package/localization/nb.xlf +69 -54
  69. package/localization/nl.xlf +69 -54
  70. package/localization/pl.xlf +69 -54
  71. package/localization/pt-BR.xlf +69 -54
  72. package/localization/pt-PT.xlf +69 -54
  73. package/localization/ro.xlf +69 -54
  74. package/localization/ru.xlf +69 -54
  75. package/localization/sk.xlf +69 -54
  76. package/localization/sl.xlf +69 -54
  77. package/localization/sv.xlf +69 -54
  78. package/localization/tr.xlf +69 -54
  79. package/localization/uk.xlf +69 -54
  80. package/localization/zh-hans.xlf +69 -54
  81. package/package.json +2 -53
  82. package/src/lib/combobox.ts +455 -455
  83. package/src/lib/highlighter-fill.svg +5 -5
  84. package/src/snippets/Beispiel-Choice.html +57 -57
  85. package/src/snippets/Beispiel-Mark.html +13 -13
  86. package/src/snippets/Beispiel-Order.html +52 -52
  87. package/src/snippets/Beispiel-Text.html +20 -20
  88. package/src/snippets/choice.html +7 -7
  89. package/src/snippets/cloze.html +3 -3
  90. package/src/snippets/mark.html +3 -3
  91. package/src/snippets/order.html +7 -7
  92. package/src/snippets/pairing.html +9 -9
  93. package/src/snippets/speech.html +3 -3
  94. package/src/snippets/text.html +3 -3
  95. package/src/snippets/wordsearch.html +3 -3
  96. package/src/widgets/webwriter-choice-item.ts +250 -250
  97. package/src/widgets/webwriter-choice.ts +314 -314
  98. package/src/widgets/webwriter-cloze-gap.ts +215 -215
  99. package/src/widgets/webwriter-cloze.ts +135 -135
  100. package/src/widgets/webwriter-mark.ts +431 -431
  101. package/src/widgets/webwriter-order-item.ts +437 -437
  102. package/src/widgets/webwriter-order.ts +284 -284
  103. package/src/widgets/webwriter-pairing-item.ts +160 -160
  104. package/src/widgets/webwriter-pairing.ts +192 -192
  105. package/src/widgets/webwriter-quiz.ts +281 -281
  106. package/src/widgets/webwriter-speech.ts +272 -272
  107. package/src/widgets/webwriter-task-explainer.ts +42 -42
  108. package/src/widgets/webwriter-task-hint.ts +22 -22
  109. package/src/widgets/webwriter-task-prompt.ts +23 -23
  110. package/src/widgets/webwriter-task.ts +546 -546
  111. package/src/widgets/webwriter-text.ts +135 -135
  112. package/tsconfig.json +6 -6
@@ -1,282 +1,282 @@
1
- import {css, html, PropertyValues} from "lit"
2
- import {LitElementWw, option} from "@webwriter/lit"
3
- import {customElement, property, query, queryAssignedElements} from "lit/decorators.js"
4
-
5
- import SlButton from "@shoelace-style/shoelace/dist/components/button/button.component.js"
6
- import SlButtonGroup from "@shoelace-style/shoelace/dist/components/button-group/button-group.component.js"
7
- import SlDropdown from "@shoelace-style/shoelace/dist/components/dropdown/dropdown.component.js"
8
- import SlMenu from "@shoelace-style/shoelace/dist/components/menu/menu.component.js"
9
- import SlMenuItem from "@shoelace-style/shoelace/dist/components/menu-item/menu-item.component.js"
10
- import SlIcon from "@shoelace-style/shoelace/dist/components/icon/icon.component.js"
11
- import IconUIChecksGrid from "bootstrap-icons/icons/ui-checks-grid.svg"
12
- import Icon123 from "bootstrap-icons/icons/123.svg"
13
- import IconCardText from "bootstrap-icons/icons/card-text.svg"
14
- import IconHighlighter from "bootstrap-icons/icons/highlighter.svg"
15
- import IconSubtract from "bootstrap-icons/icons/subtract.svg"
16
- import IconBodyText from "bootstrap-icons/icons/body-text.svg"
17
- import IconMic from "bootstrap-icons/icons/mic.svg"
18
- import IconImage from "bootstrap-icons/icons/image.svg"
19
- import IconSearch from "bootstrap-icons/icons/search.svg"
20
- import IconGrid3x3Gap from "bootstrap-icons/icons/grid-3x3-gap.svg"
21
-
22
- import "@shoelace-style/shoelace/dist/themes/light.css"
23
- import type { WebwriterTask } from "./webwriter-task.js"
24
-
25
- import LOCALIZE from "../../localization/generated"
26
- import {msg} from "@lit/localize"
27
-
28
-
29
- function shuffle<T>(a: T[]) {
30
- for (let i = a.length - 1; i > 0; i--) {
31
- const j = Math.floor(Math.random() * (i + 1));
32
- [a[i], a[j]] = [a[j], a[i]];
33
- }
34
- return a;
35
- }
36
-
37
- declare global {interface HTMLElementTagNameMap {
38
- "webwriter-quiz": WebwriterQuiz;
39
- }}
40
-
41
- @customElement("webwriter-quiz")
42
- export class WebwriterQuiz extends LitElementWw {
43
-
44
- localize = LOCALIZE
45
-
46
- get answerTypes() {
47
- return {
48
- "webwriter-choice": {
49
- label: msg("Choice"),
50
- icon: IconUIChecksGrid
51
- },
52
- "webwriter-order": {
53
- label: msg("Order"),
54
- icon: Icon123
55
- },
56
- "webwriter-text": {
57
- label: msg("Text"),
58
- icon: IconCardText
59
- },
60
- "webwriter-mark": {
61
- label: msg("Mark"),
62
- icon: IconHighlighter
63
- },/*
64
- "webwriter-pairing": {
65
- label: this.msg("Pairing"),
66
- icon: IconSubtract
67
- },
68
- "webwriter-cloze": {
69
- label: this.msg("Cloze"),
70
- icon: IconBodyText
71
- },*/
72
- "webwriter-speech": {
73
- label: msg("Speech"),
74
- //advanced: true,
75
- icon: IconMic
76
- },
77
- /*"webwriter-wordsearch": {
78
- label: this.msg("Word Search"),
79
- advanced: true,
80
- icon: IconSearch
81
- },
82
- "webwriter-memory": {
83
- label: this.msg("Memory"),
84
- advanced: true,
85
- icon: IconGrid3x3Gap
86
- }*/
87
- }
88
- }
89
-
90
- static scopedElements = {
91
- "sl-button": SlButton,
92
- "sl-button-group": SlButtonGroup,
93
- "sl-icon": SlIcon,
94
- "sl-dropdown": SlDropdown,
95
- "sl-menu": SlMenu,
96
- "sl-menu-item": SlMenuItem,
97
- }
98
-
99
- addTask(answerTypeName: string) {
100
- const task = this.ownerDocument.createElement("webwriter-task") as WebwriterTask
101
- task.setAttribute("counter", this.counter)
102
- const prompt = this.ownerDocument.createElement("webwriter-task-prompt")
103
- const p = this.ownerDocument.createElement("p")
104
- prompt.append(p)
105
- prompt.slot = "prompt"
106
- const answer = this.ownerDocument.createElement(answerTypeName)
107
- task.appendChild(prompt)
108
- task.appendChild(answer)
109
- this.appendChild(task)
110
- document.getSelection().setBaseAndExtent(p, 0, p, 0)
111
- }
112
-
113
- static styles = css`
114
- :host {
115
- border-top: 1px solid darkgray;
116
- border-bottom: 1px solid darkgray;
117
- padding: 1ch;
118
- display: flex !important;
119
- flex-direction: column;
120
- gap: 2rem;
121
- counter-reset: task;
122
- }
123
-
124
- :host(:not([contenteditable=true]):not([contenteditable=""])) .author-only {
125
- display: none;
126
- }
127
-
128
- :host(:is([contenteditable=true], [contenteditable=""])) .user-only {
129
- display: none;
130
- }
131
-
132
- :host(:is([contenteditable=true], [contenteditable=""])) ::slotted(*) {
133
- order: unset !important;
134
- }
135
-
136
- sl-button-group::part(base) {
137
- display: flex;
138
- }
139
-
140
- sl-button-group > *:not(sl-dropdown) {
141
- flex-grow: 1;
142
- }
143
-
144
- sl-dropdown[data-empty] {
145
- display: none;
146
- }
147
-
148
- sl-button:not([caret])::part(label) {
149
- padding: 0;
150
- display: flex;
151
- flex-direction: row;
152
- width: 100%;
153
- justify-content: space-evenly;
154
- align-items: center;
155
- }
156
-
157
- sl-icon {
158
- width: 18px;
159
- height: 18px;
160
- }
161
-
162
- sl-menu::part(menu) {
163
- z-index: 10000;
164
- }
165
-
166
- .user-actions {
167
- & #submit {
168
- flex-grow: 3;
169
- }
170
-
171
- & #reset {
172
- flex-grow: 1;
173
- }
174
- }
175
- `
176
-
177
- @property({type: Boolean, attribute: true, reflect: true})
178
- @option({type: Boolean, label: {"en": "Random Task Order"}})
179
- accessor randomOrder = false
180
-
181
- get counter(): "number" | "roman" | "roman-capitalized" | "alphabetical" | "alphabetical-capitalized" {
182
- return this.tasks[0]?.counter
183
- }
184
-
185
- @property({attribute: true}) //@ts-ignore
186
- @option({
187
- type: "select",
188
- label: {
189
- "en": "Counter"
190
- },
191
- options: [
192
- {value: undefined, label: {"en": "None"}},
193
- {value: "number", label: {"en": "1. 2. 3."}},
194
- {value: "roman", label: {"en": "i. ii. iii."}},
195
- {value: "roman-capitalized", label: {"en": "I. II. III."}},
196
- {value: "alphabetical", label: {"en": "a. b. c."}},
197
- {value: "alphabetical-capitalized", label: {"en": "A. B. C."}},
198
- ]
199
- })
200
- set counter(value) {
201
- this.tasks.forEach(el => el.counter = value)
202
- }
203
-
204
- shuffleTasks() {
205
- const n = this.tasks.length
206
- const nums = shuffle([...(new Array(n)).keys()])
207
- this.tasks.forEach((el, i) => el.style.order = String(nums[i]))
208
- }
209
-
210
- handleSubmit(e: Event) {
211
- this.submitted = true
212
- this.dispatchEvent(new Event("submit"))
213
- this.tasks.forEach(task => task.handleSubmit())
214
- }
215
-
216
- handleReset = () => {
217
- this.tasks.forEach(task => task.reset())
218
- this.requestUpdate()
219
- this.submitted = false
220
- }
221
-
222
- observer: MutationObserver
223
-
224
- connectedCallback(): void {
225
- super.connectedCallback()
226
- this.observer = new MutationObserver(() => {
227
- if(!this.contentEditable && this.randomOrder) {
228
- this.shuffleTasks()
229
- }
230
- })
231
- this.observer.observe(this, {childList: true})
232
- }
233
-
234
- disconnectedCallback(): void {
235
- super.disconnectedCallback()
236
- this.observer.disconnect()
237
- }
238
-
239
- @query("slot")
240
- accessor slotEl: HTMLSlotElement
241
-
242
- @queryAssignedElements()
243
- accessor tasks: WebwriterTask[]
244
-
245
- @property({type: Boolean, attribute: true, reflect: true})
246
- accessor submitted = false
247
-
248
- get isChanged() {
249
- return this.tasks.some(task => task.isChanged)
250
- }
251
-
252
- render() {
253
- const basicAnswerTypes = Object.keys(this.answerTypes).filter(k => !this.answerTypes[k]?.advanced)
254
- const otherAnswerTypes = Object.keys(this.answerTypes).filter(k => this.answerTypes[k]?.advanced)
255
- return html`
256
- <slot ?inert=${this.submitted} @ww-answer-change=${() => this.requestUpdate()}></slot>
257
- <sl-button-group class="user-only user-actions">
258
- <sl-button id="submit" @click=${this.handleSubmit}>${msg("Submit")}</sl-button>
259
- <sl-button ?disabled=${!this.isChanged} id="reset" @click=${this.handleReset}>${msg("Reset")}</sl-button>
260
- </sl-button-group>
261
- <sl-button-group class="author-only">
262
- ${basicAnswerTypes.map(k => html`
263
- <sl-button @click=${() => this.addTask(k)}>
264
- <sl-icon src=${this.answerTypes[k]?.icon}></sl-icon>
265
- ${this.answerTypes[k].label}
266
- </sl-button>
267
- `)}
268
- <sl-dropdown data-empty=${!otherAnswerTypes.length} placement="bottom-end" hoist>
269
- <sl-button slot="trigger" caret></sl-button>
270
- <sl-menu>
271
- ${otherAnswerTypes.map(k => html`
272
- <sl-menu-item @click=${() => this.addTask(k)}>
273
- <sl-icon src=${this.answerTypes[k]?.icon}></sl-icon>
274
- ${this.answerTypes[k].label}
275
- </sl-menu-item>
276
- `)}
277
- </sl-menu>
278
- </sl-dropdown>
279
- </sl-button-group>
280
- `
281
- }
1
+ import {css, html, PropertyValues} from "lit"
2
+ import {LitElementWw, option} from "@webwriter/lit"
3
+ import {customElement, property, query, queryAssignedElements} from "lit/decorators.js"
4
+
5
+ import SlButton from "@shoelace-style/shoelace/dist/components/button/button.component.js"
6
+ import SlButtonGroup from "@shoelace-style/shoelace/dist/components/button-group/button-group.component.js"
7
+ import SlDropdown from "@shoelace-style/shoelace/dist/components/dropdown/dropdown.component.js"
8
+ import SlMenu from "@shoelace-style/shoelace/dist/components/menu/menu.component.js"
9
+ import SlMenuItem from "@shoelace-style/shoelace/dist/components/menu-item/menu-item.component.js"
10
+ import SlIcon from "@shoelace-style/shoelace/dist/components/icon/icon.component.js"
11
+ import IconUIChecksGrid from "bootstrap-icons/icons/ui-checks-grid.svg"
12
+ import Icon123 from "bootstrap-icons/icons/123.svg"
13
+ import IconCardText from "bootstrap-icons/icons/card-text.svg"
14
+ import IconHighlighter from "bootstrap-icons/icons/highlighter.svg"
15
+ import IconSubtract from "bootstrap-icons/icons/subtract.svg"
16
+ import IconBodyText from "bootstrap-icons/icons/body-text.svg"
17
+ import IconMic from "bootstrap-icons/icons/mic.svg"
18
+ import IconImage from "bootstrap-icons/icons/image.svg"
19
+ import IconSearch from "bootstrap-icons/icons/search.svg"
20
+ import IconGrid3x3Gap from "bootstrap-icons/icons/grid-3x3-gap.svg"
21
+
22
+ import "@shoelace-style/shoelace/dist/themes/light.css"
23
+ import type { WebwriterTask } from "./webwriter-task.js"
24
+
25
+ import LOCALIZE from "../../localization/generated"
26
+ import {msg} from "@lit/localize"
27
+
28
+
29
+ function shuffle<T>(a: T[]) {
30
+ for (let i = a.length - 1; i > 0; i--) {
31
+ const j = Math.floor(Math.random() * (i + 1));
32
+ [a[i], a[j]] = [a[j], a[i]];
33
+ }
34
+ return a;
35
+ }
36
+
37
+ declare global {interface HTMLElementTagNameMap {
38
+ "webwriter-quiz": WebwriterQuiz;
39
+ }}
40
+
41
+ @customElement("webwriter-quiz")
42
+ export class WebwriterQuiz extends LitElementWw {
43
+
44
+ localize = LOCALIZE
45
+
46
+ get answerTypes() {
47
+ return {
48
+ "webwriter-choice": {
49
+ label: msg("Choice"),
50
+ icon: IconUIChecksGrid
51
+ },
52
+ "webwriter-order": {
53
+ label: msg("Order"),
54
+ icon: Icon123
55
+ },
56
+ "webwriter-text": {
57
+ label: msg("Text"),
58
+ icon: IconCardText
59
+ },
60
+ "webwriter-mark": {
61
+ label: msg("Mark"),
62
+ icon: IconHighlighter
63
+ },/*
64
+ "webwriter-pairing": {
65
+ label: this.msg("Pairing"),
66
+ icon: IconSubtract
67
+ },
68
+ "webwriter-cloze": {
69
+ label: this.msg("Cloze"),
70
+ icon: IconBodyText
71
+ },*/
72
+ "webwriter-speech": {
73
+ label: msg("Speech"),
74
+ //advanced: true,
75
+ icon: IconMic
76
+ },
77
+ /*"webwriter-wordsearch": {
78
+ label: this.msg("Word Search"),
79
+ advanced: true,
80
+ icon: IconSearch
81
+ },
82
+ "webwriter-memory": {
83
+ label: this.msg("Memory"),
84
+ advanced: true,
85
+ icon: IconGrid3x3Gap
86
+ }*/
87
+ }
88
+ }
89
+
90
+ static scopedElements = {
91
+ "sl-button": SlButton,
92
+ "sl-button-group": SlButtonGroup,
93
+ "sl-icon": SlIcon,
94
+ "sl-dropdown": SlDropdown,
95
+ "sl-menu": SlMenu,
96
+ "sl-menu-item": SlMenuItem,
97
+ }
98
+
99
+ addTask(answerTypeName: string) {
100
+ const task = this.ownerDocument.createElement("webwriter-task") as WebwriterTask
101
+ task.setAttribute("counter", this.counter)
102
+ const prompt = this.ownerDocument.createElement("webwriter-task-prompt")
103
+ const p = this.ownerDocument.createElement("p")
104
+ prompt.append(p)
105
+ prompt.slot = "prompt"
106
+ const answer = this.ownerDocument.createElement(answerTypeName)
107
+ task.appendChild(prompt)
108
+ task.appendChild(answer)
109
+ this.appendChild(task)
110
+ document.getSelection().setBaseAndExtent(p, 0, p, 0)
111
+ }
112
+
113
+ static styles = css`
114
+ :host {
115
+ border-top: 1px solid darkgray;
116
+ border-bottom: 1px solid darkgray;
117
+ padding: 1ch;
118
+ display: flex !important;
119
+ flex-direction: column;
120
+ gap: 2rem;
121
+ counter-reset: task;
122
+ }
123
+
124
+ :host(:not([contenteditable=true]):not([contenteditable=""])) .author-only {
125
+ display: none;
126
+ }
127
+
128
+ :host(:is([contenteditable=true], [contenteditable=""])) .user-only {
129
+ display: none;
130
+ }
131
+
132
+ :host(:is([contenteditable=true], [contenteditable=""])) ::slotted(*) {
133
+ order: unset !important;
134
+ }
135
+
136
+ sl-button-group::part(base) {
137
+ display: flex;
138
+ }
139
+
140
+ sl-button-group > *:not(sl-dropdown) {
141
+ flex-grow: 1;
142
+ }
143
+
144
+ sl-dropdown[data-empty] {
145
+ display: none;
146
+ }
147
+
148
+ sl-button:not([caret])::part(label) {
149
+ padding: 0;
150
+ display: flex;
151
+ flex-direction: row;
152
+ width: 100%;
153
+ justify-content: space-evenly;
154
+ align-items: center;
155
+ }
156
+
157
+ sl-icon {
158
+ width: 18px;
159
+ height: 18px;
160
+ }
161
+
162
+ sl-menu::part(menu) {
163
+ z-index: 10000;
164
+ }
165
+
166
+ .user-actions {
167
+ & #submit {
168
+ flex-grow: 3;
169
+ }
170
+
171
+ & #reset {
172
+ flex-grow: 1;
173
+ }
174
+ }
175
+ `
176
+
177
+ @property({type: Boolean, attribute: true, reflect: true})
178
+ @option({type: Boolean, label: {"en": "Random Task Order"}})
179
+ accessor randomOrder = false
180
+
181
+ get counter(): "number" | "roman" | "roman-capitalized" | "alphabetical" | "alphabetical-capitalized" {
182
+ return this.tasks[0]?.counter
183
+ }
184
+
185
+ @property({attribute: true}) //@ts-ignore
186
+ @option({
187
+ type: "select",
188
+ label: {
189
+ "en": "Counter"
190
+ },
191
+ options: [
192
+ {value: undefined, label: {"en": "None"}},
193
+ {value: "number", label: {"en": "1. 2. 3."}},
194
+ {value: "roman", label: {"en": "i. ii. iii."}},
195
+ {value: "roman-capitalized", label: {"en": "I. II. III."}},
196
+ {value: "alphabetical", label: {"en": "a. b. c."}},
197
+ {value: "alphabetical-capitalized", label: {"en": "A. B. C."}},
198
+ ]
199
+ })
200
+ set counter(value) {
201
+ this.tasks.forEach(el => el.counter = value)
202
+ }
203
+
204
+ shuffleTasks() {
205
+ const n = this.tasks.length
206
+ const nums = shuffle([...(new Array(n)).keys()])
207
+ this.tasks.forEach((el, i) => el.style.order = String(nums[i]))
208
+ }
209
+
210
+ handleSubmit(e: Event) {
211
+ this.submitted = true
212
+ this.dispatchEvent(new Event("submit"))
213
+ this.tasks.forEach(task => task.handleSubmit())
214
+ }
215
+
216
+ handleReset = () => {
217
+ this.tasks.forEach(task => task.reset())
218
+ this.requestUpdate()
219
+ this.submitted = false
220
+ }
221
+
222
+ observer: MutationObserver
223
+
224
+ connectedCallback(): void {
225
+ super.connectedCallback()
226
+ this.observer = new MutationObserver(() => {
227
+ if(!this.contentEditable && this.randomOrder) {
228
+ this.shuffleTasks()
229
+ }
230
+ })
231
+ this.observer.observe(this, {childList: true})
232
+ }
233
+
234
+ disconnectedCallback(): void {
235
+ super.disconnectedCallback()
236
+ this.observer.disconnect()
237
+ }
238
+
239
+ @query("slot")
240
+ accessor slotEl: HTMLSlotElement
241
+
242
+ @queryAssignedElements()
243
+ accessor tasks: WebwriterTask[]
244
+
245
+ @property({type: Boolean, attribute: true, reflect: true})
246
+ accessor submitted = false
247
+
248
+ get isChanged() {
249
+ return this.tasks.some(task => task.isChanged)
250
+ }
251
+
252
+ render() {
253
+ const basicAnswerTypes = Object.keys(this.answerTypes).filter(k => !this.answerTypes[k]?.advanced)
254
+ const otherAnswerTypes = Object.keys(this.answerTypes).filter(k => this.answerTypes[k]?.advanced)
255
+ return html`
256
+ <slot ?inert=${this.submitted} @ww-answer-change=${() => this.requestUpdate()}></slot>
257
+ <sl-button-group class="user-only user-actions">
258
+ <sl-button id="submit" @click=${this.handleSubmit}>${msg("Submit")}</sl-button>
259
+ <sl-button ?disabled=${!this.isChanged} id="reset" @click=${this.handleReset}>${msg("Reset")}</sl-button>
260
+ </sl-button-group>
261
+ <sl-button-group class="author-only">
262
+ ${basicAnswerTypes.map(k => html`
263
+ <sl-button @click=${() => this.addTask(k)}>
264
+ <sl-icon src=${this.answerTypes[k]?.icon}></sl-icon>
265
+ ${this.answerTypes[k].label}
266
+ </sl-button>
267
+ `)}
268
+ <sl-dropdown data-empty=${!otherAnswerTypes.length} placement="bottom-end" hoist>
269
+ <sl-button slot="trigger" caret></sl-button>
270
+ <sl-menu>
271
+ ${otherAnswerTypes.map(k => html`
272
+ <sl-menu-item @click=${() => this.addTask(k)}>
273
+ <sl-icon src=${this.answerTypes[k]?.icon}></sl-icon>
274
+ ${this.answerTypes[k].label}
275
+ </sl-menu-item>
276
+ `)}
277
+ </sl-menu>
278
+ </sl-dropdown>
279
+ </sl-button-group>
280
+ `
281
+ }
282
282
  }