@webwriter/quiz 1.1.3 → 2.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 (167) hide show
  1. package/.github/workflows/publish.yml +8 -0
  2. package/LICENSE +6 -6
  3. package/README.md +778 -0
  4. package/assets/icons/grip-horizontal.svg +8 -0
  5. package/assets/icons/grip-vertical.svg +8 -0
  6. package/assets/icons/truefalse.svg +6 -0
  7. package/custom-elements.json +2779 -0
  8. package/custom.d.ts +180 -176
  9. package/dist/api/index.d.ts +27 -0
  10. package/dist/api/index.js +40 -0
  11. package/dist/migrate.js +1434 -0
  12. package/dist/widgets/webwriter-choice-item.js +4036 -3895
  13. package/dist/widgets/webwriter-choice.js +2269 -5821
  14. package/dist/widgets/webwriter-gap-item.js +3987 -0
  15. package/dist/widgets/webwriter-gap.js +2908 -0
  16. package/dist/widgets/webwriter-mark.js +4335 -3489
  17. package/dist/widgets/webwriter-order-item.js +4014 -3306
  18. package/dist/widgets/webwriter-order.js +2944 -4386
  19. package/dist/widgets/webwriter-pairing-item.js +1058 -2421
  20. package/dist/widgets/webwriter-pairing.js +2993 -4383
  21. package/dist/widgets/webwriter-quiz-hint.js +2469 -0
  22. package/dist/widgets/webwriter-quiz.js +4933 -6099
  23. package/dist/widgets/webwriter-speech.js +6077 -5230
  24. package/dist/widgets/webwriter-task-prompt.js +4775 -2165
  25. package/dist/widgets/webwriter-task.js +2364 -8382
  26. package/dist/widgets/webwriter-text.js +4169 -4337
  27. package/dist/widgets/webwriter-true-false.js +4674 -0
  28. package/editing-config.json +106 -0
  29. package/lit-localize.json +21 -15
  30. package/localization/de.xlf +407 -74
  31. package/localization/es.xlf +407 -69
  32. package/localization/fr.xlf +407 -69
  33. package/localization/generated/de.ts +112 -29
  34. package/localization/generated/es.ts +112 -29
  35. package/localization/generated/fr.ts +112 -29
  36. package/localization/generated/index.d.ts +5 -0
  37. package/localization/generated/index.js +2 -2
  38. package/localization/generated/it.ts +112 -29
  39. package/localization/generated/locale-codes.js +31 -79
  40. package/localization/generated/nl.ts +112 -29
  41. package/localization/it.xlf +407 -69
  42. package/localization/nl.xlf +407 -69
  43. package/package.json +119 -252
  44. package/scripts/apply-translations.mjs +97 -0
  45. package/src/api/index.ts +72 -0
  46. package/src/lib/artboard.ts +686 -0
  47. package/src/lib/draggable-grid.ts +432 -0
  48. package/src/lib/encrypted-property.ts +202 -0
  49. package/src/lib/hint-popup.ts +152 -0
  50. package/src/lib/utils.ts +97 -0
  51. package/src/lib/webwriter-quirks.ts +121 -0
  52. package/src/migrate/index.ts +622 -0
  53. package/src/migrate/legacy-solution.ts +362 -0
  54. package/src/snippets/order.html +9 -8
  55. package/src/widgets/choice/webwriter-choice-item.ts +316 -0
  56. package/src/widgets/choice/webwriter-choice.ts +328 -0
  57. package/src/widgets/choice/webwriter-true-false.ts +113 -0
  58. package/src/widgets/dnd/webwriter-dnd-draggable.ts +28 -0
  59. package/src/widgets/dnd/webwriter-dnd-dropzone.ts +9 -0
  60. package/src/widgets/dnd/webwriter-dnd.ts +36 -0
  61. package/src/widgets/gap/webwriter-gap-item.ts +250 -0
  62. package/src/widgets/gap/webwriter-gap.ts +447 -0
  63. package/src/widgets/order/webwriter-order-item.ts +243 -0
  64. package/src/widgets/order/webwriter-order.ts +282 -0
  65. package/src/widgets/pairing/webwriter-pairing-item.ts +46 -0
  66. package/src/widgets/pairing/webwriter-pairing.ts +934 -0
  67. package/src/widgets/speech/audio-player.ts +325 -0
  68. package/src/widgets/speech/audio-recorder.ts +285 -0
  69. package/src/widgets/speech/waveform.ts +113 -0
  70. package/src/widgets/speech/webwriter-speech.ts +87 -0
  71. package/src/widgets/webwriter-mark.ts +374 -432
  72. package/src/widgets/webwriter-quiz-hint.ts +36 -0
  73. package/src/widgets/webwriter-quiz.ts +320 -282
  74. package/src/widgets/webwriter-task-prompt.ts +49 -24
  75. package/src/widgets/webwriter-task.ts +216 -547
  76. package/src/widgets/webwriter-text.ts +291 -156
  77. package/tsconfig.api.json +12 -0
  78. package/tsconfig.json +8 -7
  79. package/dist/widgets/webwriter-choice.css +0 -444
  80. package/dist/widgets/webwriter-cloze-gap.css +0 -444
  81. package/dist/widgets/webwriter-cloze-gap.js +0 -5866
  82. package/dist/widgets/webwriter-cloze.css +0 -444
  83. package/dist/widgets/webwriter-cloze.js +0 -3786
  84. package/dist/widgets/webwriter-mark.css +0 -444
  85. package/dist/widgets/webwriter-order.css +0 -444
  86. package/dist/widgets/webwriter-pairing-item.css +0 -444
  87. package/dist/widgets/webwriter-pairing.css +0 -444
  88. package/dist/widgets/webwriter-quiz.css +0 -444
  89. package/dist/widgets/webwriter-task-explainer.css +0 -444
  90. package/dist/widgets/webwriter-task-explainer.js +0 -2872
  91. package/dist/widgets/webwriter-task-hint.css +0 -444
  92. package/dist/widgets/webwriter-task-hint.js +0 -2811
  93. package/dist/widgets/webwriter-task.css +0 -444
  94. package/dist/widgets/webwriter-text.css +0 -444
  95. package/localization/bg.xlf +0 -69
  96. package/localization/cs.xlf +0 -69
  97. package/localization/da.xlf +0 -69
  98. package/localization/el.xlf +0 -69
  99. package/localization/et.xlf +0 -69
  100. package/localization/fi.xlf +0 -69
  101. package/localization/generated/bg.ts +0 -30
  102. package/localization/generated/cs.ts +0 -30
  103. package/localization/generated/da.ts +0 -30
  104. package/localization/generated/el.ts +0 -30
  105. package/localization/generated/et.ts +0 -30
  106. package/localization/generated/fi.ts +0 -30
  107. package/localization/generated/hu.ts +0 -30
  108. package/localization/generated/id.ts +0 -30
  109. package/localization/generated/ja.ts +0 -30
  110. package/localization/generated/ko.ts +0 -30
  111. package/localization/generated/lt.ts +0 -30
  112. package/localization/generated/lv.ts +0 -30
  113. package/localization/generated/nb.ts +0 -30
  114. package/localization/generated/pl.ts +0 -30
  115. package/localization/generated/pt-BR.ts +0 -30
  116. package/localization/generated/pt-PT.ts +0 -30
  117. package/localization/generated/ro.ts +0 -30
  118. package/localization/generated/ru.ts +0 -30
  119. package/localization/generated/sk.ts +0 -30
  120. package/localization/generated/sl.ts +0 -30
  121. package/localization/generated/sv.ts +0 -30
  122. package/localization/generated/tr.ts +0 -30
  123. package/localization/generated/uk.ts +0 -30
  124. package/localization/generated/zh-hans.ts +0 -30
  125. package/localization/hu.xlf +0 -69
  126. package/localization/id.xlf +0 -69
  127. package/localization/ja.xlf +0 -69
  128. package/localization/ko.xlf +0 -69
  129. package/localization/lt.xlf +0 -69
  130. package/localization/lv.xlf +0 -69
  131. package/localization/nb.xlf +0 -69
  132. package/localization/pl.xlf +0 -69
  133. package/localization/pt-BR.xlf +0 -69
  134. package/localization/pt-PT.xlf +0 -69
  135. package/localization/ro.xlf +0 -69
  136. package/localization/ru.xlf +0 -69
  137. package/localization/sk.xlf +0 -69
  138. package/localization/sl.xlf +0 -69
  139. package/localization/sv.xlf +0 -69
  140. package/localization/tr.xlf +0 -69
  141. package/localization/uk.xlf +0 -69
  142. package/localization/zh-hans.xlf +0 -69
  143. package/src/lib/combobox.ts +0 -456
  144. package/src/lib/highlighter-fill.svg +0 -6
  145. package/src/snippets/Beispiel-Choice.html +0 -57
  146. package/src/snippets/Beispiel-Mark.html +0 -14
  147. package/src/snippets/Beispiel-Order.html +0 -52
  148. package/src/snippets/Beispiel-Text.html +0 -20
  149. package/src/snippets/blank.html +0 -1
  150. package/src/snippets/choice.html +0 -8
  151. package/src/snippets/cloze.html +0 -4
  152. package/src/snippets/mark.html +0 -4
  153. package/src/snippets/pairing.html +0 -10
  154. package/src/snippets/speech.html +0 -4
  155. package/src/snippets/text.html +0 -4
  156. package/src/snippets/wordsearch.html +0 -4
  157. package/src/widgets/webwriter-choice-item.ts +0 -254
  158. package/src/widgets/webwriter-choice.ts +0 -353
  159. package/src/widgets/webwriter-cloze-gap.ts +0 -216
  160. package/src/widgets/webwriter-cloze.ts +0 -136
  161. package/src/widgets/webwriter-order-item.ts +0 -441
  162. package/src/widgets/webwriter-order.ts +0 -327
  163. package/src/widgets/webwriter-pairing-item.ts +0 -161
  164. package/src/widgets/webwriter-pairing.ts +0 -193
  165. package/src/widgets/webwriter-speech.ts +0 -273
  166. package/src/widgets/webwriter-task-explainer.ts +0 -43
  167. package/src/widgets/webwriter-task-hint.ts +0 -23
@@ -1,441 +0,0 @@
1
- import {css, html, PropertyValues} from "lit"
2
- import {styleMap} from "lit/directives/style-map.js"
3
- import {LitElementWw} from "@webwriter/lit"
4
- import {customElement, property, query} from "lit/decorators.js"
5
-
6
- import SlIconButton from "@shoelace-style/shoelace/dist/components/icon-button/icon-button.component.js"
7
-
8
- import IconArrowUp from "bootstrap-icons/icons/arrow-up.svg"
9
- import IconArrowDown from "bootstrap-icons/icons/arrow-down.svg"
10
- import "@shoelace-style/shoelace/dist/themes/light.css"
11
-
12
- import LOCALIZE from "../../localization/generated"
13
- import {msg} from "@lit/localize"
14
-
15
- declare global {interface HTMLElementTagNameMap {
16
- "webwriter-order-item": WebwriterOrderItem;
17
- }}
18
-
19
- function exchangeElements<A extends Node, B extends Node>(a: A, b: B) {
20
- var clonedA = a.cloneNode(true) as A
21
- var clonedB = b.cloneNode(true) as B
22
- b.parentNode.replaceChild(clonedA, b)
23
- a.parentNode.replaceChild(clonedB, a)
24
- return clonedA
25
- }
26
-
27
- @customElement("webwriter-order-item")
28
- export class WebwriterOrderItem extends LitElementWw {
29
-
30
- localize = LOCALIZE
31
-
32
- static scopedElements = {
33
- "sl-icon-button": SlIconButton
34
- }
35
-
36
- @property({attribute: false, type: Boolean})
37
- accessor showSolution = true
38
-
39
- @property({attribute: true, reflect: true, converter: {toAttribute: (v: boolean) => v? "true": "false", fromAttribute: (attr: string) => attr === "true"}})
40
- accessor draggable = true
41
-
42
- get elementIndex() {
43
- return Array.from(this.parentElement?.children ?? []).indexOf(this)
44
- }
45
-
46
- getSibling(i: number) {
47
- return this.parentElement.children.item(i)
48
- }
49
-
50
- emitClearDropPreview = () => {
51
- this.dispatchEvent(new CustomEvent("webwriter-clear-drop-preview", {bubbles: true}))
52
- }
53
-
54
- connectedCallback(): void {
55
- super.connectedCallback()
56
- this.dropPreview = undefined
57
- this.addEventListener("dragstart", e => {
58
- this.dropPreview = undefined
59
- e.dataTransfer.setData("text/html", this.outerHTML)
60
- e.dataTransfer.setData("text/plain", this.id)
61
- // setTimeout(() => this.baseEl.style.visibility = "hidden", 0)
62
- e.stopPropagation()
63
- })
64
- this.addEventListener("drop", e => {
65
- e.stopPropagation()
66
- const id = e.dataTransfer.getData("text/plain")
67
- const inTopHalf = e.offsetY < this.offsetHeight / 2
68
- const inLeftHalf = e.offsetX < this.offsetWidth / 2
69
- const el = document.querySelector(`webwriter-order-item#${id}`)
70
- const offset = (this.layout === "tiles"? inLeftHalf: inTopHalf)? 0: 1
71
- el.dispatchEvent(new CustomEvent("ww-moveto", {bubbles: true, detail: {i: this.elementIndex + offset}}))
72
- // this.insertAdjacentHTML(inTopHalf? "beforebegin": "afterend", html)
73
- }, {passive: true})
74
- this.addEventListener("dragover", e => {
75
- const inTopHalf = e.offsetY < this.offsetHeight / 2
76
- const inLeftHalf = e.offsetX < this.offsetWidth / 2
77
- if(this.layout === "tiles") {
78
- this.dropPreview = inLeftHalf? "left": "right"
79
- }
80
- else {
81
- this.dropPreview = inTopHalf? "top": "bottom"
82
- }
83
- e.preventDefault()
84
- e.stopImmediatePropagation()
85
- })
86
- this.addEventListener("dragleave", () => this.emitClearDropPreview(), {passive: true})
87
- document.addEventListener("dragend", (e) => {
88
- // (e.target as HTMLElement)?.remove()
89
- this.emitClearDropPreview()
90
- }, {passive: true})
91
- }
92
-
93
- @property({type: String, attribute: true, reflect: true})
94
- accessor dropPreview: "top" | "bottom" | "left" | "right" | undefined = undefined
95
-
96
- @property({type: String, attribute: true, reflect: true})
97
- accessor layout: "list" | "tiles" = "list"
98
-
99
- @property({type: Number, attribute: false})
100
- accessor validOrder: number | undefined
101
-
102
- @property({type: Boolean, attribute: true, reflect: true})
103
- accessor hideOrderButtons = false
104
-
105
- static styles = css`
106
-
107
- :host {
108
- position: relative;
109
- }
110
-
111
- :host(:is([contenteditable=true], [contenteditable=""])) .user-only {
112
- display: none;
113
- }
114
-
115
- ::marker {
116
- font-weight: bold;
117
- }
118
-
119
- :host(:is([contenteditable=true], [contenteditable=""])) ::marker {
120
- color: var(--sl-color-success-800);
121
- }
122
-
123
- ol {
124
- padding-left: 0;
125
- margin: 0;
126
- width: 100%;
127
- position: relative;
128
- }
129
-
130
- .solution {
131
- display: flex;
132
- align-items: center;
133
- justify-content: center;
134
- position: absolute;
135
- top: -7px;
136
- left: -13px;
137
- border: 2px solid var(--sl-color-gray-500);
138
- border-radius: 100%;
139
- width: 15px;
140
- height: 15px;
141
- font-size: 10px;
142
- z-index: 1;
143
- color: white;
144
- font-weight: bold;
145
-
146
- &[data-valid] {
147
- background: var(--sl-color-success-600);
148
- }
149
-
150
- &:not([data-valid]) {
151
- background: var(--sl-color-danger-600);
152
- }
153
- }
154
-
155
-
156
- :host([layout=tiles]) {
157
- border: 2px solid var(--sl-color-gray-500);
158
- border-radius: 5px;
159
- aspect-ratio: 1;
160
- min-width: 125px;
161
- width: 125px;
162
- max-width: 350px;
163
- min-height: 125px;
164
- height: 125px;
165
- max-height: 350px;
166
-
167
- & .solution {
168
- left: unset;
169
- top: -10px;
170
- right: -10px;
171
- width: 20px;
172
- height: 20px;
173
- font-size: 15px;
174
- }
175
-
176
- & ol[part=base] {
177
- padding-left: 0;
178
- margin: 0;
179
- aspect-ratio: 1;
180
- min-width: 125px;
181
- width: 125px;
182
- max-width: 350px;
183
- min-height: 125px;
184
- height: 125px;
185
- max-height: 350px;
186
- overflow-y: auto;
187
- scrollbar-width: thin;
188
- resize: both;
189
- }
190
-
191
- & ::slotted(:is(picture, audio, video, img, iframe)) {
192
- height: 100%;
193
- width: 100%;
194
- }
195
-
196
- & ::slotted(:not(:is(picture, audio, video, img, iframe))) {
197
- margin: 5px !important;
198
- }
199
- }
200
-
201
- :host(:not([layout=tiles])) {
202
-
203
- width: 100% !important;
204
- height: unset !important;
205
-
206
- & ol[part=base] {
207
- width: 100% !important;
208
- height: auto !important;
209
- }
210
- }
211
-
212
- li {
213
- list-style-type: inherit;
214
- list-style-position: inside;
215
- flex-direction: row;
216
- align-items: center;
217
- font-size: var(--sl-input-font-size-medium);
218
- cursor: move;
219
- }
220
-
221
- :host([droppreview])::before {
222
- content: "";
223
- position: absolute;
224
- background: var(--sl-color-primary-600);
225
- }
226
-
227
- :host([droppreview=top])::before {
228
- height: 2px;
229
- width: 100%;
230
- top: -2px;
231
- }
232
-
233
- :host([droppreview=bottom])::before {
234
- height: 2px;
235
- width: 100%;
236
- bottom: -2px;
237
- }
238
-
239
- :host([droppreview=left])::before {
240
- left: -17px;
241
- height: 100%;
242
- width: 15px;
243
- }
244
-
245
- :host([droppreview=right])::before {
246
- right: -17px;
247
- height: 100%;
248
- width: 15px;
249
- }
250
-
251
- slot {
252
- display: inline-block;
253
- width: calc(100% - var(--offset));
254
- cursor: text;
255
- }
256
-
257
- #order-buttons {
258
- position: absolute;
259
- right: 0;
260
- top: 0;
261
-
262
- & .order-button::part(base) {
263
- padding: 0;
264
- }
265
- }
266
-
267
- :host([hideorderbuttons]) #order-buttons {
268
- display: none;
269
- }
270
-
271
- #handle {
272
- box-sizing: border-box;
273
- border-radius: 2px;
274
- position: absolute;
275
- left: 0px;
276
- bottom: 0px;
277
- width: 20px;
278
- height: 25px;
279
- display: inline-block;
280
- overflow: hidden;
281
- line-height: 5px;
282
- padding: 1.5px 1px;
283
- padding-bottom: 0;
284
- cursor: move;
285
- vertical-align: middle;
286
- font-size: 12px;
287
- font-family: sans-serif;
288
- letter-spacing: 2px;
289
- color: #cccccc;
290
- text-shadow: 1px 0 1px black;
291
- font-weight: 600;
292
- opacity: 0.95;
293
- transform: rotate(135deg);
294
- }
295
- #handle::after {
296
- content: '.. .. ..';
297
- }
298
-
299
- :host(:not([layout=tiles])) :is(#handle, #count) {
300
- display: none;
301
- }
302
-
303
- :host(:not([layout=list])) :is(#up, #down) {
304
- display: none;
305
- }
306
-
307
- :host([layout=tiles]) #main-li {
308
- display: contents;
309
- }
310
-
311
- :host([layout=tiles]) slot {
312
- width: 100%;
313
- box-sizing: border-box;
314
- }
315
-
316
-
317
- #count {
318
- display: flex;
319
- justify-content: center;
320
- align-items: center;
321
- display: inline-block;
322
- width: 3ch;
323
- position: absolute;
324
- bottom: -14px;
325
- left: -14px;
326
- z-index: 10;
327
- & li {
328
- display: list-item;
329
- width: 4ch;
330
- height: 4ch;
331
- font-size: 12px;
332
- font-weight: bold;
333
- border-radius: 100%;
334
- border: 2px solid var(--sl-color-gray-600);
335
- color: var(--sl-color-gray-600);
336
- box-sizing: border-box;
337
- background: white;
338
- padding-left: 7px;
339
- padding-top: 4px;
340
- overflow: hidden;
341
-
342
- &[data-two-digit] {
343
- padding-left: 4px;
344
- }
345
- }
346
-
347
- }
348
- `
349
-
350
- handleClick = (e: PointerEvent, up=false) => {
351
- if((e.target as HTMLElement).id === "up") {
352
- this.dispatchEvent(new CustomEvent("ww-moveup", {bubbles: true}))
353
- }
354
- else {
355
- this.dispatchEvent(new CustomEvent("ww-movedown", {bubbles: true}))
356
- }
357
- e.stopPropagation()
358
- }
359
-
360
- @query("[part=base]")
361
- accessor baseEl: HTMLElement
362
-
363
- @query("#content")
364
- accessor contentSlotEl: HTMLSlotElement
365
-
366
- observer: MutationObserver
367
-
368
- protected async updated(_changedProperties: PropertyValues) {
369
- if(_changedProperties.has("layout") && this.layout === "list") {
370
- // console.log("switched to list")
371
- this.syncSize(true)
372
- this.observer?.disconnect()
373
- this.requestUpdate()
374
- }
375
- else if(_changedProperties.has("layout") && this.layout === "tiles") {
376
- this.syncSize()
377
- if(!this.observer) {
378
- this.observer = new MutationObserver(() => this.syncSize())
379
- this.observer.observe(this.baseEl, {attributeFilter: ["style"], attributes: true})
380
- }
381
- }
382
- }
383
-
384
- protected firstUpdated(_changedProperties: PropertyValues): void {
385
- this.requestUpdate()
386
- }
387
-
388
- syncSize(clear=false) {
389
- if(this.baseEl && !clear) {
390
- this.style.width = this.baseEl.style.width
391
- this.style.height = this.baseEl.style.height
392
- }
393
- else if(this.baseEl && clear) {
394
- this.style.width = this.baseEl.style.width = null
395
- this.style.height = this.baseEl.style.height = null
396
- }
397
- }
398
-
399
- disconnectedCallback(): void {
400
- super.disconnectedCallback()
401
- this.observer?.disconnect()
402
- this.observer = undefined
403
- }
404
-
405
- get nextElementInOrder() {
406
- return Array.from(this?.parentElement?.children ?? []).find((el: HTMLElement) => {
407
- const elOrder = parseInt(getComputedStyle(el).order)
408
- return elOrder === this.elementIndex + 1
409
- })
410
- }
411
-
412
- get prevElementInOrder() {
413
- return Array.from(this?.parentElement?.children ?? []).find((el: HTMLElement) => {
414
- const elOrder = parseInt(getComputedStyle(el).order)
415
- return elOrder === this.elementIndex - 1
416
- })
417
- }
418
-
419
- get inCorrectPosition() {
420
- return this.elementIndex === this.validOrder
421
- }
422
-
423
- render() {
424
- const solution = this.validOrder !== undefined? html`<span class="solution" ?data-valid=${this.elementIndex === this.validOrder}>${this.validOrder + 1}</span>`: null
425
- return html`<ol part="base" start=${this.elementIndex + 1}>
426
- ${this.layout === "list" && this.showSolution ? solution: null}
427
- <li id="main-li">
428
- <slot id="content" style=${styleMap({"--ww-placeholder": `"${msg("Option")}"`, "--offset": `${this.contentSlotEl?.offsetLeft + 1}px`})}></slot>
429
- </li>
430
- <div id="order-buttons" part="order-buttons" draggable="false">
431
- <sl-icon-button ?disabled=${!this.previousElementSibling} id="up" class="order-button user-only" src=${IconArrowUp} @click=${this.handleClick} @mouseup=${() => this.draggable = true}></sl-icon-button>
432
- <sl-icon-button ?disabled=${!this.nextElementSibling} id="down" class="order-button user-only" src=${IconArrowDown} @click=${this.handleClick}></sl-icon-button>
433
- </div>
434
- </ol>
435
- <ol id="count" start=${this.elementIndex + 1}>
436
- <li ?data-two-digit=${this.elementIndex + 1 >= 10}></li>
437
- ${this.layout === "tiles" && this.showSolution? solution: null}
438
- </ol>
439
- <div id="handle"></div>`
440
- }
441
- }