├── .gitignore
├── LICENSE
├── README.md
├── package.js
├── tooltips.coffee
├── tooltips.html
└── versions.json
/.gitignore:
--------------------------------------------------------------------------------
1 | .build*
2 | .versions
3 | .DS_Store
4 | node_modules
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Lookback Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Reactive tooltips for Meteor
2 |
3 | Forget about adding clunky Bootstrap packages. The `lookback:tooltips` package provides tooltips "The Meteor Way" – the reactive way! Barebones, minimal and functional.
4 |
5 | ## Install
6 |
7 | ```bash
8 | meteor add lookback:tooltips
9 | ```
10 |
11 | ## Usage
12 |
13 | You can now use the `tooltips` (singleton) template in your layouts/templates.
14 |
15 | ```html
16 |
17 |
Some content.
18 |
19 | {{ > tooltips }}
20 |
21 | ```
22 |
23 | **You must style the `show` and `hide` classes on a tooltip.**
24 |
25 | Commonly, you would give the tooltip element these basic styles (see the `_tooltip.scss` file in the `example` folder):
26 |
27 | ```css
28 | .tooltip {
29 | z-index: 1001;
30 | pointer-events: none;
31 | transition: opacity .1s ease-out;
32 | opacity: 0;
33 | }
34 |
35 | .tooltip.hide {
36 | opacity: 0;
37 | }
38 |
39 | .tooltip.show {
40 | opacity: 1;
41 | }
42 | ```
43 |
44 | ### Basic
45 |
46 | Attach a tooltip to an element with the `data-tooltip` data attribute:
47 |
48 | ```html
49 |
50 |
Some content.
51 |
52 |
53 |
54 |
55 |
56 | {{ > tooltips }}
57 |
58 | ```
59 |
60 | The tooltip will show when hovering over the button.
61 |
62 | ### Markup
63 |
64 | You can embed markup in your tooltips as well:
65 |
66 | ```html
67 |
68 | ```
69 |
70 | Or refer to an external element containing the markup:
71 |
72 | ```html
73 |
74 | I'm a tooltip with some more markup!
75 |
76 |
77 |
78 | ```
79 |
80 | Remember to hide the external tooltip elements with CSS!
81 |
82 | ### Direction
83 |
84 | You may specify the **direction** of the tooltip around the element as well, with the `data-tooltip-direction` attribute.
85 |
86 | ```html
87 |
88 |
Some content.
89 |
90 |
91 |
92 |
93 |
94 | {{ > tooltips }}
95 |
96 | ```
97 |
98 | The `data-tooltip-direction` attribute takes these values:
99 |
100 | - `n` - Top *(default)*
101 | - `s` - Bottom
102 | - `e` - Right
103 | - `w` - Left
104 |
105 | ### Offsets
106 |
107 | Tooltips support **offsets** from their element when you specify the `data-tooltip-top` and `data-tooltip-left` data attributes on the element.
108 |
109 | ```html
110 |
111 |
Some content.
112 |
113 |
114 |
115 |
116 |
117 | {{ > tooltips }}
118 |
119 | ```
120 | The tooltip in the example above will be offset 50 pixels *to the north* (upwards on screen).
121 |
122 | Both attributes takes positive and negative numbers, intepreted as **pixels**.
123 |
124 | ### Triggers
125 |
126 | Tooltips support different **triggers**, other then on hover, which is the default. Supported triggers are:
127 |
128 | - `hover`
129 | - `click`
130 | - `focus`
131 | - `manual`
132 |
133 | To use the manual trigger, trigger these events on the element:
134 |
135 | - `tooltips:show`
136 | - `tooltips:hide`
137 | - `tooltips:toggle`
138 |
139 | ```html
140 |
141 |
Some content.
142 |
143 |
144 |
145 |
146 |
147 | {{ > tooltips }}
148 |
149 | ```
150 |
151 | Please see the example app for more examples.
152 |
153 | ## Styling
154 |
155 | You can use `data-tooltip-classes` to define custom classes for your tooltip. It adds additional classes to the `.tooltip` element.
156 |
157 | ```html
158 |
159 |
160 |
161 | ```
162 |
163 | The following styles that are included are inlined on the tooltip element itself:
164 |
165 | ```html
166 |
167 |
Content
168 |
169 | ```
170 |
171 | ## Helper classes
172 |
173 | The package adds some helper classes to the tooltip element, for styling and transitioning.
174 |
175 | The main tooltip element has these classes:
176 |
177 | - `tooltip`
178 | - `tooltip--top`, `tooltip--bottom`, `tooltip--left`, `tooltip--right` – For the tooltip direction. Usable for adding CSS arrows and other stuff to a tooltips, depending on its direction. *Defaults to* `tooltip--top`.
179 |
180 | The content wrapper has the `inner` class. Usable for separate styling as well as for transitioning.
181 |
182 | When hovering over the triggering element, a `show` class will be added to the main tooltip element. When the tooltip is inactive, it'll have the `hide` class.
183 |
184 | ## Disabling for other viewports
185 |
186 | It's possible to completely disable the tooltips, or just for a certain viewport. By setting the `Tooltips.disable` option (defaults to `false`), you can pass in `true` to disable all tooltips, or a [`matchMedia`](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia) string which disables all tooltips for that viewport.
187 |
188 | ```js
189 | // Disable for all
190 | Tooltips.disable = true;
191 |
192 | // Disabling for all touch devices:
193 | // https://github.com/Modernizr/Modernizr/blob/master/feature-detects/touchevents.js
194 | var isTouch = !!('ontouchstart' in window) || !!(window.DocumentTouch && document instanceof DocumentTouch);
195 | Tooltips.disable = isTouch;
196 |
197 | // Disable for devices/browsers over 500 px in width
198 | Tooltips.disable = '(min-width: 500px)';
199 |
200 | // Disable for devices/browser below 400 px in width
201 | Tooltips.disable = '(max-width: 400px)';
202 | ```
203 |
204 | You can also disable individual tooltips directly from the markup, by setting the `data-tooltip-disable` attribute:
205 |
206 | ```html
207 |
208 |
209 | ```
210 |
211 | ## API
212 |
213 | This packages exposes an API in the `Tooltips` namespace on the client:
214 |
215 | ```js
216 | // Set a tooltip. Second argument is optional. If passed, it'll be
217 | // the CSS position for the tooltip.
218 | Tooltips.set('Text', {top: 10, left: 30});
219 |
220 | // Get the tooltip. Creates a reactive dependency, and returns an object.
221 | var tip = Tooltips.get();
222 |
223 | /*
224 | =>
225 | tip.text == 'Text';
226 | tip.css == {top: 0, left: 0};
227 | tip.direction == 'tooltip--top';
228 | */
229 |
230 | // Disable all tooltips. Can be `true|false` or a `matchMedia` query.
231 | // Defaults to false.
232 | Tooltips.disable = true;
233 |
234 | // Set position of the tooltip. Second argument is optional. If passed, it'll
235 | // be the direction of the tooltip, and must be one of `n`, `s`, `e`, `w`
236 | // (north, south, east, west).
237 | Tooltips.setPosition({top: 10, left: 30}, 'n');
238 |
239 | // Hide all tooltips
240 | Tooltips.hide();
241 | ```
242 |
243 | ## Version history
244 |
245 | - `0.5.5` - Fixed error where examples templates were compiled into package code
246 | - `0.5.4` - Minor update to README
247 | - `0.5.3` - Allow adding custom classes to tooltips via `data-tooltip-classes`
248 | - `0.5.2` - Make tooltip content update on reactive changes (thanks to using `$.fn.attr()` instead of `$.fn.data()`).
249 | - `0.5.1` - Fix CSS issue in IE.
250 | - `0.5.0` - Support for custom events and triggering. Thanks, [@jazzdragon](http://github.com/jazzdragon)!
251 | - `0.4.0` - Allow inline markup in tooltips.
252 | - `0.3.2` - Fix not being able to set directions for certain directions.
253 | - `0.3.1` - Fix bug where a tooltip's position would be off, if it was placed near the window edge and thus would break into multiple lines.
254 | - `0.3.0` - Add support for disabling tooltips completely, or for certain viewports.
255 | - `0.2.2`
256 | - Export `setPosition` function.
257 | - *Experimental:* Allow removal of tooltips when element is removed.
258 | - `0.2.1` - Fix rounding errors when positioning.
259 | - `0.2.0` - Expose public API namespace (`Tooltips`).
260 | - `0.1.2` - Use `mouseover` event instead of `mouseenter`.
261 | - `0.1.1` - Require Meteor 0.9.3.
262 | - `0.1.0` - Initial publish.
263 |
264 | ## Contributions
265 |
266 | Contributions are welcome. Please open issues and/or file Pull Requests.
267 |
268 | ***
269 |
270 | Made by [Lookback](http://lookback.io).
271 |
--------------------------------------------------------------------------------
/package.js:
--------------------------------------------------------------------------------
1 | Package.describe({
2 | name: 'lookback:tooltips',
3 | summary: 'Reactive tooltips.',
4 | version: '0.6.1',
5 | git: 'https://github.com/lookback/meteor-tooltips.git'
6 | });
7 |
8 | Package.on_use(function(api) {
9 | api.versionsFrom('1.0.4');
10 | api.use('coffeescript reactive-var jquery templating tracker'.split(' '), 'client');
11 |
12 | api.add_files('tooltips.html tooltips.coffee'.split(' '), 'client');
13 | api.export('Tooltips', 'client');
14 | });
15 |
--------------------------------------------------------------------------------
/tooltips.coffee:
--------------------------------------------------------------------------------
1 | # Defaults
2 |
3 | Tooltip =
4 | text: false
5 | css: {top: 0, left: 0}
6 | direction: 'tooltip--top'
7 | classes: ''
8 |
9 | dep = new Tracker.Dependency()
10 | offset = [10, 10]
11 |
12 | DIRECTION_MAP =
13 | 'n': 'tooltip--top'
14 | 's': 'tooltip--bottom'
15 | 'e': 'tooltip--right'
16 | 'w': 'tooltip--left'
17 |
18 | # Tooltip functions
19 |
20 | getTooltip = ->
21 | dep.depend()
22 | return Tooltip
23 |
24 | setTooltip = (what, where) ->
25 | Tooltip.css = where if where
26 | Tooltip.text = what
27 | dep.changed()
28 |
29 | setPosition = (position, direction) ->
30 | Tooltip.css = position
31 | Tooltip.direction = DIRECTION_MAP[direction] if direction
32 | dep.changed()
33 |
34 | setClasses = (classes) ->
35 | Tooltip.classes = classes or ''
36 |
37 | hideTooltip = ->
38 | setTooltip false
39 |
40 | toggleTooltip = ->
41 | if getTooltip().text then hideTooltip() else showTooltip null, $(this)
42 |
43 | positionTooltip = ($el) ->
44 | direction = $el.attr('data-tooltip-direction') or 'n'
45 | $tooltip = $(".tooltip")
46 |
47 | position = $el.offset()
48 | offLeft = $el.attr('data-tooltip-left')
49 | offTop = $el.attr('data-tooltip-top')
50 |
51 | if _.isUndefined(offLeft)
52 | offLeft = 0
53 | else
54 | hasOffsetLeft = true
55 |
56 | if _.isUndefined(offTop)
57 | offTop = 0
58 | else
59 | hasOffsetTop = true
60 |
61 | position.top = switch direction
62 | when 'w', 'e' then (center vertically $tooltip, $el) + offTop
63 | when 'n' then position.top - $tooltip.outerHeight() - (if hasOffsetTop then offTop else offset[1])
64 | when 's' then position.top + $el.outerHeight() + (if hasOffsetTop then offTop else offset[1])
65 |
66 | position.left = switch direction
67 | when 'n', 's' then (center horizontally $tooltip, $el) + offLeft
68 | when 'w' then position.left - $tooltip.outerWidth() - (if hasOffsetLeft then offLeft else offset[0])
69 | when 'e' then position.left + $el.outerWidth() + (if hasOffsetLeft then offLeft else offset[0])
70 |
71 | setPosition(position, direction)
72 |
73 | showTooltip = (evt, $el) ->
74 | $el = $el or $(this)
75 | viewport = $el.attr 'data-tooltip-disable'
76 |
77 | if viewport and _.isString(viewport)
78 | mq = window.matchMedia(viewport)
79 | return false if mq.matches
80 |
81 | content = if selector = $el.attr 'data-tooltip-element'
82 | $target = $(selector)
83 | $target.length and $target.html()
84 | else
85 | $el.attr('data-tooltip')
86 |
87 | setTooltip(content)
88 | setPosition(top: 0, left: 0)
89 | setClasses($el.attr('data-tooltip-classes'))
90 |
91 | Tracker.afterFlush -> positionTooltip($el)
92 |
93 |
94 | # Positioning
95 |
96 | center = (args) ->
97 | middle = args[0] + args[1] / 2
98 | middle - Math.round(args[2] / 2)
99 |
100 | horizontally = ($el, $reference) ->
101 | [$reference.offset().left, $reference.outerWidth(), $el.outerWidth()]
102 |
103 | vertically = ($el, $reference) ->
104 | [$reference.offset().top, $reference.outerHeight(), $el.outerHeight()]
105 |
106 | # Exports
107 |
108 | Tooltips =
109 | disable: false
110 | set: setTooltip
111 | get: getTooltip
112 | hide: hideTooltip
113 | setPosition: setPosition
114 |
115 | # Enable/disable for viewports
116 |
117 | Template.tooltips.onCreated ->
118 | @disabled = new ReactiveVar(Tooltips.disable)
119 |
120 | if Tooltips.disable and _.isString(Tooltips.disable)
121 | mq = window.matchMedia(Tooltips.disable)
122 | @disabled.set(mq.matches)
123 |
124 | mq.addListener (changed) =>
125 | @disabled.set(changed.matches)
126 |
127 |
128 | # Template helpers
129 |
130 | Template.tooltips.helpers
131 | display: ->
132 | tip = getTooltip()
133 |
134 | if Template.instance().disabled.get() is true
135 | return 'hide'
136 |
137 | if tip.text then 'show' else 'hide'
138 |
139 | position: ->
140 | css = getTooltip().css
141 | return "position: absolute; top: #{css.top}px; left: #{css.left}px;"
142 |
143 | content: ->
144 | getTooltip().text
145 |
146 | direction: ->
147 | getTooltip().direction
148 |
149 | classes: ->
150 | getTooltip().classes
151 |
152 | # Init
153 |
154 | Template.tooltip.onRendered ->
155 |
156 | this.lastNode._uihooks =
157 | insertElement: (node, next) ->
158 | next.parentNode.insertBefore(node, next)
159 |
160 | moveElement: (node, next) ->
161 | Tooltips.hide()
162 | next.parentNode.insertBefore(node, next)
163 |
164 | removeElement: (node) ->
165 | Tooltips.hide()
166 | node.parentNode.removeChild(node)
167 |
168 | Meteor.startup ->
169 |
170 | $(document).on 'mouseover', '[data-tooltip]:not([data-tooltip-trigger]), [data-tooltip-element]:not([data-tooltip-trigger]), [data-tooltip-trigger="hover"]', showTooltip
171 |
172 | $(document).on 'mouseout', '[data-tooltip]:not([data-tooltip-trigger]), [data-tooltip-element]:not([data-tooltip-trigger]), [data-tooltip-trigger="hover"]', hideTooltip
173 |
174 | $(document).on 'click', '[data-tooltip-trigger="click"]', toggleTooltip
175 | $(document).on 'focus', '[data-tooltip-trigger="focus"]', showTooltip
176 | $(document).on 'blur', '[data-tooltip-trigger="focus"]', hideTooltip
177 | $(document).on 'tooltips:show', '[data-tooltip-trigger="manual"]', showTooltip
178 | $(document).on 'tooltips:hide', '[data-tooltip-trigger="manual"]', hideTooltip
179 | $(document).on 'tooltips:toggle', '[data-tooltip-trigger="manual"]', toggleTooltip
180 |
--------------------------------------------------------------------------------
/tooltips.html:
--------------------------------------------------------------------------------
1 |
2 |