mirror of
https://github.com/nasa/openmct.git
synced 2025-02-01 00:45:41 +00:00
Merge remote-tracking branch 'origin/open880' into open-master
This commit is contained in:
commit
0b29e05a31
@ -26,9 +26,32 @@
|
||||
"items": [
|
||||
{
|
||||
"method": "add",
|
||||
"control": "button",
|
||||
"glyph": "+",
|
||||
"control": "menu-button",
|
||||
"text": "Add",
|
||||
"inclusive": true
|
||||
"inclusive": true,
|
||||
"options": [
|
||||
{
|
||||
"name": "Box",
|
||||
"glyph": "\u2610",
|
||||
"key": "fixed.box"
|
||||
},
|
||||
{
|
||||
"name": "Line",
|
||||
"glyph": "-",
|
||||
"key": "fixed.line"
|
||||
},
|
||||
{
|
||||
"name": "Text",
|
||||
"glyph": "\u1D1B",
|
||||
"key": "fixed.text"
|
||||
},
|
||||
{
|
||||
"name": "Image",
|
||||
"glyph": "\u2353",
|
||||
"key": "fixed.image"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -61,13 +84,35 @@
|
||||
{
|
||||
"key": "FixedController",
|
||||
"implementation": "FixedController.js",
|
||||
"depends": [ "$scope", "telemetrySubscriber", "telemetryFormatter" ]
|
||||
"depends": [
|
||||
"$scope",
|
||||
"$q",
|
||||
"dialogService",
|
||||
"telemetrySubscriber",
|
||||
"telemetryFormatter"
|
||||
]
|
||||
}
|
||||
],
|
||||
"templates": [
|
||||
{
|
||||
"key": "fixed.telemetry",
|
||||
"templateUrl": "templates/elements/telemetry.html"
|
||||
},
|
||||
{
|
||||
"key": "fixed.box",
|
||||
"templateUrl": "templates/elements/box.html"
|
||||
},
|
||||
{
|
||||
"key": "fixed.line",
|
||||
"templateUrl": "templates/elements/line.html"
|
||||
},
|
||||
{
|
||||
"key": "fixed.text",
|
||||
"templateUrl": "templates/elements/text.html"
|
||||
},
|
||||
{
|
||||
"key": "fixed.image",
|
||||
"templateUrl": "templates/elements/image.html"
|
||||
}
|
||||
],
|
||||
"types": [
|
||||
|
3
platform/features/layout/res/templates/elements/box.html
Normal file
3
platform/features/layout/res/templates/elements/box.html
Normal file
@ -0,0 +1,3 @@
|
||||
<div ng-style="{ background: ngModel.element.fill }"
|
||||
style="width: 100%; height: 100%;">
|
||||
</div>
|
@ -0,0 +1,3 @@
|
||||
<div ng-style="{ 'background-image': 'url(' + ngModel.element.url + ')'}"
|
||||
style="width: 100%; height: 100%; background-size: contain; background-repeat: no-repeat; background-position: center;">
|
||||
</div>
|
10
platform/features/layout/res/templates/elements/line.html
Normal file
10
platform/features/layout/res/templates/elements/line.html
Normal file
@ -0,0 +1,10 @@
|
||||
<svg ng-attr-width="{{parameters.gridSize[0] * ngModel.width()}}"
|
||||
ng-attr-height="{{parameters.gridSize[1] * ngModel.height()}}">
|
||||
<line ng-attr-x1="{{parameters.gridSize[0] * ngModel.x1() + 1}}"
|
||||
ng-attr-y1="{{parameters.gridSize[1] * ngModel.y1() + 1}}"
|
||||
ng-attr-x2="{{parameters.gridSize[0] * ngModel.x2() + 1}}"
|
||||
ng-attr-y2="{{parameters.gridSize[1] * ngModel.y2() + 1}}"
|
||||
stroke="lightgray"
|
||||
stroke-width="2">
|
||||
</line>
|
||||
</svg>
|
After Width: | Height: | Size: 486 B |
@ -0,0 +1,4 @@
|
||||
<div ng-style="{ background: ngModel.element.fill }"
|
||||
style="width: 100%; height: 100%; overflow: hidden;">
|
||||
{{ngModel.element.text}}
|
||||
</div>
|
@ -14,6 +14,7 @@
|
||||
<mct-include ng-repeat="element in controller.getElements()"
|
||||
style="position: absolute;"
|
||||
key="element.template"
|
||||
parameters="{ gridSize: controller.getGridSize() }"
|
||||
ng-class="{ test: controller.selected(element) }"
|
||||
ng-style="element.style"
|
||||
ng-click="controller.select(element)"
|
||||
|
@ -17,7 +17,7 @@ define(
|
||||
* @constructor
|
||||
* @param {Scope} $scope the controller's Angular scope
|
||||
*/
|
||||
function FixedController($scope, telemetrySubscriber, telemetryFormatter) {
|
||||
function FixedController($scope, $q, dialogService, telemetrySubscriber, telemetryFormatter) {
|
||||
var gridSize = DEFAULT_GRID_SIZE,
|
||||
gridExtent = DEFAULT_GRID_EXTENT,
|
||||
dragging,
|
||||
@ -52,13 +52,13 @@ define(
|
||||
|
||||
// Convert from element x/y/width/height to an
|
||||
// apropriate ng-style argument, to position elements.
|
||||
function convertPosition(raw) {
|
||||
function convertPosition(elementProxy) {
|
||||
// Multiply position/dimensions by grid size
|
||||
return {
|
||||
left: (gridSize[0] * raw.x) + 'px',
|
||||
top: (gridSize[1] * raw.y) + 'px',
|
||||
width: (gridSize[0] * raw.width) + 'px',
|
||||
height: (gridSize[1] * raw.height) + 'px'
|
||||
left: (gridSize[0] * elementProxy.x()) + 'px',
|
||||
top: (gridSize[1] * elementProxy.y()) + 'px',
|
||||
width: (gridSize[0] * elementProxy.width()) + 'px',
|
||||
height: (gridSize[1] * elementProxy.height()) + 'px'
|
||||
};
|
||||
}
|
||||
|
||||
@ -89,7 +89,7 @@ define(
|
||||
|
||||
if (e) {
|
||||
// Provide a displayable position (convert from grid to px)
|
||||
e.style = convertPosition(element);
|
||||
e.style = convertPosition(e);
|
||||
// Template names are same as type names, presently
|
||||
e.template = element.type;
|
||||
}
|
||||
@ -163,8 +163,8 @@ define(
|
||||
subscribe($scope.domainObject);
|
||||
}
|
||||
|
||||
// Position a panel after a drop event
|
||||
function handleDrop(e, id, position) {
|
||||
// Add an element to this view
|
||||
function addElement(element) {
|
||||
// Ensure that configuration field is populated
|
||||
$scope.configuration = $scope.configuration || {};
|
||||
// Make sure there is a "elements" field in the
|
||||
@ -172,7 +172,23 @@ define(
|
||||
$scope.configuration.elements =
|
||||
$scope.configuration.elements || [];
|
||||
// Store the position of this element.
|
||||
$scope.configuration.elements.push({
|
||||
$scope.configuration.elements.push(element);
|
||||
// Refresh displayed elements
|
||||
refreshElements();
|
||||
// Select the newly-added element
|
||||
if (selection) {
|
||||
selection.select(elementProxies[elementProxies.length - 1]);
|
||||
}
|
||||
// Mark change as persistable
|
||||
if ($scope.commit) {
|
||||
$scope.commit("Dropped an element.");
|
||||
}
|
||||
}
|
||||
|
||||
// Position a panel after a drop event
|
||||
function handleDrop(e, id, position) {
|
||||
// Store the position of this element.
|
||||
addElement({
|
||||
type: "fixed.telemetry",
|
||||
x: Math.floor(position.x / gridSize[0]),
|
||||
y: Math.floor(position.y / gridSize[1]),
|
||||
@ -180,17 +196,14 @@ define(
|
||||
width: DEFAULT_DIMENSIONS[0],
|
||||
height: DEFAULT_DIMENSIONS[1]
|
||||
});
|
||||
// Mark change as persistable
|
||||
if ($scope.commit) {
|
||||
$scope.commit("Dropped an element.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Track current selection state
|
||||
if (Array.isArray($scope.selection)) {
|
||||
selection = new LayoutSelection(
|
||||
$scope.selection,
|
||||
new FixedProxy($scope.configuration)
|
||||
new FixedProxy(addElement, $q, dialogService)
|
||||
);
|
||||
}
|
||||
|
||||
@ -222,6 +235,14 @@ define(
|
||||
getCellStyles: function () {
|
||||
return cellStyles;
|
||||
},
|
||||
/**
|
||||
* Get the size of the grid, in pixels. The returned array
|
||||
* is in the form `[x, y]`.
|
||||
* @returns {number[]} the grid size
|
||||
*/
|
||||
getGridSize: function () {
|
||||
return gridSize;
|
||||
},
|
||||
/**
|
||||
* Set the size of the viewable fixed position area.
|
||||
* @memberof FixedController#
|
||||
@ -303,9 +324,11 @@ define(
|
||||
*/
|
||||
continueDrag: function (delta) {
|
||||
if (dragging) {
|
||||
// Update x/y values
|
||||
dragging.element.x(dragging.x + Math.round(delta[0] / gridSize[0]));
|
||||
dragging.element.y(dragging.y + Math.round(delta[1] / gridSize[1]));
|
||||
dragging.element.style = convertPosition(dragging.element.element);
|
||||
// Update display position
|
||||
dragging.element.style = convertPosition(dragging.element);
|
||||
}
|
||||
},
|
||||
/**
|
||||
|
@ -1,22 +1,42 @@
|
||||
/*global define,window*/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
['./elements/ElementFactory'],
|
||||
function (ElementFactory) {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Proxy for configuring a fixed position view via the toolbar.
|
||||
* @constructor
|
||||
* @param configuration the view configuration object
|
||||
* @param {Function} addElementCallback callback to invoke when
|
||||
* elements are created
|
||||
* @param $q Angular's $q, for promise-handling
|
||||
* @param {DialogService} dialogService dialog service to use
|
||||
* when adding a new element will require user input
|
||||
*/
|
||||
function FixedProxy(configuration) {
|
||||
function FixedProxy(addElementCallback, $q, dialogService) {
|
||||
var factory = new ElementFactory(dialogService);
|
||||
|
||||
return {
|
||||
/**
|
||||
* Add a new visual element to this view.
|
||||
*/
|
||||
add: function (type) {
|
||||
window.alert("Placeholder. Should add a " + type + ".");
|
||||
// Place a configured element into the view configuration
|
||||
function addElement(element) {
|
||||
// Configure common properties of the element
|
||||
element.x = element.x || 0;
|
||||
element.y = element.y || 0;
|
||||
element.width = element.width || 1;
|
||||
element.height = element.height || 1;
|
||||
element.type = type;
|
||||
|
||||
// Finally, add it to the view's configuration
|
||||
addElementCallback(element);
|
||||
}
|
||||
|
||||
// Defer creation to the factory
|
||||
$q.when(factory.createElement(type)).then(addElement);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
89
platform/features/layout/src/elements/ElementFactory.js
Normal file
89
platform/features/layout/src/elements/ElementFactory.js
Normal file
@ -0,0 +1,89 @@
|
||||
/*global define*/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
"use strict";
|
||||
|
||||
var INITIAL_STATES = {
|
||||
"fixed.image": {},
|
||||
"fixed.box": {
|
||||
fill: "#888",
|
||||
border: "transparent"
|
||||
},
|
||||
"fixed.line": {
|
||||
x: 5,
|
||||
y: 9,
|
||||
x2: 6,
|
||||
y2: 6
|
||||
},
|
||||
"fixed.text": {
|
||||
fill: "transparent",
|
||||
border: "transparent"
|
||||
}
|
||||
},
|
||||
DIALOGS = {
|
||||
"fixed.image": {
|
||||
name: "Image Properties",
|
||||
sections: [
|
||||
{
|
||||
rows: [
|
||||
{
|
||||
key: "url",
|
||||
control: "textfield",
|
||||
name: "Image URL",
|
||||
required: true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"fixed.text": {
|
||||
name: "Text Element Properties",
|
||||
sections: [
|
||||
{
|
||||
rows: [
|
||||
{
|
||||
key: "text",
|
||||
control: "textfield",
|
||||
name: "Text",
|
||||
required: true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The ElementFactory creates new instances of elements for the
|
||||
* fixed position view, prompting for user input where necessary.
|
||||
* @param {DialogService} dialogService service to request user input
|
||||
* @constructor
|
||||
*/
|
||||
function ElementFactory(dialogService) {
|
||||
return {
|
||||
/**
|
||||
* Create a new element for the fixed position view.
|
||||
* @param {string} type the type of element to create
|
||||
* @returns {Promise|object} the created element, or a promise
|
||||
* for that element
|
||||
*/
|
||||
createElement: function (type) {
|
||||
var initialState = INITIAL_STATES[type] || {};
|
||||
|
||||
// Clone that state
|
||||
initialState = JSON.parse(JSON.stringify(initialState));
|
||||
|
||||
// Show a dialog to configure initial state, if appropriate
|
||||
return DIALOGS[type] ? dialogService.getUserInput(
|
||||
DIALOGS[type],
|
||||
initialState
|
||||
) : initialState;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return ElementFactory;
|
||||
}
|
||||
);
|
@ -1,12 +1,16 @@
|
||||
/*global define*/
|
||||
|
||||
define(
|
||||
['./TelemetryProxy'],
|
||||
function (TelemetryProxy) {
|
||||
['./TelemetryProxy', './ElementProxy', './LineProxy'],
|
||||
function (TelemetryProxy, ElementProxy, LineProxy) {
|
||||
"use strict";
|
||||
|
||||
return {
|
||||
"fixed.telemetry": TelemetryProxy
|
||||
"fixed.telemetry": TelemetryProxy,
|
||||
"fixed.line": LineProxy,
|
||||
"fixed.box": ElementProxy,
|
||||
"fixed.image": ElementProxy,
|
||||
"fixed.text": ElementProxy
|
||||
};
|
||||
}
|
||||
);
|
@ -11,18 +11,54 @@ define(
|
||||
* This handles the generic operations (e.g. remove) so that
|
||||
* subclasses only need to implement element-specific behaviors.
|
||||
* @constructor
|
||||
* @param element the telemetry element
|
||||
* @param element the fixed position element, as stored in its
|
||||
* configuration
|
||||
* @param index the element's index within its array
|
||||
* @param {Array} elements the full array of elements
|
||||
*/
|
||||
function ElementProxy(element, index, elements) {
|
||||
return {
|
||||
/**
|
||||
* The element as stored in the view configuration.
|
||||
*/
|
||||
element: element,
|
||||
/**
|
||||
* Get and/or set the x position of this element.
|
||||
* Units are in fixed position grid space.
|
||||
* @param {number} [x] the new x position (if setting)
|
||||
* @returns {number} the x position
|
||||
*/
|
||||
x: new AccessorMutator(element, 'x'),
|
||||
/**
|
||||
* Get and/or set the y position of this element.
|
||||
* Units are in fixed position grid space.
|
||||
* @param {number} [y] the new y position (if setting)
|
||||
* @returns {number} the y position
|
||||
*/
|
||||
y: new AccessorMutator(element, 'y'),
|
||||
/**
|
||||
* Get and/or set the z index of this element.
|
||||
* @param {number} [z] the new z index (if setting)
|
||||
* @returns {number} the z index
|
||||
*/
|
||||
z: new AccessorMutator(element, 'z'),
|
||||
/**
|
||||
* Get and/or set the width of this element.
|
||||
* Units are in fixed position grid space.
|
||||
* @param {number} [w] the new width (if setting)
|
||||
* @returns {number} the width
|
||||
*/
|
||||
width: new AccessorMutator(element, 'width'),
|
||||
/**
|
||||
* Get and/or set the height of this element.
|
||||
* Units are in fixed position grid space.
|
||||
* @param {number} [h] the new height (if setting)
|
||||
* @returns {number} the height
|
||||
*/
|
||||
height: new AccessorMutator(element, 'height'),
|
||||
/**
|
||||
* Remove this element from the fixed position view.
|
||||
*/
|
||||
remove: function () {
|
||||
if (elements[index] === element) {
|
||||
elements.splice(index, 1);
|
||||
|
113
platform/features/layout/src/elements/LineProxy.js
Normal file
113
platform/features/layout/src/elements/LineProxy.js
Normal file
@ -0,0 +1,113 @@
|
||||
/*global define*/
|
||||
|
||||
define(
|
||||
['./ElementProxy'],
|
||||
function (ElementProxy) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Selection/diplay proxy for line elements of a fixed position
|
||||
* view.
|
||||
* @constructor
|
||||
* @param element the fixed position element, as stored in its
|
||||
* configuration
|
||||
* @param index the element's index within its array
|
||||
* @param {Array} elements the full array of elements
|
||||
*/
|
||||
function LineProxy(element, index, elements) {
|
||||
var proxy = new ElementProxy(element, index, elements);
|
||||
|
||||
/**
|
||||
* Get the top-left x coordinate, in grid space, of
|
||||
* this line's bounding box.
|
||||
* @returns {number} the x coordinate
|
||||
*/
|
||||
proxy.x = function (v) {
|
||||
var x = Math.min(element.x, element.x2),
|
||||
delta = v - x;
|
||||
if (arguments.length > 0 && delta) {
|
||||
element.x += delta;
|
||||
element.x2 += delta;
|
||||
}
|
||||
return x;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the top-left y coordinate, in grid space, of
|
||||
* this line's bounding box.
|
||||
* @returns {number} the y coordinate
|
||||
*/
|
||||
proxy.y = function (v) {
|
||||
var y = Math.min(element.y, element.y2),
|
||||
delta = v - y;
|
||||
if (arguments.length > 0 && delta) {
|
||||
element.y += delta;
|
||||
element.y2 += delta;
|
||||
}
|
||||
return y;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the width, in grid space, of
|
||||
* this line's bounding box.
|
||||
* @returns {number} the width
|
||||
*/
|
||||
proxy.width = function () {
|
||||
return Math.max(Math.abs(element.x - element.x2), 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the height, in grid space, of
|
||||
* this line's bounding box.
|
||||
* @returns {number} the height
|
||||
*/
|
||||
proxy.height = function () {
|
||||
return Math.max(Math.abs(element.y - element.y2), 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the x position, in grid units relative to
|
||||
* the top-left corner, of the first point in this line
|
||||
* segment.
|
||||
* @returns {number} the x position of the first point
|
||||
*/
|
||||
proxy.x1 = function () {
|
||||
return element.x - proxy.x();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the y position, in grid units relative to
|
||||
* the top-left corner, of the first point in this line
|
||||
* segment.
|
||||
* @returns {number} the y position of the first point
|
||||
*/
|
||||
proxy.y1 = function () {
|
||||
return element.y - proxy.y();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the x position, in grid units relative to
|
||||
* the top-left corner, of the second point in this line
|
||||
* segment.
|
||||
* @returns {number} the x position of the second point
|
||||
*/
|
||||
proxy.x2 = function () {
|
||||
return element.x2 - proxy.x();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the y position, in grid units relative to
|
||||
* the top-left corner, of the second point in this line
|
||||
* segment.
|
||||
* @returns {number} the y position of the second point
|
||||
*/
|
||||
proxy.y2 = function () {
|
||||
return element.y2 - proxy.y();
|
||||
};
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
return LineProxy;
|
||||
}
|
||||
);
|
@ -7,6 +7,8 @@ define(
|
||||
|
||||
describe("The Fixed Position controller", function () {
|
||||
var mockScope,
|
||||
mockQ,
|
||||
mockDialogService,
|
||||
mockSubscriber,
|
||||
mockFormatter,
|
||||
mockDomainObject,
|
||||
@ -58,6 +60,11 @@ define(
|
||||
'telemetrySubscriber',
|
||||
[ 'subscribe' ]
|
||||
);
|
||||
mockQ = jasmine.createSpyObj('$q', ['when']);
|
||||
mockDialogService = jasmine.createSpyObj(
|
||||
'dialogService',
|
||||
['getUserInput']
|
||||
);
|
||||
mockFormatter = jasmine.createSpyObj(
|
||||
'telemetryFormatter',
|
||||
[ 'formatDomainValue', 'formatRangeValue' ]
|
||||
@ -99,6 +106,8 @@ define(
|
||||
|
||||
controller = new FixedController(
|
||||
mockScope,
|
||||
mockQ,
|
||||
mockDialogService,
|
||||
mockSubscriber,
|
||||
mockFormatter
|
||||
);
|
||||
@ -263,8 +272,6 @@ define(
|
||||
.toHaveBeenCalledWith(jasmine.any(String));
|
||||
});
|
||||
|
||||
|
||||
|
||||
it("unsubscribes when destroyed", function () {
|
||||
// Make an object available
|
||||
findWatch('domainObject')(mockDomainObject);
|
||||
@ -275,6 +282,12 @@ define(
|
||||
// Should have unsubscribed
|
||||
expect(mockSubscription.unsubscribe).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("exposes its grid size", function () {
|
||||
// Template needs to be able to pass this into line
|
||||
// elements to size SVGs appropriately
|
||||
expect(controller.getGridSize()).toEqual(testGrid);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -6,13 +6,46 @@ define(
|
||||
"use strict";
|
||||
|
||||
describe("Fixed Position view's selection proxy", function () {
|
||||
it("has a placeholder message when clicked", function () {
|
||||
var oldAlert = window.alert;
|
||||
window.alert = jasmine.createSpy('alert');
|
||||
new FixedProxy({}).add('');
|
||||
expect(window.alert).toHaveBeenCalledWith(jasmine.any(String));
|
||||
window.alert = oldAlert;
|
||||
var mockCallback,
|
||||
mockQ,
|
||||
mockDialogService,
|
||||
mockPromise,
|
||||
proxy;
|
||||
|
||||
beforeEach(function () {
|
||||
mockCallback = jasmine.createSpy('callback');
|
||||
mockQ = jasmine.createSpyObj('$q', ['when']);
|
||||
mockDialogService = jasmine.createSpyObj('dialogService', ['getUserInput']);
|
||||
mockPromise = jasmine.createSpyObj('promise', ['then']);
|
||||
|
||||
mockQ.when.andReturn(mockPromise);
|
||||
|
||||
proxy = new FixedProxy(mockCallback, mockQ, mockDialogService);
|
||||
});
|
||||
|
||||
it("handles promised element creation", function () {
|
||||
// The element factory may return promises (e.g. if
|
||||
// user input is required) so make sure proxy is wrapping these
|
||||
proxy.add("fixed.box");
|
||||
expect(mockQ.when).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("notifies its callback when an element is created", function () {
|
||||
proxy.add("fixed.box");
|
||||
// Callback should not have been invoked yet
|
||||
expect(mockCallback).not.toHaveBeenCalled();
|
||||
// Resolve the promise
|
||||
mockPromise.then.mostRecentCall.args[0]({});
|
||||
// Should have fired the callback
|
||||
expect(mockCallback).toHaveBeenCalledWith({
|
||||
type: "fixed.box",
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 1,
|
||||
height: 1
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
|
50
platform/features/layout/test/elements/ElementFactorySpec.js
Normal file
50
platform/features/layout/test/elements/ElementFactorySpec.js
Normal file
@ -0,0 +1,50 @@
|
||||
/*global define,describe,it,expect,beforeEach,jasmine*/
|
||||
|
||||
define(
|
||||
['../../src/elements/ElementFactory'],
|
||||
function (ElementFactory) {
|
||||
"use strict";
|
||||
|
||||
var DIALOG_ELEMENTS = [ 'image', 'text' ],
|
||||
NON_DIALOG_ELEMENTS = [ 'box', 'line' ];
|
||||
|
||||
describe("The fixed position element factory", function () {
|
||||
var mockDialogService,
|
||||
mockPromise,
|
||||
factory;
|
||||
|
||||
beforeEach(function () {
|
||||
mockDialogService = jasmine.createSpyObj(
|
||||
'dialogService',
|
||||
[ 'getUserInput' ]
|
||||
);
|
||||
mockPromise = jasmine.createSpyObj(
|
||||
'promise',
|
||||
[ 'then' ]
|
||||
);
|
||||
|
||||
mockDialogService.getUserInput.andReturn(mockPromise);
|
||||
mockPromise.then.andReturn(mockPromise);
|
||||
|
||||
factory = new ElementFactory(mockDialogService);
|
||||
});
|
||||
|
||||
DIALOG_ELEMENTS.forEach(function (type) {
|
||||
it("shows a dialog for " + type + " elements", function () {
|
||||
expect(factory.createElement('fixed.' + type))
|
||||
.toEqual(mockPromise);
|
||||
expect(mockDialogService.getUserInput).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
NON_DIALOG_ELEMENTS.forEach(function (type) {
|
||||
it("immediately provides " + type + " elements", function () {
|
||||
var result = factory.createElement('fixed.' + type);
|
||||
expect(result).toBeDefined();
|
||||
expect(result).not.toEqual(mockPromise);
|
||||
expect(mockDialogService.getUserInput).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -5,8 +5,13 @@ define(
|
||||
function (ElementProxies) {
|
||||
"use strict";
|
||||
|
||||
// Expect these element types to have proxies
|
||||
var ELEMENT_TYPES = [
|
||||
"fixed.telemetry"
|
||||
"fixed.telemetry",
|
||||
"fixed.line",
|
||||
"fixed.box",
|
||||
"fixed.text",
|
||||
"fixed.image"
|
||||
];
|
||||
|
||||
// Verify that the set of proxies exposed matches the specific
|
||||
|
72
platform/features/layout/test/elements/LineProxySpec.js
Normal file
72
platform/features/layout/test/elements/LineProxySpec.js
Normal file
@ -0,0 +1,72 @@
|
||||
/*global define,describe,it,expect,beforeEach,jasmine*/
|
||||
|
||||
define(
|
||||
['../../src/elements/LineProxy'],
|
||||
function (LineProxy) {
|
||||
"use strict";
|
||||
|
||||
describe("A fixed position line proxy", function () {
|
||||
var vertical, horizontal, diagonal, reversed;
|
||||
|
||||
beforeEach(function () {
|
||||
vertical = { x: 1, y: 4, x2: 1, y2: 8 };
|
||||
horizontal = { x: 3, y: 3, x2: 12, y2: 3 };
|
||||
diagonal = { x: 3, y: 8, x2: 5, y2: 11 };
|
||||
reversed = { x2: 3, y2: 8, x: 5, y: 11 };
|
||||
});
|
||||
|
||||
it("ensures visible width for vertical lines", function () {
|
||||
expect(new LineProxy(vertical).width()).toEqual(1);
|
||||
});
|
||||
|
||||
it("ensures visible height for horizontal lines", function () {
|
||||
expect(new LineProxy(horizontal).height()).toEqual(1);
|
||||
});
|
||||
|
||||
it("provides a bounding box for lines", function () {
|
||||
var proxy = new LineProxy(diagonal);
|
||||
expect(proxy.x()).toEqual(3);
|
||||
expect(proxy.y()).toEqual(8);
|
||||
expect(proxy.width()).toEqual(2);
|
||||
expect(proxy.height()).toEqual(3);
|
||||
});
|
||||
|
||||
it("bounds lines identically regardless of point order", function () {
|
||||
// That is, x(), width(), y(), and height() should always give
|
||||
// the same results for the same line segments, regardless of
|
||||
// which point is x,y and which is x2,y2
|
||||
['x', 'y', 'width', 'height'].forEach(function (method) {
|
||||
expect(new LineProxy(diagonal)[method]())
|
||||
.toEqual(new LineProxy(reversed)[method]());
|
||||
});
|
||||
});
|
||||
|
||||
it("adjusts both ends when mutating x", function () {
|
||||
var proxy = new LineProxy(diagonal);
|
||||
proxy.x(6);
|
||||
expect(diagonal).toEqual({ x: 6, y: 8, x2: 8, y2: 11 });
|
||||
});
|
||||
|
||||
it("adjusts both ends when mutating y", function () {
|
||||
var proxy = new LineProxy(diagonal);
|
||||
proxy.y(6);
|
||||
expect(diagonal).toEqual({ x: 3, y: 6, x2: 5, y2: 9 });
|
||||
});
|
||||
|
||||
it("provides internal positions for SVG lines", function () {
|
||||
var proxy;
|
||||
proxy = new LineProxy(diagonal);
|
||||
expect(proxy.x1()).toEqual(0);
|
||||
expect(proxy.y1()).toEqual(0);
|
||||
expect(proxy.x2()).toEqual(2);
|
||||
expect(proxy.y2()).toEqual(3);
|
||||
proxy = new LineProxy(reversed);
|
||||
expect(proxy.x1()).toEqual(2);
|
||||
expect(proxy.y1()).toEqual(3);
|
||||
expect(proxy.x2()).toEqual(0);
|
||||
expect(proxy.y2()).toEqual(0);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -5,7 +5,9 @@
|
||||
"LayoutDrag",
|
||||
"LayoutSelection",
|
||||
"elements/AccessorMutator",
|
||||
"elements/ElementFactory",
|
||||
"elements/ElementProxies",
|
||||
"elements/ElementProxy",
|
||||
"elements/LineProxy",
|
||||
"elements/TelemetryProxy"
|
||||
]
|
@ -45,6 +45,10 @@
|
||||
{
|
||||
"key": "composite",
|
||||
"templateUrl": "templates/controls/composite.html"
|
||||
},
|
||||
{
|
||||
"key": "menu-button",
|
||||
"templateUrl": "templates/controls/menu-button.html"
|
||||
}
|
||||
],
|
||||
"controllers": [
|
||||
|
27
platform/forms/res/templates/controls/menu-button.html
Normal file
27
platform/forms/res/templates/controls/menu-button.html
Normal file
@ -0,0 +1,27 @@
|
||||
<div class="s-btn s-icon-btn s-very-subtle btn-menu menu-element dropdown click-invoke"
|
||||
ng-controller="ClickAwayController as toggle">
|
||||
|
||||
<span ng-click="toggle.toggle()">
|
||||
<span class="ui-symbol icon">{{structure.glyph}}</span>
|
||||
<span class="title-label" ng-if="structure.text">
|
||||
{{structure.text}}
|
||||
</span>
|
||||
<span class='ui-symbol icon invoke-menu'
|
||||
ng-if="!structure.text">
|
||||
v
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<div class="menu dropdown" ng-show="toggle.isActive()">
|
||||
<ul>
|
||||
<li ng-repeat="option in structure.options">
|
||||
<a href="" ng-click="structure.click(option.key); toggle.setState(false)">
|
||||
<span class="ui-symbol type-icon icon">
|
||||
{{option.glyph}}
|
||||
</span>
|
||||
{{option.name}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
Loading…
x
Reference in New Issue
Block a user