Fix disclosure fading and global package toggles
Fade the shared virtual item group from transparent to opaque while expanding and reverse the opacity while collapsing, preserving the clipping row as the geometry owner. Move the downloads package toggle to the far toolbar edge and add a collector toolbar toggle backed by raw unfiltered package IDs. Cover opacity endpoints, intermediate frame geometry, toolbar ordering, and filter-independent collector toggle semantics.
This commit is contained in:
@@ -18,6 +18,7 @@ export interface CollectorViewActions {
|
||||
onLinkSelectionChange: (linkId: string, selected: boolean) => void;
|
||||
onPackageSelectionChange: (packageId: string, selected: boolean) => void;
|
||||
onPackageCollapseChange: (packageId: string) => void;
|
||||
onToggleAllPackages: () => void;
|
||||
onRemoveSelected: () => void;
|
||||
}
|
||||
|
||||
@@ -58,6 +59,15 @@ function packageSize(row: CollectorWorkspacePackageRow): string {
|
||||
return `${row.unknownSizeCount > 0 ? "≥ " : ""}${humanSize(row.totalBytes)}`;
|
||||
}
|
||||
|
||||
export function toggleAllCollectorPackageIds(
|
||||
packageIds: readonly string[],
|
||||
collapsedPackageIds: ReadonlySet<string>
|
||||
): Set<string> {
|
||||
return packageIds.some((packageId) => !collapsedPackageIds.has(packageId))
|
||||
? new Set(packageIds)
|
||||
: new Set();
|
||||
}
|
||||
|
||||
function CollectorHosterLabel({ hoster }: { hoster: ReturnType<typeof formatHosterLabel> }): ReactElement {
|
||||
return (
|
||||
<span className="collector-hoster-label" title={hoster.title}>
|
||||
@@ -94,7 +104,10 @@ export function CollectorToolbar({ model, actions }: CollectorViewProps): ReactE
|
||||
<button className="collector-action" disabled={model.busy || model.totalCount === 0} onClick={actions.onSubmitAll} type="button">{`Alle übergeben (${model.totalCount})`}</button>
|
||||
<button className="collector-action collector-action-danger" disabled={model.busy || model.selectedCount === 0} onClick={actions.onRemoveSelected} type="button">Auswahl entfernen</button>
|
||||
</ToolbarGroup>
|
||||
<ToolbarSearch label="Links durchsuchen" onChange={(event) => actions.onQueryChange(event.target.value)} placeholder="Name, URL oder Hoster" value={model.query} />
|
||||
<ToolbarGroup className="collector-toolbar-tail" label="Suche und Paketdarstellung">
|
||||
<ToolbarSearch label="Links durchsuchen" onChange={(event) => actions.onQueryChange(event.target.value)} placeholder="Name, URL oder Hoster" value={model.query} />
|
||||
<button className="collector-action" disabled={model.busy || model.totalCount === 0} onClick={actions.onToggleAllPackages} type="button">Alle ein-/ausklappen</button>
|
||||
</ToolbarGroup>
|
||||
</Toolbar>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
.collector-sidebar-filter span:last-child { color: var(--ui-text-muted); font-variant-numeric: tabular-nums; }
|
||||
.collector-toolbar { --collector-toolbar-action-gap: 4px; gap: var(--collector-toolbar-action-gap); min-width: 0; width: 100%; }
|
||||
.collector-toolbar .ui-toolbar-group { gap: var(--collector-toolbar-action-gap); }
|
||||
.collector-toolbar-tail { flex: 1 1 auto; justify-content: flex-end; margin-left: auto; }
|
||||
.collector-toolbar-tail .ui-toolbar-search { margin-left: 0; }
|
||||
.collector-action { background: var(--ui-input); border: 1px solid var(--ui-border); border-radius: 6px; color: var(--ui-text-secondary); font: inherit; height: 36px; padding: 0 12px; white-space: nowrap; }
|
||||
.collector-action:hover:not(:disabled) { background: var(--ui-hover); color: var(--ui-text); }
|
||||
.collector-action-primary { background: var(--ui-primary); border-color: var(--ui-primary); color: var(--ui-primary-text); }
|
||||
@@ -62,4 +64,4 @@
|
||||
.collector-dialog-primary { background: var(--ui-primary); border-color: var(--ui-primary); color: var(--ui-primary-text); }
|
||||
.collector-dialog-secondary { background: var(--ui-modal-secondary); color: var(--ui-text-secondary); }
|
||||
@media (max-width: 1366px) { .collector-view { grid-template-columns: 56px minmax(0, 1fr); } .collector-sidebar { overflow: hidden; } .collector-sidebar-filter { padding: 0 7px; } .collector-sidebar-filter span:first-child { overflow: hidden; text-overflow: ellipsis; } .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, .8fr) minmax(78px, .65fr) minmax(100px, .9fr) minmax(112px, 1fr) minmax(132px, 1fr); min-width: 820px; } }
|
||||
@media (max-width: 1120px) { .collector-toolbar { flex-wrap: wrap; } .collector-toolbar .ui-toolbar-search { flex: 1 0 100%; width: 100%; } .collector-table-header-row, .collector-package-row, .collector-file-row { grid-template-columns: 42px minmax(200px, 2fr) minmax(92px, .8fr) minmax(72px, .65fr) minmax(94px, .9fr) minmax(106px, 1fr) minmax(124px, 1fr); min-width: 760px; } }
|
||||
@media (max-width: 1120px) { .collector-toolbar { flex-wrap: wrap; } .collector-toolbar-tail { flex: 1 0 100%; } .collector-toolbar-tail .ui-toolbar-search { flex: 1 1 auto; width: auto; } .collector-table-header-row, .collector-package-row, .collector-file-row { grid-template-columns: 42px minmax(200px, 2fr) minmax(92px, .8fr) minmax(72px, .65fr) minmax(94px, .9fr) minmax(106px, 1fr) minmax(124px, 1fr); min-width: 760px; } }
|
||||
|
||||
@@ -102,9 +102,8 @@ export function DownloadsSidebar({ actions, model }: { actions: DownloadsViewAct
|
||||
{filters.map((filter) => <button aria-current={model.filter === filter.id ? "page" : undefined} className={model.filter === filter.id ? "is-active" : ""} data-sliding-selection-active={model.filter === filter.id} data-sliding-selection-item="true" key={filter.id} onClick={() => actions.onFilterChange(filter.id)} type="button"><span>{filter.label}</span><b>{model.counts[filter.id]}</b></button>)}
|
||||
</SlidingSelection>
|
||||
<label className="downloads-provider-filter"><span>Service</span><select aria-label="Service filtern" disabled={model.providerOptions.length <= 1} onChange={(event) => actions.onProviderFilterChange(event.target.value)} value={model.providerFilter}><option value="all">Alle Services</option>{model.providerOptions.map((provider) => <option key={provider.id} value={provider.id}>{provider.label}</option>)}</select></label>
|
||||
<label className="downloads-sidebar-search"><span>Downloads durchsuchen</span><input className="downloads-search-input" onChange={(event) => actions.onQueryChange(event.target.value)} placeholder="Paket, Datei oder Service" type="search" value={model.query} /></label>
|
||||
<label className="downloads-sidebar-search"><span>Downloads durchsuchen</span><input className="downloads-search-input" onChange={(event) => actions.onQueryChange(event.target.value)} placeholder="Paket, Datei oder Service" type="search" value={model.query} /></label>
|
||||
<div className="downloads-sidebar-actions">
|
||||
<button onClick={actions.onToggleAllPackages} type="button">Alle ein-/ausklappen</button>
|
||||
<button disabled={model.empty} onClick={actions.onClearAll} type="button">Liste leeren</button>
|
||||
</div>
|
||||
<label className="downloads-clipboard-toggle"><input checked={model.clipboardWatcher} onChange={actions.onToggleClipboardWatcher} type="checkbox" />Zwischenablage überwachen</label>
|
||||
@@ -151,11 +150,12 @@ export function DownloadsToolbar({ actions, model }: { actions: DownloadsViewAct
|
||||
<span className="downloads-toolbar-divider" />
|
||||
<button disabled={!hasSelectedPackage} onClick={actions.onMoveSelectionUp} type="button">Nach oben</button>
|
||||
<button disabled={!hasSelectedPackage} onClick={actions.onMoveSelectionDown} type="button">Nach unten</button>
|
||||
<button disabled={!onePackage} onClick={actions.onRenameSelection} type="button">Umbenennen</button>
|
||||
<button disabled={!hasSelection} onClick={actions.onRemoveSelection} type="button">Entfernen</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
<button disabled={!onePackage} onClick={actions.onRenameSelection} type="button">Umbenennen</button>
|
||||
<button disabled={!hasSelection} onClick={actions.onRemoveSelection} type="button">Entfernen</button>
|
||||
<button className="downloads-toolbar-toggle-all" onClick={actions.onToggleAllPackages} type="button">Alle ein-/ausklappen</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function tableState(model: DownloadsViewModel): ReactElement | null {
|
||||
if (model.empty) return <div className="downloads-empty-state" data-visual-region="downloads-empty-state" role="row"><div role="cell"><strong>Noch keine Downloads</strong><span>Füge Links hinzu, um den ersten Download zu starten.</span></div></div>;
|
||||
|
||||
@@ -62,7 +62,7 @@ function itemGroup(
|
||||
packageId,
|
||||
items: items.map((row) => ({ ...row, height: DOWNLOAD_FILE_ROW_HEIGHT })),
|
||||
height,
|
||||
disclosureOpacity: 1,
|
||||
disclosureOpacity: phase === "entering" ? 0 : 1,
|
||||
disclosurePhase: phase
|
||||
};
|
||||
}
|
||||
@@ -197,8 +197,8 @@ export function activateDownloadDisclosureTransition(rows: readonly DownloadDisc
|
||||
return rows.map((row) => {
|
||||
if (row.type !== "item-group") return row;
|
||||
return row.disclosurePhase === "entering"
|
||||
? { ...row, height: row.items.length * DOWNLOAD_FILE_ROW_HEIGHT }
|
||||
: { ...row, height: 0 };
|
||||
? { ...row, height: row.items.length * DOWNLOAD_FILE_ROW_HEIGHT, disclosureOpacity: 1 }
|
||||
: { ...row, height: 0, disclosureOpacity: 0 };
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -237,6 +237,10 @@
|
||||
background: var(--ui-border);
|
||||
}
|
||||
|
||||
.downloads-toolbar-toggle-all {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.downloads-schedule-slot {
|
||||
display: grid;
|
||||
grid-template-columns: 0fr;
|
||||
|
||||
Reference in New Issue
Block a user