@webwriter/quiz 1.0.2 → 1.0.4
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.
- package/LICENSE +6 -6
- package/custom.d.ts +175 -175
- package/dist/widgets/webwriter-choice-item.js +3257 -59
- package/dist/widgets/webwriter-choice.js +4941 -79
- package/dist/widgets/webwriter-cloze-gap.js +4092 -179
- package/dist/widgets/webwriter-cloze.js +2415 -36
- package/dist/widgets/webwriter-mark.js +2471 -61
- package/dist/widgets/webwriter-order-item.js +2447 -68
- package/dist/widgets/webwriter-order.js +4990 -135
- package/dist/widgets/webwriter-pairing-item.js +1581 -48
- package/dist/widgets/webwriter-pairing.js +3686 -72
- package/dist/widgets/webwriter-quiz.js +6938 -76
- package/dist/widgets/webwriter-speech.js +6265 -76
- package/dist/widgets/webwriter-task-explainer.js +1564 -31
- package/dist/widgets/webwriter-task-hint.js +1523 -28
- package/dist/widgets/webwriter-task-prompt.js +1586 -29
- package/dist/widgets/webwriter-task.js +7132 -101
- package/dist/widgets/webwriter-text.js +4044 -54
- package/package.json +200 -200
- package/src/lib/combobox.ts +455 -455
- package/src/snippets/choice.html +7 -7
- package/src/snippets/cloze.html +3 -3
- package/src/snippets/mark.html +3 -3
- package/src/snippets/order.html +7 -7
- package/src/snippets/pairing.html +9 -9
- package/src/snippets/speech.html +3 -3
- package/src/snippets/text.html +3 -3
- package/src/snippets/wordsearch.html +3 -3
- package/src/widgets/webwriter-choice-item.ts +250 -250
- package/src/widgets/webwriter-choice.ts +309 -309
- package/src/widgets/webwriter-cloze-gap.ts +216 -216
- package/src/widgets/webwriter-cloze.ts +135 -135
- package/src/widgets/webwriter-mark.ts +434 -434
- package/src/widgets/webwriter-order-item.ts +444 -444
- package/src/widgets/webwriter-order.ts +272 -272
- package/src/widgets/webwriter-pairing-item.ts +155 -155
- package/src/widgets/webwriter-pairing.ts +187 -187
- package/src/widgets/webwriter-quiz.ts +280 -280
- package/src/widgets/webwriter-speech.ts +267 -267
- package/src/widgets/webwriter-task-explainer.ts +37 -37
- package/src/widgets/webwriter-task-hint.ts +16 -16
- package/src/widgets/webwriter-task-prompt.ts +17 -17
- package/src/widgets/webwriter-task.ts +543 -543
- package/src/widgets/webwriter-text.ts +130 -130
- package/tsconfig.json +6 -6
|
@@ -1,136 +1,136 @@
|
|
|
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
|
-
declare global {interface HTMLElementTagNameMap {
|
|
14
|
-
"webwriter-cloze": WebwriterCloze;
|
|
15
|
-
}}
|
|
16
|
-
|
|
17
|
-
@customElement("webwriter-cloze")
|
|
18
|
-
export class WebwriterCloze extends LitElementWw {
|
|
19
|
-
|
|
20
|
-
static shadowRootOptions: ShadowRootInit = {...LitElementWw.shadowRootOptions, delegatesFocus: false}
|
|
21
|
-
|
|
22
|
-
static localization = {}
|
|
23
|
-
|
|
24
|
-
msg = (str: string) => this.lang in WebwriterCloze.localization? WebwriterCloze.localization[this.lang][str] ?? str: str
|
|
25
|
-
|
|
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": `"${this.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
|
-
}
|
|
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
|
+
declare global {interface HTMLElementTagNameMap {
|
|
14
|
+
"webwriter-cloze": WebwriterCloze;
|
|
15
|
+
}}
|
|
16
|
+
|
|
17
|
+
@customElement("webwriter-cloze")
|
|
18
|
+
export class WebwriterCloze extends LitElementWw {
|
|
19
|
+
|
|
20
|
+
static shadowRootOptions: ShadowRootInit = {...LitElementWw.shadowRootOptions, delegatesFocus: false}
|
|
21
|
+
|
|
22
|
+
static localization = {}
|
|
23
|
+
|
|
24
|
+
msg = (str: string) => this.lang in WebwriterCloze.localization? WebwriterCloze.localization[this.lang][str] ?? str: str
|
|
25
|
+
|
|
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": `"${this.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
136
|
}
|