会社で使うTailWindCSSのテーブルコンポーネント一覧 | Smart Life Hub

会社で使うTailWindCSSのテーブルコンポーネント一覧

TailwindCSS_Logo

会社で使えるようにTailWindCSSのコンポーネントを羅列していきます。

ユーザー一覧コンポーネント

user-table-1

※画像をクリックで拡大できます。

.tsx
// pages/index.js
import Head from 'next/head'
import UserList from '../components/UserList'

export default function Home() {
  return (
    <div className="bg-gray-100">
      <Head>
        <title>ユーザーリスト</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
      </Head>
      <div className="container mx-auto px-4 sm:px-6 lg:px-8 py-8">
        <UserList />
      </div>
    </div>
  )
}

// components/UserList.js
import { useState } from 'react'
import UserListHeader from './UserListHeader'
import UserListTable from './UserListTable'

export default function UserList() {
  const [users, setUsers] = useState([
    { id: 1, name: '山田 花子', title: 'フロントエンド開発者', email: 'hanako.yamada@example.com', role: 'メンバー' },
    { id: 2, name: '佐藤 太郎', title: 'デザイナー', email: 'taro.sato@example.com', role: '管理者' },
  ])

  const addUser = () => {
    // ユーザー追加のロジックをここに実装
  }

  return (
    <>
      <UserListHeader onAddUser={addUser} />
      <UserListTable users={users} />
    </>
  )
}

// components/UserListHeader.js
export default function UserListHeader({ onAddUser }) {
  return (
    <div className="sm:flex sm:items-center">
      <div className="sm:flex-auto">
        <h1 className="text-base font-semibold leading-6 text-gray-900">ユーザー</h1>
        <p className="mt-2 text-sm text-gray-700">
          アカウント内のすべてのユーザーの名前、役職、メール、役割を含むリストです。
        </p>
      </div>
      <div className="mt-4 sm:ml-16 sm:mt-0 sm:flex-none">
        <button
          type="button"
          onClick={onAddUser}
          className="block rounded-md bg-indigo-600 px-3 py-2 text-center text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
        >
          ユーザー追加
        </button>
      </div>
    </div>
  )
}

// components/UserListTable.js
import { useState, useRef, useEffect } from 'react'

export default function UserListTable({ users }) {
  const [activeMenu, setActiveMenu] = useState(null)
  const menuRef = useRef()

  useEffect(() => {
    const handleClickOutside = (event) => {
      if (menuRef.current && !menuRef.current.contains(event.target)) {
        setActiveMenu(null)
      }
    }

    document.addEventListener('mousedown', handleClickOutside)
    return () => {
      document.removeEventListener('mousedown', handleClickOutside)
    }
  }, [])

  const toggleActions = (userId) => {
    setActiveMenu(activeMenu === userId ? null : userId)
  }

  return (
    <div className="mt-8 flow-root">
      <div className="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
        <div className="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8">
          <table className="min-w-full divide-y divide-gray-300">
            <thead>
              <tr>
                <th scope="col" className="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-0">名前</th>
                <th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">役職</th>
                <th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">メール</th>
                <th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">役割</th>
                <th scope="col" className="relative py-3.5 pl-3 pr-4 sm:pr-0">
                  <span className="sr-only">操作</span>
                </th>
              </tr>
            </thead>
            <tbody className="divide-y divide-gray-200 bg-white">
              {users.map((user) => (
                <tr key={user.id}>
                  <td className="whitespace-nowrap py-5 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-0">{user.name}</td>
                  <td className="whitespace-nowrap px-3 py-5 text-sm text-gray-500">{user.title}</td>
                  <td className="whitespace-nowrap px-3 py-5 text-sm text-gray-500">{user.email}</td>
                  <td className="whitespace-nowrap px-3 py-5 text-sm text-gray-500">{user.role}</td>
                  <td className="relative whitespace-nowrap py-5 pl-3 pr-4 text-right text-sm font-medium sm:pr-0">
                    <button onClick={() => toggleActions(user.id)} className="text-indigo-600 hover:text-indigo-900">
                      編集
                    </button>
                    {activeMenu === user.id && (
                      <div ref={menuRef} className="absolute right-0 mt-2 w-48 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5">
                        <div className="py-1" role="menu" aria-orientation="vertical" aria-labelledby="options-menu">
                          <a href="#" className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" role="menuitem">編集</a>
                          <a href="#" className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" role="menuitem">削除</a>
                        </div>
                      </div>
                    )}
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>
    </div>
  )
}

// pages/_app.js
import '../styles/globals.css'

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

export default MyApp

// styles/globals.css
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';

ユーザー一覧(チェックボックスあり)

user-table-2
.tsx
"use client"

import { useState } from "react"
import { Checkbox } from "@/components/ui/checkbox"
import { Button } from "@/components/ui/button"

interface User {
  id: string
  name: string
  title: string
  email: string
  role: string
}

const initialUsers: User[] = [
  { id: "1", name: "Lindsay Walton", title: "Front-end Developer", email: "lindsay.walton@example.com", role: "Member" },
  { id: "2", name: "Courtney Henry", title: "Designer", email: "courtney.henry@example.com", role: "Admin" },
  { id: "3", name: "Tom Cook", title: "Director of Product", email: "tom.cook@example.com", role: "Member" },
  { id: "4", name: "Whitney Francis", title: "Copywriter", email: "whitney.francis@example.com", role: "Admin" },
  { id: "5", name: "Leonard Krasner", title: "Senior Designer", email: "leonard.krasner@example.com", role: "Owner" },
  { id: "6", name: "Floyd Miles", title: "Principal Designer", email: "floyd.miles@example.com", role: "Member" },
]

export default function Component() {
  const [users, setUsers] = useState<User[]>(initialUsers)
  const [selectedUsers, setSelectedUsers] = useState<string[]>([])

  const handleSelectUser = (userId: string) => {
    setSelectedUsers(prev => 
      prev.includes(userId) ? prev.filter(id => id !== userId) : [...prev, userId]
    )
  }

  const handleSelectAll = () => {
    setSelectedUsers(selectedUsers.length === users.length ? [] : users.map(user => user.id))
  }

  const handleBulkEdit = () => {
    console.log("Bulk edit for:", selectedUsers)
  }

  const handleDeleteAll = () => {
    console.log("Delete all users")
  }

  const handleAddUser = () => {
    console.log("Add new user")
  }

  return (
    <div className="p-8">
      <div className="sm:flex sm:items-center">
        <div className="sm:flex-auto">
          <h1 className="text-2xl font-semibold text-gray-900">Users</h1>
          <p className="mt-2 text-sm text-gray-700">
            A list of all the users in your account including their name, title, email and role.
          </p>
        </div>
        <div className="mt-4 sm:mt-0 sm:ml-16 sm:flex-none">
          <Button onClick={handleAddUser}>Add user</Button>
        </div>
      </div>
      <div className="mt-8 flex flex-col">
        <div className="-my-2 -mx-4 overflow-x-auto sm:-mx-6 lg:-mx-8">
          <div className="inline-block min-w-full py-2 align-middle md:px-6 lg:px-8">
            <div className="overflow-hidden shadow ring-1 ring-black ring-opacity-5 md:rounded-lg">
              <table className="min-w-full divide-y divide-gray-300">
                <thead className="bg-gray-50">
                  <tr>
                    <th scope="col" className="relative w-12 px-6 sm:w-16 sm:px-8">
                      <Checkbox
                        checked={selectedUsers.length === users.length}
                        onCheckedChange={handleSelectAll}
                        aria-label="Select all users"
                      />
                    </th>
                    <th scope="col" className="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
                      Name
                    </th>
                    <th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">
                      Title
                    </th>
                    <th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">
                      Email
                    </th>
                    <th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">
                      Role
                    </th>
                    <th scope="col" className="relative py-3.5 pl-3 pr-4 sm:pr-6">
                      <span className="sr-only">Edit</span>
                    </th>
                  </tr>
                </thead>
                <tbody className="divide-y divide-gray-200 bg-white">
                  {users.map((user) => (
                    <tr key={user.id} className={selectedUsers.includes(user.id) ? "bg-indigo-50" : undefined}>
                      <td className="relative w-12 px-6 sm:w-16 sm:px-8">
                        <Checkbox
                          checked={selectedUsers.includes(user.id)}
                          onCheckedChange={() => handleSelectUser(user.id)}
                          aria-label={`Select ${user.name}`}
                        />
                      </td>
                      <td className="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6">
                        {user.name}
                      </td>
                      <td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{user.title}</td>
                      <td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{user.email}</td>
                      <td className="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{user.role}</td>
                      <td className="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6">
                        <Button variant="link" className="text-indigo-600 hover:text-indigo-900">
                          Edit<span className="sr-only">, {user.name}</span>
                        </Button>
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          </div>
        </div>
      </div>
      {selectedUsers.length > 0 && (
        <div className="mt-4 flex space-x-4">
          <Button onClick={handleBulkEdit}>Bulk edit</Button>
          <Button variant="destructive" onClick={handleDeleteAll}>Delete all</Button>
        </div>
      )}
    </div>
  )
}

インボイス用

※画像をクリックで拡大できます。

.tsx
import { Button } from "@/components/ui/button"

export default function Component() {
  return (
    <div className="p-4 sm:p-6 bg-white">
      <div className="flex flex-col sm:flex-row justify-between items-start sm:items-center mb-6">
        <h1 className="text-2xl font-bold mb-2 sm:mb-0">Invoice</h1>
        <Button className="bg-indigo-600 hover:bg-indigo-700 text-white w-full sm:w-auto">Print</Button>
      </div>
      
      <p className="text-gray-600 mb-8">
        For work completed from August 1, 2022 to August 31, 2022.
      </p>
      
      <div className="overflow-x-auto">
        <table className="w-full mb-8">
          <thead className="hidden sm:table-header-group">
            <tr className="border-b">
              <th className="text-left py-2">Project</th>
              <th className="text-right py-2">Hours</th>
              <th className="text-right py-2">Rate</th>
              <th className="text-right py-2">Price</th>
            </tr>
          </thead>
          <tbody>
            {[
              { name: "Logo redesign", description: "New logo and digital asset playbook.", hours: 20, rate: 100, price: 2000 },
              { name: "Website redesign", description: "Design and program new company website.", hours: 52, rate: 100, price: 5200 },
              { name: "Business cards", description: "Design and production of 3.5\" x 2.0\" business cards.", hours: 12, rate: 100, price: 1200 },
              { name: "T-shirt design", description: "Three t-shirt design concepts.", hours: 4, rate: 100, price: 400 },
            ].map((item, index) => (
              <tr key={index} className="border-b sm:border-none">
                <td className="py-4 sm:py-2">
                  <div className="font-medium">{item.name}</div>
                  <div className="text-sm text-gray-500">{item.description}</div>
                  <div className="sm:hidden mt-2">
                    <div className="flex justify-between">
                      <span>Hours:</span>
                      <span>{item.hours.toFixed(1)}</span>
                    </div>
                    <div className="flex justify-between">
                      <span>Rate:</span>
                      <span>${item.rate.toFixed(2)}</span>
                    </div>
                    <div className="flex justify-between font-medium">
                      <span>Price:</span>
                      <span>${item.price.toFixed(2)}</span>
                    </div>
                  </div>
                </td>
                <td className="text-right py-2 hidden sm:table-cell">{item.hours.toFixed(1)}</td>
                <td className="text-right py-2 hidden sm:table-cell">${item.rate.toFixed(2)}</td>
                <td className="text-right py-2 hidden sm:table-cell">${item.price.toFixed(2)}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
      
      <div className="flex justify-end">
        <div className="w-full sm:w-64">
          <div className="flex justify-between mb-2">
            <span>Subtotal</span>
            <span>$8,800.00</span>
          </div>
          <div className="flex justify-between mb-2">
            <span>Tax</span>
            <span>$1,760.00</span>
          </div>
          <div className="flex justify-between font-bold text-lg">
            <span>Total</span>
            <span>$10,560.00</span>
          </div>
        </div>
      </div>
    </div>
  )
}