Merge "Prefetches Collaborators"
[pharos-tools.git] / dashboard / src / templates / dashboard / searchable_select_multiple.html
index ee460dd..91ed09c 100644 (file)
@@ -1,41 +1,57 @@
 <script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
 
-<div class="autocomplete" style="width:400px;">
+<div id="search_select_outer" class="autocomplete">
     <div id="warning_pane" style="background: #FFFFFF; color: #CC0000;">
         {% if incompatible == "true" %}
         <h3>Warning: Incompatible Configuration</h3>
         <p>Please make a different selection, as the current config conflicts with the selected pod</p>
         {% endif %}
     </div>
-    <input id="user_field" name="ignore_this" class="form-control" autocomplete="off" type="text" placeholder="{{placeholder}}" value="{{initial.name}}" oninput="search(this.value)"
+    <div id="added_counter">
+        <p id="added_number">0</p>
+        <p id="addable_limit">/ {% if selectable_limit > -1 %} {{ selectable_limit }} {% else %} &infin; {% endif %}added</p>
+    </div>
+
+    <div id="added_list">
+
+    </div>
+
+    <input id="user_field" name="ignore_this" class="form-control" autocomplete="off" type="text" placeholder="{{placeholder}}" value="" oninput="search(this.value)"
     {% if disabled %} disabled {% endif %}
     >
+    </input>
 
     <input type="hidden" id="selector" name="{{ name }}" class="form-control" style="display: none;"
     {% if disabled %} disabled {% endif %}
     >
     </input>
 
-    <ul id="drop_results"></ul>
-
-
-    <div id="default_entry_wrap" style="display: none;">
-        <div class="list_entry unremovable_list_entry">
-            <p id="default_text" class="full_name"></p>
-            <button class="btn-remove btn disabled">remove</button>
-        </div>
+    <div id="scroll_restrictor">
+        <ul id="drop_results"></ul>
     </div>
+    <style>
+        #scroll_restrictor {
+            flex: 1;
+            position: relative;
+            overflow-y: auto;
+            padding-bottom: 10px;
+        }
 
-    <div id="added_list">
+        #added_list {
+            margin-bottom: 5px;
+        }
 
-    </div>
-    <div id="added_counter" style="text-align: center; margin: 10px;"><p id="added_number" style="display: inline;">0</p><p style="display: inline;">/
-        {% if selectable_limit > -1 %} {{ selectable_limit }} {% else %} &infin; {% endif %}added</p></div>
-    <style>
+        .autocomplete {
+            display: flex;
+            flex: 1;
+            flex-direction: column;
+        }
         #user_field {
             font-size: 14pt;
-            width: 400px;
             padding: 5px;
+            height: 40px;
+            border: 1px solid #ccc;
+            border-radius: 5px;
 
         }
 
             list-style-type: none;
             padding: 0;
             margin: 0;
-            max-height: 300px;
             min-height: 0;
-            overflow-y: scroll;
-            overflow-x: hidden;
             border: solid 1px #ddd;
-            display: none;
+            border-top: none;
+            border-bottom: none;
+            visibility: inherit;
+            flex: 1;
+
+            position: absolute;
+            width: 100%;
 
         }
 
         #drop_results li a{
             font-size: 14pt;
-            border: 1px solid #ddd;
             background-color: #f6f6f6;
-            padding: 12px;
+            padding: 7px;
             text-decoration: none;
             display: block;
-            width: 400px;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            white-space: nowrap;
         }
 
-        .btn-remove {
-            float: right;
-            height: 30px;
-            margin: 4px;
+        #drop_results li a {
+            border-bottom: 1px solid #ddd;
         }
 
         .list_entry {
-            width: 400px;
-            border: 1px solid #ddd;
-            border-radius: 3px;
+            border: 1px solid #ccc;
+            border-radius: 5px;
             margin-top: 5px;
             vertical-align: middle;
             line-height: 40px;
             height: 40px;
             padding-left: 12px;
+            width: 100%;
+            display: flex;
         }
 
         #drop_results li a:hover{
             background-color: #ffffff;
         }
 
-        .small_name {
-            display: inline-block;
+        .added_entry_text {
+            white-space: nowrap;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            display: inline;
+            width: 100%;
+        }
+
+        .btn-remove {
+            float: right;
+            height: 30px;
+            margin: 4px;
+            padding: 1px;
+            max-width: 20%;
+            width: 15%;
+            min-width: 70px;
+            overflow: hidden;
+            text-overflow: ellipsis;
+        }
+
+        .entry_tooltip {
+            display: none;
         }
 
-        .full_name {
+        #drop_results li a:hover .entry_tooltip {
+            position: absolute;
+            background: #444;
+            color: #ddd;
+            text-align: center;
+            font-size: 12pt;
+            border-radius: 3px;
+
+        }
+
+        #drop_results {
+            max-width: 100%;
             display: inline-block;
+            list-style-type: none;
+            overflow: hidden;
+            white-space: nowrap;
+            text-overflow: ellipsis;
+        }
+
+        #drop_results li {
+            overflow: hidden;
+            text-overflow: ellipsis;
+        }
+
+        #added_counter {
+            text-align: center;
         }
 
+        #added_number, #addable_limit {
+            display: inline;
+        }
     </style>
 </div>
 
 <script type="text/javascript">
     //flags
-    var show_from_noentry = {{show_from_noentry|default:"false"}};
-    var show_x_results = {{show_x_results|default:-1}};
-    var results_scrollable = {{results_scrollable|default:"false"}};
-    var selectable_limit = {{selectable_limit|default:-1}};
-    var field_name  = "{{name|default:"users"}}";
-    var placeholder = "{{placeholder|default:"begin typing"}}";
-    var default_entry = "{{default_entry}}";
+    var show_from_noentry = {{show_from_noentry|yesno:"true,false"}}; // whether to show any results before user starts typing
+    var show_x_results = {{show_x_results|default:-1}}; // how many results to show at a time, -1 shows all results
+    var results_scrollable = {{results_scrollable|yesno:"true,false"}}; // whether list should be scrollable
+    var selectable_limit = {{selectable_limit|default:-1}}; // how many selections can be made, -1 allows infinitely many
+    var placeholder = "{{placeholder|default:"begin typing"}}"; // placeholder that goes in text box
 
     //needed info
-    var items = {{items|safe}}
+    var items = {{items|safe}} // items to add to trie. Type is a dictionary of dictionaries with structure:
+        /*
+        {
+            id# : {
+                "id": any, identifiable on backend
+                "small_name": string, displayed first (before separator), searchable (use for e.g. username)
+                "expanded_name": string, displayed second (after separator), searchable (use for e.g. email address)
+                "string": string, not displayed, still searchable
+            }
+        }
+        */
+
+    /* used later:
+    {{ selectable_limit }}: changes what number displays for field
+    {{ name }}: form identifiable name, relevant for backend
+        // when submitted, form will contain field data in post with name as the key
+    {{ placeholder }}: "greyed out" contents put into search field initially to guide user as to what they're searching for
+    {{ initial }}: in search_field_init(), marked safe, an array of id's each referring to an id from items
+    */
 
     //tries
     var expanded_name_trie = {}
 
     var added_items = [];
 
-    var added_template = {{ added_list|default:"{}" }};
-
-    if( default_entry )
-    {
-        var default_entry_div = document.getElementById("default_entry_wrap");
-        default_entry_div.style.display = "inherit";
-
-        var entry_p = document.getElementById("default_text");
-        entry_p.innerText = default_entry;
-    }
-
-    init();
+    search_field_init();
 
     if( show_from_noentry )
     {
         }
     }
 
-    function init() {
+    function search_field_init() {
         build_all_tries(items);
 
         var initial = {{ initial|safe }};
             if( str.length == 1 )
             {
                 new_trie.isComplete = true;
-                new_trie.itemID = id;
+                if( !new_trie.ids )
+                {
+                    new_trie.ids = [];
+                }
+                new_trie.ids.push(id);
             }
             inner_trie = new_trie;
             str = str.substring(1);
         }
         if ( trie.isComplete )
         {
-            itemIDs.push( trie.itemID );
+            itemIDs.push(...trie.ids);
         }
 
         return itemIDs;
         return results;
     }
 
+    function generate_element_text(obj)
+    {
+        var content_strings = [obj['expanded_name'], obj['small_name'], obj['string']].filter(x => Boolean(x));
+        var result = content_strings.shift();
+        if( result == null || content_strings.length < 1) return result;
+        return result + " (" + content_strings.join(", ") + ")";
+    }
+
     function dropdown(ids)
     {
         /*
             var result_entry = document.createElement("li");
             var result_button = document.createElement("a");
             var obj = items[id];
-            var result_text = document.createTextNode(obj['small_name'] + " : " + obj['expanded_name']);
-            result_button.appendChild(result_text);
+            var result_text = generate_element_text(obj);
+            result_button.appendChild(document.createTextNode(result_text));
             result_button.setAttribute('onclick', 'select_item("' + obj['id'] + '")');
+            var tooltip = document.createElement("span");
+            var tooltiptext = document.createTextNode(result_text);
+            tooltip.appendChild(tooltiptext);
+            tooltip.setAttribute('class', 'entry_tooltip');
+            result_button.appendChild(tooltip);
             result_entry.appendChild(result_button);
             drop.appendChild(result_entry);
         }
 
+        var scroll_restrictor = document.getElementById("scroll_restrictor");
+
         if( !drop.firstChild )
         {
-            drop.style.display = 'none';
+            scroll_restrictor.style.visibility = 'hidden';
         }
         else
         {
-            drop.style.display = 'inherit';
+            scroll_restrictor.style.visibility = 'inherit';
         }
     }
 
     function select_item(item_id)
     {
         //TODO make faster
-        var item = items[item_id];
+        var item = items[item_id]['id'];
         if( (selectable_limit > -1 && added_items.length < selectable_limit) || selectable_limit < 0 )
         {
             if( added_items.indexOf(item) == -1 )
                 added_items.push(item);
             }
         }
-
         update_selected_list();
+        // clear search bar contents
+        document.getElementById("user_field").value = "";
         document.getElementById("user_field").focus();
+        search("");
     }
 
     function remove_item(item_ref)
     {
-
         item = Object.values(items)[item_ref];
         var index = added_items.indexOf(item);
         added_items.splice(index, 1);
         document.getElementById("user_field").focus();
     }
 
-    function edit_item(item_id){
-        var wf_type = "{{wf_type}}";
-        parent.add_edit_wf(wf_type, item_id);
-    }
-
     function update_selected_list()
     {
         document.getElementById("added_number").innerText = added_items.length;
 
         for( var key in added_items )
         {
-            item = added_items[key];
+            item_id = added_items[key];
+            item = items[item_id];
 
-            list_html += '<div class="list_entry"><p class="full_name">'
-                + item["expanded_name"]
-                + '</p><p class="small_name">, '
-                + item["small_name"]
-                + '</p><button onclick="remove_item('
+            var element_entry_text = generate_element_text(item);
+
+            list_html += '<div class="list_entry">'
+                + '<p class="added_entry_text">'
+                + element_entry_text
+                + '</p>'
+                + '<button onclick="remove_item('
                 + Object.values(items).indexOf(item)
                 + ')" class="btn-remove btn">remove</button>';
-            {% if edit %}
-                list_html += '<button onclick="edit_item('
-                + item['id']
-                + ')" class="btn-remove btn">edit</button>';
-            {% endif %}
                 list_html += '</div>';
         }
 
         added_list.innerHTML = list_html;
     }
-
 </script>
-<style>
-    .full_name {
-        display: inline-block;
-    }
-    .small_name {
-        display: inline-block;
-    }
-</style>