35 lines
1.8 KiB
TypeScript
35 lines
1.8 KiB
TypeScript
import { useLayoutEffect, useRef, type ReactNode } from 'react';
|
|
|
|
/** Keep the same composer mounted while the empty state becomes a conversation. */
|
|
export function CodingChatBody({ welcome, hidden, bubble, children }: {
|
|
welcome: boolean; hidden: boolean; bubble?: HTMLElement | null; children: ReactNode;
|
|
}) {
|
|
const ref = useRef<HTMLDivElement>(null);
|
|
useLayoutEffect(() => {
|
|
const body = ref.current;
|
|
const content = body?.querySelector<HTMLElement>('.classroom-chat-view');
|
|
if (!body || !content || !welcome || hidden || !bubble) return;
|
|
const update = () => {
|
|
const area = body.getBoundingClientRect();
|
|
const message = bubble.getBoundingClientRect();
|
|
const overlaps = message.height > 0 && message.right > area.left && message.left < area.right && message.bottom > area.top;
|
|
// The teacher floats over the page; it never narrows or shifts the main column.
|
|
// Only compact screens need scrollable space to reveal the welcome below the overlay.
|
|
if (overlaps && area.width < 900) {
|
|
content.style.setProperty('--teacher-top-inset', `${Math.max(0, Math.ceil(message.bottom - content.getBoundingClientRect().top + 24))}px`);
|
|
} else {
|
|
content.style.removeProperty('--teacher-top-inset');
|
|
}
|
|
};
|
|
update();
|
|
const observer = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(update);
|
|
observer?.observe(body); observer?.observe(bubble);
|
|
window.addEventListener('resize', update);
|
|
return () => {
|
|
observer?.disconnect(); window.removeEventListener('resize', update);
|
|
content.style.removeProperty('--teacher-top-inset');
|
|
};
|
|
}, [welcome, hidden, bubble]);
|
|
return <div ref={ref} className="coding-chat-body" data-welcome={welcome} hidden={hidden}>{children}</div>;
|
|
}
|