@webwriter/quiz 1.0.5 → 1.0.7

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 (128) hide show
  1. package/LICENSE +6 -6
  2. package/custom.d.ts +175 -175
  3. package/dist/widgets/webwriter-choice-item.css +1 -1
  4. package/dist/widgets/webwriter-choice-item.js +1886 -802
  5. package/dist/widgets/webwriter-choice.css +1 -1
  6. package/dist/widgets/webwriter-choice.js +1943 -846
  7. package/dist/widgets/webwriter-cloze-gap.css +1 -1
  8. package/dist/widgets/webwriter-cloze-gap.js +1988 -902
  9. package/dist/widgets/webwriter-cloze.css +1 -1
  10. package/dist/widgets/webwriter-cloze.js +1833 -748
  11. package/dist/widgets/webwriter-mark.css +1 -1
  12. package/dist/widgets/webwriter-mark.js +1848 -762
  13. package/dist/widgets/webwriter-order-item.css +1 -1
  14. package/dist/widgets/webwriter-order-item.js +1884 -799
  15. package/dist/widgets/webwriter-order.css +1 -1
  16. package/dist/widgets/webwriter-order.js +1882 -790
  17. package/dist/widgets/webwriter-pairing-item.css +1 -1
  18. package/dist/widgets/webwriter-pairing-item.js +1718 -614
  19. package/dist/widgets/webwriter-pairing.css +1 -1
  20. package/dist/widgets/webwriter-pairing.js +1873 -781
  21. package/dist/widgets/webwriter-quiz.css +1 -1
  22. package/dist/widgets/webwriter-quiz.js +2241 -1026
  23. package/dist/widgets/webwriter-speech.css +1 -1
  24. package/dist/widgets/webwriter-speech.js +2179 -961
  25. package/dist/widgets/webwriter-task-explainer.css +1 -1
  26. package/dist/widgets/webwriter-task-explainer.js +1704 -600
  27. package/dist/widgets/webwriter-task-hint.css +1 -1
  28. package/dist/widgets/webwriter-task-hint.js +1695 -591
  29. package/dist/widgets/webwriter-task-prompt.css +1 -1
  30. package/dist/widgets/webwriter-task-prompt.js +1670 -566
  31. package/dist/widgets/webwriter-task.css +1 -1
  32. package/dist/widgets/webwriter-task.js +2361 -1143
  33. package/dist/widgets/webwriter-text.css +1 -1
  34. package/dist/widgets/webwriter-text.js +1876 -770
  35. package/icon.svg +1 -0
  36. package/lit-localize.json +15 -0
  37. package/localization/bg.xlf +54 -0
  38. package/localization/cs.xlf +54 -0
  39. package/localization/da.xlf +54 -0
  40. package/localization/de.xlf +54 -0
  41. package/localization/el.xlf +54 -0
  42. package/localization/es.xlf +54 -0
  43. package/localization/et.xlf +54 -0
  44. package/localization/fi.xlf +54 -0
  45. package/localization/fr.xlf +54 -0
  46. package/localization/generated/bg.ts +25 -0
  47. package/localization/generated/cs.ts +25 -0
  48. package/localization/generated/da.ts +25 -0
  49. package/localization/generated/de.ts +25 -0
  50. package/localization/generated/el.ts +25 -0
  51. package/localization/generated/es.ts +25 -0
  52. package/localization/generated/et.ts +25 -0
  53. package/localization/generated/fi.ts +25 -0
  54. package/localization/generated/fr.ts +25 -0
  55. package/localization/generated/hu.ts +25 -0
  56. package/localization/generated/id.ts +25 -0
  57. package/localization/generated/index.js +3 -0
  58. package/localization/generated/it.ts +25 -0
  59. package/localization/generated/ja.ts +25 -0
  60. package/localization/generated/ko.ts +25 -0
  61. package/localization/generated/locale-codes.js +79 -0
  62. package/localization/generated/lt.ts +25 -0
  63. package/localization/generated/lv.ts +25 -0
  64. package/localization/generated/nb.ts +25 -0
  65. package/localization/generated/nl.ts +25 -0
  66. package/localization/generated/pl.ts +25 -0
  67. package/localization/generated/pt-BR.ts +25 -0
  68. package/localization/generated/pt-PT.ts +25 -0
  69. package/localization/generated/ro.ts +25 -0
  70. package/localization/generated/ru.ts +25 -0
  71. package/localization/generated/sk.ts +25 -0
  72. package/localization/generated/sl.ts +25 -0
  73. package/localization/generated/sv.ts +25 -0
  74. package/localization/generated/tr.ts +25 -0
  75. package/localization/generated/uk.ts +25 -0
  76. package/localization/generated/zh-hans.ts +25 -0
  77. package/localization/hu.xlf +54 -0
  78. package/localization/id.xlf +54 -0
  79. package/localization/it.xlf +54 -0
  80. package/localization/ja.xlf +54 -0
  81. package/localization/ko.xlf +54 -0
  82. package/localization/lt.xlf +54 -0
  83. package/localization/lv.xlf +54 -0
  84. package/localization/nb.xlf +54 -0
  85. package/localization/nl.xlf +54 -0
  86. package/localization/pl.xlf +54 -0
  87. package/localization/pt-BR.xlf +54 -0
  88. package/localization/pt-PT.xlf +54 -0
  89. package/localization/ro.xlf +54 -0
  90. package/localization/ru.xlf +54 -0
  91. package/localization/sk.xlf +54 -0
  92. package/localization/sl.xlf +54 -0
  93. package/localization/sv.xlf +54 -0
  94. package/localization/tr.xlf +54 -0
  95. package/localization/uk.xlf +54 -0
  96. package/localization/zh-hans.xlf +54 -0
  97. package/package.json +281 -193
  98. package/src/lib/combobox.ts +455 -455
  99. package/src/lib/highlighter-fill.svg +5 -5
  100. package/src/snippets/Beispiel-Choice.html +57 -0
  101. package/src/snippets/Beispiel-Mark.html +14 -0
  102. package/src/snippets/Beispiel-Order.html +52 -0
  103. package/src/snippets/Beispiel-Text.html +20 -0
  104. package/src/snippets/choice.html +7 -7
  105. package/src/snippets/cloze.html +3 -3
  106. package/src/snippets/mark.html +3 -3
  107. package/src/snippets/order.html +7 -7
  108. package/src/snippets/pairing.html +9 -9
  109. package/src/snippets/speech.html +3 -3
  110. package/src/snippets/text.html +3 -3
  111. package/src/snippets/wordsearch.html +3 -3
  112. package/src/widgets/webwriter-choice-item.ts +250 -250
  113. package/src/widgets/webwriter-choice.ts +314 -309
  114. package/src/widgets/webwriter-cloze-gap.ts +215 -216
  115. package/src/widgets/webwriter-cloze.ts +135 -135
  116. package/src/widgets/webwriter-mark.ts +431 -434
  117. package/src/widgets/webwriter-order-item.ts +437 -436
  118. package/src/widgets/webwriter-order.ts +284 -279
  119. package/src/widgets/webwriter-pairing-item.ts +160 -155
  120. package/src/widgets/webwriter-pairing.ts +192 -187
  121. package/src/widgets/webwriter-quiz.ts +281 -280
  122. package/src/widgets/webwriter-speech.ts +272 -267
  123. package/src/widgets/webwriter-task-explainer.ts +42 -37
  124. package/src/widgets/webwriter-task-hint.ts +22 -16
  125. package/src/widgets/webwriter-task-prompt.ts +23 -17
  126. package/src/widgets/webwriter-task.ts +546 -543
  127. package/src/widgets/webwriter-text.ts +135 -130
  128. package/tsconfig.json +6 -6
@@ -1,280 +1,285 @@
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
- export function shuffle<T>(a: T[]) {
15
- for (let i = a.length - 1; i > 0; i--) {
16
- const j = Math.floor(Math.random() * (i + 1));
17
- [a[i], a[j]] = [a[j], a[i]];
18
- }
19
- return a;
20
- }
21
-
22
- declare global {interface HTMLElementTagNameMap {
23
- "webwriter-order": WebwriterOrder;
24
- }}
25
-
26
- @customElement("webwriter-order")
27
- export class WebwriterOrder extends LitElementWw {
28
-
29
- static scopedElements = {
30
- "sl-button": SlButton,
31
- "sl-icon": SlIcon
32
- }
33
-
34
- static styles = css`
35
- :host {
36
- display: flex !important;
37
- flex-direction: column;
38
- align-items: flex-start;
39
- counter-reset: orderItem;
40
- gap: 2px;
41
- }
42
-
43
- :host([layout=tiles]) {
44
- flex-direction: row;
45
- flex-wrap: wrap;
46
- gap: 15px;
47
- }
48
-
49
- :host(:is([contenteditable=true], [contenteditable=""])) {
50
- --text-color: var(--sl-color-success-700);
51
- }
52
-
53
- :host ::slotted(*::part(counter)) {
54
- counter-increment: orderItem;
55
- content: counter(orderItem) '. ';
56
- cursor: move;
57
- text-align: right;
58
- padding-right: 0.5em;
59
- min-width: 1.5em;
60
- color: var(--text-color, auto);
61
- }
62
-
63
- sl-button::part(label) {
64
- padding: 0;
65
- display: flex;
66
- flex-direction: row;
67
- align-items: center;
68
- }
69
-
70
- sl-button::part(base) {
71
- border: none;
72
- background: transparent;
73
- }
74
-
75
- sl-icon {
76
- width: 19px;
77
- height: 19px;
78
- padding: var(--sl-spacing-x-small);
79
- padding-left: 0;
80
- }
81
-
82
- #add-option {
83
- order: 2147483647;
84
- }
85
-
86
- #add-option:not(:hover)::part(base) {
87
- color: darkgray;
88
- }
89
-
90
- :host([layout=tiles]) #add-option {
91
- width: 125px;
92
- height: 125px;
93
- overflow: hidden;
94
- border: 2px solid var(--sl-color-gray-300);
95
- border-radius: 5px;
96
- padding: 5px;
97
- display: flex;
98
- align-items: center;
99
- justify-content: center;
100
- }
101
-
102
-
103
- :host(:not([contenteditable=true]):not([contenteditable=""])) .author-only {
104
- display: none;
105
- }
106
- `
107
-
108
- get layout(): "list" | "tiles" {
109
- return this.children?.item(0)?.getAttribute("layout") as any ?? "list"
110
- }
111
-
112
- @property({type: String, attribute: true, reflect: true}) //@ts-ignore
113
- @option({
114
- type: "select",
115
- options: [
116
- {value: "list", label: {"en": "List"}},
117
- {value: "tiles", label: {"en": "Tiles"}}
118
- ]
119
- })
120
- set layout(value) {
121
- this.querySelectorAll("webwriter-order-item").forEach(el => el.setAttribute("layout", value))
122
- this.requestUpdate("layout")
123
- }
124
-
125
- get hideOrderButtons() {
126
- return !!this.items[0]?.hideOrderButtons
127
- }
128
-
129
- @property({type: Boolean, attribute: true, reflect: true}) //@ts-ignore
130
- @option({type: Boolean, label: {_: "Hide order buttons"}})
131
- set hideOrderButtons(value: boolean) {
132
- this.items.forEach(item => item.hideOrderButtons = value)
133
- }
134
-
135
-
136
- observer: MutationObserver
137
-
138
- connectedCallback() {
139
- super.connectedCallback()
140
- this.addEventListener("ww-moveup", this.handleMove)
141
- this.addEventListener("ww-movedown", this.handleMove)
142
- this.addEventListener("ww-moveto", this.handleMove)
143
- this.observer = new MutationObserver(() => {
144
- this.items.forEach(item => item.requestUpdate())
145
- if(this.isContentEditable) {
146
- this.solution = this.items.map(item => item.id)
147
- }
148
- this.clearDropPreviews()
149
- })
150
- this.observer.observe(this, {childList: true})
151
- // this.shadowRoot.adoptedStyleSheets = [...this.shadowRoot.adoptedStyleSheets, this.orderSheet]
152
- }
153
-
154
- serializedCallback() {
155
- this.shuffleItems()
156
- }
157
-
158
- disconnectedCallback(): void {
159
- super.disconnectedCallback()
160
- this.observer?.disconnect()
161
- this.observer = undefined
162
- }
163
-
164
- orderSheet = new CSSStyleSheet()
165
-
166
- addItem() {
167
- const orderItem = this.ownerDocument.createElement("webwriter-order-item")
168
- const p = this.ownerDocument.createElement("p")
169
- orderItem.appendChild(p)
170
- orderItem.setAttribute("layout", this.layout)
171
- this.append(orderItem)
172
- this.solution = [...this.#solution, orderItem.id]
173
- this.ownerDocument.getSelection().setBaseAndExtent(p, 0, p, 0)
174
- }
175
-
176
- shuffleItems() {
177
- const n = this.items.length
178
- const nums = shuffle([...(new Array(n)).keys()])
179
- this.innerHTML = nums.map(i => this.children.item(i).outerHTML).join("")
180
- }
181
-
182
- clearDropPreviews = () => {
183
- this.items.forEach(item => item.dropPreview = undefined)
184
- }
185
-
186
- @query("#items-slot")
187
- accessor itemsSlot: HTMLSlotElement
188
-
189
- @queryAssignedElements()
190
- accessor items: WebwriterOrderItem[]
191
-
192
- handleKeyDown(e: KeyboardEvent) {
193
- if(e.key == "ArrowDown") {
194
- e.stopPropagation()
195
- e.preventDefault()
196
- }
197
- else if(e.key === "ArrowUp") {
198
- e.stopPropagation()
199
- e.preventDefault()
200
- }
201
- }
202
-
203
- /*
204
- If contenteditable: Reorder solution -> Triggers orderSheet change
205
- If not contenteditable: Reorder value -> Triggers orderSheet change
206
- */
207
-
208
- moveChild(elem: HTMLElement, i: number) {
209
- /*
210
- let items = this.items.map(el => el !== elem? el: undefined)
211
- items.splice(i, 0, elem as WebwriterOrderItem)
212
- items = items.filter(child => child)
213
- this.replaceChildren(...items)
214
- this.items.forEach(item => item.requestUpdate())*/
215
- if(i === 0) {
216
- this.insertAdjacentElement("afterbegin", elem)
217
- }
218
- else if(i < this.items.length) {
219
- this.items[i].insertAdjacentElement("beforebegin", elem)
220
- }
221
- else {
222
- this.insertAdjacentElement("beforeend", elem)
223
- }
224
- }
225
-
226
- handleMove = (e: CustomEvent) => {
227
- const elem = e.target as HTMLElement
228
- const children = Array.from(this.children)
229
- const pos = children.indexOf(elem)
230
- let i: number
231
- if(e.type === "ww-moveup") {
232
- i = Math.max(0, pos - 1)
233
- }
234
- else if(e.type === "ww-movedown") {
235
- i = Math.min(children.length, pos + 2)
236
-
237
- }
238
- else if(e.type === "ww-moveto") {
239
- i = e.detail.i
240
- }
241
- this.moveChild(elem, i)
242
- }
243
-
244
-
245
- #solution: string[] = []
246
-
247
- get solution() {
248
- return this.#solution?.length? this.#solution: undefined
249
- }
250
-
251
- @property({attribute: false})
252
- set solution(value: string[]) {
253
- this.#solution = value
254
- if(value) {
255
- this.dispatchEvent(new CustomEvent("ww-answer-change", {
256
- bubbles: true,
257
- composed: true
258
- }))
259
- }
260
- }
261
-
262
-
263
- reportSolution() {
264
- this.solution.forEach((id, i) => this.querySelector(`#${id}`).validOrder = i)
265
- }
266
-
267
- reset() {
268
- this.solution = undefined
269
- this.shuffleItems()
270
- }
271
-
272
- render() {
273
- return html`
274
- <slot id="items-slot" @webwriter-clear-drop-preview=${this.clearDropPreviews}></slot>
275
- <sl-button size="small" id="add-option" class="author-only" @click=${() => this.addItem()}>
276
- <sl-icon src=${IconPlus}></sl-icon><span>Add Option</span>
277
- </sl-button>
278
- `
279
- }
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
+
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: {_: "Hide order buttons"}})
136
+ set hideOrderButtons(value: boolean) {
137
+ this.items.forEach(item => item.hideOrderButtons = value)
138
+ }
139
+
140
+
141
+ observer: MutationObserver
142
+
143
+ connectedCallback() {
144
+ super.connectedCallback()
145
+ this.addEventListener("ww-moveup", this.handleMove)
146
+ this.addEventListener("ww-movedown", this.handleMove)
147
+ this.addEventListener("ww-moveto", this.handleMove)
148
+ this.observer = new MutationObserver(() => {
149
+ this.items.forEach(item => item.requestUpdate())
150
+ if(this.isContentEditable) {
151
+ this.solution = this.items.map(item => item.id)
152
+ }
153
+ this.clearDropPreviews()
154
+ })
155
+ this.observer.observe(this, {childList: true})
156
+ // this.shadowRoot.adoptedStyleSheets = [...this.shadowRoot.adoptedStyleSheets, this.orderSheet]
157
+ }
158
+
159
+ serializedCallback() {
160
+ this.shuffleItems()
161
+ }
162
+
163
+ disconnectedCallback(): void {
164
+ super.disconnectedCallback()
165
+ this.observer?.disconnect()
166
+ this.observer = undefined
167
+ }
168
+
169
+ orderSheet = new CSSStyleSheet()
170
+
171
+ addItem() {
172
+ const orderItem = this.ownerDocument.createElement("webwriter-order-item")
173
+ const p = this.ownerDocument.createElement("p")
174
+ orderItem.appendChild(p)
175
+ orderItem.setAttribute("layout", this.layout)
176
+ this.append(orderItem)
177
+ this.solution = [...this.#solution, orderItem.id]
178
+ this.ownerDocument.getSelection().setBaseAndExtent(p, 0, p, 0)
179
+ }
180
+
181
+ shuffleItems() {
182
+ const n = this.items.length
183
+ const nums = shuffle([...(new Array(n)).keys()])
184
+ this.innerHTML = nums.map(i => this.children.item(i).outerHTML).join("")
185
+ }
186
+
187
+ clearDropPreviews = () => {
188
+ this.items.forEach(item => item.dropPreview = undefined)
189
+ }
190
+
191
+ @query("#items-slot")
192
+ accessor itemsSlot: HTMLSlotElement
193
+
194
+ @queryAssignedElements()
195
+ accessor items: WebwriterOrderItem[]
196
+
197
+ handleKeyDown(e: KeyboardEvent) {
198
+ if(e.key == "ArrowDown") {
199
+ e.stopPropagation()
200
+ e.preventDefault()
201
+ }
202
+ else if(e.key === "ArrowUp") {
203
+ e.stopPropagation()
204
+ e.preventDefault()
205
+ }
206
+ }
207
+
208
+ /*
209
+ If contenteditable: Reorder solution -> Triggers orderSheet change
210
+ If not contenteditable: Reorder value -> Triggers orderSheet change
211
+ */
212
+
213
+ moveChild(elem: HTMLElement, i: number) {
214
+ /*
215
+ let items = this.items.map(el => el !== elem? el: undefined)
216
+ items.splice(i, 0, elem as WebwriterOrderItem)
217
+ items = items.filter(child => child)
218
+ this.replaceChildren(...items)
219
+ this.items.forEach(item => item.requestUpdate())*/
220
+ if(i === 0) {
221
+ this.insertAdjacentElement("afterbegin", elem)
222
+ }
223
+ else if(i < this.items.length) {
224
+ this.items[i].insertAdjacentElement("beforebegin", elem)
225
+ }
226
+ else {
227
+ this.insertAdjacentElement("beforeend", elem)
228
+ }
229
+ }
230
+
231
+ handleMove = (e: CustomEvent) => {
232
+ const elem = e.target as HTMLElement
233
+ const children = Array.from(this.children)
234
+ const pos = children.indexOf(elem)
235
+ let i: number
236
+ if(e.type === "ww-moveup") {
237
+ i = Math.max(0, pos - 1)
238
+ }
239
+ else if(e.type === "ww-movedown") {
240
+ i = Math.min(children.length, pos + 2)
241
+
242
+ }
243
+ else if(e.type === "ww-moveto") {
244
+ i = e.detail.i
245
+ }
246
+ this.moveChild(elem, i)
247
+ }
248
+
249
+
250
+ #solution: string[] = []
251
+
252
+ get solution() {
253
+ return this.#solution?.length? this.#solution: undefined
254
+ }
255
+
256
+ @property({attribute: false})
257
+ set solution(value: string[]) {
258
+ this.#solution = value
259
+ if(value) {
260
+ this.dispatchEvent(new CustomEvent("ww-answer-change", {
261
+ bubbles: true,
262
+ composed: true
263
+ }))
264
+ }
265
+ }
266
+
267
+
268
+ reportSolution() {
269
+ this.solution.forEach((id, i) => (this.querySelector(`#${id}`) as any).validOrder = i)
270
+ }
271
+
272
+ reset() {
273
+ this.solution = undefined
274
+ this.shuffleItems()
275
+ }
276
+
277
+ render() {
278
+ return html`
279
+ <slot id="items-slot" @webwriter-clear-drop-preview=${this.clearDropPreviews}></slot>
280
+ <sl-button size="small" id="add-option" class="author-only" @click=${() => this.addItem()}>
281
+ <sl-icon src=${IconPlus}></sl-icon><span>${msg("Add Option")}</span>
282
+ </sl-button>
283
+ `
284
+ }
280
285
  }