@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,216 +0,0 @@
1
- import {LitElement, html, css, RenderOptions} from "lit"
2
- import {LitElementWw} from "@webwriter/lit"
3
- import {customElement, query} from "lit/decorators.js"
4
-
5
- import SlIconButton from "@shoelace-style/shoelace/dist/components/icon-button/icon-button.component.js"
6
- import SlOption from "@shoelace-style/shoelace/dist/components/option/option.component.js"
7
- import IconPlus from "bootstrap-icons/icons/plus.svg"
8
- import IconSlashCircle from "bootstrap-icons/icons/slash-circle.svg"
9
- import IconCheckCircle from "bootstrap-icons/icons/check-circle.svg"
10
- import IconX from "bootstrap-icons/icons/x.svg"
11
-
12
- import { Combobox } from "../lib/combobox"
13
- import { property } from "lit/decorators/property.js"
14
- import { queryAsync } from "lit/decorators/query-async.js"
15
- import "@shoelace-style/shoelace/dist/themes/light.css"
16
-
17
- import LOCALIZE from "../../localization/generated"
18
- import {msg} from "@lit/localize"
19
-
20
- declare global {interface HTMLElementTagNameMap {
21
- "webwriter-cloze-gap": WebwriterClozeGap;
22
- }}
23
-
24
- @customElement("webwriter-cloze-gap")
25
- export class WebwriterClozeGap extends LitElementWw {
26
-
27
- localize = LOCALIZE
28
-
29
- @property({type: Array, attribute: true, reflect: true})
30
- accessor solution: string[]
31
-
32
- @property({type: String, attribute: true, reflect: true})
33
- accessor value: string
34
-
35
- @property({type: Array, attribute: true, reflect: true})
36
- accessor distraction: string[] = []
37
-
38
- @property({type: Boolean, attribute: true, reflect: true})
39
- accessor showOptions = false
40
-
41
- static scopedElements = {
42
- "ww-combobox": Combobox,
43
- "sl-icon-button": SlIconButton,
44
- "sl-option": SlOption
45
- }
46
-
47
- static styles = css`
48
- :host {
49
- display: inline-block !important;
50
- vertical-align: top;
51
- margin: 0 1ch;
52
- max-width: calc(100% - 2ch);
53
- max-height: 1lh;
54
- }
55
-
56
- :host(::after) {
57
- content: " ";
58
- display: block;
59
- }
60
-
61
- sl-icon-button[data-hidden] {
62
- visibility: hidden;
63
- }
64
-
65
- sl-icon-button::part(base) {
66
- padding: 0;
67
- }
68
-
69
- :host(:not(:focus-within)) #add {
70
- visibility: hidden;
71
- }
72
-
73
- sl-icon-button {
74
- overflow: visible;
75
- margin-right: 0;
76
- }
77
-
78
- ww-combobox::part(base) {
79
- min-height: unset;
80
- padding: 2px 2px;
81
- }
82
-
83
- :host(:is([contenteditable=true], [contenteditable=""])) ww-combobox::part(input) {
84
- color: var(--sl-color-success-700);
85
- }
86
-
87
- sl-option::part(base) {
88
- padding: 0;
89
- font-size: var(--sl-input-font-size-small);
90
- }
91
-
92
- sl-option::part(label) {
93
- padding: var(--sl-spacing-2x-small) 0;
94
- }
95
-
96
- sl-option::part(label):hover {
97
- color: var(--sl-color-primary-600);
98
- }
99
-
100
- sl-option::part(checked-icon) {
101
- display: none;
102
- }
103
-
104
- .remove::part(base):hover {
105
- color: var(--sl-color-danger-600) !important;
106
- }
107
-
108
- .remove::part(base):active {
109
- color: var(--sl-color-danger-800) !important;
110
- }
111
-
112
- :is(.toggle, .remove)::part(base) {
113
- padding: var(--sl-spacing-2x-small);
114
- }
115
-
116
- .solution::part(base) {
117
- color: var(--sl-color-success-700);
118
- }
119
-
120
- .toggle {
121
- color: inherit;
122
- }
123
-
124
-
125
- `
126
- @query("ww-combobox")
127
- accessor input: Combobox
128
-
129
- async focus() {
130
- (await this.input).focus()
131
- }
132
-
133
- handleChange = (e: CustomEvent) => {
134
- e.preventDefault()
135
- if(!this.isContentEditable) {
136
- this.value = this.input.value as string
137
- }
138
- }
139
-
140
- addSolution(value: string) {
141
- this.solution = Array.from(new Set([...(this.solution ?? []), value]))
142
- }
143
-
144
- toggleOptionType(value: string) {
145
- if(this.solution.includes(value)) {
146
- this.solution = this.solution.filter(v => v !== value)
147
- this.distraction = Array.from(new Set([...(this.distraction ?? []), value]))
148
- }
149
- else if(this.distraction.includes(value)) {
150
- this.distraction = this.distraction.filter(v => v !== value)
151
- this.solution = Array.from(new Set([...(this.solution ?? []), value]))
152
- }
153
- }
154
-
155
- removeOption(value: string) {
156
- this.solution = this.solution.filter(v => v !== value)
157
- this.distraction = this.distraction.filter(v => v !== value)
158
- if(this.allOptions.length === 0) {
159
- this.isAdding = false
160
- }
161
- }
162
-
163
- handleAddClick = (e: MouseEvent) => {
164
- e.stopImmediatePropagation()
165
- e.preventDefault()
166
- this.addSolution(this.input.value as string)
167
- this.isAdding = true
168
- this.input.value = ""
169
- this.input.open = true
170
- }
171
-
172
- handleToggleClick = (e: MouseEvent, value: string) => {
173
- e.stopImmediatePropagation()
174
- e.preventDefault()
175
- this.toggleOptionType(value)
176
- }
177
-
178
- handleRemoveClick = (e: MouseEvent, value: string) => {
179
- e.stopImmediatePropagation()
180
- e.preventDefault()
181
- this.removeOption(value)
182
- }
183
-
184
- handleKeydown = (e: KeyboardEvent) => {
185
- if(e.key === "Enter") {
186
- this.handleAddClick(e as any)
187
- }
188
- }
189
-
190
- handleBlur = (e: FocusEvent) => {
191
- this.isAdding = false
192
- if(this.solution.length === 1) {
193
- this.input.value = this.solution[0]
194
- }
195
- }
196
-
197
- get allOptions() {
198
- return [...(this.solution ?? []), ...this.distraction]
199
- }
200
-
201
- @property({type: Boolean, attribute: false})
202
- accessor isAdding = false
203
-
204
- render() {
205
- return html`<ww-combobox @blur=${this.handleBlur} ?suggestions=${this.allOptions.length > 1 || this.isAdding || this.distraction.length > 0} size="small" autosize .value=${this.value} @sl-change=${this.handleChange} @sl-input=${() => this.requestUpdate()} @keydown=${this.handleKeydown} >
206
- <sl-icon-button id="add" title="Add another solution" ?disabled=${this.allOptions.includes(this.input?.value as string)} ?data-hidden=${!this.input?.value} src=${IconPlus} size="small" slot="prefix" @click=${this.handleAddClick} @mousedown=${(e: Event) => {e.stopPropagation(); e.preventDefault()}} @focus=${e => e.stopPropagation()} ></sl-icon-button>
207
- ${this.allOptions.sort().map(value => html`
208
- <sl-option size="small" value=${value} class=${this.solution.includes(value)? "solution": "distraction"} >
209
- <sl-icon-button title=${this.solution.includes(value)? "Change to a distraction": "Change to a solution"} class="toggle" slot="prefix" src=${this.solution.includes(value)? IconCheckCircle: IconSlashCircle} @click=${(e: any) => this.handleToggleClick(e, value)} @mousedown=${(e: Event) => {e.stopPropagation(); e.preventDefault()}}></sl-icon-button>
210
- <span title=${this.solution.includes(value)? "Solution": "Distraction"}>${value}</span>
211
- <sl-icon-button title="Remove" class="remove" slot="suffix" src=${IconX} @click=${(e: any) => this.handleRemoveClick(e, value)} @mousedown=${(e: Event) => {e.stopPropagation(); e.preventDefault()}}></sl-icon-button>
212
- </sl-option>
213
- `)}
214
- </ww-combobox>`
215
- }
216
- }
@@ -1,136 +0,0 @@
1
- import {html, css, LitElement} from "lit"
2
- import {LitElementWw} from "@webwriter/lit"
3
- import {customElement, query} from "lit/decorators.js"
4
- import {styleMap} from "lit/directives/style-map.js"
5
-
6
- import SlIconButton from "@shoelace-style/shoelace/dist/components/icon-button/icon-button.component.js"
7
-
8
-
9
- import IconTextarea from "bootstrap-icons/icons/textarea.svg"
10
- import { WebwriterClozeGap } from "./webwriter-cloze-gap.js"
11
- import "@shoelace-style/shoelace/dist/themes/light.css"
12
-
13
- import LOCALIZE from "../../localization/generated"
14
- import {msg} from "@lit/localize"
15
-
16
- declare global {interface HTMLElementTagNameMap {
17
- "webwriter-cloze": WebwriterCloze;
18
- }}
19
-
20
- @customElement("webwriter-cloze")
21
- export class WebwriterCloze extends LitElementWw {
22
-
23
- localize = LOCALIZE
24
-
25
- static shadowRootOptions: ShadowRootInit = {...LitElementWw.shadowRootOptions, delegatesFocus: false}
26
-
27
- static scopedElements = {
28
- "sl-icon-button": SlIconButton
29
- }
30
-
31
- static styles = css`
32
- :host {
33
- min-height: 1rem;
34
- position: relative;
35
- }
36
-
37
- slot {
38
- display: block;
39
- cursor: text;
40
- }
41
-
42
- slot[data-empty]:after {
43
- content: var(--placeholder);
44
- position: absolute;
45
- left: 0;
46
- top: 0;
47
- color: darkgray;
48
- pointer-events: none;
49
- user-select: none;
50
- }
51
-
52
- slot::after, slot::before {
53
- content: ' ';
54
- }
55
-
56
- #add-gap {
57
- position: absolute;
58
- right: 0;
59
- top: 0;
60
- background: rgba(255, 255, 255, 0.85);
61
-
62
- &[data-highlighting]::part(base) {
63
- background: var(--sl-color-primary-100);
64
- }
65
- }
66
-
67
- :host(:not([contenteditable=true]):not([contenteditable=""])) .author-only {
68
- display: none;
69
- }
70
- `
71
-
72
- @query("#add-gap")
73
- accessor addGapButton: SlIconButton
74
-
75
- observer: MutationObserver
76
-
77
- connectedCallback(): void {
78
- super.connectedCallback()
79
- document.addEventListener("selectionchange", e => {
80
- const sel = document.getSelection()
81
- const node = document.getSelection()?.anchorNode
82
- const el = node.nodeType === node.TEXT_NODE? node.parentElement: node as HTMLElement
83
- this.addGapButton.toggleAttribute("data-visible", el?.closest("webwriter-cloze")? true: false)
84
- if(el?.closest("webwriter-cloze") && !sel.isCollapsed) {
85
- this.addGapButton.toggleAttribute("data-highlighting", true)
86
- }
87
- else {
88
- this.addGapButton.toggleAttribute("data-highlighting", false)
89
- }
90
- })
91
- this.observer = new MutationObserver(() => this.requestUpdate())
92
- this.observer.observe(this, {characterData: true, childList: true, subtree: true})
93
- }
94
-
95
-
96
- disconnectedCallback(): void {
97
- super.disconnectedCallback()
98
- this.observer?.disconnect()
99
- }
100
-
101
- toggleGap = () => {
102
- const sel = document.getSelection()
103
- const selectedElement = sel.anchorNode.childNodes.item(sel.anchorOffset)
104
- if(selectedElement?.nodeName.toLowerCase() === "webwriter-cloze-gap") {
105
- const gap = selectedElement as WebwriterClozeGap
106
- selectedElement.replaceWith(document.createTextNode(gap.value))
107
- }
108
- else if(this.contains(sel.anchorNode)) {
109
- if(sel.anchorNode !== this) {
110
- const textContent = sel.toString()
111
- sel.deleteFromDocument()
112
- const textNode = sel.anchorNode as Text
113
- const at = sel.anchorOffset
114
- const clozeGap = document.createElement("webwriter-cloze-gap")
115
- clozeGap.classList.add("webwriter-new")
116
- clozeGap.setAttribute("value", textContent)
117
- const afterTextNode = textNode.splitText(at)
118
- afterTextNode.textContent = afterTextNode.textContent.trimStart()
119
- textNode.textContent = textNode.textContent.trimEnd()
120
- textNode.parentElement.insertBefore(clozeGap, afterTextNode)
121
- }
122
- else {
123
- const clozeGap = document.createElement("webwriter-cloze-gap")
124
- clozeGap.setAttribute("value", "")
125
- this.append(clozeGap)
126
- }
127
- }
128
- }
129
-
130
- render() {
131
- return html`
132
- <slot style=${styleMap({"--placeholder": `"${msg("Text to add gaps to")}"`})} ?data-empty=${!this.textContent.trim() && !this.querySelectorAll("& > p *:not(br)").length}></slot>
133
- <sl-icon-button class="author-only" id="add-gap" src=${IconTextarea} @click=${this.toggleGap}></sl-icon-button>
134
- `
135
- }
136
- }