diff --git a/.changeset/popular-guests-carry.md b/.changeset/popular-guests-carry.md new file mode 100644 index 000000000..52065f7ae --- /dev/null +++ b/.changeset/popular-guests-carry.md @@ -0,0 +1,5 @@ +--- +'@siafoundation/design-system': minor +--- + +Table row data now supports an isSelected prop. diff --git a/.changeset/stupid-planets-hide.md b/.changeset/stupid-planets-hide.md new file mode 100644 index 000000000..7d8b1b2d2 --- /dev/null +++ b/.changeset/stupid-planets-hide.md @@ -0,0 +1,5 @@ +--- +'renterd': minor +--- + +The keys table now has pagination controls. diff --git a/apps/renterd-e2e/src/specs/keys.spec.ts b/apps/renterd-e2e/src/specs/keys.spec.ts index bee5151e7..d937d13a8 100644 --- a/apps/renterd-e2e/src/specs/keys.spec.ts +++ b/apps/renterd-e2e/src/specs/keys.spec.ts @@ -39,8 +39,8 @@ test('batch delete multiple keys', async ({ page }) => { const rowIdx3 = getKeyRowByIndex(page, 3) // Select all 4 keys. - await rowIdx0.getByLabel('select key').click() - await rowIdx3.getByLabel('select key').click({ modifiers: ['Shift'] }) + await rowIdx0.click() + await rowIdx3.click({ modifiers: ['Shift'] }) // Delete all 4 keys. const menu = page.getByLabel('key multiselect menu') diff --git a/apps/renterd/components/Keys/KeysBatchMenu/KeysBatchDelete.tsx b/apps/renterd/components/Keys/KeysBatchMenu/KeysBatchDelete.tsx index 6d0433cec..1fd8c5bb9 100644 --- a/apps/renterd/components/Keys/KeysBatchMenu/KeysBatchDelete.tsx +++ b/apps/renterd/components/Keys/KeysBatchMenu/KeysBatchDelete.tsx @@ -15,15 +15,11 @@ import { useDialog } from '../../../contexts/dialog' import { useKeys } from '../../../contexts/keys' export function KeysBatchDelete() { - const { selectionMap, deselect } = useKeys() + const { multiSelect } = useKeys() - const ids = useMemo( - () => Object.entries(selectionMap).map(([_, item]) => item.id), - [selectionMap] - ) const keys = useMemo( - () => Object.entries(selectionMap).map(([_, item]) => item.key), - [selectionMap] + () => Object.entries(multiSelect.selectionMap).map(([_, item]) => item.key), + [multiSelect.selectionMap] ) const { openConfirmDialog } = useDialog() const settingsS3 = useSettingsS3() @@ -43,13 +39,13 @@ export function KeysBatchDelete() { }, }, }) - deselect(ids) + multiSelect.deselectAll() if (response.error) { triggerErrorToast({ title: 'Error deleting keys', body: response.error }) } else { triggerSuccessToast({ title: `Keys deleted` }) } - }, [settingsS3.data, settingsS3Update, deselect, keys, ids]) + }, [settingsS3.data, settingsS3Update, multiSelect, keys]) return ( diff --git a/libs/design-system/src/multi/useMultiSelect.tsx b/libs/design-system/src/multi/useMultiSelect.tsx index 54ba4a182..2339d1450 100644 --- a/libs/design-system/src/multi/useMultiSelect.tsx +++ b/libs/design-system/src/multi/useMultiSelect.tsx @@ -2,6 +2,8 @@ import { MouseEvent, useCallback, useMemo, useState } from 'react' +export type MultiSelect = ReturnType + export function useMultiSelect(dataset?: Item[]) { const [selectionMap, setSelectionMap] = useState>({}) const [, setLastSelectedItem] = useState<{ @@ -10,7 +12,7 @@ export function useMultiSelect(dataset?: Item[]) { }>() const onSelect = useCallback( - (id: string, e: MouseEvent) => { + (id: string, e: MouseEvent) => { if (!dataset) { return } @@ -61,6 +63,31 @@ export function useMultiSelect(dataset?: Item[]) { return getIsPageAllSelected({ dataset, selectionMap }) }, [dataset, selectionMap]) + const selectedIds = useMemo( + () => + Object.entries(selectionMap) + .filter(([_, item]) => !!item) + .map(([id]) => id), + [selectionMap] + ) + + const someSelectedItemsOutsideCurrentPage = useMemo(() => { + if (!dataset) { + if (selectedIds.length === 0) { + return false + } + return true + } + return selectedIds.some((id) => !dataset.some((datum) => datum.id === id)) + }, [dataset, selectedIds]) + + const someSelectedOnCurrentPage = useMemo(() => { + if (!dataset) { + return false + } + return dataset.some((datum) => selectionMap[datum.id]) + }, [dataset, selectionMap]) + const onSelectPage = useCallback(() => { if (!dataset) { return @@ -112,15 +139,32 @@ export function useMultiSelect(dataset?: Item[]) { [selectionMap] ) - return { - onSelect, - onSelectPage, - selectionMap, - isPageAllSelected, - selectionCount, - deselect, - deselectAll, - } + return useMemo( + () => ({ + onSelect, + onSelectPage, + selectionMap, + selectedIds, + isPageAllSelected, + selectionCount, + someSelectedItemsOutsideCurrentPage, + someSelectedOnCurrentPage, + deselect, + deselectAll, + }), + [ + onSelect, + onSelectPage, + selectionMap, + selectedIds, + isPageAllSelected, + selectionCount, + someSelectedItemsOutsideCurrentPage, + someSelectedOnCurrentPage, + deselect, + deselectAll, + ] + ) } function getIsPageAllSelected({