Separate collector package and file columns
Keep package names in the Name column and render file counts or individual filenames in a dedicated File column. Rebalance responsive table tracks and preserve accessible package context for virtualized file rows.
This commit is contained in:
@@ -314,8 +314,8 @@ function CollectorPackageGroup({ row, model, actions, selected, focusIndexStart,
|
||||
type="button"
|
||||
>{row.collapsed ? "+" : "−"}</button>
|
||||
<strong title={row.name}>{row.name}</strong>
|
||||
<small>{row.totalCount} Dateien</small>
|
||||
</span>
|
||||
<span className="collector-file-cell is-package" role="cell">{row.totalCount} Dateien</span>
|
||||
<span className="collector-size-cell" role="cell">{packageSize(row)}</span>
|
||||
<span className="collector-hoster-cell" role="cell">
|
||||
{row.hosters.map(formatHosterLabel).map((hoster) => <CollectorHosterLabel hoster={hoster} key={hoster.title} />)}
|
||||
@@ -335,7 +335,8 @@ function CollectorPackageGroup({ row, model, actions, selected, focusIndexStart,
|
||||
<span className="collector-column-select" role="cell">
|
||||
<input aria-label={`${link.fileName} auswählen`} checked={selected.has(link.id)} data-collector-focus-index={interaction.focusIndex} onChange={(event) => actions.onLinkSelectionChange(link.id, event.target.checked)} tabIndex={interaction.tabIndex} type="checkbox" />
|
||||
</span>
|
||||
<span className="collector-name-cell is-file" role="cell" title={link.url}><span className={`collector-link-state is-${link.availability}`} />{link.fileName}</span>
|
||||
<span aria-label={`Paket ${row.name}`} className="collector-name-cell is-empty" role="cell" />
|
||||
<span className="collector-file-cell" role="cell" title={link.url}><span className={`collector-link-state is-${link.availability}`} />{link.fileName}</span>
|
||||
<span className="collector-size-cell" role="cell">{link.fileSizeBytes === null ? "Unbekannt" : humanSize(link.fileSizeBytes)}</span>
|
||||
<span className="collector-hoster-cell" role="cell"><CollectorHosterLabel hoster={hoster} /></span>
|
||||
<span className={`collector-status-cell is-${link.availability}`} role="cell">{linkAvailability(link.availability)}</span>
|
||||
@@ -428,7 +429,8 @@ export function CollectorContent({ model, actions }: CollectorViewProps): ReactE
|
||||
<DataTableHeader className="collector-table-header">
|
||||
<div aria-rowindex={1} className="collector-table-header-row" role="row" style={collectorHeaderScrollStyle(viewport.scrollLeft)}>
|
||||
<span aria-label="Auswahl" className="collector-column-select" role="columnheader" />
|
||||
<span role="columnheader">Name</span>
|
||||
<span className="collector-column-name" role="columnheader">Name</span>
|
||||
<span className="collector-column-file" role="columnheader">Datei</span>
|
||||
<span role="columnheader">Größe</span>
|
||||
<span role="columnheader">Hoster</span>
|
||||
<span role="columnheader">Status</span>
|
||||
|
||||
@@ -197,8 +197,8 @@
|
||||
.collector-file-row {
|
||||
align-items: center;
|
||||
display: grid;
|
||||
grid-template-columns: 48px minmax(260px, 2.4fr) minmax(110px, 0.8fr) minmax(90px, 0.65fr) minmax(115px, 0.9fr) minmax(130px, 1fr) minmax(150px, 1fr);
|
||||
min-width: 960px;
|
||||
grid-template-columns: 48px minmax(200px, 1.4fr) minmax(300px, 2fr) minmax(110px, 0.75fr) minmax(90px, 0.65fr) minmax(115px, 0.85fr) minmax(130px, 1fr) minmax(150px, 1fr);
|
||||
min-width: 1200px;
|
||||
}
|
||||
|
||||
.collector-table-header-row {
|
||||
@@ -217,11 +217,11 @@
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
.collector-table-header-row > span:nth-child(2) {
|
||||
.collector-column-name {
|
||||
padding-left: 48px;
|
||||
}
|
||||
|
||||
.collector-table-header-row > span:nth-child(n+3) {
|
||||
.collector-table-header-row > span:nth-child(n+4) {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
@@ -247,7 +247,7 @@
|
||||
.collector-virtual-spacer {
|
||||
flex: 0 0 var(--collector-virtual-total-height);
|
||||
height: var(--collector-virtual-total-height);
|
||||
min-width: 960px;
|
||||
min-width: 1200px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -338,20 +338,22 @@
|
||||
}
|
||||
|
||||
.collector-name-cell strong,
|
||||
.collector-name-cell.is-file {
|
||||
.collector-file-cell {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.collector-name-cell small {
|
||||
color: var(--ui-text-muted);
|
||||
white-space: nowrap;
|
||||
.collector-file-cell {
|
||||
align-items: center;
|
||||
color: var(--ui-text);
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.collector-name-cell.is-file {
|
||||
color: var(--ui-text);
|
||||
padding-left: 32px;
|
||||
.collector-file-cell.is-package {
|
||||
color: var(--ui-text-muted);
|
||||
}
|
||||
|
||||
.collector-collapse-button {
|
||||
@@ -521,6 +523,19 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1530px) {
|
||||
.collector-table-header-row,
|
||||
.collector-package-row,
|
||||
.collector-file-row {
|
||||
grid-template-columns: 44px minmax(180px, 1.2fr) minmax(260px, 1.7fr) minmax(98px, 0.75fr) minmax(78px, 0.65fr) minmax(100px, 0.85fr) minmax(112px, 1fr) minmax(132px, 1fr);
|
||||
min-width: 1040px;
|
||||
}
|
||||
|
||||
.collector-virtual-spacer {
|
||||
min-width: 1040px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1366px) {
|
||||
.collector-view {
|
||||
grid-template-columns: 56px minmax(0, 1fr);
|
||||
@@ -542,13 +557,6 @@
|
||||
.collector-action {
|
||||
padding: 0 9px;
|
||||
}
|
||||
|
||||
.collector-table-header-row,
|
||||
.collector-package-row,
|
||||
.collector-file-row {
|
||||
grid-template-columns: 44px minmax(220px, 2.2fr) minmax(98px, 0.8fr) minmax(78px, 0.65fr) minmax(100px, 0.9fr) minmax(112px, 1fr) minmax(132px, 1fr);
|
||||
min-width: 820px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1120px) {
|
||||
@@ -568,7 +576,11 @@
|
||||
.collector-table-header-row,
|
||||
.collector-package-row,
|
||||
.collector-file-row {
|
||||
grid-template-columns: 42px minmax(200px, 2fr) minmax(92px, 0.8fr) minmax(72px, 0.65fr) minmax(94px, 0.9fr) minmax(106px, 1fr) minmax(124px, 1fr);
|
||||
min-width: 760px;
|
||||
grid-template-columns: 42px minmax(170px, 1.1fr) minmax(240px, 1.6fr) minmax(92px, 0.75fr) minmax(72px, 0.65fr) minmax(94px, 0.85fr) minmax(106px, 1fr) minmax(124px, 1fr);
|
||||
min-width: 940px;
|
||||
}
|
||||
|
||||
.collector-virtual-spacer {
|
||||
min-width: 940px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -388,10 +388,12 @@ describe("CollectorView", () => {
|
||||
it("renders expandable package and file rows with preview columns", () => {
|
||||
const html = renderToStaticMarkup(<CollectorView actions={createActions()} model={buildCollectorWorkspaceViewModel(packages, "all", "", false, [], [], "", true)} />);
|
||||
|
||||
for (const heading of ["Name", "Größe", "Hoster", "Status", "Verfügbarkeit", "Hinzugefügt"]) expect(html).toContain(`>${heading}<`);
|
||||
for (const heading of ["Name", "Datei", "Größe", "Hoster", "Status", "Verfügbarkeit", "Hinzugefügt"]) expect(html).toContain(`>${heading}<`);
|
||||
expect(html).toContain("SBS14HD");
|
||||
expect(html).toContain("SBS14HD.part01.rar");
|
||||
expect(html).toContain("SBS14HD.part02.rar");
|
||||
expect(html).toMatch(/collector-package-row[\s\S]*collector-name-cell[\s\S]*<strong[^>]*>SBS14HD<\/strong>[\s\S]*collector-file-cell is-package[^>]*>2 Dateien<[\s\S]*collector-size-cell/);
|
||||
expect(html).toMatch(/collector-file-row[\s\S]*aria-label="Paket SBS14HD" class="collector-name-cell is-empty"[^>]*><\/span>[\s\S]*collector-file-cell[^>]*>[\s\S]*SBS14HD\.part01\.rar[\s\S]*collector-size-cell/);
|
||||
expect(html).toContain("2/2 online");
|
||||
expect(html).toContain(">Online<");
|
||||
expect(html).toContain("aria-label=\"SBS14HD einklappen\"");
|
||||
@@ -535,11 +537,15 @@ describe("CollectorView", () => {
|
||||
expect(css).toMatch(/@media \(prefers-reduced-motion: reduce\)[\s\S]*\.collector-virtual-spacer\.is-motion-enabled \.collector-virtual-package\s*\{[^}]*transition-duration:\s*300ms, 300ms !important;/s);
|
||||
expect(css).toMatch(/\.collector-package-items-frame\.is-expanding\s*\{[^}]*animation:\s*collector-items-expand/s);
|
||||
expect(css).not.toMatch(/\.collector-package-items-frame\.is-animated\s*\{[^}]*animation:\s*collector-items-expand/s);
|
||||
expect(css).toMatch(/\.collector-table-header-row > span:nth-child\(2\)\s*\{[^}]*padding-left:\s*48px;/s);
|
||||
expect(css).toMatch(/\.collector-name-cell\.is-file\s*\{[^}]*padding-left:\s*32px;/s);
|
||||
expect(css).toMatch(/\.collector-column-name\s*\{[^}]*padding-left:\s*48px;/s);
|
||||
expect(css).toMatch(/\.collector-name-cell strong,\s*\.collector-file-cell\s*\{[^}]*text-overflow:\s*ellipsis;/s);
|
||||
expect(css).toMatch(/\.collector-file-cell\s*\{[^}]*display:\s*flex;/s);
|
||||
expect(css).toMatch(/\.collector-table-header,\s*\.collector-table-body\s*\{[^}]*scrollbar-gutter:\s*stable;/s);
|
||||
expect(css).toMatch(/\.collector-size-cell,\s*\.collector-hoster-cell,\s*\.collector-status-cell,\s*\.collector-availability-cell,\s*\.collector-added-cell\s*\{[^}]*text-align:\s*center;/s);
|
||||
expect(css).toMatch(/\.collector-table-header-row > span:nth-child\(n\+3\)\s*\{[^}]*justify-content:\s*center;[^}]*text-align:\s*center;/s);
|
||||
expect(css).toMatch(/\.collector-table-header-row > span:nth-child\(n\+4\)\s*\{[^}]*justify-content:\s*center;[^}]*text-align:\s*center;/s);
|
||||
expect(css).toMatch(/@media \(max-width: 1530px\)[\s\S]*grid-template-columns:[^;]+;[\s\S]*min-width:\s*1040px;/s);
|
||||
expect(css).toMatch(/@media \(max-width: 1530px\)[\s\S]*\.collector-virtual-spacer\s*\{[^}]*min-width:\s*1040px;/s);
|
||||
expect(css).toMatch(/@media \(max-width: 1120px\)[\s\S]*\.collector-virtual-spacer\s*\{[^}]*min-width:\s*940px;/s);
|
||||
expect(collectorHeaderScrollStyle(37)).toEqual({ transform: "translateX(-37px)" });
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user