@qti-components/elements 1.10.0-next.1 → 1.10.0-next.3

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.
@@ -8,7 +8,7 @@ import { consume } from '@lit/context';
8
8
  import { css, html } from 'lit';
9
9
  import { property, state } from 'lit/decorators.js';
10
10
  import { itemContext } from '@qti-components/base';
11
- import { QtiVariableDeclaration } from '@qti-components/base';
11
+ import { QtiVariableDeclaration, readRecordFieldValues } from '@qti-components/base';
12
12
  export class QtiResponseDeclaration extends QtiVariableDeclaration {
13
13
  static { this.styles = [
14
14
  css `
@@ -23,11 +23,16 @@ export class QtiResponseDeclaration extends QtiVariableDeclaration {
23
23
  }
24
24
  connectedCallback() {
25
25
  super.connectedCallback();
26
- const defaultValue = this.defaultValues(this.cardinality);
26
+ // A record value is its field map; the variable types only spell out string | string[].
27
+ const defaultValue = this.cardinality === 'record'
28
+ ? this.#recordDefaultValue
29
+ : this.defaultValues(this.cardinality);
30
+ const { correctResponse, fieldBaseTypes } = this.#readCorrectResponse();
27
31
  const responseVariable = {
28
32
  baseType: this.baseType,
29
33
  identifier: this.identifier,
30
- correctResponse: this.#correctResponse,
34
+ correctResponse,
35
+ ...(fieldBaseTypes ? { fieldBaseTypes } : {}),
31
36
  cardinality: this.cardinality || 'single',
32
37
  mapping: this.#mapping,
33
38
  defaultValue,
@@ -43,6 +48,38 @@ export class QtiResponseDeclaration extends QtiVariableDeclaration {
43
48
  detail: { variable: responseVariable }
44
49
  }));
45
50
  }
51
+ /**
52
+ * The correct response, and for `record` cardinality the declared base type of each field.
53
+ *
54
+ * A record's correct response is a field map, `{ time: '4.0', point: '460 245' }`, read from
55
+ * `qti-value field-identifier="…" base-type="…"`. The field base types are kept beside it, since
56
+ * the record declaration itself has no `base-type` to say how a field is to be compared.
57
+ */
58
+ #readCorrectResponse() {
59
+ if (this.cardinality !== 'record') {
60
+ return { correctResponse: this.#correctResponse };
61
+ }
62
+ const correctResponse = this.querySelector('qti-correct-response');
63
+ if (!correctResponse) {
64
+ return { correctResponse: undefined };
65
+ }
66
+ const values = Array.from(correctResponse.querySelectorAll('qti-value'));
67
+ const { value, baseTypes } = readRecordFieldValues(values);
68
+ values.forEach(element => element.remove());
69
+ return {
70
+ // The variable types hold string | string[]; a record is stored as its field map.
71
+ correctResponse: (Object.keys(value).length ? value : undefined),
72
+ fieldBaseTypes: baseTypes
73
+ };
74
+ }
75
+ /** A record's `qti-default-value`, as a field map like its correct response. */
76
+ get #recordDefaultValue() {
77
+ const values = this.querySelectorAll('qti-default-value > qti-value');
78
+ if (values.length === 0)
79
+ return null;
80
+ const { value } = readRecordFieldValues(values);
81
+ return Object.keys(value).length ? value : null;
82
+ }
46
83
  get #correctResponse() {
47
84
  let result;
48
85
  const correctResponse = this.querySelector('qti-correct-response');
@@ -1 +1 @@
1
- {"version":3,"file":"qti-response-declaration.js","sourceRoot":"","sources":["../../../src/components/qti-response-declaration/qti-response-declaration.ts"],"names":[],"mappings":"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","sourcesContent":["import { consume } from '@lit/context';\nimport { css, html } from 'lit';\nimport { property, state } from 'lit/decorators.js';\n\nimport { itemContext } from '@qti-components/base';\nimport { QtiVariableDeclaration } from '@qti-components/base';\n\nimport type { QtiAreaMapping } from '@qti-components/base';\nimport type { BaseType, Cardinality } from '@qti-components/base';\nimport type { ResponseVariable } from '@qti-components/base';\nimport type { QtiMapping } from '@qti-components/base';\nimport type { ItemContext } from '@qti-components/base';\n\nexport class QtiResponseDeclaration extends QtiVariableDeclaration {\n @property({ type: String, attribute: 'base-type' }) baseType: BaseType;\n\n @property({ type: String }) identifier: string;\n\n @property({ type: String }) cardinality: Cardinality;\n\n @consume({ context: itemContext, subscribe: true })\n @state()\n public itemContext?: ItemContext;\n\n static override styles = [\n css`\n :host {\n display: none;\n }\n `\n ];\n\n override render() {\n const value = this.itemContext?.variables.find(v => v.identifier === this.identifier)?.value;\n return html`${JSON.stringify(value, null, 2)}`;\n }\n\n public override connectedCallback() {\n super.connectedCallback();\n const defaultValue = this.defaultValues(this.cardinality);\n\n const responseVariable: ResponseVariable = {\n baseType: this.baseType,\n identifier: this.identifier,\n correctResponse: this.#correctResponse,\n cardinality: this.cardinality || 'single',\n mapping: this.#mapping,\n defaultValue,\n areaMapping: this.#areaMapping,\n value: null,\n type: 'response',\n candidateResponse: null\n };\n responseVariable.value = defaultValue;\n\n this.dispatchEvent(\n new CustomEvent('qti-register-variable', {\n bubbles: true,\n composed: true,\n detail: { variable: responseVariable }\n })\n );\n }\n\n get #correctResponse(): string | string[] {\n let result: string | string[];\n const correctResponse = this.querySelector('qti-correct-response');\n if (correctResponse) {\n const values = correctResponse.querySelectorAll('qti-value');\n if (this.cardinality === 'single' && values.length > 0) {\n result = values[0].textContent.trim();\n values[0].remove();\n } else if (this.cardinality !== 'single') {\n result = [];\n for (let i = 0; i < values.length; i++) {\n result.push(values[i].textContent.trim());\n values[i].remove();\n }\n }\n }\n return result;\n }\n\n get #mapping(): QtiMapping {\n const mappingElement = this.querySelector('qti-mapping');\n const lowerBound = parseFloat(mappingElement?.getAttribute('lower-bound'));\n const uppperBound = parseFloat(mappingElement?.getAttribute('upper-bound'));\n const mappingValue = {\n defaultValue: Number(mappingElement?.getAttribute('default-value')) || 0,\n lowerBound: isNaN(lowerBound) ? null : lowerBound,\n upperBound: isNaN(uppperBound) ? null : uppperBound,\n mapEntries: Array.from(mappingElement?.querySelectorAll('qti-map-entry') || []).map(el => ({\n mapKey: el.getAttribute('map-key') || '',\n mappedValue: Number(el.getAttribute('mapped-value')) || 0,\n caseSensitive: el.hasAttribute('case-sensitive') ? el.getAttribute('case-sensitive') !== 'false' : false\n }))\n };\n return mappingValue;\n }\n\n get #areaMapping(): QtiAreaMapping {\n const areaMappingElement = this.querySelector('qti-area-mapping') as HTMLElement;\n\n const defaultValue = Number(areaMappingElement?.getAttribute('default-value')) || 0;\n const lowerBound = parseFloat(areaMappingElement?.getAttribute('lower-bound'));\n const uppperBound = parseFloat(areaMappingElement?.getAttribute('upper-bound'));\n\n const areaMapEntries = Array.from(areaMappingElement?.querySelectorAll('qti-area-map-entry') || []).map(\n (el: HTMLElement) => ({\n shape: (el.getAttribute('shape') as 'default' | 'circle' | 'rect' | 'ellipse' | 'poly') || 'default',\n coords: el.getAttribute('coords') || '',\n mappedValue: Number(el.getAttribute('mapped-value')) || 0,\n defaultValue: Number(el.getAttribute('default-value')) || 0\n })\n );\n\n return {\n defaultValue,\n lowerBound: isNaN(lowerBound) ? null : lowerBound,\n upperBound: isNaN(uppperBound) ? null : uppperBound,\n areaMapEntries\n };\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'qti-response-declaration': QtiResponseDeclaration;\n }\n}\n"]}
1
+ {"version":3,"file":"qti-response-declaration.js","sourceRoot":"","sources":["../../../src/components/qti-response-declaration/qti-response-declaration.ts"],"names":[],"mappings":"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","sourcesContent":["import { consume } from '@lit/context';\nimport { css, html } from 'lit';\nimport { property, state } from 'lit/decorators.js';\n\nimport { itemContext } from '@qti-components/base';\nimport { QtiVariableDeclaration, readRecordFieldValues } from '@qti-components/base';\n\nimport type { QtiAreaMapping } from '@qti-components/base';\nimport type { BaseType, Cardinality } from '@qti-components/base';\nimport type { RecordValue, ResponseVariable } from '@qti-components/base';\nimport type { QtiMapping } from '@qti-components/base';\nimport type { ItemContext } from '@qti-components/base';\n\nexport class QtiResponseDeclaration extends QtiVariableDeclaration {\n @property({ type: String, attribute: 'base-type' }) baseType: BaseType;\n\n @property({ type: String }) identifier: string;\n\n @property({ type: String }) cardinality: Cardinality;\n\n @consume({ context: itemContext, subscribe: true })\n @state()\n public itemContext?: ItemContext;\n\n static override styles = [\n css`\n :host {\n display: none;\n }\n `\n ];\n\n override render() {\n const value = this.itemContext?.variables.find(v => v.identifier === this.identifier)?.value;\n return html`${JSON.stringify(value, null, 2)}`;\n }\n\n public override connectedCallback() {\n super.connectedCallback();\n // A record value is its field map; the variable types only spell out string | string[].\n const defaultValue =\n this.cardinality === 'record'\n ? (this.#recordDefaultValue as unknown as string | string[] | null)\n : this.defaultValues(this.cardinality);\n const { correctResponse, fieldBaseTypes } = this.#readCorrectResponse();\n\n const responseVariable: ResponseVariable = {\n baseType: this.baseType,\n identifier: this.identifier,\n correctResponse,\n ...(fieldBaseTypes ? { fieldBaseTypes } : {}),\n cardinality: this.cardinality || 'single',\n mapping: this.#mapping,\n defaultValue,\n areaMapping: this.#areaMapping,\n value: null,\n type: 'response',\n candidateResponse: null\n };\n responseVariable.value = defaultValue;\n\n this.dispatchEvent(\n new CustomEvent('qti-register-variable', {\n bubbles: true,\n composed: true,\n detail: { variable: responseVariable }\n })\n );\n }\n\n /**\n * The correct response, and for `record` cardinality the declared base type of each field.\n *\n * A record's correct response is a field map, `{ time: '4.0', point: '460 245' }`, read from\n * `qti-value field-identifier=\"…\" base-type=\"…\"`. The field base types are kept beside it, since\n * the record declaration itself has no `base-type` to say how a field is to be compared.\n */\n #readCorrectResponse(): { correctResponse: string | string[]; fieldBaseTypes?: ResponseVariable['fieldBaseTypes'] } {\n if (this.cardinality !== 'record') {\n return { correctResponse: this.#correctResponse };\n }\n const correctResponse = this.querySelector('qti-correct-response');\n if (!correctResponse) {\n return { correctResponse: undefined };\n }\n const values = Array.from(correctResponse.querySelectorAll('qti-value'));\n const { value, baseTypes } = readRecordFieldValues(values);\n values.forEach(element => element.remove());\n return {\n // The variable types hold string | string[]; a record is stored as its field map.\n correctResponse: (Object.keys(value).length ? value : undefined) as unknown as string | string[],\n fieldBaseTypes: baseTypes\n };\n }\n\n /** A record's `qti-default-value`, as a field map like its correct response. */\n get #recordDefaultValue(): RecordValue | null {\n const values = this.querySelectorAll('qti-default-value > qti-value');\n if (values.length === 0) return null;\n const { value } = readRecordFieldValues(values);\n return Object.keys(value).length ? value : null;\n }\n\n get #correctResponse(): string | string[] {\n let result: string | string[];\n const correctResponse = this.querySelector('qti-correct-response');\n if (correctResponse) {\n const values = correctResponse.querySelectorAll('qti-value');\n if (this.cardinality === 'single' && values.length > 0) {\n result = values[0].textContent.trim();\n values[0].remove();\n } else if (this.cardinality !== 'single') {\n result = [];\n for (let i = 0; i < values.length; i++) {\n result.push(values[i].textContent.trim());\n values[i].remove();\n }\n }\n }\n return result;\n }\n\n get #mapping(): QtiMapping {\n const mappingElement = this.querySelector('qti-mapping');\n const lowerBound = parseFloat(mappingElement?.getAttribute('lower-bound'));\n const uppperBound = parseFloat(mappingElement?.getAttribute('upper-bound'));\n const mappingValue = {\n defaultValue: Number(mappingElement?.getAttribute('default-value')) || 0,\n lowerBound: isNaN(lowerBound) ? null : lowerBound,\n upperBound: isNaN(uppperBound) ? null : uppperBound,\n mapEntries: Array.from(mappingElement?.querySelectorAll('qti-map-entry') || []).map(el => ({\n mapKey: el.getAttribute('map-key') || '',\n mappedValue: Number(el.getAttribute('mapped-value')) || 0,\n caseSensitive: el.hasAttribute('case-sensitive') ? el.getAttribute('case-sensitive') !== 'false' : false\n }))\n };\n return mappingValue;\n }\n\n get #areaMapping(): QtiAreaMapping {\n const areaMappingElement = this.querySelector('qti-area-mapping') as HTMLElement;\n\n const defaultValue = Number(areaMappingElement?.getAttribute('default-value')) || 0;\n const lowerBound = parseFloat(areaMappingElement?.getAttribute('lower-bound'));\n const uppperBound = parseFloat(areaMappingElement?.getAttribute('upper-bound'));\n\n const areaMapEntries = Array.from(areaMappingElement?.querySelectorAll('qti-area-map-entry') || []).map(\n (el: HTMLElement) => ({\n shape: (el.getAttribute('shape') as 'default' | 'circle' | 'rect' | 'ellipse' | 'poly') || 'default',\n coords: el.getAttribute('coords') || '',\n mappedValue: Number(el.getAttribute('mapped-value')) || 0,\n defaultValue: Number(el.getAttribute('default-value')) || 0\n })\n );\n\n return {\n defaultValue,\n lowerBound: isNaN(lowerBound) ? null : lowerBound,\n upperBound: isNaN(uppperBound) ? null : uppperBound,\n areaMapEntries\n };\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'qti-response-declaration': QtiResponseDeclaration;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@qti-components/elements",
3
3
  "description": "QTI elements utilities",
4
- "version": "1.10.0-next.1",
4
+ "version": "1.10.0-next.3",
5
5
  "author": "",
6
6
  "dependencies": {
7
- "@qti-components/base": "^2.4.1-next.0",
7
+ "@qti-components/base": "^2.5.0-next.2",
8
8
  "@qti-components/transformers": "^1.7.3",
9
9
  "@qti-components/utilities": "^1.3.3"
10
10
  },