├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .prettierrc ├── README.md ├── babel.config.js ├── cypress.json ├── package.json ├── public ├── favicon.ico ├── img │ └── icons │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon-120x120.png │ │ ├── apple-touch-icon-152x152.png │ │ ├── apple-touch-icon-180x180.png │ │ ├── apple-touch-icon-60x60.png │ │ ├── apple-touch-icon-76x76.png │ │ ├── apple-touch-icon.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── msapplication-icon-144x144.png │ │ ├── mstile-150x150.png │ │ └── safari-pinned-tab.svg ├── index.html ├── manifest.json └── robots.txt ├── src ├── App.vue ├── assets │ ├── geo │ │ └── 110m.json │ ├── logo.png │ ├── logos-sm.png │ ├── sass │ │ ├── _markdown.scss │ │ ├── _ui-controls.scss │ │ ├── _vue-slider.scss │ │ ├── fontawesome │ │ │ ├── _animated.scss │ │ │ ├── _bordered-pulled.scss │ │ │ ├── _core.scss │ │ │ ├── _fixed-width.scss │ │ │ ├── _icons.scss │ │ │ ├── _larger.scss │ │ │ ├── _list.scss │ │ │ ├── _mixins.scss │ │ │ ├── _path.scss │ │ │ ├── _rotated-flipped.scss │ │ │ ├── _screen-reader.scss │ │ │ ├── _stacked.scss │ │ │ ├── _variables.scss │ │ │ └── font-awesome.scss │ │ └── style.scss │ └── svg │ │ ├── line-width-brush.svg │ │ └── pie.svg ├── components │ ├── HelloWorld.vue │ ├── base │ │ ├── CheckBox.vue │ │ └── ValueSlider.vue │ ├── d3 │ │ ├── D3Arc.vue │ │ ├── D3Pie.vue │ │ └── finished │ │ │ ├── D3Arc.vue │ │ │ ├── D3Axis.vue │ │ │ ├── D3HierarchyLayout.vue │ │ │ ├── D3HierarchyLink.vue │ │ │ ├── D3HierarchyNode.vue │ │ │ ├── D3Line.vue │ │ │ ├── D3Pie.vue │ │ │ ├── PanZoom │ │ │ ├── ScaleTicks.vue │ │ │ └── index.vue │ │ │ ├── ParallelCoord │ │ │ ├── ColumnBrush.vue │ │ │ ├── DefaultColumn.vue │ │ │ └── index.vue │ │ │ ├── VersorDrag.vue │ │ │ └── WordCloud.vue │ └── navigation │ │ ├── SideBar.vue │ │ └── SideBarListItems.vue ├── main.ts ├── registerServiceWorker.ts ├── router │ ├── index.ts │ └── sections │ │ ├── demos.js │ │ ├── exercises.js │ │ └── index.js ├── router_old.ts ├── shims-tsx.d.ts ├── shims-vue.d.ts ├── store.ts ├── utils │ ├── mixins │ │ ├── MarkdownUtils.js │ │ └── bounds.js │ └── versor.js └── views │ ├── About.vue │ ├── BasePage.vue │ ├── ChartView.vue │ ├── Home.vue │ ├── MainPage.vue │ ├── PageView.vue │ ├── conclusion │ ├── index.vue │ └── readme.md │ ├── examples │ ├── axis │ │ ├── DemoComponent.vue │ │ ├── index.vue │ │ └── readme.md │ ├── clustered-bubble-charts │ │ ├── DemoComponent.vue │ │ ├── PopulationComponent.vue │ │ ├── index.vue │ │ └── readme.md │ ├── hierarchy │ │ ├── DemoComponent.vue │ │ ├── PopulationComponent.vue │ │ ├── circle-packing │ │ │ ├── CircleItem.vue │ │ │ ├── DemoComponent.vue │ │ │ ├── index.vue │ │ │ └── readme.md │ │ ├── cluster │ │ │ ├── DemoComponent.vue │ │ │ ├── index.vue │ │ │ └── readme.md │ │ ├── edge-bundling │ │ │ ├── DemoComponent.vue │ │ │ ├── TreemapSection.vue │ │ │ ├── index.vue │ │ │ └── readme.md │ │ ├── index.vue │ │ ├── partitions │ │ │ ├── DemoComponent.vue │ │ │ ├── index.vue │ │ │ └── readme.md │ │ ├── readme.md │ │ ├── threads │ │ │ ├── DemoComponent.vue │ │ │ ├── RecursiveTree.vue │ │ │ ├── ThreadArc.vue │ │ │ ├── index.vue │ │ │ └── readme.md │ │ ├── tree-map │ │ │ ├── DemoComponent.vue │ │ │ ├── index.vue │ │ │ └── readme.md │ │ └── tree │ │ │ ├── DemoComponent.vue │ │ │ ├── index.vue │ │ │ └── readme.md │ ├── index.vue │ ├── panzoom │ │ ├── DemoComponent.vue │ │ ├── index.vue │ │ └── readme.md │ ├── parallelcoords │ │ ├── DemoComponent.vue │ │ ├── index.vue │ │ └── readme.md │ ├── scaling │ │ ├── DemoComponent.vue │ │ ├── ScaleDisplay.vue │ │ ├── index.vue │ │ └── readme.md │ ├── ticks │ │ └── index.vue │ └── versor-dragging │ │ ├── index.vue │ │ └── readme.md │ ├── exercises │ ├── arc │ │ ├── index.vue │ │ └── readme.md │ ├── connecting-dots │ │ ├── component.vue │ │ ├── dataset.json │ │ ├── finished.vue │ │ ├── index.vue │ │ └── readme.md │ ├── creating-dots │ │ ├── component.vue │ │ ├── dataset.json │ │ ├── finished.vue │ │ ├── index.vue │ │ └── readme.md │ └── pie-chart │ │ ├── index.vue │ │ └── readme.md │ ├── introduction │ ├── index.vue │ └── readme.md │ ├── overview │ ├── D3View.vue │ ├── L1View.vue │ ├── RenderingWithVue.vue │ ├── index.vue │ └── readme.md │ └── resources │ ├── BrewerColors.vue │ ├── index.vue │ ├── semiology │ ├── index.vue │ └── readme.md │ ├── uxprinciples │ ├── index.vue │ └── readme.md │ └── xenographics │ ├── index.vue │ └── readme.md ├── static ├── .gitkeep ├── demo_data │ ├── cars.csv │ ├── gates.csv │ ├── geo │ │ └── 110m.json │ ├── hierarchy │ │ ├── edisco.json │ │ ├── flare.json │ │ ├── populations.json │ │ └── stats.json │ ├── nutrients.csv │ ├── quantifiable │ │ ├── bostock-repos.json │ │ ├── observables.json │ │ └── simple │ │ │ └── lineData.json │ ├── time │ │ ├── browser-usage.csv │ │ ├── nations.json │ │ └── user-activity.json │ ├── vgsales.csv │ └── winequality-red.csv ├── fonts │ └── fa │ │ ├── FontAwesome.otf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.svg │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ └── fontawesome-webfont.woff2 ├── images │ ├── d3-lib-overview.png │ └── semology-of-graphics.png └── markdown │ ├── 1 - introduction.md │ ├── 2 - overview.md │ ├── 3 - Vue and D3.md │ ├── 4 - Excercise 1.md │ └── test.md ├── tests ├── e2e │ ├── plugins │ │ └── index.js │ ├── specs │ │ └── test.js │ └── support │ │ ├── commands.js │ │ └── index.js └── unit │ ├── .eslintrc.js │ └── example.spec.ts ├── tsconfig.json ├── tslint.json ├── vue.config.js └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | /dist/** 2 | /node_modules/** 3 | /public/** 4 | /**/*.ts -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | 4 | env: { 5 | es6: true, 6 | node: true, 7 | browser: true 8 | }, 9 | 10 | extends: ['eslint:recommended', 'plugin:vue/essential'], 11 | plugins: ['html', 'prettier'], 12 | 13 | rules: { 14 | 'vue/return-in-computed-property': 'off', 15 | 'vue/no-side-effects-in-computed-properties': 'off', 16 | 'vue/attribute-hyphenation': 'error', 17 | 'vue/valid-v-model': 'warning', 18 | 'func-call-spacing': 2, 19 | 'no-multi-spaces': [ 20 | 'error', 21 | { 22 | exceptions: { 23 | Property: true, 24 | ImportDeclaration: true, 25 | VariableDeclarator: true 26 | } 27 | } 28 | ], 29 | 'no-mixed-spaces-and-tabs': 'error', 30 | 'key-spacing': [ 31 | 'error', 32 | { 33 | singleLine: { 34 | beforeColon: false, 35 | afterColon: true 36 | }, 37 | multiLine: { 38 | beforeColon: false, 39 | afterColon: true 40 | }, 41 | align: { 42 | beforeColon: false, 43 | afterColon: true, 44 | on: 'value' 45 | } 46 | } 47 | ], 48 | 'comma-spacing': ['error', { 49 | before: false, 50 | after: true 51 | }], 52 | 'no-tabs': 0, 53 | 'space-before-function-paren': 0, 54 | 'space-in-parens': 0, 55 | 'arrow-parens': 0, 56 | 'valid-jsdoc': [ 57 | 0, 58 | { 59 | requireReturn: false, 60 | requireReturnType: false 61 | } 62 | ], 63 | 'comma-dangle': [ 64 | 'error', 65 | 'never' 66 | ], 67 | 'generator-star-spacing': 0, 68 | indent: [ 69 | 'error', 70 | 2 71 | ], 72 | quotes: [ 73 | 'error', 74 | 'single' 75 | ], 76 | 'quote-props': [ 77 | 'error', 78 | 'as-needed' 79 | ], 80 | eqeqeq: [ 81 | 'error', 82 | 'always', 83 | { 84 | null: 'ignore' 85 | } 86 | ], 87 | 'prefer-arrow-callback': [ 88 | 'error', 89 | { 90 | allowNamedFunctions: false, 91 | allowUnboundThis: true 92 | } 93 | ], 94 | 'no-console': [ 95 | 0, 96 | { 97 | allow: [ 98 | 'warn' 99 | ] 100 | } 101 | ], 102 | 'no-inner-declarations': [ 103 | 'error', 104 | 'both' 105 | ], 106 | 'no-var': 'error', 107 | 'no-unused-vars': [ 108 | 'error', 109 | { 110 | vars: 'all', 111 | args: 'after-used', 112 | ignoreRestSiblings: false 113 | } 114 | ], 115 | 'no-func-assign': 'error', 116 | semi: [ 117 | 'error', 118 | 'never' 119 | ], 120 | 'no-debugger': 0, 121 | 'no-trailing-spaces': [ 122 | 2, 123 | { 124 | skipBlankLines: false 125 | } 126 | ] 127 | 128 | }, 129 | 130 | parserOptions: { 131 | parser: 'babel-eslint', 132 | sourceType: 'module', 133 | ecmaVersion: 6, 134 | ecmaFeatures: { 135 | jsx: true 136 | } 137 | } 138 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | /tests/e2e/videos/ 6 | /tests/e2e/screenshots/ 7 | 8 | # local env files 9 | .env.local 10 | .env.*.local 11 | 12 | # Log files 13 | npm-debug.log* 14 | yarn-debug.log* 15 | yarn-error.log* 16 | 17 | # Editor directories and files 18 | .idea 19 | .vscode 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw* 25 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "htmlWhitespaceSensitivity": "ignore", 5 | "trailingComma": "none", 6 | "jsxBracketSameLine": true, 7 | "proseWrap": "always" 8 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vue and D3 Workshop 2 | 3 | `Leveraging Design with Reactivity` 4 | 5 | ## Prerequisites 6 | 7 | - [Node](https://nodejs.org/en/) 8 | - [Vue Devtools](https://github.com/vuejs/vue-devtools) 9 | - [VS Code](https://code.visualstudio.com/) 10 | - [Vue Extension Pack](https://marketplace.visualstudio.com/items?itemName=mubaidr.vuejs-extension-pack) 11 | 12 | ## Build Setup 13 | 14 | ```bash 15 | # install dependencies 16 | npm install 17 | 18 | # serve with hot reload at localhost:8080 19 | npm run dev 20 | 21 | # build for production with minification 22 | npm run build 23 | ``` 24 | 25 | for more information, until i update this readme, check out some of these great 26 | resources: 27 | 28 | > ### Related to Data Analysis and Visualization 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | [\*\* Also check out his video!](https://www.youtube.com/watch?v=vc1bq0qIKoA) 41 | 42 | > ### Vue.js and other information based on SVG's 43 | 44 | [Vue.js](https://vuejs.org) 45 | 46 | [Vue.js Guide](https://vuejs.org/v2/guide) 47 | 48 | [Vue.js Styleguide](https://vuejs.org/v2/style-guide) 49 | 50 | [Inline SVG - Powers and Limitations](https://css-tricks.com/lodge/svg/20-2/) 51 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['@vue/app'] 3 | } 4 | -------------------------------------------------------------------------------- /cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "pluginsFile": "tests/e2e/plugins/index.js" 3 | } 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-d3-workshop", 3 | "version": "1.0.0", 4 | "private": true, 5 | "description": "A Vue.js project", 6 | "author": "Jonathan Snyder ", 7 | "scripts": { 8 | "serve": "vue-cli-service serve", 9 | "build": "vue-cli-service build", 10 | "lint": "vue-cli-service lint", 11 | "dev": "vue-cli-service serve --open", 12 | "test:e2e": "vue-cli-service test:e2e", 13 | "test:unit": "vue-cli-service test:unit" 14 | }, 15 | "dependencies": { 16 | "animate.css": "^3.7.0", 17 | "axios": "^0.21.1", 18 | "chroma-js": "^2.0.3", 19 | "crossfilter2": "^1.4.6", 20 | "d3": "^5.9.1", 21 | "d3-cloud": "^1.2.5", 22 | "lodash": "^4.17.11", 23 | "normalize.css": "^8.0.1", 24 | "register-service-worker": "^1.6.2", 25 | "topojson-client": "^3.0.0", 26 | "versor": "0.0.4", 27 | "vue": "^2.6.7", 28 | "vue-axios": "^2.1.4", 29 | "vue-class-component": "^7.0.1", 30 | "vue-markdown": "^2.2.4", 31 | "vue-property-decorator": "^7.3.0", 32 | "vue-router": "^3.0.1", 33 | "vuebar": "0.0.20", 34 | "vuex": "^3.1.0" 35 | }, 36 | "devDependencies": { 37 | "@types/chroma-js": "^1.4.1", 38 | "@types/crossfilter": "0.0.32", 39 | "@types/d3": "^5.7.1", 40 | "@types/d3.cloud.layout": "^1.2.32", 41 | "@types/gsap": "^1.20.1", 42 | "@types/jest": "^23.3.14", 43 | "@types/lodash": "^4.14.121", 44 | "@types/webpack-chain": "^5.0.1", 45 | "@vue/cli": "^3.4.1", 46 | "@vue/cli-plugin-babel": "^3.4.1", 47 | "@vue/cli-plugin-e2e-cypress": "^3.4.1", 48 | "@vue/cli-plugin-eslint": "^3.4.1", 49 | "@vue/cli-plugin-pwa": "^3.4.1", 50 | "@vue/cli-plugin-typescript": "^3.4.1", 51 | "@vue/cli-plugin-unit-jest": "^3.4.1", 52 | "@vue/cli-service": "^3.4.1", 53 | "@vue/eslint-config-prettier": "^4.0.1", 54 | "@vue/eslint-config-typescript": "^3.2.1", 55 | "@vue/test-utils": "^1.0.0-beta.29", 56 | "babel-core": "7.0.0-bridge.0", 57 | "babel-eslint": "^10.0.1", 58 | "copy-webpack-plugin": "^5.0.0", 59 | "core-js": "^2.6.5", 60 | "eslint": "^5.14.1", 61 | "eslint-config-prettier": "^4.1.0", 62 | "eslint-plugin-html": "^5.0.3", 63 | "eslint-plugin-prettier": "^3.0.1", 64 | "eslint-plugin-vue": "^5.2.2", 65 | "gsap": "^2.1.1", 66 | "html-loader": "^0.5.5", 67 | "markdown-loader": "^4.0.0", 68 | "node-sass": "^4.11.0", 69 | "sass-loader": "^7.0.1", 70 | "ts-jest": "^23.0.0", 71 | "tslint": "^5.13.0", 72 | "typescript": "^3.3.3333", 73 | "vue-resize": "^0.4.5", 74 | "vue-template-compiler": "^2.6.7" 75 | }, 76 | "postcss": { 77 | "plugins": { 78 | "autoprefixer": {} 79 | } 80 | }, 81 | "browserslist": [ 82 | "> 1%", 83 | "last 2 versions", 84 | "not ie <= 8" 85 | ], 86 | "jest": { 87 | "moduleFileExtensions": [ 88 | "js", 89 | "jsx", 90 | "json", 91 | "vue", 92 | "ts", 93 | "tsx" 94 | ], 95 | "transform": { 96 | "^.+\\.vue$": "vue-jest", 97 | ".+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$": "jest-transform-stub", 98 | "^.+\\.tsx?$": "ts-jest" 99 | }, 100 | "moduleNameMapper": { 101 | "^@/(.*)$": "/src/$1" 102 | }, 103 | "snapshotSerializers": [ 104 | "jest-serializer-vue" 105 | ], 106 | "testMatch": [ 107 | "**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)" 108 | ], 109 | "testURL": "http://localhost/" 110 | }, 111 | "engines": { 112 | "node": ">= 6.0.0", 113 | "npm": ">= 3.0.0" 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thegoodideaco/vue-d3-workshop/1934e6d82e1d98c680a1f6144f3bbbe80969eb99/public/favicon.ico -------------------------------------------------------------------------------- /public/img/icons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thegoodideaco/vue-d3-workshop/1934e6d82e1d98c680a1f6144f3bbbe80969eb99/public/img/icons/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/img/icons/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thegoodideaco/vue-d3-workshop/1934e6d82e1d98c680a1f6144f3bbbe80969eb99/public/img/icons/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thegoodideaco/vue-d3-workshop/1934e6d82e1d98c680a1f6144f3bbbe80969eb99/public/img/icons/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thegoodideaco/vue-d3-workshop/1934e6d82e1d98c680a1f6144f3bbbe80969eb99/public/img/icons/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thegoodideaco/vue-d3-workshop/1934e6d82e1d98c680a1f6144f3bbbe80969eb99/public/img/icons/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thegoodideaco/vue-d3-workshop/1934e6d82e1d98c680a1f6144f3bbbe80969eb99/public/img/icons/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thegoodideaco/vue-d3-workshop/1934e6d82e1d98c680a1f6144f3bbbe80969eb99/public/img/icons/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thegoodideaco/vue-d3-workshop/1934e6d82e1d98c680a1f6144f3bbbe80969eb99/public/img/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /public/img/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thegoodideaco/vue-d3-workshop/1934e6d82e1d98c680a1f6144f3bbbe80969eb99/public/img/icons/favicon-16x16.png -------------------------------------------------------------------------------- /public/img/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thegoodideaco/vue-d3-workshop/1934e6d82e1d98c680a1f6144f3bbbe80969eb99/public/img/icons/favicon-32x32.png -------------------------------------------------------------------------------- /public/img/icons/msapplication-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thegoodideaco/vue-d3-workshop/1934e6d82e1d98c680a1f6144f3bbbe80969eb99/public/img/icons/msapplication-icon-144x144.png -------------------------------------------------------------------------------- /public/img/icons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thegoodideaco/vue-d3-workshop/1934e6d82e1d98c680a1f6144f3bbbe80969eb99/public/img/icons/mstile-150x150.png -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | A Vue D3 Workshop 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "d3-workshop-v2", 3 | "short_name": "d3-workshop-v2", 4 | "icons": [ 5 | { 6 | "src": "./img/icons/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "./img/icons/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "start_url": "./index.html", 17 | "display": "standalone", 18 | "background_color": "#2D1C4E", 19 | "theme_color": "#4DBA87" 20 | } -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 30 | 31 | 58 | -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thegoodideaco/vue-d3-workshop/1934e6d82e1d98c680a1f6144f3bbbe80969eb99/src/assets/logo.png -------------------------------------------------------------------------------- /src/assets/logos-sm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thegoodideaco/vue-d3-workshop/1934e6d82e1d98c680a1f6144f3bbbe80969eb99/src/assets/logos-sm.png -------------------------------------------------------------------------------- /src/assets/sass/_markdown.scss: -------------------------------------------------------------------------------- 1 | pre { 2 | code { 3 | font-size: 0.7em; 4 | overflow-x: auto; 5 | display: block; 6 | } 7 | } 8 | 9 | .readme { 10 | font-size: 0.9rem; 11 | } 12 | -------------------------------------------------------------------------------- /src/assets/sass/_ui-controls.scss: -------------------------------------------------------------------------------- 1 | legend { 2 | background-color: #000; 3 | color: #fff; 4 | padding: 3px 6px; 5 | } 6 | 7 | .output { 8 | font: 1rem 'Fira Sans', sans-serif; 9 | } 10 | 11 | .value-slider { 12 | border: 1px dashed rgba(#fff, 0.5); 13 | padding: 5px; 14 | border-radius: 3px; 15 | margin-top: 10px; 16 | 17 | 18 | 19 | label { 20 | margin-bottom: 5px; 21 | display: block; 22 | font-size: .7rem; 23 | font-weight: bold; 24 | text-transform: uppercase; 25 | } 26 | 27 | input { 28 | width: 100%; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/assets/sass/_vue-slider.scss: -------------------------------------------------------------------------------- 1 | $bar-color: rgb(3, 246, 137); 2 | .vb { 3 | >.vb-dragger { 4 | z-index: 5; 5 | width: 12px; 6 | right: 0; 7 | >.vb-dragger-styler { 8 | backface-visibility: hidden; 9 | transform: rotate3d(0, 0, 0, 0); 10 | transition: background-color 100ms ease-out, margin 100ms ease-out, height 100ms ease-out; 11 | background-color: rgba($bar-color, .1); 12 | margin: 5px 5px 5px 0; 13 | border-radius: 20px; 14 | height: calc(100% - 10px); 15 | display: block; 16 | } 17 | } 18 | &.vb-scrolling-phantom>.vb-dragger>.vb-dragger-styler { 19 | background-color: rgba($bar-color, .3); 20 | } 21 | >.vb-dragger:hover>.vb-dragger-styler, 22 | &.vb-dragging>.vb-dragger>.vb-dragger-styler { 23 | background-color: rgba($bar-color, .5); 24 | // margin: 0px; 25 | height: 100%; 26 | } 27 | &.vb-dragging-phantom>.vb-dragger>.vb-dragger-styler { 28 | background-color: rgba($bar-color, .5); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/assets/sass/fontawesome/_animated.scss: -------------------------------------------------------------------------------- 1 | // Spinning Icons 2 | // -------------------------- 3 | 4 | .#{$fa-css-prefix}-spin { 5 | -webkit-animation: fa-spin 2s infinite linear; 6 | animation: fa-spin 2s infinite linear; 7 | } 8 | 9 | .#{$fa-css-prefix}-pulse { 10 | -webkit-animation: fa-spin 1s infinite steps(8); 11 | animation: fa-spin 1s infinite steps(8); 12 | } 13 | 14 | @-webkit-keyframes fa-spin { 15 | 0% { 16 | -webkit-transform: rotate(0deg); 17 | transform: rotate(0deg); 18 | } 19 | 100% { 20 | -webkit-transform: rotate(359deg); 21 | transform: rotate(359deg); 22 | } 23 | } 24 | 25 | @keyframes fa-spin { 26 | 0% { 27 | -webkit-transform: rotate(0deg); 28 | transform: rotate(0deg); 29 | } 30 | 100% { 31 | -webkit-transform: rotate(359deg); 32 | transform: rotate(359deg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/assets/sass/fontawesome/_bordered-pulled.scss: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-border { 5 | padding: .2em .25em .15em; 6 | border: solid .08em $fa-border-color; 7 | border-radius: .1em; 8 | } 9 | 10 | .#{$fa-css-prefix}-pull-left { float: left; } 11 | .#{$fa-css-prefix}-pull-right { float: right; } 12 | 13 | .#{$fa-css-prefix} { 14 | &.#{$fa-css-prefix}-pull-left { margin-right: .3em; } 15 | &.#{$fa-css-prefix}-pull-right { margin-left: .3em; } 16 | } 17 | 18 | /* Deprecated as of 4.4.0 */ 19 | .pull-right { float: right; } 20 | .pull-left { float: left; } 21 | 22 | .#{$fa-css-prefix} { 23 | &.pull-left { margin-right: .3em; } 24 | &.pull-right { margin-left: .3em; } 25 | } 26 | -------------------------------------------------------------------------------- /src/assets/sass/fontawesome/_core.scss: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix} { 5 | display: inline-block; 6 | font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/assets/sass/fontawesome/_fixed-width.scss: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .#{$fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /src/assets/sass/fontawesome/_larger.scss: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .#{$fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .#{$fa-css-prefix}-2x { font-size: 2em; } 11 | .#{$fa-css-prefix}-3x { font-size: 3em; } 12 | .#{$fa-css-prefix}-4x { font-size: 4em; } 13 | .#{$fa-css-prefix}-5x { font-size: 5em; } 14 | -------------------------------------------------------------------------------- /src/assets/sass/fontawesome/_list.scss: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: $fa-li-width; 7 | list-style-type: none; 8 | > li { position: relative; } 9 | } 10 | .#{$fa-css-prefix}-li { 11 | position: absolute; 12 | left: -$fa-li-width; 13 | width: $fa-li-width; 14 | top: (2em / 14); 15 | text-align: center; 16 | &.#{$fa-css-prefix}-lg { 17 | left: -$fa-li-width + (4em / 14); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/assets/sass/fontawesome/_mixins.scss: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | @mixin fa-icon() { 5 | display: inline-block; 6 | font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | } 13 | 14 | @mixin fa-icon-rotate($degrees, $rotation) { 15 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation})"; 16 | -webkit-transform: rotate($degrees); 17 | -ms-transform: rotate($degrees); 18 | transform: rotate($degrees); 19 | } 20 | 21 | @mixin fa-icon-flip($horiz, $vert, $rotation) { 22 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}, mirror=1)"; 23 | -webkit-transform: scale($horiz, $vert); 24 | -ms-transform: scale($horiz, $vert); 25 | transform: scale($horiz, $vert); 26 | } 27 | 28 | 29 | // Only display content to screen readers. A la Bootstrap 4. 30 | // 31 | // See: http://a11yproject.com/posts/how-to-hide-content/ 32 | 33 | @mixin sr-only { 34 | position: absolute; 35 | width: 1px; 36 | height: 1px; 37 | padding: 0; 38 | margin: -1px; 39 | overflow: hidden; 40 | clip: rect(0,0,0,0); 41 | border: 0; 42 | } 43 | 44 | // Use in conjunction with .sr-only to only display content when it's focused. 45 | // 46 | // Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 47 | // 48 | // Credit: HTML5 Boilerplate 49 | 50 | @mixin sr-only-focusable { 51 | &:active, 52 | &:focus { 53 | position: static; 54 | width: auto; 55 | height: auto; 56 | margin: 0; 57 | overflow: visible; 58 | clip: auto; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/assets/sass/fontawesome/_path.scss: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?v=#{$fa-version}'); 7 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?#iefix&v=#{$fa-version}') format('embedded-opentype'), 8 | url('#{$fa-font-path}/fontawesome-webfont.woff2?v=#{$fa-version}') format('woff2'), 9 | url('#{$fa-font-path}/fontawesome-webfont.woff?v=#{$fa-version}') format('woff'), 10 | url('#{$fa-font-path}/fontawesome-webfont.ttf?v=#{$fa-version}') format('truetype'), 11 | url('#{$fa-font-path}/fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular') format('svg'); 12 | // src: url('#{$fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | -------------------------------------------------------------------------------- /src/assets/sass/fontawesome/_rotated-flipped.scss: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-rotate-90 { @include fa-icon-rotate(90deg, 1); } 5 | .#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); } 6 | .#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); } 7 | 8 | .#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); } 9 | .#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(1, -1, 2); } 10 | 11 | // Hook for IE8-9 12 | // ------------------------- 13 | 14 | :root .#{$fa-css-prefix}-rotate-90, 15 | :root .#{$fa-css-prefix}-rotate-180, 16 | :root .#{$fa-css-prefix}-rotate-270, 17 | :root .#{$fa-css-prefix}-flip-horizontal, 18 | :root .#{$fa-css-prefix}-flip-vertical { 19 | filter: none; 20 | } 21 | -------------------------------------------------------------------------------- /src/assets/sass/fontawesome/_screen-reader.scss: -------------------------------------------------------------------------------- 1 | // Screen Readers 2 | // ------------------------- 3 | 4 | .sr-only { @include sr-only(); } 5 | .sr-only-focusable { @include sr-only-focusable(); } 6 | -------------------------------------------------------------------------------- /src/assets/sass/fontawesome/_stacked.scss: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .#{$fa-css-prefix}-stack-1x, .#{$fa-css-prefix}-stack-2x { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | text-align: center; 17 | } 18 | .#{$fa-css-prefix}-stack-1x { line-height: inherit; } 19 | .#{$fa-css-prefix}-stack-2x { font-size: 2em; } 20 | .#{$fa-css-prefix}-inverse { color: $fa-inverse; } 21 | -------------------------------------------------------------------------------- /src/assets/sass/fontawesome/font-awesome.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | 6 | @import "variables"; 7 | @import "mixins"; 8 | @import "path"; 9 | @import "core"; 10 | @import "larger"; 11 | @import "fixed-width"; 12 | @import "list"; 13 | @import "bordered-pulled"; 14 | @import "animated"; 15 | @import "rotated-flipped"; 16 | @import "stacked"; 17 | @import "icons"; 18 | @import "screen-reader"; 19 | -------------------------------------------------------------------------------- /src/assets/sass/style.scss: -------------------------------------------------------------------------------- 1 | @import './fontawesome/font-awesome'; 2 | @import url('https://fonts.googleapis.com/css?family=Lato:100,300,400,400i,700'); 3 | @import './_ui-controls'; 4 | @import './_vue-slider'; 5 | @import './_markdown'; 6 | 7 | 8 | 9 | * { 10 | box-sizing: border-box; 11 | } 12 | 13 | html, 14 | body { 15 | font-size: 20px; 16 | font-family: 'Lato', Arial, sans-serif; 17 | font-weight: 300; 18 | height: 100%; 19 | } 20 | 21 | h1, 22 | h2, 23 | h3, 24 | h4, 25 | h5 { 26 | font-weight: 300; 27 | margin-top: 0; 28 | margin-bottom: 10px; 29 | color: #03f68a; 30 | >span { 31 | font-weight: 500; 32 | color: saturate(#41b883, 50); 33 | } 34 | &.small { 35 | font-size: 0.8rem; 36 | } 37 | } 38 | 39 | h2 { 40 | color: #f89d41; 41 | } 42 | 43 | a { 44 | text-decoration: none; 45 | color: #41b883; 46 | font-weight: 400; 47 | &:hover, 48 | &:active, 49 | &:focus, 50 | &.router-link-exact-active { 51 | color: darken(#fff, 1); 52 | } 53 | } 54 | 55 | .router-link-active { 56 | color: #fff; 57 | } 58 | 59 | .router-link-exact-active { 60 | pointer-events: none; 61 | } 62 | 63 | #app { 64 | position: relative; 65 | -webkit-font-smoothing: antialiased; 66 | -moz-osx-font-smoothing: grayscale; 67 | background-color: #203243; 68 | color: #fcfcfc; 69 | min-height: 100%; 70 | } 71 | 72 | $page-transition-speed: 200ms; 73 | .page-fade-leave-active { 74 | animation-duration: $page-transition-speed; 75 | animation-fill-mode: both; 76 | animation-name: fadeOut; 77 | animation-timing-function: cubic-bezier(0.78, 0.03, 1, 1.02); 78 | } 79 | 80 | .page-fade-enter-active { 81 | animation-duration: $page-transition-speed; 82 | animation-fill-mode: both; 83 | animation-name: fadeIn; 84 | animation-timing-function: cubic-bezier(0, 0.71, 0.58, 1); 85 | } 86 | 87 | div.page-container { 88 | // padding-right: 40px; 89 | article { 90 | // display: flex; 91 | // flex-flow: column nowrap; 92 | flex: 0 0 500px; // height: 100%; 93 | // padding-right: 50px; 94 | img { 95 | max-width: 100%; 96 | height: auto; 97 | } 98 | section { 99 | >div { 100 | margin-right: 50px; 101 | } 102 | } 103 | } 104 | } 105 | 106 | .section-content { 107 | // padding-right: 50px; 108 | blockquote { 109 | background-color: rgb(23, 37, 51); 110 | font-style: italic; 111 | font-weight: 300; 112 | font-size: 0.9rem; 113 | color: #fcfcfc; 114 | margin: 0; 115 | padding: 5px 15px; 116 | border: 1px dashed rgb(28, 221, 135); 117 | margin-bottom: 15px; 118 | } 119 | } 120 | 121 | .example-content { 122 | position: relative; 123 | display: flex; 124 | align-items: center; 125 | justify-content: center; 126 | flex: 1 1 auto; 127 | overflow: hidden; 128 | padding: 50px; 129 | // margin-left: auto; 130 | circle { 131 | cursor: pointer; 132 | } 133 | >div { 134 | width: 100%; 135 | height: 100%; 136 | } 137 | .well { 138 | flex: 1; 139 | width: 100%; 140 | height: 100%; 141 | background-color: rgba(#fff, 0.1); 142 | } 143 | svg { 144 | position: relative; 145 | top: 50%; 146 | left: 50%; 147 | height: inherit; 148 | width: inherit; 149 | display: block; 150 | overflow: visible; 151 | } 152 | .centered { 153 | transform: translateX(50%) translateY(50%); 154 | } 155 | } 156 | 157 | .gpu, 158 | .vb-content { 159 | transform: translateZ(0); 160 | position: relative; 161 | } 162 | 163 | .fill { 164 | height: 100%; 165 | width: 100%; 166 | } 167 | -------------------------------------------------------------------------------- /src/assets/svg/pie.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 126 | 127 | 134 | 135 | 136 | 137 | 153 | -------------------------------------------------------------------------------- /src/components/base/CheckBox.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 38 | 39 | 56 | -------------------------------------------------------------------------------- /src/components/base/ValueSlider.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 57 | 58 | 63 | 64 | -------------------------------------------------------------------------------- /src/components/d3/D3Arc.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 44 | -------------------------------------------------------------------------------- /src/components/d3/D3Pie.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 58 | -------------------------------------------------------------------------------- /src/components/d3/finished/D3Arc.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 98 | 99 | 108 | -------------------------------------------------------------------------------- /src/components/d3/finished/D3Axis.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 85 | 86 | 117 | -------------------------------------------------------------------------------- /src/components/d3/finished/D3HierarchyLayout.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 122 | 123 | 128 | -------------------------------------------------------------------------------- /src/components/d3/finished/D3HierarchyLink.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/components/d3/finished/D3HierarchyNode.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 71 | 72 | 77 | -------------------------------------------------------------------------------- /src/components/d3/finished/D3Line.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 47 | 48 | 54 | -------------------------------------------------------------------------------- /src/components/d3/finished/D3Pie.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 92 | -------------------------------------------------------------------------------- /src/components/d3/finished/PanZoom/ScaleTicks.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 85 | 86 | 117 | -------------------------------------------------------------------------------- /src/components/d3/finished/ParallelCoord/ColumnBrush.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 155 | 156 | 168 | -------------------------------------------------------------------------------- /src/components/d3/finished/ParallelCoord/DefaultColumn.vue: -------------------------------------------------------------------------------- 1 | 48 | 49 | 139 | 140 | 166 | -------------------------------------------------------------------------------- /src/components/navigation/SideBar.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 39 | 40 | 91 | -------------------------------------------------------------------------------- /src/components/navigation/SideBarListItems.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import Axios from 'axios' 2 | import Vue from 'vue' 3 | import VueAxios from 'vue-axios' 4 | import App from './App.vue' 5 | import './registerServiceWorker' 6 | import router from './router/index' 7 | import store from './store' 8 | 9 | import '@/assets/sass/style.scss' 10 | import 'animate.css' 11 | import 'normalize.css' 12 | import 'vue-resize/dist/vue-resize.css' 13 | import VueResize from 'vue-resize' 14 | 15 | import chroma from 'chroma-js' 16 | import Vuebar from 'vuebar' 17 | 18 | import _ from 'lodash' 19 | 20 | // TODO: Remove this 21 | const w = window as any 22 | w._ = _ 23 | 24 | const debug = process.env.NODE_ENV !== 'production' 25 | 26 | Vue.config.productionTip = false 27 | Vue.config.performance = debug 28 | 29 | Vue.use(VueAxios, Axios) 30 | 31 | // Add custom scrollbar so it doesn't make things look ewwie 32 | Vue.use(Vuebar) 33 | 34 | Vue.use(VueResize) 35 | 36 | // These are globally added to ALL components, beware 37 | Vue.mixin({ 38 | data() { 39 | return { 40 | colors: chroma.scale(chroma.brewer.Set3).colors 41 | } 42 | } 43 | }) 44 | 45 | if (debug) { 46 | Vue.mixin({ 47 | methods: { 48 | openInEditor() { 49 | return Axios.get('__open-in-editor', { 50 | params: { 51 | file: (this.$options as any).__file 52 | } 53 | }) 54 | } 55 | } 56 | }) 57 | } 58 | 59 | new Vue({ 60 | router, 61 | store, 62 | render: h => h(App) 63 | }).$mount('#app') 64 | -------------------------------------------------------------------------------- /src/registerServiceWorker.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:no-console */ 2 | 3 | import { register } from 'register-service-worker' 4 | 5 | if (process.env.NODE_ENV === 'production') { 6 | register(`${process.env.BASE_URL}service-worker.js`, { 7 | ready() { 8 | console.log( 9 | 'App is being served from cache by a service worker.\n' + 10 | 'For more details, visit https://goo.gl/AFskqB' 11 | ) 12 | }, 13 | registered() { 14 | console.log('Service worker has been registered.') 15 | }, 16 | cached() { 17 | console.log('Content has been cached for offline use.') 18 | }, 19 | updatefound() { 20 | console.log('New content is downloading.') 21 | }, 22 | updated() { 23 | console.log('New content is available; please refresh.') 24 | }, 25 | offline() { 26 | console.log( 27 | 'No internet connection found. App is running in offline mode.' 28 | ) 29 | }, 30 | error(error) { 31 | console.error('Error during service worker registration:', error) 32 | } 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /src/router/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router, { RouteConfig } from 'vue-router' 3 | 4 | import sections from './sections/exercises' 5 | 6 | import demos from './sections/demos' 7 | 8 | Vue.use(Router) 9 | 10 | export default new Router({ 11 | routes: [ 12 | { 13 | path: '/', 14 | name: 'Introduction', 15 | component: () => import('@/views/introduction/index.vue') 16 | }, 17 | { 18 | path: '/overview', 19 | name: 'Overview', 20 | component: () => import('@/views/overview/index.vue') 21 | }, 22 | ...sections, 23 | // { 24 | // path: '/conclusion', 25 | // name: 'Conclusion', 26 | // component: () => 27 | // import ('@/views/conclusion') 28 | // }, 29 | { 30 | path: '/resources', 31 | name: 'Resources', 32 | component: () => import('@/views/resources/index.vue'), 33 | redirect: { 34 | name: 'Brewer Colors' 35 | }, 36 | children: [ 37 | { 38 | name: 'Brewer Colors', 39 | path: 'brewer', 40 | component: () => import('@/views/resources/BrewerColors.vue') 41 | }, 42 | { 43 | name: 'Xenographics', 44 | path: 'xenographics', 45 | component: () => import('@/views/resources/xenographics/index.vue') 46 | }, 47 | { 48 | name: 'Semiology of Graphics', 49 | path: 'semiology-of-graphics', 50 | component: () => import('@/views/resources/semiology/index.vue') 51 | }, 52 | { 53 | name: 'UX Principles as Algorithms', 54 | path: 'ux-principles', 55 | component: () => import('@/views/resources/uxprinciples/index.vue') 56 | } 57 | ] 58 | }, 59 | demos, 60 | { 61 | path: '/chart', 62 | component: () => import('@/views/ChartView.vue') 63 | }, 64 | { 65 | path: '*', 66 | redirect: '/' 67 | } 68 | ] 69 | }) 70 | -------------------------------------------------------------------------------- /src/router/sections/demos.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {import('vue-router').RouteConfig} 3 | */ 4 | const config = { 5 | path: '/demos', 6 | name: 'Demos', 7 | 8 | component: () => import('@/views/examples'), 9 | children: [ 10 | { 11 | path: 'parallelcoords', 12 | name: 'Parallel Coords', 13 | component: () => import('@/views/examples/parallelcoords/index.vue') 14 | }, 15 | { 16 | path: 'scales', 17 | name: 'Scales', 18 | component: () => import('@/views/examples/scaling/index.vue') 19 | }, 20 | { 21 | path: 'axis', 22 | name: 'Axis', 23 | component: () => import('@/views/examples/axis/index.vue') 24 | }, 25 | { 26 | path: 'pan-zoom', 27 | name: 'Pan and Zoom', 28 | component: () => import('@/views/examples/panzoom/index.vue') 29 | }, 30 | { 31 | path: 'versor', 32 | name: 'Versor Dragging', 33 | component: () => import('@/views/examples/versor-dragging/index.vue') 34 | }, 35 | { 36 | path: 'cluster-bubble', 37 | name: 'Clustered Bubble Charts', 38 | component: () => 39 | import('@/views/examples/clustered-bubble-charts/index.vue') 40 | }, 41 | { 42 | path: 'hierarchy', 43 | name: 'Visualizing Hierarchies', 44 | component: () => import('@/views/examples/hierarchy/index.vue'), 45 | children: [ 46 | { 47 | path: 'cluster', 48 | name: 'Clusters', 49 | component: () => 50 | import('@/views/examples/hierarchy/cluster/index.vue') 51 | }, 52 | { 53 | path: 'tree', 54 | name: 'Tree Layout', 55 | component: () => import('@/views/examples/hierarchy/tree/index.vue') 56 | }, 57 | { 58 | path: 'treemap', 59 | name: 'Treemap Layout', 60 | component: () => 61 | import('@/views/examples/hierarchy/tree-map/index.vue') 62 | }, 63 | { 64 | path: 'partition', 65 | name: 'Partition Layout', 66 | component: () => 67 | import('@/views/examples/hierarchy/partitions/index.vue') 68 | }, 69 | { 70 | path: 'pack', 71 | name: 'Circle Packing', 72 | component: () => 73 | import('@/views/examples/hierarchy/circle-packing/index.vue') 74 | } 75 | ] 76 | } 77 | ] 78 | } 79 | 80 | /** 81 | * Manages sections for demo displays 82 | * @module router-demos 83 | */ 84 | export default config 85 | -------------------------------------------------------------------------------- /src/router/sections/exercises.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | path: '/making-dots', 4 | name: 'Making Dots', 5 | component: () => import('@/views/exercises/creating-dots') 6 | }, 7 | { 8 | path: '/connecting-dots', 9 | name: 'Connecting the Dots', 10 | component: () => import('@/views/exercises/connecting-dots') 11 | }, 12 | { 13 | path: '/arc', 14 | name: 'Creating an Arc', 15 | component: () => import('@/views/exercises/arc') 16 | }, 17 | { 18 | path: '/pie', 19 | name: 'Composing a Pie', 20 | component: () => import('@/views/exercises/pie-chart') 21 | } 22 | ] 23 | -------------------------------------------------------------------------------- /src/router/sections/index.js: -------------------------------------------------------------------------------- 1 | import exercises from './exercises' 2 | 3 | export default [...exercises] 4 | -------------------------------------------------------------------------------- /src/router_old.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import Home from './views/Home.vue' 4 | 5 | Vue.use(Router) 6 | 7 | export default new Router({ 8 | routes: [ 9 | { 10 | path: '/', 11 | name: 'home', 12 | component: Home 13 | }, 14 | { 15 | path: '/about', 16 | name: 'about', 17 | // route level code-splitting 18 | // this generates a separate chunk (about.[hash].js) for this route 19 | // which is lazy-loaded when the route is visited. 20 | component: () => 21 | import(/* webpackChunkName: "about" */ './views/About.vue') 22 | } 23 | ] 24 | }) 25 | -------------------------------------------------------------------------------- /src/shims-tsx.d.ts: -------------------------------------------------------------------------------- 1 | import Vue, { VNode } from 'vue' 2 | import { LoDashStatic } from 'lodash'; 3 | 4 | declare global { 5 | namespace JSX { 6 | // tslint:disable no-empty-interface 7 | interface Element extends VNode {} 8 | // tslint:disable no-empty-interface 9 | interface ElementClass extends Vue {} 10 | interface IntrinsicElements { 11 | [elem: string]: any 12 | } 13 | } 14 | 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import Vue from 'vue' 3 | export default Vue 4 | } 5 | 6 | declare module 'vuebar' 7 | 8 | declare module '*.md' { 9 | export default String 10 | } 11 | 12 | declare module '*.json' { 13 | export default Object 14 | } -------------------------------------------------------------------------------- /src/store.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | Vue.use(Vuex) 5 | 6 | export default new Vuex.Store({ 7 | state: {}, 8 | mutations: {}, 9 | actions: {} 10 | }) 11 | -------------------------------------------------------------------------------- /src/utils/mixins/MarkdownUtils.js: -------------------------------------------------------------------------------- 1 | export default { 2 | beforeMount() { 3 | const div = document.createElement('div') 4 | div.innerHTML = this.readme 5 | const links = div.querySelectorAll('a') 6 | links.forEach(element => { 7 | element.target = '_blank' 8 | }) 9 | 10 | this.readme = div.innerHTML 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/utils/mixins/bounds.js: -------------------------------------------------------------------------------- 1 | export default { 2 | data() { 3 | return { 4 | dimensions: { 5 | width: 0, 6 | height: 0 7 | // viewBox: '0 0 700 700' 8 | } 9 | } 10 | }, 11 | methods: { 12 | updateDimensions() { 13 | // _.debounce(() => { 14 | this.$nextTick(() => { 15 | const bounds = (this.$refs.svg || this.$el).getBoundingClientRect() 16 | this.dimensions.width = bounds.width 17 | this.dimensions.height = bounds.height 18 | }) 19 | } 20 | }, 21 | mounted() { 22 | window.addEventListener('resize', this.updateDimensions) 23 | this.updateDimensions() 24 | 25 | }, 26 | activated() { 27 | window.addEventListener('resize', this.updateDimensions) 28 | }, 29 | deactivated() { 30 | window.removeEventListener('resize', this.updateDimensions) 31 | }, 32 | beforeDestroy() { 33 | window.removeEventListener('resize', this.updateDimensions) 34 | } 35 | } -------------------------------------------------------------------------------- /src/utils/versor.js: -------------------------------------------------------------------------------- 1 | let acos = Math.acos, 2 | asin = Math.asin, 3 | atan2 = Math.atan2, 4 | cos = Math.cos, 5 | max = Math.max, 6 | min = Math.min, 7 | PI = Math.PI, 8 | sin = Math.sin, 9 | sqrt = Math.sqrt, 10 | radians = PI / 180, 11 | degrees = 180 / PI 12 | 13 | // Returns the unit quaternion for the given Euler rotation angles [λ, φ, γ]. 14 | let versor = function(e) { 15 | let l = (e[0] / 2) * radians, 16 | sl = sin(l), 17 | cl = cos(l), // λ / 2 18 | p = (e[1] / 2) * radians, 19 | sp = sin(p), 20 | cp = cos(p), // φ / 2 21 | g = (e[2] / 2) * radians, 22 | sg = sin(g), 23 | cg = cos(g) // γ / 2 24 | return [ 25 | cl * cp * cg + sl * sp * sg, 26 | sl * cp * cg - cl * sp * sg, 27 | cl * sp * cg + sl * cp * sg, 28 | cl * cp * sg - sl * sp * cg 29 | ] 30 | } 31 | 32 | // Returns Cartesian coordinates [x, y, z] given spherical coordinates [λ, φ]. 33 | versor.cartesian = function(e) { 34 | let l = e[0] * radians, 35 | p = e[1] * radians, 36 | cp = cos(p) 37 | return [cp * cos(l), cp * sin(l), sin(p)] 38 | } 39 | 40 | // Returns the Euler rotation angles [λ, φ, γ] for the given quaternion. 41 | versor.rotation = function(q) { 42 | return [ 43 | atan2( 44 | 2 * (q[0] * q[1] + q[2] * q[3]), 45 | 1 - 2 * (q[1] * q[1] + q[2] * q[2]) 46 | ) * degrees, 47 | asin(max(-1, min(1, 2 * (q[0] * q[2] - q[3] * q[1])))) * degrees, 48 | atan2( 49 | 2 * (q[0] * q[3] + q[1] * q[2]), 50 | 1 - 2 * (q[2] * q[2] + q[3] * q[3]) 51 | ) * degrees 52 | ] 53 | } 54 | 55 | // Returns the quaternion to rotate between two cartesian points on the sphere. 56 | // alpha for tweening [0,1] 57 | versor.delta = function(v0, v1, alpha) { 58 | if (arguments.length === 2) alpha = 1 59 | let w = cross(v0, v1), 60 | l = sqrt(dot(w, w)) 61 | if (!l) return [1, 0, 0, 0] 62 | let t = (alpha * acos(max(-1, min(1, dot(v0, v1))))) / 2, 63 | s = sin(t) // t = θ / 2 64 | return [cos(t), (w[2] / l) * s, (-w[1] / l) * s, (w[0] / l) * s] 65 | } 66 | 67 | // Returns the quaternion that represents q0 * q1. 68 | versor.multiply = function(q0, q1) { 69 | return [ 70 | q0[0] * q1[0] - q0[1] * q1[1] - q0[2] * q1[2] - q0[3] * q1[3], 71 | q0[0] * q1[1] + q0[1] * q1[0] + q0[2] * q1[3] - q0[3] * q1[2], 72 | q0[0] * q1[2] - q0[1] * q1[3] + q0[2] * q1[0] + q0[3] * q1[1], 73 | q0[0] * q1[3] + q0[1] * q1[2] - q0[2] * q1[1] + q0[3] * q1[0] 74 | ] 75 | } 76 | 77 | function cross(v0, v1) { 78 | return [ 79 | v0[1] * v1[2] - v0[2] * v1[1], 80 | v0[2] * v1[0] - v0[0] * v1[2], 81 | v0[0] * v1[1] - v0[1] * v1[0] 82 | ] 83 | } 84 | 85 | function dot(v0, v1) { 86 | return v0[0] * v1[0] + v0[1] * v1[1] + v0[2] * v1[2] 87 | } 88 | 89 | export default versor 90 | -------------------------------------------------------------------------------- /src/views/About.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/views/BasePage.vue: -------------------------------------------------------------------------------- 1 | 42 | 43 | 103 | 104 | 173 | -------------------------------------------------------------------------------- /src/views/ChartView.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 13 | 14 | 29 | -------------------------------------------------------------------------------- /src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 16 | -------------------------------------------------------------------------------- /src/views/MainPage.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 43 | -------------------------------------------------------------------------------- /src/views/PageView.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 12 | 13 | 18 | -------------------------------------------------------------------------------- /src/views/conclusion/index.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 43 | -------------------------------------------------------------------------------- /src/views/conclusion/readme.md: -------------------------------------------------------------------------------- 1 | # Final Words 2 | 3 | I wish I could write more, but I'm out of time. I hoped you all enjoyed this workshop, and learned a couple new things tonight. 4 | 5 | I can't thank you all enough, for having me here! -------------------------------------------------------------------------------- /src/views/examples/axis/DemoComponent.vue: -------------------------------------------------------------------------------- 1 | 59 | 60 | 139 | 140 | 187 | -------------------------------------------------------------------------------- /src/views/examples/axis/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 27 | -------------------------------------------------------------------------------- /src/views/examples/axis/readme.md: -------------------------------------------------------------------------------- 1 | # D3-Axis as a Component 2 | 3 | `D3-Axis` is meant for displaying visual ticks. These represent equidistant 4 | positions along a particular dimension 5 | 6 | These are incredibly useful not just for accurately depicting scale, but for the 7 | use of navigating, and zooming into visual data. A holistic view is best when it 8 | is part of a system where the user can navigate within the data. For example, 9 | zooming in on a month worth of data that is part of a 2 year timeline 10 | 11 | D3's Axis library is actually quite simple. The core functionality revolves 12 | around `D3-Scale`. 13 | 14 | `D3-Scale` provides tick functionality, whereas `D3-Axis` is made to display it, 15 | and manipulate the DOM. In reality this can be converted to a collection of 16 | components that use Vue's template syntax, to allow for more customization. For 17 | example, providing a slot per tick, to allow more abstraction. 18 | -------------------------------------------------------------------------------- /src/views/examples/clustered-bubble-charts/PopulationComponent.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 20 | 21 | 26 | -------------------------------------------------------------------------------- /src/views/examples/clustered-bubble-charts/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 41 | 42 | 58 | -------------------------------------------------------------------------------- /src/views/examples/clustered-bubble-charts/readme.md: -------------------------------------------------------------------------------- 1 | # Clustered Bubble Charts 2 | 3 | This clustered [force layout](https://bl.ocks.org/mbostock/4062045) is 4 | implemented using two custom forces. The first, `cluster`, pushes nodes towards 5 | the largest node of the same color. A second `collide` force prevents circles 6 | from overlapping by 7 | [detecting collisions](https://bl.ocks.org/mbostock/3231298). 8 | 9 | This example uses standard gravity; compare to 10 | [custom gravity](https://bl.ocks.org/mbostock/1748247) applied only to the 11 | largest node of each color. To minimize distracting jitter during 12 | initialization, try an [entry transition](https://bl.ocks.org/mbostock/7881887). 13 | -------------------------------------------------------------------------------- /src/views/examples/hierarchy/PopulationComponent.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 20 | 21 | 26 | -------------------------------------------------------------------------------- /src/views/examples/hierarchy/circle-packing/CircleItem.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 24 | 25 | 30 | -------------------------------------------------------------------------------- /src/views/examples/hierarchy/circle-packing/index.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 41 | 42 | 58 | -------------------------------------------------------------------------------- /src/views/examples/hierarchy/circle-packing/readme.md: -------------------------------------------------------------------------------- 1 | # Circle Packing 2 | 3 | Circle Packing is similar to tree maps, but create a circular pattern. They are 4 | clustered by parent, and sized according to their value to one another. 5 | -------------------------------------------------------------------------------- /src/views/examples/hierarchy/cluster/DemoComponent.vue: -------------------------------------------------------------------------------- 1 | 43 | 44 | 116 | 117 | 163 | -------------------------------------------------------------------------------- /src/views/examples/hierarchy/cluster/index.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 41 | 42 | 58 | -------------------------------------------------------------------------------- /src/views/examples/hierarchy/cluster/readme.md: -------------------------------------------------------------------------------- 1 | # Cluster Layout 2 | 3 | The **cluster layout** produces 4 | [dendrograms](http://en.wikipedia.org/wiki/Dendrogram): node-link diagrams that 5 | place leaf nodes of the tree at the same depth. Dendograms are typically less 6 | compact than [tidy trees](#/demos/hierarchy/tree), but are useful when all the leaves should be 7 | at the same level, such as for hierarchical clustering or 8 | [phylogenetic tree diagrams](http://bl.ocks.org/mbostock/c034d66572fd6bd6815a). 9 | -------------------------------------------------------------------------------- /src/views/examples/hierarchy/edge-bundling/TreemapSection.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 27 | 28 | 33 | -------------------------------------------------------------------------------- /src/views/examples/hierarchy/edge-bundling/index.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 46 | 47 | 63 | -------------------------------------------------------------------------------- /src/views/examples/hierarchy/edge-bundling/readme.md: -------------------------------------------------------------------------------- 1 | # Circle Packing 2 | 3 | Circle Packing is similar to tree maps, but create a circular pattern. They are 4 | clustered by parent, and sized according to their value to one another. 5 | -------------------------------------------------------------------------------- /src/views/examples/hierarchy/index.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 71 | 72 | 93 | -------------------------------------------------------------------------------- /src/views/examples/hierarchy/partitions/DemoComponent.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 156 | 157 | 174 | -------------------------------------------------------------------------------- /src/views/examples/hierarchy/partitions/index.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 41 | 42 | 58 | -------------------------------------------------------------------------------- /src/views/examples/hierarchy/partitions/readme.md: -------------------------------------------------------------------------------- 1 | # Partition Layout 2 | 3 | The `partition layout` produces adjacency diagrams: a space-filling variant of a node-link tree diagram. Rather than drawing a link between parent and child in the hierarchy, nodes are drawn as solid areas (either arcs or rectangles), and their placement relative to other nodes reveals their position in the hierarchy. The size of the nodes encodes a quantitative dimension that would be difficult to show in a node-link diagram. 4 | -------------------------------------------------------------------------------- /src/views/examples/hierarchy/readme.md: -------------------------------------------------------------------------------- 1 | # Visualizing Hierarchies 2 | 3 | ## 2D layout algorithms for visualizing hierarchical data 4 | 5 | This example shows a couple of ways that we can visualize a nested tree-like 6 | structure. 7 | 8 | In this example, we are providing a dataset of nested children, representing 9 | folders. Each folder has a value that represents the size of that individual 10 | folder. 11 | 12 | This is only one type of example, however there are alot of ways we can use a 13 | hierarchy to determine other useful data displays, such as a 14 | [word tree](https://www.jasondavies.com/wordtree/?source=cat-in-the-hat.txt&prefix=Thing&reverse=0&phrase-line=0) 15 | -------------------------------------------------------------------------------- /src/views/examples/hierarchy/threads/DemoComponent.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 101 | 102 | 124 | -------------------------------------------------------------------------------- /src/views/examples/hierarchy/threads/RecursiveTree.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/views/examples/hierarchy/threads/ThreadArc.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 89 | 90 | 99 | -------------------------------------------------------------------------------- /src/views/examples/hierarchy/threads/index.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 35 | 36 | 52 | -------------------------------------------------------------------------------- /src/views/examples/hierarchy/threads/readme.md: -------------------------------------------------------------------------------- 1 | # EMail Threading 2 | 3 | E-mail threading is great! 4 | -------------------------------------------------------------------------------- /src/views/examples/hierarchy/tree-map/index.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 41 | 42 | 58 | -------------------------------------------------------------------------------- /src/views/examples/hierarchy/tree-map/readme.md: -------------------------------------------------------------------------------- 1 | # Circle Packing 2 | 3 | Circle Packing is similar to tree maps, but create a circular pattern. They are 4 | clustered by parent, and sized according to their value to one another. 5 | -------------------------------------------------------------------------------- /src/views/examples/hierarchy/tree/index.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 41 | 42 | 58 | -------------------------------------------------------------------------------- /src/views/examples/hierarchy/tree/readme.md: -------------------------------------------------------------------------------- 1 | # Tree Layout 2 | 3 | Tree Layouts show the hierarchy similar to the cluster layout, however the axis 4 | is matched based on the depth of the nodes. 5 | -------------------------------------------------------------------------------- /src/views/examples/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | 9 | 16 | -------------------------------------------------------------------------------- /src/views/examples/panzoom/DemoComponent.vue: -------------------------------------------------------------------------------- 1 | 54 | 55 | 107 | 108 | 168 | -------------------------------------------------------------------------------- /src/views/examples/panzoom/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 27 | -------------------------------------------------------------------------------- /src/views/examples/panzoom/readme.md: -------------------------------------------------------------------------------- 1 | # D3-Axis as a Component 2 | 3 | `D3-Axis` is meant for displaying visual ticks. These represent equidistant 4 | positions along a particular dimension 5 | 6 | These are incredibly useful not just for accurately depicting scale, but for the 7 | use of navigating, and zooming into visual data. A holistic view is best when it 8 | is part of a system where the user can navigate within the data. For example, 9 | zooming in on a month worth of data that is part of a 2 year timeline 10 | 11 | D3's Axis library is actually quite simple. The core functionality revolves 12 | around `D3-Scale`. 13 | 14 | `D3-Scale` provides tick functionality, whereas `D3-Axis` is made to display it, 15 | and manipulate the DOM. In reality this can be converted to a collection of 16 | components that use Vue's template syntax, to allow for more customization. For 17 | example, providing a slot per tick, to allow more abstraction. 18 | 19 | ## All of the parts 20 | 21 | What we ultimately want to do is create a container that can _display_ data 22 | visuals, as well as _interact_ with them. There are a couple ways we will want 23 | to interact. 24 | 25 | ### Zoom and Pan 26 | 27 | The user will need to be able to zoom into the display, while being able to also 28 | pan around. Would need to be available for touchscreen as well. 29 | 30 | ### Brushing 31 | 32 | Brushing is the act of selecting a range within the domain of the scaled item. 33 | This has other useful features, such as being able to get a range of data's 34 | visible domain to be used for taking action on a collection. 35 | 36 | ### Tick Displays 37 | 38 | Ticks are actually the easiest part, as they are generated from scales 39 | themselves. This means that whenever we change the range or domain, the ticks 40 | will reflect the new values 41 | 42 | ## Primary Goal 43 | 44 | ### Reusability 45 | 46 | We want to be able to use this with as much as we can, so we need to make sure 47 | that it is not dependant on anything else outside of it's own scope. This means 48 | that we need to avoid injections, and focus more on 2 way binding of general 49 | properties. 50 | 51 | Assigning colors, text etc.. needs to be avoided completely, however we should 52 | provide a path for these features later on. D3 already integrates a good pattern 53 | for creating objects as methods, which we will setup in this component in a 54 | similar fashion. 55 | -------------------------------------------------------------------------------- /src/views/examples/parallelcoords/index.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 38 | 39 | 47 | -------------------------------------------------------------------------------- /src/views/examples/parallelcoords/readme.md: -------------------------------------------------------------------------------- 1 | # Parallel Coords 2 | 3 | **Parallel coordinates** are a common way of visualizing Multivariate data. 4 | 5 | ## Use Case 6 | 7 | Parallel Coords allow for `filtering` of all dimensions, and matching them all 8 | against each other. This gives the viewer a flexible way to navigate the data. 9 | 10 | ## How to use 11 | 12 | The `Parallel Coord Component` needs two main things. 13 | 14 | - The dataset 15 | - List of dimension keys to generate columns 16 | 17 | Other additional options are available 18 | 19 | _It is important to note that there are some 20 | [gotchas](https://github.com/crossfilter/crossfilter/wiki/Crossfilter-Gotchas) 21 | when dealing with datasets while doing multidimensional filtering with 22 | Crossfilter._ 23 | 24 | ## Data Structure 25 | 26 | The data structure it looks for is a simple array of objects. 27 | -------------------------------------------------------------------------------- /src/views/examples/scaling/DemoComponent.vue: -------------------------------------------------------------------------------- 1 | 72 | 73 | 122 | -------------------------------------------------------------------------------- /src/views/examples/scaling/ScaleDisplay.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 104 | 105 | 120 | 121 | -------------------------------------------------------------------------------- /src/views/examples/scaling/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | -------------------------------------------------------------------------------- /src/views/examples/ticks/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/views/examples/versor-dragging/index.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 51 | 52 | 68 | -------------------------------------------------------------------------------- /src/views/examples/versor-dragging/readme.md: -------------------------------------------------------------------------------- 1 | # Versor Dragging 2 | 3 | This is from 4 | [Mike Bostock's example](https://bl.ocks.org/mbostock/1e10b76becaa4ea4471262bcae619dae) 5 | 6 | There are many different projections we can provide, each with it's own way of 7 | using versor. 8 | 9 | These include: 10 | 11 | - geoAzimuthalEquidistant 12 | - geoAzimuthalEqualArea 13 | - geoGnomonic 14 | - geoOrthographic 15 | - geoStereographic 16 | - geoConicConformal 17 | - geoConicEqualArea 18 | - geoConicEquidistant 19 | - geoEquirectangular 20 | - geoMercator 21 | - geoTransverseMercator 22 | 23 | By changing this value, we can depict a new 2d projection, with our mouse x,y 24 | values mapped 25 | -------------------------------------------------------------------------------- /src/views/exercises/arc/index.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 62 | 63 | 91 | -------------------------------------------------------------------------------- /src/views/exercises/arc/readme.md: -------------------------------------------------------------------------------- 1 | # Creating an Arc 2 | 3 | So, just like `line`, Arc is another shape that D3 offers. The same patterns 4 | apply, but with different properties. Some of this deals with trigonometry, but 5 | for the most part, D3 will take care of the hard parts. 6 | 7 | One thing to note, is that both `line` and `arc` are both considered pieces of a 8 | `shape layout`, such as `Pie` and `Area`, which combine arcs and lines together 9 | into one shape. The next exercise will cover the composition of these shapes. 10 | 11 | ## Basic Setup 12 | 13 | Since we are going to reuse this arc inside of of another shape, let's name it 14 | accordingly. 15 | 16 | `@/components/d3/D3Arc.vue` 17 | 18 | ```html 19 | 20 | 23 | 24 | 27 | ``` 28 | 29 | ## Defining the properties 30 | 31 | There are **5** properties of the arc that can be reactive: 32 | 33 | - startAngle 34 | - endAngle 35 | - innerRadius 36 | - outerRadius 37 | - borderRadius 38 | 39 | Let's set these up as actual properties of the Vue component itself 40 | 41 | ```html 42 | 45 | 46 | 72 | ``` 73 | 74 | ## Adding D3 75 | 76 | Now that we have these props setup, let's implement the arc generator from D3. 77 | 78 | ```html 79 | 82 | 83 | 124 | ``` 125 | 126 | ## Rendering the Path 127 | 128 | Just like `line`, what we will end up getting is another generated path. So now 129 | we just need to include it in our template 130 | 131 | ```html 132 | 136 | 137 | 178 | ``` 179 | 180 | ## Summary 181 | 182 | Arc is just one of many different types of shapes that D3 offers, such as 183 | `Links`. With these different shapes, you can generate out the path in a 184 | reactive envronment, matching it very nicely with Vue's Reactivity, and Template 185 | Rendering. This also opens up doors for creating graphics in an external 186 | program, such as Adobe Illustrator, and then simply modifying it within the 187 | template, to produce dots, lines, and anything else that makes up what the core 188 | functionality of what vector rendering is, shapes! 189 | -------------------------------------------------------------------------------- /src/views/exercises/connecting-dots/component.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 30 | -------------------------------------------------------------------------------- /src/views/exercises/connecting-dots/dataset.json: -------------------------------------------------------------------------------- 1 | [ 2 | 3 | [-300, 4 | 70 5 | ], 6 | [-150, -100], 7 | [-76, 8 | 6 9 | ], 10 | [ 11 | 50, -200 12 | ], 13 | [ 14 | 150, -100 15 | ], 16 | [ 17 | 350, -300 18 | ] 19 | ] 20 | -------------------------------------------------------------------------------- /src/views/exercises/connecting-dots/finished.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 57 | 58 | 64 | -------------------------------------------------------------------------------- /src/views/exercises/connecting-dots/index.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 41 | -------------------------------------------------------------------------------- /src/views/exercises/connecting-dots/readme.md: -------------------------------------------------------------------------------- 1 | # Connecting the Dots 2 | 3 | ## Enter D3-Shape 4 | 5 | [D3-Shape](https://github.com/d3/d3-shape) is one of the data visualization sections of D3. And what it does is 6 | pretty amazing. But first, let's look at a special element called the `` 7 | element. 8 | 9 | ```html 10 | 11 | 17 | 18 | ``` 19 | 20 | In this example, the path element has three attributes. `fill` `stroke` and `d` 21 | 22 | `fill` and `stroke` both deal with misc styling, but `d` is pretty unique. 23 | 24 | `d` stands for _definition_ meaning the path definition. It is a list of 25 | [path commands](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d#Path_commands) 26 | where each command is made of a letter and some numbers representing the 27 | parameter of the command. 28 | 29 | In short, the `d` attribute is what _draws_ the shape you want to display 30 | 31 | ### D3-Shape Transforms The Definition! 32 | 33 | What this means, is that D3 can actually generate a path based on the values you 34 | give it. Let's do this with our current dataset we are displaying, and turn this 35 | area of dots into a more uniform line graph, using `D3.line` 36 | 37 | ```html 38 | 53 | 54 | 81 | ``` 82 | 83 | You can extend the line shape even further by applying 84 | [curves to it!](http://bl.ocks.org/d3indepth/raw/b6d4845973089bc1012dec1674d3aff8/) 85 | 86 | ### Animation Magic 87 | 88 | There are many ways to animate these shapes. Most functionality will come from 89 | javascript implementations. However, modern browsers (_chrome only it seems_) can actually _transition_ 90 | the path definition. Let's try it out 91 | 92 | ```html 93 | 108 | 109 | 146 | 147 | 153 | ``` 154 | 155 | ### Summary 156 | 157 | With a couple of additions, we were able to introduce some core D3-Shape 158 | functionality using the `line` shape. We were also able to leverage Vue's 159 | template to render the D3 data to a path element, and we still have our reactive 160 | system managing all of the new functionality 161 | -------------------------------------------------------------------------------- /src/views/exercises/creating-dots/component.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 31 | -------------------------------------------------------------------------------- /src/views/exercises/creating-dots/dataset.json: -------------------------------------------------------------------------------- 1 | [ 2 | 3 | [-300, 4 | 70 5 | ], 6 | [-150, -100], 7 | [-76, 8 | 6 9 | ], 10 | [ 11 | 50, -200 12 | ], 13 | [ 14 | 150, -100 15 | ], 16 | [ 17 | 350, -300 18 | ] 19 | 20 | ] 21 | -------------------------------------------------------------------------------- /src/views/exercises/creating-dots/finished.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 30 | -------------------------------------------------------------------------------- /src/views/exercises/creating-dots/index.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 42 | -------------------------------------------------------------------------------- /src/views/exercises/creating-dots/readme.md: -------------------------------------------------------------------------------- 1 | # Making Dots 2 | 3 | ## So, what is a SVG 4 | 5 | SVG's are basically HTML DOM Elements, that behave like HTML DOM elements, but 6 | have some extra functionality. They are mainly used to draw vector images, which 7 | is a fancy way of saying shapes created with math functions. 8 | 9 | Let's setup our component to have a `SVG` as it's template. We will then use the 10 | `circle` element tag to visualize some data 11 | 12 | ```html 13 | 14 | 20 | 21 | 24 | ``` 25 | 26 | ### Let's draw some dots 27 | 28 | For this exercise, we will use a simple dataset, containing a collection of x 29 | and y coordinates. These have been created already, so let's go ahead and import 30 | them 31 | 32 | ```html 33 | 43 | 44 | 54 | ``` 55 | 56 | ### Adding Interactivity 57 | 58 | Now that we have the dots plotted, let's introduce some functionality when they 59 | are interacted with. For a simple use case, let's `log` the item being clicked 60 | 61 | ```html 62 | 73 | 74 | 89 | ``` 90 | 91 | ### Summary 92 | 93 | So what we've done above, is we've created a reactive environment for ourselves, 94 | that will automatically update the positioning of our dots based on the dataset 95 | provided. 96 | 97 | We can scale this component by adding more computations that allow for different 98 | ways of visualizing whatever data we want to see. This gives us a way to create 99 | base components, which can be reusable in certain circumstances, or for 100 | composing different visuals, such as dashboards that show multiple dimensions of 101 | a dataset at any given time. 102 | -------------------------------------------------------------------------------- /src/views/exercises/pie-chart/index.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 89 | 90 | 130 | -------------------------------------------------------------------------------- /src/views/exercises/pie-chart/readme.md: -------------------------------------------------------------------------------- 1 | # Composing a Pie Chart 2 | 3 | Since SVG's can nest elements, we can use that to construct a pie chart using 4 | the `Arc Component` we just made. This matches perfectly with Vue's Nestable 5 | Component Pattern. So let's do exactly that. This will also be a reusable 6 | component, so let's create it the same way we did with `D3Arc` 7 | 8 | `@/components/d3/D3Pie.vue` 9 | 10 | ```html 11 | 21 | 22 | 25 | ``` 26 | 27 | ## Adding D3Arc 28 | 29 | Let's import the Arc component we just made, as well as `d3`, and put it into 30 | our template 31 | 32 | **Note: Notice how we have created the g element. This is short for group, and 33 | since we will have multiple arcs, this component will represent a group of 34 | paths** 35 | 36 | ```html 37 | 42 | 43 | 52 | ``` 53 | 54 | ## Enter D3 Pie Layouts 55 | 56 | I call these layouts, however they are classified as shapes in the D3 library. I reference them this way because they are used to generate multiple paths, not an individual path definition. Let's add it! 57 | 58 | ```html 59 | 60 | ``` -------------------------------------------------------------------------------- /src/views/introduction/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 37 | -------------------------------------------------------------------------------- /src/views/introduction/readme.md: -------------------------------------------------------------------------------- 1 | # Vue and D3 Workshop 2 | 3 | ## Introduction 4 | 5 | ### Who am I 6 | 7 | Hello! My name is Jon Snyder, I am the on the Front End UX Team over at 8 | [One Discovery](http://www.onediscovery.com). We create eDiscovery Software for 9 | law firms. The main focus is to provide insight into their data, as well as 10 | managing it. 11 | 12 | It can get complex. Each case is different, making data exploration an important 13 | aspect of this job. 14 | 15 | I also like to make [games!](https://play.google.com/store/apps/details?id=com.goodideaco.handrawn) 16 | 17 | ## Data Visualization 18 | 19 | Data is _the_ basic building block of analytical exploration. It tells a story, 20 | in an infinite amount of ways. But how do we read the story? How do we choose 21 | what chapters to focus on, and where to look for clues? 22 | 23 | In a neverending cycle of trying to interpret data, we need a way in which to 24 | _try_ and see the data from different perspectives. 25 | 26 | > "_The greatest value of a picture is when it forces us to notice what we never 27 | > expected to see._" 28 | > 29 | > -[John Tukey](https://en.wikipedia.org/wiki/John_Tukey) 30 | 31 | _We have this amazing thing called vision. and out of all our senses, it's the 32 | most abundant in collecting data, giving us a better view of the world around 33 | us. However it opens doors for entirely new ways of understanding, which in 34 | return can open even more doors when combining parts of this spectrum._ 35 | 36 | Having an interface that leverages these particular dimensions not only gives us 37 | insight from a viewing perspective, but also allows for navigation into the data 38 | that is being shown. 39 | 40 | ### Successful Data Visuals are hard 41 | 42 | There are alot of factors that go into data visualizations. However, the 43 | ultimate goal is to be able to _gain insight_. 44 | 45 | Data can come in many forms. Most of the time we work with data that is 46 | quantitive, ordinal, or categorical. And alot of the time, datasets can include 47 | more than one type in some way shape or form. 48 | 49 | So what do we want data visual to be able to do? 50 | 51 | - Reveal Insights 52 | - Cause Change 53 | - Reveal More Charts 54 | 55 | In order to have this, we need to determine how the user can interact with it, 56 | in order to _navigate the data_ 57 | 58 | Maps, Scatterplots, Bar charts, etc... Ultimately gives us a holistic view at 59 | first glance. We can see EVERYTHING. But the ability to dive in deeper, to 60 | select groups of data items, and relational dimensions, allows us to focus on 61 | particular sections of the data at any given moment. 62 | -------------------------------------------------------------------------------- /src/views/overview/D3View.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/views/overview/L1View.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 22 | -------------------------------------------------------------------------------- /src/views/overview/RenderingWithVue.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 37 | 38 | 53 | -------------------------------------------------------------------------------- /src/views/overview/index.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 70 | -------------------------------------------------------------------------------- /src/views/overview/readme.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | This is an overview of how these two libraries work together 4 | 5 | ## Vue 6 | 7 | Vue (pronounced `/vjuː/`, like view) is a **progressive framework** for building 8 | user interfaces. It is designed from the ground up to be incrementally 9 | adoptable, and can easily scale between a library and a framework depending on 10 | different use cases. It consists of an approachable core library that focuses on 11 | the view layer only, and an ecosystem of supporting libraries that helps you 12 | tackle complexity in large Single-Page Applications. 13 | 14 | ### Reactivity 15 | 16 | Unline imperitive programming, this way of programming gives is enormous 17 | benefits. Mike Bostock explained this perfectly in his 18 | [recent keynote](https://youtu.be/aT4JvF7sglg?t=8m53s). When you have a reactive 19 | system, you spend less time needing to re-evaluate calculations, and more time 20 | managing the current state of things, while calculations are done for you. 21 | 22 | #### Quick Example 23 | 24 | ```js 25 | var a = 1 26 | 27 | var b = 2 28 | 29 | var c = a + b // 3 30 | ``` 31 | 32 | Now, if we change the value of `a` or `b`, does the value of `c` change? no, no 33 | it does not. Bummer... 34 | 35 | But when things are _reactive_, this evaluation can be done for you 36 | automagically. 37 | 38 | Let's setup a Vue component to show this idea: 39 | 40 | ```html 41 | 42 | ``` 43 | 44 | ### SVG Template Rendering 45 | 46 | Vue has a pretty cool way of rendering templated content. Instead of only 47 | allowing render functions that are placed within code, Vue offers template 48 | syntax that's almost indistinguishable from regular dom elements. In fact, a 49 | template can consist of just html, and Vue will handle it without a problem. 50 | 51 | ```html 52 | 53 | ``` 54 | 55 | ### Interactivity 56 | 57 | Because of the nature in which Vue handles event listeners, we can utilize it's 58 | templating system to give us a clearer image of what is looking for user 59 | interaction. And because D3 has many utoilities surrounding SVG elements, Vue 60 | gives us a way to instantly manage the interactions. 61 | 62 | ```html 63 | 64 | ``` 65 | 66 | ### Ease of Use 67 | 68 | Vue is not the only library out there that can do this. However, I feel that the 69 | amount of time it takes to actually create a reactive environment, that can 70 | leverage SVG elements and the nesting of them, gives Vue a leg up in this 71 | regard. 72 | 73 | ## D3 74 | 75 | Elijah Meeks wrote an article recently titled 76 | [D3 is not a Data Visualization Library](https://medium.com/@Elijah_Meeks/d3-is-not-a-data-visualization-library-67ba549e8520), 77 | and it is such a perfect definition. D3 is _not_ a library for making charts. 78 | Think of D3 as a toolbox of utilities that can you can use to help produce 79 | charts, or xenographics, or really cool interactive stuff. 80 | 81 | ### What D3 is made out of 82 | 83 | - Dataviz / Geographical 84 | - Animation 85 | - Analysis 86 | - Data Utilities (Lodash-y stuff) 87 | - DOM Utilities (jQuery-ish stuff) 88 | 89 | ### What Vue Replaces 90 | 91 | We are not required to use all of these libraries together. If Lodash is more 92 | your style, feel free. I personally like chroma.js for color transformation for 93 | example. However, Vue can completely take over everything that D3's DOM 94 | manipulation can do. And when you combine that with reactivity, your data comes 95 | to life. 96 | 97 | Vue does have the ability to track animations, but a seperate animation library 98 | can also be used instead of D3's collection of animation utilities. 99 | -------------------------------------------------------------------------------- /src/views/resources/BrewerColors.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 62 | 63 | 92 | -------------------------------------------------------------------------------- /src/views/resources/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | 9 | 16 | -------------------------------------------------------------------------------- /src/views/resources/semiology/index.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 43 | -------------------------------------------------------------------------------- /src/views/resources/semiology/readme.md: -------------------------------------------------------------------------------- 1 | # Semiology of Graphics 2 | 3 | **Jacques Bertin** (27 July 1918 – 3 May 2010) was a French 4 | [cartographer](https://en.wikipedia.org/wiki/Cartographer 'Cartographer') and 5 | theorist, known from his book _Semiologie Graphique_ ( _Semiology of Graphics_), 6 | published in 1967. This monumental work, based on his experience as a 7 | cartographer and geographer, represents the first and widest intent to provide a 8 | theoretical foundation to 9 | [Information Visualization](https://en.wikipedia.org/wiki/Information_Visualization 'Information Visualization'). 10 | 11 | We typically think of dimensions as spatial and quantitative, such as a position 12 | in space represented by real numbers ⟨_x, y, z_⟩. Yet with abstract data there 13 | are also non-quantitative dimensions; for example, diamond cut quality (fair, 14 | good, very good, ideal) is ordinal, while diamond cut shape (princess, round, 15 | marquise, _etc._) is categorical. 16 | 17 | A systematic classification of the use of visual elements to display data and 18 | relationships, primarily in static graphics. Bertin's system consisted of seven 19 | visual variables: position, form (shape), orientation, color (hue), texture, 20 | value (lightness or darkness of color), and size, combined with a visual 21 | semantics for linking data attributes to visual elements. 22 | 23 | ![](/static/images/semology-of-graphics.png) 24 | 25 | > Within the plane a mark can be at the top or the bottom, to the right or the 26 | > left. The eye perceives two independent dimensions along X and Y, which are 27 | > distinguished orthogonally. A variation in light energy produces a third 28 | > dimension in Z, which is independent of X and Y… 29 | 30 | > The eye is sensitive, along the Z dimension, to 6 independent visual 31 | > variables, which can be superimposed on the planar figures: the size of the 32 | > marks, their value, texture, color, orientation, and shape. They can represent 33 | > differences (≠), similarities (≡), a quantified order (Q), or a nonquantified 34 | > order (O), and can express groups, hierarchies, or vertical movements. 35 | -------------------------------------------------------------------------------- /src/views/resources/uxprinciples/index.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 43 | -------------------------------------------------------------------------------- /src/views/resources/uxprinciples/readme.md: -------------------------------------------------------------------------------- 1 | # UX Principles as Algorithms 2 | 3 | There is a law in UX called `Fitts's Law`. This law states that the time required to rapidly move to a target area is a function of the ratio between the distance to the target and the width of the target. 4 | 5 | To actually be able to evaulate this as an equation, means that we can tie together these rules. Mike Bostock told a brilliant explanation of how he managed to use Voronoi Overlays to performthis in a [Working Example](http://mbostock.github.io/d3/talk/20111116/airports.html) -------------------------------------------------------------------------------- /src/views/resources/xenographics/index.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 45 | -------------------------------------------------------------------------------- /src/views/resources/xenographics/readme.md: -------------------------------------------------------------------------------- 1 | ## Xenographics 2 | 3 | Xeno.graphics is a collection of unusual charts and maps, managed by 4 | [Maarten Lambrechts](http://www.maartenlambrechts.com/). Its objective is to 5 | create a repository of novel, innovative and experimental visualizations to 6 | inspire you, to fight xenographphobia and popularize new chart types. 7 | 8 | The xenographics collection will keep on growing. If you know of one that isn’t 9 | here already, please [submit it](http://xeno.graphics/submit/). You can also 10 | expect some posts about certain topics around xenographics. 11 | -------------------------------------------------------------------------------- /static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thegoodideaco/vue-d3-workshop/1934e6d82e1d98c680a1f6144f3bbbe80969eb99/static/.gitkeep -------------------------------------------------------------------------------- /static/demo_data/quantifiable/simple/lineData.json: -------------------------------------------------------------------------------- 1 | [ 2 | [ 3 | [-300, 4 | 70 5 | ], 6 | [-150, -100], 7 | [-76, 8 | 6 9 | ], 10 | [ 11 | 50, -200 12 | ], 13 | [ 14 | 150, -100 15 | ], 16 | [ 17 | 350, -300 18 | ], 19 | [ 20 | 438, -200 21 | ] 22 | ] 23 | ] 24 | -------------------------------------------------------------------------------- /static/fonts/fa/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thegoodideaco/vue-d3-workshop/1934e6d82e1d98c680a1f6144f3bbbe80969eb99/static/fonts/fa/FontAwesome.otf -------------------------------------------------------------------------------- /static/fonts/fa/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thegoodideaco/vue-d3-workshop/1934e6d82e1d98c680a1f6144f3bbbe80969eb99/static/fonts/fa/fontawesome-webfont.eot -------------------------------------------------------------------------------- /static/fonts/fa/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thegoodideaco/vue-d3-workshop/1934e6d82e1d98c680a1f6144f3bbbe80969eb99/static/fonts/fa/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /static/fonts/fa/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thegoodideaco/vue-d3-workshop/1934e6d82e1d98c680a1f6144f3bbbe80969eb99/static/fonts/fa/fontawesome-webfont.woff -------------------------------------------------------------------------------- /static/fonts/fa/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thegoodideaco/vue-d3-workshop/1934e6d82e1d98c680a1f6144f3bbbe80969eb99/static/fonts/fa/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /static/images/d3-lib-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thegoodideaco/vue-d3-workshop/1934e6d82e1d98c680a1f6144f3bbbe80969eb99/static/images/d3-lib-overview.png -------------------------------------------------------------------------------- /static/images/semology-of-graphics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thegoodideaco/vue-d3-workshop/1934e6d82e1d98c680a1f6144f3bbbe80969eb99/static/images/semology-of-graphics.png -------------------------------------------------------------------------------- /static/markdown/1 - introduction.md: -------------------------------------------------------------------------------- 1 | # Vue and D3 Workshop 2 | 3 | ## Introduction 4 | 5 | ### Hello! My name is Jon Snyder 6 | 7 | I am the on the Front End UX Team over at 8 | [One Discovery](http://www.onediscovery.com). We create eDiscovery Software for 9 | law firms. The main focus is to provide insight into their data, as well as 10 | managing it. 11 | 12 | It can get complex. Each case is different, making data exploration an important 13 | aspect of this job. 14 | 15 | ## Data Visualization 16 | 17 | Data is _the_ basic building block of analytical exploration. It tells a story, 18 | in an infinite amount of ways. But how do we read the story? How do we choose 19 | what chapters to focus on, and where to look for clues? 20 | 21 | In a neverending cycle of trying to interpret data, we need a way in which to 22 | _try_ and see the data from different perspectives. 23 | 24 | > "_The greatest value of a picture is when it forces us to notice what we never 25 | > expected to see._" 26 | > 27 | > -John Tukey 28 | 29 | We have this amazing thing called vision. and out of all our senses, it's the 30 | most abundant in collecting data, giving us a better view of the world around 31 | us. However it opens doors for entirely new ways of understanding, which in 32 | return can open even more doors when combining parts of this spectrum. 33 | 34 | Having an interface that leverages these particular dimensions not only gives us 35 | insight from a viewing perspective, but also allows for navigation into the data 36 | that is being shown. 37 | -------------------------------------------------------------------------------- /static/markdown/2 - overview.md: -------------------------------------------------------------------------------- 1 | # Workshop 2 | 3 | ## Overview 4 | In this workshop we will be covering the following sections: 5 | 6 | - [Vue and D3 Overview](#vue-and-d3) 7 | - [Working with Shapes - Lines](#d3-line) 8 | - [Working with Shapes - Arcs](#d3-arc-and-d3-pie) 9 | 10 | ## Vue and D3 11 | 12 | Vue and D3 complement each other on various principles. In this excercise we will leverage Vue's `reactive state` while using `D3-Scale` 13 | 14 | 15 | ## D3-Line 16 | 17 | In this excercise, we will explore `D3-Shape` and how it works along side of Vue's `Template Syntax`. 18 | We will go a bit into depth on animating and styling the SVG output, as well as implementing user interaction 19 | 20 | ## D3-Arc and D3-Pie 21 | 22 | One unique feature that Vue provides is it's way to `nest components`. Since SVG's are considered DOM markup, we can manage our visuals to follow the same pattern. We will be creating `Arc components`, and then nesting them inside of a `Pie chart component`. -------------------------------------------------------------------------------- /static/markdown/3 - Vue and D3.md: -------------------------------------------------------------------------------- 1 | # Vue and D3 2 | 3 | This is an overview of how these two libraries work together 4 | 5 | ## Vue 6 | 7 | ### Reactivity 8 | 9 | ### Interactivity 10 | 11 | ### Ease of Use 12 | 13 | ### SVG Template Rendering 14 | 15 | ## D3 16 | 17 | ### What D3 is made out of 18 | 19 | ### What Vue replaces 20 | 21 | ### Data Analysis vs. Creative Mediums -------------------------------------------------------------------------------- /static/markdown/4 - Excercise 1.md: -------------------------------------------------------------------------------- 1 | # Excercise #1 2 | 3 | ## `Using D3 within Vue` 4 | 5 | For this Excecise, we will be using `D3-Scale` and Vue's `Reactivity System` to 6 | construct a `Dynamic Range Slider`. 7 | 8 | Let's make a range slider that we can use, to dynamically change some properties. 9 | These properties will tie directly into components that we will make in the next 10 | excercise. 11 | 12 | ### Creating the component 13 | 14 | Let's start by creating a component called `ValueSlider` 15 | 16 | `ValueSlider.vue` 17 | 18 | ```html 19 | 20 | 21 | 24 | ``` 25 | 26 | With a typical HMTL standard slider input, we can have min, max, steps and value. 27 | Let's create the markup for a slider input element 28 | 29 | ```html 30 | 41 | ``` 42 | 43 | Now, we need to bind these values to some reactive objects within our vue 44 | component. We also want to treat this as a component that takes a v-model directive. In this case we will use Vue's `model` property 45 | 46 | ```html 47 | 59 | 60 | 90 | ``` 91 | 92 | _**Note:** Vue DevTools prints out events, and since we are using the input event, this will fire off many many times. I would reccomend filtering, or pausing events if the steps are very granular._ 93 | 94 | We can simplify our bindings with `v-bind` and `$props` 95 | 96 | ```html 97 | 105 | ``` 106 | 107 | ### Meet `D3-Scale` 108 | 109 | D3-Scale allows you to map a range of inputs to a domain of different values. This can be morethan just Linear. Similar to an ease function, this reads an input value, and returns a new value based on the range and domain that is specified 110 | 111 | Let's add this functionality to our component. This way we can have it control both, allowing us to modify what the value is on two different scales 112 | 113 | ```html 114 | 115 | ``` -------------------------------------------------------------------------------- /tests/e2e/plugins/index.js: -------------------------------------------------------------------------------- 1 | // https://docs.cypress.io/guides/guides/plugins-guide.html 2 | 3 | // if you need a custom webpack configuration you can uncomment the following import 4 | // and then use the `file:preprocessor` event 5 | // as explained in the cypress docs 6 | // https://docs.cypress.io/api/plugins/preprocessors-api.html#Examples 7 | 8 | // /* eslint-disable import/no-extraneous-dependencies, global-require */ 9 | // const webpack = require('@cypress/webpack-preprocessor') 10 | 11 | module.exports = (on, config) => { 12 | // on('file:preprocessor', webpack({ 13 | // webpackOptions: require('@vue/cli-service/webpack.config'), 14 | // watchOptions: {} 15 | // })) 16 | 17 | return Object.assign({}, config, { 18 | fixturesFolder: 'tests/e2e/fixtures', 19 | integrationFolder: 'tests/e2e/specs', 20 | screenshotsFolder: 'tests/e2e/screenshots', 21 | videosFolder: 'tests/e2e/videos', 22 | supportFile: 'tests/e2e/support/index.js' 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /tests/e2e/specs/test.js: -------------------------------------------------------------------------------- 1 | // https://docs.cypress.io/api/introduction/api.html 2 | 3 | // describe('My First Test', () => { 4 | // it('Visits the app root url', () => { 5 | // cy.visit('/') 6 | // cy.contains('h1', 'Welcome to Your Vue.js + TypeScript App') 7 | // }) 8 | // }) 9 | -------------------------------------------------------------------------------- /tests/e2e/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add("login", (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This is will overwrite an existing command -- 25 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) 26 | -------------------------------------------------------------------------------- /tests/e2e/support/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | -------------------------------------------------------------------------------- /tests/unit/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | jest: true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /tests/unit/example.spec.ts: -------------------------------------------------------------------------------- 1 | import HelloWorld from '@/components/HelloWorld.vue' 2 | import { shallowMount } from '@vue/test-utils' 3 | 4 | describe('HelloWorld.vue', () => { 5 | it('renders props.msg when passed', () => { 6 | const msg = 'new message' 7 | const wrapper = shallowMount(HelloWorld, { 8 | propsData: { msg } 9 | }) 10 | expect(wrapper.text()).toMatch(msg) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "importHelpers": true, 8 | "noImplicitAny": false, 9 | "noImplicitThis": false, 10 | // "checkJs": true, 11 | // "allowJs": true, 12 | "suppressExcessPropertyErrors": true, 13 | // "resolveJsonModule": true, 14 | "moduleResolution": "node", 15 | "experimentalDecorators": true, 16 | "esModuleInterop": true, 17 | "allowSyntheticDefaultImports": true, 18 | "sourceMap": true, 19 | "baseUrl": ".", 20 | "types": [ 21 | "webpack-env", 22 | "jest", 23 | ], 24 | "paths": { 25 | "@/*": [ 26 | "src/*" 27 | ] 28 | }, 29 | "lib": [ 30 | "esnext", 31 | "dom", 32 | "dom.iterable", 33 | "scripthost" 34 | ], 35 | // "allowJs": true, 36 | // "checkJs": true, 37 | }, 38 | "include": [ 39 | "src/**/*.ts", 40 | "src/**/*.js", 41 | "src/**/*.tsx", 42 | "src/**/*.vue", 43 | "src/**/*.scss", 44 | "src/**/*.md", 45 | "tests/**/*.ts", 46 | "tests/**/*.tsx" 47 | ], 48 | "exclude": [ 49 | "node_modules" 50 | ] 51 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "warning", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "jsRules": { 7 | "eofline": true, 8 | "semicolon": [true, "never"], 9 | "quotemark": [true, "single"], 10 | "align": true, 11 | "jsdoc-format": false, 12 | "no-unused-expression":true, 13 | "ordered-imports": false, 14 | "variable-name": true, 15 | "trailing-comma": [true, { 16 | "multiline": "never", 17 | "singleline": "never" 18 | }] 19 | }, 20 | "linterOptions": { 21 | "exclude": [ 22 | "node_modules/**" 23 | ] 24 | }, 25 | "rules": { 26 | "quotemark": [true, "single"], 27 | "indent": [true, "spaces", 2], 28 | "interface-name": false, 29 | "object-literal-sort-keys": false, 30 | "no-consecutive-blank-lines": true, 31 | "semicolon": [true, "never"], 32 | "eofline": true, 33 | "trailing-comma": [true, { 34 | "multiline": "never", 35 | "singleline": "never" 36 | }], 37 | "curly": false, 38 | "no-bitwise":false, 39 | "arrow-parens": false, 40 | "no-console": false, 41 | "no-unused-expression":true, 42 | "ordered-imports": false, 43 | "variable-name": true 44 | } 45 | } -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | // const Config = require('webpack-chain') 2 | const path = require('path') 3 | 4 | const debug = process.env.NODE_ENV !== 'production' 5 | 6 | function resolve(dir) { 7 | return path.join(__dirname, dir) 8 | } 9 | 10 | module.exports = { 11 | assetsDir: undefined, 12 | productionSourceMap: false, 13 | 14 | css: { 15 | sourceMap: debug, 16 | 17 | // Always false in dev, make false for production 18 | extract: false 19 | }, 20 | 21 | pwa: { 22 | name: 'Vue D3 Workshop' 23 | }, 24 | 25 | baseUrl: undefined, 26 | outputDir: undefined, 27 | parallel: undefined, 28 | 29 | /** 30 | * @param {Config} config 31 | */ 32 | configureWebpack: config => { 33 | // if (process.env.NODE_ENV === 'production') { 34 | const CopyWebpackPlugin = require('copy-webpack-plugin') 35 | config.plugins.push( 36 | new CopyWebpackPlugin([{ 37 | from: resolve('static'), 38 | ignore: ['.*'], 39 | to: 'static' 40 | }]) 41 | ) 42 | 43 | config.module.rules.push({ 44 | test: /\.md$/, 45 | use: [{ 46 | loader: 'html-loader' 47 | }, 48 | { 49 | loader: 'markdown-loader', 50 | options: { 51 | /* your options here */ 52 | } 53 | } 54 | ] 55 | }) 56 | }, 57 | 58 | runtimeCompiler: true, 59 | lintOnSave: undefined 60 | } --------------------------------------------------------------------------------