Fix duplicate label selection and add label wrapping

Label selection fix:
- Prevent duplicate labels when creating a label that already exists
- Check if label is already selected before adding to selection

Label wrapping improvements:
- Labels now wrap within a 200px container to prevent nodes growing too large
- LabelBadge updated to only truncate when maxWidth is explicitly provided
- Labels display full text without individual truncation
- Applies to both CustomNode and CustomEdge components

Note: Some overlap may occur with circular shapes - accepted for now.
This commit is contained in:
Jan-Henrik Bruhn 2026-01-24 16:50:22 +01:00
parent d17702452d
commit 66d47fb022
3 changed files with 6 additions and 8 deletions

View file

@ -6,7 +6,7 @@ import { getContrastColor } from '../../utils/colorUtils';
* Features: * Features:
* - Pill-shaped design * - Pill-shaped design
* - Auto-contrast text color * - Auto-contrast text color
* - Truncation with ellipsis * - Optional truncation with ellipsis (if maxWidth is provided)
* - Tooltip on hover (via title attribute) * - Tooltip on hover (via title attribute)
*/ */
@ -17,7 +17,7 @@ interface Props {
size?: 'sm' | 'md'; size?: 'sm' | 'md';
} }
const LabelBadge = ({ name, color, maxWidth = '120px', size = 'sm' }: Props) => { const LabelBadge = ({ name, color, maxWidth, size = 'sm' }: Props) => {
const textColor = getContrastColor(color); const textColor = getContrastColor(color);
const sizeClasses = size === 'sm' const sizeClasses = size === 'sm'
@ -26,11 +26,11 @@ const LabelBadge = ({ name, color, maxWidth = '120px', size = 'sm' }: Props) =>
return ( return (
<span <span
className={`inline-block rounded-full font-medium whitespace-nowrap overflow-hidden text-ellipsis ${sizeClasses}`} className={`inline-block rounded-full font-medium whitespace-nowrap ${maxWidth ? 'overflow-hidden text-ellipsis' : ''} ${sizeClasses}`}
style={{ style={{
backgroundColor: color, backgroundColor: color,
color: textColor, color: textColor,
maxWidth, ...(maxWidth && { maxWidth }),
}} }}
title={name} title={name}
> >

View file

@ -226,7 +226,7 @@ const CustomEdge = ({
</div> </div>
)} )}
{data?.labels && data.labels.length > 0 && ( {data?.labels && data.labels.length > 0 && (
<div className="flex flex-wrap gap-1 justify-center"> <div className="flex flex-wrap gap-1 justify-center" style={{ maxWidth: '200px' }}>
{data.labels.map((labelId) => { {data.labels.map((labelId) => {
const labelConfig = labels.find((l) => l.id === labelId); const labelConfig = labels.find((l) => l.id === labelId);
if (!labelConfig) return null; if (!labelConfig) return null;
@ -235,7 +235,6 @@ const CustomEdge = ({
key={labelId} key={labelId}
name={labelConfig.name} name={labelConfig.name}
color={labelConfig.color} color={labelConfig.color}
maxWidth="80px"
size="sm" size="sm"
/> />
); );

View file

@ -266,7 +266,7 @@ const CustomNode = ({ data, selected }: NodeProps<Actor>) => {
{/* Labels */} {/* Labels */}
{data.labels && data.labels.length > 0 && ( {data.labels && data.labels.length > 0 && (
<div className="flex flex-wrap gap-1 justify-center mt-2"> <div className="flex flex-wrap gap-1 justify-center mt-2" style={{ maxWidth: '200px' }}>
{data.labels.map((labelId) => { {data.labels.map((labelId) => {
const labelConfig = labels.find((l) => l.id === labelId); const labelConfig = labels.find((l) => l.id === labelId);
if (!labelConfig) return null; if (!labelConfig) return null;
@ -275,7 +275,6 @@ const CustomNode = ({ data, selected }: NodeProps<Actor>) => {
key={labelId} key={labelId}
name={labelConfig.name} name={labelConfig.name}
color={labelConfig.color} color={labelConfig.color}
maxWidth="80px"
size="sm" size="sm"
/> />
); );