feat(graph): update tags to render on a single line by default with expand option (#27829)
This commit is contained in:
parent
d72a1d4e6e
commit
b8486fb53f
@ -6,11 +6,11 @@ import { GraphError } from 'nx/src/command-line/graph/graph';
|
|||||||
/* eslint-enable @nx/enforce-module-boundaries */
|
/* eslint-enable @nx/enforce-module-boundaries */
|
||||||
import { EyeIcon } from '@heroicons/react/24/outline';
|
import { EyeIcon } from '@heroicons/react/24/outline';
|
||||||
import { PropertyInfoTooltip, Tooltip } from '@nx/graph/ui-tooltips';
|
import { PropertyInfoTooltip, Tooltip } from '@nx/graph/ui-tooltips';
|
||||||
import { TooltipTriggerText } from '../target-configuration-details/tooltip-trigger-text';
|
|
||||||
import { twMerge } from 'tailwind-merge';
|
import { twMerge } from 'tailwind-merge';
|
||||||
import { Pill } from '../pill';
|
import { TagList } from '../tag-list/tag-list';
|
||||||
import { TargetTechnologies } from '../target-technologies/target-technologies';
|
|
||||||
import { TargetConfigurationGroupList } from '../target-configuration-details-group-list/target-configuration-details-group-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 {
|
export interface ProjectDetailsProps {
|
||||||
project: ProjectGraphProjectNode;
|
project: ProjectGraphProjectNode;
|
||||||
@ -97,21 +97,14 @@ export const ProjectDetails = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap justify-between py-2">
|
<div className="flex flex-wrap justify-between py-2">
|
||||||
<div>
|
<div className="min-w-0">
|
||||||
{projectData.metadata?.description ? (
|
{projectData.metadata?.description ? (
|
||||||
<p className="mb-2 text-sm capitalize text-gray-500 dark:text-slate-400">
|
<p className="mb-2 text-sm capitalize text-gray-500 dark:text-slate-400">
|
||||||
{projectData.metadata?.description}
|
{projectData.metadata?.description}
|
||||||
</p>
|
</p>
|
||||||
) : null}
|
) : null}
|
||||||
{projectData.tags && projectData.tags.length ? (
|
{projectData.tags && projectData.tags.length ? (
|
||||||
<p>
|
<TagList tags={projectData.tags} />
|
||||||
<span className="font-medium">Tags:</span>
|
|
||||||
{projectData.tags?.map((tag) => (
|
|
||||||
<span className="ml-2 font-mono lowercase">
|
|
||||||
<Pill text={tag} />
|
|
||||||
</span>
|
|
||||||
))}
|
|
||||||
</p>
|
|
||||||
) : null}
|
) : null}
|
||||||
{projectData.root ? (
|
{projectData.root ? (
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
@ -0,0 +1,42 @@
|
|||||||
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
|
import { TagList } from './tag-list';
|
||||||
|
|
||||||
|
const meta: Meta<typeof TagList> = {
|
||||||
|
component: TagList,
|
||||||
|
title: 'TagList',
|
||||||
|
};
|
||||||
|
export default meta;
|
||||||
|
|
||||||
|
type Story = StoryObj<typeof TagList>;
|
||||||
|
|
||||||
|
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',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
78
graph/ui-project-details/src/lib/tag-list/tag-list.tsx
Normal file
78
graph/ui-project-details/src/lib/tag-list/tag-list.tsx
Normal file
@ -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<HTMLSpanElement>(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 (
|
||||||
|
<div className="relative max-w-full">
|
||||||
|
<p className="flex min-w-0 font-medium leading-loose">
|
||||||
|
<span className="inline-block">Tags:</span>
|
||||||
|
|
||||||
|
<span
|
||||||
|
className={`inline-block ${
|
||||||
|
isExpanded ? 'whitespace-normal' : 'whitespace-nowrap'
|
||||||
|
} w-full max-w-full overflow-hidden transition-all duration-300`}
|
||||||
|
style={{
|
||||||
|
maskImage:
|
||||||
|
isOverflowing && !isExpanded
|
||||||
|
? 'linear-gradient(to right, black 80%, transparent 100%)'
|
||||||
|
: 'none',
|
||||||
|
}}
|
||||||
|
ref={tagsContainerRef}
|
||||||
|
>
|
||||||
|
{tags.map((tag) => (
|
||||||
|
<span key={tag} className="ml-2 font-mono lowercase">
|
||||||
|
<Pill text={tag} />
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
{isExpanded && (
|
||||||
|
<button
|
||||||
|
onClick={() => setExpanded(false)}
|
||||||
|
className="inline-block px-2 align-middle"
|
||||||
|
>
|
||||||
|
Show less
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
{isOverflowing && !isExpanded && (
|
||||||
|
<button
|
||||||
|
onClick={() => setExpanded(true)}
|
||||||
|
className="ml-1 inline-block whitespace-nowrap"
|
||||||
|
>
|
||||||
|
Show more
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user