├── 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 | 
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 | 
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 |
--------------------------------------------------------------------------------