@qti-components/portable-custom-interaction 1.0.1

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,CAAC;AAEpB,cAAc,mCAAmC,CAAC","sourcesContent":["import './register';\n\nexport * from './qti-portable-custom-interaction';\n"]}
@@ -0,0 +1,202 @@
1
+ /**
2
+ * Shared status values for PCI lifecycle callbacks.
3
+ *
4
+ * Matches the status vocabulary used in IMS QTI 3 PCI documentation.
5
+ */
6
+ export type PciInteractionStatus = 'interacting' | 'suspended' | 'closed' | 'solution' | 'review';
7
+ /**
8
+ * Payload passed to `onready` once a PCI reports it is ready.
9
+ */
10
+ export interface PciReadyPayload {
11
+ responseIdentifier: string;
12
+ status: PciInteractionStatus;
13
+ }
14
+ /**
15
+ * Payload that may be passed to `ondone` when PCI finishes candidate interaction.
16
+ */
17
+ export interface PciDonePayload {
18
+ responseIdentifier: string;
19
+ status: PciInteractionStatus;
20
+ outcome?: unknown;
21
+ }
22
+ /**
23
+ * Configuration object shape exposed to a PCI instance.
24
+ *
25
+ * Notes for authors:
26
+ * - `properties` contains `data-*` attributes from the host element using dataset rules.
27
+ * - `templateVariables`, `contextVariables`, and `boundTo` follow QTI Appendix-C style JSON values.
28
+ * - `onready` must be called by the PCI once it is interactive.
29
+ * - `ondone` is optional and may be used for explicit completion workflows.
30
+ */
31
+ export interface ConfigProperties<T> {
32
+ properties: T;
33
+ templateVariables: Record<string, unknown>;
34
+ contextVariables: Record<string, unknown>;
35
+ boundTo: unknown;
36
+ responseIdentifier: string;
37
+ onready: (payload: PciReadyPayload) => void;
38
+ ondone?: (payload: PciDonePayload) => void;
39
+ status?: PciInteractionStatus;
40
+ }
41
+ /**
42
+ * IMS-style PCI factory and instance contract.
43
+ *
44
+ * A PCI module registers an object with a `typeIdentifier` and a `getInstance(...)` factory method.
45
+ * The returned PCI instance should implement response/state read/write methods.
46
+ */
47
+ export interface IMSpci<T> {
48
+ /**
49
+ * Stable PCI type key, expected to match `custom-interaction-type-identifier`.
50
+ */
51
+ typeIdentifier: string;
52
+ /** @access public
53
+ * @method getInstance Create a new instance of this portable custom interaction
54
+ * Will be called by the qtiCustomInteractionContext
55
+ * @param {DOM Element} dom - the DOM Element this PCI is being added to
56
+ * @param {Object} configuration - the configuration to apply to this PCI
57
+ * @param {String} state - a previous saved state to apply to this instance.
58
+ * This must have been obtained from a prior call to getState on an
59
+ * instance of this type (same typeIdentifier)
60
+ */
61
+ getInstance: (dom: HTMLElement, configuration: Configuration<T>, state: string) => void;
62
+ /** @access public
63
+ * @method getResponse
64
+ * @return {Object} - the value to assign to the bound QTI response variable
65
+ */
66
+ getResponse: () => QtiVariableJSON | undefined;
67
+ /** @access public
68
+ * @method setResponse
69
+ * @param {Object} value - the value to assign to the bound QTI response variable
70
+ * NOTE: This is not an official QTI method, so no guarantee it is implemented
71
+ * in all PCIs.
72
+ */
73
+ setResponse: (value: QtiVariableJSON) => void;
74
+ /** @access public
75
+ * @method getState
76
+ * @return {String} The current state of this PCI. May be passed to
77
+ * getInstance to later restore this PCI instance.
78
+ * FIXME: host runtime can round-trip object states via prefixed JSON strings;
79
+ * this type remains `string` for IMS compatibility.
80
+ */
81
+ getState: () => string;
82
+ /** @access public
83
+ * @method setState
84
+ * @param {String} value - a state previously obtained from getState
85
+ * to restore this PCI instance to a prior state.
86
+ * FIXME: runtime may deserialize prefixed JSON state before applying it.
87
+ */
88
+ setState(value: string): unknown;
89
+ /**
90
+ * Optional teardown callback. If present, host may call before unload/cleanup.
91
+ */
92
+ oncompleted?: () => void;
93
+ /**
94
+ * Non-standard optional destroy hook; included for compatibility with legacy engines.
95
+ */
96
+ destroy?: () => void;
97
+ }
98
+ /**
99
+ * Bridge callback signature used by this codebase for PCI registration.
100
+ *
101
+ * IMPORTANT:
102
+ * - This type is intentionally minimal for existing call sites.
103
+ * - Runtime initialization payload in `qti-portable-custom-interaction.ts` contains
104
+ * additional fields (`boundTo`, `templateVariables`, `contextVariables`, etc.).
105
+ * FIXME: align this type with runtime payload to remove ambiguity in generated docs.
106
+ */
107
+ export declare type Configuration<T> = {
108
+ onready: (pci: IMSpci<ConfigProperties<T>>, state?: string) => void;
109
+ properties: T;
110
+ };
111
+ /**
112
+ * QTI base-type `file` representation.
113
+ */
114
+ export type QtiFileData = {
115
+ data: string;
116
+ mime: string;
117
+ name?: string;
118
+ };
119
+ export type QtiBaseBoolean = boolean;
120
+ export type QtiBaseInteger = number;
121
+ export type QtiBaseFloat = number;
122
+ export type QtiBaseString = string;
123
+ export type QtiBasePoint = [number, number];
124
+ export type QtiBasePair = [string, string];
125
+ export type QtiBaseDirectedPair = [string, string];
126
+ export type QtiBaseDuration = string;
127
+ export type QtiBaseFile = QtiFileData;
128
+ export type QtiBaseUri = string;
129
+ export type QtiBaseIntOrIdentifier = number | string;
130
+ export type QtiBaseIdentifier = string;
131
+ /**
132
+ * Convenience union for primitive response values used by this implementation.
133
+ */
134
+ export type ResponseType = boolean | number | string | QtiBasePair | QtiBasePoint | QtiFileData | Array<boolean | number | string | QtiBasePair | QtiBasePoint | QtiFileData>;
135
+ /**
136
+ * A QTI record entry for `record` cardinality values.
137
+ */
138
+ export type QtiRecordItem = {
139
+ name: string;
140
+ base?: QtiBaseTypeJSON | null;
141
+ list?: QtiListTypeJSON | null;
142
+ };
143
+ export type ResponseVariableType = QtiVariableJSON;
144
+ /**
145
+ * QTI Appendix-C style JSON representation for base cardinality.
146
+ */
147
+ export type QtiBaseTypeJSON = {
148
+ boolean?: QtiBaseBoolean;
149
+ integer?: QtiBaseInteger;
150
+ float?: QtiBaseFloat;
151
+ string?: QtiBaseString;
152
+ point?: QtiBasePoint;
153
+ pair?: QtiBasePair;
154
+ directedPair?: QtiBaseDirectedPair;
155
+ duration?: QtiBaseDuration;
156
+ file?: QtiBaseFile;
157
+ uri?: QtiBaseUri;
158
+ intOrIdentifier?: QtiBaseIntOrIdentifier;
159
+ identifier?: QtiBaseIdentifier;
160
+ } | null;
161
+ /**
162
+ * QTI Appendix-C style JSON representation for list cardinality.
163
+ */
164
+ export type QtiListTypeJSON = {
165
+ boolean?: QtiBaseBoolean[];
166
+ integer?: QtiBaseInteger[];
167
+ float?: QtiBaseFloat[];
168
+ string?: QtiBaseString[];
169
+ point?: QtiBasePoint[];
170
+ pair?: QtiBasePair[];
171
+ directedPair?: QtiBaseDirectedPair[];
172
+ duration?: QtiBaseDuration[];
173
+ file?: QtiBaseFile[];
174
+ uri?: QtiBaseUri[];
175
+ intOrIdentifier?: QtiBaseIntOrIdentifier[];
176
+ identifier?: QtiBaseIdentifier[];
177
+ } | null;
178
+ /**
179
+ * Complete QTI variable JSON envelope used by PCI bridge messaging.
180
+ */
181
+ export declare type QtiVariableJSON = {
182
+ base?: QtiBaseTypeJSON;
183
+ list?: QtiListTypeJSON;
184
+ record?: QtiRecordItem[] | null;
185
+ };
186
+ /**
187
+ * RequireJS module resolution config accepted by helper utilities and stories.
188
+ */
189
+ export interface ModuleResolutionConfig {
190
+ waitSeconds?: number;
191
+ context?: string;
192
+ catchError?: boolean;
193
+ paths: {
194
+ [key: string]: string | string[];
195
+ };
196
+ shim?: {
197
+ [key: string]: {
198
+ deps?: string[];
199
+ exports?: string;
200
+ };
201
+ };
202
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=interface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interface.js","sourceRoot":"","sources":["../src/interface.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Shared status values for PCI lifecycle callbacks.\n *\n * Matches the status vocabulary used in IMS QTI 3 PCI documentation.\n */\nexport type PciInteractionStatus = 'interacting' | 'suspended' | 'closed' | 'solution' | 'review';\n\n/**\n * Payload passed to `onready` once a PCI reports it is ready.\n */\nexport interface PciReadyPayload {\n responseIdentifier: string; // The response identifier of the PCI\n status: PciInteractionStatus; // Current status\n}\n\n/**\n * Payload that may be passed to `ondone` when PCI finishes candidate interaction.\n */\nexport interface PciDonePayload {\n responseIdentifier: string; // The response identifier of the PCI\n status: PciInteractionStatus; // The final status after completion\n outcome?: unknown; // Optional outcome data (e.g., score, correctness)\n}\n\n/**\n * Configuration object shape exposed to a PCI instance.\n *\n * Notes for authors:\n * - `properties` contains `data-*` attributes from the host element using dataset rules.\n * - `templateVariables`, `contextVariables`, and `boundTo` follow QTI Appendix-C style JSON values.\n * - `onready` must be called by the PCI once it is interactive.\n * - `ondone` is optional and may be used for explicit completion workflows.\n */\nexport interface ConfigProperties<T> {\n properties: T; // Follows dataset conversion rules (camelCased keys)\n templateVariables: Record<string, unknown>; // Follows structure in Appendix C\n contextVariables: Record<string, unknown>; // Follows structure in Appendix C\n boundTo: unknown; // Follows structure in Appendix C\n responseIdentifier: string; // Unique within interaction scope\n\n onready: (payload: PciReadyPayload) => void; // Callback for when PCI is fully constructed and ready\n ondone?: (payload: PciDonePayload) => void; // Optional callback when candidate finishes interaction\n\n status?: PciInteractionStatus; // Optional, defaults to \"interacting\"\n}\n\n/**\n * IMS-style PCI factory and instance contract.\n *\n * A PCI module registers an object with a `typeIdentifier` and a `getInstance(...)` factory method.\n * The returned PCI instance should implement response/state read/write methods.\n */\nexport interface IMSpci<T> {\n /**\n * Stable PCI type key, expected to match `custom-interaction-type-identifier`.\n */\n typeIdentifier: string;\n\n /** @access public\n * @method getInstance Create a new instance of this portable custom interaction\n * Will be called by the qtiCustomInteractionContext\n * @param {DOM Element} dom - the DOM Element this PCI is being added to\n * @param {Object} configuration - the configuration to apply to this PCI\n * @param {String} state - a previous saved state to apply to this instance.\n * This must have been obtained from a prior call to getState on an\n * instance of this type (same typeIdentifier)\n */\n getInstance: (dom: HTMLElement, configuration: Configuration<T>, state: string) => void;\n\n /** @access public\n * @method getResponse\n * @return {Object} - the value to assign to the bound QTI response variable\n */\n getResponse: () => QtiVariableJSON | undefined;\n\n /** @access public\n * @method setResponse\n * @param {Object} value - the value to assign to the bound QTI response variable\n * NOTE: This is not an official QTI method, so no guarantee it is implemented\n * in all PCIs.\n */\n setResponse: (value: QtiVariableJSON) => void;\n\n /** @access public\n * @method getState\n * @return {String} The current state of this PCI. May be passed to\n * getInstance to later restore this PCI instance.\n * FIXME: host runtime can round-trip object states via prefixed JSON strings;\n * this type remains `string` for IMS compatibility.\n */\n getState: () => string;\n\n /** @access public\n * @method setState\n * @param {String} value - a state previously obtained from getState\n * to restore this PCI instance to a prior state.\n * FIXME: runtime may deserialize prefixed JSON state before applying it.\n */\n setState(value: string): unknown;\n\n /**\n * Optional teardown callback. If present, host may call before unload/cleanup.\n */\n oncompleted?: () => void;\n\n /**\n * Non-standard optional destroy hook; included for compatibility with legacy engines.\n */\n destroy?: () => void; // Not used in IMS and not in TAO implementation, so not used here (optional)\n}\n\n/**\n * Bridge callback signature used by this codebase for PCI registration.\n *\n * IMPORTANT:\n * - This type is intentionally minimal for existing call sites.\n * - Runtime initialization payload in `qti-portable-custom-interaction.ts` contains\n * additional fields (`boundTo`, `templateVariables`, `contextVariables`, etc.).\n * FIXME: align this type with runtime payload to remove ambiguity in generated docs.\n */\nexport declare type Configuration<T> = {\n onready: (pci: IMSpci<ConfigProperties<T>>, state?: string) => void;\n properties: T;\n // PK: following this: https://www.imsglobal.org/spec/qti/v3p0/impl#h.1mc9puik2ft6\n // All below are not used in the IMS Reference implementation, keeping for ref\n /* \n ondone: (pci: IMSPci<T>,response:any,state:string, status?: \"interacting\" | \"closed\" | \"solution\" | \"review\" ) => void;\n status: string;\n templateVariables: any; \n boundTo: any;\n contextVariables\n responseIdentifier\n */\n};\n\n/**\n * QTI base-type `file` representation.\n */\nexport type QtiFileData = {\n data: string;\n mime: string;\n name?: string;\n};\n\nexport type QtiBaseBoolean = boolean;\nexport type QtiBaseInteger = number;\nexport type QtiBaseFloat = number;\nexport type QtiBaseString = string;\nexport type QtiBasePoint = [number, number];\nexport type QtiBasePair = [string, string];\nexport type QtiBaseDirectedPair = [string, string];\nexport type QtiBaseDuration = string; // ISO 8601 duration format\nexport type QtiBaseFile = QtiFileData;\nexport type QtiBaseUri = string;\nexport type QtiBaseIntOrIdentifier = number | string;\nexport type QtiBaseIdentifier = string;\n\n/**\n * Convenience union for primitive response values used by this implementation.\n */\nexport type ResponseType =\n | boolean\n | number\n | string\n | QtiBasePair\n | QtiBasePoint\n | QtiFileData\n | Array<boolean | number | string | QtiBasePair | QtiBasePoint | QtiFileData>;\n\n/**\n * A QTI record entry for `record` cardinality values.\n */\nexport type QtiRecordItem = {\n name: string;\n base?: QtiBaseTypeJSON | null;\n list?: QtiListTypeJSON | null;\n};\nexport type ResponseVariableType = QtiVariableJSON;\n\n/**\n * QTI Appendix-C style JSON representation for base cardinality.\n */\nexport type QtiBaseTypeJSON = {\n boolean?: QtiBaseBoolean;\n integer?: QtiBaseInteger;\n float?: QtiBaseFloat;\n string?: QtiBaseString;\n point?: QtiBasePoint;\n pair?: QtiBasePair;\n directedPair?: QtiBaseDirectedPair;\n duration?: QtiBaseDuration;\n file?: QtiBaseFile;\n uri?: QtiBaseUri;\n intOrIdentifier?: QtiBaseIntOrIdentifier;\n identifier?: QtiBaseIdentifier;\n} | null;\n\n/**\n * QTI Appendix-C style JSON representation for list cardinality.\n */\nexport type QtiListTypeJSON = {\n boolean?: QtiBaseBoolean[];\n integer?: QtiBaseInteger[];\n float?: QtiBaseFloat[];\n string?: QtiBaseString[];\n point?: QtiBasePoint[];\n pair?: QtiBasePair[];\n directedPair?: QtiBaseDirectedPair[];\n duration?: QtiBaseDuration[];\n file?: QtiBaseFile[];\n uri?: QtiBaseUri[];\n intOrIdentifier?: QtiBaseIntOrIdentifier[];\n identifier?: QtiBaseIdentifier[];\n} | null;\n\n/**\n * Complete QTI variable JSON envelope used by PCI bridge messaging.\n */\nexport declare type QtiVariableJSON = {\n base?: QtiBaseTypeJSON;\n list?: QtiListTypeJSON;\n record?: QtiRecordItem[] | null;\n};\n\n/**\n * RequireJS module resolution config accepted by helper utilities and stories.\n */\nexport interface ModuleResolutionConfig {\n waitSeconds?: number;\n context?: string;\n catchError?: boolean;\n paths: {\n [key: string]: string | string[];\n };\n shim?: {\n [key: string]: {\n deps?: string[]; // Array of dependencies\n exports?: string; // The global variable to use as the module's value\n };\n };\n}\n"]}
@@ -0,0 +1,115 @@
1
+ import { Interaction } from '@qti-components/base';
2
+ import type { CSSResultGroup } from 'lit';
3
+ import type { BaseType, Cardinality, ItemContext, QtiContext } from '@qti-components/base';
4
+ import type { QtiVariableJSON, ResponseVariableType } from './interface';
5
+ export declare class QtiPortableCustomInteraction extends Interaction {
6
+ #private;
7
+ protected _iframeLoaded: boolean;
8
+ protected _pendingMessages: Array<{
9
+ method: string;
10
+ params: any;
11
+ }>;
12
+ protected iframe: HTMLIFrameElement;
13
+ protected _iframeMessageOrigin: string | null;
14
+ private _iframeObjectUrl;
15
+ static styles: CSSResultGroup;
16
+ module: string;
17
+ customInteractionTypeIdentifier: string;
18
+ requirePathsJson: string;
19
+ requireShimJson: string;
20
+ requireJsUrl: string;
21
+ baseUrl: string;
22
+ useDefaultPaths: boolean;
23
+ private _errorMessage;
24
+ protected context?: ItemContext;
25
+ protected qtiContext?: QtiContext;
26
+ response: string | string[] | null;
27
+ /**
28
+ * Get direct child elements by tag name
29
+ */
30
+ private getDirectChildrenByTag;
31
+ /**
32
+ * Coerce a scalar value to the expected base-type when possible
33
+ */
34
+ private coerceBaseValue;
35
+ /**
36
+ * Normalize an input into a list form
37
+ */
38
+ private normalizeListValue;
39
+ /**
40
+ * Convert a variable value to QtiVariableJSON
41
+ */
42
+ private variableToQtiVariableJSON;
43
+ /**
44
+ * Convert a record object into QtiVariableJSON
45
+ */
46
+ private recordToQtiVariableJSON;
47
+ /**
48
+ * Build a record entry from an arbitrary value
49
+ */
50
+ private recordEntryFromValue;
51
+ /**
52
+ * Extract default QTI_CONTEXT values from qti-context-declaration elements
53
+ */
54
+ private getQtiContextDefaultsFromItem;
55
+ /**
56
+ * Build a QTI_CONTEXT record for PCI consumption
57
+ */
58
+ private getQtiContextRecord;
59
+ /**
60
+ * Collect template variables referenced by qti-template-variable children
61
+ */
62
+ private getTemplateVariables;
63
+ /**
64
+ * Collect context variables referenced by qti-context-variable children
65
+ */
66
+ private getContextVariables;
67
+ /**
68
+ * Converts response variables to QtiVariableJSON
69
+ */
70
+ protected responseVariablesToQtiVariableJSON(input: string | string[], cardinality: Cardinality, baseType: BaseType): QtiVariableJSON;
71
+ validate(): boolean;
72
+ set value(v: string | null);
73
+ get value(): string | null;
74
+ set boundTo(newValue: Record<string, ResponseVariableType>);
75
+ get boundTo(): Record<string, QtiVariableJSON>;
76
+ disconnectedCallback(): void;
77
+ /**
78
+ * IFRAME MODE: Send message to iframe
79
+ */
80
+ protected sendMessageToIframe(method: string, params: any): void;
81
+ /**
82
+ * IFRAME MODE: Handle iframe messages
83
+ */
84
+ protected handleIframeMessage: (event: MessageEvent) => void;
85
+ /**
86
+ * IFRAME MODE: Create iframe element
87
+ */
88
+ protected createIframe(): void;
89
+ connectedCallback(): void;
90
+ /**
91
+ * IFRAME MODE: Generate iframe HTML content
92
+ */
93
+ protected generateIframeContent(): string;
94
+ /**
95
+ * Toggle the display of the correct response
96
+ * @param responseVariable The response variable containing the correct response
97
+ * @param show Whether to show or hide the correct response
98
+ */
99
+ toggleInternalCorrectResponse(show: boolean): void;
100
+ /**
101
+ * Method to disable the PCI for review mode
102
+ * This can be used when showing the correct response
103
+ */
104
+ disable(): void;
105
+ /**
106
+ * Method to enable the PCI for interactive mode
107
+ */
108
+ enable(): void;
109
+ render(): import("lit").TemplateResult<1>;
110
+ }
111
+ declare global {
112
+ interface HTMLElementTagNameMap {
113
+ 'qti-portable-custom-interaction': QtiPortableCustomInteraction;
114
+ }
115
+ }