fix(admin): stabilize node sorting with duplicate sort values
This commit is contained in:
parent
bbea15dea4
commit
08702fb643
@ -227,8 +227,7 @@ export default function Nodes() {
|
|||||||
}}
|
}}
|
||||||
onSort={async (source, target, items) => {
|
onSort={async (source, target, items) => {
|
||||||
// NOTE: `items` is the current page's items from ProTable.
|
// NOTE: `items` is the current page's items from ProTable.
|
||||||
// We should avoid mutating it in-place, and we should persist the sort
|
// Avoid mutating it in-place, and persist sort changes reliably.
|
||||||
// changes reliably (await the API call).
|
|
||||||
const sourceIndex = items.findIndex(
|
const sourceIndex = items.findIndex(
|
||||||
(item) => String(item.id) === source
|
(item) => String(item.id) === source
|
||||||
);
|
);
|
||||||
@ -239,19 +238,27 @@ export default function Nodes() {
|
|||||||
if (sourceIndex === -1 || targetIndex === -1) return items;
|
if (sourceIndex === -1 || targetIndex === -1) return items;
|
||||||
|
|
||||||
const prevSortById = new Map(items.map((it) => [it.id, it.sort]));
|
const prevSortById = new Map(items.map((it) => [it.id, it.sort]));
|
||||||
const originalSorts = items.map((item) => item.sort);
|
|
||||||
|
|
||||||
const next = items.slice();
|
const next = items.slice();
|
||||||
const [movedItem] = next.splice(sourceIndex, 1);
|
const [movedItem] = next.splice(sourceIndex, 1);
|
||||||
next.splice(targetIndex, 0, movedItem!);
|
next.splice(targetIndex, 0, movedItem!);
|
||||||
|
|
||||||
// Keep the existing `sort` values bound to positions (so reordering swaps
|
// IMPORTANT:
|
||||||
// sort values instead of inventing new ones).
|
// Some installations have duplicate / empty `sort` values (commonly 0 or null)
|
||||||
const updatedItems = next.map((item, index) => {
|
// which makes the order appear "random" after refresh and also makes
|
||||||
const originalSort = originalSorts[index];
|
// "swap sort values" strategies a no-op.
|
||||||
const newSort = originalSort !== undefined ? originalSort : item.sort;
|
//
|
||||||
return { ...item, sort: newSort };
|
// To make the ordering stable, we re-index the current page to a strictly
|
||||||
});
|
// increasing sequence.
|
||||||
|
const numericSorts = items
|
||||||
|
.map((it) => (typeof it.sort === "number" ? it.sort : Number.NaN))
|
||||||
|
.filter((v) => Number.isFinite(v)) as number[];
|
||||||
|
const baseSort = numericSorts.length ? Math.min(...numericSorts) : 0;
|
||||||
|
|
||||||
|
const updatedItems = next.map((item, index) => ({
|
||||||
|
...item,
|
||||||
|
sort: baseSort + index,
|
||||||
|
}));
|
||||||
|
|
||||||
const changedItems = updatedItems.filter(
|
const changedItems = updatedItems.filter(
|
||||||
(item) => item.sort !== prevSortById.get(item.id)
|
(item) => item.sort !== prevSortById.get(item.id)
|
||||||
@ -259,8 +266,7 @@ export default function Nodes() {
|
|||||||
|
|
||||||
if (changedItems.length > 0) {
|
if (changedItems.length > 0) {
|
||||||
await resetSortWithNode({
|
await resetSortWithNode({
|
||||||
// Send all changed rows (within the current page) so backend can
|
// Send all changed rows (within the current page) so backend can persist.
|
||||||
// persist the new ordering.
|
|
||||||
sort: changedItems.map((item) => ({
|
sort: changedItems.map((item) => ({
|
||||||
id: item.id,
|
id: item.id,
|
||||||
sort: item.sort,
|
sort: item.sort,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user