├── .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 | 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 | 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 | 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 | 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 | 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 | 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 | 8 | 9 | 14 | -------------------------------------------------------------------------------- /versions.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": [ 3 | [ 4 | "base64", 5 | "1.0.1" 6 | ], 7 | [ 8 | "blaze", 9 | "2.0.3" 10 | ], 11 | [ 12 | "coffeescript", 13 | "1.0.4" 14 | ], 15 | [ 16 | "deps", 17 | "1.0.5" 18 | ], 19 | [ 20 | "ejson", 21 | "1.0.4" 22 | ], 23 | [ 24 | "geojson-utils", 25 | "1.0.1" 26 | ], 27 | [ 28 | "htmljs", 29 | "1.0.2" 30 | ], 31 | [ 32 | "id-map", 33 | "1.0.1" 34 | ], 35 | [ 36 | "jquery", 37 | "1.0.1" 38 | ], 39 | [ 40 | "json", 41 | "1.0.1" 42 | ], 43 | [ 44 | "meteor", 45 | "1.1.3" 46 | ], 47 | [ 48 | "minimongo", 49 | "1.0.5" 50 | ], 51 | [ 52 | "observe-sequence", 53 | "1.0.3" 54 | ], 55 | [ 56 | "ordered-dict", 57 | "1.0.1" 58 | ], 59 | [ 60 | "random", 61 | "1.0.1" 62 | ], 63 | [ 64 | "reactive-var", 65 | "1.0.3" 66 | ], 67 | [ 68 | "templating", 69 | "1.0.9" 70 | ], 71 | [ 72 | "tracker", 73 | "1.0.3" 74 | ], 75 | [ 76 | "underscore", 77 | "1.0.1" 78 | ] 79 | ], 80 | "pluginDependencies": [], 81 | "toolVersion": "meteor-tool@1.0.35", 82 | "format": "1.0" 83 | } --------------------------------------------------------------------------------