fix(ui): back recent header only when stuck
This commit is contained in:
@@ -1705,9 +1705,10 @@ const SessionSidebarComponent: React.FC<SessionSidebarProps> = ({
|
|||||||
openSidebarMenuKey={openSidebarMenuKey}
|
openSidebarMenuKey={openSidebarMenuKey}
|
||||||
expansionState={recentExpandedParents}
|
expansionState={recentExpandedParents}
|
||||||
variant="section"
|
variant="section"
|
||||||
|
isDesktopShellRuntime={isDesktopShellRuntime}
|
||||||
/>
|
/>
|
||||||
) : null,
|
) : null,
|
||||||
[activitySections, editingId, hasSessionSearchQuery, isVSCode, openSidebarMenuKey, recentExpandedParents, renderSessionNode, showRecentSection],
|
[activitySections, editingId, hasSessionSearchQuery, isDesktopShellRuntime, isVSCode, openSidebarMenuKey, recentExpandedParents, renderSessionNode, showRecentSection],
|
||||||
);
|
);
|
||||||
const isInlineEditing = Boolean(renamingFolderId || editingId || editingProjectDialogId);
|
const isInlineEditing = Boolean(renamingFolderId || editingId || editingProjectDialogId);
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
resolveMenuOpenSessionId,
|
resolveMenuOpenSessionId,
|
||||||
} from './sessionNodeItemUtils';
|
} from './sessionNodeItemUtils';
|
||||||
import type { SessionNodeRenderExtras } from './sessionNodeItemUtils';
|
import type { SessionNodeRenderExtras } from './sessionNodeItemUtils';
|
||||||
|
import { useStickyHeader } from './hooks/useStickyProjectHeaders';
|
||||||
|
|
||||||
type ActivityItem = {
|
type ActivityItem = {
|
||||||
node: SessionNode;
|
node: SessionNode;
|
||||||
@@ -45,6 +46,7 @@ type Props = {
|
|||||||
variant?: 'section' | 'flat';
|
variant?: 'section' | 'flat';
|
||||||
initialVisibleCount?: number;
|
initialVisibleCount?: number;
|
||||||
batchSize?: number;
|
batchSize?: number;
|
||||||
|
isDesktopShellRuntime: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type RenderExtras = SessionNodeRenderExtras;
|
type RenderExtras = SessionNodeRenderExtras;
|
||||||
@@ -60,12 +62,17 @@ export function SidebarActivitySections(props: Props): React.ReactNode {
|
|||||||
variant = 'section',
|
variant = 'section',
|
||||||
initialVisibleCount = MAX_VISIBLE_RECENT_SESSIONS,
|
initialVisibleCount = MAX_VISIBLE_RECENT_SESSIONS,
|
||||||
batchSize = MAX_VISIBLE_RECENT_SESSIONS,
|
batchSize = MAX_VISIBLE_RECENT_SESSIONS,
|
||||||
|
isDesktopShellRuntime,
|
||||||
} = props;
|
} = props;
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const stickyZoneHeaders = useSessionDisplayStore((state) => state.stickyZoneHeaders);
|
const stickyZoneHeaders = useSessionDisplayStore((state) => state.stickyZoneHeaders);
|
||||||
const [collapsed, setCollapsed] = React.useState<Set<string>>(new Set());
|
const [collapsed, setCollapsed] = React.useState<Set<string>>(new Set());
|
||||||
const [visibleCountBySection, setVisibleCountBySection] = React.useState<Map<string, number>>(new Map());
|
const [visibleCountBySection, setVisibleCountBySection] = React.useState<Map<string, number>>(new Map());
|
||||||
const flatVariant = variant === 'flat';
|
const flatVariant = variant === 'flat';
|
||||||
|
const { isStuck: isRecentHeaderStuck, sentinelRef: recentHeaderSentinelRef } = useStickyHeader({
|
||||||
|
enabled: stickyZoneHeaders && !flatVariant,
|
||||||
|
isDesktopShellRuntime,
|
||||||
|
});
|
||||||
|
|
||||||
const resetSectionLimit = React.useCallback((key: string) => {
|
const resetSectionLimit = React.useCallback((key: string) => {
|
||||||
setVisibleCountBySection((prev) => {
|
setVisibleCountBySection((prev) => {
|
||||||
@@ -177,11 +184,17 @@ export function SidebarActivitySections(props: Props): React.ReactNode {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={section.key} className="relative space-y-1">
|
<div key={section.key} className="relative space-y-1">
|
||||||
{/* Zone header styled like a project header band; sticky with a
|
{/* Zone header styled like a project header band; its solid
|
||||||
solid sidebar backing so rows never show through. */}
|
backing applies only after the header is actually stuck. */}
|
||||||
|
<div
|
||||||
|
ref={recentHeaderSentinelRef}
|
||||||
|
className="absolute top-0 h-px w-full pointer-events-none"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
<div className={cn(
|
<div className={cn(
|
||||||
'-ml-2.5 -mr-2',
|
'-ml-2.5 -mr-2',
|
||||||
stickyZoneHeaders && 'oc-zone-header-backing sticky top-0 z-20 bg-sidebar',
|
stickyZoneHeaders && 'sticky top-0 z-20 bg-sidebar',
|
||||||
|
stickyZoneHeaders && isRecentHeaderStuck && 'oc-zone-header-backing',
|
||||||
)}>
|
)}>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -7,6 +7,37 @@ type Args = {
|
|||||||
projectHeaderSentinelRefs: React.MutableRefObject<Map<string, HTMLDivElement | null>>;
|
projectHeaderSentinelRefs: React.MutableRefObject<Map<string, HTMLDivElement | null>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type StickyHeaderArgs = {
|
||||||
|
enabled: boolean;
|
||||||
|
isDesktopShellRuntime: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useStickyHeader = (args: StickyHeaderArgs) => {
|
||||||
|
const { enabled, isDesktopShellRuntime } = args;
|
||||||
|
const [sentinel, setSentinel] = React.useState<HTMLDivElement | null>(null);
|
||||||
|
const [isStuck, setIsStuck] = React.useState(false);
|
||||||
|
|
||||||
|
const sentinelRef = React.useCallback((node: HTMLDivElement | null) => {
|
||||||
|
setSentinel(node);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!enabled || !isDesktopShellRuntime || !sentinel) {
|
||||||
|
setIsStuck(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const observer = new IntersectionObserver(([entry]) => {
|
||||||
|
setIsStuck(entry.intersectionRatio < 1);
|
||||||
|
}, { threshold: 1 });
|
||||||
|
|
||||||
|
observer.observe(sentinel);
|
||||||
|
return () => observer.disconnect();
|
||||||
|
}, [enabled, isDesktopShellRuntime, sentinel]);
|
||||||
|
|
||||||
|
return { isStuck, sentinelRef };
|
||||||
|
};
|
||||||
|
|
||||||
export const useStickyProjectHeaders = (args: Args): Set<string> => {
|
export const useStickyProjectHeaders = (args: Args): Set<string> => {
|
||||||
const { enabled = true, isDesktopShellRuntime, projectSections, projectHeaderSentinelRefs } = args;
|
const { enabled = true, isDesktopShellRuntime, projectSections, projectHeaderSentinelRefs } = args;
|
||||||
const [stuckProjectHeaders, setStuckProjectHeaders] = React.useState<Set<string>>(new Set());
|
const [stuckProjectHeaders, setStuckProjectHeaders] = React.useState<Set<string>>(new Set());
|
||||||
|
|||||||
Reference in New Issue
Block a user