Compare commits

...

6 Commits

Author SHA1 Message Date
0a820aa869 merge latest tcr 2019-08-12 12:41:33 -07:00
7c92b6d206 Continued work on Notebook svc 2019-08-12 12:40:07 -07:00
395a1caeec improvement to new notebook entry 2019-04-02 10:55:53 -07:00
339b315642 working embeds 2019-04-01 14:52:00 -07:00
601d59d6bd merge with tcr 2019-04-01 13:34:52 -07:00
c6e2d5a363 working new entry and delete entry 2019-02-04 18:43:10 -08:00
10 changed files with 416 additions and 7 deletions

View File

@ -78,7 +78,7 @@
]
}));
openmct.install(openmct.plugins.SummaryWidget());
openmct.install(openmct.plugins.Notebook());
openmct.install(openmct.plugins.NotebookSVC());
openmct.install(openmct.plugins.Filters(['table', 'telemetry.plot.overlay']));
openmct.install(openmct.plugins.ObjectMigration());
openmct.install(openmct.plugins.ClearData(['table', 'telemetry.plot.overlay', 'telemetry.plot.stacked']));

View File

@ -0,0 +1,31 @@
<template>
<div class="c-ne__embed">
<div class="c-ne__embed__snap-thumb"
v-if="embed.snapshot">
<img v-bind:src="embed.snapshot.src">
</div>
<div class="c-ne__embed__info">
<div class="c-ne__embed__name">
<a class="c-ne__embed__link"
:href="objectLink"
:class="embed.cssClass">
{{embed.name}}
</a>
<a class="c-ne__embed__context-available icon-arrow-down"></a>
</div>
<div class="c-ne__embed__time" v-if="embed.snapshot">
{{formatTime(embed.createdOn, 'YYYY-MM-DD HH:mm:ss')}}
</div>
</div>
</div>
</template>
<script>
import objectLinkMixin from '../../../ui/mixins/object-link';
export default {
inject: ['openmct', 'formatTime'],
props: ['embed'],
mixins: [objectLinkMixin]
}
</script>

View File

@ -0,0 +1,63 @@
<template>
<li class="c-notebook__entry c-ne has-local-controls"
@dragover.prevent
@drop.prevent="onTreeItemDrop">
<div class="c-ne__time-and-content">
<div class="c-ne__time">
<span>{{formatTime(entry.createdOn, 'YYYY-MM-DD')}}</span>
<span>{{formatTime(entry.createdOn, 'HH:mm:ss')}}</span>
</div>
<div class="c-ne__content">
<div class="c-ne__text c-input-inline"
contenteditable="true"
ref="contenteditable"
@blur="updateEntry"
v-html="entry.text">
</div>
<div class="c-ne__embeds">
<notebook-embed
v-for="embed in entry.embeds"
:key="embed.id"
:embed="embed"
:objectPath="embed.objectPath">
</notebook-embed>
</div>
</div>
</div>
<div class="c-ne__local-controls--hidden">
<button class="c-click-icon c-click-icon--major icon-trash"
title="Delete this entry"
@click="deleteEntry">
</button>
</div>
</li>
</template>
<script>
import NotebookEmbed from './embed.vue';
export default {
inject: ['formatTime'],
props: ['entry'],
components: {
NotebookEmbed
},
methods: {
updateEntry(event) {
this.$emit('update-entry', this.entry.id, event.target.innerText);
},
deleteEntry() {
this.$emit('delete-entry', this.entry.id);
},
onTreeItemDrop(event) {
this.$emit('drop-embed', this.entry.id, event);
}
},
mounted() {
if (!this.entry.text && !this.entry.embeds.length) {
this.$refs.contenteditable.focus();
}
}
}
</script>

View File

@ -0,0 +1,198 @@
<template>
<div class="c-notebook"
@dragover.stop
@drop.stop>
<div class="c-notebook__head">
<search class="c-notebook__search"
:value="searchValue"
@input="searchEntries"
@clear="searchEntries">
</search>
<div class="c-notebook__controls ">
<select class="c-notebook__controls__time" v-model="timeFrame">
<option value="0" :selected="timeFrame==='0'">Show all</option>
<option value="1" :selected="timeFrame==='1'">Last hour</option>
<option value="8" :selected="timeFrame==='8'">Last 8 hours</option>
<option value="24" :selected="timeFrame==='24'">Last 24 hours</option>
</select>
<select class="c-notebook__controls__time" v-model="sortOrder">
<option value="newest" :selected="sortOrder === 'newest'">Newest first</option>
<option value="oldest" :selected="sortOrder === 'oldest'">Oldest first</option>
</select>
</div>
</div>
<div class="c-notebook__drag-area icon-plus"
@click="newEntry">
<span class="c-notebook__drag-area__label">To start a new entry, click here or drag and drop any object</span>
</div>
<div class="c-notebook__entries">
<ul>
<entry
v-for="entry in entries"
:key="entry.id"
:entry="entry"
@update-entry="persistEntry"
@delete-entry="deleteEntry"
@drop-embed="addEmbed">
</entry>
</ul>
</div>
</div>
</template>
<style lang="scss">
</style>
<script>
import Search from '../../../ui/components/search.vue';
import Entry from './entry.vue';
import Moment from 'moment';
import searchVue from '../../../ui/components/search.vue';
function formatTime(unixTime, format) {
return Moment(unixTime).format(format)
}
export default {
inject: ['openmct', 'providedDomainObject'],
components: {
Search,
Entry
},
provide: {
formatTime
},
data() {
return {
domainObject: this.providedDomainObject,
timeFrame: '0',
searchValue: '',
sortOrder: this.providedDomainObject.configuration.sortOrder
}
},
methods: {
searchEntries(value) {
this.searchValue = value;
},
updateDomainObject(domainObject) {
this.domainObject = domainObject;
},
newEntry() {
this.searchValue = '';
let date = Date.now(),
entry = {'id': 'entry-' + date, 'createdOn': date, 'embeds':[]},
entries = this.domainObject.entries,
lastEntry = entries[entries.length - 1];
if (lastEntry && !lastEntry.text && !lastEntry.embeds.length) {
let lastEntryComponent = this.$children.find(entryComponent => {
if (entryComponent.entry) {
return entryComponent.entry.id === lastEntry.id;
}
});
lastEntryComponent.$refs.contenteditable.focus();
} else {
entries.push(entry);
this.openmct.objects.mutate(this.domainObject, 'entries', entries);
}
},
findEntry(entryId) {
return this.domainObject.entries.findIndex(entry => {
return entry.id === entryId;
})
},
persistEntry(entryId, text) {
let entryPos = this.findEntry(entryId);
this.openmct.objects.mutate(this.domainObject, `entries[${entryPos}].text`, text);
},
deleteEntry(entryId) {
let entryPos = this.findEntry(entryId),
entries = this.domainObject.entries;
if (entryPos !== -1) {
let dialog = this.openmct.overlays.dialog({
iconClass: 'alert',
message: 'This action will permanently delete this entry. Do you wish to continue?',
buttons: [
{
label: "Ok",
emphasis: true,
callback: () => {
entries.splice(entryPos, 1);
this.openmct.objects.mutate(this.domainObject, 'entries', entries);
dialog.dismiss();
}
},
{
label: "Cancel",
callback: () => {
dialog.dismiss();
}
}
]
});
}
},
applySearch(entries) {
return entries.filter((entry) => {
if (entry.text.includes(this.searchValue)) {
return entry;
}
});
},
addEmbed(entryId, event) {
var data = event.dataTransfer.getData('openmct/domain-object-path');
if (data) {
var objectPath = JSON.parse(data),
domainObject = objectPath[0],
domainObjectKey = domainObject.identifier.key,
domainObjectType = this.openmct.types.get(domainObject.type),
cssClass = domainObjectType && domainObjectType.definition ?
domainObjectType.definition.cssClass : 'icon-object-unknown',
entryPos = this.findEntry(entryId),
currentEntryEmbeds = this.domainObject.entries[entryPos].embeds,
newEmbed = {
id: '' + Date.now(),
domainObject: domainObject,
objectPath: objectPath,
type: domainObjectKey,
cssClass: cssClass,
name: domainObject.name,
snapshot: ''
};
currentEntryEmbeds.push(newEmbed);
this.openmct.objects.mutate(this.domainObject, 'entries[' + entryPos + '].embeds', currentEntryEmbeds);
}
},
onDrop() {
console.log('droped');
}
},
computed: {
entries() {
let entries = [...this.domainObject.entries];
if (this.searchValue !== '') {
entries = this.applySearch(entries);
}
if (this.sortOrder === 'newest') {
return entries.reverse();
} else {
return entries;
}
}
},
mounted() {
this.openmct.objects.observe(this.domainObject, '*', this.updateDomainObject);
}
}
</script>

View File

@ -0,0 +1,67 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is 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.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([
'vue',
'./components/notebook.vue'
], function (
Vue,
Notebook
) {
function NotebookViewProvider(openmct) {
return {
key: 'notebook',
name: 'Notebook',
cssClass: 'icon-notebook',
canView: function (domainObject) {
return domainObject.type === 'notebook';
},
view: function (domainObject) {
let component;
return {
show: function (element) {
component = new Vue({
components: {
Notebook: Notebook.default
},
provide: {
openmct,
providedDomainObject: domainObject
},
el: element,
template: '<notebook></notebook>'
});
},
destroy: function (element) {
component.$destroy();
component = undefined;
}
};
},
priority: function () {
return 1;
}
};
}
return NotebookViewProvider;
});

View File

@ -0,0 +1,47 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is 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.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([
'./notebook-view-provider'
], function (
NotebookViewProvider
) {
return function plugin() {
return function install(openmct) {
openmct.objectViews.addProvider(new NotebookViewProvider(openmct));
openmct.types.addType('notebook', {
name: "Notebook SVC",
creatable: true,
description: "Create and save timestamped notes with embedded object snapshots.",
cssClass: 'icon-notebook',
initialize: function (domainObject) {
domainObject.configuration = {
sortOrder: 'oldest'
};
domainObject.entries = [];
}
});
};
};
});

View File

@ -35,7 +35,7 @@ define([
installed = true;
openmct.legacyRegistry.register('notebook', {
openmct.legacyRegistry.register('notebook-old', {
name: 'Notebook Plugin',
extensions: {
types: [
@ -73,10 +73,10 @@ define([
}
});
openmct.legacyRegistry.enable('notebook');
openmct.legacyRegistry.enable('notebook-old');
openmct.objectViews.addProvider({
key: 'notebook-vue',
key: 'notebook-old',
name: 'Notebook View',
cssClass: 'icon-notebook',
canView: function (domainObject) {

View File

@ -41,6 +41,7 @@ define([
'./flexibleLayout/plugin',
'./tabs/plugin',
'./LADTable/plugin',
'./notebook-svc/plugin',
'./filters/plugin',
'./objectMigration/plugin',
'./goToOriginalAction/plugin',
@ -66,6 +67,7 @@ define([
FlexibleLayout,
Tabs,
LADTable,
NotebookSVC,
Filters,
ObjectMigration,
GoToOriginalAction,
@ -159,12 +161,13 @@ define([
plugins.SummaryWidget = SummaryWidget;
plugins.TelemetryMean = TelemetryMean;
plugins.URLIndicator = URLIndicatorPlugin;
plugins.Notebook = Notebook;
// plugins.Notebook = Notebook;
plugins.DisplayLayout = DisplayLayoutPlugin.default;
plugins.FolderView = FolderView;
plugins.Tabs = Tabs;
plugins.FlexibleLayout = FlexibleLayout;
plugins.LADTable = LADTable;
plugins.NotebookSVC = NotebookSVC;
plugins.Filters = Filters;
plugins.ObjectMigration = ObjectMigration.default;
plugins.GoToOriginalAction = GoToOriginalAction.default;

View File

@ -111,7 +111,7 @@ export default {
event.dataTransfer.setData("openmct/composable-domain-object", JSON.stringify(this.domainObject));
}
// serialize domain object anyway, because some views can drag-and-drop objects without composition
// (eg. notabook.)
// (eg. notebook.)
event.dataTransfer.setData("openmct/domain-object-path", serializedPath);
event.dataTransfer.setData(`openmct/domain-object/${keyString}`, this.domainObject);
}

View File

@ -90,7 +90,7 @@ class NotebookSnapshot {
};
function validateLocation(newParentObj) {
return newParentObj.model.type === 'notebook';
return newParentObj.model.type === 'notebook' || newParentObj.model.type === 'notebook-svc';
}
return overlayModel;