Skip to content

Commit 3272004

Browse files
committed
12/17/2022 - This is far from complete, do not use.
1 parent 3ce78b1 commit 3272004

File tree

7 files changed

+316
-0
lines changed

7 files changed

+316
-0
lines changed

appsscript.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"timeZone": "America/Denver",
3+
"dependencies": {
4+
},
5+
"exceptionLogging": "STACKDRIVER",
6+
"runtimeVersion": "V8"
7+
}

constructorFunctions.gs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* Constructor function to be used in conjunction with the native filter() method to pull unique values from a flattened one dimensional array.
3+
* This constructor function (and its useage with the filter function in other parts of this program) were
4+
* shamelessly stolen from: https://stackoverflow.com/questions/1960473/get-all-unique-values-in-a-javascript-array-remove-duplicates
5+
*
6+
* An example use of the filter function used with a constructor function would be something like:
7+
* //var array = [a.b.c.c.d.d.]
8+
* //var unique = array.filter(onlyUnique);
9+
*
10+
* There is also a fancy ECMA 6 way of writing this:
11+
* //var myArray = ['a', 1, 'a', 2, '1'];
12+
* //var unique = myArray.filter((v, i, a) => a.indexOf(v) === i)
13+
*
14+
* @value - the array value being passed into the function
15+
* @index - the index of the value being passed within the array
16+
* @self - I believe this is calling to the array somehow when this is nested inside of the filter function
17+
*
18+
*/
19+
function onlyUnique(value, index, self) {
20+
return self.indexOf(value) === index;
21+
}

createMenu.html

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<base target="_top">
5+
<script>
6+
function getCreateVals(){
7+
var obj = {};
8+
9+
obj.optsRange = document.getElementById("optsRange").value;
10+
obj.ddsRange = document.getElementById("ddsRange").value;
11+
12+
google.script.run.createDDs(obj);
13+
Console.log("Ranges Entered");
14+
closeDialog()
15+
}
16+
function closeDialog(){
17+
google.script.host.close();
18+
Console.log("Create Canceled")
19+
20+
}
21+
</script>
22+
<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
23+
24+
</head>
25+
<body>
26+
<div class = 'inline form-group'>
27+
<label for ='Options_Range'>Range of Dropdown Options:</label>
28+
<input type = 'text' id = 'optsRange'style="width: 200px;">
29+
30+
</div>
31+
<br></br>
32+
<div class = 'inline form-group'>
33+
<label for ='Dropdowns_Range'>Range of Dropdown Menus:</label>
34+
<input type = 'text' id = 'ddsRange'style="width: 200px;">
35+
36+
</div>
37+
<br></br>
38+
<br></br>
39+
<br></br>
40+
<input type = 'button' value = 'Cancel' onclick = 'closeDialog()'>
41+
<input type = 'button' value = 'Save' onclick = 'getCreateVals()'>
42+
</body>
43+
</html>

functions.gs

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
/**
2+
* This function creates a new sheet called "Menus_1" that contains the
3+
* dropdown categories. Caution: running this will overwrite the
4+
* current version of Menus_1, this should be considered a temporary function
5+
*/
6+
7+
function createNew() {
8+
var ss = SpreadsheetApp.getActiveSpreadsheet();
9+
var dProps = PropertiesService.getDocumentProperties();
10+
11+
//TODO: make this array dynamic so that the user can enter as many
12+
//categories as they need
13+
var headArr = [
14+
["Cat_1","Cat_2","Cat_3","Cat_4","Cat_5"]
15+
];
16+
17+
ss.insertSheet().setName("Menus_1");
18+
19+
var menSht = ss.getSheetByName("Menus_1");
20+
var menRng = menSht.getRange("Menus_1!$A$1:$E$1");
21+
menRng.setValues(headArr);
22+
23+
}
24+
25+
/**
26+
* This function takes range arguments from the 'createMenu' form as input and returns
27+
* a nested object using the nestObj() function.
28+
*
29+
* @obj - an object containing two strings as values. First string is the range where
30+
* the dropdown menu options are stored in hierarchical order, the second string is where the
31+
*
32+
*/
33+
function createDDs(obj){
34+
var ss = SpreadsheetApp.getActiveSpreadsheet();
35+
var dProps = PropertiesService.getDocumentProperties();
36+
37+
areDDs();
38+
var ddObj = JSON.parse(dProps.getProperties()['dropdowns']);
39+
var count = countProp(ddObj);
40+
41+
//TODO: error handling. If this throws an error, it means that the original objects were
42+
//misnnamed in the html document. This is probably not as essential since I don't see
43+
// a condition where this would actually happen.
44+
45+
var optsRng = obj.optsRange;
46+
var ddsRng = obj.ddsRange;
47+
48+
//TODO:if there are already dropdowns in the target range set in user properties, overwrite
49+
// the existing menus with the new one being declared. A structured error message might be
50+
//good to use here at some point, but I think the user can figure out if they made
51+
//drowpdon menus that overlap each other because it should cause some obvious problems
52+
//for them right away.
53+
54+
ss.getRange("Sheet1!F2").setValue(JSON.stringify(ddObj));
55+
ss.getRange("Sheet1!F3").setValue(count);
56+
57+
}
58+
59+
/**
60+
* This function checks if the 'dropdowns' object is present
61+
* in the document properties. If not, it creates one. Note that even though 'newObj' is
62+
* declared as an object in this script, Google's document properties seems to only be capable of storing
63+
* stringified JSON objects - hence the reason for JSON.parse() being used in the createDDs() function.
64+
*/
65+
66+
function areDDs(){
67+
var dProps = PropertiesService.getDocumentProperties();
68+
var bool = (dProps.getProperties()['dropdowns'] == null);
69+
var newObj = {"dropdowns":{}};
70+
71+
//Logger.log(JSON.stringify(dProps));
72+
//Logger.log(dProps.getProperties()['dropdowns']);
73+
//Logger.log('boolean: ' + bool);
74+
75+
//if there is not a dropdown object, create one
76+
77+
78+
if(bool === true){
79+
dProps.setProperties(newObj)
80+
}
81+
82+
//Logger.log("updated boolean : "+ (dProps.getProperties()['dropdowns'] == null))
83+
//Logger.log("getProperties : " + JSON.stringify(dProps.getProperties()['dropdowns']));
84+
//Logger.log(dProps.getProperties.hasOwnProperty('dropdowns'));
85+
//Logger.log(JSON.stringify(dProps.getProperties()['dropdowns']))
86+
87+
}
88+
89+
/**
90+
* A function that deletes all document properties
91+
*/
92+
93+
function deleteDocProps(){
94+
PropertiesService.getDocumentProperties().deleteAllProperties()
95+
}
96+
97+
/**
98+
* Todo: create a function that will count the number of dropdown functions present in the document properties
99+
* The number given below is just a placeholder.
100+
*/
101+
102+
function countProp(obj){
103+
var res = Object.keys(obj).length;
104+
return res
105+
106+
}
107+
108+
/**
109+
* Todo: create a function that compares the dimensions of two arrays
110+
*/
111+
112+
function compareRange(range1,range2){
113+
114+
}
115+
116+
/**
117+
* This function takes an array argument and returns a JSON object containing sub objects (labeled tier_1, tier_2, etc.) that correspond
118+
* with the dropdown column tiers.
119+
*
120+
* @array - a one-dimensional array containing categories that will be
121+
* nested within the returned JSON object. This function contains a method for parsing individual columns of 2d arrays that was
122+
* pulled from: https://stackoverflow.com/questions/7848004/get-column-from-a-two-dimensional-array
123+
*
124+
* @array - the subject array of values
125+
* @headers - a true/false value indicating whether the array includes a single header row that should not be included in the dd
126+
* objects
127+
*
128+
*/
129+
130+
function nestObj(array,header = true){
131+
var obj = {};
132+
var arr = array;
133+
134+
//get the dimensions of the array as seperate variables
135+
var width = arr[0].length;
136+
var height = arr.length;
137+
138+
//iterate over each column in the array in oreder to create "tiers" of dropdown menu objects
139+
//that correspond to the number of columns.
140+
for (var i = 0; i < width-1 ;i++){
141+
var nestObj = {};
142+
var key = "tier_" + (i+1).toString(); //note that the tiers are not 0-indexed
143+
var firstCol = arr.map(function(value,index) { return value[i]; });
144+
var firstUnqVal = firstCol.filter(onlyUnique);
145+
var firstUnqLen = firstUnqVal.length;
146+
147+
//iterate over just the unique objects extracted from the current column
148+
for (var j = 0; j < firstUnqLen; j++){
149+
var jVal = firstUnqVal[j];
150+
151+
//create empty array to hold the nested values
152+
nestArr = [];
153+
path = [];
154+
155+
//create array that serves as the value for the deepest tier of the current nested object.
156+
for (var k = 0; k < height; k++){
157+
var nxtVal = arr[k][i+1];
158+
var curVal = arr[k][i];
159+
var rowArr = arr[k];
160+
161+
if(jVal == curVal && !nestArr.includes(nxtVal) && (!nxtVal == null || !nxtVal.length == 0 || !nxtVal.length == undefined)){
162+
nestArr.push(nxtVal);
163+
var path = rowArr.slice(0,i+1);
164+
};
165+
};
166+
//create the path
167+
set(nestObj,path,nestArr);
168+
};
169+
obj[key] = nestObj;
170+
};
171+
return obj
172+
}
173+
174+
/**
175+
* This accepts an object, an array, and a "val" - a last value. The second array argument gets converted into
176+
* a nested array, and the innermost object. This is based on a function available at https://stackoverflow.com/questions/5484673/javascript-how-to-dynamically-create-nested-objects-using-object-names-given-by
177+
*
178+
* @obj - object to have key value pairings added
179+
* @path - the path of the nested value as a one-dimensional array
180+
* @val - the value of the innermost key in the nested object
181+
*/
182+
const set = (obj, path, val) => {
183+
const keys = path;
184+
const lastKey = keys.pop();
185+
const lastObj = keys.reduce((obj, key) =>
186+
obj[key] = obj[key] || {},
187+
obj);
188+
lastObj[lastKey] = val;
189+
}
190+
191+
function openCreateForm(){
192+
var modalForm = HtmlService.createHtmlOutputFromFile('createMenu');
193+
SpreadsheetApp.getUi().showModalDialog(modalForm,'Create New Set of Cascading Dropdown Menus');
194+
}
195+

menuFunctions.gs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* This function creates the modal dialogue box where the end user
3+
* enters the ranges of the category index (i.e. the range where they define categories)
4+
* and the dropdown index (i.e. the part of the sheet where the cascading dropdown menus will exist).
5+
*
6+
*/
7+
8+
function createDDForm() {
9+
10+
}

onOpen.gs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* The onOpen() function is a reserved name in Google Apps script and runs whenever the Spreadsheet is opened or
3+
* initialized. In this script, the onOpen() function creates the dropdown menus in the user interface, and
4+
* automatically runs the isDropDown() function.
5+
*/
6+
7+
function onOpen() {
8+
var ss = SpreadsheetApp.getActiveSpreadsheet();
9+
var dProps = PropertiesService.getDocumentProperties();
10+
var ddTF = true; //TODO: finish writing the areDDs function that determines whether a document property called 'dropdowns' exists or not.
11+
12+
var menuOpts = [{
13+
name: '❇️ Create Dropdowns',
14+
functionName: 'openCreateForm'
15+
},{
16+
name: '🔄 Refresh Dropdowns',
17+
functionName: 'refreshDDForm'
18+
},
19+
{
20+
name: "✅ Manage Dropdowns",
21+
functionName: 'manageDDForm'
22+
}];
23+
24+
ss.addMenu("Custom",menuOpts);
25+
26+
}

test.gs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
function testFunction() {
2+
var ss = SpreadsheetApp.getActiveSpreadsheet();
3+
var range = ss.getRange("Menus_1!$A$2:$D$53").getValues();
4+
5+
//Testing the nestObj function
6+
7+
//var testResult = nestObj(range);
8+
//Logger.log(testResult)
9+
10+
areDDs()
11+
deleteDocProps()
12+
13+
14+
}

0 commit comments

Comments
 (0)