├── sketchpack.json ├── LICENSE ├── README.md └── personas.sketchplugin /sketchpack.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Personas", 3 | "description": "Allows for consistent user of personas across mocks", 4 | "tags": ["generator", "personas", "ux"] 5 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Stan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Personas for Sketch 2 | Bringing user personas into Sketch 3 | 4 | ## Installation 5 | Recommended: Use [Sketch Toolbox](http://sketchtoolbox.com/) to keep Personas up-to-date. 6 | 7 | Alternatively, [download](https://github.com/nolastan/Sketch-Personas/archive/master.zip) and open `personas.sketchplugin`. Now open Sketch to find "personas" in the Plugins menu. 8 | 9 | ## Usage 10 | Select shapes and named text layers to generate a persona: 11 | 12 | ![](http://s15.postimg.org/r0h9jqt5n/personas.gif) 13 | 14 | Select groups to generate multiple personas: 15 | 16 | ![](http://i.imgur.com/CUFI8nw.gif) 17 | 18 | (gif created with [Generate GIF plugin](https://github.com/nathco/generate-gif)) 19 | 20 | Note that the plugin will react based on the **layer's name**, not its text content. 21 | 22 | ## Supported Layer Names 23 | Data comes from [RandomAPI](https://randomuser.me/). 24 | 25 | The following layer names are supported: 26 | - gender 27 | - name 28 | - location 29 | - email 30 | - username 31 | - password 32 | - salt 33 | - md5 34 | - sha1 35 | - sha256 36 | - registered 37 | - dob 38 | - phone 39 | - cell 40 | - SSN 41 | 42 | Name shape layers one of the following to generate the corresponding sized profile picture: 43 | - thumbnail 44 | - medium (default) 45 | - large 46 | -------------------------------------------------------------------------------- /personas.sketchplugin: -------------------------------------------------------------------------------- 1 | const PATTERN_FILL_IMAGE = 1; 2 | const FILL_TYPE_FILL = 4; 3 | 4 | if (isNothingSelected()) { 5 | var app = NSApplication.sharedApplication(); 6 | return [app displayDialog:"Please select at least one layer or group." withTitle:"Nothing Selected"]; 7 | } 8 | 9 | if (isGroupSelected()) { 10 | updateGroups(context.selection); 11 | } else { 12 | updateLayers(context.selection, getUser()); 13 | } 14 | 15 | function updateGroups(groups) { 16 | var users = getUsers(groups.count()); 17 | for (var i=0; i < groups.count(); i++) { 18 | var group = groups[i]; 19 | var user = users[i].user; 20 | updateLayers(group.layers().array(), user); 21 | } 22 | } 23 | 24 | function updateLayers(layers, user) { 25 | for (var i=0; i < layers.count(); i++) { 26 | var layer = layers[i]; 27 | if (layer.className() == 'MSShapeGroup') { 28 | setImage(layer, user); 29 | } else { 30 | setText(layer, user); 31 | } 32 | } 33 | 34 | } 35 | 36 | function getUser() { 37 | return getUsers(1)[0]; 38 | } 39 | 40 | function getUsers(count) { 41 | var request = NSMutableURLRequest.new(); 42 | [request setHTTPMethod:@"GET"]; 43 | var queryString = "http://api.randomuser.me?nat=us&results=" + count; 44 | [request setURL:[NSURL URLWithString:queryString]]; 45 | 46 | var error = NSError.new(); 47 | var responseCode = null; 48 | 49 | var oResponseData = [NSURLConnection sendSynchronousRequest:request returningResponse:responseCode error:error]; 50 | 51 | var dataString = [[NSString alloc] initWithData:oResponseData encoding:NSUTF8StringEncoding]; 52 | var data = JSON.parse(dataString); 53 | 54 | return data.results; 55 | 56 | } 57 | 58 | function setImage(layer, user) { 59 | 60 | var name = layer.name(); 61 | 62 | if (name == 'large' || name == 'thumbnail') { 63 | size = name 64 | } else { 65 | size = 'medium' 66 | } 67 | 68 | var imageURLString = user.picture[size]; 69 | var url = [[NSURL alloc] initWithString: imageURLString]; 70 | var imageData = [[NSImage alloc] initByReferencingURL:url]; 71 | var fill = layer.style().fills().firstObject(); 72 | 73 | var image = [[MSImageData alloc] initWithImage: imageData convertColorSpace: false]] 74 | 75 | layer.style().fills().firstObject().setImage(image); 76 | layer.style().fills().firstObject().setPatternFillType(PATTERN_FILL_IMAGE); 77 | fill.setFillType(FILL_TYPE_FILL); 78 | } 79 | 80 | function setText(layer, user) { 81 | var name = layer.name(); 82 | var text; 83 | 84 | if (name == 'name') { 85 | text = user.name.first + ' ' + user.name.last; 86 | text = capitalize(text); 87 | } else if (name == 'location') { 88 | text = user.location.city + ', ' + user.location.state; 89 | text = capitalize(text); 90 | } else if (name == 'dob') { 91 | unix_time = user.dob; 92 | date = new Date(unix_time * 1000); 93 | text = date.getMonth() + "/" + date.getDate() + "/" + date.getFullYear(); 94 | } else { 95 | text = user[name]; 96 | } 97 | 98 | layer.stringValue = text; 99 | updateDisplayHack(layer); 100 | } 101 | 102 | function updateDisplayHack(layer) { 103 | layer.name = layer.name(); 104 | layer.adjustFrameToFit(); 105 | } 106 | 107 | function capitalize(s){ 108 | return s.toLowerCase().replace( /\b./g, function(a) { return a.toUpperCase(); } ); 109 | } 110 | 111 | function isGroupSelected() { 112 | return context.selection.firstObject().className() == 'MSLayerGroup'; 113 | } 114 | 115 | function isNothingSelected() { 116 | return context.selection.count() == 0; 117 | } 118 | --------------------------------------------------------------------------------