Skip to content

Instantly share code, notes, and snippets.

@hraban
Forked from coinsandsteeldev/dialog.html
Last active December 1, 2019 22:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hraban/0a79392fecd213be143ef4f3f834b9f5 to your computer and use it in GitHub Desktop.
Save hraban/0a79392fecd213be143ef4f3f834b9f5 to your computer and use it in GitHub Desktop.
Google Sheets script to allow multi-select in cells with data-validation (adapted from https://www.youtube.com/watch?v=dm4z9l26O0I)
<!DOCTYPE html>
<html>
<head>
<script>
// https://github.com/reaxis/mu
(function(d){"object"===typeof exports?module.exports=d():"function"===typeof define&&define.amd?define(d):mu=µ=d()})(function(){function d(){}function m(a){return a.replace(/-(.)/g,function(a,c){return c.toUpperCase()})}function k(a){return"[object Array]"==={}.toString.call(a)}function g(a){return[].slice.call(a)}d.one=function(a){return document.querySelector(a)};d.all=function(a){return g(document.querySelectorAll(a))};d.create=function(a){return document.createElement(a)};var h={one:function(a){return this.querySelector(a)},all:function(a){return g(this.querySelectorAll(a))},each:function(a){a.bind(this)(0);return this},"µAddEventListener":Node.prototype.addEventListener,addEventListener:function(){this.µEventCache=this.µEventCache||[];this.µEventCache.push(arguments);this.µAddEventListener.apply(this,arguments)},on:function(a,b){this.addEventListener(a,b);return this},add:function(){return g(arguments).reduce(function(a,b){k(b)?a.add.apply(a,b):a.appendChild(b.nodeType?b:document.createTextNode(b));return a},this)},css:function(a){if("string"===typeof a)return this.style[m(a)];for(var b in a)this.style[m(b)]=a[b];return this},attr:function(a){if("string"===typeof a)return this.getAttribute(a);for(var b in a)this.setAttribute(b,a[b]);return this},cls:function(){this.classList.add.apply(this.classList,arguments);return this},empty:function(){this.innerHTML="";return this},remove:function(){return this.parentNode.removeChild(this)},text:function(a){if("undefined"===typeof a)return this.textContent;this.textContent=a;return this},copy:function(){var a=this.cloneNode(!1);this.µEventCache&&this.µEventCache.each(function(){a.addEventListener.apply(a,this)});return a.add.apply(a,g(this.childNodes))}},e;for(e in h)Node.prototype[e]=h[e];Window.prototype.on=Node.prototype.on;h={each:function(a){this.forEach(function(b,c){a.bind(b)(c)});return this},one:function(a){var b=[];this.each(function(){this.querySelector(a)&&b.push(this.querySelector(a))});return b},all:function(a){var b=[];this.each(function(){b=b.concat(g(this.querySelectorAll(a)))});return b},add:function(){var a=arguments;return this.each(function(){return g(a).reduce(function(a,c){k(c)?a.add.apply(a,c.copy()):a.appendChild(c.nodeType?c.copy():document.createTextNode(c));return a},this)})}};for(e in h)Array.prototype[e]=h[e];"on css attr cls empty remove text copy".split(" ").each(function(){var a=this+"";Array.prototype[a]=function(){var b=arguments,c=[];this.each(function(){c.push(this[a].apply(this,b))});return c}});var l={img:["src","alt","title"],a:["href"],input:["type","name","value"],option:["value"],abbr:["title"],canvas:["width","height"]};e={ul:"li",ol:"li",tr:"td",table:"tr"};"section nav article aside header footer address main div span p strong em h1 h2 h3 h4 h5 h6 li td".split(" ").each(function(){l[this]=[]});for(var f in l)d[f]=d[f.toUpperCase()]=function(a,b){return function(){var c=g(arguments);return d.create(a).attr(b.reduce(function(a,b){c.length&&(a[b]=c.shift());return a},{})).add(c)}}(f,l[f]);for(f in e)d[f]=d[f.toUpperCase()]=function(a,b){return function(){return d.create(a).add(g(arguments).map(function(a){return d[b].apply(null,k(a)?a:[a])}))}}(f,e[f]);return d});
</script>
<script>
function isArray(o) {
return Object.prototype.toString.call(o) === '[object Array]';
}
var data;
var formId = 'form';
function drawFormAux() {
if (!data) return;
if (!isArray(data)) {
return µ.p('This cell has no ', µ.a('https://support.google.com/drive/answer/139705?hl=en', 'Data validation'));
}
const inputs = data.map(([head, ...tail]) => {
if (!head) return;
const suffix = tail.filter(x => x).join('; ');
const label = head + (suffix ? ` - ${suffix}` : '');
return [µ.create('label').add(µ.input('checkbox', head, head), ' ', label), µ.create('br')];
}).filter(x => x);
return µ.div(inputs);
}
function drawForm() {
var outputEl = document.getElementById(formId);
try {
const node = drawFormAux();
if (node) {
outputEl.innerHTML = node.outerHTML;
}
} catch (e) {
outputEl.innerHTML = µ.div(µ.h1(e.message), µ.pre(e.stack)).innerHTML;
}
}
function onData(result) {
data = result
drawForm();
}
google.script.run
.withSuccessHandler(onData)
.getValidationData();
function set() {
google.script.run
.withSuccessHandler(x=>{})
.fillCell(document.getElementById(formId))
}
function update() {
google.script.run.withSuccessHandler(x=>{
}).updateCell(document.getElementById(formId))
}
function reset() {
drawForm()
}
</script>
</head>
<body>
<div style='position:fixed; margin-top: 10px; background-color: white; height: 30px; width: 100%; top: 0;'>
<input type="button" value="Set" onclick="set()" />
<input type="button" value="Update" onclick="update()" />
<input type="button" value="Reset" onclick="reset()" />
<input type="button" value="Refresh validation" onclick="google.script.run.showDialog()" />
</div>
<div style="font-family: sans-serif; margin-top: 30px;">
<form id="form" name="form">
</form>
</div>
</body>
<html>
function onOpen(e) {
SpreadsheetApp.getUi()
.createMenu('Scripts')
.addItem('Multi-select for this cell', 'showDialog')
.addToUi();
}
function showDialog() {
var html = HtmlService.createHtmlOutputFromFile('dialog').setSandboxMode(HtmlService.SandboxMode.IFRAME);
SpreadsheetApp.getUi()
.showSidebar(html);
}
function getValidationData(){
try {
return SpreadsheetApp.getActiveRange().getDataValidation().getCriteriaValues()[0].getValues();
} catch(e) {
return null
}
}
function setValues_(e, update) {
var separator = ', '
var selectedValues = Object.keys(e);
var total = selectedValues.length;
if (total === 0) {
return;
}
var range = SpreadsheetApp.getActiveRange()
var value = selectedValues.join(separator)
if (!update) {
range.setValue(value);
return;
}
var values = range.getValues()
// check every cell in range
for (var row = 0; row < values.length; ++row) {
for (var column = 0; column < values[row].length; ++column) {
//typeof values[row][column] === Array ? values[row][column].split(separator) : [values[row][column]+'']
var currentValues = values[row][column].split(separator);
var newValues = Array.from(new Set(currentValues));
if (newValues.length > 0) {
range.getCell(row+1, column+1).setValue(newValues.join(separator)+separator+value)
} else {
range.getCell(row+1, column+1).setValue(value);
}
}
}
}
function updateCell(e) {
return setValues_(e, true)
}
function fillCell(e) {
setValues_(e)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment