Sort MyItems alphabetically when building the tree #1717 (#4376)

* Sort MyItems alphabetically when building the tree

* Fixed brace indentation

* Modified tree-item to accept is-new prop for initial highlighting

* Adjusted sort function and properly sort children before splicing treetItems

* Mods for #1717
- Refined color and timing, color is now theme-compliant;
- Added new theme color constants;
- Smoke tested in Espresso and Snow themes;

Co-authored-by: Andrew Henry <akhenry@gmail.com>
Co-authored-by: Charles Hacskaylo <charlesh88@gmail.com>
This commit is contained in:
Michael Rogers 2021-11-04 13:35:10 -07:00 committed by GitHub
parent 980777691a
commit 61dd85c704
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 77 additions and 8 deletions

View File

@ -382,6 +382,7 @@ $colorItemTreeEditingFg: $editUIColor;
$colorItemTreeEditingIcon: $editUIColor; $colorItemTreeEditingIcon: $editUIColor;
$colorItemTreeVC: $colorDisclosureCtrl; $colorItemTreeVC: $colorDisclosureCtrl;
$colorItemTreeVCHover: $colorDisclosureCtrlHov; $colorItemTreeVCHover: $colorDisclosureCtrlHov;
$colorItemTreeNewNode: rgba($colorBodyFg, 0.7);
$shdwItemTreeIcon: none; $shdwItemTreeIcon: none;
// Layout frame controls // Layout frame controls

View File

@ -386,6 +386,7 @@ $colorItemTreeEditingFg: $editUIColor;
$colorItemTreeEditingIcon: $editUIColor; $colorItemTreeEditingIcon: $editUIColor;
$colorItemTreeVC: $colorDisclosureCtrl; $colorItemTreeVC: $colorDisclosureCtrl;
$colorItemTreeVCHover: $colorDisclosureCtrlHov; $colorItemTreeVCHover: $colorDisclosureCtrlHov;
$colorItemTreeNewNode: rgba($colorBodyFg, 0.7);
$shdwItemTreeIcon: none; $shdwItemTreeIcon: none;
// Layout frame controls // Layout frame controls

View File

@ -382,6 +382,7 @@ $colorItemTreeEditingFg: $editUIColor;
$colorItemTreeEditingIcon: $editUIColor; $colorItemTreeEditingIcon: $editUIColor;
$colorItemTreeVC: $colorDisclosureCtrl; $colorItemTreeVC: $colorDisclosureCtrl;
$colorItemTreeVCHover: $colorDisclosureCtrlHov; $colorItemTreeVCHover: $colorDisclosureCtrlHov;
$colorItemTreeNewNode: rgba($colorBodyFg, 0.5);
$shdwItemTreeIcon: none; $shdwItemTreeIcon: none;
// Layout frame controls // Layout frame controls

View File

@ -105,7 +105,12 @@
color: $colorItemTreeSelectedFg; color: $colorItemTreeSelectedFg;
} }
} }
&.is-new {
animation-name: animTemporaryHighlight;
animation-timing-function: ease-out;
animation-duration: 3s;
animation-iteration-count: 1;
}
&.is-context-clicked { &.is-context-clicked {
box-shadow: inset $colorItemTreeSelectedBg 0 0 0 1px; box-shadow: inset $colorItemTreeSelectedBg 0 0 0 1px;
} }
@ -289,13 +294,19 @@
} }
@keyframes animSlideLeft { @keyframes animSlideLeft {
0% {opactiy: 0; transform: translateX(100%);} 0% {opacity: 0; transform: translateX(100%);}
10% {opacity: 1;} 10% {opacity: 1;}
100% {transform: translateX(0);} 100% {transform: translateX(0);}
} }
@keyframes animSlideRight { @keyframes animSlideRight {
0% {opactiy: 0; transform: translateX(-100%);} 0% {opacity: 0; transform: translateX(-100%);}
10% {opacity: 1;} 10% {opacity: 1;}
100% {transform: translateX(0);} 100% {transform: translateX(0);}
} }
@keyframes animTemporaryHighlight {
from { background: transparent; }
30% { background: $colorItemTreeNewNode; }
100% { background: transparent; }
}

View File

@ -74,6 +74,7 @@
:node="treeItem" :node="treeItem"
:active-search="activeSearch" :active-search="activeSearch"
:left-offset="!activeSearch ? treeItem.leftOffset : '0px'" :left-offset="!activeSearch ? treeItem.leftOffset : '0px'"
:is-new="treeItem.isNew"
:item-offset="itemOffset" :item-offset="itemOffset"
:item-index="index" :item-index="index"
:item-height="itemHeight" :item-height="itemHeight"
@ -112,7 +113,8 @@ import search from '../components/search.vue';
const ITEM_BUFFER = 25; const ITEM_BUFFER = 25;
const LOCAL_STORAGE_KEY__TREE_EXPANDED = 'mct-tree-expanded'; const LOCAL_STORAGE_KEY__TREE_EXPANDED = 'mct-tree-expanded';
const RETURN_ALL_DESCDNDANTS = true; const RETURN_ALL_DESCENDANTS = true;
const SORT_MY_ITEMS_ALPH_ASC = true;
const TREE_ITEM_INDENT_PX = 18; const TREE_ITEM_INDENT_PX = 18;
export default { export default {
@ -430,9 +432,40 @@ export default {
return scrollTopAmount >= treeStart && scrollTopAmount < treeEnd; return scrollTopAmount >= treeStart && scrollTopAmount < treeEnd;
}, },
sortNameDescending(a, b) {
// sorting tree children items
if (!(a.name && b.name)) {
if (a.object.name > b.object.name) {
return 1;
}
if (b.object.name > a.object.name) {
return -1;
}
}
// sorting compositon items
if (a.name > b.name) {
return 1;
}
if (b.name > a.name) {
return -1;
}
return 0;
},
async loadAndBuildTreeItemsFor(domainObject, parentObjectPath, abortSignal) { async loadAndBuildTreeItemsFor(domainObject, parentObjectPath, abortSignal) {
let collection = this.openmct.composition.get(domainObject); let collection = this.openmct.composition.get(domainObject);
let composition = await collection.load(abortSignal); let composition = await collection.load(abortSignal);
// determine if any part of the parent's path includes a key value of mine; aka My Items
const isNestedInMyItems = Boolean(parentObjectPath.find(path => path.identifier.key === 'mine'));
if (SORT_MY_ITEMS_ALPH_ASC && isNestedInMyItems) {
const sortedComposition = composition.sort(this.sortNameDescending);
composition = sortedComposition;
}
if (parentObjectPath.length) { if (parentObjectPath.length) {
let navigationPath = this.buildNavigationPath(parentObjectPath); let navigationPath = this.buildNavigationPath(parentObjectPath);
@ -456,7 +489,7 @@ export default {
return this.buildTreeItem(object, parentObjectPath); return this.buildTreeItem(object, parentObjectPath);
}); });
}, },
buildTreeItem(domainObject, parentObjectPath) { buildTreeItem(domainObject, parentObjectPath, isNew = false) {
let objectPath = [domainObject].concat(parentObjectPath); let objectPath = [domainObject].concat(parentObjectPath);
let navigationPath = this.buildNavigationPath(objectPath); let navigationPath = this.buildNavigationPath(objectPath);
@ -464,6 +497,7 @@ export default {
id: this.openmct.objects.makeKeyString(domainObject.identifier), id: this.openmct.objects.makeKeyString(domainObject.identifier),
object: domainObject, object: domainObject,
leftOffset: ((objectPath.length - 1) * TREE_ITEM_INDENT_PX) + 'px', leftOffset: ((objectPath.length - 1) * TREE_ITEM_INDENT_PX) + 'px',
isNew,
objectPath, objectPath,
navigationPath navigationPath
}; };
@ -476,11 +510,16 @@ export default {
compositionAddHandler(navigationPath) { compositionAddHandler(navigationPath) {
return (domainObject) => { return (domainObject) => {
let parentItem = this.getTreeItemByPath(navigationPath); let parentItem = this.getTreeItemByPath(navigationPath);
let newItem = this.buildTreeItem(domainObject, parentItem.objectPath); let newItem = this.buildTreeItem(domainObject, parentItem.objectPath, true);
let allDescendants = this.getChildrenInTreeFor(parentItem, RETURN_ALL_DESCDNDANTS); let allDescendants = this.getChildrenInTreeFor(parentItem, RETURN_ALL_DESCENDANTS);
let afterItem = allDescendants.length ? allDescendants.pop() : parentItem; let afterItem = allDescendants.length ? allDescendants.pop() : parentItem;
this.addItemToTreeAfter(newItem, afterItem); this.addItemToTreeAfter(newItem, afterItem);
const isNestedInMyItems = Boolean(parentItem.objectPath && parentItem.objectPath.find(path => path.identifier.key === 'mine'));
if (SORT_MY_ITEMS_ALPH_ASC && isNestedInMyItems) {
this.sortTreeComposition(this.sortNameDescending, navigationPath);
}
}; };
}, },
compositionRemoveHandler(navigationPath) { compositionRemoveHandler(navigationPath) {
@ -512,6 +551,14 @@ export default {
const removeIndex = this.getTreeItemIndex(item.navigationPath); const removeIndex = this.getTreeItemIndex(item.navigationPath);
this.treeItems.splice(removeIndex, 1); this.treeItems.splice(removeIndex, 1);
}, },
sortTreeComposition(algorithem, parentPath) {
const parentIndex = this.getTreeItemIndex(parentPath);
const parentItem = this.treeItems[parentIndex];
const allDescendants = this.getChildrenInTreeFor(parentItem);
const sortedChildren = allDescendants.sort(algorithem);
this.treeItems.splice(parentIndex + 1, allDescendants.length, ...sortedChildren);
},
addItemToTreeAfter(addItem, afterItem) { addItemToTreeAfter(addItem, afterItem) {
const addIndex = this.getTreeItemIndex(afterItem.navigationPath); const addIndex = this.getTreeItemIndex(afterItem.navigationPath);
this.treeItems.splice(addIndex + 1, 0, addItem); this.treeItems.splice(addIndex + 1, 0, addItem);

View File

@ -8,7 +8,8 @@
:class="{ :class="{
'is-alias': isAlias, 'is-alias': isAlias,
'is-navigated-object': navigated, 'is-navigated-object': navigated,
'is-context-clicked': contextClickActive 'is-context-clicked': contextClickActive,
'is-new': isNewItem
}" }"
@click.capture="handleClick" @click.capture="handleClick"
@contextmenu.capture="handleContextMenu" @contextmenu.capture="handleContextMenu"
@ -59,6 +60,10 @@ export default {
type: String, type: String,
default: '0px' default: '0px'
}, },
isNew: {
type: Boolean,
default: false
},
itemIndex: { itemIndex: {
type: Number, type: Number,
required: false, required: false,
@ -104,6 +109,9 @@ export default {
return parentKeyString !== this.node.object.location; return parentKeyString !== this.node.object.location;
}, },
isNewItem() {
return this.isNew;
},
isLoading() { isLoading() {
return Boolean(this.loadingItems[this.navigationPath]); return Boolean(this.loadingItems[this.navigationPath]);
}, },