├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── examples ├── css │ ├── brush_chart.gif │ ├── details_chart.gif │ ├── filter_chart.gif │ ├── find_chart.gif │ ├── frame_chart.gif │ ├── move2_chart.gif │ ├── move_chart.gif │ ├── parallel_chart.gif │ ├── radar_chart.gif │ ├── smart_tooltip_chart.gif │ └── style.css ├── index.html ├── js │ ├── brushing.js │ ├── chart-component.js │ ├── parallel-coordinates.js │ ├── radar-controller.js │ ├── radar.js │ ├── scatterplot-controller.js │ ├── scatterplot.js │ └── smart-tooltips.js ├── json │ ├── radar-dataset.json │ ├── scatterplot-dataset.json │ ├── treemap-data.json │ └── us-states.json └── server.js ├── lerna.json ├── package.json ├── packages ├── .gitkeep ├── charts-colors │ ├── README.md │ ├── package.json │ └── src │ │ ├── index.js │ │ └── palettes │ │ ├── colorBlindSafe.js │ │ ├── index.js │ │ └── qualitative.js ├── charts-design │ ├── IBM-Design-Charts.sketch │ ├── IBM-Design-Charts_Base.sketch │ ├── IBM-Design-Charts_Line.sketch │ ├── IBM-Design-Charts_Scatterplot.sketch │ └── IBM-Design-Charts_Stacked-Bar.sketch └── eslint-config-charts │ ├── LICENSE.md │ ├── README.md │ ├── index.js │ ├── package.json │ ├── plugins │ ├── babel.js │ └── import.js │ └── rules │ ├── base.js │ ├── best-practices.js │ ├── es6.js │ ├── node.js │ ├── strict.js │ ├── style.js │ └── variables.js └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | insert_final_newline = true 5 | charset = utf-8 6 | trim_trailing_whitespace = true 7 | end_of_line = lf 8 | 9 | [*.{js,json}] 10 | indent_style = space 11 | indent_size = 2 12 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | packages/*/node_modules 2 | packages/*/lib 3 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./packages/eslint-config-charts/index.js" 3 | } 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **Do you want to request either a *feature or user story*, or report a *bug*?** 2 | 3 | **What is the current behavior?** 4 | 5 | **If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem via https://jsfiddle.net or similar (template: https://jsfiddle.net/reactjs/69z2wepo/).** 6 | 7 | **What is the expected behavior?** 8 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Closes # 2 | 3 | {{short description}} 4 | 5 | #### Changelog 6 | 7 | **New** 8 | 9 | - {{new thing}} 10 | 11 | **Changed** 12 | 13 | - {{change thing}} 14 | 15 | **Removed** 16 | 17 | - {{removed thing}} 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .env 3 | learn-debug.log 4 | node_modules 5 | npm-debug.log 6 | lib/ 7 | _gh-pages 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - '6' 5 | - '7' 6 | 7 | cache: 8 | yarn: true 9 | directories: 10 | - node_modules 11 | 12 | before_script: 13 | - yarn run bootstrap 14 | 15 | script: 16 | - yarn test 17 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Workflow 4 | 5 | After cloning `ibm-design-charts`, run `yarn` to fetch its dependencies. 6 | 7 | Then, you can run several commands: 8 | 9 | - `yarn run bootstrap` to bootstrap the project, link dependencies, and do the initial build step for each package 10 | - `yarn run lint` checks the code style. 11 | - `yarn test -- --watch` runs our test suite and puts your terminal in `watch` mode 12 | - `yarn test ` runs tests with matching filenames. 13 | 14 | ## Sending a Pull Request 15 | 16 | - Create your own branch from `master` (`git checkout -b branch_name`) 17 | - Add your changes with the corresponding tests/documentation 18 | - Ensure your commit messages describe the changes made. (`git commit -m `) 19 | - Ensure the test suite passes (`yarn test`) 20 | - Make sure your code lints (`yarn run lint`) 21 | - Push your local branch to it's remote location.(`git push origin branch_name`) 22 | - Create your Pull Request, filling out all the details of the Pull Request template 23 | - Make sure one or more team members give it a 🚀 (or a 👍) to sign-off on it 24 | 25 | ## Lerna 26 | 27 | For full documentation, make sure to check out [Lerna](https://lernajs.io/). 28 | 29 | ### Process 30 | 31 | We're currently publishing packages in Lerna's independent mode, so each package is able to have it's own version instead of bumping each one at the same time. You can see which packages have been updated since the last release by running: 32 | 33 | ```bash 34 | yarn run lerna -- updated 35 | ``` 36 | 37 | You can also view all public packages by running: 38 | 39 | ```bash 40 | yarn run lerna -- ls 41 | ``` 42 | 43 | ### Publishing 44 | 45 | To publish all applicable packages, and appropriately tag/release in `git`, you can run the following: 46 | 47 | ```bash 48 | yarn run lerna -- publish 49 | ``` 50 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2017 IBM 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `ibm-design-charts` [![Build Status](https://travis-ci.org/IBM-Design/charts.svg?branch=master)](https://travis-ci.org/IBM-Design/charts) 2 | 3 | ## Up & Running 4 | 5 | - Clone or fork this repository 6 | - Make sure you have the following technologies installed on your computer: 7 | - [node.js](https://nodejs.org/) - Requires node.js 6.0.0 or greater 8 | - [Watchman](https://facebook.github.io/watchman/) - A file watching service 9 | - [Yarn Package Manager](https://yarnpkg.com/en/docs/install) 10 | - run `yarn` to install dependencies 11 | - run `yarn run bootstrap` to bootstrap the project, link dependencies, and do the initial build step for each package 12 | - run `yarn test` to run the tests that will run during PRs 13 | 14 | ## Design 15 | 16 | You can find all Sketch files that define the visual specs of a chart in [charts-design](./packages/charts-design/). 17 | 18 | ## Structure 19 | 20 | The project currently leverages [Lerna](https://lernajs.io/) in order to manage multiple packages contained within this project. To learn more about this, please view our [`CONTRIBUTING.md`](./CONTRIBUTING.md). 21 | 22 | ## Examples 23 | 24 | At the moment, we only have `./examples` to show what our desired output is. This page uses inaccessible work and is only examples instead of reusable pieces. To run: 25 | 26 | 1. `git clone https://github.com/IBM-Design/charts.git` 27 | 2. `cd charts` 28 | 3. `yarn install` 29 | 4. `yarn run examples` 30 | -------------------------------------------------------------------------------- /examples/css/brush_chart.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM-Design/charts/39da9c3784f6c4a7cd23df1327b6014334dc2e82/examples/css/brush_chart.gif -------------------------------------------------------------------------------- /examples/css/details_chart.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM-Design/charts/39da9c3784f6c4a7cd23df1327b6014334dc2e82/examples/css/details_chart.gif -------------------------------------------------------------------------------- /examples/css/filter_chart.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM-Design/charts/39da9c3784f6c4a7cd23df1327b6014334dc2e82/examples/css/filter_chart.gif -------------------------------------------------------------------------------- /examples/css/find_chart.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM-Design/charts/39da9c3784f6c4a7cd23df1327b6014334dc2e82/examples/css/find_chart.gif -------------------------------------------------------------------------------- /examples/css/frame_chart.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM-Design/charts/39da9c3784f6c4a7cd23df1327b6014334dc2e82/examples/css/frame_chart.gif -------------------------------------------------------------------------------- /examples/css/move2_chart.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM-Design/charts/39da9c3784f6c4a7cd23df1327b6014334dc2e82/examples/css/move2_chart.gif -------------------------------------------------------------------------------- /examples/css/move_chart.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM-Design/charts/39da9c3784f6c4a7cd23df1327b6014334dc2e82/examples/css/move_chart.gif -------------------------------------------------------------------------------- /examples/css/parallel_chart.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM-Design/charts/39da9c3784f6c4a7cd23df1327b6014334dc2e82/examples/css/parallel_chart.gif -------------------------------------------------------------------------------- /examples/css/radar_chart.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM-Design/charts/39da9c3784f6c4a7cd23df1327b6014334dc2e82/examples/css/radar_chart.gif -------------------------------------------------------------------------------- /examples/css/smart_tooltip_chart.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM-Design/charts/39da9c3784f6c4a7cd23df1327b6014334dc2e82/examples/css/smart_tooltip_chart.gif -------------------------------------------------------------------------------- /examples/css/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | font-family: "Helvetica Neue", Helvetica; 3 | box-sizing: border-box; 4 | } 5 | 6 | body { 7 | background-color: #fff; 8 | } 9 | 10 | .container { 11 | width: 800px; 12 | margin: 0 auto; 13 | text-align: center; 14 | background-color: white; 15 | position: relative; 16 | padding-top: 50px; 17 | } 18 | 19 | .title { 20 | font-size: 16px; 21 | color: #5F92CD; 22 | margin: 10px auto; 23 | text-align: left; 24 | width: 800px; 25 | } 26 | 27 | .menu { 28 | margin: 0 auto; 29 | border-bottom: 1px solid #F6F6F6; 30 | height: 40px; 31 | position: relative; 32 | padding: 0; 33 | } 34 | 35 | .label { 36 | font-size: 14px; 37 | color: #808080; 38 | margin: 10px 0; 39 | } 40 | 41 | .scatterplot .slider { 42 | background-color: #464646; 43 | height: 42px; 44 | width: 270px; 45 | margin: 10px auto; 46 | opacity: 0; 47 | } 48 | 49 | .scatterplot .search-container { 50 | border: 0; 51 | margin: 25px auto; 52 | height: 20px; 53 | width: 325px; 54 | display: flex; 55 | opacity: 0; 56 | position: relative; 57 | } 58 | 59 | .scatterplot .erase { 60 | justify-content: center; 61 | align-items: center; 62 | height: 30px; 63 | } 64 | .scatterplot .erase:focus a { 65 | outline: 0; 66 | } 67 | .scatterplot .erase:hover .erase-fill-element, 68 | .scatterplot .erase:focus .erase-fill-element { 69 | fill: #5a3ec8; 70 | } 71 | 72 | .scatterplot .erase svg { 73 | cursor: pointer; 74 | } 75 | 76 | .scatterplot .menuEl, .scatterplot .menuEl2 { 77 | display: inline-block; 78 | width: 170px; 79 | font-size: 14px; 80 | text-align: center; 81 | font-weight: bold; 82 | cursor: pointer; 83 | } 84 | 85 | .scatterplot .search { 86 | border: 0; 87 | width: 290px; 88 | height: 30px; 89 | font-size: 14px; 90 | border-bottom: 1px solid #c3c3c3; 91 | display: inline-block; 92 | padding: 5px; 93 | } 94 | 95 | .scatterplot .search:focus { 96 | outline: none; 97 | } 98 | 99 | .scatterplot .focus-line { 100 | width: 290px; 101 | height: 1px; 102 | top: 29px; 103 | left: 0px; 104 | position: absolute; 105 | } 106 | 107 | .scatterplot .focus-line.active { 108 | border-bottom: 2px solid #5a3ec8; 109 | } 110 | 111 | .scatterplot .label { 112 | text-align: center; 113 | margin: 5px 0; 114 | } 115 | 116 | .scatterplot .labelEl, .labelEl2 { 117 | opacity: 0; 118 | height: 0; 119 | } 120 | 121 | .scatterplot .visible { 122 | height: 20px; 123 | opacity: 1; 124 | } 125 | 126 | .scatterplot .tooltip-text { 127 | fill: white; 128 | text-anchor: middle; 129 | font-size: 13px; 130 | } 131 | 132 | .scatterplot .tick line { 133 | stroke: rgb(100, 220, 205); 134 | opacity: 0.2; 135 | } 136 | 137 | .scatterplot .tick text { 138 | font-size: 12px; 139 | fill: #5f92cd; 140 | } 141 | 142 | .scatterplot .axis--y .tick text { 143 | text-anchor: end; 144 | } 145 | 146 | 147 | .scatterplot .axis--x .tick:nth-of-type(1) text { 148 | text-anchor: start; 149 | } 150 | 151 | .scatterplot .axis--x .tick:last-of-type text { 152 | text-anchor: end; 153 | } 154 | 155 | .scatterplot path.domain { 156 | fill: none; 157 | stroke: none; 158 | stroke-width: 1px; 159 | } 160 | 161 | .scatterplot text.pasttext { 162 | transition: opacity .6s; 163 | } 164 | 165 | .scatterplot g.data { 166 | transition: opacity .3s; 167 | } 168 | 169 | .scatterplot .xHoverline,.scatterplot .yHoverline,.scatterplot .xHoverlineText,.scatterplot .yHoverlineText { 170 | transition: opacity .6s; 171 | } 172 | 173 | .scatterplot .xHoverlineText, .scatterplot .yHoverlineText { 174 | font-weight: bold; 175 | } 176 | 177 | .scatterplot circle.turnover { 178 | fill: none; 179 | stroke: #2b357f; 180 | stroke-width: 0.4; 181 | opacity: 0.0; 182 | stroke-dasharray: 2 3; 183 | } 184 | 185 | .scatterplot circle.turnover-background { 186 | opacity: 0.85; 187 | } 188 | 189 | .scatterplot circle.turnover-background.IE { 190 | fill: #878787; 191 | opacity: 0.08; 192 | } 193 | 194 | .scatterplot circle.patent { 195 | fill: #fff; 196 | stroke: none; 197 | stroke-width: 0; 198 | pointer-events: none; 199 | } 200 | 201 | .scatterplot circle.top { 202 | fill: #64DCCD; 203 | stroke: none; 204 | stroke-width: 1; 205 | display: block; 206 | } 207 | 208 | .scatterplot line.class-line { 209 | stroke-width: 10; 210 | pointer-events: none; 211 | /*stroke:none !important;*/ 212 | } 213 | 214 | .scatterplot line.class-line.data_01 { 215 | stroke: #000000; 216 | stroke-width: 1; 217 | } 218 | 219 | .scatterplot line.class-line.data_02 { 220 | stroke: #000000; 221 | stroke-width: 1; 222 | } 223 | 224 | .scatterplot line.class-line.data_03 { 225 | stroke: #000000; 226 | stroke-width: 1; 227 | } 228 | 229 | .scatterplot line.class-line.data_04 { 230 | stroke: #000000; 231 | stroke-width: 1; 232 | } 233 | 234 | .scatterplot line.class-line.data_05 { 235 | stroke: #006d5d; 236 | } 237 | 238 | .scatterplot line.class-line.data_06 { 239 | stroke: #008571; 240 | } 241 | 242 | .scatterplot line.class-line.data_07 { 243 | stroke: #00b4a0; 244 | } 245 | 246 | .scatterplot line.class-line.data_08 { 247 | stroke: #41d6c3; 248 | } 249 | 250 | .scatterplot line.class-line.data_09 { 251 | stroke: #6eedd8; 252 | } 253 | 254 | .scatterplot line.class-line.data_10 { 255 | stroke: #a7fae6; 256 | } 257 | 258 | .scatterplot line.class-line.data_11 { 259 | stroke: #fde876; 260 | } 261 | 262 | .scatterplot line.class-line.data_12 { 263 | stroke: #fdd600; 264 | } 265 | 266 | .scatterplot line.class-line.data_13 { 267 | stroke: #efc100; 268 | } 269 | 270 | .scatterplot line.class-line.data_14 { 271 | stroke: #000000; 272 | stroke-width: 1; 273 | } 274 | 275 | .scatterplot line.class-line.data_15 { 276 | stroke: #000000; 277 | stroke-width: 1; 278 | } 279 | 280 | .scatterplot line.class-line.data_16 { 281 | stroke: #000000; 282 | stroke-width: 1; 283 | } 284 | 285 | .scatterplot line.class-line.data_17 { 286 | stroke: #000000; 287 | stroke-width: 1; 288 | } 289 | 290 | .scatterplot line.class-line.data_18 { 291 | stroke: #000000; 292 | stroke-width: 1; 293 | } 294 | 295 | .scatterplot text.company-name { 296 | fill: #3a4f5a; 297 | font-size: 14px; 298 | font-family: "Proxima Nova"; 299 | font-weight: normal; 300 | display: none; 301 | } 302 | 303 | .scatterplot .move-gif { 304 | width: 100%; 305 | height: 60vw; 306 | background: url('./move_chart.gif') no-repeat center center; 307 | background-size: contain; 308 | } 309 | 310 | .scatterplot .move2-gif { 311 | width: 100%; 312 | height: 60vw; 313 | background: url('./move2_chart.gif') no-repeat center center; 314 | background-size: contain; 315 | } 316 | 317 | .scatterplot .frame-gif { 318 | width: 100%; 319 | height: 60vw; 320 | background: url('./frame_chart.gif') no-repeat center center; 321 | background-size: contain; 322 | } 323 | 324 | .scatterplot .find-gif { 325 | width: 100%; 326 | height: 60vw; 327 | background: url('./find_chart.gif') no-repeat center center; 328 | background-size: contain; 329 | } 330 | 331 | .scatterplot .filter-gif { 332 | width: 100%; 333 | height: 60vw; 334 | background: url('./filter_chart.gif') no-repeat center center; 335 | background-size: contain; 336 | } 337 | 338 | .scatterplot .details-gif { 339 | width: 100%; 340 | height: 60vw; 341 | background: url('./details_chart.gif') no-repeat center center; 342 | background-size: contain; 343 | } 344 | 345 | 346 | /* BRUSH CHART */ 347 | 348 | .hoverable0 { 349 | fill: #F6F6F6; 350 | } 351 | 352 | .hoverable1 { 353 | fill: none; 354 | stroke: #AE8DC2; 355 | stroke-miterlimit: 10; 356 | } 357 | 358 | .hoverable2 { 359 | fill: #AE8DC2; 360 | } 361 | 362 | .hoverable3 { 363 | fill: #AE8DC2; 364 | stroke: #F6F6F6; 365 | stroke-miterlimit: 1; 366 | } 367 | 368 | .hover1, .hover2, .hover3, .hover4, .hover5, .hover6 { 369 | transition: .3s all; 370 | } 371 | 372 | .brush { 373 | padding: 50px 0; 374 | margin: 0; 375 | display: flex; 376 | align-items: center; 377 | flex-direction: column; 378 | } 379 | 380 | .brush .brush-gif { 381 | width: 100%; 382 | background: url('./brush_chart.gif') no-repeat center center; 383 | background-size: contain; 384 | height: 45vw; 385 | margin-bottom: 30px; 386 | } 387 | 388 | 389 | /* RADAR CSS */ 390 | 391 | .radar { 392 | margin: 0; 393 | font-size: 14px; 394 | display: flex; 395 | align-items: center; 396 | flex-direction: column; 397 | } 398 | 399 | .radar #chart { 400 | position: absolute; 401 | top: 50px; 402 | left: 100px; 403 | } 404 | 405 | .radar .radarChart { 406 | position: relative; 407 | width: 800px; 408 | display: flex; 409 | align-items: center; 410 | justify-content: center; 411 | } 412 | 413 | .radar .barChart { 414 | width: 800px; 415 | text-align: center; 416 | display: flex; 417 | flex-direction: column; 418 | justify-content: center; 419 | align-items: center; 420 | height: 100px; 421 | } 422 | 423 | .radar .legend { 424 | position: absolute; 425 | right: 50px; 426 | top: 100px; 427 | z-index: 2; 428 | } 429 | 430 | .radar .radar-gif { 431 | width: 100%; 432 | background: url('./radar_chart.gif') no-repeat center center; 433 | background-size: contain; 434 | height: 85vw; 435 | margin-top: 30px; 436 | margin-bottom: 30px; 437 | } 438 | 439 | 440 | /* PARALLEL COORDINATES */ 441 | 442 | .parallel-coordinates { 443 | padding-top: 50px; 444 | margin: 0; 445 | display: flex; 446 | align-items: center; 447 | flex-direction: column; 448 | } 449 | 450 | .parallel-coordinates-container { 451 | margin-top: 25px; 452 | } 453 | 454 | .parallel-coordinates .background path { 455 | fill: none; 456 | stroke: #fff; 457 | shape-rendering: crispEdges; 458 | } 459 | 460 | .parallel-coordinates .foreground path { 461 | fill: none; 462 | stroke: #BF6292; 463 | stroke-width: 2px; 464 | } 465 | 466 | .parallel-coordinates .axis line, .parallel-coordinates .axis path { 467 | fill: none; 468 | stroke: #5F92CD; 469 | shape-rendering: crispEdges; 470 | } 471 | 472 | .parallel-coordinates .axis text { 473 | fill: #5F92CD; 474 | /*text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff;*/ 475 | cursor: move; 476 | font-size: 12px; 477 | } 478 | 479 | .parallel-coordinates .axis .axis-title { 480 | font-size: 14px; 481 | fill: #3C6492 !important; 482 | pointer-events: none; 483 | } 484 | 485 | .parallel-coordinates .axis-title-background { 486 | fill: #FDE390; 487 | } 488 | 489 | .parallel-coordinates .axis-title-background:hover { 490 | fill: #f9d35b 491 | } 492 | 493 | .parallel-coordinates .parallel-coordinates-gif { 494 | width: 100%; 495 | background: url('./parallel_chart.gif') no-repeat center center; 496 | background-size: contain; 497 | height: 60vw; 498 | margin-top: 30px; 499 | margin-bottom: 30px; 500 | } 501 | 502 | /* SMART TOOLTIPS */ 503 | 504 | .smart-tooltips { 505 | padding-top: 50px; 506 | margin: 0; 507 | display: flex; 508 | align-items: center; 509 | flex-direction: column; 510 | } 511 | 512 | .smart-tooltips-container { 513 | width: 800px; 514 | height: 470px; 515 | background-color: white; 516 | } 517 | 518 | .smart-tooltips #Layer_1 { 519 | width: 24px; 520 | pointer-events: none; 521 | } 522 | 523 | .smart-tooltips .main-container { 524 | width: 800px; 525 | display: flex; 526 | } 527 | 528 | .smart-tooltips .hoverable-menu { 529 | position: absolute; 530 | width: 400px; 531 | left: 200px; 532 | top: 180px; 533 | display: flex; 534 | flex-direction: column; 535 | justify-content: center; 536 | align-items: center; 537 | opacity: 1; 538 | transition: 1s all; 539 | z-index: 2; 540 | } 541 | 542 | .smart-tooltips .hoverable-menu-el-container { 543 | display: none; 544 | justify-content: space-between; 545 | } 546 | 547 | .smart-tooltips .rect-title { 548 | font-size: 14px; 549 | color: #B2B2B2; 550 | } 551 | 552 | .smart-tooltips .rect-subtitle { 553 | font-size: 14px; 554 | } 555 | 556 | .smart-tooltips .hoverable-menu-rect-el { 557 | width: 80px; 558 | height: 30px; 559 | margin: 20px; 560 | background-color: #464646; 561 | padding: 7px; 562 | color: white; 563 | opacity: 0; 564 | } 565 | 566 | .smart-tooltips .hoverable-menu-el { 567 | width: 44px; 568 | height: 44px; 569 | border-radius: 44px; 570 | box-shadow: 1px 1px 10px #b2b2b2; 571 | margin: 5px; 572 | display: inline-block; 573 | display: flex; 574 | justify-content: center; 575 | align-items: center; 576 | } 577 | 578 | .smart-tooltips .smart-tooltips-placeholder { 579 | font-size: 14px; 580 | color: #808080; 581 | text-align: left; 582 | } 583 | 584 | .smart-tooltips .smart-tooltips-explanation { 585 | font-size: 14px; 586 | text-align: center; 587 | position: absolute; 588 | color: #808080; 589 | width: 480px; 590 | left: 50%; 591 | margin-left: -240px; 592 | top: 100px; 593 | } 594 | 595 | .smart-tooltips .smart-tooltips-title { 596 | font-weight: bold; 597 | font-size: 16px; 598 | text-align: left; 599 | } 600 | 601 | .smart-tooltips .viz-title { 602 | font-size: 16px; 603 | padding: 25px 0; 604 | font-weight: 500; 605 | color: #5f92cd; 606 | } 607 | 608 | .smart-tooltips .clicked { 609 | display: none; 610 | } 611 | 612 | .smart-tooltips .smart-tooltips-gif { 613 | width: 100%; 614 | background: url('./smart_tooltip_chart.gif') no-repeat center center; 615 | background-size: contain; 616 | height: 484px; 617 | } 618 | /* Tabs */ 619 | 620 | /* specify the translate from each origin to each destination */ 621 | 622 | 623 | .scatterplot .menuEl, 624 | .scatterplot .menuEl2 { 625 | display: inline-block; 626 | width: 150px; 627 | font-size: 14px; 628 | text-align: center; 629 | font-weight: bold; 630 | cursor: pointer; 631 | margin-top: 0; 632 | height: 100%; 633 | line-height: 40px; 634 | } 635 | 636 | .ease { 637 | -webkit-transition: left .5s; 638 | -moz-transition: left .5s; 639 | -o-transition: left .5s; 640 | transition: left .5s; 641 | } 642 | 643 | .tabs { 644 | background: #fff; 645 | position: relative; 646 | height: inherit; 647 | } 648 | 649 | .tabs > input, 650 | .tabs > span { 651 | width: 150px; 652 | height: 40px; 653 | line-height: 40px; 654 | position: absolute; 655 | top: 0; 656 | font-weight: bold; 657 | } 658 | 659 | .tabs > input { 660 | cursor: pointer; 661 | filter: alpha(opacity=0); 662 | opacity: 0; 663 | position: absolute; 664 | z-index: 99; 665 | height: inherit; 666 | margin: 0; 667 | } 668 | 669 | .tabs > span { 670 | text-align: center; 671 | overflow: hidden; 672 | } 673 | 674 | .tabs > span i, 675 | .tabs > span { 676 | -webkit-transition: left .5s; 677 | -moz-transition: left .5s; 678 | -o-transition: left .5s; 679 | transition: left .5s; 680 | } 681 | 682 | .tabs > input:hover + span { 683 | background: rgba(255,255,255,.1); 684 | } 685 | 686 | .tabs > input:checked + span { 687 | background: #eaeaea; 688 | } 689 | 690 | .tabs > input:checked + span, 691 | .tabs > input:hover + span { 692 | color: #323232; 693 | } 694 | 695 | .tab-1, .tab-1 + span { 696 | left: 0; 697 | } 698 | 699 | .tab-2, .tab-2 + span { 700 | left: 150px; 701 | } 702 | 703 | .tab-3, .tab-3 + span { 704 | left: 300px; 705 | } 706 | 707 | .tab-container .line{ 708 | background: #5a3ec8; 709 | width: 150px; 710 | height: 4px; 711 | position: absolute; 712 | bottom: 0; 713 | } 714 | 715 | .tab-1:checked ~ .line { 716 | left: 0; 717 | } 718 | .tab-2:checked ~ .line { 719 | left: 150px; 720 | } 721 | .tab-3:checked ~ .line { 722 | left: 300px; 723 | } 724 | 725 | .tab-1:focus ~ .span-1, 726 | .tab-2:focus ~ .span-2, 727 | .tab-3:focus ~ .span-3 { 728 | background: #eaeaea; 729 | } 730 | .tab-1:focus ~ .line{ 731 | left: 0; 732 | } 733 | .tab-2:focus ~ .line{ 734 | left: 150px; 735 | } 736 | .tab-3:focus ~ .line{ 737 | left: 300px; 738 | } 739 | 740 | .tab-1:hover ~ .line { 741 | left: 0; 742 | } 743 | .tab-2:hover ~ .line { 744 | left: 150px; 745 | } 746 | .tab-3:hover ~ .line { 747 | left: 300px; 748 | } 749 | 750 | /* MEDIA QUERIES */ 751 | 752 | .gif-container, .gif-container2 { 753 | visibility: hidden; 754 | height: 0; 755 | position: relative; 756 | } 757 | 758 | .brush-gif-container { 759 | display: none; 760 | } 761 | 762 | .radar-gif-container { 763 | display: none; 764 | } 765 | 766 | .parallel-coordinates-gif-container { 767 | display: none; 768 | } 769 | 770 | .smart-tooltips-gif-container { 771 | display: none; 772 | } 773 | 774 | .scatterplot .move-gif, .scatterplot .move2-gif, .scatterplot .frame-gif, .scatterplot .find-gif, .scatterplot .filter-gif, .scatterplot .details-gif { 775 | visibility: hidden; 776 | height: 0; 777 | } 778 | 779 | @media (max-width: 800px) { 780 | .title { 781 | width: 100%; 782 | padding: 0 5%; 783 | } 784 | .scatterplot .container { 785 | width: 100%; 786 | margin: 0 auto; 787 | } 788 | .scatterplot .gif-container, 789 | .scatterplot .gif-container2 { 790 | height: 60vw; 791 | } 792 | .scatterplot .gif-container, .scatterplot .gif-container2 { 793 | display: block; 794 | } 795 | .scatterplot .move-gif, .scatterplot .move2-gif, .scatterplot .frame-gif, .scatterplot .find-gif, .scatterplot .filter-gif, .scatterplot .details-gif { 796 | position: absolute; 797 | height: 60vw; 798 | } 799 | .scatterplot .move-gif, 800 | .scatterplot .move2-gif { 801 | visibility: initial; 802 | } 803 | 804 | .scatterplot .search-container { 805 | display: none; 806 | } 807 | .scatterplot .svg-container, .scatterplot .svg-container2 { 808 | visibility: hidden; 809 | height: 0; 810 | } 811 | .scatterplot .menuEl, .scatterplot .menuEl2 { 812 | width: 26%; 813 | font-size: 12px; 814 | } 815 | .tab-container .line { 816 | width: 26%; 817 | } 818 | .scatterplot .slider { 819 | visibility: hidden; 820 | height: 0; 821 | } 822 | .brush .container { 823 | visibility: hidden; 824 | height: 0; 825 | } 826 | .brush-gif-container { 827 | display: block; 828 | } 829 | .radar .container { 830 | visibility: hidden; 831 | height: 0; 832 | } 833 | .radar-gif-container { 834 | display: block; 835 | } 836 | .parallel-coordinates .container { 837 | visibility: hidden; 838 | height: 0; 839 | } 840 | .parallel-coordinates-gif-container { 841 | display: block; 842 | } 843 | .smart-tooltips .container { 844 | visibility: hidden; 845 | height: 0; 846 | } 847 | .smart-tooltips-gif-container { 848 | display: block; 849 | } 850 | .tab-container span { 851 | width: 26%; 852 | } 853 | .tab-2, .tab-2 + span { 854 | left: 26%; 855 | } 856 | .tab-3, .tab-3 + span { 857 | left: 52%; 858 | } 859 | .tab-2:checked ~ .line { 860 | left: 26%; 861 | } 862 | .tab-3:checked ~ .line { 863 | left: 52%; 864 | } 865 | .tab-2:hover ~ .line { 866 | left: 26%; 867 | } 868 | .tab-3:hover ~ .line { 869 | left: 52%; 870 | } 871 | } 872 | 873 | @media all and (-ms-high-contrast: none), (-ms-high-contrast: active) { 874 | .title { 875 | margin: 10px auto; 876 | text-align: left; 877 | width: 800px; 878 | } 879 | .label { 880 | width: 400px; 881 | margin: 0 auto; 882 | } 883 | .menu { 884 | max-width: 1000px; 885 | } 886 | .scatterplot .container { 887 | width: 100%; 888 | margin: 0 auto; 889 | } 890 | .scatterplot .gif-container, .scatterplot .gif-container2, .scatterplot .move-gif, .scatterplot .move2-gif { 891 | display: block; 892 | } 893 | .scatterplot .move-gif, .scatterplot .move2-gif, .scatterplot .frame-gif, .scatterplot .find-gif, .scatterplot .filter-gif, .scatterplot .details-gif { 894 | width: 800px !important; 895 | height: 600px !important; 896 | margin: 0 auto; 897 | visibility: visible; 898 | } 899 | .scatterplot .search-container { 900 | visibility: hidden; 901 | height: 0; 902 | } 903 | .scatterplot .svg-container, .scatterplot .svg-container2 { 904 | visibility: hidden; 905 | height: 0; 906 | } 907 | .scatterplot .menuEl, .scatterplot .menuEl2 { 908 | width: 26%; 909 | font-size: 12px; 910 | } 911 | .scatterplot .slider { 912 | visibility: hidden; 913 | height: 0 914 | } 915 | .brush .container { 916 | visibility: hidden; 917 | height: 0 918 | } 919 | .brush-gif-container { 920 | display: block; 921 | } 922 | .brush-gif { 923 | width: 800px !important; 924 | height: 600px !important; 925 | margin: 0 auto; 926 | } 927 | .radar .container { 928 | visibility: hidden; 929 | height: 0; 930 | } 931 | .radar-gif-container { 932 | display: block; 933 | } 934 | .radar-gif { 935 | width: 800px !important; 936 | height: 600px !important; 937 | margin: 0 auto; 938 | } 939 | .parallel-coordinates .container { 940 | visibility: hidden; 941 | height: 0; 942 | } 943 | .parallel-coordinates-gif-container { 944 | display: block; 945 | } 946 | .parallel-coordinates-gif { 947 | width: 800px !important; 948 | height: 600px !important; 949 | margin: 0 auto; 950 | } 951 | .smart-tooltips .container { 952 | visibility: hidden; 953 | height: 0; 954 | } 955 | .smart-tooltips-gif-container { 956 | display: block; 957 | } 958 | .smart-tooltips-gif { 959 | width: 800px !important; 960 | height: 600px !important; 961 | margin: 0 auto; 962 | } 963 | } 964 | -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | IBM Interaction 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 |
19 |
20 | 31 | 50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | Pan and zoom by dragging and scrolling. 63 |
64 |
65 | Reduce axis ranges to frame the visualisation. 66 |
67 |
68 | Type the name of a US state. 69 |
70 |
71 |
72 | 83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 | Pan to move within the frame, scroll to zoom. 96 |
97 |
98 | Act on filter to reduce the number of elements. 99 |
100 |
101 | Hover bubbles to reveal details. 102 |
103 |
104 |
105 | 116 | 117 | 118 | 119 | 120 | 121 |
122 |
123 |
124 | 125 |
126 |
127 | Hover elements to see the linking behaviour 128 |
129 | 130 | 131 | 132 | 133 |
134 |
135 | Daily Average Profits by Category 136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 | Set timespan with the slider, filter categories using the legend 149 |
150 |
151 | 152 | 153 | 154 | 155 |
156 |
157 | Product Comparison 158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 | Make a selection by clicking on a value along any axis. 167 |
168 |
169 | Sort dimensions by dragging axis horizontally. 170 |
171 |
172 | 173 | 174 | 175 | 176 |
177 |
178 | Product Sales by Category 179 |
180 |
181 |
182 |
183 |
184 |
185 |
$320k
186 |
187 |
188 |
189 |
190 |
191 | 192 |
193 |
194 | 195 |
196 |
197 | 198 |
199 |
200 | 201 | 202 |
203 |
204 | 205 |
206 |
207 | 208 |
209 |
210 | 211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 | Hover bar to show label, click it to reveal smart tooltips 222 |
223 |
224 | Hover the icons to explore single actions 225 |
226 |
227 | 228 | 229 | 230 | 231 | 232 | -------------------------------------------------------------------------------- /examples/js/brushing.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | const width = 250, 4 | height = 200, 5 | grey = '#F6F6F6', 6 | pink = '#AE8DC2', 7 | teal = '#41D6C3'; 8 | 9 | let treeMapContainer = d3.select('.treemap') 10 | .append('svg') 11 | .attr('width', width) 12 | .attr('height', height) 13 | .attr("fill", grey) 14 | .append('g'); 15 | 16 | let MapContainer = d3.select('.map') 17 | .append('svg') 18 | .attr('width', width) 19 | .attr('height', height) 20 | .attr("fill", grey) 21 | .append('g'); 22 | 23 | const projection = d3.geoAlbersUsa() 24 | .translate([width/2, height/2]) 25 | .scale([350]); 26 | 27 | const path = d3.geoPath().projection(projection); 28 | 29 | // Load Data 30 | 31 | d3.queue() 32 | .defer(d3.json, "../json/treemap-data.json") 33 | .defer(d3.json, "../json/us-states.json") 34 | .await(render); 35 | 36 | function render(err, treeMapData, mapData) { 37 | 38 | if(err) throw err; 39 | 40 | // Tree Map 41 | 42 | let treeRoot = d3.hierarchy(treeMapData, (d) => d.children) 43 | .sum((d) => d.value); 44 | 45 | let treemap = d3.treemap(treeRoot) 46 | .size([width, height]) 47 | .round(true); 48 | 49 | treemap(treeRoot) 50 | 51 | let nodes = treeRoot.descendants(); 52 | 53 | treeMapContainer.selectAll('rect') 54 | .data(nodes) 55 | .enter().append('rect') 56 | .attr("x", (d) => d.x0) 57 | .attr("y", (d) => d.y0) 58 | .attr("width", (d) => d.x1 - d.x0) 59 | .attr("height", (d) => d.y1 - d.y0) 60 | .attr("class", (d) => d.data.name) 61 | .attr("stroke", grey) 62 | .attr("stroke-width", 2) 63 | .attr("fill", pink) 64 | .on('mouseenter', function (d) { 65 | d3.selectAll("rect." + d.data.name) 66 | .transition() 67 | .duration(200) 68 | .ease(d3.easeLinear) 69 | .attr('fill', teal); 70 | 71 | d3.selectAll("path." + d.data.name) 72 | .transition() 73 | .duration(200) 74 | .ease(d3.easeLinear) 75 | .attr('fill', teal); 76 | }) 77 | .on('mouseleave', function (d) { 78 | d3.selectAll("rect." + d.data.name) 79 | .transition() 80 | .duration(200) 81 | .ease(d3.easeLinear) 82 | .attr('fill', pink); 83 | 84 | d3.selectAll("path." + d.data.name) 85 | .transition() 86 | .duration(200) 87 | .ease(d3.easeLinear) 88 | .attr('fill', pink); 89 | }); 90 | 91 | // US map 92 | 93 | let states = MapContainer.selectAll("path") 94 | .data(mapData.features) 95 | .enter().append("path") 96 | .attr('class', (d)=> d.properties.name ) 97 | .attr("d", path) 98 | .attr("stroke", grey) 99 | .attr("stroke-width", 1) 100 | .attr('fill', pink) 101 | .on('mouseenter', function (d) { 102 | d3.selectAll("path." + d.properties.name) 103 | .transition() 104 | .duration(200) 105 | .ease(d3.easeLinear) 106 | .attr('fill', teal); 107 | 108 | d3.selectAll("rect." + d.properties.name) 109 | .transition() 110 | .duration(200) 111 | .ease(d3.easeLinear) 112 | .attr('fill', teal); 113 | }) 114 | .on('mouseleave', function (d) { 115 | d3.selectAll("path." + d.properties.name) 116 | .transition() 117 | .duration(200) 118 | .ease(d3.easeLinear) 119 | .attr('fill', pink); 120 | 121 | d3.selectAll("rect." + d.properties.name) 122 | .transition() 123 | .duration(200) 124 | .ease(d3.easeLinear) 125 | .attr('fill', pink); 126 | }); 127 | 128 | } 129 | 130 | })() 131 | 132 | 133 | -------------------------------------------------------------------------------- /examples/js/chart-component.js: -------------------------------------------------------------------------------- 1 | /* global d3 */ 2 | function ChartComponent() { 3 | var width = 500 4 | var height = 500 5 | var maxTurnover = 1217306928.42753 6 | var defaultRadius = 3 7 | var maxTurnoverRadius = width / 30 8 | var turnoverSlice = [50000000, 250000000, 500000000, 750000000, 1000000000] 9 | var dashAmountValue = 50000000 10 | var selectedCompanyName = '' 11 | 12 | function chart(selection) { 13 | var defaultCircleArea = defaultRadius * defaultRadius * Math.PI 14 | var scaleTurnover = d3.scaleLinear() 15 | .domain([0, (defaultCircleArea + maxTurnover)]) 16 | .range([0, (defaultCircleArea + (maxTurnoverRadius * maxTurnoverRadius * Math.PI))]) 17 | 18 | var turnoverBackground = selection.selectAll('circle.turnover-background') 19 | .data(function (d) { 20 | return [d] 21 | }) 22 | .style('fill', function (d) { 23 | return stringNoSpaces(d.class) === stringNoSpaces(selectedCompanyName) ? '#FD7171' : '#64DCCD' 24 | }) 25 | 26 | turnoverBackground 27 | .enter() 28 | .append('circle') 29 | .attr('class', 'turnover-background') 30 | .style('fill', function (d) { 31 | return stringNoSpaces(d.class) === stringNoSpaces(selectedCompanyName) ? '#FD7171' : '#64DCCD' 32 | }) 33 | .attr('cx', 0) 34 | .attr('cy', 0) 35 | .attr('r', 0) 36 | .attr('r', function (d) { 37 | return [Math.sqrt((scaleTurnover(parseFloat(d.radius)) / Math.PI) + (Math.PI * defaultRadius * defaultRadius))] 38 | }) 39 | 40 | turnoverBackground.exit().remove() 41 | 42 | var turnoverCircles = selection.selectAll('circle.turnover') 43 | .data(function (d) { 44 | var turnoverData = [] 45 | for (var i = 0; i < turnoverSlice.length; i++) { 46 | if (d.radius > turnoverSlice[i]) { 47 | turnoverData.push((Math.sqrt(scaleTurnover(turnoverSlice[i]) / Math.PI))) 48 | } 49 | } 50 | return turnoverData 51 | }) 52 | .style('pointer-events', 'none') 53 | 54 | turnoverCircles.enter() 55 | .append('circle') 56 | .classed('turnover', true) 57 | .attr('cx', 0) 58 | .attr('cy', 0) 59 | .attr('r', function (d) { 60 | return d 61 | }) 62 | .style('pointer-events', 'none') 63 | 64 | turnoverCircles 65 | .attr('r', function (d) { 66 | return d 67 | }) 68 | 69 | turnoverCircles.exit().remove() 70 | 71 | var classesNames = ['data_01', 'data_02', 'data_03', 'data_04', 'data_05', 'data_06', 'data_07', 'data_08', 'data_09', 'data_10', 72 | 'data_11', 'data_12', 'data_13', 'data_14', 'data_15', 'data_16', 'data_17', 'data_18'] 73 | var classesAngle = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18] 74 | 75 | var scaleClass = d3.scaleLinear() 76 | .domain([0, maxTurnover]) 77 | .range([0, 150]) 78 | 79 | var classesLine = selection.selectAll('line.class-line') 80 | .data(function (d) { 81 | var classesLineData = [] 82 | 83 | for (var i = 0; i < classesNames.length; i++) { 84 | var className = classesNames[i] 85 | var classValue = parseFloat(d[className]) 86 | var dashes = Math.floor(classValue / dashAmountValue) * 5 87 | var xClass = Math.cos(-(180 / 9 * (classesAngle[i])) * Math.PI / 180) * (scaleClass(classValue) + dashes) 88 | var yClass = Math.sin(-(180 / 9 * (classesAngle[i])) * Math.PI / 180) * (scaleClass(classValue) + dashes) 89 | var xStart = (Math.cos(-(180 / 9 * (classesAngle[i])) * Math.PI / 180) * defaultRadius) 90 | var yStart = (Math.sin(-(180 / 9 * (classesAngle[i])) * Math.PI / 180) * defaultRadius) 91 | var hasMarker = ((i >= 0 && i <= 3) || (i >= 13)) 92 | classesLineData.push({ 93 | className: classesNames[i], 94 | classValue: classValue, 95 | x1: xStart, 96 | y1: yStart, 97 | x2: xStart + xClass, 98 | y2: yStart + yClass, 99 | hasMarker: hasMarker, 100 | }) 101 | } 102 | return classesLineData 103 | }) 104 | 105 | classesLine.enter() 106 | .append('line') 107 | .attr('class', function (d) { 108 | return 'class-line ' + d.className 109 | }) 110 | .attr('opacity', 0) 111 | .attr('x1', 0) 112 | .attr('y1', 0) 113 | .attr('x2', 0) 114 | .attr('y2', 0) 115 | .style('stroke-linecap', function (d) { 116 | if (d.className.indexOf('canale') === 0) { 117 | return null 118 | } else { 119 | return 'round' 120 | } 121 | }) 122 | .attr('marker-end', function (d) { 123 | if (d.hasMarker) { 124 | return 'url(#marker-circle)' 125 | } 126 | }) 127 | .style('pointer-events', 'none') 128 | 129 | selection.selectAll('circle.top') 130 | .data([0]) 131 | .enter() 132 | .append('circle') 133 | .classed('top', true) 134 | .attr('cx', 0) 135 | .attr('cy', 0) 136 | .style('opacity', 1) 137 | .attr('r', 10) 138 | .style('opacity', 0.0) 139 | .style('pointer-events', 'none') 140 | 141 | selection.selectAll('circle.patent') 142 | .data([0]) 143 | .enter() 144 | .append('circle') 145 | .classed('patent', true) 146 | .attr('cx', 0) 147 | .attr('cy', 0) 148 | .style('opacity', 0) 149 | .attr('r', 6) 150 | .style('pointer-events', 'none') 151 | } 152 | 153 | function stringNoSpaces(str) { 154 | return str.replace(' ', '').toLowerCase() 155 | } 156 | 157 | chart.dashAmountValue = function (value) { 158 | if (!arguments.length) return dashAmountValue 159 | dashAmountValue = value 160 | return chart 161 | } 162 | chart.turnoverSlice = function (value) { 163 | if (!arguments.length) return turnoverSlice 164 | turnoverSlice = value 165 | return chart 166 | } 167 | chart.defaultRadius = function (value) { 168 | if (!arguments.length) return defaultRadius 169 | defaultRadius = value 170 | return chart 171 | } 172 | chart.maxTurnoverRadius = function (value) { 173 | if (!arguments.length) return maxTurnoverRadius 174 | maxTurnoverRadius = value 175 | return chart 176 | } 177 | chart.maxTurnover = function (value) { 178 | if (!arguments.length) return maxTurnover 179 | maxTurnover = value 180 | return chart 181 | } 182 | chart.width = function (value) { 183 | if (!arguments.length) return width 184 | width = value 185 | return chart 186 | } 187 | chart.height = function (value) { 188 | if (!arguments.length) return height 189 | height = value 190 | return chart 191 | } 192 | chart.company = function (value) { 193 | if (!arguments.length) return selectedCompanyName 194 | selectedCompanyName = value 195 | return chart 196 | } 197 | 198 | return chart 199 | } 200 | -------------------------------------------------------------------------------- /examples/js/parallel-coordinates.js: -------------------------------------------------------------------------------- 1 | /* global d3 _ */ 2 | (function () { 3 | var margin = { 4 | top: 50, 5 | right: 100, 6 | bottom: 50, 7 | left: 100, 8 | } 9 | var width = 800 - margin.left - margin.right 10 | var height = 400 - margin.top - margin.bottom 11 | var clickEnable = false 12 | 13 | var x = d3.scalePoint().rangeRound([0, width], 1) 14 | var y = {} 15 | var dragging = {} 16 | var dimensions 17 | var line = d3.line() 18 | var axis = d3.axisLeft() 19 | var foreground 20 | var keys = ["Price", "Production Cost", "Unit Sold", "Weight"] 21 | 22 | var dataset = [{ 23 | "Price": 9, 24 | "Production Cost": 4, 25 | "Unit Sold": 600, 26 | "Weight": 500, 27 | }, { 28 | "Price": 8, 29 | "Production Cost": 1, 30 | "Unit Sold": 400, 31 | "Weight": 500, 32 | }, { 33 | "Price": 1, 34 | "Production Cost": 6, 35 | "Unit Sold": 100, 36 | "Weight": 200, 37 | }, { 38 | "Price": 5, 39 | "Production Cost": 3, 40 | "Unit Sold": 1000, 41 | "Weight": 200, 42 | }, { 43 | "Price": 2, 44 | "Production Cost": 5, 45 | "Unit Sold": 200, 46 | "Weight": 500, 47 | }, { 48 | "Price": 7, 49 | "Production Cost": 2, 50 | "Unit Sold": 100, 51 | "Weight": 600, 52 | }, { 53 | "Price": 10, 54 | "Production Cost": 6, 55 | "Unit Sold": 300, 56 | "Weight": 1000, 57 | }, { 58 | "Price": 2, 59 | "Production Cost": 6, 60 | "Unit Sold": 900, 61 | "Weight": 100, 62 | }, { 63 | "Price": 4, 64 | "Production Cost": 4, 65 | "Unit Sold": 700, 66 | "Weight": 400, 67 | }, { 68 | "Price": 9, 69 | "Production Cost": 4, 70 | "Unit Sold": 600, 71 | "Weight": 800, 72 | }, { 73 | "Price": 6, 74 | "Production Cost": 3, 75 | "Unit Sold": 500, 76 | "Weight": 800, 77 | }, { 78 | "Price": 6, 79 | "Production Cost": 3, 80 | "Unit Sold": 700, 81 | "Weight": 1000, 82 | }, { 83 | "Price": 5, 84 | "Production Cost": 6, 85 | "Unit Sold": 900, 86 | "Weight": 600, 87 | }, { 88 | "Price": 1, 89 | "Production Cost": 2, 90 | "Unit Sold": 200, 91 | "Weight": 500, 92 | }, { 93 | "Price": 4, 94 | "Production Cost": 4, 95 | "Unit Sold": 100, 96 | "Weight": 300, 97 | }, { 98 | "Price": 8, 99 | "Production Cost": 3, 100 | "Unit Sold": 200, 101 | "Weight": 1000, 102 | }] 103 | 104 | var svg = d3.select(".parallel-coordinates-container").append("svg") 105 | .attr("width", width + margin.left + margin.right) 106 | .attr("height", height + margin.top + margin.bottom) 107 | .append("g") 108 | .attr("transform", "translate(" + margin.left + "," + margin.top + ")") 109 | 110 | // Extract the list of dimensions and create a scale for each. 111 | x.domain(dimensions = d3.keys(dataset[0]).filter(function (d) { 112 | return y[d] = d3.scaleLinear() 113 | .domain(d3.extent(dataset, function (p) { 114 | return +p[d] 115 | })) 116 | .range([height, 0]) 117 | })) 118 | 119 | var foregroundRect = svg.append("rect") 120 | .attr('width', 600) 121 | .attr('height', 300) 122 | .attr('fill', 'transparent') 123 | 124 | // Add blue foreground lines for focus. 125 | foreground = svg.append("g") 126 | .attr("class", "foreground") 127 | .selectAll("path") 128 | .data(dataset) 129 | .enter() 130 | .append("path") 131 | .attr('class', function (d, i) { 132 | return 'Price-' + d['Price'] + ' ProductionCost-' + d['Production Cost'] + ' UnitSold-' + d['Unit Sold'] + ' Weight-' + d['Weight'] + ' foreground' 133 | }) 134 | .attr("d", path) 135 | 136 | foregroundRect.on('click', function (d, i) { 137 | clickEnable = false 138 | svg.selectAll('circle.circle') 139 | .transition() 140 | .attr('opacity', 0) 141 | 142 | svg.selectAll('path.foreground') 143 | .transition() 144 | .attr('opacity', 1) 145 | }) 146 | 147 | // Add a group element for each dimension. 148 | var g = svg.selectAll(".dimension") 149 | .data(dimensions) 150 | .enter() 151 | .append("g") 152 | .attr("class", "dimension") 153 | .attr("transform", function (d) { 154 | return "translate(" + x(d) + ")" 155 | }) 156 | .call( 157 | d3.drag() 158 | .on("start", function (d) { 159 | dragging[d] = x(d) 160 | }) 161 | .on("drag", function (d) { 162 | dragging[d] = Math.min(width, Math.max(0, d3.event.x)) 163 | foreground.attr("d", path) 164 | dimensions.sort(function (a, b) { 165 | return position(a) - position(b) 166 | }) 167 | x.domain(dimensions) 168 | g.attr("transform", function (d) { 169 | return "translate(" + position(d) + ")" 170 | }) 171 | }) 172 | .on("end", function (d) { 173 | delete dragging[d] 174 | transition(d3.select(this)).attr("transform", "translate(" + x(d) + ")") 175 | transition(foreground).attr("d", path) 176 | keys = dimensions 177 | })) 178 | 179 | g.append("g") 180 | .attr("class", "axis") 181 | .each(function (d) { 182 | d3.select(this).call(axis.scale(y[d])) 183 | }) 184 | .append("rect") 185 | .attr("class", "axis-title-background") 186 | .attr("y", -50) 187 | .attr("x", -55) 188 | .attr("width", 110) 189 | .attr("height", 30) 190 | .attr("cursor", "move") 191 | 192 | g.append("g") 193 | .attr("class", "axis") 194 | .each(function (d) { 195 | d3.select(this).call(axis.scale(y[d])) 196 | }) 197 | .append("text") 198 | .attr("class", "axis-title") 199 | .style("text-anchor", "middle") 200 | .attr("y", -30) 201 | .attr("x", -10) 202 | .text(function (d) { 203 | if (d === 'Production Cost') return 'Prod. Cost' 204 | return d 205 | }) 206 | 207 | var iconPath = g.selectAll("path.icon") 208 | .attr("class", "icon") 209 | .data(function (d) { return [d] }) 210 | 211 | iconPath 212 | .enter() 213 | .append("path") 214 | .attr("d", "M11.5 9H.5C.2 9 0 8.8 0 8.5S.2 8 .5 8h11c.3 0 .5.2.5.5s-.2.5-.5.5zM11.5 5H.5C.2 5 0 4.8 0 4.5S.2 4 .5 4h11c.3 0 .5.2.5.5s-.2.5-.5.5zM11.5 1H.5C.2 1 0 .8 0 .5S.2 0 .5 0h11c.3 0 .5.2.5.5s-.2.5-.5.5z") 215 | .attr("stroke", "#efc100") 216 | .attr("stroke-width", "1") 217 | .attr("transform", "translate(42, -41) rotate(90)") 218 | 219 | drawCircles(dataset) 220 | 221 | function drawCircles(dataset) { 222 | var circles = g.selectAll("g.circles") 223 | .data(function (d) { 224 | var global = [] 225 | dataset.forEach(function (datum) { 226 | var obj = {} 227 | obj[d] = datum[d] 228 | global.push(obj) 229 | }) 230 | return global 231 | }) 232 | 233 | var circlesEnter = circles 234 | .enter() 235 | .append('g') 236 | .attr("class", "circles") 237 | .attr("parent-index", function (d, i) { 238 | return i 239 | }) 240 | 241 | circles.exit().remove() 242 | 243 | var circle = circlesEnter 244 | .selectAll('.circle') 245 | .data(function (d, i) { 246 | return [d] 247 | }) 248 | .attr('cy', function (d, i) { 249 | var index = keys.indexOf(_.keys(d)[0]) 250 | return y[keys[index]](d[keys[index]]) 251 | }) 252 | .attr('cx', function (d, i) { 253 | return position([keys[i]]) 254 | }) 255 | 256 | circle 257 | .enter() 258 | .append("circle") 259 | .attr("class", 'circle') 260 | .attr("r", 6) 261 | .attr('cy', function (d, i) { 262 | var index = keys.indexOf(_.keys(d)[0]) 263 | return y[keys[index]](d[keys[index]]) 264 | }) 265 | .attr('cx', function (d, i) { 266 | return position([keys[i]]) 267 | }) 268 | .attr('index', function (d, i) { 269 | var index = keys.indexOf(_.keys(d)[0]) 270 | var parentIndex = d3.select(this.parentNode).attr("parent-index") 271 | var thisClass = dimensions[index].replace(' ', '-').replace(' ', '-') 272 | return thisClass + '-' + parentIndex 273 | }) 274 | .attr('fill', '#BF6292') 275 | .attr('stroke', '#fff') 276 | .attr('stroke-width', 1) 277 | .attr('opacity', 0) 278 | .on('mouseover', function (d, i) { 279 | if (!clickEnable) { 280 | var index = keys.indexOf(_.keys(d)[0]) 281 | var className = keys[index] 282 | var classValue = d[className] 283 | 284 | svg.selectAll('circle.circle') 285 | .transition() 286 | .attr('opacity', 0) 287 | 288 | d3.select(this) 289 | .transition() 290 | .attr('opacity', 1) 291 | 292 | svg.selectAll('path.foreground') 293 | .transition() 294 | .attr('opacity', 0.1) 295 | 296 | svg.selectAll('path.' + className.replace(' ', '') + '-' + classValue) 297 | .transition() 298 | .attr('opacity', 1) 299 | } 300 | }) 301 | .on('mouseout', function () { 302 | if (!clickEnable) { 303 | d3.select(this) 304 | .transition() 305 | .attr('opacity', 0) 306 | 307 | svg.selectAll('path.foreground') 308 | .transition() 309 | .attr('opacity', 1) 310 | } 311 | }) 312 | .on('click', function (d, i) { 313 | clickEnable = !clickEnable 314 | if (clickEnable) { 315 | var index = keys.indexOf(_.keys(d)[0]) 316 | var className = keys[index] 317 | var classValue = d[className] 318 | d3.select(this) 319 | .transition() 320 | .attr('opacity', 1) 321 | 322 | svg.selectAll('path.foreground') 323 | .transition() 324 | .attr('opacity', 0.1) 325 | 326 | svg.selectAll('path.' + className.replace(' ', '') + '-' + classValue) 327 | .transition() 328 | .attr('opacity', 1) 329 | } else { 330 | d3.select(this) 331 | .transition() 332 | .attr('opacity', 0) 333 | 334 | svg.selectAll('path.foreground') 335 | .transition() 336 | .attr('opacity', 1) 337 | } 338 | }) 339 | 340 | var circleUpdate = circles 341 | .selectAll('.circle') 342 | .data(function (d) { 343 | return d 344 | }) 345 | .attr('cy', function (d, i) { 346 | var index = keys.indexOf(_.keys(d)[0]) 347 | return y[keys[index]](d[keys[index]]) 348 | }) 349 | .attr('cx', function (d, i) { 350 | return position([keys[i]]) 351 | }) 352 | 353 | circleUpdate 354 | .enter() 355 | .append("circle") 356 | .attr("class", 'circle') 357 | .attr("r", 5) 358 | .attr('cy', function (d, i) { 359 | var index = keys.indexOf(_.keys(d)[0]) 360 | return y[keys[index]](d[keys[index]]) 361 | }) 362 | .attr('cx', function (d, i) { 363 | return position([keys[i]]) 364 | }) 365 | .attr('index', function (d, i) { 366 | var index = keys.indexOf(_.keys(d)[0]) 367 | var parentIndex = d3.select(this.parentNode).attr("parent-index") 368 | var thisClass = dimensions[index].replace(' ', '-').replace(' ', '-') 369 | return thisClass + '-' + parentIndex 370 | }) 371 | .attr('fill', '#BF6292') 372 | .attr('stroke', '#fff') 373 | .attr('stroke-width', 1) 374 | .attr('opacity', 0) 375 | .on('mouseover', function (d, i) { 376 | if (!clickEnable) { 377 | var index = keys.indexOf(_.keys(d)[0]) 378 | var className = keys[index] 379 | var classValue = d[className] 380 | 381 | svg.selectAll('circle.circle') 382 | .transition() 383 | .attr('opacity', 0) 384 | 385 | d3.select(this) 386 | .transition() 387 | .attr('opacity', 1) 388 | 389 | svg.selectAll('path.foreground') 390 | .transition() 391 | .attr('opacity', 0.1) 392 | 393 | svg.selectAll('path.' + className.replace(' ', '') + '-' + classValue) 394 | .transition() 395 | .attr('opacity', 1) 396 | } 397 | }) 398 | .on('mouseout', function () { 399 | if (!clickEnable) { 400 | d3.select(this) 401 | .transition() 402 | .attr('opacity', 0) 403 | 404 | svg.selectAll('path.foreground') 405 | .transition() 406 | .attr('opacity', 1) 407 | } 408 | }) 409 | .on('click', function (d, i) { 410 | clickEnable = !clickEnable 411 | if (clickEnable) { 412 | var index = keys.indexOf(_.keys(d)[0]) 413 | var className = keys[index] 414 | var classValue = d[className] 415 | d3.select(this) 416 | .transition() 417 | .attr('opacity', 1) 418 | 419 | svg.selectAll('path.foreground') 420 | .transition() 421 | .attr('opacity', 0.1) 422 | 423 | svg.selectAll('path.' + className.replace(' ', '') + '-' + classValue) 424 | .transition() 425 | .attr('opacity', 1) 426 | } else { 427 | d3.select(this) 428 | .transition() 429 | .attr('opacity', 0) 430 | 431 | svg.selectAll('path.foreground') 432 | .transition() 433 | .attr('opacity', 1) 434 | } 435 | }) 436 | } 437 | 438 | function position(d) { 439 | var v = dragging[d] 440 | return v == null ? x(d) : v 441 | } 442 | 443 | function transition(g) { 444 | return g.transition().duration(500) 445 | } 446 | 447 | // Returns the path for a given data point. 448 | function path(d) { 449 | return line(dimensions.map(function (p) { 450 | return [position(p), y[p](d[p])] 451 | })) 452 | } 453 | })() 454 | -------------------------------------------------------------------------------- /examples/js/radar-controller.js: -------------------------------------------------------------------------------- 1 | /* global d3 _ */ 2 | (function () { 3 | var radarMargin = { 4 | top: 100, 5 | right: 100, 6 | bottom: 100, 7 | left: 100, 8 | } 9 | var radarWidth = Math.min(550, window.innerWidth - 10) - radarMargin.left - radarMargin.right 10 | var radarHeight = Math.min(radarWidth, window.innerHeight - radarMargin.top - radarMargin.bottom - 20) 11 | var radarColorscale = ['#8ED3C8', '#9772B1', '#FDE390'] 12 | var radarColorscaleObject = {'EMEA': '#8ED3C8', 'Americas': '#9772B1', 'APAC': '#FDE390'} 13 | var LegendOptions = { 14 | 'EMEA': { 15 | name: 'EMEA', 16 | active: true, 17 | }, 18 | 'Americas': { 19 | name: 'Americas', 20 | active: true, 21 | }, 22 | 'APAC': { 23 | name: 'APAC', 24 | active: true, 25 | }, 26 | } 27 | 28 | var radarChart 29 | var summedByDateArray 30 | var summedArray 31 | var newMinCoex = 0 32 | var newMaxCoex = 30 33 | 34 | var barchartG 35 | var barchartDataset 36 | var barchartWidth 37 | var barchartHeight 38 | var heightScale 39 | var dataset 40 | 41 | var group 42 | var immutableScaleX 43 | var scaleX 44 | 45 | var Format = d3.format(',d') 46 | 47 | d3.json('../json/radar-dataset.json', function (radarDataset) { 48 | dataset = radarDataset 49 | initData(radarDataset, true) 50 | drawLegend() 51 | drawBarchart() 52 | drawSlider() 53 | }) 54 | 55 | function initData(dataset, init) { 56 | summedByDateArray = [] 57 | summedArray = [] 58 | var groupedByArea = _.groupBy(dataset, 'area') 59 | var groupedByDate = _.toArray(_.groupBy(dataset, 'date')) 60 | 61 | _.forEach(groupedByDate, function (d) { 62 | var summed = _.reduce(d, function (result, value, key) { 63 | result.value = (result.value || 0) + value.value 64 | result.area = value.area 65 | result.axis = value.axis 66 | result.date = value.date 67 | return result 68 | }, {}) 69 | summedByDateArray.push(summed) 70 | }, {}) 71 | 72 | _.forEach(groupedByArea, function (value) { 73 | var groupedByCategory = _.toArray(_.groupBy(value, 'axis')) 74 | groupedByCategory.map(function (d, i) { 75 | var summed = _.reduce(d, function (result, value, key) { 76 | result.value = (result.value || 0) + value.value 77 | result.area = value.area 78 | result.axis = value.axis 79 | result.date = value.date 80 | return result 81 | }, {}) 82 | summed.value = summed.value / (newMaxCoex - newMinCoex + 1) 83 | summedArray.push(summed) 84 | }) 85 | }, {}) 86 | 87 | var groupedSummedByArea = _.toArray(_.groupBy(summedArray, 'area')) 88 | var radarChartOptions = { 89 | w: radarWidth, 90 | h: radarHeight, 91 | margin: radarMargin, 92 | maxValue: 0.5, 93 | levels: 5, 94 | roundStrokes: true, 95 | color: radarColorscale, 96 | } 97 | 98 | if (init) { 99 | radarChart = new RadarChart(".radarChart", groupedSummedByArea, radarChartOptions) 100 | } else { 101 | radarChart.updateData(groupedSummedByArea) 102 | } 103 | } 104 | 105 | function drawLegend() { 106 | var svgLegend = d3.select('.legend') 107 | .append('svg') 108 | .classed('svgLegend', true) 109 | .attr("width", 100) 110 | .attr("height", 100) 111 | 112 | var legendG = svgLegend 113 | .selectAll('g.legend') 114 | .data(_.values(LegendOptions)) 115 | .enter() 116 | .append("g") 117 | .attr("class", "legend") 118 | .attr('transform', function (d, i) { 119 | return 'translate(0, ' + (20 * i) + ')' 120 | }) 121 | 122 | var legendRect = legendG.selectAll('rect.legendRect') 123 | .data(function (d, i) { return [d] }) 124 | 125 | legendRect 126 | .enter() 127 | .append("rect") 128 | .attr("class", "legendRect") 129 | .attr("x", 20) 130 | .attr("y", function (d, i) { 131 | return i * 25 + 1 132 | }) 133 | .attr("width", 16) 134 | .attr("height", 16) 135 | .style("fill", function (d, i) { 136 | return radarColorscaleObject[d.name] 137 | }) 138 | 139 | var legendText = legendG.selectAll('text.legendText') 140 | .data(function (d, i) { return [d] }) 141 | 142 | legendText 143 | .enter() 144 | .append("text") 145 | .attr("class", function (d, i) { 146 | return "legendText " + d.name 147 | }) 148 | .attr("x", 40) 149 | .attr("y", function (d, i) { 150 | return i * 28 + 14 151 | }) 152 | .attr("font-size", "14px") 153 | .attr("fill", function (d, i) { 154 | return d.active ? '#5F92CD' : '#cccccc' 155 | }) 156 | .style('cursor', 'pointer') 157 | .text(function (d) { 158 | return d.name 159 | }) 160 | .on('click', function (d, i) { 161 | LegendOptions[d.name].active = !LegendOptions[d.name].active 162 | if (LegendOptions[d.name].active) { 163 | d3.selectAll('.radarArea.' + d.name) 164 | .style('display', 'block') 165 | 166 | d3.selectAll('.legendText.' + d.name) 167 | .style('opacity', 1) 168 | 169 | d3.selectAll('.radarCircle.' + d.name) 170 | .style('display', 'block') 171 | 172 | d3.selectAll('.checkboxPath.' + d.name) 173 | .style('display', 'block') 174 | } else { 175 | d3.selectAll('.radarArea.' + d.name) 176 | .style('display', 'none') 177 | 178 | d3.selectAll('.legendText.' + d.name) 179 | .style('opacity', 0.2) 180 | 181 | d3.selectAll('.radarCircle.' + d.name) 182 | .style('display', 'none') 183 | 184 | d3.selectAll('.checkboxPath.' + d.name) 185 | .style('display', 'none') 186 | } 187 | }) 188 | 189 | var checkboxRect = legendG.selectAll('rect.checkboxRect') 190 | .data(function (d, i) { return [d] }) 191 | 192 | checkboxRect 193 | .enter() 194 | .append("rect") 195 | .attr("class", "checkboxRect") 196 | .attr("x", 20) 197 | .attr("y", function (d, i) { 198 | return i * 25 + 1 199 | }) 200 | .attr("width", 16) 201 | .attr("height", 16) 202 | .attr("stroke-width", 0) 203 | .attr("stroke", "#5F92CD") 204 | .attr("cursor", "pointer") 205 | .attr("fill-opacity", 0) 206 | .on('click', function (d, i) { 207 | LegendOptions[d.name].active = !LegendOptions[d.name].active 208 | if (LegendOptions[d.name].active) { 209 | d3.selectAll('.radarArea.' + d.name) 210 | .style('display', 'block') 211 | 212 | d3.selectAll('.legendText.' + d.name) 213 | .style('opacity', 1) 214 | 215 | d3.selectAll('.radarCircle.' + d.name) 216 | .style('display', 'block') 217 | 218 | d3.selectAll('.checkboxPath.' + d.name) 219 | .style('display', 'block') 220 | } else { 221 | d3.selectAll('.radarArea.' + d.name) 222 | .style('display', 'none') 223 | 224 | d3.selectAll('.legendText.' + d.name) 225 | .style('opacity', 0.2) 226 | 227 | d3.selectAll('.radarCircle.' + d.name) 228 | .style('display', 'none') 229 | 230 | d3.selectAll('.checkboxPath.' + d.name) 231 | .style('display', 'none') 232 | } 233 | }) 234 | 235 | var checkboxPath = legendG.selectAll('path.checkboxPath') 236 | .data(function (d, i) { return [d] }) 237 | 238 | checkboxPath 239 | .enter() 240 | .append("path") 241 | .attr("class", function (d) { 242 | return "checkboxPath " + d.name 243 | }) 244 | .attr("d", function () { 245 | return "M15.886,3.735L6.765,15.373l-3.439-3.849" 246 | }) 247 | .attr("fill", "none") 248 | .attr("transform", "translate(20, 1), scale(0.8)") 249 | .attr("stroke-width", 2) 250 | .attr("stroke", "#FFFFFF") 251 | .attr("pointer-events", "none") 252 | } 253 | 254 | function drawBarchart() { 255 | barchartHeight = 100 256 | barchartWidth = 10.35 257 | 258 | barchartDataset = summedByDateArray 259 | 260 | heightScale = d3.scaleLinear() 261 | .domain([0, 30000]) 262 | .range([0, barchartHeight]) 263 | 264 | heightScale.domain[1] = d3.max(summedByDateArray, function (d) { 265 | return d.value 266 | }) 267 | 268 | var svgBarchart = d3.selectAll('.barChart') 269 | .append('svg') 270 | .attr("width", 380) 271 | .attr("height", barchartHeight) 272 | 273 | barchartG = svgBarchart 274 | .append("g") 275 | .attr("class", "barchartG") 276 | 277 | var barcharts = barchartG 278 | .selectAll('.barchart') 279 | .data(summedByDateArray) 280 | .attr("x", function (d, i) { 281 | return (barchartWidth + 2) * i 282 | }) 283 | .attr('y', function (d, i) { 284 | return barchartHeight - heightScale(d.value) 285 | }) 286 | .attr("height", function (d, i) { 287 | return heightScale(d.value) 288 | }) 289 | 290 | barcharts 291 | .enter() 292 | .append("rect") 293 | .classed('barchart', true) 294 | .attr("x", function (d, i) { 295 | return (barchartWidth + 2) * i 296 | }) 297 | .attr('y', function (d, i) { 298 | return barchartHeight - heightScale(d.value) 299 | }) 300 | .attr('width', barchartWidth) 301 | .attr("height", function (d, i) { 302 | return heightScale(d.value) 303 | }) 304 | .attr('fill', '#5F92CD') 305 | 306 | barcharts.exit().remove() 307 | } 308 | 309 | function drawSlider() { 310 | immutableScaleX = d3.scaleLinear() 311 | .domain([0, 30]) 312 | .range([55, 427]) 313 | 314 | scaleX = d3.scaleLinear() 315 | .domain([0, 30]) 316 | .range([55, 427]) 317 | 318 | var svgSlider = d3.select('.barChart').append('svg') 319 | .attr('width', 500) 320 | .attr('height', 150) 321 | 322 | group = svgSlider.append('g') 323 | .attr('class', 'group') 324 | .attr('transform', 'translate(7,20)') 325 | 326 | group 327 | .append('line') 328 | .attr('x1', scaleX(0)) 329 | .attr('x2', scaleX(30)) 330 | .attr('y1', 0) 331 | .attr('y2', 0) 332 | .attr('stroke', '#706F6F') 333 | 334 | var text = group.selectAll('text.sliderValue') 335 | .data([newMinCoex, newMaxCoex]) 336 | 337 | text 338 | .enter() 339 | .append('text') 340 | .classed('sliderValue', true) 341 | .attr('dx', function (d, i) { 342 | return (i === 0) ? 35 : 450 343 | }) 344 | .attr('dy', 5) 345 | .style('text-anchor', function (d, i) { 346 | return i === 0 ? 'end' : 'start' 347 | }) 348 | .style('text-transform', 'uppercase') 349 | .text(function (d, i) { 350 | return (Math.abs(parseInt(d)) + 1) + ' Jan' 351 | }) 352 | .attr('fill', '#000') 353 | .style('font-size', '12px') 354 | 355 | text.exit().remove() 356 | 357 | var sliderXLine = group.selectAll('#sliderXLine') 358 | .data([0]) 359 | .attr('x1', immutableScaleX(0)) 360 | .attr('x2', immutableScaleX(30)) 361 | 362 | sliderXLine 363 | .enter() 364 | .append('line') 365 | .attr('id', 'sliderXLine') 366 | .attr('x1', immutableScaleX(0) + 3) 367 | .attr('y1', 0) 368 | .attr('x2', immutableScaleX(30)) 369 | .attr('y2', 0) 370 | .attr('stroke-width', 14) 371 | .style('opacity', 0.6) 372 | .attr('stroke-linecap', 'round') 373 | .attr('stroke', '#F05F5B') 374 | 375 | sliderXLine.exit().remove() 376 | 377 | var sliderXEnd = group.selectAll('#sliderXEnd') 378 | .data([0]) 379 | .attr('cx', immutableScaleX(30) + 3) 380 | 381 | sliderXEnd 382 | .enter() 383 | .append('circle') 384 | .attr('id', 'sliderXEnd') 385 | .attr('cx', immutableScaleX(30) + 3) 386 | .attr('cy', 0) 387 | .attr('r', 7) 388 | .style('opacity', 1) 389 | .attr('fill', '#F05F5B') 390 | .style('cursor', 'ew-resize') 391 | .call(d3.drag() 392 | .on("drag", function () { 393 | draggedX('end', barchartDataset) 394 | }) 395 | ) 396 | 397 | sliderXEnd.exit().remove() 398 | 399 | var sliderXStart = group.selectAll('#sliderXStart') 400 | .data([0]) 401 | .attr('cx', immutableScaleX(0) + 3) 402 | 403 | sliderXStart 404 | .enter() 405 | .append('circle') 406 | .attr('id', 'sliderXStart') 407 | .attr('cx', immutableScaleX(0) + 3) 408 | .attr('cy', 0) 409 | .attr('r', 7) 410 | .style('opacity', 1) 411 | .attr('fill', '#F05F5B') 412 | .style('cursor', 'ew-resize') 413 | .call(d3.drag() 414 | .on("drag", function () { 415 | draggedX('start', barchartDataset) 416 | }) 417 | ) 418 | 419 | sliderXStart.exit().remove() 420 | 421 | var sumText = group.selectAll('text.sumText') 422 | .data([0]) 423 | .text(function (d, i) { 424 | var totalSum = 0 425 | summedByDateArray.forEach(function (d) { 426 | totalSum += d.value 427 | }) 428 | return 'SUM OF PROFITS ' + Format(totalSum) + ' $' 429 | }) 430 | 431 | sumText 432 | .enter() 433 | .append('text') 434 | .classed('sumText', true) 435 | .attr('dx', 240) 436 | .attr('dy', 40) 437 | .style('text-transform', 'uppercase') 438 | .style('text-anchor', 'middle') 439 | .style('font-size', 14) 440 | .attr('fill', '#808080') 441 | .text(function (d, i) { 442 | var totalSum = 0 443 | summedByDateArray.forEach(function (d) { 444 | totalSum += d.value 445 | }) 446 | return 'SUM OF PROFITS ' + Format(totalSum) + ' $' 447 | }) 448 | 449 | sumText.exit().remove() 450 | } 451 | 452 | function draggedX(type, barchartDataset) { 453 | var initialvalue = scaleX(newMinCoex) 454 | if (type === 'start') { 455 | newMinCoex += immutableScaleX.invert(initialvalue - (d3.event.dx * -1)) 456 | if (newMinCoex > 30) { 457 | newMinCoex = 30 458 | } 459 | if (newMinCoex < 0) { 460 | newMinCoex = 0 461 | } 462 | if ((newMaxCoex - newMinCoex) <= 0) { 463 | newMinCoex = newMaxCoex 464 | } 465 | } else { 466 | newMaxCoex -= immutableScaleX.invert(initialvalue - (d3.event.dx)) 467 | 468 | if (newMaxCoex < 0) { 469 | newMaxCoex = 0 470 | } 471 | if (newMaxCoex > 30) { 472 | newMaxCoex = 30 473 | } 474 | if ((newMaxCoex - newMinCoex) <= 0) { 475 | newMaxCoex = newMinCoex 476 | } 477 | } 478 | scaleX.domain([newMinCoex, newMaxCoex]) 479 | if (type === 'end') { 480 | group.selectAll('#sliderXEnd') 481 | .attr('cx', immutableScaleX(newMaxCoex) + 5) 482 | } else { 483 | group.selectAll('#sliderXStart') 484 | .attr('cx', immutableScaleX(newMinCoex) + 3) 485 | } 486 | 487 | group.selectAll('#sliderXLine') 488 | .attr('x1', immutableScaleX(newMinCoex) + 3) 489 | .attr('x2', immutableScaleX(newMaxCoex)) 490 | 491 | var filter1 = newMinCoex 492 | var filter2 = newMaxCoex 493 | 494 | var dateFilter1, dateFilter2 495 | 496 | var datasetCopy = JSON.parse(JSON.stringify(dataset)) 497 | 498 | var filteredDataset = datasetCopy.map(function (data) { 499 | var ceiledFilter1 = Math.ceil(filter1) + 1 500 | var ceiledFilter2 = Math.ceil(filter2) + 1 501 | dateFilter1 = new Date('01/' + ceiledFilter1 + '/2016') 502 | dateFilter2 = new Date('01/' + ceiledFilter2 + '/2016') 503 | dateFilter2 = dateFilter2.setDate(dateFilter2.getDate() + 1) 504 | if (new Date(data.date) > dateFilter1 && new Date(data.date) <= dateFilter2) { 505 | return data 506 | } else { 507 | data.value = 0 508 | return data 509 | } 510 | }) 511 | 512 | group.selectAll('text.sliderValue') 513 | .data([Math.ceil(filter1), Math.ceil(filter2)]) 514 | .attr('dx', function (d, i) { 515 | return (i === 0) ? 35 : 450 516 | }) 517 | .text(function (d, i) { 518 | return (Math.abs(parseInt(d)) + 1) + ' Jan' 519 | }) 520 | 521 | initData(filteredDataset, false) 522 | 523 | var barcharts = barchartG 524 | .selectAll('.barchart') 525 | .data(barchartDataset) 526 | .attr("x", function (d, i) { 527 | return (barchartWidth + 2) * i 528 | }) 529 | .attr('y', function (d, i) { 530 | return barchartHeight - heightScale(d.value) 531 | }) 532 | .attr("height", function (d, i) { 533 | return heightScale(d.value) 534 | }) 535 | .attr('fill', function (d, i) { 536 | var ceiledFilter1 = Math.ceil(filter1) + 1 537 | var ceiledFilter2 = Math.ceil(filter2) + 1 538 | dateFilter1 = new Date('01/' + ceiledFilter1 + '/2016') 539 | dateFilter2 = new Date('01/' + ceiledFilter2 + '/2016') 540 | dateFilter2 = dateFilter2.setDate(dateFilter2.getDate() + 1) 541 | if (new Date(d.date) > dateFilter1 && new Date(d.date) <= dateFilter2) { 542 | return '#5F92CD' 543 | } else { 544 | return '#CDDBF1' 545 | } 546 | }) 547 | 548 | barcharts.exit().remove() 549 | 550 | group.selectAll('text.sumText') 551 | .data([0]) 552 | .text(function (d, i) { 553 | var totalSum = 0 554 | var ceiledFilter1 = Math.ceil(filter1) + 1 555 | var ceiledFilter2 = Math.ceil(filter2) + 1 556 | dateFilter1 = new Date('01/' + ceiledFilter1 + '/2016') 557 | dateFilter2 = new Date('01/' + ceiledFilter2 + '/2016') 558 | dateFilter2 = dateFilter2.setDate(dateFilter2.getDate() + 1) 559 | summedByDateArray.forEach(function (d) { 560 | if (new Date(d.date) > dateFilter1 && new Date(d.date) <= dateFilter2) { 561 | totalSum += d.value 562 | } 563 | }) 564 | return 'SUM OF PROFITS ' + Format(totalSum) + ' $' 565 | }) 566 | } 567 | })() 568 | -------------------------------------------------------------------------------- /examples/js/radar.js: -------------------------------------------------------------------------------- 1 | /* global d3 */ 2 | function RadarChart(id, data, options) { 3 | var cfg = { 4 | w: 600, 5 | h: 600, 6 | margin: { 7 | top: 20, 8 | right: 20, 9 | bottom: 20, 10 | left: 20, 11 | }, 12 | levels: 3, 13 | maxValue: 0, 14 | labelFactor: 1.15, 15 | wrapWidth: 60, 16 | opacityArea: 0.7, 17 | dotRadius: 6, 18 | opacityCircles: 0.1, 19 | strokeWidth: 0, 20 | roundStrokes: false, 21 | color: ['#8ED3C8', '#9772B1', '#FDE390'], 22 | } 23 | 24 | var categoryColor = { 25 | 'EMEA': '#8ED3C8', 26 | 'Americas': '#9772B1', 27 | 'APAC': '#FDE390', 28 | } 29 | 30 | if (typeof options !== 'undefined') { 31 | for (var i in options) { 32 | if (typeof options[i] !== 'undefined') { 33 | cfg[i] = options[i] 34 | } 35 | } 36 | } 37 | 38 | var maxValue = Math.max(cfg.maxValue, d3.max(data, function (i) { 39 | return d3.max(i.map(function (o) { 40 | return o.value 41 | })) 42 | })) 43 | 44 | var allAxis = (data[0].map(function (i, j) { 45 | return i.axis 46 | })) 47 | var total = allAxis.length 48 | var radius = Math.min(cfg.w / 2, cfg.h / 2) 49 | var Format = d3.format(',d') 50 | var angleSlice = Math.PI * 2 / total 51 | var rScale = d3.scaleLinear() 52 | .range([0, radius]) 53 | .domain([0, maxValue]) 54 | 55 | var svg = d3.select(id).append("svg") 56 | .attr("width", cfg.w + cfg.margin.left + cfg.margin.right) 57 | .attr("height", cfg.h + cfg.margin.top + cfg.margin.bottom) 58 | .attr("class", "radar" + id) 59 | 60 | var g = svg.append("g") 61 | .attr("transform", "translate(" + (cfg.w / 2 + cfg.margin.left) + "," + (cfg.h / 2 + cfg.margin.top) + ")") 62 | 63 | drawAxis() 64 | 65 | this.updateData = function (dataset) { 66 | var radarLine = d3.radialLine() 67 | .radius(function (d) { 68 | return rScale(d.value) 69 | }) 70 | .angle(function (d, i) { 71 | return i * angleSlice 72 | }) 73 | 74 | maxValue = Math.max(cfg.maxValue, d3.max(dataset, function (i) { 75 | return d3.max(i.map(function (o) { 76 | return o.value 77 | })) 78 | })) 79 | 80 | rScale.domain([0, maxValue]) 81 | 82 | var blobWrapper = g.selectAll(".radarWrapper") 83 | .data(dataset) 84 | 85 | var blobWrapperEnter = blobWrapper 86 | .enter() 87 | .append("g") 88 | .attr("class", "radarWrapper") 89 | 90 | blobWrapper.exit().remove() 91 | 92 | /* RADAR AREAS */ 93 | 94 | var radarAreas = blobWrapper.selectAll("path.radarArea") 95 | .data(function (d, i) { 96 | return [d] 97 | }) 98 | 99 | radarAreas 100 | .enter().append("path") 101 | .attr("class", function (d, i) { 102 | return "radarArea " + d[0].area 103 | }) 104 | .style("fill", function (d, i) { 105 | return categoryColor[d[0].area] 106 | }) 107 | .style("fill-opacity", cfg.opacityArea) 108 | .attr("d", function (d, i) { 109 | return radarLine(d) 110 | }) 111 | .on('mouseover', function (d, i) { 112 | d3.selectAll(".radarArea") 113 | .transition().duration(200) 114 | .style("fill-opacity", 0.1) 115 | 116 | d3.selectAll(".radarCircle") 117 | .transition().duration(200) 118 | .style("opacity", 0) 119 | 120 | d3.selectAll(".radarCircle." + d[0].area) 121 | .transition().duration(200) 122 | .style("opacity", 0.8) 123 | 124 | d3.select(this) 125 | .transition().duration(200) 126 | .style("fill-opacity", 0.7) 127 | }) 128 | .on('mouseout', function (d) { 129 | d3.selectAll(".radarArea") 130 | .transition().duration(200) 131 | .style("fill-opacity", cfg.opacityArea) 132 | 133 | d3.selectAll(".radarCircle") 134 | .transition().duration(200) 135 | .style("opacity", 0) 136 | }) 137 | 138 | radarAreas 139 | .transition() 140 | .duration(500) 141 | .attr("d", function (d, i) { 142 | return radarLine(d) 143 | }) 144 | 145 | radarAreas.exit().remove() 146 | 147 | var radarAreasEnter = blobWrapperEnter.selectAll("path.radarArea") 148 | .data(function (d, i) { 149 | return [d] 150 | }) 151 | 152 | radarAreasEnter 153 | .enter().append("path") 154 | .attr("class", function (d, i) { 155 | return "radarArea " + d[0].area 156 | }) 157 | .style("fill", function (d, i) { 158 | return categoryColor[d[0].area] 159 | }) 160 | .style("fill-opacity", cfg.opacityArea) 161 | .attr("d", function (d, i) { 162 | return radarLine(d) 163 | }) 164 | .on('mouseover', function (d, i) { 165 | d3.selectAll(".radarArea") 166 | .transition().duration(200) 167 | .style("fill-opacity", 0.1) 168 | 169 | d3.selectAll(".radarCircle") 170 | .transition().duration(200) 171 | .style("opacity", 0) 172 | 173 | d3.selectAll(".radarCircle." + d[0].area) 174 | .transition().duration(200) 175 | .style("opacity", 0.8) 176 | 177 | d3.select(this) 178 | .transition().duration(200) 179 | .style("fill-opacity", 0.7) 180 | }) 181 | .on('mouseout', function (d) { 182 | d3.selectAll(".radarArea") 183 | .transition().duration(200) 184 | .style("fill-opacity", cfg.opacityArea) 185 | 186 | d3.selectAll(".radarCircle") 187 | .transition().duration(200) 188 | .style("opacity", 0) 189 | }) 190 | 191 | radarAreasEnter 192 | .transition() 193 | .duration(500) 194 | .attr("d", function (d, i) { 195 | return radarLine(d) 196 | }) 197 | 198 | radarAreasEnter.exit().remove() 199 | 200 | /* RADAR CIRCLES */ 201 | 202 | var blobWrapperCircle = blobWrapper 203 | .selectAll(".radarCircle") 204 | .data(function (d, i) { 205 | return d 206 | }) 207 | .attr("cx", function (d, i) { 208 | return rScale(d.value) * Math.cos(angleSlice * i - Math.PI / 2) 209 | }) 210 | .attr("cy", function (d, i) { 211 | return rScale(d.value) * Math.sin(angleSlice * i - Math.PI / 2) 212 | }) 213 | 214 | blobWrapperCircle 215 | .enter() 216 | .append("circle") 217 | .attr("class", function (d, i) { 218 | return "radarCircle " + d.area 219 | }) 220 | .attr("r", cfg.dotRadius) 221 | .attr("cx", function (d, i) { 222 | return rScale(d.value) * Math.cos(angleSlice * i - Math.PI / 2) 223 | }) 224 | .attr("cy", function (d, i) { 225 | return rScale(d.value) * Math.sin(angleSlice * i - Math.PI / 2) 226 | }) 227 | .attr("stroke-width", 2) 228 | .attr("stroke", "#fff") 229 | .style("fill", function (d) { 230 | return categoryColor[d.area] 231 | }) 232 | .style("opacity", 0) 233 | .on("mouseover", function (d, i) { 234 | d3.selectAll(".radarArea") 235 | .transition().duration(200) 236 | .style("fill-opacity", 0.1) 237 | 238 | d3.select(".radarArea." + d.area) 239 | .transition().duration(200) 240 | .style("fill-opacity", 0.7) 241 | 242 | d3.select(this) 243 | .transition().duration(200) 244 | .style("opacity", 1) 245 | 246 | var newX = parseFloat(d3.select(this).attr('cx')) - 10 247 | var newY = parseFloat(d3.select(this).attr('cy')) - 10 248 | 249 | tooltip 250 | .attr('x', newX + 10) 251 | .attr('y', newY - 15) 252 | .text((Format(d.value)) + '$') 253 | .transition().duration(200) 254 | .style('opacity', 1) 255 | 256 | tooltipRect 257 | .attr('x', newX - 30) 258 | .attr('y', newY - 35) 259 | .transition().duration(200) 260 | .style('opacity', 1) 261 | }) 262 | .on("mouseout", function () { 263 | d3.selectAll(".radarArea") 264 | .transition().duration(200) 265 | .style("fill-opacity", 0.7) 266 | 267 | tooltip.transition().duration(200) 268 | .style("opacity", 0) 269 | 270 | tooltipRect.transition().duration(200) 271 | .style("opacity", 0) 272 | 273 | d3.selectAll('.radarCircle').style('opacity', 0) 274 | }) 275 | 276 | blobWrapperCircle.exit().remove() 277 | 278 | var blobWrapperCircleEnter = blobWrapperEnter 279 | .selectAll(".radarCircle") 280 | .data(function (d, i) { 281 | return d 282 | }) 283 | .attr("cx", function (d, i) { 284 | return rScale(d.value) * Math.cos(angleSlice * i - Math.PI / 2) 285 | }) 286 | .attr("cy", function (d, i) { 287 | return rScale(d.value) * Math.sin(angleSlice * i - Math.PI / 2) 288 | }) 289 | 290 | blobWrapperCircleEnter 291 | .enter() 292 | .append("circle") 293 | .attr("class", function (d, i) { 294 | return "radarCircle " + d.area 295 | }) 296 | .attr("r", cfg.dotRadius) 297 | .attr("cx", function (d, i) { 298 | return rScale(d.value) * Math.cos(angleSlice * i - Math.PI / 2) 299 | }) 300 | .attr("cy", function (d, i) { 301 | return rScale(d.value) * Math.sin(angleSlice * i - Math.PI / 2) 302 | }) 303 | .attr("stroke-width", 2) 304 | .attr("stroke", "#fff") 305 | .style("fill", function (d) { 306 | return categoryColor[d.area] 307 | }) 308 | .style("opacity", 0) 309 | .on("mouseover", function (d, i) { 310 | d3.selectAll(".radarArea") 311 | .transition().duration(200) 312 | .style("fill-opacity", 0.1) 313 | 314 | d3.select(".radarArea." + d.area) 315 | .transition().duration(200) 316 | .style("fill-opacity", 0.7) 317 | 318 | var newX = parseFloat(d3.select(this).attr('cx')) - 10 319 | var newY = parseFloat(d3.select(this).attr('cy')) - 10 320 | 321 | d3.select(this) 322 | .transition().duration(200) 323 | .style("opacity", 1) 324 | 325 | d3.select('.radarTooltip') 326 | .attr('x', newX + 10) 327 | .attr('y', newY - 15) 328 | .text((Format(d.value)) + '$') 329 | .transition().duration(200) 330 | .style('opacity', 1) 331 | 332 | d3.select('.radarTooltipBackground') 333 | .attr('x', newX - 30) 334 | .attr('y', newY - 35) 335 | .transition().duration(200) 336 | .style('opacity', 1) 337 | }) 338 | .on("mouseout", function () { 339 | console.log('mouseout circle') 340 | d3.selectAll(".radarArea") 341 | .transition().duration(200) 342 | .style("fill-opacity", 0.7) 343 | 344 | d3.select('.radarTooltip').transition().duration(200) 345 | .style("opacity", 0) 346 | 347 | d3.select('.radarTooltipBackground').transition().duration(200) 348 | .style("opacity", 0) 349 | 350 | d3.selectAll('.radarCircle').style('opacity', 0) 351 | }) 352 | 353 | blobWrapperCircleEnter.exit().remove() 354 | 355 | var tooltipRect = g.selectAll('.radarTooltipBackground') 356 | .data([0]) 357 | 358 | tooltipRect 359 | .enter() 360 | .append("rect") 361 | .attr("class", "radarTooltipBackground") 362 | .attr('width', 80) 363 | .attr('height', 30) 364 | .style('fill', '#464646') 365 | .style('pointer-events', 'none') 366 | .style("opacity", 0) 367 | 368 | var tooltip = g.selectAll('.radarTooltip') 369 | .data([0]) 370 | 371 | tooltip 372 | .enter() 373 | .append("text") 374 | .style("opacity", 0) 375 | .style("fill", '#ffffff') 376 | .style("font-size", '14px') 377 | .style("text-anchor", 'middle') 378 | .attr("class", "radarTooltip") 379 | .style("opacity", 0) 380 | } 381 | 382 | function drawAxis() { 383 | var axisGrid = g.append("g").attr("class", "axisWrapper") 384 | 385 | axisGrid.selectAll(".levels") 386 | .data(d3.range(1, (cfg.levels + 1)).reverse()) 387 | .enter() 388 | .append("circle") 389 | .attr("class", "gridCircle") 390 | .attr("r", function (d, i) { 391 | return radius / cfg.levels * d 392 | }) 393 | .style("fill", "#5F92CD") 394 | .style("stroke", "#5F92CD") 395 | .style("fill-opacity", 0) 396 | 397 | var axis = axisGrid.selectAll(".axis") 398 | .data(allAxis) 399 | .enter() 400 | .append("g") 401 | .attr("class", "axis") 402 | 403 | axis.append("line") 404 | .attr("x1", 0) 405 | .attr("y1", 0) 406 | .attr("x2", function (d, i) { 407 | return rScale(maxValue * 1) * Math.cos(angleSlice * i - Math.PI / 2) 408 | }) 409 | .attr("y2", function (d, i) { 410 | return rScale(maxValue * 1) * Math.sin(angleSlice * i - Math.PI / 2) 411 | }) 412 | .attr("class", "line") 413 | .style("stroke", "#5F92CD") 414 | .style("stroke-width", "1px") 415 | 416 | axis.append("text") 417 | .attr("class", "gridLegend") 418 | .style("font-size", "14px") 419 | .attr("text-anchor", "middle") 420 | .attr("dy", "0.35em") 421 | .attr("fill", "#5F92CD") 422 | .attr("x", function (d, i) { 423 | return rScale(maxValue * cfg.labelFactor) * Math.cos(angleSlice * i - Math.PI / 2) 424 | }) 425 | .attr("y", function (d, i) { 426 | return rScale(maxValue * cfg.labelFactor) * Math.sin(angleSlice * i - Math.PI / 2) - ((i === 2) ? 13 : 0) 427 | }) 428 | .text(function (d) { 429 | return d 430 | }) 431 | .call(wrap, cfg.wrapWidth) 432 | } 433 | 434 | function wrap(text, width) { 435 | text.each(function () { 436 | var text = d3.select(this) 437 | var words = text.text().split(/\s+/).reverse() 438 | var word 439 | var line = [] 440 | var lineNumber = 0 441 | var lineHeight = 1 442 | var y = text.attr("y") 443 | var x = text.attr("x") 444 | var dy = parseFloat(text.attr("dy")) 445 | var tspan = text.text(null).append("tspan").attr("x", x).attr("y", y).attr("dy", dy + "em") 446 | 447 | while (word = words.pop()) { 448 | line.push(word) 449 | tspan.text(line.join(" ")) 450 | if (tspan.node().getComputedTextLength() > width) { 451 | line.pop() 452 | tspan.text(line.join(" ")) 453 | line = [word] 454 | tspan = text.append("tspan").attr("x", x).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word) 455 | } 456 | } 457 | }) 458 | } 459 | 460 | this.updateData(data) 461 | } 462 | -------------------------------------------------------------------------------- /examples/js/scatterplot-controller.js: -------------------------------------------------------------------------------- 1 | /* global d3 ChartComponent Scatterplot */ 2 | 3 | (function () { 4 | /* CHART 1 */ 5 | var elContainer = d3.select('.svg-container') 6 | var component = new ChartComponent() 7 | var chart = new Scatterplot(elContainer, component, 'svg1') 8 | var scatterplotDataset 9 | 10 | d3.select('.search').on("input", function () { 11 | chart.updateData(scatterplotDataset, false, false, true, false, this.value) 12 | }) 13 | 14 | d3.select('.erase').on("click", function () { 15 | chart.updateData(scatterplotDataset, false, false, false, false, null) 16 | document.getElementById('searchField').value = '' 17 | }) 18 | 19 | d3.selectAll('.menuEl').on("click", function (d, i) { 20 | d3.selectAll('.menuEl').classed('active', false) 21 | d3.select(this).classed('active', true) 22 | 23 | if (i === 2) { 24 | d3.select('.search-container').style('opacity', 1) 25 | chart.updateData(scatterplotDataset, true, false, true, false, null) 26 | } else { 27 | d3.select('.search-container').style('opacity', 0) 28 | i === 0 ? chart.updateData(scatterplotDataset, true, false, false, false) : chart.updateData(scatterplotDataset, false, true, false, false) 29 | } 30 | d3.selectAll('.labelEl').classed('visible', false) 31 | d3.select('.label-' + (i + 1)).classed('visible', true) 32 | 33 | d3.selectAll('.gif-container > div').style('display', 'none') 34 | if (i === 0) { 35 | d3.select('.move-gif').style('display', 'block') 36 | d3.select('.move-gif').style('visibility', 'initial') 37 | } else if (i === 1) { 38 | d3.select('.frame-gif').style('display', 'block') 39 | d3.select('.frame-gif').style('visibility', 'initial') 40 | } else if (i === 2) { 41 | d3.select('.find-gif').style('display', 'block') 42 | d3.select('.find-gif').style('visibility', 'initial') 43 | } 44 | }) 45 | 46 | /* CHART 2 */ 47 | var elContainer2 = d3.select('.svg-container2') 48 | var component2 = new ChartComponent() 49 | var chart2 = new Scatterplot(elContainer2, component2, 'svg2') 50 | 51 | var filter1 = 0 52 | var filter2 = 500 53 | 54 | if (window.innerWidth <= 800) { 55 | d3.select('.move-gif').style('display', 'block') 56 | d3.select('.move2-gif').style('display', 'block') 57 | } 58 | 59 | d3.selectAll('.menuEl2').on("click", function (d, i) { 60 | d3.selectAll('.menuEl2').classed('active', false) 61 | d3.select(this).classed('active', true) 62 | d3.selectAll('.labelEl2').style('opacity', 0) 63 | d3.select('.labelEl2.label-' + (i + 1)).style('opacity', 1) 64 | 65 | if (i === 1) { 66 | d3.select('.slider').style('opacity', 1) 67 | } else { 68 | d3.select('.slider').style('opacity', 0) 69 | } 70 | /* dataset, move, zoom, search, hover, company */ 71 | if (i === 2) { 72 | chart2.updateData(scatterplotDataset, false, false, false, true) 73 | } else if (i === 1) { 74 | chart2.updateData(scatterplotDataset, false, false, false, false) 75 | } else { 76 | chart2.updateData(scatterplotDataset, true, false, false, false) 77 | } 78 | 79 | if (i === 0 || i === 2) { 80 | newMinCoex = 0 81 | newMaxCoex = 500 82 | group.selectAll('#sliderXEnd') 83 | .attr('cx', immutableScaleX(newMaxCoex)) 84 | group.selectAll('#sliderXStart') 85 | .attr('cx', immutableScaleX(newMinCoex)) 86 | 87 | group.selectAll('#sliderXLine') 88 | .attr('x1', immutableScaleX(0)) 89 | .attr('x2', immutableScaleX(500)) 90 | 91 | group.selectAll('text.sliderValue') 92 | .text(function (d, i) { 93 | return i === 0 ? 0 : 500 94 | }) 95 | } 96 | 97 | d3.selectAll('.gif-container2 > div').style('display', 'none') 98 | if (i === 0) { 99 | d3.select('.move2-gif').style('display', 'block') 100 | d3.select('.move2-gif').style('visibility', 'initial') 101 | } else if (i === 1) { 102 | d3.select('.filter-gif').style('display', 'block') 103 | d3.select('.filter-gif').style('visibility', 'initial') 104 | } else if (i === 2) { 105 | d3.select('.details-gif').style('display', 'block') 106 | d3.select('.details-gif').style('visibility', 'initial') 107 | } 108 | }) 109 | 110 | var newMinCoex = 0 111 | var newMaxCoex = 500 112 | 113 | var immutableScaleX = d3.scaleLinear() 114 | .domain([0, 500]) 115 | .range([20, 200]) 116 | 117 | var scaleX = d3.scaleLinear() 118 | .domain([0, 500]) 119 | .range([20, 200]) 120 | 121 | var svg = d3.select('.slider').append('svg') 122 | .attr('width', 265) 123 | .attr('height', 42) 124 | 125 | var group = svg.append('g') 126 | .attr('class', 'group') 127 | .attr('transform', 'translate(21,21)') 128 | 129 | group 130 | .append('line') 131 | .attr('x1', scaleX(0)) 132 | .attr('x2', scaleX(500)) 133 | .attr('y1', 0) 134 | .attr('y2', 0) 135 | .attr('stroke', '#706F6F') 136 | 137 | var text = group.selectAll('text.sliderValue') 138 | .data([newMinCoex, newMaxCoex]) 139 | .attr('dx', function (d, i) { 140 | return (i === 0) ? 12 : 210 141 | }) 142 | .style('text-anchor', function (d, i) { 143 | return (i === 0) ? 'end' : 'start' 144 | }) 145 | 146 | text 147 | .enter() 148 | .append('text') 149 | .classed('sliderValue', true) 150 | .attr('dx', function (d, i) { 151 | return (i === 0) ? 12 : 210 152 | }) 153 | .attr('dy', 5) 154 | .text(function (d, i) { 155 | return Math.abs(parseInt(d)) 156 | }) 157 | .attr('font-size', '14px') 158 | .attr('fill', '#e0e0e0') 159 | .style('text-anchor', function (d, i) { 160 | return (i === 0) ? 'end' : 'start' 161 | }) 162 | 163 | text.exit().remove() 164 | 165 | var sliderXLine = group.selectAll('#sliderXLine') 166 | .data([0]) 167 | .attr('x1', immutableScaleX(0)) 168 | .attr('x2', immutableScaleX(500)) 169 | 170 | sliderXLine 171 | .enter() 172 | .append('line') 173 | .attr('id', 'sliderXLine') 174 | .attr('x1', immutableScaleX(0) + 5) 175 | .attr('y1', 0) 176 | .attr('x2', immutableScaleX(500) - 5) 177 | .attr('y2', 0) 178 | .attr('stroke-width', 10) 179 | .style('opacity', 0.6) 180 | .attr('stroke-linecap', 'round') 181 | .attr('stroke', '#ff5050') 182 | 183 | sliderXLine.exit().remove() 184 | 185 | var sliderXEnd = group.selectAll('#sliderXEnd') 186 | .data([0]) 187 | .attr('cx', immutableScaleX(500)) 188 | 189 | sliderXEnd 190 | .enter() 191 | .append('circle') 192 | .attr('id', 'sliderXEnd') 193 | .attr('cx', immutableScaleX(500)) 194 | .attr('cy', 0) 195 | .attr('r', 5) 196 | .style('opacity', 1) 197 | .attr('fill', '#ff5050') 198 | .style('cursor', 'ew-resize') 199 | .call(d3.drag() 200 | .on("drag", function () { 201 | draggedX('end') 202 | }) 203 | ) 204 | 205 | sliderXEnd.exit().remove() 206 | 207 | var sliderXStart = group.selectAll('#sliderXStart') 208 | .data([0]) 209 | .attr('cx', immutableScaleX(0)) 210 | 211 | sliderXStart 212 | .enter() 213 | .append('circle') 214 | .attr('id', 'sliderXStart') 215 | .attr('cx', immutableScaleX(0)) 216 | .attr('cy', 0) 217 | .attr('r', 5) 218 | .style('opacity', 1) 219 | .attr('fill', '#ff5050') 220 | .style('cursor', 'ew-resize') 221 | .call(d3.drag() 222 | .on("drag", function () { 223 | draggedX('start') 224 | }) 225 | ) 226 | 227 | sliderXStart.exit().remove() 228 | 229 | function draggedX(type) { 230 | var initialvalue = scaleX(newMinCoex) 231 | if (type === 'start') { 232 | newMinCoex += immutableScaleX.invert(initialvalue - (d3.event.dx * -1)) 233 | if (newMinCoex > 500) { 234 | newMinCoex = 500 235 | } 236 | if (newMinCoex < 0) { 237 | newMinCoex = 0 238 | } 239 | if ((newMaxCoex - newMinCoex) < 1) { 240 | newMinCoex = newMaxCoex - 1 241 | } 242 | } else { 243 | newMaxCoex -= immutableScaleX.invert(initialvalue - (d3.event.dx)) 244 | 245 | if (newMaxCoex < 0) { 246 | newMaxCoex = 0 247 | } 248 | if (newMaxCoex > 500) { 249 | newMaxCoex = 500 250 | } 251 | if ((newMaxCoex - newMinCoex) < 1) { 252 | newMaxCoex = newMinCoex + 1 253 | } 254 | } 255 | scaleX.domain([newMinCoex, newMaxCoex]) 256 | if (type === 'end') { 257 | group.selectAll('#sliderXEnd') 258 | .attr('cx', immutableScaleX(newMaxCoex)) 259 | } else { 260 | group.selectAll('#sliderXStart') 261 | .attr('cx', immutableScaleX(newMinCoex)) 262 | } 263 | 264 | group.selectAll('#sliderXLine') 265 | .attr('x1', immutableScaleX(newMinCoex)) 266 | .attr('x2', immutableScaleX(newMaxCoex)) 267 | 268 | filter1 = newMinCoex 269 | filter2 = newMaxCoex 270 | 271 | var filteredDataset = scatterplotDataset.filter(function (data) { 272 | return data.radius > filter1 && data.radius < filter2 273 | }) 274 | 275 | group.selectAll('text.sliderValue') 276 | .data([newMinCoex, newMaxCoex]) 277 | .attr('dx', function (d, i) { 278 | return (i === 0) ? 12 : 210 279 | }) 280 | .text(function (d, i) { 281 | return Math.abs(parseInt(d)) 282 | }) 283 | 284 | chart2.updateData(filteredDataset, false, false) 285 | } 286 | 287 | /* DATA LOAD */ 288 | 289 | d3.json('../json/scatterplot-dataset.json', function (dataset) { 290 | scatterplotDataset = dataset 291 | chart.updateData(scatterplotDataset, true, false, false, false) // (dataset, move, zoom, search, hover, company) 292 | chart2.updateData(scatterplotDataset, true, false, false, false) 293 | }) 294 | })() 295 | -------------------------------------------------------------------------------- /examples/js/scatterplot.js: -------------------------------------------------------------------------------- 1 | /* global d3 */ 2 | function Scatterplot(el, component, svgClass) { 3 | var width = el.node().getBoundingClientRect().width 4 | var height = width / 1.8 5 | var horizontalMargin = 50 6 | var verticalMargin = 50 7 | var data 8 | var minCoex = 0 9 | var maxCoex = 100 10 | var minGrowth = 0 11 | var maxGrowth = 100 12 | var zoomEnabled = false 13 | var moveEnabled = true 14 | var searchEnabled = false 15 | var hoverEnabled = false 16 | var selectedCompany 17 | var xAxis, yAxis, gX, gY 18 | 19 | var newMinCoex = minCoex 20 | var newMaxCoex = maxCoex 21 | var newMinGrowth = minGrowth 22 | var newMaxGrowth = maxGrowth 23 | 24 | var scaleX = d3.scaleLinear() 25 | .domain([minCoex, maxCoex]) 26 | .range([10 + horizontalMargin, width]) 27 | 28 | var immutableScaleX = d3.scaleLinear() 29 | .domain([minCoex, maxCoex]) 30 | .range([10 + horizontalMargin, width]) 31 | 32 | var scaleY = d3.scaleLinear() 33 | .domain([minGrowth, maxGrowth]) 34 | .range([height - (verticalMargin), 0]) 35 | 36 | var immutableScaleY = d3.scaleLinear() 37 | .domain([minGrowth, maxGrowth]) 38 | .range([height - (verticalMargin), 0]) 39 | 40 | var svg = el.append('svg') 41 | .attr('width', width) 42 | .attr('height', height + 5) 43 | .attr('class', svgClass) 44 | 45 | svg.append('defs') 46 | .append('marker') 47 | .attr('id', 'marker-circle') 48 | .attr('markerWidth', 4) 49 | .attr('markerHeight', 4) 50 | .attr('markerUnits', 'strokeWidth') 51 | .attr('refX', 2) 52 | .attr('refY', 2) 53 | .attr('orient', 'auto') 54 | .append('circle') 55 | .attr('cx', 2) 56 | .attr('cy', 2) 57 | .attr('r', 2) 58 | 59 | var background = svg.append('rect') 60 | .attr('class', 'background') 61 | .style('fill', '#F6F6F6') 62 | .attr('width', width - (horizontalMargin)) 63 | .attr('height', height - (verticalMargin) + 5) 64 | .attr('transform', 'translate(' + (horizontalMargin + 3) + ',' + 0 + ')') 65 | 66 | var globalG = svg.append('g') 67 | .attr('class', 'globalG') 68 | .attr('transform', function () { 69 | return 'translate(' + 0 + ',' + 0 + ')' 70 | }) 71 | 72 | var maskLeft = svg.append('rect') 73 | .style('fill', '#ffffff') 74 | .attr('width', horizontalMargin + 5) 75 | .attr('height', height) 76 | .attr('transform', 'translate(' + 0 + ',' + -18 + ')') 77 | 78 | var maskBottom = svg.append('rect') 79 | .style('fill', '#ffffff') 80 | .attr('width', width) 81 | .attr('height', verticalMargin + 10) 82 | .attr('transform', 'translate(' + 0 + ',' + (height - verticalMargin + 5) + ')') 83 | 84 | createAxis() 85 | 86 | var dataG = globalG.append('g') 87 | .attr('class', 'dataG') 88 | 89 | var legendG = svg.append('g') 90 | .attr('class', 'legendG') 91 | 92 | var zoom = d3.zoom() 93 | .scaleExtent([1, 6]) 94 | .translateExtent([[-100, -100], [width + 90, height + 100]]) 95 | .on("zoom", zoomed) 96 | 97 | svg.call(zoom) 98 | 99 | function removeDragBars() { 100 | svg.select('#draggableXStart').remove() 101 | svg.select('#draggableXEnd').remove() 102 | svg.select('#draggableXLine').remove() 103 | svg.select('#draggableYStart').remove() 104 | svg.select('#draggableYEnd').remove() 105 | svg.select('#draggableYLine').remove() 106 | svg.selectAll('.axis-line').remove() 107 | } 108 | 109 | function zoomed() { 110 | if (moveEnabled) { 111 | d3.select('.' + svgClass + ' .globalG').attr("transform", d3.event.transform) 112 | gX.call(xAxis.scale(d3.event.transform.rescaleX(scaleX))) 113 | gY.call(yAxis.scale(d3.event.transform.rescaleY(scaleY))) 114 | 115 | svg.selectAll('.axis--x text') 116 | .attr('transform', 'translate(' + 0 + ',' + (height + 30) + ')') 117 | } 118 | } 119 | 120 | function resetted() { 121 | svg 122 | .transition() 123 | .duration(750) 124 | .call(zoom.transform, d3.zoomIdentity) 125 | .on('end', function () { 126 | moveEnabled = false 127 | }) 128 | } 129 | 130 | function resetDragBars() { 131 | scaleX.domain([minCoex, maxCoex]) 132 | scaleY.domain([minGrowth, maxGrowth]) 133 | newMinCoex = minCoex 134 | newMaxCoex = maxCoex 135 | newMinGrowth = minGrowth 136 | newMaxGrowth = maxGrowth 137 | } 138 | 139 | function createAxis() { 140 | xAxis = d3.axisBottom(scaleX) 141 | .ticks(4) 142 | .tickSize(height - verticalMargin + 5) 143 | .tickPadding(8 - height) 144 | 145 | yAxis = d3.axisRight(scaleY) 146 | .ticks(4) 147 | .tickSize(width - horizontalMargin) 148 | .tickPadding(8 - width) 149 | 150 | gX = svg.append("g") 151 | .attr("class", "axis axis--x") 152 | .call(xAxis) 153 | 154 | gY = svg.append("g") 155 | .attr("class", "axis axis--y") 156 | .attr('transform', 'translate(' + (horizontalMargin + 10) + ',' + 5 + ')') 157 | .call(yAxis) 158 | 159 | svg.selectAll('.axis--x text') 160 | .attr('transform', 'translate(' + 0 + ',' + (height + 32) + ')') 161 | } 162 | 163 | this.updateData = function (dataset, move, zoom, search, hover, company) { 164 | if (dataset !== undefined) { 165 | data = dataset 166 | } 167 | 168 | if (move !== undefined) { 169 | if (!move) { 170 | resetted() 171 | } else { 172 | moveEnabled = move 173 | resetDragBars() 174 | removeDragBars() 175 | } 176 | } 177 | 178 | if (zoom !== undefined) { 179 | zoomEnabled = zoom 180 | } 181 | if (hover !== undefined) { 182 | hoverEnabled = hover 183 | } 184 | 185 | if (search !== undefined) { 186 | if (!search) { 187 | component.company('') 188 | svg.selectAll('.tooltip').remove() 189 | svg.selectAll('.tooltip-text').remove() 190 | svg.selectAll('.' + svgClass + ' g.data') 191 | .style('opacity', 1) 192 | } else { 193 | resetted() 194 | if (company) { 195 | selectedCompany = company 196 | component.company(selectedCompany) 197 | 198 | if (svg.select('g.data[company="' + (stringNoSpaces(selectedCompany)) + '"]').node()) { 199 | svg.selectAll('g.data') 200 | .style('opacity', 0.3) 201 | svg.selectAll('g.data[company="' + (stringNoSpaces(selectedCompany)) + '"]') 202 | .style('opacity', 1) 203 | 204 | var x = svg.select('g.data[company="' + (stringNoSpaces(selectedCompany)) + '"]').node().__data__.x 205 | var y = svg.select('g.data[company="' + (stringNoSpaces(selectedCompany)) + '"]').node().__data__.y 206 | 207 | svg 208 | .append('rect') 209 | .attr('class', 'tooltip') 210 | .style('fill', '#464646') 211 | .style('opacity', 1) 212 | .style('display', 'block') 213 | .attr('x', scaleX(x) - 50) 214 | .attr('y', scaleY(y) - 50) 215 | .attr('width', 100) 216 | .attr('height', 30) 217 | 218 | svg 219 | .append('text') 220 | .attr('class', 'tooltip-text') 221 | .style('opacity', 1) 222 | .style('font-size', '14px') 223 | .attr('dx', scaleX(x)) 224 | .attr('dy', scaleY(y) - 30) 225 | .text(function () { 226 | return selectedCompany 227 | }) 228 | } else { 229 | svg.selectAll('.tooltip').remove() 230 | svg.selectAll('.tooltip-text').remove() 231 | svg.selectAll('.' + svgClass + ' g.data') 232 | .style('opacity', 1) 233 | } 234 | } 235 | } 236 | searchEnabled = search 237 | } 238 | 239 | var maxTurnover = parseFloat(d3.max(data, function (d) { 240 | return parseFloat(d.radius) 241 | })) 242 | component.maxTurnover(maxTurnover) 243 | 244 | var g = dataG.selectAll('g.data') 245 | .data(data, function (d) { 246 | return d.class 247 | }) 248 | .attr('transform', function (d) { 249 | var x = scaleX(d.x) 250 | var y 251 | if (isNaN(d.y)) { 252 | y = scaleY(0) 253 | } else { 254 | y = scaleY(d.y) 255 | } 256 | return 'translate(' + x + ',' + y + ')' 257 | }) 258 | .call(component) 259 | 260 | g.enter() 261 | .append('g') 262 | .classed('data', true) 263 | .attr('company', function (d) { 264 | return getSlug(d.class) 265 | }) 266 | .attr('transform', function (d) { 267 | var x = scaleX(d.x) 268 | var y 269 | if (isNaN(d.y)) { 270 | y = scaleY(0) 271 | } else { 272 | y = scaleY(d.y) 273 | } 274 | return 'translate(' + x + ',' + y + ')' 275 | }) 276 | .style('opacity', 1) 277 | .call(component) 278 | 279 | g.exit().remove() 280 | 281 | svg.selectAll('.turnover-background') 282 | .on('mouseenter', function () { 283 | if (hoverEnabled) { 284 | over(this) 285 | } 286 | }) 287 | .on('mouseleave', function () { 288 | if (hoverEnabled) { 289 | out(this) 290 | } 291 | }) 292 | 293 | if (zoomEnabled) { 294 | setupDrag(gX, gY) 295 | } 296 | if (searchEnabled) { 297 | removeDragBars() 298 | } 299 | } 300 | 301 | function getSlug(txt) { 302 | return txt.toLowerCase().replace(/ /g, '') 303 | } 304 | 305 | function setupDrag(gX, gY) { 306 | var draggableXLine = svg.selectAll('#draggableXLine') 307 | .data([0]) 308 | .attr('x1', immutableScaleX(newMaxCoex) - 2) 309 | .attr('x2', immutableScaleX(newMinCoex) + 5) 310 | 311 | svg 312 | .append('line') 313 | .classed('axis-line', true) 314 | .attr('x1', immutableScaleX(newMaxCoex) - 2) 315 | .attr('x2', immutableScaleX(newMinCoex) + 5) 316 | .attr('transform', 'translate(' + (-5) + ',' + (height - 30) + ')') 317 | .attr('y1', 0) 318 | .attr('y2', 0) 319 | .attr('stroke', '#c7c7c7') 320 | 321 | draggableXLine 322 | .enter() 323 | .append('line') 324 | .attr('id', 'draggableXLine') 325 | .attr('x1', immutableScaleX(newMaxCoex) - 2) 326 | .attr('y1', 0) 327 | .attr('x2', immutableScaleX(newMinCoex) + 5) 328 | .attr('y2', 0) 329 | .attr('stroke-width', 10) 330 | .style('opacity', 0.6) 331 | .attr('transform', 'translate(' + (-5) + ',' + (height - 30) + ')') 332 | .attr('stroke-linecap', 'round') 333 | .attr('stroke', '#ff5050') 334 | .style('cursor', 'move') 335 | .call( 336 | d3.drag() 337 | .on("drag", function () { 338 | draggedXLine() 339 | }) 340 | ) 341 | 342 | draggableXLine.exit().remove() 343 | 344 | var draggableXEnd = svg.selectAll('#draggableXEnd') 345 | .data([0]) 346 | .attr('cx', immutableScaleX(newMinCoex) + 5) 347 | 348 | draggableXEnd 349 | .enter() 350 | .append('circle') 351 | .attr('id', 'draggableXEnd') 352 | .attr('cx', immutableScaleX(newMinCoex) + 5) 353 | .attr('cy', 0) 354 | .attr('r', 5) 355 | .style('opacity', 1) 356 | .attr('transform', 'translate(' + (-5) + ',' + (height - 30) + ')') 357 | .attr('fill', '#ff5050') 358 | .style('cursor', 'ew-resize') 359 | .call( 360 | d3.drag() 361 | .on("drag", function () { 362 | draggedX('end') 363 | }) 364 | ) 365 | 366 | draggableXEnd.exit().remove() 367 | 368 | var draggableXStart = svg.selectAll('#draggableXStart') 369 | .data([0]) 370 | .attr('cx', immutableScaleX(newMaxCoex) - 2) 371 | 372 | draggableXStart 373 | .enter() 374 | .append('circle') 375 | .attr('id', 'draggableXStart') 376 | .attr('cx', immutableScaleX(newMaxCoex) - 2) 377 | .attr('cy', 0) 378 | .attr('r', 5) 379 | .style('opacity', 1) 380 | .attr('transform', 'translate(' + (-5) + ',' + (height - 30) + ')') 381 | .attr('fill', '#ff5050') 382 | .style('cursor', 'ew-resize') 383 | .call(d3.drag() 384 | .on("drag", function () { 385 | draggedX('start') 386 | }) 387 | ) 388 | 389 | draggableXStart.exit().remove() 390 | 391 | var draggableYLine = svg.selectAll('#draggableYLine') 392 | .data([0]) 393 | .attr('y1', immutableScaleY(newMinGrowth) - 8) 394 | .attr('y2', immutableScaleY(newMaxGrowth) + 1) 395 | 396 | svg 397 | .append('line') 398 | .classed('axis-line', true) 399 | .attr('x1', 0) 400 | .attr('x2', 0) 401 | .attr('transform', 'translate(' + 40 + ',' + 5 + ')') 402 | .attr('y1', immutableScaleY(newMinGrowth) - 8) 403 | .attr('y2', immutableScaleY(newMaxGrowth) + 1) 404 | .attr('stroke', '#706F6F') 405 | 406 | draggableYLine 407 | .enter() 408 | .append('line') 409 | .attr('id', 'draggableYLine') 410 | .attr('x1', 0) 411 | .attr('y1', immutableScaleY(newMinGrowth) - 8) 412 | .attr('x2', 0) 413 | .attr('y2', immutableScaleY(newMaxGrowth) + 1) 414 | .attr('stroke-width', 10) 415 | .style('opacity', 0.6) 416 | .attr('stroke-linecap', 'round') 417 | .attr('stroke', '#ff5050') 418 | .attr('transform', 'translate(' + 40 + ',' + 5 + ')') 419 | .style('cursor', 'move') 420 | .call( 421 | d3.drag() 422 | .on("drag", function () { 423 | draggedYLine() 424 | }) 425 | ) 426 | 427 | draggableYLine.exit().remove() 428 | 429 | var draggableYEnd = svg.selectAll('#draggableYEnd') 430 | .data([0]) 431 | .attr('cy', immutableScaleY(newMaxGrowth) + 1) 432 | 433 | draggableYEnd 434 | .enter() 435 | .append('circle') 436 | .attr('id', 'draggableYEnd') 437 | .attr('cx', 0) 438 | .attr('cy', immutableScaleY(newMaxGrowth) + 1) 439 | .attr('r', 5) 440 | .style('opacity', 1) 441 | .attr('fill', '#ff5050') 442 | .style('cursor', 'ns-resize') 443 | .attr('transform', 'translate(' + 40 + ',' + 5 + ')') 444 | .call( 445 | d3.drag() 446 | .on("drag", function () { 447 | draggedY('end') 448 | }) 449 | ) 450 | 451 | draggableYEnd.exit().remove() 452 | 453 | var draggableYStart = svg.selectAll('#draggableYStart') 454 | .data([0]) 455 | .attr('cy', immutableScaleY(newMinGrowth) - 8) 456 | 457 | draggableYStart 458 | .enter() 459 | .append('circle') 460 | .attr('id', 'draggableYStart') 461 | .attr('cx', 0) 462 | .attr('cy', immutableScaleY(newMinGrowth) - 8) 463 | .attr('r', 5) 464 | .attr('fill', '#ff5050') 465 | .attr('transform', 'translate(' + 40 + ',' + 5 + ')') 466 | .style('opacity', 1) 467 | .style('cursor', 'ns-resize') 468 | .call( 469 | d3.drag() 470 | .on("drag", function () { 471 | draggedY('start') 472 | }) 473 | ) 474 | 475 | draggableYStart.exit().remove() 476 | } 477 | 478 | function draggedY(type) { 479 | var initialvalue = immutableScaleY(0) 480 | if (type === 'start') { 481 | newMinGrowth += immutableScaleY.invert(initialvalue + (d3.event.dy)) 482 | if (newMinGrowth < minGrowth) { 483 | newMinGrowth = minGrowth 484 | } 485 | if (newMinGrowth > maxGrowth) { 486 | newMinGrowth = maxGrowth 487 | } 488 | if ((newMaxGrowth - newMinGrowth) < 1) { 489 | newMinGrowth = newMaxGrowth - 1 490 | } 491 | } else { 492 | newMaxGrowth -= immutableScaleY.invert(initialvalue - (d3.event.dy)) 493 | 494 | if (newMaxGrowth < minGrowth) { 495 | newMaxGrowth = minGrowth 496 | } 497 | if (newMaxGrowth > maxGrowth) { 498 | newMaxGrowth = maxGrowth 499 | } 500 | if ((newMaxGrowth - newMinGrowth) < 1) { 501 | newMaxGrowth = newMinGrowth + 1 502 | } 503 | } 504 | gY.call(yAxis.scale(scaleY)) 505 | scaleY.domain([newMinGrowth, newMaxGrowth]) 506 | svg.selectAll('g.data').attr('transform', function (d) { 507 | var x = scaleX(d.x) 508 | var y 509 | if (isNaN(d.y)) { 510 | y = scaleY(0) 511 | } else { 512 | y = scaleY(d.y) 513 | } 514 | return 'translate(' + x + ',' + y + ')' 515 | }) 516 | svg.selectAll('#draggableYStart') 517 | .attr('cy', immutableScaleY(newMinGrowth) - 8) 518 | svg.selectAll('#draggableYEnd') 519 | .attr('cy', immutableScaleY(newMaxGrowth) + 1) 520 | svg.selectAll('#draggableYLine') 521 | .attr('y1', immutableScaleY(newMaxGrowth) + 1) 522 | .attr('y2', immutableScaleY(newMinGrowth) - 8) 523 | 524 | d3.event.sourceEvent.stopPropagation() 525 | } 526 | 527 | function draggedX(type) { 528 | var initialvalue = scaleX(newMinCoex) 529 | if (type === 'end') { 530 | newMinCoex += immutableScaleX.invert(initialvalue - (d3.event.dx * -1)) 531 | if (newMinCoex > 100) { 532 | newMinCoex = 100 533 | } 534 | if (newMinCoex < 0) { 535 | newMinCoex = 0 536 | } 537 | if ((newMaxCoex - newMinCoex) < 1) { 538 | newMinCoex = newMaxCoex - 1 539 | } 540 | } else { 541 | newMaxCoex -= immutableScaleX.invert(initialvalue - (d3.event.dx)) 542 | 543 | if (newMaxCoex < 0) { 544 | newMaxCoex = 0 545 | } 546 | if (newMaxCoex > 100) { 547 | newMaxCoex = 100 548 | } 549 | if ((newMaxCoex - newMinCoex) < 1) { 550 | newMaxCoex = newMinCoex + 1 551 | } 552 | } 553 | gX.call(xAxis.scale(scaleX)) 554 | scaleX.domain([newMinCoex, newMaxCoex]) 555 | svg.selectAll('g.data') 556 | .attr('transform', function (d) { 557 | var x = scaleX(d.x) 558 | var y 559 | if (isNaN(d.y)) { 560 | y = scaleY(0) 561 | } else { 562 | y = scaleY(d.y) 563 | } 564 | return 'translate(' + x + ',' + y + ')' 565 | }) 566 | svg.selectAll('#draggableXEnd') 567 | .attr('cx', immutableScaleX(newMinCoex) + 5) 568 | svg.selectAll('#draggableXStart') 569 | .attr('cx', immutableScaleX(newMaxCoex) - 2) 570 | svg.selectAll('#draggableXLine') 571 | .attr('x1', immutableScaleX(newMaxCoex) - 2) 572 | .attr('x2', immutableScaleX(newMinCoex) + 5) 573 | svg.selectAll('.axis--x text') 574 | .attr('transform', 'translate(' + 0 + ',' + (height + 30) + ')') 575 | 576 | d3.event.sourceEvent.stopPropagation() 577 | } 578 | 579 | function draggedYLine() { 580 | var initialvalue = immutableScaleY(0) 581 | 582 | if (newMaxGrowth < maxGrowth) { 583 | newMinGrowth += immutableScaleY.invert(initialvalue + (d3.event.dy)) 584 | if (newMinGrowth < minGrowth) { 585 | newMinGrowth = minGrowth 586 | } 587 | } 588 | 589 | if (newMinGrowth > minGrowth) { 590 | newMaxGrowth -= immutableScaleY.invert(initialvalue - (d3.event.dy)) 591 | if (newMaxGrowth > maxGrowth) { 592 | newMaxGrowth = maxGrowth 593 | } 594 | } 595 | 596 | scaleY.domain([newMinGrowth, newMaxGrowth]) 597 | 598 | gY.call(yAxis.scale(scaleY)) 599 | 600 | svg.selectAll('text.growthAxisText') 601 | .data([newMaxGrowth, newMinGrowth]) 602 | .text(function (d) { 603 | return parseInt(d) 604 | }) 605 | 606 | svg.selectAll('g.data').attr('transform', function (d) { 607 | var x = scaleX(d.x) 608 | var y 609 | if (isNaN(d.y)) { 610 | y = scaleY(0) 611 | } else { 612 | y = scaleY(d.y) 613 | } 614 | if (d.y > 100) { 615 | y = scaleY(100) - 20 616 | } 617 | return 'translate(' + x + ',' + y + ')' 618 | }) 619 | svg.selectAll('#draggableYStart') 620 | .attr('cy', immutableScaleY(newMinGrowth) - 8) 621 | svg.selectAll('#draggableYEnd') 622 | .attr('cy', immutableScaleY(newMaxGrowth) + 1) 623 | svg.selectAll('#draggableYLine') 624 | .attr('y1', immutableScaleY(newMinGrowth) - 8) 625 | .attr('y2', immutableScaleY(newMaxGrowth) + 1) 626 | d3.event.sourceEvent.stopPropagation() 627 | } 628 | 629 | function draggedXLine() { 630 | var initialvalue = scaleX(newMinCoex) 631 | 632 | if (newMaxCoex < 100) { 633 | newMinCoex += immutableScaleX.invert(initialvalue - (d3.event.dx * -1)) 634 | if (newMinCoex < 0) { 635 | newMinCoex = 0 636 | } 637 | } 638 | 639 | if (newMinCoex > 0) { 640 | newMaxCoex -= immutableScaleX.invert(initialvalue - (d3.event.dx)) 641 | if (newMaxCoex > 100) { 642 | newMaxCoex = 100 643 | } 644 | } 645 | 646 | gX.call(xAxis.scale(scaleX)) 647 | 648 | scaleX.domain([newMinCoex, newMaxCoex]) 649 | 650 | svg.selectAll('text.coexAxisText') 651 | .data([newMaxCoex, newMinCoex]) 652 | .text(function (d, i) { 653 | return Math.abs(parseInt(d - 100)) 654 | }) 655 | 656 | svg.selectAll('g.data') 657 | .attr('transform', function (d) { 658 | var x = scaleX(d.x) 659 | var y 660 | if (isNaN(d.y)) { 661 | y = scaleY(0) 662 | } else { 663 | y = scaleY(d.y) 664 | } 665 | if (d.y > 100) { 666 | y = scaleY(100) - 20 667 | } 668 | return 'translate(' + x + ',' + y + ')' 669 | }) 670 | svg.selectAll('#draggableXStart') 671 | .attr('cx', immutableScaleX(newMaxCoex) - 2) 672 | svg.selectAll('#draggableXEnd') 673 | .attr('cx', immutableScaleX(newMinCoex) + 5) 674 | svg.selectAll('#draggableXLine') 675 | .attr('x1', immutableScaleX(newMaxCoex) - 2) 676 | .attr('x2', immutableScaleX(newMinCoex) + 5) 677 | d3.event.sourceEvent.stopPropagation() 678 | } 679 | 680 | function over(el) { 681 | var parent = d3.select(el).node().parentNode 682 | var element = d3.select(parent) 683 | var n = 0 684 | svg.selectAll('.' + svgClass + ' g.data') 685 | .style('opacity', 0.3) 686 | 687 | element.selectAll('circle.turnover-background') 688 | .classed('over', true) 689 | 690 | element.selectAll('circle.patent') 691 | .style('opacity', 1.0) 692 | 693 | element.selectAll('line') 694 | .attr('stroke-width', 10) 695 | .style('opacity', 0.8) 696 | .transition() 697 | .duration(100) 698 | .delay(function (d, i) { 699 | return i * 40 700 | }) 701 | .on("start", function () { 702 | if (n === 0) { 703 | element.selectAll('circle.patent') 704 | .style('opacity', 1) 705 | } 706 | n++ 707 | d3.active(this) 708 | .attr('x2', function (d) { 709 | return d.x2 710 | }) 711 | .attr('y2', function (d) { 712 | return d.y2 713 | }) 714 | .transition() 715 | }) 716 | 717 | element 718 | .style('opacity', 1.0) 719 | } 720 | 721 | function out(el) { 722 | var parent = d3.select(el).node().parentNode 723 | var element = d3.select(parent) 724 | 725 | var n = 18 726 | 727 | element.selectAll('line') 728 | .transition() 729 | .duration(100) 730 | .attr('x2', 0) 731 | .attr('y2', 0) 732 | .on("end", function () { 733 | n-- 734 | d3.select(this).style('opacity', 0) 735 | if (n === 0) { 736 | d3.select(this).style('opacity', 0) 737 | element.selectAll('circle.patent') 738 | .style('opacity', 0) 739 | } 740 | }) 741 | 742 | if (n > 0) { 743 | element.selectAll('line') 744 | .attr('x2', 0) 745 | .attr('y2', 0) 746 | .style('opacity', 0) 747 | } 748 | 749 | svg.selectAll('.' + svgClass + ' g.data') 750 | .style('opacity', 1) 751 | } 752 | 753 | function stringNoSpaces(str) { 754 | return str.replace(' ', '').toLowerCase() 755 | } 756 | } 757 | 758 | /* exported Scatterplot */ 759 | -------------------------------------------------------------------------------- /examples/js/smart-tooltips.js: -------------------------------------------------------------------------------- 1 | /* global d3 */ 2 | (function () { 3 | var element = d3.select('.smart-tooltips-container') 4 | var colors = ['#9772B1', '#F9D35B', '#8ED3C8'] 5 | var width = 800 6 | var height = 450 7 | var clicked = false 8 | var classExplanation = { 9 | 'class-de': { 10 | title: 'Exclude', 11 | description: 'Exclude item from the visualization.', 12 | }, 13 | 'class-bt': { 14 | title: 'Keep', 15 | description: 'Keep item in the visualization.', 16 | }, 17 | 'class-u': { 18 | title: 'Go up', 19 | description: 'Aggregate items according to higher level categorization.', 20 | }, 21 | 'class-d': { 22 | title: 'Go down', 23 | description: 'Break down item according to lower categorization.', 24 | }, 25 | 'class-b': { 26 | title: 'Benchmark', 27 | description: 'Set this item as a benchmark within the visualization.', 28 | }, 29 | 'class-c': { 30 | title: 'Compare', 31 | description: 'Compare item with others showing full data set distribution.', 32 | }, 33 | 'class-h': { 34 | title: 'Highlight', 35 | description: 'Make this item recognizable within the visualization.', 36 | }, 37 | } 38 | 39 | var svg = element 40 | .append('svg') 41 | .attr('width', width) 42 | .attr('height', height) 43 | .attr('class', 'smart-tolltips') 44 | 45 | svg 46 | .selectAll('line') 47 | .data([100, 200, 300, 399]) 48 | .enter() 49 | .append('line') 50 | .attr('x1', 0) 51 | .attr('y1', function (d, i) { return d}) 52 | .attr('x2', width) 53 | .attr('y2', function (d, i) { return d}) 54 | .attr('stroke', '#A9C1E5') 55 | 56 | svg 57 | .selectAll('rect') 58 | .data([90, 180, 70]) 59 | .enter() 60 | .append('rect') 61 | .attr('width', 60) 62 | .attr('height', function (d, i) { return d}) 63 | .attr('x', function (d, i) { 64 | return 220 + i * 150 65 | }) 66 | .attr('y', function (d, i) { 67 | return height - d - 50 68 | }) 69 | .attr('fill', function (d, i) { 70 | return colors[i] 71 | }) 72 | .attr('opacity', function (d, i) { 73 | return i === 1 ? 1 : 0.3 74 | }) 75 | .attr('cursor', function (d, i) { 76 | return i === 1 ? 'pointer' : 'default' 77 | }) 78 | .on('click', function (d, i) { 79 | if (i === 1) { 80 | if (clicked) { 81 | d3.select('.hoverable-menu-el-container') 82 | .style('display', 'none') 83 | d3.select('.initial') 84 | .style('display', 'none') 85 | d3.select('.clicked') 86 | .style('display', 'block') 87 | } else { 88 | d3.select('.hoverable-menu-el-container') 89 | .style('display', 'flex') 90 | 91 | d3.select('.initial') 92 | .style('display', 'block') 93 | d3.select('.clicked') 94 | .style('display', 'none') 95 | } 96 | d3.selectAll('.hoverable-menu-rect-el') 97 | .style('opacity', 0) 98 | clicked = !clicked 99 | } 100 | }) 101 | .on('mouseover', function (d, i) { 102 | if (i === 1) { 103 | d3.select(this).attr('fill', '#eec200') 104 | if (!clicked) { 105 | d3.selectAll('.hoverable-menu-rect-el') 106 | .style('opacity', 1) 107 | } 108 | } 109 | }) 110 | .on('mouseout', function (d, i) { 111 | d3.select(this).attr('fill', colors[i]) 112 | d3.selectAll('.hoverable-menu-rect-el') 113 | .style('opacity', 0) 114 | }) 115 | 116 | svg 117 | .selectAll('text') 118 | .data(['Clothes', 'Electronic', 'Health']) 119 | .enter() 120 | .append('text') 121 | .attr('dx', function (d, i) { 122 | return 250 + i * 150 123 | }) 124 | .attr('dy', function (d, i) { 125 | return height - 10 126 | }) 127 | .text(function (d, i) { 128 | return d 129 | }) 130 | .attr('font-size', '14px') 131 | .attr('text-anchor', 'middle') 132 | .attr('fill', function (d, i) { 133 | return '#5f92cd' 134 | }) 135 | .attr('opacity', function (d, i) { 136 | return i === 1 ? 1 : 0.3 137 | }) 138 | 139 | d3.selectAll('.hoverable-menu-el') 140 | .on('mouseover', function () { 141 | var type = d3.select(this).attr('type') 142 | var x = d3.select(this).node().offsetLeft + 170 143 | 144 | svg 145 | .append('rect') 146 | .attr('width', 100) 147 | .attr('height', 30) 148 | .attr('class', 'smart-tooltips-rect') 149 | .attr('fill', '#464646') 150 | .attr('x', x) 151 | .attr('y', 80) 152 | 153 | svg 154 | .append('text') 155 | .attr('class', 'smart-tooltips-text') 156 | .attr('dx', x + 50) 157 | .attr('dy', 100) 158 | .attr('fill', 'white') 159 | .style('font-size', '14px') 160 | .attr('text-anchor', 'middle') 161 | .text(function (d) { 162 | return classExplanation[type].title 163 | }) 164 | 165 | d3.select('.smart-tooltips-explanation') 166 | .html(classExplanation[type].description) 167 | 168 | svg.select('.smart-tooltips-placeholder') 169 | .style('display', 'none') 170 | 171 | d3.select(this) 172 | .selectAll('path') 173 | .style('fill', '#F0605B') 174 | }) 175 | .on('mouseout', function () { 176 | svg.selectAll('.smart-tooltips-rect') 177 | .remove() 178 | 179 | svg.selectAll('.smart-tooltips-text') 180 | .remove() 181 | 182 | d3.select('.smart-tooltips-explanation') 183 | .html('') 184 | 185 | d3.select(this) 186 | .selectAll('path') 187 | .style('fill', '#706F6F') 188 | }) 189 | })() 190 | -------------------------------------------------------------------------------- /examples/json/scatterplot-dataset.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "x": 1, 3 | "y": 12, 4 | "radius": 163, 5 | "rank": 0, 6 | "name": "", 7 | "class": "Alabama", 8 | "data_01": 247, 9 | "data_02": 222, 10 | "data_03": 31, 11 | "data_04": 256, 12 | "data_05": 108, 13 | "data_06": 55, 14 | "data_07": 392, 15 | "data_08": 28, 16 | "data_09": 286, 17 | "data_10": 48, 18 | "data_11": 275, 19 | "data_12": 160, 20 | "data_13": 102, 21 | "data_14": 113, 22 | "data_15": 128, 23 | "data_16": 385, 24 | "data_17": 70, 25 | "data_18": 260 26 | }, { 27 | "x": 5, 28 | "y": 98, 29 | "radius": 499, 30 | "rank": 1, 31 | "name": "", 32 | "class": "Alaska", 33 | "data_01": 118, 34 | "data_02": 86, 35 | "data_03": 337, 36 | "data_04": 280, 37 | "data_05": 142, 38 | "data_06": 137, 39 | "data_07": 25, 40 | "data_08": 135, 41 | "data_09": 222, 42 | "data_10": 333, 43 | "data_11": 37, 44 | "data_12": 160, 45 | "data_13": 183, 46 | "data_14": 337, 47 | "data_15": 157, 48 | "data_16": 62, 49 | "data_17": 97, 50 | "data_18": 216 51 | }, { 52 | "x": 16, 53 | "y": 39, 54 | "radius": 127, 55 | "rank": 2, 56 | "name": "", 57 | "class": "American Samoa", 58 | "data_01": 396, 59 | "data_02": 266, 60 | "data_03": 317, 61 | "data_04": 370, 62 | "data_05": 291, 63 | "data_06": 288, 64 | "data_07": 102, 65 | "data_08": 298, 66 | "data_09": 80, 67 | "data_10": 206, 68 | "data_11": 127, 69 | "data_12": 23, 70 | "data_13": 340, 71 | "data_14": 42, 72 | "data_15": 378, 73 | "data_16": 333, 74 | "data_17": 182, 75 | "data_18": 52 76 | }, { 77 | "x": 73, 78 | "y": 50, 79 | "radius": 30, 80 | "rank": 3, 81 | "name": "", 82 | "class": "Arizona", 83 | "data_01": 81, 84 | "data_02": 6, 85 | "data_03": 38, 86 | "data_04": 378, 87 | "data_05": 86, 88 | "data_06": 372, 89 | "data_07": 294, 90 | "data_08": 198, 91 | "data_09": 293, 92 | "data_10": 209, 93 | "data_11": 291, 94 | "data_12": 120, 95 | "data_13": 344, 96 | "data_14": 150, 97 | "data_15": 233, 98 | "data_16": 393, 99 | "data_17": 344, 100 | "data_18": 165 101 | }, { 102 | "x": 77, 103 | "y": 1, 104 | "radius": 182, 105 | "rank": 4, 106 | "name": "", 107 | "class": "Arkansas", 108 | "data_01": 281, 109 | "data_02": 352, 110 | "data_03": 387, 111 | "data_04": 88, 112 | "data_05": 210, 113 | "data_06": 260, 114 | "data_07": 52, 115 | "data_08": 275, 116 | "data_09": 193, 117 | "data_10": 371, 118 | "data_11": 133, 119 | "data_12": 288, 120 | "data_13": 230, 121 | "data_14": 327, 122 | "data_15": 13, 123 | "data_16": 237, 124 | "data_17": 162, 125 | "data_18": 183 126 | }, { 127 | "x": 62, 128 | "y": 70, 129 | "radius": 355, 130 | "rank": 5, 131 | "name": "", 132 | "class": "California", 133 | "data_01": 160, 134 | "data_02": 293, 135 | "data_03": 75, 136 | "data_04": 250, 137 | "data_05": 303, 138 | "data_06": 268, 139 | "data_07": 386, 140 | "data_08": 300, 141 | "data_09": 340, 142 | "data_10": 319, 143 | "data_11": 243, 144 | "data_12": 178, 145 | "data_13": 335, 146 | "data_14": 244, 147 | "data_15": 216, 148 | "data_16": 140, 149 | "data_17": 249, 150 | "data_18": 131 151 | }, { 152 | "x": 28, 153 | "y": 54, 154 | "radius": 383, 155 | "rank": 6, 156 | "name": "", 157 | "class": "Colorado", 158 | "data_01": 150, 159 | "data_02": 79, 160 | "data_03": 228, 161 | "data_04": 18, 162 | "data_05": 319, 163 | "data_06": 166, 164 | "data_07": 29, 165 | "data_08": 290, 166 | "data_09": 128, 167 | "data_10": 69, 168 | "data_11": 72, 169 | "data_12": 213, 170 | "data_13": 253, 171 | "data_14": 153, 172 | "data_15": 123, 173 | "data_16": 398, 174 | "data_17": 18, 175 | "data_18": 276 176 | }, { 177 | "x": 13, 178 | "y": 21, 179 | "radius": 35, 180 | "rank": 7, 181 | "name": "", 182 | "class": "Connecticut", 183 | "data_01": 136, 184 | "data_02": 137, 185 | "data_03": 327, 186 | "data_04": 342, 187 | "data_05": 308, 188 | "data_06": 312, 189 | "data_07": 71, 190 | "data_08": 126, 191 | "data_09": 173, 192 | "data_10": 116, 193 | "data_11": 206, 194 | "data_12": 357, 195 | "data_13": 224, 196 | "data_14": 158, 197 | "data_15": 131, 198 | "data_16": 197, 199 | "data_17": 346, 200 | "data_18": 142 201 | }, { 202 | "x": 85, 203 | "y": 31, 204 | "radius": 270, 205 | "rank": 8, 206 | "name": "", 207 | "class": "Delaware", 208 | "data_01": 121, 209 | "data_02": 321, 210 | "data_03": 8, 211 | "data_04": 272, 212 | "data_05": 15, 213 | "data_06": 178, 214 | "data_07": 346, 215 | "data_08": 388, 216 | "data_09": 94, 217 | "data_10": 347, 218 | "data_11": 191, 219 | "data_12": 210, 220 | "data_13": 282, 221 | "data_14": 46, 222 | "data_15": 266, 223 | "data_16": 335, 224 | "data_17": 364, 225 | "data_18": 50 226 | }, { 227 | "x": 85, 228 | "y": 39, 229 | "radius": 390, 230 | "rank": 9, 231 | "name": "", 232 | "class": "District Of Columbia", 233 | "data_01": 276, 234 | "data_02": 105, 235 | "data_03": 302, 236 | "data_04": 182, 237 | "data_05": 230, 238 | "data_06": 14, 239 | "data_07": 160, 240 | "data_08": 398, 241 | "data_09": 323, 242 | "data_10": 153, 243 | "data_11": 125, 244 | "data_12": 342, 245 | "data_13": 0, 246 | "data_14": 376, 247 | "data_15": 332, 248 | "data_16": 280, 249 | "data_17": 27, 250 | "data_18": 157 251 | }, { 252 | "x": 91, 253 | "y": 35, 254 | "radius": 89, 255 | "rank": 10, 256 | "name": "", 257 | "class": "Federated States Of Micronesia", 258 | "data_01": 305, 259 | "data_02": 192, 260 | "data_03": 302, 261 | "data_04": 233, 262 | "data_05": 192, 263 | "data_06": 267, 264 | "data_07": 46, 265 | "data_08": 316, 266 | "data_09": 242, 267 | "data_10": 73, 268 | "data_11": 213, 269 | "data_12": 161, 270 | "data_13": 258, 271 | "data_14": 28, 272 | "data_15": 374, 273 | "data_16": 392, 274 | "data_17": 46, 275 | "data_18": 181 276 | }, { 277 | "x": 65, 278 | "y": 46, 279 | "radius": 177, 280 | "rank": 11, 281 | "name": "", 282 | "class": "Florida", 283 | "data_01": 147, 284 | "data_02": 50, 285 | "data_03": 394, 286 | "data_04": 223, 287 | "data_05": 139, 288 | "data_06": 206, 289 | "data_07": 158, 290 | "data_08": 151, 291 | "data_09": 257, 292 | "data_10": 340, 293 | "data_11": 304, 294 | "data_12": 250, 295 | "data_13": 108, 296 | "data_14": 211, 297 | "data_15": 81, 298 | "data_16": 21, 299 | "data_17": 327, 300 | "data_18": 99 301 | }, { 302 | "x": 93, 303 | "y": 26, 304 | "radius": 316, 305 | "rank": 12, 306 | "name": "", 307 | "class": "Georgia", 308 | "data_01": 199, 309 | "data_02": 246, 310 | "data_03": 201, 311 | "data_04": 338, 312 | "data_05": 74, 313 | "data_06": 213, 314 | "data_07": 353, 315 | "data_08": 153, 316 | "data_09": 154, 317 | "data_10": 84, 318 | "data_11": 252, 319 | "data_12": 275, 320 | "data_13": 354, 321 | "data_14": 105, 322 | "data_15": 380, 323 | "data_16": 159, 324 | "data_17": 43, 325 | "data_18": 38 326 | }, { 327 | "x": 63, 328 | "y": 28, 329 | "radius": 243, 330 | "rank": 13, 331 | "name": "", 332 | "class": "Guam", 333 | "data_01": 375, 334 | "data_02": 117, 335 | "data_03": 154, 336 | "data_04": 293, 337 | "data_05": 359, 338 | "data_06": 252, 339 | "data_07": 12, 340 | "data_08": 324, 341 | "data_09": 184, 342 | "data_10": 132, 343 | "data_11": 143, 344 | "data_12": 100, 345 | "data_13": 166, 346 | "data_14": 265, 347 | "data_15": 224, 348 | "data_16": 193, 349 | "data_17": 146, 350 | "data_18": 54 351 | }, { 352 | "x": 41, 353 | "y": 11, 354 | "radius": 424, 355 | "rank": 14, 356 | "name": "", 357 | "class": "Hawaii", 358 | "data_01": 81, 359 | "data_02": 336, 360 | "data_03": 256, 361 | "data_04": 97, 362 | "data_05": 145, 363 | "data_06": 78, 364 | "data_07": 297, 365 | "data_08": 9, 366 | "data_09": 272, 367 | "data_10": 230, 368 | "data_11": 132, 369 | "data_12": 228, 370 | "data_13": 156, 371 | "data_14": 394, 372 | "data_15": 244, 373 | "data_16": 114, 374 | "data_17": 321, 375 | "data_18": 60 376 | }, { 377 | "x": 9, 378 | "y": 16, 379 | "radius": 415, 380 | "rank": 15, 381 | "name": "", 382 | "class": "Idaho", 383 | "data_01": 364, 384 | "data_02": 395, 385 | "data_03": 45, 386 | "data_04": 22, 387 | "data_05": 193, 388 | "data_06": 252, 389 | "data_07": 283, 390 | "data_08": 41, 391 | "data_09": 398, 392 | "data_10": 190, 393 | "data_11": 355, 394 | "data_12": 217, 395 | "data_13": 161, 396 | "data_14": 233, 397 | "data_15": 285, 398 | "data_16": 238, 399 | "data_17": 129, 400 | "data_18": 75 401 | }, { 402 | "x": 78, 403 | "y": 3, 404 | "radius": 131, 405 | "rank": 16, 406 | "name": "", 407 | "class": "Illinois", 408 | "data_01": 4, 409 | "data_02": 113, 410 | "data_03": 256, 411 | "data_04": 295, 412 | "data_05": 107, 413 | "data_06": 108, 414 | "data_07": 264, 415 | "data_08": 14, 416 | "data_09": 197, 417 | "data_10": 145, 418 | "data_11": 136, 419 | "data_12": 372, 420 | "data_13": 301, 421 | "data_14": 172, 422 | "data_15": 68, 423 | "data_16": 166, 424 | "data_17": 147, 425 | "data_18": 233 426 | }, { 427 | "x": 64, 428 | "y": 74, 429 | "radius": 126, 430 | "rank": 17, 431 | "name": "", 432 | "class": "Indiana", 433 | "data_01": 22, 434 | "data_02": 270, 435 | "data_03": 163, 436 | "data_04": 98, 437 | "data_05": 18, 438 | "data_06": 268, 439 | "data_07": 145, 440 | "data_08": 338, 441 | "data_09": 68, 442 | "data_10": 232, 443 | "data_11": 112, 444 | "data_12": 362, 445 | "data_13": 289, 446 | "data_14": 320, 447 | "data_15": 84, 448 | "data_16": 375, 449 | "data_17": 269, 450 | "data_18": 349 451 | }, { 452 | "x": 41, 453 | "y": 10, 454 | "radius": 341, 455 | "rank": 18, 456 | "name": "", 457 | "class": "Iowa", 458 | "data_01": 2, 459 | "data_02": 138, 460 | "data_03": 14, 461 | "data_04": 239, 462 | "data_05": 46, 463 | "data_06": 299, 464 | "data_07": 209, 465 | "data_08": 221, 466 | "data_09": 100, 467 | "data_10": 53, 468 | "data_11": 265, 469 | "data_12": 393, 470 | "data_13": 292, 471 | "data_14": 31, 472 | "data_15": 339, 473 | "data_16": 323, 474 | "data_17": 262, 475 | "data_18": 47 476 | }, { 477 | "x": 50, 478 | "y": 48, 479 | "radius": 4, 480 | "rank": 19, 481 | "name": "", 482 | "class": "Kansas", 483 | "data_01": 98, 484 | "data_02": 228, 485 | "data_03": 197, 486 | "data_04": 48, 487 | "data_05": 165, 488 | "data_06": 168, 489 | "data_07": 159, 490 | "data_08": 329, 491 | "data_09": 214, 492 | "data_10": 367, 493 | "data_11": 143, 494 | "data_12": 315, 495 | "data_13": 350, 496 | "data_14": 195, 497 | "data_15": 89, 498 | "data_16": 127, 499 | "data_17": 55, 500 | "data_18": 284 501 | }, { 502 | "x": 59, 503 | "y": 93, 504 | "radius": 431, 505 | "rank": 20, 506 | "name": "", 507 | "class": "Kentucky", 508 | "data_01": 249, 509 | "data_02": 235, 510 | "data_03": 381, 511 | "data_04": 194, 512 | "data_05": 184, 513 | "data_06": 1, 514 | "data_07": 153, 515 | "data_08": 104, 516 | "data_09": 1, 517 | "data_10": 315, 518 | "data_11": 212, 519 | "data_12": 338, 520 | "data_13": 305, 521 | "data_14": 256, 522 | "data_15": 247, 523 | "data_16": 68, 524 | "data_17": 112, 525 | "data_18": 300 526 | }, { 527 | "x": 59, 528 | "y": 16, 529 | "radius": 32, 530 | "rank": 21, 531 | "name": "", 532 | "class": "Louisiana", 533 | "data_01": 287, 534 | "data_02": 176, 535 | "data_03": 5, 536 | "data_04": 234, 537 | "data_05": 292, 538 | "data_06": 74, 539 | "data_07": 140, 540 | "data_08": 9, 541 | "data_09": 127, 542 | "data_10": 55, 543 | "data_11": 282, 544 | "data_12": 222, 545 | "data_13": 93, 546 | "data_14": 354, 547 | "data_15": 94, 548 | "data_16": 112, 549 | "data_17": 6, 550 | "data_18": 379 551 | }, { 552 | "x": 44, 553 | "y": 46, 554 | "radius": 230, 555 | "rank": 22, 556 | "name": "", 557 | "class": "Maine", 558 | "data_01": 84, 559 | "data_02": 310, 560 | "data_03": 178, 561 | "data_04": 313, 562 | "data_05": 171, 563 | "data_06": 344, 564 | "data_07": 144, 565 | "data_08": 332, 566 | "data_09": 239, 567 | "data_10": 246, 568 | "data_11": 386, 569 | "data_12": 256, 570 | "data_13": 312, 571 | "data_14": 225, 572 | "data_15": 352, 573 | "data_16": 334, 574 | "data_17": 363, 575 | "data_18": 164 576 | }, { 577 | "x": 64, 578 | "y": 82, 579 | "radius": 437, 580 | "rank": 23, 581 | "name": "", 582 | "class": "Marshall Islands", 583 | "data_01": 107, 584 | "data_02": 24, 585 | "data_03": 195, 586 | "data_04": 29, 587 | "data_05": 3, 588 | "data_06": 154, 589 | "data_07": 69, 590 | "data_08": 1, 591 | "data_09": 276, 592 | "data_10": 194, 593 | "data_11": 18, 594 | "data_12": 35, 595 | "data_13": 176, 596 | "data_14": 257, 597 | "data_15": 20, 598 | "data_16": 78, 599 | "data_17": 108, 600 | "data_18": 392 601 | }, { 602 | "x": 20, 603 | "y": 65, 604 | "radius": 232, 605 | "rank": 24, 606 | "name": "", 607 | "class": "Maryland", 608 | "data_01": 236, 609 | "data_02": 158, 610 | "data_03": 377, 611 | "data_04": 256, 612 | "data_05": 218, 613 | "data_06": 52, 614 | "data_07": 77, 615 | "data_08": 33, 616 | "data_09": 381, 617 | "data_10": 205, 618 | "data_11": 382, 619 | "data_12": 337, 620 | "data_13": 307, 621 | "data_14": 299, 622 | "data_15": 157, 623 | "data_16": 134, 624 | "data_17": 159, 625 | "data_18": 380 626 | }, { 627 | "x": 47, 628 | "y": 33, 629 | "radius": 425, 630 | "rank": 25, 631 | "name": "", 632 | "class": "Massachusetts", 633 | "data_01": 109, 634 | "data_02": 351, 635 | "data_03": 98, 636 | "data_04": 28, 637 | "data_05": 104, 638 | "data_06": 251, 639 | "data_07": 379, 640 | "data_08": 181, 641 | "data_09": 393, 642 | "data_10": 264, 643 | "data_11": 86, 644 | "data_12": 345, 645 | "data_13": 168, 646 | "data_14": 207, 647 | "data_15": 77, 648 | "data_16": 150, 649 | "data_17": 17, 650 | "data_18": 325 651 | }, { 652 | "x": 86, 653 | "y": 36, 654 | "radius": 249, 655 | "rank": 26, 656 | "name": "", 657 | "class": "Michigan", 658 | "data_01": 359, 659 | "data_02": 223, 660 | "data_03": 193, 661 | "data_04": 256, 662 | "data_05": 97, 663 | "data_06": 57, 664 | "data_07": 294, 665 | "data_08": 206, 666 | "data_09": 243, 667 | "data_10": 327, 668 | "data_11": 21, 669 | "data_12": 46, 670 | "data_13": 106, 671 | "data_14": 357, 672 | "data_15": 22, 673 | "data_16": 344, 674 | "data_17": 395, 675 | "data_18": 102 676 | }, { 677 | "x": 52, 678 | "y": 96, 679 | "radius": 467, 680 | "rank": 27, 681 | "name": "", 682 | "class": "Minnesota", 683 | "data_01": 57, 684 | "data_02": 226, 685 | "data_03": 132, 686 | "data_04": 242, 687 | "data_05": 92, 688 | "data_06": 217, 689 | "data_07": 144, 690 | "data_08": 231, 691 | "data_09": 134, 692 | "data_10": 248, 693 | "data_11": 20, 694 | "data_12": 179, 695 | "data_13": 70, 696 | "data_14": 348, 697 | "data_15": 371, 698 | "data_16": 132, 699 | "data_17": 162, 700 | "data_18": 134 701 | }, { 702 | "x": 45, 703 | "y": 8, 704 | "radius": 214, 705 | "rank": 28, 706 | "name": "", 707 | "class": "Mississippi", 708 | "data_01": 245, 709 | "data_02": 146, 710 | "data_03": 392, 711 | "data_04": 81, 712 | "data_05": 330, 713 | "data_06": 98, 714 | "data_07": 209, 715 | "data_08": 123, 716 | "data_09": 75, 717 | "data_10": 6, 718 | "data_11": 65, 719 | "data_12": 169, 720 | "data_13": 291, 721 | "data_14": 145, 722 | "data_15": 62, 723 | "data_16": 233, 724 | "data_17": 249, 725 | "data_18": 352 726 | }, { 727 | "x": 92, 728 | "y": 39, 729 | "radius": 153, 730 | "rank": 29, 731 | "name": "", 732 | "class": "Missouri", 733 | "data_01": 49, 734 | "data_02": 84, 735 | "data_03": 105, 736 | "data_04": 136, 737 | "data_05": 153, 738 | "data_06": 288, 739 | "data_07": 223, 740 | "data_08": 34, 741 | "data_09": 62, 742 | "data_10": 307, 743 | "data_11": 158, 744 | "data_12": 225, 745 | "data_13": 301, 746 | "data_14": 282, 747 | "data_15": 339, 748 | "data_16": 244, 749 | "data_17": 375, 750 | "data_18": 342 751 | }, { 752 | "x": 90, 753 | "y": 5, 754 | "radius": 227, 755 | "rank": 30, 756 | "name": "", 757 | "class": "Montana", 758 | "data_01": 57, 759 | "data_02": 54, 760 | "data_03": 15, 761 | "data_04": 244, 762 | "data_05": 317, 763 | "data_06": 381, 764 | "data_07": 314, 765 | "data_08": 254, 766 | "data_09": 40, 767 | "data_10": 155, 768 | "data_11": 138, 769 | "data_12": 86, 770 | "data_13": 280, 771 | "data_14": 309, 772 | "data_15": 223, 773 | "data_16": 289, 774 | "data_17": 342, 775 | "data_18": 209 776 | }, { 777 | "x": 46, 778 | "y": 30, 779 | "radius": 288, 780 | "rank": 31, 781 | "name": "", 782 | "class": "Nebraska", 783 | "data_01": 320, 784 | "data_02": 243, 785 | "data_03": 212, 786 | "data_04": 176, 787 | "data_05": 190, 788 | "data_06": 287, 789 | "data_07": 310, 790 | "data_08": 336, 791 | "data_09": 90, 792 | "data_10": 64, 793 | "data_11": 95, 794 | "data_12": 135, 795 | "data_13": 75, 796 | "data_14": 369, 797 | "data_15": 139, 798 | "data_16": 127, 799 | "data_17": 115, 800 | "data_18": 229 801 | }, { 802 | "x": 77, 803 | "y": 19, 804 | "radius": 32, 805 | "rank": 32, 806 | "name": "", 807 | "class": "Nevada", 808 | "data_01": 245, 809 | "data_02": 19, 810 | "data_03": 193, 811 | "data_04": 82, 812 | "data_05": 138, 813 | "data_06": 89, 814 | "data_07": 41, 815 | "data_08": 22, 816 | "data_09": 105, 817 | "data_10": 11, 818 | "data_11": 165, 819 | "data_12": 164, 820 | "data_13": 163, 821 | "data_14": 40, 822 | "data_15": 358, 823 | "data_16": 296, 824 | "data_17": 311, 825 | "data_18": 14 826 | }, { 827 | "x": 30, 828 | "y": 86, 829 | "radius": 455, 830 | "rank": 33, 831 | "name": "", 832 | "class": "New Hampshire", 833 | "data_01": 333, 834 | "data_02": 56, 835 | "data_03": 161, 836 | "data_04": 341, 837 | "data_05": 174, 838 | "data_06": 126, 839 | "data_07": 219, 840 | "data_08": 15, 841 | "data_09": 205, 842 | "data_10": 17, 843 | "data_11": 91, 844 | "data_12": 19, 845 | "data_13": 210, 846 | "data_14": 147, 847 | "data_15": 56, 848 | "data_16": 182, 849 | "data_17": 327, 850 | "data_18": 381 851 | }, { 852 | "x": 62, 853 | "y": 41, 854 | "radius": 209, 855 | "rank": 34, 856 | "name": "", 857 | "class": "New Jersey", 858 | "data_01": 360, 859 | "data_02": 187, 860 | "data_03": 390, 861 | "data_04": 24, 862 | "data_05": 6, 863 | "data_06": 39, 864 | "data_07": 48, 865 | "data_08": 45, 866 | "data_09": 128, 867 | "data_10": 247, 868 | "data_11": 353, 869 | "data_12": 176, 870 | "data_13": 350, 871 | "data_14": 383, 872 | "data_15": 191, 873 | "data_16": 295, 874 | "data_17": 78, 875 | "data_18": 329 876 | }, { 877 | "x": 53, 878 | "y": 54, 879 | "radius": 41, 880 | "rank": 35, 881 | "name": "", 882 | "class": "New Mexico", 883 | "data_01": 346, 884 | "data_02": 160, 885 | "data_03": 188, 886 | "data_04": 52, 887 | "data_05": 260, 888 | "data_06": 283, 889 | "data_07": 59, 890 | "data_08": 383, 891 | "data_09": 78, 892 | "data_10": 132, 893 | "data_11": 27, 894 | "data_12": 317, 895 | "data_13": 222, 896 | "data_14": 156, 897 | "data_15": 7, 898 | "data_16": 396, 899 | "data_17": 131, 900 | "data_18": 73 901 | }, { 902 | "x": 50, 903 | "y": 77, 904 | "radius": 19, 905 | "rank": 36, 906 | "name": "", 907 | "class": "New York", 908 | "data_01": 237, 909 | "data_02": 263, 910 | "data_03": 122, 911 | "data_04": 93, 912 | "data_05": 108, 913 | "data_06": 111, 914 | "data_07": 360, 915 | "data_08": 172, 916 | "data_09": 298, 917 | "data_10": 326, 918 | "data_11": 275, 919 | "data_12": 98, 920 | "data_13": 276, 921 | "data_14": 106, 922 | "data_15": 357, 923 | "data_16": 266, 924 | "data_17": 135, 925 | "data_18": 308 926 | }, { 927 | "x": 97, 928 | "y": 69, 929 | "radius": 290, 930 | "rank": 37, 931 | "name": "", 932 | "class": "North Carolina", 933 | "data_01": 87, 934 | "data_02": 388, 935 | "data_03": 214, 936 | "data_04": 202, 937 | "data_05": 240, 938 | "data_06": 102, 939 | "data_07": 42, 940 | "data_08": 128, 941 | "data_09": 145, 942 | "data_10": 104, 943 | "data_11": 334, 944 | "data_12": 226, 945 | "data_13": 260, 946 | "data_14": 144, 947 | "data_15": 52, 948 | "data_16": 195, 949 | "data_17": 370, 950 | "data_18": 313 951 | }, { 952 | "x": 22, 953 | "y": 91, 954 | "radius": 459, 955 | "rank": 38, 956 | "name": "", 957 | "class": "North Dakota", 958 | "data_01": 42, 959 | "data_02": 380, 960 | "data_03": 191, 961 | "data_04": 107, 962 | "data_05": 390, 963 | "data_06": 55, 964 | "data_07": 306, 965 | "data_08": 361, 966 | "data_09": 366, 967 | "data_10": 240, 968 | "data_11": 200, 969 | "data_12": 279, 970 | "data_13": 115, 971 | "data_14": 230, 972 | "data_15": 138, 973 | "data_16": 79, 974 | "data_17": 386, 975 | "data_18": 330 976 | }, { 977 | "x": 89, 978 | "y": 0, 979 | "radius": 477, 980 | "rank": 39, 981 | "name": "", 982 | "class": "Northern Mariana Islands", 983 | "data_01": 67, 984 | "data_02": 128, 985 | "data_03": 162, 986 | "data_04": 205, 987 | "data_05": 116, 988 | "data_06": 370, 989 | "data_07": 178, 990 | "data_08": 168, 991 | "data_09": 259, 992 | "data_10": 382, 993 | "data_11": 16, 994 | "data_12": 356, 995 | "data_13": 67, 996 | "data_14": 351, 997 | "data_15": 133, 998 | "data_16": 181, 999 | "data_17": 35, 1000 | "data_18": 50 1001 | }, { 1002 | "x": 43, 1003 | "y": 9, 1004 | "radius": 280, 1005 | "rank": 40, 1006 | "name": "", 1007 | "class": "Ohio", 1008 | "data_01": 295, 1009 | "data_02": 33, 1010 | "data_03": 276, 1011 | "data_04": 39, 1012 | "data_05": 39, 1013 | "data_06": 279, 1014 | "data_07": 41, 1015 | "data_08": 75, 1016 | "data_09": 290, 1017 | "data_10": 323, 1018 | "data_11": 371, 1019 | "data_12": 230, 1020 | "data_13": 264, 1021 | "data_14": 365, 1022 | "data_15": 149, 1023 | "data_16": 369, 1024 | "data_17": 302, 1025 | "data_18": 173 1026 | }, { 1027 | "x": 98, 1028 | "y": 50, 1029 | "radius": 434, 1030 | "rank": 41, 1031 | "name": "", 1032 | "class": "Oklahoma", 1033 | "data_01": 86, 1034 | "data_02": 311, 1035 | "data_03": 55, 1036 | "data_04": 13, 1037 | "data_05": 4, 1038 | "data_06": 106, 1039 | "data_07": 140, 1040 | "data_08": 106, 1041 | "data_09": 126, 1042 | "data_10": 137, 1043 | "data_11": 352, 1044 | "data_12": 96, 1045 | "data_13": 293, 1046 | "data_14": 182, 1047 | "data_15": 314, 1048 | "data_16": 26, 1049 | "data_17": 214, 1050 | "data_18": 330 1051 | }, { 1052 | "x": 24, 1053 | "y": 27, 1054 | "radius": 57, 1055 | "rank": 42, 1056 | "name": "", 1057 | "class": "Oregon", 1058 | "data_01": 370, 1059 | "data_02": 392, 1060 | "data_03": 372, 1061 | "data_04": 308, 1062 | "data_05": 110, 1063 | "data_06": 235, 1064 | "data_07": 262, 1065 | "data_08": 361, 1066 | "data_09": 291, 1067 | "data_10": 309, 1068 | "data_11": 231, 1069 | "data_12": 372, 1070 | "data_13": 247, 1071 | "data_14": 231, 1072 | "data_15": 182, 1073 | "data_16": 36, 1074 | "data_17": 204, 1075 | "data_18": 5 1076 | }, { 1077 | "x": 96, 1078 | "y": 80, 1079 | "radius": 164, 1080 | "rank": 43, 1081 | "name": "", 1082 | "class": "Palau", 1083 | "data_01": 133, 1084 | "data_02": 90, 1085 | "data_03": 49, 1086 | "data_04": 298, 1087 | "data_05": 183, 1088 | "data_06": 46, 1089 | "data_07": 328, 1090 | "data_08": 92, 1091 | "data_09": 278, 1092 | "data_10": 349, 1093 | "data_11": 34, 1094 | "data_12": 357, 1095 | "data_13": 167, 1096 | "data_14": 232, 1097 | "data_15": 370, 1098 | "data_16": 366, 1099 | "data_17": 63, 1100 | "data_18": 182 1101 | }, { 1102 | "x": 52, 1103 | "y": 31, 1104 | "radius": 289, 1105 | "rank": 44, 1106 | "name": "", 1107 | "class": "Pennsylvania", 1108 | "data_01": 38, 1109 | "data_02": 10, 1110 | "data_03": 367, 1111 | "data_04": 203, 1112 | "data_05": 361, 1113 | "data_06": 232, 1114 | "data_07": 311, 1115 | "data_08": 273, 1116 | "data_09": 258, 1117 | "data_10": 354, 1118 | "data_11": 213, 1119 | "data_12": 273, 1120 | "data_13": 369, 1121 | "data_14": 87, 1122 | "data_15": 9, 1123 | "data_16": 314, 1124 | "data_17": 78, 1125 | "data_18": 169 1126 | }, { 1127 | "x": 55, 1128 | "y": 98, 1129 | "radius": 483, 1130 | "rank": 45, 1131 | "name": "", 1132 | "class": "Puerto Rico", 1133 | "data_01": 128, 1134 | "data_02": 222, 1135 | "data_03": 50, 1136 | "data_04": 161, 1137 | "data_05": 185, 1138 | "data_06": 198, 1139 | "data_07": 387, 1140 | "data_08": 256, 1141 | "data_09": 284, 1142 | "data_10": 133, 1143 | "data_11": 314, 1144 | "data_12": 364, 1145 | "data_13": 75, 1146 | "data_14": 133, 1147 | "data_15": 387, 1148 | "data_16": 203, 1149 | "data_17": 237, 1150 | "data_18": 355 1151 | }, { 1152 | "x": 41, 1153 | "y": 95, 1154 | "radius": 417, 1155 | "rank": 46, 1156 | "name": "", 1157 | "class": "Rhode Island", 1158 | "data_01": 17, 1159 | "data_02": 208, 1160 | "data_03": 115, 1161 | "data_04": 341, 1162 | "data_05": 290, 1163 | "data_06": 166, 1164 | "data_07": 147, 1165 | "data_08": 326, 1166 | "data_09": 109, 1167 | "data_10": 92, 1168 | "data_11": 193, 1169 | "data_12": 303, 1170 | "data_13": 158, 1171 | "data_14": 356, 1172 | "data_15": 238, 1173 | "data_16": 399, 1174 | "data_17": 19, 1175 | "data_18": 349 1176 | }, { 1177 | "x": 54, 1178 | "y": 52, 1179 | "radius": 87, 1180 | "rank": 47, 1181 | "name": "", 1182 | "class": "South Carolina", 1183 | "data_01": 179, 1184 | "data_02": 338, 1185 | "data_03": 219, 1186 | "data_04": 329, 1187 | "data_05": 314, 1188 | "data_06": 358, 1189 | "data_07": 263, 1190 | "data_08": 391, 1191 | "data_09": 115, 1192 | "data_10": 124, 1193 | "data_11": 9, 1194 | "data_12": 167, 1195 | "data_13": 103, 1196 | "data_14": 165, 1197 | "data_15": 115, 1198 | "data_16": 331, 1199 | "data_17": 358, 1200 | "data_18": 220 1201 | }, { 1202 | "x": 75, 1203 | "y": 18, 1204 | "radius": 355, 1205 | "rank": 48, 1206 | "name": "", 1207 | "class": "South Dakota", 1208 | "data_01": 272, 1209 | "data_02": 168, 1210 | "data_03": 223, 1211 | "data_04": 93, 1212 | "data_05": 196, 1213 | "data_06": 126, 1214 | "data_07": 1, 1215 | "data_08": 29, 1216 | "data_09": 33, 1217 | "data_10": 348, 1218 | "data_11": 166, 1219 | "data_12": 144, 1220 | "data_13": 369, 1221 | "data_14": 121, 1222 | "data_15": 337, 1223 | "data_16": 320, 1224 | "data_17": 237, 1225 | "data_18": 25 1226 | }, { 1227 | "x": 29, 1228 | "y": 17, 1229 | "radius": 291, 1230 | "rank": 49, 1231 | "name": "", 1232 | "class": "Tennessee", 1233 | "data_01": 123, 1234 | "data_02": 109, 1235 | "data_03": 378, 1236 | "data_04": 171, 1237 | "data_05": 99, 1238 | "data_06": 288, 1239 | "data_07": 3, 1240 | "data_08": 124, 1241 | "data_09": 336, 1242 | "data_10": 40, 1243 | "data_11": 384, 1244 | "data_12": 359, 1245 | "data_13": 275, 1246 | "data_14": 89, 1247 | "data_15": 87, 1248 | "data_16": 127, 1249 | "data_17": 38, 1250 | "data_18": 73 1251 | }, { 1252 | "x": 12, 1253 | "y": 29, 1254 | "radius": 356, 1255 | "rank": 50, 1256 | "name": "", 1257 | "class": "Texas", 1258 | "data_01": 271, 1259 | "data_02": 127, 1260 | "data_03": 32, 1261 | "data_04": 199, 1262 | "data_05": 307, 1263 | "data_06": 336, 1264 | "data_07": 266, 1265 | "data_08": 60, 1266 | "data_09": 209, 1267 | "data_10": 371, 1268 | "data_11": 247, 1269 | "data_12": 128, 1270 | "data_13": 189, 1271 | "data_14": 62, 1272 | "data_15": 299, 1273 | "data_16": 105, 1274 | "data_17": 102, 1275 | "data_18": 63 1276 | }, { 1277 | "x": 45, 1278 | "y": 97, 1279 | "radius": 431, 1280 | "rank": 51, 1281 | "name": "", 1282 | "class": "Utah", 1283 | "data_01": 116, 1284 | "data_02": 287, 1285 | "data_03": 271, 1286 | "data_04": 115, 1287 | "data_05": 260, 1288 | "data_06": 86, 1289 | "data_07": 0, 1290 | "data_08": 394, 1291 | "data_09": 283, 1292 | "data_10": 339, 1293 | "data_11": 310, 1294 | "data_12": 291, 1295 | "data_13": 252, 1296 | "data_14": 168, 1297 | "data_15": 321, 1298 | "data_16": 242, 1299 | "data_17": 298, 1300 | "data_18": 296 1301 | }, { 1302 | "x": 36, 1303 | "y": 14, 1304 | "radius": 136, 1305 | "rank": 52, 1306 | "name": "", 1307 | "class": "Vermont", 1308 | "data_01": 203, 1309 | "data_02": 238, 1310 | "data_03": 104, 1311 | "data_04": 132, 1312 | "data_05": 266, 1313 | "data_06": 344, 1314 | "data_07": 399, 1315 | "data_08": 207, 1316 | "data_09": 102, 1317 | "data_10": 57, 1318 | "data_11": 343, 1319 | "data_12": 148, 1320 | "data_13": 30, 1321 | "data_14": 363, 1322 | "data_15": 356, 1323 | "data_16": 344, 1324 | "data_17": 340, 1325 | "data_18": 198 1326 | }, { 1327 | "x": 96, 1328 | "y": 1, 1329 | "radius": 66, 1330 | "rank": 53, 1331 | "name": "", 1332 | "class": "Virgin Islands", 1333 | "data_01": 276, 1334 | "data_02": 167, 1335 | "data_03": 105, 1336 | "data_04": 44, 1337 | "data_05": 79, 1338 | "data_06": 276, 1339 | "data_07": 35, 1340 | "data_08": 230, 1341 | "data_09": 286, 1342 | "data_10": 335, 1343 | "data_11": 187, 1344 | "data_12": 93, 1345 | "data_13": 305, 1346 | "data_14": 399, 1347 | "data_15": 365, 1348 | "data_16": 295, 1349 | "data_17": 23, 1350 | "data_18": 81 1351 | }, { 1352 | "x": 4, 1353 | "y": 46, 1354 | "radius": 162, 1355 | "rank": 54, 1356 | "name": "", 1357 | "class": "Virginia", 1358 | "data_01": 164, 1359 | "data_02": 52, 1360 | "data_03": 173, 1361 | "data_04": 130, 1362 | "data_05": 164, 1363 | "data_06": 37, 1364 | "data_07": 250, 1365 | "data_08": 250, 1366 | "data_09": 43, 1367 | "data_10": 173, 1368 | "data_11": 337, 1369 | "data_12": 365, 1370 | "data_13": 268, 1371 | "data_14": 204, 1372 | "data_15": 398, 1373 | "data_16": 273, 1374 | "data_17": 321, 1375 | "data_18": 195 1376 | }, { 1377 | "x": 87, 1378 | "y": 8, 1379 | "radius": 72, 1380 | "rank": 55, 1381 | "name": "", 1382 | "class": "Washington", 1383 | "data_01": 45, 1384 | "data_02": 281, 1385 | "data_03": 387, 1386 | "data_04": 239, 1387 | "data_05": 227, 1388 | "data_06": 189, 1389 | "data_07": 342, 1390 | "data_08": 38, 1391 | "data_09": 24, 1392 | "data_10": 250, 1393 | "data_11": 189, 1394 | "data_12": 312, 1395 | "data_13": 243, 1396 | "data_14": 166, 1397 | "data_15": 18, 1398 | "data_16": 144, 1399 | "data_17": 222, 1400 | "data_18": 26 1401 | }, { 1402 | "x": 79, 1403 | "y": 31, 1404 | "radius": 359, 1405 | "rank": 56, 1406 | "name": "", 1407 | "class": "West Virginia", 1408 | "data_01": 122, 1409 | "data_02": 43, 1410 | "data_03": 347, 1411 | "data_04": 317, 1412 | "data_05": 120, 1413 | "data_06": 257, 1414 | "data_07": 209, 1415 | "data_08": 205, 1416 | "data_09": 126, 1417 | "data_10": 254, 1418 | "data_11": 174, 1419 | "data_12": 153, 1420 | "data_13": 354, 1421 | "data_14": 228, 1422 | "data_15": 354, 1423 | "data_16": 0, 1424 | "data_17": 194, 1425 | "data_18": 262 1426 | }, { 1427 | "x": 13, 1428 | "y": 97, 1429 | "radius": 455, 1430 | "rank": 57, 1431 | "name": "", 1432 | "class": "Wisconsin", 1433 | "data_01": 32, 1434 | "data_02": 73, 1435 | "data_03": 129, 1436 | "data_04": 12, 1437 | "data_05": 327, 1438 | "data_06": 90, 1439 | "data_07": 166, 1440 | "data_08": 54, 1441 | "data_09": 64, 1442 | "data_10": 118, 1443 | "data_11": 377, 1444 | "data_12": 124, 1445 | "data_13": 1, 1446 | "data_14": 24, 1447 | "data_15": 35, 1448 | "data_16": 59, 1449 | "data_17": 387, 1450 | "data_18": 36 1451 | }, { 1452 | "x": 45, 1453 | "y": 42, 1454 | "radius": 210, 1455 | "rank": 58, 1456 | "name": "", 1457 | "class": "Wyoming", 1458 | "data_01": 214, 1459 | "data_02": 62, 1460 | "data_03": 211, 1461 | "data_04": 67, 1462 | "data_05": 149, 1463 | "data_06": 59, 1464 | "data_07": 290, 1465 | "data_08": 366, 1466 | "data_09": 394, 1467 | "data_10": 234, 1468 | "data_11": 305, 1469 | "data_12": 344, 1470 | "data_13": 293, 1471 | "data_14": 323, 1472 | "data_15": 0, 1473 | "data_16": 45, 1474 | "data_17": 189, 1475 | "data_18": 269 1476 | }] 1477 | -------------------------------------------------------------------------------- /examples/json/treemap-data.json: -------------------------------------------------------------------------------- 1 | { 2 | "children":[ 3 | { 4 | "name":"Missouri", 5 | "value":60 6 | }, 7 | { 8 | "name":"Colorado", 9 | "value":8, 10 | "children":[ 11 | { 12 | "name":"Nebraska", 13 | "value":12 14 | } 15 | ] 16 | }, 17 | { 18 | "name":"Texas", 19 | "value":6, 20 | "children":[ 21 | { 22 | "name":"Arkansas", 23 | "value":2 24 | }, 25 | { 26 | "name":"Oklahoma", 27 | "value":3 28 | } 29 | ] 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /examples/server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var path = require('path'); 3 | 4 | var port = 8080; 5 | var app = express(); 6 | 7 | app.use('/', express.static('examples')); 8 | 9 | app.get('*', function (req, res) { 10 | res.sendFile(path.join(__dirname, 'index.html')); 11 | }); 12 | 13 | app.listen(port, function(err) { 14 | if (err) { 15 | console.log(err); 16 | return; 17 | } 18 | console.log('App is live at http://localhost:' + port); 19 | }); 20 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "lerna": "2.0.0-beta.32", 3 | "packages": [ 4 | "packages/*" 5 | ], 6 | "version": "0.0.0", 7 | "independent": "true" 8 | } 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ibm-design/charts", 3 | "version": "0.0.0", 4 | "description": "Code implementations of the IBM Design Language's data visualization guidance.", 5 | "repository": { 6 | "url": "https://github.com/IBM-Design/charts.git", 7 | "type": "git" 8 | }, 9 | "license": "Apache-2.0", 10 | "scripts": { 11 | "bootstrap": "lerna bootstrap && lerna run build", 12 | "examples": "node examples/server.js", 13 | "gh-pages": "yarn run gh-pages:clean && yarn run gh-pages:build && yarn run gh-pages:copy && yarn run gh-pages:publish", 14 | "gh-pages:build": "lerna run storybook:build", 15 | "gh-pages:clean": "rimraf _gh-pages", 16 | "gh-pages:copy": "cp public/* _gh-pages/$1", 17 | "gh-pages:publish": "git-directory-deploy --directory _gh-pages", 18 | "lerna": "lerna", 19 | "test": "eslint packages" 20 | }, 21 | "devDependencies": { 22 | "babel-eslint": "^7.1.1", 23 | "eslint": "^3.13.1", 24 | "eslint-plugin-babel": "^4.0.0", 25 | "eslint-plugin-import": "^2.2.0", 26 | "express": "^4.14.0", 27 | "git-directory-deploy": "^1.5.1", 28 | "jest-cli": "^18.1.0", 29 | "lerna": "2.0.0-beta.32", 30 | "rimraf": "^2.5.4" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM-Design/charts/39da9c3784f6c4a7cd23df1327b6014334dc2e82/packages/.gitkeep -------------------------------------------------------------------------------- /packages/charts-colors/README.md: -------------------------------------------------------------------------------- 1 | # `@ibm-design/charts-colors` 2 | 3 | ## Usage 4 | 5 | ```bash 6 | yarn add @ibm-design/charts-colors 7 | ``` 8 | -------------------------------------------------------------------------------- /packages/charts-colors/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ibm-design/charts-colors", 3 | "version": "0.0.0", 4 | "description": "IBM Design Charts color palettes based off of the IBM Design Colors", 5 | "main": "lib/index.js", 6 | "license": "Apache-2.0", 7 | "scripts": { 8 | "build": "npm run clean && babel -d lib src", 9 | "clean": "rimraf lib" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/IBM-Design/charts/tree/master/packages/charts-colors" 14 | }, 15 | "dependencies": { 16 | "ibm-design-colors": "^2.0.3" 17 | }, 18 | "devDependencies": { 19 | "babel-cli": "^6.23.0", 20 | "json-loader": "^0.5.4" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/charts-colors/src/index.js: -------------------------------------------------------------------------------- 1 | export { default as colors } from 'ibm-design-colors/ibm-colors'; 2 | export { default as palettes } from './palettes'; 3 | -------------------------------------------------------------------------------- /packages/charts-colors/src/palettes/colorBlindSafe.js: -------------------------------------------------------------------------------- 1 | import ibmColors from 'ibm-design-colors/ibm-colors'; 2 | 3 | export default [ 4 | ibmColors.ultramarine['40'], 5 | ibmColors.indigo['50'], 6 | ibmColors.magenta['50'], 7 | ibmColors.orange['40'], 8 | ibmColors.gold['20'], 9 | ]; 10 | -------------------------------------------------------------------------------- /packages/charts-colors/src/palettes/index.js: -------------------------------------------------------------------------------- 1 | import colorBlindSafe from './colorBlindSafe'; 2 | import qualitative from './qualitative'; 3 | 4 | export default { 5 | 'qualitative': qualitative, 6 | 'colorBlindSafe': colorBlindSafe, 7 | }; 8 | -------------------------------------------------------------------------------- /packages/charts-colors/src/palettes/qualitative.js: -------------------------------------------------------------------------------- 1 | import ibmColors from 'ibm-design-colors/ibm-colors'; 2 | 3 | export default [ 4 | ibmColors.blue['40'], 5 | ibmColors.aqua['20'], 6 | ibmColors.green['30'], 7 | ibmColors.lime['20'], 8 | ibmColors.gold['20'], 9 | ibmColors.peach['30'], 10 | ibmColors.magenta['40'], 11 | ibmColors.indigo['40'], 12 | ]; 13 | -------------------------------------------------------------------------------- /packages/charts-design/IBM-Design-Charts.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM-Design/charts/39da9c3784f6c4a7cd23df1327b6014334dc2e82/packages/charts-design/IBM-Design-Charts.sketch -------------------------------------------------------------------------------- /packages/charts-design/IBM-Design-Charts_Base.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM-Design/charts/39da9c3784f6c4a7cd23df1327b6014334dc2e82/packages/charts-design/IBM-Design-Charts_Base.sketch -------------------------------------------------------------------------------- /packages/charts-design/IBM-Design-Charts_Line.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM-Design/charts/39da9c3784f6c4a7cd23df1327b6014334dc2e82/packages/charts-design/IBM-Design-Charts_Line.sketch -------------------------------------------------------------------------------- /packages/charts-design/IBM-Design-Charts_Scatterplot.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM-Design/charts/39da9c3784f6c4a7cd23df1327b6014334dc2e82/packages/charts-design/IBM-Design-Charts_Scatterplot.sketch -------------------------------------------------------------------------------- /packages/charts-design/IBM-Design-Charts_Stacked-Bar.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM-Design/charts/39da9c3784f6c4a7cd23df1327b6014334dc2e82/packages/charts-design/IBM-Design-Charts_Stacked-Bar.sketch -------------------------------------------------------------------------------- /packages/eslint-config-charts/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2017 IBM 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | -------------------------------------------------------------------------------- /packages/eslint-config-charts/README.md: -------------------------------------------------------------------------------- 1 | # `@ibm-design/charts-eslint-config` 2 | 3 | This is currently copied over from GHE as an opinionated `eslint` configuration. 4 | -------------------------------------------------------------------------------- /packages/eslint-config-charts/index.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | module.exports = { 5 | parser: 'babel-eslint', 6 | parserOptions: { 7 | ecmaVersion: 2017, 8 | sourceType: 'module', 9 | }, 10 | 11 | env: { 12 | // Enable these blindly because we can't make a per-file decision about this. 13 | browser: true, 14 | es6: true, 15 | node: true, 16 | jest: true, 17 | jasmine: true, 18 | }, 19 | 20 | globals: { 21 | __DEV__: true, 22 | }, 23 | 24 | extends: [ 25 | './rules/base', 26 | './rules/best-practices', 27 | './rules/es6', 28 | './rules/node', 29 | './rules/strict', 30 | './rules/style', 31 | './rules/variables', 32 | 33 | './plugins/babel', 34 | './plugins/import', 35 | ].map(require.resolve), 36 | 37 | rules: {}, 38 | }; 39 | -------------------------------------------------------------------------------- /packages/eslint-config-charts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ibm-design/charts-eslint-config", 3 | "version": "0.0.0", 4 | "main": "index.js", 5 | "peerDependencies": { 6 | "babel-eslint": "^7.1.1", 7 | "eslint": "^3.13.1", 8 | "eslint-plugin-babel": "^4.0.0", 9 | "eslint-plugin-import": "^2.2.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/eslint-config-charts/plugins/babel.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const OFF = 0; 4 | 5 | // eslint-plugin-babel 6 | module.exports = { 7 | plugins: ['babel'], 8 | rules: { 9 | 'babel/generator-star-spacing': OFF, 10 | 'babel/new-cap': OFF, 11 | 'babel/array-bracket-spacing': OFF, 12 | 'babel/object-curly-spacing': OFF, 13 | 'babel/object-shorthand': OFF, 14 | 'babel/arrow-parens': OFF, 15 | 'babel/no-await-in-loop': OFF, 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /packages/eslint-config-charts/plugins/import.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const ERROR = 2; 4 | 5 | module.exports = { 6 | plugins: ['import'], 7 | rules: { 8 | 'import/no-unresolved': ERROR, 9 | 'import/named': ERROR, 10 | 'import/no-absolute-path': ERROR, 11 | 12 | 'import/export': ERROR, 13 | 'import/no-named-as-default': ERROR, 14 | 'import/no-extraneous-dependencies': ERROR, 15 | 'import/no-mutable-exports': ERROR, 16 | 17 | 'import/no-duplicates': ERROR, 18 | 'import/extensions': ERROR, 19 | }, 20 | }; 21 | -------------------------------------------------------------------------------- /packages/eslint-config-charts/rules/base.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const OFF = 0; 4 | const ERROR = 2; 5 | 6 | module.exports = { 7 | rules: { 8 | 'comma-dangle': [ERROR, 'always-multiline'], 9 | 10 | // equivalent to jshint boss 11 | 'no-cond-assign': OFF, 12 | 'no-console': ERROR, 13 | // prohibits things like `while (true)` 14 | 'no-constant-condition': OFF, 15 | // we need to be able to match these 16 | 'no-control-regex': OFF, 17 | // equivalent to jshint debug 18 | 'no-debugger': ERROR, 19 | // equivalent to jshint W004 20 | 'no-dupe-args': ERROR, 21 | // syntax error in strict mode, almost certainly unintended in any case 22 | 'no-dupe-keys': ERROR, 23 | // almost certainly a bug 24 | 'no-duplicate-case': ERROR, 25 | // almost certainly a bug 26 | 'no-empty-character-class': ERROR, 27 | // would warn on uncommented empty `catch (ex) {}` blocks 28 | 'no-empty': OFF, 29 | // can cause subtle bugs in IE 8, and we shouldn't do this anyways 30 | 'no-ex-assign': ERROR, 31 | // we shouldn't do this anyways 32 | 'no-extra-boolean-cast': ERROR, 33 | // parens may be used to improve clarity, equivalent to jshint W068 34 | 'no-extra-parens': [ERROR, 'functions'], 35 | // equivalent to jshint W032 36 | 'no-extra-semi': ERROR, 37 | // a function delaration shouldn't be rewritable 38 | 'no-func-assign': ERROR, 39 | // babel and es6 allow block-scoped functions 40 | 'no-inner-declarations': OFF, 41 | // will cause a runtime error 42 | 'no-invalid-regexp': ERROR, 43 | // disallow non-space or tab whitespace characters 44 | 'no-irregular-whitespace': ERROR, 45 | // write `if (!(a in b))`, not `if (!a in b)`, equivalent to jshint W007 46 | 'no-negated-in-lhs': ERROR, 47 | // will cause a runtime error 48 | 'no-obj-calls': ERROR, 49 | // improves legibility 50 | 'no-regex-spaces': ERROR, 51 | // equivalent to jshint elision 52 | 'no-sparse-arrays': ERROR, 53 | // equivalent to jshint W027 54 | 'no-unreachable': ERROR, 55 | // equivalent to jshint use-isnan 56 | 'use-isnan': ERROR, 57 | // probably too noisy ATM 58 | 'valid-jsdoc': OFF, 59 | // equivalent to jshint notypeof 60 | 'valid-typeof': ERROR, 61 | // we already require semicolons 62 | 'no-unexpected-multiline': OFF, 63 | }, 64 | }; 65 | -------------------------------------------------------------------------------- /packages/eslint-config-charts/rules/best-practices.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const OFF = 0; 4 | const ERROR = 2; 5 | 6 | module.exports = { 7 | rules: { 8 | // probably a bug, we shouldn't actually even use this yet, because of IE8 9 | 'accessor-pairs': [ERROR, {setWithoutGet: true}], 10 | // probably too noisy ATM 11 | 'block-scoped-var': OFF, 12 | // cyclomatic complexity, we're too far gone 13 | 'complexity': OFF, 14 | // require return statements to either always or never specify values 15 | 'consistent-return': ERROR, 16 | // style guide: Always use brackets, even when optional. 17 | 'curly': [ERROR, 'all'], 18 | // we don't do this/care about this 19 | 'default-case': OFF, 20 | // disabled in favor of our temporary fork 21 | 'dot-notation': OFF, 22 | // we don't do this/care about this, but probably should eventually 23 | 'dot-location': OFF, 24 | // disabled as it's too noisy ATM 25 | 'eqeqeq': [OFF, 'allow-null'], 26 | // we don't do this/care about this, equivalent to jshint forin 27 | 'guard-for-in': OFF, 28 | // we have too many internal examples/tools using this 29 | 'no-alert': OFF, 30 | // incompatible with 'use strict' equivalent to jshint noarg 31 | 'no-caller': ERROR, 32 | // we don't care about this right now, but might later 33 | 'no-case-declarations': OFF, 34 | // we don't do this/care about this 35 | 'no-div-regex': OFF, 36 | // we don't do this/care about this 37 | 'no-else-return': OFF, 38 | // avoid mistaken variables when destructuring 39 | 'no-empty-pattern': ERROR, 40 | // see eqeqeq: we explicitly allow this, equivalent to jshint eqnull 41 | 'no-eq-null': OFF, 42 | // equivalent to jshint evil 43 | 'no-eval': ERROR, 44 | // should only be triggered on polyfills, which we can fix case-by-case 45 | 'no-extend-native': ERROR, 46 | // might be a sign of a bug 47 | 'no-extra-bind': ERROR, 48 | // equivalent to jshint W089 49 | 'no-fallthrough': ERROR, 50 | // equivalent to jshint W008 51 | 'no-floating-decimal': ERROR, 52 | // implicit coercion is often idiomatic 53 | 'no-implicit-coercion': OFF, 54 | // equivalent to jshint evil/W066 55 | 'no-implied-eval': ERROR, 56 | // will likely create more signal than noise 57 | 'no-invalid-this': OFF, 58 | // babel should handle this fine 59 | 'no-iterator': OFF, 60 | // Should be effectively equivalent to jshint W028 - allowing the use 61 | // of labels in very specific situations. ESLint no-empty-labels was 62 | // deprecated. 63 | 'no-labels': [ERROR, {allowLoop: true, allowSwitch: true}], 64 | // lone blocks create no scope, will ignore blocks with let/const 65 | 'no-lone-blocks': ERROR, 66 | // equivalent to jshint loopfunc 67 | 'no-loop-func': OFF, 68 | // we surely have these, don't bother with it 69 | 'no-magic-numbers': OFF, 70 | // we may use this for alignment in some places 71 | 'no-multi-spaces': OFF, 72 | // equivalent to jshint multistr, consider using es6 template strings 73 | 'no-multi-str': ERROR, 74 | // equivalent to jshint W02OFF, similar to no-extend-native 75 | 'no-native-reassign': [ERROR, {exceptions: ['Map', 'Set']}], 76 | // equivalent to jshint evil/W054 77 | 'no-new-func': ERROR, 78 | // don't use constructors for side-effects, equivalent to jshint nonew 79 | 'no-new': ERROR, 80 | // very limited uses, mostly in third_party 81 | 'no-new-wrappers': ERROR, 82 | // deprecated in ES5, but we still use it in some places 83 | 'no-octal-escape': ERROR, 84 | // deprecated in ES5, may cause unexpected behavior 85 | 'no-octal': ERROR, 86 | // treats function parameters as constants, probably too noisy ATM 87 | 'no-param-reassign': OFF, 88 | // only relevant to node code 89 | 'no-process-env': OFF, 90 | // deprecated in ES3.ERROR, equivalent to jshint proto 91 | 'no-proto': ERROR, 92 | // jshint doesn't catch this, but this is inexcusable 93 | 'no-redeclare': ERROR, 94 | // equivalent to jshint boss 95 | 'no-return-assign': OFF, 96 | // equivalent to jshint scripturl 97 | 'no-script-url': ERROR, 98 | // not in jshint, but is in jslint, and is almost certainly a mistake 99 | 'no-self-compare': ERROR, 100 | // there are very limited valid use-cases for this 101 | 'no-sequences': ERROR, 102 | // we're already pretty good about this, and it hides stack traces 103 | 'no-throw-literal': ERROR, 104 | // breaks on `foo && foo.bar()` expression statements, which are common 105 | 'no-unused-expressions': OFF, 106 | // disallow unnecessary .call() and .apply() 107 | 'no-useless-call': ERROR, 108 | // disallow concatenating string literals 109 | 'no-useless-concat': ERROR, 110 | // this has valid use-cases, eg. to circumvent no-unused-expressions 111 | 'no-void': OFF, 112 | // this journey is 1% finished (allow TODO comments) 113 | 'no-warning-comments': OFF, 114 | // equivalent to jshint withstmt 115 | 'no-with': OFF, 116 | // require radix argument in parseInt, we do this in most places already 117 | 'radix': ERROR, 118 | // we don't do this/care about this 119 | 'vars-on-top': OFF, 120 | // equivalent to jshint immed 121 | 'wrap-iife': OFF, 122 | // probably too noisy ATM 123 | 'yoda': OFF, 124 | }, 125 | }; 126 | -------------------------------------------------------------------------------- /packages/eslint-config-charts/rules/es6.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const OFF = 0; 4 | const ERROR = 2; 5 | 6 | module.exports = { 7 | env: { 8 | es6: true, 9 | }, 10 | parserOptions: { 11 | ecmaVersion: 6, 12 | sourceType: 'module', 13 | }, 14 | rules: { 15 | 'arrow-body-style': OFF, 16 | 'arrow-parens': OFF, 17 | // tbgs finds *very few* places where we don't put spaces around => 18 | 'arrow-spacing': [ERROR, {before: true, after: true}], 19 | // violation of the ES6 spec, won't transform 20 | 'constructor-super': ERROR, 21 | // https://github.com/babel/babel-eslint#known-issues 22 | 'generator-star-spacing': OFF, 23 | 'no-class-assign': ERROR, 24 | 'no-confusing-arrow': OFF, 25 | // this is a runtime error 26 | 'no-const-assign': ERROR, 27 | 'no-dupe-class-members': ERROR, 28 | // violation of the ES6 spec, won't transform, `this` is part of the TDZ 29 | 'no-this-before-super': ERROR, 30 | // we have way too much ES3 & ES5 code 31 | 'no-var': OFF, 32 | 'object-shorthand': OFF, 33 | 'prefer-const': OFF, 34 | 'prefer-spread': OFF, 35 | // we don't support/polyfill this yet 36 | 'prefer-reflect': OFF, 37 | 'prefer-template': OFF, 38 | // there are legitimate use-cases for an empty generator 39 | 'require-yield': OFF, 40 | }, 41 | }; 42 | -------------------------------------------------------------------------------- /packages/eslint-config-charts/rules/node.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const OFF = 0; 4 | 5 | module.exports = { 6 | rules: { 7 | 'callback-return': OFF, 8 | 'global-require': OFF, 9 | 'handle-callback-err': OFF, 10 | 'no-mixed-requires': OFF, 11 | 'no-new-require': OFF, 12 | 'no-path-concat': OFF, 13 | 'no-process-exit': OFF, 14 | 'no-restricted-modules': OFF, 15 | 'no-sync': OFF, 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /packages/eslint-config-charts/rules/strict.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const OFF = 0; 4 | 5 | module.exports = { 6 | rules: { 7 | // Strict Mode 8 | 'strict': OFF, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /packages/eslint-config-charts/rules/style.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const OFF = 0; 4 | const ERROR = 2; 5 | 6 | // This pattern will match these texts: 7 | // var Foo = require('Foo'); 8 | // var Bar = require('Foo').Bar; 9 | // var BarFoo = require(Bar + 'Foo'); 10 | // var {Bar, Foo} = require('Foo'); 11 | // import type {Bar, Foo} from 'Foo'; 12 | // Also supports 'let' and 'const'. 13 | const variableNamePattern = String.raw`\s*[a-zA-Z_$][a-zA-Z_$\d]*\s*`; 14 | const maxLenIgnorePattern = String.raw`^(?:var|let|const|import type)\s+` + 15 | '{?' + variableNamePattern + '(?:,' + variableNamePattern + ')*}?' + 16 | String.raw`\s*(?:=\s*require\(|from)[a-zA-Z_+./"'\s\d\-]+\)?[^;\n]*[;\n]`; 17 | 18 | module.exports = { 19 | rules: { 20 | 'array-bracket-spacing': ERROR, 21 | // TODO: enable this with consensus on single line blocks 22 | 'block-spacing': OFF, 23 | 'brace-style': [ERROR, '1tbs', {allowSingleLine: true}], 24 | // too noisy at the moment, and jshint didn't check it 25 | 'camelcase': [OFF, {properties: 'always'}], 26 | 'comma-spacing': [ERROR, {before: false, after: true}], 27 | // jshint had laxcomma, but that was against our style guide 28 | 'comma-style': [ERROR, 'last'], 29 | 'computed-property-spacing': [ERROR, 'never'], 30 | // we may use more contextually relevant names for this than self 31 | 'consistent-this': [OFF, 'self'], 32 | // should be handled by a generic TXT linter instead 33 | 'eol-last': OFF, 34 | 'func-names': OFF, 35 | // too noisy ATM 36 | 'func-style': [OFF, 'declaration'], 37 | // no way we could enforce min/max lengths or patterns for vars 38 | 'id-length': OFF, 39 | 'id-match': OFF, 40 | // we weren't enforcing this with jshint, so erroring would be too noisy 41 | 'indent': [ERROR, 2, {SwitchCase: 1}], 42 | // we use single quotes for JS literals, double quotes for JSX literals 43 | 'jsx-quotes': [ERROR, 'prefer-double'], 44 | // we may use extra spaces for alignment 45 | 'key-spacing': [OFF, {beforeColon: false, afterColon: true}], 46 | 'keyword-spacing': [ERROR], 47 | 'lines-around-comment': OFF, 48 | // should be handled by a generic TXT linter instead 49 | 'linebreak-style': [OFF, 'unix'], 50 | 'max-depth': OFF, 51 | 'max-len': [ERROR, 120, 2, 52 | {'ignorePattern': maxLenIgnorePattern}, 53 | ], 54 | 'max-nested-callbacks': OFF, 55 | 'max-params': OFF, 56 | 'max-statements': OFF, 57 | // https://facebook.com/groups/995898333776940/1027358627297577 58 | 'new-cap': OFF, 59 | // equivalent to jshint W058 60 | 'new-parens': ERROR, 61 | 'newline-after-var': OFF, 62 | 'no-array-constructor': ERROR, 63 | 'no-bitwise': ERROR, 64 | 'no-continue': OFF, 65 | 'no-inline-comments': OFF, 66 | // doesn't play well with `if (__DEV__) {}` 67 | 'no-lonely-if': OFF, 68 | // stopgap, irrelevant if we can eventually turn `indent` on to error 69 | 'no-mixed-spaces-and-tabs': ERROR, 70 | // don't care 71 | 'no-multiple-empty-lines': OFF, 72 | 'no-negated-condition': OFF, 73 | // we do this a bunch of places, and it's less bad with proper indentation 74 | 'no-nested-ternary': OFF, 75 | // similar to FacebookWebJSLintLinter's checkPhpStyleArray 76 | 'no-new-object': ERROR, 77 | 'no-plusplus': OFF, 78 | 'no-restricted-syntax': OFF, 79 | 'no-spaced-func': ERROR, 80 | 'no-ternary': OFF, 81 | // should be handled by a generic TXT linter instead 82 | 'no-trailing-spaces': OFF, 83 | // we use this for private/protected identifiers 84 | 'no-underscore-dangle': OFF, 85 | // disallow `let isYes = answer === 1 ? true : false;` 86 | 'no-unneeded-ternary': ERROR, 87 | // too noisy ATM 88 | 'object-curly-spacing': OFF, 89 | // makes indentation warnings clearer 90 | 'one-var': [ERROR, {initialized: 'never'}], 91 | // prefer `x += 4` over `x = x + 4` 92 | 'operator-assignment': [ERROR, 'always'], 93 | // equivalent to jshint laxbreak 94 | 'operator-linebreak': OFF, 95 | 'padded-blocks': OFF, 96 | // probably too noisy on pre-ES5 code 97 | 'quote-props': [OFF, 'as-needed'], 98 | 'quotes': [ERROR, 'single', 'avoid-escape'], 99 | 'require-jsdoc': OFF, 100 | 'semi-spacing': [ERROR, {before: false, after: true}], 101 | // equivalent to jshint asi/W032 102 | 'semi': [ERROR, 'always'], 103 | 'sort-vars': OFF, 104 | // require `if () {` instead of `if (){` 105 | 'space-before-blocks': [ERROR, 'always'], 106 | // require `function foo()` instead of `function foo ()` 107 | 'space-before-function-paren': [ 108 | ERROR, 109 | {anonymous: 'never', named: 'never'}, 110 | ], 111 | // incompatible with our legacy inline type annotations 112 | 'space-in-parens': [OFF, 'never'], 113 | 'space-infix-ops': [ERROR, {int32Hint: true}], 114 | 'space-unary-ops': [ERROR, {words: true, nonwords: false}], 115 | // TODO: Figure out a way to do this that doesn't break typechecks 116 | // or wait for https://github.com/eslint/eslint/issues/2897 117 | 'spaced-comment': 118 | [OFF, 'always', {exceptions: ['jshint', 'jslint', 'eslint', 'global']}], 119 | 'wrap-regex': OFF, 120 | }, 121 | }; 122 | -------------------------------------------------------------------------------- /packages/eslint-config-charts/rules/variables.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const OFF = 0; 4 | const ERROR = 2; 5 | 6 | module.exports = { 7 | rules: { 8 | // we don't do this/care about this 9 | 'init-declarations': OFF, 10 | // equivalent to jshint W002, catches an IE8 bug 11 | 'no-catch-shadow': ERROR, 12 | // equivalent to jshint W051, is a strict mode violation 13 | 'no-delete-var': ERROR, 14 | // we should avoid labels anyways 15 | 'no-label-var': ERROR, 16 | // redefining undefined, NaN, Infinity, arguments, and eval is bad, mkay? 17 | 'no-shadow-restricted-names': ERROR, 18 | // a definite code-smell, but probably too noisy 19 | 'no-shadow': OFF, 20 | // it's nice to be explicit sometimes: `let foo = undefined;` 21 | 'no-undef-init': ERROR, 22 | // equivalent to jshint undef, turned into an error in getConfig 23 | 'no-undef': ERROR, 24 | // using undefined is safe because we enforce no-shadow-restricted-names 25 | 'no-undefined': OFF, 26 | // equivalent to jshint unused 27 | 'no-unused-vars': [ERROR, {args: 'none'}], 28 | // too noisy 29 | 'no-use-before-define': OFF, 30 | }, 31 | }; 32 | --------------------------------------------------------------------------------