mirror of
https://github.com/nasa/openmct.git
synced 2024-12-19 05:07:52 +00:00
Flexible Layout Refactor (#2223)
* only store identifiers in frames * move drop hints outside frame * fix resizing * fix drag and drop frames * fix reordering of columns * multiple improvements * fix styling for drop hint in empty container * fix frame reorder in same column * better drop target show logic * better frame drop to logic * fix container reordering * add uuids for frames and containers to prevent clashing * toolbar updates * use shared subobject component to ease styling * add type cssClass to subobject component header, and delete frame header vue file * add context menu in subobject views * change height and width to size for both frames and containers * remove uneccesary methods from resizeHanfle and inline logic instead * remove left click logic from context-menu mixin, add a click handler to dropDownContextMenu to show menu * make a mixin to listen for isEditing * encapsulate drop hints, and pass allowDrop logic to check if drop hint is valid * use event.dataTransfer instead of vue events for container reordering * remove vue events for frame dnd and use html events * better implementation of toolbar * remove unused event * fix container resizing when adding new container * make reviewer requested changes * add containerId to event vs having a JSON object * watch domainObject from flexible layouts and pass down to components * change domainObject directly on add Container * update domainObject on change, and cahnge toolBarProvider to function that returns an object * fix plugin * set domainObject as data property in felxibleLayouts * use class instead of inline styles * Cleanup code inline this.$el for components that measure their own size replace snapToPercentage with Math.round inject object as layoutObject, not dObject. * reuse sizing logic between frames and containers * clean up handlers split handlers for createFrame and moveFrame events in container. reorganize methods for each to clarify how they operate. * ObjectView only stops propagation when it handles event * use ids in toolbar to ensure correct items are mutated Because index may change due to drag and drop events, deleteFrame and deleteContainer should operate using identifiers instead of index. Also, generate path for hasFrame using indexes when object is selected, otherwise hasFrame may refer to the incorrect path.
This commit is contained in:
parent
e07cfc9394
commit
a87fc51fbb
@ -3,13 +3,15 @@ import Overlay from './Overlay';
|
||||
import Vue from 'vue';
|
||||
|
||||
class Dialog extends Overlay {
|
||||
constructor({iconClass, message, title, ...options}) {
|
||||
constructor({iconClass, message, title, hint, timestamp, ...options}) {
|
||||
|
||||
let component = new Vue({
|
||||
provide: {
|
||||
iconClass,
|
||||
message,
|
||||
title
|
||||
title,
|
||||
hint,
|
||||
timestamp
|
||||
},
|
||||
components: {
|
||||
DialogComponent: DialogComponent
|
||||
|
@ -23,8 +23,13 @@
|
||||
<div class="u-contents">
|
||||
<div class="c-so-view__header">
|
||||
<div class="c-so-view__header__start">
|
||||
<div class="c-so-view__name icon-object">{{ item.domainObject.name }}</div>
|
||||
<div class="c-so-view__context-actions c-disclosure-button"></div>
|
||||
<div class="c-so-view__name"
|
||||
:class="cssClass">
|
||||
{{ item.domainObject.name }}
|
||||
</div>
|
||||
<context-menu-drop-down
|
||||
:object-path="objectPath">
|
||||
</context-menu-drop-down>
|
||||
</div>
|
||||
<div class="c-so-view__header__end">
|
||||
<div class="c-button icon-expand local-controls--hidden"></div>
|
||||
@ -98,7 +103,8 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import ObjectView from '../../../ui/components/layout/ObjectView.vue'
|
||||
import ObjectView from '../../../ui/components/layout/ObjectView.vue';
|
||||
import contextMenuDropDown from './contextMenuDropDown.vue';
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
@ -107,12 +113,25 @@
|
||||
},
|
||||
components: {
|
||||
ObjectView,
|
||||
contextMenuDropDown
|
||||
},
|
||||
data() {
|
||||
let type = this.openmct.types.get(this.item.domainObject.type);
|
||||
|
||||
return {
|
||||
cssClass: type.definition.cssClass,
|
||||
objectPath: [this.item.domainObject].concat(this.openmct.router.path)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.item.config.attachListeners();
|
||||
if (this.item.config) {
|
||||
this.item.config.attachListeners();
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
this.item.config.removeListeners();
|
||||
if (this.item.config) {
|
||||
this.item.config.removeListeners();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
14
src/plugins/displayLayout/components/contextMenuDropDown.vue
Normal file
14
src/plugins/displayLayout/components/contextMenuDropDown.vue
Normal file
@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<div class="c-so-view__context-actions c-disclosure-button"
|
||||
@click="showContextMenu"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import contextMenu from '../../../ui/components/mixins/context-menu'
|
||||
|
||||
export default {
|
||||
props: ['object-path'],
|
||||
mixins: [contextMenu]
|
||||
}
|
||||
</script>
|
||||
|
@ -22,47 +22,52 @@
|
||||
|
||||
<template>
|
||||
<div class="c-fl-container"
|
||||
:style="[{'flex-basis': size}]"
|
||||
:class="{'is-empty': frames.length === 1}">
|
||||
:style="[{'flex-basis': sizeString}]"
|
||||
:class="{'is-empty': !frames.length}">
|
||||
<div class="c-fl-container__header icon-grippy-ew"
|
||||
v-show="isEditing"
|
||||
draggable="true"
|
||||
@dragstart="startContainerDrag"
|
||||
@dragend="stopContainerDrag">
|
||||
<span class="c-fl-container__size-indicator">{{ size }}</span>
|
||||
@dragstart="startContainerDrag">
|
||||
<span class="c-fl-container__size-indicator">{{ sizeString }}</span>
|
||||
</div>
|
||||
|
||||
<drop-hint
|
||||
class="c-fl-frame__drop-hint"
|
||||
:index="-1"
|
||||
:allow-drop="allowDrop"
|
||||
@object-drop-to="moveOrCreateFrame">
|
||||
</drop-hint>
|
||||
|
||||
<div class="c-fl-container__frames-holder">
|
||||
<div class="u-contents"
|
||||
v-for="(frame, i) in frames"
|
||||
:key="i">
|
||||
<template
|
||||
v-for="(frame, i) in frames">
|
||||
|
||||
<frame-component
|
||||
class="c-fl-container__frame"
|
||||
:style="{
|
||||
'flex-basis': `${frame.height}%`
|
||||
}"
|
||||
:key="frame.id"
|
||||
:frame="frame"
|
||||
:size="frame.height"
|
||||
:index="i"
|
||||
:containerIndex="index"
|
||||
:isEditing="isEditing"
|
||||
:isDragging="isDragging"
|
||||
@frame-drag-from="frameDragFrom"
|
||||
@frame-drop-to="frameDropTo"
|
||||
@delete-frame="promptBeforeDeletingFrame"
|
||||
@add-container="addContainer">
|
||||
:containerIndex="index">
|
||||
</frame-component>
|
||||
|
||||
<drop-hint
|
||||
class="c-fl-frame__drop-hint"
|
||||
:key="i"
|
||||
:index="i"
|
||||
:allowDrop="allowDrop"
|
||||
@object-drop-to="moveOrCreateFrame">
|
||||
</drop-hint>
|
||||
|
||||
<resize-handle
|
||||
v-if="i !== 0 && (i !== frames.length - 1)"
|
||||
v-show="isEditing"
|
||||
v-if="(i !== frames.length - 1)"
|
||||
:key="i"
|
||||
:index="i"
|
||||
:orientation="rowsLayout ? 'horizontal' : 'vertical'"
|
||||
@init-move="startFrameResizing"
|
||||
@move="frameResizing"
|
||||
@end-move="endFrameResizing">
|
||||
</resize-handle>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -71,61 +76,98 @@
|
||||
import FrameComponent from './frame.vue';
|
||||
import Frame from '../utils/frame';
|
||||
import ResizeHandle from './resizeHandle.vue';
|
||||
import DropHint from './dropHint.vue';
|
||||
import isEditingMixin from '../mixins/isEditing';
|
||||
|
||||
const SNAP_TO_PERCENTAGE = 1;
|
||||
const MIN_FRAME_SIZE = 5;
|
||||
|
||||
export default {
|
||||
inject:['openmct', 'domainObject'],
|
||||
props: ['size', 'frames', 'index', 'isEditing', 'isDragging', 'rowsLayout'],
|
||||
inject:['openmct'],
|
||||
props: ['container', 'index', 'rowsLayout'],
|
||||
mixins: [isEditingMixin],
|
||||
components: {
|
||||
FrameComponent,
|
||||
ResizeHandle
|
||||
ResizeHandle,
|
||||
DropHint
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
initialPos: 0,
|
||||
frameIndex: 0,
|
||||
maxMoveSize: 0
|
||||
computed: {
|
||||
frames() {
|
||||
return this.container.frames;
|
||||
},
|
||||
sizeString() {
|
||||
return `${Math.round(this.container.size)}%`
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
frameDragFrom(frameIndex) {
|
||||
this.$emit('frame-drag-from', this.index, frameIndex);
|
||||
},
|
||||
frameDropTo(frameIndex, event) {
|
||||
let domainObject = event.dataTransfer.getData('domainObject'),
|
||||
frameObject;
|
||||
allowDrop(event, index) {
|
||||
if (event.dataTransfer.getData('domainObject')) {
|
||||
return true;
|
||||
}
|
||||
let frameId = event.dataTransfer.getData('frameid'),
|
||||
containerIndex = Number(event.dataTransfer.getData('containerIndex'));
|
||||
|
||||
if (domainObject) {
|
||||
frameObject = new Frame(JSON.parse(domainObject));
|
||||
if (!frameId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.$emit('frame-drop-to', this.index, frameIndex, frameObject);
|
||||
if (containerIndex === this.index) {
|
||||
let frame = this.container.frames.filter((f) => f.id === frameId)[0],
|
||||
framePos = this.container.frames.indexOf(frame);
|
||||
|
||||
if (index === -1) {
|
||||
return framePos !== 0;
|
||||
} else {
|
||||
return framePos !== index && (framePos - 1) !== index
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
moveOrCreateFrame(insertIndex, event) {
|
||||
if (event.dataTransfer.types.includes('domainobject')) {
|
||||
// create frame using domain object
|
||||
let domainObject = JSON.parse(event.dataTransfer.getData('domainObject'));
|
||||
this.$emit(
|
||||
'create-frame',
|
||||
this.index,
|
||||
insertIndex,
|
||||
domainObject.identifier
|
||||
);
|
||||
return;
|
||||
};
|
||||
// move frame.
|
||||
let frameId = event.dataTransfer.getData('frameid');
|
||||
let containerIndex = Number(event.dataTransfer.getData('containerIndex'));
|
||||
this.$emit(
|
||||
'move-frame',
|
||||
this.index,
|
||||
insertIndex,
|
||||
frameId,
|
||||
containerIndex
|
||||
);
|
||||
},
|
||||
startFrameResizing(index) {
|
||||
let beforeFrame = this.frames[index],
|
||||
afterFrame = this.frames[index + 1];
|
||||
|
||||
this.maxMoveSize = beforeFrame.height + afterFrame.height;
|
||||
|
||||
this.maxMoveSize = beforeFrame.size + afterFrame.size;
|
||||
},
|
||||
frameResizing(index, delta, event) {
|
||||
|
||||
let percentageMoved = (delta / this.getElSize(this.$el))*100,
|
||||
let percentageMoved = Math.round(delta / this.getElSize() * 100),
|
||||
beforeFrame = this.frames[index],
|
||||
afterFrame = this.frames[index + 1];
|
||||
|
||||
beforeFrame.height = this.snapToPercentage(beforeFrame.height + percentageMoved);
|
||||
afterFrame.height = this.snapToPercentage(afterFrame.height - percentageMoved);
|
||||
beforeFrame.size = this.getFrameSize(beforeFrame.size + percentageMoved);
|
||||
afterFrame.size = this.getFrameSize(afterFrame.size - percentageMoved);
|
||||
},
|
||||
endFrameResizing(index, event) {
|
||||
this.persist();
|
||||
},
|
||||
getElSize(el) {
|
||||
getElSize() {
|
||||
if (this.rowsLayout) {
|
||||
return el.offsetWidth;
|
||||
return this.$el.offsetWidth;
|
||||
} else {
|
||||
return el.offsetHeight;
|
||||
return this.$el.offsetHeight;
|
||||
}
|
||||
},
|
||||
getFrameSize(size) {
|
||||
@ -137,76 +179,23 @@ export default {
|
||||
return size;
|
||||
}
|
||||
},
|
||||
snapToPercentage(value){
|
||||
let rem = value % SNAP_TO_PERCENTAGE,
|
||||
roundedValue;
|
||||
|
||||
if (rem < 0.5) {
|
||||
roundedValue = Math.floor(value/SNAP_TO_PERCENTAGE)*SNAP_TO_PERCENTAGE;
|
||||
} else {
|
||||
roundedValue = Math.ceil(value/SNAP_TO_PERCENTAGE)*SNAP_TO_PERCENTAGE;
|
||||
}
|
||||
|
||||
return this.getFrameSize(roundedValue);
|
||||
},
|
||||
persist() {
|
||||
this.$emit('persist', this.index);
|
||||
},
|
||||
promptBeforeDeletingFrame(frameIndex) {
|
||||
let deleteFrame = this.deleteFrame;
|
||||
|
||||
let prompt = this.openmct.overlays.dialog({
|
||||
iconClass: 'alert',
|
||||
message: `This action will remove ${this.frames[frameIndex].domainObject.name} from this Flexible Layout. Do you want to continue?`,
|
||||
buttons: [
|
||||
{
|
||||
label: 'Ok',
|
||||
emphasis: 'true',
|
||||
callback: function () {
|
||||
deleteFrame(frameIndex);
|
||||
prompt.dismiss();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Cancel',
|
||||
callback: function () {
|
||||
prompt.dismiss();
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
},
|
||||
deleteFrame(frameIndex) {
|
||||
this.frames.splice(frameIndex, 1);
|
||||
this.$parent.recalculateOldFrameSize(this.frames);
|
||||
this.persist();
|
||||
},
|
||||
deleteContainer() {
|
||||
this.$emit('delete-container', this.index);
|
||||
},
|
||||
addContainer() {
|
||||
this.$emit('add-container', this.index);
|
||||
},
|
||||
startContainerDrag(event) {
|
||||
event.stopPropagation();
|
||||
this.$emit('start-container-drag', this.index);
|
||||
},
|
||||
stopContainerDrag(event) {
|
||||
event.stopPropagation();
|
||||
this.$emit('stop-container-drag');
|
||||
event.dataTransfer.setData('containerid', this.container.id);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
let context = {
|
||||
item: this.domainObject,
|
||||
method: this.deleteContainer,
|
||||
item: this.$parent.domainObject,
|
||||
addContainer: this.addContainer,
|
||||
index: this.index,
|
||||
type: 'container'
|
||||
type: 'container',
|
||||
containerId: this.container.id
|
||||
}
|
||||
|
||||
this.unsubscribeSelection = this.openmct.selection.selectable(this.$el, context, false);
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.unsubscribeSelection();
|
||||
}
|
||||
|
@ -21,7 +21,8 @@
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="isEditing"
|
||||
v-show="isValidTarget">
|
||||
<div class="c-drop-hint c-drop-hint--always-show"
|
||||
:class="{'is-mouse-over': isMouseOver}"
|
||||
@dragenter="dragenter"
|
||||
@ -36,11 +37,21 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import isEditingMixin from '../mixins/isEditing';
|
||||
|
||||
export default {
|
||||
props:['index'],
|
||||
props:{
|
||||
index: Number,
|
||||
allowDrop: {
|
||||
type: Function,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
mixins: [isEditingMixin],
|
||||
data() {
|
||||
return {
|
||||
isMouseOver: false
|
||||
isMouseOver: false,
|
||||
isValidTarget: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -51,8 +62,23 @@ export default {
|
||||
this.isMouseOver = false;
|
||||
},
|
||||
dropHandler(event) {
|
||||
this.$emit('object-drop-to', event, this.index);
|
||||
this.$emit('object-drop-to', this.index, event);
|
||||
this.isValidTarget = false;
|
||||
},
|
||||
dragstart(event) {
|
||||
this.isValidTarget = this.allowDrop(event, this.index);
|
||||
},
|
||||
dragend() {
|
||||
this.isValidTarget = false;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener('dragstart', this.dragstart);
|
||||
document.addEventListener('dragend', this.dragend);
|
||||
},
|
||||
destroyed() {
|
||||
document.removeEventListener('dragstart', this.dragstart);
|
||||
document.removeEventListener('dragend', this.dragend);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -1,3 +1,25 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
<template>
|
||||
<div class="c-fl">
|
||||
<div class="c-fl__empty"
|
||||
@ -10,40 +32,31 @@
|
||||
'c-fl--rows': rowsLayout === true
|
||||
}">
|
||||
|
||||
<div class="u-contents"
|
||||
v-for="(container, index) in containers"
|
||||
:key="index">
|
||||
|
||||
<template v-for="(container, index) in containers">
|
||||
|
||||
<drop-hint
|
||||
style="flex-basis: 15px;"
|
||||
class="c-fl-frame__drop-hint"
|
||||
v-if="index === 0 && containers.length > 1"
|
||||
v-show="isContainerDragging"
|
||||
:key="index"
|
||||
:index="-1"
|
||||
@object-drop-to="containerDropTo">
|
||||
:allow-drop="allowContainerDrop"
|
||||
@object-drop-to="moveContainer">
|
||||
</drop-hint>
|
||||
|
||||
<container-component
|
||||
class="c-fl__container"
|
||||
ref="containerComponent"
|
||||
:key="container.id"
|
||||
:index="index"
|
||||
:size="`${Math.round(container.width)}%`"
|
||||
:frames="container.frames"
|
||||
:isEditing="isEditing"
|
||||
:isDragging="isDragging"
|
||||
:container="container"
|
||||
:rowsLayout="rowsLayout"
|
||||
@addFrame="addFrame"
|
||||
@frame-drag-from="frameDragFromHandler"
|
||||
@frame-drop-to="frameDropToHandler"
|
||||
@persist="persist"
|
||||
@delete-container="promptBeforeDeletingContainer"
|
||||
@add-container="addContainer"
|
||||
@start-container-drag="startContainerDrag"
|
||||
@stop-container-drag="stopContainerDrag">
|
||||
@move-frame="moveFrame"
|
||||
@create-frame="createFrame"
|
||||
@persist="persist">
|
||||
</container-component>
|
||||
|
||||
<resize-handle
|
||||
v-if="index !== (containers.length - 1)"
|
||||
v-show="isEditing"
|
||||
:key="index"
|
||||
:index="index"
|
||||
:orientation="rowsLayout ? 'vertical' : 'horizontal'"
|
||||
@init-move="startContainerResizing"
|
||||
@ -52,14 +65,15 @@
|
||||
</resize-handle>
|
||||
|
||||
<drop-hint
|
||||
style="flex-basis: 15px;"
|
||||
class="c-fl-frame__drop-hint"
|
||||
v-if="containers.length > 1"
|
||||
v-show="isContainerDragging"
|
||||
:key="index"
|
||||
:index="index"
|
||||
@object-drop-to="containerDropTo">
|
||||
:allowDrop="allowContainerDrop"
|
||||
@object-drop-to="moveContainer">
|
||||
</drop-hint>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -383,7 +397,7 @@
|
||||
}
|
||||
|
||||
&__drop-hint {
|
||||
flex: 1 1 100%;
|
||||
flex: 1 0 100%;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
@ -397,31 +411,63 @@
|
||||
<script>
|
||||
import ContainerComponent from './container.vue';
|
||||
import Container from '../utils/container';
|
||||
import Frame from '../utils/frame';
|
||||
import ResizeHandle from './resizeHandle.vue';
|
||||
import DropHint from './dropHint.vue';
|
||||
import isEditingMixin from '../mixins/isEditing';
|
||||
|
||||
const SNAP_TO_PERCENTAGE = 1,
|
||||
MIN_CONTAINER_SIZE = 5;
|
||||
const MIN_CONTAINER_SIZE = 5;
|
||||
|
||||
// Resize items so that newItem fits proportionally (newItem must be an element
|
||||
// of items). If newItem does not have a size or is sized at 100%, newItem will
|
||||
// have size set to 1/n * 100, where n is the total number of items.
|
||||
function sizeItems(items, newItem) {
|
||||
if (items.length === 1) {
|
||||
newItem.size = 100;
|
||||
} else {
|
||||
if (!newItem.size || newItem.size === 100) {
|
||||
newItem.size = Math.round(100 / items.length);
|
||||
}
|
||||
let oldItems = items.filter(item => item !== newItem);
|
||||
// Resize oldItems to fit inside remaining space;
|
||||
let remainder = 100 - newItem.size;
|
||||
oldItems.forEach((item) => {
|
||||
item.size = Math.round(item.size * remainder / 100);
|
||||
});
|
||||
// Ensure items add up to 100 in case of rounding error.
|
||||
let total = items.reduce((t, item) => t + item.size, 0);
|
||||
let excess = Math.round(100 - total);
|
||||
oldItems[oldItems.length - 1].size += excess;
|
||||
}
|
||||
}
|
||||
|
||||
// Scales items proportionally so total is equal to 100. Assumes that an item
|
||||
// was removed from array.
|
||||
function sizeToFill(items) {
|
||||
if (items.length === 0) {
|
||||
return;
|
||||
}
|
||||
let oldTotal = items.reduce((total, item) => total + item.size, 0);
|
||||
items.forEach((item) => {
|
||||
item.size = Math.round(item.size * 100 / oldTotal);
|
||||
});
|
||||
// Ensure items add up to 100 in case of rounding error.
|
||||
let total = items.reduce((t, item) => t + item.size, 0);
|
||||
let excess = Math.round(100 - total);
|
||||
items[items.length - 1].size += excess;
|
||||
}
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'domainObject'],
|
||||
inject: ['openmct', 'layoutObject'],
|
||||
mixins: [isEditingMixin],
|
||||
components: {
|
||||
ContainerComponent,
|
||||
ResizeHandle,
|
||||
DropHint
|
||||
},
|
||||
data() {
|
||||
let containers = this.domainObject.configuration.containers,
|
||||
rowsLayout = this.domainObject.configuration.rowsLayout;
|
||||
|
||||
return {
|
||||
containers: containers,
|
||||
dragFrom: [],
|
||||
isEditing: false,
|
||||
isDragging: false,
|
||||
isContainerDragging: false,
|
||||
rowsLayout: rowsLayout,
|
||||
maxMoveSize: 0
|
||||
domainObject: this.layoutObject
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -431,144 +477,105 @@ export default {
|
||||
} else {
|
||||
return 'Columns'
|
||||
}
|
||||
},
|
||||
containers() {
|
||||
return this.domainObject.configuration.containers;
|
||||
},
|
||||
rowsLayout() {
|
||||
return this.domainObject.configuration.rowsLayout;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
areAllContainersEmpty() {
|
||||
return !!!this.containers.filter(container => container.frames.length > 1).length;
|
||||
return !!!this.containers.filter(container => container.frames.length).length;
|
||||
},
|
||||
addContainer() {
|
||||
let newSize = 100/(this.containers.length+1);
|
||||
|
||||
let container = new Container(newSize)
|
||||
|
||||
this.recalculateContainerSize(newSize);
|
||||
|
||||
let container = new Container();
|
||||
this.containers.push(container);
|
||||
},
|
||||
recalculateContainerSize(newSize) {
|
||||
this.containers.forEach((container) => {
|
||||
container.width = newSize;
|
||||
});
|
||||
},
|
||||
recalculateNewFrameSize(multFactor, framesArray){
|
||||
framesArray.forEach((frame, index) => {
|
||||
if (index === 0) {
|
||||
return;
|
||||
}
|
||||
let frameSize = frame.height
|
||||
frame.height = this.snapToPercentage(multFactor * frameSize);
|
||||
});
|
||||
},
|
||||
recalculateOldFrameSize(framesArray) {
|
||||
let totalRemainingSum = framesArray.map((frame,i) => {
|
||||
if (i !== 0) {
|
||||
return frame.height
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}).reduce((a, c) => a + c);
|
||||
|
||||
framesArray.forEach((frame, index) => {
|
||||
|
||||
if (index === 0) {
|
||||
return;
|
||||
}
|
||||
if (framesArray.length === 2) {
|
||||
|
||||
frame.height = 100;
|
||||
} else {
|
||||
|
||||
let newSize = frame.height + ((frame.height / totalRemainingSum) * (100 - totalRemainingSum));
|
||||
frame.height = this.snapToPercentage(newSize);
|
||||
}
|
||||
});
|
||||
},
|
||||
addFrame(frame, index) {
|
||||
this.containers[index].addFrame(frame);
|
||||
},
|
||||
frameDragFromHandler(containerIndex, frameIndex) {
|
||||
this.dragFrom = [containerIndex, frameIndex];
|
||||
},
|
||||
frameDropToHandler(containerIndex, frameIndex, frameObject) {
|
||||
let newContainer = this.containers[containerIndex];
|
||||
|
||||
this.isDragging = false;
|
||||
|
||||
if (!frameObject) {
|
||||
frameObject = this.containers[this.dragFrom[0]].frames.splice(this.dragFrom[1], 1)[0];
|
||||
this.recalculateOldFrameSize(this.containers[this.dragFrom[0]].frames);
|
||||
}
|
||||
|
||||
if (!frameObject.height) {
|
||||
frameObject.height = 100 / Math.max(newContainer.frames.length - 1, 1);
|
||||
}
|
||||
|
||||
newContainer.frames.splice((frameIndex + 1), 0, frameObject);
|
||||
|
||||
let newTotalHeight = newContainer.frames.reduce((total, frame) => {
|
||||
let num = Number(frame.height);
|
||||
|
||||
if(isNaN(num)) {
|
||||
return total;
|
||||
} else {
|
||||
return total + num;
|
||||
}
|
||||
},0);
|
||||
let newMultFactor = 100 / newTotalHeight;
|
||||
|
||||
this.recalculateNewFrameSize(newMultFactor, newContainer.frames);
|
||||
|
||||
sizeItems(this.containers, container);
|
||||
this.persist();
|
||||
},
|
||||
deleteContainer(containerId) {
|
||||
let container = this.containers.filter(c => c.id === containerId)[0];
|
||||
let containerIndex = this.containers.indexOf(container);
|
||||
this.containers.splice(containerIndex, 1);
|
||||
sizeToFill(this.containers);
|
||||
this.persist();
|
||||
},
|
||||
moveFrame(toContainerIndex, toFrameIndex, frameId, fromContainerIndex) {
|
||||
let toContainer = this.containers[toContainerIndex];
|
||||
let fromContainer = this.containers[fromContainerIndex];
|
||||
let frame = fromContainer.frames.filter(f => f.id === frameId)[0];
|
||||
let fromIndex = fromContainer.frames.indexOf(frame);
|
||||
fromContainer.frames.splice(fromIndex, 1);
|
||||
sizeToFill(fromContainer.frames);
|
||||
toContainer.frames.splice(toFrameIndex + 1, 0, frame);
|
||||
sizeItems(toContainer.frames, frame);
|
||||
this.persist();
|
||||
},
|
||||
createFrame(containerIndex, insertFrameIndex, objectIdentifier) {
|
||||
let frame = new Frame(objectIdentifier);
|
||||
let container = this.containers[containerIndex];
|
||||
container.frames.splice(insertFrameIndex + 1, 0, frame);
|
||||
sizeItems(container.frames, frame);
|
||||
this.persist();
|
||||
},
|
||||
deleteFrame(frameId) {
|
||||
let container = this.containers
|
||||
.filter(c => c.frames.some(f => f.id === frameId))[0];
|
||||
let containerIndex = this.containers.indexOf(container);
|
||||
let frame = container
|
||||
.frames
|
||||
.filter((f => f.id === frameId))[0];
|
||||
let frameIndex = container.frames.indexOf(frame);
|
||||
container.frames.splice(frameIndex, 1);
|
||||
sizeToFill(container.frames);
|
||||
this.persist(containerIndex);
|
||||
},
|
||||
allowContainerDrop(event, index) {
|
||||
if (!event.dataTransfer.types.includes('containerid')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let containerId = event.dataTransfer.getData('containerid'),
|
||||
container = this.containers.filter((c) => c.id === containerId)[0],
|
||||
containerPos = this.containers.indexOf(container);
|
||||
|
||||
if (index === -1) {
|
||||
return containerPos !== 0;
|
||||
} else {
|
||||
return containerPos !== index && (containerPos - 1) !== index
|
||||
}
|
||||
},
|
||||
persist(index){
|
||||
if (index) {
|
||||
this.openmct.objects.mutate(this.domainObject, `.configuration.containers[${index}]`, this.containers[index]);
|
||||
this.openmct.objects.mutate(this.domainObject, `configuration.containers[${index}]`, this.containers[index]);
|
||||
} else {
|
||||
this.openmct.objects.mutate(this.domainObject, '.configuration.containers', this.containers);
|
||||
this.openmct.objects.mutate(this.domainObject, 'configuration.containers', this.containers);
|
||||
}
|
||||
},
|
||||
isEditingHandler(isEditing) {
|
||||
this.isEditing = isEditing;
|
||||
|
||||
if (this.isEditing) {
|
||||
this.$el.click(); //force selection of flexible-layout for toolbar
|
||||
}
|
||||
|
||||
if (this.isDragging && isEditing === false) {
|
||||
this.isDragging = false;
|
||||
}
|
||||
},
|
||||
dragstartHandler() {
|
||||
if (this.isEditing) {
|
||||
this.isDragging = true;
|
||||
}
|
||||
},
|
||||
dragendHandler() {
|
||||
this.isDragging = false;
|
||||
},
|
||||
startContainerResizing(index) {
|
||||
let beforeContainer = this.containers[index],
|
||||
afterContainer = this.containers[index + 1];
|
||||
|
||||
this.maxMoveSize = beforeContainer.width + afterContainer.width;
|
||||
this.maxMoveSize = beforeContainer.size + afterContainer.size;
|
||||
},
|
||||
containerResizing(index, delta, event) {
|
||||
let percentageMoved = (delta/this.getElSize(this.$el))*100,
|
||||
let percentageMoved = Math.round(delta / this.getElSize() * 100),
|
||||
beforeContainer = this.containers[index],
|
||||
afterContainer = this.containers[index + 1];
|
||||
|
||||
beforeContainer.width = this.getContainerSize(this.snapToPercentage(beforeContainer.width + percentageMoved));
|
||||
afterContainer.width = this.getContainerSize(this.snapToPercentage(afterContainer.width - percentageMoved));
|
||||
beforeContainer.size = this.getContainerSize(beforeContainer.size + percentageMoved);
|
||||
afterContainer.size = this.getContainerSize(afterContainer.size - percentageMoved);
|
||||
},
|
||||
endContainerResizing(event) {
|
||||
this.persist();
|
||||
},
|
||||
getElSize(el) {
|
||||
getElSize() {
|
||||
if (this.rowsLayout) {
|
||||
return el.offsetHeight;
|
||||
return this.$el.offsetHeight;
|
||||
} else {
|
||||
return el.offsetWidth;
|
||||
return this.$el.offsetWidth;
|
||||
}
|
||||
},
|
||||
getContainerSize(size) {
|
||||
@ -580,80 +587,19 @@ export default {
|
||||
return size;
|
||||
}
|
||||
},
|
||||
snapToPercentage(value) {
|
||||
let rem = value % SNAP_TO_PERCENTAGE,
|
||||
roundedValue;
|
||||
|
||||
if (rem < 0.5) {
|
||||
roundedValue = Math.floor(value/SNAP_TO_PERCENTAGE)*SNAP_TO_PERCENTAGE;
|
||||
updateDomainObject(newDomainObject) {
|
||||
this.domainObject = newDomainObject;
|
||||
},
|
||||
moveContainer(toIndex, event) {
|
||||
let containerId = event.dataTransfer.getData('containerid');
|
||||
let container = this.containers.filter(c => c.id === containerId)[0];
|
||||
let fromIndex = this.containers.indexOf(container);
|
||||
this.containers.splice(fromIndex, 1);
|
||||
if (fromIndex > toIndex) {
|
||||
this.containers.splice(toIndex + 1, 0, container);
|
||||
} else {
|
||||
roundedValue = Math.ceil(value/SNAP_TO_PERCENTAGE)*SNAP_TO_PERCENTAGE;
|
||||
this.containers.splice(toIndex, 0, container);
|
||||
}
|
||||
|
||||
return roundedValue;
|
||||
},
|
||||
toggleLayoutDirection(v) {
|
||||
this.rowsLayout = v;
|
||||
},
|
||||
promptBeforeDeletingContainer(containerIndex) {
|
||||
let deleteContainer = this.deleteContainer;
|
||||
|
||||
let prompt = this.openmct.overlays.dialog({
|
||||
iconClass: 'alert',
|
||||
message: `This action will permanently delete container ${containerIndex + 1} from this Flexible Layout`,
|
||||
buttons: [
|
||||
{
|
||||
label: 'Ok',
|
||||
emphasis: 'true',
|
||||
callback: function () {
|
||||
deleteContainer(containerIndex);
|
||||
prompt.dismiss();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Cancel',
|
||||
callback: function () {
|
||||
prompt.dismiss();
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
},
|
||||
deleteContainer(containerIndex) {
|
||||
this.containers.splice(containerIndex, 1);
|
||||
|
||||
this.recalculateContainerSize(100/this.containers.length);
|
||||
this.persist();
|
||||
},
|
||||
addContainer(containerIndex) {
|
||||
let newContainer = new Container();
|
||||
|
||||
if (typeof containerIndex === 'number') {
|
||||
this.containers.splice(containerIndex+1, 0, newContainer);
|
||||
} else {
|
||||
|
||||
this.containers.push(newContainer);
|
||||
}
|
||||
|
||||
this.recalculateContainerSize(100/this.containers.length);
|
||||
this.persist();
|
||||
},
|
||||
startContainerDrag(index) {
|
||||
this.isContainerDragging = true;
|
||||
this.containerDragFrom = index;
|
||||
},
|
||||
stopContainerDrag() {
|
||||
this.isContainerDragging = false;
|
||||
},
|
||||
containerDropTo(event, index) {
|
||||
let fromContainer = this.containers.splice(this.containerDragFrom, 1)[0];
|
||||
|
||||
if (index === -1) {
|
||||
this.containers.unshift(fromContainer);
|
||||
} else {
|
||||
this.containers.splice(index, 0, fromContainer);
|
||||
}
|
||||
|
||||
this.persist();
|
||||
}
|
||||
},
|
||||
@ -662,23 +608,17 @@ export default {
|
||||
let context = {
|
||||
item: this.domainObject,
|
||||
addContainer: this.addContainer,
|
||||
deleteContainer: this.deleteContainer,
|
||||
deleteFrame: this.deleteFrame,
|
||||
type: 'flexible-layout'
|
||||
}
|
||||
|
||||
this.unsubscribeSelection = this.openmct.selection.selectable(this.$el, context, true);
|
||||
|
||||
this.openmct.objects.observe(this.domainObject, 'configuration.rowsLayout', this.toggleLayoutDirection);
|
||||
this.openmct.editor.on('isEditing', this.isEditingHandler);
|
||||
|
||||
document.addEventListener('dragstart', this.dragstartHandler);
|
||||
document.addEventListener('dragend', this.dragendHandler);
|
||||
this.unobserve = this.openmct.objects.observe(this.domainObject, '*', this.updateDomainObject);
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.unsubscribeSelection();
|
||||
|
||||
this.openmct.editor.off('isEditing', this.isEditingHandler);
|
||||
document.removeEventListener('dragstart', this.dragstartHandler);
|
||||
document.removeEventListener('dragend', this.dragendHandler);
|
||||
this.unobserve();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -22,103 +22,78 @@
|
||||
|
||||
<template>
|
||||
<div class="c-fl-frame"
|
||||
:class="{
|
||||
'is-dragging': isDragging,
|
||||
[frame.cssClass]: true
|
||||
}"
|
||||
@dragstart="initDrag"
|
||||
@drag="continueDrag">
|
||||
:style="{
|
||||
'flex-basis': `${frame.size}%`
|
||||
}">
|
||||
|
||||
<div class="c-frame c-fl-frame__drag-wrapper is-selectable is-moveable"
|
||||
:class="{'no-frame': noFrame}"
|
||||
draggable="true"
|
||||
ref="frame"
|
||||
v-if="frame.domainObject">
|
||||
@dragstart="initDrag"
|
||||
ref="frame">
|
||||
|
||||
<frame-header
|
||||
v-if="index !== 0"
|
||||
ref="dragObject"
|
||||
class="c-fl-frame__header"
|
||||
:domainObject="frame.domainObject">
|
||||
</frame-header>
|
||||
|
||||
<object-view
|
||||
class="c-fl-frame__object-view"
|
||||
:object="frame.domainObject">
|
||||
</object-view>
|
||||
<subobject-view
|
||||
v-if="item.domainObject.identifier"
|
||||
:item="item">
|
||||
</subobject-view>
|
||||
|
||||
<div class="c-fl-frame__size-indicator"
|
||||
v-if="isEditing"
|
||||
v-show="frame.height && frame.height < 100">
|
||||
{{frame.height}}%
|
||||
v-show="frame.size && frame.size < 100">
|
||||
{{frame.size}}%
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<drop-hint
|
||||
v-show="isEditing && isDragging"
|
||||
class="c-fl-frame__drop-hint"
|
||||
:class="{'is-dragging': isDragging}"
|
||||
@object-drop-to="dropHandler">
|
||||
</drop-hint>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ObjectView from '../../../ui/components/layout/ObjectView.vue';
|
||||
import DropHint from './dropHint.vue';
|
||||
import ResizeHandle from './resizeHandle.vue';
|
||||
import FrameHeader from '../../../ui/components/utils/frameHeader.vue';
|
||||
import SubobjectView from '../../displayLayout/components/SubobjectView.vue';
|
||||
import isEditingMixin from '../mixins/isEditing';
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'domainObject'],
|
||||
props: ['frame', 'index', 'containerIndex', 'isEditing', 'isDragging'],
|
||||
inject: ['openmct'],
|
||||
props: ['frame', 'index', 'containerIndex'],
|
||||
mixins: [isEditingMixin],
|
||||
data() {
|
||||
return {
|
||||
noFrame: this.frame.noFrame
|
||||
item: {domainObject: {}}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
ObjectView,
|
||||
DropHint,
|
||||
ResizeHandle,
|
||||
FrameHeader
|
||||
SubobjectView
|
||||
},
|
||||
computed: {
|
||||
noFrame() {
|
||||
return this.frame.noFrame;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setDomainObject(object) {
|
||||
this.item.domainObject = object;
|
||||
this.setSelection();
|
||||
},
|
||||
setSelection() {
|
||||
let context = {
|
||||
item: this.item.domainObject,
|
||||
addContainer: this.addContainer,
|
||||
type: 'frame',
|
||||
frameId: this.frame.id
|
||||
};
|
||||
|
||||
this.unsubscribeSelection = this.openmct.selection.selectable(this.$refs.frame, context, false);
|
||||
},
|
||||
initDrag(event) {
|
||||
this.$emit('frame-drag-from', this.index);
|
||||
},
|
||||
dropHandler(event) {
|
||||
this.$emit('frame-drop-to', this.index, event);
|
||||
},
|
||||
continueDrag(event) {
|
||||
if (!this.isDragging) {
|
||||
this.isDragging = true;
|
||||
}
|
||||
},
|
||||
deleteFrame() {
|
||||
this.$emit('delete-frame', this.index);
|
||||
},
|
||||
addContainer() {
|
||||
this.$emit('add-container');
|
||||
},
|
||||
toggleFrame(v) {
|
||||
this.noFrame = v;
|
||||
event.dataTransfer.setData('frameid', this.frame.id);
|
||||
event.dataTransfer.setData('containerIndex', this.containerIndex);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
if (this.frame.domainObject.identifier) {
|
||||
let context = {
|
||||
item: this.frame.domainObject,
|
||||
method: this.deleteFrame,
|
||||
addContainer: this.addContainer,
|
||||
type: 'frame',
|
||||
index: this.index
|
||||
}
|
||||
|
||||
this.unsubscribeSelection = this.openmct.selection.selectable(this.$refs.frame, context, false);
|
||||
|
||||
this.openmct.objects.observe(this.domainObject, `configuration.containers[${this.containerIndex}].frames[${this.index}].noFrame`, this.toggleFrame);
|
||||
if (this.frame.domainObjectIdentifier) {
|
||||
this.openmct.objects.get(this.frame.domainObjectIdentifier).then((object)=>{
|
||||
this.setDomainObject(object);
|
||||
});
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
|
@ -23,16 +23,21 @@
|
||||
<template>
|
||||
<div class="c-fl-frame__resize-handle"
|
||||
:class="[orientation]"
|
||||
v-show="isEditing && !isDragging"
|
||||
@mousedown="mousedown">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import isEditingMixin from '../mixins/isEditing';
|
||||
|
||||
export default {
|
||||
props: ['orientation', 'index'],
|
||||
mixins: [isEditingMixin],
|
||||
data() {
|
||||
return {
|
||||
initialPos: 0
|
||||
initialPos: 0,
|
||||
isDragging: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -47,7 +52,18 @@ export default {
|
||||
mousemove(event) {
|
||||
event.preventDefault();
|
||||
|
||||
let delta = this.getMousePosition(event) - this.getElSizeFromRect(this.$el);
|
||||
let elSize, mousePos, delta;
|
||||
|
||||
if (this.orientation === 'horizontal') {
|
||||
elSize = this.$el.getBoundingClientRect().x;
|
||||
mousePos = event.clientX;
|
||||
} else {
|
||||
elSize = this.$el.getBoundingClientRect().y;
|
||||
mousePos = event.clientY;
|
||||
}
|
||||
|
||||
delta = mousePos - elSize;
|
||||
|
||||
this.$emit('move', this.index, delta, event);
|
||||
},
|
||||
mouseup(event) {
|
||||
@ -56,20 +72,20 @@ export default {
|
||||
document.body.removeEventListener('mousemove', this.mousemove);
|
||||
document.body.removeEventListener('mouseup', this.mouseup);
|
||||
},
|
||||
getMousePosition(event) {
|
||||
if (this.orientation === 'horizontal') {
|
||||
return event.clientX;
|
||||
} else {
|
||||
return event.clientY;
|
||||
}
|
||||
},
|
||||
getElSizeFromRect(el) {
|
||||
if (this.orientation === 'horizontal') {
|
||||
return el.getBoundingClientRect().x;
|
||||
} else {
|
||||
return el.getBoundingClientRect().y;
|
||||
}
|
||||
setDragging(event) {
|
||||
this.isDragging = true;
|
||||
},
|
||||
unsetDragging(event) {
|
||||
this.isDragging = false;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener('dragstart', this.setDragging);
|
||||
document.addEventListener('dragend', this.unsetDragging);
|
||||
},
|
||||
destroyed() {
|
||||
document.removeEventListener('dragstart', this.setDragging);
|
||||
document.removeEventListener('dragend', this.unsetDragging);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -46,7 +46,7 @@ define([
|
||||
},
|
||||
provide: {
|
||||
openmct,
|
||||
domainObject
|
||||
layoutObject: domainObject
|
||||
},
|
||||
el: element,
|
||||
template: '<flexible-layout-component></flexible-layout-component>'
|
||||
|
19
src/plugins/flexibleLayout/mixins/isEditing.js
Normal file
19
src/plugins/flexibleLayout/mixins/isEditing.js
Normal file
@ -0,0 +1,19 @@
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
data() {
|
||||
return {
|
||||
isEditing: this.openmct.editor.isEditing()
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.openmct.editor.on('isEditing', this.toggleEditing);
|
||||
},
|
||||
destroyed() {
|
||||
this.openmct.editor.off('isEditing', this.toggleEditing);
|
||||
},
|
||||
methods: {
|
||||
toggleEditing(value) {
|
||||
this.isEditing = value;
|
||||
}
|
||||
}
|
||||
};
|
@ -22,10 +22,12 @@
|
||||
|
||||
define([
|
||||
'./flexibleLayoutViewProvider',
|
||||
'./utils/container'
|
||||
'./utils/container',
|
||||
'./toolbarProvider'
|
||||
], function (
|
||||
FlexibleLayoutViewProvider,
|
||||
Container
|
||||
Container,
|
||||
ToolBarProvider
|
||||
) {
|
||||
return function plugin() {
|
||||
|
||||
@ -45,119 +47,9 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
openmct.toolbars.addProvider({
|
||||
name: "Flexible Layout Toolbar",
|
||||
key: "flex-layout",
|
||||
description: "A toolbar for objects inside a Flexible Layout.",
|
||||
forSelection: function (selection) {
|
||||
let context = selection[0].context;
|
||||
let toolbar = ToolBarProvider.default(openmct);
|
||||
|
||||
return (openmct.editor.isEditing() && context && context.type &&
|
||||
(context.type === 'flexible-layout' || context.type === 'container' || context.type === 'frame'));
|
||||
},
|
||||
toolbar: function (selection) {
|
||||
|
||||
let primary = selection[0],
|
||||
parent = selection[1],
|
||||
deleteFrame,
|
||||
toggleContainer,
|
||||
deleteContainer,
|
||||
addContainer,
|
||||
toggleFrame,
|
||||
separator;
|
||||
|
||||
addContainer = {
|
||||
control: "button",
|
||||
domainObject: parent ? parent.context.item : primary.context.item,
|
||||
method: parent ? parent.context.addContainer : primary.context.addContainer,
|
||||
key: "add",
|
||||
icon: "icon-plus-in-rect",
|
||||
title: 'Add Container'
|
||||
};
|
||||
|
||||
separator = {
|
||||
control: "separator",
|
||||
domainObject: selection[0].context.item,
|
||||
key: "separator"
|
||||
};
|
||||
|
||||
toggleContainer = {
|
||||
control: 'toggle-button',
|
||||
key: 'toggle-layout',
|
||||
domainObject: parent ? parent.context.item : primary.context.item,
|
||||
property: 'configuration.rowsLayout',
|
||||
options: [
|
||||
{
|
||||
value: false,
|
||||
icon: 'icon-columns',
|
||||
title: 'Columns'
|
||||
},
|
||||
{
|
||||
value: true,
|
||||
icon: 'icon-rows',
|
||||
title: 'Rows'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
if (primary.context.type === 'frame') {
|
||||
|
||||
deleteFrame = {
|
||||
control: "button",
|
||||
domainObject: primary.context.item,
|
||||
method: primary.context.method,
|
||||
key: "remove",
|
||||
icon: "icon-trash",
|
||||
title: "Remove Frame"
|
||||
};
|
||||
toggleFrame = {
|
||||
control: "toggle-button",
|
||||
domainObject: parent.context.item,
|
||||
property: `configuration.containers[${parent.context.index}].frames[${primary.context.index}].noFrame`,
|
||||
options: [
|
||||
{
|
||||
value: true,
|
||||
icon: 'icon-frame-hide',
|
||||
title: "Hide frame"
|
||||
},
|
||||
{
|
||||
value: false,
|
||||
icon: 'icon-frame-show',
|
||||
title: "Show frame"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
} else if (primary.context.type === 'container') {
|
||||
|
||||
deleteContainer = {
|
||||
control: "button",
|
||||
domainObject: primary.context.item,
|
||||
method: primary.context.method,
|
||||
key: "remove",
|
||||
icon: "icon-trash",
|
||||
title: "Remove Container"
|
||||
};
|
||||
|
||||
} else if (primary.context.type === 'flexible-layout') {
|
||||
|
||||
addContainer = {
|
||||
control: "button",
|
||||
domainObject: primary.context.item,
|
||||
method: primary.context.addContainer,
|
||||
key: "add",
|
||||
icon: "icon-plus-in-rect",
|
||||
title: 'Add Container'
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
let toolbar = [toggleContainer, addContainer, toggleFrame, separator, deleteFrame, deleteContainer];
|
||||
|
||||
return toolbar.filter(button => button !== undefined);
|
||||
}
|
||||
});
|
||||
openmct.toolbars.addProvider(toolbar);
|
||||
};
|
||||
};
|
||||
});
|
||||
|
215
src/plugins/flexibleLayout/toolbarProvider.js
Normal file
215
src/plugins/flexibleLayout/toolbarProvider.js
Normal file
@ -0,0 +1,215 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
|
||||
function ToolbarProvider(openmct) {
|
||||
|
||||
return {
|
||||
name: "Flexible Layout Toolbar",
|
||||
key: "flex-layout",
|
||||
description: "A toolbar for objects inside a Flexible Layout.",
|
||||
forSelection: function (selection) {
|
||||
let context = selection[0].context;
|
||||
|
||||
return (openmct.editor.isEditing() && context && context.type &&
|
||||
(context.type === 'flexible-layout' || context.type === 'container' || context.type === 'frame'));
|
||||
},
|
||||
toolbar: function (selection) {
|
||||
|
||||
let primary = selection[0],
|
||||
secondary = selection[1],
|
||||
tertiary = selection[2],
|
||||
deleteFrame,
|
||||
toggleContainer,
|
||||
deleteContainer,
|
||||
addContainer,
|
||||
toggleFrame,
|
||||
separator;
|
||||
|
||||
separator = {
|
||||
control: "separator",
|
||||
domainObject: selection[0].context.item,
|
||||
key: "separator"
|
||||
};
|
||||
|
||||
toggleContainer = {
|
||||
control: 'toggle-button',
|
||||
key: 'toggle-layout',
|
||||
domainObject: secondary ? secondary.context.item : primary.context.item,
|
||||
property: 'configuration.rowsLayout',
|
||||
options: [
|
||||
{
|
||||
value: false,
|
||||
icon: 'icon-columns',
|
||||
title: 'Columns'
|
||||
},
|
||||
{
|
||||
value: true,
|
||||
icon: 'icon-rows',
|
||||
title: 'Rows'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
if (primary.context.type === 'frame') {
|
||||
let frameId = primary.context.frameId;
|
||||
let layoutObject = tertiary.context.item;
|
||||
let containers = layoutObject
|
||||
.configuration
|
||||
.containers;
|
||||
let container = containers
|
||||
.filter(c => c.frames.some(f => f.id === frameId))[0];
|
||||
let frame = container
|
||||
.frames
|
||||
.filter((f => f.id === frameId))[0];
|
||||
let containerIndex = containers.indexOf(container);
|
||||
let frameIndex = container.frames.indexOf(frame);
|
||||
|
||||
deleteFrame = {
|
||||
control: "button",
|
||||
domainObject: primary.context.item,
|
||||
method: function () {
|
||||
let deleteFrameAction = tertiary.context.deleteFrame;
|
||||
|
||||
let prompt = openmct.overlays.dialog({
|
||||
iconClass: 'alert',
|
||||
message: `This action will remove this frame from this Flexible Layout. Do you want to continue?`,
|
||||
buttons: [
|
||||
{
|
||||
label: 'Ok',
|
||||
emphasis: 'true',
|
||||
callback: function () {
|
||||
deleteFrameAction(primary.context.frameId);
|
||||
prompt.dismiss();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Cancel',
|
||||
callback: function () {
|
||||
prompt.dismiss();
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
},
|
||||
key: "remove",
|
||||
icon: "icon-trash",
|
||||
title: "Remove Frame"
|
||||
};
|
||||
toggleFrame = {
|
||||
control: "toggle-button",
|
||||
domainObject: secondary.context.item,
|
||||
property: `configuration.containers[${containerIndex}].frames[${frameIndex}].noFrame`,
|
||||
options: [
|
||||
{
|
||||
value: true,
|
||||
icon: 'icon-frame-hide',
|
||||
title: "Hide frame"
|
||||
},
|
||||
{
|
||||
value: false,
|
||||
icon: 'icon-frame-show',
|
||||
title: "Show frame"
|
||||
}
|
||||
]
|
||||
};
|
||||
addContainer = {
|
||||
control: "button",
|
||||
domainObject: tertiary.context.item,
|
||||
method: tertiary.context.addContainer,
|
||||
key: "add",
|
||||
icon: "icon-plus-in-rect",
|
||||
title: 'Add Container'
|
||||
};
|
||||
|
||||
} else if (primary.context.type === 'container') {
|
||||
|
||||
deleteContainer = {
|
||||
control: "button",
|
||||
domainObject: primary.context.item,
|
||||
method: function () {
|
||||
let removeContainer = secondary.context.deleteContainer,
|
||||
containerId = primary.context.containerId;
|
||||
|
||||
let prompt = openmct.overlays.dialog({
|
||||
iconClass: 'alert',
|
||||
message: `This action will permanently delete container ${containerIndex + 1} from this Flexible Layout`,
|
||||
buttons: [
|
||||
{
|
||||
label: 'Ok',
|
||||
emphasis: 'true',
|
||||
callback: function () {
|
||||
removeContainer(containerId);
|
||||
prompt.dismiss();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Cancel',
|
||||
callback: function () {
|
||||
prompt.dismiss();
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
},
|
||||
key: "remove",
|
||||
icon: "icon-trash",
|
||||
title: "Remove Container"
|
||||
};
|
||||
|
||||
addContainer = {
|
||||
control: "button",
|
||||
domainObject: secondary.context.item,
|
||||
method: secondary.context.addContainer,
|
||||
key: "add",
|
||||
icon: "icon-plus-in-rect",
|
||||
title: 'Add Container'
|
||||
};
|
||||
|
||||
} else if (primary.context.type === 'flexible-layout') {
|
||||
|
||||
addContainer = {
|
||||
control: "button",
|
||||
domainObject: primary.context.item,
|
||||
method: primary.context.addContainer,
|
||||
key: "add",
|
||||
icon: "icon-plus-in-rect",
|
||||
title: 'Add Container'
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
let toolbar = [
|
||||
toggleContainer,
|
||||
addContainer,
|
||||
toggleFrame ? separator: undefined,
|
||||
toggleFrame,
|
||||
deleteFrame || deleteContainer ? separator: undefined,
|
||||
deleteFrame,
|
||||
deleteContainer
|
||||
];
|
||||
|
||||
return toolbar.filter(button => button !== undefined);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default ToolbarProvider;
|
@ -1,13 +1,10 @@
|
||||
import Frame from './frame';
|
||||
import uuid from 'uuid';
|
||||
|
||||
class Container {
|
||||
constructor (width) {
|
||||
this.frames = [new Frame({}, '', 'c-fl-frame--first-in-container')];
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
addFrame(frameObject) {
|
||||
this.frames.push(frameObject);
|
||||
constructor (size) {
|
||||
this.id = uuid();
|
||||
this.frames = [];
|
||||
this.size = size;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,11 @@
|
||||
import uuid from 'uuid';
|
||||
|
||||
class Frame {
|
||||
constructor(domainObject, height, cssClass) {
|
||||
this.domainObject = domainObject;
|
||||
this.height = height;
|
||||
this.cssClass = cssClass ? cssClass : '';
|
||||
constructor(domainObjectIdentifier, size) {
|
||||
this.id = uuid();
|
||||
this.domainObjectIdentifier = domainObjectIdentifier;
|
||||
this.size = size;
|
||||
|
||||
this.noFrame = false;
|
||||
}
|
||||
}
|
||||
|
@ -429,6 +429,8 @@ export default {
|
||||
}
|
||||
},
|
||||
start: function (event) {
|
||||
event.preventDefault(); // stop from firing drag event
|
||||
|
||||
this.startPosition = this.getPosition(event);
|
||||
document.body.addEventListener('mousemove', this.updatePosition);
|
||||
document.body.addEventListener('mouseup', this.end);
|
||||
|
@ -104,10 +104,10 @@ export default {
|
||||
parentObject.composition.push(childObject.identifier);
|
||||
this.openmct.objects.mutate(parentObject, 'composition', parentObject.composition);
|
||||
}
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,8 +9,10 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// TODO: handle mobile contet menu listeners.
|
||||
// TODO: handle mobile context menu listeners.
|
||||
|
||||
this.$el.addEventListener('contextmenu', this.showContextMenu);
|
||||
|
||||
this.objectPath.forEach((o, i) => {
|
||||
let removeListener = this.openmct.objects.observe(
|
||||
o,
|
||||
|
@ -1,56 +0,0 @@
|
||||
<template>
|
||||
<div class="c-frame-header">
|
||||
<div class="c-frame-header__start">
|
||||
<div class="c-frame-header__name" :class="cssClass">{{ domainObject.name }}</div>
|
||||
<div class="c-frame-header__context-actions c-disclosure-button"></div>
|
||||
</div>
|
||||
<div class="c-frame-header__end">
|
||||
<div class="c-button icon-expand local-controls--hidden"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import '~styles/sass-base';
|
||||
|
||||
.c-frame-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&__start,
|
||||
&__end {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
&__end {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
&__name {
|
||||
display: flex;
|
||||
&:before {
|
||||
margin-right: $interiorMarginSm;
|
||||
}
|
||||
}
|
||||
|
||||
.no-frame & {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
props:['domainObject'],
|
||||
data () {
|
||||
let type = this.openmct.types.get(this.domainObject.type);
|
||||
|
||||
return {
|
||||
cssClass: type.definition.cssClass
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
Loading…
Reference in New Issue
Block a user