Files
brk/website_next/wallets/selector/index.js
T
2026-06-19 23:16:27 +02:00

112 lines
2.8 KiB
JavaScript

/**
* @typedef {Object} StoredWallet
* @property {string} id
* @property {string} name
*/
/**
* @typedef {Object} WalletSelectorOptions
* @property {() => string} getSelectedId
* @property {(walletId: string) => void} onSelect
*
* @typedef {Object} WalletSelectorButton
* @property {HTMLButtonElement} button
* @property {string} id
*/
/**
* @param {HTMLElement} walletList
* @param {StoredWallet[]} wallets
* @param {WalletSelectorOptions} options
* @returns {WalletSelectorButton[]}
*/
function renderButtons(walletList, wallets, options) {
const buttons = /** @type {WalletSelectorButton[]} */ ([]);
walletList.replaceChildren();
for (const wallet of wallets) {
const button = document.createElement("button");
const selected = wallet.id === options.getSelectedId();
button.type = "button";
button.setAttribute("aria-pressed", selected ? "true" : "false");
button.append(wallet.name);
button.addEventListener("click", () => {
options.onSelect(wallet.id);
});
buttons.push({ button, id: wallet.id });
walletList.append(button);
}
return buttons;
}
/**
* @param {HTMLElement} walletList
* @param {WalletSelectorOptions} options
*/
export function createSelector(walletList, options) {
/** @type {WalletSelectorButton[]} */
let buttons = [];
function selectSnappedWallet() {
if (buttons.length === 0) return;
const listRect = walletList.getBoundingClientRect();
const listCenter = listRect.left + listRect.width / 2;
const closest = buttons.reduce((best, item) => {
const rect = item.button.getBoundingClientRect();
const center = rect.left + rect.width / 2;
const distance = Math.abs(center - listCenter);
return distance < best.distance
? { item, distance }
: best;
}, {
item: buttons[0],
distance: Number.POSITIVE_INFINITY,
});
if (closest.item.id !== options.getSelectedId()) {
options.onSelect(closest.item.id);
}
}
walletList.addEventListener("scrollend", () => {
selectSnappedWallet();
});
walletList.addEventListener("wheel", (event) => {
const delta = Math.abs(event.deltaX) > Math.abs(event.deltaY)
? event.deltaX
: event.deltaY;
if (delta === 0) return;
const maxScrollLeft = walletList.scrollWidth - walletList.clientWidth;
const nextScrollLeft = Math.max(
0,
Math.min(maxScrollLeft, walletList.scrollLeft + delta),
);
if (nextScrollLeft === walletList.scrollLeft) return;
event.preventDefault();
walletList.scrollLeft = nextScrollLeft;
}, { passive: false });
return {
clear() {
walletList.replaceChildren();
buttons = [];
},
/**
* @param {StoredWallet[]} wallets
*/
render(wallets) {
buttons = renderButtons(walletList, wallets, options);
},
};
}