fix(registry): непрозрачный фон sticky-шапки, отказ от table-fixed

- шапка: [&_th]:bg-background вместо полупрозрачного bg-muted/50 — иначе сквозь
  sticky-шапку проступают строки и она нечитаема (в plain-варианте фона не было вовсе)
- ресайз: table-fixed + w-full таблицы + w-max wrapper давали циклическую зависимость
  ширин — колонки без заданной ширины схлопывались до нуля при первом drag.
  В auto-раскладке <colgroup> задаёт предпочтительную ширину без схлопывания

check/build чисто, vitest 129/129
This commit is contained in:
2026-09-14 23:54:10 +03:00
parent 9c1cd347bc
commit c1fe1c4e02

View File

@@ -40,7 +40,6 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@
import ContractNumberDisplay from '@/components/ContractNumberDisplay'; import ContractNumberDisplay from '@/components/ContractNumberDisplay';
import RegistryCellEditor from './RegistryCellEditor'; import RegistryCellEditor from './RegistryCellEditor';
import { InlineField } from '@/components/fields/InlineField'; import { InlineField } from '@/components/fields/InlineField';
import { useSkinComponents } from '@/themes/ThemeProvider';
interface TaskAssigneeInfo { interface TaskAssigneeInfo {
userId: number; userId: number;
@@ -269,9 +268,6 @@ const REGISTRY_VIRTUALIZATION_THRESHOLD = 30;
const TaskRegistry = ({ tasks, relatedTaskTitles = {}, fields, statuses, users = [], roles = [], formId, onFilteredCountChange, onTaskClick, filters, sort, setFilter, clearFilter, clearAll, setSort, clearSort, hasActiveFilters, searches, onAddSearch, onRemoveSearch, isAdmin, selectedTaskIds, anchorTaskId, onToggleSelected, onToggleSelectAll, onSelectRange, onSetAnchorTaskId, onClearSelection, onRequestBulkDelete }: TaskRegistryProps) => { const TaskRegistry = ({ tasks, relatedTaskTitles = {}, fields, statuses, users = [], roles = [], formId, onFilteredCountChange, onTaskClick, filters, sort, setFilter, clearFilter, clearAll, setSort, clearSort, hasActiveFilters, searches, onAddSearch, onRemoveSearch, isAdmin, selectedTaskIds, anchorTaskId, onToggleSelected, onToggleSelectAll, onSelectRange, onSetAnchorTaskId, onClearSelection, onRequestBulkDelete }: TaskRegistryProps) => {
const { toast } = useToast(); const { toast } = useToast();
// Вид шапки таблицы из скина: для plain-варианта sticky-шапке нужен явный фон,
// иначе при прокрутке сквозь неё проступают строки
const { table: skinTable } = useSkinComponents();
// Текущая редактируемая ячейка: { taskId, columnKey } // Текущая редактируемая ячейка: { taskId, columnKey }
const [editingCell, setEditingCell] = useState<{ taskId: number; columnKey: string } | null>(null); const [editingCell, setEditingCell] = useState<{ taskId: number; columnKey: string } | null>(null);
@@ -451,7 +447,6 @@ const TaskRegistry = ({ tasks, relatedTaskTitles = {}, fields, statuses, users =
useEffect(() => { useEffect(() => {
try { localStorage.setItem(`registry-colwidths-${formId}`, JSON.stringify(columnWidths)); } catch { /* ignore */ } try { localStorage.setItem(`registry-colwidths-${formId}`, JSON.stringify(columnWidths)); } catch { /* ignore */ }
}, [columnWidths, formId]); }, [columnWidths, formId]);
const hasCustomWidths = Object.keys(columnWidths).length > 0;
const resetColumnWidth = (key: string) => setColumnWidths(prev => { const resetColumnWidth = (key: string) => setColumnWidths(prev => {
const next = { ...prev }; const next = { ...prev };
delete next[key]; delete next[key];
@@ -973,7 +968,10 @@ const TaskRegistry = ({ tasks, relatedTaskTitles = {}, fields, statuses, users =
</div> </div>
) : ( ) : (
<div className="rounded-md border w-fit min-w-full"> <div className="rounded-md border w-fit min-w-full">
<Table wrapperClassName="w-max min-w-full overflow-visible" className={hasCustomWidths ? 'table-fixed' : ''}> {/* table-fixed НЕ используем: в связке с w-full таблицы и w-max wrapper он
схлопывает колонки без заданной ширины. В auto-раскладке <colgroup>
задаёт предпочтительную ширину без схлопывания. */}
<Table wrapperClassName="w-max min-w-full overflow-visible">
{/* Ширины колонок, заданные пользователем (drag шапки); без записи — автоширина */} {/* Ширины колонок, заданные пользователем (drag шапки); без записи — автоширина */}
<colgroup> <colgroup>
{isAdmin && <col style={{ width: 32 }} />} {isAdmin && <col style={{ width: 32 }} />}
@@ -981,7 +979,9 @@ const TaskRegistry = ({ tasks, relatedTaskTitles = {}, fields, statuses, users =
<col key={k} style={columnWidths[k] ? { width: columnWidths[k] } : undefined} /> <col key={k} style={columnWidths[k] ? { width: columnWidths[k] } : undefined} />
))} ))}
</colgroup> </colgroup>
<TableHeader className={`sticky top-0 z-10${skinTable.header === 'plain' ? ' [&_tr]:bg-background' : ''}`}> {/* Непрозрачный фон th обязателен: полупрозрачный bg-muted/50 (и его отсутствие
в plain-варианте) делает sticky-шапку нечитаемой поверх строк */}
<TableHeader className="sticky top-0 z-10 [&_th]:bg-background">
<TableRow> <TableRow>
{isAdmin && ( {isAdmin && (
<TableHead className="w-8 px-2"> <TableHead className="w-8 px-2">