Skip to content

Commit b308f86

Browse files
committed
Add foreach plugin
1 parent 13e5057 commit b308f86

File tree

4 files changed

+140
-35
lines changed

4 files changed

+140
-35
lines changed

src/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { HashRouter as Router, Routes, Route } from "react-router-dom";
22
import Clock from "./plugins/clock";
3+
import ForEach from "./plugins/foreach";
34
import OnLoad from "./plugins/onLoad";
45
import Landing from "./pages/Landing";
56

@@ -9,6 +10,7 @@ function App() {
910
<Routes>
1011
<Route path="/" element={<Landing />} />
1112
<Route path="/clock" element={<Clock />} />
13+
<Route path="/foreach" element={<ForEach />} />
1214
<Route path="/onload" element={<OnLoad />} />
1315
</Routes>
1416
</Router>

src/pages/Landing.tsx

Lines changed: 6 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,19 @@ export default function Landing() {
1212
}, 3000);
1313
};
1414

15-
const clockUrl = `${window.location.origin}/simons-sigmacomputing-plugins/#/clock`;
16-
const onloadUrl = `${window.location.origin}/simons-sigmacomputing-plugins/#/onload`;
15+
const clockUrl = `${window.location.origin}/ryans-sigmacomputing-plugins/#/foreach`;
1716

1817
return (
1918
<div style={landingStyles.container}>
2019
<header style={landingStyles.header}>
2120
<h1 style={landingStyles.headerTitle}>
22-
Simon's Sigma Computing Plugins
21+
Ryan's Sigma Computing Plugins
2322
</h1>
2423
<p style={landingStyles.subtitle}>
25-
This is a Github Pages site for Simon's utility plugins, usually
24+
This is a Github Pages site for ryan's utility plugins, usually
2625
focused around action integrations in Sigma. The source code is
2726
available under an open license on{" "}
28-
<a href="https://github.com/simonsigmacomputing/simons-sigmacomputing-plugins">
27+
<a href="https://github.com/ryansigmacomputing/ryans-sigmacomputing-plugins">
2928
Github
3029
</a>
3130
.
@@ -36,7 +35,7 @@ export default function Landing() {
3635
<h2 style={landingStyles.pluginsSectionTitle}>Available Plugins</h2>
3736

3837
<div style={landingStyles.pluginCard}>
39-
<h3 style={landingStyles.pluginCardTitle}>Clock Plugin</h3>
38+
<h3 style={landingStyles.pluginCardTitle}>ForEach Plugin</h3>
4039
<p style={landingStyles.pluginDescription}>
4140
A timer-based plugin that executes actions at regular intervals.
4241
Configure the tick rate in milliseconds, control the running
@@ -61,37 +60,10 @@ export default function Landing() {
6160
</button>
6261
</div>
6362
</div>
64-
65-
<div style={landingStyles.pluginCard}>
66-
<h3 style={landingStyles.pluginCardTitle}>OnLoad Plugin</h3>
67-
<p style={landingStyles.pluginDescription}>
68-
An initialization plugin that triggers an action when loaded, with
69-
optional configurable delay. Ideal for setting up workflows or
70-
executing startup actions automatically.
71-
</p>
72-
<p style={landingStyles.pluginFeatures}>
73-
<strong>Features:</strong> Automatic on-load triggering, optional
74-
delay (currently broken due to Sigma-side bug), manual step
75-
execution
76-
</p>
77-
<div style={landingStyles.urlContainer}>
78-
<strong>URL:</strong>{" "}
79-
<code style={landingStyles.pluginCode}>{onloadUrl}</code>
80-
<button
81-
style={landingStyles.copyButton}
82-
onClick={() => {
83-
copyToClipboard(onloadUrl);
84-
}}
85-
title="Copy URL to clipboard"
86-
>
87-
{copiedUrl === onloadUrl ? "✓ Copied!" : "Copy"}
88-
</button>
89-
</div>
90-
</div>
9163
</section>
9264
</main>
9365
<footer style={landingStyles.footer}>
94-
<p>&copy; 2025 Simon's Sigma Computing Plugins</p>
66+
<p>&copy; 2025 ryan's Sigma Computing Plugins</p>
9567
</footer>
9668
</div>
9769
);

src/plugins/foreach.tsx

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import { useEffect, useMemo } from "react";
2+
import {
3+
useConfig,
4+
useEditorPanelConfig,
5+
useActionTrigger,
6+
useActionEffect,
7+
useVariable,
8+
} from "@sigmacomputing/plugin";
9+
10+
import type { ActualVariable } from "../types";
11+
import {
12+
pluginContainerStyles,
13+
pluginHeaderStyles,
14+
pluginTitleStyles,
15+
pluginContentStyles,
16+
pluginStatusItemStyles,
17+
pluginLabelStyles,
18+
pluginValueStyles,
19+
statusColors,
20+
} from "../styles/pluginStyles";
21+
22+
interface ForEachConfig_t {
23+
index: string;
24+
endIndex: string;
25+
isDisabled: string;
26+
doOneLoop: string;
27+
runNextLoop: string;
28+
}
29+
30+
function ForEach() {
31+
useEditorPanelConfig([
32+
{
33+
type: "variable",
34+
name: "index",
35+
label: "Current index we are looping over",
36+
allowedTypes: ["number"],
37+
},
38+
{
39+
type: "variable",
40+
name: "endIndex",
41+
label: "Index to end at",
42+
allowedTypes: ["number"],
43+
},
44+
{
45+
type: "variable",
46+
name: "isDisabled",
47+
label: "Is Disabled?",
48+
allowedTypes: ["boolean"],
49+
},
50+
{ type: "action-effect", name: "doOneLoop", label: "Do One Loop" },
51+
{ type: "action-trigger", name: "runNextLoop", label: "Run next loop" },
52+
]);
53+
54+
const config: ForEachConfig_t = useConfig() as ForEachConfig_t;
55+
56+
// Extract the variables from config panel
57+
const [indexVar] = useVariable(config.index);
58+
const [endIndexVar] = useVariable(config.endIndex);
59+
const [isDisabledVar] = useVariable(config.isDisabled);
60+
61+
62+
const index = useMemo(() => {
63+
const value = indexVar?.defaultValue as ActualVariable | undefined;
64+
return value?.type === "number" && value.value !== null
65+
? value.value
66+
: 0;
67+
}, [indexVar]);
68+
69+
const endIndex = useMemo(() => {
70+
const value = endIndexVar?.defaultValue as ActualVariable | undefined;
71+
return value?.type === "number" && value.value !== null
72+
? value.value
73+
: 0;
74+
}, [endIndexVar]);
75+
76+
const isDisabled = useMemo(() => {
77+
const value = isDisabledVar?.defaultValue as ActualVariable | undefined;
78+
return value?.type === "boolean" && value.value !== null
79+
? value.value
80+
: true;
81+
}, [isDisabledVar]);
82+
83+
// Set up action trigger (sending data out)
84+
const runNextLoop = useActionTrigger(config.runNextLoop);
85+
// On receiving effect (input to plugin), do the trigger (output from plugin)
86+
useActionEffect(config.doOneLoop, () => {
87+
runNextLoop();
88+
});
89+
90+
// Set up the interval
91+
useEffect(() => {
92+
if (isDisabled) {
93+
return;
94+
}
95+
if (index >= endIndex) {
96+
return;
97+
}
98+
runNextLoop();
99+
}, [isDisabled, runNextLoop, index, endIndex]);
100+
101+
return (
102+
<div style={pluginContainerStyles}>
103+
<div style={pluginHeaderStyles}>
104+
<h2 style={pluginTitleStyles}>ForEach Plugin</h2>
105+
</div>
106+
<div style={pluginContentStyles}>
107+
<div style={pluginStatusItemStyles}>
108+
<span style={pluginLabelStyles}>Status:</span>
109+
<span
110+
style={{
111+
...pluginValueStyles,
112+
color: isDisabled ? statusColors.stopped : statusColors.running,
113+
}}
114+
>
115+
{isDisabled ? "Disabled" : "Running"}
116+
</span>
117+
</div>
118+
<div style={pluginStatusItemStyles}>
119+
<span style={pluginLabelStyles}>Current Index:</span>
120+
<span style={pluginValueStyles}>{index}</span>
121+
</div>
122+
<div style={pluginStatusItemStyles}>
123+
<span style={pluginLabelStyles}>End Index:</span>
124+
<span style={pluginValueStyles}>{endIndex}</span>
125+
</div>
126+
</div>
127+
</div>
128+
);
129+
}
130+
131+
export default ForEach;

vite.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ export default defineConfig({
1010
},
1111
}),
1212
],
13-
base: "/simons-sigmacomputing-plugins/",
13+
base: "/ryans-sigmacomputing-plugins/",
1414
});

0 commit comments

Comments
 (0)