import {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {useDomScale} from '@/react/context/dom-scale.js'; import {useRadians} from '@/react/context/radians.js'; import useAnimationFrame from '@/react/hooks/use-animation-frame.js'; import {RESOLUTION} from '@/util/constants.js'; import {render} from '@/util/dialogue.js'; import DialogueCaret from './dialogue-caret.jsx'; import styles from './dialogue.module.css'; export default function Dialogue({ camera, dialogue, scale, }) { const domScale = useDomScale(); const ref = useRef(); const [dimensions, setDimensions] = useState({h: 0, w: 0}); const [caret, setCaret] = useState(0); const radians = useRadians(); useEffect(() => { return dialogue.addSkipListener(() => { if (caret >= dialogue.letters.length - 1) { dialogue.onClose(); } else { setCaret(dialogue.letters.length - 1); } }); }, [caret, dialogue]); useEffect(() => { const {params} = dialogue.letters[caret]; let handle; if (caret >= dialogue.letters.length - 1) { const {linger} = dialogue; if (!linger) { return; } handle = setTimeout(() => { dialogue.onClose(); }, linger * 1000); } else { let jump = caret; while (0 === dialogue.letters[jump].params.rate.frequency && jump < dialogue.letters.length - 1) { jump += 1; } setCaret(jump); if (jump < dialogue.letters.length - 1) { handle = setTimeout(() => { setCaret(caret + 1); }, params.rate.frequency * 1000) } } return () => { clearTimeout(handle); } }, [caret, dialogue]); const updateDimensions = useCallback(() => { if (ref.current) { const {height, width} = ref.current.getBoundingClientRect(); setDimensions({h: height / domScale, w: width / domScale}); } }, [domScale]); useAnimationFrame(updateDimensions); const localRender = useMemo( () => render(dialogue.letters, styles.letter), [dialogue.letters], ); let position = 'function' === typeof dialogue.position ? dialogue.position() : dialogue.position || {x: 0, y: 0}; position = { x: position.x + dialogue.offset.x, y: position.y + dialogue.offset.y, }; const bounds = { x: dimensions.w / (2 * scale), y: dimensions.h / (2 * scale), }; const left = Math.max( Math.min( position.x * scale, RESOLUTION.x - bounds.x * scale - 16 + camera.x, ), bounds.x * scale + 16 + camera.x, ); const top = Math.max( Math.min( position.y * scale - dimensions.h / 2, RESOLUTION.y - bounds.y * scale - 16 + camera.y, ), bounds.y * scale + 16 + camera.y, ); return (

{localRender(caret, radians)}

); }