Library/Components/Tables/TableExpandable

TableExpandable

react
MatrixTableTablesTableSortable
TableExpandable.tsx — preview
reactlive
TableExpandable.tsx
"use client"

import { useState, useRef, useEffect } from 'react'

interface TableRow {
  id: string
  name: string
  email: string
  status: string
  joinDate: string
  details: {
    phone: string
    address: string
    company: string
    department: string
  }
}

interface TableExpandableProps {
  data?: TableRow[]
}

const defaultData: TableRow[] = [
  {
    id: '1',
    name: 'Sarah Johnson',
    email: 'sarah.johnson@example.com',
    status: 'Active',
    joinDate: '2023-01-15',
    details: {
      phone: '+1 (555) 123-4567',
      address: '123 Oak Street, San Francisco, CA 94102',
      company: 'TechCorp Inc',
      department: 'Engineering'
    }
  },
  {
    id: '2',
    name: 'Michael Chen',
    email: 'michael.chen@example.com',
    status: 'Active',
    joinDate: '2023-03-22',
    details: {
      phone: '+1 (555) 234-5678',
      address: '456 Maple Avenue, New York, NY 10001',
      company: 'InnovateLabs',
      department: 'Product'
    }
  },
  {
    id: '3',
    name: 'Emma Williams',
    email: 'emma.williams@example.com',
    status: 'Inactive',
    joinDate: '2022-11-08',
    details: {
      phone: '+1 (555) 345-6789',
      address: '789 Pine Road, Austin, TX 78701',
      company: 'DataSystems LLC',
      department: 'Analytics'
    }
  },
  {
    id: '4',
    name: 'David Rodriguez',
    email: 'david.rodriguez@example.com',
    status: 'Active',
    joinDate: '2023-05-10',
    details: {
      phone: '+1 (555) 456-7890',
      address: '321 Elm Street, Seattle, WA 98101',
      company: 'CloudNine',
      department: 'Infrastructure'
    }
  },
  {
    id: '5',
    name: 'Lisa Anderson',
    email: 'lisa.anderson@example.com',
    status: 'Active',
    joinDate: '2023-02-28',
    details: {
      phone: '+1 (555) 567-8901',
      address: '654 Birch Lane, Boston, MA 02101',
      company: 'DesignStudio',
      department: 'Design'
    }
  }
]

export default function TableExpandable({ data = defaultData }: TableExpandableProps) {
  const [expandedRows, setExpandedRows] = useState<Set<string>>(new Set())
  const [expandedHeights, setExpandedHeights] = useState<Record<string, number>>({})
  const contentRefs = useRef<Record<string, HTMLDivElement>>({})

  useEffect(() => {
    const newHeights: Record<string, number> = {}
    expandedRows.forEach((rowId) => {
      const ref = contentRefs.current[rowId]
      if (ref) {
        newHeights[rowId] = ref.scrollHeight
      }
    })
    setExpandedHeights(newHeights)
  }, [expandedRows])

  const toggleRow = (rowId: string) => {
    const newExpandedRows = new Set(expandedRows)
    if (newExpandedRows.has(rowId)) {
      newExpandedRows.delete(rowId)
    } else {
      newExpandedRows.add(rowId)
    }
    setExpandedRows(newExpandedRows)
  }

  const expandAll = () => {
    setExpandedRows(new Set(data.map(row => row.id)))
  }

  const collapseAll = () => {
    setExpandedRows(new Set())
  }

  const isAllExpanded = expandedRows.size === data.length && data.length > 0
  const isSomeExpanded = expandedRows.size > 0 && expandedRows.size < data.length

  return (
    <div className="w-full h-screen bg-[#0A0A0A] p-8 overflow-auto">
      <div className="max-w-6xl mx-auto">
        <div className="mb-6 flex items-center justify-between">
          <h1 className="text-3xl font-bold text-[#F5F5F0]">Users Directory</h1>
          <div className="flex gap-3">
            <button
              onClick={expandAll}
              className="px-4 py-2 bg-[#C9A84C] text-[#0A0A0A] font-semibold rounded-lg hover:bg-[#D9B85C] transition-colors duration-200"
            >
              Expand All
            </button>
            <button
              onClick={collapseAll}
              className="px-4 py-2 border border-[#C9A84C] text-[#C9A84C] font-semibold rounded-lg hover:bg-[#C9A84C] hover:text-[#0A0A0A] transition-colors duration-200"
            >
              Collapse All
            </button>
          </div>
        </div>

        <div className="border border-[#333333] rounded-lg overflow-hidden">
          <table className="w-full">
            <thead>
              <tr className="bg-[#1A1A1A] border-b border-[#333333]">
                <th className="w-12 px-6 py-4 text-left"></th>
                <th className="px-6 py-4 text-left text-[#F5F5F0] font-semibold">Name</th>
                <th className="px-6 py-4 text-left text-[#F5F5F0] font-semibold">Email</th>
                <th className="px-6 py-4 text-left text-[#F5F5F0] font-semibold">Status</th>
                <th className="px-6 py-4 text-left text-[#F5F5F0] font-semibold">Join Date</th>
              </tr>
            </thead>
            <tbody>
              {data.map((row, index) => {
                const isExpanded = expandedRows.has(row.id)
                const height = expandedHeights[row.id] || 0

                return (
                  <tbody key={row.id}>
                    <tr
                      className={`border-b border-[#333333] hover:bg-[#1A1A1A] transition-colors duration-200 ${
                        index === data.length - 1 ? '' : ''
                      }`}
                    >
                      <td className="px-6 py-4">
                        <button
                          onClick={() => toggleRow(row.id)}
                          className="w-8 h-8 flex items-center justify-center rounded hover:bg-[#333333] transition-colors duration-200"
                        >
                          <svg
                            className={`w-5 h-5 text-[#C9A84C] transition-transform duration-300 ${
                              isExpanded ? 'rotate-180' : ''
                            }`}
                            fill="none"
                            stroke="currentColor"
                            viewBox="0 0 24 24"
                          >
                            <path
                              strokeLinecap="round"
                              strokeLinejoin="round"
                              strokeWidth={2}
                              d="M19 14l-7 7m0 0l-7-7m7 7V3"
                            />
                          </svg>
                        </button>
                      </td>
                      <td className="px-6 py-4 text-[#F5F5F0]">{row.name}</td>
                      <td className="px-6 py-4 text-[#A9A9A0]">{row.email}</td>
                      <td className="px-6 py-4">
                        <span
                          className={`inline-flex px-3 py-1 rounded-full text-sm font-medium ${
                            row.status === 'Active'
                              ? 'bg-green-900 text-green-200'
                              : 'bg-gray-700 text-gray-300'
                          }`}
                        >
                          {row.status}
                        </span>
                      </td>
                      <td className="px-6 py-4 text-[#A9A9A0]">{row.joinDate}</td>
                    </tr>
                    <tr
                      className="bg-[#1A1A1A] border-b border-[#333333]"
                      style={{
                        maxHeight: isExpanded ? `${height + 32}px` : '0px',
                        overflow: 'hidden',
                        transition: 'max-height 0.3s ease-in-out'
                      }}
                    >
                      <td colSpan={5} className="p-0">
                        <div
                          ref={(el) => {
                            if (el) contentRefs.current[row.id] = el
                          }}
                          className="px-6 py-4"
                        >
                          <div className="grid grid-cols-2 md:grid-cols-4 gap-6">
                            <div>
                              <h4 className="text-[#C9A84C] font-semibold text-sm mb-1">Phone</h4>
                              <p className="text-[#F5F5F0]">{row.details.phone}</p>
                            </div>
                            <div>
                              <h4 className="text-[#C9A84C] font-semibold text-sm mb-1">Company</h4>
                              <p className="text-[#F5F5F0]">{row.details.company}</p>
                            </div>
                            <div>
                              <h4 className="text-[#C9A84C] font-semibold text-sm mb-1">Department</h4>
                              <p className="text-[#F5F5F0]">{row.details.department}</p>
                            </div>
                            <div>
                              <h4 className="text-[#C9A84C] font-semibold text-sm mb-1">Address</h4>
                              <p className="text-[#F5F5F0]">{row.details.address}</p>
                            </div>
                          </div>
                        </div>
                      </td>
                    </tr>
                  </tbody>
                )
              })}
            </tbody>
          </table>
        </div>

        <div className="mt-6 text-[#A9A9A0] text-sm">
          {expandedRows.size > 0 && (
            <p>
              {expandedRows.size} row{expandedRows.size !== 1 ? 's' : ''} expanded
            </p>
          )}
        </div>
      </div>
    </div>
  )
}
Same category

Similar components

MatrixTable
MatrixTable
DataTable
DataTable
AnalyticsTable
AnalyticsTable
DataTable
DataTable