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';
|
} from '@renderer/store/slices/teamSlice';
|
||||||
import { createChipFromSelection } from '@renderer/utils/chipUtils';
|
import { createChipFromSelection } from '@renderer/utils/chipUtils';
|
||||||
import { sumContextInjectionTokens } from '@renderer/utils/contextMath';
|
import { sumContextInjectionTokens } from '@renderer/utils/contextMath';
|
||||||
import { formatProjectPath } from '@renderer/utils/pathDisplay';
|
|
||||||
import { buildMemberColorMap } from '@renderer/utils/memberHelpers';
|
import { buildMemberColorMap } from '@renderer/utils/memberHelpers';
|
||||||
|
import { formatProjectPath } from '@renderer/utils/pathDisplay';
|
||||||
import { buildTaskCountsByOwner, normalizePath } from '@renderer/utils/pathNormalize';
|
import { buildTaskCountsByOwner, normalizePath } from '@renderer/utils/pathNormalize';
|
||||||
import { nameColorSet } from '@renderer/utils/projectColor';
|
import { nameColorSet } from '@renderer/utils/projectColor';
|
||||||
import { resolveProjectIdByPath } from '@renderer/utils/projectLookup';
|
import { resolveProjectIdByPath } from '@renderer/utils/projectLookup';
|
||||||
|
|
@ -2011,6 +2011,7 @@ export const TeamDetailView = ({
|
||||||
onReplyToMessage: handleReplyToMessage,
|
onReplyToMessage: handleReplyToMessage,
|
||||||
onRestartTeam: handleRestartTeam,
|
onRestartTeam: handleRestartTeam,
|
||||||
onTaskIdClick: handleTaskIdClick,
|
onTaskIdClick: handleTaskIdClick,
|
||||||
|
inlineScrollContainerRef: contentRef,
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
activeMembers,
|
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 { Sheet, type SheetRef } from 'react-modal-sheet';
|
||||||
|
|
||||||
import { Badge } from '@renderer/components/ui/badge';
|
import { Badge } from '@renderer/components/ui/badge';
|
||||||
|
|
@ -91,6 +100,13 @@ interface MessagesPanelProps {
|
||||||
onRestartTeam?: () => void;
|
onRestartTeam?: () => void;
|
||||||
/** Callback when a task ID link is clicked. */
|
/** Callback when a task ID link is clicked. */
|
||||||
onTaskIdClick?: (taskId: string) => void;
|
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(
|
export function reconcilePendingRepliesByMember(
|
||||||
|
|
@ -214,6 +230,11 @@ export const MessagesPanel = memo(function MessagesPanel({
|
||||||
const sidebarScrollRef = useRef<HTMLDivElement | null>(null);
|
const sidebarScrollRef = useRef<HTMLDivElement | null>(null);
|
||||||
const bottomSheetRef = useRef<SheetRef>(null);
|
const bottomSheetRef = useRef<SheetRef>(null);
|
||||||
const bottomSheetStickyTopRef = useRef<HTMLDivElement | null>(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(() => {
|
const handleExpandContent = useCallback(() => {
|
||||||
// no-op: user is reading expanded content, not composing
|
// no-op: user is reading expanded content, not composing
|
||||||
}, []);
|
}, []);
|
||||||
|
|
@ -1063,6 +1084,7 @@ export const MessagesPanel = memo(function MessagesPanel({
|
||||||
<Sheet.Content
|
<Sheet.Content
|
||||||
className="min-h-0 bg-[var(--color-surface-sidebar)]"
|
className="min-h-0 bg-[var(--color-surface-sidebar)]"
|
||||||
scrollClassName="flex min-h-full flex-col"
|
scrollClassName="flex min-h-full flex-col"
|
||||||
|
scrollRef={bottomSheetScrollRef}
|
||||||
disableDrag={(state) => state.scrollPosition !== 'top'}
|
disableDrag={(state) => state.scrollPosition !== 'top'}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue