├── .buildpacks ├── .gitignore ├── CODEOWNERS ├── CONTRIBUTING.md ├── LICENSE.txt ├── README.md ├── app.js ├── chart-ops.js ├── chart.js ├── index.html ├── package.json └── render-chart.js /.buildpacks: -------------------------------------------------------------------------------- 1 | https://github.com/mojodna/heroku-buildpack-cairo.git 2 | https://github.com/heroku/heroku-buildpack-nodejs.git 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Comment line immediately above ownership line is reserved for related gus information. Please be careful while editing. 2 | #ECCN:Open Source 3 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Code 2 | 3 | External contributors will be required to sign a Contributor's License Agreement. You will be prompted to sign it when you open a pull request. 4 | 5 | 1. Create a new issue before starting your project so that we can keep track of what you are trying to add/fix. That way, we can also offer suggestions or let you know if there is already an effort in progress. 6 | 2. Fork off this repository. 7 | 3. Create a topic branch for the issue that you are trying to add. When possible, you should branch off the default branch. 8 | 4. Edit the code in your fork. 9 | 5. Send us a well documented pull request when you are done. 10 | 11 | The **GitHub pull requests** should meet the following criteria: 12 | 13 | - descriptive title 14 | - brief summary 15 | - @mention several relevant people to review the code 16 | - add helpful GitHub comments on lines that you have questions / concerns about 17 | 18 | We'll review your code, suggest any needed changes, and merge it in. Thank you. 19 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017-present, Salesforce.com, Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the Salesforce.com nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Portion Control 2 | 3 | pie 4 | 5 | 6 | Render a badge to quickly communicate project values. 7 | 8 | 9 | # Usage 10 | 11 | * Meditate on your projects' values. You can pick up to 4. 12 | 13 | * Add an image tag: 14 | 15 | ```html 16 | performance=65, readability=15, and scalability=20 17 | ``` 18 | 19 | Don't forget the `alt` tag for a11y. Doubling the size makes it look nice on retina screens. 20 | 21 | Enjoy! 22 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-present, salesforce.com, inc. All rights reserved 2 | // Licensed under BSD 3-Clause - see LICENSE.txt or git.io/sfdc-license 3 | 4 | const express = require('express') 5 | const app = express() 6 | const Task = require('data.task') 7 | const renderChart = require('./render-chart') 8 | 9 | const compression = require('compression') 10 | const port = process.env.PORT || 3000 11 | 12 | app.use(compression()) 13 | app.use(express.static(__dirname)) 14 | app.set('view engine', 'ejs') 15 | 16 | const listen = (app, port) => 17 | new Task((rej, res) => 18 | app.listen(port, (err) => 19 | err ? rej(err) : res(port))) 20 | 21 | app.get('/pie.png', (req, res) => 22 | renderChart(req.query) 23 | .fork(error => res 24 | .status(500) 25 | .send(`error ${error}`), 26 | result => res 27 | .contentType('image/png') 28 | .send(result))) 29 | 30 | listen(app, port) 31 | .map(port => `Listening on port ${port}`) 32 | .fork(console.error, console.log) 33 | 34 | -------------------------------------------------------------------------------- /chart-ops.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-present, salesforce.com, inc. All rights reserved 2 | // Licensed under BSD 3-Clause - see LICENSE.txt or git.io/sfdc-license 3 | 4 | module.exports = (width, labels, data) => 5 | ({ 6 | type: "doughnut", 7 | data: { 8 | labels, 9 | datasets: [{ 10 | data, 11 | backgroundColor: [ 12 | "rgba(255, 105, 120, 1)", 13 | "rgba(52, 0, 104, 1)", 14 | "rgba(177, 237, 232, 1)", 15 | "rgba(109, 67, 90, 1)" 16 | ] 17 | }] 18 | }, 19 | options: { 20 | legend: { 21 | labels: { 22 | boxWidth: (width / labels.length) / 4, 23 | fontSize: 24 24 | } 25 | } 26 | } 27 | }) 28 | -------------------------------------------------------------------------------- /chart.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-present, salesforce.com, inc. All rights reserved 2 | // Licensed under BSD 3-Clause - see LICENSE.txt or git.io/sfdc-license 3 | 4 | const Task = require('data.task') 5 | const ChartjsNode = require('chartjs-node') 6 | 7 | const create = ({width, height}) => 8 | new ChartjsNode(width, height) 9 | 10 | const draw = (chart, opts) => 11 | new Task((rej, res) => 12 | chart.drawChart(opts).then(() => res(chart)).catch(rej)) 13 | 14 | const toBuffer = chart => 15 | new Task((rej, res) => 16 | chart.getImageBuffer('image/png').then(res).catch(rej)) 17 | 18 | const destroy = chart => 19 | new Task((rej, res) => 20 | res(chart.destroy())) 21 | 22 | module.exports = {create, draw, toBuffer, destroy} 23 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Portion Control 6 | 7 | 8 | pie 9 | 10 | 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "portion-control", 3 | "version": "1.0.0", 4 | "description": "Quickly communicate project values", 5 | "scripts": { 6 | "start": "node app", 7 | "start-dev": "nodemon app", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/salesforce-ux/portion-control.git" 13 | }, 14 | "author": "", 15 | "license": "BSD-Clause-3", 16 | "bugs": { 17 | "url": "https://github.com/salesforce-ux/portion-control/issues" 18 | }, 19 | "homepage": "https://github.com/salesforce-ux/portion-control#readme", 20 | "dependencies": { 21 | "canvas": "^1.6.2", 22 | "chart.js": "^2.4.0", 23 | "chartjs-node": "^1.4.0", 24 | "compression": "^1.6.2", 25 | "data.task": "^3.1.1", 26 | "express": "^4.14.0", 27 | "immutable": "^3.8.1" 28 | }, 29 | "devDependencies": { 30 | "nodemon": "^1.11.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /render-chart.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017-present, salesforce.com, inc. All rights reserved 2 | // Licensed under BSD 3-Clause - see LICENSE.txt or git.io/sfdc-license 3 | 4 | const Task = require('data.task') 5 | const Chart = require('./chart') 6 | const {Map} = require('immutable') 7 | const chartOptions = require('./chart-ops') 8 | 9 | const optsFromValues = (width, values) => 10 | chartOptions(width, values.keySeq().toArray(), values.valueSeq().toArray()) 11 | 12 | const formatSize = size => { 13 | const [width, height] = size.split('x').map(Number) 14 | return {width, height} 15 | } 16 | 17 | const formatQuery = query => 18 | Task.of(query) 19 | .map(Map) 20 | .map(params => 21 | ({ size: formatSize(params.get('size')), 22 | values: params.delete('size') 23 | })) 24 | 25 | module.exports = query => 26 | formatQuery(query) 27 | .chain(({size, values}) => 28 | Chart.draw(Chart.create(size), optsFromValues(size.width, values))) 29 | .chain(chart => 30 | Chart.toBuffer(chart) 31 | .chain(buffer => 32 | Chart.destroy(chart) 33 | .map(() => buffer))) 34 | --------------------------------------------------------------------------------