MediaWiki:Gadget-GrAnnotations.js: Difference between revisions
Tag: Undo |
No edit summary |
||
| Line 1: | Line 1: | ||
/** | /** | ||
* gr_annotations.js — grantha.io inline Notes + Bookmarks + Feedback (v6) | * gr_annotations.js — grantha.io inline Notes + Bookmarks + Feedback (v6 + Strategy B) | ||
*/ | */ | ||
| Line 37: | Line 37: | ||
var _fabSelVer = -1; | var _fabSelVer = -1; | ||
var _mobile = window.innerWidth < 768 || 'ontouchstart' in window; | var _mobile = window.innerWidth < 768 || 'ontouchstart' in window; | ||
var _fabTouched = false; // | var _fabTouched = false; // flag to prevent hideActions when tapping fab | ||
function uid() { return 'gra_' + Date.now() + '_' + Math.random().toString(36).slice(2,7); } | function uid() { return 'gra_' + Date.now() + '_' + Math.random().toString(36).slice(2,7); } | ||
| Line 244: | Line 244: | ||
if (_mobile) { | if (_mobile) { | ||
fabW = 200; fabH = 48; | fabW = 200; fabH = 48; | ||
top = rect. | /* Strategy B: place fab BELOW the selection — the native iOS/Android | ||
selection menu appears ABOVE it, so the two never collide. */ | |||
top = rect.bottom + window.scrollY + 14; | |||
left = rect.left + rect.width / 2 - fabW / 2; | left = rect.left + rect.width / 2 - fabW / 2; | ||
/* Near viewport bottom: flip above, clearing the native bar (~42px) */ | |||
if (rect.bottom + fabH + 22 > window.innerHeight) { | |||
top = rect.top + window.scrollY - fabH - 56; | |||
} | |||
top = clamp(top, window.scrollY + 8, window.scrollY + window.innerHeight - fabH - 8); | top = clamp(top, window.scrollY + 8, window.scrollY + window.innerHeight - fabH - 8); | ||
left = clamp(left, 8, window.innerWidth - fabW - 8); | left = clamp(left, 8, window.innerWidth - fabW - 8); | ||
| Line 527: | Line 533: | ||
function wireEvents() { | function wireEvents() { | ||
/* Suppress native context menu inside article content */ | /* Suppress native context menu inside article content (Android/desktop) */ | ||
document.addEventListener('contextmenu', function(e) { | document.addEventListener('contextmenu', function(e) { | ||
var tag = e.target.tagName; | var tag = e.target.tagName; | ||
| Line 542: | Line 548: | ||
}); | }); | ||
/* Mobile: | /* Mobile: show fab once selection settles (~350ms, alongside native menu). | ||
var _lastTouchEnd = 0; | Strategy B: don't race the native menu — appear with it, in our own space. */ | ||
document.addEventListener('touchend', function(e) { | var _lastTouchEnd = 0; | ||
document.addEventListener('touchend', function(e) { | |||
if (!_mobile) return; | |||
if ($fab[0] && $fab[0].contains(e.target)) return; | |||
_lastTouchEnd = Date.now(); | |||
clearTimeout(_selTimer); | |||
_selTimer = setTimeout(function() { | |||
}, { passive: true }); | var sel = window.getSelection(); | ||
if (!sel || sel.isCollapsed || !sel.toString().trim()) return; | |||
tryShowActions(); | |||
}, 350); | |||
}, { passive: true }); | |||
/* selectionchange debounced */ | /* Mobile: reposition fab live while user drags the selection handles, | ||
hide it if selection is cleared */ | |||
document.addEventListener('selectionchange', function() { | |||
if (!_mobile) return; | |||
if (!$fab.hasClass('gra-fab-visible')) return; | |||
clearTimeout(_selTimer); | |||
_selTimer = setTimeout(function() { | |||
var sel = window.getSelection(); | |||
if (!sel || sel.isCollapsed || !sel.toString().trim()) { hideActions(); return; } | |||
if (captureSelection()) showFab(_selRect); | |||
}, 250); | |||
}); | |||
/* selectionchange debounced (desktop) */ | |||
var _selTimer = null; | var _selTimer = null; | ||
document.addEventListener('selectionchange', function() { | document.addEventListener('selectionchange', function() { | ||