Skip to content

Commit d8f3745

Browse files
committed
Issue #1809 has been fixed.
1 parent 3391eb5 commit d8f3745

File tree

7 files changed

+390
-89
lines changed

7 files changed

+390
-89
lines changed

packages/Webkul/Admin/src/Resources/assets/js/app.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,10 @@ window.app = createApp({
5252
}
5353

5454
const parentElement = event.currentTarget.parentElement;
55-
55+
5656
if (parentElement.classList.contains('sidebar-collapsed')) {
5757
parentElement.classList.remove('sidebar-collapsed');
58-
58+
5959
parentElement.classList.add('sidebar-not-collapsed');
6060
}
6161

@@ -67,7 +67,7 @@ window.app = createApp({
6767
}
6868

6969
const parentElement = event.currentTarget.parentElement;
70-
70+
7171
if (parentElement.classList.contains('sidebar-not-collapsed')) {
7272
parentElement.classList.remove('sidebar-not-collapsed');
7373

@@ -79,7 +79,7 @@ window.app = createApp({
7979
const sidebar = this.$refs.sidebar;
8080

8181
if (
82-
sidebar &&
82+
sidebar &&
8383
!sidebar.contains(event.target)
8484
) {
8585
this.isMenuActive = false;
@@ -107,6 +107,7 @@ import VeeValidate from "./plugins/vee-validate";
107107
import CreateElement from "./plugins/createElement";
108108
import Draggable from "./plugins/draggable";
109109
import VueCal from "./plugins/vue-cal";
110+
110111
[
111112
Admin,
112113
Axios,
@@ -123,9 +124,11 @@ import VueCal from "./plugins/vue-cal";
123124
*/
124125
import Debounce from "./directives/debounce";
125126
import DOMPurify from "./directives/dompurify";
127+
import ToolTip from "./directives/tooltip";
126128

127129
app.directive("debounce", Debounce);
128130
app.directive("safe-html", DOMPurify);
131+
app.directive("tooltip", ToolTip);
129132

130133
export default app;
131134

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
export default {
2+
mounted(el, binding) {
3+
initTooltip(el, binding);
4+
},
5+
updated(el, binding) {
6+
initTooltip(el, binding);
7+
}
8+
};
9+
10+
const initTooltip = (el, binding) => {
11+
const defaultOptions = {
12+
placement: 'top',
13+
trigger: 'hover',
14+
html: false,
15+
content: '',
16+
delay: { show: 200, hide: 100 }
17+
};
18+
19+
const options = {
20+
...defaultOptions,
21+
...(typeof binding.value === 'object' ? binding.value : { content: binding.value })
22+
};
23+
24+
let tooltip = document.getElementById(`tooltip-${el.tooltipId}`);
25+
26+
if (! tooltip) {
27+
el.tooltipId = Math.random().toString(36).substring(2, 9);
28+
tooltip = document.createElement('div');
29+
tooltip.id = `tooltip-${el.tooltipId}`;
30+
tooltip.className = 'max-w-[250px] break-words rounded-lg bg-gray-800 px-4 py-3 text-sm leading-snug text-white shadow-lg transition-opacity transition-transform duration-200';
31+
tooltip.style.display = 'none';
32+
tooltip.style.position = 'absolute';
33+
tooltip.style.zIndex = '10000';
34+
35+
const inner = document.createElement('div');
36+
inner.className = 'tooltip-inner';
37+
38+
const arrow = document.createElement('div');
39+
arrow.className = 'absolute h-0 w-0 border-solid';
40+
41+
tooltip.appendChild(inner);
42+
tooltip.appendChild(arrow);
43+
document.body.appendChild(tooltip);
44+
45+
if (options.html) {
46+
inner.innerHTML = options.content;
47+
} else {
48+
inner.textContent = options.content;
49+
}
50+
51+
el._tooltip = tooltip;
52+
53+
const showTooltip = () => {
54+
tooltip.style.display = 'block';
55+
56+
const rect = el.getBoundingClientRect();
57+
const tooltipRect = tooltip.getBoundingClientRect();
58+
59+
let top, left;
60+
61+
switch (options.placement) {
62+
case 'top':
63+
top = rect.top - tooltipRect.height - 10;
64+
left = rect.left + (rect.width / 2) - (tooltipRect.width / 2);
65+
arrow.style.top = 'auto';
66+
arrow.style.bottom = '-5px';
67+
arrow.style.left = '50%';
68+
arrow.style.transform = 'translateX(-50%)';
69+
break;
70+
case 'bottom':
71+
top = rect.bottom + 10;
72+
left = rect.left + (rect.width / 2) - (tooltipRect.width / 2);
73+
arrow.style.bottom = 'auto';
74+
arrow.style.top = '-5px';
75+
arrow.style.left = '50%';
76+
arrow.style.transform = 'translateX(-50%) rotate(180deg)';
77+
break;
78+
case 'left':
79+
top = rect.top + (rect.height / 2) - (tooltipRect.height / 2);
80+
left = rect.left - tooltipRect.width - 10;
81+
arrow.style.top = '50%';
82+
arrow.style.left = 'auto';
83+
arrow.style.right = '-5px';
84+
arrow.style.transform = 'translateY(-50%) rotate(-90deg)';
85+
break;
86+
case 'right':
87+
top = rect.top + (rect.height / 2) - (tooltipRect.height / 2);
88+
left = rect.right + 10;
89+
arrow.style.top = '50%';
90+
arrow.style.right = 'auto';
91+
arrow.style.left = '-5px';
92+
arrow.style.transform = 'translateY(-50%) rotate(90deg)';
93+
break;
94+
}
95+
96+
if (top < 0) {
97+
top = 0;
98+
}
99+
100+
if (left < 0) {
101+
left = 0;
102+
}
103+
104+
if (left + tooltipRect.width > window.innerWidth) {
105+
left = window.innerWidth - tooltipRect.width;
106+
}
107+
108+
tooltip.style.top = `${top + window.scrollY}px`;
109+
tooltip.style.left = `${left + window.scrollX}px`;
110+
};
111+
112+
const hideTooltip = () => {
113+
tooltip.style.display = 'none';
114+
};
115+
116+
if (options.trigger === 'hover') {
117+
el.addEventListener('mouseenter', () => {
118+
el._showTimeout = setTimeout(showTooltip, options.delay.show);
119+
});
120+
121+
el.addEventListener('mouseleave', () => {
122+
clearTimeout(el._showTimeout);
123+
el._hideTimeout = setTimeout(hideTooltip, options.delay.hide);
124+
});
125+
} else if (options.trigger === 'click') {
126+
el.addEventListener('click', showTooltip);
127+
128+
document.addEventListener('click', (e) => {
129+
if (e.target !== el && !el.contains(e.target)) {
130+
hideTooltip();
131+
}
132+
});
133+
}
134+
} else {
135+
const inner = tooltip.querySelector('.tooltip-inner');
136+
137+
if (options.html) {
138+
inner.innerHTML = options.content;
139+
} else {
140+
inner.textContent = options.content;
141+
}
142+
}
143+
}

0 commit comments

Comments
 (0)