Jump to content
  • JavaScript Popup Sidebar for Spotfire


    A popup style sidebar for Spotfire

     

    sidebar-popup.thumb.gif.a534c90979daf2a8b5bd5adf34479a87.gif The Popup sidebar saves real state. 
    • Intended to save maximum real state
    • Shows or hides with menu or close buttons
    • Overlays other visualizations
    • Can group page navigation items as well as Spotfire controls

    How to use

    Add a TextArea and edit the HTML. Add the sample html code below and then the provided JavaScript at the end. Just create a new script and copy+paste the code from the sidebar-popup.js into a new JavaScript. 

    html 

    Example no navigation grouping

    <DIV id="sidebar" style="FONT-SIZE: 20px; BACKGROUND: #3498db; COLOR: white"></DIV>
    
    <span id="sidebar-logo">C. Corp</span>
    
    <DIV id="sidebar-nav" >
    	<a>Page 1</a>
    	<a>Page 2</a>
    	<a>SpotfireControl (repalce with link spotfire control)</a>
    	<a>Page 4</a>  
    	<a>Page 5</a>
    	<a>Page 6</a>  
    	<a>Page 7</a>
    	<a>Page 8</a>  
    </div>
    <div id="sidebar-nav-icons" >house,user,circle,box,umbrella,music,heart,gear</div>
    
    
    
    <div id="sidebar-controls">
    
       <div>Control group 1</div> 
       <div>
           <div>replace with spotfire control</div>
           <div>replace with spotfire control</div>
           <div>replace with spotfire control</div>
       </div> 
    
       <div>Control group 2</div> 
       <div>
           <div>replace with spotfire control</div>
           <div>replace with spotfire control</div>
           <div>replace with spotfire control</div>
       </div> 
    
    </div>
     

    Example with groups

    <DIV id="sidebar" style="FONT-SIZE: 15px; BACKGROUND: #3498db; COLOR: white"></DIV>
    
    <span id="sidebar-logo">
       <img src="bffeccd1047746e5bd1a3a11c6048a9d.png"/>
    </span>
    
     
    <DIV id="sidebar-nav" > 
    	<a>Page 1</a>
    	<li>
    	  <a>Page 2***</a>
    	  <a>Page 3</a>
    	</li>
            <a>Page 4</a>  
            <li>
    	  <a>Page 5</a>
    	  <a>Page 6</a>  
    	</li>
    	  <a>Page 7</a>
            <li>
    	  <a>Page 8</a>  
    	</li>
    </div>
    
    <div id="sidebar-nav-icons" >house,user,circle,box,umbrella,music,heart,gear
    </div> 
    
    
    <div id="sidebar-controls" style="border:1px solid black;background:orange">
    
       <div>Control group 1</div> 
       <div>
           <div>replace with spotfire control</div>
           <div>replace with spotfire control</div>
           <div>replace with spotfire control</div>
       </div> 
    
       <div>Control group 2</div> 
       <div>
           <div>replace with spotfire control</div>
           <div>replace with spotfire control</div>
           <div>replace with spotfire control</div>
       </div> 
    
    </div>
     

    Example with scrollbar and as a popup panel

    scrollsidebar.gif.5970430f6c92f8be6ef6101c23a3e28d.gif

    <DIV id="sidebar" style="FONT-SIZE: 20px; BACKGROUND: #3498db; COLOR: white"></DIV>
    
      <span id="sidebar-logo">Options</span>
    
      <DIV id="sidebar-nav" style="padding:20px;overflow-y: auto;height: 80vh;" >
          Spotfire controls here
      </div>
    
    <div id="sidebar-nav-icons" ></div>
     

    JavaScript

    sidebar-popup.js

    sidebar = document.getElementById("sidebar")
    h=sidebar.offsetParent.offsetHeight;
    hasLogo = document.querySelector("#sidebar-logo");
    fontSize = parseInt(window.getComputedStyle(document.querySelector("#sidebar")).fontSize);
    hasTrigger = document.querySelector("#sidebar-trigger");
    hasFeathers = typeof feather != "undefined";
    
    template = `
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css" />
    
    <span class="sidebar-trigger" id="showMenuBtn" title="click to show menu">
    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-menu"><line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line></svg>
    </span>
     
    
    <nav class="main-menu"> 
    
    <span class="right" id="hideMenuBtn" title="clcik to hide menu" ><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-x"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg></span>
    
        <ul><li class="sidebar-logo"></li></ul>
        
        <ul id="navItems" class="sidebar-nav"></ul>
        <ul id="navItems" class="sidebar-controls">
    
     
        </ul>
        
     
      </nav>  
      
    <style>      
     
    #showMenuBtn {
    	FONT-SIZE: 20px;
    	COLOR: ${sidebar.style.background};
    	width: 25px;
    	text-align: center;
    	border-radius: 15px;
    	cursor: pointer;
    	position: absolute;
    }
    
    #hideMenuBtn {
        cursor: pointer;
        z-index: 40;
        position: absolute;
    }
    
    
    #navItems li a {
        position: relative;
        display: flex;
        vertical-align: middle;
        align-items: center;
        font-size:${fontSize}px;
        line-height:3;
    }
    
    
    .submenu {
      overflow: hidden;
      max-height: 0;
      margin-left:2em;
      -webkit-transition: all 0.5s ease-out;
    }
    
    .submenu:before {
        content: "▼";
        position: absolute;
        right: 15px;
        top: ${fontSize*.75}px;
    }
    
    .icon {
        width: 22.5px;
        height: 22.5px;
        margin-right: 15px;
        margin-left: 15px;
        display: flex;
        align-items: center;
        align-content: flex-end;
        justify-content: center;
    }
    
    .right{
      position:absolute;
      right:10px;
    }
    
    /*when using text as logo*/
    .sidebar-logo {
        color: ${sidebar.style.color};	
        font-size: ${fontSize*1.5}px;
        width: 100% !important;
        top: -7px;
        padding: 2px;
        text-align: center;
        border-bottom: 1px solid white;
    }
    
    /*when using an image as logo*/
    .sidebar-logo img{
        background: ${sidebar.style.background};  		
        color: ${sidebar.style.color};	
        height:unset !important;
        max-width:50% !important;
        min-width:${fontSize*3}px;
    }
    
    
    .main-menu {
     position:fixed;
     z-index:10;
     box-shadow: 3px 3px 10px 1px #888;
     background: ${sidebar.style.background};					
     border-right:1px solid #555;
     height:${h}px;
     width:${fontSize*3}px;
     overflow:hidden;
     transition:width .05s linear;
    }
    
    .main-menu, nav.main-menu.expanded {
    	width:260px;
    	overflow:visible;
    }
    
    
    .main-menu>ul { 
     margin:7px 0;
    }
    
    .main-menu li {
     position:relative;
     display:block;
     width:260px;
     cursor:default;
    }
    
    details li{
     display:contents;
    }
    
    .main-menu li>a {
     color:${sidebar.style.color}!important;
     text-decoration:none !important;
     transition:all .1s linear;
     width:260+${fontSize}px;
    }
    
    nav ul,nav li {
     padding:0;
    }
    
    .main-menu li:xhover .submenu {
      max-height: 200px;
    }
    
    .main-menu .expanded {
        max-height: fit-content;
    }
    
    details {
        padding: 10px;
    }
    
    summary {
        padding-left: 10px;
    	cursor:default;
    }
    
    details > div {
        padding: 10px 20px 0 10px;
        display: flex;
        flex-direction: column;
        align-items: stretch;
    }
    
    details > div  a {
        color: white !important;
    }
    
    
    details .sf-element-dropdown-list-item.sfpc-selected{
        color:${sidebar.style.background} !important;
        background:${sidebar.style.color} !important;
    }
    
    details .sf-element-text-box{
        color:${sidebar.style.background} !important;
    } 
    
    details .sf-element-dropdown .sf-element-text-box{
        color:${sidebar.style.background} !important;
    } 
    
    details .sf-element-filter-item .sf-element-text-box,
    details .sf-element-item-slider,
    details .sf-element-text-box
    {
        color:${sidebar.style.color} !important;
    } 
    
    details .sfc-list-box{
        background:${sidebar.style.background} !important;
    }
    
    .DropdownListContainer {
    	z-index:100;
    }
    
    </style>
    `
      
    
    //move components (ids) to template placeholders (class names)
    function moveElement(component,templateComponent){ 
    
    
       if(templateComponent!=undefined){
    
          if (component.id == "sidebar-nav") {
    
    		ul = document.getElementById("navItems");
    
    		icons = document.querySelector("#sidebar-nav-icons");
    		if (hasFeathers) icons = icons.innerText.split(",").map(x=>`<i class="icon" data-feather="${x}"></i>`);
    		if (!hasFeathers) icons = icons.innerText.split(",").map(x=>`<i class="icon fa-solid fa-${x}"></i>`);
    
    
    		navItems = [...document.querySelectorAll("#sidebar-nav > *")].forEach((x)=>{
    
    		    if (x.nodeName=="A"){
    		        if(x.innerText.endsWith("***")) x.innerText = x.innerText.replace("***","");
    		    }
    
    
    		    if (x.nodeName=="A" && x.nextElementSibling?.nodeName!="LI"){
    		      if(x.innerText.endsWith("***")) x.innerText = x.innerText.replace("***","");
    
    		       li = document.createElement("li");
    		       x.insertAdjacentHTML("afterbegin",icons.shift());
    		       li.append(x);
    		       ul.append(li);
    		    }
    
    		    if (x.nodeName=="A" && x.nextElementSibling?.nodeName=="LI"){
    		       li = document.createElement("li");
    		       x.insertAdjacentHTML("afterbegin",icons.shift().replace("icon","icon parent"));
    		       li.append(x);
    		       ul.append(li);
    		     
    		    }
    		    
    		    if (x.nodeName=="LI"){
    		       sul = document.createElement("ul");
    		       sul.className="submenu";
    			
    		       [...x.querySelectorAll("a")].forEach(a=>{
    
    			if(a.innerText.endsWith("***")) a.innerText = a.innerText.replace("***","");
    
    			  li = document.createElement("li");
    			  a.insertAdjacentHTML("afterbegin",icons.shift());
    			  li.append(a);
    			  sul.append(li);
    
    		       })
    		       ul.lastChild.append(sul); 
    		    }  
    
    		});
          }
    
    
    	if(component.id=="sidebar-controls"){ 
    		let controls = [...document.querySelectorAll("#sidebar-controls > *")]
    		for (i=0;i<controls.length;i+=2){
    
    			isOpen=false;
    			if(controls[i].innerText.endsWith("***")){
    				isOpen=true;
    				controls[i].innerText = controls[i].innerText.replace("***","");
    			}
    		
    			sum = document.createElement("summary");
    			det = document.createElement("details");
    			div = document.createElement("div");
    			det.append(sum);
    			sum.append(...controls[i].childNodes);
    			div.append(...controls[i+1].childNodes);
                            det.append(div);
    			templateComponent.append(det);
    			if(isOpen) sum.click();
    
    		}
    	} 
    
    
          templateComponent.append(...component.childNodes);	
       }
       //if (!templateComponent) console.log(`⚠️ class ".${component.id}" does not exists on template!`);
       component.remove();
    }
    
    sidebar.innerHTML = template
    
    //move elements
    var components = "nav,title,logo,options,controls,nav-icons,trigger"; 
    components = components.split(",").map(x=>{return "#sidebar-"+x}).join(", "); 
    [...document.querySelectorAll(components)].forEach(component=>{templateComponent="."+component.id;moveElement(component,document.querySelector(templateComponent)) });
    
    iconSize = parseInt(window.getComputedStyle(sidebar).getPropertyValue("font-size").replace("px",""))-5+"px";
    if (hasFeathers) feather.replace({width:iconSize,height:iconSize})
    
    //show or hide menu
    function showMenu(isVisible){
    	document.querySelector(".main-menu").hidden=!isVisible
    	document.getElementById("showMenuBtn").hidden=isVisible;
    	
    	console.log(isVisible)
    
    	//save state (TBD)
    	window.localStorage.setItem("sidebarVisibleState",isVisible);
    }
    document.getElementById("hideMenuBtn").onclick=()=>{showMenu(false)};
    document.getElementById("showMenuBtn").onclick=()=>{showMenu(true)};
    
    //expand on click
    [...document.querySelectorAll(".sidebar-nav li")].forEach(x=>{if(x.lastChild.className=="submenu"){
    x.onclick = function(){x.lastChild.classList.toggle("expanded")};  
    }})
    
    //load state (TBD)
    showMenu(JSON.parse(window.localStorage.getItem("sidebarVisibleState")));
     

    See also

    JavaScript Sidebar for Spotfire

    JavaScript Masthead for Spotfire

    IronPython Masthead for Spotfire

     

    • Thanks 1

    User Feedback

    Recommended Comments

    There are no comments to display.


×
×
  • Create New...