Files
syscom-master-admin-ui/src/app/shared/components/data-table/data-table.html
T

289 lines
12 KiB
HTML

<!-- Start::row-1 -->
<div class="grid grid-cols-12 data-table-modern">
<div class="xl:col-span-12 col-span-12">
<div class="box overflow-visible">
<div class="box-header justify-between items-center gap-3">
<h5 class="box-title">
{{ tableTitle () }}
</h5>
<div class="flex flex-wrap items-center gap-3">
@if(showFilterButton()){
<div animate.enter="filter-btn-enter" animate.leave="filter-btn-leave" class="flex items-center">
<app-button action="custom" icon="ti ti-filter" label="Filter" size="sm" iconClass="!text-[1rem]"
[variant]="filterActive() ? 'primary-full' : 'outline-primary'"
className="!rounded-full shadow-sm !whitespace-nowrap !mb-0" (buttonClicked)="onFilterClick($event)" />
</div>
}
@if(showImportExportButtons()){
<div class="flex items-center gap-2">
<app-button action="custom" icon="ri-upload-2-line" label="Import CSV" variant="outline-primary" size="sm"
className="!rounded-full shadow-sm !mb-0" (buttonClicked)="onImportClick($event)" />
<app-button action="custom" icon="ri-download-2-line" label="Export" variant="secondary" size="sm"
className="!rounded-full shadow-sm !mb-0" (buttonClicked)="onExportClick($event)" />
</div>
}
@if(showAddButton()){
<div class="flex items-center">
<app-button action="add" [label]="buttonTitle()" size="sm" iconClass="!text-[1rem]"
className="!rounded-full shadow-sm !mb-0" (buttonClicked)="onAddClick($event)" />
</div>
}
<!-- @if (toolbarTemplate(); as toolbar) {
<div>
<ng-container [ngTemplateOutlet]="toolbar.templateRef" />
</div>
} -->
@if (showSearch()) {
<div class="search-wrapper flex items-center">
<i class="ri-search-line search-icon"></i>
<input #searchInput class="form-control form-control-sm brdr-clr" type="text"
[placeholder]="searchPlaceholder()" aria-label="table search" (input)="onSearch(searchInput.value)">
</div>
}
</div>
</div>
@if (toolbarTemplate(); as toolbar) {
<div class="table-filter-row" [class.is-open]="filterActive()">
<div class="table-filter-row-inner">
<div class="table-filter-row-content" #filterRowContent>
<ng-container [ngTemplateOutlet]="toolbar.templateRef" />
@if (filterActionsTemplate(); as filterActions) {
<div class="table-filter-row-actions" [class.is-pinned]="filterRowOverflowing()">
<ng-container [ngTemplateOutlet]="filterActions.templateRef" />
</div>
}
</div>
<button type="button"
class="ti-btn ti-btn-icon ti-btn-sm ti-btn-light !rounded-full !m-0 shrink-0 filter-close-btn"
aria-label="Close filter" (click)="onFilterClick($event)">
<i class="ti ti-x"></i>
</button>
</div>
</div>
}
<div class="box-body !p-0 modern-table-body">
<div class="table-responsive overflow-x-auto overflow-y-visible modern-table-wrapper">
<table [class]="tableClass()">
<thead>
<tr [class]="trHeadClass()">
@for (column of columns(); track column.key) {
<th scope="col" [class]="getHeaderClass(column)" [style.width]="column.width || null"
(click)="onSort(column)">
<span class="inline-flex items-center gap-1" [class.cursor-pointer]="column.sortable">
{{ column.label }}
@if (column.sortable) {
<i [class]="getSortIconClass(column) + ' text-xs align-middle'"></i>
}
</span>
</th>
}
@if (actions().length > 0) {
<th scope="col" [class]="composeClass(actionHeaderClass(), 'relative overflow-visible')">Action</th>
}
</tr>
</thead>
<tbody>
@if (loading()) {
<tr class="border-b border-defaultborder">
<td [attr.colspan]="totalVisibleColumns()" class="py-10">
<div class="flex flex-col items-center justify-center gap-3 text-center">
<i class="ti ti-loader-2 animate-spin text-[1.5rem]"></i>
<span class="text-[0.875rem]">Loading data...</span>
</div>
</td>
</tr>
} @else if (rows().length === 0) {
<tr class="border-b border-defaultborder bg-light/30 dark:bg-black/10">
<td [attr.colspan]="totalVisibleColumns()" class="p-0">
<div class="flex min-h-[140px] flex-col items-center justify-center px-4 py-8 text-center">
<div
class="mb-3 inline-flex h-11 w-11 items-center justify-center rounded-full bg-primary/10 text-primary dark:bg-primary/15">
<i class="ti ti-database-off text-[1.25rem]" aria-hidden="true"></i>
</div>
<p class="text-sm font-semibold text-defaulttextcolor dark:text-white/80">
{{ emptyMessage() }}
</p>
@if (emptyDescription()) {
<p class="mt-1 max-w-md text-sm text-textmuted">
{{ emptyDescription() }}
</p>
}
</div>
</td>
</tr>
} @else
{
@for (row of rows(); track row['id'] ?? $index; let rowIndex = $index) {
<tr [class]="getRowClass(row, $index)" (click)="onRowClick(row)">
@for (column of columns(); track column.key) {
<td [class]="getCellClass(column)">
@if (getCellTemplate(column); as customTemplate) {
<ng-container [ngTemplateOutlet]="customTemplate.templateRef"
[ngTemplateOutletContext]="getCellTemplateContext(column, row)" />
} @else if (column.badge) {
<span [class]="getBadgeClass(column, row)">
{{ getDisplayValue(column, row) }}
</span>
} @else {
{{ getDisplayValue(column, row) }}
}
</td>
}
@if (actions().length > 0) {
<td [class]="composeClass(actionCellClass(), 'overflow-visible')" (click)="$event.stopPropagation()">
<div class="inline-flex w-full justify-center">
<button type="button" cdkOverlayOrigin #actionMenuOrigin="cdkOverlayOrigin" data-action-menu-trigger
class="
ti-btn ti-btn-icon ti-btn-sm ti-btn-primary
!m-0 !h-6 !w-6 !p-0
" aria-label="Open row actions" aria-haspopup="menu" [attr.aria-controls]="getActionMenuId(row)"
[attr.aria-expanded]="isActionMenuOpen(row)" (click)="toggleActionMenu($event, row)">
<i class="ri-more-2-fill text-sm"></i>
</button>
<ng-template cdkConnectedOverlay [cdkConnectedOverlayOrigin]="actionMenuOrigin"
[cdkConnectedOverlayOpen]="isActionMenuOpen(row)"
[cdkConnectedOverlayPositions]="actionMenuPositions" [cdkConnectedOverlayViewportMargin]="8"
[cdkConnectedOverlayPush]="true" [cdkConnectedOverlayHasBackdrop]="false"
(overlayOutsideClick)="onActionMenuOutsideClick($event)">
<div [id]="getActionMenuId(row)" role="menu" class="
w-[170px]
max-w-[calc(100vw-16px)]
overflow-hidden
rounded-lg
border border-defaultborder
bg-white
p-1.5
shadow-xl
dark:border-defaultborder/10
dark:bg-bodybg
" (click)="$event.stopPropagation()">
@for (action of actions(); track $index) {
@if (isActionVisible(action, row)) {
<button type="button" role="menuitem" class="
flex w-full items-center gap-2
rounded-md px-3 py-2
text-start text-sm
transition-colors
hover:bg-light
dark:hover:bg-white/10
" [disabled]="isActionDisabled(action, row)" [class.opacity-50]="isActionDisabled(action, row)"
[class.cursor-not-allowed]="isActionDisabled(action, row)"
[class.!text-danger]="action.type === 'delete'"
(click)="onDropdownActionClick($event, action, row)">
@if (action.icon) {
<i [class]="action.icon"></i>
}
<span>{{ action.label }}</span>
</button>
}
}
</div>
</ng-template>
</div>
</td>
}
</tr>
}
}
</tbody>
</table>
</div>
</div>
@if (showPaginator()) {
<div class="box-footer border-t-0 modern-table-footer">
<div class="flex items-center justify-between flex-wrap gap-3">
<!-- Record details and page-size selection -->
<div class="flex items-center flex-wrap gap-3">
<div class="text-sm text-defaulttextcolor">
Showing
<b>{{ startRecord() }}</b>
to
<b>{{ endRecord() }}</b>
of
<b>{{ totalRecords() }}</b>
entries
</div>
@if (pageSizeOptions().length > 0) {
<div class="flex items-center gap-2">
<label for="dataTablePageSize" class="text-sm whitespace-nowrap text-defaulttextcolor">
Show
</label>
<select id="dataTablePageSize"
class="form-select form-select-sm !w-[80px] !py-1 !ps-2.5 !pe-6 text-defaulttextcolor bg-white dark:bg-bodybg border border-defaultborder rounded-md"
[value]="pageSize()"
(change)="onPageSizeChange($event)">
@for (size of pageSizeOptions(); track size) {
<option [value]="size" [selected]="size === pageSize()" class="text-defaulttextcolor bg-white dark:bg-bodybg">
{{ size }}
</option>
}
</select>
<span class="text-sm whitespace-nowrap text-defaulttextcolor">
entries
</span>
</div>
}
</div>
<!-- Pagination -->
<nav aria-label="Table page navigation">
<ul class="ti-pagination mb-0">
<!-- Previous -->
<li class="page-item" [class.disabled]="pageIndex() <= 1">
<button type="button" class="page-link px-3 py-[0.375rem]" [disabled]="pageIndex() <= 1"
(click)="goToPage(pageIndex() - 1)">
Previous
</button>
</li>
<!-- Page numbers -->
@for (page of visiblePages(); track page) {
<li class="page-item" [class.active]="page === pageIndex()">
<button type="button" class="page-link px-3 py-[0.375rem]" [class.active]="page === pageIndex()"
[attr.aria-current]="
page === pageIndex() ? 'page' : null
" (click)="goToPage(page)">
{{ page }}
</button>
</li>
}
<!-- Next -->
<li class="page-item" [class.disabled]="pageIndex() >= totalPages()">
<button type="button" class="page-link px-3 py-[0.375rem]" [disabled]="pageIndex() >= totalPages()"
(click)="goToPage(pageIndex() + 1)">
Next
</button>
</li>
</ul>
</nav>
</div>
</div>
}
</div>
</div>
</div>
<!--End::row-1 -->