refactor(DataTable): enhance table styling and functionality with fixed action column, improved striped rows, and new props for UX customization
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m52s

This commit is contained in:
2026-02-16 15:56:28 +07:00
parent 1fe9cf4794
commit af8af84ab7
3 changed files with 84 additions and 13 deletions
+26 -12
View File
@@ -34,6 +34,8 @@ import Tooltip from './Tooltip.jsx';
* - [{ icon: Component, label: string, onClick: (item) => void, variant?: string }]
* @param {boolean} props.selectable - Включить чекбоксы выбора (default: true)
* @param {number} props.skeletonRows - Количество строк скелетона (default: 10)
* @param {boolean} props.striped - Чередование фона строк (Tabler UX, default: true)
* @param {boolean} props.compact - Уменьшенные отступы ячеек (default: false)
*/
function DataTable({
columns = [],
@@ -56,11 +58,13 @@ function DataTable({
actions = [],
selectable = true,
skeletonRows = 10,
striped = true,
compact = false,
}) {
const hasActions = onEdit || onDelete || onCopy || actions.length > 0;
const isEditing = (item) => inlineEdit && item[itemKey] === inlineEdit.editingKey;
// Рендер заголовка колонки
// Рендер заголовка колонки (col.title — подпись, col.tooltip — подсказка для UX)
const renderColumnHeader = (col) => {
const isSortable = col.sortable !== false && onSort;
const isActive = sortField === col.key;
@@ -77,22 +81,27 @@ function DataTable({
</div>
);
const thProps = {
key: col.key,
scope: 'col',
style: col.width ? { width: col.width } : undefined,
};
if (isSortable) {
return (
<th
key={col.key}
<th
{...thProps}
className="cursor-pointer user-select-none"
onClick={() => onSort(col.key)}
style={col.width ? { width: col.width } : undefined}
>
{content}
{col.tooltip ? <Tooltip content={col.tooltip} position="top">{content}</Tooltip> : content}
</th>
);
}
return (
<th key={col.key} style={col.width ? { width: col.width } : undefined}>
{content}
<th {...thProps}>
{col.tooltip ? <Tooltip content={col.tooltip} position="top">{content}</Tooltip> : content}
</th>
);
};
@@ -210,15 +219,20 @@ function DataTable({
}
const allSelected = items.length > 0 && items.every(i => selectedItems.has(i[itemKey]));
const tableClass = [
'table card-table table-vcenter table-nowrap mb-0',
striped && 'table-striped',
compact && 'table-sm',
].filter(Boolean).join(' ');
return (
<>
<div className="table-responsive">
<table className="table card-table table-vcenter table-nowrap mb-0">
<table className={tableClass} role="grid">
<thead>
<tr>
{selectable && (
<th style={{ width: '40px' }}>
<th scope="col" style={{ width: '40px' }}>
<input
type="checkbox"
className="form-check-input"
@@ -229,7 +243,7 @@ function DataTable({
</th>
)}
{columns.map(renderColumnHeader)}
{hasActions && <th className="text-end">Действия</th>}
{hasActions && <th scope="col" className="text-end td-actions">Действия</th>}
</tr>
</thead>
<tbody>
@@ -261,7 +275,7 @@ function DataTable({
</td>
))}
{hasActions && (
<td className="text-end">
<td className="text-end td-actions">
{renderActions(item)}
</td>
)}
+15 -1
View File
@@ -296,7 +296,7 @@ button:focus-visible,
z-index: 2;
}
/* ===== Table Improvements ===== */
/* ===== Table Improvements (Tabler + UX) ===== */
.table thead th {
font-weight: 600;
text-transform: uppercase;
@@ -318,6 +318,20 @@ button:focus-visible,
background-color: rgba(32, 107, 196, 0.1);
}
/* Колонка «Действия» — фиксированная ширина, не сжимается (лаконичный UX) */
.table .td-actions {
min-width: 140px;
white-space: nowrap;
}
/* Выбранная строка поверх zebra (table-striped) */
.table-striped tbody tr.table-selected > * {
--tblr-table-bg-type: rgba(32, 107, 196, 0.08);
}
.table-striped tbody tr.table-selected:hover > * {
--tblr-table-bg-type: rgba(32, 107, 196, 0.12);
}
.user-select-none {
user-select: none;
-webkit-user-select: none;