mirror of
https://github.com/nasa/openmct.git
synced 2025-06-15 21:58:13 +00:00
context menu and shared object link generation (#2199)
* temporarily disable remove dialog which is broken * temporarily remove broken context action policy * let openmct generate legacy objects * ensure composition loads in specified order * redo nav and add context menu support to tree * componentize grid and list view, add context menus
This commit is contained in:
@ -69,8 +69,8 @@ define([], function () {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
setTimeout(() => this.removeCallback(domainObject));
|
||||||
|
|
||||||
dialog = this.dialogService.showBlockingMessage(model);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return RemoveDialog;
|
return RemoveDialog;
|
||||||
|
@ -55,16 +55,19 @@ define(
|
|||||||
navigatedObject = this.navigationService.getNavigation(),
|
navigatedObject = this.navigationService.getNavigation(),
|
||||||
actionMetadata = action.getMetadata ? action.getMetadata() : {};
|
actionMetadata = action.getMetadata ? action.getMetadata() : {};
|
||||||
|
|
||||||
|
// FIXME: need to restore support for changing contextual actions
|
||||||
|
// based on edit mode.
|
||||||
// if (navigatedObject.hasCapability("editor") && navigatedObject.getCapability("editor").isEditContextRoot()) {
|
// if (navigatedObject.hasCapability("editor") && navigatedObject.getCapability("editor").isEditContextRoot()) {
|
||||||
if (selectedObject.hasCapability("editor") && selectedObject.getCapability("editor").inEditContext()) {
|
// if (selectedObject.hasCapability("editor") && selectedObject.getCapability("editor").inEditContext()) {
|
||||||
return this.editModeBlacklist.indexOf(actionMetadata.key) === -1;
|
// return this.editModeBlacklist.indexOf(actionMetadata.key) === -1;
|
||||||
} else {
|
// } else {
|
||||||
//Target is in the context menu
|
// //Target is in the context menu
|
||||||
return this.nonEditContextBlacklist.indexOf(actionMetadata.key) === -1;
|
// return this.nonEditContextBlacklist.indexOf(actionMetadata.key) === -1;
|
||||||
}
|
// }
|
||||||
// } else {
|
// } else {
|
||||||
// return true;
|
// return true;
|
||||||
// }
|
// }
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
return EditContextualActionPolicy;
|
return EditContextualActionPolicy;
|
||||||
|
30
src/MCT.js
30
src/MCT.js
@ -41,6 +41,7 @@ define([
|
|||||||
'./styles-new/core.scss',
|
'./styles-new/core.scss',
|
||||||
'./styles-new/notebook.scss',
|
'./styles-new/notebook.scss',
|
||||||
'./ui/components/layout/Layout.vue',
|
'./ui/components/layout/Layout.vue',
|
||||||
|
'../platform/core/src/capabilities/ContextualDomainObject',
|
||||||
'vue'
|
'vue'
|
||||||
], function (
|
], function (
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
@ -63,6 +64,7 @@ define([
|
|||||||
coreStyles,
|
coreStyles,
|
||||||
NotebookStyles,
|
NotebookStyles,
|
||||||
Layout,
|
Layout,
|
||||||
|
ContextualDomainObject,
|
||||||
Vue
|
Vue
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
@ -241,6 +243,34 @@ define([
|
|||||||
this.legacyBundle.extensions[category].push(extension);
|
this.legacyBundle.extensions[category].push(extension);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a legacy object, for compatibility purposes only. This method
|
||||||
|
* will be deprecated and removed in the future.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
MCT.prototype.legacyObject = function (domainObject) {
|
||||||
|
if (Array.isArray(domainObject)) {
|
||||||
|
// an array of domain objects. [object, ...ancestors] representing
|
||||||
|
// a single object with a given chain of ancestors. We instantiate
|
||||||
|
// as a single contextual domain object.
|
||||||
|
return domainObject
|
||||||
|
.map((o) => {
|
||||||
|
let keyString = objectUtils.makeKeyString(o.identifier);
|
||||||
|
let oldModel = objectUtils.toOldFormat(o);
|
||||||
|
return this.$injector.get('instantiate')(oldModel, keyString);
|
||||||
|
})
|
||||||
|
.reverse()
|
||||||
|
.reduce((parent, child) => {
|
||||||
|
return new ContextualDomainObject(child, parent);
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
let keyString = objectUtils.makeKeyString(domainObject.identifier);
|
||||||
|
let oldModel = objectUtils.toOldFormat(domainObject);
|
||||||
|
return this.$injector.get('instantiate')(oldModel, keyString);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set path to where assets are hosted. This should be the path to main.js.
|
* Set path to where assets are hosted. This should be the path to main.js.
|
||||||
* @memberof module:openmct.MCT#
|
* @memberof module:openmct.MCT#
|
||||||
|
@ -177,7 +177,11 @@ define([
|
|||||||
CompositionCollection.prototype.load = function () {
|
CompositionCollection.prototype.load = function () {
|
||||||
return this.provider.load(this.domainObject)
|
return this.provider.load(this.domainObject)
|
||||||
.then(function (children) {
|
.then(function (children) {
|
||||||
return Promise.all(children.map(this.onProviderAdd, this));
|
return Promise.all(children.map((c) => this.publicAPI.objects.get(c)));
|
||||||
|
}.bind(this))
|
||||||
|
.then(function (childObjects) {
|
||||||
|
childObjects.forEach(c => this.add(c, true));
|
||||||
|
return childObjects;
|
||||||
}.bind(this))
|
}.bind(this))
|
||||||
.then(function (children) {
|
.then(function (children) {
|
||||||
this.emit('load');
|
this.emit('load');
|
||||||
|
160
src/plugins/folderView/components/GridItem.vue
Normal file
160
src/plugins/folderView/components/GridItem.vue
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
<template>
|
||||||
|
<a class="l-grid-view__item c-grid-item"
|
||||||
|
:class="{ 'is-alias': item.isAlias === true }"
|
||||||
|
:href="objectLink">
|
||||||
|
<div class="c-grid-item__type-icon"
|
||||||
|
:class="(item.type.cssClass != undefined) ? 'bg-' + item.type.cssClass : 'bg-icon-object-unknown'">
|
||||||
|
</div>
|
||||||
|
<div class="c-grid-item__details">
|
||||||
|
<!-- Name and metadata -->
|
||||||
|
<div class="c-grid-item__name"
|
||||||
|
:title="item.model.name">{{item.model.name}}</div>
|
||||||
|
<div class="c-grid-item__metadata"
|
||||||
|
:title="item.type.name">
|
||||||
|
<span class="c-grid-item__metadata__type">{{item.type.name}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="c-grid-item__controls">
|
||||||
|
<div class="icon-people" title='Shared'></div>
|
||||||
|
<button class="c-click-icon icon-info c-info-button" title='More Info'></button>
|
||||||
|
<div class="icon-pointer-right c-pointer-icon"></div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import "~styles/sass-base";
|
||||||
|
|
||||||
|
/******************************* GRID ITEMS */
|
||||||
|
.c-grid-item {
|
||||||
|
// Mobile-first
|
||||||
|
@include button($bg: $colorItemBg, $fg: $colorItemFg);
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
padding: $interiorMarginLg;
|
||||||
|
|
||||||
|
&__type-icon {
|
||||||
|
filter: $colorKeyFilter;
|
||||||
|
flex: 0 0 $gridItemMobile;
|
||||||
|
font-size: floor($gridItemMobile / 2);
|
||||||
|
margin-right: $interiorMarginLg;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-alias {
|
||||||
|
// Object is an alias to an original.
|
||||||
|
[class*='__type-icon'] {
|
||||||
|
@include isAlias();
|
||||||
|
color: $colorIconAliasForKeyFilter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__details {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column nowrap;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__name {
|
||||||
|
@include ellipsize();
|
||||||
|
color: $colorItemFg;
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-weight: 400;
|
||||||
|
margin-bottom: $interiorMarginSm;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__metadata {
|
||||||
|
color: $colorItemFgDetails;
|
||||||
|
font-size: 0.9em;
|
||||||
|
|
||||||
|
body.mobile & {
|
||||||
|
[class*='__item-count'] {
|
||||||
|
&:before {
|
||||||
|
content: ' - ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__controls {
|
||||||
|
color: $colorItemFgDetails;
|
||||||
|
flex: 0 0 64px;
|
||||||
|
font-size: 1.2em;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
> * + * {
|
||||||
|
margin-left: $interiorMargin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body.desktop & {
|
||||||
|
$transOutMs: 300ms;
|
||||||
|
flex-flow: column nowrap;
|
||||||
|
transition: background $transOutMs ease-in-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $colorItemBgHov;
|
||||||
|
transition: $transIn;
|
||||||
|
|
||||||
|
.c-grid-item__type-icon {
|
||||||
|
filter: $colorKeyFilterHov;
|
||||||
|
transform: scale(1);
|
||||||
|
transition: $transInBounce;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> * {
|
||||||
|
margin: 0; // Reset from mobile
|
||||||
|
}
|
||||||
|
|
||||||
|
&__controls {
|
||||||
|
align-items: start;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
order: 1;
|
||||||
|
.c-info-button,
|
||||||
|
.c-pointer-icon { display: none; }
|
||||||
|
}
|
||||||
|
|
||||||
|
&__type-icon {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
font-size: floor($gridItemDesk / 3);
|
||||||
|
margin: $interiorMargin 22.5% $interiorMargin * 3 22.5%;
|
||||||
|
order: 2;
|
||||||
|
transform: scale(0.9);
|
||||||
|
transform-origin: center;
|
||||||
|
transition: all $transOutMs ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__details {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
justify-content: flex-end;
|
||||||
|
order: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__metadata {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
&__type {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
@include ellipsize();
|
||||||
|
}
|
||||||
|
|
||||||
|
&__item-count {
|
||||||
|
opacity: 0.7;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import contextMenu from '../../../ui/components/mixins/context-menu';
|
||||||
|
import objectLink from '../../../ui/components/mixins/object-link';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [contextMenu, objectLink],
|
||||||
|
props: ['item']
|
||||||
|
}
|
||||||
|
</script>
|
@ -1,28 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="l-grid-view">
|
<div class="l-grid-view">
|
||||||
<div v-for="(item, index) in items"
|
<grid-item v-for="(item, index) in items"
|
||||||
v-bind:key="index"
|
:key="index"
|
||||||
class="l-grid-view__item c-grid-item"
|
:item="item"
|
||||||
:class="{ 'is-alias': item.isAlias === true }"
|
:object-path="item.objectPath">
|
||||||
@click="navigate(item)">
|
</grid-item>
|
||||||
<div class="c-grid-item__type-icon"
|
|
||||||
:class="(item.type.cssClass != undefined) ? 'bg-' + item.type.cssClass : 'bg-icon-object-unknown'">
|
|
||||||
</div>
|
|
||||||
<div class="c-grid-item__details">
|
|
||||||
<!-- Name and metadata -->
|
|
||||||
<div class="c-grid-item__name"
|
|
||||||
:title="item.model.name">{{item.model.name}}</div>
|
|
||||||
<div class="c-grid-item__metadata"
|
|
||||||
:title="item.type.name">
|
|
||||||
<span class="c-grid-item__metadata__type">{{item.type.name}}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="c-grid-item__controls">
|
|
||||||
<div class="icon-people" title='Shared'></div>
|
|
||||||
<button class="c-click-icon icon-info c-info-button" title='More Info'></button>
|
|
||||||
<div class="icon-pointer-right c-pointer-icon"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -177,17 +159,11 @@
|
|||||||
<script>
|
<script>
|
||||||
|
|
||||||
import compositionLoader from './composition-loader';
|
import compositionLoader from './composition-loader';
|
||||||
|
import GridItem from './GridItem.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: {GridItem},
|
||||||
mixins: [compositionLoader],
|
mixins: [compositionLoader],
|
||||||
inject: ['domainObject', 'openmct'],
|
inject: ['openmct']
|
||||||
methods: {
|
|
||||||
navigate(item) {
|
|
||||||
let currentLocation = this.openmct.router.currentLocation.path,
|
|
||||||
navigateToPath = `${currentLocation}/${this.openmct.objects.makeKeyString(item.model.identifier)}`;
|
|
||||||
|
|
||||||
this.openmct.router.setPath(navigateToPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
72
src/plugins/folderView/components/ListItem.vue
Normal file
72
src/plugins/folderView/components/ListItem.vue
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<template>
|
||||||
|
<tr class="c-list-item"
|
||||||
|
:class="{ 'is-alias': item.isAlias === true }"
|
||||||
|
@click="navigate">
|
||||||
|
<td class="c-list-item__name">
|
||||||
|
<a :href="objectLink" ref="objectLink">
|
||||||
|
<div class="c-list-item__type-icon"
|
||||||
|
:class="item.type.cssClass"></div>
|
||||||
|
{{item.model.name}}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td class="c-list-item__type">{{ item.type.name }}</td>
|
||||||
|
<td class="c-list-item__date-created">{{ formatTime(item.model.persisted, 'YYYY-MM-DD HH:mm:ss:SSS') }}Z</td>
|
||||||
|
<td class="c-list-item__date-updated">{{ formatTime(item.model.modified, 'YYYY-MM-DD HH:mm:ss:SSS') }}Z</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import "~styles/sass-base";
|
||||||
|
|
||||||
|
/******************************* LIST ITEM */
|
||||||
|
.c-list-item {
|
||||||
|
&__name {
|
||||||
|
@include ellipsize();
|
||||||
|
}
|
||||||
|
|
||||||
|
&__type-icon {
|
||||||
|
color: $colorKey;
|
||||||
|
display: inline-block;
|
||||||
|
width: 1em;
|
||||||
|
margin-right:$interiorMarginSm;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-alias {
|
||||||
|
// Object is an alias to an original.
|
||||||
|
[class*='__type-icon'] {
|
||||||
|
&:after {
|
||||||
|
color: $colorIconAlias;
|
||||||
|
content: $glyph-icon-link;
|
||||||
|
font-family: symbolsfont;
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
text-shadow: rgba(black, 0.5) 0 1px 2px;
|
||||||
|
top: auto; left: -1px; bottom: 1px; right: auto;
|
||||||
|
transform-origin: bottom left;
|
||||||
|
transform: scale(0.65);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import moment from 'moment';
|
||||||
|
import contextMenu from '../../../ui/components/mixins/context-menu';
|
||||||
|
import objectLink from '../../../ui/components/mixins/object-link';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [contextMenu, objectLink],
|
||||||
|
props: ['item'],
|
||||||
|
methods: {
|
||||||
|
formatTime(timestamp, format) {
|
||||||
|
return moment(timestamp).format(format);
|
||||||
|
},
|
||||||
|
navigate() {
|
||||||
|
this.$refs.objectLink.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -2,60 +2,50 @@
|
|||||||
<div class="c-table c-table--sortable c-list-view">
|
<div class="c-table c-table--sortable c-list-view">
|
||||||
<table class="c-table__body">
|
<table class="c-table__body">
|
||||||
<thead class="c-table__header">
|
<thead class="c-table__header">
|
||||||
<tr>
|
<tr>
|
||||||
<th class="is-sortable"
|
<th class="is-sortable"
|
||||||
:class="{
|
:class="{
|
||||||
'is-sorting': sortBy === 'model.name',
|
'is-sorting': sortBy === 'model.name',
|
||||||
'asc': ascending,
|
'asc': ascending,
|
||||||
'desc': !ascending
|
'desc': !ascending
|
||||||
}"
|
}"
|
||||||
@click="sort('model.name', true)">
|
@click="sort('model.name', true)">
|
||||||
Name
|
Name
|
||||||
</th>
|
</th>
|
||||||
<th class="is-sortable"
|
<th class="is-sortable"
|
||||||
:class="{
|
:class="{
|
||||||
'is-sorting': sortBy === 'type.name',
|
'is-sorting': sortBy === 'type.name',
|
||||||
'asc': ascending,
|
'asc': ascending,
|
||||||
'desc': !ascending
|
'desc': !ascending
|
||||||
}"
|
}"
|
||||||
@click="sort('type.name', true)">
|
@click="sort('type.name', true)">
|
||||||
Type
|
Type
|
||||||
</th>
|
</th>
|
||||||
<th class="is-sortable"
|
<th class="is-sortable"
|
||||||
:class="{
|
:class="{
|
||||||
'is-sorting': sortBy === 'model.persisted',
|
'is-sorting': sortBy === 'model.persisted',
|
||||||
'asc': ascending,
|
'asc': ascending,
|
||||||
'desc': !ascending
|
'desc': !ascending
|
||||||
}"
|
}"
|
||||||
@click="sort('model.persisted', false)">
|
@click="sort('model.persisted', false)">
|
||||||
Created Date
|
Created Date
|
||||||
</th>
|
</th>
|
||||||
<th class="is-sortable"
|
<th class="is-sortable"
|
||||||
:class="{
|
:class="{
|
||||||
'is-sorting': sortBy === 'model.modified',
|
'is-sorting': sortBy === 'model.modified',
|
||||||
'asc': ascending,
|
'asc': ascending,
|
||||||
'desc': !ascending
|
'desc': !ascending
|
||||||
}"
|
}"
|
||||||
@click="sort('model.modified', false)">
|
@click="sort('model.modified', false)">
|
||||||
Updated Date
|
Updated Date
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr class="c-list-item"
|
<list-item v-for="(item,index) in sortedItems"
|
||||||
v-for="(item,index) in sortedItems"
|
:item="item"
|
||||||
v-bind:key="index"
|
:object-path="item.objectPath">
|
||||||
:class="{ 'is-alias': item.isAlias === true }"
|
</list-item>
|
||||||
@click="navigate(item)">
|
|
||||||
<td class="c-list-item__name">
|
|
||||||
<div class="c-list-item__type-icon"
|
|
||||||
:class="item.type.cssClass"></div>
|
|
||||||
{{item.model.name}}
|
|
||||||
</td>
|
|
||||||
<td class="c-list-item__type">{{ item.type.name }}</td>
|
|
||||||
<td class="c-list-item__date-created">{{ formatTime(item.model.persisted, 'YYYY-MM-DD HH:mm:ss:SSS') }}Z</td>
|
|
||||||
<td class="c-list-item__date-updated">{{ formatTime(item.model.modified, 'YYYY-MM-DD HH:mm:ss:SSS') }}Z</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@ -96,48 +86,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.c-list-item {
|
|
||||||
&__name {
|
|
||||||
@include ellipsize();
|
|
||||||
}
|
|
||||||
|
|
||||||
&__type-icon {
|
|
||||||
color: $colorKey;
|
|
||||||
display: inline-block;
|
|
||||||
width: 1em;
|
|
||||||
margin-right:$interiorMarginSm;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.is-alias {
|
|
||||||
// Object is an alias to an original.
|
|
||||||
[class*='__type-icon'] {
|
|
||||||
&:after {
|
|
||||||
color: $colorIconAlias;
|
|
||||||
content: $glyph-icon-link;
|
|
||||||
font-family: symbolsfont;
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
text-shadow: rgba(black, 0.5) 0 1px 2px;
|
|
||||||
top: auto; left: -1px; bottom: 1px; right: auto;
|
|
||||||
transform-origin: bottom left;
|
|
||||||
transform: scale(0.65);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/******************************* LIST ITEM */
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
import lodash from 'lodash';
|
import lodash from 'lodash';
|
||||||
import moment from 'moment';
|
|
||||||
import compositionLoader from './composition-loader';
|
import compositionLoader from './composition-loader';
|
||||||
|
import ListItem from './ListItem.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: {ListItem},
|
||||||
mixins: [compositionLoader],
|
mixins: [compositionLoader],
|
||||||
inject: ['domainObject', 'openmct'],
|
inject: ['domainObject', 'openmct'],
|
||||||
data() {
|
data() {
|
||||||
@ -156,15 +114,6 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
formatTime(timestamp, format) {
|
|
||||||
return moment(timestamp).format(format);
|
|
||||||
},
|
|
||||||
navigate(item) {
|
|
||||||
let currentLocation = this.openmct.router.currentLocation.path,
|
|
||||||
navigateToPath = `${currentLocation}/${this.openmct.objects.makeKeyString(item.model.identifier)}`;
|
|
||||||
|
|
||||||
this.openmct.router.setPath(navigateToPath);
|
|
||||||
},
|
|
||||||
sort(field, defaultDirection) {
|
sort(field, defaultDirection) {
|
||||||
if (this.sortBy === field) {
|
if (this.sortBy === field) {
|
||||||
this.ascending = !this.ascending;
|
this.ascending = !this.ascending;
|
||||||
|
@ -31,16 +31,19 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
add(child, index, anything) {
|
add(child, index, anything) {
|
||||||
var type = this.openmct.types.get(child.type) || unknownObjectType;
|
var type = this.openmct.types.get(child.type) || unknownObjectType;
|
||||||
|
|
||||||
this.items.push({
|
this.items.push({
|
||||||
model: child,
|
model: child,
|
||||||
type: type.definition,
|
type: type.definition,
|
||||||
isAlias: this.domainObject.identifier.key !== child.location
|
isAlias: this.domainObject.identifier.key !== child.location,
|
||||||
|
objectPath: [child].concat(openmct.router.path)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
remove(child) {
|
remove(identifier) {
|
||||||
// TODO: implement remove action
|
this.items = this.items
|
||||||
console.log('remove child? might be identifier');
|
.filter((i) => {
|
||||||
|
return i.model.identifier.key !== identifier.key
|
||||||
|
|| i.model.identifier.namespace !== identifier.namespace
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,51 +2,37 @@
|
|||||||
<a class="c-tree__item__label"
|
<a class="c-tree__item__label"
|
||||||
draggable="true"
|
draggable="true"
|
||||||
@dragstart="dragStart"
|
@dragstart="dragStart"
|
||||||
:href="urlLink">
|
:href="objectLink">
|
||||||
<div class="c-tree__item__type-icon"
|
<div class="c-tree__item__type-icon"
|
||||||
:class="cssClass"></div>
|
:class="typeClass"></div>
|
||||||
<div class="c-tree__item__name">{{ domainObject.name }}</div>
|
<div class="c-tree__item__name">{{ domainObject.name }}</div>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
import ContextMenu from '../mixins/context-menu';
|
||||||
|
import ObjectLink from '../mixins/object-link';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
mixins: [ContextMenu, ObjectLink],
|
||||||
inject: ['openmct'],
|
inject: ['openmct'],
|
||||||
props: {
|
props: {
|
||||||
'domainObject': Object,
|
'domainObject': Object,
|
||||||
'path': Array
|
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
urlLink() {
|
typeClass() {
|
||||||
if (!this.path) {
|
let type = this.openmct.types.get(this.domainObject.type);
|
||||||
return;
|
if (!type) {
|
||||||
|
return 'icon-object-unknown';
|
||||||
}
|
}
|
||||||
return '#/browse/' + this.path
|
return type.definition.cssClass;
|
||||||
.map(o => this.openmct.objects.makeKeyString(o))
|
|
||||||
.join('/');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
cssClass: 'icon-object-unknown'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
let type = this.openmct.types.get(this.domainObject.type);
|
|
||||||
|
|
||||||
if (type.definition.cssClass) {
|
|
||||||
this.cssClass = type.definition.cssClass;
|
|
||||||
} else {
|
|
||||||
console.log("Failed to get typeDef.cssClass for object", this.domainObject.name, this.domainObject.type);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
dragStart(event) {
|
dragStart(event) {
|
||||||
event.dataTransfer.setData("domainObject", JSON.stringify(this.domainObject));
|
event.dataTransfer.setData("domainObject", JSON.stringify(this.domainObject));
|
||||||
}
|
}
|
||||||
},
|
|
||||||
destroyed() {
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -114,7 +114,7 @@
|
|||||||
return {
|
return {
|
||||||
id: this.openmct.objects.makeKeyString(c.identifier),
|
id: this.openmct.objects.makeKeyString(c.identifier),
|
||||||
object: c,
|
object: c,
|
||||||
path: [c.identifier]
|
objectPath: [c]
|
||||||
};
|
};
|
||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
|
@ -7,7 +7,9 @@
|
|||||||
:expanded="expanded"
|
:expanded="expanded"
|
||||||
@click="toggleChildren">
|
@click="toggleChildren">
|
||||||
</view-control>
|
</view-control>
|
||||||
<object-label :domainObject="node.object" :path="node.path"></object-label>
|
<object-label :domainObject="node.object"
|
||||||
|
:objectPath="node.objectPath">
|
||||||
|
</object-label>
|
||||||
</div>
|
</div>
|
||||||
<ul v-if="expanded" class="c-tree">
|
<ul v-if="expanded" class="c-tree">
|
||||||
<tree-item v-for="child in children"
|
<tree-item v-for="child in children"
|
||||||
@ -76,12 +78,13 @@
|
|||||||
this.children.push({
|
this.children.push({
|
||||||
id: this.openmct.objects.makeKeyString(child.identifier),
|
id: this.openmct.objects.makeKeyString(child.identifier),
|
||||||
object: child,
|
object: child,
|
||||||
path: this.node.path.concat([child.identifier])
|
objectPath: [child].concat(this.node.objectPath)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
removeChild(child) {
|
removeChild(identifier) {
|
||||||
// TODO: remove child on remove event.
|
let removeId = this.openmct.objects.makeKeyString(identifier);
|
||||||
console.log('Tree should remove child', child);
|
this.children = this.children
|
||||||
|
.filter(c => c.id !== removeId);
|
||||||
},
|
},
|
||||||
finishLoading () {
|
finishLoading () {
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
|
38
src/ui/components/mixins/context-menu.js
Normal file
38
src/ui/components/mixins/context-menu.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
export default {
|
||||||
|
inject: ['openmct'],
|
||||||
|
props: {
|
||||||
|
'objectPath': {
|
||||||
|
type: Array,
|
||||||
|
default() {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
// TODO: handle mobile contet menu listeners.
|
||||||
|
this.$el.addEventListener('contextmenu', this.showContextMenu);
|
||||||
|
this.objectPath.forEach((o, i) => {
|
||||||
|
let removeListener = this.openmct.objects.observe(
|
||||||
|
o,
|
||||||
|
'*',
|
||||||
|
(newDomainObject) => {
|
||||||
|
this.objectPath.splice(i, 1, newDomainObject);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.$once('hook:destroyed', removeListener);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
destroyed() {
|
||||||
|
this.$el.removeEventListener('contextmenu', this.showContextMenu);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
showContextMenu(event) {
|
||||||
|
let legacyObject = this.openmct.legacyObject(this.objectPath);
|
||||||
|
legacyObject.getCapability('action').perform({
|
||||||
|
key: 'menu',
|
||||||
|
domainObject: legacyObject,
|
||||||
|
event: event
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
22
src/ui/components/mixins/object-link.js
Normal file
22
src/ui/components/mixins/object-link.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
export default {
|
||||||
|
inject: ['openmct'],
|
||||||
|
props: {
|
||||||
|
'objectPath': {
|
||||||
|
type: Array,
|
||||||
|
default() {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
objectLink() {
|
||||||
|
if (!this.objectPath.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return '#/browse/' + this.objectPath
|
||||||
|
.map(o => this.openmct.objects.makeKeyString(o.identifier))
|
||||||
|
.reverse()
|
||||||
|
.join('/');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
Reference in New Issue
Block a user