Skip to content

Instantly share code, notes, and snippets.

Revisions

  1. ffflabs created this gist Jan 25, 2023.
    11 changes: 11 additions & 0 deletions data-table-with-tailwindcss-alpinejs.markdown
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,11 @@
    Data Table with TailwindCSS & AlpineJS
    --------------------------------------
    Alpine Js & TailwindCSS Datatable..

    Modified version of @mithicher Pen

    https://codepen.io/mithicher/pen/OJyRjvb

    A [Pen](https://codepen.io/Salmi/pen/NWjvMRY) by [salmi iliass](https://codepen.io/Salmi) on [CodePen](https://codepen.io).

    [License](https://codepen.io/license/pen/NWjvMRY).
    126 changes: 126 additions & 0 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,126 @@
    <body class="antialiased sans-serif bg-gray-100">
    <div class="container mx-auto px-4" x-data="datatables()" x-cloak>
    <div x-show="selectedUsers.length" class="bg-indigo-200 fixed top-4 right-4 z-40 w-1/4 shadow">
    <div class="container mx-auto px-4 py-4">
    <div class="flex md:items-center">
    <div class="mr-4 flex-shrink-0">
    <svg class="h-8 w-8 text-indigo-600" viewBox="0 0 20 20" fill="currentColor">
    <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd" />
    </svg>
    </div>
    <div x-html="selectedUsers.length + ' rows are selected'" class="text-indigo-800 text-lg"></div>
    </div>
    </div>
    </div>

    <div class="border-b mb-4">
    <h1 class="text-3xl py-3 border-b mb-6">Datatable</h1>
    <pre><strong>Source Pen :</strong> <a href="https://codepen.io/mithicher/pen/OJyRjvb" target="_blank" class="text-blue-500 hover:text-blue-700 underline">Table UI with TailwindCSS & AlpineJS by @mithicher</a>

    <strong>Things I added :</strong>
    Upgraded AlpineJS to v3 & TailwindCSS to v2
    Added Search Functionality
    Refactor Vanilla JS Code & DOM Selectors to only AlpineJS Code..
    No need for specific Class names anymore
    </pre>
    </div>
    <div class="mb-4 flex justify-between items-center">

    <div class="flex-1 pr-4">
    <div class="relative md:w-1/3">
    <input type="search" x-model="search" class="w-full pl-10 pr-4 py-2 rounded-lg shadow focus:outline-none focus:shadow-outline text-gray-600 font-medium" placeholder="Search...">
    <div class="absolute top-0 left-0 inline-flex items-center p-2">
    <svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-gray-400" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
    <rect x="0" y="0" width="24" height="24" stroke="none"></rect>
    <circle cx="10" cy="10" r="7" />
    <line x1="21" y1="21" x2="15" y2="15" />
    </svg>
    </div>
    </div>
    </div>
    <div>
    <div class="shadow rounded-lg flex">
    <div class="relative">
    <button @click.prevent="open = !open" class="rounded-lg inline-flex items-center bg-white hover:text-blue-500 focus:outline-none focus:shadow-outline text-gray-500 font-semibold py-2 px-2 md:px-4">
    <svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 md:hidden" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
    <rect x="0" y="0" width="24" height="24" stroke="none"></rect>
    <path d="M5.5 5h13a1 1 0 0 1 0.5 1.5L14 12L14 19L10 16L10 12L5 6.5a1 1 0 0 1 0.5 -1.5" />
    </svg>
    <span class="hidden md:block">Display</span>
    <svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 ml-1" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
    <rect x="0" y="0" width="24" height="24" stroke="none"></rect>
    <polyline points="6 9 12 15 18 9" />
    </svg>
    </button>

    <div x-show="open" @click.away="open = false" class="z-40 absolute top-0 right-0 w-40 bg-white rounded-lg shadow-lg mt-12 -mr-1 block py-1 overflow-hidden">
    <template x-for="heading in headings">
    <label class="flex justify-start items-center text-truncate hover:bg-gray-100 px-4 py-2">
    <div class="text-blue-600 mr-3">
    <input type="checkbox" class="form-checkbox focus:outline-none focus:shadow-outline" checked @click="toggleColumn(heading.key)">
    </div>
    <div class="select-none text-gray-700" x-text="heading.value"></div>
    </label>
    </template>
    </div>
    </div>
    </div>
    </div>
    </div>

    <div class="overflow-x-auto bg-white rounded-lg shadow overflow-y-auto relative" style="height: 405px;">
    <table class="border-collapse table-auto w-full whitespace-no-wrap bg-white table-striped relative">
    <thead>
    <tr class="text-left">
    <th class="py-2 px-3 sticky top-0 border-b border-indigo-200 bg-indigo-100">
    <label class="text-indigo-500 inline-flex justify-between items-center hover:bg-gray-300 px-2 py-2 rounded-lg cursor-pointer">
    <input type="checkbox" class="form-checkbox focus:outline-none focus:shadow-outline" @click="selectAllCheckbox($event);">
    </label>
    </th>
    <template x-for="heading in headings">
    <th class="bg-indigo-100 sticky top-0 border-b border-indigo-200 px-6 py-2 text-gray-700 font-bold tracking-wider uppercase text-xs" x-text="heading.value" x-show="columns.includes(heading.key)"></th>
    </template>
    </tr>
    </thead>
    <tbody>
    <template x-for="user in filtered(users, 'firstName', 'lastName','emailAddress', 'phoneNumber')" :key="user.userId">
    <tr>
    <td class="border-dashed border-t border-gray-300 px-3">
    <label class="text-blue-500 inline-flex justify-between items-center hover:bg-gray-200 px-2 py-2 rounded-lg cursor-pointer">
    <input type="checkbox" x-model="user.selected" class="form-checkbox rowCheckbox focus:outline-none focus:shadow-outline" :name="user.userId">
    </label>
    </td>
    <td class="border-dashed border-t border-gray-300" x-show="columns.includes('userId')">
    <span class="text-gray-700 px-6 py-3 flex items-center" x-text="user.userId"></span>
    </td>
    <td class="border-dashed border-t border-gray-300" x-show="columns.includes('firstName')">
    <span class="text-gray-700 px-6 py-3 flex items-center" x-text="user.firstName"></span>
    </td>
    <td class="border-dashed border-t border-gray-300" x-show="columns.includes('lastName')">
    <span class="text-gray-700 px-6 py-3 flex items-center" x-text="user.lastName"></span>
    </td>
    <td class="border-dashed border-t border-gray-300" x-show="columns.includes('emailAddress')">
    <span class="text-gray-700 px-6 py-3 flex items-center" x-text="user.emailAddress"></span>
    </td>
    <td class="border-dashed border-t border-gray-300" x-show="columns.includes('gender')">
    <span class="text-gray-700 px-6 py-3 flex items-center" x-text="user.gender"></span>
    </td>
    <td class="border-dashed border-t border-gray-300" x-show="columns.includes('phoneNumber')">
    <span class="text-gray-700 px-6 py-3 flex items-center" x-text="user.phoneNumber"></span>
    </td>
    </tr>
    </template>
    </tbody>
    </table>
    </div>
    </div>

    <!-- Alpine Plugins -->
    <script defer src="https://unpkg.com/@alpinejs/[email protected]/dist/cdn.min.js"></script>

    <!-- Alpine Core -->
    <script defer src="https://unpkg.com/[email protected]/dist/cdn.min.js"></script>

    </body>

    </html>
    222 changes: 222 additions & 0 deletions script.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,222 @@
    function datatables() {
    return {
    headings: [
    {
    key: "userId",
    value: "User ID"
    },
    {
    key: "firstName",
    value: "Firstname"
    },
    {
    key: "lastName",
    value: "Lastname"
    },
    {
    key: "emailAddress",
    value: "Email"
    },
    {
    key: "gender",
    value: "Gender"
    },
    {
    key: "phoneNumber",
    value: "Phone"
    }
    ],
    users: [
    {
    selected: false,
    userId: 1,
    firstName: "Cort",
    lastName: "Tosh",
    emailAddress: "[email protected]",
    gender: "Male",
    phoneNumber: "327-626-5542"
    },
    {
    selected: false,
    userId: 2,
    firstName: "Brianne",
    lastName: "Dzeniskevich",
    emailAddress: "[email protected]",
    gender: "Female",
    phoneNumber: "144-190-8956"
    },
    {
    selected: false,
    userId: 3,
    firstName: "Isadore",
    lastName: "Botler",
    emailAddress: "[email protected]",
    gender: "Male",
    phoneNumber: "350-937-0792"
    },
    {
    selected: false,
    userId: 4,
    firstName: "Janaya",
    lastName: "Klosges",
    emailAddress: "[email protected]",
    gender: "Female",
    phoneNumber: "502-438-7799"
    },
    {
    selected: false,
    userId: 5,
    firstName: "Freddi",
    lastName: "Di Claudio",
    emailAddress: "[email protected]",
    gender: "Female",
    phoneNumber: "265-448-9627"
    },
    {
    selected: false,
    userId: 6,
    firstName: "Oliy",
    lastName: "Mairs",
    emailAddress: "[email protected]",
    gender: "Female",
    phoneNumber: "221-516-2295"
    },
    {
    selected: false,
    userId: 7,
    firstName: "Tabb",
    lastName: "Wiseman",
    emailAddress: "[email protected]",
    gender: "Male",
    phoneNumber: "171-817-5020"
    },
    {
    selected: false,
    userId: 8,
    firstName: "Joela",
    lastName: "Betteriss",
    emailAddress: "[email protected]",
    gender: "Female",
    phoneNumber: "481-100-9345"
    },
    {
    selected: false,
    userId: 9,
    firstName: "Alistair",
    lastName: "Vasyagin",
    emailAddress: "[email protected]",
    gender: "Male",
    phoneNumber: "520-669-8364"
    },
    {
    selected: false,
    userId: 10,
    firstName: "Nealon",
    lastName: "Ratray",
    emailAddress: "[email protected]",
    gender: "Male",
    phoneNumber: "993-654-9793"
    },
    {
    selected: false,
    userId: 11,
    firstName: "Annissa",
    lastName: "Kissick",
    emailAddress: "[email protected]",
    gender: "Female",
    phoneNumber: "283-425-2705"
    },
    {
    selected: false,
    userId: 12,
    firstName: "Nissie",
    lastName: "Sidnell",
    emailAddress: "[email protected]",
    gender: "Female",
    phoneNumber: "754-391-3116"
    },
    {
    selected: false,
    userId: 13,
    firstName: "Madalena",
    lastName: "Fouch",
    emailAddress: "[email protected]",
    gender: "Female",
    phoneNumber: "584-300-9004"
    },
    {
    selected: false,
    userId: 14,
    firstName: "Rozina",
    lastName: "Atkins",
    emailAddress: "[email protected]",
    gender: "Female",
    phoneNumber: "792-856-0845"
    },
    {
    selected: false,
    userId: 15,
    firstName: "Lorelle",
    lastName: "Sandcroft",
    emailAddress: "[email protected]",
    gender: "Female",
    phoneNumber: "882-911-7241"
    }
    ],

    open: false,

    search: '',

    columns: [],

    get selectedUsers() {
    return this.users.filter((user) => user.selected);
    },

    init() {
    this.columns = this.headings.map((h) => {
    return h.key;
    });
    },

    toggleColumn(key) {
    this.columns.includes(key)
    ? (this.columns = this.columns.filter((i) => i !== key))
    : this.columns.push(key);
    },

    selectAllCheckbox() {
    let filteredUsers = this.filtered(this.users);
    if (filteredUsers.length === this.selectedUsers.length) {
    return filteredUsers.map((user) => (user.selected = false));
    }
    filteredUsers.map((user) => (user.selected = true));
    },

    filtered(...items) {
    // Search filter Function for any Array of Objects !

    // You can pass only the Array of Objects,
    // it will search all props of every Object except "ID"
    // Example : filtered(users)

    // OR you can pass additional props, it will only search passed props
    // Example : filtered(users, 'firstName', 'lastName','emailAddress', 'phoneNumber')

    values = items.shift(); // get the list of objects
    props = items.length ? items : null; // get list of props

    return values.filter((i) => {
    y = Object.assign({}, i);
    delete y['userId']; // Specifie the id prop to remove from object
    if (props) {
    okeys = Object.keys(y).filter((b) => !props.includes(b));
    okeys.map((d) => delete y[d]);
    }
    itemToSearch = Object.values(y).join(); // Object to array, then join to String
    return itemToSearch.toLowerCase().includes(this.search.toLowerCase()); // Return filtred Object
    });
    }
    };
    }
    66 changes: 66 additions & 0 deletions style.css
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,66 @@
    [x-cloak] {
    display: none;
    }

    /* input:checked + svg {
    display: block;
    } */
    [type="checkbox"] {
    box-sizing: border-box;
    padding: 0;
    }

    .form-checkbox {
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
    -webkit-print-color-adjust: exact;
    color-adjust: exact;
    display: inline-block;
    vertical-align: middle;
    background-origin: border-box;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    flex-shrink: 0;
    color: currentColor;
    background-color: #fff;
    border-color: #bfc0c2;
    border-width: 1px;
    border-radius: 0.25rem;
    height: 1.2em;
    width: 1.2em;
    }

    .form-checkbox:checked {
    background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M5.707 7.293a1 1 0 0 0-1.414 1.414l2 2a1 1 0 0 0 1.414 0l4-4a1 1 0 0 0-1.414-1.414L7 8.586 5.707 7.293z'/%3e%3c/svg%3e");
    border-color: transparent;
    background-color: currentColor;
    background-size: 100% 100%;
    background-position: center;
    background-repeat: no-repeat;
    }



    /* width */
    ::-webkit-scrollbar {
    width: 0.5em;
    }

    /* Track */
    ::-webkit-scrollbar-track {
    background: #f1f1f1;
    }

    /* Handle */
    ::-webkit-scrollbar-thumb {
    background: #7e8590;

    }

    /* Handle on hover */
    ::-webkit-scrollbar-thumb:hover {
    background: #555;
    }
    1 change: 1 addition & 0 deletions styles
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    <link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.4/tailwind.min.css" rel="stylesheet" />