PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.3.8
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.3.8
2.4.0 2.4.1 2.3.8 2.3.7 2.3.6 2.3.5 2.3.4 2.3.3 2.3.2 2.3.1 2.2.0 2.1.21 2.1.20 2.1.19 2.1.18 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.16 2.1.2 All 57 releases
easy-invoice / assets / js / tooltip-manager.js

tooltip-manager.js in Easy Invoice – Invoice Generator, PDF Quotes & Payments 2.3.8, at assets/js/tooltip-manager.js

339 lines 13.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Easy Invoice Tooltip Manager
3 * Reusable tooltip system for the Easy Invoice plugin
4 */
5 (function($) {
6 'use strict';
7
8 // Tooltip Manager object
9 window.EasyInvoiceTooltip = {
10
11 // Initialize tooltip system
12 init: function() {
13 this.setupTooltipContainer();
14 this.bindEvents();
15 },
16
17 // Create tooltip container if it doesn't exist
18 setupTooltipContainer: function() {
19 if ($('#easy-invoice-tooltip').length === 0) {
20 var tooltipHTML = `
21 <div id="easy-invoice-tooltip" class="fixed z-[9999] px-3 py-2 text-sm text-white bg-gray-900 rounded-lg shadow-xl opacity-0 transition-all duration-200 transform scale-95" style="display: none; pointer-events: none; user-select: none;">
22 <div class="tooltip-content font-medium"></div>
23 <div class="tooltip-arrow" style="position: absolute; width: 0; height: 0; border: 6px solid transparent;"></div>
24 </div>
25 `;
26 $('body').append(tooltipHTML);
27 }
28 },
29
30 // Bind tooltip events
31 bindEvents: function() {
32 var self = this;
33
34 // Mouse enter event
35 $(document).off('mouseenter', '[data-tooltip]').on('mouseenter', '[data-tooltip]', function(e) {
36 var tooltipText = $(this).data('tooltip');
37 var position = $(this).data('tooltip-position') || 'top'; // top, bottom, left, right
38 var delay = $(this).data('tooltip-delay') || 200;
39
40 self.showTooltip($(this), tooltipText, position, delay);
41 });
42
43 // Mouse leave event
44 $(document).off('mouseleave', '[data-tooltip]').on('mouseleave', '[data-tooltip]', function() {
45 self.hideTooltip();
46 });
47
48 // Hide tooltip on scroll
49 $(window).off('scroll.tooltip').on('scroll.tooltip', function() {
50 self.hideTooltip();
51 });
52
53
54 },
55
56 // Show tooltip
57 showTooltip: function($element, text, position, delay) {
58 var self = this;
59 var $tooltip = $('#easy-invoice-tooltip');
60
61 // Set tooltip content
62 $tooltip.find('.tooltip-content').text(text);
63
64 // Clear any existing timeout
65 if (self.showTimeout) {
66 clearTimeout(self.showTimeout);
67 }
68
69 // Show tooltip after delay
70 self.showTimeout = setTimeout(function() {
71 // Disable transitions temporarily to prevent sliding
72 $tooltip.css('transition', 'none');
73
74 // First make tooltip visible but transparent to get dimensions
75 $tooltip.show().css('opacity', '0').removeClass('opacity-0 scale-95');
76
77 // Position tooltip
78 self.positionTooltip($element, $tooltip, position);
79
80 // Re-enable transitions and make it visible with animation
81 setTimeout(function() {
82 $tooltip.css('transition', 'all 0.2s ease');
83 $tooltip.addClass('opacity-100 scale-100').css('opacity', '1');
84 }, 10);
85 }, delay);
86 },
87
88 // Hide tooltip
89 hideTooltip: function() {
90 var $tooltip = $('#easy-invoice-tooltip');
91
92 // Clear any existing timeout
93 if (this.showTimeout) {
94 clearTimeout(this.showTimeout);
95 }
96
97 $tooltip.removeClass('opacity-100 scale-100').addClass('opacity-0 scale-95');
98 setTimeout(function() {
99 $tooltip.hide();
100 }, 200);
101 },
102
103 // Position tooltip
104 positionTooltip: function($element, $tooltip, position) {
105 var elementRect = $element[0].getBoundingClientRect();
106 var $arrow = $tooltip.find('.tooltip-arrow');
107
108 // Get tooltip dimensions after it's visible
109 var tooltipWidth = $tooltip.outerWidth();
110 var tooltipHeight = $tooltip.outerHeight();
111
112 var left, top;
113 var arrowStyles = {};
114 var offset = 12; // Space between tooltip and element
115
116 // Calculate position based on preference
117 switch (position) {
118 case 'top':
119 left = elementRect.left + (elementRect.width / 2) - (tooltipWidth / 2);
120 top = elementRect.top - tooltipHeight - offset;
121 arrowStyles = {
122 'position': 'absolute',
123 'top': '100%',
124 'left': '50%',
125 'transform': 'translateX(-50%)',
126 'width': '0',
127 'height': '0',
128 'border-left': '6px solid transparent',
129 'border-right': '6px solid transparent',
130 'border-top': '6px solid #111827',
131 'border-bottom': 'none'
132 };
133 break;
134
135 case 'bottom':
136 left = elementRect.left + (elementRect.width / 2) - (tooltipWidth / 2);
137 top = elementRect.bottom + offset;
138 arrowStyles = {
139 'position': 'absolute',
140 'bottom': '100%',
141 'left': '50%',
142 'transform': 'translateX(-50%)',
143 'width': '0',
144 'height': '0',
145 'border-left': '6px solid transparent',
146 'border-right': '6px solid transparent',
147 'border-bottom': '6px solid #111827',
148 'border-top': 'none'
149 };
150 break;
151
152 case 'left':
153 left = elementRect.left - tooltipWidth - offset;
154 top = elementRect.top + (elementRect.height / 2) - (tooltipHeight / 2);
155 arrowStyles = {
156 'position': 'absolute',
157 'left': '100%',
158 'top': '50%',
159 'transform': 'translateY(-50%)',
160 'width': '0',
161 'height': '0',
162 'border-top': '6px solid transparent',
163 'border-bottom': '6px solid transparent',
164 'border-left': '6px solid #111827',
165 'border-right': 'none'
166 };
167 break;
168
169 case 'right':
170 left = elementRect.right + offset;
171 top = elementRect.top + (elementRect.height / 2) - (tooltipHeight / 2);
172 arrowStyles = {
173 'position': 'absolute',
174 'right': '100%',
175 'top': '50%',
176 'transform': 'translateY(-50%)',
177 'width': '0',
178 'height': '0',
179 'border-top': '6px solid transparent',
180 'border-bottom': '6px solid transparent',
181 'border-right': '6px solid #111827',
182 'border-left': 'none'
183 };
184 break;
185
186 default:
187 left = elementRect.left + (elementRect.width / 2) - (tooltipWidth / 2);
188 top = elementRect.top - tooltipHeight - offset;
189 arrowStyles = {
190 'position': 'absolute',
191 'top': '100%',
192 'left': '50%',
193 'transform': 'translateX(-50%)',
194 'width': '0',
195 'height': '0',
196 'border-left': '6px solid transparent',
197 'border-right': '6px solid transparent',
198 'border-top': '6px solid #111827',
199 'border-bottom': 'none'
200 };
201 }
202
203 // Ensure tooltip doesn't go off screen
204 var adjustedPosition = this.adjustPosition(left, top, { width: tooltipWidth, height: tooltipHeight }, position);
205 left = adjustedPosition.left;
206 top = adjustedPosition.top;
207
208 // Update arrow styles if position changed
209 if (adjustedPosition.position !== position) {
210 arrowStyles = this.getArrowStyles(adjustedPosition.position);
211 }
212
213 // Apply positioning
214 $tooltip.css({
215 left: left + 'px',
216 top: top + 'px'
217 });
218
219 // Update arrow with complete styles
220 $arrow.css(arrowStyles);
221 },
222
223 // Adjust position to keep tooltip on screen
224 adjustPosition: function(left, top, tooltipRect, position) {
225 var windowWidth = window.innerWidth;
226 var windowHeight = window.innerHeight;
227 var margin = 10;
228
229 // Check horizontal bounds
230 if (left < margin) {
231 left = margin;
232 } else if (left + tooltipRect.width > windowWidth - margin) {
233 left = windowWidth - tooltipRect.width - margin;
234 }
235
236 // Check vertical bounds and adjust position if needed
237 if (top < margin) {
238 if (position === 'top') {
239 top = margin;
240 position = 'bottom';
241 }
242 } else if (top + tooltipRect.height > windowHeight - margin) {
243 if (position === 'bottom') {
244 top = windowHeight - tooltipRect.height - margin;
245 position = 'top';
246 }
247 }
248
249 return { left: left, top: top, position: position };
250 },
251
252 // Get arrow styles for position
253 getArrowStyles: function(position) {
254 switch (position) {
255 case 'top':
256 return {
257 'position': 'absolute',
258 'top': '100%',
259 'left': '50%',
260 'transform': 'translateX(-50%)',
261 'width': '0',
262 'height': '0',
263 'border-left': '6px solid transparent',
264 'border-right': '6px solid transparent',
265 'border-top': '6px solid #111827',
266 'border-bottom': 'none'
267 };
268 case 'bottom':
269 return {
270 'position': 'absolute',
271 'bottom': '100%',
272 'left': '50%',
273 'transform': 'translateX(-50%)',
274 'width': '0',
275 'height': '0',
276 'border-left': '6px solid transparent',
277 'border-right': '6px solid transparent',
278 'border-bottom': '6px solid #111827',
279 'border-top': 'none'
280 };
281 case 'left':
282 return {
283 'position': 'absolute',
284 'left': '100%',
285 'top': '50%',
286 'transform': 'translateY(-50%)',
287 'width': '0',
288 'height': '0',
289 'border-top': '6px solid transparent',
290 'border-bottom': '6px solid transparent',
291 'border-left': '6px solid #111827',
292 'border-right': 'none'
293 };
294 case 'right':
295 return {
296 'position': 'absolute',
297 'right': '100%',
298 'top': '50%',
299 'transform': 'translateY(-50%)',
300 'width': '0',
301 'height': '0',
302 'border-top': '6px solid transparent',
303 'border-bottom': '6px solid transparent',
304 'border-right': '6px solid #111827',
305 'border-left': 'none'
306 };
307 default:
308 return {
309 'position': 'absolute',
310 'top': '100%',
311 'left': '50%',
312 'transform': 'translateX(-50%)',
313 'width': '0',
314 'height': '0',
315 'border-left': '6px solid transparent',
316 'border-right': '6px solid transparent',
317 'border-top': '6px solid #111827',
318 'border-bottom': 'none'
319 };
320 }
321 },
322
323 // Public method to show tooltip programmatically
324 show: function($element, text, position, delay) {
325 this.showTooltip($element, text, position || 'top', delay || 0);
326 },
327
328 // Public method to hide tooltip programmatically
329 hide: function() {
330 this.hideTooltip();
331 }
332 };
333
334 // Initialize tooltip system when document is ready
335 $(document).ready(function() {
336 EasyInvoiceTooltip.init();
337 });
338
339 })(jQuery);