fix(graph): use class sticky for sticky header (#23082)

This commit is contained in:
Emily Xiong 2024-05-01 11:10:19 -04:00 committed by GitHub
parent 8cf7191809
commit 4f4f77c68f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 44 additions and 129 deletions

View File

@ -1,4 +1,3 @@
import { forwardRef } from 'react';
import { TargetConfigurationGroupHeader } from '../target-configuration-details-group-header/target-configuration-details-group-header';
export interface TargetConfigurationGroupContainerProps {
@ -7,27 +6,21 @@ export interface TargetConfigurationGroupContainerProps {
children: React.ReactNode;
}
export const TargetConfigurationGroupContainer = forwardRef(
(
{
targetGroupName,
targetsNumber,
children,
}: TargetConfigurationGroupContainerProps,
ref: React.Ref<any>
) => {
return (
<div>
<div ref={ref} className="mb-4 w-full">
<TargetConfigurationGroupHeader
targetGroupName={targetGroupName}
targetsNumber={targetsNumber}
/>
<div className="rounded-md border border-slate-200 p-2 dark:border-slate-700">
{children}
</div>
</div>
export function TargetConfigurationGroupContainer({
targetGroupName,
targetsNumber,
children,
}: TargetConfigurationGroupContainerProps) {
return (
<div className="mb-4 w-full">
<TargetConfigurationGroupHeader
targetGroupName={targetGroupName}
targetsNumber={targetsNumber}
className="sticky top-0 z-10 bg-white dark:bg-slate-900"
/>
<div className="rounded-md border border-slate-200 p-2 dark:border-slate-700">
{children}
</div>
);
}
);
</div>
);
}

View File

@ -1,12 +1,9 @@
/* eslint-disable @nx/enforce-module-boundaries */
// nx-ignore-next-line
import type { ProjectGraphProjectNode } from '@nx/devkit';
import { RefObject, createRef, useEffect, useRef, useState } from 'react';
import { Transition } from '@headlessui/react';
import { TargetConfigurationDetailsListItem } from '../target-configuration-details-list-item/target-configuration-details-list-item';
import { TargetConfigurationGroupContainer } from '../target-configuration-details-group-container/target-configuration-details-group-container';
import { TargetConfigurationGroupHeader } from '../target-configuration-details-group-header/target-configuration-details-group-header';
import { groupTargets } from '../utils/group-targets';
export interface TargetConfigurationGroupListProps {
@ -29,80 +26,13 @@ export function TargetConfigurationGroupList({
onViewInTaskGraph,
className = '',
}: TargetConfigurationGroupListProps) {
const [stickyHeaderContent, setStickHeaderContent] = useState('');
const targetsGroup = groupTargets(project);
const targetGroupRefs = useRef(
Object.keys(targetsGroup.groups).reduce((acc, targetGroupName) => {
acc[targetGroupName] = createRef();
return acc;
}, {} as Record<string, RefObject<any>>)
);
const targetNameRefs = useRef(
targetsGroup.targets.reduce((acc, targetName) => {
acc[targetName] = createRef();
return acc;
}, {} as Record<string, RefObject<any>>)
);
useEffect(() => {
window.addEventListener('scroll', isSticky);
return () => {
window.removeEventListener('scroll', isSticky);
};
}, []);
const isSticky = () => {
const scrollTop = window.scrollY + 30; // 30px for the header
const foundTargetGroup: string | undefined = Object.keys(
targetGroupRefs.current
).find((targetGroupName) => {
const targetGroup = targetGroupRefs.current[targetGroupName];
if (
targetGroup &&
targetGroup.current &&
scrollTop >= targetGroup.current.offsetTop &&
scrollTop <
targetGroup.current.offsetTop + targetGroup.current.offsetHeight
) {
return true;
}
return false;
});
if (foundTargetGroup) {
setStickHeaderContent(foundTargetGroup);
} else {
setStickHeaderContent('');
}
};
return (
<>
<Transition
show={!!stickyHeaderContent}
enter="transition-opacity ease-linear duration-100"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="transition-opacity ease-linear duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed left-0 right-0 top-0 z-10 mb-8 border-b-2 border-slate-900/10 bg-slate-50 dark:border-slate-300/10 dark:bg-slate-800 dark:text-slate-300">
<div className="mx-auto max-w-6xl px-8 pt-2">
<TargetConfigurationGroupHeader
targetGroupName={stickyHeaderContent}
targetsNumber={
project.data.metadata?.targetGroups?.[stickyHeaderContent]
?.length ?? 0
}
/>
</div>
</div>
</Transition>
{Object.entries(targetsGroup.groups).map(([targetGroupName, targets]) => {
return (
<TargetConfigurationGroupContainer
ref={targetGroupRefs.current[targetGroupName]}
targetGroupName={targetGroupName}
targetsNumber={targets.length}
key={targetGroupName}
@ -110,7 +40,6 @@ export function TargetConfigurationGroupList({
<ul className={className}>
{targets.map((targetName) => (
<TargetConfigurationDetailsListItem
ref={targetNameRefs.current[targetName]}
project={project}
sourceMap={sourceMap}
variant={variant}
@ -129,7 +58,6 @@ export function TargetConfigurationGroupList({
{targetsGroup.targets.map((targetName) => {
return (
<TargetConfigurationDetailsListItem
ref={targetNameRefs.current[targetName]}
project={project}
sourceMap={sourceMap}
variant={variant}

View File

@ -1,7 +1,6 @@
/* eslint-disable @nx/enforce-module-boundaries */
// nx-ignore-next-line
import type { ProjectGraphProjectNode } from '@nx/devkit';
import { forwardRef, Ref } from 'react';
import TargetConfigurationDetails from '../target-configuration-details/target-configuration-details';
export interface TargetConfigurationDetailsListItemProps {
@ -17,36 +16,31 @@ export interface TargetConfigurationDetailsListItemProps {
collapsable: boolean;
}
export const TargetConfigurationDetailsListItem = forwardRef(
(
{
project,
variant,
sourceMap,
onRunTarget,
onViewInTaskGraph,
targetName,
collapsable,
}: TargetConfigurationDetailsListItemProps,
ref: Ref<HTMLLIElement>
) => {
const target = project.data.targets?.[targetName];
if (!target) {
return null;
}
return (
<li className="mb-4 last:mb-0" key={`target-${targetName}`} ref={ref}>
<TargetConfigurationDetails
variant={variant}
projectName={project.name}
targetName={targetName}
targetConfiguration={target}
sourceMap={sourceMap}
onRunTarget={onRunTarget}
onViewInTaskGraph={onViewInTaskGraph}
collapsable={collapsable}
/>
</li>
);
export function TargetConfigurationDetailsListItem({
project,
variant,
sourceMap,
onRunTarget,
onViewInTaskGraph,
targetName,
collapsable,
}: TargetConfigurationDetailsListItemProps) {
const target = project.data.targets?.[targetName];
if (!target) {
return null;
}
);
return (
<li className="mb-4 last:mb-0" key={`target-${targetName}`}>
<TargetConfigurationDetails
variant={variant}
projectName={project.name}
targetName={targetName}
targetConfiguration={target}
sourceMap={sourceMap}
onRunTarget={onRunTarget}
onViewInTaskGraph={onViewInTaskGraph}
collapsable={collapsable}
/>
</li>
);
}