Skip to content

Instantly share code, notes, and snippets.

@gjroelofs
Last active April 27, 2022 19:38
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save gjroelofs/554474a20482f00d2c3c666d3ec50b85 to your computer and use it in GitHub Desktop.
Save gjroelofs/554474a20482f00d2c3c666d3ec50b85 to your computer and use it in GitHub Desktop.
/*************** Hierarchical Sections *******************/
var folders = {
// "<name>" : JQueryObject (section)
};
var childMapping = {
// "PX-2": [
// "Sprint Planning"
// ],
};
var parentMapping = {
// "Sprint Planning": "PX-2",
};
var DEBUG = false;
var codaDocID = window.location.href;
codaDocID = /https:\/\/coda.io\/d\/[^\/]*_d([^\/]*)\//g.exec(codaDocID)[1];
function isEmpty(str) {
return (!str || 0 === str.length);
}
/* Get the folder display name. */
function getFolderName(folder){
return folder.find("div.section_list_item--folderInput--12MBfIRp")
.first()
.text();
}
function isOpenFolder(folder){
return folder.attr('class').includes("folder--open--2ZilK7HF");
}
/* Retrieve the attachment div for a folder to which other folders can be attached. */
function grabFolderList(folder){
var listParent = folder.children(".details_with_chevron--root--3tk2hW-f");
listParent = listParent.children(".details_view--root--pSxtTLYV");
listParent = listParent.children().last();
// Grab either the list view (folder has sections), or the div empty div above it.
return listParent;
}
/* Ensures that the child div is added as a visual child of the folder div. */
function insertIntoFolder(child, folder){
var list = grabFolderList(folder);
child.css('padding-left', '20px')
child.appendTo(list);
}
function setupChildrenForFolder(folder){
// Find the name of this folder.
// Grab the first occurence as we can have child folders.
var name = getFolderName(folder);
if(DEBUG) console.log("Setting up children for Folder: "+ name);
// Go through all expected and existing children, and insert them into this parent.
if(!(name in childMapping)) return;
var children = childMapping[name];
for (index = 0; index < children.length; ++index) {
var child = children[index]
if(!(child in folders)){
console.error("Requested child was an unknown folder: " + child + " for parent folder: " + name + " with child mapping: " + children);
continue;
}
if(DEBUG) console.log("Folder "+ name + " will receive child: " + child);
insertIntoFolder(folders[child], folder);
}
}
// Define attrchange on JQuery objects as an adhoc MutationObserver implementation.
$(function() {
(function($) {
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
$.fn.attrchange = function(callback) {
if (MutationObserver) {
var options = {
subtree: false,
attributes: true
};
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(e) {
callback.call(e.target, e.attributeName);
});
});
return this.each(function() {
observer.observe(this, options);
});
}
}
})(jQuery);
});
function startup(){
function handleFolderCreation(){
// Find the name of this folder
// Grab the first occurence as we can have child folders.
var name = getFolderName($(this));
// Store the folder under its name
folders[name] = $(this);
if(DEBUG) console.log("Setting up closed folder: "+ name);
// See if we can attach ourselves to a parent, if not, hide.
if(name in parentMapping){
var parent = parentMapping[name]
if(parent in folders && isOpenFolder(folders[parent])){
// Insert this folder into the appropriate parent.
if(DEBUG) console.log("Folder "+ name + " attached to parent: " + parent);
insertIntoFolder($(this), folders[parent]);
} else {
// If not yet created, hide
if(DEBUG){
if(parent in folders){
console.log("Folder "+ name + " detached, as parent was closed (folded).");
} else {
console.log("Folder "+ name + " detached, as parent was unknown at the moment, awaiting creation.");
}
}
$(this).detach();
}
}
// If the folder is open, setup children as well.
if(isOpenFolder($(this))){
setupChildrenForFolder($(this));
}
/**
* Attach a listener to the folder to run if it opens.
* Insert children into folders that are/become open and those children are available.
*/
$(this).attrchange(function(attrName){
if( attrName != 'class') return;
if( !isOpenFolder($(this))) return;
setupChildrenForFolder($(this));
});
}
/**
* Store any folder created, and detach if it is parented.
* (or reparent if the parent already exists.)
*/
$(".folder--root--1pRx1koR").each(handleFolderCreation);
$(document).arrive(".folder--root--1pRx1koR", handleFolderCreation);
}
// Find the Folder Hierarchy table and record the child/parent mapping, then call startup.
$.getJSON('https://coda.io/apis/v1beta1/docs/' + codaDocID + '/tables/Folder%20Hierarchy/columns', function(data) {
// Create a folder <-> id mapping
var folderIDMapping = {};
data.items.forEach(function(e){
if(DEBUG) console.log("Mapped Folder: " + e.name + " to: " + e.id);
folderIDMapping[e.name] = e.id;
});
$.getJSON('https://coda.io/apis/v1beta1/docs/' + codaDocID + '/tables/Folder%20Hierarchy/rows?limit=500', function(data) {
data.items.forEach(function(e){
var parent = e.values[folderIDMapping["Parent"]];
if(!isEmpty(parent)){
if(DEBUG) console.log("Mapped parent for Folder: " + e.name + " to: " + parent);
parentMapping[e.name] = parent;
}
var childrenRaw = e.values[folderIDMapping["Children"]];
if(!isEmpty(childrenRaw)){
if(DEBUG) console.log("Mapped children for Folder: " + e.name + " to: " + childrenRaw);
childMapping[e.name] = childrenRaw.split(",");
}
});
// Once we have the mapping, go set everything up.
startup();
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment