Review and integrate mobile-2008 (#2040)

Fixes #2008
* Viewport metatag updated
* Fix to Time Conductor for Safari
* Remove data visualization element in TC
* Hide TC ticks when on mobile
* Add touch functionality to mct-drag
* Reduced size of image thumbnails; Changed min-heights of image and thumbnail
holders for .mobile.phone
* remove create dialog from mobile
This commit is contained in:
Charles Hacskaylo 2018-05-25 11:19:27 -07:00 committed by Andrew Henry
parent e19ce4ac8c
commit c909831dd4
16 changed files with 305 additions and 116 deletions

View File

@ -21,68 +21,69 @@
--> -->
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, shrink-to-fit=no">
<title></title> <meta name="apple-mobile-web-app-capable" content="yes">
<script src="bower_components/requirejs/require.js"> </script> <title></title>
<script> <script src="bower_components/requirejs/require.js"> </script>
var THIRTY_MINUTES = 30 * 60 * 1000; <script>
var THIRTY_MINUTES = 30 * 60 * 1000;
require(['openmct'], function (openmct) { require(['openmct'], function (openmct) {
[ [
'example/eventGenerator', 'example/eventGenerator',
'example/styleguide' 'example/styleguide'
].forEach( ].forEach(
openmct.legacyRegistry.enable.bind(openmct.legacyRegistry) openmct.legacyRegistry.enable.bind(openmct.legacyRegistry)
); );
openmct.install(openmct.plugins.MyItems()); openmct.install(openmct.plugins.MyItems());
openmct.install(openmct.plugins.LocalStorage()); openmct.install(openmct.plugins.LocalStorage());
openmct.install(openmct.plugins.Espresso()); openmct.install(openmct.plugins.Espresso());
openmct.install(openmct.plugins.Generator()); openmct.install(openmct.plugins.Generator());
openmct.install(openmct.plugins.ExampleImagery()); openmct.install(openmct.plugins.ExampleImagery());
openmct.install(openmct.plugins.UTCTimeSystem()); openmct.install(openmct.plugins.UTCTimeSystem());
openmct.install(openmct.plugins.ImportExport()); openmct.install(openmct.plugins.ImportExport());
openmct.install(openmct.plugins.AutoflowView({ openmct.install(openmct.plugins.AutoflowView({
type: "telemetry.panel" type: "telemetry.panel"
})); }));
openmct.install(openmct.plugins.Conductor({ openmct.install(openmct.plugins.Conductor({
menuOptions: [ menuOptions: [
{ {
name: "Fixed", name: "Fixed",
timeSystem: 'utc', timeSystem: 'utc',
bounds: { bounds: {
start: Date.now() - THIRTY_MINUTES, start: Date.now() - THIRTY_MINUTES,
end: Date.now() end: Date.now()
}
},
{
name: "Realtime",
timeSystem: 'utc',
clock: 'local',
clockOffsets: {
start: -25 * 60 * 1000,
end: 5 * 60 * 1000
}
} }
}, ]
{ }));
name: "Realtime", openmct.install(openmct.plugins.SummaryWidget());
timeSystem: 'utc', openmct.time.clock('local', {start: -THIRTY_MINUTES, end: 0});
clock: 'local', openmct.time.timeSystem('utc');
clockOffsets: { openmct.start();
start: -25 * 60 * 1000, });
end: 5 * 60 * 1000 </script>
} <link rel="stylesheet" href="platform/commonUI/general/res/css/startup-base.css">
} <link rel="stylesheet" href="platform/commonUI/general/res/css/openmct.css">
] <link rel="icon" type="image/png" href="platform/commonUI/general/res/images/favicons/favicon-32x32.png" sizes="32x32">
})); <link rel="icon" type="image/png" href="platform/commonUI/general/res/images/favicons/favicon-96x96.png" sizes="96x96">
openmct.install(openmct.plugins.SummaryWidget()); <link rel="icon" type="image/png" href="platform/commonUI/general/res/images/favicons/favicon-16x16.png" sizes="16x16">
openmct.time.clock('local', {start: -THIRTY_MINUTES, end: 0}); <link rel="shortcut icon" href="platform/commonUI/general/res/images/favicons/favicon.ico">
openmct.time.timeSystem('utc'); </head>
openmct.start(); <body>
}); <div class="l-splash-holder s-splash-holder">
</script> <div class="l-splash s-splash"></div>
<link rel="stylesheet" href="platform/commonUI/general/res/css/startup-base.css"> </div>
<link rel="stylesheet" href="platform/commonUI/general/res/css/openmct.css"> </body>
<link rel="icon" type="image/png" href="platform/commonUI/general/res/images/favicons/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="platform/commonUI/general/res/images/favicons/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="platform/commonUI/general/res/images/favicons/favicon-16x16.png" sizes="16x16">
<link rel="shortcut icon" href="platform/commonUI/general/res/images/favicons/favicon.ico">
</head>
<body class="user-environ">
<div class="l-splash-holder s-splash-holder">
<div class="l-splash s-splash"></div>
</div>
</body>
</html> </html>

View File

@ -113,7 +113,8 @@ define([
"agentService", "agentService",
"$window", "$window",
"$location", "$location",
"$attrs" "$attrs",
"navigationService"
] ]
}, },
{ {

View File

@ -24,14 +24,14 @@
define( define(
[], [],
function () { function () {
var navigationListenerAdded = false;
/** /**
* Controller to provide the ability to show/hide the tree in * Controller to provide the ability to show/hide the tree in
* Browse mode. * Browse mode.
* @constructor * @constructor
* @memberof platform/commonUI/browse * @memberof platform/commonUI/browse
*/ */
function PaneController($scope, agentService, $window, $location, $attrs) { function PaneController($scope, agentService, $window, $location, $attrs, navigationService) {
var self = this; var self = this;
this.agentService = agentService; this.agentService = agentService;
var hideParameterPresent = $location.search().hasOwnProperty($attrs.hideParameter); var hideParameterPresent = $location.search().hasOwnProperty($attrs.hideParameter);
@ -61,6 +61,11 @@ define(
self.state = false; self.state = false;
} }
}; };
if (navigationService && navigationService.addListener && !navigationListenerAdded) {
navigationService.addListener(this.callback);
navigationListenerAdded = true;
}
} }
/** /**

View File

@ -327,7 +327,8 @@ define([
"key": "mctDrag", "key": "mctDrag",
"implementation": MCTDrag, "implementation": MCTDrag,
"depends": [ "depends": [
"$document" "$document",
"agentService"
] ]
}, },
{ {

View File

@ -81,7 +81,7 @@ $tabularTdPadLR: $itemPadLR;
$tabularTdPadTB: 2px; $tabularTdPadTB: 2px;
/*************** Imagery */ /*************** Imagery */
$imageMainControlBarH: 25px; $imageMainControlBarH: 25px;
$imageThumbsD: 120px; $imageThumbsD: 100px;
$imageThumbsWrapperH: 155px; $imageThumbsWrapperH: 155px;
$imageThumbPad: 1px; $imageThumbPad: 1px;
/*************** Ticks */ /*************** Ticks */

View File

@ -205,3 +205,14 @@
display: none; display: none;
} }
} }
/*************************************** MOBILE */
body.mobile.phone {
.t-imagery {
.l-image-main-wrapper,
.l-image-thumbs-wrapper {
//background: red;
min-height: 10px !important;
}
}
}

View File

@ -26,8 +26,8 @@ body.mobile {
} }
.pane.right.items { .pane.right.items {
//@include test();
@include slMenuTransitions; @include slMenuTransitions;
left: 0 !important;
margin-left: 0 !important; margin-left: 0 !important;
.holder-object-and-inspector { .holder-object-and-inspector {
@include slMenuTransitions; @include slMenuTransitions;
@ -47,12 +47,10 @@ body.mobile {
right: $bodyMargin !important; right: $bodyMargin !important;
} }
// When the tree is hidden, these are the // Assume landscape layout or either orientation for tablet
// classes used for the left menu and the // Phone portrait overrides below via media query
// right representation.
.pane-tree-hidden { .pane-tree-hidden {
// Sets the left tree menu when the tree // Sets the left tree menu when the tree is hidden.
// is hidden.
.pane.left.treeview { .pane.left.treeview {
@include trans-prop-nice(opacity, 150ms); @include trans-prop-nice(opacity, 150ms);
opacity: 0 !important; opacity: 0 !important;
@ -73,12 +71,12 @@ body.mobile {
.pane.left.treeview { .pane.left.treeview {
@include trans-prop-nice(opacity, 250ms, $delay: 250ms); @include trans-prop-nice(opacity, 250ms, $delay: 250ms);
@include background-image(linear-gradient(90deg, rgba(black, 0) 98%, rgba(black, 0.3) 100%)); @include background-image(linear-gradient(90deg, rgba(black, 0) 98%, rgba(black, 0.3) 100%));
right: auto !important;
width: $proporMenuWithView !important; width: $proporMenuWithView !important;
right: auto !important;
} }
// Sets the right representation when the tree is shown. // Sets the right representation when the tree is shown.
.pane.right.items { .pane.right.items {
left: $proporMenuWithView !important; transform: translateX($proporMenuWithView);
} }
} }
@ -92,12 +90,19 @@ body.mobile {
} }
.object-browse-bar { .object-browse-bar {
&.t-primary { margin-left: 45px; } &.t-primary {
margin-left: 45px;
.title-label {
// Prevent inline editing of the object title in the main view in mobile
pointer-events: none;
}
}
.context-available { .context-available {
font-size: 0.9em;
opacity: 1 !important; opacity: 1 !important;
} }
.view-switcher { .view-switcher {
margin-right: 0 !important;
.title-label { .title-label {
// Hide the name in mobile // Hide the name in mobile
display: none; display: none;
@ -131,16 +136,17 @@ body.mobile {
} }
} }
body.phone.portrait { @media only screen and (max-device-width: $phoMaxW) and (orientation: portrait) {
.pane-tree-showing { body.mobile {
.pane.left.treeview { .pane-tree-showing {
width: $proporMenuOnly !important; .pane.left.treeview {
} width: $proporMenuOnly !important;
.pane.right.items { }
left: 0 !important; .pane.right.items {
transform: translateX($proporMenuOnly); transform: translateX($proporMenuOnly);
.holder-object-and-inspector { .holder-object-and-inspector {
opacity: 0; opacity: 0;
}
} }
} }
} }

View File

@ -28,7 +28,8 @@ $output-bourbon-deprecation-warnings: false;
} }
body, html { body, html {
overflow: hidden; height: 100%;
width: 100%;
} }
.l-splash-holder { .l-splash-holder {

View File

@ -20,6 +20,7 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
.user-environ,
.browse-area, .browse-area,
.edit-area, .edit-area,
.editor { .editor {
@ -47,6 +48,9 @@
} }
.user-environ { .user-environ {
height: 100%;
width: 100%;
overflow: hidden !important;
.browse-area, .browse-area,
.editor { .editor {
top: 0; left: 0; right: 0; bottom: $ueFooterH; top: 0; left: 0; right: 0; bottom: $ueFooterH;

View File

@ -46,7 +46,7 @@ define(
* @constructor * @constructor
* *
*/ */
function MCTDrag($document) { function MCTDrag($document, agentService) {
// Link; install event handlers. // Link; install event handlers.
function link(scope, element, attrs) { function link(scope, element, attrs) {
@ -55,10 +55,26 @@ define(
// only be attached to the element being linked, as the // only be attached to the element being linked, as the
// mouse may leave this element during the drag. // mouse may leave this element during the drag.
var body = $document.find('body'), var body = $document.find('body'),
isMobile = agentService.isMobile(),
touchEvents,
initialPosition, initialPosition,
$event, $event,
delta; delta;
if (isMobile) {
touchEvents = {
start: 'touchstart',
end: 'touchend',
move: 'touchmove'
};
} else {
touchEvents = {
start: 'mousedown',
end: "mouseup",
move: "mousemove"
};
}
// Utility function to cause evaluation of mctDrag, // Utility function to cause evaluation of mctDrag,
// mctDragUp, etc // mctDragUp, etc
function fireListener(name) { function fireListener(name) {
@ -103,8 +119,8 @@ define(
// Called only when the drag ends (on mouseup) // Called only when the drag ends (on mouseup)
function endDrag(event) { function endDrag(event) {
// Detach event handlers // Detach event handlers
body.off("mouseup", endDrag); body.off(touchEvents.end, endDrag);
body.off("mousemove", continueDrag); body.off(touchEvents.move, continueDrag);
// Also call continueDrag, to fire mctDrag // Also call continueDrag, to fire mctDrag
// and do its usual position update // and do its usual position update
@ -125,8 +141,8 @@ define(
// Listen for mouse events at the body level, // Listen for mouse events at the body level,
// since the mouse may leave the element during // since the mouse may leave the element during
// the drag. // the drag.
body.on("mouseup", endDrag); body.on(touchEvents.end, endDrag);
body.on("mousemove", continueDrag); body.on(touchEvents.move, continueDrag);
// Set an initial position // Set an initial position
updatePosition(event); updatePosition(event);
@ -141,8 +157,8 @@ define(
return false; return false;
} }
// Listen for mousedown on the element // Listen for start event on the element
element.on("mousedown", startDrag); element.on(touchEvents.start, startDrag);
} }
return { return {

View File

@ -26,8 +26,9 @@ define(
var JQLITE_METHODS = ["on", "off", "find"]; var JQLITE_METHODS = ["on", "off", "find"];
describe("The mct-drag directive", function () { describe("The mct-drag directive in Mobile", function () {
var mockDocument, var mockDocument,
mockAgentService,
mockScope, mockScope,
mockElement, mockElement,
testAttrs, testAttrs,
@ -45,6 +46,8 @@ define(
beforeEach(function () { beforeEach(function () {
mockDocument = mockDocument =
jasmine.createSpyObj("$document", JQLITE_METHODS); jasmine.createSpyObj("$document", JQLITE_METHODS);
mockAgentService =
jasmine.createSpyObj("agentService", ["isMobile"]);
mockScope = mockScope =
jasmine.createSpyObj("$scope", ["$eval", "$apply"]); jasmine.createSpyObj("$scope", ["$eval", "$apply"]);
mockElement = mockElement =
@ -59,8 +62,137 @@ define(
}; };
mockDocument.find.andReturn(mockBody); mockDocument.find.andReturn(mockBody);
mockAgentService.isMobile.andReturn(true);
mctDrag = new MCTDrag(mockDocument); mctDrag = new MCTDrag(mockDocument, mockAgentService);
mctDrag.link(mockScope, mockElement, testAttrs);
});
it("is valid as an attribute", function () {
expect(mctDrag.restrict).toEqual("A");
});
it("listens for touchstart on its element", function () {
expect(mockElement.on).toHaveBeenCalledWith(
"touchstart",
jasmine.any(Function)
);
// Verify no interactions with body as well
expect(mockBody.on).not.toHaveBeenCalled();
});
it("invokes mctDragDown when dragging begins", function () {
var event = testEvent(42, 60);
mockElement.on.mostRecentCall.args[1](event);
expect(mockScope.$eval).toHaveBeenCalledWith(
testAttrs.mctDragDown,
{ delta: [0, 0], $event: event }
);
});
it("listens for touchmove after dragging begins", function () {
mockElement.on.mostRecentCall.args[1](testEvent(42, 60));
expect(mockBody.on).toHaveBeenCalledWith(
"touchmove",
jasmine.any(Function)
);
expect(mockBody.on).toHaveBeenCalledWith(
"touchend",
jasmine.any(Function)
);
});
it("invokes mctDrag expression during drag", function () {
var event;
mockElement.on.mostRecentCall.args[1](testEvent(42, 60));
// Find and invoke the touchmove listener
mockBody.on.calls.forEach(function (call) {
if (call.args[0] === 'touchmove') {
call.args[1](event = testEvent(52, 200));
}
});
// Should have passed that delta to mct-drag expression
expect(mockScope.$eval).toHaveBeenCalledWith(
testAttrs.mctDrag,
{ delta: [10, 140], $event: event }
);
});
it("invokes mctDragUp expression after drag", function () {
var event;
mockElement.on.mostRecentCall.args[1](testEvent(42, 60));
// Find and invoke the touchmove listener
mockBody.on.calls.forEach(function (call) {
if (call.args[0] === 'touchmove') {
call.args[1](testEvent(52, 200));
}
});
// Find and invoke the touchmove listener
mockBody.on.calls.forEach(function (call) {
if (call.args[0] === 'touchend') {
call.args[1](event = testEvent(40, 71));
}
});
// Should have passed that delta to mct-drag-up expression
// and that delta should have been relative to the
// initial position
expect(mockScope.$eval).toHaveBeenCalledWith(
testAttrs.mctDragUp,
{ delta: [-2, 11], $event: event }
);
// Should also have unregistered listeners
expect(mockBody.off).toHaveBeenCalled();
});
});
describe("The mct-drag directive in Desktop", function () {
var mockDocument,
mockAgentService,
mockScope,
mockElement,
testAttrs,
mockBody,
mctDrag;
function testEvent(x, y) {
return {
pageX: x,
pageY: y,
preventDefault: jasmine.createSpy("preventDefault")
};
}
beforeEach(function () {
mockDocument =
jasmine.createSpyObj("$document", JQLITE_METHODS);
mockAgentService =
jasmine.createSpyObj("agentService", ["isMobile"]);
mockScope =
jasmine.createSpyObj("$scope", ["$eval", "$apply"]);
mockElement =
jasmine.createSpyObj("element", JQLITE_METHODS);
mockBody =
jasmine.createSpyObj("body", JQLITE_METHODS);
testAttrs = {
mctDragDown: "starting a drag",
mctDrag: "continuing a drag",
mctDragUp: "ending a drag"
};
mockDocument.find.andReturn(mockBody);
mockAgentService.isMobile.andReturn(false);
mctDrag = new MCTDrag(mockDocument, mockAgentService);
mctDrag.link(mockScope, mockElement, testAttrs); mctDrag.link(mockScope, mockElement, testAttrs);
}); });

View File

@ -44,11 +44,26 @@ define(
*/ */
function MobileClassifier(agentService, $document) { function MobileClassifier(agentService, $document) {
var body = $document.find('body'); var body = $document.find('body');
Object.keys(DeviceMatchers).forEach(function (key) {
Object.keys(DeviceMatchers).forEach(function (key, index, array) {
if (DeviceMatchers[key](agentService)) { if (DeviceMatchers[key](agentService)) {
body.addClass(key); body.addClass(key);
} }
}); });
if (agentService.isMobile()) {
var mediaQuery = window.matchMedia('(orientation: landscape)');
mediaQuery.addListener(function (event) {
if (event.matches) {
body.removeClass('portrait');
body.addClass('landscape');
} else {
body.removeClass('landscape');
body.addClass('portrait');
}
});
}
} }
return MobileClassifier; return MobileClassifier;

View File

@ -145,14 +145,14 @@
margin-right: $interiorMarginSm; margin-right: $interiorMarginSm;
} }
&.start-w { &.start-w {
@include background-image(linear-gradient(270deg, transparent, $wBgColor $ticksBlockerFadeW)); @include background-image(linear-gradient(270deg, rgba($wBgColor,0), rgba($wBgColor,1) $ticksBlockerFadeW));
padding-right: $ticksBlockerFadeW; padding-right: $ticksBlockerFadeW;
.title:before { .title:before {
content: 'Start'; content: 'Start';
} }
} }
&.end-w { &.end-w {
@include background-image(linear-gradient(90deg, transparent, $wBgColor $ticksBlockerFadeW)); @include background-image(linear-gradient(90deg, rgba($wBgColor,0), rgba($wBgColor,1) $ticksBlockerFadeW));
padding-left: $ticksBlockerFadeW; padding-left: $ticksBlockerFadeW;
right: 0; right: 0;
.title:before { .title:before {

View File

@ -79,12 +79,9 @@
<input type="submit" class="off"> <input type="submit" class="off">
</form> </form>
<conductor-axis view-service="tcController.conductorViewService"></conductor-axis> <conductor-axis class="mobile-hide" view-service="tcController.conductorViewService"></conductor-axis>
</div> </div>
<!-- Holds data visualization, time of interest -->
<conductor-toi view-service="tcController.conductorViewService"></conductor-toi>
<!-- Holds time system and session selectors, and zoom control --> <!-- Holds time system and session selectors, and zoom control -->
<div class="l-time-conductor-controls l-row-elem l-flex-row flex-elem"> <div class="l-time-conductor-controls l-row-elem l-flex-row flex-elem">
<mct-include <mct-include

View File

@ -183,7 +183,7 @@
font-size: 0.9em; font-size: 0.9em;
&.tick-label-y { &.tick-label-y {
text-align: left; text-align: left;
} }
} }
} }
} }
@ -199,12 +199,6 @@
&.l-pane-r { &.l-pane-r {
left: 0; left: 0;
&:hover {
.l-hover-btns-holder {
@include trans-prop-nice-fade(100ms);
opacity: 1;
}
}
} }
&.l-pane-top { &.l-pane-top {
@ -246,8 +240,6 @@
.l-hover-btns-holder { .l-hover-btns-holder {
@include absPosDefault(); @include absPosDefault();
box-sizing: border-box; box-sizing: border-box;
@include trans-prop-nice-fade(500ms);
opacity: 0;
height: $timelineTopPaneHeaderH; height: $timelineTopPaneHeaderH;
left: auto; left: auto;
padding: $interiorMargin $interiorMargin $interiorMargin $interiorMargin * 10; padding: $interiorMargin $interiorMargin $interiorMargin $interiorMargin * 10;
@ -338,4 +330,4 @@
&:hover { &:hover {
background-color: $colorItemTreeHoverBg; background-color: $colorItemTreeHoverBg;
} }
} }

View File

@ -41,6 +41,7 @@ define(
*/ */
function ContextMenuGesture($timeout, agentService, element, domainObject) { function ContextMenuGesture($timeout, agentService, element, domainObject) {
var isPressing, var isPressing,
isDragging,
longTouchTime = 500; longTouchTime = 500;
function showMenu(event) { function showMenu(event) {
@ -67,16 +68,22 @@ define(
// After the timeout, if 'isPressing' is // After the timeout, if 'isPressing' is
// true, display context menu for object // true, display context menu for object
$timeout(function () { $timeout(function () {
if (isPressing) { if (isPressing && !isDragging) {
showMenu(event); showMenu(event);
} }
}, longTouchTime); }, longTouchTime);
} }
}); });
// Whenever the touch event ends, 'isPressing' is false. // If on Mobile Device, and user scrolls/drags set flag to true
element.on('touchmove', function () {
isDragging = true;
});
// Whenever the touch event ends, 'isPressing' & 'isDragging' is false.
element.on('touchend', function () { element.on('touchend', function () {
isPressing = false; isPressing = false;
isDragging = false;
}); });
} }