Skip to content

Commit d601a27

Browse files
committed
Core: New Tooltip component
1 parent abe9831 commit d601a27

File tree

6 files changed

+344
-0
lines changed

6 files changed

+344
-0
lines changed

scripts/build-config.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ const config = {
9292
// Autocomplete
9393
'autocomplete',
9494

95+
// Tooltip
96+
'tooltip',
97+
9598
// VI Video Ads
9699
'vi',
97100

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
import $ from 'dom7';
2+
import Utils from '../../utils/utils';
3+
import Support from '../../utils/support';
4+
import Framework7Class from '../../utils/class';
5+
6+
class Tooltip extends Framework7Class {
7+
constructor(app, params = {}) {
8+
// Extends with open/close Modal methods;
9+
super(app, params);
10+
11+
const tooltip = this;
12+
13+
const defaults = {};
14+
15+
// Extend defaults with modules params
16+
tooltip.useModulesParams(defaults);
17+
18+
tooltip.params = Utils.extend(defaults, params);
19+
20+
const { el } = tooltip.params;
21+
if (!el) return tooltip;
22+
23+
const $el = $(el);
24+
if ($el.length === 0) return tooltip;
25+
26+
const $tooltipEl = $(tooltip.render()).eq(0);
27+
28+
Utils.extend(tooltip, {
29+
app,
30+
$el,
31+
el: $el && $el[0],
32+
$tooltipEl,
33+
tooltipEl: $tooltipEl && $tooltipEl[0],
34+
text: tooltip.params.text || '',
35+
});
36+
37+
$el[0].f7Tooltip = tooltip;
38+
39+
const touchesStart = {};
40+
let isTouched;
41+
function handleTouchStart(e) {
42+
if (isTouched) return;
43+
isTouched = true;
44+
touchesStart.x = e.type === 'touchstart' ? e.targetTouches[0].pageX : e.pageX;
45+
touchesStart.y = e.type === 'touchstart' ? e.targetTouches[0].pageY : e.pageY;
46+
tooltip.show(this);
47+
}
48+
function handleTouchMove(e) {
49+
if (!isTouched) return;
50+
const x = e.type === 'touchmove' ? e.targetTouches[0].pageX : e.pageX;
51+
const y = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY;
52+
const distance = (
53+
((x - touchesStart.x) ** 2) +
54+
((y - touchesStart.y) ** 2)
55+
) ** 0.5;
56+
if (distance > 50) {
57+
isTouched = false;
58+
tooltip.hide();
59+
}
60+
}
61+
function handleTouchEnd() {
62+
if (!isTouched) return;
63+
isTouched = false;
64+
tooltip.hide();
65+
}
66+
function handleMouseEnter() {
67+
tooltip.show(this);
68+
}
69+
function handleMouseLeave() {
70+
tooltip.hide();
71+
}
72+
function handleTransitionEnd() {
73+
if (!$tooltipEl.hasClass('tooltip-in')) {
74+
$tooltipEl.removeClass('tooltip-out').remove();
75+
}
76+
}
77+
78+
tooltip.attachEvents = function attachEvents() {
79+
if (Support.touch) {
80+
const passive = Support.passiveListener ? { passive: true } : false;
81+
$el.on(app.touchEvents.start, handleTouchStart, passive);
82+
app.on('touchmove', handleTouchMove);
83+
app.on('touchend:passive', handleTouchEnd);
84+
return;
85+
}
86+
$tooltipEl.on('transitionend webkitTransitionEnd', handleTransitionEnd);
87+
$el.on('mouseenter', handleMouseEnter);
88+
$el.on('mouseleave', handleMouseLeave);
89+
};
90+
tooltip.detachEvents = function detachEvents() {
91+
if (Support.touch) {
92+
const passive = Support.passiveListener ? { passive: true } : false;
93+
$el.off(app.touchEvents.start, handleTouchStart, passive);
94+
app.off('touchmove', handleTouchMove);
95+
app.off('touchend:passive', handleTouchEnd);
96+
return;
97+
}
98+
$tooltipEl.off('transitionend webkitTransitionEnd', handleTransitionEnd);
99+
$el.off('mouseenter', handleMouseEnter);
100+
$el.off('mouseleave', handleMouseLeave);
101+
};
102+
103+
// Install Modules
104+
tooltip.useModules();
105+
106+
tooltip.init();
107+
108+
return tooltip;
109+
}
110+
position(targetEl) {
111+
const tooltip = this;
112+
const { $tooltipEl, app } = tooltip;
113+
$tooltipEl.css({ left: '', top: '' });
114+
const $targetEl = $(targetEl || tooltip.el);
115+
const [width, height] = [$tooltipEl.width(), $tooltipEl.height()];
116+
117+
$tooltipEl.css({ left: '', top: '' });
118+
119+
let targetWidth;
120+
let targetHeight;
121+
let targetOffsetLeft;
122+
let targetOffsetTop;
123+
if ($targetEl && $targetEl.length > 0) {
124+
targetWidth = $targetEl.outerWidth();
125+
targetHeight = $targetEl.outerHeight();
126+
127+
const targetOffset = $targetEl.offset();
128+
targetOffsetLeft = targetOffset.left - app.left;
129+
targetOffsetTop = targetOffset.top - app.top;
130+
131+
const targetParentPage = $targetEl.parents('.page');
132+
if (targetParentPage.length > 0) {
133+
targetOffsetTop -= targetParentPage[0].scrollTop;
134+
}
135+
}
136+
let [left, top] = [0, 0, 0];
137+
138+
// Top Position
139+
let position = 'top';
140+
141+
if (height < targetOffsetTop) {
142+
// On top
143+
top = targetOffsetTop - height;
144+
} else if (height < app.height - targetOffsetTop - targetHeight) {
145+
// On bottom
146+
position = 'bottom';
147+
top = targetOffsetTop + targetHeight;
148+
} else {
149+
// On middle
150+
position = 'middle';
151+
top = ((targetHeight / 2) + targetOffsetTop) - (height / 2);
152+
if (top <= 0) {
153+
top = 8;
154+
} else if (top + height >= app.height) {
155+
top = app.height - height - 8;
156+
}
157+
}
158+
159+
// Horizontal Position
160+
if (position === 'top' || position === 'bottom') {
161+
left = ((targetWidth / 2) + targetOffsetLeft) - (width / 2);
162+
if (left < 8) left = 8;
163+
if (left + width > app.width) left = app.width - width - 8;
164+
if (left < 0) left = 0;
165+
} else if (position === 'middle') {
166+
left = targetOffsetLeft - width;
167+
if (left < 8 || (left + width > app.width)) {
168+
if (left < 8) left = targetOffsetLeft + targetWidth;
169+
if (left + width > app.width) left = app.width - width - 8;
170+
}
171+
}
172+
173+
// Apply Styles
174+
$tooltipEl.css({ top: `${top}px`, left: `${left}px` });
175+
}
176+
show(aroundEl) {
177+
const tooltip = this;
178+
const { app, $tooltipEl, $el } = tooltip;
179+
app.root.append($tooltipEl);
180+
tooltip.position(aroundEl);
181+
const $aroundEl = $(aroundEl);
182+
$el.trigger('tooltip:show', tooltip);
183+
$tooltipEl.trigger('tooltip:show', tooltip);
184+
if ($aroundEl.length && $aroundEl[0] !== $el[0]) {
185+
$aroundEl.trigger('tooltip:show', tooltip);
186+
}
187+
tooltip.emit('local::show tooltipShow', tooltip);
188+
$tooltipEl.removeClass('tooltip-out').addClass('tooltip-in');
189+
return tooltip;
190+
}
191+
hide() {
192+
const tooltip = this;
193+
const { $tooltipEl, $el } = tooltip;
194+
$el.trigger('tooltip:hide', tooltip);
195+
$tooltipEl.trigger('tooltip:hide', tooltip);
196+
tooltip.emit('local::hide tooltipHide', tooltip);
197+
$tooltipEl.addClass('tooltip-out').removeClass('tooltip-in');
198+
return tooltip;
199+
}
200+
render() {
201+
const tooltip = this;
202+
if (tooltip.params.render) return tooltip.params.render.call(tooltip, tooltip);
203+
const { cssClass, text } = tooltip.params;
204+
return `
205+
<div class="tooltip ${cssClass || ''}">
206+
<div class="tooltip-content">${text || ''}</div>
207+
</div>
208+
`.trim();
209+
}
210+
init() {
211+
const tooltip = this;
212+
tooltip.attachEvents();
213+
}
214+
destroy() {
215+
const tooltip = this;
216+
if (!tooltip.$el || tooltip.destroyed) return;
217+
tooltip.$el.trigger('tooltip:beforedestroy', tooltip);
218+
tooltip.emit('local::beforeDestroy tooltipBeforeDestroy', tooltip);
219+
tooltip.$tooltipEl.remove();
220+
delete tooltip.$el[0].f7Tooltip;
221+
tooltip.detachEvents();
222+
Utils.deleteProps(tooltip);
223+
tooltip.destroyed = true;
224+
}
225+
}
226+
export default Tooltip;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.ios {
2+
@import (multiple) '../../less/colors-ios.less';
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.md {
2+
@import (multiple) '../../less/colors-md.less';
3+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import $ from 'dom7';
2+
import Tooltip from './tooltip-class';
3+
import ConstructorMethods from '../../utils/constructor-methods';
4+
5+
export default {
6+
name: 'tooltip',
7+
static: {
8+
Tooltip,
9+
},
10+
create() {
11+
const app = this;
12+
app.tooltip = ConstructorMethods({
13+
defaultSelector: '.tooltip',
14+
constructor: Tooltip,
15+
app,
16+
domProp: 'f7Tooltip',
17+
});
18+
app.tooltip.show = function show(el) {
19+
const $el = $(el);
20+
if ($el.length === 0) return undefined;
21+
const tooltip = $el[0].f7Tooltip;
22+
if (!tooltip) return undefined;
23+
tooltip.show($el[0]);
24+
return tooltip;
25+
};
26+
app.tooltip.hide = function hide(el) {
27+
const $el = $(el);
28+
if ($el.length === 0) return undefined;
29+
const tooltip = $el[0].f7Tooltip;
30+
if (!tooltip) return undefined;
31+
tooltip.hide();
32+
return tooltip;
33+
};
34+
},
35+
params: {
36+
tooltip: {
37+
el: null,
38+
text: null,
39+
cssClass: null,
40+
render: null,
41+
},
42+
},
43+
on: {
44+
tabMounted(tabEl) {
45+
const app = this;
46+
$(tabEl).find('.tooltip-init').each((index, el) => {
47+
const text = $(el).attr('data-tooltip');
48+
if (!text) return;
49+
app.tooltip.create({ el, text });
50+
});
51+
},
52+
tabBeforeRemove(tabEl) {
53+
$(tabEl).find('.tooltip-init').each((index, el) => {
54+
if (el.f7Tooltip) el.f7Tooltip.destroy();
55+
});
56+
},
57+
pageInit(page) {
58+
const app = this;
59+
page.$el.find('.tooltip-init').each((index, el) => {
60+
const text = $(el).attr('data-tooltip');
61+
if (!text) return;
62+
app.tooltip.create({ el, text });
63+
});
64+
},
65+
pageBeforeRemove(page) {
66+
page.$el.find('.tooltip-init').each((index, el) => {
67+
if (el.f7Tooltip) el.f7Tooltip.destroy();
68+
});
69+
},
70+
},
71+
};
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/* === Tooltip === */
2+
.tooltip {
3+
position: absolute;
4+
z-index: 20000;
5+
background: rgba(0,0,0,0.87);
6+
border-radius: 4px;
7+
padding: 8px 16px;
8+
color: #fff;
9+
font-size: 14px;
10+
box-sizing: border-box;
11+
line-height: 1.2;
12+
opacity: 0;
13+
transform: scale(0.9);
14+
transition-duration: 150ms;
15+
transition-property: opacity, transform;
16+
z-index: 99000;
17+
font-weight: 500;
18+
font-weight: 500;
19+
&.tooltip-in {
20+
transform: scale(1);
21+
opacity: 1;
22+
}
23+
&.tooltip-out {
24+
opacity: 0;
25+
transform: scale(1);
26+
}
27+
.device-desktop & {
28+
font-size: 12px;
29+
padding: 6px 8px;
30+
}
31+
}
32+
33+
& when (@includeIosTheme) {
34+
@import url('./tooltip-ios.less');
35+
}
36+
& when (@includeMdTheme) {
37+
@import url('./tooltip-md.less');
38+
}

0 commit comments

Comments
 (0)