Skip to content

Instantly share code, notes, and snippets.

@rheajt
Last active May 25, 2022 02:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save rheajt/7f6b124784e7cddfea99c585d4703d7f to your computer and use it in GitHub Desktop.
Save rheajt/7f6b124784e7cddfea99c585d4703d7f to your computer and use it in GitHub Desktop.

WonderWall with Google Forms

Project started with @get_sharpe to turn form responses into a "wonder" wall for inquiry based learning

TODOS:

  1. create an addon for sheets or forms that creates the form/sheet to display.
  2. convert all jquery to pure javascript
  3. remove unneeded styles from the stylesheet

CSS taken from: SkeletonCSS BubbleCSS

function doGet(e) {
if(!e.parameters.sheetId) {
return HtmlService.createTemplateFromFile('Start').evaluate().setSandboxMode(HtmlService.SandboxMode.IFRAME);
} else {
var html = HtmlService.createTemplateFromFile('Viewer');
//set variable of sheet id to post in hidden input field for async request when page loads
html.sheetName = SpreadsheetApp.openById(e.parameters.sheetId).getName();
html.sheetId = e.parameters.sheetId;
return html.evaluate().setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
}
function getUrl(sheetId) {
return ScriptApp.getService().getUrl() + '?sheetId=' + sheetId;
}
function getSheetData(id) {
var data = SpreadsheetApp.openById(id).getActiveSheet().getDataRange().getValues();
data.shift();
return JSON.stringify(data);
}
function loadPadlet() {
return HtmlService.createHtmlOutputFromFile('Padlet').setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
function getOAuthToken() {
DriveApp.getRootFolder();
return ScriptApp.getOAuthToken();
}
<!DOCTYPE html>
<html>
<head>
<base target="_blank">
<link href="//fonts.googleapis.com/css?family=Raleway:400,300,600" rel="stylesheet" type="text/css">
<?!= HtmlService.createHtmlOutputFromFile('Stylesheet').setSandboxMode(HtmlService.SandboxMode.IFRAME).getContent() ?>
</head>
<body class="container">
<h1>Form Response Viewer</h1>
<p>Created by <a href="https://twitter.com/rheajt">@rheajt</a> and <a href="https://twitter.com/get_sharpe">@get_sharpe</a> while doing a live coding session for the rad innovators from <a href="https://twitter.com/WendyGorton/lists/googleei-col16">#COL16</a></p>
<label>What sheet are you loading?</label>
<input id="sheet-id" type="text" name="sheetId" placeholder="URL or KEY of Sheet" />
<button type="button" onclick='getOAuthToken()'>Select a file</button>
<p id='result'></p>
<?!= HtmlService.createHtmlOutputFromFile('StartJavascript').setSandboxMode(HtmlService.SandboxMode.IFRAME).getContent() ?>
</body>
</html>
<script>
var DEVELOPER_KEY = 'API_KEY_FROM_DEV_CONSOLE';
var DIALOG_DIMENSIONS = {width: 600, height: 425};
var pickerApiLoaded = false;
function onApiLoad() {
//when the key input changes update the headers in the select boxes
document.getElementById('sheet-id').addEventListener('change', function(e) {
changeId(e.target.value);
});
gapi.load('picker', {'callback': function() {
pickerApiLoaded = true;
}});
}
function getOAuthToken() {
google.script.run.withSuccessHandler(createPicker)
.withFailureHandler(showError).getOAuthToken();
return false;
}
function createPicker(token) {
if (pickerApiLoaded && token) {
var picker = new google.picker.PickerBuilder()
.addView(google.picker.ViewId.SPREADSHEETS)
.enableFeature(google.picker.Feature.NAV_HIDDEN)
.hideTitleBar()
.setOAuthToken(token)
.setDeveloperKey(DEVELOPER_KEY)
.setCallback(pickerCallback)
.setOrigin('https://script.google.com')
.setSize(DIALOG_DIMENSIONS.width - 2,
DIALOG_DIMENSIONS.height - 2)
.build();
picker.setVisible(true);
} else {
showError('Unable to load the file picker.');
}
}
function pickerCallback(data) {
var action = data[google.picker.Response.ACTION];
if (action == google.picker.Action.PICKED) {
var doc = data[google.picker.Response.DOCUMENTS][0];
var id = doc[google.picker.Document.ID];
document.getElementById('sheet-id').value = id;
changeId(id);
} else if (action == google.picker.Action.CANCEL) {
document.getElementById('result').innerHTML = 'Picker canceled.';
}
}
function showError(message) {
document.getElementById('result').innerHTML = 'Error: ' + message;
}
function changeId(id) {
google.script.run
.withSuccessHandler(placeUrl)
.getUrl(id);
}
function placeUrl(response) {
document.getElementById('result').innerHTML = '<a href="' + response + '">' + response + '</a>';
}
</script>
<script src="https://apis.google.com/js/api.js?onload=onApiLoad"></script>
<style>
/* custom styles */
#wrapper {
width: 90%;
max-width: 1900px;
min-width: 800px;
margin: 50px auto;
}
#pin-columns {
-webkit-column-count: 3;
-webkit-column-gap: 10px;
-webkit-column-fill: auto;
-moz-column-count: 3;
-moz-column-gap: 10px;
-moz-column-fill: auto;
column-count: 4;
column-gap: 15px;
column-fill: auto;
}
.pin {
display: inline-block;
border: 2px solid #FAFAFA;
box-shadow: 0 1px 2px rgba(34, 25, 25, 0.4);
margin: 0 2px 15px;
-webkit-column-break-inside: avoid;
-moz-column-break-inside: avoid;
column-break-inside: avoid;
padding: 15px;
padding-bottom: 5px;
opacity: 1;
-webkit-transition: all .2s ease;
-moz-transition: all .2s ease;
-o-transition: all .2s ease;
transition: all .2s ease;
}
.pin img {
width: 100%;
border-bottom: 1px solid #ccc;
padding-bottom: 15px;
margin-bottom: 5px;
}
.pin p {
font: 12px/18px Arial, sans-serif;
color: #333;
margin: 0;
}
@media (min-width: 960px) {
#pin-columns {
-webkit-column-count: 4;
-moz-column-count: 4;
column-count: 4;
}
}
@media (min-width: 1100px) {
#pin-columns {
-webkit-column-count: 5;
-moz-column-count: 5;
column-count: 5;
}
}
@media (min-width: 1360px) {
#pin-columns {
-webkit-column-count: 6;
-moz-column-count: 6;
column-count: 6;
}
}
@media (min-width: 1600px) {
#pin-columns {
-webkit-column-count: 7;
-moz-column-count: 7;
column-count: 7;
}
}
@media (min-width: 1830px) {
#pin-columns {
-webkit-column-count: 8;
-moz-column-count: 8;
column-count: 8;
}
}
#pin-columns:hover .pin:not(:hover) {
opacity: 0.4;
}
.triangle-isosceles {
position:relative;
padding:15px;
margin:1em 0 3em;
color:#000;
background:#f3961c; /* default background for browsers without gradient support */
/* css3 */
background:-webkit-gradient(linear, 0 0, 0 100%, from(#f9d835), to(#f3961c));
background:-moz-linear-gradient(#f9d835, #f3961c);
background:-o-linear-gradient(#f9d835, #f3961c);
background:linear-gradient(#f9d835, #f3961c);
-webkit-border-radius:10px;
-moz-border-radius:10px;
border-radius:10px;
}
.triangle-isosceles:after {
content:"";
position:absolute;
bottom:-15px; /* value = - border-top-width - border-bottom-width */
left:50px; /* controls horizontal position */
border-width:15px 15px 0; /* vary these values to change the angle of the vertex */
border-style:solid;
border-color:#f3961c transparent;
/* reduce the damage in FF3.0 */
display:block;
width:0;
}
/*
* Skeleton V2.0.4
* Copyright 2014, Dave Gamache
* www.getskeleton.com
* Free to use under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
* 12/29/2014
*/
/* Table of contents
––––––––––––––––––––––––––––––––––––––––––––––––––
- Grid
- Base Styles
- Typography
- Links
- Buttons
- Forms
- Lists
- Code
- Tables
- Spacing
- Utilities
- Clearing
- Media Queries
*/
/* Grid
–––––––––––––––––––––––––––––––––––––––––––––––––– */
.container {
position: relative;
width: 100%;
max-width: 960px;
margin: 0 auto;
padding: 0 20px;
box-sizing: border-box; }
.column,
.columns {
width: 100%;
float: left;
box-sizing: border-box; }
/* For devices larger than 400px */
@media (min-width: 400px) {
.container {
width: 85%;
padding: 0; }
}
/* For devices larger than 550px */
@media (min-width: 550px) {
.container {
width: 80%; }
.column,
.columns {
margin-left: 4%; }
.column:first-child,
.columns:first-child {
margin-left: 0; }
.one.column,
.one.columns { width: 4.66666666667%; }
.two.columns { width: 13.3333333333%; }
.three.columns { width: 22%; }
.four.columns { width: 30.6666666667%; }
.five.columns { width: 39.3333333333%; }
.six.columns { width: 48%; }
.seven.columns { width: 56.6666666667%; }
.eight.columns { width: 65.3333333333%; }
.nine.columns { width: 74.0%; }
.ten.columns { width: 82.6666666667%; }
.eleven.columns { width: 91.3333333333%; }
.twelve.columns { width: 100%; margin-left: 0; }
.one-third.column { width: 30.6666666667%; }
.two-thirds.column { width: 65.3333333333%; }
.one-half.column { width: 48%; }
/* Offsets */
.offset-by-one.column,
.offset-by-one.columns { margin-left: 8.66666666667%; }
.offset-by-two.column,
.offset-by-two.columns { margin-left: 17.3333333333%; }
.offset-by-three.column,
.offset-by-three.columns { margin-left: 26%; }
.offset-by-four.column,
.offset-by-four.columns { margin-left: 34.6666666667%; }
.offset-by-five.column,
.offset-by-five.columns { margin-left: 43.3333333333%; }
.offset-by-six.column,
.offset-by-six.columns { margin-left: 52%; }
.offset-by-seven.column,
.offset-by-seven.columns { margin-left: 60.6666666667%; }
.offset-by-eight.column,
.offset-by-eight.columns { margin-left: 69.3333333333%; }
.offset-by-nine.column,
.offset-by-nine.columns { margin-left: 78.0%; }
.offset-by-ten.column,
.offset-by-ten.columns { margin-left: 86.6666666667%; }
.offset-by-eleven.column,
.offset-by-eleven.columns { margin-left: 95.3333333333%; }
.offset-by-one-third.column,
.offset-by-one-third.columns { margin-left: 34.6666666667%; }
.offset-by-two-thirds.column,
.offset-by-two-thirds.columns { margin-left: 69.3333333333%; }
.offset-by-one-half.column,
.offset-by-one-half.columns { margin-left: 52%; }
}
/* Base Styles
–––––––––––––––––––––––––––––––––––––––––––––––––– */
/* NOTE
html is set to 62.5% so that all the REM measurements throughout Skeleton
are based on 10px sizing. So basically 1.5rem = 15px :) */
html {
font-size: 62.5%; }
body {
font-size: 1.5em; /* currently ems cause chrome bug misinterpreting rems on body element */
line-height: 1.6;
font-weight: 400;
font-family: "Raleway", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif;
color: #222; }
/* Typography
–––––––––––––––––––––––––––––––––––––––––––––––––– */
h1, h2, h3, h4, h5, h6 {
margin-top: 0;
margin-bottom: 2rem;
font-weight: 300; }
h1 { font-size: 4.0rem; line-height: 1.2; letter-spacing: -.1rem;}
h2 { font-size: 3.6rem; line-height: 1.25; letter-spacing: -.1rem; }
h3 { font-size: 3.0rem; line-height: 1.3; letter-spacing: -.1rem; }
h4 { font-size: 2.4rem; line-height: 1.35; letter-spacing: -.08rem; }
h5 { font-size: 1.8rem; line-height: 1.5; letter-spacing: -.05rem; }
h6 { font-size: 1.5rem; line-height: 1.6; letter-spacing: 0; }
/* Larger than phablet */
@media (min-width: 550px) {
h1 { font-size: 5.0rem; }
h2 { font-size: 4.2rem; }
h3 { font-size: 3.6rem; }
h4 { font-size: 3.0rem; }
h5 { font-size: 2.4rem; }
h6 { font-size: 1.5rem; }
}
p {
margin-top: 0; }
/* Links
–––––––––––––––––––––––––––––––––––––––––––––––––– */
a {
color: #1EAEDB; }
a:hover {
color: #0FA0CE; }
/* Buttons
–––––––––––––––––––––––––––––––––––––––––––––––––– */
.button,
button,
input[type="submit"],
input[type="reset"],
input[type="button"] {
display: inline-block;
height: 38px;
padding: 0 30px;
color: #555;
text-align: center;
font-size: 11px;
font-weight: 600;
line-height: 38px;
letter-spacing: .1rem;
text-transform: uppercase;
text-decoration: none;
white-space: nowrap;
background-color: transparent;
border-radius: 4px;
border: 1px solid #bbb;
cursor: pointer;
box-sizing: border-box; }
.button:hover,
button:hover,
input[type="submit"]:hover,
input[type="reset"]:hover,
input[type="button"]:hover,
.button:focus,
button:focus,
input[type="submit"]:focus,
input[type="reset"]:focus,
input[type="button"]:focus {
color: #333;
border-color: #888;
outline: 0; }
.button.button-primary,
button.button-primary,
input[type="submit"].button-primary,
input[type="reset"].button-primary,
input[type="button"].button-primary {
color: #FFF;
background-color: #33C3F0;
border-color: #33C3F0; }
.button.button-primary:hover,
button.button-primary:hover,
input[type="submit"].button-primary:hover,
input[type="reset"].button-primary:hover,
input[type="button"].button-primary:hover,
.button.button-primary:focus,
button.button-primary:focus,
input[type="submit"].button-primary:focus,
input[type="reset"].button-primary:focus,
input[type="button"].button-primary:focus {
color: #FFF;
background-color: #1EAEDB;
border-color: #1EAEDB; }
/* Forms
–––––––––––––––––––––––––––––––––––––––––––––––––– */
input[type="email"],
input[type="number"],
input[type="search"],
input[type="text"],
input[type="tel"],
input[type="url"],
input[type="password"],
textarea,
select {
height: 38px;
padding: 6px 10px; /* The 6px vertically centers text on FF, ignored by Webkit */
background-color: #fff;
border: 1px solid #D1D1D1;
border-radius: 4px;
box-shadow: none;
box-sizing: border-box; }
/* Removes awkward default styles on some inputs for iOS */
input[type="email"],
input[type="number"],
input[type="search"],
input[type="text"],
input[type="tel"],
input[type="url"],
input[type="password"],
textarea {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none; }
textarea {
min-height: 65px;
padding-top: 6px;
padding-bottom: 6px; }
input[type="email"]:focus,
input[type="number"]:focus,
input[type="search"]:focus,
input[type="text"]:focus,
input[type="tel"]:focus,
input[type="url"]:focus,
input[type="password"]:focus,
textarea:focus,
select:focus {
border: 1px solid #33C3F0;
outline: 0; }
label,
legend {
display: block;
margin-bottom: .5rem;
font-weight: 600; }
fieldset {
padding: 0;
border-width: 0; }
input[type="checkbox"],
input[type="radio"] {
display: inline; }
label > .label-body {
display: inline-block;
margin-left: .5rem;
font-weight: normal; }
/* Lists
–––––––––––––––––––––––––––––––––––––––––––––––––– */
ul {
list-style: circle inside; }
ol {
list-style: decimal inside; }
ol, ul {
padding-left: 0;
margin-top: 0; }
ul ul,
ul ol,
ol ol,
ol ul {
margin: 1.5rem 0 1.5rem 3rem;
font-size: 90%; }
li {
margin-bottom: 1rem; }
/* Code
–––––––––––––––––––––––––––––––––––––––––––––––––– */
code {
padding: .2rem .5rem;
margin: 0 .2rem;
font-size: 90%;
white-space: nowrap;
background: #F1F1F1;
border: 1px solid #E1E1E1;
border-radius: 4px; }
pre > code {
display: block;
padding: 1rem 1.5rem;
white-space: pre; }
/* Tables
–––––––––––––––––––––––––––––––––––––––––––––––––– */
th,
td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #E1E1E1; }
th:first-child,
td:first-child {
padding-left: 0; }
th:last-child,
td:last-child {
padding-right: 0; }
/* Spacing
–––––––––––––––––––––––––––––––––––––––––––––––––– */
button,
.button {
margin-bottom: 1rem; }
input,
textarea,
select,
fieldset {
margin-bottom: 1.5rem; }
pre,
blockquote,
dl,
figure,
table,
p,
ul,
ol,
form {
margin-bottom: 2.5rem; }
/* Utilities
–––––––––––––––––––––––––––––––––––––––––––––––––– */
.u-full-width {
width: 100%;
box-sizing: border-box; }
.u-max-full-width {
max-width: 100%;
box-sizing: border-box; }
.u-pull-right {
float: right; }
.u-pull-left {
float: left; }
/* Misc
–––––––––––––––––––––––––––––––––––––––––––––––––– */
hr {
margin-top: 3rem;
margin-bottom: 3.5rem;
border-width: 0;
border-top: 1px solid #E1E1E1; }
/* Clearing
–––––––––––––––––––––––––––––––––––––––––––––––––– */
/* Self Clearing Goodness */
.container:after,
.row:after,
.u-cf {
content: "";
display: table;
clear: both; }
/* Media Queries
–––––––––––––––––––––––––––––––––––––––––––––––––– */
/*
Note: The best way to structure the use of media queries is to create the queries
near the relevant code. For example, if you wanted to change the styles for buttons
on small devices, paste the mobile query code up in the buttons section and style it
there.
*/
/* Larger than mobile */
@media (min-width: 400px) {}
/* Larger than phablet (also point when grid becomes active) */
@media (min-width: 550px) {}
/* Larger than tablet */
@media (min-width: 750px) {}
/* Larger than desktop */
@media (min-width: 1000px) {}
/* Larger than Desktop HD */
@media (min-width: 1200px) {}
</style>
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<?!= HtmlService.createHtmlOutputFromFile('Stylesheet').setSandboxMode(HtmlService.SandboxMode.IFRAME).getContent() ?>
</head>
<body>
<h1 style="text-align: center;"><?= sheetName ?> <button id="stop-refresh">Stop</button></h1>
<input type="hidden" id="sheet-id" value="<?= sheetId ?>" />
<div id="wrapper">
<div id="pin-columns"></div>
</div>
<?!= HtmlService.createHtmlOutputFromFile('ViewerJavascript').setSandboxMode(HtmlService.SandboxMode.IFRAME).getContent() ?>
</body>
</html>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script>
$(function() {
var refresh = setInterval(getSheetData, 10000);
getSheetData();
$('#stop-refresh').click(function() {
clearInterval(refresh);
});
function getSheetData() {
google.script.run
.withSuccessHandler(function(response) {
var bubbleCount = 0;
var bubbleColor = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'];
var html = '';
JSON.parse(response).forEach(function(each) {
html += '<div class="pin" style="background-color:' + bubbleColor[bubbleCount] + '">';
html += '<div class="triangle-isosceles">' + each[3] + '</div>';
if(bubbleCount < 6) {
bubbleCount++;
} else {
bubbleCount = 0;
}
if(each[4]) {
html += '<img src="' + each[4] + '" />';
}
html += '<p>' + each[1] + '</p>';
html += '</div>';
});
$('#pin-columns').empty().append(html);
})
.getSheetData($('#sheet-id').val());
}
})
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment