mirror of
https://github.com/tabler/tabler.git
synced 2026-08-29 13:21:29 +04:00
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: codecalm <1282324+codecalm@users.noreply.github.com>
210 lines
7.9 KiB
Plaintext
210 lines
7.9 KiB
Plaintext
---
|
|
import BadgesList from './BadgesList.astro'
|
|
import Icon from './Icon.astro'
|
|
import { formatLongDate } from '@shared/lib/date-format'
|
|
import { randomItem, randomDate } from '@shared/lib/pseudo-random'
|
|
import Avatar from './Avatar.astro'
|
|
import Button from './Button.astro'
|
|
import ButtonList from './ButtonList.astro'
|
|
import Pagination from './Pagination.astro'
|
|
import people from '@data/people.json'
|
|
import tableProperties from '@data/table-properties.json'
|
|
import CaptureScript from '../components/CaptureScript.astro'
|
|
import { requireIndex } from '@shared/lib/array'
|
|
|
|
interface Props {
|
|
id?: string
|
|
}
|
|
|
|
interface Person {
|
|
full_name?: string
|
|
city?: string
|
|
country?: string
|
|
[key: string]: unknown
|
|
}
|
|
|
|
interface AdvancedTableProperties {
|
|
headers: { 'data-sort': string; 'name': string }[]
|
|
}
|
|
|
|
const { id: tableId = 'advanced-table' } = Astro.props
|
|
|
|
const statuses = ['Active', 'Inactive', 'Requested']
|
|
const perPage = ['10', '20', '50', '100']
|
|
const categories = ['Agencies', 'Individual', 'Event', 'Ticket']
|
|
const tags = ['QTA,Event,Tickets,TODO', 'Event,Tickets', 'QTA,Event', 'Tickets']
|
|
|
|
const advancedTable = (tableProperties as { 'advanced-table': AdvancedTableProperties })['advanced-table']
|
|
const headers = advancedTable.headers
|
|
|
|
const rows = (people as Person[]).map((person, i) => {
|
|
const index = i + 1
|
|
const status = randomItem(index + 5, statuses)
|
|
const itemTags = randomItem(index + 5, tags).split(',')
|
|
const category = randomItem(index, categories)
|
|
const date = formatLongDate(randomDate(index))
|
|
return { person, index, status, itemTags, category, date }
|
|
})
|
|
|
|
const perPageDefault = requireIndex(perPage, 1)
|
|
const sortKeys = headers.map((header) => header['data-sort'])
|
|
---
|
|
|
|
<div class="card">
|
|
<div class="card-table">
|
|
<div class="card-header">
|
|
<div class="row w-full">
|
|
<div class="col">
|
|
<h3 class="card-title mb-0">Employee</h3>
|
|
<p class="text-secondary m-0">Table description.</p>
|
|
</div>
|
|
<div class="col-md-auto col-sm-12">
|
|
<ButtonList class="ms-auto d-flex flex-wrap">
|
|
<div class="input-group input-group-flat w-auto">
|
|
<span class="input-group-text">
|
|
<Icon name="search" />
|
|
</span>
|
|
|
|
<input id={`${tableId}-search`} type="text" class="form-control" autocomplete="off" />
|
|
|
|
<span class="input-group-text">
|
|
<kbd>ctrl + K</kbd>
|
|
</span>
|
|
</div>
|
|
|
|
<button type="button" class="btn btn-icon" aria-label="More actions">
|
|
<Icon name="dots" />
|
|
</button>
|
|
<div class="dropdown">
|
|
<button type="button" class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Download</button>
|
|
<div class="dropdown-menu">
|
|
<button type="button" class="dropdown-item">Action</button>
|
|
<button type="button" class="dropdown-item">Another action</button>
|
|
<button type="button" class="dropdown-item">Third action</button>
|
|
</div>
|
|
</div>
|
|
<Button />
|
|
</ButtonList>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id={tableId}>
|
|
<div class="table-responsive">
|
|
<table class="table table-vcenter table-selectable">
|
|
<thead>
|
|
<tr>
|
|
<th class="w-1"></th>
|
|
{
|
|
headers.map((header) => (
|
|
<th>
|
|
<button type="button" class="table-sort d-flex justify-content-between" data-sort={header['data-sort']}>
|
|
{header.name}
|
|
</button>
|
|
</th>
|
|
))
|
|
}
|
|
</tr>
|
|
</thead>
|
|
|
|
<tbody class="table-tbody">
|
|
{
|
|
rows.map(({ person, status, itemTags, category, date }) => (
|
|
<tr>
|
|
<td>
|
|
<input class="form-check-input m-0 align-middle table-selectable-check" type="checkbox" aria-label="Select invoice" value="true" />
|
|
</td>
|
|
<td class={requireIndex(headers, 0)['data-sort']}>
|
|
<Avatar person={person} size="xs" class="me-2" />
|
|
{person.full_name}
|
|
</td>
|
|
<td class={requireIndex(headers, 1)['data-sort']}>
|
|
{person.city}, {person.country}
|
|
</td>
|
|
<td class={requireIndex(headers, 2)['data-sort']}>{status === 'Active' ? <span class="badge bg-success-lt">Active</span> : status === 'Inactive' ? <span class="badge bg-danger-lt">Inactive</span> : <span class="badge">Requested</span>}</td>
|
|
<td class={requireIndex(headers, 3)['data-sort']}>{date}</td>
|
|
<td class={requireIndex(headers, 4)['data-sort']}>
|
|
<BadgesList>
|
|
{itemTags.map((tag) => (
|
|
<span class="badge">{tag}</span>
|
|
))}
|
|
</BadgesList>
|
|
</td>
|
|
<td class={`${requireIndex(headers, 5)['data-sort']} py-0`}>
|
|
<span class="on-unchecked">{category}</span>
|
|
<div class="on-checked">
|
|
<div class="d-flex justify-content-end">
|
|
<Button icon="dots" iconOnly />
|
|
</div>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
))
|
|
}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="card-footer d-flex align-items-center">
|
|
<div class="dropdown" data-table-id={tableId}>
|
|
<button type="button" class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
|
<span id={`${tableId}-page-count`} class="me-1">{perPageDefault}</span>
|
|
<span>records</span>
|
|
</button>
|
|
<div class="dropdown-menu">
|
|
{
|
|
perPage.map((record) => (
|
|
<button type="button" class="dropdown-item" onclick="setPageListItems(this)" data-value={record}>
|
|
{record} records
|
|
</button>
|
|
))
|
|
}
|
|
</div>
|
|
</div>
|
|
|
|
<Pagination class="m-0 ms-auto" count={10} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<CaptureScript>
|
|
<!-- BEGIN ADVANCED TABLE -->
|
|
<script is:inline define:vars={{ tableId, sortKeys, perPageDefault }}>
|
|
// Astro wraps define:vars scripts in an IIFE, so this needs an explicit window
|
|
// assignment to stay reachable from the onclick="setPageListItems(this)" markup below.
|
|
// Each AdvancedTable instance redefines this identically, so it stays safe to share:
|
|
// the table id is resolved from the clicked element instead of being closed over.
|
|
window.setPageListItems = function setPageListItems(el) {
|
|
const id = el.closest('[data-table-id]')?.dataset.tableId
|
|
if (!id || !window.tabler_list?.[id]) return
|
|
window.tabler_list[id].page = parseInt(el.dataset.value)
|
|
window.tabler_list[id].update()
|
|
document.querySelector(`#${id}-page-count`).innerHTML = el.dataset.value
|
|
}
|
|
|
|
function initAdvancedTable() {
|
|
window.tabler_list ??= {}
|
|
window.tabler_list[tableId] = new List(tableId, {
|
|
sortClass: 'table-sort',
|
|
listClass: 'table-tbody',
|
|
page: parseInt(perPageDefault),
|
|
pagination: {
|
|
item: (value) => `<li class="page-item"><a class="page-link cursor-pointer">${value.page}</a></li>`,
|
|
innerWindow: 1,
|
|
outerWindow: 1,
|
|
left: 0,
|
|
right: 0,
|
|
},
|
|
valueNames: sortKeys,
|
|
})
|
|
const list = window.tabler_list[tableId]
|
|
|
|
const searchInput = document.querySelector(`#${tableId}-search`)
|
|
searchInput?.addEventListener('input', () => list.search(searchInput.value))
|
|
}
|
|
|
|
document.readyState !== 'loading' ? initAdvancedTable() : document.addEventListener('DOMContentLoaded', initAdvancedTable, { once: true })
|
|
</script>
|
|
<!-- END ADVANCED TABLE -->
|
|
</CaptureScript>
|