MediaWiki:Gadget-GrAnnotations.js: Difference between revisions
No edit summary |
No edit summary |
||
| (11 intermediate revisions by the same user not shown) | |||
| Line 79: | Line 79: | ||
' <span class="gra-icon gra-icon-search" aria-hidden="true"></span>', | ' <span class="gra-icon gra-icon-search" aria-hidden="true"></span>', | ||
' <span class="gra-fab-btn-label">Search</span>', | ' <span class="gra-fab-btn-label">Search</span>', | ||
' </button>', | |||
' <button class="gra-fab-btn gra-fab-btn-dismiss" id="gra-fab-dismiss" type="button" aria-label="Dismiss">', | |||
' <span class="gra-icon gra-icon-dismiss" aria-hidden="true"></span>', | |||
' <span class="gra-fab-btn-label">Close</span>', | |||
' </button>', | ' </button>', | ||
'</div>', | '</div>', | ||
| Line 243: | Line 247: | ||
var fabW, fabH, top, left; | var fabW, fabH, top, left; | ||
if (_mobile) { | if (_mobile) { | ||
/* Docked as a fixed bar below the reader toolbar. | |||
/* | All positioning is handled by CSS via .gra-fab-mobile-docked, | ||
so it never collides with the native selection menu and never | |||
clips at screen edges or causes horizontal scroll. */ | |||
$fab.css({ position: '', top: '', left: '', visibility: '' }) | |||
.addClass('gra-fab-visible gra-fab-mobile-docked'); | |||
$fab.css({ position: ' | |||
.addClass('gra-fab-visible'); | |||
return; | return; | ||
} | } | ||
| Line 267: | Line 264: | ||
} | } | ||
function hideFab() { $fab.removeClass('gra-fab-visible'); } | function hideFab() { $fab.removeClass('gra-fab-visible gra-fab-mobile-docked'); } | ||
function hideActions() { hideFab(); } | function hideActions() { hideFab(); } | ||
| Line 475: | Line 472: | ||
html += '<div class="gra-note-card" data-gra-id="'+esc(n.id)+'">' | html += '<div class="gra-note-card" data-gra-id="'+esc(n.id)+'">' | ||
+ '<div class="gra-card-header">' | + '<div class="gra-card-header">' | ||
+ '< | + '<span class="gra-icon gra-icon-note" aria-hidden="true"></span>' | ||
+ '<div class="gra-card-meta">' | + '<div class="gra-card-meta">' | ||
+ (n.ts ? '<div class="gra-card-ts">'+esc(fmtTs(n.ts))+'</div>' : '') | + (n.ts ? '<div class="gra-card-ts">'+esc(fmtTs(n.ts))+'</div>' : '') | ||
| Line 548: | Line 545: | ||
}); | }); | ||
/* Mobile: show fab | /* Separate timers so mobile + desktop never clobber each other */ | ||
var _selTimer = null; /* desktop debounce */ | |||
var _mobShowTimer = null; /* mobile show-on-touchend */ | |||
/* Mobile: show fab quickly after finger lifts (selection settled). | |||
180ms feels instant while still letting the range stabilise. */ | |||
document.addEventListener('touchend', function(e) { | document.addEventListener('touchend', function(e) { | ||
if (!_mobile) return; | if (!_mobile) return; | ||
if ($fab[0] && $fab[0].contains(e.target)) return; | if ($fab[0] && $fab[0].contains(e.target)) return; | ||
clearTimeout(_mobShowTimer); | |||
clearTimeout( | _mobShowTimer = setTimeout(function() { | ||
var sel = window.getSelection(); | var sel = window.getSelection(); | ||
if (!sel || sel.isCollapsed || !sel.toString().trim()) return; | if (!sel || sel.isCollapsed || !sel.toString().trim()) return; | ||
tryShowActions(); | tryShowActions(); | ||
}, | }, 180); | ||
}, { passive: true }); | }, { passive: true }); | ||
/* Mobile: | /* Mobile: only HIDE the fab when selection is cleared while it's visible. | ||
(Reposition isn't needed now that the bar is docked, and re-running | |||
showFab here was causing the lag/flicker.) */ | |||
document.addEventListener('selectionchange', function() { | document.addEventListener('selectionchange', function() { | ||
if (!_mobile) return; | if (!_mobile) return; | ||
if (!$fab.hasClass('gra-fab-visible')) return; | if (!$fab.hasClass('gra-fab-visible')) return; | ||
var sel = window.getSelection(); | |||
if (!sel || sel.isCollapsed || !sel.toString().trim()) { | |||
clearTimeout(_mobShowTimer); | |||
hideActions(); | |||
} | |||
} | |||
}); | }); | ||
/* selectionchange debounced (desktop) */ | /* selectionchange debounced (desktop only) */ | ||
document.addEventListener('selectionchange', function() { | document.addEventListener('selectionchange', function() { | ||
if (_mobile) return; | |||
_selVersion++; | _selVersion++; | ||
clearTimeout(_selTimer); | clearTimeout(_selTimer); | ||
| Line 585: | Line 584: | ||
if (v !== _selVersion) return; | if (v !== _selVersion) return; | ||
if (_fabSelVer === v) return; | if (_fabSelVer === v) return; | ||
tryShowActions(); | tryShowActions(); | ||
}, 600); | }, 600); | ||
| Line 647: | Line 645: | ||
else if (q) { $(document).trigger($.Event('keydown', {ctrlKey:true, key:'k', keyCode:75})); } | else if (q) { $(document).trigger($.Event('keydown', {ctrlKey:true, key:'k', keyCode:75})); } | ||
}); | }); | ||
/* ── Dismiss button: hide toolbar + clear selection (mobile) ── */ | |||
(function () { | |||
var dismissEl = document.getElementById('gra-fab-dismiss'); | |||
if (!dismissEl) return; | |||
function doDismiss(e) { | |||
e.preventDefault(); e.stopPropagation(); | |||
hideActions(); | |||
_selRange = null; _selText = ''; _selRect = null; | |||
if (window.getSelection) { | |||
var s = window.getSelection(); | |||
if (s && s.removeAllRanges) s.removeAllRanges(); | |||
} | |||
} | |||
dismissEl.addEventListener('touchend', doDismiss, { passive: false }); | |||
dismissEl.addEventListener('click', doDismiss); | |||
}()); | |||
/* Feedback composer */ | /* Feedback composer */ | ||