fix(graph): use class sticky for sticky header (#23082)
This commit is contained in:
parent
8cf7191809
commit
4f4f77c68f
@ -1,4 +1,3 @@
|
|||||||
import { forwardRef } from 'react';
|
|
||||||
import { TargetConfigurationGroupHeader } from '../target-configuration-details-group-header/target-configuration-details-group-header';
|
import { TargetConfigurationGroupHeader } from '../target-configuration-details-group-header/target-configuration-details-group-header';
|
||||||
|
|
||||||
export interface TargetConfigurationGroupContainerProps {
|
export interface TargetConfigurationGroupContainerProps {
|
||||||
@ -7,27 +6,21 @@ export interface TargetConfigurationGroupContainerProps {
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TargetConfigurationGroupContainer = forwardRef(
|
export function TargetConfigurationGroupContainer({
|
||||||
(
|
|
||||||
{
|
|
||||||
targetGroupName,
|
targetGroupName,
|
||||||
targetsNumber,
|
targetsNumber,
|
||||||
children,
|
children,
|
||||||
}: TargetConfigurationGroupContainerProps,
|
}: TargetConfigurationGroupContainerProps) {
|
||||||
ref: React.Ref<any>
|
|
||||||
) => {
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="mb-4 w-full">
|
||||||
<div ref={ref} className="mb-4 w-full">
|
|
||||||
<TargetConfigurationGroupHeader
|
<TargetConfigurationGroupHeader
|
||||||
targetGroupName={targetGroupName}
|
targetGroupName={targetGroupName}
|
||||||
targetsNumber={targetsNumber}
|
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">
|
<div className="rounded-md border border-slate-200 p-2 dark:border-slate-700">
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
|||||||
@ -1,12 +1,9 @@
|
|||||||
/* eslint-disable @nx/enforce-module-boundaries */
|
/* eslint-disable @nx/enforce-module-boundaries */
|
||||||
// nx-ignore-next-line
|
// nx-ignore-next-line
|
||||||
import type { ProjectGraphProjectNode } from '@nx/devkit';
|
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 { 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 { 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';
|
import { groupTargets } from '../utils/group-targets';
|
||||||
|
|
||||||
export interface TargetConfigurationGroupListProps {
|
export interface TargetConfigurationGroupListProps {
|
||||||
@ -29,80 +26,13 @@ export function TargetConfigurationGroupList({
|
|||||||
onViewInTaskGraph,
|
onViewInTaskGraph,
|
||||||
className = '',
|
className = '',
|
||||||
}: TargetConfigurationGroupListProps) {
|
}: TargetConfigurationGroupListProps) {
|
||||||
const [stickyHeaderContent, setStickHeaderContent] = useState('');
|
|
||||||
const targetsGroup = groupTargets(project);
|
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 (
|
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]) => {
|
{Object.entries(targetsGroup.groups).map(([targetGroupName, targets]) => {
|
||||||
return (
|
return (
|
||||||
<TargetConfigurationGroupContainer
|
<TargetConfigurationGroupContainer
|
||||||
ref={targetGroupRefs.current[targetGroupName]}
|
|
||||||
targetGroupName={targetGroupName}
|
targetGroupName={targetGroupName}
|
||||||
targetsNumber={targets.length}
|
targetsNumber={targets.length}
|
||||||
key={targetGroupName}
|
key={targetGroupName}
|
||||||
@ -110,7 +40,6 @@ export function TargetConfigurationGroupList({
|
|||||||
<ul className={className}>
|
<ul className={className}>
|
||||||
{targets.map((targetName) => (
|
{targets.map((targetName) => (
|
||||||
<TargetConfigurationDetailsListItem
|
<TargetConfigurationDetailsListItem
|
||||||
ref={targetNameRefs.current[targetName]}
|
|
||||||
project={project}
|
project={project}
|
||||||
sourceMap={sourceMap}
|
sourceMap={sourceMap}
|
||||||
variant={variant}
|
variant={variant}
|
||||||
@ -129,7 +58,6 @@ export function TargetConfigurationGroupList({
|
|||||||
{targetsGroup.targets.map((targetName) => {
|
{targetsGroup.targets.map((targetName) => {
|
||||||
return (
|
return (
|
||||||
<TargetConfigurationDetailsListItem
|
<TargetConfigurationDetailsListItem
|
||||||
ref={targetNameRefs.current[targetName]}
|
|
||||||
project={project}
|
project={project}
|
||||||
sourceMap={sourceMap}
|
sourceMap={sourceMap}
|
||||||
variant={variant}
|
variant={variant}
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
/* eslint-disable @nx/enforce-module-boundaries */
|
/* eslint-disable @nx/enforce-module-boundaries */
|
||||||
// nx-ignore-next-line
|
// nx-ignore-next-line
|
||||||
import type { ProjectGraphProjectNode } from '@nx/devkit';
|
import type { ProjectGraphProjectNode } from '@nx/devkit';
|
||||||
import { forwardRef, Ref } from 'react';
|
|
||||||
import TargetConfigurationDetails from '../target-configuration-details/target-configuration-details';
|
import TargetConfigurationDetails from '../target-configuration-details/target-configuration-details';
|
||||||
|
|
||||||
export interface TargetConfigurationDetailsListItemProps {
|
export interface TargetConfigurationDetailsListItemProps {
|
||||||
@ -17,9 +16,7 @@ export interface TargetConfigurationDetailsListItemProps {
|
|||||||
collapsable: boolean;
|
collapsable: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TargetConfigurationDetailsListItem = forwardRef(
|
export function TargetConfigurationDetailsListItem({
|
||||||
(
|
|
||||||
{
|
|
||||||
project,
|
project,
|
||||||
variant,
|
variant,
|
||||||
sourceMap,
|
sourceMap,
|
||||||
@ -27,15 +24,13 @@ export const TargetConfigurationDetailsListItem = forwardRef(
|
|||||||
onViewInTaskGraph,
|
onViewInTaskGraph,
|
||||||
targetName,
|
targetName,
|
||||||
collapsable,
|
collapsable,
|
||||||
}: TargetConfigurationDetailsListItemProps,
|
}: TargetConfigurationDetailsListItemProps) {
|
||||||
ref: Ref<HTMLLIElement>
|
|
||||||
) => {
|
|
||||||
const target = project.data.targets?.[targetName];
|
const target = project.data.targets?.[targetName];
|
||||||
if (!target) {
|
if (!target) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<li className="mb-4 last:mb-0" key={`target-${targetName}`} ref={ref}>
|
<li className="mb-4 last:mb-0" key={`target-${targetName}`}>
|
||||||
<TargetConfigurationDetails
|
<TargetConfigurationDetails
|
||||||
variant={variant}
|
variant={variant}
|
||||||
projectName={project.name}
|
projectName={project.name}
|
||||||
@ -48,5 +43,4 @@ export const TargetConfigurationDetailsListItem = forwardRef(
|
|||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user