refactor(team): wire scroll owners for MessagesPanel layouts
First step toward virtualizing ActivityTimeline. Makes the real scroll container observable per layout, without changing behavior. - `TeamDetailView` forwards `contentRef` to `MessagesPanel` as `inlineScrollContainerRef`. `MessagesPanel` accepts it as an optional prop (unused in this release) so a follow-up can wire the inline viewport to virtualization consumers. - `MessagesPanel` creates `bottomSheetScrollRef` and passes it to `Sheet.Content scrollRef`. react-modal-sheet merges it with its internal scroll ref, so the element stays the same DOM node; this only exposes it to us. - Sidebar already owns `sidebarScrollRef`; no change there. Behavior is unchanged — this only exposes refs for the follow-up that will thread a viewport contract into ActivityTimeline.
This commit is contained in:
parent
52677b55d0
commit
49e029163b
2 changed files with 25 additions and 2 deletions
|
|
@ -39,8 +39,8 @@ import {
|
|||
} from '@renderer/store/slices/teamSlice';
|
||||
import { createChipFromSelection } from '@renderer/utils/chipUtils';
|
||||
import { sumContextInjectionTokens } from '@renderer/utils/contextMath';
|
||||
import { formatProjectPath } from '@renderer/utils/pathDisplay';
|
||||
import { buildMemberColorMap } from '@renderer/utils/memberHelpers';
|
||||
import { formatProjectPath } from '@renderer/utils/pathDisplay';
|
||||
import { buildTaskCountsByOwner, normalizePath } from '@renderer/utils/pathNormalize';
|
||||
import { nameColorSet } from '@renderer/utils/projectColor';
|
||||
import { resolveProjectIdByPath } from '@renderer/utils/projectLookup';
|
||||
|
|
@ -2011,6 +2011,7 @@ export const TeamDetailView = ({
|
|||
onReplyToMessage: handleReplyToMessage,
|
||||
onRestartTeam: handleRestartTeam,
|
||||
onTaskIdClick: handleTaskIdClick,
|
||||
inlineScrollContainerRef: contentRef,
|
||||
}),
|
||||
[
|
||||
activeMembers,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,13 @@
|
|||
import { memo, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
memo,
|
||||
type RefObject,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useLayoutEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { Sheet, type SheetRef } from 'react-modal-sheet';
|
||||
|
||||
import { Badge } from '@renderer/components/ui/badge';
|
||||
|
|
@ -91,6 +100,13 @@ interface MessagesPanelProps {
|
|||
onRestartTeam?: () => void;
|
||||
/** Callback when a task ID link is clicked. */
|
||||
onTaskIdClick?: (taskId: string) => void;
|
||||
/**
|
||||
* Scroll container owned by the parent view when `position === 'inline'`.
|
||||
* MessagesPanel does not own this element — the viewport lives in
|
||||
* TeamDetailView's content scroll area. Plumbed for future viewport
|
||||
* consumers (virtualization); unused in this release.
|
||||
*/
|
||||
inlineScrollContainerRef?: RefObject<HTMLDivElement | null>;
|
||||
}
|
||||
|
||||
export function reconcilePendingRepliesByMember(
|
||||
|
|
@ -214,6 +230,11 @@ export const MessagesPanel = memo(function MessagesPanel({
|
|||
const sidebarScrollRef = useRef<HTMLDivElement | null>(null);
|
||||
const bottomSheetRef = useRef<SheetRef>(null);
|
||||
const bottomSheetStickyTopRef = useRef<HTMLDivElement | null>(null);
|
||||
// Scroll container inside `Sheet.Content` for the bottom-sheet layout.
|
||||
// react-modal-sheet merges this ref with its own internal scroll ref.
|
||||
// Held here so future viewport consumers (virtualization) can observe the
|
||||
// true scrolling element in bottom-sheet mode.
|
||||
const bottomSheetScrollRef = useRef<HTMLDivElement | null>(null);
|
||||
const handleExpandContent = useCallback(() => {
|
||||
// no-op: user is reading expanded content, not composing
|
||||
}, []);
|
||||
|
|
@ -1063,6 +1084,7 @@ export const MessagesPanel = memo(function MessagesPanel({
|
|||
<Sheet.Content
|
||||
className="min-h-0 bg-[var(--color-surface-sidebar)]"
|
||||
scrollClassName="flex min-h-full flex-col"
|
||||
scrollRef={bottomSheetScrollRef}
|
||||
disableDrag={(state) => state.scrollPosition !== 'top'}
|
||||
>
|
||||
<div
|
||||
|
|
|
|||
Loading…
Reference in a new issue