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>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title></title>
<script src="bower_components/requirejs/require.js"> </script>
<script>
var THIRTY_MINUTES = 30 * 60 * 1000;
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, shrink-to-fit=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<title></title>
<script src="bower_components/requirejs/require.js"> </script>
<script>
var THIRTY_MINUTES = 30 * 60 * 1000;
require(['openmct'], function (openmct) {
[
'example/eventGenerator',
'example/styleguide'
].forEach(
openmct.legacyRegistry.enable.bind(openmct.legacyRegistry)
);
openmct.install(openmct.plugins.MyItems());
openmct.install(openmct.plugins.LocalStorage());
openmct.install(openmct.plugins.Espresso());
openmct.install(openmct.plugins.Generator());
openmct.install(openmct.plugins.ExampleImagery());
openmct.install(openmct.plugins.UTCTimeSystem());
openmct.install(openmct.plugins.ImportExport());
openmct.install(openmct.plugins.AutoflowView({
type: "telemetry.panel"
}));
openmct.install(openmct.plugins.Conductor({
menuOptions: [
{
name: "Fixed",
timeSystem: 'utc',
bounds: {
start: Date.now() - THIRTY_MINUTES,
end: Date.now()
require(['openmct'], function (openmct) {
[
'example/eventGenerator',
'example/styleguide'
].forEach(
openmct.legacyRegistry.enable.bind(openmct.legacyRegistry)
);
openmct.install(openmct.plugins.MyItems());
openmct.install(openmct.plugins.LocalStorage());
openmct.install(openmct.plugins.Espresso());
openmct.install(openmct.plugins.Generator());
openmct.install(openmct.plugins.ExampleImagery());
openmct.install(openmct.plugins.UTCTimeSystem());
openmct.install(openmct.plugins.ImportExport());
openmct.install(openmct.plugins.AutoflowView({
type: "telemetry.panel"
}));
openmct.install(openmct.plugins.Conductor({
menuOptions: [
{
name: "Fixed",
timeSystem: 'utc',
bounds: {
start: Date.now() - THIRTY_MINUTES,
end: Date.now()
}
},
{
name: "Realtime",
timeSystem: 'utc',
clock: 'local',
clockOffsets: {
start: -25 * 60 * 1000,
end: 5 * 60 * 1000
}
}
},
{
name: "Realtime",
timeSystem: 'utc',
clock: 'local',
clockOffsets: {
start: -25 * 60 * 1000,
end: 5 * 60 * 1000
}
}
]
}));
openmct.install(openmct.plugins.SummaryWidget());
openmct.time.clock('local', {start: -THIRTY_MINUTES, end: 0});
openmct.time.timeSystem('utc');
openmct.start();
});
</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">
<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>
]
}));
openmct.install(openmct.plugins.SummaryWidget());
openmct.time.clock('local', {start: -THIRTY_MINUTES, end: 0});
openmct.time.timeSystem('utc');
openmct.start();
});
</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">
<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>
<div class="l-splash-holder s-splash-holder">
<div class="l-splash s-splash"></div>
</div>
</body>
</html>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -26,8 +26,9 @@ define(
var JQLITE_METHODS = ["on", "off", "find"];
describe("The mct-drag directive", function () {
describe("The mct-drag directive in Mobile", function () {
var mockDocument,
mockAgentService,
mockScope,
mockElement,
testAttrs,
@ -45,6 +46,8 @@ define(
beforeEach(function () {
mockDocument =
jasmine.createSpyObj("$document", JQLITE_METHODS);
mockAgentService =
jasmine.createSpyObj("agentService", ["isMobile"]);
mockScope =
jasmine.createSpyObj("$scope", ["$eval", "$apply"]);
mockElement =
@ -59,8 +62,137 @@ define(
};
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);
});

View File

@ -44,11 +44,26 @@ define(
*/
function MobileClassifier(agentService, $document) {
var body = $document.find('body');
Object.keys(DeviceMatchers).forEach(function (key) {
Object.keys(DeviceMatchers).forEach(function (key, index, array) {
if (DeviceMatchers[key](agentService)) {
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;

View File

@ -145,14 +145,14 @@
margin-right: $interiorMarginSm;
}
&.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;
.title:before {
content: 'Start';
}
}
&.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;
right: 0;
.title:before {

View File

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

View File

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

View File

@ -41,6 +41,7 @@ define(
*/
function ContextMenuGesture($timeout, agentService, element, domainObject) {
var isPressing,
isDragging,
longTouchTime = 500;
function showMenu(event) {
@ -67,16 +68,22 @@ define(
// After the timeout, if 'isPressing' is
// true, display context menu for object
$timeout(function () {
if (isPressing) {
if (isPressing && !isDragging) {
showMenu(event);
}
}, 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 () {
isPressing = false;
isDragging = false;
});
}