120 lines
2.9 KiB
TypeScript
120 lines
2.9 KiB
TypeScript
import MuiSlider from "@mui/material/Slider";
|
|
import React from "react";
|
|
import { useState, useEffect, useRef } from "react";
|
|
|
|
type StateItem = {
|
|
value: number,
|
|
status: "open" | "owned" | "locked",
|
|
};
|
|
|
|
type State = Array<StateItem>;
|
|
|
|
type ClientAction = {
|
|
action_type: "grab" | "release",
|
|
slider: number,
|
|
} | {
|
|
action_type: "move",
|
|
slider: number,
|
|
new_value: number,
|
|
}
|
|
|
|
const Sliders: React.FC = () => {
|
|
const ws = useRef<WebSocket>();
|
|
const reconnectInterval = useRef<number>();
|
|
|
|
const [state, setState] = useState<State>();
|
|
|
|
const connect = () => {
|
|
// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/readyState
|
|
if (ws.current !== undefined && ws.current.readyState !== 3) return;
|
|
|
|
const wsURL = new URL("ws", window.location.href);
|
|
wsURL.protocol = wsURL.protocol.replace("http", "ws");
|
|
ws.current = new WebSocket(wsURL.href);
|
|
|
|
ws.current.onmessage = (ev) => {
|
|
setState(JSON.parse(ev.data));
|
|
}
|
|
|
|
}
|
|
|
|
useEffect(() => {
|
|
connect();
|
|
reconnectInterval.current = window.setInterval(connect, 1000);
|
|
|
|
return () => {
|
|
if (reconnectInterval.current !== undefined) window.clearInterval(reconnectInterval.current);
|
|
if (ws.current !== undefined) ws.current.close();
|
|
};
|
|
}, []);
|
|
|
|
const cb = (action: ClientAction) => {
|
|
if (ws.current !== undefined && ws.current.readyState !== 3) {
|
|
ws.current.send(JSON.stringify({
|
|
action: action,
|
|
}));
|
|
}
|
|
}
|
|
|
|
return <>
|
|
{state?.map((item, index) => <Slider key={index} item={item} index={index} cb={cb} />)}
|
|
</>
|
|
}
|
|
|
|
|
|
const styleOverride = {
|
|
"& .MuiSlider-track": { transition: "none" },
|
|
"& .MuiSlider-thumb": { transition: "none" },
|
|
};
|
|
|
|
const Slider: React.FC<{
|
|
item: StateItem,
|
|
index: number,
|
|
cb: (action: ClientAction) => void,
|
|
}> = ({ item, index, cb }) => {
|
|
|
|
const disabled = item.status === "locked";
|
|
const [value, setValue] = useState(item.value);
|
|
|
|
useEffect(() => {
|
|
if (item.status !== "owned") setValue(item.value);
|
|
}, [item]);
|
|
|
|
const onChange = (n: number) => {
|
|
setValue(n);
|
|
cb({
|
|
action_type: "move",
|
|
slider: index,
|
|
new_value: n,
|
|
});
|
|
};
|
|
|
|
const onGrab = () => {
|
|
cb({
|
|
action_type: "grab",
|
|
slider: index,
|
|
});
|
|
};
|
|
|
|
const onRelease = () => {
|
|
cb({
|
|
action_type: "release",
|
|
slider: index,
|
|
});
|
|
};
|
|
|
|
return <MuiSlider
|
|
min={0}
|
|
max={255}
|
|
sx={disabled ? styleOverride : {}}
|
|
disabled={disabled}
|
|
value={value}
|
|
onChange={(_, n) => { onChange(n as number) }}
|
|
onMouseDown={onGrab}
|
|
onTouchStart={onGrab}
|
|
onMouseUp={onRelease}
|
|
onTouchEnd={onRelease}
|
|
/>
|
|
};
|
|
|
|
export default Sliders; |