@citolab/tspci 2.4.0-alpha.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@citolab/tspci",
3
- "version": "2.4.0-alpha.0",
3
+ "version": "2.4.0",
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",
@@ -46,5 +46,5 @@
46
46
  "help": "tspci --help",
47
47
  "init": "tspci init --force --path=../../pcis"
48
48
  },
49
- "gitHead": "881b3b40f3e31eafdc38ff79b51a16384e67f89d"
49
+ "gitHead": "3f39a92aa0d565cb5a0fc4b4f2d3102aab9402dc"
50
50
  }
@@ -1,25 +1,24 @@
1
1
  import "preact/debug";
2
2
  import { render } from "preact";
3
- import { initStore, Store } from "@citolab/preact-store";
4
-
3
+ import { IStore } from "@citolab/preact-store";
5
4
  import * as ctx from "qtiCustomInteractionContext";
6
5
  import Interaction from "./interaction";
7
6
  import style from "./styles.css";
8
7
  import { Configuration, IMSpci, QtiVariableJSON } from "@citolab/tspci";
9
8
  import configProps from "./config.json";
10
- import { IStore } from "@citolab/preact-store";
9
+ import { StateModel, initStore } from "./store";
11
10
 
12
11
  type PropTypes = typeof configProps;
13
12
 
14
13
  class App implements IMSpci<PropTypes> {
15
- typeIdentifier = "myPci"; // typeIdentifier is mandatory for all PCI's
14
+ typeIdentifier = "###___PCI_NAME___###"; // typeIdentifier is mandatory for all PCI's
16
15
  config: Configuration<PropTypes>; // reference to the interface of the config object which you get when getInstance is called by the player
17
16
  state: string; // keep a reference to the state
18
17
  shadowdom: ShadowRoot; // Not mandatory, but its wise to create a shadowroot
19
18
  store: IStore<StateModel>;
20
19
 
21
20
  private logActions: { type: string; payload: unknown }[] = []; // optional logActions
22
-
21
+ private initialState: StateModel = { input: undefined }; // optional initial state
23
22
  constructor() {
24
23
  ctx && ctx.register(this);
25
24
  }
@@ -28,12 +27,17 @@ class App implements IMSpci<PropTypes> {
28
27
  config.properties = { ...configProps, ...config.properties }; // merge current props with incoming
29
28
  this.config = config;
30
29
 
31
- const initState: StateModel = stateString ? JSON.parse(stateString).state : { input: 0 };
32
30
  this.logActions = stateString ? JSON.parse(stateString).log : [];
33
- this.store = initStore(this.initialState );
34
- if (restoredState || logActions) {
35
- this.store.restoreState(restoredState, logActions);
31
+ this.store = initStore(this.initialState);
32
+ try {
33
+ const restoredState = stateString ? JSON.parse(stateString) : null;
34
+ if (restoredState) {
35
+ this.store.restoreState(restoredState?.state, this.logActions);
36
+ }
37
+ } catch (error) {
38
+ console.log(error);
36
39
  }
40
+
37
41
  this.shadowdom = dom.attachShadow({ mode: "closed" });
38
42
  this.render();
39
43
 
@@ -45,7 +49,7 @@ class App implements IMSpci<PropTypes> {
45
49
  const css = document.createElement("style");
46
50
  css.innerHTML = style;
47
51
  this.shadowdom.appendChild(css);
48
- render(<Interaction config={this.config.properties} dom={this.shadowdom} />, this.shadowdom);
52
+ render(<Interaction config={this.config.properties} dom={this.shadowdom} store={this.store} />, this.shadowdom);
49
53
  };
50
54
 
51
55
  getState = () =>
@@ -1,12 +1,23 @@
1
1
  import { useStore, IStore, Action } from "@citolab/preact-store";
2
2
  import { ActionType, StateModel } from "./store";
3
3
  import configProps from "./config.json";
4
+ import { useState } from "preact/hooks";
5
+
4
6
  import procenten from "./assets/procenten.png"; // image types are bundled inside the js
5
7
 
6
8
  type PropTypes = typeof configProps;
7
9
 
8
- const Interaction = ({ config, dom }: { config: PropTypes; dom: Document | ShadowRoot, store: IStore<StateModel> }) => {
10
+ const Interaction = ({ config, dom, store }: { config: PropTypes; dom: Document | ShadowRoot, store: IStore<StateModel> }) => {
9
11
  const state = useStore(store);
12
+ const [eventHandled, setEventHandled] = useState<boolean>(false);
13
+
14
+ const handleInput = (e: Event) => {
15
+ if (!eventHandled) {
16
+ const input = e.target as HTMLInputElement;
17
+ store.dispatch<{ input: number }>({ type: "SET_INPUT", payload: { input: +input.value } });
18
+ setEventHandled(true);
19
+ }
20
+ };
10
21
 
11
22
  return <div className="pci-container">
12
23
  <h1>{config.title}</h1>
@@ -16,18 +27,15 @@ const Interaction = ({ config, dom }: { config: PropTypes; dom: Document | Shado
16
27
  <div className="interaction">
17
28
  <label htmlFor="tentacles">{config.prompt}</label>
18
29
  <input type="number"
19
- onKeyUp={(e) => {
20
- const input = e.target as HTMLInputElement;
21
- store.dispatch<{ input: number }>({ type: "SET_INPUT", payload: { input: +input.value } });
22
- }
23
- }
24
- onChange={(e) => {
25
- const input = e.target as HTMLInputElement;
26
- store.dispatch<{ input: number }>({ type: "SET_INPUT", payload: { input: +input.value } });
27
- }} min="0" max="100" />%
30
+ value={state.input}
31
+ onInput={handleInput}
32
+ onPaste={() => setEventHandled(false)}
33
+ onKeyDown={() => setEventHandled(false)}
34
+ min="0" max="100" />%
28
35
  </div>
29
36
  </div>;
30
37
  };
31
38
 
32
39
 
33
40
  export default Interaction;
41
+
@@ -4,7 +4,7 @@ export type ActionType = "SET_INPUT" | "RESTORE_STATE";
4
4
  export type StateModel = { input: number };
5
5
 
6
6
  export const initStore = (initialState: StateModel, restoreData?: {
7
- state: StateModel;
7
+ state?: StateModel;
8
8
  actions: { type: string; payload: any; timestamp?: number }[];
9
9
  }) => {
10
10
  const store = new Store<StateModel>(initialState, restoreData);
package/public/index.html CHANGED
@@ -99,8 +99,8 @@
99
99
  async function scrubRange(value) {
100
100
  if (!isReplaying.current) {
101
101
  setCurrentRangeValue(value);
102
- resetResponse();
103
102
  if (store) {
103
+ store.reset();
104
104
  isReplaying.current = true;
105
105
  replayedIndex.current = 0;
106
106
  setCurrentRangeValue(value);
@@ -172,7 +172,7 @@
172
172
 
173
173
  async function playState() {
174
174
  if (store && !isReplaying.current) {
175
- resetResponse();
175
+ store.reset();
176
176
  isReplaying.current = true;
177
177
  replayedIndex.current = 0;
178
178
  await store.replay([...actions]);
package/src/cli.js CHANGED
@@ -232,7 +232,7 @@ const run = async () => {
232
232
  console.log(chalk.green(figlet.textSync("@citolab/tspci", { horizontalLayout: "full" })));
233
233
 
234
234
  // prompt for name
235
- let pciName = (await askOption("What your pci identifier (typeIdentifier) (default: myPci) ")) || "myPci";
235
+ let pciName = (await askOption("What your pci identifier (typeIdentifier). this can only contain alphanumeric characters (default: myPci) ")) || "myPci";
236
236
  let destinationFolder = path.join(process.cwd(), pciName);
237
237
  if (args.find((arg) => arg.startsWith("--path"))) {
238
238
  const pathIndex = args.findIndex((arg) => arg.startsWith("--path"));
@@ -255,11 +255,11 @@ const run = async () => {
255
255
  return;
256
256
  }
257
257
  // check if contains other characters than letters and numbers
258
- if (!/^[a-zA-Z0-9]+$/.test(pciName)) {
258
+ if (!/^[a-zA-Z]+$/.test(pciName)) {
259
259
  console.log(
260
- chalk.yellow("The identifier of your pci can only contain letters and numbers, stripping special characters")
260
+ chalk.yellow("The identifier of your pci can only contain alphanumeric characters, stripping others")
261
261
  );
262
- pciName = pciName.replace(/[^a-zA-Z0-9]/g, "");
262
+ pciName = pciName.replace(/[^a-zA-Z]/g, "");
263
263
  }
264
264
  console.log(chalk.green(`pci: ${pciName}`));
265
265
  const description =