mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-09-13 19:52:20 +02:00
82 lines
3.1 KiB
JavaScript
82 lines
3.1 KiB
JavaScript
// Mobile card selection affordance shared by Memories, Skills, and Tasks.
|
|
// A long press enters the existing bulk-select mode and selects the card held.
|
|
|
|
const CARD_SELECTORS = '.memory-item[data-memory-id], .skill-card[data-skill-name], .task-card[data-id]';
|
|
const HOLD_MS = 450;
|
|
const MOVE_CANCEL_PX = 10;
|
|
|
|
function _mobileSelectEnabled() {
|
|
return window.innerWidth <= 768 && ('ontouchstart' in window || navigator.maxTouchPoints > 0);
|
|
}
|
|
|
|
function _selectButtonFor(card) {
|
|
if (card.matches('.skill-card')) return document.getElementById('skills-select-btn');
|
|
if (card.matches('.task-card')) return document.getElementById('tasks-select-btn');
|
|
return document.getElementById('memory-select-btn');
|
|
}
|
|
|
|
function _cardAfterRerender(card) {
|
|
if (card.matches('.skill-card')) {
|
|
const name = card.dataset.skillName;
|
|
return [...document.querySelectorAll('.skill-card[data-skill-name]')]
|
|
.find(el => el.dataset.skillName === name);
|
|
}
|
|
const isTask = card.matches('.task-card');
|
|
const attr = isTask ? 'data-id' : 'data-memory-id';
|
|
const value = card.getAttribute(attr);
|
|
if (!value) return null;
|
|
return document.querySelector(`${isTask ? '.task-card' : '.memory-item'}[${attr}="${CSS.escape(value)}"]`);
|
|
}
|
|
|
|
function _selectCard(card) {
|
|
const button = _selectButtonFor(card);
|
|
if (!button) return;
|
|
if (!button.classList.contains('active')) button.click();
|
|
setTimeout(() => {
|
|
const checkbox = _cardAfterRerender(card)?.querySelector('.memory-select-cb');
|
|
if (checkbox && !checkbox.checked) checkbox.click();
|
|
}, 40);
|
|
}
|
|
|
|
function _bind(card) {
|
|
if (card.dataset.mobileSelectHoldBound === '1') return;
|
|
card.dataset.mobileSelectHoldBound = '1';
|
|
let timer = null;
|
|
let start = null;
|
|
let fired = false;
|
|
const cancel = () => {
|
|
if (timer) clearTimeout(timer);
|
|
timer = null;
|
|
start = null;
|
|
};
|
|
card.addEventListener('pointerdown', (event) => {
|
|
if (!_mobileSelectEnabled() || event.pointerType === 'mouse') return;
|
|
if (event.target.closest('button, input, textarea, select, a, .memory-item-actions')) return;
|
|
if (card.classList.contains('doclib-card-expanded') || card.classList.contains('email-card-expanded')) return;
|
|
fired = false;
|
|
start = { x: event.clientX, y: event.clientY };
|
|
timer = setTimeout(() => {
|
|
timer = null;
|
|
fired = true;
|
|
card._suppressNextClick = true;
|
|
setTimeout(() => { card._suppressNextClick = false; }, 500);
|
|
try { navigator.vibrate?.(15); } catch (_) {}
|
|
_selectCard(card);
|
|
}, HOLD_MS);
|
|
});
|
|
card.addEventListener('pointermove', (event) => {
|
|
if (!start) return;
|
|
if (Math.hypot(event.clientX - start.x, event.clientY - start.y) > MOVE_CANCEL_PX) cancel();
|
|
});
|
|
card.addEventListener('pointerup', () => { if (!fired) cancel(); else { start = null; fired = false; } });
|
|
card.addEventListener('pointercancel', cancel);
|
|
}
|
|
|
|
function bindCards(root = document) {
|
|
if (!_mobileSelectEnabled()) return;
|
|
root.querySelectorAll?.(CARD_SELECTORS).forEach(_bind);
|
|
}
|
|
|
|
bindCards();
|
|
new MutationObserver(() => bindCards()).observe(document.documentElement, { childList: true, subtree: true });
|