diff --git a/public/locales/en.json b/public/locales/en.json
index dae6b84..d09c3b6 100644
--- a/public/locales/en.json
+++ b/public/locales/en.json
@@ -343,6 +343,7 @@
"showRelativeDatesDescription": "Everywhere on the site, if a date is less than 7 days ago, it will show as a relative date instead.",
"transactionDefaultRaw": "Default to the 'Raw' tab instead of 'CommonMeta' on the transaction page",
"defaultPageSize": "Default page size for table listings",
+ "tableHotkeys": "Enable table navigation hotkeys (left and right arrows).",
"subMenuDebug": "Debug settings",
"advancedWalletFormats": "Advanced wallet formats",
diff --git a/src/pages/blocks/BlocksTable.tsx b/src/pages/blocks/BlocksTable.tsx
index 7de362e..7eac690 100644
--- a/src/pages/blocks/BlocksTable.tsx
+++ b/src/pages/blocks/BlocksTable.tsx
@@ -38,7 +38,7 @@
order: lowest ? "ASC" : "DESC"
});
- const { paginationTableProps } = useMalleablePagination(
+ const { paginationTableProps, hotkeys } = useMalleablePagination(
res, res?.blocks,
"blocks.tableTotal",
options, setOptions, setPagination
@@ -57,7 +57,7 @@
debug("results? %b res.blocks.length: %d res.count: %d res.total: %d", !!res, res?.blocks?.length, res?.count, res?.total);
- return
+ const tbl =
className="blocks-table"
size="small"
@@ -141,4 +141,9 @@
}
]}
/>;
+
+ return <>
+ {tbl}
+ {hotkeys}
+ >;
}
diff --git a/src/pages/names/NamesTable.tsx b/src/pages/names/NamesTable.tsx
index 97ef21b..f653f8d 100644
--- a/src/pages/names/NamesTable.tsx
+++ b/src/pages/names/NamesTable.tsx
@@ -40,7 +40,7 @@
order: sortNew ? "DESC" : "ASC"
});
- const { paginationTableProps } = useMalleablePagination(
+ const { paginationTableProps, hotkeys } = useMalleablePagination(
res, res?.names,
"names.tableTotal",
options, setOptions, setPagination
@@ -59,7 +59,7 @@
debug("results? %b res.names.length: %d res.count: %d res.total: %d", !!res, res?.names?.length, res?.count, res?.total);
- return
+ const tbl =
className="names-table"
size="small"
@@ -162,4 +162,9 @@
}
]}
/>;
+
+ return <>
+ {tbl}
+ {hotkeys}
+ >;
}
diff --git a/src/pages/settings/SettingsPage.tsx b/src/pages/settings/SettingsPage.tsx
index 80b8ad6..ca211e4 100644
--- a/src/pages/settings/SettingsPage.tsx
+++ b/src/pages/settings/SettingsPage.tsx
@@ -107,6 +107,11 @@
+
+ {/* Enable table navigation hotkeys (left and right arrows) */}
+
+
+
{/* Debug settings */}
diff --git a/src/pages/transactions/TransactionsTable.tsx b/src/pages/transactions/TransactionsTable.tsx
index 4825e26..931b58a 100644
--- a/src/pages/transactions/TransactionsTable.tsx
+++ b/src/pages/transactions/TransactionsTable.tsx
@@ -84,7 +84,7 @@
order: "DESC"
});
- const { paginationTableProps } = useMalleablePagination(
+ const { paginationTableProps, hotkeys } = useMalleablePagination(
res, res?.transactions,
"transactions.tableTotal",
options, setOptions, setPagination
@@ -118,7 +118,7 @@
debug("results? %b res.transactions.length: %d res.count: %d res.total: %d", !!res, res?.transactions?.length, res?.count, res?.total);
- return
+ const tbl =
className="transactions-table"
size="small"
@@ -229,4 +229,9 @@
}
]}
/>;
+
+ return <>
+ {tbl}
+ {hotkeys}
+ >;
}
diff --git a/src/utils/settings.ts b/src/utils/settings.ts
index f38e89a..f9ac8fc 100644
--- a/src/utils/settings.ts
+++ b/src/utils/settings.ts
@@ -48,6 +48,8 @@
readonly transactionDefaultRaw: boolean;
/** Default page size for table listings. */
readonly defaultPageSize: number;
+ /** Enable table navigation hotkeys (left and right arrows). */
+ readonly tableHotkeys: boolean;
// ===========================================================================
// DEBUG SETTINGS
@@ -69,6 +71,7 @@
showRelativeDates: false,
transactionDefaultRaw: false,
defaultPageSize: 15,
+ tableHotkeys: true,
walletFormats: false
};
diff --git a/src/utils/table.tsx b/src/utils/table.tsx
index 7563865..7080f08 100644
--- a/src/utils/table.tsx
+++ b/src/utils/table.tsx
@@ -1,13 +1,15 @@
// Copyright (c) 2020-2021 Drew Lemmy
// This file is part of KristWeb 2 under GPL-3.0.
// Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt
-import { useState, useEffect, useMemo, Dispatch, SetStateAction } from "react";
+import { useState, useEffect, useCallback, useMemo, Dispatch, SetStateAction } from "react";
import { TablePaginationConfig, TableProps, Pagination } from "antd";
import { SorterResult } from "antd/lib/table/interface";
import usePagination from "antd/lib/table/hooks/usePagination";
import { useTranslation, TFunction } from "react-i18next";
-import { useIntegerSetting } from "./settings";
+import { useIntegerSetting, useBooleanSetting } from "./settings";
+
+import { GlobalHotKeys } from "react-hotkeys";
import { useHistory, useLocation } from "react-router-dom";
@@ -97,6 +99,7 @@
setPagination?: Dispatch>
): {
paginationTableProps: Pick, "onChange" | "pagination">;
+ hotkeys: JSX.Element | null;
} {
const { t } = useTranslation();
@@ -131,6 +134,11 @@
}
);
+ const { hotkeys } = usePaginationHotkeys(
+ currentPageSize, res?.total || 0,
+ options, setOptions, setPaginationPos
+ );
+
// Update the pagination
useEffect(() => {
if (setPagination) {
@@ -145,7 +153,8 @@
paginationTableProps: {
onChange: handleLookupTableChange(defaultPageSize, setOptions, setPaginationPos),
pagination: paginationConfig
- }
+ },
+ hotkeys
};
}
@@ -233,3 +242,64 @@
return { options, setOptions: wrappedSetOptions };
}
+
+/** Provides a GlobalHotKeys component that will add the left and right arrow
+ * key hotkeys, allowing keyboard control of the table's pagination. */
+function usePaginationHotkeys(
+ defaultPageSize: number,
+ total: number | undefined,
+ options: LookupFilterOptionsBase,
+ setOptions: (opts: LookupFilterOptionsBase) => void,
+ setPaginationPos?: Dispatch>
+): { hotkeys: JSX.Element | null } {
+ const enableHotkeys = useBooleanSetting("tableHotkeys");
+
+ const navigate = useCallback((direction: "prev" | "next") => {
+ const mul = direction === "next" ? 1 : -1;
+ const pageSize = options?.limit ?? defaultPageSize;
+
+ // The offset for the lookup options
+ const minOffset = 0;
+ const maxOffset = (total || 1) - 1; // TODO: this isn't quite correct
+ const newOffsetRaw = (options?.offset || 0) + (pageSize * mul);
+ const newOffset = Math.min(Math.max(newOffsetRaw, minOffset), maxOffset);
+
+ // The page number for paginationPos
+ const newPage = Math.max(Math.floor(newOffset / pageSize) + 1, 1);
+
+ debug(
+ "hotkeys navigating %s (%d) across %d entries (%d total) ||| " +
+ "old offset: %d new offset: %d (%d) new page: %d ||| " +
+ "min is: %d max is: %d",
+ direction, mul, pageSize, total,
+ options?.offset, newOffset, newOffsetRaw, newPage,
+ minOffset, maxOffset
+ );
+
+ // Update the table and pagination
+ setOptions({ ...options, offset: newOffset });
+ setPaginationPos?.({ current: newPage, pageSize });
+ }, [defaultPageSize, total, options, setOptions, setPaginationPos]);
+
+ // Enforce that the hotkeys get the newest `navigate` function, especially
+ // when the `total` changes
+ const hotkeys = useMemo(() => (
+ { e?.preventDefault(); navigate("prev"); },
+ NEXT: e => { e?.preventDefault(); navigate("next"); }
+ }}
+ />
+ ), [navigate]);
+
+ return {
+ hotkeys: enableHotkeys ? hotkeys : null
+ };
+}