@adriansteffan/reactive 0.0.42 → 0.0.44
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/.claude/settings.local.json +11 -2
- package/README.md +106 -3
- package/dist/{mod-UqYdghJl.js → mod-D6DlS3ur.js} +6483 -6120
- package/dist/mod.d.ts +54 -2
- package/dist/reactive.es.js +42 -33
- package/dist/reactive.umd.js +38 -36
- package/dist/style.css +1 -1
- package/dist/{web-pL-YTTVv.js → web-D7VcCd-t.js} +1 -1
- package/dist/{web-eGzX65_f.js → web-o3I0sgwu.js} +1 -1
- package/package.json +1 -1
- package/src/components/canvasblock.tsx +112 -70
- package/src/components/checkdevice.tsx +15 -0
- package/src/components/enterfullscreen.tsx +5 -3
- package/src/components/exitfullscreen.tsx +4 -1
- package/src/components/experimentprovider.tsx +7 -2
- package/src/components/experimentrunner.tsx +66 -52
- package/src/components/microphonecheck.tsx +3 -0
- package/src/components/mobilefilepermission.tsx +3 -0
- package/src/components/plaininput.tsx +17 -0
- package/src/components/prolificending.tsx +3 -0
- package/src/components/quest.tsx +58 -8
- package/src/components/storeui.tsx +15 -11
- package/src/components/text.tsx +11 -0
- package/src/components/upload.tsx +56 -271
- package/src/mod.tsx +1 -0
- package/src/utils/bytecode.ts +50 -0
- package/src/utils/simulation.ts +268 -0
- package/src/utils/upload.ts +299 -0
- package/template/README.md +59 -0
- package/template/backend/src/backend.ts +1 -0
- package/template/package.json +2 -0
- package/template/simulate.ts +15 -0
- package/template/src/Experiment.tsx +58 -5
- package/template/src/main.tsx +1 -1
- package/template/tsconfig.json +2 -3
- package/tsconfig.json +1 -0
- package/vite.config.ts +1 -1
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { useState } from 'react';
|
|
3
|
-
import { ExperimentRunner, BaseComponentProps, ExperimentConfig } from '@adriansteffan/reactive';
|
|
2
|
+
import { useState, useRef } from 'react';
|
|
3
|
+
import { ExperimentRunner, BaseComponentProps, ExperimentConfig, registerSimulation } from '@adriansteffan/reactive';
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
const config: ExperimentConfig = { showProgressBar: true };
|
|
7
7
|
|
|
8
|
+
// --- Custom Components ---
|
|
9
|
+
|
|
8
10
|
const CustomTrial = ({ next, maxCount }: BaseComponentProps & { maxCount: number }) => {
|
|
9
11
|
const [count, setCount] = useState(0);
|
|
12
|
+
const startTime = useRef(performance.now());
|
|
10
13
|
|
|
11
14
|
return (
|
|
12
15
|
<>
|
|
@@ -20,7 +23,7 @@ const CustomTrial = ({ next, maxCount }: BaseComponentProps & { maxCount: number
|
|
|
20
23
|
onClick={() => {
|
|
21
24
|
setCount(count + 1);
|
|
22
25
|
if (count + 1 === maxCount) {
|
|
23
|
-
next({});
|
|
26
|
+
next({ totalTime: performance.now() - startTime.current, clicks: maxCount });
|
|
24
27
|
}
|
|
25
28
|
}}
|
|
26
29
|
className='mt-4 px-4 py-2 bg-blue-500 text-white rounded-sm hover:bg-blue-600 transition-colors'
|
|
@@ -31,6 +34,24 @@ const CustomTrial = ({ next, maxCount }: BaseComponentProps & { maxCount: number
|
|
|
31
34
|
);
|
|
32
35
|
};
|
|
33
36
|
|
|
37
|
+
// Register a simulation for the custom trial.
|
|
38
|
+
// The decision function determines how fast the participant clicks.
|
|
39
|
+
// The simulate function uses the trial logic (clicking maxCount times) to produce response data.
|
|
40
|
+
registerSimulation('CustomTrial', (trialProps, _experimentState, simulators, participant) => {
|
|
41
|
+
let totalTime = 0;
|
|
42
|
+
for (let i = 0; i < (trialProps.maxCount || 1); i++) {
|
|
43
|
+
const result = simulators.click(trialProps, participant);
|
|
44
|
+
participant = result.participantState;
|
|
45
|
+
totalTime += result.value;
|
|
46
|
+
}
|
|
47
|
+
return { responseData: { totalTime, clicks: trialProps.maxCount }, participantState: participant, duration: totalTime };
|
|
48
|
+
}, {
|
|
49
|
+
click: (_trialProps: any, participant: any) => ({
|
|
50
|
+
value: 200 + Math.random() * 500,
|
|
51
|
+
participantState: participant,
|
|
52
|
+
}),
|
|
53
|
+
});
|
|
54
|
+
|
|
34
55
|
const CustomQuestion = () => {
|
|
35
56
|
return (
|
|
36
57
|
<>
|
|
@@ -39,7 +60,9 @@ const CustomQuestion = () => {
|
|
|
39
60
|
);
|
|
40
61
|
};
|
|
41
62
|
|
|
42
|
-
|
|
63
|
+
// --- Timeline ---
|
|
64
|
+
|
|
65
|
+
export const experiment = [
|
|
43
66
|
{
|
|
44
67
|
name: 'introtext',
|
|
45
68
|
type: 'Text',
|
|
@@ -57,9 +80,25 @@ const experiment = [
|
|
|
57
80
|
),
|
|
58
81
|
},
|
|
59
82
|
},
|
|
83
|
+
{
|
|
84
|
+
name: 'nickname',
|
|
85
|
+
type: 'PlainInput',
|
|
86
|
+
props: {
|
|
87
|
+
content: <p>What is your nickname?</p>,
|
|
88
|
+
buttonText: 'Submit',
|
|
89
|
+
placeholder: 'Enter your nickname',
|
|
90
|
+
},
|
|
91
|
+
simulators: {
|
|
92
|
+
respond: (_trialProps: any, participant: any) => ({
|
|
93
|
+
value: participant.nickname,
|
|
94
|
+
participantState: participant,
|
|
95
|
+
}),
|
|
96
|
+
},
|
|
97
|
+
},
|
|
60
98
|
{
|
|
61
99
|
name: 'customtrial',
|
|
62
100
|
type: 'CustomTrial',
|
|
101
|
+
simulate: true,
|
|
63
102
|
props: {
|
|
64
103
|
maxCount: 5,
|
|
65
104
|
},
|
|
@@ -115,6 +154,20 @@ export default function Experiment() {
|
|
|
115
154
|
timeline={experiment}
|
|
116
155
|
components={{CustomTrial}}
|
|
117
156
|
questions={{CustomQuestion}}
|
|
157
|
+
hybridParticipant={{ id: 0, nickname: 'test' }}
|
|
118
158
|
/>
|
|
119
159
|
);
|
|
120
|
-
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// --- Simulation config ---
|
|
163
|
+
// Define how simulated participants are generated.
|
|
164
|
+
// Each participant is an object whose properties are available in simulator decision functions.
|
|
165
|
+
export const simulationConfig = {
|
|
166
|
+
participants: {
|
|
167
|
+
generator: (i: number) => ({
|
|
168
|
+
id: i,
|
|
169
|
+
nickname: `participant_${i}`,
|
|
170
|
+
}),
|
|
171
|
+
count: 10,
|
|
172
|
+
},
|
|
173
|
+
};
|
package/template/src/main.tsx
CHANGED
|
@@ -6,7 +6,7 @@ import { ExperimentProvider } from "@adriansteffan/reactive";
|
|
|
6
6
|
|
|
7
7
|
ReactDOM.createRoot(document.getElementById("root")!).render(
|
|
8
8
|
<React.StrictMode>
|
|
9
|
-
<ExperimentProvider disableSettings={import.meta.env.VITE_DISABLE_SETTINGS}>
|
|
9
|
+
<ExperimentProvider disableSettings={import.meta.env.VITE_DISABLE_SETTINGS} disableHybridSimulation={!!import.meta.env.VITE_DISABLE_HYBRID_SIMULATION}>
|
|
10
10
|
<Experiment />
|
|
11
11
|
</ExperimentProvider>
|
|
12
12
|
</React.StrictMode>
|
package/template/tsconfig.json
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
3
|
"noImplicitAny": false,
|
|
4
|
-
"target": "
|
|
4
|
+
"target": "ES2020",
|
|
5
5
|
"useDefineForClassFields": true,
|
|
6
|
-
"lib": ["ES2020", "DOM", "DOM.Iterable"
|
|
6
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
7
7
|
"module": "ESNext",
|
|
8
|
-
"downlevelIteration": true,
|
|
9
8
|
"skipLibCheck": true,
|
|
10
9
|
|
|
11
10
|
/* Bundler mode */
|
package/tsconfig.json
CHANGED
package/vite.config.ts
CHANGED
|
@@ -26,7 +26,7 @@ export default defineConfig(() => {
|
|
|
26
26
|
fileName: (format: string) => `reactive.${format}.js`,
|
|
27
27
|
},
|
|
28
28
|
rollupOptions: {
|
|
29
|
-
external: ['react', 'react-dom'],
|
|
29
|
+
external: ['react', 'react-dom', 'child_process', 'os'],
|
|
30
30
|
output: {
|
|
31
31
|
globals: {
|
|
32
32
|
react: 'React',
|