agent-ecosystem/src/renderer/components/ui/ExpandableContent.tsx
iliya da4d98ec2b refactor: enhance task management protocols and notification handling
- Updated task management instructions in tasks.js to clarify the process for handling newly assigned tasks that must wait due to ongoing work, emphasizing the importance of leaving comments with reasons and estimated completion times.
- Improved member briefing messages to include critical reminders about task status and comment handling.
- Enhanced TeamDataService to implement task comment notification features, ensuring leads are notified of teammate comments on tasks.
- Refactored related UI components to support better interaction and visibility of task statuses and notifications.
2026-03-14 17:46:15 +02:00

110 lines
3.9 KiB
TypeScript

import { useCallback, useRef, useState } from 'react';
import { ChevronDown, ChevronUp } from 'lucide-react';
const DEFAULT_COLLAPSED_HEIGHT = 200; // px
interface ExpandableContentProps {
/** Content to render inside the expandable container. */
children: React.ReactNode;
/** Maximum height (px) before truncation kicks in. Default: 200. */
collapsedHeight?: number;
/** Extra className applied to the outermost wrapper. */
className?: string;
/** Called when the user clicks "Show more" to expand the content. */
onExpand?: () => void;
}
/**
* Generic expand/collapse wrapper with:
* - Collapsed: content clipped at `collapsedHeight`, mask-image fade, "Show more" button
* - Expanded: full content, sticky "Show less" button at viewport bottom
*
* Uses CSS `mask-image` for the fade so it works on any background color
* (zebra stripes, card backgrounds, etc.) without needing to know the bg color.
*/
export const ExpandableContent = ({
children,
collapsedHeight = DEFAULT_COLLAPSED_HEIGHT,
className,
onExpand,
}: ExpandableContentProps): React.JSX.Element => {
const anchorRef = useRef<HTMLDivElement>(null);
const [expanded, setExpanded] = useState(false);
const [needsTruncation, setNeedsTruncation] = useState(false);
// Measure content height via callback ref — re-runs when children change
const measureRef = useCallback(
(node: HTMLDivElement | null) => {
if (node) {
requestAnimationFrame(() => {
setNeedsTruncation(node.scrollHeight > collapsedHeight);
});
}
},
// Re-measure when children identity changes (content prop in callers)
// eslint-disable-next-line react-hooks/exhaustive-deps -- children identity triggers re-measure
[children, collapsedHeight]
);
const handleCollapse = useCallback(() => {
setExpanded(false);
anchorRef.current?.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
}, []);
return (
<div ref={anchorRef} className={className}>
<div
ref={measureRef}
className="relative"
style={
!expanded && needsTruncation
? {
maxHeight: collapsedHeight,
overflow: 'hidden',
WebkitMaskImage: 'linear-gradient(to bottom, black 60%, transparent 100%)',
maskImage: 'linear-gradient(to bottom, black 60%, transparent 100%)',
}
: undefined
}
>
{children}
</div>
{/* Show more */}
{!expanded && needsTruncation ? (
<div className="relative flex justify-center" style={{ marginTop: -15 }}>
<button
type="button"
className="flex items-center gap-1 rounded-md border border-[var(--color-border)] bg-[var(--color-surface)] px-2.5 py-1 text-[11px] text-[var(--color-text-secondary)] shadow-sm transition-colors hover:bg-[var(--color-surface-raised)] hover:text-[var(--color-text)]"
onClick={(e) => {
e.stopPropagation();
setExpanded(true);
onExpand?.();
}}
>
<ChevronDown size={12} />
Show more
</button>
</div>
) : null}
{/* Sticky Show less */}
{expanded && needsTruncation ? (
<div className="sticky bottom-0 z-10 flex justify-center pb-1 pt-2">
<button
type="button"
className="flex items-center gap-1 rounded-md border border-[var(--color-border)] bg-[var(--color-surface-raised)] px-2.5 py-1 text-[11px] text-[var(--color-text-muted)] shadow-sm transition-colors hover:bg-[var(--color-surface)] hover:text-[var(--color-text-secondary)]"
onClick={(e) => {
e.stopPropagation();
handleCollapse();
}}
>
<ChevronUp size={12} />
Show less
</button>
</div>
) : null}
</div>
);
};