Skip to main content
Glama
Table.tsx8.31 kB
'use client'; import { MoveDiagonal } from 'lucide-react'; import { type FC, type HTMLAttributes, useState } from 'react'; import { cn } from '../../utils/cn'; import { Button } from '../Button'; import { ExpandCollapse } from '../ExpandCollapse'; import { Modal, ModalSize } from '../Modal'; /** * Properties for the Table component that extends standard HTML table attributes * * @interface TableProps * @extends {HTMLAttributes<HTMLTableElement>} * * @property {boolean} [isRollable] - Whether the table content can be collapsed/expanded using the ExpandCollapse wrapper * @property {string} [className] - Additional CSS classes for custom styling * @property {ReactNode} children - The table content including thead, tbody, tfoot elements * * @example * ```tsx * // Basic table usage * <Table> * <thead> * <tr> * <th>Name</th> * <th>Email</th> * </tr> * </thead> * <tbody> * <tr> * <td>John Doe</td> * <td>john@example.com</td> * </tr> * </tbody> * </Table> * * // Collapsible table with custom styling * <Table isRollable className="border border-gray-300 rounded-lg"> * <thead> * <tr> * <th>Product</th> * <th>Price</th> * <th>Stock</th> * </tr> * </thead> * <tbody> * <tr> * <td>Laptop</td> * <td>$999</td> * <td>15</td> * </tr> * </tbody> * </Table> * ``` */ type TableProps = HTMLAttributes<HTMLTableElement> & { isRollable?: boolean; displayModal?: boolean; }; /** * Table component that provides an enhanced table experience with modal expansion and collapsible content * * The Table component wraps a standard HTML table element with additional functionality: * - **Modal Expansion**: Click the diagonal arrow button to view the table in a full-screen modal * - **Collapsible Content**: Optionally wrap content in an ExpandCollapse component for space-saving * - **Responsive Design**: Handles large tables gracefully with modal overflow * - **Sticky Controls**: Table controls remain accessible even when scrolling * * ## Features * - **Modal View**: Full-screen modal for better viewing of large tables * - **Expand/Collapse**: Optional collapsible wrapper to save space * - **Responsive**: Handles overflow and responsive behavior automatically * - **Accessibility**: Maintains proper table semantics and keyboard navigation * - **Customizable**: Supports all standard HTML table attributes and styling * * ## Best Practices * - Use semantic HTML table structure (thead, tbody, tfoot) * - Provide proper column headers with scope attributes * - Use the isRollable prop for large tables that might need space management * - Apply consistent styling through the className prop * - Consider pagination for very large datasets * * @param {TableProps} props - The properties for the Table component * @returns {JSX.Element} The rendered table with enhanced functionality * * @example * ```tsx * // Simple data table * <Table> * <thead> * <tr> * <th scope="col">Name</th> * <th scope="col">Email</th> * <th scope="col">Status</th> * </tr> * </thead> * <tbody> * <tr> * <td>John Doe</td> * <td>john@example.com</td> * <td> * <span className="px-2 py-1 bg-green-100 text-green-800 rounded-full text-xs"> * Active * </span> * </td> * </tr> * <tr> * <td>Jane Smith</td> * <td>jane@example.com</td> * <td> * <span className="px-2 py-1 bg-yellow-100 text-yellow-800 rounded-full text-xs"> * Pending * </span> * </td> * </tr> * </tbody> * </Table> * * // Large collapsible table with custom styling * <Table * isRollable * className="border border-gray-200 rounded-lg overflow-hidden" * > * <thead className="bg-gray-50"> * <tr> * <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase"> * Product ID * </th> * <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase"> * Name * </th> * <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase"> * Category * </th> * <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase"> * Price * </th> * <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase"> * Stock * </th> * </tr> * </thead> * <tbody className="bg-white divide-y divide-gray-200"> * {products.map((product) => ( * <tr key={product.id} className="hover:bg-gray-50"> * <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900"> * #{product.id} * </td> * <td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900"> * {product.name} * </td> * <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> * {product.category} * </td> * <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900"> * ${product.price.toFixed(2)} * </td> * <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> * {product.stock} units * </td> * </tr> * ))} * </tbody> * </Table> * * // Financial data table with formatted numbers * <Table className="w-full border-collapse"> * <thead> * <tr className="border-b-2 border-gray-300"> * <th scope="col" className="text-left py-3 px-4">Quarter</th> * <th scope="col" className="text-right py-3 px-4">Revenue</th> * <th scope="col" className="text-right py-3 px-4">Profit</th> * <th scope="col" className="text-right py-3 px-4">Growth</th> * </tr> * </thead> * <tbody> * <tr className="border-b border-gray-200"> * <td className="py-3 px-4 font-medium">Q1 2024</td> * <td className="py-3 px-4 text-right">$2,450,000</td> * <td className="py-3 px-4 text-right text-green-600">$345,000</td> * <td className="py-3 px-4 text-right text-green-600">+12.5%</td> * </tr> * <tr className="border-b border-gray-200"> * <td className="py-3 px-4 font-medium">Q2 2024</td> * <td className="py-3 px-4 text-right">$2,780,000</td> * <td className="py-3 px-4 text-right text-green-600">$398,000</td> * <td className="py-3 px-4 text-right text-green-600">+13.5%</td> * </tr> * </tbody> * </Table> * ``` * * @see {@link ExpandCollapse} - Component used for collapsible table content * @see {@link Modal} - Component used for full-screen table view * @see {@link Button} - Component used for the modal trigger button */ export const Table: FC<TableProps> = ({ className, isRollable = false, displayModal, ...props }) => { const [isModalOpen, setIsModalOpen] = useState(false); return ( <div className="relative"> {displayModal && ( <div className="sticky top-48 z-10"> <div className="absolute top-4 right-2"> <Button variant="hoverable" size="icon-md" onClick={() => { setIsModalOpen(true); }} label="Move" Icon={MoveDiagonal} /> </div> </div> )} <ExpandCollapse isRollable={isRollable} className="max-w-full overflow-x-auto" > <table className={cn( 'min-w-full max-w-full table-auto overflow-x-auto bg-background text-left', className )} {...props} /> </ExpandCollapse> <Modal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)} size={ModalSize.XL} hasCloseButton > {isModalOpen ? ( <div className="grid"> <table className={cn( 'min-w-full max-w-full table-auto text-left', className )} {...props} /> </div> ) : ( <></> )} </Modal> </div> ); };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/aymericzip/intlayer'

If you have feedback or need assistance with the MCP directory API, please join our Discord server