├── README.md ├── example.json ├── how-to.gif ├── sparkline-example.png └── sparkliner.sketchplugin └── Contents └── Sketch ├── checkdata.js ├── get-json-and-parse-it.js ├── manifest.json └── sparkliner.js /README.md: -------------------------------------------------------------------------------- 1 | # Sparkliner — easy way to make sparkline graph 2 | > A sparkline is a very small line chart, typically drawn without axes or coordinates. It presents the general shape of the variation (typically over time) in some measurement, such as temperature or stock market price, in a simple and highly condensed way. 3 | > - [Wikipedia](https://en.wikipedia.org/wiki/Sparkline). 4 | 5 | ## How it works 6 | ![Gif more than thousand words](/how-to.gif) 7 | 8 | ##Order 9 | 1. **Create an artboard**; 10 | 2. Create a start rectangle. It sets sizes of a future sparkline graph; 11 | 3. Start command from menu or press shortcuts `ctrl + shift + g `; 12 | 4. Find and import your JSON file; 13 | 5. Get sparkline. 14 | 15 | ## How install the plugin 16 | 1. [Download the zip file with the Sparkliner](https://github.com/Volorf/Sparkliner/archive/master.zip). 17 | 2. Copy the contents to the Plugin Folder (Plugin > Manage Plugins... > [Show Plugins Folder](http://frolovoleg.ru/images/sketch-plugin-folder.png)). 18 | 19 | ## What do you need first 20 | Sparkliner creates graph with JSON data. First your need have it. It looks like that (example.json): 21 | 22 | ``` json 23 | { 24 | "dataArray": [10, 30, 3, 8, 39, 25, 22, 89, 74, 7, 2, 40, 61, 17, 35, 31, 8, 14, 39, 32, 53], 25 | "strokeColor": "#00AAFF", 26 | "thickness": 1, 27 | "displayEndPoint": true, 28 | "endPointColor": "#DD2020", 29 | "endPointRadius": 2, 30 | "removeInitialBox": true 31 | } 32 | ``` 33 | Open the file in your favorite editor and change the values of the names how you need (e.g. `"strokeColor" ` is a name. `"#00AAFF"` is its a value. Together they are a pair). Or create new one. 34 | 35 | ## What are they 36 | 37 | ``` "dataArray" ``` 38 | Data is an array. It defines a graph curve. Only numbers. 39 | 40 | ``` "strokeColor" ``` 41 | Color of sparkline. You need to write HEX color here. Don't forget about "quotes". 42 | 43 | ``` "thickness" ``` 44 | Thickness of line. Only one number. 45 | 46 | ``` "displayEndPoint" ``` 47 | If it equals true sparkline is made with an end marker. If it equals false - it is not. 48 | 49 | ``` "endPointColor" ``` 50 | Color of end marker. It needs a HEX. 51 | 52 | ``` "endPointRadius" ``` 53 | Radius of end marker. Only one number. 54 | 55 | ``` "removeInitialBox" ``` 56 | It is start rectangle that set sizes of sparkline graph. By default plugin removes it. If you need it you can set false. 57 | 58 | ## Links 59 | [Linkedin](https://www.linkedin.com/in/oleg-frolov-6a6a4752/) | [Dribbble](https://dribbble.com/Volorf) | [Facebook](https://www.facebook.com/volorf) 60 | -------------------------------------------------------------------------------- /example.json: -------------------------------------------------------------------------------- 1 | { 2 | "dataArray": [10, 30, 3, 8, 39, 25, 22, 89, 74, 7, 2, 40, 61, 17, 35, 31, 8, 14, 39, 32, 53], 3 | "strokeColor": "#00AAFF", 4 | "thickness": 1, 5 | "displayEndPoint": true, 6 | "endPointColor": "#DD2020", 7 | "endPointRadius": 2, 8 | "removeInitialBox": true 9 | } 10 | -------------------------------------------------------------------------------- /how-to.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volorf/Sparkliner/fec7eeaccec61bde8cc8345f0c9bbaa2a134e147/how-to.gif -------------------------------------------------------------------------------- /sparkline-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Volorf/Sparkliner/fec7eeaccec61bde8cc8345f0c9bbaa2a134e147/sparkline-example.png -------------------------------------------------------------------------------- /sparkliner.sketchplugin/Contents/Sketch/checkdata.js: -------------------------------------------------------------------------------- 1 | var someThingWentWrong = 0 2 | var msg = "Wrong data type:" 3 | 4 | 5 | // Check gata 6 | 7 | var checkDataArray = 0 8 | var checkStrokeColor = typeof(strokeColor) 9 | var checkThickness = typeof(thickness) 10 | var checkDisplayEndPoint = typeof(displayEndPoint) 11 | var checkEndPointColor = typeof(endPointColor) 12 | var checkEndPointRadius = typeof(endPointRadius) 13 | var checkRemoveInitialBox = typeof(removeInitialBox) 14 | 15 | for ( i = 0; i < dataArray.length; i++) { 16 | if (typeof(dataArray[i]) != "number") { 17 | checkDataArray = 1 18 | msg = msg + " 'dataArray'" 19 | someThingWentWrong = 1 20 | } 21 | } 22 | 23 | if (checkStrokeColor != "string") { 24 | msg = msg + " 'strokeColor'" 25 | someThingWentWrong = 1 26 | } 27 | 28 | if (checkThickness != "number") { 29 | msg = msg + " 'thickness'" 30 | someThingWentWrong = 1 31 | } 32 | 33 | if (checkDisplayEndPoint != "boolean") { 34 | msg = msg + " 'displayEndPoint'" 35 | someThingWentWrong = 1 36 | } 37 | 38 | if (checkEndPointColor != "string") { 39 | msg = msg + " 'endPointColor'" 40 | someThingWentWrong = 1 41 | } 42 | 43 | if (checkEndPointRadius != "number") { 44 | msg = msg + " 'endPointRadius'" 45 | someThingWentWrong = 1 46 | } 47 | 48 | if (checkRemoveInitialBox != "boolean") { 49 | msg = msg + " 'removeInitialBox'" 50 | someThingWentWrong = 1 51 | } 52 | 53 | if (someThingWentWrong == 1) { 54 | doc.showMessage(msg) 55 | } 56 | -------------------------------------------------------------------------------- /sparkliner.sketchplugin/Contents/Sketch/get-json-and-parse-it.js: -------------------------------------------------------------------------------- 1 | var openPanel = NSOpenPanel.openPanel() 2 | 3 | openPanel.setTitle('Import JSON') 4 | openPanel.setMessage('Select a JSON file.') 5 | openPanel.setPrompt('Import') 6 | 7 | var openPanelButtonPressed = openPanel.runModal() 8 | 9 | if (openPanelButtonPressed == NSFileHandlingPanelOKButton) { 10 | var allowedUrl = openPanel.URL() 11 | } 12 | 13 | var filePath = allowedUrl 14 | 15 | var data = JSON.parse(NSString.stringWithContentsOfFile(filePath)) 16 | 17 | var options = { 18 | dataArray: data.dataArray, 19 | strokeColor: data.strokeColor, 20 | thickness: data.thickness, 21 | displayEndPoint: data.displayEndPoint, 22 | endPointColor: data.endPointColor, 23 | endPointRadius: data.endPointRadius, 24 | removeInitialBox: data.removeInitialBox 25 | } 26 | -------------------------------------------------------------------------------- /sparkliner.sketchplugin/Contents/Sketch/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Sparkliner", 3 | "description": "Plugin to create sparkline graph", 4 | "author": "Oleg Frolov", 5 | "authorEmail": "frolololeg@gmail.com", 6 | "homepage": "https://github.com/Volorf/Sparkliner", 7 | "version": 2.0, 8 | "identifier": "com.example.sketch.sparkliner", 9 | "compatibleVersion": 41.2, 10 | "bundleVersion": 2, 11 | "commands": [ 12 | { 13 | "name": "Upload JSON", 14 | "identifier": "createWithJson", 15 | "shortcut": "ctrl shift g", 16 | "script": "sparkliner.js", 17 | "handler": "createWithJson" 18 | }, 19 | { 20 | "name": "Enter data", 21 | "identifier": "enterData", 22 | "shortcut": "ctrl shift e", 23 | "script": "sparkliner.js", 24 | "handler": "enterData" 25 | }, 26 | { 27 | "name": "Random Data", 28 | "identifier": "randomData", 29 | "script": "sparkliner.js", 30 | "handler": "randomData" 31 | } 32 | ], 33 | "menu": { 34 | "items": [ 35 | "createWithJson", 36 | "enterData", 37 | "randomData" 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /sparkliner.sketchplugin/Contents/Sketch/sparkliner.js: -------------------------------------------------------------------------------- 1 | var defaults = { 2 | strokeColor: "#00AAFF", 3 | thickness: 1, 4 | displayEndPoint: true, 5 | endPointColor: "#DD2020", 6 | endPointRadius: 2, 7 | removeInitialBox: true 8 | }; 9 | 10 | /* 11 | * Create a color from an SVG string 12 | * @method makeColor 13 | * @param {String} SVGString 14 | */ 15 | function makeColor(SVGString) { 16 | return MSImmutableColor.colorWithSVGString(SVGString).newMutableCounterpart(); 17 | } 18 | 19 | /* 20 | * Generate the sparkline 21 | * @method generateSparkline 22 | * @param {Object} context [description] 23 | * @param {Object} options [description] 24 | */ 25 | function generateSparkline(context, options) { 26 | 27 | if (!options || typeof options !== 'object') options = defaults; 28 | 29 | var selectedLayers = context.selection; 30 | var selectedCount = selectedLayers.count(); 31 | var doc = context.document; 32 | 33 | var box = selectedLayers[0]; 34 | var daysAmount = options.dataArray.length; 35 | 36 | if (selectedCount == 1) { 37 | 38 | var boxWidth = box.frame().width(); 39 | var boxHeight = box.frame().height(); 40 | var boxX = box.frame().x(); 41 | var boxY = box.frame().y(); 42 | 43 | // Define width of one column 44 | var dotOffset = boxWidth / (daysAmount - 1); 45 | 46 | // Find max value in array 47 | var maxValueInArray = Math.max.apply(Math, options.dataArray); 48 | 49 | // Find min value in array 50 | var minValueInArray = Math.min.apply(Math, options.dataArray); 51 | 52 | // Find delta of values in array 53 | var deltaValueInArray = maxValueInArray - minValueInArray; 54 | 55 | // Define relevant unit for the box 56 | var relevantUnit = boxHeight / deltaValueInArray; 57 | 58 | var dotY = 0; 59 | var dotX = 0; 60 | 61 | var endPoint = options.dataArray.length - 1; 62 | 63 | var path = NSBezierPath.bezierPath(); 64 | 65 | // doc.showMessage(minValueInArray.toString()); 66 | 67 | path.moveToPoint(NSMakePoint(0,0)); 68 | 69 | for (var i = 0; i < options.dataArray.length; i++) { 70 | dotX = dotOffset * i; 71 | dotY = - (options.dataArray[i] - minValueInArray) * relevantUnit; 72 | if (i == 0) { 73 | path.moveToPoint(NSMakePoint(dotX,dotY)); 74 | } 75 | path.lineToPoint(NSMakePoint(dotX,dotY)); 76 | 77 | // Create end markpoint 78 | if (i == endPoint) { 79 | 80 | var ovalShape = MSOvalShape.alloc().init(); 81 | ovalShape.frame = MSRect.rectWithRect(NSMakeRect(0,0,(options.endPointRadius * 2),(options.endPointRadius * 2))); 82 | 83 | var shapeGroup = MSShapeGroup.shapeWithPath(ovalShape); 84 | // Changed layer.style().fills().addNewStylePart() to layer.style().addStylePartOfType(0) for Sketch 3.8 85 | var fill = shapeGroup.style().addStylePartOfType(0); 86 | fill.color = makeColor(options.endPointColor); 87 | shapeGroup.frame().midX = dotX + boxX; 88 | shapeGroup.frame().midY = dotY + boxY + boxHeight; 89 | 90 | } 91 | 92 | } 93 | 94 | var shape = MSShapeGroup.shapeWithBezierPath(path); 95 | // Changed layer.style().borders().addNewStylePart() to style().addStylePartOfType(1) for Sketch 3.8 96 | var border = shape.style().addStylePartOfType(1); 97 | border.color = makeColor(options.strokeColor); 98 | border.thickness = options.thickness; 99 | 100 | shape.frame().x = boxX; 101 | shape.frame().y = boxY; 102 | 103 | // Add graph to current artboard 104 | doc.currentPage().currentArtboard().addLayers([shape]); 105 | if (options.displayEndPoint == true) { 106 | doc.currentPage().currentArtboard().addLayers([shapeGroup]); 107 | } 108 | 109 | // Remove initial box 110 | if (options.removeInitialBox == true) { 111 | doc.currentPage().currentArtboard().removeLayer(box); 112 | } 113 | 114 | } else { 115 | doc.showMessage("You should select one rectangle."); 116 | } 117 | 118 | } 119 | 120 | /* 121 | * Create a sparkline from JSON 122 | * @method createWithJson 123 | * @param {Object} context 124 | */ 125 | function createWithJson(context) { 126 | 127 | @import "get-json-and-parse-it.js" 128 | 129 | generateSparkline(context, options); 130 | } 131 | 132 | /* 133 | * Create a sparkline from user-inputted data 134 | * @method enterData 135 | * @param {Object} context 136 | */ 137 | function enterData(context) { 138 | 139 | var doc = context.document; 140 | 141 | var dataFromTextFieldArray = doc.askForUserInput_initialValue("Enter your data", "10, 30, 3, 8, 39, 25, 22, 89, 74, 7, 2, 40, 61, 17, 35, 31, 8, 14, 39, 32, 53"); 142 | 143 | var dataArray = dataFromTextFieldArray.trim().split(", ").map(Number); 144 | 145 | var options = Object.assign({}, defaults, { dataArray: dataArray }); 146 | 147 | generateSparkline(context, options); 148 | } 149 | 150 | /* 151 | * Create a sparkline from random data 152 | * @method randomData 153 | * @param {Object} context 154 | */ 155 | function randomData(context) { 156 | 157 | var dataArray = []; 158 | 159 | for (var i = 0; i < 20; i++) dataArray.push(Math.floor(Math.random() * 100)); 160 | 161 | var options = Object.assign({}, defaults, { dataArray: dataArray }); 162 | 163 | generateSparkline(context, options); 164 | 165 | } 166 | --------------------------------------------------------------------------------