From 5f2e7f6fb2ed89a83705b8a8d3dd040e861ab9d5 Mon Sep 17 00:00:00 2001 From: Patrick de Klein Date: Tue, 18 Feb 2025 20:07:29 +0100 Subject: [PATCH] feat: add toggleCorrectResponse method to display correct answer in QtiMatchInteraction --- .../qti-match-interaction.ts | 48 +++++++++++++ .../qti-order-interaction.ts | 69 ++++++++++++------- 2 files changed, 94 insertions(+), 23 deletions(-) diff --git a/src/lib/qti-components/qti-interaction/qti-match-interaction/qti-match-interaction.ts b/src/lib/qti-components/qti-interaction/qti-match-interaction/qti-match-interaction.ts index 03752106..e20239a6 100644 --- a/src/lib/qti-components/qti-interaction/qti-match-interaction/qti-match-interaction.ts +++ b/src/lib/qti-components/qti-interaction/qti-match-interaction/qti-match-interaction.ts @@ -8,6 +8,7 @@ import { DragDropInteractionMixin } from '../internal/drag-drop/drag-drop-intera import styles from './qti-match-interaction.styles'; import { Interaction } from '../../../exports/interaction'; +import type { ResponseVariable } from '../../../exports/variables'; import type { CSSResultGroup } from 'lit'; import type { ResponseInteraction } from '../../../exports/expression-result'; import type { QtiSimpleAssociableChoice } from '../qti-simple-associable-choice'; @@ -96,6 +97,53 @@ export class QtiMatchInteraction extends DragDropInteractionMixin( ); }; + public toggleCorrectResponse(responseVariable: ResponseVariable, show: boolean): void { + if (show && responseVariable.correctResponse) { + let matches: { text: string; gap: string }[] = []; + const response = Array.isArray(responseVariable.correctResponse) + ? responseVariable.correctResponse + : [responseVariable.correctResponse]; + + if (response) { + matches = response.map(x => { + const split = x.split(' '); + return { text: split[0], gap: split[1] }; + }); + } + + const gaps = this.querySelectorAll('qti-simple-match-set > qti-simple-associable-choice'); + gaps.forEach(gap => { + const identifier = gap.getAttribute('identifier'); + const textIdentifier = matches.find(x => x.gap === identifier)?.text; + const text = this.querySelector( + `qti-simple-associable-choice[identifier="${textIdentifier}"]` + )?.textContent.trim(); + if (textIdentifier && text) { + if (!gap.nextElementSibling?.classList.contains('correct-option')) { + const textSpan = document.createElement('span'); + textSpan.classList.add('correct-option'); + textSpan.textContent = text; + + // Apply styles + textSpan.style.border = '1px solid var(--qti-correct)'; + textSpan.style.borderRadius = '4px'; + textSpan.style.padding = '2px 4px'; + textSpan.style.display = 'inline-block'; + + gap.insertAdjacentElement('beforebegin', textSpan); + } + } else if (gap.nextElementSibling?.classList.contains('correct-option')) { + gap.nextElementSibling.remove(); + } + }); + } else { + const correctOptions = this.querySelectorAll('.correct-option'); + correctOptions.forEach(option => { + option.remove(); + }); + } + } + set correctResponse(responseValue: string | string[]) { if (responseValue === '') { this.correctOptions = []; diff --git a/src/lib/qti-components/qti-interaction/qti-order-interaction/qti-order-interaction.ts b/src/lib/qti-components/qti-interaction/qti-order-interaction/qti-order-interaction.ts index f63ff5bf..04d669ba 100644 --- a/src/lib/qti-components/qti-interaction/qti-order-interaction/qti-order-interaction.ts +++ b/src/lib/qti-components/qti-interaction/qti-order-interaction/qti-order-interaction.ts @@ -1,12 +1,12 @@ import { html } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; -import { unsafeHTML } from 'lit/directives/unsafe-html.js'; import { DragDropInteractionMixin } from '../internal/drag-drop/drag-drop-interaction-mixin'; import { ShuffleMixin } from '../internal/shuffle/shuffle-mixin'; // Import the mixin import { Interaction } from '../../../exports/interaction'; import styles from './qti-order-interaction.styles'; +import type { ResponseVariable } from '../../../exports/variables'; import type { QtiSimpleChoice } from '../qti-simple-choice'; @customElement('qti-order-interaction') export class QtiOrderInteraction extends ShuffleMixin( @@ -34,38 +34,61 @@ export class QtiOrderInteraction extends ShuffleMixin(
- ${Array.from(Array(this.nrChoices)).map( - (_, i) => - html`${this.showCorrectResponses && - this.correctResponses.length > i - ? unsafeHTML(`${this.correctResponses[i]}`) - : ''}` + ${[...Array(this.nrChoices)].map( + (_, i) => html`` )}
`; } - set correctResponse(value: string | string[]) { - if (value === '') { - this.showCorrectResponses = false; - return; - } + public toggleCorrectResponse(responseVariable: ResponseVariable, show: boolean): void { + if (show && responseVariable.correctResponse) { + let matches: { text: string }[] = []; + const response = Array.isArray(responseVariable.correctResponse) + ? responseVariable.correctResponse + : [responseVariable.correctResponse]; - if (this.correctResponses.length === 0) { - const responses = Array.isArray(value) ? value : [value]; + if (response) { + matches = response.map(x => { + const split = x.split(' '); + return { text: split[0] }; + }); + } - responses.forEach(response => { - let simpleChoice = this.querySelector(`qti-simple-choice[identifier="${response}"]`); - if (!simpleChoice) { - simpleChoice = this.shadowRoot.querySelector(`qti-simple-choice[identifier="${response}"]`); - } + const gaps = this.querySelectorAll('qti-simple-choice'); + gaps.forEach((gap, i) => { + const identifier = gap.getAttribute('identifier'); + const textIdentifier = matches.find(x => x.text === identifier)?.text; + const text = this.querySelector(`qti-simple-choice[identifier="${textIdentifier}"]`)?.textContent.trim(); + if (textIdentifier && text) { + if (!gap.nextElementSibling?.classList.contains('correct-option')) { + const textSpan = document.createElement('span'); + textSpan.classList.add('correct-option'); + textSpan.textContent = text; + + // Apply styles + textSpan.style.border = '1px solid var(--qti-correct)'; + textSpan.style.borderRadius = '4px'; + textSpan.style.padding = '2px 4px'; + textSpan.style.display = 'inline-block'; + + const relativeDrop = this.shadowRoot.querySelector(`drop-list[identifier="droplist${i}"]`); + relativeDrop.insertAdjacentElement('afterend', textSpan); + } + } else { + const relativeDrop = this.shadowRoot.querySelector(`drop-list[identifier="droplist${i}"]`); - const text = simpleChoice?.textContent.trim(); - this.correctResponses = [...this.correctResponses, text]; + if (relativeDrop.nextElementSibling?.classList.contains('correct-option')) { + gap.nextElementSibling.remove(); + } + } + }); + } else { + const correctOptions = this.shadowRoot.querySelectorAll('.correct-option'); + correctOptions.forEach(option => { + option.remove(); }); } - - this.showCorrectResponses = true; } // some interactions have a different way of getting the response