vojo/src/app/components/message/content/EventContent.tsx

92 lines
3 KiB
TypeScript

import { Box, Icon, IconSrc, color } from 'folds';
import React, { ReactNode, useRef } from 'react';
import classNames from 'classnames';
import * as layoutCss from '../layout/layout.css';
import { ChannelEventContent } from '../layout/Channel';
import { useStreamLayoutDebug } from '../layout/streamDebug';
import { ScreenSize, useScreenSizeContext } from '../../../hooks/useScreenSize';
export type EventContentProps = {
time: ReactNode;
iconSrc: IconSrc;
content: ReactNode;
railStart?: boolean;
railEnd?: boolean;
// M3: pick rendering. `'stream'` = 3-track rail/dot grid (DM/Bots).
// `'channel'` = no-rail flex row (channels timeline). Time slot is
// dropped for channels — syslines render compact without timestamp.
layout?: 'stream' | 'channel';
};
export function EventContent({
time,
iconSrc,
content,
railStart,
railEnd,
layout = 'stream',
}: EventContentProps) {
const compact = useScreenSizeContext() === ScreenSize.Mobile;
const rootRef = useRef<HTMLDivElement>(null);
const timeRef = useRef<HTMLSpanElement>(null);
const railRef = useRef<HTMLSpanElement>(null);
const dotRef = useRef<HTMLSpanElement>(null);
const bodyRef = useRef<HTMLDivElement>(null);
useStreamLayoutDebug(
'sysline',
{
root: rootRef,
timeColumn: timeRef,
rail: railRef,
dot: dotRef,
content: bodyRef,
},
true
);
if (layout === 'channel') {
return <ChannelEventContent iconSrc={iconSrc} content={content} />;
}
// Sysline = thin one-line state-event row that lives ON the rail.
// Same 2-track grid as message rows (StreamRoot) — track 1 dot column,
// track 2 body — so the dot's X aligns with the dots above and below it.
// The timestamp trails the body (content → time), mirroring the message
// header's nick → time order now that the dot leads the row.
return (
<div
className={classNames(layoutCss.StreamRoot({ compact }), layoutCss.StreamSysline)}
ref={rootRef}
>
<span className={layoutCss.StreamDotColumn} aria-hidden>
<span
className={classNames(
layoutCss.StreamRail,
railStart && railEnd && layoutCss.StreamRailSingle,
railStart && !railEnd && layoutCss.StreamSyslineRailStart,
railEnd && !railStart && layoutCss.StreamSyslineRailEnd
)}
ref={railRef}
/>
<span
className={classNames(layoutCss.StreamDotHalo, layoutCss.StreamSyslineDotHalo)}
ref={dotRef}
>
<span
className={layoutCss.StreamDotFill}
style={{ backgroundColor: color.Surface.OnContainer, opacity: 0.42 }}
/>
</span>
</span>
<Box gap="200" alignItems="Center" style={{ minWidth: 0 }} ref={bodyRef}>
<Icon style={{ opacity: 0.5, flexShrink: 0 }} size="50" src={iconSrc} />
<div className={layoutCss.StreamSyslineBody}>{content}</div>
{time && (
<span className={layoutCss.StreamSyslineTime} ref={timeRef}>
{time}
</span>
)}
</Box>
</div>
);
}