jspsych-tangram 0.0.13 → 0.0.15

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.
Files changed (51) hide show
  1. package/dist/afc/index.browser.js +17751 -0
  2. package/dist/afc/index.browser.js.map +1 -0
  3. package/dist/afc/index.browser.min.js +42 -0
  4. package/dist/afc/index.browser.min.js.map +1 -0
  5. package/dist/afc/index.cjs +443 -0
  6. package/dist/afc/index.cjs.map +1 -0
  7. package/dist/afc/index.d.ts +169 -0
  8. package/dist/afc/index.js +441 -0
  9. package/dist/afc/index.js.map +1 -0
  10. package/dist/construct/index.browser.js +8 -2
  11. package/dist/construct/index.browser.js.map +1 -1
  12. package/dist/construct/index.browser.min.js +10 -10
  13. package/dist/construct/index.browser.min.js.map +1 -1
  14. package/dist/construct/index.cjs +8 -2
  15. package/dist/construct/index.cjs.map +1 -1
  16. package/dist/construct/index.js +8 -2
  17. package/dist/construct/index.js.map +1 -1
  18. package/dist/index.cjs +379 -11
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.ts +178 -12
  21. package/dist/index.js +379 -12
  22. package/dist/index.js.map +1 -1
  23. package/dist/nback/index.browser.js +6 -4
  24. package/dist/nback/index.browser.js.map +1 -1
  25. package/dist/nback/index.browser.min.js +8 -8
  26. package/dist/nback/index.browser.min.js.map +1 -1
  27. package/dist/nback/index.cjs +6 -4
  28. package/dist/nback/index.cjs.map +1 -1
  29. package/dist/nback/index.js +6 -4
  30. package/dist/nback/index.js.map +1 -1
  31. package/dist/prep/index.browser.js +8 -2
  32. package/dist/prep/index.browser.js.map +1 -1
  33. package/dist/prep/index.browser.min.js +10 -10
  34. package/dist/prep/index.browser.min.js.map +1 -1
  35. package/dist/prep/index.cjs +8 -2
  36. package/dist/prep/index.cjs.map +1 -1
  37. package/dist/prep/index.js +8 -2
  38. package/dist/prep/index.js.map +1 -1
  39. package/package.json +1 -1
  40. package/src/core/components/board/GameBoard.tsx +13 -42
  41. package/src/core/components/board/useGameBoard.ts +52 -0
  42. package/src/core/components/index.ts +4 -2
  43. package/src/core/components/pieces/BlueprintRing.tsx +0 -25
  44. package/src/core/components/pieces/useBlueprintRing.ts +39 -0
  45. package/src/index.ts +2 -1
  46. package/src/plugins/tangram-afc/AFCApp.tsx +341 -0
  47. package/src/plugins/tangram-afc/index.ts +140 -0
  48. package/src/plugins/tangram-nback/NBackApp.tsx +3 -3
  49. package/tangram-construct.min.js +10 -10
  50. package/tangram-nback.min.js +8 -8
  51. package/tangram-prep.min.js +10 -10
@@ -0,0 +1,169 @@
1
+ import { JsPsychPlugin, ParameterType, JsPsych, TrialType } from 'jspsych';
2
+
3
+ declare const info: {
4
+ name: string;
5
+ version: string;
6
+ parameters: {
7
+ /** Left tangram specification to display */
8
+ tangram_left: {
9
+ type: ParameterType;
10
+ default: undefined;
11
+ description: string;
12
+ };
13
+ /** Right tangram specification to display */
14
+ tangram_right: {
15
+ type: ParameterType;
16
+ default: undefined;
17
+ description: string;
18
+ };
19
+ /** HTML content to display above the tangrams as instructions */
20
+ instructions: {
21
+ type: ParameterType;
22
+ default: string;
23
+ description: string;
24
+ };
25
+ /** Text to display on left response button */
26
+ button_text_left: {
27
+ type: ParameterType;
28
+ default: string;
29
+ description: string;
30
+ };
31
+ /** Text to display on right response button */
32
+ button_text_right: {
33
+ type: ParameterType;
34
+ default: string;
35
+ description: string;
36
+ };
37
+ /** Whether to show tangram decomposed into individual primitives with borders */
38
+ show_tangram_decomposition: {
39
+ type: ParameterType;
40
+ default: boolean;
41
+ description: string;
42
+ };
43
+ /** Whether to use distinct colors for each primitive shape type */
44
+ use_primitive_colors: {
45
+ type: ParameterType;
46
+ default: boolean;
47
+ description: string;
48
+ };
49
+ /** Indices mapping primitives to colors from the color palette */
50
+ primitive_color_indices: {
51
+ type: ParameterType;
52
+ default: number[];
53
+ description: string;
54
+ };
55
+ /** Callback fired when trial ends */
56
+ onTrialEnd: {
57
+ type: ParameterType;
58
+ default: undefined;
59
+ description: string;
60
+ };
61
+ };
62
+ data: {
63
+ /** Reaction time in milliseconds */
64
+ rt: {
65
+ type: ParameterType;
66
+ description: string;
67
+ };
68
+ /** Response choice */
69
+ response: {
70
+ type: ParameterType;
71
+ description: string;
72
+ };
73
+ };
74
+ citations: string;
75
+ };
76
+ type Info = typeof info;
77
+ /**
78
+ * **tangram-afc**
79
+ *
80
+ * A jsPsych plugin for alternative forced choice (AFC) trials displaying two tangrams
81
+ * side-by-side with response buttons.
82
+ *
83
+ * @author Justin Yang & Sean Paul Anderson
84
+ * @see {@link https://github.com/cogtoolslab/tangram_construction.git/tree/main/experiments/jspsych-tangram-prep}
85
+ */
86
+ declare class TangramAFCPlugin implements JsPsychPlugin<Info> {
87
+ private jsPsych;
88
+ static info: {
89
+ name: string;
90
+ version: string;
91
+ parameters: {
92
+ /** Left tangram specification to display */
93
+ tangram_left: {
94
+ type: ParameterType;
95
+ default: undefined;
96
+ description: string;
97
+ };
98
+ /** Right tangram specification to display */
99
+ tangram_right: {
100
+ type: ParameterType;
101
+ default: undefined;
102
+ description: string;
103
+ };
104
+ /** HTML content to display above the tangrams as instructions */
105
+ instructions: {
106
+ type: ParameterType;
107
+ default: string;
108
+ description: string;
109
+ };
110
+ /** Text to display on left response button */
111
+ button_text_left: {
112
+ type: ParameterType;
113
+ default: string;
114
+ description: string;
115
+ };
116
+ /** Text to display on right response button */
117
+ button_text_right: {
118
+ type: ParameterType;
119
+ default: string;
120
+ description: string;
121
+ };
122
+ /** Whether to show tangram decomposed into individual primitives with borders */
123
+ show_tangram_decomposition: {
124
+ type: ParameterType;
125
+ default: boolean;
126
+ description: string;
127
+ };
128
+ /** Whether to use distinct colors for each primitive shape type */
129
+ use_primitive_colors: {
130
+ type: ParameterType;
131
+ default: boolean;
132
+ description: string;
133
+ };
134
+ /** Indices mapping primitives to colors from the color palette */
135
+ primitive_color_indices: {
136
+ type: ParameterType;
137
+ default: number[];
138
+ description: string;
139
+ };
140
+ /** Callback fired when trial ends */
141
+ onTrialEnd: {
142
+ type: ParameterType;
143
+ default: undefined;
144
+ description: string;
145
+ };
146
+ };
147
+ data: {
148
+ /** Reaction time in milliseconds */
149
+ rt: {
150
+ type: ParameterType;
151
+ description: string;
152
+ };
153
+ /** Response choice */
154
+ response: {
155
+ type: ParameterType;
156
+ description: string;
157
+ };
158
+ };
159
+ citations: string;
160
+ };
161
+ constructor(jsPsych: JsPsych);
162
+ /**
163
+ * Launches the trial by invoking startAFCTrial
164
+ * with the display element, parameters, and jsPsych instance.
165
+ */
166
+ trial(display_element: HTMLElement, trial: TrialType<Info>): void;
167
+ }
168
+
169
+ export { TangramAFCPlugin as default };
@@ -0,0 +1,441 @@
1
+ import { ParameterType } from 'jspsych';
2
+ import React, { useRef, useState } from 'react';
3
+ import { createRoot } from 'react-dom/client';
4
+
5
+ function polysAABB(polys) {
6
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
7
+ for (const poly of polys) {
8
+ for (const p of poly) {
9
+ if (p.x < minX) minX = p.x;
10
+ if (p.y < minY) minY = p.y;
11
+ if (p.x > maxX) maxX = p.x;
12
+ if (p.y > maxY) maxY = p.y;
13
+ }
14
+ }
15
+ const width = Math.max(1, maxX - minX);
16
+ const height = Math.max(1, maxY - minY);
17
+ return { min: { x: minX, y: minY }, max: { x: maxX, y: maxY }, width, height, cx: (minX + maxX) / 2, cy: (minY + maxY) / 2 };
18
+ }
19
+
20
+ const CONFIG = {
21
+ color: {
22
+ bands: {
23
+ silhouette: { fillEven: "#ffffff", stroke: "#b1b1b1" }},
24
+ silhouetteMask: "#374151",
25
+ tangramDecomposition: { stroke: "#fef2cc" },
26
+ primitiveColors: [
27
+ // from seaborn "colorblind" palette, 6 colors, with red omitted
28
+ "#0173b2",
29
+ "#de8f05",
30
+ "#029e73",
31
+ "#cc78bc",
32
+ "#ca9161"
33
+ ]
34
+ },
35
+ opacity: {
36
+ silhouetteMask: 0.25,
37
+ piece: { normal: 1 }
38
+ },
39
+ size: {
40
+ stroke: { bandPx: 5, tangramDecompositionPx: 1 }},
41
+ layout: {
42
+ grid: { stepPx: 20, unitPx: 40 }}};
43
+
44
+ const GRID_PX = CONFIG.layout.grid.stepPx;
45
+ function igcd(a, b) {
46
+ a = Math.round(Math.abs(a));
47
+ b = Math.round(Math.abs(b));
48
+ while (b) [a, b] = [b, a % b];
49
+ return a || 1;
50
+ }
51
+ function inferUnitFromPolys(polys) {
52
+ let g = 0;
53
+ for (const poly of polys) {
54
+ for (let i = 0; i < poly.length; i++) {
55
+ const a = poly[i], b = poly[(i + 1) % poly.length];
56
+ if (!a || !b) continue;
57
+ const dx = Math.round(Math.abs(b.x - a.x));
58
+ const dy = Math.round(Math.abs(b.y - a.y));
59
+ if (dx) g = g ? igcd(g, dx) : dx;
60
+ if (dy) g = g ? igcd(g, dy) : dy;
61
+ }
62
+ }
63
+ return g || 1;
64
+ }
65
+ function placeSilhouetteGridAlignedAsPolys(polys, S, rectCenter) {
66
+ if (!polys || polys.length === 0) return [];
67
+ const a = polysAABB(polys);
68
+ const cx0 = (a.min.x + a.max.x) / 2;
69
+ const cy0 = (a.min.y + a.max.y) / 2;
70
+ const cx = Math.round(rectCenter.cx / GRID_PX) * GRID_PX;
71
+ const cy = Math.round(rectCenter.cy / GRID_PX) * GRID_PX;
72
+ const tx = cx - S * cx0;
73
+ const ty = cy - S * cy0;
74
+ const stx = Math.round(tx / GRID_PX) * GRID_PX;
75
+ const sty = Math.round(ty / GRID_PX) * GRID_PX;
76
+ return polys.map((poly) => poly.map((p) => ({ x: S * p.x + stx, y: S * p.y + sty })));
77
+ }
78
+
79
+ function startAFCTrial(display_element, params, _jsPsych) {
80
+ const root = createRoot(display_element);
81
+ root.render(React.createElement(AFCView, { params }));
82
+ return { root, display_element, jsPsych: _jsPsych };
83
+ }
84
+ function AFCView({ params }) {
85
+ const {
86
+ tangramLeft,
87
+ tangramRight,
88
+ instructions,
89
+ buttonTextLeft,
90
+ buttonTextRight,
91
+ showTangramDecomposition,
92
+ usePrimitiveColors,
93
+ primitiveColorIndices,
94
+ onTrialEnd
95
+ } = params;
96
+ const trialStartTime = useRef(Date.now());
97
+ const [hasResponded, setHasResponded] = useState(false);
98
+ const handleResponse = (choice) => {
99
+ if (hasResponded) return;
100
+ setHasResponded(true);
101
+ const rt = Date.now() - trialStartTime.current;
102
+ if (onTrialEnd) {
103
+ onTrialEnd({
104
+ rt,
105
+ response: choice,
106
+ show_tangram_decomposition: showTangramDecomposition,
107
+ use_primitive_colors: usePrimitiveColors,
108
+ primitive_color_indices: primitiveColorIndices,
109
+ button_text_left: buttonTextLeft,
110
+ button_text_right: buttonTextRight
111
+ });
112
+ }
113
+ };
114
+ return /* @__PURE__ */ React.createElement("div", { style: {
115
+ display: "flex",
116
+ flexDirection: "column",
117
+ alignItems: "center",
118
+ justifyContent: "center",
119
+ padding: "20px",
120
+ background: "#fff7e0ff",
121
+ minHeight: "100vh"
122
+ } }, instructions && /* @__PURE__ */ React.createElement(
123
+ "div",
124
+ {
125
+ style: {
126
+ maxWidth: "800px",
127
+ width: "100%",
128
+ marginBottom: "30px",
129
+ textAlign: "center",
130
+ fontSize: "18px",
131
+ lineHeight: "1.5"
132
+ },
133
+ dangerouslySetInnerHTML: { __html: instructions }
134
+ }
135
+ ), /* @__PURE__ */ React.createElement("div", { style: {
136
+ display: "flex",
137
+ flexDirection: "row",
138
+ gap: "50px",
139
+ justifyContent: "center",
140
+ alignItems: "flex-start",
141
+ flexWrap: "wrap"
142
+ } }, /* @__PURE__ */ React.createElement(
143
+ TangramOption,
144
+ {
145
+ tangram: tangramLeft,
146
+ buttonText: buttonTextLeft,
147
+ onClick: () => handleResponse("left"),
148
+ disabled: hasResponded,
149
+ showDecomposition: showTangramDecomposition,
150
+ usePrimitiveColors,
151
+ primitiveColorIndices
152
+ }
153
+ ), /* @__PURE__ */ React.createElement(
154
+ TangramOption,
155
+ {
156
+ tangram: tangramRight,
157
+ buttonText: buttonTextRight,
158
+ onClick: () => handleResponse("right"),
159
+ disabled: hasResponded,
160
+ showDecomposition: showTangramDecomposition,
161
+ usePrimitiveColors,
162
+ primitiveColorIndices
163
+ }
164
+ )));
165
+ }
166
+ function TangramOption({
167
+ tangram,
168
+ buttonText,
169
+ onClick,
170
+ disabled,
171
+ showDecomposition,
172
+ usePrimitiveColors,
173
+ primitiveColorIndices
174
+ }) {
175
+ if (!tangram) {
176
+ return /* @__PURE__ */ React.createElement("div", { style: { width: 300, height: 300, background: "#eee" } }, "No Tangram Data");
177
+ }
178
+ const CANON = /* @__PURE__ */ new Set([
179
+ "square",
180
+ "smalltriangle",
181
+ "parallelogram",
182
+ "medtriangle",
183
+ "largetriangle"
184
+ ]);
185
+ const filteredTans = tangram.solutionTans.filter((tan) => {
186
+ const tanName = tan.name ?? tan.kind;
187
+ return CANON.has(tanName);
188
+ });
189
+ const mask = filteredTans.map((tan) => {
190
+ const polygon = tan.vertices.map(([x, y]) => ({ x: x ?? 0, y: -(y ?? 0) }));
191
+ return polygon;
192
+ });
193
+ const primitiveDecomposition = filteredTans.map((tan) => ({
194
+ kind: tan.name ?? tan.kind,
195
+ polygon: tan.vertices.map(([x, y]) => ({ x: x ?? 0, y: -(y ?? 0) }))
196
+ }));
197
+ const DISPLAY_SIZE = 300;
198
+ const viewport = {
199
+ w: DISPLAY_SIZE,
200
+ h: DISPLAY_SIZE
201
+ };
202
+ const scaleS = React.useMemo(() => {
203
+ const u = inferUnitFromPolys(mask);
204
+ return u ? CONFIG.layout.grid.unitPx / u : 1;
205
+ }, [mask]);
206
+ const centerPos = {
207
+ cx: viewport.w / 2,
208
+ cy: viewport.h / 2
209
+ };
210
+ const pathD = (poly) => {
211
+ if (!poly || poly.length === 0) return "";
212
+ const moves = poly.map((p, i) => `${i === 0 ? "M" : "L"} ${p.x} ${p.y}`);
213
+ return moves.join(" ") + " Z";
214
+ };
215
+ const renderSilhouette = () => {
216
+ if (showDecomposition) {
217
+ const rawPolys = primitiveDecomposition.map((primInfo) => primInfo.polygon);
218
+ const placedPolys = placeSilhouetteGridAlignedAsPolys(rawPolys, scaleS, centerPos);
219
+ return /* @__PURE__ */ React.createElement("g", { key: "sil-decomposed", pointerEvents: "none" }, placedPolys.map((scaledPoly, i) => {
220
+ const primInfo = primitiveDecomposition[i];
221
+ let fillColor = CONFIG.color.silhouetteMask;
222
+ if (usePrimitiveColors && primInfo?.kind && primitiveColorIndices) {
223
+ const kindToIndex = {
224
+ "square": 0,
225
+ "smalltriangle": 1,
226
+ "parallelogram": 2,
227
+ "medtriangle": 3,
228
+ "largetriangle": 4
229
+ };
230
+ const primitiveIndex = kindToIndex[primInfo.kind];
231
+ if (primitiveIndex !== void 0 && primitiveColorIndices[primitiveIndex] !== void 0) {
232
+ const colorIndex = primitiveColorIndices[primitiveIndex];
233
+ const color = CONFIG.color.primitiveColors[colorIndex];
234
+ if (color) {
235
+ fillColor = color;
236
+ }
237
+ }
238
+ }
239
+ return /* @__PURE__ */ React.createElement(React.Fragment, { key: `prim-${i}` }, /* @__PURE__ */ React.createElement(
240
+ "path",
241
+ {
242
+ d: pathD(scaledPoly),
243
+ fill: fillColor,
244
+ opacity: usePrimitiveColors ? CONFIG.opacity.piece.normal : CONFIG.opacity.silhouetteMask,
245
+ stroke: "none"
246
+ }
247
+ ), /* @__PURE__ */ React.createElement(
248
+ "path",
249
+ {
250
+ d: pathD(scaledPoly),
251
+ fill: "none",
252
+ stroke: CONFIG.color.tangramDecomposition.stroke,
253
+ strokeWidth: CONFIG.size.stroke.tangramDecompositionPx
254
+ }
255
+ ));
256
+ }));
257
+ } else {
258
+ const placedPolys = placeSilhouetteGridAlignedAsPolys(mask, scaleS, centerPos);
259
+ return /* @__PURE__ */ React.createElement("g", { key: "sil-unified", pointerEvents: "none" }, placedPolys.map((scaledPoly, i) => {
260
+ const primInfo = primitiveDecomposition[i];
261
+ let fillColor = CONFIG.color.silhouetteMask;
262
+ if (usePrimitiveColors && primInfo?.kind && primitiveColorIndices) {
263
+ const kindToIndex = {
264
+ "square": 0,
265
+ "smalltriangle": 1,
266
+ "parallelogram": 2,
267
+ "medtriangle": 3,
268
+ "largetriangle": 4
269
+ };
270
+ const primitiveIndex = kindToIndex[primInfo.kind];
271
+ if (primitiveIndex !== void 0 && primitiveColorIndices[primitiveIndex] !== void 0) {
272
+ const colorIndex = primitiveColorIndices[primitiveIndex];
273
+ const color = CONFIG.color.primitiveColors[colorIndex];
274
+ if (color) {
275
+ fillColor = color;
276
+ }
277
+ }
278
+ }
279
+ return /* @__PURE__ */ React.createElement(
280
+ "path",
281
+ {
282
+ key: `sil-${i}`,
283
+ d: pathD(scaledPoly),
284
+ fill: fillColor,
285
+ opacity: usePrimitiveColors ? CONFIG.opacity.piece.normal : CONFIG.opacity.silhouetteMask,
286
+ stroke: "none"
287
+ }
288
+ );
289
+ }));
290
+ }
291
+ };
292
+ return /* @__PURE__ */ React.createElement("div", { style: {
293
+ display: "flex",
294
+ flexDirection: "column",
295
+ alignItems: "center",
296
+ gap: "20px"
297
+ } }, /* @__PURE__ */ React.createElement(
298
+ "svg",
299
+ {
300
+ width: viewport.w,
301
+ height: viewport.h,
302
+ viewBox: `0 0 ${viewport.w} ${viewport.h}`,
303
+ style: {
304
+ display: "block",
305
+ background: CONFIG.color.bands.silhouette.fillEven,
306
+ border: `${CONFIG.size.stroke.bandPx}px solid ${CONFIG.color.bands.silhouette.stroke}`,
307
+ borderRadius: "8px"
308
+ }
309
+ },
310
+ renderSilhouette()
311
+ ), /* @__PURE__ */ React.createElement(
312
+ "button",
313
+ {
314
+ className: "jspsych-btn",
315
+ onClick,
316
+ disabled,
317
+ style: {
318
+ padding: "12px 30px",
319
+ fontSize: "16px",
320
+ cursor: disabled ? "not-allowed" : "pointer",
321
+ opacity: disabled ? 0.5 : 1
322
+ }
323
+ },
324
+ buttonText
325
+ ));
326
+ }
327
+
328
+ const info = {
329
+ name: "tangram-afc",
330
+ version: "1.0.0",
331
+ parameters: {
332
+ /** Left tangram specification to display */
333
+ tangram_left: {
334
+ type: ParameterType.COMPLEX,
335
+ default: void 0,
336
+ description: "TangramSpec object defining left target shape to display"
337
+ },
338
+ /** Right tangram specification to display */
339
+ tangram_right: {
340
+ type: ParameterType.COMPLEX,
341
+ default: void 0,
342
+ description: "TangramSpec object defining right target shape to display"
343
+ },
344
+ /** HTML content to display above the tangrams as instructions */
345
+ instructions: {
346
+ type: ParameterType.STRING,
347
+ default: "",
348
+ description: "HTML content to display above the tangrams as instructions"
349
+ },
350
+ /** Text to display on left response button */
351
+ button_text_left: {
352
+ type: ParameterType.STRING,
353
+ default: "Select Left",
354
+ description: "Text to display on left response button"
355
+ },
356
+ /** Text to display on right response button */
357
+ button_text_right: {
358
+ type: ParameterType.STRING,
359
+ default: "Select Right",
360
+ description: "Text to display on right response button"
361
+ },
362
+ /** Whether to show tangram decomposed into individual primitives with borders */
363
+ show_tangram_decomposition: {
364
+ type: ParameterType.BOOL,
365
+ default: false,
366
+ description: "Whether to show tangram decomposed into individual primitives with borders"
367
+ },
368
+ /** Whether to use distinct colors for each primitive shape type */
369
+ use_primitive_colors: {
370
+ type: ParameterType.BOOL,
371
+ default: false,
372
+ description: "Whether each primitive shape type should have its own distinct color in the displayed tangram"
373
+ },
374
+ /** Indices mapping primitives to colors from the color palette */
375
+ primitive_color_indices: {
376
+ type: ParameterType.OBJECT,
377
+ default: [0, 1, 2, 3, 4],
378
+ description: "Array of 5 integers indexing into primitiveColors array, mapping [square, smalltriangle, parallelogram, medtriangle, largetriangle] to colors"
379
+ },
380
+ /** Callback fired when trial ends */
381
+ onTrialEnd: {
382
+ type: ParameterType.FUNCTION,
383
+ default: void 0,
384
+ description: "Callback when trial completes with full data"
385
+ }
386
+ },
387
+ data: {
388
+ /** Reaction time in milliseconds */
389
+ rt: {
390
+ type: ParameterType.INT,
391
+ description: "Milliseconds between trial start and button click"
392
+ },
393
+ /** Response choice */
394
+ response: {
395
+ type: ParameterType.STRING,
396
+ description: "Which button was clicked: 'left' or 'right'"
397
+ }
398
+ },
399
+ citations: ""
400
+ };
401
+ class TangramAFCPlugin {
402
+ constructor(jsPsych) {
403
+ this.jsPsych = jsPsych;
404
+ }
405
+ static {
406
+ this.info = info;
407
+ }
408
+ /**
409
+ * Launches the trial by invoking startAFCTrial
410
+ * with the display element, parameters, and jsPsych instance.
411
+ */
412
+ trial(display_element, trial) {
413
+ const wrappedOnTrialEnd = (data) => {
414
+ if (trial.onTrialEnd) {
415
+ trial.onTrialEnd(data);
416
+ }
417
+ const reactContext = display_element.__reactContext;
418
+ if (reactContext?.root) {
419
+ reactContext.root.unmount();
420
+ }
421
+ display_element.innerHTML = "";
422
+ this.jsPsych.finishTrial(data);
423
+ };
424
+ const params = {
425
+ tangramLeft: trial.tangram_left,
426
+ tangramRight: trial.tangram_right,
427
+ instructions: trial.instructions,
428
+ buttonTextLeft: trial.button_text_left,
429
+ buttonTextRight: trial.button_text_right,
430
+ showTangramDecomposition: trial.show_tangram_decomposition,
431
+ usePrimitiveColors: trial.use_primitive_colors,
432
+ primitiveColorIndices: trial.primitive_color_indices,
433
+ onTrialEnd: wrappedOnTrialEnd
434
+ };
435
+ const { root, display_element: element, jsPsych } = startAFCTrial(display_element, params, this.jsPsych);
436
+ element.__reactContext = { root, jsPsych };
437
+ }
438
+ }
439
+
440
+ export { TangramAFCPlugin as default };
441
+ //# sourceMappingURL=index.js.map