mirror of
https://github.com/nasa/openmct.git
synced 2025-01-18 18:57:01 +00:00
Merge pull request #1666 from nasa/layout-issue-1658
Added selection capability for the panels in layout. Also, added a to…
This commit is contained in:
commit
e205bf1fa4
@ -28,8 +28,4 @@
|
||||
key="'menu-arrow'"
|
||||
mct-object='domainObject'
|
||||
class="flex-elem context-available-w"></mct-representation>
|
||||
</span>
|
||||
<a class="s-button icon-expand t-btn-view-large"
|
||||
title="View large"
|
||||
mct-trigger-modal>
|
||||
</a>
|
||||
</span>
|
@ -21,32 +21,11 @@
|
||||
*****************************************************************************/
|
||||
.t-fixed-position {
|
||||
&.l-fixed-position {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: auto;
|
||||
height: auto;
|
||||
|
||||
.l-grid-holder {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
.l-grid {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
}
|
||||
@extend .abs;
|
||||
}
|
||||
|
||||
.l-fixed-position-item {
|
||||
position: absolute;
|
||||
border: 1px solid transparent;
|
||||
|
||||
&.s-not-selected {
|
||||
opacity: 0.8;
|
||||
}
|
||||
@ -105,37 +84,8 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.l-fixed-position-item-handle {
|
||||
$brd: 1px solid $colorKey;
|
||||
background: rgba($colorKey, 0.5);
|
||||
cursor: crosshair;
|
||||
border: $brd;
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
.edit-mode .t-fixed-position {
|
||||
&.l-fixed-position {
|
||||
.l-grid-holder {
|
||||
.l-grid {
|
||||
&.l-grid-x {
|
||||
@include bgTicks($colorGridLines, 'x');
|
||||
}
|
||||
|
||||
&.l-grid-y {
|
||||
@include bgTicks($colorGridLines, 'y');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.l-fixed-position-item {
|
||||
&:not(.s-selected) {
|
||||
border: 1px dotted rgba($colorKey, 0.75);
|
||||
&:hover {
|
||||
border: 1px dotted rgba($colorKey, 1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
.s-status-editing {
|
||||
.l-fixed-position-item-handle.edit-corner { display: block; }
|
||||
}
|
||||
|
@ -704,11 +704,6 @@ textarea {
|
||||
}
|
||||
}
|
||||
|
||||
.view-switcher,
|
||||
.t-btn-view-large {
|
||||
@include trans-prop-nice-fade($controlFadeMs);
|
||||
}
|
||||
|
||||
/******************************************************** BROWSER ELEMENTS */
|
||||
body.desktop {
|
||||
::-webkit-scrollbar {
|
||||
|
@ -19,12 +19,24 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
.s-status-editing .l-object-wrapper,
|
||||
.edit-main {
|
||||
// .s-status-editing .l-object-wrapper is relevant to New Edit Mode;
|
||||
// .edit-main is legacy for old edit mode.
|
||||
$handleD: 15px;
|
||||
$cr: 5px;
|
||||
.t-edit-handle-holder { display: none; }
|
||||
|
||||
.l-grid-holder {
|
||||
display: none;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
.l-grid {
|
||||
@extend .abs;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
&.l-grid-y { background-position: 0 1px; }
|
||||
}
|
||||
}
|
||||
|
||||
.s-status-editing {
|
||||
$handleD: 5px;
|
||||
.t-edit-handle-holder { display: block; }
|
||||
.edit-corner,
|
||||
.edit-handle {
|
||||
position: absolute;
|
||||
@ -32,81 +44,59 @@
|
||||
}
|
||||
|
||||
.edit-corner {
|
||||
background: rgba($colorSelectableSelectedPrimary, 0.5);
|
||||
cursor: crosshair;
|
||||
display: none; // Hide by default
|
||||
border: 1px solid $colorSelectableSelectedPrimary;
|
||||
width: $handleD;
|
||||
height: $handleD;
|
||||
$o: (-1 * $handleD) + 1px;
|
||||
&:hover {
|
||||
z-index: 11;
|
||||
}
|
||||
&.edit-resize-nw {
|
||||
border-bottom-right-radius: $cr;
|
||||
cursor: nw-resize;
|
||||
top: 0; left: 0;
|
||||
}
|
||||
&.edit-resize-ne {
|
||||
border-bottom-left-radius: $cr;
|
||||
cursor: ne-resize;
|
||||
top: 0; right: 0;
|
||||
}
|
||||
&.edit-resize-se {
|
||||
border-top-left-radius: $cr;
|
||||
cursor: se-resize;
|
||||
bottom: 0; right: 0;
|
||||
}
|
||||
&.edit-resize-sw {
|
||||
border-top-right-radius: $cr;
|
||||
cursor: sw-resize;
|
||||
bottom: 0; left: 0;
|
||||
}
|
||||
|
||||
&.edit-resize-nw { top: $o; left: $o; }
|
||||
&.edit-resize-ne { top: $o; right: $o; }
|
||||
&.edit-resize-se { bottom: $o; right: $o; }
|
||||
&.edit-resize-sw { bottom: $o; left: $o; }
|
||||
}
|
||||
|
||||
.edit-handle {
|
||||
top: $handleD; right: $handleD; bottom: $handleD; left: $handleD;
|
||||
&.edit-move {
|
||||
$m: 0; //$handleD;
|
||||
cursor: move;
|
||||
left: $m;
|
||||
right: $m;
|
||||
top: $m;
|
||||
bottom: $m;
|
||||
z-index: 1;
|
||||
|
||||
}
|
||||
&.edit-resize-n {
|
||||
top: 0px; bottom: auto;
|
||||
height: $handleD;
|
||||
cursor: n-resize;
|
||||
}
|
||||
&.edit-resize-e {
|
||||
right: 0px; left: auto;
|
||||
width: $handleD;
|
||||
cursor: e-resize;
|
||||
}
|
||||
&.edit-resize-s {
|
||||
bottom: 0px; top: auto;
|
||||
height: $handleD;
|
||||
cursor: s-resize;
|
||||
}
|
||||
&.edit-resize-w {
|
||||
left: 0px; right: auto;
|
||||
width: $handleD;
|
||||
cursor: w-resize;
|
||||
}
|
||||
.edit-handle.edit-move {
|
||||
// main move box for the whole frame element
|
||||
$m: 0;
|
||||
left: $m;
|
||||
right: $m;
|
||||
top: $m;
|
||||
bottom: $m;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.frame.child-frame.panel {
|
||||
&:hover {
|
||||
@include boxShdwLarge();
|
||||
border-color: $colorSelectableSelectedPrimary;
|
||||
.view-switcher {
|
||||
opacity: 1;
|
||||
}
|
||||
.edit-corner {
|
||||
background-color: rgba($colorKey, 0.8);
|
||||
&:hover {
|
||||
background-color: rgba($colorKey, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Editing Grids
|
||||
.l-grid-holder {
|
||||
display: block;
|
||||
.l-grid {
|
||||
&.l-grid-x { @include bgTicks($colorGridLines, 'x'); }
|
||||
&.l-grid-y { @include bgTicks($colorGridLines, 'y'); }
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent nested frames from showing their grids
|
||||
.t-frame-outer .l-grid-holder { display: none !important; }
|
||||
|
||||
// Prevent nested elements from showing s-hover-border
|
||||
.t-frame-outer .s-hover-border {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
// Prevent nested frames from being selectable until we have proper sub-object editing
|
||||
.t-frame-outer .t-frame-outer {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
@ -193,6 +193,7 @@
|
||||
|
||||
@include animToParams(overlayIn, $dur: $durLargeViewExpand, $delay: 0);
|
||||
background: $colorBodyBg;
|
||||
z-index: 101;
|
||||
|
||||
.abs.inner-holder {
|
||||
opacity: 0;
|
||||
@ -203,10 +204,19 @@
|
||||
@include animToParams(contentsIn, $dur: 50ms, $delay: $durLargeViewExpand * 1.25);
|
||||
}
|
||||
|
||||
// Hide View Large button
|
||||
.t-btn-view-large {
|
||||
display: none;
|
||||
}
|
||||
z-index: 101;
|
||||
|
||||
// But show View Large button when it's nested inside a Layout
|
||||
.t-frame-inner .t-frame-inner .t-btn-view-large { display: block; }
|
||||
}
|
||||
}
|
||||
// When multiple Large Views are visible, hide the blocker for all but the first
|
||||
& + .l-large-view {
|
||||
.blocker {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,28 +23,43 @@
|
||||
$ohH: $btnFrameH;
|
||||
$bc: $colorInteriorBorder;
|
||||
&.child-frame.panel {
|
||||
background: $colorBodyBg;
|
||||
border: 1px solid $bc;
|
||||
z-index: 0; // Needed to prevent child-frame controls from showing through when another child-frame is above
|
||||
&:hover {
|
||||
border-color: lighten($bc, 10%);
|
||||
&:not(.no-frame) {
|
||||
background: $colorBodyBg;
|
||||
border: 1px solid $bc;
|
||||
&:hover {
|
||||
border-color: lighten($bc, 10%);
|
||||
}
|
||||
}
|
||||
}
|
||||
.object-browse-bar {
|
||||
font-size: 0.75em;
|
||||
height: $ohH;
|
||||
line-height: $ohH;
|
||||
.right {
|
||||
@include trans-prop-nice-fade($controlFadeMs);
|
||||
padding-left: $interiorMargin;
|
||||
}
|
||||
}
|
||||
|
||||
&.t-object-type-timer,
|
||||
&.t-object-type-clock,
|
||||
&.t-object-type-hyperlink {
|
||||
// Hide the right side buttons for objects where they don't make sense
|
||||
// Note that this will hide the view Switcher button if applied
|
||||
// to an object that it.
|
||||
.object-browse-bar .right { display: none; }
|
||||
}
|
||||
|
||||
> .object-holder.abs {
|
||||
top: $ohH + $interiorMargin;
|
||||
}
|
||||
.contents {
|
||||
$myM: $interiorMargin;
|
||||
top: $myM;
|
||||
right: $myM;
|
||||
bottom: $myM;
|
||||
left: $myM;
|
||||
$m: $interiorMargin;
|
||||
top: $m;
|
||||
right: $m;
|
||||
bottom: $m;
|
||||
left: $m;
|
||||
}
|
||||
&.frame-template {
|
||||
.s-button,
|
||||
@ -67,15 +82,25 @@
|
||||
}
|
||||
}
|
||||
.view-switcher {
|
||||
margin-left: $interiorMargin; // Kick other top bar elements away when I'm present.
|
||||
margin-right: $interiorMargin; // Kick other top bar elements away when I'm present.
|
||||
// Hide the name when the view switcher is in a frame context
|
||||
.title-label {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.no-frame {
|
||||
background: transparent !important;
|
||||
border: none !important;
|
||||
.object-browse-bar .right {
|
||||
$m: 0; // $interiorMarginSm;
|
||||
background: rgba(black, 0.3);
|
||||
border-radius: $basicCr;
|
||||
padding: $interiorMarginSm;
|
||||
position: absolute;
|
||||
top: $m; right: $m;
|
||||
z-index: 2;
|
||||
}
|
||||
&.t-frame-outer > .t-rep-frame {
|
||||
&.contents {
|
||||
$m: 2px;
|
||||
@ -85,7 +110,7 @@
|
||||
left: $m;
|
||||
}
|
||||
> .t-frame-inner {
|
||||
> .object-browse-bar {
|
||||
> .object-browse-bar .left {
|
||||
display: none;
|
||||
}
|
||||
> .object-holder.abs {
|
||||
@ -117,8 +142,7 @@
|
||||
body.desktop .frame {
|
||||
// Hide local controls initially and show it them on hover when they're in an element that's in a frame context
|
||||
// Frame template is used because we need to target the lowest nested frame
|
||||
.view-switcher,
|
||||
.t-btn-view-large {
|
||||
.right {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
@ -126,8 +150,7 @@ body.desktop .frame {
|
||||
// Target the first descendant so that we only show the elements in the outermost container.
|
||||
// Handles the case where we have layouts in layouts.
|
||||
&:hover > .object-browse-bar {
|
||||
.view-switcher,
|
||||
.t-btn-view-large {
|
||||
.right {
|
||||
opacity: 1;
|
||||
pointer-events: inherit;
|
||||
}
|
||||
|
@ -19,21 +19,36 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
.s-selectable {
|
||||
border: 1px solid transparent;
|
||||
.s-hover-border {
|
||||
border: 1px dotted transparent;
|
||||
}
|
||||
|
||||
&.s-hover {
|
||||
// Styles when hovering over a selectable object
|
||||
border-color: $colorSelectableHov !important;
|
||||
.s-status-editing {
|
||||
// Limit to editing mode until we have sub-object selection
|
||||
.s-hover-border {
|
||||
// Show a border by default so user can see object bounds and empty objects
|
||||
border: 1px dotted rgba($colorSelectableSelectedPrimary, 0.3) !important;
|
||||
&:hover {
|
||||
border-color: rgba($colorSelectableSelectedPrimary, 0.7) !important;
|
||||
}
|
||||
}
|
||||
|
||||
&.s-selected {
|
||||
.s-selected > .s-hover-border,
|
||||
.s-selected.s-hover-border {
|
||||
// Styles for a selected object. Also used by legacy Fixed Position/Panel objects.
|
||||
border-color: $colorSelectableSelectedPrimary !important;
|
||||
@include boxShdwLarge();
|
||||
// Show edit-corners if you got 'em
|
||||
.edit-corner {
|
||||
display: block;
|
||||
&:hover {
|
||||
background-color: rgba($colorKey, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.s-moveable {
|
||||
@include boxShdwLarge();
|
||||
.s-selected > .s-moveable,
|
||||
.s-selected.s-moveable {
|
||||
cursor: move;
|
||||
}
|
||||
}
|
||||
}
|
@ -62,7 +62,29 @@ define([
|
||||
"type": "layout",
|
||||
"template": layoutTemplate,
|
||||
"editable": true,
|
||||
"uses": []
|
||||
"uses": [],
|
||||
"toolbar": {
|
||||
"sections": [
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"method": "showFrame",
|
||||
"cssClass": "icon-frame-show",
|
||||
"control": "button",
|
||||
"title": "Show frame",
|
||||
"description": "Show frame"
|
||||
},
|
||||
{
|
||||
"method": "hideFrame",
|
||||
"cssClass": "icon-frame-hide",
|
||||
"control": "button",
|
||||
"title": "Hide frame",
|
||||
"description": "Hide frame"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "fixed",
|
||||
|
@ -25,15 +25,20 @@
|
||||
<!-- Background grid -->
|
||||
<div class="l-grid-holder" ng-click="controller.clearSelection()">
|
||||
<div class="l-grid l-grid-x"
|
||||
ng-if="!controller.getGridSize()[0] < 3"
|
||||
ng-style="{ 'background-size': controller.getGridSize() [0] + 'px 100%' }"></div>
|
||||
<div class="l-grid l-grid-y"
|
||||
ng-if="!controller.getGridSize()[1] < 3"
|
||||
ng-style="{ 'background-size': '100% ' + controller.getGridSize() [1] + 'px' }"></div>
|
||||
</div>
|
||||
|
||||
<!-- Fixed position elements -->
|
||||
<div ng-repeat="element in controller.getElements()"
|
||||
class="l-fixed-position-item"
|
||||
ng-class="{ 's-not-selected': controller.selected() && !controller.selected(element) }"
|
||||
class="l-fixed-position-item s-selectable s-moveable s-hover-border"
|
||||
ng-class="{
|
||||
's-not-selected': controller.selected() && !controller.selected(element),
|
||||
's-selected': controller.selected(element)
|
||||
}"
|
||||
ng-style="element.style"
|
||||
ng-click="controller.select(element)">
|
||||
<mct-include key="element.template"
|
||||
@ -43,15 +48,15 @@
|
||||
</div>
|
||||
|
||||
<!-- Selection highlight, handles -->
|
||||
<span ng-if="controller.selected()">
|
||||
<div class="l-fixed-position-item s-selectable s-selected s-moveable"
|
||||
<span class="s-selected s-moveable" ng-if="controller.selected()">
|
||||
<div class="l-fixed-position-item t-edit-handle-holder"
|
||||
mct-drag-down="controller.moveHandle().startDrag(controller.selected())"
|
||||
mct-drag="controller.moveHandle().continueDrag(delta)"
|
||||
mct-drag-up="controller.moveHandle().endDrag()"
|
||||
ng-style="controller.selected().style">
|
||||
</div>
|
||||
<div ng-repeat="handle in controller.handles()"
|
||||
class="l-fixed-position-item-handle"
|
||||
class="l-fixed-position-item-handle edit-corner"
|
||||
ng-style="handle.style()"
|
||||
mct-drag-down="handle.startDrag()"
|
||||
mct-drag="handle.continueDrag(delta)"
|
||||
|
@ -29,15 +29,21 @@
|
||||
</mct-representation>
|
||||
</div>
|
||||
<div class="btn-bar right l-flex-row flex-elem flex-justify-end flex-fixed">
|
||||
<mct-representation key="'switcher'"
|
||||
ng-model="representation"
|
||||
mct-object="domainObject">
|
||||
<mct-representation
|
||||
key="'switcher'"
|
||||
ng-model="representation"
|
||||
mct-object="domainObject">
|
||||
</mct-representation>
|
||||
<a class="s-button icon-expand t-btn-view-large"
|
||||
title="View large"
|
||||
mct-trigger-modal>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="abs object-holder">
|
||||
<mct-representation key="representation.selected.key"
|
||||
mct-object="representation.selected.key && domainObject">
|
||||
<mct-representation
|
||||
key="representation.selected.key"
|
||||
mct-object="representation.selected.key && domainObject">
|
||||
</mct-representation>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -19,19 +19,33 @@
|
||||
this source code distribution or the Licensing information page available
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<div class="l-layout"
|
||||
ng-controller="LayoutController as controller">
|
||||
|
||||
<div class='frame child-frame panel abs'
|
||||
<div class="abs l-layout"
|
||||
ng-controller="LayoutController as controller"
|
||||
ng-click="controller.clearSelection()">
|
||||
|
||||
<!-- Background grid -->
|
||||
<div class="l-grid-holder" ng-click="controller.clearSelection()">
|
||||
<div class="l-grid l-grid-x"
|
||||
ng-if="!controller.getGridSize()[0] < 3"
|
||||
ng-style="{ 'background-size': controller.getGridSize() [0] + 'px 100%' }"></div>
|
||||
<div class="l-grid l-grid-y"
|
||||
ng-if="!controller.getGridSize()[1] < 3"
|
||||
ng-style="{ 'background-size': '100% ' + controller.getGridSize() [1] + 'px' }"></div>
|
||||
</div>
|
||||
|
||||
<div class='abs frame t-frame-outer child-frame panel s-selectable s-moveable s-hover-border'
|
||||
ng-class="{ 'no-frame': !controller.hasFrame(childObject), 's-selected':controller.selected(childObject) }"
|
||||
ng-repeat="childObject in composition"
|
||||
ng-click="controller.select($event, childObject.getId())"
|
||||
ng-style="controller.getFrameStyle(childObject.getId())">
|
||||
|
||||
<mct-representation key="'frame'"
|
||||
class="frame child-frame holder contents abs"
|
||||
class="t-rep-frame holder contents abs"
|
||||
mct-object="childObject">
|
||||
</mct-representation>
|
||||
<!-- Drag handles -->
|
||||
<span ng-show="domainObject.hasCapability('editor')">
|
||||
<span class="abs t-edit-handle-holder s-hover-border" ng-if="controller.selected(childObject)">
|
||||
<span class="edit-handle edit-move"
|
||||
mct-drag-down="controller.startDrag(childObject.getId(), [1,1], [0,0])"
|
||||
mct-drag="controller.continueDrag(delta)"
|
||||
|
@ -33,6 +33,10 @@ define(
|
||||
DEFAULT_GRID_SIZE = [32, 32],
|
||||
MINIMUM_FRAME_SIZE = [320, 180];
|
||||
|
||||
var DEFAULT_HIDDEN_FRAME_TYPES = [
|
||||
'hyperlink'
|
||||
];
|
||||
|
||||
/**
|
||||
* The LayoutController is responsible for supporting the
|
||||
* Layout view. It arranges frames according to saved configuration
|
||||
@ -65,13 +69,10 @@ define(
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure that configuration field is populated
|
||||
$scope.configuration = $scope.configuration || {};
|
||||
// Make sure there is a "panels" field in the
|
||||
// view configuration.
|
||||
$scope.configuration.panels =
|
||||
$scope.configuration.panels || {};
|
||||
// Store the position of this panel.
|
||||
|
||||
$scope.configuration.panels[id] = {
|
||||
position: [
|
||||
Math.floor(position.x / self.gridSize[0]),
|
||||
@ -79,6 +80,11 @@ define(
|
||||
],
|
||||
dimensions: self.defaultDimensions()
|
||||
};
|
||||
|
||||
// Store the id so that the newly-dropped object
|
||||
// gets selected during refresh composition
|
||||
self.droppedIdToSelectAfterRefresh = id;
|
||||
|
||||
// Mark change as persistable
|
||||
if ($scope.commit) {
|
||||
$scope.commit("Dropped a frame.");
|
||||
@ -111,6 +117,13 @@ define(
|
||||
|
||||
$scope.composition = composition;
|
||||
self.layoutPanels(ids);
|
||||
self.setFrames(ids);
|
||||
|
||||
// If there is a newly-dropped object, select it.
|
||||
if (self.droppedIdToSelectAfterRefresh) {
|
||||
self.select(null, self.droppedIdToSelectAfterRefresh);
|
||||
delete self.droppedIdToSelectAfterRefresh;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -123,13 +136,18 @@ define(
|
||||
// saved by the EditRepresenter.
|
||||
$scope.configuration =
|
||||
$scope.configuration || {};
|
||||
// Make sure there is a "panels" field in the
|
||||
// view configuration.
|
||||
|
||||
$scope.configuration.panels =
|
||||
$scope.configuration.panels || {};
|
||||
// Store the position of this panel.
|
||||
|
||||
$scope.configuration.panels[self.activeDragId] =
|
||||
self.rawPositions[self.activeDragId];
|
||||
$scope.configuration.panels[self.activeDragId] || {};
|
||||
|
||||
$scope.configuration.panels[self.activeDragId].position =
|
||||
self.rawPositions[self.activeDragId].position;
|
||||
$scope.configuration.panels[self.activeDragId].dimensions =
|
||||
self.rawPositions[self.activeDragId].dimensions;
|
||||
|
||||
// Mark this object as dirty to encourage persistence
|
||||
if ($scope.commit) {
|
||||
$scope.commit("Moved frame.");
|
||||
@ -144,6 +162,10 @@ define(
|
||||
// Watch for changes to the grid size in the model
|
||||
$scope.$watch("model.layoutGrid", updateGridSize);
|
||||
|
||||
$scope.$watch("selection", function (selection) {
|
||||
this.selection = selection;
|
||||
}.bind(this));
|
||||
|
||||
// Update composed objects on screen, and position panes
|
||||
$scope.$watchCollection("model.composition", refreshComposition);
|
||||
|
||||
@ -151,6 +173,42 @@ define(
|
||||
$scope.$on("mctDrop", handleDrop);
|
||||
}
|
||||
|
||||
// Utility function to copy raw positions from configuration,
|
||||
// without writing directly to configuration (to avoid triggering
|
||||
// persistence from watchers during drags).
|
||||
function shallowCopy(obj, keys) {
|
||||
var copy = {};
|
||||
keys.forEach(function (k) {
|
||||
copy[k] = obj[k];
|
||||
});
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the frames value. If a configuration panel has "hasFrame' property,
|
||||
* use that value, otherwise set a default value. A 'hyperlink' object should
|
||||
* have no frame by default.
|
||||
*
|
||||
* @param {string[]} ids the object ids
|
||||
* @private
|
||||
*/
|
||||
LayoutController.prototype.setFrames = function (ids) {
|
||||
var panels = shallowCopy(this.$scope.configuration.panels || {}, ids);
|
||||
|
||||
this.frames = {};
|
||||
|
||||
this.$scope.composition.forEach(function (object) {
|
||||
var id = object.getId();
|
||||
panels[id] = panels[id] || {};
|
||||
|
||||
if (panels[id].hasOwnProperty('hasFrame')) {
|
||||
this.frames[id] = panels[id].hasFrame;
|
||||
} else {
|
||||
this.frames[id] = DEFAULT_HIDDEN_FRAME_TYPES.indexOf(object.getModel().type) === -1;
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
|
||||
// Convert from { positions: ..., dimensions: ... } to an
|
||||
// appropriate ng-style argument, to position frames.
|
||||
LayoutController.prototype.convertPosition = function (raw) {
|
||||
@ -237,6 +295,7 @@ define(
|
||||
this.gridSize
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Continue an active drag gesture.
|
||||
* @param {number[]} delta the offset, in pixels,
|
||||
@ -251,17 +310,6 @@ define(
|
||||
}
|
||||
};
|
||||
|
||||
// Utility function to copy raw positions from configuration,
|
||||
// without writing directly to configuration (to avoid triggering
|
||||
// persistence from watchers during drags).
|
||||
function shallowCopy(obj, keys) {
|
||||
var copy = {};
|
||||
keys.forEach(function (k) {
|
||||
copy[k] = obj[k];
|
||||
});
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute panel positions based on the layout's object model.
|
||||
* Defined as member function to facilitate testing.
|
||||
@ -293,9 +341,99 @@ define(
|
||||
* view configuration.
|
||||
*/
|
||||
LayoutController.prototype.endDrag = function () {
|
||||
this.dragInProgress = true;
|
||||
|
||||
setTimeout(function () {
|
||||
this.dragInProgress = false;
|
||||
}.bind(this), 0);
|
||||
|
||||
this.endDragInScope();
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the object is currently selected.
|
||||
*
|
||||
* @param {string} obj the object to check for selection
|
||||
* @returns {boolean} true if selected, otherwise false
|
||||
*/
|
||||
LayoutController.prototype.selected = function (obj) {
|
||||
return !!this.selectedId && this.selectedId === obj.getId();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the active user selection in this view.
|
||||
*
|
||||
* @param event the mouse event
|
||||
* @param {string} id the object id
|
||||
*/
|
||||
LayoutController.prototype.select = function (event, id) {
|
||||
if (event) {
|
||||
event.stopPropagation();
|
||||
if (this.selection) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
this.selectedId = id;
|
||||
|
||||
var selectedObj = {};
|
||||
selectedObj[this.frames[id] ? 'hideFrame' : 'showFrame'] = this.toggleFrame.bind(this, id);
|
||||
|
||||
if (this.selection) {
|
||||
this.selection.select(selectedObj);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback to show/hide the object frame.
|
||||
*
|
||||
* @param {string} id the object id
|
||||
* @private
|
||||
*/
|
||||
LayoutController.prototype.toggleFrame = function (id) {
|
||||
var configuration = this.$scope.configuration;
|
||||
|
||||
if (!configuration.panels[id]) {
|
||||
configuration.panels[id] = {};
|
||||
}
|
||||
|
||||
this.frames[id] = configuration.panels[id].hasFrame = !this.frames[id];
|
||||
this.select(undefined, id); // reselect so toolbar updates
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the current user selection.
|
||||
*/
|
||||
LayoutController.prototype.clearSelection = function () {
|
||||
if (this.dragInProgress) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.selection) {
|
||||
this.selection.deselect();
|
||||
delete this.selectedId;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the object has frame.
|
||||
*
|
||||
* @param {object} obj the object
|
||||
* @return {boolean} true if object has frame, otherwise false
|
||||
*/
|
||||
LayoutController.prototype.hasFrame = function (obj) {
|
||||
return this.frames[obj.getId()];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the size of the grid, in pixels. The returned array
|
||||
* is in the form `[x, y]`.
|
||||
* @returns {number[]} the grid size
|
||||
*/
|
||||
LayoutController.prototype.getGridSize = function () {
|
||||
return this.gridSize;
|
||||
};
|
||||
|
||||
return LayoutController;
|
||||
}
|
||||
);
|
||||
|
@ -27,7 +27,6 @@ define([
|
||||
) {
|
||||
|
||||
var OVERLAY_TEMPLATE = '' +
|
||||
'<div class="abs overlay l-large-view">' +
|
||||
' <div class="abs blocker"></div>' +
|
||||
' <div class="abs outer-holder">' +
|
||||
' <a class="close icon-x-in-circle"></a>' +
|
||||
@ -37,8 +36,7 @@ define([
|
||||
' <a class="t-done s-button major">Done</a>' +
|
||||
' </div>' +
|
||||
' </div>' +
|
||||
' </div>' +
|
||||
'</div>';
|
||||
' </div>';
|
||||
|
||||
/**
|
||||
* MCT Trigger Modal is intended for use in only one location: inside the
|
||||
@ -81,7 +79,8 @@ define([
|
||||
function openOverlay() {
|
||||
// Remove frame classes from being applied in a non-frame context
|
||||
$(frame).removeClass('frame frame-template');
|
||||
overlay = document.createElement('span');
|
||||
overlay = document.createElement('div');
|
||||
$(overlay).addClass('abs overlay l-large-view');
|
||||
overlay.innerHTML = OVERLAY_TEMPLATE;
|
||||
overlayContainer = overlay.querySelector('.t-contents');
|
||||
closeButton = overlay.querySelector('a.close');
|
||||
|
@ -49,10 +49,30 @@ define(
|
||||
},
|
||||
useCapability: function () {
|
||||
return mockCompositionCapability;
|
||||
},
|
||||
getModel: function () {
|
||||
if (id === 'b') {
|
||||
return {
|
||||
type : 'hyperlink'
|
||||
};
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Utility function to find a watch for a given expression
|
||||
function findWatch(expr) {
|
||||
var watch;
|
||||
mockScope.$watch.calls.forEach(function (call) {
|
||||
if (call.args[0] === expr) {
|
||||
watch = call.args[1];
|
||||
}
|
||||
});
|
||||
return watch;
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockScope = jasmine.createSpyObj(
|
||||
"$scope",
|
||||
@ -60,7 +80,7 @@ define(
|
||||
);
|
||||
mockEvent = jasmine.createSpyObj(
|
||||
'event',
|
||||
['preventDefault']
|
||||
['preventDefault', 'stopPropagation']
|
||||
);
|
||||
|
||||
testModel = {};
|
||||
@ -68,6 +88,7 @@ define(
|
||||
mockComposition = ["a", "b", "c"];
|
||||
mockCompositionObjects = mockComposition.map(mockDomainObject);
|
||||
|
||||
|
||||
testConfiguration = {
|
||||
panels: {
|
||||
a: {
|
||||
@ -82,10 +103,19 @@ define(
|
||||
mockScope.domainObject = mockDomainObject("mockDomainObject");
|
||||
mockScope.model = testModel;
|
||||
mockScope.configuration = testConfiguration;
|
||||
mockScope.selection = jasmine.createSpyObj(
|
||||
'selection',
|
||||
['select', 'get', 'selected', 'deselect']
|
||||
);
|
||||
|
||||
spyOn(mockScope.domainObject, "useCapability").andCallThrough();
|
||||
|
||||
controller = new LayoutController(mockScope);
|
||||
spyOn(controller, "layoutPanels").andCallThrough();
|
||||
|
||||
findWatch("selection")(mockScope.selection);
|
||||
|
||||
jasmine.Clock.useMock();
|
||||
});
|
||||
|
||||
// Model changes will indicate that panel positions
|
||||
@ -289,6 +319,111 @@ define(
|
||||
expect(controller.getFrameStyle("b"))
|
||||
.not.toEqual(oldStyle);
|
||||
});
|
||||
|
||||
it("allows panels to be selected", function () {
|
||||
mockScope.$watchCollection.mostRecentCall.args[1]();
|
||||
var childObj = mockCompositionObjects[0];
|
||||
|
||||
controller.select(mockEvent, childObj.getId());
|
||||
|
||||
expect(mockEvent.stopPropagation).toHaveBeenCalled();
|
||||
|
||||
expect(controller.selected(childObj)).toBe(true);
|
||||
});
|
||||
|
||||
it("allows selection to be cleared", function () {
|
||||
mockScope.$watchCollection.mostRecentCall.args[1]();
|
||||
var childObj = mockCompositionObjects[0];
|
||||
|
||||
controller.select(null, childObj.getId());
|
||||
controller.clearSelection();
|
||||
|
||||
expect(controller.selected(childObj)).toBeFalsy();
|
||||
});
|
||||
|
||||
it("prevents clearing selection while drag is in progress", function () {
|
||||
mockScope.$watchCollection.mostRecentCall.args[1]();
|
||||
var childObj = mockCompositionObjects[0];
|
||||
var id = childObj.getId();
|
||||
|
||||
controller.select(mockEvent, id);
|
||||
|
||||
// Do a drag
|
||||
controller.startDrag(id, [1, 1], [0, 0]);
|
||||
controller.continueDrag([100, 100]);
|
||||
controller.endDrag();
|
||||
|
||||
// Because mouse position could cause clearSelection to be called, this should be ignored.
|
||||
controller.clearSelection();
|
||||
|
||||
expect(controller.selected(childObj)).toBe(true);
|
||||
|
||||
// Shoud be able to clear the selection after dragging is done.
|
||||
jasmine.Clock.tick(0);
|
||||
controller.clearSelection();
|
||||
|
||||
expect(controller.selected(childObj)).toBe(false);
|
||||
});
|
||||
|
||||
it("clears selection after moving/resizing", function () {
|
||||
mockScope.$watchCollection.mostRecentCall.args[1]();
|
||||
var childObj = mockCompositionObjects[0];
|
||||
var id = childObj.getId();
|
||||
|
||||
controller.select(mockEvent, id);
|
||||
|
||||
// Do a drag
|
||||
controller.startDrag(id, [1, 1], [0, 0]);
|
||||
controller.continueDrag([100, 100]);
|
||||
controller.endDrag();
|
||||
|
||||
jasmine.Clock.tick(0);
|
||||
controller.clearSelection();
|
||||
|
||||
expect(controller.selected(childObj)).toBe(false);
|
||||
});
|
||||
|
||||
it("shows frames by default", function () {
|
||||
mockScope.$watchCollection.mostRecentCall.args[1]();
|
||||
|
||||
expect(controller.hasFrame(mockCompositionObjects[0])).toBe(true);
|
||||
});
|
||||
|
||||
it("hyperlinks hide frame by default", function () {
|
||||
mockScope.$watchCollection.mostRecentCall.args[1]();
|
||||
|
||||
expect(controller.hasFrame(mockCompositionObjects[1])).toBe(false);
|
||||
});
|
||||
|
||||
it("hides frame when selected object has frame ", function () {
|
||||
mockScope.$watchCollection.mostRecentCall.args[1]();
|
||||
var childObj = mockCompositionObjects[0];
|
||||
controller.select(mockEvent, childObj.getId());
|
||||
|
||||
expect(mockScope.selection.select).toHaveBeenCalled();
|
||||
|
||||
var selectedObj = mockScope.selection.select.mostRecentCall.args[0];
|
||||
|
||||
expect(controller.hasFrame(childObj)).toBe(true);
|
||||
expect(selectedObj.hideFrame).toBeDefined();
|
||||
expect(selectedObj.hideFrame).toEqual(jasmine.any(Function));
|
||||
});
|
||||
|
||||
it("shows frame when selected object has no frame", function () {
|
||||
mockScope.$watchCollection.mostRecentCall.args[1]();
|
||||
|
||||
var childObj = mockCompositionObjects[1];
|
||||
controller.select(mockEvent, childObj.getId());
|
||||
|
||||
expect(mockScope.selection.select).toHaveBeenCalled();
|
||||
|
||||
var selectedObj = mockScope.selection.select.mostRecentCall.args[0];
|
||||
|
||||
expect(controller.hasFrame(childObj)).toBe(false);
|
||||
expect(selectedObj.showFrame).toBeDefined();
|
||||
expect(selectedObj.showFrame).toEqual(jasmine.any(Function));
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user