Skip to content

Instantly share code, notes, and snippets.

@fastestOrange
Created May 21, 2021 18:21
Show Gist options
  • Save fastestOrange/1a6d71e40e8d6a17c4726720d78bf387 to your computer and use it in GitHub Desktop.
Save fastestOrange/1a6d71e40e8d6a17c4726720d78bf387 to your computer and use it in GitHub Desktop.
import React from "react";
import { Machine, mapState, matchesState } from "xstate";
import cx from "classnames";
import nested from "nested-property";
import objectToNotation from "object-to-dot-notation";
export default class Lights extends React.PureComponent {
constructor(props) {
super(props);
this.machine = Machine({
key: "machine",
parallel: true,
states: {
lights: {
initial: "on.green",
states: {
on: {
on: { TOGGLE: "off" },
initial: "green",
states: {
green: {
on: {
TIMER: "yellow"
}
},
yellow: {
on: {
TIMER: "red"
}
},
red: {
on: {
TIMER: "green"
},
initial: "walk",
states: {
walk: {
on: {
PED_TIMER: "wait"
}
},
wait: {
on: {
PED_TIMER: "stop"
}
},
stop: {
PED_TIMER: "green"
}
}
}
}
},
off: {
on: { TOGGLE: "on" }
}
}
},
animation: {
initial: "on",
states: {
on: {
on: { TOGGLE_ANIMATION: "off" }
},
off: {
on: { TOGGLE_ANIMATION: "on" }
}
}
}
}
});
this.interval = null;
this.state = {
...this.machine.initialState
};
}
componentDidMount() {
if (this.state.animation === "on") {
this.toggleAnimation();
}
}
componentDidCatch() {
clearInterval(this.interval);
this.interval = null;
}
componentWillUnmount() {
clearInterval(this.interval);
}
toggleAnimation = () => {
console.log('toggleAnimation', this.state.animation);
if (this.state.animation === "on") {
this.interval = setInterval(this.updateLights, 1200);
} else {
clearInterval(this.interval);
this.interval = null;
}
};
updateLights = () => {
const { lights } = this.state;
const action = matchesState("on.red.walk", lights) ? "PED_TIMER" : "TIMER";
console.log("lights", objectToNotation(lights), action);
this.setState({
...this.machine.transition(this.state, action).value
});
};
handleClickAnimation = () => {
this.setState(
{
...this.machine.transition(this.state, "TOGGLE_ANIMATION").value
},
() => {
this.toggleAnimation();
}
);
};
handleClickStatus = () => {
const { lights } = this.state;
let nextState = this.machine.transition(
{
...this.state,
animation: lights === 'off' ? 'on' : 'off',
},
"TOGGLE"
).value;
this.setState(nextState, () => {
console.log("animation", objectToNotation(this.state.animation));
console.log("lights", objectToNotation(this.state.lights));
this.toggleAnimation();
});
};
render() {
const { animation, lights } = this.state;
const output = {
cta: animation === "off" ? "START" : "STOP",
status: lights === "off" ? "FIX" : "BREAK"
};
if (lights === "off") {
output.cta = "DISABLED";
}
return (
<div>
<div className="traffic-lights">
<ul className="cars">
<li>
<div
className={cx("red", {
active: matchesState("on.red", lights),
broken: lights === "off"
})}
/>
</li>
<li>
<div
className={cx("yellow", {
active: matchesState("on.yellow", lights)
})}
/>
</li>
<li>
<div
className={cx("green", {
active: matchesState("on.green", lights)
})}
/>
</li>
</ul>
<div className="pedestrians">
<div
className={cx("red", {
active: !matchesState("on.red", lights),
broken: lights === "off",
blink: matchesState("on.red.wait", lights)
})}
>
DON'T WALK
</div>
<div
className={cx("green", {
active: matchesState("on.red", lights)
})}
>
WALK
</div>
</div>
</div>
<div>
<button
className="advance"
onClick={this.handleClickAnimation}
disabled={lights === "off"}
>
{output.cta}
</button>
<br />
<br />
<button
onClick={this.handleClickStatus}
className={matchesState("off", lights) ? "off" : ""}
>
{output.status}
</button>
</div>
</div>
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment