Flexible Layout (#2201)

* first cut of flexible layout

* better drag handling

* add drop targets to every row

* enable drag and drop between columns and rows

* enable persistance

* add editing capability

* chage rows to frames and columns to containers, switch draggable to whole frame object

* Merge latest, resolve conflicts.

Need to just apply these changes to Deep's branch and push

* enhancements to drag targets

* WIP in flexibleLayout, container.vue files

- Refined classes and markup;
- min-width changed to flex-basis;
- Added toggle direction button;

* Significant progress but still WIP

- Refined classes and markup;
- Layout toggling working;
- Add Container working properly;
- TODOs: fix sizing in empty container, fix bordering, more refinements;

* add resizing of frames - still wip

* Significant enhancements

- Moved all CSS into flexibleLayout.vue;
- Layout now improved for empty container and drop hints;
- Proportional sizing now better for frames and containers;

* Resize handle WIP

* abstract splitter and logic into self contained component that will emit an event when mouse is moving

* Resize handle WIP

- Minor tweak to handle padding and hover;

* add container resize todo persist

* persist container resize

* add frame header, fix column resize on last column

* Refinements to resize-handle

- Fixed sizing;
- Transition on hover;
- TODOs: needs is-dragging to maintain hover style while dragging;

* fix drop hints showing after drop

* move header

* improve mouse move gesture

* Added frame size indicator

* add snapto functionality

* Refined container and frame size indicators

- Also added overflow handling to l-grid-view

* improve resizing logic

* add selection on frames

* Various resizing-frames related

- Fixed overflow - now frame widths can be collapsed to 5% minimum;
- Sizing indicators refined, better positioning and layout;
- Added grippy drag indicators to column heads;
- TODOs: add column head cursors and hover effects, hide indicators
when not in edit mode, handle nested layout and flex layouts while
editing

* Selecting and emtpy layout messaging

- Better empty layout message;
- Moved s-selected to proper element in c-fl-frame;

* Drop-hint and sizing related various

- Drop-hints for first placeholder container now display;
- Drop-hints moved into drag-wrapper;

* add delete frame

* Editing various

- Adjust Snow theme constants related to editing;
- Changed delete message wording;

* Updated icon and added description

* add toggle and remove container to toolbar

* miscellaneous cleanup

* add container button to toolbar

* improve toolbar

* code cleanup in plugin.js

* Various icons, toolbar separator

- Copied in c-toolbar__separator and associated changes in _controls
from Pegah's layout_alpha branch - may have conflicts later.
- Added separator to FL toolbar;
- Updated icons for grippy-ew, toolbar icons;

* add check for empty containers"

* logic to resize frames on drop

* fix delete frame and persisting toolbar

* Significant changes to edit / selection styling

- Both Flexible and fixed Display Layouts addressed;
- Both themes addressed;
- Changed drop-hint icon to icon-plus;

* add correct icons to frame header and fix toolbars showing up in wrong views

* Moving and resizing various

- Cursors;
- Grippy added to frame resize-handle, WIP!;

* add container reordering

* add frame/no frame support to toolbar'

* fix regression of resize handles showing after last frame in container

* force selection of flexible-layout when editing is first clicked, to apply correct toolbar

* make changes to simplify toolbar

* Modified sizing algorithm slightly

* make changes reviewer requested

* fix regression that causes top drop hint to not show

* remove unused variables and bind events to vue

* unsub selection before destroy
This commit is contained in:
Deep Tailor
2018-11-08 17:17:14 -08:00
committed by Pete Richards
parent d13d59bfa0
commit 1069a45cfc
19 changed files with 1530 additions and 38 deletions

View File

@@ -0,0 +1,214 @@
/*****************************************************************************
* 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-container"
:style="[{'flex-basis': size}]"
:class="{'is-empty': frames.length === 1}">
<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>
</div>
<div class="c-fl-container__frames-holder">
<div class="u-contents"
v-for="(frame, i) in frames"
:key="i">
<frame-component
class="c-fl-container__frame"
:style="{
'flex-basis': `${frame.height}%`
}"
: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">
</frame-component>
<resize-handle
v-if="i !== 0 && (i !== frames.length - 1)"
v-show="isEditing"
:index="i"
:orientation="rowsLayout ? 'horizontal' : 'vertical'"
@init-move="startFrameResizing"
@move="frameResizing"
@end-move="endFrameResizing">
</resize-handle>
</div>
</div>
</div>
</template>
<script>
import FrameComponent from './frame.vue';
import Frame from '../utils/frame';
import ResizeHandle from './resizeHandle.vue';
const SNAP_TO_PERCENTAGE = 1;
const MIN_FRAME_SIZE = 5;
export default {
inject:['openmct', 'domainObject'],
props: ['size', 'frames', 'index', 'isEditing', 'isDragging', 'rowsLayout'],
components: {
FrameComponent,
ResizeHandle
},
data() {
return {
initialPos: 0,
frameIndex: 0,
maxMoveSize: 0
}
},
methods: {
frameDragFrom(frameIndex) {
this.$emit('frame-drag-from', this.index, frameIndex);
},
frameDropTo(frameIndex, event) {
let domainObject = event.dataTransfer.getData('domainObject'),
frameObject;
if (domainObject) {
frameObject = new Frame(JSON.parse(domainObject));
}
this.$emit('frame-drop-to', this.index, frameIndex, frameObject);
},
startFrameResizing(index) {
let beforeFrame = this.frames[index],
afterFrame = this.frames[index + 1];
this.maxMoveSize = beforeFrame.height + afterFrame.height;
},
frameResizing(index, delta, event) {
let percentageMoved = (delta / this.getElSize(this.$el))*100,
beforeFrame = this.frames[index],
afterFrame = this.frames[index + 1];
beforeFrame.height = this.snapToPercentage(beforeFrame.height + percentageMoved);
afterFrame.height = this.snapToPercentage(afterFrame.height - percentageMoved);
},
endFrameResizing(index, event) {
this.persist();
},
getElSize(el) {
if (this.rowsLayout) {
return el.offsetWidth;
} else {
return el.offsetHeight;
}
},
getFrameSize(size) {
if (size < MIN_FRAME_SIZE) {
return MIN_FRAME_SIZE
} else if (size > (this.maxMoveSize - MIN_FRAME_SIZE)) {
return (this.maxMoveSize - MIN_FRAME_SIZE);
} else {
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');
}
},
mounted() {
let context = {
item: this.domainObject,
method: this.deleteContainer,
addContainer: this.addContainer,
index: this.index,
type: 'container'
}
this.unsubscribeSelection = this.openmct.selection.selectable(this.$el, context, false);
},
beforeDestroy() {
this.unsubscribeSelection();
}
}
</script>