mirror of
https://github.com/nasa/openmct.git
synced 2025-06-14 21:28:12 +00:00
Three Dot Menu Prototype (#3325)
* Three dot menu implementation Co-authored-by: Deep Tailor <deep.j.tailor@nasa.gov> Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
This commit is contained in:
committed by
GitHub
parent
d232dacc65
commit
6375ecda34
40
src/plugins/notebook/actions/CopyToNotebookAction.js
Normal file
40
src/plugins/notebook/actions/CopyToNotebookAction.js
Normal file
@ -0,0 +1,40 @@
|
||||
import { getDefaultNotebook } from '../utils/notebook-storage';
|
||||
import { addNotebookEntry } from '../utils/notebook-entries';
|
||||
|
||||
export default class CopyToNotebookAction {
|
||||
constructor(openmct) {
|
||||
this.openmct = openmct;
|
||||
|
||||
this.cssClass = 'icon-duplicate';
|
||||
this.description = 'Copy value to notebook as an entry';
|
||||
this.group = "action";
|
||||
this.key = 'copyToNotebook';
|
||||
this.name = 'Copy to Notebook';
|
||||
this.priority = 1;
|
||||
}
|
||||
|
||||
copyToNotebook(entryText) {
|
||||
const notebookStorage = getDefaultNotebook();
|
||||
this.openmct.objects.get(notebookStorage.notebookMeta.identifier)
|
||||
.then(domainObject => {
|
||||
addNotebookEntry(this.openmct, domainObject, notebookStorage, null, entryText);
|
||||
|
||||
const defaultPath = `${domainObject.name} - ${notebookStorage.section.name} - ${notebookStorage.page.name}`;
|
||||
const msg = `Saved to Notebook ${defaultPath}`;
|
||||
this.openmct.notifications.info(msg);
|
||||
});
|
||||
}
|
||||
|
||||
invoke(objectPath, view = {}) {
|
||||
let viewContext = view.getViewContext && view.getViewContext();
|
||||
|
||||
this.copyToNotebook(viewContext.formattedValueForCopy());
|
||||
}
|
||||
|
||||
appliesTo(objectPath, view = {}) {
|
||||
let viewContext = view.getViewContext && view.getViewContext();
|
||||
|
||||
return viewContext && viewContext.formattedValueForCopy
|
||||
&& typeof viewContext.formattedValueForCopy === 'function';
|
||||
}
|
||||
}
|
@ -111,7 +111,7 @@ import Search from '@/ui/components/search.vue';
|
||||
import SearchResults from './SearchResults.vue';
|
||||
import Sidebar from './Sidebar.vue';
|
||||
import { clearDefaultNotebook, getDefaultNotebook, setDefaultNotebook, setDefaultNotebookSection, setDefaultNotebookPage } from '../utils/notebook-storage';
|
||||
import { DEFAULT_CLASS, addNotebookEntry, createNewEmbed, getNotebookEntries, mutateObject } from '../utils/notebook-entries';
|
||||
import { addNotebookEntry, createNewEmbed, getNotebookEntries, mutateObject } from '../utils/notebook-entries';
|
||||
import objectUtils from 'objectUtils';
|
||||
|
||||
import { throttle } from 'lodash';
|
||||
@ -431,14 +431,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
const classList = domainObject.classList || [];
|
||||
const index = classList.indexOf(DEFAULT_CLASS);
|
||||
if (!classList.length || index < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
classList.splice(index, 1);
|
||||
mutateObject(this.openmct, domainObject, 'classList', classList);
|
||||
this.openmct.status.delete(domainObject.identifier);
|
||||
},
|
||||
searchItem(input) {
|
||||
this.search = input;
|
||||
|
@ -1,29 +1,17 @@
|
||||
<template>
|
||||
<div class="c-menu-button c-ctrl-wrapper c-ctrl-wrapper--menus-left">
|
||||
<button
|
||||
class="c-button--menu icon-notebook"
|
||||
class="c-icon-button c-button--menu icon-camera"
|
||||
title="Take a Notebook Snapshot"
|
||||
@click="setNotebookTypes"
|
||||
@click.stop="toggleMenu"
|
||||
@click.stop.prevent="showMenu"
|
||||
>
|
||||
<span class="c-button__label"></span>
|
||||
<span
|
||||
title="Take Notebook Snapshot"
|
||||
class="c-icon-button__label"
|
||||
>
|
||||
Snapshot
|
||||
</span>
|
||||
</button>
|
||||
<div
|
||||
v-show="showMenu"
|
||||
class="c-menu"
|
||||
>
|
||||
<ul>
|
||||
<li
|
||||
v-for="(type, index) in notebookTypes"
|
||||
:key="index"
|
||||
:class="type.cssClass"
|
||||
:title="type.name"
|
||||
@click="snapshot(type)"
|
||||
>
|
||||
{{ type.name }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -57,22 +45,20 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
notebookSnapshot: null,
|
||||
notebookTypes: [],
|
||||
showMenu: false
|
||||
notebookTypes: []
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.notebookSnapshot = new Snapshot(this.openmct);
|
||||
|
||||
document.addEventListener('click', this.hideMenu);
|
||||
},
|
||||
destroyed() {
|
||||
document.removeEventListener('click', this.hideMenu);
|
||||
this.setDefaultNotebookStatus();
|
||||
},
|
||||
methods: {
|
||||
setNotebookTypes() {
|
||||
showMenu(event) {
|
||||
const notebookTypes = [];
|
||||
const defaultNotebook = getDefaultNotebook();
|
||||
const elementBoundingClientRect = this.$el.getBoundingClientRect();
|
||||
const x = elementBoundingClientRect.x;
|
||||
const y = elementBoundingClientRect.y + elementBoundingClientRect.height;
|
||||
|
||||
if (defaultNotebook) {
|
||||
const domainObject = defaultNotebook.domainObject;
|
||||
@ -83,28 +69,24 @@ export default {
|
||||
notebookTypes.push({
|
||||
cssClass: 'icon-notebook',
|
||||
name: `Save to Notebook ${defaultPath}`,
|
||||
type: NOTEBOOK_DEFAULT
|
||||
callBack: () => {
|
||||
return this.snapshot(NOTEBOOK_DEFAULT);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
notebookTypes.push({
|
||||
cssClass: 'icon-notebook',
|
||||
cssClass: 'icon-camera',
|
||||
name: 'Save to Notebook Snapshots',
|
||||
type: NOTEBOOK_SNAPSHOT
|
||||
callBack: () => {
|
||||
return this.snapshot(NOTEBOOK_SNAPSHOT);
|
||||
}
|
||||
});
|
||||
|
||||
this.notebookTypes = notebookTypes;
|
||||
},
|
||||
toggleMenu() {
|
||||
this.showMenu = !this.showMenu;
|
||||
},
|
||||
hideMenu() {
|
||||
this.showMenu = false;
|
||||
this.openmct.menus.showMenu(x, y, notebookTypes);
|
||||
},
|
||||
snapshot(notebook) {
|
||||
this.hideMenu();
|
||||
|
||||
this.$nextTick(() => {
|
||||
const element = document.querySelector('.c-overlay__contents')
|
||||
|| document.getElementsByClassName('l-shell__main-container')[0];
|
||||
@ -124,6 +106,15 @@ export default {
|
||||
|
||||
this.notebookSnapshot.capture(snapshotMeta, notebook.type, element);
|
||||
});
|
||||
},
|
||||
setDefaultNotebookStatus() {
|
||||
let defaultNotebookObject = getDefaultNotebook();
|
||||
|
||||
if (defaultNotebookObject && defaultNotebookObject.notebookMeta) {
|
||||
let notebookIdentifier = defaultNotebookObject.notebookMeta.identifier;
|
||||
|
||||
this.openmct.status.set(notebookIdentifier, 'notebook-default');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -4,7 +4,7 @@
|
||||
<div class="l-browse-bar__start">
|
||||
<div class="l-browse-bar__object-name--w">
|
||||
<div class="l-browse-bar__object-name c-object-label">
|
||||
<div class="c-object-label__type-icon icon-notebook"></div>
|
||||
<div class="c-object-label__type-icon icon-camera"></div>
|
||||
<div class="c-object-label__name">
|
||||
Notebook Snapshots
|
||||
<span v-if="snapshots.length"
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="c-indicator c-indicator--clickable icon-notebook"
|
||||
<div class="c-indicator c-indicator--clickable icon-camera"
|
||||
:class="[
|
||||
{ 's-status-off': snapshotCount === 0 },
|
||||
{ 's-status-on': snapshotCount > 0 },
|
||||
|
@ -1,3 +1,4 @@
|
||||
import CopyToNotebookAction from './actions/CopyToNotebookAction';
|
||||
import Notebook from './components/Notebook.vue';
|
||||
import NotebookSnapshotIndicator from './components/NotebookSnapshotIndicator.vue';
|
||||
import SnapshotContainer from './snapshot-container';
|
||||
@ -13,6 +14,8 @@ export default function NotebookPlugin() {
|
||||
|
||||
installed = true;
|
||||
|
||||
openmct.actions.register(new CopyToNotebookAction(openmct));
|
||||
|
||||
const notebookType = {
|
||||
name: 'Notebook',
|
||||
description: 'Create and save timestamped notes with embedded object snapshots.',
|
||||
|
@ -20,7 +20,7 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import { createOpenMct, createMouseEvent, resetApplicationState } from 'utils/testing';
|
||||
import { createOpenMct, resetApplicationState } from 'utils/testing';
|
||||
import NotebookPlugin from './plugin';
|
||||
import Vue from 'vue';
|
||||
|
||||
@ -133,90 +133,4 @@ describe("Notebook plugin:", () => {
|
||||
expect(hasMajorElements).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Notebook Snapshots view:", () => {
|
||||
let snapshotIndicator;
|
||||
let drawerElement;
|
||||
|
||||
function clickSnapshotIndicator() {
|
||||
const indicator = element.querySelector('.icon-notebook');
|
||||
const button = indicator.querySelector('button');
|
||||
const clickEvent = createMouseEvent('click');
|
||||
|
||||
button.dispatchEvent(clickEvent);
|
||||
}
|
||||
|
||||
beforeAll(() => {
|
||||
snapshotIndicator = openmct.indicators.indicatorObjects
|
||||
.find(indicator => indicator.key === 'notebook-snapshot-indicator').element;
|
||||
|
||||
element.append(snapshotIndicator);
|
||||
|
||||
return Vue.nextTick();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
snapshotIndicator.remove();
|
||||
if (drawerElement) {
|
||||
drawerElement.remove();
|
||||
}
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
drawerElement = document.querySelector('.l-shell__drawer');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
if (drawerElement) {
|
||||
drawerElement.classList.remove('is-expanded');
|
||||
}
|
||||
});
|
||||
|
||||
it("has Snapshots indicator", () => {
|
||||
const hasSnapshotIndicator = snapshotIndicator !== null && snapshotIndicator !== undefined;
|
||||
expect(hasSnapshotIndicator).toBe(true);
|
||||
});
|
||||
|
||||
it("snapshots container has class isExpanded", () => {
|
||||
let classes = drawerElement.classList;
|
||||
const isExpandedBefore = classes.contains('is-expanded');
|
||||
|
||||
clickSnapshotIndicator();
|
||||
classes = drawerElement.classList;
|
||||
const isExpandedAfterFirstClick = classes.contains('is-expanded');
|
||||
|
||||
const success = isExpandedBefore === false
|
||||
&& isExpandedAfterFirstClick === true;
|
||||
|
||||
expect(success).toBe(true);
|
||||
});
|
||||
|
||||
it("snapshots container does not have class isExpanded", () => {
|
||||
let classes = drawerElement.classList;
|
||||
const isExpandedBefore = classes.contains('is-expanded');
|
||||
|
||||
clickSnapshotIndicator();
|
||||
classes = drawerElement.classList;
|
||||
const isExpandedAfterFirstClick = classes.contains('is-expanded');
|
||||
|
||||
clickSnapshotIndicator();
|
||||
classes = drawerElement.classList;
|
||||
const isExpandedAfterSecondClick = classes.contains('is-expanded');
|
||||
|
||||
const success = isExpandedBefore === false
|
||||
&& isExpandedAfterFirstClick === true
|
||||
&& isExpandedAfterSecondClick === false;
|
||||
|
||||
expect(success).toBe(true);
|
||||
});
|
||||
|
||||
it("show notebook snapshots container text", () => {
|
||||
clickSnapshotIndicator();
|
||||
|
||||
const notebookSnapshots = drawerElement.querySelector('.l-browse-bar__object-name');
|
||||
const snapshotsText = notebookSnapshots.textContent.trim();
|
||||
|
||||
expect(snapshotsText).toBe('Notebook Snapshots');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -49,7 +49,7 @@ export default class Snapshot {
|
||||
.then(domainObject => {
|
||||
addNotebookEntry(this.openmct, domainObject, notebookStorage, embed);
|
||||
|
||||
const defaultPath = `${domainObject.name} > ${notebookStorage.section.name} > ${notebookStorage.page.name}`;
|
||||
const defaultPath = `${domainObject.name} - ${notebookStorage.section.name} - ${notebookStorage.page.name}`;
|
||||
const msg = `Saved to Notebook ${defaultPath}`;
|
||||
this._showNotification(msg);
|
||||
});
|
||||
|
@ -1,5 +1,6 @@
|
||||
import objectLink from '../../../ui/mixins/object-link';
|
||||
export const DEFAULT_CLASS = 'is-notebook-default';
|
||||
|
||||
export const DEFAULT_CLASS = 'notebook-default';
|
||||
const TIME_BOUNDS = {
|
||||
START_BOUND: 'tc.startBound',
|
||||
END_BOUND: 'tc.endBound',
|
||||
@ -96,7 +97,7 @@ export function createNewEmbed(snapshotMeta, snapshot = '') {
|
||||
};
|
||||
}
|
||||
|
||||
export function addNotebookEntry(openmct, domainObject, notebookStorage, embed = null) {
|
||||
export function addNotebookEntry(openmct, domainObject, notebookStorage, embed = null, entryText = '') {
|
||||
if (!openmct || !domainObject || !notebookStorage) {
|
||||
return;
|
||||
}
|
||||
@ -117,14 +118,14 @@ export function addNotebookEntry(openmct, domainObject, notebookStorage, embed =
|
||||
const entry = {
|
||||
id,
|
||||
createdOn: date,
|
||||
text: '',
|
||||
text: entryText,
|
||||
embeds
|
||||
};
|
||||
|
||||
const newEntries = addEntryIntoPage(notebookStorage, entries, entry);
|
||||
|
||||
addDefaultClass(domainObject);
|
||||
mutateObject(openmct, domainObject, 'configuration.entries', newEntries);
|
||||
addDefaultClass(domainObject, openmct);
|
||||
openmct.objects.mutate(domainObject, 'configuration.entries', newEntries);
|
||||
|
||||
return id;
|
||||
}
|
||||
@ -197,13 +198,6 @@ export function mutateObject(openmct, object, key, value) {
|
||||
openmct.objects.mutate(object, key, value);
|
||||
}
|
||||
|
||||
function addDefaultClass(domainObject) {
|
||||
const classList = domainObject.classList || [];
|
||||
if (classList.includes(DEFAULT_CLASS)) {
|
||||
return;
|
||||
}
|
||||
|
||||
classList.push(DEFAULT_CLASS);
|
||||
|
||||
domainObject.classList = classList;
|
||||
function addDefaultClass(domainObject, openmct) {
|
||||
openmct.status.set(domainObject.identifier, DEFAULT_CLASS);
|
||||
}
|
||||
|
Reference in New Issue
Block a user