Sidebar Functionality - google-apps-script

I have continued to develop a sidebar capability within my google sheet.
I have am looking for some assistance in creating a flow within the sidebar and using links or buttons to create some output messages.
Problem 1) - I have created a sidebar with forward and back arrows which I want to link other html files to when they are clicked within the sidebar, therefore creating a flow or dialog that can be moved back or forth in the same sequence. i.e.. I click next and the function openTheSidebar2() runs
I have have a search dropdown feature which in the code below gives Primary risk categories.
Problem 2) - I would like these to have the ability to provide a simple paragraph (explanation of each category) when they are selected. Would I need to create a different html file for each category? I'm hoping I can add the each message for each category in the same html file.
Is hyperlinks the wrong way to go about this altogether?
Any help is much appreciated
code.gs
function onOpen() {
menu();
}
function menu() {
SpreadsheetApp.getUi().createMenu('Risk Menu')
.addItem('Generate Risk Waiver', 'createNewCase')
.addSubMenu(SpreadsheetApp.getUi().createMenu('Risk Help')
.addItem('Risk Guidance', 'openTheSidebar')
.addItem('Risk Title', 'openTheSidebar2'))
.addToUi();
}
function openTheSidebar() {
var userInterface=HtmlService.createTemplateFromFile('example3').evaluate()
.setTitle('Risk Rating');
SpreadsheetApp.getUi().showSidebar(userInterface);
}
function openTheSidebar2() {
var userInterface2=HtmlService.createTemplateFromFile('example4').evaluate()
.setTitle('Primary Risk Category');
SpreadsheetApp.getUi().showSidebar(userInterface2);
}
function openTheSidebar3() {
var userInterface3=HtmlService.createTemplateFromFile('example').evaluate()
.setTitle('Cause');
SpreadsheetApp.getUi().showSidebar(userInterface3);
}
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename).getContent();
}
function getRowColumn() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getActiveSheet();
var rg=sh.getActiveCell();
var rObj={row:rg.getColumn() ,column:rg.getRow()};
return rObj;
}
function getCellA1() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getActiveSheet();
var rg=sh.getActiveCell();
var rObj={A1:rg.getA1Notation()};
return rObj;
}
function onCheckOpenSideBar(e) {
if(e.range.getSheet().getName()!='Option 1')return;
if(e.range.rowStart==2 && e.range.columnStart==24) {
if(e.value=='TRUE') {
openTheSidebar();
e.range.getSheet().getRange(e.range.rowStart,e.range.columnStart).setValue("FALSE");
}
}
if(e.range.getSheet().getName()!='Option 1')return;
if(e.range.rowStart==2 && e.range.columnStart==8) {
if(e.value=='TRUE') {
openTheSidebar2();
e.range.getSheet().getRange(e.range.rowStart,e.range.columnStart).setValue("FALSE");
}
}
if(e.range.getSheet().getName()!='Option 1')return;
if(e.range.rowStart==2 && e.range.columnStart==1) {
if(e.value=='TRUE') {
openTheSidebar3();
e.range.getSheet().getRange(e.range.rowStart,e.range.columnStart).setValue("FALSE");
}
}}
function createOnEditTrigger() {
ScriptApp.newTrigger('onCheckOpenSideBar').forSpreadsheet(SpreadsheetApp.getActive()).onEdit().create();
}
example4.html
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
.dropbtn {
background-color: #4CAF50;
color: white;
padding: 16px;
font-size: 16px;
border: none;
cursor: pointer;
}
.dropbtn:hover, .dropbtn:focus {
background-color: #3e8e41;
}
#myInput {
border-box: box-sizing;
background-image: url('searchicon.png');
background-position: 14px 12px;
background-repeat: no-repeat;
font-size: 16px;
padding: 14px 20px 12px 45px;
border: none;
border-bottom: 1px solid #ddd;
}
#myInput:focus {outline: 3px solid #ddd;}
.dropdown {
position: relative;
display: inline-block;
}
.dropdown-content {
display: none;
position: absolute;
background-color: #f6f6f6;
min-width: 230px;
overflow: auto;
border: 1px solid #ddd;
z-index: 1;
}
.dropdown-content a {
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
}
.dropdown a:hover {background-color: #ddd;}
.show {display: block;}
a {
text-decoration: none;
display: inline-block;
padding: 8px 16px;
}
a:hover {
background-color: #ddd;
color: black;
}
.previous {
background-color: #f1f1f1;
color: black;
}
.next {
background-color: #4CAF50;
color: white;
}
.round {
border-radius: 50%;
}
</style>
</head>
<body>
<p>Click on the button to open the dropdown menu, and use the input field to search for a specific dropdown link.</p>
<div class="dropdown">
<button onclick="myFunction()" class="dropbtn">Categories</button>
‹
&#8250
<div id="myDropdown" class="dropdown-content">
<input type="text" placeholder="Search.." id="myInput" onkeyup="filterFunction()">
IT Security & Vulnerability
Information Security - Data Only
Technical Debt or Decommission
Availability (SLA)
Service Continuity & Resilience
Capacity & Performance
Licencing & Asset Management, Contracts, Maintenance,
Legal & Regulatory Compliance
Supplier & Vendor Management
Shadow IT
Resource
Knowledge / Skills / Tooling)
Procedure & Process
Service Ownership
Project
Environmental (includes facilities)
Architecture and Strategic
</div>
</div>
<script>
/* When the user clicks on the button,
toggle between hiding and showing the dropdown content */
function myFunction() {
document.getElementById("myDropdown").classList.toggle("show");
}
function filterFunction() {
var input, filter, ul, li, a, i;
input = document.getElementById("myInput");
filter = input.value.toUpperCase();
div = document.getElementById("myDropdown");
a = div.getElementsByTagName("a");
for (i = 0; i < a.length; i++) {
txtValue = a[i].textContent || a[i].innerText;
if (txtValue.toUpperCase().indexOf(filter) > -1) {
a[i].style.display = "";
} else {
a[i].style.display = "none";
}
}
}
</script>
</body>
</html>

I think I understand your question, but if I've got it wrong, just let me know. Your question is somewhat broad and there are many solutions to what you want to do. Here are a couple of ways I might start to approach it.
Method 1: Serve it all at once; show and hide as needed
As shown here, use a helper function include to break up your HTML files for development and then pull them all together when you run the app.
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename).getContent();
}
Content snippet in its own .html file, and give the outermost HTML element a distinct id.
<section id="IT_Security_And_Vulnerability" class="hidden">
<h1>IT Security & Vulnerability</h1>
<p> ...
</p>
</section>
Print all those html files into your main html file you return to the user.
Main html file snippets:
<style>
.hidden {
display: none;
}
</style>
<?= include("#IT_Security_And_Vulnerability") ?>
<?= include("#Information_Security_Data_Only") ?>
<!-- etc -->
<script>
const body = document.querySelector("body"); // this just grabs the sidebar body
body.addEventListener("click", handleClick);
function handleClick(clickEvent) {
if (clickEvent.target.tagname != "A") { // this listener only for clicking links
return;
}
clickEvent.target.preventDefault(); // you may or may not want/need this to prevent browser from trying to actually navigate with your <a> tag
const id = clickEvent.target.getAttribute("href");
const sections = document.querySelectorAll("section");
for (let i = 0, l = sections.length; i < l; ++i) { // hide everyone
sections[i].classList.add("hidden");
}
document.querySelector(id).classList.remove("hidden"); // show the selected one
}
</script>
Method 2: Use google.script.run to fetch content as needed
Still using the include function on the server, this serves up the content on demand.
...
<section class="contentContainer">
<!-- populated by js -->
</section>
...
<script>
const body = document.querySelector("body"); // this just grabs the sidebar body
body.addEventListener("click", handleClick);
function refresh(html) {
const contentContainer = document.querySelector(".contentContainer");
contentContainer.innerHTML = html;
}
function handleClick(clickEvent) {
if (clickEvent.target.tagname != "A") {
return;
}
clickEvent.target.preventDefault(); // you may or may not want/need this.
const reference = clickEvent.target.getAttribute("href");
google.script.run.withSuccessHandler(refresh).include(reference);
}
</script>

Related

How to call a remote navigation menu in html?

everytime I make a new tab in my nav menu, I need to edit all my html files.
How do I make a remote file with all the nav items, that I then call to show up?
Simple: make your navigation into a separate HTML file and use an iframe to call it.
Another way is to make it one whole HTML file and use the following:
var d = {
e: function(id){
return(document.getElementById(id) || "err:404");
},
v: function(id){
return(document.getElementById(id).value || "err:404");
}
};
var System = {
tabs: {
init: function(){
try{
var tabs = d.e("tabs").querySelectorAll("tab");
for(var i = 0; i < tabs.length; i++){
var e = tabs[i];
e.onclick = function(){
System.tabs.activate(this);
};
}
System.tabs.activate(tabs[0]);
}catch(err){
alert(err.stack);
}
},
activate: function(name){
try{
var tabs = d.e("tabs").querySelectorAll("tab");
for(var i = 0; i < tabs.length; i++){
tabs[i].setAttribute("active","false");
}
name.setAttribute("active","true");
var contents = d.e("content").querySelectorAll("pane");
for(var i = 0; i < contents.length; i++){
contents[i].style.display = "none";
}
contents[Array.from(tabs).indexOf(name)].style.display = "block";
}catch(err){
alert(err.stack);
}
}
}
};
window.onload = function(){
System.tabs.init();
};
tab{
display: inline-block;
background: #000000;
color: #888888;
height: 20px;
border-right: 1px solid #444444;
border-left: 1px solid #444444;
border-bottom: 1px solid #222222;
}
tab[active="false"]:hover{
background: #000000;
color: #ffffff;
}
tab[active="true"]{
background: #080808;
color: #ffffff;
border-bottom: 1px solid #000000;
}
tab[active="true"]:hover{
background: #181818;
color: #ffffff;
}
<div id="tabs">
<tab>Tab name</tab><tab>Another tab name</tab>
</div>
<div id="content">
<pane>
<p>Hi there! Welcome to some tab program I made! Please click the tab labeled <code>Another tab name</code> to test out the feature.</p>
</pane>
<pane>
<p>The best part is: no need for element IDs or JQuery or any of that difficult stuff! Feel free to click as many times as you want to see how perfect the tabs work.</p>
</pane>
</div>
I realized that the first option might not work with the current knowledge I know. It is still possible though. Meanwhile the second is obviously gonna work very good even though all I know is that it can work in Chrome, but I can't test it in other browsers because this is a school Chromebook that I am on and my dad is refusing to repair mine and my brother's shared PC.

is there another way for hyperlink

i write these code, but in output, when i click on a, this will not work.
what should i do so that this works properly.
actually, initially i want to hide filter list and when i type some keywords in input box it will show that possible lists.
till this, it is working properly but when i click on 'a' there is no response.
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
* {
box-sizing: border-box;
}
#myInput {
background-image: url('logo.png');
background-size: 25px;
background-position: 10px 12px;
background-repeat: no-repeat;
width: 98%;
font-size: 16px;
padding: 12px 20px 12px 40px;
border: 1px solid #ddd;
margin-bottom: 12px;
}
#myUL {
list-style-type: none;
padding: 0;
margin: 0;
}
#myUL li a {
border: 1px solid #ddd;
margin-top: -1px;
/* Prevent double borders */
background-color: #f6f6f6;
padding: 12px;
text-decoration: none;
font-size: 18px;
color: black;
display: block
}
#myUL li a:hover:not(.header) {
background-color: #eee;
}
img {
width: 50px;
justify-content: center;
align-item: center;
position: relative;
left: 50%;
transform: translatex(-50%);
}
span {
display: none;
}
</style>
</head>
<body>
<div id="search">
<img src="logo.png"></img>
<div id="input">
<input type="text" id="myInput" onkeyup="myFunction()" placeholder="Search for names.." title="Type in a name">
</div>
<div id="ul">
<ul id="myUL">
<li>Adele</li>
<li>Agnes</li>
<li>Billy</li>
<li>Bob</li>
<li>Calvin</li>
<li>Christina</li>
<li>Cindy</li>
</ul>
</div>
</div>
<script>
var UL = document.getElementById("myUL");
// hilde the list by default
UL.style.display = "none";
var searchBox = document.getElementById("myInput");
// show the list when the input receive focus
searchBox.addEventListener("focus", function() {
// UL.style.display = "block";
});
// hide the list when the input receive focus
searchBox.addEventListener("blur", function() {
UL.style.display = "none";
});
function myFunction() {
var input, filter, ul, li, a, i;
input = document.getElementById("myInput");
ul = document.getElementById("myUL");
filter = input.value.toUpperCase();
// if the input is empty hide the list
if (filter.trim().length < 1) {
ul.style.display = "none";
return false;
} else {
ul.style.display = "block";
}
li = ul.getElementsByTagName("li");
for (i = 0; i < li.length; i++) {
a = li[i].getElementsByTagName("a")[0];
// This is when you want to find words that contain the search string
if (a.innerHTML.toUpperCase().indexOf(filter) > -1) {
li[i].style.display = "";
} else {
li[i].style.display = "none";
}
// This is when you want to find words that start the search string
/*if (a.innerHTML.toUpperCase().startsWith(filter)) {
li[i].style.display = "";
} else {
li[i].style.display = "none";
}*/
}
}
</script>
</body>
</html>
but href is not working.
I don't believe "ghib" is a valid href. Either use # for a placeholder or include a URL.
If you were trying to link to another section on your page you'd need to href="#id".
Hope the above helps!
// hide the list when the input receive focus
searchBox.addEventListener("blur", function(){
UL.style.display = "none";
});
You are blurring the unordered list whenever you leave the input field, which hides your <li></li> whenever you click anywhere outside of the input field (hence why the a href isn't registering).

Browse button that will display image file from a local directory folder in html

Hi I wanted to know if its possible to create a button in html that functions like a "Browse" button like when clicked it will show the computer directory such as the Local Drive, Documents etc.
What I wanted to do is use this "Browse" button to set a Folder path that has Images (.png/.jpeg) then once directory folder has been selected the image file name should show up in a list form.
*Note: The machine is connected on a LAN network everything is shared and with
out any restriction.
Sample
Path:
C:\Documents\TargetFolder (this is what will be browsed using the "Browse" button the path location may change could be from a different location within the same machine or different computer over the same network that is why a "Browse" button is needed)
Output:
From the Source(TargetFolder) lets say with 20 image file a list should show up with the image file name, path properties(created date and time )and the actual image pulled up. Switches as well base on what was selected on the list
Is this possible?
browse button window
webpage looks like this
What you want actually is a file manager (file gallery to be more precise) in web browser.
there is no such thing in that would help you get that without writing hundreds of lines of code.
This link might help you https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file
and also follow the below code
var input = document.querySelector('input');
var preview = document.querySelector('.preview');
input.style.opacity = 0;input.addEventListener('change', updateImageDisplay);function updateImageDisplay() {
while(preview.firstChild) {
preview.removeChild(preview.firstChild);
}
var curFiles = input.files;
if(curFiles.length === 0) {
var para = document.createElement('p');
para.textContent = 'No files currently selected for upload';
preview.appendChild(para);
} else {
var list = document.createElement('ol');
preview.appendChild(list);
for(var i = 0; i < curFiles.length; i++) {
var listItem = document.createElement('li');
var para = document.createElement('p');
if(validFileType(curFiles[i])) {
para.textContent = 'File name ' + curFiles[i].name + ', file size ' + returnFileSize(curFiles[i].size) + '.';
var image = document.createElement('img');
image.src = window.URL.createObjectURL(curFiles[i]);
listItem.appendChild(image);
listItem.appendChild(para);
} else {
para.textContent = 'File name ' + curFiles[i].name + ': Not a valid file type. Update your selection.';
listItem.appendChild(para);
}
list.appendChild(listItem);
}
}
}var fileTypes = [
'image/jpeg',
'image/pjpeg',
'image/png'
]
function validFileType(file) {
for(var i = 0; i < fileTypes.length; i++) {
if(file.type === fileTypes[i]) {
return true;
}
}
return false;
}function returnFileSize(number) {
if(number < 1024) {
return number + 'bytes';
} else if(number >= 1024 && number < 1048576) {
return (number/1024).toFixed(1) + 'KB';
} else if(number >= 1048576) {
return (number/1048576).toFixed(1) + 'MB';
}
}
html {
font-family: sans-serif;
}
form {
width: 600px;
background: #ccc;
margin: 0 auto;
padding: 20px;
border: 1px solid black;
}
form ol {
padding-left: 0;
}
form li, div > p {
background: #eee;
display: flex;
justify-content: space-between;
margin-bottom: 10px;
list-style-type: none;
border: 1px solid black;
}
form img {
height: 64px;
order: 1;
}
form p {
line-height: 32px;
padding-left: 10px;
}
form label, form button {
background-color: #7F9CCB;
padding: 5px 10px;
border-radius: 5px;
border: 1px ridge black;
font-size: 0.8rem;
height: auto;
}
form label:hover, form button:hover {
background-color: #2D5BA3;
color: white;
}
form label:active, form button:active {
background-color: #0D3F8F;
color: white;
}
<form method="post" enctype="multipart/form-data">
<div>
<label for="image_uploads">Choose images to upload (PNG, JPG)</label>
<input type="file" id="image_uploads" name="image_uploads" accept=".jpg, .jpeg, .png" multiple>
</div>
<div class="preview">
<p>No files currently selected for upload</p>
</div>
<div>
<button>Submit</button>
</div>
</form>
Not exactly what you asked for, but (if it works for your use-case), the user could select multiple files from the same directory using the browse button.
There is a lovely example on MDN
https://developer.mozilla.org/en-US/docs/Web/API/File/Using_files_from_web_applications#example_showing_thumbnails_of_user-selected_images
<input type="file" id="input" multiple>
const inputElement = document.getElementById("input");
inputElement.addEventListener("change", handleFiles, false);
function handleFiles() {
const fileList = this.files; /* now you can work with the file list */
}
function handleFiles(files) {
for (let i = 0; i < files.length; i++) {
const file = files[i];
if (!file.type.startsWith('image/')){ continue }
const img = document.createElement("img");
img.classList.add("obj");
img.file = file;
preview.appendChild(img); // Assuming that "preview" is the div output where the content will be displayed.
const reader = new FileReader();
reader.onload = (function(aImg) { return function(e) { aImg.src = e.target.result; }; })(img);
reader.readAsDataURL(file);
}
}

Start Content from bottom of container and push up

So what I'm trying to accomplish seems like easy CSS etc. Im changing a messaging system and what the conversations to start at the bottom sorta like Facebook or text message where it has one person on the left and the other on the right.
How do I get the divs to go up as new content is added via ajax? I saw this similar question but didn't quite understand what he mean focus on the LI. An example would be great.
Something like this perhaps?
<!DOCTYPE html>
<html>
<head>
<script>
function byId(e){return document.getElementById(e);}
function newEl(tag){return document.createElement(tag);}
function newTxt(txt){return document.createTextNode(txt);}
function prependChild(parent, element)
{
if (parent.childNodes)
parent.insertBefore(element, parent.childNodes[0]);
else
parent.appendChild(element)
}
window.addEventListener('load', mInit, false);
function mInit()
{
byId('btnAddNew').addEventListener('click', onAddBtnClicked, false);
}
function onAddBtnClicked()
{
var txtInputElem = byId('newMsgTxtInput');
var msgTxt = txtInputElem.value;
var li = newEl('li');
li.appendChild( newTxt( msgTxt ) );
var ulTgt = byId('msgTarget');
var existingLiItems = ulTgt.querySelectorAll('li');
var numItemsExisting = existingLiItems.length;
if (numItemsExisting%2 != 0)
li.className = 'even';
else
li.className = 'odd';
prependChild(ulTgt, li);
}
</script>
<style>
.controls
{
display: inline-block;
padding: 8px;
margin: 8px;
border: solid 1px #555;
border-radius: 4px;
color: #777;
background-color: #ddd;
}
#msgTarget
{
width: 275px;
border: solid 1px #555;
margin: 8px;
border-radius: 4px;
}
/*
Doesn't work 'properly' - since we add items at the top, rather than the bottom
The first item added will be 'even', then the second item gets added and now it's the first child 'even'
so, the item added first is now an 'odd' child. A better way may be to count the number of existing li
items the ul contains, before assigning the new li elements a class of even or odd that will enable css
styling.
#msgTarget li:nth-child(odd)
{
background-color: #CCC;
}
#msgTarget li:nth-child(even)
{
background-color: #5C5;
}
*/
.odd
{
background-color: #CCC;
}
.even
{
background-color: #5C5;
}
</style>
</head>
<body>
<div class='dlg'>
<div class='controls'>
<input id='newMsgTxtInput'/><button id='btnAddNew'>Add message</button>
</div>
<ul id='msgTarget'></ul>
</div>
</body>
</html>

HTML/CSS - Using a image for input type=file

How do use this image:
http://h899310.devhost.se/proxy/newProxy/uplfile.png
Instead of the regular:
<input type="file" />
Have a look at Styling an input type="file".
I'm not very sure on whether you want to style file upload fields, or whether you simply want to use a png file in a style.
Quirksmode.org has a section on styling file upload fields though, that you would want to refer to.
If you want to use the PNG file to use in a style inside a page, you should like at how to set backgrounds using images, although this may not work for all HTML elements.
I did something like this and it worked perfectly!
<script type="text/javascript">
var t = 0;
var IE = navigator.appName;
var OP = navigator.userAgent.indexOf('Opera');
var tmp = '';
function operaFix() {
if (OP != -1) {
document.getElementById('browser').style.left = -120 + 'px';
}
}
function startBrowse() {
tmp = document.getElementById('dummy_path').value;
getFile();
}
function getFile() {
// IF Netscape or Opera is used...
//////////////////////////////////////////////////////////////////////////////////////////////
if (OP != -1) {
displayPath();
if (tmp != document.getElementById('dummy_path').value && document.getElementById('dummy_path').value
!= '') {
clearTimeout(0);
return;
}
setTimeout("getFile()", 20);
// If IE is used...
//////////////////////////////////////////////////////////////////////////////////////////////
} else if (IE == "Microsoft Internet Explorer") {
if (t == 3) {
displayPath();
clearTimeout(0);
t = 0;
return;
}
t++;
setTimeout("getFile()", 20);
// Or if some other, better browser is used... like Firefox for example :)
//////////////////////////////////////////////////////////////////////////////////////////////
} else {
displayPath();
}
}
function displayPath() {
document.getElementById('dummy_path').value = document.getElementById('browser').value;
}
</script>
<style type="text/css">
#browser
{
position: absolute;
left: -132px;
opacity: 0;
filter: alpha(opacity=0);
}
#browser_box
{
width: 104px;
height: 22px;
position: relative;
overflow: hidden;
background: url(button1_off.jpg) no-repeat;
}
#browser_box:active
{
background: url(button1_on.jpg) no-repeat;
}
#dummy_path
{
width: 350px;
font-family: verdana;
font-size: 10px;
font-style: italic;
color: #3a3c48;
border: 1px solid #3a3c48;
padding-left: 2px;
background: #dcdce0;
}
</style>
<body onload="operaFix()">
<div id="browser_box">
<input type="file" name="my_file" id="browser" onclick="startBrowse()" />
</div>
</body>