Diff -- /var/www/vhosts/3dshawn.com/site1/assets/js/ds_help.js

O Operator
Diff

/var/www/vhosts/3dshawn.com/site1/assets/js/ds_help.js

added on local at 2026-07-12 00:35:23

Added
+132
lines
Removed
-0
lines
Context
0
unchanged
Blobs
from
to 5d9af64ff731
Restore this content →
Unified diff
Naive LCS-based line diff. Additions in emerald, deletions in rose. Both sides decompressed live from BLOB_STORE.
1/*
2 * DriftSense -- help-tooltip runtime.
3 *
4 * Scans for <span class="ds-help" data-help-title data-help-body
5 * [data-help-link] [data-help-link-label]>...
6 * elements and wires up a single reusable floating popover that
7 * fades in on hover/focus.
8 *
9 * One popover element in the DOM at any time. Position math flips
10 * below-chip when the chip is too close to the top of the viewport.
11 */
12(function () {
13 'use strict';
14
15 var pop = null;
16 var currentAnchor = null;
17 var hideTimer = null;
18
19 function ensurePop() {
20 if (pop) return pop;
21 pop = document.createElement('div');
22 pop.className = 'ds-help-popover';
23 pop.setAttribute('role', 'tooltip');
24 pop.innerHTML = ''
25 + '<span class="dsh-title"></span>'
26 + '<span class="dsh-body"></span>'
27 + '<a class="dsh-link" href="#" style="display:none"></a>';
28 document.body.appendChild(pop);
29 // Keep the popover alive when the pointer moves onto it so the
30 // user can click links inside.
31 pop.addEventListener('mouseenter', function () {
32 if (hideTimer) { clearTimeout(hideTimer); hideTimer = null; }
33 });
34 pop.addEventListener('mouseleave', schedHide);
35 return pop;
36 }
37
38 function show(anchor) {
39 var el = ensurePop();
40 var title = anchor.getAttribute('data-help-title') || '';
41 var body = anchor.getAttribute('data-help-body') || '';
42 var link = anchor.getAttribute('data-help-link') || '';
43 var lbl = anchor.getAttribute('data-help-link-label') || 'Learn more';
44
45 el.querySelector('.dsh-title').innerHTML = title;
46 el.querySelector('.dsh-body').innerHTML = body;
47 var a = el.querySelector('.dsh-link');
48 if (link) {
49 a.setAttribute('href', link);
50 a.innerHTML = lbl + ' <svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2"><polyline points="9 18 15 12 9 6"/></svg>';
51 a.style.display = '';
52 } else {
53 a.style.display = 'none';
54 }
55
56 // Position: center of anchor, then measure + flip if needed
57 var rect = anchor.getBoundingClientRect();
58 var vh = window.innerHeight || document.documentElement.clientHeight;
59 var vw = window.innerWidth || document.documentElement.clientWidth;
60
61 // Temporarily reveal to measure
62 el.style.left = '-9999px';
63 el.style.top = '-9999px';
64 el.classList.remove('below');
65 el.classList.add('on');
66 var ph = el.offsetHeight;
67 var pw = el.offsetWidth;
68 el.classList.remove('on');
69
70 var above = rect.top >= ph + 18;
71 var cx = rect.left + rect.width / 2;
72 // Clamp horizontally so popover stays on-screen
73 var pad = 12;
74 if (cx - pw / 2 < pad) cx = pw / 2 + pad;
75 if (cx + pw / 2 > vw - pad) cx = vw - pw / 2 - pad;
76
77 if (above) {
78 el.classList.remove('below');
79 el.style.left = cx + 'px';
80 el.style.top = (rect.top - 10) + 'px';
81 el.style.transformOrigin = 'center bottom';
82 el.style.transform = 'translate(-50%, calc(-100% - 0px))';
83 } else {
84 el.classList.add('below');
85 el.style.left = cx + 'px';
86 el.style.top = (rect.bottom + 10) + 'px';
87 el.style.transformOrigin = 'center top';
88 el.style.transform = 'translate(-50%, 0)';
89 }
90 requestAnimationFrame(function () {
91 el.classList.add('on');
92 });
93 }
94
95 function hide() {
96 if (pop) pop.classList.remove('on');
97 currentAnchor = null;
98 }
99 function schedHide() {
100 if (hideTimer) clearTimeout(hideTimer);
101 hideTimer = setTimeout(hide, 120);
102 }
103
104 function attach(el) {
105 if (el.__dsHelpAttached) return; el.__dsHelpAttached = true;
106 el.setAttribute('tabindex', el.getAttribute('tabindex') || '0');
107 el.setAttribute('aria-label', 'Help');
108 el.addEventListener('mouseenter', function () {
109 if (hideTimer) { clearTimeout(hideTimer); hideTimer = null; }
110 currentAnchor = el;
111 show(el);
112 });
113 el.addEventListener('mouseleave', schedHide);
114 el.addEventListener('focus', function () { currentAnchor = el; show(el); });
115 el.addEventListener('blur', hide);
116 el.addEventListener('keydown', function (e) {
117 if (e.key === 'Escape') hide();
118 });
119 }
120
121 function init(root) {
122 (root || document).querySelectorAll('.ds-help').forEach(attach);
123 }
124 if (document.readyState === 'loading') {
125 document.addEventListener('DOMContentLoaded', function () { init(); });
126 } else {
127 init();
128 }
129 window.dsHelpInit = init;
130 window.addEventListener('resize', hide);
131 window.addEventListener('scroll', hide, true);
132})();