@qti-components/base 2.3.0 → 2.4.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.
@@ -10,7 +10,7 @@ import type { ResponseVariable } from '../lib/variables';
10
10
  */
11
11
  export declare abstract class Interaction extends LitElement implements ValidatableInteraction {
12
12
  #private;
13
- private _context;
13
+ private get _context();
14
14
  /**
15
15
  * Delivery configuration, from the nearest provider — `qti-test` or `qti-item`.
16
16
  *
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { consume, provide } from '@lit/context';
7
+ import { ContextConsumer, consume, provide } from '@lit/context';
8
8
  import { LitElement } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { configContext } from '../context/config.context';
@@ -15,6 +15,86 @@ import { itemContext } from '../context/item.context';
15
15
  * `@qti-components/corrections` package.
16
16
  */
17
17
  export class Interaction extends LitElement {
18
+ /**
19
+ * The item's variables. The interaction takes its response from here as well as publishing it:
20
+ * when the item's value for this response changes to something other than what the interaction
21
+ * shows, it adopts it. That is how `item.variables = …` reaches it, with no list of interactions
22
+ * kept by the item. See {@link Interaction.#adoptResponse}.
23
+ */
24
+ #itemContext;
25
+ get _context() {
26
+ return this.#itemContext.value;
27
+ }
28
+ #seenReadonly;
29
+ #seenDisabled;
30
+ /**
31
+ * Follows the item: its `readonly` and `disabled`, and the value it holds for this response.
32
+ * `readonly` and `disabled` are only taken when the item says something, so an interaction
33
+ * authored with either stays so inside an item that does not mention it.
34
+ */
35
+ #followItem(context) {
36
+ if (context?.readonly !== undefined && context.readonly !== this.#seenReadonly) {
37
+ this.#seenReadonly = context.readonly;
38
+ this.readonly = context.readonly;
39
+ }
40
+ if (context?.disabled !== undefined && context.disabled !== this.#seenDisabled) {
41
+ this.#seenDisabled = context.disabled;
42
+ this.disabled = context.disabled;
43
+ }
44
+ this.#adoptResponse(context);
45
+ }
46
+ /** The item's last value for this response, serialised, to tell a change from an unrelated update. */
47
+ #seenResponse;
48
+ /**
49
+ * What this interaction last published or adopted, serialised. It is kept from the interaction's
50
+ * own `qti-interaction-response` events, which every emitter dispatches on the interaction itself,
51
+ * and not read back from `response`: that getter has no common shape (the drag-drop interactions
52
+ * return one comma-joined string where they publish an array), so it cannot be compared.
53
+ */
54
+ #lastResponse;
55
+ #adoptScheduled;
56
+ /**
57
+ * Adopts the item's value for this response when it changed and the interaction does not already
58
+ * show it.
59
+ *
60
+ * Two things keep this from fighting the candidate. It reacts to the item's value for *this*
61
+ * response changing, not to any context update, so a score or a timer ticking never rewrites what
62
+ * is being typed. And when the item's value is what the interaction itself last published (which
63
+ * is what happens right after it publishes an answer) it does nothing, so there is no echo.
64
+ *
65
+ * The first value is only adopted when it says something: an unanswered item must not wipe a
66
+ * `response` attribute the author put on the interaction.
67
+ *
68
+ * Setting `response` on an interaction that has not rendered yet is what the item used to avoid
69
+ * by waiting, so until the first update this waits too, then looks at the latest value.
70
+ */
71
+ #adoptResponse(context) {
72
+ const id = this.responseIdentifier ?? this.getAttribute('response-identifier');
73
+ const variable = context?.variables?.find(v => v.type === 'response' && v.identifier === id);
74
+ if (!variable)
75
+ return;
76
+ if (!this.hasUpdated) {
77
+ if (!this.#adoptScheduled) {
78
+ this.#adoptScheduled = true;
79
+ void this.updateComplete.then(() => {
80
+ this.#adoptScheduled = false;
81
+ this.#adoptResponse(this._context);
82
+ });
83
+ }
84
+ return;
85
+ }
86
+ const first = this.#seenResponse === undefined;
87
+ const serialised = JSON.stringify(variable.value ?? null);
88
+ if (serialised === this.#seenResponse)
89
+ return;
90
+ this.#seenResponse = serialised;
91
+ if (first && (variable.value === null || variable.value === undefined))
92
+ return;
93
+ if (serialised === this.#lastResponse)
94
+ return;
95
+ this.#lastResponse = serialised;
96
+ this.response = variable.value;
97
+ }
18
98
  #didLogDisableAfterIfMaxChoicesReachedDeprecation;
19
99
  static { this.formAssociated = true; }
20
100
  get internals() {
@@ -157,6 +237,18 @@ export class Interaction extends LitElement {
157
237
  }
158
238
  constructor() {
159
239
  super();
240
+ /**
241
+ * The item's variables. The interaction takes its response from here as well as publishing it:
242
+ * when the item's value for this response changes to something other than what the interaction
243
+ * shows, it adopts it. That is how `item.variables = …` reaches it, with no list of interactions
244
+ * kept by the item. See {@link Interaction.#adoptResponse}.
245
+ */
246
+ this.#itemContext = new ContextConsumer(this, {
247
+ context: itemContext,
248
+ subscribe: true,
249
+ callback: context => this.#followItem(context)
250
+ });
251
+ this.#adoptScheduled = false;
160
252
  this.#didLogDisableAfterIfMaxChoicesReachedDeprecation = false;
161
253
  /*
162
254
  * Published to the choice elements inside this interaction — their role, and how to tell
@@ -170,6 +262,9 @@ export class Interaction extends LitElement {
170
262
  this.disabled = false;
171
263
  this.readonly = false;
172
264
  this._internals = this.attachInternals();
265
+ this.addEventListener('qti-interaction-response', (e) => {
266
+ this.#lastResponse = JSON.stringify(e.detail?.response ?? null);
267
+ });
173
268
  }
174
269
  /**
175
270
  * Optional lifecycle bridge for interaction subclasses that do not need changed-property data.
@@ -185,9 +280,6 @@ export class Interaction extends LitElement {
185
280
  */
186
281
  firstUpdated(_changedProperties) { }
187
282
  }
188
- __decorate([
189
- consume({ context: itemContext, subscribe: true })
190
- ], Interaction.prototype, "_context", void 0);
191
283
  __decorate([
192
284
  consume({ context: configContext, subscribe: true })
193
285
  ], Interaction.prototype, "configContext", void 0);
@@ -1 +1 @@
1
- {"version":3,"file":"interaction.js","sourceRoot":"","sources":["../../src/abstract/interaction.ts"],"names":[],"mappings":"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","sourcesContent":["import { consume, provide } from '@lit/context';\nimport { LitElement } from 'lit';\nimport { property } from 'lit/decorators.js';\n\nimport { configContext } from '../context/config.context';\nimport { interactionContext } from '../context/interaction.context';\nimport { itemContext } from '../context/item.context';\n\nimport type { PropertyValues } from 'lit';\nimport type { InteractionContext } from '../context/interaction.context';\nimport type { ConfigContext, ValidationDisplayMode } from '../context/config.context';\nimport type { ItemContext } from '../context/types/item.types';\nimport type { ValidatableInteraction } from '../lib/interaction.interface';\nimport type { ResponseVariable } from '../lib/variables';\n\n/**\n * Shared QTI interaction base. Formative correction behavior is supplied by the\n * `@qti-components/corrections` package.\n */\nexport abstract class Interaction extends LitElement implements ValidatableInteraction {\n @consume({ context: itemContext, subscribe: true })\n private _context: ItemContext;\n\n /**\n * Delivery configuration, from the nearest provider — `qti-test` or `qti-item`.\n *\n * **Public so it can be assigned directly, which is the development-time route.** An interaction\n * has to work standalone, and in a story or a spec there is usually no provider above it — so\n * setting the field is all that is needed:\n *\n * orderInteraction.configContext = { allowReorder: false, validationDisplayMode: 'none' };\n *\n * With no provider, nothing ever overwrites it. That is the whole contract, and its limit: where a\n * provider DOES exist it re-emits on its next update and wins, because `@consume` writes this same\n * field. There is no merging — set the value on the provider in that case.\n *\n * In a lit-html template, bind it as a property rather than assigning after the fact:\n *\n * html`<qti-order-interaction .configContext=${{ allowReorder: false }}>…`\n *\n * The binding is committed on the detached template clone, before the fragment is inserted, so it\n * is in place by `connectedCallback` — which matters for anything that reads config on connect.\n *\n * It was `protected`, which fooled nobody: every caller reached it anyway through an\n * `as any` / `as { configContext?: … }` cast. Public says what was already true and removes the\n * casts, rather than pretending at an encapsulation the type system was the only thing enforcing.\n */\n @consume({ context: configContext, subscribe: true })\n public configContext: ConfigContext;\n\n #didLogDisableAfterIfMaxChoicesReachedDeprecation = false;\n\n /*\n * Published to the choice elements inside this interaction — their role, and how to tell\n * whether they are draggable. Provided here on the base rather than in a mixin so that\n * ChoicesMixin and the drag-drop mixins can each contribute without two providers of the same\n * context competing on one element.\n *\n * Reassign, never mutate: an in-place change does not notify consumers.\n */\n @provide({ context: interactionContext })\n protected _interactionContext: Readonly<InteractionContext> = { choiceRole: null, draggablesSelector: null };\n\n static formAssociated = true;\n protected _internals: ElementInternals;\n\n get internals(): ElementInternals {\n return this._internals;\n }\n\n @property({ type: String, attribute: 'response-identifier' }) responseIdentifier: string;\n\n @property({ reflect: true, type: Boolean }) disabled = false;\n\n @property({ reflect: true, type: Boolean }) readonly = false;\n\n @property({ type: String }) name;\n\n /** Extension point used by wrappers that should not register as candidate inputs. */\n protected get registersWithItem(): boolean {\n return true;\n }\n\n get isInline(): boolean {\n return false;\n }\n\n get responseVariable(): ResponseVariable | undefined {\n if (!this._context?.variables) {\n return undefined;\n }\n const responseVariables = this._context.variables.filter(v => v.type === 'response') as ResponseVariable[];\n const responseIdentifier = this.getAttribute('response-identifier');\n return responseVariables.find(v => v.identifier === responseIdentifier);\n }\n\n abstract validate(): boolean;\n\n get value(): string | null {\n return JSON.stringify(this.response);\n }\n\n set value(val: string | null) {\n this.response = val ? JSON.parse(val) : null;\n }\n\n abstract get response(): string | string[] | null;\n abstract set response(val: string | string[] | null);\n\n public reportValidity(): boolean {\n const isValid = this._internals.validity.valid;\n const mode = this.validationDisplayMode;\n\n if (mode === 'native' || mode === 'both') {\n this._internals.reportValidity();\n }\n\n if (mode === 'inline' || mode === 'both') {\n this.updateInlineValidationMessage();\n } else {\n this.clearInlineValidationMessage();\n }\n\n if (mode === 'none') {\n this.clearInlineValidationMessage();\n }\n\n return isValid;\n }\n\n protected get validationDisplayMode(): ValidationDisplayMode {\n return this.configContext?.validationDisplayMode ?? 'inline';\n }\n\n protected resolveAllowReorder(options?: { defaultWhenUnset?: boolean }): boolean {\n const defaultWhenUnset = options?.defaultWhenUnset ?? true;\n const config = this.configContext;\n if (!config) {\n return defaultWhenUnset;\n }\n if (config.allowReorder !== undefined) {\n return config.allowReorder;\n }\n return defaultWhenUnset;\n }\n\n protected resolveDisableAfterMaxReached(options?: { defaultWhenUnset?: boolean }): boolean {\n const defaultWhenUnset = options?.defaultWhenUnset ?? false;\n const config = this.configContext;\n\n if (!config) {\n return defaultWhenUnset;\n }\n\n if (config.disableAfterMaxReached !== undefined) {\n return config.disableAfterMaxReached;\n }\n\n if (config.disableAfterIfMaxChoicesReached !== undefined) {\n if (!this.#didLogDisableAfterIfMaxChoicesReachedDeprecation) {\n this.#didLogDisableAfterIfMaxChoicesReachedDeprecation = true;\n console.warn(\n '[QTI Config] `disableAfterIfMaxChoicesReached` is deprecated. Use `disableAfterMaxReached` instead.'\n );\n }\n return config.disableAfterIfMaxChoicesReached;\n }\n\n return defaultWhenUnset;\n }\n\n protected setInteractionValidity(\n isValid: boolean,\n validityMessage = '',\n anchor?: HTMLElement | null,\n options?: { suppressInline?: boolean }\n ): void {\n const validityAnchor = anchor ?? this;\n this._internals.setValidity(isValid ? {} : { customError: true }, validityMessage, validityAnchor);\n\n if (\n options?.suppressInline !== true &&\n (this.validationDisplayMode === 'inline' || this.validationDisplayMode === 'both')\n ) {\n this.updateInlineValidationMessage();\n }\n }\n\n protected updateInlineValidationMessage(): void {\n const validationMessageElement = this.getValidationMessageElement();\n if (!validationMessageElement) {\n return;\n }\n\n if (!this._internals.validity.valid) {\n validationMessageElement.textContent = this._internals.validationMessage;\n validationMessageElement.style.setProperty('display', 'block', 'important');\n return;\n }\n\n validationMessageElement.textContent = '';\n validationMessageElement.style.display = 'none';\n }\n\n protected clearInlineValidationMessage(): void {\n const validationMessageElement = this.getValidationMessageElement();\n if (!validationMessageElement) {\n return;\n }\n\n validationMessageElement.textContent = '';\n validationMessageElement.style.display = 'none';\n }\n\n protected getValidationMessageElement(): HTMLElement | null {\n return this.shadowRoot?.querySelector('#validation-message') as HTMLElement | null;\n }\n\n public reset(): void {\n this.response = null;\n }\n\n public formResetCallback(): void {\n this.reset();\n }\n\n public override connectedCallback(): void {\n super.connectedCallback();\n\n if (!this.registersWithItem) {\n return;\n }\n\n this.dispatchEvent(\n new CustomEvent('qti-register-interaction', {\n bubbles: true,\n composed: true,\n cancelable: false,\n detail: {\n interactionElement: this,\n responseIdentifier: this.responseIdentifier\n }\n })\n );\n }\n\n public saveResponse(value: string | string[], state?: string | null): void {\n this.dispatchEvent(\n new CustomEvent('qti-interaction-response', {\n bubbles: true,\n composed: true,\n cancelable: false,\n detail: {\n responseIdentifier: this.responseIdentifier,\n response: Array.isArray(value) ? [...value] : value,\n ...(state !== undefined ? { state } : {})\n }\n })\n );\n }\n\n constructor() {\n super();\n this._internals = this.attachInternals();\n }\n\n /**\n * Optional lifecycle bridge for interaction subclasses that do not need changed-property data.\n *\n * The parameter is `PropertyValues`, matching LitElement, and NOT `PropertyValues<this>`.\n * The polymorphic `this` narrowed the parameter per subclass, which made every interaction\n * class structurally incompatible with `LitElement` — so `Constructor<LitElement>`, the\n * standard constraint for a Lit mixin, rejected all of them and consumers could not wrap an\n * interaction in a mixin without casting. `this` buys nothing in a parameter position and\n * costs contravariant compatibility with the base class.\n *\n * Still optional, so subclasses that ignore the argument can override with `firstUpdated()`.\n */\n protected override firstUpdated(_changedProperties?: PropertyValues): void {}\n}\n"]}
1
+ {"version":3,"file":"interaction.js","sourceRoot":"","sources":["../../src/abstract/interaction.ts"],"names":[],"mappings":"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","sourcesContent":["import { ContextConsumer, consume, provide } from '@lit/context';\nimport { LitElement } from 'lit';\nimport { property } from 'lit/decorators.js';\n\nimport { configContext } from '../context/config.context';\nimport { interactionContext } from '../context/interaction.context';\nimport { itemContext } from '../context/item.context';\n\nimport type { PropertyValues } from 'lit';\nimport type { InteractionContext } from '../context/interaction.context';\nimport type { ConfigContext, ValidationDisplayMode } from '../context/config.context';\nimport type { ItemContext } from '../context/types/item.types';\nimport type { ValidatableInteraction } from '../lib/interaction.interface';\nimport type { ResponseVariable } from '../lib/variables';\n\n/**\n * Shared QTI interaction base. Formative correction behavior is supplied by the\n * `@qti-components/corrections` package.\n */\nexport abstract class Interaction extends LitElement implements ValidatableInteraction {\n /**\n * The item's variables. The interaction takes its response from here as well as publishing it:\n * when the item's value for this response changes to something other than what the interaction\n * shows, it adopts it. That is how `item.variables = …` reaches it, with no list of interactions\n * kept by the item. See {@link Interaction.#adoptResponse}.\n */\n #itemContext = new ContextConsumer(this, {\n context: itemContext,\n subscribe: true,\n callback: context => this.#followItem(context)\n });\n\n private get _context(): ItemContext | undefined {\n return this.#itemContext.value;\n }\n\n #seenReadonly?: boolean;\n #seenDisabled?: boolean;\n\n /**\n * Follows the item: its `readonly` and `disabled`, and the value it holds for this response.\n * `readonly` and `disabled` are only taken when the item says something, so an interaction\n * authored with either stays so inside an item that does not mention it.\n */\n #followItem(context: ItemContext | undefined) {\n if (context?.readonly !== undefined && context.readonly !== this.#seenReadonly) {\n this.#seenReadonly = context.readonly;\n this.readonly = context.readonly;\n }\n if (context?.disabled !== undefined && context.disabled !== this.#seenDisabled) {\n this.#seenDisabled = context.disabled;\n this.disabled = context.disabled;\n }\n this.#adoptResponse(context);\n }\n\n /** The item's last value for this response, serialised, to tell a change from an unrelated update. */\n #seenResponse?: string;\n /**\n * What this interaction last published or adopted, serialised. It is kept from the interaction's\n * own `qti-interaction-response` events, which every emitter dispatches on the interaction itself,\n * and not read back from `response`: that getter has no common shape (the drag-drop interactions\n * return one comma-joined string where they publish an array), so it cannot be compared.\n */\n #lastResponse?: string;\n #adoptScheduled = false;\n\n /**\n * Adopts the item's value for this response when it changed and the interaction does not already\n * show it.\n *\n * Two things keep this from fighting the candidate. It reacts to the item's value for *this*\n * response changing, not to any context update, so a score or a timer ticking never rewrites what\n * is being typed. And when the item's value is what the interaction itself last published (which\n * is what happens right after it publishes an answer) it does nothing, so there is no echo.\n *\n * The first value is only adopted when it says something: an unanswered item must not wipe a\n * `response` attribute the author put on the interaction.\n *\n * Setting `response` on an interaction that has not rendered yet is what the item used to avoid\n * by waiting, so until the first update this waits too, then looks at the latest value.\n */\n #adoptResponse(context: ItemContext | undefined) {\n const id = this.responseIdentifier ?? this.getAttribute('response-identifier');\n const variable = context?.variables?.find(v => v.type === 'response' && v.identifier === id);\n if (!variable) return;\n\n if (!this.hasUpdated) {\n if (!this.#adoptScheduled) {\n this.#adoptScheduled = true;\n void this.updateComplete.then(() => {\n this.#adoptScheduled = false;\n this.#adoptResponse(this._context);\n });\n }\n return;\n }\n\n const first = this.#seenResponse === undefined;\n const serialised = JSON.stringify(variable.value ?? null);\n if (serialised === this.#seenResponse) return;\n this.#seenResponse = serialised;\n if (first && (variable.value === null || variable.value === undefined)) return;\n if (serialised === this.#lastResponse) return;\n this.#lastResponse = serialised;\n this.response = variable.value as string | string[] | null;\n }\n\n /**\n * Delivery configuration, from the nearest provider — `qti-test` or `qti-item`.\n *\n * **Public so it can be assigned directly, which is the development-time route.** An interaction\n * has to work standalone, and in a story or a spec there is usually no provider above it — so\n * setting the field is all that is needed:\n *\n * orderInteraction.configContext = { allowReorder: false, validationDisplayMode: 'none' };\n *\n * With no provider, nothing ever overwrites it. That is the whole contract, and its limit: where a\n * provider DOES exist it re-emits on its next update and wins, because `@consume` writes this same\n * field. There is no merging — set the value on the provider in that case.\n *\n * In a lit-html template, bind it as a property rather than assigning after the fact:\n *\n * html`<qti-order-interaction .configContext=${{ allowReorder: false }}>…`\n *\n * The binding is committed on the detached template clone, before the fragment is inserted, so it\n * is in place by `connectedCallback` — which matters for anything that reads config on connect.\n *\n * It was `protected`, which fooled nobody: every caller reached it anyway through an\n * `as any` / `as { configContext?: … }` cast. Public says what was already true and removes the\n * casts, rather than pretending at an encapsulation the type system was the only thing enforcing.\n */\n @consume({ context: configContext, subscribe: true })\n public configContext: ConfigContext;\n\n #didLogDisableAfterIfMaxChoicesReachedDeprecation = false;\n\n /*\n * Published to the choice elements inside this interaction — their role, and how to tell\n * whether they are draggable. Provided here on the base rather than in a mixin so that\n * ChoicesMixin and the drag-drop mixins can each contribute without two providers of the same\n * context competing on one element.\n *\n * Reassign, never mutate: an in-place change does not notify consumers.\n */\n @provide({ context: interactionContext })\n protected _interactionContext: Readonly<InteractionContext> = { choiceRole: null, draggablesSelector: null };\n\n static formAssociated = true;\n protected _internals: ElementInternals;\n\n get internals(): ElementInternals {\n return this._internals;\n }\n\n @property({ type: String, attribute: 'response-identifier' }) responseIdentifier: string;\n\n @property({ reflect: true, type: Boolean }) disabled = false;\n\n @property({ reflect: true, type: Boolean }) readonly = false;\n\n @property({ type: String }) name;\n\n /** Extension point used by wrappers that should not register as candidate inputs. */\n protected get registersWithItem(): boolean {\n return true;\n }\n\n get isInline(): boolean {\n return false;\n }\n\n get responseVariable(): ResponseVariable | undefined {\n if (!this._context?.variables) {\n return undefined;\n }\n const responseVariables = this._context.variables.filter(v => v.type === 'response') as ResponseVariable[];\n const responseIdentifier = this.getAttribute('response-identifier');\n return responseVariables.find(v => v.identifier === responseIdentifier);\n }\n\n abstract validate(): boolean;\n\n get value(): string | null {\n return JSON.stringify(this.response);\n }\n\n set value(val: string | null) {\n this.response = val ? JSON.parse(val) : null;\n }\n\n abstract get response(): string | string[] | null;\n abstract set response(val: string | string[] | null);\n\n public reportValidity(): boolean {\n const isValid = this._internals.validity.valid;\n const mode = this.validationDisplayMode;\n\n if (mode === 'native' || mode === 'both') {\n this._internals.reportValidity();\n }\n\n if (mode === 'inline' || mode === 'both') {\n this.updateInlineValidationMessage();\n } else {\n this.clearInlineValidationMessage();\n }\n\n if (mode === 'none') {\n this.clearInlineValidationMessage();\n }\n\n return isValid;\n }\n\n protected get validationDisplayMode(): ValidationDisplayMode {\n return this.configContext?.validationDisplayMode ?? 'inline';\n }\n\n protected resolveAllowReorder(options?: { defaultWhenUnset?: boolean }): boolean {\n const defaultWhenUnset = options?.defaultWhenUnset ?? true;\n const config = this.configContext;\n if (!config) {\n return defaultWhenUnset;\n }\n if (config.allowReorder !== undefined) {\n return config.allowReorder;\n }\n return defaultWhenUnset;\n }\n\n protected resolveDisableAfterMaxReached(options?: { defaultWhenUnset?: boolean }): boolean {\n const defaultWhenUnset = options?.defaultWhenUnset ?? false;\n const config = this.configContext;\n\n if (!config) {\n return defaultWhenUnset;\n }\n\n if (config.disableAfterMaxReached !== undefined) {\n return config.disableAfterMaxReached;\n }\n\n if (config.disableAfterIfMaxChoicesReached !== undefined) {\n if (!this.#didLogDisableAfterIfMaxChoicesReachedDeprecation) {\n this.#didLogDisableAfterIfMaxChoicesReachedDeprecation = true;\n console.warn(\n '[QTI Config] `disableAfterIfMaxChoicesReached` is deprecated. Use `disableAfterMaxReached` instead.'\n );\n }\n return config.disableAfterIfMaxChoicesReached;\n }\n\n return defaultWhenUnset;\n }\n\n protected setInteractionValidity(\n isValid: boolean,\n validityMessage = '',\n anchor?: HTMLElement | null,\n options?: { suppressInline?: boolean }\n ): void {\n const validityAnchor = anchor ?? this;\n this._internals.setValidity(isValid ? {} : { customError: true }, validityMessage, validityAnchor);\n\n if (\n options?.suppressInline !== true &&\n (this.validationDisplayMode === 'inline' || this.validationDisplayMode === 'both')\n ) {\n this.updateInlineValidationMessage();\n }\n }\n\n protected updateInlineValidationMessage(): void {\n const validationMessageElement = this.getValidationMessageElement();\n if (!validationMessageElement) {\n return;\n }\n\n if (!this._internals.validity.valid) {\n validationMessageElement.textContent = this._internals.validationMessage;\n validationMessageElement.style.setProperty('display', 'block', 'important');\n return;\n }\n\n validationMessageElement.textContent = '';\n validationMessageElement.style.display = 'none';\n }\n\n protected clearInlineValidationMessage(): void {\n const validationMessageElement = this.getValidationMessageElement();\n if (!validationMessageElement) {\n return;\n }\n\n validationMessageElement.textContent = '';\n validationMessageElement.style.display = 'none';\n }\n\n protected getValidationMessageElement(): HTMLElement | null {\n return this.shadowRoot?.querySelector('#validation-message') as HTMLElement | null;\n }\n\n public reset(): void {\n this.response = null;\n }\n\n public formResetCallback(): void {\n this.reset();\n }\n\n public override connectedCallback(): void {\n super.connectedCallback();\n\n if (!this.registersWithItem) {\n return;\n }\n\n this.dispatchEvent(\n new CustomEvent('qti-register-interaction', {\n bubbles: true,\n composed: true,\n cancelable: false,\n detail: {\n interactionElement: this,\n responseIdentifier: this.responseIdentifier\n }\n })\n );\n }\n\n public saveResponse(value: string | string[], state?: string | null): void {\n this.dispatchEvent(\n new CustomEvent('qti-interaction-response', {\n bubbles: true,\n composed: true,\n cancelable: false,\n detail: {\n responseIdentifier: this.responseIdentifier,\n response: Array.isArray(value) ? [...value] : value,\n ...(state !== undefined ? { state } : {})\n }\n })\n );\n }\n\n constructor() {\n super();\n this._internals = this.attachInternals();\n this.addEventListener('qti-interaction-response', (e: Event) => {\n this.#lastResponse = JSON.stringify((e as CustomEvent).detail?.response ?? null);\n });\n }\n\n /**\n * Optional lifecycle bridge for interaction subclasses that do not need changed-property data.\n *\n * The parameter is `PropertyValues`, matching LitElement, and NOT `PropertyValues<this>`.\n * The polymorphic `this` narrowed the parameter per subclass, which made every interaction\n * class structurally incompatible with `LitElement` — so `Constructor<LitElement>`, the\n * standard constraint for a Lit mixin, rejected all of them and consumers could not wrap an\n * interaction in a mixin without casting. `this` buys nothing in a parameter position and\n * costs contravariant compatibility with the base class.\n *\n * Still optional, so subclasses that ignore the argument can override with `firstUpdated()`.\n */\n protected override firstUpdated(_changedProperties?: PropertyValues): void {}\n}\n"]}
@@ -5,8 +5,7 @@ export declare abstract class QtiFeedback extends LitElement {
5
5
  outcomeIdentifier: string;
6
6
  protected identifier: string;
7
7
  showStatus: string;
8
- private _context?;
8
+ private get _context();
9
9
  private _computedContext?;
10
- connectedCallback(): void;
11
10
  checkShowFeedback(outcomeIdentifier: string): void;
12
11
  }
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { consume } from '@lit/context';
7
+ import { ContextConsumer, consume } from '@lit/context';
8
8
  import { LitElement } from 'lit';
9
9
  import { property, state } from 'lit/decorators.js';
10
10
  import { computedContext } from '../context/computed.context';
@@ -14,14 +14,45 @@ export class QtiFeedback extends LitElement {
14
14
  constructor() {
15
15
  super(...arguments);
16
16
  this.showHide = 'show';
17
+ /**
18
+ * The item's variables. Feedback follows them itself: every time the item publishes a new context
19
+ * this decides, synchronously, whether its own outcome now calls for showing it, so the item has
20
+ * no list of feedback elements to push to. Synchronous matters. A host that sets an outcome
21
+ * reads `showStatus` straight after, with no await in between.
22
+ */
23
+ this.#itemContext = new ContextConsumer(this, {
24
+ context: itemContext,
25
+ subscribe: true,
26
+ callback: context => this.#followItem(context)
27
+ });
17
28
  }
18
- connectedCallback() {
19
- super.connectedCallback();
20
- this.dispatchEvent(new CustomEvent('qti-register-feedback', {
21
- bubbles: true,
22
- composed: true,
23
- detail: this
24
- }));
29
+ /**
30
+ * The item's variables. Feedback follows them itself: every time the item publishes a new context
31
+ * this decides, synchronously, whether its own outcome now calls for showing it, so the item has
32
+ * no list of feedback elements to push to. Synchronous matters. A host that sets an outcome
33
+ * reads `showStatus` straight after, with no await in between.
34
+ */
35
+ #itemContext;
36
+ get _context() {
37
+ return this.#itemContext.value;
38
+ }
39
+ /** What the last context carried that decides this feedback, to tell a relevant update from noise. */
40
+ #seen;
41
+ /**
42
+ * Re-decide when this feedback's outcome changed, or once an attempt has been made. Before the
43
+ * first attempt an unchanged outcome says nothing yet: a `hide` feedback would otherwise switch
44
+ * on for an item nobody has answered.
45
+ */
46
+ #followItem(context) {
47
+ const variables = context?.variables;
48
+ if (!variables || !this.outcomeIdentifier)
49
+ return;
50
+ const outcome = JSON.stringify(variables.find(v => v.identifier === this.outcomeIdentifier)?.value ?? null);
51
+ const numAttempts = Number(variables.find(v => v.identifier === 'numAttempts')?.value) || 0;
52
+ const relevant = this.#seen === undefined ? numAttempts > 0 : this.#seen !== outcome || numAttempts > 0;
53
+ this.#seen = outcome;
54
+ if (relevant)
55
+ this.checkShowFeedback(this.outcomeIdentifier);
25
56
  }
26
57
  checkShowFeedback(outcomeIdentifier) {
27
58
  const outcomeVariable = this._context.variables.find(v => v.identifier === outcomeIdentifier) || null;
@@ -95,10 +126,6 @@ __decorate([
95
126
  __decorate([
96
127
  property({ type: String, attribute: false })
97
128
  ], QtiFeedback.prototype, "showStatus", void 0);
98
- __decorate([
99
- consume({ context: itemContext, subscribe: true }),
100
- state()
101
- ], QtiFeedback.prototype, "_context", void 0);
102
129
  __decorate([
103
130
  consume({ context: computedContext, subscribe: true }),
104
131
  state()
@@ -1 +1 @@
1
- {"version":3,"file":"qti-feedback.js","sourceRoot":"","sources":["../../src/abstract/qti-feedback.ts"],"names":[],"mappings":"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","sourcesContent":["import { consume } from '@lit/context';\nimport { LitElement } from 'lit';\nimport { property, state } from 'lit/decorators.js';\n\nimport { computedContext } from '../context/computed.context';\nimport { itemContext } from '../context/item.context';\nimport { IsNullOrUndefined } from '../utils/utils';\n\nimport type { ComputedContext } from '../context/types/computed.types';\nimport type { ItemContext } from '../context/types/item.types';\n\nexport abstract class QtiFeedback extends LitElement {\n @property({ type: String, attribute: 'show-hide' })\n protected showHide: 'show' | 'hide' = 'show';\n\n @property({ type: String, attribute: 'outcome-identifier' })\n public outcomeIdentifier: string;\n\n @property({ type: String })\n protected identifier: string;\n\n @property({ type: String, attribute: false })\n public showStatus: string;\n\n @consume({ context: itemContext, subscribe: true })\n @state()\n private _context?: ItemContext;\n\n @consume({ context: computedContext, subscribe: true })\n @state()\n private _computedContext?: ComputedContext;\n\n public override connectedCallback() {\n super.connectedCallback();\n this.dispatchEvent(\n new CustomEvent<QtiFeedback>('qti-register-feedback', {\n bubbles: true,\n composed: true,\n detail: this\n })\n );\n }\n\n public checkShowFeedback(outcomeIdentifier: string) {\n const outcomeVariable = this._context.variables.find(v => v.identifier === outcomeIdentifier) || null;\n if (this.outcomeIdentifier !== outcomeIdentifier || !outcomeVariable) return;\n let isFound = false;\n if (Array.isArray(outcomeVariable.value)) {\n isFound = outcomeVariable.value.includes(this.identifier);\n } else {\n isFound =\n (!IsNullOrUndefined(this.identifier) &&\n !IsNullOrUndefined(outcomeVariable?.value) &&\n this.identifier === outcomeVariable.value) ||\n false;\n }\n\n this.#showFeedback(isFound);\n }\n\n #showFeedback(value: boolean) {\n if (!this.#sessionControlAllowsFeedback()) {\n this.showStatus = 'off';\n return;\n }\n this.showStatus = (value && this.showHide === 'show') || (!value && this.showHide === 'hide') ? 'on' : 'off';\n }\n\n /**\n * The QTI `show-feedback` constraint governs exactly one state: after the end of\n * the last attempt. Up to that point the spec requires any applicable feedback to\n * be shown — \"a value of max-attempts greater than 1, by definition, indicates\n * that any applicable feedback must be shown\" — and only \"once the maximum number\n * of allowed attempts have been used (or for adaptive items, completionStatus has\n * been set to completed)\" does `show-feedback` decide.\n *\n * This only ever forces feedback off, so `show-feedback` being true settles it:\n * there is no state in which the constraint hides feedback. It defaults to\n * false, and an absent value suppresses once the attempts are gone.\n *\n * That leaves one question — is the item out of attempts? — and the two item\n * kinds answer it from different variables, so each reads only its own.\n */\n #sessionControlAllowsFeedback(): boolean {\n if (!this._computedContext) return true;\n const activeItem = this._computedContext.testParts\n ?.find(tp => tp.active)\n ?.sections.flatMap(s => s.items)\n .find(i => i.active);\n if (!activeItem || activeItem.showFeedback) return true;\n\n // Adaptive items ignore max-attempts: \"the number of attempts is limited by\n // the value of the completionStatus built-in outcome variable\".\n if (activeItem.adaptive) return activeItem.completionStatus !== 'completed';\n\n // `max-attempts=\"0\"` is \"no limit\", so the last attempt never arrives.\n const maxAttempts = activeItem.maxAttempts ?? 1;\n if (maxAttempts <= 0) return true;\n\n const numAttempts = Number(this._context?.variables?.find(v => v.identifier === 'numAttempts')?.value) || 0;\n return numAttempts < maxAttempts;\n }\n}\n"]}
1
+ {"version":3,"file":"qti-feedback.js","sourceRoot":"","sources":["../../src/abstract/qti-feedback.ts"],"names":[],"mappings":"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","sourcesContent":["import { ContextConsumer, consume } from '@lit/context';\nimport { LitElement } from 'lit';\nimport { property, state } from 'lit/decorators.js';\n\nimport { computedContext } from '../context/computed.context';\nimport { itemContext } from '../context/item.context';\nimport { IsNullOrUndefined } from '../utils/utils';\n\nimport type { ComputedContext } from '../context/types/computed.types';\nimport type { ItemContext } from '../context/types/item.types';\n\nexport abstract class QtiFeedback extends LitElement {\n @property({ type: String, attribute: 'show-hide' })\n protected showHide: 'show' | 'hide' = 'show';\n\n @property({ type: String, attribute: 'outcome-identifier' })\n public outcomeIdentifier: string;\n\n @property({ type: String })\n protected identifier: string;\n\n @property({ type: String, attribute: false })\n public showStatus: string;\n\n /**\n * The item's variables. Feedback follows them itself: every time the item publishes a new context\n * this decides, synchronously, whether its own outcome now calls for showing it, so the item has\n * no list of feedback elements to push to. Synchronous matters. A host that sets an outcome\n * reads `showStatus` straight after, with no await in between.\n */\n #itemContext = new ContextConsumer(this, {\n context: itemContext,\n subscribe: true,\n callback: context => this.#followItem(context)\n });\n\n private get _context(): ItemContext | undefined {\n return this.#itemContext.value;\n }\n\n /** What the last context carried that decides this feedback, to tell a relevant update from noise. */\n #seen?: string;\n\n @consume({ context: computedContext, subscribe: true })\n @state()\n private _computedContext?: ComputedContext;\n\n /**\n * Re-decide when this feedback's outcome changed, or once an attempt has been made. Before the\n * first attempt an unchanged outcome says nothing yet: a `hide` feedback would otherwise switch\n * on for an item nobody has answered.\n */\n #followItem(context: ItemContext | undefined) {\n const variables = context?.variables;\n if (!variables || !this.outcomeIdentifier) return;\n const outcome = JSON.stringify(variables.find(v => v.identifier === this.outcomeIdentifier)?.value ?? null);\n const numAttempts = Number(variables.find(v => v.identifier === 'numAttempts')?.value) || 0;\n const relevant = this.#seen === undefined ? numAttempts > 0 : this.#seen !== outcome || numAttempts > 0;\n this.#seen = outcome;\n if (relevant) this.checkShowFeedback(this.outcomeIdentifier);\n }\n\n public checkShowFeedback(outcomeIdentifier: string) {\n const outcomeVariable = this._context.variables.find(v => v.identifier === outcomeIdentifier) || null;\n if (this.outcomeIdentifier !== outcomeIdentifier || !outcomeVariable) return;\n let isFound = false;\n if (Array.isArray(outcomeVariable.value)) {\n isFound = outcomeVariable.value.includes(this.identifier);\n } else {\n isFound =\n (!IsNullOrUndefined(this.identifier) &&\n !IsNullOrUndefined(outcomeVariable?.value) &&\n this.identifier === outcomeVariable.value) ||\n false;\n }\n\n this.#showFeedback(isFound);\n }\n\n #showFeedback(value: boolean) {\n if (!this.#sessionControlAllowsFeedback()) {\n this.showStatus = 'off';\n return;\n }\n this.showStatus = (value && this.showHide === 'show') || (!value && this.showHide === 'hide') ? 'on' : 'off';\n }\n\n /**\n * The QTI `show-feedback` constraint governs exactly one state: after the end of\n * the last attempt. Up to that point the spec requires any applicable feedback to\n * be shown — \"a value of max-attempts greater than 1, by definition, indicates\n * that any applicable feedback must be shown\" — and only \"once the maximum number\n * of allowed attempts have been used (or for adaptive items, completionStatus has\n * been set to completed)\" does `show-feedback` decide.\n *\n * This only ever forces feedback off, so `show-feedback` being true settles it:\n * there is no state in which the constraint hides feedback. It defaults to\n * false, and an absent value suppresses once the attempts are gone.\n *\n * That leaves one question — is the item out of attempts? — and the two item\n * kinds answer it from different variables, so each reads only its own.\n */\n #sessionControlAllowsFeedback(): boolean {\n if (!this._computedContext) return true;\n const activeItem = this._computedContext.testParts\n ?.find(tp => tp.active)\n ?.sections.flatMap(s => s.items)\n .find(i => i.active);\n if (!activeItem || activeItem.showFeedback) return true;\n\n // Adaptive items ignore max-attempts: \"the number of attempts is limited by\n // the value of the completionStatus built-in outcome variable\".\n if (activeItem.adaptive) return activeItem.completionStatus !== 'completed';\n\n // `max-attempts=\"0\"` is \"no limit\", so the last attempt never arrives.\n const maxAttempts = activeItem.maxAttempts ?? 1;\n if (maxAttempts <= 0) return true;\n\n const numAttempts = Number(this._context?.variables?.find(v => v.identifier === 'numAttempts')?.value) || 0;\n return numAttempts < maxAttempts;\n }\n}\n"]}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Catches context requests nobody answered and asks again when a provider connects, so a component
3
+ * that upgrades before the element providing its context still gets the value.
4
+ *
5
+ * It has to live on the document and not on one of our elements: the case it exists for is the
6
+ * providing element not having upgraded yet, so there is nothing of ours to attach it to. It holds
7
+ * only requests that subscribe, which every `@consume` in this library does, and it holds them
8
+ * weakly, so nothing is kept alive that would not be anyway.
9
+ *
10
+ * Once per page, however many copies of the package the page ends up with.
11
+ */
12
+ export declare const installContextRoot: () => void;
@@ -0,0 +1,24 @@
1
+ import { ContextRoot } from '@lit/context';
2
+ const INSTALLED = Symbol.for('qti-components.context-root');
3
+ /**
4
+ * Catches context requests nobody answered and asks again when a provider connects, so a component
5
+ * that upgrades before the element providing its context still gets the value.
6
+ *
7
+ * It has to live on the document and not on one of our elements: the case it exists for is the
8
+ * providing element not having upgraded yet, so there is nothing of ours to attach it to. It holds
9
+ * only requests that subscribe, which every `@consume` in this library does, and it holds them
10
+ * weakly, so nothing is kept alive that would not be anyway.
11
+ *
12
+ * Once per page, however many copies of the package the page ends up with.
13
+ */
14
+ export const installContextRoot = () => {
15
+ if (typeof document === 'undefined')
16
+ return;
17
+ const page = globalThis;
18
+ if (page[INSTALLED])
19
+ return;
20
+ page[INSTALLED] = new ContextRoot();
21
+ page[INSTALLED].attach(document.documentElement);
22
+ };
23
+ installContextRoot();
24
+ //# sourceMappingURL=context-root.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-root.js","sourceRoot":"","sources":["../../src/context/context-root.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;AAE5D;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAS,EAAE;IAC3C,IAAI,OAAO,QAAQ,KAAK,WAAW;QAAE,OAAO;IAC5C,MAAM,IAAI,GAAG,UAAgE,CAAC;IAC9E,IAAI,IAAI,CAAC,SAAS,CAAC;QAAE,OAAO;IAC5B,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,WAAW,EAAE,CAAC;IACpC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;AACnD,CAAC,CAAC;AAEF,kBAAkB,EAAE,CAAC","sourcesContent":["import { ContextRoot } from '@lit/context';\n\nconst INSTALLED = Symbol.for('qti-components.context-root');\n\n/**\n * Catches context requests nobody answered and asks again when a provider connects, so a component\n * that upgrades before the element providing its context still gets the value.\n *\n * It has to live on the document and not on one of our elements: the case it exists for is the\n * providing element not having upgraded yet, so there is nothing of ours to attach it to. It holds\n * only requests that subscribe, which every `@consume` in this library does, and it holds them\n * weakly, so nothing is kept alive that would not be anyway.\n *\n * Once per page, however many copies of the package the page ends up with.\n */\nexport const installContextRoot = (): void => {\n if (typeof document === 'undefined') return;\n const page = globalThis as unknown as Record<symbol, ContextRoot | undefined>;\n if (page[INSTALLED]) return;\n page[INSTALLED] = new ContextRoot();\n page[INSTALLED].attach(document.documentElement);\n};\n\ninstallContextRoot();\n"]}
@@ -3,7 +3,9 @@ export interface SessionContext {
3
3
  navPartId?: string | null;
4
4
  navSectionId?: string | null;
5
5
  navItemRefId?: string | null;
6
+ /** @deprecated Never set by the library. Removed in the next major. */
6
7
  navItemLoading?: boolean;
8
+ /** @deprecated Never set by the library. Removed in the next major. */
7
9
  navTestLoading?: boolean;
8
10
  view?: View;
9
11
  }
@@ -1,4 +1,4 @@
1
1
  import { createContext } from '@lit/context';
2
2
  export const INITIAL_SESSION_CONTEXT = { view: 'candidate' };
3
- export const sessionContext = createContext(Symbol('testContext'));
3
+ export const sessionContext = createContext(Symbol('sessionContext'));
4
4
  //# sourceMappingURL=session.context.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"session.context.js","sourceRoot":"","sources":["../../src/context/session.context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAa7C,MAAM,CAAC,MAAM,uBAAuB,GAA6B,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;AAEvF,MAAM,CAAC,MAAM,cAAc,GAAG,aAAa,CAA2B,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC","sourcesContent":["import { createContext } from '@lit/context';\n\nexport type View = 'author' | 'candidate' | 'proctor' | 'scorer' | 'testConstructor' | 'tutor' | '';\n\nexport interface SessionContext {\n navPartId?: string | null;\n navSectionId?: string | null;\n navItemRefId?: string | null;\n navItemLoading?: boolean;\n navTestLoading?: boolean;\n view?: View;\n}\n\nexport const INITIAL_SESSION_CONTEXT: Readonly<SessionContext> = { view: 'candidate' };\n\nexport const sessionContext = createContext<Readonly<SessionContext>>(Symbol('testContext'));\n"]}
1
+ {"version":3,"file":"session.context.js","sourceRoot":"","sources":["../../src/context/session.context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAe7C,MAAM,CAAC,MAAM,uBAAuB,GAA6B,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;AAEvF,MAAM,CAAC,MAAM,cAAc,GAAG,aAAa,CAA2B,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC","sourcesContent":["import { createContext } from '@lit/context';\n\nexport type View = 'author' | 'candidate' | 'proctor' | 'scorer' | 'testConstructor' | 'tutor' | '';\n\nexport interface SessionContext {\n navPartId?: string | null;\n navSectionId?: string | null;\n navItemRefId?: string | null;\n /** @deprecated Never set by the library. Removed in the next major. */\n navItemLoading?: boolean;\n /** @deprecated Never set by the library. Removed in the next major. */\n navTestLoading?: boolean;\n view?: View;\n}\n\nexport const INITIAL_SESSION_CONTEXT: Readonly<SessionContext> = { view: 'candidate' };\n\nexport const sessionContext = createContext<Readonly<SessionContext>>(Symbol('sessionContext'));\n"]}
@@ -8,5 +8,15 @@ export interface ItemContext {
8
8
  * Used for interactions that support state save/restore (e.g. PCI).
9
9
  */
10
10
  state?: Record<string, string | null>;
11
+ /** The item is shown for reading only. Interactions follow it; see `qti-assessment-item`'s `readonly`. */
12
+ readonly?: boolean;
13
+ /** The item is locked, for example after it was submitted. Interactions follow it; see `qti-assessment-item`'s `disabled`. */
14
+ disabled?: boolean;
15
+ /**
16
+ * Whether the interactions in the item are valid right now (a required answer is given, a limit is
17
+ * respected). Published by the item whenever it checks, so a reader never has to ask the item.
18
+ * Unset until the item has checked once.
19
+ */
20
+ valid?: boolean;
11
21
  }
12
22
  export declare const itemContextVariables: VariableDeclaration<string | string[]>[];
@@ -1 +1 @@
1
- {"version":3,"file":"item.types.js","sourceRoot":"","sources":["../../../src/context/types/item.types.ts"],"names":[],"mappings":"AAaA,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC;QACE,UAAU,EAAE,kBAAkB;QAC9B,WAAW,EAAE,QAAQ;QACrB,QAAQ,EAAE,QAAQ;QAClB,KAAK,EAAE,SAAS;QAChB,IAAI,EAAE,SAAS;KAChB;IACD;QACE,UAAU,EAAE,aAAa;QACzB,WAAW,EAAE,QAAQ;QACrB,QAAQ,EAAE,SAAS;QACnB,KAAK,EAAE,GAAG;QACV,IAAI,EAAE,UAAU;KACjB;CAC0C,CAAC","sourcesContent":["import type { VariableDeclaration } from '../../lib/variables';\n\nexport interface ItemContext {\n identifier?: string;\n href?: string;\n variables?: ReadonlyArray<VariableDeclaration<string | string[] | null>>;\n /**\n * Optional per-interaction opaque state, keyed by responseIdentifier.\n * Used for interactions that support state save/restore (e.g. PCI).\n */\n state?: Record<string, string | null>;\n}\n\nexport const itemContextVariables = [\n {\n identifier: 'completionStatus',\n cardinality: 'single',\n baseType: 'string',\n value: 'unknown',\n type: 'outcome'\n },\n {\n identifier: 'numAttempts',\n cardinality: 'single',\n baseType: 'integer',\n value: '0',\n type: 'response'\n }\n] as VariableDeclaration<string | string[]>[];\n"]}
1
+ {"version":3,"file":"item.types.js","sourceRoot":"","sources":["../../../src/context/types/item.types.ts"],"names":[],"mappings":"AAuBA,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC;QACE,UAAU,EAAE,kBAAkB;QAC9B,WAAW,EAAE,QAAQ;QACrB,QAAQ,EAAE,QAAQ;QAClB,KAAK,EAAE,SAAS;QAChB,IAAI,EAAE,SAAS;KAChB;IACD;QACE,UAAU,EAAE,aAAa;QACzB,WAAW,EAAE,QAAQ;QACrB,QAAQ,EAAE,SAAS;QACnB,KAAK,EAAE,GAAG;QACV,IAAI,EAAE,UAAU;KACjB;CAC0C,CAAC","sourcesContent":["import type { VariableDeclaration } from '../../lib/variables';\n\nexport interface ItemContext {\n identifier?: string;\n href?: string;\n variables?: ReadonlyArray<VariableDeclaration<string | string[] | null>>;\n /**\n * Optional per-interaction opaque state, keyed by responseIdentifier.\n * Used for interactions that support state save/restore (e.g. PCI).\n */\n state?: Record<string, string | null>;\n /** The item is shown for reading only. Interactions follow it; see `qti-assessment-item`'s `readonly`. */\n readonly?: boolean;\n /** The item is locked, for example after it was submitted. Interactions follow it; see `qti-assessment-item`'s `disabled`. */\n disabled?: boolean;\n /**\n * Whether the interactions in the item are valid right now (a required answer is given, a limit is\n * respected). Published by the item whenever it checks, so a reader never has to ask the item.\n * Unset until the item has checked once.\n */\n valid?: boolean;\n}\n\nexport const itemContextVariables = [\n {\n identifier: 'completionStatus',\n cardinality: 'single',\n baseType: 'string',\n value: 'unknown',\n type: 'outcome'\n },\n {\n identifier: 'numAttempts',\n cardinality: 'single',\n baseType: 'integer',\n value: '0',\n type: 'response'\n }\n] as VariableDeclaration<string | string[]>[];\n"]}
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import './context/context-root';
1
2
  import './register';
2
3
  export * from './context/computed-item.context';
3
4
  export * from './context/types/computed-item.types';
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import './context/context-root';
1
2
  import './register';
2
3
  export * from './context/computed-item.context';
3
4
  export * from './context/types/computed-item.types';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,CAAC;AAEpB,cAAc,iCAAiC,CAAC;AAChD,cAAc,qCAAqC,CAAC;AACpD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0BAA0B,CAAC;AACzC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,wBAAwB,CAAC;AACvC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AAEvC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,oCAAoC,CAAC;AAEnD,cAAc,wBAAwB,CAAC;AACvC,cAAc,qCAAqC,CAAC;AACpD,cAAc,qCAAqC,CAAC;AACpD,cAAc,yBAAyB,CAAC;AACxC,cAAc,2BAA2B,CAAC;AAE1C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AAEvC,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,eAAe,CAAC","sourcesContent":["import './register';\n\nexport * from './context/computed-item.context';\nexport * from './context/types/computed-item.types';\nexport * from './context/computed.context';\nexport * from './context/types/computed.types';\nexport * from './context/config.context';\nexport * from './context/interaction.context';\nexport * from './context/item.context';\nexport * from './context/types/item.types';\nexport * from './context/qti.context';\nexport * from './context/session.context';\nexport * from './context/test.context';\n\nexport * from './styles/box-sizing.styles';\nexport * from './styles/drop-region.styles';\nexport * from './styles/validation-message.styles';\n\nexport * from './abstract/interaction';\nexport * from './abstract/qti-variable-declaration';\nexport * from './abstract/qti-condition-expression';\nexport * from './abstract/qti-feedback';\nexport * from './abstract/qti-expression';\n\nexport * from './lib/response';\nexport * from './lib/expression-result';\nexport * from './lib/interaction.interface';\nexport * from './lib/variables';\nexport * from './lib/qti-rule-base';\nexport * from './lib/custom-operators';\n\nexport * from './utils/custom-state-set';\nexport * from './utils/scoring-helper';\nexport * from './utils/utils';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,wBAAwB,CAAC;AAChC,OAAO,YAAY,CAAC;AAEpB,cAAc,iCAAiC,CAAC;AAChD,cAAc,qCAAqC,CAAC;AACpD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0BAA0B,CAAC;AACzC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,wBAAwB,CAAC;AACvC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AAEvC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,oCAAoC,CAAC;AAEnD,cAAc,wBAAwB,CAAC;AACvC,cAAc,qCAAqC,CAAC;AACpD,cAAc,qCAAqC,CAAC;AACpD,cAAc,yBAAyB,CAAC;AACxC,cAAc,2BAA2B,CAAC;AAE1C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AAEvC,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,eAAe,CAAC","sourcesContent":["import './context/context-root';\nimport './register';\n\nexport * from './context/computed-item.context';\nexport * from './context/types/computed-item.types';\nexport * from './context/computed.context';\nexport * from './context/types/computed.types';\nexport * from './context/config.context';\nexport * from './context/interaction.context';\nexport * from './context/item.context';\nexport * from './context/types/item.types';\nexport * from './context/qti.context';\nexport * from './context/session.context';\nexport * from './context/test.context';\n\nexport * from './styles/box-sizing.styles';\nexport * from './styles/drop-region.styles';\nexport * from './styles/validation-message.styles';\n\nexport * from './abstract/interaction';\nexport * from './abstract/qti-variable-declaration';\nexport * from './abstract/qti-condition-expression';\nexport * from './abstract/qti-feedback';\nexport * from './abstract/qti-expression';\n\nexport * from './lib/response';\nexport * from './lib/expression-result';\nexport * from './lib/interaction.interface';\nexport * from './lib/variables';\nexport * from './lib/qti-rule-base';\nexport * from './lib/custom-operators';\n\nexport * from './utils/custom-state-set';\nexport * from './utils/scoring-helper';\nexport * from './utils/utils';\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@qti-components/base",
3
3
  "description": "QTI shared utilities",
4
- "version": "2.3.0",
4
+ "version": "2.4.0",
5
5
  "author": "",
6
6
  "dependencies": {
7
7
  "@qti-components/utilities": "^1.3.3"