From 5b67156394bc1e780385d85edb097c8cd385372b Mon Sep 17 00:00:00 2001 From: shamrickus Date: Sat, 5 Mar 2022 08:32:16 -0700 Subject: [PATCH 01/14] Improve tenant selection --- traffic_portal/app/src/app.js | 1 + .../src/common/directives/_directives.scss | 78 ++++++ .../treeSelect/TreeSelectDirective.d.ts | 34 +++ .../treeSelect/TreeSelectDirective.js | 223 ++++++++++++++++++ .../src/common/directives/treeSelect/index.js | 22 ++ .../treeSelect/tree.select.tpl.html | 40 ++++ .../form.deliveryService.DNS.tpl.html | 4 +- .../form.deliveryService.HTTP.tpl.html | 4 +- .../form.deliveryService.Steering.tpl.html | 4 +- .../modules/form/origin/form.origin.tpl.html | 4 +- .../modules/form/tenant/form.tenant.tpl.html | 6 +- .../modules/form/user/form.user.tpl.html | 4 +- .../user/register/form.user.register.tpl.html | 4 +- traffic_portal/app/src/styles/main.scss | 1 + 14 files changed, 407 insertions(+), 22 deletions(-) create mode 100644 traffic_portal/app/src/common/directives/_directives.scss create mode 100644 traffic_portal/app/src/common/directives/treeSelect/TreeSelectDirective.d.ts create mode 100644 traffic_portal/app/src/common/directives/treeSelect/TreeSelectDirective.js create mode 100644 traffic_portal/app/src/common/directives/treeSelect/index.js create mode 100644 traffic_portal/app/src/common/directives/treeSelect/tree.select.tpl.html diff --git a/traffic_portal/app/src/app.js b/traffic_portal/app/src/app.js index 3e78ab2489..61b893f001 100644 --- a/traffic_portal/app/src/app.js +++ b/traffic_portal/app/src/app.js @@ -447,6 +447,7 @@ var trafficPortal = angular.module('trafficPortal', [ // directives require('./common/directives/match').name, require('./common/directives/dragAndDrop').name, + require('./common/directives/treeSelect').name, // services require('./common/service/application').name, diff --git a/traffic_portal/app/src/common/directives/_directives.scss b/traffic_portal/app/src/common/directives/_directives.scss new file mode 100644 index 0000000000..6b452b08b8 --- /dev/null +++ b/traffic_portal/app/src/common/directives/_directives.scss @@ -0,0 +1,78 @@ +/* + + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +div.tree-select-root { + position: relative; + + input.display-field { + background-color: #fff; + + &::after { + content :'< >'; + transform: rotate(90deg); + font-size: 25px; + background: black; + position: absolute; + top: 0; + width: 40px; + height: 40px; + right: 0; + } + } + + div.tree-drop-down { + width: 100%; + background: white; + position: absolute; + z-index: 2; + box-shadow: rgba(0, 0, 0, 0.38) 0px 3px 3px; + border: 1px solid #0000001f; + font-size: 16px; + + div.search-box { + width: calc(100% - 10px); + margin: 5px auto 5px; + + @media (min-width: 768px) { + div.helptooltip { + left: calc(100% - 50px); + } + } + } + + ul.nav { + max-height: 550px; + overflow-y: scroll; + overflow-x: hidden; + + & > li.tree-row > a { + padding: 5px 0 5px 10px; + + + i { + margin-right: 3px; + } + + @for $i from 0 through 30 { + &.depth-#{$i} { + position: relative; + left: #{$i * 8}px; + } + } + } + } + } +} diff --git a/traffic_portal/app/src/common/directives/treeSelect/TreeSelectDirective.d.ts b/traffic_portal/app/src/common/directives/treeSelect/TreeSelectDirective.d.ts new file mode 100644 index 0000000000..488c0e3161 --- /dev/null +++ b/traffic_portal/app/src/common/directives/treeSelect/TreeSelectDirective.d.ts @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export namespace TreeSelectDirective { + export interface TreeData { + name: string; + id: string; + children: [TreeData]; + } + export interface RowData { + label: string; + value: string; + depth: number; + collapsed: boolean; + hidden: boolean; + children: [RowData]; + } +} diff --git a/traffic_portal/app/src/common/directives/treeSelect/TreeSelectDirective.js b/traffic_portal/app/src/common/directives/treeSelect/TreeSelectDirective.js new file mode 100644 index 0000000000..e938d8d716 --- /dev/null +++ b/traffic_portal/app/src/common/directives/treeSelect/TreeSelectDirective.js @@ -0,0 +1,223 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +var TreeSelectDirective = function($document) { + return { + restrict: "E", + templateUrl: "common/directives/treeSelect/tree.select.tpl.html", + replace: true, + scope: { + treeData: '=', + initialValue: '<', + handle: '@', + onUpdate: "&" + }, + link: function(scope, element, attrs) { + /** + * Non-recursed ordered list of rows to display (before filtering) + * @type [TreeSelectDirective.RowData] + */ + scope.treeRows = []; + /** @type string */ + scope.searchText = ""; + /** @type boolean */ + scope.shown = false; + /** @type string */ + scope.selected = null; + + // Bound variables + /** @type [TreeSelectDirective.TreeData] */ + scope.treeData; + /** @type string */ + scope.initialValue; + /** + * Used for form validation, will be assigned to an id attribute + * @type string + */ + scope.handle; + /** + * Used to properly update the parent on value change, useful for validation. + * @callback onUpdate + * @param {string} value + */ + scope.onUpdate; + + + element.bind("click", + /** + * Handles click events within this element, prevents $document propagation as well as binds + * the close event to it. + * @param evt + */ + function(evt) { + if(scope.shown) { + evt.stopPropagation(); + $document.on("click", closeSelect); + } + }); + /** + * Detects when a click is trigger outside this element. Used to close dropdown and ensure + * there is no $document event pollution. + * @param evt + */ + const closeSelect = function(evt){ + scope.close(); + $document.off("click", closeSelect); + scope.$apply(); + }; + + /** + * Converts a tree data node into row data recursively. + * @param {TreeSelectDirective.TreeData} row + * @param {number} depth + * @returns TreeSelectDirective.RowData + */ + const addNode = function(row, depth) { + scope.treeRows.push({ + label: row.name, + value: row.id, + depth: depth, + children: [], + collapsed: false, + hidden: false + }); + const last = scope.treeRows.length - 1; + if(row.id === scope.initialValue) { + scope.selected = scope.treeRows[last]; + } + if(row.children != null) { + for(let child of row.children) { + if(child == undefined) continue; + scope.treeRows[last].children.push(addNode(child, depth + 1)); + } + } + return scope.treeRows[last]; + } + + /** + * Collapses a row data and recursively hides and collapses its children. + * @param {TreeSelectDirective.RowData} row + * @param {boolean?} state + */ + const collapseRecurse = function(row, state) { + if(row.children.length === 0 ) return; + for(let treeRow of scope.treeRows) { + if (treeRow.value === row.value) { + if(state == null) + treeRow.collapsed = !treeRow.collapsed; + else + treeRow.collapsed = state; + for(let treeChild of treeRow.children) { + treeChild.hidden = treeRow.collapsed; + collapseRecurse(treeChild, treeRow.collapsed); + } + } + } + } + + /** + * Returns true if the inputs letters are also present in text with the same order + * @param {string} text + * @param {string} input + * @returns {boolean} + */ + const fuzzyMatch = function(text, input) { + if(input === "") return true; + if(text == undefined) return false; + text = text.toString().toLowerCase(); + input = input.toString().toLowerCase(); + let n = -1; + for(let i in input) { + const letter = input[i]; + if (!~(n = text.indexOf(letter, n + 1))) return false; + } + return true; + } + + /** + * Triggers onUpdate binding + */ + scope.update = function() { + scope.onUpdate({value: scope.selected}); + } + + /** + * Toggle the dropdown menu + */ + scope.toggle = function() { + scope.shown = !scope.shown; + } + /** + * Close the dropdown menu. + */ + scope.close = function() { + scope.shown = false; + } + + + /** + * Updates the selection when clicking a dropdown option + * @param {TreeSelectDirective.RowData} row + */ + scope.select = function(row) { + scope.selected = row; + scope.selection = row.value; + scope.close(); + } + /** + * When collapse icon is clicked on row data + * @param {TreeSelectDirective.RowData} row + * @param evt + */ + scope.collapse = function(row, evt) { + evt.stopPropagation(); + return collapseRecurse(row); + } + + /** + * Returns true if the row data + * @param {TreeSelectDirective.RowData} testRow + * @returns {boolean} + */ + scope.checkFilters = function(testRow) { + if(testRow.hidden && scope.searchText.length === 0) + return false; + return fuzzyMatch(testRow.label, scope.searchText); + } + + /** + * Gets the FontAwesome icon class based on if the row data has children and is collapsed + * @param {TreeSelectDirective.RowData} row + * @returns {string} + */ + scope.getClass = function(row) { + if(row.collapsed) return "fa-minus"; + else if(row.children.length > 0) return "fa-plus"; + else return "fa-file"; + } + + for(let data of scope.treeData) { + addNode(data, 0); + } + } + } +}; + +TreeSelectDirective.$inject = ['$document']; +module.exports = TreeSelectDirective; diff --git a/traffic_portal/app/src/common/directives/treeSelect/index.js b/traffic_portal/app/src/common/directives/treeSelect/index.js new file mode 100644 index 0000000000..5a478d8520 --- /dev/null +++ b/traffic_portal/app/src/common/directives/treeSelect/index.js @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +//application directives +module.exports = angular.module('trafficPortal.directives.treeSelect',[]) + .directive('treeSelect', require('./TreeSelectDirective')); diff --git a/traffic_portal/app/src/common/directives/treeSelect/tree.select.tpl.html b/traffic_portal/app/src/common/directives/treeSelect/tree.select.tpl.html new file mode 100644 index 0000000000..a638e6dff2 --- /dev/null +++ b/traffic_portal/app/src/common/directives/treeSelect/tree.select.tpl.html @@ -0,0 +1,40 @@ + +
+ +
+ + +
+
diff --git a/traffic_portal/app/src/common/modules/form/deliveryService/form.deliveryService.DNS.tpl.html b/traffic_portal/app/src/common/modules/form/deliveryService/form.deliveryService.DNS.tpl.html index fbed3754bc..a0941750ef 100644 --- a/traffic_portal/app/src/common/modules/form/deliveryService/form.deliveryService.DNS.tpl.html +++ b/traffic_portal/app/src/common/modules/form/deliveryService/form.deliveryService.DNS.tpl.html @@ -159,9 +159,7 @@

Previous Value

- + Required View Details  
- + Required View Details  
- + Required View Details