fix: keep source selector stable
This commit is contained in:
@@ -1481,6 +1481,7 @@
|
||||
let priceRefreshTimer = null;
|
||||
let pendingVoteOptionId = null;
|
||||
let pendingVoteRemove = false;
|
||||
let pendingStableOptionOrder = null;
|
||||
|
||||
// ── Init ───────────────────────────────────────────────────
|
||||
function init() {
|
||||
@@ -1760,6 +1761,10 @@
|
||||
}
|
||||
|
||||
function setOptionSource(optionId, sourceKey) {
|
||||
pendingStableOptionOrder = {
|
||||
tabId: activeTab,
|
||||
optionIds: getRenderedOptionIds(),
|
||||
};
|
||||
state.priceSourceSelections = {
|
||||
...state.priceSourceSelections,
|
||||
[optionId]: normalizeSourceKey(sourceKey),
|
||||
@@ -1769,12 +1774,19 @@
|
||||
}
|
||||
|
||||
function setSortMode(sortMode) {
|
||||
pendingStableOptionOrder = null;
|
||||
const allowedModes = new Set(['vote-desc', 'vote-asc', 'price-asc', 'price-desc']);
|
||||
state.sortMode = allowedModes.has(sortMode) ? sortMode : 'vote-desc';
|
||||
localStorage.setItem('cabo_sort_mode', state.sortMode);
|
||||
render();
|
||||
}
|
||||
|
||||
function getRenderedOptionIds() {
|
||||
return [...document.querySelectorAll('#optionsList .option-card[data-option-id]')]
|
||||
.map(card => card.dataset.optionId)
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function getOptionOrderIndexMap() {
|
||||
return new Map(state.options.map((opt, index) => [opt.id, index]));
|
||||
}
|
||||
@@ -1817,6 +1829,24 @@
|
||||
});
|
||||
}
|
||||
|
||||
function applyPendingStableOrder(sortedOptions) {
|
||||
if (!pendingStableOptionOrder || pendingStableOptionOrder.tabId !== activeTab) {
|
||||
pendingStableOptionOrder = null;
|
||||
return sortedOptions;
|
||||
}
|
||||
|
||||
const order = new Map(pendingStableOptionOrder.optionIds.map((optionId, index) => [optionId, index]));
|
||||
pendingStableOptionOrder = null;
|
||||
if (!order.size) return sortedOptions;
|
||||
|
||||
return [...sortedOptions].sort((a, b) => {
|
||||
const aIndex = order.has(a.id) ? order.get(a.id) : Number.MAX_SAFE_INTEGER;
|
||||
const bIndex = order.has(b.id) ? order.get(b.id) : Number.MAX_SAFE_INTEGER;
|
||||
if (aIndex !== bIndex) return aIndex - bIndex;
|
||||
return sortedOptions.indexOf(a) - sortedOptions.indexOf(b);
|
||||
});
|
||||
}
|
||||
|
||||
function escapeHtml(value) {
|
||||
return String(value ?? '')
|
||||
.replace(/&/g, '&')
|
||||
@@ -2308,7 +2338,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
const sorted = sortOptionsByMode(opts, getOptionOrderIndexMap());
|
||||
const sorted = applyPendingStableOrder(sortOptionsByMode(opts, getOptionOrderIndexMap()));
|
||||
const maxVotes = sorted[0] ? getVoteEntries(sorted[0]).length : 1;
|
||||
const budgetBoard = activeTab === 'budget' ? renderBudgetBoard() : '';
|
||||
|
||||
@@ -2325,7 +2355,7 @@
|
||||
: (opt.url ? `<a href="${opt.url}" target="_blank" rel="noopener noreferrer" class="option-link" onclick="event.stopPropagation()">🔗 ${opt.url.replace(/^https?:\/\//, '').split('/')[0]}</a>` : '');
|
||||
|
||||
return `
|
||||
<div class="option-card${hasVoted ? ' voted' : ''}">
|
||||
<div class="option-card${hasVoted ? ' voted' : ''}" data-option-id="${escapeHtml(opt.id)}">
|
||||
<div class="option-top">
|
||||
<div class="option-name">${opt.name}</div>
|
||||
<div class="option-votes">${voteEntries.length} vote${voteEntries.length !== 1 ? 's' : ''}</div>
|
||||
|
||||
Reference in New Issue
Block a user