I like that solution but it gave me issues when wanting to hide certain folders. Also it would show the loading animation for a split second as it had to search a bunch. So I did a variation of this. Just place that entire thing into an html element, the JSON, is just a sample of the bubble database which you can transform easily, then setup event listeners to actually trigger actions in bubble or that html is setup to change the ‘category’ url param, so you can use that.
a {
text-decoration: none;
}
/**
- Hidden fallback
*/
[hidden] {
display: none;
visibility: hidden;
}
/**
- Styling navigation
*/
header {
margin-right: 5px;
margin-left: auto;
max-width: 22.5rem;
box-shadow: 0 3px 12px rgba(0, 0, 0, 0.25);
}
ul, ol {
display: block;
padding-left: 20px;
}
/**
- Styling top level items
/
.nav a, .nav label {
display: block;
padding: 0.85rem;
color: #545969; / Font color /
background-color: transparent;
box-shadow: inset 0 -1px #1d1d1d;
transition: all 0.25s ease-in;
font-family: ‘Segoe UI’; / Font family /
font-size: 12px; / Font size /
font-weight: 600; / Font weight /
}
.nav a:focus, .nav a:hover,
.nav label:focus,
.nav label:hover {
color: #404452; / Font color */
background: transparent;
}
.nav label {
cursor: pointer;
}
.nav__list input[type=checkbox]:checked + label + ul {
/* reset the height when checkbox is checked */
max-height: 1000px;
}
/**
- Rotating chevron icon
*/
label > span {
float: right;
transition: transform 0.65s ease;
}
.nav__list input[type=checkbox]:checked + label > span {
transform: rotate(90deg);
}
.nav__list ul {
height: 100%;
margin-left: 0;
padding-left: 1;
max-height: 0;
overflow: hidden;
transition: max-height 0.5s ease-in-out;
}
/**
- Display the list when the checkbox is checked
*/
.nav__list input[type=checkbox]:checked ~ ul {
max-height: 1000px;
}
Dynamic Menu
/* Add your CSS styles here */
<script>
// Your JSON data
const results = [
{
"Active Yes/No": true,
"Company": "1671226981392x620093986539615600",
"Nested Count (spacing)": 0,
"Order": 2,
"SOP Category Childs": [
"1688811052709x338905242781351940",
"1688811083885x505462552484642800"
],
"Title": "Social Media",
"_id": "1688810258362x893811630213955600"
},
{
"Active Yes/No": true,
"Modified Date": "2023-07-08T10:10:26.125Z",
"Order": 2,
"Title": "Customer Service",
"_id": "1688811024438x477018805721104400"
},
{
"Active Yes/No": true,
"Company": "1671226981392x620093986539615600",
"Nested Count (spacing)": 2,
"Order": 3,
"SOP Category Childs": [
"1688811164839x195187245402030080"
],
"SOP Category Parent": "1688810258362x893811630213955600",
"Title": "Social Media - Rules",
"_id": "1688811052709x338905242781351940"
},
{
"Active Yes/No": true,
"Company": "1671226981392x620093986539615600",
"Nested Count (spacing)": 2,
"Order": 4,
"SOP Category Childs": [
"1688811182090x391550732909674500"
],
"SOP Category Parent": "1688810258362x893811630213955600",
"Title": "Social Media - Laws",
"_id": "1688811083885x505462552484642800"
},
{
"Active Yes/No": true,
"Company": "1671226981392x620093986539615600",
"Nested Count (spacing)": 3,
"Order": 5,
"SOP Category Parent": "1688811052709x338905242781351940",
"Title": "Rules- Kings",
"_id": "1688811164839x195187245402030080"
},
{
"Active Yes/No": true,
"Company": "1671226981392x620093986539615600",
"Nested Count (spacing)": 3,
"Order": 6,
"SOP Category Childs": [
"1688811366238x870957420259573800"
],
"SOP Category Parent": "1688811083885x505462552484642800",
"Title": "Laws - RRR",
"_id": "1688811182090x391550732909674500"
},
{
"Active Yes/No": true,
"Company": "1671226981392x620093986539615600",
"Nested Count (spacing)": 4,
"Order": 7,
"SOP Category Parent": "1688811182090x391550732909674500",
"Title": "LAWS RRRR - DDD",
"_id": "1688811366238x870957420259573800"
}
];
function transformData(results) {
let map = {},
node,
roots = [],
i;
for (i = 0; i < results.length; i += 1) {
map[results[i]._id] = i;
results[i].children = [];
}
for (i = 0; i < results.length; i += 1) {
node = results[i];
if (node['SOP Category Parent'] !== undefined) {
results[map[node['SOP Category Parent']]].children.push(node);
} else {
roots.push(node);
}
}
return roots;
}
function createMenu(categories) {
let html = '<ul class="nav__list">';
categories.forEach((category, index) => {
let formattedId = `group-${category._id}`.replace(/\W/g, '_');
let padding = category["Nested Count (spacing)"] * 2 || 0; // Each nested level adds 5px
let url = `bubble.io/categories?category=${encodeURIComponent(category.Title)}`;
html += `<li>
<input id="${formattedId}" type="checkbox" hidden />
<label for="${formattedId}" style="padding-left:${padding}px;" onclick="changePageURL('${url}')"><span class="fa fa-angle-right"></span>${category.Title}</label>`;
if (category.children && category.children.length > 0) {
html += createMenu(category.children); // Recursive call for nested categories
}
html += '</li>';
});
html += '</ul>';
return html;
}
function changePageURL(url) {
history.pushState({}, null, url);
}
// Transform data
let transformedData = transformData(results);
// Create menu and add it to the DOM
let menuHTML = createMenu(transformedData);
document.querySelector('.nav').innerHTML = menuHTML;