@webwriter/quiz 1.1.3 → 1.1.5

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/.github/workflows/publish.yml +8 -0
  2. package/LICENSE +6 -6
  3. package/custom.d.ts +175 -175
  4. package/dist/widgets/webwriter-choice-item.js +69 -205
  5. package/dist/widgets/webwriter-choice.js +72 -205
  6. package/dist/widgets/webwriter-cloze-gap.js +74 -213
  7. package/dist/widgets/webwriter-cloze.js +71 -204
  8. package/dist/widgets/webwriter-mark.js +72 -205
  9. package/dist/widgets/webwriter-order-item.js +68 -201
  10. package/dist/widgets/webwriter-order.js +70 -197
  11. package/dist/widgets/webwriter-pairing-item.js +57 -160
  12. package/dist/widgets/webwriter-pairing.js +69 -196
  13. package/dist/widgets/webwriter-quiz.js +72 -211
  14. package/dist/widgets/webwriter-speech.js +71 -204
  15. package/dist/widgets/webwriter-task-explainer.js +57 -160
  16. package/dist/widgets/webwriter-task-hint.js +55 -158
  17. package/dist/widgets/webwriter-task-prompt.js +61 -170
  18. package/dist/widgets/webwriter-task.js +80 -205
  19. package/dist/widgets/webwriter-text.js +94 -205
  20. package/lit-localize.json +14 -14
  21. package/localization/bg.xlf +72 -69
  22. package/localization/cs.xlf +72 -69
  23. package/localization/da.xlf +72 -69
  24. package/localization/de.xlf +77 -74
  25. package/localization/el.xlf +72 -69
  26. package/localization/es.xlf +72 -69
  27. package/localization/et.xlf +72 -69
  28. package/localization/fi.xlf +72 -69
  29. package/localization/fr.xlf +72 -69
  30. package/localization/generated/bg.ts +30 -29
  31. package/localization/generated/cs.ts +30 -29
  32. package/localization/generated/da.ts +30 -29
  33. package/localization/generated/de.ts +30 -29
  34. package/localization/generated/el.ts +30 -29
  35. package/localization/generated/es.ts +30 -29
  36. package/localization/generated/et.ts +30 -29
  37. package/localization/generated/fi.ts +30 -29
  38. package/localization/generated/fr.ts +30 -29
  39. package/localization/generated/hu.ts +30 -29
  40. package/localization/generated/id.ts +30 -29
  41. package/localization/generated/index.js +2 -2
  42. package/localization/generated/it.ts +30 -29
  43. package/localization/generated/ja.ts +30 -29
  44. package/localization/generated/ko.ts +30 -29
  45. package/localization/generated/locale-codes.js +79 -79
  46. package/localization/generated/lt.ts +30 -29
  47. package/localization/generated/lv.ts +30 -29
  48. package/localization/generated/nb.ts +30 -29
  49. package/localization/generated/nl.ts +30 -29
  50. package/localization/generated/pl.ts +30 -29
  51. package/localization/generated/pt-BR.ts +30 -29
  52. package/localization/generated/pt-PT.ts +30 -29
  53. package/localization/generated/ro.ts +30 -29
  54. package/localization/generated/ru.ts +30 -29
  55. package/localization/generated/sk.ts +30 -29
  56. package/localization/generated/sl.ts +30 -29
  57. package/localization/generated/sv.ts +30 -29
  58. package/localization/generated/tr.ts +30 -29
  59. package/localization/generated/uk.ts +30 -29
  60. package/localization/generated/zh-hans.ts +30 -29
  61. package/localization/hu.xlf +72 -69
  62. package/localization/id.xlf +72 -69
  63. package/localization/it.xlf +72 -69
  64. package/localization/ja.xlf +72 -69
  65. package/localization/ko.xlf +72 -69
  66. package/localization/lt.xlf +72 -69
  67. package/localization/lv.xlf +72 -69
  68. package/localization/nb.xlf +72 -69
  69. package/localization/nl.xlf +72 -69
  70. package/localization/pl.xlf +72 -69
  71. package/localization/pt-BR.xlf +72 -69
  72. package/localization/pt-PT.xlf +72 -69
  73. package/localization/ro.xlf +72 -69
  74. package/localization/ru.xlf +72 -69
  75. package/localization/sk.xlf +72 -69
  76. package/localization/sl.xlf +72 -69
  77. package/localization/sv.xlf +72 -69
  78. package/localization/tr.xlf +72 -69
  79. package/localization/uk.xlf +72 -69
  80. package/localization/zh-hans.xlf +72 -69
  81. package/package.json +4 -3
  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 +256 -253
  97. package/src/widgets/webwriter-choice.ts +352 -352
  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 +440 -440
  102. package/src/widgets/webwriter-order.ts +326 -326
  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 +554 -546
  111. package/src/widgets/webwriter-text.ts +176 -155
  112. package/tsconfig.json +6 -6
@@ -1,327 +1,327 @@
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 SlIcon from "@shoelace-style/shoelace/dist/components/icon/icon.component.js"
7
- import "@shoelace-style/shoelace/dist/themes/light.css"
8
- import { Sortable } from "@shopify/draggable"
9
- import IconPlus from "bootstrap-icons/icons/plus.svg"
10
-
11
- import { WebwriterOrderItem } from "./webwriter-order-item"
12
- import { ifDefined } from "lit/directives/if-defined.js"
13
-
14
- import LOCALIZE from "../../localization/generated"
15
- import {msg} from "@lit/localize"
16
-
17
- export function shuffle<T>(a: T[]) {
18
- for (let i = a.length - 1; i > 0; i--) {
19
- const j = Math.floor(Math.random() * (i + 1));
20
- [a[i], a[j]] = [a[j], a[i]];
21
- }
22
- return a;
23
- }
24
-
25
- declare global {interface HTMLElementTagNameMap {
26
- "webwriter-order": WebwriterOrder;
27
- }}
28
-
29
- @customElement("webwriter-order")
30
- export class WebwriterOrder extends LitElementWw {
31
-
32
- localize = LOCALIZE
33
-
34
- static scopedElements = {
35
- "sl-button": SlButton,
36
- "sl-icon": SlIcon
37
- }
38
-
39
- static styles = css`
40
- :host {
41
- display: flex !important;
42
- flex-direction: column;
43
- align-items: flex-start;
44
- counter-reset: orderItem;
45
- gap: 2px;
46
- }
47
-
48
- :host([layout=tiles]) {
49
- flex-direction: row;
50
- flex-wrap: wrap;
51
- gap: 15px;
52
- }
53
-
54
- :host(:is([contenteditable=true], [contenteditable=""])) {
55
- --text-color: var(--sl-color-success-700);
56
- }
57
-
58
- :host ::slotted(*::part(counter)) {
59
- counter-increment: orderItem;
60
- content: counter(orderItem) '. ';
61
- cursor: move;
62
- text-align: right;
63
- padding-right: 0.5em;
64
- min-width: 1.5em;
65
- color: var(--text-color, auto);
66
- }
67
-
68
- sl-button::part(label) {
69
- padding: 0;
70
- display: flex;
71
- flex-direction: row;
72
- align-items: center;
73
- }
74
-
75
- sl-button::part(base) {
76
- border: none;
77
- background: transparent;
78
- }
79
-
80
- sl-icon {
81
- width: 19px;
82
- height: 19px;
83
- padding: var(--sl-spacing-x-small);
84
- padding-left: 0;
85
- }
86
-
87
- #add-option {
88
- order: 2147483647;
89
- }
90
-
91
- #add-option:not(:hover)::part(base) {
92
- color: darkgray;
93
- }
94
-
95
- :host([layout=tiles]) #add-option {
96
- width: 125px;
97
- height: 125px;
98
- overflow: hidden;
99
- border: 2px solid var(--sl-color-gray-300);
100
- border-radius: 5px;
101
- padding: 5px;
102
- display: flex;
103
- align-items: center;
104
- justify-content: center;
105
- }
106
- // broken for some reason o.O
107
- /*
108
- :host(:not([contenteditable=true]):not([contenteditable=""])) .author-only {
109
- display: none;
110
- } */
111
- `
112
-
113
- get layout(): "list" | "tiles" {
114
- return this.children?.item(0)?.getAttribute("layout") as any ?? "list"
115
- }
116
-
117
- @property({type: String, attribute: true, reflect: true}) //@ts-ignore
118
- @option({
119
- type: "select",
120
- options: [
121
- {value: "list", label: {"en": "List"}},
122
- {value: "tiles", label: {"en": "Tiles"}}
123
- ]
124
- })
125
- set layout(value) {
126
- this.querySelectorAll("webwriter-order-item").forEach(el => el.setAttribute("layout", value))
127
- this.requestUpdate("layout")
128
- }
129
-
130
- get hideOrderButtons() {
131
- return !!this.items[0]?.hideOrderButtons
132
- }
133
-
134
- @property({type: Boolean, attribute: true, reflect: true}) //@ts-ignore
135
- @option({type: Boolean, label: {"en": "Hide order buttons", "de": "Pfeile zum Verschieben verstecken"}})
136
- set hideOrderButtons(value: boolean) {
137
- this.items.forEach(item => item.hideOrderButtons = value)
138
- }
139
-
140
- @property({type: String, attribute: true, reflect: true})
141
- @option({
142
- type: "select",
143
- label: {"de": "Lösung anzeigen", "en": "Show solution"},
144
- options: [
145
- {value: "right", label: {"en": "Right answers", "de": "Korrekte Antworten"}},
146
- {value: "both", label: {"en": "all (indicator)", "de": "Alle (Indikator)"}},
147
- {value: "full", label: {"en": "all (conclusive)", "de": "Alle (vollständig)"}}
148
- ],
149
- })
150
- accessor showSolution = "right"
151
-
152
- observer: MutationObserver
153
-
154
- connectedCallback() {
155
- super.connectedCallback()
156
- this.addEventListener("ww-moveup", this.handleMove)
157
- this.addEventListener("ww-movedown", this.handleMove)
158
- this.addEventListener("ww-moveto", this.handleMove)
159
- this.observer = new MutationObserver(() => {
160
- this.items.forEach(item => item.requestUpdate())
161
- if(this.isContentEditable) {
162
- this.solution = this.items.map(item => item.id)
163
- }
164
- this.clearDropPreviews()
165
- })
166
- this.observer.observe(this, {childList: true})
167
- // this.shadowRoot.adoptedStyleSheets = [...this.shadowRoot.adoptedStyleSheets, this.orderSheet]
168
- }
169
-
170
- serializedCallback() {
171
- this.shuffleItems()
172
- }
173
-
174
- disconnectedCallback(): void {
175
- super.disconnectedCallback()
176
- this.observer?.disconnect()
177
- this.observer = undefined
178
- }
179
-
180
- orderSheet = new CSSStyleSheet()
181
-
182
- addItem() {
183
- const orderItem = this.ownerDocument.createElement("webwriter-order-item")
184
- const p = this.ownerDocument.createElement("p")
185
- orderItem.appendChild(p)
186
- orderItem.setAttribute("layout", this.layout)
187
- this.append(orderItem)
188
- this.solution = [...this.#solution, orderItem.id]
189
- this.ownerDocument.getSelection().setBaseAndExtent(p, 0, p, 0)
190
- }
191
-
192
- shuffleItems() {
193
- const n = this.items.length
194
- const nums = shuffle([...(new Array(n)).keys()])
195
- this.innerHTML = nums.map(i => this.children.item(i).outerHTML).join("")
196
- }
197
-
198
- clearDropPreviews = () => {
199
- this.items.forEach(item => item.dropPreview = undefined)
200
- }
201
-
202
- @query("#items-slot")
203
- accessor itemsSlot: HTMLSlotElement
204
-
205
- @queryAssignedElements()
206
- accessor items: WebwriterOrderItem[]
207
-
208
- handleKeyDown(e: KeyboardEvent) {
209
- if(e.key == "ArrowDown") {
210
- e.stopPropagation()
211
- e.preventDefault()
212
- }
213
- else if(e.key === "ArrowUp") {
214
- e.stopPropagation()
215
- e.preventDefault()
216
- }
217
- }
218
-
219
- /*
220
- If contenteditable: Reorder solution -> Triggers orderSheet change
221
- If not contenteditable: Reorder value -> Triggers orderSheet change
222
- */
223
-
224
- moveChild(elem: HTMLElement, i: number) {
225
- /*
226
- let items = this.items.map(el => el !== elem? el: undefined)
227
- items.splice(i, 0, elem as WebwriterOrderItem)
228
- items = items.filter(child => child)
229
- this.replaceChildren(...items)
230
- this.items.forEach(item => item.requestUpdate())*/
231
- if(i === 0) {
232
- this.insertAdjacentElement("afterbegin", elem)
233
- }
234
- else if(i < this.items.length) {
235
- this.items[i].insertAdjacentElement("beforebegin", elem)
236
- }
237
- else {
238
- this.insertAdjacentElement("beforeend", elem)
239
- }
240
- }
241
-
242
- handleMove = (e: CustomEvent) => {
243
- const elem = e.target as HTMLElement
244
- const children = Array.from(this.children)
245
- const pos = children.indexOf(elem)
246
- let i: number
247
- if(e.type === "ww-moveup") {
248
- i = Math.max(0, pos - 1)
249
- }
250
- else if(e.type === "ww-movedown") {
251
- i = Math.min(children.length, pos + 2)
252
-
253
- }
254
- else if(e.type === "ww-moveto") {
255
- i = e.detail.i
256
- }
257
- this.moveChild(elem, i)
258
- }
259
-
260
-
261
- #solution: string[] = []
262
-
263
- get solution() {
264
- return this.#solution?.length? this.#solution: undefined
265
- }
266
-
267
- @property({attribute: false})
268
- set solution(value: string[]) {
269
- this.#solution = value
270
- if(value) {
271
- this.dispatchEvent(new CustomEvent("ww-answer-change", {
272
- bubbles: true,
273
- composed: true
274
- }))
275
- }
276
- }
277
-
278
-
279
- reportSolution() {
280
- this.solution.forEach((id, i) => (this.querySelector(`#${id}`) as any).validOrder = i)
281
- let wrongSolution: boolean = false
282
- this.items.forEach(item => {
283
- if(this.items.indexOf(item) != this.solution.indexOf(item.id)){
284
- wrongSolution = true
285
- if(this.showSolution != "full"){
286
- this.items.forEach(i => {
287
- i.showSolution = false
288
- })
289
- }
290
- }
291
- })
292
- if(wrongSolution){
293
- if(this.showSolution != "right"){
294
- this.style.backgroundColor = "#F9B5C4"
295
- }
296
- }else{
297
- this.style.backgroundColor = "#BCE194"
298
- }
299
- this.items.forEach(item=>{
300
- item.style.pointerEvents="none"
301
- })
302
- }
303
-
304
- reset() {
305
- this.solution = undefined
306
- this.style.backgroundColor = ""
307
- this.shuffleItems()
308
- this.items.forEach(item=>{
309
- item.style.pointerEvents="auto"
310
- })
311
- }
312
-
313
- protected firstUpdated(_changedProperties: PropertyValues): void {
314
- this.showSolution = this.showSolution
315
- }
316
-
317
- render() {
318
- return html`
319
- <slot id="items-slot" @webwriter-clear-drop-preview=${this.clearDropPreviews}></slot>
320
- ${this.hasAttribute("contenteditable")?html`
321
- <sl-button size="small" id="add-option" @click=${() => this.addItem()}>
322
- <sl-icon src=${IconPlus}></sl-icon><span>${msg("Add Option")}</span>
323
- </sl-button>
324
- `:""}
325
- `
326
- }
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 SlIcon from "@shoelace-style/shoelace/dist/components/icon/icon.component.js"
7
+ import "@shoelace-style/shoelace/dist/themes/light.css"
8
+ import { Sortable } from "@shopify/draggable"
9
+ import IconPlus from "bootstrap-icons/icons/plus.svg"
10
+
11
+ import { WebwriterOrderItem } from "./webwriter-order-item"
12
+ import { ifDefined } from "lit/directives/if-defined.js"
13
+
14
+ import LOCALIZE from "../../localization/generated"
15
+ import {msg} from "@lit/localize"
16
+
17
+ export function shuffle<T>(a: T[]) {
18
+ for (let i = a.length - 1; i > 0; i--) {
19
+ const j = Math.floor(Math.random() * (i + 1));
20
+ [a[i], a[j]] = [a[j], a[i]];
21
+ }
22
+ return a;
23
+ }
24
+
25
+ declare global {interface HTMLElementTagNameMap {
26
+ "webwriter-order": WebwriterOrder;
27
+ }}
28
+
29
+ @customElement("webwriter-order")
30
+ export class WebwriterOrder extends LitElementWw {
31
+
32
+ localize = LOCALIZE
33
+
34
+ static scopedElements = {
35
+ "sl-button": SlButton,
36
+ "sl-icon": SlIcon
37
+ }
38
+
39
+ static styles = css`
40
+ :host {
41
+ display: flex !important;
42
+ flex-direction: column;
43
+ align-items: flex-start;
44
+ counter-reset: orderItem;
45
+ gap: 2px;
46
+ }
47
+
48
+ :host([layout=tiles]) {
49
+ flex-direction: row;
50
+ flex-wrap: wrap;
51
+ gap: 15px;
52
+ }
53
+
54
+ :host(:is([contenteditable=true], [contenteditable=""])) {
55
+ --text-color: var(--sl-color-success-700);
56
+ }
57
+
58
+ :host ::slotted(*::part(counter)) {
59
+ counter-increment: orderItem;
60
+ content: counter(orderItem) '. ';
61
+ cursor: move;
62
+ text-align: right;
63
+ padding-right: 0.5em;
64
+ min-width: 1.5em;
65
+ color: var(--text-color, auto);
66
+ }
67
+
68
+ sl-button::part(label) {
69
+ padding: 0;
70
+ display: flex;
71
+ flex-direction: row;
72
+ align-items: center;
73
+ }
74
+
75
+ sl-button::part(base) {
76
+ border: none;
77
+ background: transparent;
78
+ }
79
+
80
+ sl-icon {
81
+ width: 19px;
82
+ height: 19px;
83
+ padding: var(--sl-spacing-x-small);
84
+ padding-left: 0;
85
+ }
86
+
87
+ #add-option {
88
+ order: 2147483647;
89
+ }
90
+
91
+ #add-option:not(:hover)::part(base) {
92
+ color: darkgray;
93
+ }
94
+
95
+ :host([layout=tiles]) #add-option {
96
+ width: 125px;
97
+ height: 125px;
98
+ overflow: hidden;
99
+ border: 2px solid var(--sl-color-gray-300);
100
+ border-radius: 5px;
101
+ padding: 5px;
102
+ display: flex;
103
+ align-items: center;
104
+ justify-content: center;
105
+ }
106
+ // broken for some reason o.O
107
+ /*
108
+ :host(:not([contenteditable=true]):not([contenteditable=""])) .author-only {
109
+ display: none;
110
+ } */
111
+ `
112
+
113
+ get layout(): "list" | "tiles" {
114
+ return this.children?.item(0)?.getAttribute("layout") as any ?? "list"
115
+ }
116
+
117
+ @property({type: String, attribute: true, reflect: true}) //@ts-ignore
118
+ @option({
119
+ type: "select",
120
+ options: [
121
+ {value: "list", label: {"en": "List"}},
122
+ {value: "tiles", label: {"en": "Tiles"}}
123
+ ]
124
+ })
125
+ set layout(value) {
126
+ this.querySelectorAll("webwriter-order-item").forEach(el => el.setAttribute("layout", value))
127
+ this.requestUpdate("layout")
128
+ }
129
+
130
+ get hideOrderButtons() {
131
+ return !!this.items[0]?.hideOrderButtons
132
+ }
133
+
134
+ @property({type: Boolean, attribute: true, reflect: true}) //@ts-ignore
135
+ @option({type: Boolean, label: {"en": "Hide order buttons", "de": "Pfeile zum Verschieben verstecken"}})
136
+ set hideOrderButtons(value: boolean) {
137
+ this.items.forEach(item => item.hideOrderButtons = value)
138
+ }
139
+
140
+ @property({type: String, attribute: true, reflect: true})
141
+ @option({
142
+ type: "select",
143
+ label: {"de": "Lösung anzeigen", "en": "Show solution"},
144
+ options: [
145
+ {value: "right", label: {"en": "Right answers", "de": "Korrekte Antworten"}},
146
+ {value: "both", label: {"en": "all (indicator)", "de": "Alle (Indikator)"}},
147
+ {value: "full", label: {"en": "all (conclusive)", "de": "Alle (vollständig)"}}
148
+ ],
149
+ })
150
+ accessor showSolution = "right"
151
+
152
+ observer: MutationObserver
153
+
154
+ connectedCallback() {
155
+ super.connectedCallback()
156
+ this.addEventListener("ww-moveup", this.handleMove)
157
+ this.addEventListener("ww-movedown", this.handleMove)
158
+ this.addEventListener("ww-moveto", this.handleMove)
159
+ this.observer = new MutationObserver(() => {
160
+ this.items.forEach(item => item.requestUpdate())
161
+ if(this.isContentEditable) {
162
+ this.solution = this.items.map(item => item.id)
163
+ }
164
+ this.clearDropPreviews()
165
+ })
166
+ this.observer.observe(this, {childList: true})
167
+ // this.shadowRoot.adoptedStyleSheets = [...this.shadowRoot.adoptedStyleSheets, this.orderSheet]
168
+ }
169
+
170
+ serializedCallback() {
171
+ this.shuffleItems()
172
+ }
173
+
174
+ disconnectedCallback(): void {
175
+ super.disconnectedCallback()
176
+ this.observer?.disconnect()
177
+ this.observer = undefined
178
+ }
179
+
180
+ orderSheet = new CSSStyleSheet()
181
+
182
+ addItem() {
183
+ const orderItem = this.ownerDocument.createElement("webwriter-order-item")
184
+ const p = this.ownerDocument.createElement("p")
185
+ orderItem.appendChild(p)
186
+ orderItem.setAttribute("layout", this.layout)
187
+ this.append(orderItem)
188
+ this.solution = [...this.#solution, orderItem.id]
189
+ this.ownerDocument.getSelection().setBaseAndExtent(p, 0, p, 0)
190
+ }
191
+
192
+ shuffleItems() {
193
+ const n = this.items.length
194
+ const nums = shuffle([...(new Array(n)).keys()])
195
+ this.innerHTML = nums.map(i => this.children.item(i).outerHTML).join("")
196
+ }
197
+
198
+ clearDropPreviews = () => {
199
+ this.items.forEach(item => item.dropPreview = undefined)
200
+ }
201
+
202
+ @query("#items-slot")
203
+ accessor itemsSlot: HTMLSlotElement
204
+
205
+ @queryAssignedElements()
206
+ accessor items: WebwriterOrderItem[]
207
+
208
+ handleKeyDown(e: KeyboardEvent) {
209
+ if(e.key == "ArrowDown") {
210
+ e.stopPropagation()
211
+ e.preventDefault()
212
+ }
213
+ else if(e.key === "ArrowUp") {
214
+ e.stopPropagation()
215
+ e.preventDefault()
216
+ }
217
+ }
218
+
219
+ /*
220
+ If contenteditable: Reorder solution -> Triggers orderSheet change
221
+ If not contenteditable: Reorder value -> Triggers orderSheet change
222
+ */
223
+
224
+ moveChild(elem: HTMLElement, i: number) {
225
+ /*
226
+ let items = this.items.map(el => el !== elem? el: undefined)
227
+ items.splice(i, 0, elem as WebwriterOrderItem)
228
+ items = items.filter(child => child)
229
+ this.replaceChildren(...items)
230
+ this.items.forEach(item => item.requestUpdate())*/
231
+ if(i === 0) {
232
+ this.insertAdjacentElement("afterbegin", elem)
233
+ }
234
+ else if(i < this.items.length) {
235
+ this.items[i].insertAdjacentElement("beforebegin", elem)
236
+ }
237
+ else {
238
+ this.insertAdjacentElement("beforeend", elem)
239
+ }
240
+ }
241
+
242
+ handleMove = (e: CustomEvent) => {
243
+ const elem = e.target as HTMLElement
244
+ const children = Array.from(this.children)
245
+ const pos = children.indexOf(elem)
246
+ let i: number
247
+ if(e.type === "ww-moveup") {
248
+ i = Math.max(0, pos - 1)
249
+ }
250
+ else if(e.type === "ww-movedown") {
251
+ i = Math.min(children.length, pos + 2)
252
+
253
+ }
254
+ else if(e.type === "ww-moveto") {
255
+ i = e.detail.i
256
+ }
257
+ this.moveChild(elem, i)
258
+ }
259
+
260
+
261
+ #solution: string[] = []
262
+
263
+ get solution() {
264
+ return this.#solution?.length? this.#solution: undefined
265
+ }
266
+
267
+ @property({attribute: false})
268
+ set solution(value: string[]) {
269
+ this.#solution = value
270
+ if(value) {
271
+ this.dispatchEvent(new CustomEvent("ww-answer-change", {
272
+ bubbles: true,
273
+ composed: true
274
+ }))
275
+ }
276
+ }
277
+
278
+
279
+ reportSolution() {
280
+ this.solution.forEach((id, i) => (this.querySelector(`#${id}`) as any).validOrder = i)
281
+ let wrongSolution: boolean = false
282
+ this.items.forEach(item => {
283
+ if(this.items.indexOf(item) != this.solution.indexOf(item.id)){
284
+ wrongSolution = true
285
+ if(this.showSolution != "full"){
286
+ this.items.forEach(i => {
287
+ i.showSolution = false
288
+ })
289
+ }
290
+ }
291
+ })
292
+ if(wrongSolution){
293
+ if(this.showSolution != "right"){
294
+ this.style.backgroundColor = "#F9B5C4"
295
+ }
296
+ }else{
297
+ this.style.backgroundColor = "#BCE194"
298
+ }
299
+ this.items.forEach(item=>{
300
+ item.style.pointerEvents="none"
301
+ })
302
+ }
303
+
304
+ reset() {
305
+ this.solution = undefined
306
+ this.style.backgroundColor = ""
307
+ this.shuffleItems()
308
+ this.items.forEach(item=>{
309
+ item.style.pointerEvents="auto"
310
+ })
311
+ }
312
+
313
+ protected firstUpdated(_changedProperties: PropertyValues): void {
314
+ this.showSolution = this.showSolution
315
+ }
316
+
317
+ render() {
318
+ return html`
319
+ <slot id="items-slot" @webwriter-clear-drop-preview=${this.clearDropPreviews}></slot>
320
+ ${this.hasAttribute("contenteditable")?html`
321
+ <sl-button size="small" id="add-option" @click=${() => this.addItem()}>
322
+ <sl-icon src=${IconPlus}></sl-icon><span>${msg("Add Option")}</span>
323
+ </sl-button>
324
+ `:""}
325
+ `
326
+ }
327
327
  }