@webwriter/quiz 1.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 (62) hide show
  1. package/LICENSE +7 -0
  2. package/custom.d.ts +176 -0
  3. package/dist/widgets/webwriter-choice-item.css +444 -0
  4. package/dist/widgets/webwriter-choice-item.js +288 -0
  5. package/dist/widgets/webwriter-choice.css +444 -0
  6. package/dist/widgets/webwriter-choice.js +327 -0
  7. package/dist/widgets/webwriter-cloze-gap.css +444 -0
  8. package/dist/widgets/webwriter-cloze-gap.js +722 -0
  9. package/dist/widgets/webwriter-cloze.css +444 -0
  10. package/dist/widgets/webwriter-cloze.js +177 -0
  11. package/dist/widgets/webwriter-mark.css +444 -0
  12. package/dist/widgets/webwriter-mark.js +410 -0
  13. package/dist/widgets/webwriter-order-item.css +444 -0
  14. package/dist/widgets/webwriter-order-item.js +464 -0
  15. package/dist/widgets/webwriter-order.css +444 -0
  16. package/dist/widgets/webwriter-order.js +560 -0
  17. package/dist/widgets/webwriter-pairing-item.css +444 -0
  18. package/dist/widgets/webwriter-pairing-item.js +203 -0
  19. package/dist/widgets/webwriter-pairing.css +444 -0
  20. package/dist/widgets/webwriter-pairing.js +224 -0
  21. package/dist/widgets/webwriter-quiz.css +444 -0
  22. package/dist/widgets/webwriter-quiz.js +323 -0
  23. package/dist/widgets/webwriter-speech.css +444 -0
  24. package/dist/widgets/webwriter-speech.js +320 -0
  25. package/dist/widgets/webwriter-task-explainer.css +444 -0
  26. package/dist/widgets/webwriter-task-explainer.js +90 -0
  27. package/dist/widgets/webwriter-task-hint.css +444 -0
  28. package/dist/widgets/webwriter-task-hint.js +67 -0
  29. package/dist/widgets/webwriter-task-prompt.css +444 -0
  30. package/dist/widgets/webwriter-task-prompt.js +68 -0
  31. package/dist/widgets/webwriter-task.css +444 -0
  32. package/dist/widgets/webwriter-task.js +546 -0
  33. package/dist/widgets/webwriter-text.css +444 -0
  34. package/dist/widgets/webwriter-text.js +174 -0
  35. package/package.json +199 -0
  36. package/src/lib/combobox.ts +456 -0
  37. package/src/lib/highlighter-fill.svg +6 -0
  38. package/src/snippets/choice.html +8 -0
  39. package/src/snippets/cloze.html +4 -0
  40. package/src/snippets/mark.html +4 -0
  41. package/src/snippets/order.html +8 -0
  42. package/src/snippets/pairing.html +10 -0
  43. package/src/snippets/speech.html +4 -0
  44. package/src/snippets/text.html +4 -0
  45. package/src/snippets/wordsearch.html +4 -0
  46. package/src/widgets/webwriter-choice-item.ts +251 -0
  47. package/src/widgets/webwriter-choice.ts +310 -0
  48. package/src/widgets/webwriter-cloze-gap.ts +217 -0
  49. package/src/widgets/webwriter-cloze.ts +136 -0
  50. package/src/widgets/webwriter-mark.ts +435 -0
  51. package/src/widgets/webwriter-order-item.ts +445 -0
  52. package/src/widgets/webwriter-order.ts +273 -0
  53. package/src/widgets/webwriter-pairing-item.ts +156 -0
  54. package/src/widgets/webwriter-pairing.ts +188 -0
  55. package/src/widgets/webwriter-quiz.ts +281 -0
  56. package/src/widgets/webwriter-speech.ts +268 -0
  57. package/src/widgets/webwriter-task-explainer.ts +38 -0
  58. package/src/widgets/webwriter-task-hint.ts +17 -0
  59. package/src/widgets/webwriter-task-prompt.ts +18 -0
  60. package/src/widgets/webwriter-task.ts +544 -0
  61. package/src/widgets/webwriter-text.ts +131 -0
  62. package/tsconfig.json +7 -0
@@ -0,0 +1,10 @@
1
+ <webwriter-task>
2
+ <webwriter-task-prompt slot="prompt"><p></p></webwriter-task-prompt>
3
+ <webwriter-pairing>
4
+ <webwriter-pairing-item>
5
+ <picture>
6
+ <img>
7
+ </picture>
8
+ </webwriter-pairing-item>
9
+ </webwriter-pairing>
10
+ </webwriter-task>
@@ -0,0 +1,4 @@
1
+ <webwriter-task>
2
+ <webwriter-task-prompt slot="prompt"><p></p></webwriter-task-prompt>
3
+ <webwriter-speech></webwriter-speech>
4
+ </webwriter-task>
@@ -0,0 +1,4 @@
1
+ <webwriter-task>
2
+ <webwriter-task-prompt slot="prompt"><p></p></webwriter-task-prompt>
3
+ <webwriter-text></webwriter-text>
4
+ </webwriter-task>
@@ -0,0 +1,4 @@
1
+ <webwriter-task>
2
+ <webwriter-task-prompt slot="prompt"><p></p></webwriter-task-prompt>
3
+ <webwriter-wordsearch></webwriter-wordsearch>
4
+ </webwriter-task>
@@ -0,0 +1,251 @@
1
+ import {css, html, PropertyValues} from "lit"
2
+ import {styleMap} from "lit/directives/style-map.js"
3
+ import {classMap} from "lit/directives/class-map.js"
4
+ import {unsafeStatic} from "lit/static-html.js"
5
+ import {LitElementWw} from "@webwriter/lit"
6
+ import {customElement, property, query} from "lit/decorators.js"
7
+
8
+ import SlRadio from "@shoelace-style/shoelace/dist/components/radio/radio.component.js"
9
+ import SlCheckbox from "@shoelace-style/shoelace/dist/components/checkbox/checkbox.component.js"
10
+ import SlIcon from "@shoelace-style/shoelace/dist/components/icon/icon.component.js"
11
+ import "@shoelace-style/shoelace/dist/themes/light.css"
12
+ import { keyed } from "lit/directives/keyed.js"
13
+ import IconX from "bootstrap-icons/icons/x.svg"
14
+ import IconCheck from "bootstrap-icons/icons/check.svg"
15
+
16
+
17
+ declare global {interface HTMLElementTagNameMap {
18
+ "webwriter-choice-item": WebwriterChoiceItem;
19
+ }}
20
+
21
+ @customElement("webwriter-choice-item")
22
+ export class WebwriterChoiceItem extends LitElementWw {
23
+
24
+ static localization = {}
25
+
26
+ msg = (str: string) => this.lang in WebwriterChoiceItem.localization? WebwriterChoiceItem.localization[this.lang][str] ?? str: str
27
+
28
+ @property({type: Boolean, attribute: false})
29
+ accessor active = false
30
+
31
+ @property({type: Boolean, attribute: false})
32
+ accessor valid: true | false | undefined
33
+
34
+ // inactive + invalid/no validity -> nothing
35
+ // active + no validity -> blue circle
36
+ // inactive/active + valid -> green upper checkmark
37
+ // active + invalid -> red upper cross
38
+
39
+
40
+ @property({type: String, attribute: true, reflect: true})
41
+ accessor layout: "list" | "tiles" = "list"
42
+
43
+ static scopedElements = {
44
+ "sl-radio": SlRadio,
45
+ "sl-checkbox": SlCheckbox,
46
+ "sl-icon": SlIcon
47
+ }
48
+
49
+ static styles = css`
50
+
51
+ :host {
52
+ width: 100%;
53
+ position: relative;
54
+ }
55
+
56
+ :host(:not([contenteditable=true]):not([contenteditable=""])) .author-only {
57
+ display: none;
58
+ }
59
+
60
+ :host(:is([contenteditable=true], [contenteditable=""])) .user-only {
61
+ display: none;
62
+ }
63
+
64
+ :host(:is([contenteditable=true], [contenteditable=""])) sl-checkbox.valid {
65
+ --webwriter-control-color-600: var(--sl-color-success-600);
66
+ --webwriter-control-color-400: var(--sl-color-success-400);
67
+ }
68
+
69
+ sl-checkbox {
70
+ display: block;
71
+ width: 100%;
72
+ &::part(base) {
73
+ width: 100%;
74
+ display: flex;
75
+ flex-direction: row;
76
+ align-items: center;
77
+ cursor: unset;
78
+ }
79
+
80
+ &::part(control) {
81
+ cursor: pointer;
82
+ border-radius: var(--webwriter-choice-radius, 2px);
83
+ border-width: 2px;
84
+ }
85
+
86
+ &::part(control):not(:hover){
87
+ border-color: var(--sl-color-gray-500);
88
+ }
89
+
90
+ &::part(control):hover {
91
+ border-color: var(--sl-color-gray-700);
92
+ }
93
+
94
+ &::part(control--checked):not(:hover) {
95
+ background-color: var(--webwriter-control-color-600, var(--sl-color-primary-600));
96
+ border-color: var(--sl-color-gray-500);
97
+ }
98
+ &::part(control--checked):hover {
99
+ background-color: var(--webwriter-control-color-400, var(--sl-color-primary-400));
100
+ border-color: var(--webwriter-control-color-600, var(--sl-color-primary-600));
101
+ }
102
+
103
+ &::part(label) {
104
+ width: 100%;
105
+ }
106
+ }
107
+
108
+ .solution {
109
+ position: absolute;
110
+ top: -6px;
111
+ left: -6px;
112
+ border: 2px solid var(--sl-color-gray-500);
113
+ border-radius: var(--webwriter-choice-radius, 2px);
114
+ width: 12px;
115
+ height: 12px;
116
+ z-index: 1;
117
+ color: white;
118
+
119
+ &[data-valid] {
120
+ background: var(--sl-color-success-600);
121
+ }
122
+
123
+ &:not([data-valid]) {
124
+ background: var(--sl-color-danger-600);
125
+ }
126
+ }
127
+
128
+ :host([layout=tiles]) {
129
+ position: relative;
130
+ overflow: visible !important;
131
+
132
+ & ::slotted(:is(picture, audio, video, img, iframe)) {
133
+ height: 100%;
134
+ width: 100%;
135
+ }
136
+
137
+ & ::slotted(:not(:is(picture, audio, video, img, iframe))) {
138
+ margin: 5px !important;
139
+ }
140
+
141
+ sl-checkbox {
142
+ display: block;
143
+ &::part(base) {
144
+ display: flex;
145
+ flex-direction: row;
146
+ align-items: center;
147
+ cursor: unset;
148
+ position: static;
149
+ }
150
+
151
+ &::part(control) {
152
+ position: absolute;
153
+ bottom: -10px;
154
+ left: -10px;
155
+ cursor: pointer;
156
+ border-radius: var(--webwriter-choice-radius, 2px);
157
+ z-index: 100;
158
+ border-color: var(--sl-color-gray-500);
159
+ }
160
+
161
+ &::part(control--checked):not(:hover) {
162
+ background-color: var(--webwriter-control-color-600, var(--sl-color-primary-600));
163
+ border-color: var(--sl-color-gray-500);
164
+ }
165
+ &::part(control--checked):hover {
166
+ background-color: var(--webwriter-control-color-400, var(--sl-color-primary-400));
167
+ border-color: var(--webwriter-control-color-600, var(--sl-color-primary-600));
168
+ }
169
+
170
+ &::part(label) {
171
+ aspect-ratio: 1;
172
+ min-width: 125px;
173
+ max-width: 350px;
174
+ min-height: 125px;
175
+ max-height: 350px;
176
+ overflow: hidden;
177
+ resize: both;
178
+ margin-inline-start: 0;
179
+ overflow-y: auto;
180
+ scrollbar-width: thin;
181
+ }
182
+ }
183
+ }
184
+ `
185
+
186
+ handleClick = (e: PointerEvent) => {
187
+ const checkboxClicked = e.composedPath().some(v => (v as HTMLElement)?.classList?.contains("checkbox__control"))
188
+ const editable = this.isContentEditable
189
+ if(editable && !checkboxClicked) {
190
+ e.preventDefault()
191
+ }
192
+ else {
193
+ e.stopImmediatePropagation()
194
+ }
195
+ }
196
+
197
+ handleChange = (e: CustomEvent) => {
198
+ if(this.isContentEditable) {
199
+ this.valid = !this.valid
200
+ }
201
+ else {
202
+ this.active = !this.active
203
+ }
204
+ }
205
+
206
+ @query("sl-checkbox")
207
+ accessor checkbox: SlCheckbox
208
+
209
+ observer: MutationObserver
210
+
211
+ protected async updated(_changedProperties: PropertyValues) {
212
+ await this.checkbox.updateComplete
213
+ const labelEl = this.checkbox.shadowRoot.querySelector(".checkbox__label") as HTMLElement
214
+
215
+ if(_changedProperties.has("layout") && this.layout === "list") {
216
+ this.syncSize(true)
217
+ this.observer?.disconnect()
218
+ }
219
+ else if(_changedProperties.has("layout") && this.layout === "tiles") {
220
+ this.syncSize()
221
+ this.observer = new MutationObserver(() => this.syncSize())
222
+ this.observer.observe(labelEl, {attributeFilter: ["style"], attributes: true})
223
+ }
224
+ }
225
+
226
+ syncSize(clear=false) {
227
+ const labelEl = this.checkbox?.shadowRoot.querySelector(".checkbox__label") as HTMLElement
228
+ if(labelEl && !clear) {
229
+ this.style.width = labelEl.style.width
230
+ this.style.height = labelEl.style.height
231
+ }
232
+ else if(labelEl && clear) {
233
+ this.style.width = labelEl.style.width = null
234
+ this.style.height = labelEl.style.height = null
235
+ }
236
+ }
237
+
238
+ disconnectedCallback(): void {
239
+ super.disconnectedCallback()
240
+ this.observer?.disconnect()
241
+ }
242
+
243
+ render() {
244
+ return keyed(this.layout, html`
245
+ ${this.valid !== undefined && (this.active || !this.active && this.valid)? html`<sl-icon class="solution user-only" ?data-valid=${this.valid} src=${this.valid? IconCheck: IconX}></sl-icon>`: null}
246
+ <sl-checkbox class=${classMap({valid: this.valid, active: this.active})} exportparts="base, control, label" @click=${this.handleClick} @sl-change=${this.handleChange} ?checked=${this.isContentEditable? this.valid: this.active}>
247
+ <slot part="slot" style=${styleMap({"--ww-placeholder": `"${this.msg("Option")}"`})}></slot>
248
+ </sl-checkbox>
249
+ `)
250
+ }
251
+ }
@@ -0,0 +1,310 @@
1
+ import {html, css, PropertyValueMap, TemplateResult, PropertyValues} from "lit"
2
+ import {styleMap} from "lit/directives/style-map.js"
3
+ import {LitElementWw, option, action} from "@webwriter/lit"
4
+ import {customElement, property, queryAssignedElements, query} from "lit/decorators.js"
5
+
6
+ import SlButton from "@shoelace-style/shoelace/dist/components/button/button.component.js"
7
+ import SlIcon from "@shoelace-style/shoelace/dist/components/icon/icon.component.js"
8
+ import SlRadio from "@shoelace-style/shoelace/dist/components/radio/radio.component.js"
9
+ import SlCheckbox from "@shoelace-style/shoelace/dist/components/checkbox/checkbox.component.js"
10
+ import SlRadioButton from "@shoelace-style/shoelace/dist/components/radio-button/radio-button.component.js"
11
+ import SlRadioGroup from "@shoelace-style/shoelace/dist/components/radio-group/radio-group.component.js"
12
+
13
+ import IconPlusSquare from "bootstrap-icons/icons/plus-square.svg"
14
+ import IconPlusCircle from "bootstrap-icons/icons/plus-circle.svg"
15
+ import { WebwriterChoiceItem } from "./webwriter-choice-item.js"
16
+ import "@shoelace-style/shoelace/dist/themes/light.css"
17
+ import MiniMasonry from "minimasonry"
18
+
19
+
20
+ export function shuffle<T>(a: T[]) {
21
+ for (let i = a.length - 1; i > 0; i--) {
22
+ const j = Math.floor(Math.random() * (i + 1));
23
+ [a[i], a[j]] = [a[j], a[i]];
24
+ }
25
+ return a;
26
+ }
27
+
28
+ declare global {interface HTMLElementTagNameMap {
29
+ "webwriter-choice": WebwriterChoice;
30
+ }}
31
+
32
+ @customElement("webwriter-choice")
33
+ export class WebwriterChoice extends LitElementWw {
34
+
35
+ @property({type: String, attribute: true, reflect: true})
36
+ @option({
37
+ type: "select",
38
+ options: [
39
+ {value: "truefalse", label: {"en": "True/False"}},
40
+ {value: "single", label: {"en": "Single Choice"}},
41
+ {value: "multiple", label: {"en": "Multiple Choice"}},
42
+ ]
43
+ })
44
+ accessor mode: "truefalse" | "single" | "multiple" = "single"
45
+
46
+
47
+ get layout(): "list" | "tiles" {
48
+ return this.children?.item(0)?.getAttribute("layout") as any ?? "list"
49
+ }
50
+
51
+ @property({type: String, attribute: true, reflect: true}) //@ts-ignore
52
+ @option({
53
+ type: "select",
54
+ options: [
55
+ {value: "list", label: {"en": "List"}},
56
+ {value: "tiles", label: {"en": "Tiles"}}
57
+ ],
58
+ })
59
+ set layout(value) {
60
+ this.querySelectorAll("webwriter-choice-item").forEach(el => el.setAttribute("layout", value))
61
+ this.requestUpdate("layout")
62
+ }
63
+
64
+ @property({type: Boolean, attribute: true, reflect: true})
65
+ @option({
66
+ type: Boolean,
67
+ label: {"en": "Random Choice Order"}
68
+ })
69
+ accessor randomOrder = false
70
+
71
+
72
+ get solution() {
73
+ const validIDs = this.items.filter(item => item.valid).map(item => item.id)
74
+ return validIDs.length? validIDs: undefined
75
+ }
76
+
77
+ set solution(value: string[]) {
78
+ this.items.forEach(item => value.includes(item.id)? item.valid=true: null)
79
+ }
80
+
81
+
82
+ reportSolution() {
83
+ if(!this.solution) {
84
+ this.items.forEach(item => item.valid = true)
85
+ return
86
+ }
87
+ this.items.forEach(item => item.valid = (this.solution ?? []).includes(item.id))
88
+ }
89
+
90
+ reset() {
91
+ this.items.forEach(item => {item.valid = undefined; item.active = false})
92
+ this.shuffleItems()
93
+ }
94
+
95
+ static scopedElements = {
96
+ "sl-button": SlButton,
97
+ "sl-icon": SlIcon,
98
+ "sl-radio": SlRadio,
99
+ "sl-checkbox": SlCheckbox,
100
+ "sl-radio-button": SlRadioButton,
101
+ "sl-radio-group": SlRadioGroup
102
+ }
103
+
104
+ @query("slot")
105
+ accessor slotEl: HTMLSlotElement
106
+
107
+ @action()
108
+ addItem() {
109
+ /*
110
+ if(this.ownerDocument.getSelection().containsNode(this, true)) {
111
+ this.ownerDocument.getSelection().modify("move", "backward", "character")
112
+ }
113
+ setTimeout(() => {*/
114
+ const choiceItem = this.ownerDocument.createElement("webwriter-choice-item")
115
+ const p = this.ownerDocument.createElement("p")
116
+ choiceItem.appendChild(p)
117
+ choiceItem.setAttribute("layout", this.layout)
118
+ this.appendChild(choiceItem)
119
+ this.ownerDocument.getSelection().setBaseAndExtent(p, 0, p, 0)
120
+ //})
121
+ }
122
+
123
+ shuffleItems() {
124
+ const items = this.slotEl.assignedElements() as WebwriterChoiceItem[]
125
+ const n = items.length
126
+ const nums = shuffle([...(new Array(n)).keys()])
127
+ items.forEach((el, i) => el.style.order = String(nums[i]))
128
+ }
129
+
130
+ connectedCallback(): void {
131
+ super.connectedCallback()
132
+ const observer = new MutationObserver(() => {
133
+ if(!this.contentEditable && this.randomOrder) {
134
+ this.shuffleItems()
135
+ }
136
+ })
137
+ observer.observe(this, {childList: true})
138
+ }
139
+
140
+ protected firstUpdated(_changedProperties: PropertyValues): void {
141
+ if(!this.contentEditable && this.randomOrder) {
142
+ this.shuffleItems()
143
+ }
144
+ }
145
+
146
+ static styles = css`
147
+ :host {
148
+ display: flex !important;
149
+ flex-direction: column;
150
+ align-items: flex-start;
151
+ gap: 0.5rem;
152
+ }
153
+
154
+ :host([layout=tiles]) {
155
+
156
+ flex-direction: row;
157
+ flex-wrap: wrap;
158
+ gap: 15px;
159
+
160
+ & ::slotted(*) {
161
+ aspect-ratio: 1;
162
+ min-width: 125px;
163
+ width: 125px;
164
+ max-width: 350px;
165
+ min-height: 125px;
166
+ height: 125px;
167
+ max-height: 350px;
168
+ overflow: hidden;
169
+ resize: both;
170
+ border: 2px solid var(--sl-color-gray-500);
171
+ border-radius: 5px;
172
+ }
173
+ }
174
+
175
+ sl-button::part(label) {
176
+ padding: 0;
177
+ display: flex;
178
+ flex-direction: row;
179
+ align-items: center;
180
+ }
181
+
182
+ sl-button::part(base) {
183
+ border: none;
184
+ background: transparent;
185
+ }
186
+
187
+ sl-icon {
188
+ width: 19px;
189
+ height: 19px;
190
+ }
191
+
192
+ sl-icon::part(svg) {
193
+ overflow: visible;
194
+ }
195
+
196
+ :host(:not([contenteditable=true]):not([contenteditable=""])) .author-only {
197
+ display: none !important;
198
+ }
199
+
200
+ #add-option span {
201
+ margin-inline-start: 0.5em;
202
+ }
203
+
204
+ :host(:is([contenteditable=true], [contenteditable=""])) ::slotted(*) {
205
+ order: unset !important;
206
+ }
207
+
208
+ :host(:not([mode=multiple])) {
209
+ --webwriter-choice-radius: 100%;
210
+ }
211
+
212
+ #add-option:not(:hover)::part(base) {
213
+ color: darkgray;
214
+ }
215
+
216
+ :host([layout=tiles]) #add-option {
217
+ width: 125px;
218
+ height: 125px;
219
+ overflow: hidden;
220
+ border: 2px solid var(--sl-color-gray-300);
221
+ border-radius: 5px;
222
+ padding: 5px;
223
+ display: flex;
224
+ align-items: center;
225
+ justify-content: center;
226
+ }
227
+
228
+ :host([mode=truefalse]) #add-option {
229
+ display: none;
230
+ }
231
+ `
232
+
233
+ @queryAssignedElements()
234
+ accessor items: WebwriterChoiceItem[]
235
+
236
+ #value: number[] = []
237
+
238
+ get value() {
239
+ return this.#value
240
+ }
241
+
242
+ @property({type: Array, attribute: false})
243
+ set value(value: number[]) {
244
+ this.#value = value
245
+ this.items.forEach((item, i) => {
246
+ this.isContentEditable
247
+ ? item.valid = this.#value.includes(i)
248
+ : item.active = this.#value.includes(i)
249
+ })
250
+ }
251
+
252
+ private applyValue() {
253
+ this.#value = this.items
254
+ .map((item, i) => [item.valid, i] as [boolean, number])
255
+ .filter(([valid]) => valid)
256
+ .map(([_, i]) => i)
257
+ }
258
+
259
+ handleControlChange = (e: CustomEvent) => {
260
+ if(this.mode === "single") {
261
+ const item = e.target as WebwriterChoiceItem
262
+ const otherItems = this.items.filter(el => el !== item)
263
+ otherItems.forEach(el => this.isContentEditable? el.valid = false: el.active = false)
264
+ }
265
+ this.applyValue()
266
+ this.dispatchEvent(new CustomEvent("ww-answer-change", {
267
+ detail: {value: this.value},
268
+ bubbles: true,
269
+ composed: true
270
+ }))
271
+ }
272
+
273
+ handleSlotChange(e: Event) {
274
+ this.requestUpdate()
275
+ }
276
+
277
+ protected willUpdate(changed: PropertyValueMap<any>) {
278
+ if(changed.has("mode") && this.mode === "single") {
279
+ this.items
280
+ .filter(el => this.isContentEditable? el.valid: el.active)
281
+ .slice(1)
282
+ .forEach(el => this.isContentEditable? el.valid = false: el.active = false)
283
+ }
284
+ if(changed.has("mode") && this.mode === "truefalse") {
285
+ this.items.forEach(el => el.remove())
286
+ }
287
+ }
288
+
289
+ @query("#items-slot")
290
+ accessor itemsSlotEl: HTMLSlotElement
291
+
292
+ render() {
293
+ return html`
294
+ ${this.mode === "truefalse"
295
+ ? html`
296
+ <sl-radio-group>
297
+ <sl-radio-button value="true">True</sl-radio-button>
298
+ <sl-radio-button value="false">False</sl-radio-button>
299
+ </sl-radio-group>
300
+ `
301
+ : html`
302
+ <slot id="items-slot" @sl-change=${this.handleControlChange} @slotchange=${this.handleSlotChange}></slot>
303
+ `
304
+ }
305
+ <sl-button size="small" id="add-option" class="author-only" @click=${() => this.addItem()}>
306
+ <sl-icon src=${this.mode === "multiple"? IconPlusSquare: IconPlusCircle}></sl-icon><span>Add Option</span>
307
+ </sl-button>
308
+ `
309
+ }
310
+ }