Files
strudel/packages/react/src/hooks/useStrudel.mjs
T

127 lines
3.0 KiB
JavaScript

import { useRef, useCallback, useEffect, useMemo, useState } from 'react';
import core from '@strudel.cycles/core';
const repl = core.repl
import { transpiler } from '@strudel.cycles/transpiler';
import usePostMessage from './usePostMessage.mjs';
function useStrudel({
defaultOutput,
interval,
getTime,
evalOnMount = false,
initialCode = '',
autolink = false,
beforeEval,
afterEval,
onEvalError,
onToggle,
}) {
const id = useMemo(() => s4(), []);
// scheduler
const [schedulerError, setSchedulerError] = useState();
const [evalError, setEvalError] = useState();
const [code, setCode] = useState(initialCode);
const [activeCode, setActiveCode] = useState();
const [pattern, setPattern] = useState();
const [started, setStarted] = useState(false);
const isDirty = code !== activeCode;
// TODO: make sure this hook reruns when scheduler.started changes
const { scheduler, evaluate, start, stop, pause } = useMemo(
() =>
repl({
interval,
defaultOutput,
onSchedulerError: setSchedulerError,
onEvalError: (err) => {
setEvalError(err);
onEvalError?.(err);
},
getTime,
transpiler,
beforeEval: ({ code }) => {
setCode(code);
beforeEval?.();
},
afterEval: ({ pattern: _pattern, code }) => {
setActiveCode(code);
setPattern(_pattern);
setEvalError();
setSchedulerError();
if (autolink) {
window.location.hash = '#' + encodeURIComponent(btoa(code));
}
afterEval?.();
},
onToggle: (v) => {
setStarted(v);
onToggle?.(v);
},
}),
[defaultOutput, interval, getTime],
);
const broadcast = usePostMessage(({ data: { from, type } }) => {
if (type === 'start' && from !== id) {
// console.log('message', from, type);
stop();
}
});
const activateCode = useCallback(
async (autostart = true) => {
await evaluate(code, autostart);
broadcast({ type: 'start', from: id });
},
[evaluate, code],
);
const inited = useRef();
useEffect(() => {
if (!inited.current && evalOnMount && code) {
inited.current = true;
activateCode();
}
}, [activateCode, evalOnMount, code]);
// this will stop the scheduler when hot reloading in development
useEffect(() => {
return () => {
scheduler.stop();
};
}, [scheduler]);
const togglePlay = async () => {
if (started) {
scheduler.pause();
} else {
await activateCode();
}
};
const error = schedulerError || evalError;
return {
code,
setCode,
error,
schedulerError,
scheduler,
evalError,
evaluate,
activateCode,
activeCode,
isDirty,
pattern,
started,
start,
stop,
pause,
togglePlay,
};
}
export default useStrudel;
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}