@citolab/tspci 2.5.30-beta.0 → 2.5.30-beta.11

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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,100 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [2.5.30-beta.11](/compare/v2.5.30-beta.10...v2.5.30-beta.11) (2025-02-04)
7
+
8
+
9
+ ### Features
10
+
11
+ * add tailwindcss to devDependencies and set version based on @tailwindcss/postcss 907b541
12
+
13
+
14
+
15
+
16
+
17
+ ## [2.5.30-beta.10](/compare/v2.5.30-beta.9...v2.5.30-beta.10) (2025-02-04)
18
+
19
+ **Note:** Version bump only for package @citolab/tspci
20
+
21
+
22
+
23
+
24
+
25
+ ## [2.5.30-beta.9](/compare/v2.5.30-beta.8...v2.5.30-beta.9) (2025-02-04)
26
+
27
+ **Note:** Version bump only for package @citolab/tspci
28
+
29
+
30
+
31
+
32
+
33
+ ## [2.5.30-beta.8](/compare/v2.5.30-beta.7...v2.5.30-beta.8) (2025-02-04)
34
+
35
+ **Note:** Version bump only for package @citolab/tspci
36
+
37
+
38
+
39
+
40
+
41
+ ## [2.5.30-beta.7](/compare/v2.5.30-beta.6...v2.5.30-beta.7) (2025-02-04)
42
+
43
+
44
+ ### Features
45
+
46
+ * dispatch qti-interaction-changed event 5c81bab
47
+
48
+
49
+
50
+
51
+
52
+ ## [2.5.30-beta.6](/compare/v2.5.30-beta.5...v2.5.30-beta.6) (2025-02-03)
53
+
54
+ **Note:** Version bump only for package @citolab/tspci
55
+
56
+
57
+
58
+
59
+
60
+ ## [2.5.30-beta.5](/compare/v2.5.30-beta.4...v2.5.30-beta.5) (2025-02-03)
61
+
62
+ **Note:** Version bump only for package @citolab/tspci
63
+
64
+
65
+
66
+
67
+
68
+ ## [2.5.30-beta.4](/compare/v2.5.30-beta.3...v2.5.30-beta.4) (2025-02-03)
69
+
70
+ **Note:** Version bump only for package @citolab/tspci
71
+
72
+
73
+
74
+
75
+
76
+ ## [2.5.30-beta.3](/compare/v2.5.30-beta.2...v2.5.30-beta.3) (2025-02-03)
77
+
78
+ **Note:** Version bump only for package @citolab/tspci
79
+
80
+
81
+
82
+
83
+
84
+ ## [2.5.30-beta.2](/compare/v2.5.30-beta.1...v2.5.30-beta.2) (2025-02-03)
85
+
86
+ **Note:** Version bump only for package @citolab/tspci
87
+
88
+
89
+
90
+
91
+
92
+ ## [2.5.30-beta.1](/compare/v2.5.30-beta.0...v2.5.30-beta.1) (2025-02-03)
93
+
94
+ **Note:** Version bump only for package @citolab/tspci
95
+
96
+
97
+
98
+
99
+
6
100
  ## [2.5.30-beta.0](/compare/v2.5.29...v2.5.30-beta.0) (2025-02-03)
7
101
 
8
102
  **Note:** Version bump only for package @citolab/tspci
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@citolab/tspci",
3
- "version": "2.5.30-beta.0",
3
+ "version": "2.5.30-beta.11",
4
4
  "description": "an opiniated typescript ims-pci builder with tao integration",
5
5
  "author": "patrick <patrick.deklein@cito.nl>",
6
6
  "repository": "@citolab/tspci",
@@ -24,6 +24,7 @@
24
24
  "@rollup/plugin-terser": "^0.4.4",
25
25
  "@rollup/plugin-typescript": "^12.1.0",
26
26
  "@tailwindcss/nesting": "0.0.0-insiders.565cd3e",
27
+ "@tailwindcss/postcss": "^4.0.3",
27
28
  "@testing-library/dom": "^10.4.0",
28
29
  "@types/node": "^22.5.5",
29
30
  "@typescript-eslint/eslint-plugin": "^8.9.0",
@@ -50,5 +51,5 @@
50
51
  "resolutions": {
51
52
  "chokidar": "^4.0.0"
52
53
  },
53
- "gitHead": "604b6123a4e495717e4fdde55d1ad1c52cb81724"
54
+ "gitHead": "b49f38a0dcf06c589224b7730c5dc9cbd31ccccd"
54
55
  }
@@ -41,14 +41,38 @@ class Pci {
41
41
 
42
42
  this.shadowdom = dom.attachShadow({ mode: "closed" });
43
43
  this.render();
44
+ const event = {
45
+ interaction: this,
46
+ responseIdentifier: this.config.responseIdentifier,
47
+ valid: true,
48
+ value: this.getResponse(),
49
+ };
50
+ // dispatch a custom event to notify the Delivery System that the interaction has changed
51
+ const interactionChangedEvent = new CustomEvent("qtiInteractionChanged", {
52
+ detail: event,
53
+ });
54
+ this.shadowdom.dispatchEvent(interactionChangedEvent);
55
+ };
44
56
 
45
- config.onready(this);
57
+ interactionChanged = () => {
58
+ const event = {
59
+ interaction: this,
60
+ responseIdentifier: this.config.responseIdentifier,
61
+ valid: true,
62
+ value: this.getResponse(),
63
+ };
64
+ // dispatch a custom event to notify the Delivery System that the interaction has changed
65
+ const interactionChangedEvent = new CustomEvent("qti-interaction-changed", {
66
+ detail: event,
67
+ });
68
+ this.dom.dispatchEvent(interactionChangedEvent);
46
69
  };
47
70
 
48
71
  render = () => {
49
72
  this.shadowdom.addEventListener("change", (e) => {
50
73
  const value = e.target.value;
51
74
  this.state = value;
75
+ this.interactionChanged();
52
76
  });
53
77
  this.shadowdom.innerHTML = `<div class="pci-container">
54
78
  <h1>${this.config.properties.title}</h1>
@@ -4,7 +4,7 @@ import { IStore } from "@citolab/preact-store";
4
4
  import * as ctx from "qtiCustomInteractionContext";
5
5
  import Interaction from "./interaction";
6
6
  import style from "./styles.css";
7
- import { Configuration, IMSpci, QtiVariableJSON } from "@citolab/tspci";
7
+ import { ConfigProperties, IMSpci, QtiInteractionChangedDetail, QtiVariableJSON } from "@citolab/tspci";
8
8
  import configProps from "./config.json";
9
9
  import { StateModel, initStore } from "./store";
10
10
 
@@ -12,7 +12,7 @@ type PropTypes = typeof configProps;
12
12
 
13
13
  class App implements IMSpci<PropTypes> {
14
14
  typeIdentifier = "###___PCI_NAME___###"; // typeIdentifier is mandatory for all PCI's
15
- config: Configuration<PropTypes>; // reference to the interface of the config object which you get when getInstance is called by the player
15
+ config: ConfigProperties<PropTypes>; // reference to the interface of the config object which you get when getInstance is called by the player
16
16
  state: string; // keep a reference to the state
17
17
  shadowdom: ShadowRoot; // Not mandatory, but its wise to create a shadowroot
18
18
  store: IStore<StateModel>;
@@ -20,13 +20,17 @@ class App implements IMSpci<PropTypes> {
20
20
  private logActions: { type: string; payload: unknown }[] = []; // optional logActions
21
21
  private initialState: StateModel = { input: undefined }; // optional initial state
22
22
  constructor() {
23
- ctx && ctx.register(this);
23
+ if (ctx) {
24
+ ctx.register(this);
25
+ }
24
26
  }
25
27
 
26
- getInstance = (dom: HTMLElement, config: Configuration<PropTypes>, stateString: string) => {
28
+ getInstance = (dom: HTMLElement, config: ConfigProperties<PropTypes>, stateString: string) => {
27
29
  config.properties = { ...configProps, ...config.properties }; // merge current props with incoming
28
30
  this.config = config;
29
-
31
+ if (!dom) {
32
+ throw new Error("No DOM Element provided");
33
+ }
30
34
  this.logActions = stateString ? JSON.parse(stateString).log : [];
31
35
  this.store = initStore(this.initialState);
32
36
  try {
@@ -39,9 +43,26 @@ class App implements IMSpci<PropTypes> {
39
43
  }
40
44
 
41
45
  this.shadowdom = dom.attachShadow({ mode: "closed" });
46
+
47
+ this.store.subscribe(() => {
48
+ const event: QtiInteractionChangedDetail = {
49
+ interaction: this,
50
+ responseIdentifier: this.config.responseIdentifier,
51
+ valid: true,
52
+ value: this.getResponse(),
53
+ };
54
+ // dispatch a custom event to notify the Delivery System that the interaction has changed
55
+ const interactionChangedEvent = new CustomEvent("qti-interaction-changed", {
56
+ detail: event,
57
+ });
58
+ dom.dispatchEvent(interactionChangedEvent);
59
+ });
60
+
42
61
  this.render();
43
62
 
44
- this.config.onready && this.config.onready(this);
63
+ if (this.config.onready) {
64
+ this.config.onready(this);
65
+ }
45
66
  };
46
67
 
47
68
  render = () => {
@@ -1,15 +1,19 @@
1
1
  import { Store } from "@citolab/preact-store";
2
2
 
3
3
  export type ActionType = "SET_INPUT" | "RESTORE_STATE";
4
- export type StateModel = { input: number };
4
+ export type StateModel = { input: number | undefined };
5
5
 
6
- export const initStore = (initialState: StateModel, restoreData?: {
7
- state?: StateModel;
8
- actions: { type: string; payload: any; timestamp?: number }[];
9
- }) => {
6
+ export const initStore = (
7
+ initialState: StateModel,
8
+ restoreData?: {
9
+ state: StateModel;
10
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
+ actions: { type: string; payload: any; timestamp?: number }[];
12
+ }
13
+ ) => {
10
14
  const store = new Store<StateModel>(initialState, restoreData);
11
- store.addReducer<{ input: number }>("SET_INPUT", (state, { input }) => {
12
- return {...state, input };
15
+ store.addReducer<{ input: number | undefined }>("SET_INPUT", (state, { input }) => {
16
+ return { ...state, input };
13
17
  });
14
18
  return store;
15
- }
19
+ };
@@ -1,8 +1,8 @@
1
1
  module.exports = {
2
2
  plugins: [
3
- require('postcss-import'),
4
- require('@tailwindcss/nesting'),
5
- require('tailwindcss'),
6
- require('autoprefixer'),
7
- ]
8
- }
3
+ require("postcss-import"),
4
+ require("@tailwindcss/nesting"),
5
+ require("@tailwindcss/postcss"),
6
+ require("autoprefixer"),
7
+ ],
8
+ };
@@ -1,7 +1,10 @@
1
- @tailwind components;
2
- @tailwind utilities;
3
- @tailwind base;
4
-
5
- .pci-container { @apply container flex flex-col justify-center items-center; }
6
- h1 { @apply text-2xl text-red-500 m-4; }
7
- input { @apply border border-gray-400 rounded p-2 m-1; }
1
+ @import "tailwindcss";
2
+ .pci-container {
3
+ @apply container flex flex-col justify-center items-center;
4
+ }
5
+ h1 {
6
+ @apply text-2xl text-red-500 m-4;
7
+ }
8
+ input {
9
+ @apply border border-gray-400 rounded p-2 m-1;
10
+ }
@@ -1,4 +1,4 @@
1
- import { Configuration, IMSpci, QtiVariableJSON } from "@citolab/tspci";
1
+ import { ConfigProperties, IMSpci, QtiInteractionChangedDetail, QtiVariableJSON } from "@citolab/tspci";
2
2
  import * as ctx from "qtiCustomInteractionContext";
3
3
  import configProps from "./config.json";
4
4
  import Interaction from "./interaction"; // import and bundle this interaction file
@@ -10,31 +10,53 @@ type PropTypes = typeof configProps;
10
10
 
11
11
  class Pci implements IMSpci<PropTypes> {
12
12
  typeIdentifier = "###___PCI_NAME___###"; // typeIdentifier is mandatory for all PCI's
13
- config: Configuration<PropTypes>; // reference to the interface of the config object which you get when getInstance is called by the player
13
+ config: ConfigProperties<PropTypes>; // reference to the interface of the config object which you get when getInstance is called by the player
14
14
  state: string; // keep a reference to the state
15
15
  shadowdom: ShadowRoot | Element; // Not mandatory, but its wise to create a shadowroot
16
+ dom: HTMLElement;
16
17
 
17
18
  constructor() {
18
- ctx && ctx.register(this); // we assume the qtiCustomInteractionContext is avaible due to the import above
19
+ if (ctx) {
20
+ ctx.register(this); // we assume the qtiCustomInteractionContext is avaible due to the import above
21
+ }
19
22
  }
20
23
 
21
24
  // First in the lifecycle of a PCI, this method is called with the domElement ( usually qti-interaction-markup ) where we can add our dom tree.
22
25
  // config is the configuration object which has an onready
23
- getInstance = (dom: HTMLElement, config: Configuration<any>, state: string) => {
26
+ getInstance = (dom: HTMLElement, config: ConfigProperties<PropTypes>, state: string) => {
24
27
  config.properties = { ...configProps, ...config.properties }; // merge current props with incoming
25
28
  this.config = config;
26
29
  this.state = state ? state : "";
27
-
30
+ if (!dom) {
31
+ throw new Error("No DOM Element provided");
32
+ }
28
33
  this.shadowdom = dom.attachShadow({ mode: "closed" });
34
+ this.dom = dom;
35
+
29
36
  this.render();
30
37
 
31
38
  config.onready(this);
32
39
  };
33
40
 
41
+ private interactionChanged = () => {
42
+ const event: QtiInteractionChangedDetail = {
43
+ interaction: this,
44
+ responseIdentifier: this.config.responseIdentifier,
45
+ valid: true,
46
+ value: this.getResponse(),
47
+ };
48
+ // dispatch a custom event to notify the Delivery System that the interaction has changed
49
+ const interactionChangedEvent = new CustomEvent("qti-interaction-changed", {
50
+ detail: event,
51
+ });
52
+ this.dom.dispatchEvent(interactionChangedEvent);
53
+ };
54
+
34
55
  private render = () => {
35
56
  this.shadowdom.addEventListener("change", (e: Event) => {
36
57
  const value = (e.target as HTMLInputElement)?.value || "";
37
58
  this.state = value;
59
+ this.interactionChanged();
38
60
  });
39
61
  this.shadowdom.innerHTML = `<div class="pci-container">
40
62
  <h1>${this.config.properties.title}</h1>
package/readme.md CHANGED
@@ -97,7 +97,7 @@ Add the following PCI in the src folder `index.ts`
97
97
 
98
98
  ```ts
99
99
  // src/index.ts
100
- import { Configuration, IMSpci } from "@citolab/tspci";
100
+ import { ConfigProperties, IMSpci } from "@citolab/tspci";
101
101
  import * as ctx from "qtiCustomInteractionContext";
102
102
 
103
103
  class Pci implements IMSpci<{}> {
@@ -108,7 +108,7 @@ class Pci implements IMSpci<{}> {
108
108
  ctx && ctx.register(this);
109
109
  }
110
110
 
111
- getInstance = (dom: HTMLElement, config: Configuration<any>, state: string) => {
111
+ getInstance = (dom: HTMLElement, config: ConfigProperties<any>, state: string) => {
112
112
  this.shadowdom = dom.attachShadow({ mode: "closed" });
113
113
  this.render();
114
114
  config.onready(this);
@@ -337,9 +337,9 @@ Also used in the TAO export for configuring PCIs in TAO
337
337
  + type PropTypes = typeof configProps;
338
338
 
339
339
  // add to types
340
- + private config: Configuration<PropTypes>;
340
+ + private config: ConfigProperties<PropTypes>;
341
341
 
342
- getInstance = (dom: HTMLElement, config: Configuration<PropTypes>, stateString: string) => {
342
+ getInstance = (dom: HTMLElement, config: ConfigProperties<PropTypes>, stateString: string) => {
343
343
  + config.properties = { ...configProps, ...config.properties }; // merge our props with players
344
344
  + this.config = config;
345
345
 
package/src/cli.js CHANGED
@@ -289,22 +289,38 @@ const run = async () => {
289
289
 
290
290
  // read postcss.config.js
291
291
  const postcssConfig = path.resolve(cliPath, "./../preset/tailwind/postcss.config.js");
292
+ // get all plugin names
292
293
  const postcssConfigContent = fs.readFileSync(postcssConfig, "utf8");
293
294
  // get all plugin names
294
- const pluginNames = postcssConfigContent
295
- .match(/require\('(.*)'\)/g)
296
- .map((plugin) => plugin.replace(/require\('(.*)'\)/, "$1"));
295
+ // Match require statements that use either single or double quotes
296
+ const pluginMatches = postcssConfigContent.match(/require\(["']([^"']+)["']\)/g);
297
+ let pluginNames = [];
298
+ if (pluginMatches) {
299
+ pluginNames = pluginMatches.map((plugin) => plugin.replace(/require\(["']([^"']+)["']\)/, "$1"));
300
+ } else {
301
+ console.log("No PostCSS plugins found.");
302
+ }
297
303
  const packageJsonCli = path.resolve(cliPath, "./../package.json");
298
304
  const packageJsonCliContent = fs.readFileSync(packageJsonCli, "utf8");
299
305
  const packageJsonCliContentJson = JSON.parse(packageJsonCliContent);
300
306
  // add all plugins to package.json and map the version to the version in tspci package.json
307
+ let version = "latest";
301
308
  for (const pluginName of pluginNames) {
302
309
  const pluginVersion = packageJsonCliContentJson.dependencies[pluginName];
310
+ if (pluginName === "@tailwindcss/postcss") {
311
+ version = pluginVersion.toString();
312
+ }
303
313
  packageJsonContentJson.devDependencies = {
304
314
  ...packageJsonContentJson.devDependencies,
305
315
  [pluginName]: pluginVersion,
306
316
  };
307
317
  }
318
+
319
+ // add tailwindcss itself to the devDependencies
320
+ packageJsonContentJson.devDependencies = {
321
+ ...packageJsonContentJson.devDependencies,
322
+ tailwindcss: version,
323
+ };
308
324
  const newPackageJsonContent = JSON.stringify(packageJsonContentJson, null, 2);
309
325
  fs.writeFileSync(packageJson, newPackageJsonContent);
310
326
  // copy tailwind config file
@@ -430,15 +446,14 @@ const run = async () => {
430
446
  }
431
447
 
432
448
  prodBuild().then(() => {
433
- if (t) {
434
- if (!targetName) {
435
- console.error("Missing target name. Use tspci --target target or tspci --targetExt target");
436
- }
437
- const scriptFile = path.join(path.join(path.join(NODE_MODULES, `${targetName}`), "src"), `createPackage.mjs`);
438
- import(`file:///${scriptFile}`).then((script) => {
439
- script.bundle();
440
- });
449
+ if (!targetName) {
450
+ console.error("Missing target name. Use tspci --target target or tspci --targetExt target");
451
+ return;
441
452
  }
453
+ const scriptFile = path.join(path.join(path.join(NODE_MODULES, `${targetName}`), "src"), `createPackage.mjs`);
454
+ import(`file:///${scriptFile}`).then((script) => {
455
+ script.bundle();
456
+ });
442
457
  });
443
458
  };
444
459
 
package/types/index.d.ts CHANGED
@@ -1,22 +1,22 @@
1
1
  // Define the payload structure for the custom event
2
2
  export interface QtiInteractionChangedDetail {
3
- interaction: IMSpci<ConfigProperties>; // The PCI instance itself
3
+ interaction: IMSpci<unknown>; // The PCI instance itself
4
4
  responseIdentifier: string; // The response identifier provided in the getInstance call
5
5
  valid?: boolean; // Optional boolean indicating if checkValidity() returns true
6
6
  value?: QtiVariableJSON; // Optional value returned by getResponse()
7
7
  }
8
8
 
9
9
  // Define the ConfigProperties interface
10
- export interface ConfigProperties {
11
- properties: Record<string, string>; // Follows dataset conversion rules (camelCased keys)
10
+ export interface ConfigProperties<T> {
11
+ properties: T; // Follows dataset conversion rules (camelCased keys)
12
12
  templateVariables: Record<string, QtiVariableJSON>; // Follows structure in Appendix C
13
13
  contextVariables: Record<string, QtiVariableJSON>; // Follows structure in Appendix C
14
14
  boundTo: Record<string, QtiVariableJSON>; // Follows structure in Appendix C
15
15
  responseIdentifier: string; // Unique within interaction scope
16
16
 
17
- onready: (interaction: IMSpci<ConfigProperties>, state?: string) => void; // Callback when PCI is fully constructed and ready
17
+ onready: (interaction: IMSpci<T>, state?: string) => void; // Callback when PCI is fully constructed and ready
18
18
  ondone?: (
19
- interaction: IMSpci<ConfigProperties>,
19
+ interaction: IMSpci<T>,
20
20
  response: Record<string, QtiVariableJSON>,
21
21
  state: string,
22
22
  status?: "interacting" | "closed" | "solution" | "review"
@@ -25,7 +25,7 @@ export interface ConfigProperties {
25
25
  status?: "interacting" | "suspended" | "closed" | "solution" | "review"; // Optional, defaults to "interacting"
26
26
  }
27
27
 
28
- export interface IMSpci<CustomConfigProperties extends ConfigProperties> {
28
+ export interface IMSpci<T> {
29
29
  typeIdentifier: string;
30
30
 
31
31
  /** @access public
@@ -37,7 +37,7 @@ export interface IMSpci<CustomConfigProperties extends ConfigProperties> {
37
37
  * This must have been obtained from a prior call to getState on an
38
38
  * instance of this type (same typeIdentifier)
39
39
  */
40
- getInstance: (dom: HTMLElement, configuration: Configuration<CustomConfigProperties>, state: string) => void;
40
+ getInstance: (dom: HTMLElement, configuration: ConfigProperties<T>, state: string) => void;
41
41
 
42
42
  /** @access public
43
43
  * @method getResponse
@@ -55,11 +55,6 @@ export interface IMSpci<CustomConfigProperties extends ConfigProperties> {
55
55
  destroy?: () => void; // Not used in IMS and not in TAO implementation, so not used here (optional)
56
56
  }
57
57
 
58
- export declare type Configuration<T extends ConfigProperties> = {
59
- onready: () => void;
60
- properties: T;
61
- };
62
-
63
58
  export interface directedPair {
64
59
  destination: string;
65
60
  source: string;