mirror of
https://github.com/nasa/openmct.git
synced 2025-01-31 08:25:31 +00:00
Notebook context menu (#2888)
Notebook popup menu fix Co-authored-by: charlesh88 <charlesh88@gmail.com> Co-authored-by: Andrew Henry <akhenry@gmail.com>
This commit is contained in:
parent
99c095a69f
commit
da7b93f9b3
21
src/plugins/notebook/components/menu-items.vue
Normal file
21
src/plugins/notebook/components/menu-items.vue
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<template>
|
||||||
|
<div class="c-menu">
|
||||||
|
<ul>
|
||||||
|
<li
|
||||||
|
v-for="(item, index) in popupMenuItems"
|
||||||
|
:key="index"
|
||||||
|
:class="item.cssClass"
|
||||||
|
:title="item.name"
|
||||||
|
@click="item.callback"
|
||||||
|
>
|
||||||
|
{{ item.name }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
inject: ['popupMenuItems']
|
||||||
|
}
|
||||||
|
</script>
|
@ -12,24 +12,7 @@
|
|||||||
:class="embed.cssClass"
|
:class="embed.cssClass"
|
||||||
@click="changeLocation"
|
@click="changeLocation"
|
||||||
>{{ embed.name }}</a>
|
>{{ embed.name }}</a>
|
||||||
<a class="c-ne__embed__context-available icon-arrow-down"
|
<PopupMenu :popup-menu-items="popupMenuItems" />
|
||||||
@click="toggleActionMenu"
|
|
||||||
></a>
|
|
||||||
</div>
|
|
||||||
<div class="hide-menu hidden">
|
|
||||||
<div class="menu-element context-menu-wrapper mobile-disable-select">
|
|
||||||
<div class="c-menu">
|
|
||||||
<ul>
|
|
||||||
<li v-for="action in actions"
|
|
||||||
:key="action.name"
|
|
||||||
:class="action.cssClass"
|
|
||||||
@click="action.perform(embed)"
|
|
||||||
>
|
|
||||||
{{ action.name }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-if="embed.snapshot"
|
<div v-if="embed.snapshot"
|
||||||
class="c-ne__embed__time"
|
class="c-ne__embed__time"
|
||||||
@ -42,15 +25,17 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Moment from 'moment';
|
import Moment from 'moment';
|
||||||
|
import PopupMenu from './popup-menu.vue';
|
||||||
import PreviewAction from '../../../ui/preview/PreviewAction';
|
import PreviewAction from '../../../ui/preview/PreviewAction';
|
||||||
import Painterro from 'painterro';
|
import Painterro from 'painterro';
|
||||||
|
import RemoveDialog from '../utils/removeDialog';
|
||||||
import SnapshotTemplate from './snapshot-template.html';
|
import SnapshotTemplate from './snapshot-template.html';
|
||||||
import { togglePopupMenu } from '../utils/popup-menu';
|
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct'],
|
inject: ['openmct'],
|
||||||
components: {
|
components: {
|
||||||
|
PopupMenu
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
embed: {
|
embed: {
|
||||||
@ -62,23 +47,35 @@ export default {
|
|||||||
removeActionString: {
|
removeActionString: {
|
||||||
type: String,
|
type: String,
|
||||||
default() {
|
default() {
|
||||||
return 'Remove Embed';
|
return 'Remove This Embed';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
actions: [this.removeEmbedAction()],
|
popupMenuItems: []
|
||||||
agentService: this.openmct.$injector.get('agentService'),
|
|
||||||
popupService: this.openmct.$injector.get('popupService')
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
},
|
},
|
||||||
beforeMount() {
|
mounted() {
|
||||||
this.populateActionMenu();
|
this.addPopupMenuItems();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
addPopupMenuItems() {
|
||||||
|
const removeEmbed = {
|
||||||
|
cssClass: 'icon-trash',
|
||||||
|
name: this.removeActionString,
|
||||||
|
callback: this.getRemoveDialog.bind(this)
|
||||||
|
}
|
||||||
|
const preview = {
|
||||||
|
cssClass: 'icon-eye-open',
|
||||||
|
name: 'Preview',
|
||||||
|
callback: this.previewEmbed.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.popupMenuItems = [removeEmbed, preview];
|
||||||
|
},
|
||||||
annotateSnapshot() {
|
annotateSnapshot() {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
@ -183,6 +180,14 @@ export default {
|
|||||||
formatTime(unixTime, timeFormat) {
|
formatTime(unixTime, timeFormat) {
|
||||||
return Moment.utc(unixTime).format(timeFormat);
|
return Moment.utc(unixTime).format(timeFormat);
|
||||||
},
|
},
|
||||||
|
getRemoveDialog() {
|
||||||
|
const options = {
|
||||||
|
name: this.removeActionString,
|
||||||
|
callback: this.removeEmbed.bind(this)
|
||||||
|
}
|
||||||
|
const removeDialog = new RemoveDialog(this.openmct, options);
|
||||||
|
removeDialog.show();
|
||||||
|
},
|
||||||
openSnapshot() {
|
openSnapshot() {
|
||||||
const self = this;
|
const self = this;
|
||||||
const snapshot = new Vue({
|
const snapshot = new Vue({
|
||||||
@ -214,53 +219,17 @@ export default {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
populateActionMenu() {
|
previewEmbed() {
|
||||||
const self = this;
|
const self = this;
|
||||||
const actions = [new PreviewAction(self.openmct)];
|
const previewAction = new PreviewAction(self.openmct);
|
||||||
|
previewAction.invoke(JSON.parse(self.embed.objectPath));
|
||||||
|
},
|
||||||
|
removeEmbed(success) {
|
||||||
|
if (!success) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
actions.forEach((action) => {
|
this.$emit('removeEmbed', this.embed.id);
|
||||||
self.actions.push({
|
|
||||||
cssClass: action.cssClass,
|
|
||||||
name: action.name,
|
|
||||||
perform: () => {
|
|
||||||
action.invoke(JSON.parse(self.embed.objectPath));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
removeEmbed(id) {
|
|
||||||
this.$emit('removeEmbed', id);
|
|
||||||
},
|
|
||||||
removeEmbedAction() {
|
|
||||||
const self = this;
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: self.removeActionString,
|
|
||||||
cssClass: 'icon-trash',
|
|
||||||
perform: function (embed) {
|
|
||||||
const dialog = self.openmct.overlays.dialog({
|
|
||||||
iconClass: "error",
|
|
||||||
message: `This action will permanently ${self.removeActionString.toLowerCase()}. Do you wish to continue?`,
|
|
||||||
buttons: [{
|
|
||||||
label: "No",
|
|
||||||
callback: function () {
|
|
||||||
dialog.dismiss();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Yes",
|
|
||||||
emphasis: true,
|
|
||||||
callback: function () {
|
|
||||||
dialog.dismiss();
|
|
||||||
self.removeEmbed(embed.id);
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
toggleActionMenu(event) {
|
|
||||||
togglePopupMenu(event, this.openmct);
|
|
||||||
},
|
},
|
||||||
updateEmbed(embed) {
|
updateEmbed(embed) {
|
||||||
this.$emit('updateEmbed', embed);
|
this.$emit('updateEmbed', embed);
|
||||||
|
@ -10,24 +10,9 @@
|
|||||||
> {{ snapshots.length }} of {{ getNotebookSnapshotMaxCount() }}
|
> {{ snapshots.length }} of {{ getNotebookSnapshotMaxCount() }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<a class="l-browse-bar__context-actions c-disclosure-button"
|
<PopupMenu v-if="snapshots.length > 0"
|
||||||
@click="toggleActionMenu"
|
:popup-menu-items="popupMenuItems"
|
||||||
></a>
|
/>
|
||||||
<div class="hide-menu hidden">
|
|
||||||
<div class="menu-element context-menu-wrapper mobile-disable-select">
|
|
||||||
<div class="c-menu">
|
|
||||||
<ul>
|
|
||||||
<li v-for="action in actions"
|
|
||||||
:key="action.name"
|
|
||||||
:class="action.cssClass"
|
|
||||||
@click="action.perform()"
|
|
||||||
>
|
|
||||||
{{ action.name }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@ -62,14 +47,16 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import NotebookEmbed from './notebook-embed.vue';
|
import NotebookEmbed from './notebook-embed.vue';
|
||||||
|
import PopupMenu from './popup-menu.vue';
|
||||||
|
import RemoveDialog from '../utils/removeDialog';
|
||||||
import { NOTEBOOK_SNAPSHOT_MAX_COUNT } from '../snapshot-container';
|
import { NOTEBOOK_SNAPSHOT_MAX_COUNT } from '../snapshot-container';
|
||||||
import { EVENT_SNAPSHOTS_UPDATED } from '../notebook-constants';
|
import { EVENT_SNAPSHOTS_UPDATED } from '../notebook-constants';
|
||||||
import { togglePopupMenu } from '../utils/popup-menu';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct', 'snapshotContainer'],
|
inject: ['openmct', 'snapshotContainer'],
|
||||||
components: {
|
components: {
|
||||||
NotebookEmbed
|
NotebookEmbed,
|
||||||
|
PopupMenu
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
toggleSnapshot: {
|
toggleSnapshot: {
|
||||||
@ -81,54 +68,47 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
actions: [this.removeAllSnapshotAction()],
|
popupMenuItems: [],
|
||||||
|
removeActionString: 'Delete all snapshots',
|
||||||
snapshots: []
|
snapshots: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
this.addPopupMenuItems();
|
||||||
this.snapshotContainer.on(EVENT_SNAPSHOTS_UPDATED, this.snapshotsUpdated);
|
this.snapshotContainer.on(EVENT_SNAPSHOTS_UPDATED, this.snapshotsUpdated);
|
||||||
this.snapshots = this.snapshotContainer.getSnapshots();
|
this.snapshots = this.snapshotContainer.getSnapshots();
|
||||||
},
|
},
|
||||||
beforeDestory() {
|
beforeDestory() {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
addPopupMenuItems() {
|
||||||
|
const removeSnapshot = {
|
||||||
|
cssClass: 'icon-trash',
|
||||||
|
name: this.removeActionString,
|
||||||
|
callback: this.getRemoveDialog.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.popupMenuItems = [removeSnapshot];
|
||||||
|
},
|
||||||
close() {
|
close() {
|
||||||
this.toggleSnapshot();
|
this.toggleSnapshot();
|
||||||
},
|
},
|
||||||
getNotebookSnapshotMaxCount() {
|
getNotebookSnapshotMaxCount() {
|
||||||
return NOTEBOOK_SNAPSHOT_MAX_COUNT;
|
return NOTEBOOK_SNAPSHOT_MAX_COUNT;
|
||||||
},
|
},
|
||||||
removeAllSnapshotAction() {
|
getRemoveDialog() {
|
||||||
const self = this;
|
const options = {
|
||||||
|
name: this.removeActionString,
|
||||||
return {
|
callback: this.removeAllSnapshots.bind(this)
|
||||||
name: 'Delete All Snapshots',
|
}
|
||||||
cssClass: 'icon-trash',
|
const removeDialog = new RemoveDialog(this.openmct, options);
|
||||||
perform: function (embed) {
|
removeDialog.show();
|
||||||
const dialog = self.openmct.overlays.dialog({
|
|
||||||
iconClass: "error",
|
|
||||||
message: 'This action will delete all notebook snapshots. Do you want to continue?',
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
label: "No",
|
|
||||||
callback: () => {
|
|
||||||
dialog.dismiss();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Yes",
|
|
||||||
emphasis: true,
|
|
||||||
callback: () => {
|
|
||||||
self.removeAllSnapshots();
|
|
||||||
dialog.dismiss();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
removeAllSnapshots() {
|
removeAllSnapshots(success) {
|
||||||
|
if (!success) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.snapshotContainer.removeAllSnapshots();
|
this.snapshotContainer.removeAllSnapshots();
|
||||||
},
|
},
|
||||||
removeSnapshot(id) {
|
removeSnapshot(id) {
|
||||||
@ -141,9 +121,6 @@ export default {
|
|||||||
event.dataTransfer.setData('text/plain', snapshot.id);
|
event.dataTransfer.setData('text/plain', snapshot.id);
|
||||||
event.dataTransfer.setData('snapshot/id', snapshot.id);
|
event.dataTransfer.setData('snapshot/id', snapshot.id);
|
||||||
},
|
},
|
||||||
toggleActionMenu(event) {
|
|
||||||
togglePopupMenu(event, this.openmct);
|
|
||||||
},
|
|
||||||
updateSnapshot(snapshot) {
|
updateSnapshot(snapshot) {
|
||||||
this.snapshotContainer.updateSnapshot(snapshot);
|
this.snapshotContainer.updateSnapshot(snapshot);
|
||||||
}
|
}
|
||||||
|
@ -9,32 +9,19 @@
|
|||||||
@keydown.enter="updateName"
|
@keydown.enter="updateName"
|
||||||
@blur="updateName"
|
@blur="updateName"
|
||||||
>{{ page.name.length ? page.name : `Unnamed ${pageTitle}` }}</span>
|
>{{ page.name.length ? page.name : `Unnamed ${pageTitle}` }}</span>
|
||||||
<a class="c-list__item__menu-indicator icon-arrow-down"
|
<PopupMenu :popup-menu-items="popupMenuItems" />
|
||||||
@click="toggleActionMenu"
|
|
||||||
></a>
|
|
||||||
<div class="hide-menu hidden">
|
|
||||||
<div class="menu-element context-menu-wrapper mobile-disable-select">
|
|
||||||
<div class="c-menu">
|
|
||||||
<ul>
|
|
||||||
<li v-for="action in actions"
|
|
||||||
:key="action.name"
|
|
||||||
:class="action.cssClass"
|
|
||||||
@click="action.perform(page.id)"
|
|
||||||
>
|
|
||||||
{{ action.name }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { togglePopupMenu } from '../utils/popup-menu';
|
import PopupMenu from './popup-menu.vue';
|
||||||
|
import RemoveDialog from '../utils/removeDialog';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct'],
|
inject: ['openmct'],
|
||||||
|
components: {
|
||||||
|
PopupMenu
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
defaultPageId: {
|
defaultPageId: {
|
||||||
type: String,
|
type: String,
|
||||||
@ -55,7 +42,8 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
actions: [this.deletePage()]
|
popupMenuItems: [],
|
||||||
|
removeActionString: `Delete ${this.pageTitle}`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@ -64,40 +52,37 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
this.addPopupMenuItems();
|
||||||
this.toggleContentEditable();
|
this.toggleContentEditable();
|
||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
deletePage() {
|
addPopupMenuItems() {
|
||||||
const self = this;
|
const removePage = {
|
||||||
|
|
||||||
return {
|
|
||||||
name: `Delete ${this.pageTitle}`,
|
|
||||||
cssClass: 'icon-trash',
|
cssClass: 'icon-trash',
|
||||||
perform: function (id) {
|
name: this.removeActionString,
|
||||||
const dialog = self.openmct.overlays.dialog({
|
callback: this.getRemoveDialog.bind(this)
|
||||||
iconClass: "error",
|
}
|
||||||
message: 'This action will delete this page and all of its entries. Do you want to continue?',
|
|
||||||
buttons: [
|
this.popupMenuItems = [removePage];
|
||||||
{
|
},
|
||||||
label: "No",
|
deletePage(success) {
|
||||||
callback: () => {
|
if (!success) {
|
||||||
dialog.dismiss();
|
return;
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
this.$emit('deletePage', this.page.id);
|
||||||
label: "Yes",
|
},
|
||||||
emphasis: true,
|
getRemoveDialog() {
|
||||||
callback: () => {
|
const message = 'This action will delete this page and all of its entries. Do you want to continue?';
|
||||||
self.$emit('deletePage', id);
|
const options = {
|
||||||
dialog.dismiss();
|
name: this.removeActionString,
|
||||||
}
|
callback: this.deletePage.bind(this),
|
||||||
}
|
message
|
||||||
]
|
}
|
||||||
});
|
const removeDialog = new RemoveDialog(this.openmct, options);
|
||||||
}
|
removeDialog.show();
|
||||||
};
|
|
||||||
},
|
},
|
||||||
selectPage(event) {
|
selectPage(event) {
|
||||||
const target = event.target;
|
const target = event.target;
|
||||||
@ -117,10 +102,6 @@ export default {
|
|||||||
|
|
||||||
this.$emit('selectPage', id);
|
this.$emit('selectPage', id);
|
||||||
},
|
},
|
||||||
toggleActionMenu(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
togglePopupMenu(event, this.openmct);
|
|
||||||
},
|
|
||||||
toggleContentEditable(page = this.page) {
|
toggleContentEditable(page = this.page) {
|
||||||
const pageTitle = this.$el.querySelector('span');
|
const pageTitle = this.$el.querySelector('span');
|
||||||
pageTitle.contentEditable = page.isSelected;
|
pageTitle.contentEditable = page.isSelected;
|
||||||
|
93
src/plugins/notebook/components/popup-menu.vue
Normal file
93
src/plugins/notebook/components/popup-menu.vue
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
<template>
|
||||||
|
<button
|
||||||
|
class="c-popup-menu-button c-disclosure-button"
|
||||||
|
title="popup menu"
|
||||||
|
@click="showMenuItems"
|
||||||
|
>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import MenuItems from './menu-items.vue';
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
inject: ['openmct'],
|
||||||
|
props: {
|
||||||
|
domainObject: {
|
||||||
|
type: Object,
|
||||||
|
default() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
popupMenuItems: {
|
||||||
|
type: Array,
|
||||||
|
default() {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
menuItems: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
calculateMenuPosition(event, element) {
|
||||||
|
let eventPosX = event.clientX;
|
||||||
|
let eventPosY = event.clientY;
|
||||||
|
|
||||||
|
let menuDimensions = element.getBoundingClientRect();
|
||||||
|
let overflowX = (eventPosX + menuDimensions.width) - document.body.clientWidth;
|
||||||
|
let overflowY = (eventPosY + menuDimensions.height) - document.body.clientHeight;
|
||||||
|
|
||||||
|
if (overflowX > 0) {
|
||||||
|
eventPosX = eventPosX - overflowX;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (overflowY > 0) {
|
||||||
|
eventPosY = eventPosY - overflowY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
x: eventPosX,
|
||||||
|
y: eventPosY
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hideMenuItems() {
|
||||||
|
document.body.removeChild(this.menuItems.$el);
|
||||||
|
this.menuItems.$destroy();
|
||||||
|
this.menuItems = null;
|
||||||
|
document.removeEventListener('click', this.hideMenuItems);
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
showMenuItems($event) {
|
||||||
|
const menuItems = new Vue({
|
||||||
|
components: {
|
||||||
|
MenuItems
|
||||||
|
},
|
||||||
|
provide: {
|
||||||
|
popupMenuItems: this.popupMenuItems
|
||||||
|
},
|
||||||
|
template: '<MenuItems />'
|
||||||
|
});
|
||||||
|
this.menuItems = menuItems;
|
||||||
|
|
||||||
|
menuItems.$mount();
|
||||||
|
const element = this.menuItems.$el;
|
||||||
|
document.body.appendChild(element);
|
||||||
|
|
||||||
|
const position = this.calculateMenuPosition($event, element);
|
||||||
|
element.style.left = `${position.x}px`;
|
||||||
|
element.style.top = `${position.y}px`;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
document.addEventListener('click', this.hideMenuItems);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -9,24 +9,7 @@
|
|||||||
@keydown.enter="updateName"
|
@keydown.enter="updateName"
|
||||||
@blur="updateName"
|
@blur="updateName"
|
||||||
>{{ section.name.length ? section.name : `Unnamed ${sectionTitle}` }}</span>
|
>{{ section.name.length ? section.name : `Unnamed ${sectionTitle}` }}</span>
|
||||||
<a class="c-list__item__menu-indicator icon-arrow-down"
|
<PopupMenu :popup-menu-items="popupMenuItems" />
|
||||||
@click="toggleActionMenu"
|
|
||||||
></a>
|
|
||||||
<div class="hide-menu hidden">
|
|
||||||
<div class="menu-element context-menu-wrapper mobile-disable-select">
|
|
||||||
<div class="c-menu">
|
|
||||||
<ul>
|
|
||||||
<li v-for="action in actions"
|
|
||||||
:key="action.name"
|
|
||||||
:class="action.cssClass"
|
|
||||||
@click="action.perform(section.id)"
|
|
||||||
>
|
|
||||||
{{ action.name }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -34,10 +17,14 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { togglePopupMenu } from '../utils/popup-menu';
|
import PopupMenu from './popup-menu.vue';
|
||||||
|
import RemoveDialog from '../utils/removeDialog';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: ['openmct'],
|
inject: ['openmct'],
|
||||||
|
components: {
|
||||||
|
PopupMenu
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
defaultSectionId: {
|
defaultSectionId: {
|
||||||
type: String,
|
type: String,
|
||||||
@ -58,7 +45,8 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
actions: [this.deleteSectionAction()]
|
popupMenuItems: [],
|
||||||
|
removeActionString: `Delete ${this.sectionTitle}`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@ -67,40 +55,38 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
this.addPopupMenuItems();
|
||||||
this.toggleContentEditable();
|
this.toggleContentEditable();
|
||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
deleteSectionAction() {
|
addPopupMenuItems() {
|
||||||
const self = this;
|
const removeSection = {
|
||||||
|
|
||||||
return {
|
|
||||||
name: `Delete ${this.sectionTitle}`,
|
|
||||||
cssClass: 'icon-trash',
|
cssClass: 'icon-trash',
|
||||||
perform: function (id) {
|
name: this.removeActionString,
|
||||||
const dialog = self.openmct.overlays.dialog({
|
callback: this.getRemoveDialog.bind(this)
|
||||||
iconClass: "error",
|
}
|
||||||
message: 'This action will delete this section and all of its pages and entries. Do you want to continue?',
|
|
||||||
buttons: [
|
this.popupMenuItems = [removeSection];
|
||||||
{
|
},
|
||||||
label: "No",
|
deleteSection(success) {
|
||||||
callback: () => {
|
if (!success) {
|
||||||
dialog.dismiss();
|
return;
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
this.$emit('deleteSection', this.section.id);
|
||||||
label: "Yes",
|
},
|
||||||
emphasis: true,
|
getRemoveDialog() {
|
||||||
callback: () => {
|
const message = 'This action will delete this section and all of its pages and entries. Do you want to continue?';
|
||||||
self.$emit('deleteSection', id);
|
const options = {
|
||||||
dialog.dismiss();
|
name: this.removeActionString,
|
||||||
}
|
callback: this.deleteSection.bind(this),
|
||||||
}
|
message
|
||||||
]
|
}
|
||||||
});
|
|
||||||
}
|
const removeDialog = new RemoveDialog(this.openmct, options);
|
||||||
};
|
removeDialog.show();
|
||||||
},
|
},
|
||||||
selectSection(event) {
|
selectSection(event) {
|
||||||
const target = event.target;
|
const target = event.target;
|
||||||
@ -121,9 +107,6 @@ export default {
|
|||||||
|
|
||||||
this.$emit('selectSection', id);
|
this.$emit('selectSection', id);
|
||||||
},
|
},
|
||||||
toggleActionMenu(event) {
|
|
||||||
togglePopupMenu(event, this.openmct);
|
|
||||||
},
|
|
||||||
toggleContentEditable(section = this.section) {
|
toggleContentEditable(section = this.section) {
|
||||||
const sectionTitle = this.$el.querySelector('span');
|
const sectionTitle = this.$el.querySelector('span');
|
||||||
sectionTitle.contentEditable = section.isSelected;
|
sectionTitle.contentEditable = section.isSelected;
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
import $ from 'zepto';
|
|
||||||
|
|
||||||
export const togglePopupMenu = (event, openmct) => {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
const body = $(document.body);
|
|
||||||
const container = $(event.target.parentElement.parentElement);
|
|
||||||
const classList = document.querySelector('body').classList;
|
|
||||||
const isPhone = Array.from(classList).includes('phone');
|
|
||||||
const isTablet = Array.from(classList).includes('tablet');
|
|
||||||
|
|
||||||
const initiatingEvent = isPhone || isTablet
|
|
||||||
? 'touchstart'
|
|
||||||
: 'mousedown';
|
|
||||||
const menu = container.find('.menu-element');
|
|
||||||
let dismissExistingMenu;
|
|
||||||
|
|
||||||
function dismiss() {
|
|
||||||
container.find('.hide-menu').append(menu);
|
|
||||||
body.off(initiatingEvent, menuClickHandler);
|
|
||||||
dismissExistingMenu = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function menuClickHandler(e) {
|
|
||||||
window.setTimeout(() => {
|
|
||||||
dismiss();
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dismiss any menu which was already showing
|
|
||||||
if (dismissExistingMenu) {
|
|
||||||
dismissExistingMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...and record the presence of this menu.
|
|
||||||
dismissExistingMenu = dismiss;
|
|
||||||
|
|
||||||
const popupService = openmct.$injector.get('popupService');
|
|
||||||
popupService.display(menu, [event.pageX,event.pageY], {
|
|
||||||
marginX: 0,
|
|
||||||
marginY: -50
|
|
||||||
});
|
|
||||||
|
|
||||||
body.on(initiatingEvent, menuClickHandler);
|
|
||||||
}
|
|
36
src/plugins/notebook/utils/removeDialog.js
Normal file
36
src/plugins/notebook/utils/removeDialog.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
export default class RemoveDialog {
|
||||||
|
constructor(openmct, options) {
|
||||||
|
this.name = options.name;
|
||||||
|
this.openmct = openmct;
|
||||||
|
|
||||||
|
this.callback = options.callback;
|
||||||
|
this.cssClass = options.cssClass || 'icon-trash';
|
||||||
|
this.description = options.description || 'Remove action dialog';
|
||||||
|
this.iconClass = "error";
|
||||||
|
this.key = 'remove';
|
||||||
|
this.message = options.message || `This action will permanently ${this.name.toLowerCase()}. Do you wish to continue?`;
|
||||||
|
}
|
||||||
|
|
||||||
|
show() {
|
||||||
|
const dialog = this.openmct.overlays.dialog({
|
||||||
|
iconClass: this.iconClass,
|
||||||
|
message: this.message,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
label: "Ok",
|
||||||
|
callback: () => {
|
||||||
|
this.callback(true);
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Cancel",
|
||||||
|
callback: () => {
|
||||||
|
this.callback(false);
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user