├── LICENSE.md ├── index.html ├── style.css ├── .glitch-assets ├── README.md └── script.js /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Xin Xin 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. -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 🍂Leafy🍂 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |

Leafy Template 🍃

20 | 21 |
22 | 23 |
24 |
25 | 26 |
27 | 28 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | /* CSS files add styling rules to your content */ 2 | 3 | body { 4 | font-family: helvetica, arial, sans-serif; 5 | background-color: #f5e2dc; 6 | margin: 2em; 7 | padding: 2em; 8 | font-size: 14px; 9 | } 10 | 11 | .btn, 12 | p.itemPara, 13 | p { 14 | padding: 12px 16px; 15 | margin: 2px; 16 | } 17 | 18 | h1, 19 | p { 20 | padding: 12px 3px; 21 | margin: 2px; 22 | } 23 | 24 | a, 25 | p { 26 | color: black; 27 | font-size: 14px; 28 | line-height: 18px; 29 | } 30 | 31 | a.itemLink, 32 | p.itemPara { 33 | float: left; 34 | display: inline-block; 35 | border-radius: 12px; 36 | background-color: #f1f1f1; 37 | } 38 | 39 | .filterDiv, 40 | .show { 41 | float: left; 42 | width: 100%; 43 | } 44 | 45 | .filterDiv, 46 | .show, 47 | .btn { 48 | margin: 2px 2px; 49 | } 50 | 51 | .filterDiv { 52 | display: none; 53 | } 54 | 55 | .show { 56 | display: inline-block; 57 | } 58 | 59 | #myBtnContainer { 60 | margin-bottom: 20px; 61 | } 62 | 63 | .container { 64 | margin-bottom: 20px; 65 | overflow: hidden; 66 | } 67 | 68 | /* Style the buttons */ 69 | .btn { 70 | border-radius: 12px; 71 | background-color: #f1f1f1; 72 | font-size: 14px; 73 | border: none; 74 | outline: none; 75 | cursor: pointer; 76 | } 77 | 78 | .btn:hover { 79 | background-color: #ddd; 80 | } 81 | 82 | .btn.active { 83 | background-color: #666; 84 | color: white; 85 | } 86 | -------------------------------------------------------------------------------- /.glitch-assets: -------------------------------------------------------------------------------- 1 | {"name":"drag-in-files.svg","date":"2016-10-22T16:17:49.954Z","url":"https://cdn.hyperdev.com/drag-in-files.svg","type":"image/svg","size":7646,"imageWidth":276,"imageHeight":276,"thumbnail":"https://cdn.hyperdev.com/drag-in-files.svg","thumbnailWidth":276,"thumbnailHeight":276,"dominantColor":"rgb(102, 153, 205)","uuid":"adSBq97hhhpFNUna"} 2 | {"name":"click-me.svg","date":"2016-10-23T16:17:49.954Z","url":"https://cdn.hyperdev.com/click-me.svg","type":"image/svg","size":7116,"imageWidth":276,"imageHeight":276,"thumbnail":"https://cdn.hyperdev.com/click-me.svg","thumbnailWidth":276,"thumbnailHeight":276,"dominantColor":"rgb(243, 185, 186)","uuid":"adSBq97hhhpFNUnb"} 3 | {"name":"paste-me.svg","date":"2016-10-24T16:17:49.954Z","url":"https://cdn.hyperdev.com/paste-me.svg","type":"image/svg","size":7242,"imageWidth":276,"imageHeight":276,"thumbnail":"https://cdn.hyperdev.com/paste-me.svg","thumbnailWidth":276,"thumbnailHeight":276,"dominantColor":"rgb(42, 179, 185)","uuid":"adSBq97hhhpFNUnc"} 4 | {"uuid":"adSBq97hhhpFNUna","deleted":true} 5 | {"uuid":"adSBq97hhhpFNUnb","deleted":true} 6 | {"uuid":"adSBq97hhhpFNUnc","deleted":true} 7 | {"name":"unnamed.gif","date":"2020-03-21T16:15:28.184Z","url":"https://cdn.glitch.com/a0713ae5-198b-4366-b7e9-e40b63c44f84%2Funnamed.gif","type":"image/gif","size":111397,"imageWidth":512,"imageHeight":366,"thumbnail":"https://cdn.glitch.com/a0713ae5-198b-4366-b7e9-e40b63c44f84%2Fthumbnails%2Funnamed.gif","thumbnailWidth":330,"thumbnailHeight":236,"uuid":"nVjStkw6QyOmButi"} 8 | {"name":"Screen Shot 2020-03-23 at 12.33.27 AM.png","date":"2020-03-23T04:33:33.419Z","url":"https://cdn.glitch.com/a0713ae5-198b-4366-b7e9-e40b63c44f84%2FScreen%20Shot%202020-03-23%20at%2012.33.27%20AM.png","type":"image/png","size":95026,"imageWidth":1440,"imageHeight":968,"thumbnail":"https://cdn.glitch.com/a0713ae5-198b-4366-b7e9-e40b63c44f84%2Fthumbnails%2FScreen%20Shot%202020-03-23%20at%2012.33.27%20AM.png","thumbnailWidth":330,"thumbnailHeight":222,"uuid":"wbtQynsKI2fhGVGV"} 9 | {"uuid":"wbtQynsKI2fhGVGV","deleted":true} 10 | {"name":"Screen Shot 2020-03-23 at 12.57.15 AM.png","date":"2020-03-23T04:57:55.695Z","url":"https://cdn.glitch.com/a0713ae5-198b-4366-b7e9-e40b63c44f84%2FScreen%20Shot%202020-03-23%20at%2012.57.15%20AM.png","type":"image/png","size":121238,"imageWidth":2364,"imageHeight":1258,"thumbnail":"https://cdn.glitch.com/a0713ae5-198b-4366-b7e9-e40b63c44f84%2Fthumbnails%2FScreen%20Shot%202020-03-23%20at%2012.57.15%20AM.png","thumbnailWidth":330,"thumbnailHeight":176,"uuid":"btabpprs0fvrbuGt"} 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Leafy 2 | 3 | Leafy is an easy-to-use template designed to turn your google spreadsheet into a styled, sortable table on a website. 4 | 5 | ![Leafy example screenshot](https://cdn.glitch.com/a0713ae5-198b-4366-b7e9-e40b63c44f84%2FScreen%20Shot%202020-03-23%20at%2012.57.15%20AM.png?v=1584939475695) 6 | *Screenshot of example Leafy page* 7 | 8 | - Built on [Tabletop.js](https://github.com/jsoma/tabletop), which lets you use google spreadsheet as your backend database. 9 | - Use the spread sheet to generate a sortable list on your website. 10 | - Sort the list through customized categories. Tag items in your spreadsheet with multiple categories. 11 | 12 | Leafy is made for educators and organizers, with the intention of small - large scale collaborations. Build an ongoing list of readings, resources, to-dos, etc and distribute your content to the greater community. Making edits to the spreadsheet will automatically post these changes to your Leafy site. 13 | 14 | # Examples built with Leafy 15 | 16 | [Basic minimal example](https://leafy-template.glitch.me/) 17 | 18 | [Weird Game-Making Tools](http://weird-game-tools.glitch.me/), contributed by [@lee2sman](https://github.com/lee2sman) 19 | 20 | [Thesis during COVID](https://thesis-during-covid.glitch.me/), multiple contributors 21 | 22 | [Open source, experimental, and tiny tools roundup](https://tinytools.directory/), multiple contributors, compiled by [@everestpipkin](https://github.com/everestpipkin) 23 | 24 | # Getting Started 25 | 26 | ## Set up a google spreadsheet 27 | 1. Visit the google spreadsheet template I've created [here](https://docs.google.com/spreadsheets/d/1Q23ZnH7KHBHahFT65_9RisSu1Wk4gNOrowiprtxgE4A/edit?usp=sharing). Duplicate the spreadsheet and save it into your own google drive. 28 | 1. Edit the spreadsheet with your own data. You can expand or delete the number of categories. Enter URLs under the Link column, or you could also leave it empty. 29 | 1. Visit [Tabletop.js' README](https://github.com/jsoma/tabletop/blob/master/README.md), scroll down to Getting Started, and follow the instructions inside the **1) Publishing your Google Sheet** section. 30 | 31 | ## Put spreadsheet on a website 32 | 33 | We're ready to connect the spreadsheet to our website. From here you could move forward by remixing the [Leafy template](https://glitch.com/~leafy-template) hosted on Glitch, or you could also [download the repository](https://github.com/xinemata/leafy/archive/master.zip) and work locally. 34 | 35 | Now go to script.js, and look at the top section where it says `editable info`: 36 | 37 | 1. `const publicSpreadsheetUrl` replace this with your own spreadsheet URL. 38 | 1. `const categoryStartNum` let the program know where the categoy begins on the spreadsheet column. Default value is 3. 39 | 1. `const sheetName` this has to match the name of your sheet. Default value is "Sheet 1". 40 | 1. `const punctuation` this changes the punctuation between the title and the description. 41 | 1. That's it! 42 | 43 | ![Leafy animation](https://cdn.glitch.com/a0713ae5-198b-4366-b7e9-e40b63c44f84%2Funnamed.gif?v=1584807328184) 44 | 45 | ## Notes 46 | 47 | - This template doesn't support multiple sheets 48 | -------------------------------------------------------------------------------- /script.js: -------------------------------------------------------------------------------- 1 | // editable info 2 | 3 | const publicSpreadsheetUrl = 4 | "https://docs.google.com/spreadsheets/d/1Q23ZnH7KHBHahFT65_9RisSu1Wk4gNOrowiprtxgE4A/edit?usp=sharing"; // change this to your own URL 5 | const categoryStartNum = 3; // let the program know where the categoy begins on the spreadsheet column. Default value is 3. 6 | const sheetName = "Sheet1"; // this has to match your google doc sheet name 7 | const punctuation = ","; // this changes the punctuation between the title and the description. In most cases you'd want to use "," or "-" or ":" 8 | 9 | // tableTop.js script 10 | function init() { 11 | Tabletop.init({ 12 | key: publicSpreadsheetUrl, 13 | callback: showInfo, 14 | simpleSheet: true 15 | }); 16 | } 17 | 18 | function showInfo(data, tabletop) { 19 | const checked = "x"; 20 | const columnArray = tabletop.sheets()[sheetName].columnNames; 21 | const columnName = [columnArray.length]; 22 | 23 | for (let j = 0; j < columnArray.length; j++) { 24 | columnName[j] = columnArray[j]; 25 | } 26 | 27 | // create sorting buttons 28 | for (let j = categoryStartNum; j < columnArray.length; j++) { 29 | addButton(columnName[j]); 30 | } 31 | 32 | for (let i = categoryStartNum; i < columnArray.length; i++) { 33 | for (let j = 0; j < data.length; j++) { 34 | if (data[j][columnName[i]] == checked) { 35 | addElement( 36 | columnName[i], 37 | data[j][columnName[0]], 38 | data[j][columnName[1]], 39 | data[j][columnName[2]] 40 | ); 41 | } 42 | } 43 | } 44 | // alert("Successfully processed!"); // check if data is imported 45 | } 46 | 47 | function addButton(columnName) { 48 | const newButton = document.createElement("BUTTON"); 49 | const newButtonContent = document.createTextNode(columnName); 50 | 51 | newButton.appendChild(newButtonContent); 52 | newButton.className = "btn"; 53 | newButton.addEventListener("click", function() { 54 | filterSelection(columnName); 55 | btnOff(); // turns off all active buttons 56 | newButton.classList.add("active"); // turn this button on 57 | }); 58 | document.getElementById("myBtnContainer").appendChild(newButton); 59 | } 60 | 61 | function btnOff() { 62 | let btnClassArray = document.getElementsByClassName("btn"); 63 | for (let i = 0; i < btnClassArray.length; i++) { 64 | if (btnClassArray[i].classList.contains("active")) { 65 | btnClassArray[i].classList.remove("active"); 66 | } 67 | } 68 | } 69 | 70 | function addElement(columnName, person, url, description) { 71 | const hashtag1 = ["filterDiv"]; 72 | const hashtag2 = [columnName]; 73 | const hashtagArray = hashtag1.concat(hashtag2); 74 | const hashtagString = hashtagArray.join(" "); 75 | const newDiv = document.createElement("div"); 76 | newDiv.className = hashtagString; 77 | 78 | // place individual link inside individual div 79 | for (let i = 0; i < 1; i++) { 80 | let link = document.createElement("a"); 81 | let linkContent = document.createTextNode(person); 82 | link.appendChild(linkContent); 83 | link.title = person; 84 | link.href = url; 85 | link.className = "itemLink"; 86 | 87 | let para = document.createElement("p"); 88 | let paraContent = document.createTextNode(`${punctuation} ${description}`); 89 | para.appendChild(paraContent); 90 | para.className = "itemPara"; 91 | 92 | para.appendChild(link); // put into

93 | link.after(paraContent); // put

description after 94 | newDiv.appendChild(para); // put

into newDiv 95 | } 96 | document.getElementsByClassName("container")[0].appendChild(newDiv); 97 | } 98 | 99 | window.addEventListener("DOMContentLoaded", init); 100 | 101 | // filter script 102 | function filterSelection(c) { 103 | var x, i; 104 | x = document.getElementsByClassName("filterDiv"); 105 | for (i = 0; i < x.length; i++) { 106 | w3RemoveClass(x[i], "show"); 107 | if (x[i].className.indexOf(c) > -1) w3AddClass(x[i], "show"); 108 | } 109 | } 110 | 111 | function w3AddClass(element, name) { 112 | var i, arr1, arr2; 113 | arr1 = element.className.split(" "); 114 | arr2 = name.split(" "); 115 | for (i = 0; i < arr2.length; i++) { 116 | if (arr1.indexOf(arr2[i]) == -1) { 117 | element.className += " " + arr2[i]; 118 | } 119 | } 120 | } 121 | 122 | function w3RemoveClass(element, name) { 123 | var i, arr1, arr2; 124 | arr1 = element.className.split(" "); 125 | arr2 = name.split(" "); 126 | for (i = 0; i < arr2.length; i++) { 127 | while (arr1.indexOf(arr2[i]) > -1) { 128 | arr1.splice(arr1.indexOf(arr2[i]), 1); 129 | } 130 | } 131 | element.className = arr1.join(" "); 132 | } 133 | --------------------------------------------------------------------------------