mirror of
https://github.com/nasa/openmct.git
synced 2025-06-25 10:44:21 +00:00
Compare commits
6 Commits
vue-3
...
notebook-s
Author | SHA1 | Date | |
---|---|---|---|
0a820aa869 | |||
7c92b6d206 | |||
395a1caeec | |||
339b315642 | |||
601d59d6bd | |||
c6e2d5a363 |
@ -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']));
|
||||
|
31
src/plugins/notebook-svc/components/embed.vue
Normal file
31
src/plugins/notebook-svc/components/embed.vue
Normal 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>
|
63
src/plugins/notebook-svc/components/entry.vue
Normal file
63
src/plugins/notebook-svc/components/entry.vue
Normal 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>
|
198
src/plugins/notebook-svc/components/notebook.vue
Normal file
198
src/plugins/notebook-svc/components/notebook.vue
Normal 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>
|
67
src/plugins/notebook-svc/notebook-view-provider.js
Normal file
67
src/plugins/notebook-svc/notebook-view-provider.js
Normal 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;
|
||||
});
|
47
src/plugins/notebook-svc/plugin.js
Normal file
47
src/plugins/notebook-svc/plugin.js
Normal 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 = [];
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
});
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
Reference in New Issue
Block a user