Initial commit: igny8 project

This commit is contained in:
igny8
2025-11-09 10:27:02 +00:00
commit 60b8188111
27265 changed files with 4360521 additions and 0 deletions

View File

@@ -0,0 +1,173 @@
/**
* ToggleTableRow Component
* Reusable component for displaying long HTML content in table rows with expand/collapse functionality
*/
import React, { useState, useRef, useEffect } from 'react';
import { ChevronDownIcon, HorizontaLDots } from '../../icons';
import HTMLContentRenderer from './HTMLContentRenderer';
interface ToggleTableRowProps {
/** The row data */
row: any;
/** The column key that contains the toggleable content */
contentKey: string;
/** Custom label for the expanded content (e.g., "Content Outline", "Description") */
contentLabel?: string;
/** Column span for the expanded row (should match number of columns in table) */
colSpan: number;
/** Whether the row is expanded (controlled) */
isExpanded?: boolean;
/** Whether the row is initially expanded (uncontrolled) */
defaultExpanded?: boolean;
/** Callback when toggle state changes */
onToggle?: (expanded: boolean, rowId: string | number) => void;
/** Custom className */
className?: string;
}
const ToggleTableRow: React.FC<ToggleTableRowProps> = ({
row,
contentKey,
contentLabel = 'Content',
colSpan,
isExpanded: controlledExpanded,
defaultExpanded = false,
onToggle,
className = '',
}) => {
// Use controlled state if provided, otherwise use internal state
const [internalExpanded, setInternalExpanded] = useState(defaultExpanded);
const isExpanded = controlledExpanded !== undefined ? controlledExpanded : internalExpanded;
const [contentHeight, setContentHeight] = useState<number | 'auto'>('auto');
const contentRef = useRef<HTMLDivElement>(null);
// Get content - handle fallback to description if primary contentKey is empty
let content = row[contentKey];
if (!content || (typeof content === 'string' && content.trim().length === 0)) {
// Try fallback to description if primary content is empty
content = row.description || row.content_outline || null;
}
// Check if content exists - handle both strings and objects
const hasContent = content && (
typeof content === 'string'
? content.trim().length > 0
: typeof content === 'object' && content !== null && Object.keys(content).length > 0
);
useEffect(() => {
if (isExpanded && contentRef.current) {
// Measure content height for smooth animation
const height = contentRef.current.scrollHeight;
setContentHeight(height);
} else {
setContentHeight(0);
}
}, [isExpanded, content]);
const handleToggle = () => {
if (!hasContent) return;
const newExpanded = !isExpanded;
// Update internal state if uncontrolled
if (controlledExpanded === undefined) {
setInternalExpanded(newExpanded);
}
// Notify parent
if (onToggle) {
onToggle(newExpanded, row.id ?? row.id);
}
};
if (!hasContent) {
return null;
}
// Don't render anything if not expanded - no row HTML before toggle
if (!isExpanded) {
return null;
}
return (
<tr
className={`toggle-content-row expanded ${className}`}
aria-hidden={false}
>
<td
colSpan={colSpan}
className="px-5 py-0 bg-gray-50 dark:bg-gray-800/50 border-b border-gray-200 dark:border-white/[0.05]"
>
<div
ref={contentRef}
className="overflow-hidden"
>
<div className="py-4 px-2">
<div className="mb-2 text-xs font-semibold uppercase text-gray-500 dark:text-gray-400 tracking-wide">
{contentLabel}
</div>
<div className="html-content-wrapper">
<HTMLContentRenderer
content={content}
className="text-sm text-gray-700 dark:text-gray-300 leading-relaxed"
/>
</div>
</div>
</div>
</td>
</tr>
);
};
/**
* Toggle Button Component - To be used in table cells
*/
interface ToggleButtonProps {
/** Whether the row is expanded */
isExpanded: boolean;
/** Click handler */
onClick: () => void;
/** Whether content exists */
hasContent: boolean;
/** Custom className */
className?: string;
}
export const ToggleButton: React.FC<ToggleButtonProps> = ({
isExpanded,
onClick,
hasContent,
className = '',
}) => {
if (!hasContent) {
return (
<span className={`inline-flex items-center justify-center w-8 h-8 text-gray-300 dark:text-gray-600 ${className}`}>
<HorizontaLDots className="w-4 h-4" />
</span>
);
}
return (
<button
type="button"
onClick={onClick}
className={`inline-flex items-center justify-center w-8 h-8 rounded-lg transition-all duration-200 ${
isExpanded
? 'text-blue-600 dark:text-blue-400 bg-blue-50 dark:bg-blue-900/20'
: 'text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800'
} ${className}`}
aria-label={isExpanded ? 'Collapse content' : 'Expand content'}
aria-expanded={isExpanded}
>
<ChevronDownIcon
className={`w-4 h-4 transition-transform duration-200 ${
isExpanded ? 'rotate-180' : ''
}`}
/>
</button>
);
};
export default ToggleTableRow;