diff --git a/graph/ui-project-details/src/lib/project-details/project-details.tsx b/graph/ui-project-details/src/lib/project-details/project-details.tsx index bcb6894a14..c5bbdc8a50 100644 --- a/graph/ui-project-details/src/lib/project-details/project-details.tsx +++ b/graph/ui-project-details/src/lib/project-details/project-details.tsx @@ -6,11 +6,11 @@ import { GraphError } from 'nx/src/command-line/graph/graph'; /* eslint-enable @nx/enforce-module-boundaries */ import { EyeIcon } from '@heroicons/react/24/outline'; import { PropertyInfoTooltip, Tooltip } from '@nx/graph/ui-tooltips'; -import { TooltipTriggerText } from '../target-configuration-details/tooltip-trigger-text'; import { twMerge } from 'tailwind-merge'; -import { Pill } from '../pill'; -import { TargetTechnologies } from '../target-technologies/target-technologies'; +import { TagList } from '../tag-list/tag-list'; import { TargetConfigurationGroupList } from '../target-configuration-details-group-list/target-configuration-details-group-list'; +import { TooltipTriggerText } from '../target-configuration-details/tooltip-trigger-text'; +import { TargetTechnologies } from '../target-technologies/target-technologies'; export interface ProjectDetailsProps { project: ProjectGraphProjectNode; @@ -97,21 +97,14 @@ export const ProjectDetails = ({ )}
-
+
{projectData.metadata?.description ? (

{projectData.metadata?.description}

) : null} {projectData.tags && projectData.tags.length ? ( -

- Tags: - {projectData.tags?.map((tag) => ( - - - - ))} -

+ ) : null} {projectData.root ? (

diff --git a/graph/ui-project-details/src/lib/tag-list/tag-list.stories.tsx b/graph/ui-project-details/src/lib/tag-list/tag-list.stories.tsx new file mode 100644 index 0000000000..0bbc5cf5dc --- /dev/null +++ b/graph/ui-project-details/src/lib/tag-list/tag-list.stories.tsx @@ -0,0 +1,42 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { TagList } from './tag-list'; + +const meta: Meta = { + component: TagList, + title: 'TagList', +}; +export default meta; + +type Story = StoryObj; + +export const FewTags: Story = { + args: { + tags: ['tag1', 'tag2', 'tag3'], + }, +}; +export const ManyTags: Story = { + args: { + tags: [ + 'tag1', + 'tag2', + 'tag3', + 'tag4', + 'tag5', + 'tag6', + 'tag7', + 'tag8', + 'tag9', + 'tag10', + 'tag11', + 'tag12', + 'tag13', + 'tag14', + 'tag15', + 'tag16', + 'tag17', + 'tag18', + 'tag19', + 'tag20', + ], + }, +}; diff --git a/graph/ui-project-details/src/lib/tag-list/tag-list.tsx b/graph/ui-project-details/src/lib/tag-list/tag-list.tsx new file mode 100644 index 0000000000..6195181425 --- /dev/null +++ b/graph/ui-project-details/src/lib/tag-list/tag-list.tsx @@ -0,0 +1,78 @@ +import React, { useState, useRef, useEffect } from 'react'; +import { Pill } from '../pill'; + +interface TagListProps { + tags: string[]; +} + +export function TagList({ tags }: TagListProps) { + const [isExpanded, _setIsExpanded] = useState(false); + const [isOverflowing, setIsOverflowing] = useState(false); + const tagsContainerRef = useRef(null); + + const checkOverflow = () => { + requestAnimationFrame(() => { + if (tagsContainerRef.current) { + setIsOverflowing( + tagsContainerRef.current.scrollWidth > + tagsContainerRef.current.offsetWidth + ); + } + }); + }; + + const setExpanded = (value: boolean) => { + _setIsExpanded(value); + checkOverflow(); + }; + + useEffect(() => { + checkOverflow(); + + window.addEventListener('resize', checkOverflow); + return () => window.removeEventListener('resize', checkOverflow); + }, [tagsContainerRef]); + + return ( +

+

+ Tags: + + + {tags.map((tag) => ( + + + + ))} + {isExpanded && ( + + )} + + {isOverflowing && !isExpanded && ( + + )} +

+
+ ); +}