* first cut, working for objects with explicit height

* working for all objects by setting min-height explicitly to 70vh

* add composition listeners

* Tabs view WIP

- Markup and CSS BEMized;
- Stubbed in type icon in tab elements;
- TODO: refine styling on tabs;

* Tabs view WIP

- Layout enhancements;

* add types and header

* remove static icon-layout class

* move Tabs into its own plugin folder:

* simplify on composition add listener callback

* fix icon rendering

* add document dragstart and dragend listeners, with v-if div for drop target

* remove third argument from document listeners, move drop target to tab container

* Sanding and shimming on Tabs View

- WIP

* Tabs View styling

- Shippable to Deep;
- Added new c-drop-hint element in _controls.scss;
- Added new theme constants;
- Added 'empty' tabs view message;
- TODOs: add listener for dragover event for is-mouse-over styling,
integrate forthcoming changes to symbolsfont;

* add is-mouse-over class when drag enters dropHint div, add and remove listeners

* .c-drop-hint styling refined

- Refined hover effects;
- Bg icon instead of glyph character;
- TODOs: Change $bg-icon to plus sign;

* fix bug regarding persisting drop-hint styling even after drop is ended
This commit is contained in:
Deep Tailor 2018-10-26 14:14:00 -07:00 committed by Pete Richards
parent 7c54ec4f9f
commit 35d1b894e2
10 changed files with 423 additions and 12 deletions

View File

@ -77,6 +77,7 @@
openmct.install(openmct.plugins.SummaryWidget()); openmct.install(openmct.plugins.SummaryWidget());
openmct.install(openmct.plugins.Notebook()); openmct.install(openmct.plugins.Notebook());
openmct.install(openmct.plugins.FolderView()); openmct.install(openmct.plugins.FolderView());
openmct.install(openmct.plugins.Tabs());
openmct.time.clock('local', {start: -THIRTY_MINUTES, end: 0}); openmct.time.clock('local', {start: -THIRTY_MINUTES, end: 0});
openmct.time.timeSystem('utc'); openmct.time.timeSystem('utc');
openmct.start(); openmct.start();

View File

@ -37,6 +37,7 @@ define([
'./notebook/plugin', './notebook/plugin',
'./displayLayout/plugin', './displayLayout/plugin',
'./folderView/plugin', './folderView/plugin',
'./tabs/plugin',
'../../platform/features/fixed/plugin' '../../platform/features/fixed/plugin'
], function ( ], function (
_, _,
@ -55,6 +56,7 @@ define([
Notebook, Notebook,
DisplayLayoutPlugin, DisplayLayoutPlugin,
FolderView, FolderView,
Tabs,
FixedView FixedView
) { ) {
var bundleMap = { var bundleMap = {
@ -167,6 +169,7 @@ define([
plugins.Notebook = Notebook; plugins.Notebook = Notebook;
plugins.DisplayLayout = DisplayLayoutPlugin.default; plugins.DisplayLayout = DisplayLayoutPlugin.default;
plugins.FolderView = FolderView; plugins.FolderView = FolderView;
plugins.Tabs = Tabs;
plugins.FixedView = FixedView; plugins.FixedView = FixedView;
return plugins; return plugins;

View File

@ -0,0 +1,177 @@
<template>
<div class="c-tabs-view">
<div class="c-tabs-view__tabs-holder c-compact-button-holder"
:class="{
'is-dragging': isDragging,
'is-mouse-over': allowDrop
}">
<div class="c-drop-hint"
@drop="onDrop"
ref="dropHint">
</div>
<div class="c-tabs-view__empty-message"
v-if="!tabsList.length > 0">Drag objects here to add them to this view.</div>
<button class="c-tabs-view__tab c-compact-button"
v-for="(tab,index) in tabsList"
:key="index"
:class="[
{'is-current': tab=== currentTab},
tab.type.definition.cssClass
]"
@click="showTab(tab)">
<span class="c-button__label">{{tab.model.name}}</span>
</button>
</div>
<div class="c-tabs-view__object-holder"
v-for="(object, index) in tabsList"
:key="index"
:class="{'invisible': object !== currentTab}">
<div class="c-tabs-view__object-name l-browse-bar__object-name--w"
:class="currentTab.type.definition.cssClass">
<div class="l-browse-bar__object-name">
{{currentTab.model.name}}
</div>
</div>
<object-view class="c-tabs-view__object"
:object="object.model">
</object-view>
</div>
</div>
</template>
<style lang="scss">
@import '~styles/sass-base.scss';
.c-tabs-view {
$h: 20px;
@include abs();
display: flex;
flex-flow: column nowrap;
> * + * {
margin-top: $interiorMargin;
}
&__tabs-holder {
@include userSelectNone();
flex: 0 0 auto;
min-height: $h;
}
&__object-holder {
flex: 1 1 auto;
display: flex;
flex-direction: column;
}
&__object-name {
flex: 0 0 auto;
font-size: 1.2em !important;
margin: $interiorMargin 0 $interiorMarginLg 0;
}
&__object {
flex: 1 1 auto;
}
&__empty-message {
color: rgba($colorBodyFg, 0.7);
font-style: italic;
text-align: center;
line-height: $h;
width: 100%;
}
}
</style>
<script>
import ObjectView from '../../../ui/components/layout/ObjectView.vue';
var unknownObjectType = {
definition: {
cssClass: 'icon-object-unknown',
name: 'Unknown Type'
}
};
export default {
inject: ['openmct','domainObject', 'composition'],
components: {
ObjectView
},
mounted () {
if (this.composition) {
this.composition.on('add', this.addItem, this);
this.composition.load();
}
document.addEventListener('dragstart', this.dragstart);
document.addEventListener('dragend', this.dragend);
let dropHint = this.$refs.dropHint;
if (dropHint) {
dropHint.addEventListener('dragenter', this.dragenter);
dropHint.addEventListener('dragleave', this.dragleave);
}
},
data: function () {
return {
currentTab: {},
tabsList: [],
setCurrentTab: true,
isDragging: false,
allowDrop: false
};
},
methods:{
showTab (tab) {
this.currentTab = tab;
},
addItem (model) {
let type = this.openmct.types.get(model.type) || unknownObjectType,
tabItem = {
model,
type: type
};
this.tabsList.push(tabItem);
if (this.setCurrentTab) {
this.currentTab = tabItem;
this.setCurrentTab = false;
}
},
onDrop (e) {
this.setCurrentTab = true;
},
dragstart (e) {
if (e.dataTransfer.getData('domainObject')) {
this.isDragging = true;
}
},
dragend (e) {
this.isDragging = false;
this.allowDrop = false;
},
dragenter () {
this.allowDrop = true;
},
dragleave() {
this.allowDrop = false;
}
},
destroyed() {
this.composition.off('add', this.addItem, this);
document.removeEventListener('dragstart', this.dragstart);
document.removeEventListener('dragend', this.dragend);
},
beforeDestroy() {
let dropHint = this.$refs.dropHint;
dropHint.removeEventListener('dragenter', this.dragenter);
dropHint.removeEventListener('dragleave', this.dragleave);
}
}
</script>

View File

@ -0,0 +1,42 @@
/*****************************************************************************
* 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([
'./tabs'
], function (
Tabs
) {
return function plugin() {
return function install(openmct) {
openmct.objectViews.addProvider(new Tabs(openmct));
openmct.types.addType('tabs', {
name: "Tabs View",
creatable: true,
cssClass: 'icon-tabs-view',
initialize(domainObject) {
domainObject.composition = [];
}
});
};
};
});

68
src/plugins/tabs/tabs.js Normal file
View File

@ -0,0 +1,68 @@
/*****************************************************************************
* 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([
'./components/tabs.vue',
'vue'
], function (
TabsComponent,
Vue
) {
function Tabs(openmct) {
return {
key: 'tabs',
name: 'Tabs',
cssClass: 'icon-list-view',
canView: function (domainObject) {
return domainObject.type === 'tabs';
},
view: function (domainObject) {
let component;
return {
show: function (element) {
component = new Vue({
components: {
TabsComponent: TabsComponent.default
},
provide: {
openmct,
domainObject,
composition: openmct.composition.get(domainObject)
},
el: element,
template: '<tabs-component></tabs-component>'
});
},
destroy: function (element) {
component.$destroy();
component = undefined;
}
};
},
priority: function () {
return 1;
}
};
}
return Tabs;
});

View File

@ -104,10 +104,15 @@ $colorGridLines: rgba($editColor, 0.2);
$colorIconAlias: #4af6f3; $colorIconAlias: #4af6f3;
$colorIconAliasForKeyFilter: #aaa; $colorIconAliasForKeyFilter: #aaa;
// Holders
$colorTabsHolderBg: rgba(black, 0.2);
// Buttons and Controls // Buttons and Controls
$colorBtnBg: lighten($colorBodyBg, 10%); // ! $colorBtnBg: lighten($colorBodyBg, 10%);
$colorBtnBgHov: lighten($colorBtnBg, 10%); // ! $colorBtnBgHov: lighten($colorBtnBg, 10%);
$colorBtnFg: lighten($colorBodyFg, 10%); // ! $colorBtnFg: lighten($colorBodyFg, 10%);
$colorBtnReverseFg: lighten($colorBtnFg, 10%);
$colorBtnReverseBg: lighten($colorBtnBg, 10%);
$colorBtnFgHov: $colorBtnFg; $colorBtnFgHov: $colorBtnFg;
$colorBtnMajorBg: $colorKey; $colorBtnMajorBg: $colorKey;
$colorBtnMajorBgHov: $colorKeyHov; $colorBtnMajorBgHov: $colorKeyHov;
@ -119,6 +124,10 @@ $colorBtnCautionFg: $colorBtnFg;
$colorClickIcon: $colorKey; $colorClickIcon: $colorKey;
$colorClickIconBgHov: rgba($colorKey, 0.6); $colorClickIconBgHov: rgba($colorKey, 0.6);
$colorClickIconFgHov: $colorKeyHov; $colorClickIconFgHov: $colorKeyHov;
$colorDropHint: $colorKey;
$colorDropHintBg: darken($colorDropHint, 10%);
$colorDropHintBgHov: $colorDropHint;
$colorDropHintFg: lighten($colorDropHint, 40%);
// Menus // Menus
$colorMenuBg: lighten($colorBodyBg, 15%); $colorMenuBg: lighten($colorBodyBg, 15%);

View File

@ -104,10 +104,15 @@ $colorGridLines: rgba($editColor, 0.2);
$colorIconAlias: #4af6f3; $colorIconAlias: #4af6f3;
$colorIconAliasForKeyFilter: #aaa; $colorIconAliasForKeyFilter: #aaa;
// Holders
$colorTabsHolderBg: rgba($colorBodyFg, 0.2);
// Buttons and Controls // Buttons and Controls
$colorBtnBg: #aaaaaa; $colorBtnBg: #aaa;
$colorBtnBgHov: darken($colorBtnBg, 10%); $colorBtnBgHov: darken($colorBtnBg, 10%);
$colorBtnFg: #fff; $colorBtnFg: #fff;
$colorBtnReverseFg: $colorBodyBg;
$colorBtnReverseBg: $colorBodyFg;
$colorBtnFgHov: $colorBtnFg; $colorBtnFgHov: $colorBtnFg;
$colorBtnMajorBg: $colorKey; $colorBtnMajorBg: $colorKey;
$colorBtnMajorBgHov: $colorKeyHov; $colorBtnMajorBgHov: $colorKeyHov;
@ -119,6 +124,10 @@ $colorBtnCautionFg: $colorBtnFg;
$colorClickIcon: $colorKey; $colorClickIcon: $colorKey;
$colorClickIconBgHov: rgba($colorKey, 0.2); $colorClickIconBgHov: rgba($colorKey, 0.2);
$colorClickIconFgHov: $colorKeyHov; $colorClickIconFgHov: $colorKeyHov;
$colorDropHint: $colorKey;
$colorDropHintBg: darken($colorDropHint, 10%);
$colorDropHintBgHov: $colorDropHint;
$colorDropHintFg: lighten($colorDropHint, 40%);
// Menus // Menus
$colorMenuBg: lighten($colorBodyBg, 10%); $colorMenuBg: lighten($colorBodyBg, 10%);

View File

@ -32,13 +32,6 @@ button {
} }
.c-button--menu { .c-button--menu {
$m: $interiorMargin;
&:before,
> * {
margin-right: $m;
}
&:after { &:after {
content: $glyph-icon-arrow-down; content: $glyph-icon-arrow-down;
font-family: symbolsfont; font-family: symbolsfont;
@ -46,6 +39,10 @@ button {
} }
} }
.c-compact-button {
@include cCompactButtons($bg: $colorBtnBg, $fg: $colorBtnFg, $bgHov: $colorBtnBgHov);
}
/********* Icon Buttons */ /********* Icon Buttons */
.c-click-icon { .c-click-icon {
@include cClickIcon(); @include cClickIcon();
@ -487,3 +484,48 @@ input[type=number]::-webkit-outer-spin-button {
@include cControl(); @include cControl();
> * + * { margin-left: $interiorMargin; } > * + * { margin-left: $interiorMargin; }
} }
/***************************************************** DRAG AND DROP */
.c-drop-hint {
// Used in Tabs View, Flexible Grid Layouts
@include abs();
background-color: $colorDropHintBg;
color: $colorDropHintFg;
border-radius: $basicCr;
border: 1px dashed $colorDropHintFg;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
transition: $transOut;
z-index: 50;
opacity: 0; // Must use this (rather than display: none) to enable transition effects
pointer-events: none;
&:before {
$h: 80%;
$mh: 50px;
background: $bg-icon-activity; // TODO: change to $bg-icon-plus
background-size: contain;
background-position: center center;
background-repeat: no-repeat;
content: '';
display: block;
filter: $colorKeyFilterHov;
height: $h; width: $h;
max-height: $mh; max-width: $mh;
}
.is-dragging & {
pointer-events: inherit;
transition: $transIn;
opacity: 0.8;
}
.is-mouse-over & {
transition: $transIn;
background-color: $colorDropHintBgHov;
opacity: 0.9;
}
}

View File

@ -172,7 +172,7 @@
unicode-bidi:bidi-override; unicode-bidi:bidi-override;
} }
/************************** CONTROLS, BUTTONS */ /************************** CONTROLS, BUTTONS, INPUTS */
@mixin hover { @mixin hover {
body.desktop & { body.desktop & {
&:hover { &:hover {
@ -236,6 +236,15 @@
box-shadow: $shdw; box-shadow: $shdw;
} }
@mixin buttonBehavior() {
// Assign transition timings
transition: $transOut;
@include hover() {
transition: $transIn;
}
}
@mixin cControl() { @mixin cControl() {
$fs: 1em; $fs: 1em;
@include userSelectNone(); @include userSelectNone();
@ -266,11 +275,17 @@
@mixin cButton() { @mixin cButton() {
@include cControl(); @include cControl();
@include themedButton(); @include themedButton();
//@include buttonBehavior();
border-radius: $controlCr; border-radius: $controlCr;
color: $colorBtnFg; color: $colorBtnFg;
cursor: pointer; cursor: pointer;
padding: $interiorMargin floor($interiorMargin * 1.25); padding: $interiorMargin floor($interiorMargin * 1.25);
&:after,
> * {
margin-left: $interiorMarginSm;
}
@include hover() { @include hover() {
background: $colorBtnBgHov; background: $colorBtnBgHov;
color: $colorBtnFgHov; color: $colorBtnFgHov;
@ -347,6 +362,50 @@
} }
} }
@mixin cCompactButtons($bg, $fg, $bgHov) {
// Used in Tab view
// To be used in indicators popups?
$m: 1px;
$btnM: $m;
&-holder {
background: $colorTabsHolderBg;
border-radius: $controlCr;
padding: $m ($m - $btnM) ($m - $btnM) $m;
display: flex;
flex-flow: row wrap;
justify-content: stretch;
> * {
margin: 0 $btnM $btnM 0;
}
}
background: $colorBtnBg;
color: $colorBtnFg;
flex: 1 1 auto;
padding: $interiorMargin;
&:before {
opacity: 0.5;
}
> * {
margin-left: $interiorMarginSm;
}
@include hover() {
background: $bgHov;
}
&.is-current {
background: $colorBtnReverseBg;
color: $colorBtnReverseFg;
pointer-events: none;
}
}
@mixin wrappedInput() { @mixin wrappedInput() {
// An input that is wrapped. Optionally includes a __label or icon element. // An input that is wrapped. Optionally includes a __label or icon element.
// Based on .c-search. // Based on .c-search.

View File

@ -64,6 +64,7 @@
} }
&__drag-area { &__drag-area {
// TODO: recast this element to use c-drop-hint element
background: rgba($colorKey, 0.1); background: rgba($colorKey, 0.1);
border: 1px dashed rgba($colorKey, 0.7); border: 1px dashed rgba($colorKey, 0.7);
border-radius: $controlCr; border-radius: $controlCr;