@qti-components/match-interaction 1.1.3 → 1.1.5

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.
@@ -6,7 +6,6 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
6
6
  };
7
7
  import { html, nothing } from 'lit';
8
8
  import { property, state } from 'lit/decorators.js';
9
- import { unsafeHTML } from 'lit/directives/unsafe-html.js';
10
9
  import { Interaction } from '@qti-components/base';
11
10
  import { DragDropSlottedMixin, DragDropSlottedSortableMixin } from '@qti-components/interactions-core/mixins/drag-drop-observables';
12
11
  // import { DragDropInteractionMixin } from '@qti-components/interactions-core/mixins/drag-drop';
@@ -124,11 +123,11 @@ export class QtiMatchInteraction extends DragDropSlottedSortableMixin(SlottedBas
124
123
  const match = matches.find(m => m.target === targetId);
125
124
  if (match?.source) {
126
125
  const sourceChoice = this.querySelector(`qti-simple-associable-choice[identifier="${match.source}"]`);
127
- const text = sourceChoice?.textContent?.trim();
128
- if (text && !targetChoice.previousElementSibling?.classList.contains('correct-option')) {
126
+ const nodes = Array.from(sourceChoice?.childNodes || []).map(n => n.cloneNode(true));
127
+ if (nodes.length > 0 && !targetChoice.previousElementSibling?.classList.contains('correct-option')) {
129
128
  const textSpan = document.createElement('span');
130
129
  textSpan.classList.add('correct-option');
131
- textSpan.textContent = text;
130
+ nodes.forEach(node => textSpan.appendChild(node));
132
131
  // Style the span
133
132
  textSpan.style.border = '1px solid var(--qti-correct)';
134
133
  textSpan.style.borderRadius = '4px';
@@ -191,11 +190,11 @@ export class QtiMatchInteraction extends DragDropSlottedSortableMixin(SlottedBas
191
190
  <table part="table">
192
191
  <tr part="r-header">
193
192
  <td></td>
194
- ${this.targetChoices.map(col => html `<th part="r-header">${unsafeHTML(col.innerHTML)}</th>`)}
193
+ ${this.targetChoices.map(col => html `<th part="r-header">${Array.from(col.childNodes).map(n => n.cloneNode(true))}</th>`)}
195
194
  </tr>
196
195
 
197
196
  ${this.sourceChoices.map(row => html `<tr part="row">
198
- <td part="c-header">${unsafeHTML(row.innerHTML)}</td>
197
+ <td part="c-header">${Array.from(row.childNodes).map(n => n.cloneNode(true))}</td>
199
198
  ${this.targetChoices.map(col => {
200
199
  const rowId = row.getAttribute('identifier');
201
200
  const colId = col.getAttribute('identifier');
@@ -1 +1 @@
1
- {"version":3,"file":"qti-match-interaction.js","sourceRoot":"","sources":["../src/qti-match-interaction.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, nothing } from 'lit';\nimport { property, state } from 'lit/decorators.js';\nimport { unsafeHTML } from 'lit/directives/unsafe-html.js';\n\nimport { Interaction } from '@qti-components/base';\nimport { DragDropSlottedMixin, DragDropSlottedSortableMixin } from '@qti-components/interactions-core/mixins/drag-drop-observables';\n\n// import { DragDropInteractionMixin } from '@qti-components/interactions-core/mixins/drag-drop';\nimport styles from './qti-match-interaction.styles';\n\nimport type { ResponseVariable } from '@qti-components/base';\nimport type { CSSResultGroup } from 'lit';\nimport type { ResponseInteraction } from '@qti-components/base';\nimport type { QtiSimpleAssociableChoice } from '@qti-components/interactions-core/elements/qti-simple-associable-choice';\nconst SlottedBase = DragDropSlottedMixin(\n Interaction,\n 'qti-simple-match-set:first-of-type qti-simple-associable-choice, qti-simple-match-set:last-of-type > qti-simple-associable-choice > qti-simple-associable-choice',\n 'qti-simple-match-set:last-of-type > qti-simple-associable-choice',\n 'qti-simple-match-set:first-of-type'\n);\n\nexport class QtiMatchInteraction extends DragDropSlottedSortableMixin(SlottedBase, '[qti-draggable=\"true\"]') {\n static override styles: CSSResultGroup = styles;\n\n protected sourceChoices: QtiSimpleAssociableChoice[];\n protected targetChoices: QtiSimpleAssociableChoice[];\n protected lastCheckedRadio: HTMLInputElement | null = null;\n\n @property({ type: String }) class: string = '';\n\n @state() protected _response: string | string[] = [];\n // dragDropApi: TouchDragAndDrop;\n get response(): string[] {\n if (!this.classList.contains('qti-match-tabular')) return super.response as string[];\n else return this._response as string[];\n }\n set response(val: string[]) {\n if (!this.classList.contains('qti-match-tabular')) super.response = val;\n else this._response = val;\n }\n\n @property({ type: String, attribute: 'response-identifier' }) override responseIdentifier: string = '';\n\n @state() protected correctOptions: { source: string; target: string }[] = null;\n\n override async connectedCallback(): Promise<void> {\n super.connectedCallback();\n this.sourceChoices = Array.from<QtiSimpleAssociableChoice>(\n this.querySelectorAll('qti-simple-match-set:first-of-type qti-simple-associable-choice')\n );\n this.targetChoices = Array.from<QtiSimpleAssociableChoice>(\n this.querySelectorAll('qti-simple-match-set:last-of-type qti-simple-associable-choice')\n );\n\n this.response = [];\n }\n\n protected handleRadioClick = (e: { target: HTMLInputElement }) => {\n const radio = e.target as HTMLInputElement;\n if (this.lastCheckedRadio === radio) {\n radio.checked = false;\n this.lastCheckedRadio = null;\n this.handleRadioChange(e);\n } else {\n this.lastCheckedRadio = radio;\n }\n };\n\n protected handleRadioChange = (e: { target: any }) => {\n const checkbox = e.target as HTMLInputElement;\n const value = checkbox.value;\n const name = checkbox.name;\n const type = checkbox.type;\n\n if (checkbox.checked) {\n if (!this.response) {\n this.response = [value];\n } else if (this.response.indexOf(value) === -1) {\n if (type === 'radio') {\n this.response = (this.response || []).filter(v => v.indexOf(name) === -1);\n }\n this.response = [...this.response, value];\n }\n this.lastCheckedRadio = checkbox;\n } else {\n this.response = (this.response || []).filter(v => v !== value);\n this.lastCheckedRadio = null;\n }\n\n this.requestUpdate();\n this.dispatchEvent(\n new CustomEvent<ResponseInteraction>('qti-interaction-response', {\n bubbles: true,\n composed: true,\n detail: {\n responseIdentifier: this.responseIdentifier,\n response: Array.isArray(this.response) ? [...this.response] : this.response\n }\n })\n );\n };\n\n validate(): boolean {\n if (this.class.split(' ').includes('qti-match-tabular')) {\n return this.response?.length === this.sourceChoices.length;\n } else {\n return super.validate();\n }\n }\n\n #getMatches(responseVariable: ResponseVariable): { source: string; target: string }[] {\n if (!responseVariable.correctResponse) {\n return [];\n }\n const correctResponse = Array.isArray(responseVariable.correctResponse)\n ? responseVariable.correctResponse\n : [responseVariable.correctResponse];\n\n const matches: { source: string; target: string }[] = [];\n if (correctResponse) {\n correctResponse.forEach(x => {\n const split = x.split(' ');\n matches.push({ source: split[0], target: split[1] });\n });\n }\n return matches;\n }\n\n public override toggleInternalCorrectResponse(show: boolean): void {\n const responseVariable = this.responseVariable;\n\n if (!responseVariable?.correctResponse) {\n // Remove all previously added correct responses\n this.querySelectorAll('.correct-option').forEach(el => el.remove());\n return;\n }\n const matches = this.#getMatches(responseVariable);\n\n if (!this.class.split(' ').includes('qti-match-tabular')) {\n if (show) {\n // Clear old correct options first\n this.querySelectorAll('.correct-option').forEach(el => el.remove());\n\n this.targetChoices.forEach(targetChoice => {\n const targetId = targetChoice.getAttribute('identifier');\n const match = matches.find(m => m.target === targetId);\n\n if (match?.source) {\n const sourceChoice = this.querySelector(`qti-simple-associable-choice[identifier=\"${match.source}\"]`);\n const text = sourceChoice?.textContent?.trim();\n\n if (text && !targetChoice.previousElementSibling?.classList.contains('correct-option')) {\n const textSpan = document.createElement('span');\n textSpan.classList.add('correct-option');\n textSpan.textContent = text;\n\n // Style the span\n textSpan.style.border = '1px solid var(--qti-correct)';\n textSpan.style.borderRadius = '4px';\n textSpan.style.padding = '2px 4px';\n textSpan.style.display = 'inline-block';\n\n // Insert before the target choice\n targetChoice.insertAdjacentElement('beforebegin', textSpan);\n }\n }\n });\n } else {\n this.correctOptions = null;\n }\n } else {\n if (show) {\n this.correctOptions = matches || [];\n } else {\n this.correctOptions = null;\n }\n }\n }\n\n public override toggleCandidateCorrection(show: boolean) {\n const responseVariable = this.responseVariable;\n\n if (!responseVariable?.correctResponse) {\n return;\n }\n const matches = this.#getMatches(responseVariable);\n\n this.targetChoices.forEach(targetChoice => {\n const targetId = targetChoice.getAttribute('identifier');\n const targetMatches = matches.filter(m => m.target === targetId);\n\n const selectedChoices = targetChoice.querySelectorAll(`qti-simple-associable-choice`);\n\n selectedChoices.forEach(selectedChoice => {\n selectedChoice.internals.states.delete('candidate-correct');\n selectedChoice.internals.states.delete('candidate-incorrect');\n\n if (!show) {\n return;\n }\n\n const isCorrect = targetMatches.find(m => m.source === selectedChoice.identifier)?.source !== undefined;\n if (isCorrect) {\n selectedChoice.internals.states.add('candidate-correct');\n } else {\n selectedChoice.internals.states.add('candidate-incorrect');\n }\n });\n });\n }\n\n override render() {\n const isTabular = this.class.split(' ').includes('qti-match-tabular');\n const hasCorrectResponse = this.correctOptions !== null;\n return html`\n <slot name=\"prompt\"></slot>\n <slot ?hidden=${isTabular}></slot>\n\n ${isTabular\n ? html`\n <table part=\"table\">\n <tr part=\"r-header\">\n <td></td>\n ${this.targetChoices.map(col => html`<th part=\"r-header\">${unsafeHTML(col.innerHTML)}</th>`)}\n </tr>\n\n ${this.sourceChoices.map(\n row =>\n html`<tr part=\"row\">\n <td part=\"c-header\">${unsafeHTML(row.innerHTML)}</td>\n ${this.targetChoices.map(col => {\n const rowId = row.getAttribute('identifier');\n const colId = col.getAttribute('identifier');\n const value = `${rowId} ${colId}`;\n const selectedInRowCount =\n (this.response || []).filter(v => v.split(' ')[0] === rowId).length || 0;\n const checked = this.response?.includes(value) || false;\n const type = row.matchMax === 1 ? 'radio' : 'checkbox';\n const isCorrect = !!this.correctOptions?.find(\n option => option.source === rowId && option.target === colId\n );\n const part =\n type === 'radio'\n ? `rb ${checked ? 'rb-checked' : ''} ${hasCorrectResponse ? (isCorrect ? 'rb-correct' : 'rb-incorrect') : ''}`\n : `cb ${checked ? 'cb-checked' : ''} ${hasCorrectResponse ? (isCorrect ? 'cb-correct' : 'cb-incorrect') : ''}`;\n\n // disable if match max is greater than 1 and max is reached\n const disable =\n this.correctOptions?.length > 0\n ? true\n : row.matchMax === 1\n ? false\n : row.matchMax !== 0 && selectedInRowCount >= row.matchMax && !checked;\n return html`<td part=\"input-cell\">\n <div\n class=\"input-container\"\n style=\"position: relative; width: 24px; height: 24px; margin: 0 auto;\"\n >\n <input\n type=${type}\n part=${part}\n name=${rowId}\n value=${value}\n .disabled=${disable}\n @change=${(e: { target: any }) => this.handleRadioChange(e)}\n @click=${(e: { target: HTMLInputElement }) =>\n row.matchMax === 1 ? this.handleRadioClick(e) : null}\n />\n ${type === 'checkbox' && checked\n ? html`\n <svg\n part=\"checkmark\"\n viewBox=\"0 0 24 24\"\n style=\"position: absolute; width: 20px; height: 20px; top: 2px; left: 2px; pointer-events: none;\"\n >\n <path d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z\" fill=\"white\" />\n </svg>\n `\n : ''}\n </div>\n </td>`;\n })}\n </tr>`\n )}\n </table>\n `\n : nothing}\n\n <div role=\"alert\" part=\"message\" id=\"validation-message\"></div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'qti-match-interaction': QtiMatchInteraction;\n }\n}\n"]}
1
+ {"version":3,"file":"qti-match-interaction.js","sourceRoot":"","sources":["../src/qti-match-interaction.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, nothing } from 'lit';\nimport { property, state } from 'lit/decorators.js';\n\nimport { Interaction } from '@qti-components/base';\nimport {\n DragDropSlottedMixin,\n DragDropSlottedSortableMixin\n} from '@qti-components/interactions-core/mixins/drag-drop-observables';\n\n// import { DragDropInteractionMixin } from '@qti-components/interactions-core/mixins/drag-drop';\nimport styles from './qti-match-interaction.styles';\n\nimport type { ResponseVariable } from '@qti-components/base';\nimport type { CSSResultGroup } from 'lit';\nimport type { ResponseInteraction } from '@qti-components/base';\nimport type { QtiSimpleAssociableChoice } from '@qti-components/interactions-core/elements/qti-simple-associable-choice';\nconst SlottedBase = DragDropSlottedMixin(\n Interaction,\n 'qti-simple-match-set:first-of-type qti-simple-associable-choice, qti-simple-match-set:last-of-type > qti-simple-associable-choice > qti-simple-associable-choice',\n 'qti-simple-match-set:last-of-type > qti-simple-associable-choice',\n 'qti-simple-match-set:first-of-type'\n);\n\nexport class QtiMatchInteraction extends DragDropSlottedSortableMixin(SlottedBase, '[qti-draggable=\"true\"]') {\n static override styles: CSSResultGroup = styles;\n\n protected sourceChoices: QtiSimpleAssociableChoice[];\n protected targetChoices: QtiSimpleAssociableChoice[];\n protected lastCheckedRadio: HTMLInputElement | null = null;\n\n @property({ type: String }) class: string = '';\n\n @state() protected _response: string | string[] = [];\n // dragDropApi: TouchDragAndDrop;\n get response(): string[] {\n if (!this.classList.contains('qti-match-tabular')) return super.response as string[];\n else return this._response as string[];\n }\n set response(val: string[]) {\n if (!this.classList.contains('qti-match-tabular')) super.response = val;\n else this._response = val;\n }\n\n @property({ type: String, attribute: 'response-identifier' }) override responseIdentifier: string = '';\n\n @state() protected correctOptions: { source: string; target: string }[] = null;\n\n override async connectedCallback(): Promise<void> {\n super.connectedCallback();\n this.sourceChoices = Array.from<QtiSimpleAssociableChoice>(\n this.querySelectorAll('qti-simple-match-set:first-of-type qti-simple-associable-choice')\n );\n this.targetChoices = Array.from<QtiSimpleAssociableChoice>(\n this.querySelectorAll('qti-simple-match-set:last-of-type qti-simple-associable-choice')\n );\n\n this.response = [];\n }\n\n protected handleRadioClick = (e: { target: HTMLInputElement }) => {\n const radio = e.target as HTMLInputElement;\n if (this.lastCheckedRadio === radio) {\n radio.checked = false;\n this.lastCheckedRadio = null;\n this.handleRadioChange(e);\n } else {\n this.lastCheckedRadio = radio;\n }\n };\n\n protected handleRadioChange = (e: { target: any }) => {\n const checkbox = e.target as HTMLInputElement;\n const value = checkbox.value;\n const name = checkbox.name;\n const type = checkbox.type;\n\n if (checkbox.checked) {\n if (!this.response) {\n this.response = [value];\n } else if (this.response.indexOf(value) === -1) {\n if (type === 'radio') {\n this.response = (this.response || []).filter(v => v.indexOf(name) === -1);\n }\n this.response = [...this.response, value];\n }\n this.lastCheckedRadio = checkbox;\n } else {\n this.response = (this.response || []).filter(v => v !== value);\n this.lastCheckedRadio = null;\n }\n\n this.requestUpdate();\n this.dispatchEvent(\n new CustomEvent<ResponseInteraction>('qti-interaction-response', {\n bubbles: true,\n composed: true,\n detail: {\n responseIdentifier: this.responseIdentifier,\n response: Array.isArray(this.response) ? [...this.response] : this.response\n }\n })\n );\n };\n\n validate(): boolean {\n if (this.class.split(' ').includes('qti-match-tabular')) {\n return this.response?.length === this.sourceChoices.length;\n } else {\n return super.validate();\n }\n }\n\n #getMatches(responseVariable: ResponseVariable): { source: string; target: string }[] {\n if (!responseVariable.correctResponse) {\n return [];\n }\n const correctResponse = Array.isArray(responseVariable.correctResponse)\n ? responseVariable.correctResponse\n : [responseVariable.correctResponse];\n\n const matches: { source: string; target: string }[] = [];\n if (correctResponse) {\n correctResponse.forEach(x => {\n const split = x.split(' ');\n matches.push({ source: split[0], target: split[1] });\n });\n }\n return matches;\n }\n\n public override toggleInternalCorrectResponse(show: boolean): void {\n const responseVariable = this.responseVariable;\n\n if (!responseVariable?.correctResponse) {\n // Remove all previously added correct responses\n this.querySelectorAll('.correct-option').forEach(el => el.remove());\n return;\n }\n const matches = this.#getMatches(responseVariable);\n\n if (!this.class.split(' ').includes('qti-match-tabular')) {\n if (show) {\n // Clear old correct options first\n this.querySelectorAll('.correct-option').forEach(el => el.remove());\n\n this.targetChoices.forEach(targetChoice => {\n const targetId = targetChoice.getAttribute('identifier');\n const match = matches.find(m => m.target === targetId);\n\n if (match?.source) {\n const sourceChoice = this.querySelector(`qti-simple-associable-choice[identifier=\"${match.source}\"]`);\n const nodes = Array.from(sourceChoice?.childNodes || []).map(n => n.cloneNode(true));\n\n if (nodes.length > 0 && !targetChoice.previousElementSibling?.classList.contains('correct-option')) {\n const textSpan = document.createElement('span');\n textSpan.classList.add('correct-option');\n nodes.forEach(node => textSpan.appendChild(node));\n\n // Style the span\n textSpan.style.border = '1px solid var(--qti-correct)';\n textSpan.style.borderRadius = '4px';\n textSpan.style.padding = '2px 4px';\n textSpan.style.display = 'inline-block';\n\n // Insert before the target choice\n targetChoice.insertAdjacentElement('beforebegin', textSpan);\n }\n }\n });\n } else {\n this.correctOptions = null;\n }\n } else {\n if (show) {\n this.correctOptions = matches || [];\n } else {\n this.correctOptions = null;\n }\n }\n }\n\n public override toggleCandidateCorrection(show: boolean) {\n const responseVariable = this.responseVariable;\n\n if (!responseVariable?.correctResponse) {\n return;\n }\n const matches = this.#getMatches(responseVariable);\n\n this.targetChoices.forEach(targetChoice => {\n const targetId = targetChoice.getAttribute('identifier');\n const targetMatches = matches.filter(m => m.target === targetId);\n\n const selectedChoices = targetChoice.querySelectorAll(`qti-simple-associable-choice`);\n\n selectedChoices.forEach(selectedChoice => {\n selectedChoice.internals.states.delete('candidate-correct');\n selectedChoice.internals.states.delete('candidate-incorrect');\n\n if (!show) {\n return;\n }\n\n const isCorrect = targetMatches.find(m => m.source === selectedChoice.identifier)?.source !== undefined;\n if (isCorrect) {\n selectedChoice.internals.states.add('candidate-correct');\n } else {\n selectedChoice.internals.states.add('candidate-incorrect');\n }\n });\n });\n }\n\n override render() {\n const isTabular = this.class.split(' ').includes('qti-match-tabular');\n const hasCorrectResponse = this.correctOptions !== null;\n return html`\n <slot name=\"prompt\"></slot>\n <slot ?hidden=${isTabular}></slot>\n\n ${isTabular\n ? html`\n <table part=\"table\">\n <tr part=\"r-header\">\n <td></td>\n ${this.targetChoices.map(\n col => html`<th part=\"r-header\">${Array.from(col.childNodes).map(n => n.cloneNode(true))}</th>`\n )}\n </tr>\n\n ${this.sourceChoices.map(\n row =>\n html`<tr part=\"row\">\n <td part=\"c-header\">${Array.from(row.childNodes).map(n => n.cloneNode(true))}</td>\n ${this.targetChoices.map(col => {\n const rowId = row.getAttribute('identifier');\n const colId = col.getAttribute('identifier');\n const value = `${rowId} ${colId}`;\n const selectedInRowCount =\n (this.response || []).filter(v => v.split(' ')[0] === rowId).length || 0;\n const checked = this.response?.includes(value) || false;\n const type = row.matchMax === 1 ? 'radio' : 'checkbox';\n const isCorrect = !!this.correctOptions?.find(\n option => option.source === rowId && option.target === colId\n );\n const part =\n type === 'radio'\n ? `rb ${checked ? 'rb-checked' : ''} ${hasCorrectResponse ? (isCorrect ? 'rb-correct' : 'rb-incorrect') : ''}`\n : `cb ${checked ? 'cb-checked' : ''} ${hasCorrectResponse ? (isCorrect ? 'cb-correct' : 'cb-incorrect') : ''}`;\n\n // disable if match max is greater than 1 and max is reached\n const disable =\n this.correctOptions?.length > 0\n ? true\n : row.matchMax === 1\n ? false\n : row.matchMax !== 0 && selectedInRowCount >= row.matchMax && !checked;\n return html`<td part=\"input-cell\">\n <div\n class=\"input-container\"\n style=\"position: relative; width: 24px; height: 24px; margin: 0 auto;\"\n >\n <input\n type=${type}\n part=${part}\n name=${rowId}\n value=${value}\n .disabled=${disable}\n @change=${(e: { target: any }) => this.handleRadioChange(e)}\n @click=${(e: { target: HTMLInputElement }) =>\n row.matchMax === 1 ? this.handleRadioClick(e) : null}\n />\n ${type === 'checkbox' && checked\n ? html`\n <svg\n part=\"checkmark\"\n viewBox=\"0 0 24 24\"\n style=\"position: absolute; width: 20px; height: 20px; top: 2px; left: 2px; pointer-events: none;\"\n >\n <path d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z\" fill=\"white\" />\n </svg>\n `\n : ''}\n </div>\n </td>`;\n })}\n </tr>`\n )}\n </table>\n `\n : nothing}\n\n <div role=\"alert\" part=\"message\" id=\"validation-message\"></div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'qti-match-interaction': QtiMatchInteraction;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@qti-components/match-interaction",
3
3
  "description": "QTI match-interaction interaction",
4
- "version": "1.1.3",
4
+ "version": "1.1.5",
5
5
  "author": "",
6
6
  "dependencies": {
7
- "@qti-components/base": "^1.4.2",
8
- "@qti-components/interactions-core": "^1.1.3",
9
- "@qti-components/utilities": "^1.3.2"
7
+ "@qti-components/base": "^1.4.3",
8
+ "@qti-components/interactions-core": "^1.1.4",
9
+ "@qti-components/utilities": "^1.3.3"
10
10
  },
11
11
  "devDependencies": {
12
12
  "@lit/context": "^1.1.6",