Reimplementation of Display Layout in Vue (#2185)

* Saving work

* Fix conflict

* Position the panels by setting the style

* Put the div back with height set to 100% in ObjectView.
Add markup for  drag handles.

* Use default position and dimensions if the layout panel is missing those values. Set s-status-editing on the main div to get the drag handles appear for now.

* Display Layout and frames major improvements

- Moved Toolbar out of Layout.vue and into DisplayLayout.vue;
- Styles for object view, Layout, Frame, etc.
- Major refactor of markup for frame;
- Added abs() mixin;
- Styles for is-editing done;
- Styles for
- TODO: styles for selectable, moveable, etc.

* Implement drill in gesture.

* Hide the background grid when a frame is drilled in

* Edit styling and toolbar WIP

- c-search styles moved mostly into mixin;
- New .c-labeled-input class;
- Browser overrides for number-type input spinners in webkit;

* Toolbar WIP

- Custom wrapped number input added;
- Toolbar buttons WIP;

* New toolbar buttons WIP

* Define a computed property for the css class object.

* Frame edit handles markup and styling

* Toolbar, editing and selection style refinements

- Moved toolbar back into Layout.vue;
- Hard-coded 'is-editing' onto __pane-main for now,
removed from DisplayLayout.vue;
- Styles for frame in LayoutFrame.vue:
-- editing default (dotted border)
-- editing .s-selected
-- .s-drilled-in (renamed .is-drilled-in)

* Refactoring button classes

- Lots of stuff broken right now;
- TODO: lots of renaming (c-menu-button, c-icon-button, etc.);
- Removed import of _controls in search.vue;

* Fixes for selection on nested selected elements

* Fix conflict

* Significant refactoring of button and click-icon classes

- Markup and CSS updated;
- Toolbar in good shape, prior to merge of vue-layout;

* Fix issues with relative font-sizing

* Add color palette markup and CSS

- Also added Layers menu example;

* Font, font-size glyphs and size menu, and more

- Added art for font glyph and renamed from .icon-T;
- Added new glyph for font-size;
- Fixed font-sizing in controls;
- Added font-size menu;
- Re-org'd toolbar items;

* Styling tweak for c-labeled-input

- Code cleanup as well;

* Fixes for toolbar toggleMenus and labeledNumberInput

* Implement resize and move for frames.
Added stub for drag 'n drop.

* Add custom checkbox control.

- Also, code cleanup.

* Add toggleButton component

- Code and examples

* Custom checkbox code cleanups, sanding

* - Persist new position/dimensions on the domain object when frames are moved/resized.
- Bypass the selection of the layout when dragging a frame is finished to keep the frame selected.
- Set the grid size to layoutGrid from domain object or use default if it's not specified.

* Fix conflict

* Implement resize and move for frames.
Added stub for drag 'n drop.

* Remove old layout view, was triggering View Switcher
and massive confusion when viewing Layouts

- TODO: add view provider for new Layout

* Enable drag and drop

* Changed example toggle-button

- Now uses show/hide frame as toggle-button example;

* Added pseudocode for handling drag/drop composition change

* Add copyright notice

* Layout frame and contained components styling

- Hyperlinks, Hyperlink buttons, Summary Widgets now use `.u-links`
which disables their pointer-events when editing;
- Hyperlink buttons, Summary Widgets now expand to fill their
containers in a Layout;
- Markup and JS for Hyperlinks, Hyperlink buttons, Summary Widgets
somewhat
modded to use new classing, applied in legacy scss files;

* Fix icon sizing error in BrowseBar

* Edit and selecting styling for Layouts

- WIP!

* Edit and selecting styling for Layout frames

- Color vars more standardized;
- Hover and *-selected styles;

* Getting vue-toolbar reverted back to latest

- Merging this branch with vue-layout may cause conflicts;

* - Implement drag ’n drop.
- Set hasFrame to a default value if it’s not set on the configuration.
- Emit an event to the parent wrapper component to update the ‘newDomainObject’ prop whenever the domain object is mutated in the display layout child component.

* Revert emitting 'update:object' event to the parent.

* New branch from topic-core-refactor to use as central point for common
CSS work

- Manually migrated changes from vue-toolbar, expect conflicts there and
 in vue-layout;

* Manually update constants-snow from vue-toolbar branch

* Update markup to use latest button classnames

- c-menu-button > c-button--menu;
- c-icon-button > c-click-icon;

* Various from vue-conductor-style

- Mods to input styling;
- Input[] styles moved to _controls;
- New/revised constants vals;

* Resolve bizarre merge conflict when applying stash

* Code cleanup

* Alias and type-icon fixes

- More robust approach to alias indicators;
- Added alias indication to tree-item.vue;
- TODO: wire up alias indication tree-item.vue;

* Accessibility mods, convert elements to <button>

- Better reset styles for htmlInputReset mixin to allow use of <button>
without browser default styling;
- Create button;
- BrowseBar action buttons;
- c-click-icons;
- Removed Preview button from BrowseBar.vue;

* Fix styles that were affected during resolving conflicts

* Moved draggable into __label element rather than whole <li>

* Change the priority to 100 to get the view working properly

* Code cleanup

* Remove angular layout

* Display the object name in the frame header

* Tweaks to __header in LayoutFrame

- Name now does not overflow frame edge;
- Layout strategy now in parity with similar elements in main view;

* Remove test()

* Add a type for display layout to make it appear in the Create menu.

* Change the key type to 'layout'

* Clean up code and hide toolbar

* Enable toolbar, and revert changes in webpack config

* Remove commented code

* revert to the original code
This commit is contained in:
Pegah Sarram 2018-10-04 15:59:23 -07:00 committed by Pete Richards
parent afca6cd2e9
commit e7cdb334de
37 changed files with 1799 additions and 1282 deletions

View File

@ -58,7 +58,7 @@ define([], function () {
function checkNavigation() {
var navigatedObject = navigationService.getNavigation();
if (navigatedObject.hasCapability('context')) {
if (navigatedObject && navigatedObject.hasCapability('context')) {
if (!navigatedObject.getCapability('editor').isEditContextRoot()) {
preventOrphanNavigation(navigatedObject);
}

View File

@ -19,10 +19,10 @@
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<a class="l-hyperlink s-hyperlink" ng-controller="HyperlinkController as hyperlink" href="{{domainObject.getModel().url}}"
<a class="c-hyperlink u-links" ng-controller="HyperlinkController as hyperlink" href="{{domainObject.getModel().url}}"
ng-attr-target="{{hyperlink.openNewTab() ? '_blank' : undefined}}"
ng-class="{
's-button': hyperlink.isButton()
}">
<span class="label">{{domainObject.getModel().displayText}}</span>
'c-hyperlink--button u-fills-container' : hyperlink.isButton(),
'c-hyperlink--link' : !hyperlink.isButton() }">
<span class="c-hyperlink__label">{{domainObject.getModel().displayText}}</span>
</a>

View File

@ -1,83 +0,0 @@
<!--
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.
-->
<div class="abs l-layout {{ domainObject.getModel().layoutAdvancedCss }}"
ng-controller="LayoutController as controller"
ng-click="controller.bypassSelection($event)">
<!-- Background grid -->
<div class="l-grid-holder"
ng-show="!controller.drilledIn"
ng-click="controller.bypassSelection($event)">
<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="frame t-frame-outer child-frame panel s-selectable s-moveable s-hover-border t-object-type-{{ childObject.getModel().type }}"
data-layout-id="{{childObject.getId() + '-' + $id}}"
ng-class="{ 'no-frame': !controller.hasFrame(childObject), 's-drilled-in': controller.isDrilledIn(childObject) }"
ng-repeat="childObject in composition"
ng-init="controller.selectIfNew(childObject.getId() + '-' + $id, childObject)"
mct-selectable="controller.getContext(childObject)"
ng-dblclick="controller.drill($event, childObject)"
ng-style="controller.getFrameStyle(childObject.getId())">
<mct-representation key="'frame'"
class="t-rep-frame holder contents abs"
mct-object="childObject">
</mct-representation>
<!-- Drag handles -->
<span class="abs t-edit-handle-holder" ng-if="controller.selected(childObject) && !controller.isDrilledIn(childObject)">
<span class="edit-handle edit-move"
mct-drag-down="controller.startDrag(childObject.getId(), [1,1], [0,0])"
mct-drag="controller.continueDrag(delta)"
mct-drag-up="controller.endDrag()">
</span>
<span class="edit-corner edit-resize-nw"
mct-drag-down="controller.startDrag(childObject.getId(), [1,1], [-1,-1])"
mct-drag="controller.continueDrag(delta)"
mct-drag-up="controller.endDrag()">
</span>
<span class="edit-corner edit-resize-ne"
mct-drag-down="controller.startDrag(childObject.getId(), [0,1], [1,-1])"
mct-drag="controller.continueDrag(delta)"
mct-drag-up="controller.endDrag()">
</span>
<span class="edit-corner edit-resize-sw"
mct-drag-down="controller.startDrag(childObject.getId(), [1,0], [-1,1])"
mct-drag="controller.continueDrag(delta)"
mct-drag-up="controller.endDrag()">
</span>
<span class="edit-corner edit-resize-se"
mct-drag-down="controller.startDrag(childObject.getId(), [0,0], [1,1])"
mct-drag="controller.continueDrag(delta)"
mct-drag-up="controller.endDrag()">
</span>
</span>
</div>
</div>

View File

@ -1,524 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
/**
* This bundle implements object types and associated views for
* display-building.
* @namespace platform/features/layout
*/
define(
[
'zepto',
'./LayoutDrag'
],
function (
$,
LayoutDrag
) {
var DEFAULT_DIMENSIONS = [12, 8],
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
* and provides methods for updating these based on mouse
* movement.
* @memberof platform/features/layout
* @constructor
* @param {Scope} $scope the controller's Angular scope
*/
function LayoutController($scope, $element, openmct) {
var self = this,
callbackCount = 0;
this.$element = $element;
// Update grid size when it changed
function updateGridSize(layoutGrid) {
var oldSize = self.gridSize;
self.gridSize = layoutGrid || DEFAULT_GRID_SIZE;
// Only update panel positions if this actually changed things
if (self.gridSize[0] !== oldSize[0] ||
self.gridSize[1] !== oldSize[1]) {
self.layoutPanels(Object.keys(self.positions));
}
}
// Position a panel after a drop event
function handleDrop(e, id, position) {
if (e.defaultPrevented) {
return;
}
$scope.configuration = $scope.configuration || {};
$scope.configuration.panels = $scope.configuration.panels || {};
self.openmct.objects.get(id).then(function (object) {
$scope.configuration.panels[id] = {
position: [
Math.floor(position.x / self.gridSize[0]),
Math.floor(position.y / self.gridSize[1])
],
dimensions: self.defaultDimensions(),
hasFrame: self.getDefaultFrame(object.type)
};
// Store the id so that the newly-dropped object
// gets selected during refresh composition
self.droppedIdToSelectAfterRefresh = id;
self.commit();
// Populate template-facing position for this id
self.rawPositions[id] = $scope.configuration.panels[id];
self.populatePosition(id);
refreshComposition();
});
// Layout may contain embedded views which will
// listen for drops, so call preventDefault() so
// that they can recognize that this event is handled.
e.preventDefault();
}
//Will fetch fully contextualized composed objects, and populate
// scope with them.
function refreshComposition() {
//Keep a track of how many composition callbacks have been made
var thisCount = ++callbackCount;
$scope.domainObject.useCapability('composition').then(function (composition) {
var ids;
//Is this callback for the most recent composition
// request? If not, discard it. Prevents race condition
if (thisCount === callbackCount) {
ids = composition.map(function (object) {
return object.getId();
}) || [];
$scope.composition = composition;
self.layoutPanels(ids);
self.setFrames(ids);
if (self.selectedId &&
self.selectedId !== $scope.domainObject.getId() &&
composition.indexOf(self.selectedId) === -1) {
// Click triggers selection of layout parent.
self.$element[0].click();
}
}
});
}
// End drag; we don't want to put $scope into this
// because it triggers "cpws" (copy window or scope)
// errors in Angular.
this.endDragInScope = function () {
// Write to configuration; this is watched and
// saved by the EditRepresenter.
$scope.configuration =
$scope.configuration || {};
$scope.configuration.panels =
$scope.configuration.panels || {};
$scope.configuration.panels[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;
self.commit();
};
// Sets the selectable object in response to the selection change event.
function setSelection(selectable) {
var selection = selectable[0];
if (!selection) {
delete self.selectedId;
return;
}
self.selectedId = selection.context.oldItem.getId();
self.drilledIn = undefined;
self.selectable = selectable;
}
this.positions = {};
this.rawPositions = {};
this.gridSize = DEFAULT_GRID_SIZE;
this.$scope = $scope;
this.drilledIn = undefined;
this.openmct = openmct;
// Watch for changes to the grid size in the model
$scope.$watch("model.layoutGrid", updateGridSize);
// Update composed objects on screen, and position panes
$scope.$watchCollection("model.composition", refreshComposition);
openmct.selection.on('change', setSelection);
$scope.$on("$destroy", function () {
openmct.selection.off("change", setSelection);
self.unlisten();
});
$scope.$on("mctDrop", handleDrop);
self.unlisten = self.$scope.domainObject.getCapability('mutation').listen(function (model) {
$scope.configuration = model.configuration.layout;
$scope.model = model;
var panels = $scope.configuration.panels;
Object.keys(panels).forEach(function (key) {
if (self.frames && self.frames.hasOwnProperty(key)) {
self.frames[key] = panels[key].hasFrame;
}
});
});
}
// 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] = this.getDefaultFrame(object.getModel().type);
}
}, this);
};
/**
* Gets the default value for frame.
*
* @param type the domain object type
* @return {boolean} true if the object should have
* frame by default, false, otherwise
*/
LayoutController.prototype.getDefaultFrame = function (type) {
return DEFAULT_HIDDEN_FRAME_TYPES.indexOf(type) === -1;
};
// Convert from { positions: ..., dimensions: ... } to an
// appropriate ng-style argument, to position frames.
LayoutController.prototype.convertPosition = function (raw) {
var gridSize = this.gridSize;
// Multiply position/dimensions by grid size
return {
left: (gridSize[0] * raw.position[0]) + 'px',
top: (gridSize[1] * raw.position[1]) + 'px',
width: (gridSize[0] * raw.dimensions[0]) + 'px',
height: (gridSize[1] * raw.dimensions[1]) + 'px',
minWidth: (gridSize[0] * raw.dimensions[0]) + 'px',
minHeight: (gridSize[1] * raw.dimensions[1]) + 'px'
};
};
// Generate default positions for a new panel
LayoutController.prototype.defaultDimensions = function () {
var gridSize = this.gridSize;
return MINIMUM_FRAME_SIZE.map(function (min, i) {
return Math.max(
Math.ceil(min / gridSize[i]),
DEFAULT_DIMENSIONS[i]
);
});
};
// Generate a default position (in its raw format) for a frame.
// Use an index to ensure that default positions are unique.
LayoutController.prototype.defaultPosition = function (index) {
return {
position: [index, index],
dimensions: this.defaultDimensions()
};
};
// Store a computed position for a contained frame by its
// domain object id. Called in a forEach loop, so arguments
// are as expected there.
LayoutController.prototype.populatePosition = function (id, index) {
this.rawPositions[id] =
this.rawPositions[id] || this.defaultPosition(index || 0);
this.positions[id] =
this.convertPosition(this.rawPositions[id]);
};
/**
* Get a style object for a frame with the specified domain
* object identifier, suitable for use in an `ng-style`
* directive to position a frame as configured for this layout.
* @param {string} id the object identifier
* @returns {Object.<string, string>} an object with
* appropriate left, width, etc fields for positioning
*/
LayoutController.prototype.getFrameStyle = function (id) {
// Called in a loop, so just look up; the "positions"
// object is kept up to date by a watch.
return this.positions[id];
};
/**
* Start a drag gesture to move/resize a frame.
*
* The provided position and dimensions factors will determine
* whether this is a move or a resize, and what type it
* will be. For instance, a position factor of [1, 1]
* will move a frame along with the mouse as the drag
* proceeds, while a dimension factor of [0, 0] will leave
* dimensions unchanged. Combining these in different
* ways results in different handles; a position factor of
* [1, 0] and a dimensions factor of [-1, 0] will implement
* a left-edge resize, as the horizontal position will move
* with the mouse while the horizontal dimensions shrink in
* kind (and vertical properties remain unmodified.)
*
* @param {string} id the identifier of the domain object
* in the frame being manipulated
* @param {number[]} posFactor the position factor
* @param {number[]} dimFactor the dimensions factor
*/
LayoutController.prototype.startDrag = function (id, posFactor, dimFactor) {
this.activeDragId = id;
this.activeDrag = new LayoutDrag(
this.rawPositions[id],
posFactor,
dimFactor,
this.gridSize
);
};
/**
* Continue an active drag gesture.
* @param {number[]} delta the offset, in pixels,
* of the current pointer position, relative
* to its position when the drag started
*/
LayoutController.prototype.continueDrag = function (delta) {
if (this.activeDrag) {
this.rawPositions[this.activeDragId] =
this.activeDrag.getAdjustedPosition(delta);
this.populatePosition(this.activeDragId);
}
};
/**
* Compute panel positions based on the layout's object model.
* Defined as member function to facilitate testing.
* @private
*/
LayoutController.prototype.layoutPanels = function (ids) {
var configuration = this.$scope.configuration || {},
self = this;
// Pull panel positions from configuration
this.rawPositions =
shallowCopy(configuration.panels || {}, ids);
// Clear prior computed positions
this.positions = {};
// Update width/height that we are tracking
this.gridSize =
(this.$scope.model || {}).layoutGrid || DEFAULT_GRID_SIZE;
// Compute positions and add defaults where needed
ids.forEach(function (id, index) {
self.populatePosition(id, index);
});
};
/**
* End the active drag gesture. This will update the
* view configuration.
*/
LayoutController.prototype.endDrag = function () {
this.dragInProgress = true;
setTimeout(function () {
this.dragInProgress = false;
}.bind(this), 0);
this.endDragInScope();
};
/**
* Checks 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) {
var sobj = this.openmct.selection.get()[0];
return (sobj && sobj.context.oldItem.getId() === obj.getId()) ? true : false;
};
/**
* Bypasses selection if drag is in progress.
*
* @param event the angular event object
*/
LayoutController.prototype.bypassSelection = function (event) {
if (this.dragInProgress) {
if (event) {
event.stopPropagation();
}
return;
}
};
/**
* Checks if the domain object is drilled in.
*
* @param domainObject the domain object
* @return true if the object is drilled in, false otherwise
*/
LayoutController.prototype.isDrilledIn = function (domainObject) {
return this.drilledIn === domainObject.getId();
};
/**
* Puts the given object in the drilled-in mode.
*
* @param event the angular event object
* @param domainObject the domain object
*/
LayoutController.prototype.drill = function (event, domainObject) {
if (event) {
event.stopPropagation();
}
if (!domainObject.getCapability('editor').inEditContext()) {
return;
}
if (!domainObject.hasCapability('composition')) {
return;
}
// Disable since fixed position doesn't use the selection API yet
if (domainObject.getModel().type === 'telemetry.fixed') {
return;
}
this.drilledIn = domainObject.getId();
};
/**
* 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;
};
/**
* Gets the selection context.
*
* @param domainObject the domain object
* @returns {object} the context object which includes item and oldItem
*/
LayoutController.prototype.getContext = function (domainObject) {
return {
item: domainObject.useCapability('adapter'),
oldItem: domainObject
};
};
LayoutController.prototype.commit = function () {
var model = this.$scope.model;
model.configuration = model.configuration || {};
model.configuration.layout = this.$scope.configuration;
this.$scope.domainObject.useCapability('mutation', function () {
return model;
});
};
/**
* Selects a newly-dropped object.
*
* @param classSelector the css class selector
* @param domainObject the domain object
*/
LayoutController.prototype.selectIfNew = function (selector, domainObject) {
if (domainObject.getId() === this.droppedIdToSelectAfterRefresh) {
setTimeout(function () {
$('[data-layout-id="' + selector + '"]')[0].click();
delete this.droppedIdToSelectAfterRefresh;
}.bind(this), 0);
}
};
return LayoutController;
}
);

View File

@ -1,479 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
[
"../src/LayoutController",
"zepto"
],
function (
LayoutController,
$
) {
describe("The Layout controller", function () {
var mockScope,
mockEvent,
testModel,
testConfiguration,
controller,
mockCompositionCapability,
mockComposition,
mockCompositionObjects,
mockOpenMCT,
mockSelection,
mockDomainObjectCapability,
mockObjects,
unlistenFunc,
$element = [],
selectable = [];
function mockPromise(value) {
return {
then: function (thenFunc) {
return mockPromise(thenFunc(value));
}
};
}
function mockDomainObject(id) {
return {
getId: function () {
return id;
},
useCapability: function () {
return mockCompositionCapability;
},
getModel: function () {
if (id === 'b') {
return {
type : 'hyperlink'
};
} else {
return {};
}
},
getCapability: function () {
return mockDomainObjectCapability;
},
hasCapability: function (param) {
if (param === 'composition') {
return id !== 'b';
}
},
type: "testType"
};
}
beforeEach(function () {
mockScope = jasmine.createSpyObj(
"$scope",
["$watch", "$watchCollection", "$on"]
);
mockEvent = jasmine.createSpyObj(
'event',
['preventDefault', 'stopPropagation']
);
testModel = {};
mockComposition = ["a", "b", "c"];
mockCompositionObjects = mockComposition.map(mockDomainObject);
testConfiguration = {
panels: {
a: {
position: [20, 10],
dimensions: [5, 5]
}
}
};
unlistenFunc = jasmine.createSpy("unlisten");
mockDomainObjectCapability = jasmine.createSpyObj('capability',
['inEditContext', 'listen']
);
mockDomainObjectCapability.listen.and.returnValue(unlistenFunc);
mockCompositionCapability = mockPromise(mockCompositionObjects);
mockScope.domainObject = mockDomainObject("mockDomainObject");
mockScope.model = testModel;
mockScope.configuration = testConfiguration;
selectable[0] = {
context: {
oldItem: mockScope.domainObject
}
};
mockSelection = jasmine.createSpyObj("selection", [
'select',
'on',
'off',
'get'
]);
mockSelection.get.and.returnValue(selectable);
mockObjects = jasmine.createSpyObj('objects', [
'get'
]);
mockObjects.get.and.returnValue(mockPromise(mockDomainObject("mockObject")));
mockOpenMCT = {
selection: mockSelection,
objects: mockObjects
};
$element = $('<div></div>');
$(document).find('body').append($element);
spyOn($element[0], 'click');
spyOn(mockScope.domainObject, "useCapability").and.callThrough();
controller = new LayoutController(mockScope, $element, mockOpenMCT);
spyOn(controller, "layoutPanels").and.callThrough();
spyOn(controller, "commit");
jasmine.clock().install();
});
afterEach(function () {
$element.remove();
jasmine.clock().uninstall();
});
it("listens for selection change events", function () {
expect(mockOpenMCT.selection.on).toHaveBeenCalledWith(
'change',
jasmine.any(Function)
);
});
it("cleans up on scope destroy", function () {
expect(mockScope.$on).toHaveBeenCalledWith(
'$destroy',
jasmine.any(Function)
);
mockScope.$on.calls.all()[0].args[1]();
expect(mockOpenMCT.selection.off).toHaveBeenCalledWith(
'change',
jasmine.any(Function)
);
});
// Model changes will indicate that panel positions
// may have changed, for instance.
it("watches for changes to composition", function () {
expect(mockScope.$watchCollection).toHaveBeenCalledWith(
"model.composition",
jasmine.any(Function)
);
});
it("Retrieves updated composition from composition capability", function () {
mockScope.$watchCollection.calls.mostRecent().args[1]();
expect(mockScope.domainObject.useCapability).toHaveBeenCalledWith(
"composition"
);
expect(controller.layoutPanels).toHaveBeenCalledWith(
mockComposition
);
});
it("Is robust to concurrent changes to composition", function () {
var secondMockComposition = ["a", "b", "c", "d"],
secondMockCompositionObjects = secondMockComposition.map(mockDomainObject),
firstCompositionCB,
secondCompositionCB;
spyOn(mockCompositionCapability, "then");
mockScope.$watchCollection.calls.mostRecent().args[1]();
mockScope.$watchCollection.calls.mostRecent().args[1]();
firstCompositionCB = mockCompositionCapability.then.calls.all()[0].args[0];
secondCompositionCB = mockCompositionCapability.then.calls.all()[1].args[0];
//Resolve promises in reverse order
secondCompositionCB(secondMockCompositionObjects);
firstCompositionCB(mockCompositionObjects);
//Expect the promise call that was initiated most recently to
// be the one used to populate scope, irrespective of order that
// it was eventually resolved
expect(mockScope.composition).toBe(secondMockCompositionObjects);
});
it("provides styles for frames, from configuration", function () {
mockScope.$watchCollection.calls.mostRecent().args[1]();
expect(controller.getFrameStyle("a")).toEqual({
top: "320px",
left: "640px",
width: "160px",
height: "160px",
minWidth : '160px',
minHeight : '160px'
});
});
it("provides default styles for frames", function () {
var styleB, styleC;
// b and c do not have configured positions
mockScope.$watchCollection.calls.mostRecent().args[1]();
styleB = controller.getFrameStyle("b");
styleC = controller.getFrameStyle("c");
// Should have a position, but we don't care what
expect(styleB.left).toBeDefined();
expect(styleB.top).toBeDefined();
expect(styleC.left).toBeDefined();
expect(styleC.top).toBeDefined();
// Should have ensured some difference in position
expect(styleB).not.toEqual(styleC);
});
it("allows panels to be dragged", function () {
// Populate scope
mockScope.$watchCollection.calls.mostRecent().args[1]();
// Verify precondition
expect(testConfiguration.panels.b).not.toBeDefined();
// Do a drag
controller.startDrag("b", [1, 1], [0, 0]);
controller.continueDrag([100, 100]);
controller.endDrag();
// We do not look closely at the details here;
// that is tested in LayoutDragSpec. Just make sure
// that a configuration for b has been defined.
expect(testConfiguration.panels.b).toBeDefined();
});
it("invokes commit after drag", function () {
// Populate scope
mockScope.$watchCollection.calls.mostRecent().args[1]();
// Do a drag
controller.startDrag("b", [1, 1], [0, 0]);
controller.continueDrag([100, 100]);
controller.endDrag();
expect(controller.commit).toHaveBeenCalled();
});
it("listens for drop events", function () {
// Layout should position panels according to
// where the user dropped them, so it needs to
// listen for drop events.
expect(mockScope.$on).toHaveBeenCalledWith(
'mctDrop',
jasmine.any(Function)
);
// Verify precondition
expect(testConfiguration.panels.d).not.toBeDefined();
// Notify that a drop occurred
mockScope.$on.calls.mostRecent().args[1](
mockEvent,
'd',
{ x: 300, y: 100 }
);
expect(testConfiguration.panels.d).toBeDefined();
expect(mockEvent.preventDefault).toHaveBeenCalled();
expect(controller.commit).toHaveBeenCalled();
});
it("ignores drops when default has been prevented", function () {
// Avoids redundant drop-handling, WTD-1233
mockEvent.defaultPrevented = true;
// Notify that a drop occurred
mockScope.$on.calls.mostRecent().args[1](
mockEvent,
'd',
{ x: 300, y: 100 }
);
expect(testConfiguration.panels.d).not.toBeDefined();
});
it("ensures a minimum frame size", function () {
var styleB;
// Start with a very small frame size
testModel.layoutGrid = [1, 1];
// White-boxy; we know which watch is which
mockScope.$watch.calls.all()[0].args[1](testModel.layoutGrid);
mockScope.$watchCollection.calls.all()[0].args[1](testModel.composition);
styleB = controller.getFrameStyle("b");
// Resulting size should still be reasonably large pixel-size
expect(parseInt(styleB.width, 10)).toBeGreaterThan(63);
expect(parseInt(styleB.width, 10)).toBeGreaterThan(31);
});
it("ensures a minimum frame size on drop", function () {
var style;
// Start with a very small frame size
testModel.layoutGrid = [1, 1];
mockScope.$watch.calls.all()[0].args[1](testModel.layoutGrid);
// Add a new object to the composition
mockComposition = ["a", "b", "c", "d"];
mockCompositionObjects = mockComposition.map(mockDomainObject);
mockCompositionCapability = mockPromise(mockCompositionObjects);
// Notify that a drop occurred
mockScope.$on.calls.mostRecent().args[1](
mockEvent,
'd',
{ x: 300, y: 100 }
);
style = controller.getFrameStyle("d");
// Resulting size should still be reasonably large pixel-size
expect(parseInt(style.width, 10)).toBeGreaterThan(63);
expect(parseInt(style.height, 10)).toBeGreaterThan(31);
});
it("updates positions of existing objects on a drop", function () {
var oldStyle;
mockScope.$watchCollection.calls.mostRecent().args[1]();
oldStyle = controller.getFrameStyle("b");
expect(oldStyle).toBeDefined();
// ...drop event...
mockScope.$on.calls.mostRecent()
.args[1](mockEvent, 'b', { x: 300, y: 100 });
expect(controller.getFrameStyle("b"))
.not.toEqual(oldStyle);
});
it("allows objects to be selected", function () {
mockScope.$watchCollection.calls.mostRecent().args[1]();
var childObj = mockCompositionObjects[0];
selectable[0].context.oldItem = childObj;
mockOpenMCT.selection.on.calls.mostRecent().args[1](selectable);
expect(controller.selected(childObj)).toBe(true);
});
it("prevents event bubbling while drag is in progress", function () {
mockScope.$watchCollection.calls.mostRecent().args[1]();
var childObj = mockCompositionObjects[0];
// Do a drag
controller.startDrag(childObj.getId(), [1, 1], [0, 0]);
controller.continueDrag([100, 100]);
controller.endDrag();
// Because mouse position could cause the parent object to be selected, this should be ignored.
controller.bypassSelection(mockEvent);
expect(mockEvent.stopPropagation).toHaveBeenCalled();
// Shoud be able to select another object when dragging is done.
jasmine.clock().tick(0);
mockEvent.stopPropagation.calls.reset();
controller.bypassSelection(mockEvent);
expect(mockEvent.stopPropagation).not.toHaveBeenCalled();
});
it("shows frames by default", function () {
mockScope.$watchCollection.calls.mostRecent().args[1]();
expect(controller.hasFrame(mockCompositionObjects[0])).toBe(true);
});
it("hyperlinks hide frame by default", function () {
mockScope.$watchCollection.calls.mostRecent().args[1]();
expect(controller.hasFrame(mockCompositionObjects[1])).toBe(false);
});
it("selects the parent object when selected object is removed", function () {
mockScope.$watchCollection.calls.mostRecent().args[1]();
var childObj = mockCompositionObjects[0];
selectable[0].context.oldItem = childObj;
mockOpenMCT.selection.on.calls.mostRecent().args[1](selectable);
var composition = ["b", "c"];
mockScope.$watchCollection.calls.mostRecent().args[1](composition);
expect($element[0].click).toHaveBeenCalled();
});
it("allows objects to be drilled-in only when editing", function () {
mockScope.$watchCollection.calls.mostRecent().args[1]();
var childObj = mockCompositionObjects[0];
childObj.getCapability().inEditContext.and.returnValue(false);
controller.drill(mockEvent, childObj);
expect(controller.isDrilledIn(childObj)).toBe(false);
});
it("allows objects to be drilled-in only if it has sub objects", function () {
mockScope.$watchCollection.calls.mostRecent().args[1]();
var childObj = mockCompositionObjects[1];
childObj.getCapability().inEditContext.and.returnValue(true);
controller.drill(mockEvent, childObj);
expect(controller.isDrilledIn(childObj)).toBe(false);
});
it("selects a newly-dropped object", function () {
mockScope.$on.calls.mostRecent().args[1](
mockEvent,
'd',
{ x: 300, y: 100 }
);
var childObj = mockDomainObject("d");
var testElement = $("<div data-layout-id='some-id'></div>");
$element.append(testElement);
spyOn(testElement[0], 'click');
controller.selectIfNew('some-id', childObj);
jasmine.clock().tick(0);
expect(testElement[0].click).toHaveBeenCalled();
});
});
}
);

View File

@ -228,6 +228,7 @@ define([
this.legacyRegistry = defaultRegistry;
this.install(this.plugins.Plot());
this.install(this.plugins.TelemetryTable());
this.install(this.plugins.DisplayLayout());
if (typeof BUILD_CONSTANTS !== 'undefined') {
this.install(buildInfoPlugin(BUILD_CONSTANTS));

View File

@ -56,7 +56,6 @@ define([
'../platform/features/clock/bundle',
'../platform/features/fixed/bundle',
'../platform/features/imagery/bundle',
'../platform/features/layout/bundle',
'../platform/features/my-items/bundle',
'../platform/features/pages/bundle',
'../platform/features/hyperlink/bundle',
@ -99,7 +98,6 @@ define([
'platform/features/clock',
'platform/features/fixed',
'platform/features/imagery',
'platform/features/layout',
'platform/features/pages',
'platform/features/hyperlink',
'platform/features/timeline',

View File

@ -0,0 +1,331 @@
/*****************************************************************************
* 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="l-layout"
@dragover="handleDragOver"
@click="bypassSelection"
@drop="handleDrop">
<div class="l-layout__object">
<!-- Background grid -->
<div class="l-layout__grid-holder c-grid"
v-if="!drilledIn">
<div class="c-grid__x l-grid l-grid-x"
v-if="gridSize[0] >= 3"
:style="[{ backgroundSize: gridSize[0] + 'px 100%' }]">
</div>
<div class="c-grid__y l-grid l-grid-y"
v-if="gridSize[1] >= 3"
:style="[{ backgroundSize: '100%' + gridSize[1] + 'px' }]"></div>
</div>
<layout-frame v-for="item in frameItems"
class="l-layout__frame"
:key="item.id"
:item="item"
:gridSize="gridSize"
@drilledIn="updateDrilledInState"
@dragInProgress="updatePosition"
@endDrag="endDrag">
</layout-frame>
</div>
</div>
</template>
<style lang="scss">
@import "~styles/sass-base";
.l-layout,
.c-grid,
.c-grid__x,
.c-grid__y {
@include abs();
}
.l-layout {
display: flex;
flex-direction: column;
&__grid-holder {
display: none;
}
&__object {
flex: 1 1 auto;
overflow: auto;
}
&__frame {
position: absolute;
}
}
.c-grid {
z-index: -1;
pointer-events: none;
&__x { @include bgTicks($colorGridLines, 'x'); }
&__y { @include bgTicks($colorGridLines, 'y'); }
}
.is-editing {
.l-shell__main-container > .l-layout {
// Target the top-most layout container and color its background
background: rgba($editColor, 0.1);
}
.c-frame,
.l-layout {
&.s-selected,
&.s-selected-parent {
[class*="__grid-holder"] {
display: block;
}
}
}
}
</style>
<script>
import LayoutFrame from './LayoutFrame.vue';
const DEFAULT_GRID_SIZE = [32, 32],
DEFAULT_DIMENSIONS = [12, 8],
DEFAULT_POSITION = [0, 0],
MINIMUM_FRAME_SIZE = [320, 180],
DEFAULT_HIDDEN_FRAME_TYPES = [
'hyperlink'
];
export default {
data() {
return {
gridSize: [],
frameItems: [],
frames: [],
frameStyles: [],
rawPositions: {},
initSelect: true,
drilledIn: undefined
}
},
inject: ['openmct'],
props: ['domainObject'],
components: {
LayoutFrame
},
created: function () {
this.newDomainObject = this.domainObject;
this.gridSize = this.newDomainObject.layoutGrid || DEFAULT_GRID_SIZE;
this.composition = this.openmct.composition.get(this.newDomainObject);
let panels = (((this.newDomainObject.configuration || {}).layout || {}).panels || {});
if (this.composition !== undefined) {
this.composition.load().then((composition) => {
composition.forEach(function (domainObject) {
this.readLayoutConfiguration(domainObject, panels);
this.makeFrameItem(domainObject, false);
}.bind(this));
this.composition.on('add', this.onAddComposition);
this.composition.on('remove', this.onRemoveComposition);
});
}
this.unlisten = this.openmct.objects.observe(this.newDomainObject, '*', function (obj) {
this.newDomainObject = JSON.parse(JSON.stringify(obj));
this.gridSize = this.newDomainObject.layoutGrid || DEFAULT_GRID_SIZE;;
}.bind(this));
},
methods: {
readLayoutConfiguration(domainObject, panels) {
let id = this.openmct.objects.makeKeyString(domainObject.identifier);
this.rawPositions[id] = {
position: panels[id].position || DEFAULT_POSITION,
dimensions: panels[id].dimensions || this.defaultDimensions()
};
this.frameStyles[id] = this.convertPosition(this.rawPositions[id]);
this.frames[id] = panels[id].hasOwnProperty('hasFrame') ?
panels[id].hasFrame :
this.hasFrameByDefault(domainObject.type);
},
makeFrameItem(domainObject, initSelect) {
let id = this.openmct.objects.makeKeyString(domainObject.identifier);
this.frameItems.push({
id: id,
hasFrame: this.frames[id],
domainObject,
style: this.frameStyles[id],
drilledIn: this.isDrilledIn(id),
initSelect: initSelect,
rawPosition: this.rawPositions[id]
});
},
onAddComposition(domainObject) {
let id = this.openmct.objects.makeKeyString(domainObject.identifier);
this.rawPositions[id] = {
position: [
Math.floor(this.droppedObjectPosition.x / this.gridSize[0]),
Math.floor(this.droppedObjectPosition.y / this.gridSize[1])
],
dimensions: this.defaultDimensions()
};
this.frameStyles[id] = this.convertPosition(this.rawPositions[id]);
this.frames[id] = this.hasFrameByDefault(domainObject.type);
let newPanel = this.rawPositions[id];
newPanel.hasFrame = this.frames[id];
this.mutate("configuration.layout.panels[" + id + "]", newPanel);
this.makeFrameItem(domainObject, true);
},
onRemoveComposition(identifier) {
// TODO: remove the object from frameItems
},
defaultDimensions() {
let gridSize = this.gridSize;
return MINIMUM_FRAME_SIZE.map(function (min, i) {
return Math.max(
Math.ceil(min / gridSize[i]),
DEFAULT_DIMENSIONS[i]
);
});
},
convertPosition(raw) {
return {
left: (this.gridSize[0] * raw.position[0]) + 'px',
top: (this.gridSize[1] * raw.position[1]) + 'px',
width: (this.gridSize[0] * raw.dimensions[0]) + 'px',
height: (this.gridSize[1] * raw.dimensions[1]) + 'px',
minWidth: (this.gridSize[0] * raw.dimensions[0]) + 'px',
minHeight: (this.gridSize[1] * raw.dimensions[1]) + 'px'
};
},
/**
* Checks if the frame should be hidden or not.
*
* @param type the domain object type
* @return {boolean} true if the object should have
* frame by default, false, otherwise
*/
hasFrameByDefault(type) {
return DEFAULT_HIDDEN_FRAME_TYPES.indexOf(type) === -1;
},
setSelection(selection) {
if (selection.length === 0) {
return;
}
this.updateDrilledInState();
},
updateDrilledInState(id) {
this.drilledIn = id;
this.frameItems.forEach(function (item) {
item.drilledIn = item.id === id;
});
},
isDrilledIn(id) {
return this.drilledIn === id;
},
updatePosition(id, newPosition) {
let newStyle = this.convertPosition(newPosition);
this.frameStyles[id] = newStyle;
this.rawPositions[id] = newPosition;
this.frameItems.forEach(function (item) {
if (item.id === id) {
item.style = newStyle;
item.rawPosition = newPosition;
}
});
},
bypassSelection($event) {
if (this.dragInProgress) {
if ($event) {
$event.stopImmediatePropagation();
}
return;
}
},
endDrag(id) {
this.dragInProgress = true;
setTimeout(function () {
this.dragInProgress = false;
}.bind(this), 0);
let path = "configuration.layout.panels[" + id + "]";
this.mutate(path + ".dimensions", this.rawPositions[id].dimensions);
this.mutate(path + ".position", this.rawPositions[id].position);
},
mutate(path, value) {
this.openmct.objects.mutate(this.newDomainObject, path, value);
},
handleDrop($event) {
$event.preventDefault();
let child = JSON.parse($event.dataTransfer.getData('domainObject'));
let duplicates = [];
let composition = this.newDomainObject.composition;
composition.forEach((object) => {
if (this.openmct.objects.makeKeyString(JSON.parse(JSON.stringify(object))) ===
this.openmct.objects.makeKeyString(child.identifier)) {
duplicates.push(object);
}
});
// Disallow adding a duplicate object to the composition
if (duplicates.length !== 0) {
return;
}
let elementRect = this.$el.getBoundingClientRect();
this.droppedObjectPosition = {
x: $event.pageX - elementRect.left,
y: $event.pageY - elementRect.top
}
// TODO: use the composition API to add child once the default composition
// provider supports it instead of mutating the composition directly.
// this.composition.add(child).
composition.push(child.identifier);
this.mutate('composition', composition);
},
handleDragOver($event){
$event.preventDefault();
}
},
mounted() {
this.removeSelectable = this.openmct.selection.selectable(
this.$el,
{
item: this.newDomainObject
},
this.initSelect
);
this.openmct.selection.on('change', this.setSelection);
},
destroyed: function () {
this.composition.off('add', this.onAddComposition);
this.composition.off('remove', this.onRemoveComposition);
this.openmct.off('change', this.selection);
this.removeSelectable();
this.unlisten();
}
}
</script>

View File

@ -0,0 +1,41 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(function () {
function DisplayLayoutType() {
return {
name: "Display Layout",
creatable: true,
cssClass: 'icon-layout',
initialize(domainObject) {
domainObject.composition = [];
domainObject.configuration = {
layout: {
panels: {}
}
};
}
}
}
return DisplayLayoutType;
});

View File

@ -0,0 +1,115 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
[],
function () {
/**
* Handles drag interactions on frames in layouts. This will
* provides new positions/dimensions for frames based on
* relative pixel positions provided; these will take into account
* the grid size (in a snap-to sense) and will enforce some minimums
* on both position and dimensions.
*
* The provided position and dimensions factors will determine
* whether this is a move or a resize, and what type of resize it
* will be. For instance, a position factor of [1, 1]
* will move a frame along with the mouse as the drag
* proceeds, while a dimension factor of [0, 0] will leave
* dimensions unchanged. Combining these in different
* ways results in different handles; a position factor of
* [1, 0] and a dimensions factor of [-1, 0] will implement
* a left-edge resize, as the horizontal position will move
* with the mouse while the horizontal dimensions shrink in
* kind (and vertical properties remain unmodified.)
*
* @param {object} rawPosition the initial position/dimensions
* of the frame being interacted with
* @param {number[]} posFactor the position factor
* @param {number[]} dimFactor the dimensions factor
* @param {number[]} the size of each grid element, in pixels
* @constructor
* @memberof platform/features/layout
*/
function LayoutDrag(rawPosition, posFactor, dimFactor, gridSize) {
this.rawPosition = rawPosition;
this.posFactor = posFactor;
this.dimFactor = dimFactor;
this.gridSize = gridSize;
}
// Convert a delta from pixel coordinates to grid coordinates,
// rounding to whole-number grid coordinates.
function toGridDelta(gridSize, pixelDelta) {
return pixelDelta.map(function (v, i) {
return Math.round(v / gridSize[i]);
});
}
// Utility function to perform element-by-element multiplication
function multiply(array, factors) {
return array.map(function (v, i) {
return v * factors[i];
});
}
// Utility function to perform element-by-element addition
function add(array, other) {
return array.map(function (v, i) {
return v + other[i];
});
}
// Utility function to perform element-by-element max-choosing
function max(array, other) {
return array.map(function (v, i) {
return Math.max(v, other[i]);
});
}
/**
* Get a new position object in grid coordinates, with
* position and dimensions both offset appropriately
* according to the factors supplied in the constructor.
* @param {number[]} pixelDelta the offset from the
* original position, in pixels
*/
LayoutDrag.prototype.getAdjustedPosition = function (pixelDelta) {
var gridDelta = toGridDelta(this.gridSize, pixelDelta);
return {
position: max(add(
this.rawPosition.position,
multiply(gridDelta, this.posFactor)
), [0, 0]),
dimensions: max(add(
this.rawPosition.dimensions,
multiply(gridDelta, this.dimFactor)
), [1, 1])
};
};
return LayoutDrag;
}
);

View File

@ -0,0 +1,338 @@
/*****************************************************************************
* 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-frame has-local-controls is-selectable is-moveable"
:style="item.style"
:class="classObject"
@dblclick="drill(item.id, $event)">
<div class="c-frame__header">
<div class="c-frame__header__start">
<div class="c-frame__name icon-object">{{ item.domainObject.name }}</div>
<div class="c-frame__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>
<object-view class="c-frame__object-view"
:object="item.domainObject"></object-view>
<!-- Drag handles -->
<div class="c-frame-edit">
<div class="c-frame-edit__move"
@mousedown="startDrag([1,1], [0,0], $event)"></div>
<div class="c-frame-edit__handle --nw"
@mousedown="startDrag([1,1], [-1,-1], $event)"></div>
<div class="c-frame-edit__handle --ne"
@mousedown="startDrag([0,1], [1,-1], $event)"></div>
<div class="c-frame-edit__handle --sw"
@mousedown="startDrag([1,0], [-1,1], $event)"></div>
<div class="c-frame-edit__handle --se"
@mousedown="startDrag([0,0], [1,1], $event)"></div>
</div>
</div>
</template>
<style lang="scss">
@import "~styles/sass-base";
/******************************* FRAME */
.c-frame {
display: flex;
flex-direction: column;
border-width: 1px;
border-color: transparent;
/*************************** HEADER */
&__header {
display: flex;
align-items: center;
flex: 0 0 auto;
margin-bottom: $interiorMargin;
> [class*="__"] {
display: flex;
align-items: center;
}
> * + * {
margin-left: $interiorMargin;
}
[class*="__start"] {
flex: 1 1 auto;
overflow: hidden;
}
[class*="__end"] {
//justify-content: flex-end;
flex: 0 0 auto;
[class*="button"] {
font-size: 0.7em;
}
}
}
&__name {
@include ellipsize();
flex: 0 1 auto;
font-size: 1.2em;
&:before {
// Object type icon
flex: 0 0 auto;
margin-right: $interiorMarginSm;
}
}
/*************************** OBJECT VIEW */
&__object-view {
flex: 1 1 auto;
overflow: auto;
.c-object-view {
.u-fills-container {
// Expand component types that fill a container
@include abs();
}
}
}
/*************************** NO-FRAME */
&.no-frame {
> [class*="__header"] {
display: none;
}
}
&:not(.no-frame) {
background: $colorBodyBg;
border: 1px solid $colorInteriorBorder;
padding: $interiorMargin;
}
/*************************** SELECTION */
&.is-selectable {
&:hover {
box-shadow: $browseShdwSelectableHov;
}
}
&.s-selected, // LEGACY
&.is-selected {
border: $browseBorderSelected;
}
}
/*************************** EDITING */
.is-editing {
.c-frame {
&:not(.is-drilled-in).is-selectable {
border: $editBorderSelectable;
&:hover {
border: $editBorderSelectableHov;
}
&.s-selected,
&.is-selected {
border: $editBorderSelected;
> .c-frame-edit {
display: block; // Show the editing rect and handles
}
}
}
&.is-drilled-in {
border: $editBorderDrilledIn;
}
.u-links {
// Applied in markup to objects that provide links. Disable while editing.
pointer-events: none;
}
}
}
.c-frame-edit {
// The editing rect and handles
$z: 10;
@include abs();
box-shadow: rgba($editColor, 0.5) 0 0 10px;
display: none;
&__move {
@include abs();
cursor: move;
z-index: $z;
}
&__handle {
$d: 8px;
$o: floor($d * -0.5);
background: rgba($editColor, 0.3);
border: 1px solid $editColor;
position: absolute;
width: $d; height: $d;
top: auto; right: auto; bottom: auto; left: auto;
z-index: $z + 1;
&:before {
// Extended hit area
$m: -5px;
content: '';
display: block;
position: absolute;
top: $m; right: $m; bottom: $m; left: $m;
z-index: -1;
}
&:hover {
background: $editColor;
}
&.--nw {
cursor: nw-resize;
left: $o; top: $o;
}
&.--ne {
cursor: ne-resize;
right: $o; top: $o;
}
&.--se {
cursor: se-resize;
right: $o; bottom: $o;
}
&.--sw {
cursor: sw-resize;
left: $o; bottom: $o;
}
}
}
</style>
<script>
import ObjectView from '../../ui/components/layout/ObjectView.vue'
import LayoutDrag from './LayoutDrag'
export default {
inject: ['openmct'],
props: {
item: Object,
gridSize: Array
},
components: {
ObjectView
},
computed: {
classObject: function () {
return {
'is-drilled-in': this.item.drilledIn,
'no-frame': !this.item.hasFrame
}
}
},
methods: {
drill(id, $event) {
if ($event) {
$event.stopPropagation();
}
if (!this.isBeingEdited(this.item.domainObject)) {
return;
}
if (this.openmct.composition.get(this.item.domainObject) === undefined) {
return;
}
// Disable for fixed position.
if (this.item.domainObject.type === 'telemetry.fixed') {
return;
}
this.$emit('drilledIn', id);
},
isBeingEdited(object) {
// TODO: add logic when inEditContext() is implemented in Vue.
return true;
},
updatePosition(event) {
let currentPosition = [event.pageX, event.pageY];
this.initialPosition = this.initialPosition || currentPosition;
this.delta = currentPosition.map(function (value, index) {
return value - this.initialPosition[index];
}.bind(this));
},
startDrag(posFactor, dimFactor, event) {
document.body.addEventListener('mousemove', this.continueDrag);
document.body.addEventListener('mouseup', this.endDrag);
this.updatePosition(event);
this.activeDrag = new LayoutDrag(
this.item.rawPosition,
posFactor,
dimFactor,
this.gridSize
);
event.preventDefault();
},
continueDrag(event) {
event.preventDefault();
this.updatePosition(event);
if (this.activeDrag) {
this.$emit('dragInProgress', this.item.id, this.activeDrag.getAdjustedPosition(this.delta));
}
},
endDrag(event) {
document.body.removeEventListener('mousemove', this.continueDrag);
document.body.removeEventListener('mouseup', this.endDrag);
this.continueDrag(event);
this.$emit('endDrag', this.item.id);
this.initialPosition = undefined;
event.preventDefault();
}
},
mounted() {
this.removeSelectable = this.openmct.selection.selectable(
this.$el,
{
item: this.item.domainObject
},
this.item.initSelect
);
},
destroyed() {
this.removeSelectable();
}
}
</script>

View File

@ -0,0 +1,67 @@
/*****************************************************************************
* 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.
*****************************************************************************/
import Layout from './DisplayLayout.vue'
import Vue from 'vue'
import objectUtils from '../../api/objects/object-utils.js'
import DisplayLayoutType from './DisplayLayoutType.js'
export default function () {
return function (openmct) {
openmct.objectViews.addProvider({
key: 'layout.view',
canView: function (domainObject) {
return domainObject.type === 'layout';
},
view: function (domainObject) {
let component;
return {
show(container) {
component = new Vue({
components: {
Layout
},
template: '<layout :domain-object="domainObject"></layout>',
provide: {
openmct,
objectUtils
},
el: container,
data () {
return {
domainObject: domainObject
}
}
});
},
destroy() {
component.$destroy();
}
};
},
priority() {
return 100;
}
});
openmct.types.addType('layout', DisplayLayoutType());
}
}

View File

@ -35,6 +35,7 @@ define([
'./telemetryTable/plugin',
'./staticRootPlugin/plugin',
'./notebook/plugin',
'./displayLayout/plugin',
'./folderView/plugin'
], function (
_,
@ -51,6 +52,7 @@ define([
TelemetryTablePlugin,
StaticRootPlugin,
Notebook,
DisplayLayoutPlugin,
FolderView
) {
var bundleMap = {
@ -161,6 +163,7 @@ define([
plugins.TelemetryMean = TelemetryMean;
plugins.URLIndicator = URLIndicatorPlugin;
plugins.Notebook = Notebook;
plugins.DisplayLayout = DisplayLayoutPlugin.default;
plugins.FolderView = FolderView;
return plugins;

View File

@ -264,7 +264,7 @@ define([
this.applyStyle($('#widget', this.domElement), activeRule.getProperty('style'));
$('#widget', this.domElement).prop('title', activeRule.getProperty('message'));
$('#widgetLabel', this.domElement).html(activeRule.getProperty('label'));
$('#widgetLabel', this.domElement).removeClass().addClass('label widget-label ' + activeRule.getProperty('icon'));
$('#widgetLabel', this.domElement).removeClass().addClass('label widget-label c-summary-widget__label ' + activeRule.getProperty('icon'));
};
/**

View File

@ -18,7 +18,7 @@ define([
this.widget.title = datum.message;
this.label.title = datum.message;
this.label.innerHTML = datum.ruleLabel;
this.label.className = 'label widget-label ' + datum.icon;
this.label.className = 'label widget-label c-summary-widget__label ' + datum.icon;
};
SummaryWidgetView.prototype.render = function () {

View File

@ -1,5 +1,5 @@
<div class="w-summary-widget s-status-no-data">
<a class="t-summary-widget l-summary-widget s-summary-widget labeled">
<span class="label widget-label">Loading...</span>
<div class="w-summary-widget s-status-no-data c-widget-wrapper u-contents">
<a class="t-summary-widget c-button c-summary-widget u-links u-fills-container">
<span class="label widget-label c-summary-widget__label">Loading...</span>
</a>
</div>

View File

@ -121,7 +121,7 @@ $glyph-icon-pointer-right: '\e1028';
$glyph-icon-refresh: '\e1029';
$glyph-icon-save: '\e1030';
$glyph-icon-sine: '\e1031';
$glyph-icon-T: '\e1032';
$glyph-icon-font: '\e1032';
$glyph-icon-thumbs-strip: '\e1033';
$glyph-icon-two-parts-both: '\e1034';
$glyph-icon-two-parts-one-only: '\e1035';
@ -138,7 +138,7 @@ $glyph-icon-frame-show: '\e1045';
$glyph-icon-frame-hide: '\e1046';
$glyph-icon-import: '\e1047';
$glyph-icon-export: '\e1048';
$glyph-icon-minimize: '\e1049'; // 12px only
$glyph-icon-font-size: '\e1049';
$glyph-icon-activity: '\e1100';
$glyph-icon-activity-mode: '\e1101';
$glyph-icon-autoflow-tabular: '\e1102';

View File

@ -78,7 +78,7 @@
&:hover {
background: $colorBtnCautionBgHov;
}
}
}
}
@mixin cClickIcon() {
@ -111,24 +111,24 @@
// Wraps --menu elements, contains button and menu
overflow: visible;
.c-menu {
.c-menu {
// Default position of contained menu
top: 100%; left: 0;
}
top: 100%; left: 0;
}
&[class*='--menus-up'] {
.c-menu {
top: auto; bottom: 100%;
}
}
&[class*='--menus-left'] {
.c-menu {
left: auto; right: 0;
}
.c-menu {
top: auto; bottom: 100%;
}
}
&[class*='--menus-left'] {
.c-menu {
left: auto; right: 0;
}
}
}
/********* Buttons */
// Optionally can include icon in :before via markup
button {
@ -237,38 +237,11 @@ button {
}
/******************************************************** FORM ELEMENTS */
input, textarea {
font-family: inherit;
font-weight: inherit;
letter-spacing: inherit;
}
input[type=text],
input[type=search],
input[type=number] {
@include reactive-input();
padding: $inputTextP;
&.numeric {
text-align: right;
}
}
.c-input {
&--datetime {
// Sized for values such as 2018-09-28 22:32:33.468Z
width: 160px;
}
&--hrs-min-sec {
// Sized for values such as 00:25:00
width: 60px;
}
&-inline,
&--inline {
// A text input or contenteditable element that indicates edit affordance on hover and looks like an input on focus
@include reactive-input();
box-shadow: none;
/********* Inline inputs */
.c-input-inline {
// A text input or contenteditable element that indicates edit affordance on hover and looks like an input on focus
@include input-base();
border: 1px solid transparent;
display: block !important;
min-width: 0;
padding-left: 0;
@ -286,10 +259,16 @@ input[type=number] {
padding-left: $inputTextPLeftRight;
padding-right: $inputTextPLeftRight;
}
&:hover {
border-color: rgba($colorBodyFg, 0.2);
}
&:focus {
@include nice-input($shdw: rgba(0, 0, 0, 0.6) 0 1px 3px);
border-color: transparent;
}
}
&--labeled {
// TODO: replace .c-labeled-input with this
.c-labeled-input {
// An input used in the Toolbar
// Assumes label is before the input
@include cControl();
@ -298,6 +277,16 @@ input[type=number] {
margin-left: $interiorMarginSm;
}
}
/******************************************************** HYPERLINKS AND HYPERLINK BUTTONS */
.c-hyperlink {
&--link {
color: $colorKey;
}
&--button {
@include cButton();
}
}
.c-labeled-input {
@ -360,6 +349,10 @@ input[type=number] {
}
}
@mixin modalMenu() {
// Optional modifier that makes a c-menu more mobile-friendly
}
.c-menu {
@include menuOuter();
@include menuInner();
@ -583,9 +576,3 @@ input[type=number] {
}
}
}
/***************************************************** SLIDERS */
.c-slider {
@include cControl();
> * + * { margin-left: $interiorMargin; }
}

View File

@ -49,7 +49,7 @@
font-style: normal;
}
/******************************* RESETS */
/******************************************************** RESETS */
*,
:before,
:after {
@ -60,7 +60,7 @@ div {
position: relative;
}
/******************************* UTILITIES */
/******************************************************** UTILITIES */
.u-contents {
display: contents;
}
@ -74,7 +74,7 @@ div {
}
}
/******************************* BROWSER ELEMENTS */
/******************************************************** BROWSER ELEMENTS */
body.desktop {
::-webkit-scrollbar {
box-sizing: border-box;
@ -111,6 +111,13 @@ body.desktop {
}
}
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
//margin: -1px -5px inherit -5px !important;
margin-right: -5px !important;
margin-top: -1px !important;
}
/************************** HTML ENTITIES */
a {
color: $colorA;
@ -175,7 +182,6 @@ li {
padding: 0;
}
/******************************************************** HAS */
// Local Controls: Controls placed in proximity to or overlaid on components and views
body.desktop .has-local-controls {
@ -217,7 +223,21 @@ body.desktop .has-local-controls {
//}
//}
/************************** LEGACY */
/******************************************************** IS */
.is-selectable {
&:hover {
}
}
.is-editing {
.is-selectable {
}
}
/******************************************************** LEGACY */
mct-container {
display: block;
@ -289,21 +309,6 @@ a.disabled {
text-align: center;
}
.no-selection {
// aka selection = "None". Used in palettes and their menu buttons.
$c: red;
$s: 48%;
$e: 52%;
background-image: linear-gradient(-45deg,
transparent $s - 5%,
$c $s,
$c $e,
transparent $e + 5%
);
background-repeat: no-repeat;
background-size: contain;
}
.scrolling,
.scroll {
overflow: auto;
@ -386,3 +391,8 @@ a.disabled {
.t-imagery {
display: contents;
}
.t-frame-outer {
min-width: 200px;
min-height: 200px;
}

View File

@ -113,7 +113,7 @@
.icon-refresh { @include glyphBefore($glyph-icon-refresh); }
.icon-save { @include glyphBefore($glyph-icon-save); }
.icon-sine { @include glyphBefore($glyph-icon-sine); }
.icon-T { @include glyphBefore($glyph-icon-T); }
.icon-font { @include glyphBefore($glyph-icon-font); }
.icon-thumbs-strip { @include glyphBefore($glyph-icon-thumbs-strip); }
.icon-two-parts-both { @include glyphBefore($glyph-icon-two-parts-both); }
.icon-two-parts-one-only { @include glyphBefore($glyph-icon-two-parts-one-only); }
@ -130,6 +130,7 @@
.icon-frame-hide { @include glyphBefore($glyph-icon-frame-hide); }
.icon-import { @include glyphBefore($glyph-icon-import); }
.icon-export { @include glyphBefore($glyph-icon-export); }
.icon-font-size { @include glyphBefore($glyph-icon-font-size); }
.icon-activity { @include glyphBefore($glyph-icon-activity); }
.icon-activity-mode { @include glyphBefore($glyph-icon-activity-mode); }
.icon-autoflow-tabular { @include glyphBefore($glyph-icon-autoflow-tabular); }

View File

@ -177,7 +177,6 @@
}
}
@mixin input-base() {
@include htmlInputReset();
border-radius: $controlCr;

View File

@ -2,7 +2,7 @@
"metadata": {
"name": "openmct-symbols-16px",
"lastOpened": 0,
"created": 1529545133464
"created": 1537817705550
},
"iconSets": [
{
@ -525,7 +525,7 @@
"tempChar": ""
},
{
"order": 102,
"order": 149,
"prevSize": 24,
"name": "icon-T",
"id": 84,
@ -660,13 +660,21 @@
"code": 921672,
"tempChar": ""
},
{
"order": 150,
"id": 119,
"name": "icon-font-size-alt1",
"prevSize": 24,
"code": 921673,
"tempChar": ""
},
{
"order": 37,
"prevSize": 24,
"name": "icon-activity",
"id": 32,
"code": 921856,
"tempChar": ""
"tempChar": ""
},
{
"order": 36,
@ -674,7 +682,7 @@
"name": "icon-activity-mode",
"id": 31,
"code": 921857,
"tempChar": ""
"tempChar": ""
},
{
"order": 52,
@ -682,7 +690,7 @@
"name": "icon-autoflow-tabular",
"id": 47,
"code": 921858,
"tempChar": ""
"tempChar": ""
},
{
"order": 55,
@ -690,7 +698,7 @@
"name": "icon-clock",
"id": 50,
"code": 921859,
"tempChar": ""
"tempChar": ""
},
{
"order": 58,
@ -698,7 +706,7 @@
"name": "icon-database",
"id": 53,
"code": 921860,
"tempChar": ""
"tempChar": ""
},
{
"order": 57,
@ -706,7 +714,7 @@
"name": "icon-database-query",
"id": 52,
"code": 921861,
"tempChar": ""
"tempChar": ""
},
{
"order": 17,
@ -714,7 +722,7 @@
"name": "icon-dataset",
"id": 12,
"code": 921862,
"tempChar": ""
"tempChar": ""
},
{
"order": 22,
@ -722,7 +730,7 @@
"name": "icon-datatable",
"id": 17,
"code": 921863,
"tempChar": ""
"tempChar": ""
},
{
"order": 59,
@ -730,7 +738,7 @@
"name": "icon-dictionary",
"id": 54,
"code": 921864,
"tempChar": ""
"tempChar": ""
},
{
"order": 62,
@ -738,7 +746,7 @@
"name": "icon-folder",
"id": 57,
"code": 921865,
"tempChar": ""
"tempChar": ""
},
{
"order": 66,
@ -746,7 +754,7 @@
"name": "icon-image",
"id": 61,
"code": 921872,
"tempChar": ""
"tempChar": ""
},
{
"order": 68,
@ -754,7 +762,7 @@
"name": "icon-layout",
"id": 63,
"code": 921873,
"tempChar": ""
"tempChar": ""
},
{
"order": 77,
@ -762,7 +770,7 @@
"name": "icon-object",
"id": 72,
"code": 921874,
"tempChar": ""
"tempChar": ""
},
{
"order": 78,
@ -770,7 +778,7 @@
"name": "icon-object-unknown",
"id": 73,
"code": 921875,
"tempChar": ""
"tempChar": ""
},
{
"order": 79,
@ -778,7 +786,7 @@
"name": "icon-packet",
"id": 74,
"code": 921876,
"tempChar": ""
"tempChar": ""
},
{
"order": 80,
@ -786,7 +794,7 @@
"name": "icon-page",
"id": 75,
"code": 921877,
"tempChar": ""
"tempChar": ""
},
{
"order": 135,
@ -794,7 +802,7 @@
"name": "icon-plot-overlay",
"prevSize": 24,
"code": 921878,
"tempChar": ""
"tempChar": ""
},
{
"order": 113,
@ -802,7 +810,7 @@
"name": "icon-plot-stacked",
"prevSize": 24,
"code": 921879,
"tempChar": ""
"tempChar": ""
},
{
"order": 10,
@ -810,7 +818,7 @@
"name": "icon-session",
"id": 5,
"code": 921880,
"tempChar": ""
"tempChar": ""
},
{
"order": 24,
@ -818,7 +826,7 @@
"name": "icon-tabular",
"id": 19,
"code": 921881,
"tempChar": ""
"tempChar": ""
},
{
"order": 7,
@ -826,7 +834,7 @@
"name": "icon-tabular-lad",
"id": 2,
"code": 921888,
"tempChar": ""
"tempChar": ""
},
{
"order": 6,
@ -834,7 +842,7 @@
"name": "icon-tabular-lad-set",
"id": 1,
"code": 921889,
"tempChar": ""
"tempChar": ""
},
{
"order": 8,
@ -842,7 +850,7 @@
"name": "icon-tabular-realtime",
"id": 3,
"code": 921890,
"tempChar": ""
"tempChar": ""
},
{
"order": 23,
@ -850,7 +858,7 @@
"name": "icon-tabular-scrolling",
"id": 18,
"code": 921891,
"tempChar": ""
"tempChar": ""
},
{
"order": 112,
@ -858,7 +866,7 @@
"name": "icon-telemetry",
"id": 86,
"code": 921892,
"tempChar": ""
"tempChar": ""
},
{
"order": 90,
@ -866,7 +874,7 @@
"name": "icon-telemetry-panel",
"id": 85,
"code": 921893,
"tempChar": ""
"tempChar": ""
},
{
"order": 93,
@ -874,15 +882,15 @@
"name": "icon-timeline",
"id": 88,
"code": 921894,
"tempChar": ""
"tempChar": ""
},
{
"order": 116,
"id": 101,
"name": "icon-timer-v1.5",
"name": "icon-timer-v15",
"prevSize": 24,
"code": 921895,
"tempChar": ""
"tempChar": ""
},
{
"order": 11,
@ -890,7 +898,7 @@
"name": "icon-topic",
"id": 6,
"code": 921896,
"tempChar": ""
"tempChar": ""
},
{
"order": 115,
@ -898,7 +906,7 @@
"name": "icon-box-with-dashed-lines",
"id": 29,
"code": 921897,
"tempChar": ""
"tempChar": ""
},
{
"order": 126,
@ -906,7 +914,7 @@
"name": "icon-summary-widget",
"prevSize": 24,
"code": 921904,
"tempChar": ""
"tempChar": ""
},
{
"order": 139,
@ -914,13 +922,13 @@
"name": "icon-notebook",
"prevSize": 24,
"code": 921905,
"tempChar": ""
"tempChar": ""
}
],
"metadata": {
"name": "openmct-symbols-16px",
"importSize": {
"width": 512,
"width": 745,
"height": 512
},
"designer": "Charles Hacskaylo",
@ -2360,7 +2368,7 @@
},
{
"paths": [
"M0 0v256h128v-64h256v704h-192v128h640v-128h-192v-704h256v64h128v-256z"
"M800 1024h224l-384-1024h-256l-384 1024h224l84-224h408zM380 608l132-352 132 352z"
],
"grid": 16,
"tags": [
@ -2368,9 +2376,15 @@
],
"defaultCode": 228,
"id": 84,
"attrs": [],
"attrs": [
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"colorPermutations": {
"1161751207457516161751": []
"1161751207457516161751": [
{}
]
}
},
{
@ -2840,6 +2854,30 @@
]
}
},
{
"id": 119,
"paths": [
"M1226.4 320h-176l-76.22 203.24 77 205.34 87.22-232.58 90.74 242h-174.44l49.5 132h174.44l57.76 154h154l-264-704z",
"M384 0l-384 1024h224l84-224h408l84 224h224l-384-1024zM380 608l132-352 132 352z"
],
"attrs": [
{},
{}
],
"width": 1490,
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-font-size-alt1"
],
"colorPermutations": {
"1161751207457516161751": [
{},
{}
]
}
},
{
"paths": [
"M576 64h-256l320 320h-290.256c-44.264-76.516-126.99-128-221.744-128h-128v512h128c94.754 0 177.48-51.484 221.744-128h290.256l-320 320h256l448-448-448-448z"
@ -3740,7 +3778,9 @@
"classSelector": ".ui-symbol",
"showMetrics": true,
"showMetadata": true,
"embed": false
"embed": false,
"noie8": true,
"ie7": false
},
"imagePref": {
"prefix": "icon-",

View File

@ -71,7 +71,7 @@
<glyph unicode="&#xe1029;" glyph-name="icon-refresh" d="M960 528v432l-164.8-164.8c-79.8 65.2-178.8 100.8-283.2 100.8-119.6 0-232.2-46.6-316.8-131.2s-131.2-197.2-131.2-316.8 46.6-232.2 131.2-316.8c84.6-84.6 197.2-131.2 316.8-131.2s232.2 46.6 316.8 131.2c69.4 69.4 113.2 157.4 126.6 252.8h-130c-29.8-145.8-159-256-313.6-256-176.4 0-320 143.6-320 320s143.8 320 320.2 320c72 0 138.4-23.8 192-64l-176-176h432z" />
<glyph unicode="&#xe1030;" glyph-name="icon-save" d="M192.2 384c-0.2 0-0.2 0 0 0l-0.2-448h640v447.8c0 0 0 0-0.2 0.2h-639.6zM978.8 749.2l-165.4 165.4c-25 25-74.2 45.4-109.4 45.4h-576c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128v448c0 35.2 28.8 64 64 64h640c35.2 0 64-28.8 64-64v-448c70.4 0 128 57.6 128 128v576c0 35.2-20.4 84.4-45.2 109.2zM704 704c0-35.2-28.8-64-64-64h-448c-35.2 0-64 28.8-64 64v192h320v-192h128v192h128v-192z" />
<glyph unicode="&#xe1031;" glyph-name="icon-sine" d="M1022.294 448c-1.746 7.196-3.476 14.452-5.186 21.786-20.036 85.992-53.302 208.976-98 306.538-22.42 48.938-45.298 86.556-69.946 115.006-48.454 55.93-98.176 67.67-131.356 67.67s-82.902-11.74-131.356-67.672c-24.648-28.45-47.528-66.068-69.948-115.006-44.696-97.558-77.962-220.544-98-306.538-21.646-92.898-46.444-175.138-71.71-237.836-16.308-40.46-30.222-66.358-40.6-82.604-10.378 16.246-24.292 42.142-40.6 82.604-23.272 57.75-46.144 132.088-66.524 216.052h-197.362c1.746-7.196 3.476-14.452 5.186-21.786 20.036-85.992 53.302-208.976 98-306.538 22.42-48.938 45.298-86.556 69.946-115.006 48.454-55.932 98.176-67.672 131.356-67.672s82.902 11.74 131.356 67.672c24.648 28.45 47.528 66.068 69.948 115.006 44.696 97.558 77.962 220.544 98 306.538 21.646 92.898 46.444 175.138 71.71 237.836 16.308 40.46 30.222 66.358 40.6 82.604 10.378-16.246 24.292-42.142 40.6-82.604 23.274-57.748 46.146-132.086 66.526-216.050h197.36z" />
<glyph unicode="&#xe1032;" glyph-name="icon-T" d="M0 960v-256h128v64h256v-704h-192v-128h640v128h-192v704h256v-64h128v256z" />
<glyph unicode="&#xe1032;" glyph-name="icon-T" d="M800-64h224l-384 1024h-256l-384-1024h224l84 224h408zM380 352l132 352 132-352z" />
<glyph unicode="&#xe1033;" glyph-name="icon-thumbs-strip" d="M448 578c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320zM1024 578c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320zM448 2c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320zM1024 2c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320z" />
<glyph unicode="&#xe1034;" glyph-name="icon-two-parts-both" d="M896 960h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v768c0 70.4-57.6 128-128 128zM128 832h320v-768h-320v768zM896 64h-320v768h320v-768z" />
<glyph unicode="&#xe1035;" glyph-name="icon-two-parts-one-only" d="M896 960h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v768c0 70.4-57.6 128-128 128zM896 64h-320v768h320v-768z" />
@ -88,6 +88,7 @@
<glyph unicode="&#xe1046;" glyph-name="icon-frame-hide" d="M128 770h420l104 128h-652v-802.4l128 157.4zM896 130h-420l-104-128h652v802.4l-128-157.4zM832 962l-832-1024h192l832 1024zM392 578l104 128h-304v-128z" />
<glyph unicode="&#xe1047;" glyph-name="icon-import" d="M832 767.6v-639.4c0-0.2-0.2-0.2-0.4-0.4h-319.6v-192h320c105.6 0 192 86.4 192 192v640.2c0 105.6-86.4 192-192 192h-320v-192h319.6c0.2 0 0.4-0.2 0.4-0.4zM192 256v-192l384 384-384 384v-192h-192v-384z" />
<glyph unicode="&#xe1048;" glyph-name="icon-export" d="M192 128.34v639.32l0.34 0.34h319.66v192h-320c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h320v192h-319.66zM1024 448l-384 384v-192h-192v-384h192v-192l384 384z" />
<glyph unicode="&#xe1049;" glyph-name="icon-font-size-alt1" horiz-adv-x="1490" d="M1226.4 640h-176l-76.22-203.24 77-205.34 87.22 232.58 90.74-242h-174.44l49.5-132h174.44l57.76-154h154l-264 704zM384 960l-384-1024h224l84 224h408l84-224h224l-384 1024zM380 352l132 352 132-352z" />
<glyph unicode="&#xe1100;" glyph-name="icon-activity" d="M576 896h-256l320-320h-290.256c-44.264 76.516-126.99 128-221.744 128h-128v-512h128c94.754 0 177.48 51.484 221.744 128h290.256l-320-320h256l448 448-448 448z" />
<glyph unicode="&#xe1101;" glyph-name="icon-activity-mode" d="M512 960c-214.866 0-398.786-132.372-474.744-320h90.744c56.86 0 107.938-24.724 143.094-64h240.906l-192 192h256l320-320-320-320h-256l192 192h-240.906c-35.156-39.276-86.234-64-143.094-64h-90.744c75.958-187.628 259.878-320 474.744-320 282.77 0 512 229.23 512 512s-229.23 512-512 512z" />
<glyph unicode="&#xe1102;" glyph-name="icon-autoflow-tabular" d="M192 960c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h64v1024h-64zM384 960h256v-1024h-256v1024zM832 960h-64v-704h256v512c0 105.6-86.4 192-192 192z" />
@ -115,7 +116,7 @@
<glyph unicode="&#xe1124;" glyph-name="icon-telemetry" d="M32 328.34c14.28 5.62 54.44 47.54 92.96 146 42.46 108.38 116.32 237.66 227.040 237.66 52.4 0 101.42-29.16 145.7-86.68 37.34-48.5 64.84-108.92 81.34-151.080 38.52-98.38 78.68-140.3 92.96-146 14.28 5.62 54.44 47.54 92.96 146 37.4 95.5 99.14 207.14 188.94 232.46-90.462 152.598-254.314 253.3-441.686 253.3-0.075 0-0.15 0-0.226 0-282.748 0-511.988-229.24-511.988-512 0-0.032 0-0.070 0-0.108 0-35.719 3.641-70.587 10.572-104.255 8.968-7.457 16.648-13.417 21.428-15.297zM992 567.66c-14.28-5.62-54.44-47.52-92.96-146-42.46-108.38-116.32-237.66-227.040-237.66-52.4 0-101.42 29.16-145.7 86.68-37.34 48.5-64.84 108.92-81.34 151.080-38.52 98.38-78.68 140.3-92.96 146-14.28-5.62-54.44-47.52-92.96-146-37.4-95.5-99.14-207.14-188.94-232.46 90.462-152.598 254.314-253.3 441.686-253.3 0.075 0 0.15 0 0.226 0 282.748 0 511.988 229.24 511.988 512 0 0.032 0 0.070 0 0.108 0 35.719-3.641 70.587-10.572 104.255-8.968 7.457-16.648 13.417-21.428 15.297z" />
<glyph unicode="&#xe1125;" glyph-name="icon-telemetry-panel" d="M169.2 512c14 56.4 33 122 56.6 176.8 15.4 35.8 31.2 63.2 48.2 84 18.4 22.4 49 49.2 91 49.2s72.6-26.8 91-49.2c17-20.6 32.6-48.2 48.2-84 23.6-54.8 42.8-120.4 56.6-176.8h461.2v256c0 105.6-86.4 192-192 192h-640c-105.6 0-192-86.4-192-192v-256h171.2zM718.6 384h-127.2c25-93.4 48.4-144.4 63.6-168.6 15.2 24.2 38.6 75.2 63.6 168.6zM301.4 512h127.2c-25 93.4-48.4 144.4-63.6 168.6-15.2-24.2-38.6-75.2-63.6-168.6zM850.8 384c-14-56.4-33-122-56.6-176.8-15.4-35.8-31.2-63.2-48.2-84-18.4-22.4-49-49.2-91-49.2s-72.6 26.8-91 49.2c-17 20.6-32.6 48.2-48.2 84-23.6 54.8-42.8 120.4-56.6 176.8h-461.2v-256c0-105.6 86.4-192 192-192h640c105.6 0 192 86.4 192 192v256h-171.2z" />
<glyph unicode="&#xe1126;" glyph-name="icon-timeline" d="M256 704h384v-128h-384v128zM384 512h384v-128h-384v128zM320 320h384v-128h-384v128zM832 960h-128v-192h127.6c0.2 0 0.2-0.2 0.4-0.4v-639.4c0-0.2-0.2-0.2-0.4-0.4h-127.6v-192h128c105.6 0 192 86.4 192 192v640.2c0 105.6-86.4 192-192 192zM192 128.4v639.2c0 0.2 0.2 0.2 0.4 0.4h127.6v192h-128c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h128v192h-127.6c-0.2 0-0.4 0.2-0.4 0.4z" />
<glyph unicode="&#xe1127;" glyph-name="icon-timer-v1.5" horiz-adv-x="896" d="M576 813.4v82.58c0 35.346-28.654 64-64 64h-128c-35.346 0-64-28.654-64-64v-82.58c-185.040-55.080-320-226.48-320-429.42 0-247.42 200.58-448 448-448s448 200.58 448 448c0 202.96-135 374.4-320 429.42zM468 363.98l-263.76-211c-57.105 59.935-92.24 141.251-92.24 230.772 0 0.080 0 0.16 0 0.24 0 185.268 150.72 335.988 336 335.988 6.72 0 13.38-0.22 20-0.62v-355.38z" />
<glyph unicode="&#xe1127;" glyph-name="icon-timer-v15" horiz-adv-x="896" d="M576 813.4v82.58c0 35.346-28.654 64-64 64h-128c-35.346 0-64-28.654-64-64v-82.58c-185.040-55.080-320-226.48-320-429.42 0-247.42 200.58-448 448-448s448 200.58 448 448c0 202.96-135 374.4-320 429.42zM468 363.98l-263.76-211c-57.105 59.935-92.24 141.251-92.24 230.772 0 0.080 0 0.16 0 0.24 0 185.268 150.72 335.988 336 335.988 6.72 0 13.38-0.22 20-0.62v-355.38z" />
<glyph unicode="&#xe1128;" glyph-name="icon-topic" d="M454.36 483.36l86.3 86.3c9.088 8.965 21.577 14.502 35.36 14.502s26.272-5.537 35.366-14.507l86.294-86.294c19.328-19.358 42.832-34.541 69.047-44.082l1.313 171.722-57.64 57.64c-34.407 34.33-81.9 55.558-134.35 55.558s-99.943-21.228-134.354-55.562l-86.296-86.297c-9.088-8.965-21.577-14.502-35.36-14.502s-26.272 5.537-35.366 14.507l-28.674 28.654v-172.14c19.045-7.022 41.040-11.084 63.984-11.084 52.463 0 99.966 21.239 134.379 55.587zM505.64 412.64l-86.3-86.3c-9.088-8.965-21.577-14.502-35.36-14.502s-26.272 5.537-35.366 14.507l-86.294 86.294c-2 2-4.2 4-6.36 6v-197.36c33.664-30.72 78.65-49.537 128.031-49.537 52.44 0 99.923 21.22 134.333 55.541l86.296 86.296c9.088 8.965 21.577 14.502 35.36 14.502s26.272-5.537 35.366-14.507l86.294-86.294c2-2 4.2-4 6.36-6v197.36c-33.664 30.72-78.65 49.537-128.031 49.537-52.44 0-99.923-21.22-134.333-55.541zM832 960h-128v-192h127.66l0.34-0.34v-639.32l-0.34-0.34h-127.66v-192h128c105.6 0 192 86.4 192 192v640c0 105.6-86.4 192-192 192zM320 128h-127.66l-0.34 0.34v639.32l0.34 0.34h127.66v192h-128c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h128v192z" />
<glyph unicode="&#xe1129;" glyph-name="icon-box-with-dashed-lines" d="M0 576h128v-256h-128v256zM128 831.78l0.22 0.22h191.78v128h-192c-70.606-0.215-127.785-57.394-128-127.979v-192.021h128v191.78zM128 64.22v191.78h-128v-192c0.215-70.606 57.394-127.785 127.979-128h192.021v128h-191.78zM384 960h256v-128h-256v128zM896 64.22l-0.22-0.22h-191.78v-128h192c70.606 0.215 127.785 57.394 128 127.979v192.021h-128v-191.78zM896 960h-192v-128h191.78l0.22-0.22v-191.78h128v192c-0.215 70.606-57.394 127.785-127.979 128zM896 576h128v-256h-128v256zM384 64h256v-128h-256v128zM256 704h512v-512h-512v512z" />
<glyph unicode="&#xe1130;" glyph-name="icon-summary-widget" d="M896 960h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v768c0 70.4-57.6 128-128 128zM847.8 349.6l-82.6-143.2-189.6 131.6 19.2-230h-165.4l19.2 230-189.6-131.6-82.6 143.2 208.6 98.4-208.8 98.4 82.6 143.2 189.6-131.6-19.2 230h165.4l-19.2-230 189.6 131.6 82.6-143.2-208.6-98.4 208.8-98.4z" />

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -21,6 +21,26 @@
*****************************************************************************/
/************************************************************* WIDGET OBJECT */
@mixin cSummaryWidget() {
@include boxShdw($shdwBtns);
border-radius: $basicCr;
border-style: solid;
border-width: 1px;
cursor: default;
&[href] {
cursor: pointer;
}
&__label {
&:before {
// Widget icon
font-size: 0.9em;
margin-right: $interiorMarginSm;
}
}
}
.l-summary-widget {
// Widget layout classes here
@include ellipsize();
@ -33,19 +53,9 @@
}
}
.s-summary-widget {
// Widget style classes here
@include boxShdw($shdwBtns);
border-radius: $basicCr;
border-style: solid;
border-width: 1px;
box-sizing: border-box;
cursor: default;
font-size: 0.8rem;
.c-summary-widget {
@include cSummaryWidget();
padding: $interiorMarginLg $interiorMarginLg * 2;
&[href] {
cursor: pointer;
}
}
.widget-edit-holder {
@ -267,7 +277,6 @@
.widget-thumb {
@include ellipsize();
@extend .s-summary-widget;
@extend .l-summary-widget;
@include cSummaryWidget();
padding: $interiorMarginSm $interiorMargin;
}

View File

@ -31,10 +31,6 @@
.c-create-button,
.c-create-menu {
&--w {
// Wrapper for Create button and menu
overflow: visible;
}
font-size: 1.1em;
}

View File

@ -0,0 +1,168 @@
<template>
<div class="c-custom-checkbox">
<input type="checkbox"
:id="id"
:name="name"
:value="value"
:required="required"
:disabled="disabled"
@change="onChange"
:checked="state">
<label :for="id">
<div class="c-custom-checkbox__box"></div>
<div class="c-custom-checkbox__label-text">
<slot></slot>
</div>
</label>
</div>
</template>
<style lang="scss">
@import "~styles/sass-base";
.c-custom-checkbox {
$d: 14px;
display: flex;
align-items: center;
label {
@include userSelectNone();
display: flex;
align-items: center;
}
&__box {
@include nice-input();
display: flex;
align-items: center;
justify-content: center;
line-height: $d;
width: $d;
height: $d;
margin-right: $interiorMarginSm;
}
input {
opacity: 0;
position: absolute;
&:checked + label > .c-custom-checkbox__box {
background: $colorKey;
&:before {
color: $colorKeyFg;
content: $glyph-icon-check;
font-family: symbolsfont;
font-size: 0.6em;
}
}
&:not(:disabled) + label {
cursor: pointer;
}
&:disabled + label {
opacity: 0.5;
}
}
}
</style>
<script>
/*
Custom checkbox control. Use just like a checkbox in HTML, except label string is passed within tag.
Supports value, true-value, false-value, checked and disabled attributes.
Example usage:
<checkbox checked>Enable markers</checkbox>
*/
export default {
model: {
prop: 'modelValue',
event: 'input'
},
props: {
id: {
type: String,
default: function () {
return 'checkbox-id-' + this._uid;
},
},
name: {
type: String,
default: null,
},
value: {
default: null,
},
modelValue: {
default: undefined,
},
checked: {
type: Boolean,
default: false,
},
required: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
model: {}
},
computed: {
state() {
if (this.modelValue === undefined) {
return this.checked;
}
if (Array.isArray(this.modelValue)) {
return this.modelValue.indexOf(this.value) > -1;
}
return !!this.modelValue;
}
},
methods: {
onChange() {
this.toggle();
},
toggle() {
let value;
if (Array.isArray(this.modelValue)) {
value = this.modelValue.slice(0);
if (this.state) {
value.splice(value.indexOf(this.value), 1);
} else {
value.push(this.value);
}
} else {
value = !this.state;
}
this.$emit('input', value);
}
},
watch: {
checked(newValue) {
if (newValue !== this.state) {
this.toggle();
}
}
},
mounted() {
if (this.checked && !this.state) {
this.toggle();
}
},
};
</script>

View File

@ -0,0 +1,51 @@
<template>
<div class="c-labeled-input"
:title="title">
<div class="c-labeled-input__label">{{ label }}</div>
<input type="number"
v-bind="$attrs"
v-bind:value="value"
v-on="inputListeners"/>
</div>
</template>
<script>
/* Emits input and clear events */
export default {
inheritAttrs: false,
props: {
value: String,
label: String,
title: String
},
computed: {
inputListeners: function () {
let vm = this;
return Object.assign({},
this.$listeners,
{
input: function (event) {
vm.$emit('input', event.target.value);
},
change: function (event) {
vm.$emit('change', event.target.value);
}
}
)
}
},
data: function() {
return {
// active: false
}
},
methods: {
clearInput() {
// Clear the user's input and set 'active' to false
this.value = '';
this.$emit('clear','');
this.active = false;
}
}
}
</script>

View File

@ -15,45 +15,15 @@
<style lang="scss">
@import "~styles/sass-base";
/******************************* SEARCH */
.c-search {
@include nice-input();
display: flex;
align-items: center;
padding: 2px 4px;
@include wrappedInput();
padding-top: 2px;
padding-bottom: 2px;
&:before {
// Mag glass icon
content: $glyph-icon-magnify;
direction: rtl; // Aligns glyph to right-hand side of container, for transition
display: block;
font-family: symbolsfont;
flex: 0 0 auto;
opacity: 0.5;
overflow: hidden;
padding: 2px 0; // Prevents clipping
transition: width 250ms ease;
width: 1em;
}
&:hover {
box-shadow: inset rgba(black, 0.8) 0 0px 2px;
&:before {
opacity: 0.9;
}
}
&--major {
padding: 4px;
}
&__input {
background: none !important;
box-shadow: none !important; // !important needed to override default for [input]
flex: 1 1 auto;
padding-left: 2px !important;
padding-right: 2px !important;
min-width: 10px; // Must be set to allow input to collapse below browser min
}
&__clear-input {
@ -61,11 +31,6 @@
}
&.is-active {
&:before {
padding: 2px 0px;
width: 0px;
}
.c-search__clear-input {
display: block;
}

View File

@ -0,0 +1,176 @@
<template>
<div class="c-togglebutton">
<input type="checkbox"
:id="id"
:name="name"
:value="value"
:required="required"
:disabled="disabled"
@change="onChange"
:checked="state">
<label :for="id">
<div class="c-togglebutton__on"
:class="innerClassOn"></div>
<div class="c-togglebutton__off"
:class="innerClassOff"></div>
</label>
</div>
</template>
<style lang="scss">
@import "~styles/sass-base";
.c-togglebutton {
$d: 14px;
display: flex;
align-items: center;
label {
display: flex;
align-items: center;
justify-content: center;
.c-togglebutton__on {
display: none;
}
}
input {
opacity: 0;
position: absolute;
&:checked + label {
.c-togglebutton__on {
display: block;
}
.c-togglebutton__off {
display: none;
}
}
&:not(:disabled) + label {
cursor: pointer;
}
&:disabled + label {
opacity: 0.5;
}
}
}
</style>
<script>
/*
Toggle button control, based on checkboxCustom. Use just like a checkbox in HTML.
Requires inner-class-on and -off attributes to be passed.
Supports checked and disabled attributes.
Example usage:
<toggle-button checked
class="c-click-icon"
inner-class-on="icon-grid-snap-to"
inner-class-off="icon-grid-snap-no"></toggle-button>
*/
export default {
model: {
prop: 'modelValue',
event: 'input'
},
props: {
innerClassOn: {
type: String,
default: null,
required: true
},
innerClassOff: {
type: String,
default: null,
required: true
},
id: {
type: String,
default: function () {
return 'checkbox-id-' + this._uid;
},
},
name: {
type: String,
default: null,
},
value: {
default: null,
},
modelValue: {
default: undefined,
},
checked: {
type: Boolean,
default: false,
},
required: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
model: {}
},
computed: {
state() {
if (this.modelValue === undefined) {
return this.checked;
}
if (Array.isArray(this.modelValue)) {
return this.modelValue.indexOf(this.value) > -1;
}
return !!this.modelValue;
},
stateClass() {
return this.onClass;
}
},
methods: {
onChange() {
this.toggle();
},
toggle() {
let value;
if (Array.isArray(this.modelValue)) {
value = this.modelValue.slice(0);
if (this.state) {
value.splice(value.indexOf(this.value), 1);
} else {
value.push(this.value);
}
} else {
value = !this.state;
}
this.$emit('input', value);
}
},
watch: {
checked(newValue) {
if (newValue !== this.state) {
this.toggle();
}
}
},
mounted() {
if (this.checked && !this.state) {
this.toggle();
}
},
};
</script>

View File

@ -142,7 +142,6 @@
display: flex;
align-items: center;
flex: 1 1 auto;
font-size: 1.4em;
margin-right: $interiorMargin;
min-width: 0; // Forces interior to compress when pushed on
}
@ -174,6 +173,7 @@
align-items: center;
display: flex;
flex: 0 1 auto;
font-size: 1.4em;
min-width: 0;
&:before {

View File

@ -27,10 +27,12 @@
<mct-tree></mct-tree>
</div>
</pane>
<pane class="l-shell__pane-main">
<pane class="l-shell__pane-main"
:class="{ 'is-editing' : true }">
<browse-bar class="l-shell__main-view-browse-bar"
ref="browseBar">
ref="browseBar">
</browse-bar>
<toolbar class="l-shell__toolbar"></toolbar>
<object-view class="l-shell__main-container"
ref="browseObject">
</object-view>
@ -162,7 +164,7 @@
margin-right: 2.5%;
}
/********** MAIN AREA */
/******************************* MAIN AREA */
&__main-container {
// Wrapper for main views
flex: 1 1 auto;
@ -196,6 +198,11 @@
&__pane-inspector {
width: 200px;
}
&__toolbar {
flex: 0 0 auto;
margin-bottom: $interiorMargin;
}
}
}
</style>
@ -212,6 +219,7 @@
import multipane from '../controls/multipane.vue';
import pane from '../controls/pane.vue';
import BrowseBar from './BrowseBar.vue';
import Toolbar from './Toolbar.vue';
var enterFullScreen = () => {
var docElm = document.documentElement;
@ -257,7 +265,8 @@
search,
multipane,
pane,
BrowseBar
BrowseBar,
Toolbar
},
mounted() {
this.openmct.editor.on('isEditing', (isEditing)=>{

View File

@ -2,7 +2,7 @@
</template>
<style lang="scss">
.l-object-view {
.c-object-view {
display: contents;
}
</style>
@ -33,6 +33,7 @@ export default {
this.debounceUpdateView = _.debounce(this.updateView, 10);
},
mounted() {
this.currentObject = this.object;
this.updateView();
},
methods: {
@ -50,7 +51,7 @@ export default {
return;
}
this.viewContainer = document.createElement('div');
this.viewContainer.classList.add('l-object-view');
this.viewContainer.classList.add('c-object-view');
this.$el.append(this.viewContainer);
let provider = this.openmct.objectViews.getByProviderKey(this.viewKey);
if (!provider) {

View File

@ -0,0 +1,291 @@
<template>
<div class="c-toolbar">
<!-- VERSION MANUALLY RESTORED FROM VUE-LAYOUT -->
<div class="c-button-set">
<div class="c-ctrl-wrapper">
<div class="c-button--menu js-add-button icon-plus"
@click="toggleMenus">
<div class="c-button__label">Add</div>
</div>
<div class="c-menu" v-if="showMenus">
<ul>
<li v-for="item in addMenuItems"
:class="item.class"
:title="item.title">
{{ item.name }}
</li>
</ul>
</div>
</div>
</div>
<div class="c-button-set"
v-if="toolsItemSelected">
<div class="c-ctrl-wrapper"
v-if="toolsItemSelected">
<div class="c-click-icon c-click-icon--menu js-layers icon-layers"
@click="toggleMenus"></div>
<div class="c-menu" v-if="showMenus">
<ul>
<li v-for="item in layersMenuItems"
:class="item.class"
:title="item.title">
{{ item.name }}
</li>
</ul>
</div>
</div>
<div class="c-ctrl-wrapper"
v-if="toolsColorFill">
<div class="c-click-icon c-click-icon--swatched js-color-fill icon-paint-bucket"
@click="toggleMenus">
<div class="c-swatch" style="background: #33ff00;"></div>
</div>
<div class="c-menu c-palette c-palette--color"
v-if="showMenus">
<div class="c-palette__item-none"
vif="this.palette.itemNone === true">
<div class="c-palette__item"
@click="this.setColor('no-color')"></div>
No fill
</div>
<div class="c-palette__items">
<div class="c-palette__item"
v-for="color in colorPalette"
:style="{ background: color.value }"
@click="this.setColor(color.value)"></div>
</div>
</div>
</div>
<div class="c-ctrl-wrapper"
v-if="toolsColorStroke">
<div class="c-click-icon c-click-icon--swatched js-color-stroke icon-pencil">
<div class="c-toolbar-button__swatch" style="background: #ffffff;"></div>
</div>
</div>
<div class="c-ctrl-wrapper"
v-if="toolsColorText">
<div class="c-click-icon c-click-icon--swatched js-color-text icon-font">
<div class="c-toolbar-button__swatch" style="background: #333333;"></div>
</div>
</div>
</div>
<div class="c-button-set"
v-if="toolsItemSelected && toolsFontSize">
<div class="c-ctrl-wrapper">
<div class="c-click-icon c-click-icon--menu js-font-size"
@click="toggleMenus">
<div class="c-button__label">11 px</div>
</div>
<div class="c-menu" v-if="showMenus">
<ul>
<li v-for="item in fontSizeMenuItems">
{{ item.name }}
</li>
</ul>
</div>
</div>
</div>
<div class="c-button-set"
v-if="toolsItemSelected && toolsEditProperties">
<div class="c-ctrl-wrapper">
<div class="c-click-icon js-image icon-gear"></div>
</div>
</div>
<div class="c-button-set"
v-if="toolsItemSelected">
<labeledNumberInput label="X" value=1 title="X position"></labeledNumberInput>
<labeledNumberInput label="Y" value=2 title="Y position"></labeledNumberInput>
<labeledNumberInput label="W" value=3 title="Width"></labeledNumberInput>
<labeledNumberInput label="H" value=4 title="Height"></labeledNumberInput>
</div>
<div class="c-button-set"
v-if="toolsItemSelected">
<div class="c-click-icon c-click-icon--caution icon-trash"></div>
</div>
<div class="c-button-set"
v-if="toolsItemSelected">
<checkbox checked title="This is a checkbox">Checkbox</checkbox>
</div>
<div class="c-button-set"
v-if="toolsItemSelected">
<toggle-button title="Toggle frame" checked
class="c-click-icon"
inner-class-on="icon-frame-show"
inner-class-off="icon-frame-hide"></toggle-button>
<toggle-button title="Snap to grid" checked
class="c-click-icon"
inner-class-on="icon-grid-snap-to"
inner-class-off="icon-grid-snap-no"></toggle-button>
<toggle-button title="Show label and value" checked
class="c-click-icon"
inner-class-on="icon-two-parts-both"
inner-class-off="icon-two-parts-one-only"></toggle-button>
</div>
</div>
</template>
<script>
import labeledNumberInput from '../controls/labeledNumberInput.vue';
import checkbox from '../controls/checkboxCustom.vue';
import toggleButton from '../controls/toggleButton.vue';
export default {
components: {
labeledNumberInput,
checkbox,
toggleButton
},
methods: {
toggleMenus: function () {
this.showMenus = !this.showMenus;
}
},
props: {
toolsItemSelected: { type: Boolean, default: true },
toolsColorFill: { type: Boolean, default: true },
toolsColorStroke: { type: Boolean, default: true },
toolsColorText: { type: Boolean, default: true },
toolsFontSize: { type: Boolean, default: true },
toolsEditProperties: { type: Boolean, default: true },
toolSetBox: ['toolsColorFill', 'toolsColorStroke'],
toolSetLine: ['toolsColorStroke'],
toolSetText: ['toolsColorFill', 'toolsColorStroke', 'toolsColorText', 'toolsFontSize', 'toolsEditProperties'],
toolSetImage: ['toolsColorStroke', 'toolsEditProperties'],
toolSetTelemetry: ['toolsColorFill', 'toolsColorStroke', 'toolsColorText', 'toolsFontSize', 'toolsLabelValue']
},
data: function () {
return {
showMenus: false,
addMenuItems: [
{ name: 'Box', class: 'icon-box', title: 'Add Box' },
{ name: 'Line', class: 'icon-line-horz', title: 'Add Line' },
{ name: 'Text', class: 'icon-font', title: 'Add Text' },
{ name: 'Image', class: 'icon-image', title: 'Add Image' }
],
layersMenuItems: [
{ name: 'Move to top', class: 'icon-arrow-double-up', title: 'Move to top' },
{ name: 'Move up', class: 'icon-arrow-up', title: 'Move up' },
{ name: 'Move down', class: 'icon-arrow-down', title: 'Move down' },
{ name: 'Move to bottom', class: 'icon-arrow-double-down', title: 'Move to bottom' }
],
fontSizeMenuItems: [
{ value: '9', name: '9 px' },
{ value: '10', name: '10 px' },
{ value: '11', name: '11 px' },
{ value: '12', name: '12 px' },
{ value: '13', name: '13 px' },
{ value: '14', name: '14 px' },
{ value: '16', name: '16 px' },
{ value: '18', name: '18 px' },
{ value: '20', name: '20 px' },
{ value: '24', name: '24 px' },
{ value: '28', name: '28 px' },
{ value: '32', name: '32 px' },
{ value: '40', name: '40 px' },
{ value: '48', name: '48 px' },
{ value: '56', name: '56 px' },
{ value: '64', name: '64 px' },
{ value: '72', name: '72 px' },
{ value: '80', name: '80 px' },
{ value: '88', name: '88 px' },
{ value: '96', name: '96 px' },
{ value: '128', name: '128 px' },
{ value: '160', name: '160 px' }
],
colorPalette: [
{ value: '#000000' },
{ value: '#434343' },
{ value: '#666666' },
{ value: '#999999' },
{ value: '#b7b7b7' },
{ value: '#cccccc' },
{ value: '#d9d9d9' },
{ value: '#efefef' },
{ value: '#f3f3f3' },
{ value: '#ffffff' },
{ value: '#980000' },
{ value: '#ff0000' },
{ value: '#ff9900' },
{ value: '#ffff00' },
{ value: '#00ff00' },
{ value: '#00ffff' },
{ value: '#4a86e8' },
{ value: '#0000ff' },
{ value: '#9900ff' },
{ value: '#ff00ff' },
{ value: '#e6b8af' },
{ value: '#f4cccc' },
{ value: '#fce5cd' },
{ value: '#fff2cc' },
{ value: '#d9ead3' },
{ value: '#d0e0e3' },
{ value: '#c9daf8' },
{ value: '#cfe2f3' },
{ value: '#d9d2e9' },
{ value: '#ead1dc' },
{ value: '#dd7e6b' },
{ value: '#dd7e6b' },
{ value: '#f9cb9c' },
{ value: '#ffe599' },
{ value: '#b6d7a8' },
{ value: '#a2c4c9' },
{ value: '#a4c2f4' },
{ value: '#9fc5e8' },
{ value: '#b4a7d6' },
{ value: '#d5a6bd' },
{ value: '#cc4125' },
{ value: '#e06666' },
{ value: '#f6b26b' },
{ value: '#ffd966' },
{ value: '#93c47d' },
{ value: '#76a5af' },
{ value: '#6d9eeb' },
{ value: '#6fa8dc' },
{ value: '#8e7cc3' },
{ value: '#c27ba0' },
{ value: '#a61c00' },
{ value: '#cc0000' },
{ value: '#e69138' },
{ value: '#f1c232' },
{ value: '#6aa84f' },
{ value: '#45818e' },
{ value: '#3c78d8' },
{ value: '#3d85c6' },
{ value: '#674ea7' },
{ value: '#a64d79' },
{ value: '#85200c' },
{ value: '#990000' },
{ value: '#b45f06' },
{ value: '#bf9000' },
{ value: '#38761d' },
{ value: '#134f5c' },
{ value: '#1155cc' },
{ value: '#0b5394' },
{ value: '#351c75' },
{ value: '#741b47' },
{ value: '#5b0f00' },
{ value: '#660000' },
{ value: '#783f04' },
{ value: '#7f6000' },
{ value: '#274e13' },
{ value: '#0c343d' },
{ value: '#1c4587' },
{ value: '#073763' },
{ value: '#20124d' },
{ value: '#4c1130' }
]
}
}
}
</script>

View File

@ -99,7 +99,7 @@
children: []
};
},
inject: ['openmct'],
inject: ['openmct', 'domainObject'],
mounted: function () {
this.openmct.objects.get('ROOT')
.then(root => this.openmct.composition.get(root).load())

View File

@ -8,6 +8,8 @@
@click="toggleChildren">
</view-control>
<a class="c-tree__item__label"
draggable="true"
@dragstart="dragStart"
:href="href">
<div class="c-tree__item__type-icon"
:class="cssClass"></div>
@ -89,6 +91,9 @@
.then(() => this.loaded = true);
}
},
dragStart($event) {
$event.dataTransfer.setData("domainObject", JSON.stringify(this.node.object));
}
},
components: {
viewControl