feat(accounts): add multi-selection and batch removal
Support Windows-style single and Ctrl/Cmd additive row selection, clear selections with Escape, and show the selected count on the removal action. Remove selected accounts behind one confirmation, strengthen the selected-row treatment, and delete the redundant global account activation switch so individual account toggles remain authoritative.
This commit is contained in:
@@ -98,17 +98,17 @@ export interface AccountRulesViewModel {
|
||||
export interface AccountWorkspaceViewModel {
|
||||
activePanel: AccountWorkspacePanel;
|
||||
rows: readonly AccountRowViewModel[];
|
||||
selectedIds: readonly string[];
|
||||
busy: boolean;
|
||||
error?: string;
|
||||
allEnabled?: boolean;
|
||||
statusSort?: "none" | "desc" | "asc";
|
||||
selectedIds: readonly string[];
|
||||
busy: boolean;
|
||||
error?: string;
|
||||
statusSort?: "none" | "desc" | "asc";
|
||||
rules: AccountRulesViewModel;
|
||||
}
|
||||
|
||||
export interface AccountWorkspaceActions {
|
||||
onPanelChange: (panel: AccountWorkspacePanel) => void;
|
||||
onSelect: (rowId: string) => void;
|
||||
onPanelChange: (panel: AccountWorkspacePanel) => void;
|
||||
onSelect: (rowId: string, additive: boolean) => void;
|
||||
onClearSelection: () => void;
|
||||
onToggleEnabled: (rowId: string) => void;
|
||||
onEdit: (rowId: string) => void;
|
||||
onContextMenu: (rowId: string, x: number, y: number) => void;
|
||||
@@ -117,7 +117,6 @@ export interface AccountWorkspaceActions {
|
||||
onRemoveSelected: () => void;
|
||||
onCheckActive: () => void;
|
||||
onCheckAll: () => void;
|
||||
onSetAllEnabled?: (enabled: boolean) => void;
|
||||
onStatusSort?: () => void;
|
||||
onMoveProvider?: (index: number, direction: -1 | 1) => void;
|
||||
onProviderDragStart?: (event: DragEvent<HTMLElement>, index: number) => void;
|
||||
@@ -302,14 +301,14 @@ function AccountRow({
|
||||
gridTemplateColumns: string;
|
||||
minWidth: number;
|
||||
}): ReactElement {
|
||||
const selectRow = (): void => actions.onSelect(row.id);
|
||||
const onClick = (): void => selectRow();
|
||||
const selectRow = (additive = false): void => actions.onSelect(row.id, additive);
|
||||
const onClick = (event: MouseEvent<HTMLDivElement>): void => selectRow(event.ctrlKey || event.metaKey);
|
||||
const onKeyDown = (event: KeyboardEvent<HTMLDivElement>): void => {
|
||||
if (event.target !== event.currentTarget || (event.key !== "Enter" && event.key !== " ")) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
selectRow();
|
||||
selectRow(event.ctrlKey || event.metaKey);
|
||||
};
|
||||
const openContextMenu = (event: MouseEvent<HTMLElement>): void => {
|
||||
event.preventDefault();
|
||||
@@ -455,7 +454,7 @@ function AccountOverview({ model, actions }: AccountWorkspaceProps): ReactElemen
|
||||
<div className="settings-account-local-actions">
|
||||
<div>
|
||||
<button className="settings-button settings-button-secondary" disabled={model.busy} onClick={actions.onAdd} type="button">+ Hinzufügen</button>
|
||||
<button className="settings-button settings-button-secondary" disabled={model.busy || model.selectedIds.length === 0} onClick={actions.onRemoveSelected} type="button">− Entfernen</button>
|
||||
<button className="settings-button settings-button-secondary" disabled={model.busy || model.selectedIds.length === 0} onClick={actions.onRemoveSelected} type="button">− Entfernen{model.selectedIds.length > 1 ? ` (${model.selectedIds.length})` : ""}</button>
|
||||
<button className="settings-button settings-button-secondary" disabled={model.busy || !model.rows.some((row) => row.enabled && row.canCheck)} onClick={actions.onCheckActive} title="Prüft nur aktivierte Accounts." type="button">↻ Aktive aktualisieren</button>
|
||||
<button className="settings-button settings-button-secondary" disabled={model.busy} onClick={actions.onCheckAll} title="Prüft alle angelegten Accounts, auch deaktivierte." type="button">↻ Alle aktualisieren</button>
|
||||
</div>
|
||||
@@ -584,7 +583,16 @@ function AccountRules({ model, actions }: AccountWorkspaceProps): ReactElement {
|
||||
|
||||
export function AccountWorkspace({ model, actions }: AccountWorkspaceProps): ReactElement {
|
||||
return (
|
||||
<div className="settings-account-workspace">
|
||||
<div
|
||||
className="settings-account-workspace"
|
||||
onKeyDown={(event) => {
|
||||
if (event.key !== "Escape" || model.selectedIds.length === 0) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
actions.onClearSelection();
|
||||
}}
|
||||
>
|
||||
{model.busy ? <span aria-live="polite" className="settings-visually-hidden" role="status">Accountdaten werden aktualisiert.</span> : null}
|
||||
{model.error ? <span className="settings-visually-hidden" role="alert">{model.error}</span> : null}
|
||||
<header className="settings-account-heading">
|
||||
@@ -592,18 +600,7 @@ export function AccountWorkspace({ model, actions }: AccountWorkspaceProps): Rea
|
||||
<h2>Accountverwaltung</h2>
|
||||
<p>Accounts hinzufügen, prüfen und verwalten.</p>
|
||||
</div>
|
||||
{typeof model.allEnabled === "boolean" && actions.onSetAllEnabled ? (
|
||||
<label className="settings-rule-toggle settings-account-all-enabled">
|
||||
<input
|
||||
checked={model.allEnabled}
|
||||
disabled={model.busy || model.rows.length === 0}
|
||||
onChange={(event) => actions.onSetAllEnabled?.(event.target.checked)}
|
||||
type="checkbox"
|
||||
/>
|
||||
<span>Accounts zum Herunterladen verwenden</span>
|
||||
</label>
|
||||
) : null}
|
||||
</header>
|
||||
</header>
|
||||
<SlidingSelection activeKey={model.activePanel} aria-label="Accountverwaltung" aria-orientation="horizontal" axis="horizontal" className="settings-account-tabs" role="tablist">
|
||||
{ACCOUNT_WORKSPACE_PANELS.map((panel, index) => (
|
||||
<button
|
||||
|
||||
@@ -647,13 +647,22 @@
|
||||
}
|
||||
|
||||
.settings-account-row.is-selected {
|
||||
background: var(--ui-active);
|
||||
background: color-mix(in srgb, #5b8cff 22%, var(--ui-panel));
|
||||
box-shadow: inset 0 0 0 1px color-mix(in srgb, #5b8cff 58%, transparent);
|
||||
color: var(--ui-text);
|
||||
}
|
||||
|
||||
.settings-account-row.is-selected::before {
|
||||
width: 4px;
|
||||
background: #5b8cff;
|
||||
box-shadow: 0 0 10px color-mix(in srgb, #5b8cff 72%, transparent);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.settings-account-row.is-selected.is-disabled {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.settings-account-row.is-disabled {
|
||||
color: var(--ui-text-muted);
|
||||
opacity: 0.72;
|
||||
|
||||
Reference in New Issue
Block a user