├── .gitattributes ├── .gitignore ├── d3.v2.js ├── dist ├── css │ └── d3-bootstrap-plugins.min.css └── js │ ├── d3-bootstrap-plugins.js │ └── d3-bootstrap-plugins.min.js ├── examples ├── popover │ ├── index.coffee │ ├── index.css │ ├── index.html │ └── index.js └── tooltip │ ├── index.coffee │ ├── index.css │ ├── index.html │ └── index.js ├── grunt.js ├── package.json ├── readme.md └── src ├── css └── plugins.css └── js ├── begin.js ├── end.js └── plugins.coffee /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #Build and node 2 | node_modules 3 | build 4 | 5 | # Numerous always-ignore extensions 6 | *.diff 7 | *.err 8 | *.orig 9 | *.log 10 | *.rej 11 | *.swo 12 | *.swp 13 | *.vi 14 | *~ 15 | *.sass-cache 16 | 17 | # OS or Editor folders 18 | .DS_Store 19 | Thumbs.db 20 | .cache 21 | .project 22 | .settings 23 | .tmproj 24 | nbproject 25 | *.sublime-project 26 | *.sublime-workspace 27 | 28 | # Dreamweaver added files 29 | _notes 30 | dwsync.xml 31 | 32 | # Komodo 33 | *.komodoproject 34 | .komodotools 35 | 36 | # Espresso 37 | *.esproj 38 | *.espressostorage 39 | 40 | # Folders to ignore 41 | .hg 42 | .svn 43 | .CVS 44 | intermediate 45 | publish 46 | .idea 47 | 48 | # build script local files 49 | build/buildinfo.properties 50 | build/config/buildinfo.properties 51 | 52 | #emacs junk 53 | *~ 54 | \#*\# 55 | /.emacs.desktop 56 | /.emacs.desktop.lock 57 | .elc 58 | auto-save-list 59 | tramp 60 | .\#* 61 | 62 | # Org-mode 63 | .org-id-locations 64 | *_archive -------------------------------------------------------------------------------- /dist/css/d3-bootstrap-plugins.min.css: -------------------------------------------------------------------------------- 1 | .fade{opacity:0;-webkit-transition:opacity 0.15s linear;-moz-transition:opacity 0.15s linear;-ms-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear}.fade.in{opacity:1}.tooltip{position:absolute;z-index:1020;display:block;padding:5px;font-size:11px;opacity:0;filter:alpha(opacity=0);visibility:visible}.tooltip.in{opacity:0.8;filter:alpha(opacity=80)}.tooltip.top{margin-top:-2px}.tooltip.right{margin-left:2px}.tooltip.bottom{margin-top:2px}.tooltip.left{margin-left:-2px}.tooltip.top .arrow{bottom:0;left:50%;margin-left:-5px;border-top:5px solid #000;border-right:5px solid transparent;border-left:5px solid transparent}.tooltip.left .arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000}.tooltip.bottom .arrow{top:0;left:50%;margin-left:-5px;border-right:5px solid transparent;border-bottom:5px solid #000;border-left:5px solid transparent}.tooltip.right .arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-right:5px solid #000;border-bottom:5px solid transparent}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.arrow{position:absolute;width:0;height:0}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;padding:5px}.popover.top{margin-top:-5px}.popover.right{margin-left:5px}.popover.bottom{margin-top:5px}.popover.left{margin-left:-5px}.popover.top .arrow{bottom:0;left:50%;margin-left:-5px;border-top:5px solid #000;border-right:5px solid transparent;border-left:5px solid transparent}.popover.right .arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-right:5px solid #000;border-bottom:5px solid transparent}.popover.bottom .arrow{top:0;left:50%;margin-left:-5px;border-right:5px solid transparent;border-bottom:5px solid #000;border-left:5px solid transparent}.popover.left .arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000}.popover-inner{width:280px;padding:3px;overflow:hidden;background:#000;background:rgba(0,0,0,0.8);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3)}.popover-title{padding:9px 15px;line-height:1;background-color:#f5f5f5;border-bottom:1px solid #eee;-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0;margin:0}.popover-content{padding:14px;background-color:#fff;-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box}.popover-content p,.popover-content ul,.popover-content ol{margin-bottom:0} -------------------------------------------------------------------------------- /dist/js/d3-bootstrap-plugins.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | 3 | 4 | var annotate; 5 | 6 | annotate = function(options, create) { 7 | var el, move_tip; 8 | el = d3.select(this); 9 | move_tip = function(selection) { 10 | var center, offsets; 11 | center = [0, 0]; 12 | var body; 13 | body = d3.select('body'); 14 | if (options.placement === "mouse") { 15 | center = d3.mouse(body.node()); 16 | } else { 17 | offsets = this.ownerSVGElement.getBoundingClientRect(); 18 | center[0] = offsets.left; 19 | center[1] = offsets.top; 20 | center[0] += options.position[0]; 21 | center[1] += options.position[1]; 22 | center[0] += window.scrollX; 23 | center[1] += window.scrollY; 24 | } 25 | center[0] += options.displacement[0]; 26 | center[1] += options.displacement[1]; 27 | return selection.style("left", "" + center[0] + "px").style("top", "" + center[1] + "px").style("display", "block"); 28 | }; 29 | el.on("mouseover", function() { 30 | var inner, tip; 31 | tip = create(); 32 | tip.classed("annotation", true).classed(options.gravity, true).classed('fade', true).style("display", "none"); 33 | tip.append("div").attr("class", "arrow"); 34 | inner = function() { 35 | return tip.classed('in', true); 36 | }; 37 | setTimeout(inner, 10); 38 | return tip.style("display", "").call(move_tip.bind(this)); 39 | }); 40 | if (options.mousemove) { 41 | el.on("mousemove", function() { 42 | return d3.select(".annotation").call(move_tip.bind(this)); 43 | }); 44 | } 45 | return el.on("mouseout", function() { 46 | var remover, tip; 47 | tip = d3.selectAll(".annotation").classed('in', false); 48 | remover = function() { 49 | return tip.remove(); 50 | }; 51 | return setTimeout(remover, 150); 52 | }); 53 | }; 54 | 55 | d3.selection.prototype.popover = function(f) { 56 | var body; 57 | body = d3.select('body'); 58 | return this.each(function(d, i) { 59 | var create_popover, options; 60 | options = f.apply(this, arguments); 61 | create_popover = function() { 62 | var inner, tip; 63 | tip = body.append("div").classed("popover", true); 64 | inner = tip.append("div").attr("class", "popover-inner"); 65 | inner.append("h3").text(options.title).attr("class", "popover-title"); 66 | inner.append("div").attr("class", "popover-content").append("p").html(options.content[0][0].outerHTML); 67 | return tip; 68 | }; 69 | return annotate.call(this, options, create_popover); 70 | }); 71 | }; 72 | 73 | d3.selection.prototype.tooltip = function(f) { 74 | var body; 75 | body = d3.select('body'); 76 | return this.each(function(d, i) { 77 | var create_tooltip, options; 78 | options = f.apply(this, arguments); 79 | create_tooltip = function() { 80 | var tip; 81 | tip = body.append("div").classed("tooltip", true); 82 | tip.append("div").html(options.text).attr("class", "tooltip-inner"); 83 | return tip; 84 | }; 85 | return annotate.call(this, options, create_tooltip); 86 | }); 87 | }; 88 | 89 | ;})(); 90 | -------------------------------------------------------------------------------- /dist/js/d3-bootstrap-plugins.min.js: -------------------------------------------------------------------------------- 1 | /*! d3-bootstrap-plugins - v0.0.1 - 2012-07-07 2 | * https://github.com/zmaril/d3-bootstrap-plugins 3 | * Copyright (c) 2012 Zack Maril; Licensed MIT */ 4 | (function(){var a;a=function(a,b){var c,d;return c=d3.select(this),d=function(b){var c,d;return c=[0,0],a.placement==="mouse"?c=d3.mouse(body.node()):(d=this.ownerSVGElement.getBoundingClientRect(),c[0]=d.left,c[1]=d.top,c[0]+=a.position[0],c[1]+=a.position[1],c[0]+=window.scrollX,c[1]+=window.scrollY),c[0]+=a.displacement[0],c[1]+=a.displacement[1],b.style("left",""+c[0]+"px").style("top",""+c[1]+"px").style("display","block")},c.on("mouseover",function(){var c,e;return e=b(),e.classed("annotation",!0).classed(a.gravity,!0).classed("fade",!0).style("display","none"),e.append("div").attr("class","arrow"),c=function(){return e.classed("in",!0)},setTimeout(c,10),e.style("display","").call(d.bind(this))}),a.mousemove&&c.on("mousemove",function(){return d3.select(".annotation").call(d.bind(this))}),c.on("mouseout",function(){var a,b;return b=d3.selectAll(".annotation").classed("in",!1),a=function(){return b.remove()},setTimeout(a,150)})},d3.selection.prototype.popover=function(b){var c;return c=d3.select("body"),this.each(function(d,e){var g,h;return h=b.apply(this,arguments),g=function(){var a,b;return b=c.append("div").classed("popover",!0),a=b.append("div").attr("class","popover-inner"),a.append("h3").text(h.title).attr("class","popover-title"),a.append("div").attr("class","popover-content").append("p").html(h.content[0][0].outerHTML),b},a.call(this,h,g)})},d3.selection.prototype.tooltip=function(b){var c;return c=d3.select("body"),this.each(function(d,e){var g,h;return h=b.apply(this,arguments),g=function(){var a;return a=c.append("div").classed("tooltip",!0),a.append("div").html(h.text).attr("class","tooltip-inner"),a},a.call(this,h,g)})}})(); -------------------------------------------------------------------------------- /examples/popover/index.coffee: -------------------------------------------------------------------------------- 1 | graphic = new Object 2 | 3 | graphic.create = ()-> 4 | width = $(document).width()/2 5 | height = $(document).height()*.85 6 | size = d3.min([width,height]) 7 | graphic.svg = d3.select("#graphic") 8 | .append("svg") 9 | .attr("width",size) 10 | .attr("height",size) 11 | 12 | g = graphic.svg.append("g") 13 | 14 | points = [] 15 | spacing = 30 16 | for i in d3.range(0,height-spacing,spacing) 17 | for j in d3.range(0,width-spacing,spacing) 18 | points.push(x: i, y: j) 19 | 20 | g.selectAll("circle") 21 | .data(points).enter() 22 | .append("circle") 23 | .attr("cx",(d,i)-> d.x) 24 | .attr("cy",(d,i)-> d.y) 25 | .attr("r",(d,i)-> Math.round(Math.random()*spacing/2+1)) 26 | .popover( 27 | (d,i)-> 28 | r = +d3.select(this).attr('r') 29 | 30 | svg = d3.select(document.createElement("svg")) 31 | .attr("height",50) 32 | 33 | g = svg.append("g") 34 | 35 | g.append("rect") 36 | .attr("width",r*10) 37 | .attr("height",10) 38 | 39 | g.append("text") 40 | .text("10 times the radius of the cirlce") 41 | .attr("dy","25") 42 | 43 | { 44 | title: "It's a me, Rectangle" 45 | content: svg 46 | 47 | detection: "shape" 48 | 49 | placement: "fixed" 50 | gravity: "right" 51 | position: [d.x,d.y] 52 | displacement: [r+2,-72] 53 | mousemove: false 54 | } 55 | ) 56 | 57 | $(document).ready(graphic.create) 58 | -------------------------------------------------------------------------------- /examples/popover/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | font: 10px sans-serif; 3 | } 4 | #main { 5 | left: 25%; 6 | position: absolute; 7 | } 8 | #main #text { 9 | padding-bottom: 10px; 10 | } 11 | 12 | body { 13 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 14 | font-size: 13px; 15 | line-height: 18px; 16 | color: #333; 17 | } -------------------------------------------------------------------------------- /examples/popover/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 24 | 25 | 26 | 27 |
28 |
29 |

Popovers.

30 | Hover to display them. 31 |
32 |
33 |
34 | Fork me on GitHub 35 | 36 | 37 | 38 | 39 | 40 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /examples/popover/index.js: -------------------------------------------------------------------------------- 1 | var graphic; 2 | 3 | graphic = new Object; 4 | 5 | graphic.create = function() { 6 | var g, height, i, j, points, size, spacing, width, _i, _j, _len, _len2, _ref, _ref2; 7 | width = $(document).width() / 2; 8 | height = $(document).height() * .85; 9 | size = d3.min([width, height]); 10 | graphic.svg = d3.select("#graphic").append("svg").attr("width", size).attr("height", size); 11 | g = graphic.svg.append("g"); 12 | points = []; 13 | spacing = 30; 14 | _ref = d3.range(0, height - spacing, spacing); 15 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 16 | i = _ref[_i]; 17 | _ref2 = d3.range(0, width - spacing, spacing); 18 | for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { 19 | j = _ref2[_j]; 20 | points.push({ 21 | x: i, 22 | y: j 23 | }); 24 | } 25 | } 26 | return g.selectAll("circle").data(points).enter().append("circle").attr("cx", function(d, i) { 27 | return d.x; 28 | }).attr("cy", function(d, i) { 29 | return d.y; 30 | }).attr("r", function(d, i) { 31 | return Math.round(Math.random() * spacing / 2 + 1); 32 | }).popover(function(d, i) { 33 | var r, svg; 34 | r = +d3.select(this).attr('r'); 35 | svg = d3.select(document.createElement("svg")).attr("height", 50); 36 | g = svg.append("g"); 37 | g.append("rect").attr("width", r * 10).attr("height", 10); 38 | g.append("text").text("10 times the radius of the cirlce").attr("dy", "25"); 39 | return { 40 | title: "It's a me, Rectangle", 41 | content: svg, 42 | detection: "shape", 43 | placement: "fixed", 44 | gravity: "right", 45 | position: [d.x, d.y], 46 | displacement: [r + 2, -72], 47 | mousemove: false 48 | }; 49 | }); 50 | }; 51 | 52 | $(document).ready(graphic.create); 53 | -------------------------------------------------------------------------------- /examples/tooltip/index.coffee: -------------------------------------------------------------------------------- 1 | graphic = new Object 2 | 3 | graphic.create = ()-> 4 | width = $(document).width()/2 5 | height = $(document).height()*.85 6 | size = d3.min([width,height]) 7 | graphic.svg = d3.select("#graphic") 8 | .append("svg") 9 | .attr("width",size) 10 | .attr("height",size) 11 | 12 | g = graphic.svg.append("g") 13 | 14 | points = [] 15 | spacing = 30 16 | for i in d3.range(0,height-spacing,spacing) 17 | for j in d3.range(0,width-spacing,spacing) 18 | points.push(x: i, y: j) 19 | 20 | g.selectAll("circle") 21 | .data(points).enter() 22 | .append("circle") 23 | .attr("cx",(d,i)-> d.x) 24 | .attr("cy",(d,i)-> d.y) 25 | .attr("r",(d,i)-> Math.round(Math.random()*spacing/2+1)) 26 | .tooltip( 27 | (d,i)-> 28 | r = +d3.select(this).attr('r') 29 | 30 | svg = d3.select(document.createElement("svg")) 31 | .attr("height",50) 32 | 33 | g = svg.append("g") 34 | 35 | g.append("rect") 36 | .attr("width",r*10) 37 | .attr("height",10) 38 | 39 | g.append("text") 40 | .text("10 times the radius of the cirlce") 41 | .attr("dy","25") 42 | 43 | { 44 | type: "tooltip" 45 | text: "Tip for circle of radius #{r}" 46 | 47 | detection: "shape" 48 | 49 | placement: "fixed" 50 | gravity: "right" 51 | position: [d.x,d.y] 52 | displacement:[r+2,-20] 53 | mousemove: false 54 | } 55 | ) 56 | 57 | $(document).ready(graphic.create) 58 | -------------------------------------------------------------------------------- /examples/tooltip/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | font: 10px sans-serif; 3 | } 4 | #main { 5 | left: 25%; 6 | position: absolute; 7 | } 8 | #main #text { 9 | padding-bottom: 10px; 10 | } 11 | 12 | body { 13 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 14 | font-size: 13px; 15 | line-height: 18px; 16 | color: #333; 17 | } -------------------------------------------------------------------------------- /examples/tooltip/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 24 | 25 | 26 | 27 |
28 |
29 |

Tooltip.

30 | Hover to display them. 31 |
32 |
33 |
34 | Fork me on GitHub 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /examples/tooltip/index.js: -------------------------------------------------------------------------------- 1 | var graphic; 2 | 3 | graphic = new Object; 4 | 5 | graphic.create = function() { 6 | var g, height, i, j, points, size, spacing, width, _i, _j, _len, _len2, _ref, _ref2; 7 | width = $(document).width() / 2; 8 | height = $(document).height() * .85; 9 | size = d3.min([width, height]); 10 | graphic.svg = d3.select("#graphic").append("svg").attr("width", size).attr("height", size); 11 | g = graphic.svg.append("g"); 12 | points = []; 13 | spacing = 30; 14 | _ref = d3.range(0, height - spacing, spacing); 15 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 16 | i = _ref[_i]; 17 | _ref2 = d3.range(0, width - spacing, spacing); 18 | for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { 19 | j = _ref2[_j]; 20 | points.push({ 21 | x: i, 22 | y: j 23 | }); 24 | } 25 | } 26 | return g.selectAll("circle").data(points).enter().append("circle").attr("cx", function(d, i) { 27 | return d.x; 28 | }).attr("cy", function(d, i) { 29 | return d.y; 30 | }).attr("r", function(d, i) { 31 | return Math.round(Math.random() * spacing / 2 + 1); 32 | }).tooltip(function(d, i) { 33 | var r, svg; 34 | r = +d3.select(this).attr('r'); 35 | svg = d3.select(document.createElement("svg")).attr("height", 50); 36 | g = svg.append("g"); 37 | g.append("rect").attr("width", r * 10).attr("height", 10); 38 | g.append("text").text("10 times the radius of the cirlce").attr("dy", "25"); 39 | return { 40 | type: "tooltip", 41 | text: "Tip for circle of radius " + r, 42 | detection: "shape", 43 | placement: "fixed", 44 | gravity: "right", 45 | position: [d.x, d.y], 46 | displacement: [r+2, -20], 47 | mousemove: false 48 | }; 49 | }); 50 | }; 51 | 52 | $(document).ready(graphic.create); 53 | -------------------------------------------------------------------------------- /grunt.js: -------------------------------------------------------------------------------- 1 | /*global module:false*/ 2 | module.exports = function(grunt) { 3 | //Load in the coffee and css grunt machines! 4 | grunt.loadNpmTasks('grunt-coffee'); // http://github.com/avalade/grunt-coffee 5 | grunt.loadNpmTasks('grunt-css'); // http://github.com/jzaefferer/grunt-css 6 | // Project configuration. 7 | grunt.initConfig({ 8 | pkg: '', 9 | meta: { 10 | banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' + 11 | '<%= grunt.template.today("yyyy-mm-dd") %>\n' + 12 | '<%= pkg.homepage ? "* " + pkg.homepage + "\n" : "" %>' + 13 | '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' + 14 | ' Licensed <%= pkg.license %> */' 15 | }, 16 | coffee:{ 17 | coffee:{ 18 | src: ["src/js/plugins.coffee"], 19 | dest: 'build/js/', 20 | options:{ 21 | bare: true 22 | } 23 | } 24 | }, 25 | concat: { 26 | dist: { 27 | src: ['src/js/begin.js','build/js/plugins.js','src/js/end.js'], 28 | dest: 'dist/js/<%= pkg.name %>.js' 29 | } 30 | }, 31 | min: { 32 | dist: { 33 | src: ['', ''], 34 | dest: 'dist/js/<%= pkg.name %>.min.js' 35 | } 36 | }, 37 | cssmin:{ 38 | dist:{ 39 | src:["src/css/*.css"], 40 | dest:"dist/css/<%= pkg.name %>.min.css" 41 | } 42 | }, 43 | watch: { 44 | files: ["src/*/*"], 45 | tasks: 'coffee concat min cssmin' 46 | } 47 | } 48 | ); 49 | 50 | // Default task. 51 | grunt.registerTask('default', 'watch'); 52 | grunt.registerTask('build','coffee concat min cssmin'); 53 | }; 54 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "d3-bootstrap-plugins", 3 | "version": "0.0.1", 4 | "author": "Zack Maril ", 5 | "homepage": "https://github.com/zmaril/d3-bootstrap-plugins", 6 | "description": "js/css duo for bootstrap style tooltips and popovers in d3.", 7 | "author":{ 8 | "name": "Zack Maril", 9 | "email": "zack@zacharymaril.com" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/zmaril/d3-bootstrap-plugins" 14 | }, 15 | "keywords": [ 16 | "d3", 17 | "bootstrap", 18 | "tooltip", 19 | "popover" 20 | ], 21 | "dependencies" : { 22 | "d3" : "2.x.x" 23 | }, 24 | "noAnalyze": true, 25 | "devDependencies": { 26 | "grunt" : "0.3.11", 27 | "grunt-css" : "0.2.1", 28 | "grunt-coffee" : "0.0.2" 29 | }, 30 | "bundleDependencies": [ 31 | ], 32 | "license": "MIT" 33 | } 34 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # d3.js meets bootstrap 2 | js/css duo for bootstrap style [tooltips](http://bl.ocks.org/2981335) and [popovers](http://bl.ocks.org/3012212). 3 | 4 | ## How does it work? 5 | The tooltip function attaches event listeners to 6 | selections that go and display bootstrap tooltips or popovers when 7 | the specified events are detected. 8 | 9 | An example of how to use it: 10 | ```javascript 11 | selection.tooltip(function(d,i){ 12 | return { 13 | //The text within the tooltip 14 | text: d.title 15 | //Where 16 | placement: "fixed" 17 | // Base positioning. Not used when placement is "mouse" 18 | position: [d.x,d.y] 19 | //How far the tooltip is shifted from the base 20 | displacement: [0,20] //Shifting parts of the graph over. 21 | //If "mouse"" is the base poistion, then mousemove true allows 22 | //the tooltip to move with the mouse 23 | mousemove: false 24 | }; 25 | }); 26 | 27 | selection.popover(function(d,i){ 28 | //TODO: add in a svg element here based on data 29 | return { 30 | // The title that will be displayed on the popover 31 | title: "A title" 32 | //A d3 svg element 33 | content: svg 34 | placement: "fixed" 35 | gravity: "right" 36 | position: [d.x,d.y] 37 | displacement: [0,20] 38 | mousemove: false 39 | }; 40 | }); 41 | 42 | ``` 43 | 44 | Viola! Tooltips! Popovers! 45 | 46 | ## Current TODOS 47 | * Get easy voronoi detection working without breaking 48 | everything. Checkout the 49 | [voronoi](https://github.com/zmaril/d3-bootstrap-plugins/tree/voronoi) 50 | branch for current work on this. 51 | * Bring over the less files from bootstrap directly instead of 52 | replying on their css. 53 | * Write some tests. 54 | * Use it in production. 55 | 56 | ## Contributing 57 | 58 | Local development uses grunt (`npm install grunt`) 59 | 60 | `grunt watch`- automatically compile the files as you change the src directory 61 | 62 | `grunt build`- goes through concatenation and minification of js and 63 | css. 64 | 65 | If you are adding new features, please create an example that 66 | demonstrates that feature specifically. 67 | 68 | ## License 69 | 70 | ### Major components: 71 | * d3.js: [License](https://github.com/mbostock/d3/blob/master/LICENSE) 72 | * Bootstrap: [License](https://github.com/twitter/bootstrap/blob/master/LICENSE) 73 | 74 | ### Everything else: 75 | 76 | MIT License. 77 | -------------------------------------------------------------------------------- /src/css/plugins.css: -------------------------------------------------------------------------------- 1 | /* Taken from bootstrap: https://github.com/twitter/bootstrap/blob/master/less/tooltip.less */ 2 | .fade { 3 | opacity: 0; 4 | -webkit-transition: opacity 0.15s linear; 5 | -moz-transition: opacity 0.15s linear; 6 | -ms-transition: opacity 0.15s linear; 7 | -o-transition: opacity 0.15s linear; 8 | transition: opacity 0.15s linear; 9 | } 10 | 11 | .fade.in { 12 | opacity: 1; 13 | } 14 | 15 | .tooltip { 16 | position: absolute; 17 | z-index: 1020; 18 | display: block; 19 | padding: 5px; 20 | font-size: 11px; 21 | opacity: 0; 22 | filter: alpha(opacity=0); 23 | visibility: visible; 24 | } 25 | 26 | .tooltip.in { 27 | opacity: 0.8; 28 | filter: alpha(opacity=80); 29 | } 30 | 31 | .tooltip.top { 32 | margin-top: -2px; 33 | } 34 | 35 | .tooltip.right { 36 | margin-left: 2px; 37 | } 38 | 39 | .tooltip.bottom { 40 | margin-top: 2px; 41 | } 42 | 43 | .tooltip.left { 44 | margin-left: -2px; 45 | } 46 | 47 | .tooltip.top .arrow { 48 | bottom: 0; 49 | left: 50%; 50 | margin-left: -5px; 51 | border-top: 5px solid #000000; 52 | border-right: 5px solid transparent; 53 | border-left: 5px solid transparent; 54 | } 55 | 56 | .tooltip.left .arrow { 57 | top: 50%; 58 | right: 0; 59 | margin-top: -5px; 60 | border-top: 5px solid transparent; 61 | border-bottom: 5px solid transparent; 62 | border-left: 5px solid #000000; 63 | } 64 | 65 | .tooltip.bottom .arrow { 66 | top: 0; 67 | left: 50%; 68 | margin-left: -5px; 69 | border-right: 5px solid transparent; 70 | border-bottom: 5px solid #000000; 71 | border-left: 5px solid transparent; 72 | } 73 | 74 | .tooltip.right .arrow { 75 | top: 50%; 76 | left: 0; 77 | margin-top: -5px; 78 | border-top: 5px solid transparent; 79 | border-right: 5px solid #000000; 80 | border-bottom: 5px solid transparent; 81 | } 82 | 83 | .tooltip-inner { 84 | max-width: 200px; 85 | padding: 3px 8px; 86 | color: #ffffff; 87 | text-align: center; 88 | text-decoration: none; 89 | background-color: #000000; 90 | -webkit-border-radius: 4px; 91 | -moz-border-radius: 4px; 92 | border-radius: 4px; 93 | } 94 | 95 | .arrow { 96 | position: absolute; 97 | width: 0; 98 | height: 0; 99 | } 100 | 101 | .popover { 102 | position: absolute; 103 | top: 0; 104 | left: 0; 105 | z-index: 1010; 106 | display: none; 107 | padding: 5px; 108 | } 109 | 110 | .popover.top { 111 | margin-top: -5px; 112 | } 113 | 114 | .popover.right { 115 | margin-left: 5px; 116 | } 117 | 118 | .popover.bottom { 119 | margin-top: 5px; 120 | } 121 | 122 | .popover.left { 123 | margin-left: -5px; 124 | } 125 | 126 | .popover.top .arrow { 127 | bottom: 0; 128 | left: 50%; 129 | margin-left: -5px; 130 | border-top: 5px solid #000000; 131 | border-right: 5px solid transparent; 132 | border-left: 5px solid transparent; 133 | } 134 | 135 | .popover.right .arrow { 136 | top: 50%; 137 | left: 0; 138 | margin-top: -5px; 139 | border-top: 5px solid transparent; 140 | border-right: 5px solid #000000; 141 | border-bottom: 5px solid transparent; 142 | } 143 | 144 | .popover.bottom .arrow { 145 | top: 0; 146 | left: 50%; 147 | margin-left: -5px; 148 | border-right: 5px solid transparent; 149 | border-bottom: 5px solid #000000; 150 | border-left: 5px solid transparent; 151 | } 152 | 153 | .popover.left .arrow { 154 | top: 50%; 155 | right: 0; 156 | margin-top: -5px; 157 | border-top: 5px solid transparent; 158 | border-bottom: 5px solid transparent; 159 | border-left: 5px solid #000000; 160 | } 161 | 162 | .popover-inner { 163 | width: 280px; 164 | padding: 3px; 165 | overflow: hidden; 166 | background: #000000; 167 | background: rgba(0, 0, 0, 0.8); 168 | -webkit-border-radius: 6px; 169 | -moz-border-radius: 6px; 170 | border-radius: 6px; 171 | -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); 172 | -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); 173 | box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); 174 | } 175 | 176 | .popover-title { 177 | padding: 9px 15px; 178 | line-height: 1; 179 | background-color: #f5f5f5; 180 | border-bottom: 1px solid #eee; 181 | -webkit-border-radius: 3px 3px 0 0; 182 | -moz-border-radius: 3px 3px 0 0; 183 | border-radius: 3px 3px 0 0; 184 | margin: 0; 185 | } 186 | 187 | .popover-content { 188 | padding: 14px; 189 | background-color: #ffffff; 190 | -webkit-border-radius: 0 0 3px 3px; 191 | -moz-border-radius: 0 0 3px 3px; 192 | border-radius: 0 0 3px 3px; 193 | -webkit-background-clip: padding-box; 194 | -moz-background-clip: padding-box; 195 | background-clip: padding-box; 196 | } 197 | 198 | .popover-content p, 199 | .popover-content ul, 200 | .popover-content ol { 201 | margin-bottom: 0; 202 | } -------------------------------------------------------------------------------- /src/js/begin.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | 3 | -------------------------------------------------------------------------------- /src/js/end.js: -------------------------------------------------------------------------------- 1 | ;})(); -------------------------------------------------------------------------------- /src/js/plugins.coffee: -------------------------------------------------------------------------------- 1 | annotate = (options,create)-> 2 | el = d3.select(this) 3 | 4 | move_tip = (selection)-> 5 | center = [0,0] 6 | 7 | if options.placement is "mouse" 8 | center = d3.mouse(d3.select('body').node()) 9 | else 10 | offsets = @ownerSVGElement.getBoundingClientRect() 11 | center[0] = offsets.left 12 | center[1] = offsets.top 13 | 14 | center[0] += options.position[0] 15 | center[1] += options.position[1] 16 | 17 | center[0]+= window.pageXOffset 18 | center[1]+= window.pageYOffset 19 | 20 | center[0] += options.displacement[0] 21 | center[1] += options.displacement[1] 22 | 23 | selection 24 | .style("left","#{center[0]}px") 25 | .style("top","#{center[1]}px") 26 | .style("display","block") 27 | 28 | el.on("mouseover",()-> 29 | tip = create() 30 | 31 | tip.classed("annotation", true) 32 | .classed(options.gravity, true) 33 | .classed('fade', true) 34 | .style("display","none") 35 | 36 | tip.append("div") 37 | .attr("class","arrow") 38 | 39 | inner = ()-> tip.classed('in', true) 40 | 41 | setTimeout(inner,10) 42 | 43 | tip.style("display","").call(move_tip.bind(this)) 44 | ) 45 | 46 | if options.mousemove 47 | el.on("mousemove",()-> 48 | d3.select(".annotation").call(move_tip.bind(this)) 49 | ) 50 | 51 | el.on("mouseout",()-> 52 | tip = d3.selectAll(".annotation").classed('in', false) 53 | remover = ()-> tip.remove() 54 | setTimeout(remover,150) 55 | ) 56 | 57 | d3.selection.prototype.popover = (f)-> 58 | body = d3.select('body') 59 | 60 | this.each((d,i)-> 61 | options = f.apply(this,arguments) 62 | 63 | create_popover = ()-> 64 | 65 | tip = body.append("div") 66 | .classed("popover", true) 67 | 68 | inner = tip.append("div") 69 | .attr("class","popover-inner") 70 | 71 | inner.append("h3") 72 | .text(options.title) 73 | .attr("class","popover-title") 74 | 75 | inner.append("div") 76 | .attr("class","popover-content") 77 | .append("p") 78 | .html(options.content[0][0].outerHTML) 79 | 80 | return tip 81 | 82 | annotate.call(this,options,create_popover) 83 | ) 84 | 85 | 86 | d3.selection.prototype.tooltip = (f)-> 87 | body = d3.select('body') 88 | 89 | this.each (d,i)-> 90 | 91 | options = f.apply(this,arguments) 92 | 93 | create_tooltip = ()-> 94 | tip = body.append("div") 95 | .classed("tooltip", true) 96 | 97 | tip.append("div") 98 | .html(options.text) 99 | .attr("class","tooltip-inner") 100 | 101 | return tip 102 | 103 | annotate.call(this,options,create_tooltip) 104 | --------------------------------------------------------------------------------