Compare commits

...

45 Commits

Author SHA1 Message Date
charlesh88
0b828b68a3 Getting vue-toolbar reverted back to latest
- Merging this branch with vue-layout may cause conflicts;
2018-09-27 15:09:31 -07:00
charlesh88
76c21ba73a Custom checkbox code cleanups, sanding 2018-09-25 17:33:30 -07:00
charlesh88
d35800a307 Add toggleButton component
- Code and examples
2018-09-25 17:32:53 -07:00
charlesh88
ebd266cbbb Add custom checkbox control.
- Also, code cleanup.
2018-09-25 15:28:50 -07:00
charlesh88
de900702a6 Fixes for toolbar toggleMenus and labeledNumberInput 2018-09-24 15:57:48 -07:00
charlesh88
ae8202b90e Styling tweak for c-labeled-input
- Code cleanup as well;
2018-09-24 15:57:10 -07:00
charlesh88
84350d1776 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;
2018-09-24 13:40:50 -07:00
charlesh88
88a23151ec Add color palette markup and CSS
- Also added Layers menu example;
2018-09-24 11:57:54 -07:00
charlesh88
1a60a2e13a Fix issues with relative font-sizing 2018-09-21 18:26:12 -07:00
charlesh88
6e81b985a6 Merge in latest from vue-layout
- Conflicts in DisplayLayout.vue resolved;
2018-09-21 18:11:24 -07:00
charlesh88
0ee881f2d2 Significant refactoring of button and click-icon classes
- Markup and CSS updated;
- Toolbar in good shape, prior to merge of vue-layout;
2018-09-21 18:07:13 -07:00
charlesh88
908b843d8b Fixes for selection on nested selected elements 2018-09-21 16:58:17 -07:00
charlesh88
16df2567ed 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;
2018-09-21 16:45:52 -07:00
charlesh88
1aea27f9b6 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)
2018-09-21 16:17:42 -07:00
charlesh88
0d1acfd4df Frame edit handles markup and styling 2018-09-21 14:27:54 -07:00
charlesh88
13c984f3c1 Merge remote-tracking branch 'origin/vue-layout' into vue-toolbar 2018-09-21 13:20:21 -07:00
Pegah Sarram
f8aea00792 Define a computed property for the css class object. 2018-09-21 11:28:42 -07:00
charlesh88
15c52fe097 New toolbar buttons WIP 2018-09-21 00:45:55 -07:00
charlesh88
1b4aa5cbbf Toolbar WIP
- Custom wrapped number input added;
- Toolbar buttons WIP;
2018-09-20 18:01:52 -07:00
charlesh88
11acc1cfe5 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;
2018-09-20 17:17:59 -07:00
Pegah Sarram
d4c2a44a96 Hide the background grid when a frame is drilled in 2018-09-19 23:59:05 -07:00
Pegah Sarram
7792396c46 Implement drill in gesture. 2018-09-19 23:46:38 -07:00
charlesh88
984cf0ca1e Merge branch 'topic-core-refactor' into vue-layout 2018-09-18 15:01:50 -07:00
charlesh88
7483e886f1 Fix layout issues with overflow, shrinking, etc.
- Tree, main area, header, browse-bar
2018-09-18 15:01:09 -07:00
charlesh88
792633c0d1 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.
2018-09-18 14:46:15 -07:00
Pegah Sarram
dfd33251f0 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. 2018-09-17 23:12:27 -07:00
Pegah Sarram
734d368c0b Put the div back with height set to 100% in ObjectView.
Add markup for  drag handles.
2018-09-17 21:59:42 -07:00
Pegah Sarram
794098740e Position the panels by setting the style 2018-09-17 21:30:25 -07:00
Pegah Sarram
9c178a870d Fix conflict 2018-09-17 21:22:30 -07:00
Pegah Sarram
8b3a058b03 Saving work 2018-09-17 21:20:11 -07:00
Charles Hacskaylo
ef92da4d9d Custom search input component integrated (#2170)
- Integrated into main layout already; added to table.vue and
Notebook;
2018-09-17 16:06:46 -07:00
charlesh88
80b02672a6 Fix new ObjectView
- Removed empty <div> in template;
- Added display: contents to container div;
2018-09-17 11:45:56 -07:00
Charles Hacskaylo
fbf1c68c7a Notebook vue styling (#2169)
* Notebook styling WIP

- New notebook scss file;
- notebook.html converted;

* Notebook styling WIP

- Entries and embeds
- New discreteItem theme constants;

* Notebook styling shippable

- Notebook entries and embeds finished;
- TODO: Fix styling for c-input-inline when SCSS is merged and remove
s-input-inline class in entry.html;

* Notebook styling shippable

- Remove legacy Notebook styles;
- Painterro overrides;
- Code cleanup

* Notebook mobile styling; entry layout still WIP

- Entries now layout better in narrow containers but still WIP,
particularly for delete icon;
- Mobile styling;

* Notebook entry markup for better narrow Notebook layout
2018-09-17 11:02:38 -07:00
Pete Richards
5eac6e646b ObjectView supports reuse and props
Update ObjectView to better support reuse in layout and preview.

Register as component, and then mount like so:
<object-view :object="domainObject"></object-view>

It will show the default view for that object.  If you want to
specify a specific view type, you can pass an optional "view" prop
which will specify the view key to load.
2018-09-14 10:50:19 -07:00
Pete Richards
82e5bf2325 remove unnecessary container 2018-09-13 18:44:51 -07:00
Pete Richards
40b7117987 Object views (#2165)
* Use new style view providers for all object views.

* support legacy views with deprecation warning

* tidy deprecation warnings
2018-09-13 18:37:20 -07:00
Deep Tailor
07ca60e13a Dynamically generate create menu items (#2163)
* dynamically generate create menu items, populate before mount

* make reviewer requested changes:
1.Use type.get to get type definition
2.Fix type adapter for creatable properties
3.populate menu items in data function, and inject openmct

* use simpler data name (item) and remove prefix string from key
2018-09-13 15:15:01 -07:00
Deep Tailor
08cd6b1d99 Vue Browse Bar (#2164)
* Object browse bar IN PROGRESS

* Object browse bar VERY WIP

* Object browse bar WIP

- view-control renamed to disclosure-triangle;
- Good progress on object browse bar elements;

* Object browse bar WIP

- Layout of start-side elements now working with ellipsis;
- TODO: cleanups and consolidation;

* Object browse bar shippable

- Better layout approach;
- Refinements to button classes;

* Sanding and shimming on misc styles

- Tree icon shrinkage fixed;
- c-icon-button much better relative sizing;
- Removed c-button-set wrapper from Layout.vue;
- Added uppercasing of Create button for Snow theme;

* working object name, css class

* working dynamic name, css classes and view switcher
2018-09-13 15:14:28 -07:00
Andrew Henry
78ae7b334c Copyright notice examples 2018-09-13 14:19:55 -07:00
Deep Tailor
3a28caac06 Notebook Vue Version (#2160)
* working entries... wip

* componentize entries

* embed ability with drag into entry

* snapshot action partial

* working search

* abstract EntryController, working search, drag and drop

* keep deleteEntry local to entryController

* working snapshotOverlay

* fix snapshot object

* abstract Embed Controller, working context menu, working remove embed

* working preview action in embed context menu

* add overlay header with timestamp

* working annotate and new entry contextual

* Remove old notebook

* use same key, remove extra style files

* working embeds

* fixed markup for snapshot overlay
2018-09-12 15:27:19 -07:00
Andrew Henry
c883bbe6c2 Vue tables followup work. (#2162)
- Allow 'editable' property on view providers to optionally be a function
 - CSVExporter now only exports visible columns. Updated CSVExporter to ES6 exports / imports
 - Shortcut sortedIndex in insert if value is outside of first or last value in collection
 - Added table view icon
2018-09-11 11:03:32 -07:00
Pete Richards
eaa971cb56 Vue main section overhaul (#2161)
* Separate browse object from conductor

* Main layout convert to flex; padding and overflow at leaf node

* Inspector converted to BEM - WIP

 - Properties pool upper level styles converted;
 - Grid mixins moved out of Inspector and into _mixins;

* Refinements to misc elements

- user-select: none on tree;
- :before and :after reset globally to use box-sizing: border-box;
- li reset globally;

* Styling for buttons and menus; add Create button

 - WIP

* Context and Create menus

 - WIP

* Add glyph backgrounds as data URIs

- For Create menu, items grid;
- Objects only;

* Create menu refinements

- Min/max height handling;
- Code cleanup;

* Main layout head styling; various sanding

- head layout refined;
- c-icon-button, c-button-set added;
- background glyph mixin refined;
- Antialiasing refined to increase icon sharpness;

* Fix SVG data URLs: encode # chars as %23
2018-09-11 10:10:59 -07:00
Pete Richards
0301d88033 [Table] style refactor (#2157)
* [Table] Use Vue SFCs

Use Vue SFCs.  Use inject/provide to pass services to components
instead of wrapping components in closures.

* Convert CSS to BEM  - WIP!

- All in progress;
- Headers table divorced from old;
- Sizing working properly at this point;

* Reset legacy file, undo unintended change commit

* Convert CSS to BEM  - WIP!

- All in progress;
- Sizing table divorced from legacy;

* Convert CSS to BEM  - WIP!

- All in progress;
- Table body divorced from legacy;

* Convert CSS to BEM  - WIP!

- Near done, converted tabular-holder from legacy;
- Unit testing in main view and in Layout frames;
- Modded legacy CSS to properly hide control-bar with new naming
when in Layout frame;

* Convert CSS to BEM - done

- Cleanup and organization;

* Convert CSS to BEM

- Further code cleanup;

* Convert CSS to BEM

- Further code cleanup;
- Remove legacy table style imports;
2018-09-05 15:05:40 -07:00
charlesh88
e2e0cf17db Splitter refinement
- Arrow icons;
- Code cleanup;
2018-09-05 13:29:14 -07:00
Pete Richards
01a39f4fb7 [Inspector] More vue
commit 9b735b4f70bf4d21f64a1e418a5f9d7342567104
Author: Pete Richards <peter.l.richards@nasa.gov>
Date:   Fri Aug 31 16:31:48 2018 -0700

    Slight HTML tweak

commit 3e58e140f9ea3640e5551d5f43abf837610c6fb4
Author: Pete Richards <peter.l.richards@nasa.gov>
Date:   Fri Aug 31 16:26:36 2018 -0700

    [Inspector] Wire up elements pool

    Elements pool wired up to show angular elements pool.

commit d9c60f31bd6d32a5d2e2227d5476edd81e2e4360
Author: Pete Richards <peter.l.richards@nasa.gov>
Date:   Fri Aug 31 13:56:04 2018 -0700

    [Inspector] vue inspector

    Create a vue inspctor which responds to selection events and shows
    object properties and inspector views.
2018-08-31 16:34:03 -07:00
104 changed files with 5724 additions and 2737 deletions

2
app.js
View File

@@ -46,7 +46,7 @@ webpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin());
webpackConfig.plugins.push(function() { this.plugin('watch-run', function(watching, callback) { console.log('Begin compile at ' + new Date()); callback(); }) });
webpackConfig.entry.openmct = [
'webpack-hot-middleware/client',
'webpack-hot-middleware/client?reload=true',
webpackConfig.entry.openmct
];

21
copyright-notice.html Normal file
View File

@@ -0,0 +1,21 @@
<!--
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.
-->

View File

@@ -1,5 +1,5 @@
/******************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -18,27 +18,4 @@
* 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 "containment" rules, which determine which objects
* can be contained within a notebook.
*/
define(
[],
function () {
function CompositionPolicy() {
}
CompositionPolicy.prototype.allow = function (parent, child) {
var parentDef = parent.getCapability('type').getName();
if (parentDef === 'Notebook' && child.getCapability('status').list().length) {
return false;
}
return true;
};
return CompositionPolicy;
}
);
*****************************************************************************/

View File

@@ -66,5 +66,4 @@
</mct-representation>
</div>
</div>
<mct-include key="'conductor'" class="abs holder flex-elem flex-fixed l-flex-row l-time-conductor-holder"></mct-include>
</div>

View File

@@ -319,6 +319,12 @@ define([
]
}
],
"templates": [
{
key: "elementsPool",
template: elementsTemplate
}
],
"components": [
{
"type": "decorator",

View File

@@ -55,7 +55,7 @@ define(
// A view is editable unless explicitly flagged as not
(views || []).forEach(function (view) {
if (view.editable === true ||
if (isEditable(view) ||
(view.key === 'plot' && type.getKey() === 'telemetry.panel') ||
(view.key === 'table' && type.getKey() === 'table') ||
(view.key === 'rt-table' && type.getKey() === 'rttable')
@@ -64,6 +64,14 @@ define(
}
});
function isEditable(view) {
if (typeof view.editable === Function) {
return view.editable(domainObject.useCapability('adapter'));
} else {
return view.editable === true;
}
}
return count;
};

View File

@@ -1,294 +0,0 @@
define([
"legacyRegistry",
"./src/controllers/NotebookController",
"./src/controllers/NewEntryController",
"./src/controllers/SelectSnapshotController",
"./src/controllers/LayoutNotebookController",
"./src/directives/MCTSnapshot",
"./src/directives/EntryDnd",
"./src/actions/ViewSnapshot",
"./src/actions/AnnotateSnapshot",
"./src/actions/RemoveEmbed",
"./src/actions/RemoveSnapshot",
"./src/actions/NewEntryContextual",
"./src/capabilities/NotebookCapability",
"./src/policies/CompositionPolicy",
"./res/templates/notebook.html",
"./res/templates/entry.html",
"./res/templates/annotation.html",
"./res/templates/notifications.html",
"../layout/res/templates/frame.html",
"./res/templates/controls/embedControl.html",
"./res/templates/controls/snapSelect.html"
], function (
legacyRegistry,
NotebookController,
NewEntryController,
SelectSnapshotController,
LayoutNotebookController,
MCTSnapshot,
MCTEntryDnd,
ViewSnapshotAction,
AnnotateSnapshotAction,
RemoveEmbedAction,
RemoveSnapshotAction,
newEntryAction,
NotebookCapability,
CompositionPolicy,
notebookTemplate,
entryTemplate,
annotationTemplate,
notificationsTemplate,
frameTemplate,
embedControlTemplate,
snapSelectTemplate
) {
legacyRegistry.register("platform/features/notebook", {
"name": "Notebook Plugin",
"description": "Create and save timestamped notes with embedded object snapshots.",
"extensions":
{
"types": [
{
"key": "notebook",
"name": "Notebook",
"cssClass": "icon-notebook",
"description": "Create and save timestamped notes with embedded object snapshots.",
"features": ["creation"],
"model": {
"entries": [],
"composition": [],
"entryTypes": [],
"defaultSort": "-createdOn"
},
"properties": [
{
"key": "defaultSort",
"name": "Default Sort",
"control": "select",
"options": [
{
"name": "Newest First",
"value": "-createdOn"
},
{
"name": "Oldest First",
"value": "createdOn"
}
],
"cssClass": "l-inline"
}
]
}
],
"views": [
{
"key": "notebook.view",
"type": "notebook",
"cssClass": "icon-notebook",
"name": "notebook",
"template": notebookTemplate,
"editable": false,
"uses": [
"composition",
"action"
],
"gestures": [
"drop"
]
}
],
"controllers": [
{
"key": "NotebookController",
"implementation": NotebookController,
"depends": [
"$scope",
"dialogService",
"popupService",
"agentService",
"objectService",
"navigationService",
"now",
"actionService",
"$timeout",
"$element",
"$sce"
]
},
{
"key": "NewEntryController",
"implementation": NewEntryController,
"depends": ["$scope",
"$rootScope"
]
},
{
"key": "selectSnapshotController",
"implementation": SelectSnapshotController,
"depends": ["$scope",
"$rootScope"
]
},
{
"key": "LayoutNotebookController",
"implementation": LayoutNotebookController,
"depends": ["$scope"]
}
],
"representations": [
{
"key": "draggedEntry",
"template": entryTemplate
},
{
"key": "frameLayoutNotebook",
"template": frameTemplate
}
],
"templates": [
{
"key": "annotate-snapshot",
"template": annotationTemplate
},
{
"key": "notificationTemplate",
"template": notificationsTemplate
}
],
"directives": [
{
"key": "mctSnapshot",
"implementation": MCTSnapshot,
"depends": [
"$rootScope",
"$document",
"exportImageService",
"dialogService",
"notificationService"
]
},
{
"key": "mctEntryDnd",
"implementation": MCTEntryDnd,
"depends": [
"$rootScope",
"$compile",
"dndService",
"typeService",
"notificationService"
]
}
],
"actions": [
{
"key": "view-snapshot",
"implementation": ViewSnapshotAction,
"name": "View Snapshot",
"description": "View the large image in a modal",
"category": "embed",
"depends": [
"$compile"
]
},
{
"key": "annotate-snapshot",
"implementation": AnnotateSnapshotAction,
"name": "Annotate Snapshot",
"cssClass": "icon-pencil labeled",
"description": "Annotate embed's snapshot",
"category": "embed",
"depends": [
"dialogService",
"dndService",
"$rootScope"
]
},
{
"key": "remove-embed",
"implementation": RemoveEmbedAction,
"name": "Remove...",
"cssClass": "icon-trash labeled",
"description": "Remove this embed",
"category": [
"embed",
"embed-no-snap"
],
"depends": [
"dialogService"
]
},
{
"key": "remove-snapshot",
"implementation": RemoveSnapshotAction,
"name": "Remove Snapshot",
"cssClass": "icon-trash labeled",
"description": "Remove Snapshot of the embed",
"category": "embed",
"depends": [
"dialogService"
]
},
{
"key": "notebook-new-entry",
"implementation": newEntryAction,
"name": "New Notebook Entry",
"cssClass": "icon-notebook labeled",
"description": "Add a new Notebook entry",
"category": [
"view-control"
],
"depends": [
"$compile",
"$rootScope",
"dialogService",
"notificationService",
"linkService"
],
"priority": "preferred"
}
],
"licenses": [
{
"name": "painterro",
"version": "0.2.65",
"author": "Ivan Borshchov",
"description": "Painterro is JavaScript paint widget which allows editing images directly in a browser.",
"website": "https://github.com/ivictbor/painterro",
"copyright": "Copyright 2017 Ivan Borshchov",
"license": "MIT",
"link": "https://github.com/ivictbor/painterro/blob/master/LICENSE"
}
],
"capabilities": [
{
"key": "notebook",
"name": "Notebook Capability",
"description": "Provides a capability for looking for a notebook domain object",
"implementation": NotebookCapability,
"depends": [
"typeService"
]
}
],
"policies": [
{
"category": "composition",
"implementation": CompositionPolicy,
"message": "Objects of this type cannot contain objects of that type."
}
],
"controls": [
{
"key": "embed-control",
"template": embedControlTemplate
},
{
"key": "snapshot-select",
"template": snapSelectTemplate
}
]
}
});
});

View File

@@ -1,118 +0,0 @@
<div ng-controller="NotebookController as controller" class="mct-notebook w-notebook l-flex-col">
<div class="l-notebook-head holder l-flex-row flex-elem">
<div class="c-search flex-elem holder grows">
<input class="c-search__search-input"
type="text" tabindex="10000"
ng-model="entrySearch"
ng-keyup="controller.search()"/>
<a class="c-search__clear-input clear-icon icon-x-in-circle"
ng-class="{show: !(entrySearch === '' || entrySearch === undefined)}"
ng-click="entrySearch = ''; controller.search()"></a>
</div>
<div class="notebook-view-controls l-flex-row flex-elem holder">
<div class="select notebook-view-controls__filter-time">
<select ng-model="showTime">
<option value="0" selected="selected">Show all</option>
<option value="1">Last hour</option>
<option value="8">Last 8 hours</option>
<option value="24">Last 24 hours</option>
</select>
</div>
<div class="select notebook-view-controls__sort">
<select ng-model="sortEntries">
<option value="-createdOn" selected="selected">Newest first</option>
<option value="createdOn">Oldest first</option>
</select>
</div>
</div>
</div>
<!-- drag area -->
<div class="holder flex-elem l-flex-row icon-plus labeled l-notebook-drag-area" ng-click="newEntry($event)"
id="newEntry" mct-entry-dnd>
<span class="label">To start a new entry, click here or drag and drop any object</span>
</div>
<!-- entries -->
<div class="holder flex-elem grows w-notebook-entries t-entries-list" ng-mouseover="handleActive()">
<ul>
<li class="l-flex-row has-local-controls l-notebook-entry s-notebook-entry"
id="{{'entry_'+ entry.id}}"
ng-if="hoursFilter(showTime,entry.createdOn)"
ng-repeat="entry in model.entries | filter:entrySearch | orderBy: sortEntries track by $index"
ng-init="$last && finished(model.entries)"
mct-entry-dnd>
<div class="holder flex-elem l-flex-row grows w-notebook-entry-time-and-content">
<div class="holder flex-elem s-notebook-entry-time">
<span>{{entry.createdOn | date:'yyyy-MM-dd'}}</span>
<span>{{entry.createdOn | date:'HH:mm:ss'}}</span>
</div>
<div class="holder flex-elem l-flex-col grows l-notebook-entry-content">
<div contenteditable="true"
ng-blur="textBlur($event, entry.id)"
ng-focus="textFocus($event, entry.id)"
ng-model="entry.text"
placeholder="Enter text here"
class="flex-elem s-input-inline t-notebook-entry-input s-notebook-entry-text"
ng-bind="entry.text">
</div>
<!-- embeds -->
<div class="flex-elem entry-embeds l-flex-row">
<div class="l-flex-row l-entry-embed {{embed.cssClass}}"
ng-repeat="embed in entry.embeds track by $index"
ng-class="{ 'has-snapshot' : embed.snapshot }"
id="{{embed.id}}">
<div class="snap-thumb"
ng-if="embed.snapshot"
ng-click="viewSnapshot($event,embed.snapshot.src,embed.id,entry.createdOn,this,embed)">
<img ng-src="{{embed.snapshot.src}}" src="//:0" alt="{{embed.id}}">
</div>
<div class="embed-info l-flex-col">
<div class="embed-title object-header">
<a ng-click='navigate($event,embed.type)'>{{embed.name}}</a>
<a class='context-available' ng-click='openMenu($event,embed.type)'></a>
</div>
<div class="hide-menu" ng-show="false">
<div class="menu-element context-menu-wrapper mobile-disable-select">
<div class="menu context-menu">
<ul>
<li ng-repeat="menu in menuEmbed"
ng-click="menu.perform($event,embed.snapshot.src,embed.id,entry.createdOn,this,embed)"
title="{{menu.getMetadata().description}}"
class="{{menu.getMetadata().cssClass}}"
ng-if="embed.snapshot">
{{menu.getMetadata().name}}
</li>
<li ng-repeat="menu in menuEmbedNoSnap"
ng-click="menu.perform($event,embed.snapshot.src,embed.id,entry.createdOn,this)"
title="{{menu.getMetadata().description}}"
class="{{menu.getMetadata().cssClass}}"
ng-if="!embed.snapshot">
{{menu.getMetadata().name}}
</li>
<li ng-repeat="menu in embedActions"
ng-click="menu.perform()"
title="{{menu.name}}"
class="{{menu.cssClass}}">
{{menu.name}}
</li>
</ul>
</div>
</div>
</div>
<div class="embed-date"
ng-if="embed.snapshot">{{embed.id| date:'yyyy-MM-dd HH:mm:ss'}}
</div>
</div>
</div>
</div>
</div>
</div>
<!-- delete entry -->
<div class="holder flex-elem local-control local-controls-hidden notebook-entry-delete">
<a class="s-icon-button icon-trash" id={{entry.id}} title="Delete Entry" ng-click="deleteEntry($event)"></a>
</div>
</li>
</ul>
</div>
</div>

View File

@@ -1,8 +0,0 @@
<span class="status block">
<!-- DO NOT ADD SPACES BETWEEN THE SPANS - IT ADDS WHITE SPACE!! -->
<span class="status-indicator icon-bell"></span>
<span class="label">
Notifications
</span>
<span class="count"></span>
</span>

View File

@@ -1,34 +0,0 @@
<div class="t-snapshot abs l-view-header">
<div class="abs object-browse-bar l-flex-row">
<div class="left flex-elem l-flex-row grows">
<div class="object-header flex-elem l-flex-row grows">
<div class="type-icon flex-elem embed-icon holder" ng-class="cssClass"></div>
<div class="title-label flex-elem holder flex-can-shrink">{{entryName}}</div>
<a class="context-available flex-elem holder" ng-click="openMenu($event,embedType)"></a>
<div class="hide-menu" ng-show="false">
<div class="menu-element menu-view context-menu-wrapper mobile-disable-select">
<div class="menu context-menu">
<ul>
<li ng-repeat="menu in embedActions"
ng-click="menuPerform(menu)"
title="{{menu.name}}"
class="{{menu.cssClass}}">
{{menu.name}}
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<div class="btn-bar right l-flex-row flex-elem flex-justify-end flex-fixed">
<div class="flex-elem holder flex-can-shrink s-snapshot-datetime">
SNAPSHOT {{snapDate | date:'yyyy-MM-dd HH:mm:ss'}}
</div>
<a class="s-button icon-pencil" title="Annotate">
<span class="title-label">Annotate</span>
</a>
</div>
</div>
</div>

View File

@@ -1,72 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, 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 RemoveEmbed(dialogService,context) {
context = context || {};
this.domainObject = context.selectedObject || context.domainObject;
this.dialogService = dialogService;
}
RemoveEmbed.prototype.perform = function ($event,snapshot,embedId,entryId) {
var domainObject = this.domainObject;
var errorDialog = this.dialogService.showBlockingMessage({
severity: "error",
title: "This action will permanently delete this Embed. Do you want to continue?",
minimized: true, // want the notification to be minimized initially (don't show banner)
options: [{
label: "OK",
callback: function () {
errorDialog.dismiss();
remove();
}
},{
label: "Cancel",
callback: function () {
errorDialog.dismiss();
}
}]
});
function remove() {
domainObject.useCapability('mutation', function (model) {
var elementPos = model.entries.map(function (x) {
return x.createdOn;
}).indexOf(entryId);
var entryEmbeds = model.entries[elementPos].embeds;
var embedPos = entryEmbeds.map(function (x) {
return x.id;
}).indexOf(embedId);
model.entries[elementPos].embeds.splice(embedPos, 1);
});
}
};
return RemoveEmbed;
}
);

View File

@@ -1,74 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, 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 RemoveSnapshot(dialogService, context) {
context = context || {};
this.domainObject = context.selectedObject || context.domainObject;
this.dialogService = dialogService;
}
RemoveSnapshot.prototype.perform = function ($event, snapshot, embedId, entryId) {
var domainObject = this.domainObject;
var errorDialog = this.dialogService.showBlockingMessage({
severity: "error",
title: "This action will permanently delete this Snapshot. Do you want to continue?",
minimized: true, // want the notification to be minimized initially (don't show banner)
options: [{
label: "OK",
callback: function () {
errorDialog.dismiss();
remove();
}
},{
label: "Cancel",
callback: function () {
errorDialog.dismiss();
}
}]
});
function remove() {
domainObject.useCapability('mutation', function (model) {
var elementPos = model.entries.map(function (x) {
return x.createdOn;
}).indexOf(entryId);
var entryEmbeds = model.entries[elementPos].embeds;
var embedPos = entryEmbeds.map(function (x) {
return x.id;
}).indexOf(embedId);
model.entries[elementPos].embeds[embedPos].snapshot = "";
});
}
};
return RemoveSnapshot;
}
);

View File

@@ -1,132 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, 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.
*****************************************************************************/
/**
* Module defining ViewSnapshot
*/
var OVERLAY_TEMPLATE = '' +
' <div class="abs blocker"></div>' +
' <div class="abs outer-holder">' +
' <a class="close icon-x-in-circle"></a>' +
' <div class="abs inner-holder l-flex-col">' +
' <div class="t-contents flex-elem holder grows"></div>' +
' <div class="bottom-bar flex-elem holder">' +
' <a class="t-done s-button major">Done</a>' +
' </div>' +
' </div>' +
' </div>';
define([
'zepto',
"../../res/templates/snapshotHeader.html"
],
function ($, headerTemplate) {
var toggleOverlay,
overlay,
closeButton,
doneButton,
blocker,
overlayContainer,
img,
annotateButton,
annotateImg;
function ViewSnapshot($compile) {
this.$compile = $compile;
}
function openOverlay(url, header) {
overlay = document.createElement('div');
$(overlay).addClass('abs overlay l-large-view');
overlay.innerHTML = OVERLAY_TEMPLATE;
overlayContainer = overlay.querySelector('.t-contents');
closeButton = overlay.querySelector('a.close');
closeButton.addEventListener('click', toggleOverlay);
doneButton = overlay.querySelector('a.t-done');
doneButton.addEventListener('click', toggleOverlay);
blocker = overlay.querySelector('.abs.blocker');
blocker.addEventListener('click', toggleOverlay);
annotateButton = header.querySelector('a.icon-pencil');
annotateButton.addEventListener('click', annotateImg);
document.body.appendChild(overlay);
img = document.createElement('div');
$(img).addClass('abs object-holder t-image-holder s-image-holder');
img.innerHTML = '<div class="image-main s-image-main" style="background-image: url(' + url + ');"></div>';
overlayContainer.appendChild(header);
overlayContainer.appendChild(img);
}
function closeOverlay() {
overlayContainer.removeChild(img);
document.body.removeChild(overlay);
closeButton.removeEventListener('click', toggleOverlay);
closeButton = undefined;
doneButton.removeEventListener('click', toggleOverlay);
doneButton = undefined;
blocker.removeEventListener('click', toggleOverlay);
blocker = undefined;
overlayContainer = undefined;
overlay = undefined;
img = undefined;
}
ViewSnapshot.prototype.perform = function ($event, snapshot, embedId, entryId, $scope, embed) {
var isOpen = false;
// onclick for menu items in overlay header context menu
$scope.menuPerform = function (menu) {
menu.perform();
closeOverlay();
};
// Create the overlay element and add it to the document's body
$scope.cssClass = embed.cssClass;
$scope.embedType = embed.type;
$scope.entryName = embed.name;
$scope.snapDate = +embedId;
var element = this.$compile(headerTemplate)($scope);
var annotateAction = $scope.action.getActions({category: 'embed'})[1];
toggleOverlay = function () {
if (!isOpen) {
openOverlay(snapshot, element[0]);
isOpen = true;
} else {
closeOverlay();
isOpen = false;
}
};
annotateImg = function () {
closeOverlay();
annotateAction.perform($event, snapshot, embedId, entryId, $scope);
};
toggleOverlay();
};
return ViewSnapshot;
}
);

View File

@@ -1,50 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, 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 () {
/**
* The notebook capability allows a domain object to know whether the
* notebook plugin is present or not.
*
* @constructor
*/
function NotebookCapability(typeService, domainObject) {
this.domainObject = domainObject;
this.typeService = typeService;
return this;
}
/**
* Returns true if there is a notebook domain Object.
*
* @returns {Boolean}
*/
NotebookCapability.prototype.isNotebook = function () {
return !!this.typeService.getType('notebook');
};
return NotebookCapability;
}
);

View File

@@ -1,367 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, 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.
*****************************************************************************/
/*-- main controller file, here is the core functionality of the notebook plugin --*/
define(
['zepto'],
function ($) {
function NotebookController(
$scope,
dialogService,
popupService,
agentService,
objectService,
navigationService,
now,
actionService,
$timeout,
$element,
$sce
) {
$scope.entriesEl = $(document.body).find('.t-entries-list');
$scope.sortEntries = $scope.domainObject.getModel().defaultSort;
$scope.showTime = "0";
$scope.editEntry = false;
$scope.entrySearch = '';
$scope.entryTypes = [];
$scope.embedActions = [];
$scope.currentEntryValue = '';
var SECONDS_IN_AN_HOUR = 60 * 60 * 1000;
this.scope = $scope;
$scope.hoursFilter = function (hours,entryTime) {
if (+hours) {
return entryTime > (Date.now() - SECONDS_IN_AN_HOUR * (+hours));
}else {
return true;
}
};
$scope.scrollToTop = function () {
var entriesContainer = $scope.entriesEl.parent();
entriesContainer[0].scrollTop = 0;
};
$scope.findEntryEl = function (entryId) {
var element = $($scope.entriesEl).find('#entry_' + entryId);
if (element[0]) {
return element.find("[contenteditable='true']");
} else {
var entries = $scope.entriesEl.children().children(),
lastOrFirst = $scope.sortEntries === "-createdOn" ? 0 : (entries.length - 1);
return $(entries[lastOrFirst]).find("[contenteditable='true']");
}
};
$scope.findEntryPositionById = function (id) {
var foundId = -1;
$scope.domainObject.model.entries.forEach(function (element, index) {
if (element.id === id) {
foundId = index;
return;
}
});
return foundId;
};
$scope.newEntry = function ($event) {
$scope.scrollToTop();
var entries = $scope.domainObject.model.entries,
lastEntry = entries[entries.length - 1],
id = Date.now();
if (lastEntry === undefined || lastEntry.text || lastEntry.embeds) {
var createdEntry = {'id': id, 'createdOn': id};
$scope.domainObject.useCapability('mutation', function (model) {
model.entries.push(createdEntry);
});
} else {
$scope.findEntryEl(lastEntry.id).focus();
$scope.domainObject.useCapability('mutation', function (model) {
model.entries[entries.length - 1].createdOn = id;
});
}
$scope.entrySearch = '';
};
$scope.deleteEntry = function ($event) {
var delId = +$event.currentTarget.id;
var errorDialog = dialogService.showBlockingMessage({
severity: "error",
title: "This action will permanently delete this Notebook entry. Do you want to continue?",
minimized: true, // want the notification to be minimized initially (don't show banner)
options: [{
label: "OK",
callback: function () {
errorDialog.dismiss();
var elementPos = $scope.findEntryPositionById(delId);
if (elementPos !== -1) {
$scope.domainObject.useCapability('mutation', function (model) {
model.entries.splice(elementPos, 1);
});
} else {
window.console.log('delete error');
}
}
},{
label: "Cancel",
callback: function () {
errorDialog.dismiss();
}
}]
});
};
$scope.textFocus = function ($event, entryId) {
if ($event.srcElement) {
$scope.currentEntryValue = $event.srcElement.innerText;
} else {
$event.target.innerText = '';
}
};
//On text blur(when focus is removed)
$scope.textBlur = function ($event, entryId) {
// entryId is the unique numeric based on the original createdOn
if ($event.target) {
var elementPos = $scope.findEntryPositionById(+entryId);
// If the text of an entry has been changed, then update the text and the modifiedOn numeric
// Otherwise, don't do anything
if ($scope.currentEntryValue !== $event.target.innerText) {
$scope.domainObject.useCapability('mutation', function (model) {
model.entries[elementPos].text = $event.target.innerText;
model.entries[elementPos].modified = Date.now();
});
}
}
};
$scope.finished = function (model) {
var lastEntry = model[model.length - 1];
if (!lastEntry.text) {
$scope.findEntryEl(lastEntry.id).focus();
}
};
$scope.handleActive = function () {
var newEntry = $scope.entriesEl.find('.active');
if (newEntry) {
newEntry.removeClass('active');
}
};
$scope.clearSearch = function () {
$scope.entrySearch = '';
};
$scope.viewSnapshot = function ($event,snapshot,embedId,entryId,$innerScope,domainObject) {
var viewAction = $scope.action.getActions({category: 'embed'})[0];
viewAction.perform($event, snapshot, embedId, entryId, $innerScope, domainObject);
};
$scope.renderImage = function (img) {
return URL.createObjectURL(img);
};
$scope.getDomainObj = function (id) {
return objectService.getObjects([id]);
};
function refreshComp(change) {
if (change && change.length) {
change[0].getCapability('action').getActions({key: 'remove'})[0].perform();
}
}
$scope.actionToMenuOption = function (action) {
return {
key: action.getMetadata().key,
name: action.getMetadata().name,
cssClass: action.getMetadata().cssClass,
perform: action.perform
};
};
// Maintain all "conclude-editing" and "save" actions in the
// present context.
function updateActions() {
$scope.menuEmbed = $scope.action ?
$scope.action.getActions({category: 'embed'}) :
[];
$scope.menuEmbedNoSnap = $scope.action ?
$scope.action.getActions({category: 'embed-no-snap'}) :
[];
$scope.menuActions = $scope.action ?
$scope.action.getActions({key: 'window'}) :
[];
}
// Update set of actions whenever the action capability
// changes or becomes available.
$scope.$watch("action", updateActions);
$scope.navigate = function ($event,embedType) {
if ($event) {
$event.preventDefault();
}
$scope.getDomainObj(embedType).then(function (resp) {
navigationService.setNavigation(resp[embedType]);
});
};
$scope.saveSnap = function (url,embedPos,entryPos) {
var snapshot = false;
if (url) {
if (embedPos !== -1 && entryPos !== -1) {
var reader = new window.FileReader();
reader.readAsDataURL(url);
reader.onloadend = function () {
snapshot = reader.result;
$scope.domainObject.useCapability('mutation', function (model) {
if (model.entries[entryPos]) {
model.entries[entryPos].embeds[embedPos].snapshot = {
'src': snapshot,
'type': url.type,
'size': url.size,
'modified': Date.now()
};
model.entries[entryPos].embeds[embedPos].id = Date.now();
}
});
};
}
}else {
$scope.domainObject.useCapability('mutation', function (model) {
model.entries[entryPos].embeds[embedPos].snapshot = snapshot;
});
}
};
/*---popups menu embeds----*/
function getEmbedActions(embedType) {
if (!$scope.embedActions.length) {
$scope.getDomainObj(embedType).then(function (resp) {
$scope.embedActions = [];
$scope.embedActions.push($scope.actionToMenuOption(
$scope.action.getActions({key: 'mct-preview-action', selectedObject: resp[embedType]})[0]
));
$scope.embedActions.push($scope.actionToMenuOption(
$scope.action.getActions({key: 'window', selectedObject: resp[embedType]})[0]
));
$scope.embedActions.push({
key: 'navigate',
name: 'Go to Original',
cssClass: '',
perform: function () {
$scope.navigate('', embedType);
}
});
});
}
}
$scope.openMenu = function ($event,embedType) {
$event.preventDefault();
getEmbedActions(embedType);
var body = $(document).find('body'),
initiatingEvent = agentService.isMobile() ?
'touchstart' : 'mousedown',
dismissExistingMenu,
menu;
var container = $($event.currentTarget).parent().parent();
menu = container.find('.menu-element');
// Remove the context menu
function dismiss() {
container.find('.hide-menu').append(menu);
body.off("mousedown", dismiss);
dismissExistingMenu = undefined;
$scope.embedActions = [];
}
// Dismiss any menu which was already showing
if (dismissExistingMenu) {
dismissExistingMenu();
}
// ...and record the presence of this menu.
dismissExistingMenu = dismiss;
popupService.display(menu, [$event.pageX,$event.pageY], {
marginX: 0,
marginY: -50
});
// Stop propagation so that clicks or touches on the menu do not close the menu
menu.on(initiatingEvent, function (event) {
event.stopPropagation();
$timeout(dismiss, 300);
});
// Dismiss the menu when body is clicked/touched elsewhere
// ('mousedown' because 'click' breaks left-click context menus)
// ('touchstart' because 'touch' breaks context menus up)
body.on(initiatingEvent, dismiss);
};
$scope.$watchCollection("composition", refreshComp);
$scope.$watch('domainObject.getModel().defaultSort', function (newDefaultSort, oldDefaultSort) {
if (newDefaultSort !== oldDefaultSort) {
$scope.sortEntries = newDefaultSort;
}
});
$scope.$on('$destroy', function () {});
}
return NotebookController;
});

View File

@@ -31,13 +31,14 @@ define([
'./plugins/plugins',
'./adapter/indicators/legacy-indicators-plugin',
'./plugins/buildInfo/plugin',
'./adapter/vue-adapter/install',
'./ui/registries/ViewRegistry',
'./ui/registries/InspectorViewRegistry',
'./ui/registries/ToolbarRegistry',
'./ui/router/ApplicationRouter',
'./ui/router/Browse',
'../platform/framework/src/Main',
'./styles-new/core.scss',
'./styles-new/notebook.scss',
'./ui/components/layout/Layout.vue',
'vue'
], function (
@@ -51,13 +52,14 @@ define([
plugins,
LegacyIndicatorsPlugin,
buildInfoPlugin,
installVueAdapter,
ViewRegistry,
InspectorViewRegistry,
ToolbarRegistry,
ApplicationRouter,
Browse,
Main,
coreStyles,
NotebookStyles,
Layout,
Vue
) {
@@ -224,6 +226,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));
@@ -275,6 +278,7 @@ define([
}.bind(this)
});
// TODO: remove with legacy types.
this.types.listKeys().forEach(function (typeKey) {
var type = this.types.get(typeKey);
var legacyDefinition = type.toLegacyDefinition();
@@ -282,26 +286,6 @@ define([
this.legacyExtension('types', legacyDefinition);
}.bind(this));
// TODO: move this to adapter bundle.
this.legacyExtension('runs', {
depends: ['types[]'],
implementation: (types) => {
this.types.importLegacyTypes(types);
}
});
this.objectViews.getAllProviders().forEach(function (p) {
this.legacyExtension('views', {
key: p.key,
provider: p,
name: p.name,
cssClass: p.cssClass,
description: p.description,
editable: p.editable,
template: '<mct-view mct-provider-key="' + p.key + '"/>'
});
}, this);
legacyRegistry.register('adapter', this.legacyBundle);
legacyRegistry.enable('adapter');
@@ -326,8 +310,6 @@ define([
// something has depended upon objectService. Cool, right?
this.$injector.get('objectService');
console.log('Rendering app layout.');
var appLayout = new Vue({
mixins: [Layout.default],
provide: {
@@ -336,9 +318,8 @@ define([
});
domElement.appendChild(appLayout.$mount().$el);
console.log('Attaching adapter');
installVueAdapter(appLayout, this);
this.layout = appLayout;
Browse(this);
this.router.start();
this.emit('start');
}.bind(this));

View File

@@ -34,7 +34,9 @@ define([
'./runs/TimeSettingsURLHandler',
'./runs/TypeDeprecationChecker',
'./runs/LegacyTelemetryProvider',
'./services/LegacyObjectAPIInterceptor'
'./runs/RegisterLegacyTypes',
'./services/LegacyObjectAPIInterceptor',
'./views/installLegacyViews'
], function (
legacyRegistry,
ActionDialogDecorator,
@@ -49,7 +51,9 @@ define([
TimeSettingsURLHandler,
TypeDeprecationChecker,
LegacyTelemetryProvider,
LegacyObjectAPIInterceptor
RegisterLegacyTypes,
LegacyObjectAPIInterceptor,
installLegacyViews
) {
legacyRegistry.register('src/adapter', {
"extensions": {
@@ -149,6 +153,21 @@ define([
"openmct",
"instantiate"
]
},
{
implementation: installLegacyViews,
depends: [
"openmct",
"views[]",
"instantiate"
]
},
{
implementation: RegisterLegacyTypes,
depends: [
"types[]",
"openmct"
]
}
],
licenses: [

View File

@@ -0,0 +1,17 @@
define([
], function (
) {
function RegisterLegacyTypes(types, openmct) {
types.forEach(function (legacyDefinition) {
if (!openmct.types.get(legacyDefinition.key)) {
console.warn(`DEPRECATION WARNING: Migrate type ${legacyDefinition.key} from ${legacyDefinition.bundle.path} to use the new Types API. Legacy type support will be removed soon.`);
}
});
openmct.types.importLegacyTypes(types);
}
return RegisterLegacyTypes;
});

View File

@@ -0,0 +1,93 @@
define([
], function (
) {
function LegacyViewProvider(legacyView, openmct, convertToLegacyObject) {
console.warn(`DEPRECATION WARNING: Migrate ${legacyView.key} from ${legacyView.bundle.path} to use the new View APIs. Legacy view support will be removed soon.`);
return {
key: legacyView.key,
name: legacyView.name,
cssClass: legacyView.cssClass,
description: legacyView.description,
editable: legacyView.editable,
canView: function (domainObject) {
if (!domainObject || !domainObject.identifier) {
return false;
}
if (legacyView.type) {
return domainObject.type === legacyView.type;
}
let legacyObject = convertToLegacyObject(domainObject);
if (legacyView.needs) {
let meetsNeeds = legacyView.needs.every(k => legacyObject.hasCapability(k));
if (!meetsNeeds) {
return false;
}
}
return openmct.$injector.get('policyService').allow(
'view', legacyView, legacyObject
);
},
view: function (domainObject) {
let $rootScope = openmct.$injector.get('$rootScope');
let templateLinker = openmct.$injector.get('templateLinker');
let scope = $rootScope.$new();
let legacyObject = convertToLegacyObject(domainObject);
let isDestroyed = false;
scope.domainObject = legacyObject;
scope.model = legacyObject.getModel();
return {
show: function (container) {
// TODO: implement "gestures" support ?
let uses = legacyView.uses || [];
let promises = [];
let results = uses.map(function (capabilityKey, i) {
let result = legacyObject.useCapability(capabilityKey);
if (result.then) {
promises.push(result.then(function (r) {
results[i] = r;
}));
}
return result;
});
function link() {
if (isDestroyed) {
return;
}
uses.forEach(function (key, i) {
scope[key] = results[i];
});
templateLinker.link(
scope,
openmct.$angular.element(container),
legacyView
);
container.style.height = '100%';
}
if (promises.length) {
Promise.all(promises)
.then(function () {
link();
scope.$digest();
});
} else {
link();
}
},
destroy: function () {
scope.$destroy();
}
}
}
};
};
return LegacyViewProvider;
});

View File

@@ -0,0 +1,22 @@
define([
'./LegacyViewProvider',
'../../api/objects/object-utils'
], function (
LegacyViewProvider,
objectUtils
) {
function installLegacyViews(openmct, legacyViews, instantiate) {
function convertToLegacyObject(domainObject) {
let keyString = objectUtils.makeKeyString(domainObject.identifier);
let oldModel = objectUtils.toOldFormat(domainObject);
return instantiate(oldModel, keyString);
}
legacyViews.forEach(function (legacyView) {
openmct.objectViews.addProvider(new LegacyViewProvider(legacyView, openmct, convertToLegacyObject));
});
}
return installLegacyViews;
});

View File

@@ -1,41 +0,0 @@
define([
], function (
) {
class InspectorAdapter {
constructor(layout, openmct) {
console.log('installing inspector adapter');
this.openmct = openmct;
this.layout = layout;
this.$injector = openmct.$injector;
this.angular = openmct.$angular;
this.objectService = this.$injector.get('objectService');
this.templateLinker = this.$injector.get('templateLinker');
this.$timeout = this.$injector.get('$timeout');
this.templateMap = {};
this.$injector.get('templates[]').forEach((t) => {
this.templateMap[t.key] = this.templateMap[t.key] || t;
});
var $rootScope = this.$injector.get('$rootScope');
this.scope = $rootScope.$new();
this.templateLinker.link(
this.scope,
angular.element(layout.$refs.inspector.$refs.properties),
this.templateMap["inspectorRegion"]
);
this.$timeout(function () {
//hello!
});
}
}
return InspectorAdapter;
});

View File

@@ -1,16 +0,0 @@
define([
'./main-adapter',
'./tree-adapter',
'./inspector-adapter'
], function (
MainAdapter,
TreeAdapter,
InspectorAdapter
) {
return function install(layout, openmct) {
let main = new MainAdapter(layout, openmct);
let tree = new TreeAdapter(layout, openmct);
let inspector = new InspectorAdapter(layout, openmct);
}
});

View File

@@ -1,153 +0,0 @@
define([
], function (
) {
// Find an object in an array of objects.
function findObject(domainObjects, id) {
var i;
for (i = 0; i < domainObjects.length; i += 1) {
if (domainObjects[i].getId() === id) {
return domainObjects[i];
}
}
}
// recursively locate and return an object inside of a container
// via a path. If at any point in the recursion it fails to find
// the next object, it will return the parent.
function findViaComposition(containerObject, path) {
var nextId = path.shift();
if (!nextId) {
return containerObject;
}
return containerObject.useCapability('composition')
.then(function (composees) {
var nextObject = findObject(composees, nextId);
if (!nextObject) {
return containerObject;
}
if (!nextObject.hasCapability('composition')) {
return nextObject;
}
return findViaComposition(nextObject, path);
});
}
function getLastChildIfRoot(object) {
if (object.getId() !== 'ROOT') {
return object;
}
return object.useCapability('composition')
.then(function (composees) {
return composees[composees.length - 1];
});
}
function pathForObject(domainObject) {
var context = domainObject.getCapability('context'),
objectPath = context ? context.getPath() : [],
ids = objectPath.map(function (domainObj) {
return domainObj.getId();
});
return "/browse/" + ids.slice(1).join("/");
}
class MainAdapter {
constructor(layout, openmct) {
this.openmct = openmct;
this.layout = layout;
this.$injector = openmct.$injector;
this.angular = openmct.$angular;
this.objectService = this.$injector.get('objectService');
this.templateLinker = this.$injector.get('templateLinker');
this.navigationService = this.$injector.get('navigationService');
this.$timeout = this.$injector.get('$timeout');
this.templateMap = {};
this.$injector.get('templates[]').forEach((t) => {
this.templateMap[t.key] = this.templateMap[t.key] || t;
});
var $rootScope = this.$injector.get('$rootScope');
this.scope = $rootScope.$new();
this.scope.representation = {};
openmct.router.route(/^\/browse\/(.*)$/, (path, results) => {
let navigatePath = results[1];
if (!navigatePath) {
navigatePath = 'mine';
}
this.navigateToPath(navigatePath);
});
this.navigationService.addListener(o => this.navigateToObject(o));
}
navigateToPath(path) {
if (!Array.isArray(path)) {
path = path.split('/');
}
return this.getObject('ROOT')
.then(root => {
return findViaComposition(root, path);
})
.then(getLastChildIfRoot)
.then(object => {
this.setMainViewObject(object);
});
}
setMainViewObject(object) {
this.scope.domainObject = object;
this.scope.navigatedObject = object;
this.templateLinker.link(
this.scope,
angular.element(this.layout.$refs.mainContainer),
this.templateMap["browseObject"]
);
document.title = object.getModel().name;
this.scheduleDigest();
}
idsForObject(domainObject) {
return this.urlService
.urlForLocation("", domainObject)
.replace('/', '');
}
navigateToObject(object) {
let path = pathForObject(object);
let views = object.useCapability('view');
let params = this.openmct.router.getParams();
let currentViewIsValid = views.some(v => v.key === params['view']);
if (!currentViewIsValid) {
this.scope.representation = {
selected: views[0]
}
this.openmct.router.update(path, {
view: views[0].key
});
} else {
this.openmct.router.setPath(path);
}
}
scheduleDigest() {
this.$timeout(function () {
// digest done!
});
}
getObject(id) {
return this.objectService.getObjects([id])
.then(function (results) {
return results[id];
});
}
}
return MainAdapter;
});

View File

@@ -1,15 +0,0 @@
define([
], function (
) {
class TreeAdapter {
constructor(layout, openmct) {
}
}
return TreeAdapter;
});

View File

@@ -89,12 +89,13 @@ define(function () {
}
}
}
if (Array.isArray(legacyDefinition.creatable) && 'creation' in legacyDefinition.creatable) {
if (legacyDefinition.features && legacyDefinition.features.includes("creation")) {
definition.creatable = true;
}
return definition;
}
};
return Type;
});

View File

@@ -20,20 +20,18 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([
'csv',
'saveAs'
], function (CSV, saveAs) {
class CSVExporter {
export(rows, options) {
let headers = (options && options.headers) ||
(Object.keys((rows[0] || {})).sort());
let filename = (options && options.filename) || "export.csv";
let csvText = new CSV(rows, { header: headers }).encode();
let blob = new Blob([csvText], { type: "text/csv" });
saveAs(blob, filename);
}
}
import CSV from 'comma-separated-values';
import {saveAs} from 'file-saver/FileSaver';
return CSVExporter;
});
class CSVExporter {
export(rows, options) {
let headers = (options && options.headers) ||
(Object.keys((rows[0] || {})).sort());
let filename = (options && options.filename) || "export.csv";
let csvText = new CSV(rows, { header: headers }).encode();
let blob = new Blob([csvText], { type: "text/csv" });
saveAs(blob, filename);
}
};
export default CSVExporter;

View File

@@ -0,0 +1,206 @@
<template>
<div class="l-layout">
<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"
@drilledIn="updateDrilledInState"
@selected="updateSelectedState">
</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-layout {
background: rgba($colorKey, 0.1);
&.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],
MINIMUM_FRAME_SIZE = [320, 180];
export default {
data() {
return {
gridSize: DEFAULT_GRID_SIZE,
frameItems: [],
frames: [],
composition: Object,
frameStyles: [],
rawPositions: {},
isEditing: true,
initSelect: true,
drilledIn: undefined
}
},
inject: ['openmct'],
props: ['domainObject'],
components: {
LayoutFrame
},
created: function () {
console.log("domainObject", JSON.parse(JSON.stringify(this.domainObject)));
this.populatePositions(this.domainObject.configuration.layout.panels);
this.composition = this.openmct.composition.get(this.domainObject);
this.composition.on('add', this.onAddComposition);
this.composition.on('remove', this.onRemoveComposition);
this.composition.load();
},
methods: {
onAddComposition(domainObject) {
console.log('composition object', domainObject);
const id = this.openmct.objects.makeKeyString(domainObject.identifier)
this.frameItems.push({
id: id,
hasFrame: this.hasFrame(id),
domainObject,
style: this.frameStyles[id],
drilledIn: this.isDrilledIn(id),
selected: false
});
},
onRemoveComposition(identifier) {
// TODO: remove the object from frameItems
},
populatePositions(panels) {
Object.keys(panels).forEach(function (key, index) {
this.rawPositions[key] = {
position: panels[key].position || this.defaultPosition(index),
dimensions: panels[key].dimensions || this.defaultDimensions()
};
this.frameStyles[key] = this.convertPosition(this.rawPositions[key]);
this.frames[key] = panels[key].hasFrame;
}.bind(this));
},
defaultPosition(index) {
return [index, index];
},
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'
};
},
hasFrame(id) {
return this.frames[id]
},
setSelection(selection) {
if (selection.length === 0) {
return;
}
this.updateDrilledInState();
this.updateSelectedState();
},
updateDrilledInState(id) {
this.drilledIn = id;
this.frameItems.forEach(function (item) {
item.drilledIn = item.id === id;
});
},
updateSelectedState(id) {
this.frameItems.forEach(function (item) {
item.selected = item.id === id;
});
},
isDrilledIn(id) {
return this.drilledIn === id;
}
},
mounted() {
this.removeSelectable = this.openmct.selection.selectable(
this.$el,
{
item: this.domainObject
},
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();
}
}
</script>

View File

@@ -0,0 +1,251 @@
<template>
<!-- - TODO: styles for selectable, moveable, etc. -->
<div class="c-frame has-local-controls"
: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">Header</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"></div>
<div class="c-frame-edit__handle --nw"></div>
<div class="c-frame-edit__handle --ne"></div>
<div class="c-frame-edit__handle --se"></div>
<div class="c-frame-edit__handle --sw"></div>
</div>
</div>
</template>
<style lang="scss">
@import "~styles/sass-base";
/******************************* FRAME */
.c-frame {
display: flex;
flex-direction: column;
/*************************** HEADER */
&__header {
display: flex;
align-items: center;
flex: 0 0 auto;
margin-bottom: $interiorMargin;
> [class*="__"] {
display: flex;
align-items: center;
}
[class*="__start"] {
flex: 0 0 auto;
}
[class*="__end"] {
justify-content: flex-end;
flex: 1 1 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;
}
/*************************** NO-FRAME */
&.no-frame {
[class*="__header"] {
display: none;
}
}
&:not(.no-frame) {
background: $colorBodyBg;
border: 1px solid $colorInteriorBorder;
padding: $interiorMargin;
}
/*************************** SELECTION */
&.s-selected {
//Legacy name for now
border-color: $colorKey;
}
&.is-drilled-in {
border: 1px dashed deeppink;
}
}
/*************************** EDITING */
.is-editing .c-frame:not(.is-drilled-in) {
border: 1px dotted rgba($colorKey, 0.5);
&.s-selected {
> .c-frame-edit {
display: block;
}
}
}
.c-frame-edit {
$z: 10;
@include abs();
box-shadow: rgba($colorKey, 1) 0 0 10px;
display: none;
&__move {
@include abs();
cursor: move;
z-index: $z;
}
&__handle {
$d: 8px;
$o: floor($d * -0.5);
background: rgba($colorKey, 0.3);
border: 1px solid $colorKey;
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: $colorKey;
}
&.--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'
export default {
inject: ['openmct'],
props: {
item: Object
},
components: {
ObjectView
},
computed: {
classObject: function () {
return {
'is-drilled-in': this.item.drilledIn,
'no-frame': !this.item.hasFrame
}
}
},
methods: {
setSelection(selection) {
if (selection.length === 0) {
return;
}
let id = this.openmct.objects.makeKeyString(selection[0].context.item.identifier);
if (this.item.id === id) {
this.$emit('selected', id);
}
},
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;
}
},
mounted() {
this.removeSelectable = this.openmct.selection.selectable(
this.$el,
{
item: this.item.domainObject
},
this.item.selected
);
this.openmct.selection.on('change', this.setSelection);
},
destroyed() {
this.openmct.off('change', this.selection);
this.removeSelectable();
}
}
</script>

View File

@@ -0,0 +1,45 @@
import Layout from './DisplayLayout.vue'
import Vue from 'vue'
import objectUtils from '../../api/objects/object-utils.js'
export default function () {
return function (openmct) {
console.log("Installing Layout component...");
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 1;
}
});
}
}

View File

@@ -0,0 +1,215 @@
/*****************************************************************************
* 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/controllers/NotebookController",
"./src/controllers/NewEntryController",
"./src/controllers/SelectSnapshotController",
"./src/actions/NewEntryContextual",
"./src/actions/AnnotateSnapshot",
"./src/directives/MCTSnapshot",
"./src/directives/EntryDnd",
"./res/templates/controls/snapSelect.html",
"./res/templates/controls/embedControl.html",
"./res/templates/annotation.html",
"./res/templates/draggedEntry.html"
], function (
NotebookController,
NewEntryController,
SelectSnapshotController,
newEntryAction,
AnnotateSnapshotAction,
MCTSnapshotDirective,
EntryDndDirective,
snapSelectTemplate,
embedControlTemplate,
annotationTemplate,
draggedEntryTemplate
) {
var installed = false;
function NotebookPlugin() {
return function install(openmct) {
if (installed) {
return;
}
installed = true;
openmct.legacyRegistry.register('notebook', {
name: 'Notebook Plugin',
extensions: {
types: [
{
key: 'notebook',
name: 'Notebook',
cssClass: 'icon-notebook',
description: 'Create and save timestamped notes with embedded object snapshots.',
features: 'creation',
model: {
entries: [],
composition: [],
entryTypes: [],
defaultSort: '-createdOn'
},
properties: [
{
key: 'defaultSort',
name: 'Default Sort',
control: 'select',
options: [
{
name: 'Newest First',
value: "-createdOn",
},
{
name: 'Oldest First',
value: "createdOn"
}
],
cssClass: 'l-inline'
}
]
}
],
actions: [
{
"key": "notebook-new-entry",
"implementation": newEntryAction,
"name": "New Notebook Entry",
"cssClass": "icon-notebook labeled",
"description": "Add a new Notebook entry",
"category": [
"view-control"
],
"depends": [
"$compile",
"$rootScope",
"dialogService",
"notificationService",
"linkService"
],
"priority": "preferred"
},
{
"key": "annotate-snapshot",
"implementation": AnnotateSnapshotAction,
"name": "Annotate Snapshot",
"cssClass": "icon-pencil labeled",
"description": "Annotate embed's snapshot",
"category": "embed",
"depends": [
"dialogService",
"dndService",
"$rootScope"
]
}
],
controllers: [
{
"key": "NewEntryController",
"implementation": NewEntryController,
"depends": ["$scope",
"$rootScope"
]
},
{
"key": "selectSnapshotController",
"implementation": SelectSnapshotController,
"depends": ["$scope",
"$rootScope"
]
}
],
controls: [
{
"key": "snapshot-select",
"template": snapSelectTemplate
},
{
"key": "embed-control",
"template": embedControlTemplate
}
],
templates: [
{
"key": "annotate-snapshot",
"template": annotationTemplate
}
],
directives: [
{
"key": "mctSnapshot",
"implementation": MCTSnapshotDirective,
"depends": [
"$rootScope",
"$document",
"exportImageService",
"dialogService",
"notificationService"
]
},
{
"key": "mctEntryDnd",
"implementation": EntryDndDirective,
"depends": [
"$rootScope",
"$compile",
"dndService",
"typeService",
"notificationService"
]
}
],
representations: [
{
"key": "draggedEntry",
"template": draggedEntryTemplate
}
]
}
});
openmct.legacyRegistry.enable('notebook');
openmct.objectViews.addProvider({
key: 'notebook-vue',
name: 'Notebook View',
cssClass: 'icon-notebook',
canView: function (domainObject) {
return domainObject.type === 'notebook';
},
view: function (domainObject) {
var controller = new NotebookController (openmct, domainObject);
return {
show: controller.show,
destroy: controller.destroy
};
}
});
};
}
return NotebookPlugin;
});

View File

@@ -25,6 +25,5 @@
ng-options="opt.value as opt.name for opt in options"
ng-required="ngRequired"
name="mctControl">
<!-- <option value="" ng-show="!ngModel[field]">- Select One -</option> -->
</select>
</div>

View File

@@ -0,0 +1,32 @@
<div class="c-ne__embed">
<div class="c-ne__embed__snap-thumb"
v-if="embed.snapshot"
v-on:click="openSnapshot">
<img v-bind:src="embed.snapshot.src">
</div>
<div class="c-ne__embed__info">
<div class="c-ne__embed__name">
<a class="c-ne__embed__link"
v-on:click="navigate(embed.type)"
v-bind:class="[embed.cssClass]">{{embed.name}}</a>
<a class="c-ne__embed__context-available icon-arrow-down"
v-on:click="toggleActionMenu"></a>
</div>
<div class="hide-menu hidden">
<div class="menu-element context-menu-wrapper mobile-disable-select">
<div class="menu context-menu">
<ul>
<li v-for="action in actions"
v-bind:class="[action.cssClass]"
v-on:click="action.perform(embed, entry)">
{{ action.name }}
</li>
</ul>
</div>
</div>
</div>
<div class="c-ne__embed__time" v-if="embed.snapshot">
{{formatTime(embed.createdOn, 'YYYY-MM-DD HH:mm:ss')}}
</div>
</div>
</div>

View File

@@ -0,0 +1,35 @@
<li class="c-notebook__entry c-ne has-local-controls"
v-on:drop="dropOnEntry(entry.id)"
v-on:dragover="dragoverOnEntry"
>
<div class="c-ne__time-and-content">
<div class="c-ne__time">
<span>{{formatTime(entry.createdOn, 'YYYY-MM-DD')}}</span>
<span>{{formatTime(entry.createdOn, 'HH:mm:ss')}}</span>
</div>
<div class="c-ne__content">
<!-- TODO: fix styling for c-input-inline when SCSS is merged and remove s-input-inline class here -->
<div class="c-ne__text c-input-inline s-input-inline"
contenteditable="true"
ref="contenteditable"
v-on:blur="textBlur($event, entry.id)"
v-on:focus="textFocus($event, entry.id)"
v-bind:key="entry.id"
v-html="entry.text">
</div>
<div class="c-ne__embeds">
<notebook-embed
v-for="(embed, index) in entry.embeds"
v-bind:embed="embed"
v-bind:entry="entry"
></notebook-embed>
</div>
</div>
</div>
<div class="c-ne__local-controls--hidden">
<a class="c-click-icon icon-trash"
title="Delete this entry"
v-on:click="deleteEntry"></a>
</div>
</li>

View File

@@ -0,0 +1,37 @@
<div class="c-notebook">
<div class="c-notebook__head">
<search class="c-notebook__search"
v-model="entrySearch"
v-on:input="search($event)"
v-on:clear="entrySearch = ''; search($event)"></search>
<div class="c-notebook__controls">
<div class="select c-notebook__controls__time">
<select v-model="showTime">
<option value="0" selected="selected">Show all</option>
<option value="1">Last hour</option>
<option value="8">Last 8 hours</option>
<option value="24">Last 24 hours</option>
</select>
</div>
<div class="select c-notebook__controls__sort">
<select v-model="sortEntries">
<option value="-createdOn" selected="selected">Newest first</option>
<option value="createdOn">Oldest first</option>
</select>
</div>
</div>
</div>
<div class="c-notebook__drag-area icon-plus"
v-on:click="newEntry($event)"
id="newEntry" mct-entry-dnd>
<span class="c-notebook__drag-area__label">To start a new entry, click here or drag and drop any object</span>
</div>
<div class="c-notebook__entries" ng-mouseover="handleActive()">
<ul>
<notebook-entry
v-for="entry in filterBySearch(entries, entrySearch)"
v-bind:entry="entry"
></notebook-entry>
</ul>
</div>
</div>

View File

@@ -0,0 +1,50 @@
<div class="abs overlay l-large-view">
<div class="abs blocker" v-on:click="close"></div>
<div class="abs outer-holder">
<a
class="close icon-x-in-circle"
v-on:click="close">
</a>
<div class="abs inner-holder l-flex-col">
<div class="t-contents flex-elem holder grows">
<div class="t-snapshot abs l-view-header">
<div class="abs object-browse-bar l-flex-row">
<div class="left flex-elem l-flex-row grows">
<div class="object-header flex-elem l-flex-row grows">
<div class="type-icon flex-elem embed-icon holder" v-bind:class="embed.cssClass"></div>
<div class="title-label flex-elem holder flex-can-shrink">{{embed.name}}</div>
</div>
</div>
</div>
</div>
<div class="btn-bar right l-flex-row flex-elem flex-justify-end flex-fixed">
<div class="flex-elem holder flex-can-shrink s-snapshot-datetime">
SNAPSHOT {{formatTime(embed.createdOn, 'YYYY-MM-DD HH:mm:ss')}}
</div>
<a class="s-button icon-pencil" title="Annotate">
<span class="title-label">Annotate</span>
</a>
</div>
<div class="abs object-holder t-image-holder s-image-holder">
<div
class="image-main s-image-main"
v-bind:style="{ backgroundImage: 'url(' + embed.snapshot.src + ')' }">
</div>
</div>
</div>
<div
class="bottom-bar flex-elem holder"
v-on:click="close">
<a class="t-done s-button major">Done</a>
</div>
</div>
</div>
</div>

View File

@@ -26,6 +26,7 @@
define(
["painterro", "zepto"],
function (Painterro, $) {
var annotationStruct = {
title: "Annotate Snapshot",
template: "annotate-snapshot",
@@ -107,9 +108,6 @@ define(
done(true);
}
}).show(snapshot);
$(document.body).find('.ptro-icon-btn').addClass('s-button');
$(document.body).find('.ptro-input').addClass('s-button');
});
}];

View File

@@ -90,7 +90,7 @@ define(
var dialogService = this.dialogService;
var rootScope = this.$rootScope;
rootScope.newEntryText = '';
// Create the overlay element and add it to the document's body
// // Create the overlay element and add it to the document's body
this.$rootScope.selObj = domainObj;
this.$rootScope.selValue = "";
var newScope = rootScope.$new();
@@ -187,7 +187,7 @@ define(
var domainObject = context.domainObject;
if (domainObject) {
if (domainObject.getModel().type === 'Notebook') {
if (domainObject.getModel().type === 'notebook') {
// do not allow in context of a notebook
return false;
} else if (domainObject.getModel().type.includes('imagery')) {

View File

@@ -0,0 +1,130 @@
/*****************************************************************************
* 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(
['zepto'],
function ($) {
function SnapshotAction (exportImageService, dialogService, context) {
this.exportImageService = exportImageService;
this.dialogService = dialogService;
this.domainObject = context.domainObject;
}
SnapshotAction.prototype.perform = function () {
var elementToSnapshot =
$(document.body).find(".overlay .object-holder")[0] ||
$(document.body).find("[key='representation.selected.key']")[0];
$(elementToSnapshot).addClass("s-status-taking-snapshot");
this.exportImageService.exportPNGtoSRC(elementToSnapshot).then(function (blob) {
$(elementToSnapshot).removeClass("s-status-taking-snapshot");
if (blob) {
var reader = new window.FileReader();
reader.readAsDataURL(blob);
reader.onloadend = function () {
this.saveSnapshot(reader.result, blob.type, blob.size);
}.bind(this);
}
}.bind(this));
};
SnapshotAction.prototype.saveSnapshot = function (imageURL, imageType, imageSize) {
var taskForm = this.generateTaskForm(),
domainObject = this.domainObject,
domainObjectId = domainObject.getId(),
cssClass = domainObject.getCapability('type').typeDef.cssClass,
name = domainObject.model.name;
this.dialogService.getDialogResponse(
'overlay-dialog',
taskForm,
function () {
return taskForm.value;
}
).then(function (options) {
var snapshotObject = {
src: imageURL,
type: imageType,
size: imageSize
};
options.notebook.useCapability('mutation', function (model) {
var date = Date.now();
model.entries.push({
id: 'entry-' + date,
createdOn: date,
text: options.entry,
embeds: [{
name: name,
cssClass: cssClass,
type: domainObjectId,
id: 'embed-' + date,
createdOn: date,
snapshot: snapshotObject
}]
});
});
});
};
SnapshotAction.prototype.generateTaskForm = function () {
var taskForm = {
name: "Create a Notebook Entry",
hint: "Please select a Notebook",
sections: [{
rows: [{
name: 'Entry',
key: 'entry',
control: 'textarea',
required: false,
"cssClass": "l-textarea-sm"
},
{
name: 'Save in Notebook',
key: 'notebook',
control: 'locator',
validate: validateLocation
}]
}]
};
var overlayModel = {
title: taskForm.name,
message: 'AHAHAH',
structure: taskForm,
value: {'entry': ""}
};
function validateLocation(newParentObj) {
return newParentObj.model.type === 'notebook';
}
return overlayModel;
};
return SnapshotAction;
}
);

View File

@@ -0,0 +1,198 @@
/*****************************************************************************
* 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([
'moment',
'zepto',
'../utils/SnapshotOverlay',
],
function (
Moment,
$,
SnapshotOverlay
) {
function EmbedController (openmct, domainObject) {
this.openmct = openmct;
this.domainObject = domainObject;
this.objectService = openmct.$injector.get('objectService');
this.navigationService = openmct.$injector.get('navigationService');
this.popupService = openmct.$injector.get('popupService');
this.agentService = openmct.$injector.get('agentService');
this.dialogService = openmct.$injector.get('dialogService');
this.navigate = this.navigate.bind(this);
this.exposedData = this.exposedData.bind(this);
this.exposedMethods = this.exposedMethods.bind(this);
this.toggleActionMenu = this.toggleActionMenu.bind(this);
}
EmbedController.prototype.navigate = function (embedType) {
this.objectService.getObjects([embedType]).then(function (objects) {
this.navigationService.setNavigation(objects[embedType]);
}.bind(this));
};
EmbedController.prototype.openSnapshot = function () {
if (!this.snapshotOverlay) {
this.snapShotOverlay = new SnapshotOverlay(this.embed, this.formatTime);
} else {
this.snapShotOverlay = undefined;
}
};
EmbedController.prototype.formatTime = function (unixTime, timeFormat) {
return Moment(unixTime).format(timeFormat);
};
EmbedController.prototype.findInArray = function (array, id) {
var foundId = -1;
array.forEach(function (element, index) {
if (element.id === id) {
foundId = index;
return;
}
});
return foundId;
};
EmbedController.prototype.actionToMenuDecorator = function (action) {
return {
name: action.getMetadata().name,
cssClass: action.getMetadata().cssClass,
perform: action.perform
};
};
EmbedController.prototype.populateActionMenu = function (objectService, actionService) {
return function () {
var self = this;
objectService.getObjects([self.embed.type]).then(function (resp) {
var domainObject = resp[self.embed.type],
previewAction = actionService.getActions({key: 'mct-preview-action', domainObject: domainObject})[0];
self.actions.push(self.actionToMenuDecorator(previewAction));
});
};
};
EmbedController.prototype.removeEmbedAction = function () {
var self = this;
return {
name: 'Remove Embed',
cssClass: 'icon-trash',
perform: function (embed, entry) {
var entryPosition = self.findInArray(self.domainObject.entries, entry.id),
embedPosition = self.findInArray(entry.embeds, embed.id);
var warningDialog = self.dialogService.showBlockingMessage({
severity: "error",
title: "This action will permanently delete this embed. Do you wish to continue?",
options: [{
label: "OK",
callback: function () {
entry.embeds.splice(embedPosition, 1);
var dirString = 'entries[' + entryPosition + '].embeds';
self.openmct.objects.mutate(self.domainObject, dirString, entry.embeds);
warningDialog.dismiss();
}
},{
label: "Cancel",
callback: function () {
warningDialog.dismiss();
}
}]
});
}
};
};
EmbedController.prototype.toggleActionMenu = function (event) {
event.preventDefault();
var body = $(document.body),
container = $(event.target.parentElement.parentElement),
initiatingEvent = this.agentService.isMobile() ?
'touchstart' : 'mousedown',
menu = container.find('.menu-element'),
dismissExistingMenu;
// Remove the context menu
function dismiss() {
container.find('.hide-menu').append(menu);
body.off(initiatingEvent, dismiss);
menu.off(initiatingEvent, menuClickHandler);
dismissExistingMenu = undefined;
}
function menuClickHandler(e) {
e.stopPropagation();
window.setTimeout(dismiss, 300);
}
// Dismiss any menu which was already showing
if (dismissExistingMenu) {
dismissExistingMenu();
}
// ...and record the presence of this menu.
dismissExistingMenu = dismiss;
this.popupService.display(menu, [event.pageX,event.pageY], {
marginX: 0,
marginY: -50
});
// Stop propagation so that clicks or touches on the menu do not close the menu
menu.on(initiatingEvent, menuClickHandler);
body.on(initiatingEvent, dismiss);
};
EmbedController.prototype.exposedData = function () {
return {
actions: [this.removeEmbedAction()],
showActionMenu: false
};
};
EmbedController.prototype.exposedMethods = function () {
var self = this;
return {
navigate: self.navigate,
openSnapshot: self.openSnapshot,
formatTime: self.formatTime,
toggleActionMenu: self.toggleActionMenu,
actionToMenuDecorator: self.actionToMenuDecorator
};
};
return EmbedController;
});

View File

@@ -0,0 +1,150 @@
/*****************************************************************************
* 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([
'moment'
],
function (
Moment
) {
function EntryController (openmct, domainObject) {
this.openmct = openmct;
this.domainObject = domainObject;
this.dndService = this.openmct.$injector.get('dndService');
this.dialogService = this.openmct.$injector.get('dialogService');
this.currentEntryValue = '';
this.exposedMethods = this.exposedMethods.bind(this);
this.exposedData = this.exposedData.bind(this);
}
EntryController.prototype.entryPosById = function (entryId) {
var foundId = -1;
this.domainObject.entries.forEach(function (element, index) {
if (element.id === entryId) {
foundId = index;
return;
}
});
return foundId;
};
EntryController.prototype.textFocus = function ($event) {
if ($event.target) {
this.currentEntryValue = $event.target.innerText;
} else {
$event.target.innerText = '';
}
};
EntryController.prototype.textBlur = function ($event, entryId) {
if ($event.target) {
var entryPos = this.entryPosById(entryId);
if (this.currentEntryValue !== $event.target.innerText) {
this.openmct.objects.mutate(this.domainObject, 'entries[' + entryPos + '].text', $event.target.innerText);
}
}
};
EntryController.prototype.formatTime = function (unixTime, timeFormat) {
return Moment(unixTime).format(timeFormat);
};
EntryController.prototype.deleteEntry = function () {
var entryPos = this.entryPosById(this.entry.id),
domainObject = this.domainObject,
openmct = this.openmct;
if (entryPos !== -1) {
var errorDialog = this.dialogService.showBlockingMessage({
severity: "error",
title: "This action will permanently delete this Notebook entry. Do you wish to continue?",
options: [{
label: "OK",
callback: function () {
domainObject.entries.splice(entryPos, 1);
openmct.objects.mutate(domainObject, 'entries', domainObject.entries);
errorDialog.dismiss();
}
},{
label: "Cancel",
callback: function () {
errorDialog.dismiss();
}
}]
});
}
};
EntryController.prototype.dropOnEntry = function (entryId) {
var selectedObject = this.dndService.getData('mct-domain-object'),
selectedObjectId = selectedObject.getId(),
selectedModel = selectedObject.getModel(),
cssClass = selectedObject.getCapability('type').typeDef.cssClass,
entryPos = this.entryPosById(entryId),
currentEntryEmbeds = this.domainObject.entries[entryPos].embeds,
newEmbed = {
type: selectedObjectId,
id: '' + Date.now(),
cssClass: cssClass,
name: selectedModel.name,
snapshot: ''
};
currentEntryEmbeds.push(newEmbed);
this.openmct.objects.mutate(this.domainObject, 'entries[' + entryPos + '].embeds', currentEntryEmbeds);
};
EntryController.prototype.dragoverOnEntry = function () {
};
EntryController.prototype.exposedData = function () {
return {
openmct: this.openmct,
domainObject: this.domainObject,
dndService: this.dndService,
dialogService: this.dialogService,
currentEntryValue: this.currentEntryValue
};
};
EntryController.prototype.exposedMethods = function () {
return {
entryPosById: this.entryPosById,
textFocus: this.textFocus,
textBlur: this.textBlur,
formatTime: this.formatTime,
deleteEntry: this.deleteEntry,
dropOnEntry: this.dropOnEntry,
dragoverOnEntry: this.dragoverOnEntry
};
};
return EntryController;
});

View File

@@ -31,8 +31,7 @@ define(
$scope.snapshot = undefined;
$scope.snapToggle = true;
$scope.entryText = '';
var annotateAction = $rootScope.selObj.getCapability('action').getActions(
{category: 'embed'})[1];
var annotateAction = $rootScope.selObj.getCapability('action').getActions({key: 'annotate-snapshot'})[0];
$scope.$parent.$parent.ngModel[$scope.$parent.$parent.field] = $rootScope.selObj;
$scope.objectName = $rootScope.selObj.getModel().name;

View File

@@ -0,0 +1,177 @@
/*****************************************************************************
* 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([
'vue',
'./EntryController',
'./EmbedController',
'../../res/templates/notebook.html',
'../../res/templates/entry.html',
'../../res/templates/embed.html',
'../../../../ui/components/controls/search.vue'
],
function (
Vue,
EntryController,
EmbedController,
NotebookTemplate,
EntryTemplate,
EmbedTemplate,
search
) {
function NotebookController(openmct, domainObject) {
this.openmct = openmct;
this.domainObject = domainObject;
this.entrySearch = '';
this.objectService = openmct.$injector.get('objectService');
this.actionService = openmct.$injector.get('actionService');
this.show = this.show.bind(this);
this.destroy = this.destroy.bind(this);
this.newEntry = this.newEntry.bind(this);
this.entryPosById = this.entryPosById.bind(this);
}
NotebookController.prototype.initializeVue = function (container) {
var self = this,
entryController = new EntryController(this.openmct, this.domainObject),
embedController = new EmbedController(this.openmct, this.domainObject);
this.container = container;
var notebookEmbed = {
props:['embed', 'entry'],
template: EmbedTemplate,
data: embedController.exposedData,
methods: embedController.exposedMethods(),
beforeMount: embedController.populateActionMenu(self.objectService, self.actionService)
};
var entryComponent = {
props:['entry'],
template: EntryTemplate,
components: {
'notebook-embed': notebookEmbed
},
data: entryController.exposedData,
methods: entryController.exposedMethods(),
mounted: self.focusOnEntry
};
var notebookVue = Vue.extend({
template: NotebookTemplate,
components: {
'notebook-entry': entryComponent,
'search': search.default
},
data: function () {
return {
entrySearch: self.entrySearch,
showTime: '0',
sortEntries: '-createdOn',
entries: self.domainObject.entries,
currentEntryValue: ''
};
},
methods: {
search: function (event) {
if (event.target.value) {
this.entrySearch = event.target.value;
}
},
newEntry: self.newEntry,
filterBySearch: self.filterBySearch
}
});
this.NotebookVue = new notebookVue();
container.appendChild(this.NotebookVue.$mount().$el);
};
NotebookController.prototype.newEntry = function (event) {
var entries = this.domainObject.entries,
lastEntryIndex = entries.length - 1,
lastEntry = entries[lastEntryIndex],
date = Date.now();
if (lastEntry === undefined || lastEntry.text || lastEntry.embeds.length) {
var createdEntry = {'id': 'entry-' + date, 'createdOn': date, 'embeds':[]};
entries.push(createdEntry);
this.openmct.objects.mutate(this.domainObject, 'entries', entries);
} else {
lastEntry.createdOn = date;
this.openmct.objects.mutate(this.domainObject, 'entries[entries.length-1]', lastEntry);
this.focusOnEntry.bind(this.NotebookVue.$children[lastEntryIndex])();
}
this.entrySearch = '';
};
NotebookController.prototype.entryPosById = function (entryId) {
var foundId = -1;
this.domainObject.entries.forEach(function (element, index) {
if (element.id === entryId) {
foundId = index;
return;
}
});
return foundId;
};
NotebookController.prototype.focusOnEntry = function () {
if (!this.entry.text) {
this.$refs.contenteditable.focus();
}
};
NotebookController.prototype.filterBySearch = function (entryArray, filterString) {
if (filterString) {
var lowerCaseFilterString = filterString.toLowerCase();
return entryArray.filter(function (entry) {
if (entry.text) {
return entry.text.toLowerCase().includes(lowerCaseFilterString);
} else {
return false;
}
});
} else {
return entryArray;
}
};
NotebookController.prototype.show = function (container) {
this.initializeVue(container);
};
NotebookController.prototype.destroy = function (container) {
this.NotebookVue.$destroy(true);
};
return NotebookController;
});

View File

@@ -31,32 +31,34 @@ define(['zepto'], function ($) {
var selectedModel = selectedObject.getModel();
var cssClass = selectedObject.getCapability('type').typeDef.cssClass;
var entryId = -1;
var embedId = -1;
$scope.clearSearch();
if ($element[0].id === 'newEntry') {
entryId = $scope.domainObject.model.entries.length;
embedId = 0;
var lastEntry = $scope.domainObject.model.entries[entryId - 1];
if (lastEntry === undefined || lastEntry.text || lastEntry.embeds) {
$scope.domainObject.useCapability('mutation', function (model) {
model.entries.push({'createdOn': +Date.now(),
'id': +Date.now(),
'embeds': [{'type': selectedObject.getId(),
'id': '' + Date.now(),
'cssClass': cssClass,
'name': selectedModel.name,
'snapshot': ''
}]
});
'id': +Date.now(),
'embeds': [{'type': selectedObject.getId(),
'id': '' + Date.now(),
'cssClass': cssClass,
'name': selectedModel.name,
'snapshot': ''
}]
});
});
}else {
$scope.domainObject.useCapability('mutation', function (model) {
model.entries[entryId - 1] =
{'createdOn': +Date.now(),
'embeds': [{'type': selectedObject.getId(),
'id': '' + Date.now(),
'cssClass': cssClass,
'name': selectedModel.name,
'snapshot': ''
}]
'embeds': [{'type': selectedObject.getId(),
'id': '' + Date.now(),
'cssClass': cssClass,
'name': selectedModel.name,
'snapshot': ''
}]
};
});
}
@@ -75,13 +77,15 @@ define(['zepto'], function ($) {
$scope.domainObject.useCapability('mutation', function (model) {
model.entries[entryId].embeds.push({'type': selectedObject.getId(),
'id': '' + Date.now(),
'cssClass': cssClass,
'name': selectedModel.name,
'snapshot': ''
});
'id': '' + Date.now(),
'cssClass': cssClass,
'name': selectedModel.name,
'snapshot': ''
});
});
embedId = $scope.domainObject.model.entries[entryId].embeds.length - 1;
if (selectedObject) {
e.preventDefault();

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,35 +20,47 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
/**
* This bundle implements object types and associated views for
* display-building.
*/
define(
[],
function () {
/**
* The LayoutNotebookController is responsible for supporting the
* notebook feature creation on theLayout view.
**/
define([
'vue',
'../../res/templates/viewSnapshot.html'
], function (
Vue,
snapshotOverlayTemplate
) {
function SnapshotOverlay (embedObject, formatTime) {
this.embedObject = embedObject;
function LayoutNotebookController($scope) {
$scope.hasNotebookAction = undefined;
$scope.newNotebook = undefined;
var actions = $scope.domainObject.getCapability('action');
var notebookAction = actions.getActions({'key': 'notebook-new-entry'});
if (notebookAction.length > 0) {
$scope.hasNotebookAction = true;
$scope.newNotebook = function () {
notebookAction[0].perform();
this.snapshotOverlayVue = new Vue({
template: snapshotOverlayTemplate,
data: function () {
return {
embed: embedObject
};
},
methods: {
close: this.close.bind(this),
formatTime: formatTime
}
}
});
return LayoutNotebookController;
this.open();
}
);
SnapshotOverlay.prototype.open = function () {
this.overlay = document.createElement('div');
this.overlay.classList.add('abs');
document.body.appendChild(this.overlay);
this.overlay.appendChild(this.snapshotOverlayVue.$mount().$el);
};
SnapshotOverlay.prototype.close = function (event) {
event.stopPropagation();
this.snapshotOverlayVue.$destroy();
this.overlay.parentNode.removeChild(this.overlay);
};
return SnapshotOverlay;
});

View File

@@ -27,14 +27,15 @@ define([
'./autoflow/AutoflowTabularPlugin',
'./timeConductor/plugin',
'../../example/imagery/plugin',
'../../platform/features/notebook/bundle',
'../../platform/import-export/bundle',
'./summaryWidget/plugin',
'./URLIndicatorPlugin/URLIndicatorPlugin',
'./telemetryMean/plugin',
'./plot/plugin',
'./telemetryTable/plugin',
'./staticRootPlugin/plugin'
'./staticRootPlugin/plugin',
'./notebook/plugin',
'./displayLayout/plugin'
], function (
_,
UTCTimeSystem,
@@ -42,19 +43,19 @@ define([
AutoflowPlugin,
TimeConductorPlugin,
ExampleImagery,
Notebook,
ImportExport,
SummaryWidget,
URLIndicatorPlugin,
TelemetryMean,
PlotPlugin,
TelemetryTablePlugin,
StaticRootPlugin
StaticRootPlugin,
Notebook,
DisplayLayoutPlugin
) {
var bundleMap = {
LocalStorage: 'platform/persistence/local',
MyItems: 'platform/features/my-items',
Notebook: 'platform/features/notebook'
MyItems: 'platform/features/my-items'
};
var plugins = _.mapValues(bundleMap, function (bundleName, pluginName) {
@@ -155,10 +156,12 @@ define([
plugins.ExampleImagery = ExampleImagery;
plugins.Plot = PlotPlugin;
plugins.TelemetryTable = TelemetryTablePlugin;
plugins.SummaryWidget = SummaryWidget;
plugins.TelemetryMean = TelemetryMean;
plugins.URLIndicator = URLIndicatorPlugin;
plugins.Notebook = Notebook;
plugins.DisplayLayout = DisplayLayoutPlugin.default;
return plugins;
});

View File

@@ -15,7 +15,8 @@ define([
function SummaryWidgetViewProvider(openmct) {
return {
key: 'summary-widget-viewer',
name: 'Widget View',
name: 'Summary View',
cssClass: 'icon-summary-widget',
canView: function (domainObject) {
return domainObject.type === 'summary-widget';
},

View File

@@ -1,87 +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([
'lodash',
'vue',
'./table-configuration.html',
'./TelemetryTableConfiguration'
],function (
_,
Vue,
TableConfigurationTemplate,
TelemetryTableConfiguration
) {
return function TableConfigurationComponent(domainObject, openmct) {
const tableConfiguration = new TelemetryTableConfiguration(domainObject, openmct);
let unlisteners = [];
return new Vue({
template: TableConfigurationTemplate,
data() {
return {
headers: {},
configuration: tableConfiguration.getConfiguration()
}
},
methods: {
updateHeaders(headers) {
this.headers = headers;
},
toggleColumn(key) {
let isHidden = this.configuration.hiddenColumns[key] === true;
this.configuration.hiddenColumns[key] = !isHidden;
tableConfiguration.updateConfiguration(this.configuration);
},
addObject(domainObject) {
tableConfiguration.addColumnsForObject(domainObject, true);
this.updateHeaders(tableConfiguration.getAllHeaders());
},
removeObject(objectIdentifier) {
tableConfiguration.removeColumnsForObject(objectIdentifier, true);
this.updateHeaders(tableConfiguration.getAllHeaders());
}
},
mounted() {
let compositionCollection = openmct.composition.get(domainObject);
compositionCollection.load()
.then((composition) => {
tableConfiguration.addColumnsForAllObjects(composition);
this.updateHeaders(tableConfiguration.getAllHeaders());
compositionCollection.on('add', this.addObject);
unlisteners.push(compositionCollection.off.bind(compositionCollection, 'add', this.addObject));
compositionCollection.on('remove', this.removeObject);
unlisteners.push(compositionCollection.off.bind(compositionCollection, 'remove', this.removeObject));
});
},
destroyed() {
tableConfiguration.destroy();
unlisteners.forEach((unlisten) => unlisten());
}
});
}
});

View File

@@ -22,11 +22,16 @@
define([
'../../api/objects/object-utils',
'./TableConfigurationComponent'
'./components/table-configuration.vue',
'./TelemetryTableConfiguration',
'vue'
], function (
objectUtils,
TableConfigurationComponent
TableConfigurationComponent,
TelemetryTableConfiguration,
Vue
) {
function TableConfigurationViewProvider(openmct) {
let instantiateService;
@@ -51,27 +56,38 @@ define([
return instantiateService(model, id);
}
return {
key: 'table-configuration',
name: 'Telemetry Table Configuration',
canView: function (selection) {
if (selection.length === 0) {
return false;
}
let object = selection[0].context.item;
return selection.length > 0 &&
object.type === 'table' &&
return object.type === 'table' &&
isBeingEdited(object);
},
view: function (selection) {
let component;
let domainObject = selection[0].context.item;
const tableConfiguration = new TelemetryTableConfiguration(domainObject, openmct);
return {
show: function (element) {
component = TableConfigurationComponent(domainObject, openmct);
element.appendChild(component.$mount().$el);
},
component = new Vue({
provide: {
openmct,
tableConfiguration
},
components: {
TableConfiguration: TableConfigurationComponent.default
},
template: '<table-configuration></table-configuration>',
el: element
});
},
destroy: function (element) {
component.$destroy();
element.removeChild(component.$el);
component = undefined;
}
}
@@ -82,4 +98,4 @@ define([
}
}
return TableConfigurationViewProvider;
});
});

View File

@@ -36,12 +36,12 @@ define([
TelemetryTableConfiguration
) {
class TelemetryTable extends EventEmitter {
constructor(domainObject, rowCount, openmct) {
constructor(domainObject, openmct) {
super();
this.domainObject = domainObject;
this.openmct = openmct;
this.rowCount = rowCount;
this.rowCount = 100;
this.subscriptions = {};
this.tableComposition = undefined;
this.telemetryObjects = [];
@@ -85,10 +85,10 @@ define([
this.configuration.addColumnsForAllObjects(composition);
composition.forEach(this.addTelemetryObject);
this.tableComposition.on('add', this.addTelemetryObject);
this.tableComposition.on('remove', this.removeTelemetryObject);
});
});
}
}
@@ -158,7 +158,7 @@ define([
getColumnMapForObject(objectKeyString) {
let columns = this.configuration.getColumns();
return columns[objectKeyString].reduce((map, column) => {
map[column.getKey()] = column;
return map;
@@ -189,7 +189,7 @@ define([
this.filteredRows.destroy();
Object.keys(this.subscriptions).forEach(this.unsubscribe, this);
this.openmct.time.off('bounds', this.refreshData);
if (this.tableComposition !== undefined) {
this.tableComposition.off('add', this.addTelemetryObject);
this.tableComposition.off('remove', this.removeTelemetryObject);
@@ -198,4 +198,4 @@ define([
}
return TelemetryTable;
});
});

View File

@@ -1,315 +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([
'lodash',
'vue',
'./telemetry-table.html',
'./TelemetryTable',
'./TelemetryTableRowComponent',
'../../exporters/CSVExporter'
],function (
_,
Vue,
TelemetryTableTemplate,
TelemetryTable,
TelemetryTableRowComponent,
CSVExporter
) {
const VISIBLE_ROW_COUNT = 100;
const ROW_HEIGHT = 17;
const RESIZE_POLL_INTERVAL = 200;
const AUTO_SCROLL_TRIGGER_HEIGHT = 20;
return function TelemetryTableComponent(domainObject, openmct) {
const csvExporter = new CSVExporter();
const table = new TelemetryTable(domainObject, VISIBLE_ROW_COUNT, openmct);
let processingScroll = false;
let updatingView = false;
return new Vue({
template: TelemetryTableTemplate,
components: {
'telemetry-table-row': TelemetryTableRowComponent
},
data() {
return {
headers: {},
configuration: table.configuration.getConfiguration(),
headersCount: 0,
visibleRows: [],
columnWidths: [],
sizingRows: {},
rowHeight: ROW_HEIGHT,
scrollOffset: 0,
totalHeight: 0,
totalWidth: 0,
rowOffset: 0,
autoScroll: true,
sortOptions: {},
filters: {},
loading: false,
scrollable: undefined,
tableEl: undefined,
headersHolderEl: undefined,
calcTableWidth: '100%'
}
},
methods: {
updateVisibleRows() {
let start = 0;
let end = VISIBLE_ROW_COUNT;
let filteredRows = table.filteredRows.getRows();
let filteredRowsLength = filteredRows.length;
this.totalHeight = this.rowHeight * filteredRowsLength - 1;
if (filteredRowsLength < VISIBLE_ROW_COUNT) {
end = filteredRowsLength;
} else {
let firstVisible = this.calculateFirstVisibleRow();
let lastVisible = this.calculateLastVisibleRow();
let totalVisible = lastVisible - firstVisible;
let numberOffscreen = VISIBLE_ROW_COUNT - totalVisible;
start = firstVisible - Math.floor(numberOffscreen / 2);
end = lastVisible + Math.ceil(numberOffscreen / 2);
if (start < 0) {
start = 0;
end = Math.min(VISIBLE_ROW_COUNT, filteredRowsLength);
} else if (end >= filteredRowsLength) {
end = filteredRowsLength;
start = end - VISIBLE_ROW_COUNT + 1;
}
}
this.rowOffset = start;
this.visibleRows = filteredRows.slice(start, end);
},
calculateFirstVisibleRow() {
return Math.floor(this.scrollable.scrollTop / this.rowHeight);
},
calculateLastVisibleRow() {
let bottomScroll = this.scrollable.scrollTop + this.scrollable.offsetHeight;
return Math.floor(bottomScroll / this.rowHeight);
},
updateHeaders() {
let headers = table.configuration.getVisibleHeaders();
this.headers = headers;
this.headersCount = Object.values(headers).length;
Vue.nextTick().then(this.calculateColumnWidths);
},
setSizingTableWidth() {
let scrollW = this.scrollable.offsetWidth - this.scrollable.clientWidth;
if (scrollW && scrollW > 0) {
this.calcTableWidth = 'calc(100% - ' + scrollW + 'px)';
}
},
calculateColumnWidths() {
let columnWidths = [];
let totalWidth = 0;
let sizingRowEl = this.sizingTable.children[0];
let sizingCells = Array.from(sizingRowEl.children);
sizingCells.forEach((cell) => {
let columnWidth = cell.offsetWidth;
columnWidths.push(columnWidth + 'px');
totalWidth += columnWidth;
});
this.columnWidths = columnWidths;
this.totalWidth = totalWidth;
},
sortBy(columnKey) {
// If sorting by the same column, flip the sort direction.
if (this.sortOptions.key === columnKey) {
if (this.sortOptions.direction === 'asc') {
this.sortOptions.direction = 'desc';
} else {
this.sortOptions.direction = 'asc';
}
} else {
this.sortOptions = {
key: columnKey,
direction: 'asc'
}
}
table.filteredRows.sortBy(this.sortOptions);
},
scroll() {
if (!processingScroll) {
processingScroll = true;
requestAnimationFrame(()=> {
this.updateVisibleRows();
this.synchronizeScrollX();
if (this.shouldSnapToBottom()) {
this.autoScroll = true;
} else {
// If user scrolls away from bottom, disable auto-scroll.
// Auto-scroll will be re-enabled if user scrolls to bottom again.
this.autoScroll = false;
}
processingScroll = false;
});
}
},
shouldSnapToBottom() {
return this.scrollable.scrollTop >= (this.scrollable.scrollHeight - this.scrollable.offsetHeight - AUTO_SCROLL_TRIGGER_HEIGHT);
},
scrollToBottom() {
this.scrollable.scrollTop = this.scrollable.scrollHeight;
},
synchronizeScrollX() {
this.headersHolderEl.scrollLeft = this.scrollable.scrollLeft;
},
filterChanged(columnKey) {
table.filteredRows.setColumnFilter(columnKey, this.filters[columnKey]);
},
clearFilter(columnKey) {
this.filters[columnKey] = '';
table.filteredRows.setColumnFilter(columnKey, '');
},
rowsAdded(rows) {
let sizingRow;
if (Array.isArray(rows)) {
sizingRow = rows[0];
} else {
sizingRow = rows;
}
if (!this.sizingRows[sizingRow.objectKeyString]) {
this.sizingRows[sizingRow.objectKeyString] = sizingRow;
Vue.nextTick().then(this.calculateColumnWidths);
}
if (!updatingView) {
updatingView = true;
requestAnimationFrame(()=> {
this.updateVisibleRows();
if (this.autoScroll) {
Vue.nextTick().then(this.scrollToBottom);
}
updatingView = false;
});
}
},
rowsRemoved(rows) {
if (!updatingView) {
updatingView = true;
requestAnimationFrame(()=> {
this.updateVisibleRows();
updatingView = false;
});
}
},
exportAsCSV() {
const justTheData = table.filteredRows.getRows()
.map(row => row.getFormattedDatum());
const headers = Object.keys(this.headers);
csvExporter.export(justTheData, {
filename: table.domainObject.name + '.csv',
headers: headers
});
},
outstandingRequests(loading) {
this.loading = loading;
},
calculateTableSize() {
this.setSizingTableWidth();
Vue.nextTick().then(this.calculateColumnWidths);
},
pollForResize() {
let el = this.$el;
let width = el.clientWidth;
let height = el.clientHeight;
this.resizePollHandle = setInterval(() => {
if (el.clientWidth !== width || el.clientHeight !== height) {
this.calculateTableSize();
width = el.clientWidth;
height = el.clientHeight;
}
}, RESIZE_POLL_INTERVAL);
},
updateConfiguration(configuration) {
this.configuration = configuration;
this.updateHeaders();
},
addObject() {
this.updateHeaders();
},
removeObject(objectIdentifier) {
let objectKeyString = openmct.objects.makeKeyString(objectIdentifier);
delete this.sizingRows[objectKeyString];
this.updateHeaders();
}
},
created() {
this.filterChanged = _.debounce(this.filterChanged, 500);
},
mounted() {
table.on('object-added', this.addObject);
table.on('object-removed', this.removeObject);
table.on('outstanding-requests', this.outstandingRequests);
table.filteredRows.on('add', this.rowsAdded);
table.filteredRows.on('remove', this.rowsRemoved);
table.filteredRows.on('sort', this.updateVisibleRows);
table.filteredRows.on('filter', this.updateVisibleRows);
//Default sort
this.sortOptions = table.filteredRows.sortBy();
this.scrollable = this.$el.querySelector('.t-scrolling');
this.sizingTable = this.$el.querySelector('.js-sizing-table');
this.headersHolderEl = this.$el.querySelector('.mct-table-headers-w');
table.configuration.on('change', this.updateConfiguration);
this.calculateTableSize();
this.pollForResize();
table.initialize();
},
destroyed() {
table.off('object-added', this.addObject);
table.off('object-removed', this.removeObject);
table.off('outstanding-requests', this.outstandingRequests);
table.filteredRows.off('add', this.rowsAdded);
table.filteredRows.off('remove', this.rowsRemoved);
table.filteredRows.off('sort', this.updateVisibleRows);
table.filteredRows.off('filter', this.updateVisibleRows);
table.configuration.off('change', this.updateConfiguration);
clearInterval(this.resizePollHandle);
table.configuration.destroy();
table.destroy();
}
});
}
});

View File

@@ -30,12 +30,11 @@ define([], function () {
this.objectKeyString = objectKeyString;
}
getFormattedDatum() {
return Object.values(this.columns)
.reduce((formattedDatum, column) => {
formattedDatum[column.getKey()] = this.getFormattedValue(column.getKey());
return formattedDatum;
}, {});
getFormattedDatum(headers) {
return Object.keys(headers).reduce((formattedDatum, columnKey) => {
formattedDatum[columnKey] = this.getFormattedValue(columnKey);
return formattedDatum;
}, {});
}
getFormattedValue(key) {

View File

@@ -1,90 +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([
'./telemetry-table-row.html',
],function (
TelemetryTableRowTemplate
) {
return {
template: TelemetryTableRowTemplate,
data: function () {
return {
rowTop: (this.rowOffset + this.rowIndex) * this.rowHeight + 'px',
formattedRow: this.row.getFormattedDatum(),
rowLimitClass: this.row.getRowLimitClass(),
cellLimitClasses: this.row.getCellLimitClasses()
}
},
props: {
headers: {
type: Object,
required: true
},
row: {
type: Object,
required: true
},
columnWidths: {
type: Array,
required: false,
default: [],
},
rowIndex: {
type: Number,
required: false,
default: undefined
},
rowOffset: {
type: Number,
required: false,
default: 0
},
rowHeight: {
type: Number,
required: false,
default: 0
},
configuration: {
type: Object,
required: true
}
},
methods: {
calculateRowTop: function (rowOffset) {
this.rowTop = (rowOffset + this.rowIndex) * this.rowHeight + 'px';
},
formatRow: function (row) {
this.formattedRow = row.getFormattedDatum();
this.rowLimitClass = row.getRowLimitClass();
this.cellLimitClasses = row.getCellLimitClasses();
}
},
watch: {
rowOffset: 'calculateRowTop',
row: {
handler: 'formatRow',
deep: false
}
}
};
});

View File

@@ -20,25 +20,49 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(['./TelemetryTableComponent'], function (TelemetryTableComponent) {
define([
'./components/table.vue',
'../../exporters/CSVExporter',
'./TelemetryTable',
'vue'
], function (
TableComponent,
CSVExporter,
TelemetryTable,
Vue
) {
function TelemetryTableViewProvider(openmct) {
return {
key: 'table',
name: 'Telemetry Table',
editable: true,
cssClass: 'icon-tabular-realtime',
editable: function(domainObject) {
return domainObject.type === 'table';
},
canView: function (domainObject) {
return domainObject.type === 'table' || domainObject.hasOwnProperty('telemetry');
},
view: function (domainObject) {
let csvExporter = new CSVExporter.default();
let table = new TelemetryTable(domainObject, openmct);
let component;
return {
show: function (element) {
component = new TelemetryTableComponent(domainObject, openmct);
element.appendChild(component.$mount().$el);
},
component = new Vue({
components: {
TableComponent: TableComponent.default,
},
provide: {
openmct,
csvExporter,
table
},
el: element,
template: '<table-component></table-component>'
});
},
destroy: function (element) {
component.$destroy();
element.removeChild(component.$el);
component = undefined;
}
}
@@ -49,4 +73,4 @@ define(['./TelemetryTableComponent'], function (TelemetryTableComponent) {
}
}
return TelemetryTableViewProvider;
});
});

View File

@@ -82,8 +82,7 @@ define(
// Going to check for duplicates. Bound the search problem to
// items around the given time. Use sortedIndex because it
// employs a binary search which is O(log n). Can use binary search
// based on time stamp because the array is guaranteed ordered due
// to sorted insertion.
// because the array is guaranteed ordered due to sorted insertion.
let startIx = this.sortedIndex(this.rows, row);
let endIx = undefined;
@@ -113,26 +112,49 @@ define(
* @private
*/
sortedIndex(rows, testRow, lodashFunction) {
if (this.rows.length === 0) {
return 0;
}
const sortOptionsKey = this.sortOptions.key;
const testRowValue = testRow.datum[sortOptionsKey];
const firstValue = this.rows[0].datum[sortOptionsKey];
const lastValue = this.rows[this.rows.length - 1].datum[sortOptionsKey];
lodashFunction = lodashFunction || _.sortedIndex;
if (this.sortOptions.direction === 'asc') {
return lodashFunction(rows, testRow, (thisRow) => {
return thisRow.datum[sortOptionsKey];
});
if (testRowValue > lastValue) {
return this.rows.length;
} else if (testRowValue === lastValue) {
return this.rows.length - 1;
} else if (testRowValue <= firstValue) {
return 0;
} else {
return lodashFunction(rows, testRow, (thisRow) => {
return thisRow.datum[sortOptionsKey];
});
}
} else {
const testRowValue = testRow.datum[this.sortOptions.key];
// Use a custom comparison function to support descending sort.
return lodashFunction(rows, testRow, (thisRow) => {
const thisRowValue = thisRow.datum[sortOptionsKey];
if (testRowValue === thisRowValue) {
return EQUAL;
} else if (testRowValue < thisRowValue) {
return LESS_THAN;
} else {
return GREATER_THAN;
}
});
if (testRowValue >= firstValue) {
return 0;
} else if (testRowValue < lastValue) {
return this.rows.length;
} else if (testRowValue === lastValue) {
return this.rows.length - 1;
} else {
// Use a custom comparison function to support descending sort.
return lodashFunction(rows, testRow, (thisRow) => {
const thisRowValue = thisRow.datum[sortOptionsKey];
if (testRowValue === thisRowValue) {
return EQUAL;
} else if (testRowValue < thisRowValue) {
return LESS_THAN;
} else {
return GREATER_THAN;
}
});
}
}
}

View File

@@ -0,0 +1,68 @@
<template>
<div class="grid-properties">
<!--form class="form" -->
<ul class="l-inspector-part">
<h2>Table Columns</h2>
<li class="grid-row" v-for="(title, key) in headers">
<div class="grid-cell label" title="Show or Hide Column"><label :for="key + 'ColumnControl'">{{title}}</label></div>
<div class="grid-cell value"><input type="checkbox" :id="key + 'ColumnControl'" :checked="configuration.hiddenColumns[key] !== true" @change="toggleColumn(key)"></div>
</li>
</ul>
<!--/form -->
</div>
</template>
<style>
</style>
<script>
export default {
inject: ['tableConfiguration', 'openmct'],
data() {
return {
headers: {},
configuration: this.tableConfiguration.getConfiguration()
}
},
methods: {
updateHeaders(headers) {
this.headers = headers;
},
toggleColumn(key) {
let isHidden = this.configuration.hiddenColumns[key] === true;
this.configuration.hiddenColumns[key] = !isHidden;
this.tableConfiguration.updateConfiguration(this.configuration);
},
addObject(domainObject) {
this.tableConfiguration.addColumnsForObject(domainObject, true);
this.updateHeaders(this.tableConfiguration.getAllHeaders());
},
removeObject(objectIdentifier) {
this.tableConfiguration.removeColumnsForObject(objectIdentifier, true);
this.updateHeaders(this.tableConfiguration.getAllHeaders());
}
},
mounted() {
this.unlisteners = [];
let compositionCollection = this.openmct.composition.get(this.tableConfiguration.domainObject);
compositionCollection.load()
.then((composition) => {
this.tableConfiguration.addColumnsForAllObjects(composition);
this.updateHeaders(this.tableConfiguration.getAllHeaders());
compositionCollection.on('add', this.addObject);
this.unlisteners.push(compositionCollection.off.bind(compositionCollection, 'add', this.addObject));
compositionCollection.on('remove', this.removeObject);
this.unlisteners.push(compositionCollection.off.bind(compositionCollection, 'remove', this.removeObject));
});
},
destroyed() {
this.tableConfiguration.destroy();
this.unlisteners.forEach((unlisten) => unlisten());
}
}
</script>

View File

@@ -0,0 +1,76 @@
<template>
<tr :style="{ top: rowTop }" :class="rowLimitClass">
<td v-for="(title, key, headerIndex) in headers"
:style="{ width: columnWidths[headerIndex], 'max-width': columnWidths[headerIndex]}"
:title="formattedRow[key]"
:class="cellLimitClasses[key]">{{formattedRow[key]}}</td>
</tr>
</template>
<style>
</style>
<script>
export default {
data: function () {
return {
rowTop: (this.rowOffset + this.rowIndex) * this.rowHeight + 'px',
formattedRow: this.row.getFormattedDatum(this.headers),
rowLimitClass: this.row.getRowLimitClass(),
cellLimitClasses: this.row.getCellLimitClasses()
}
},
props: {
headers: {
type: Object,
required: true
},
row: {
type: Object,
required: true
},
columnWidths: {
type: Array,
required: false,
default: [],
},
rowIndex: {
type: Number,
required: false,
default: undefined
},
rowOffset: {
type: Number,
required: false,
default: 0
},
rowHeight: {
type: Number,
required: false,
default: 0
},
configuration: {
type: Object,
required: true
}
},
methods: {
calculateRowTop: function (rowOffset) {
this.rowTop = (rowOffset + this.rowIndex) * this.rowHeight + 'px';
},
formatRow: function (row) {
this.formattedRow = row.getFormattedDatum(this.headers);
this.rowLimitClass = row.getRowLimitClass();
this.cellLimitClasses = row.getCellLimitClasses();
}
},
// TODO: use computed properties
watch: {
rowOffset: 'calculateRowTop',
row: {
handler: 'formatRow',
deep: false
}
}
}
</script>

View File

@@ -0,0 +1,543 @@
<template>
<div class="c-table c-telemetry-table c-table--filterable c-table--sortable has-control-bar"
:class="{'loading': loading}">
<div class="c-table__control-bar c-control-bar">
<a class="s-button t-export icon-download labeled"
v-on:click="exportAsCSV()"
title="Export This View's Data">
Export As CSV
</a>
</div>
<!-- Headers table -->
<div class="c-table__headers-w js-table__headers-w">
<table class="c-table__headers c-telemetry-table__headers"
:style="{ 'max-width': totalWidth + 'px'}">
<thead>
<tr>
<th v-for="(title, key, headerIndex) in headers"
v-on:click="sortBy(key)"
:class="['is-sortable', sortOptions.key === key ? 'is-sorting' : '', sortOptions.direction].join(' ')"
:style="{ width: columnWidths[headerIndex], 'max-width': columnWidths[headerIndex]}">{{title}}</th>
</tr>
<tr>
<th v-for="(title, key, headerIndex) in headers"
:style="{
width: columnWidths[headerIndex],
'max-width': columnWidths[headerIndex],
}">
<search class="c-table__search"
v-model="filters[key]"
v-on:input="filterChanged(key)"
v-on:clear="clearFilter(key)" />
</th>
</tr>
</thead>
</table>
</div>
<!-- Content table -->
<div class="c-table__body-w c-telemetry-table__body-w js-telemetry-table__body-w" @scroll="scroll">
<div class="c-telemetry-table__scroll-forcer" :style="{ width: totalWidth }"></div>
<table class="c-table__body c-telemetry-table__body"
:style="{ height: totalHeight + 'px', 'max-width': totalWidth + 'px'}">
<tbody>
<telemetry-table-row v-for="(row, rowIndex) in visibleRows"
:headers="headers"
:columnWidths="columnWidths"
:rowIndex="rowIndex"
:rowOffset="rowOffset"
:rowHeight="rowHeight"
:row="row"
>
</telemetry-table-row>
</tbody>
</table>
</div>
<!-- Sizing table -->
<table class="c-telemetry-table__sizing js-telemetry-table__sizing"
:style="{width: calcTableWidth}">
<tr>
<th v-for="(title, key, headerIndex) in headers">{{title}}</th>
</tr>
<telemetry-table-row v-for="(sizingRowData, objectKeyString) in sizingRows"
:headers="headers"
:row="sizingRowData">
</telemetry-table-row>
</table>
</div>
</template>
<style lang="scss">
@import "~styles/sass-base";
.c-table {
// Can be used by any type of table, scrolling, LAD, etc.
$min-w: 50px;
display: flex;
flex-flow: column nowrap;
justify-content: flex-start;
overflow: hidden;
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
&__control-bar,
&__headers-w {
// Don't allow top level elements to grow or shrink
flex: 0 0 auto;
}
/******************************* ELEMENTS */
th, td {
display: block;
flex: 1 0 auto;
font-size: 0.7rem; // TEMP LEGACY TODO: refactor this when __main-container font-size is dealt with
white-space: nowrap;
min-width: $min-w;
padding: $tabularTdPadTB $tabularTdPadLR;
vertical-align: middle; // This is crucial to hiding f**king 4px height injected by browser by default
}
td {
color: $colorTelemFresh;
vertical-align: top;
}
&__control-bar {
margin-bottom: $interiorMarginSm;
}
/******************************* WRAPPERS */
&__headers-w {
// Wraps __headers table
background: $colorTabHeaderBg;
overflow: hidden;
}
/******************************* TABLES */
&__headers,
&__body {
tr {
display: flex;
align-items: stretch;
}
}
&__headers {
// A table
thead {
display: block;
}
th {
&:not(:first-child) {
border-left: 1px solid $colorTabHeaderBorder;
}
}
}
&__body {
// A table
tr {
&:not(:first-child) {
border-top: 1px solid $colorTabBorder;
}
}
}
/******************************* MODIFIERS */
&--filterable {
// TODO: discuss using the search.vue custom control here
.l-filter {
input[type="text"],
input[type="search"] {
$p: 20px;
transition: padding 200ms ease-in-out;
box-sizing: border-box;
padding-right: $p; // Fend off from icon
padding-left: $p; // Fend off from icon
width: 100%;
}
&.active {
// When user has typed something, hide the icon and collapse left padding
&:before {
opacity: 0;
}
input[type="text"],
input[type="search"] {
padding-left: $interiorMargin;
}
}
}
}
&--sortable {
.is-sorting {
&:after {
color: $colorIconLink;
content: $glyph-icon-arrow-tall-up;
font-family: symbolsfont;
font-size: 8px;
display: inline-block;
margin-left: $interiorMarginSm;
}
&.desc:after {
content: $glyph-icon-arrow-tall-down;
}
}
.is-sortable {
cursor: pointer;
}
}
}
.c-telemetry-table {
// Table that displays telemetry in a scrolling body area
/******************************* ELEMENTS */
&__scroll-forcer {
// Force horz scroll when needed; width set via JS
font-size: 0;
height: 1px; // Height 0 won't force scroll properly
position: relative;
}
/******************************* WRAPPERS */
&__body-w {
// Wraps __body table provides scrolling
flex: 1 1 100%;
overflow-x: auto;
overflow-y: scroll;
}
/******************************* TABLES */
&__body {
// A table
flex: 1 1 100%;
overflow-x: auto;
tr {
display: flex; // flex-flow defaults to row nowrap (which is what we want) so no need to define
align-items: stretch;
position: absolute;
height: 18px; // Needed when a row has empty values in its cells
}
td {
overflow: hidden;
text-overflow: ellipsis;
}
}
&__sizing {
// A table
display: table;
z-index: -1;
visibility: hidden;
pointer-events: none;
position: absolute;
//Add some padding to allow for decorations such as limits indicator
tr {
display: table-row;
}
th, td {
display: table-cell;
padding-right: 10px;
padding-left: 10px;
white-space: nowrap;
}
}
}
.c-table__control-bar {
margin-bottom: $interiorMarginSm;
}
/******************************* LEGACY */
.s-status-taking-snapshot,
.overlay.snapshot {
// Handle overflow-y issues with tables and html2canvas
// Replaces .l-sticky-headers .l-tabular-body { overflow: auto; }
.c-table__body-w { overflow: auto; }
}
</style>
<script>
import TelemetryTableRow from './table-row.vue';
import search from '../../../ui/components/controls/search.vue';
import _ from 'lodash';
const VISIBLE_ROW_COUNT = 100;
const ROW_HEIGHT = 17;
const RESIZE_POLL_INTERVAL = 200;
const AUTO_SCROLL_TRIGGER_HEIGHT = 20;
export default {
components: {
TelemetryTableRow,
search
},
inject: ['table', 'openmct', 'csvExporter'],
props: ['configuration'],
data() {
return {
headers: {},
visibleRows: [],
columnWidths: [],
sizingRows: {},
rowHeight: ROW_HEIGHT,
scrollOffset: 0,
totalHeight: 0,
totalWidth: 0,
rowOffset: 0,
autoScroll: true,
sortOptions: {},
filters: {},
loading: false,
scrollable: undefined,
tableEl: undefined,
headersHolderEl: undefined,
calcTableWidth: '100%',
processingScroll: false,
updatingView: false
}
},
methods: {
updateVisibleRows() {
let start = 0;
let end = VISIBLE_ROW_COUNT;
let filteredRows = this.table.filteredRows.getRows();
let filteredRowsLength = filteredRows.length;
this.totalHeight = this.rowHeight * filteredRowsLength - 1;
if (filteredRowsLength < VISIBLE_ROW_COUNT) {
end = filteredRowsLength;
} else {
let firstVisible = this.calculateFirstVisibleRow();
let lastVisible = this.calculateLastVisibleRow();
let totalVisible = lastVisible - firstVisible;
let numberOffscreen = VISIBLE_ROW_COUNT - totalVisible;
start = firstVisible - Math.floor(numberOffscreen / 2);
end = lastVisible + Math.ceil(numberOffscreen / 2);
if (start < 0) {
start = 0;
end = Math.min(VISIBLE_ROW_COUNT, filteredRowsLength);
} else if (end >= filteredRowsLength) {
end = filteredRowsLength;
start = end - VISIBLE_ROW_COUNT + 1;
}
}
this.rowOffset = start;
this.visibleRows = filteredRows.slice(start, end);
},
calculateFirstVisibleRow() {
return Math.floor(this.scrollable.scrollTop / this.rowHeight);
},
calculateLastVisibleRow() {
let bottomScroll = this.scrollable.scrollTop + this.scrollable.offsetHeight;
return Math.floor(bottomScroll / this.rowHeight);
},
updateHeaders() {
let headers = this.table.configuration.getVisibleHeaders();
this.headers = headers;
this.$nextTick().then(this.calculateColumnWidths);
},
setSizingTableWidth() {
let scrollW = this.scrollable.offsetWidth - this.scrollable.clientWidth;
if (scrollW && scrollW > 0) {
this.calcTableWidth = 'calc(100% - ' + scrollW + 'px)';
}
},
calculateColumnWidths() {
let columnWidths = [];
let totalWidth = 0;
let sizingRowEl = this.sizingTable.children[0];
let sizingCells = Array.from(sizingRowEl.children);
sizingCells.forEach((cell) => {
let columnWidth = cell.offsetWidth;
columnWidths.push(columnWidth + 'px');
totalWidth += columnWidth;
});
this.columnWidths = columnWidths;
this.totalWidth = totalWidth;
},
sortBy(columnKey) {
// If sorting by the same column, flip the sort direction.
if (this.sortOptions.key === columnKey) {
if (this.sortOptions.direction === 'asc') {
this.sortOptions.direction = 'desc';
} else {
this.sortOptions.direction = 'asc';
}
} else {
this.sortOptions = {
key: columnKey,
direction: 'asc'
}
}
this.table.filteredRows.sortBy(this.sortOptions);
},
scroll() {
if (!this.processingScroll) {
this.processingScroll = true;
requestAnimationFrame(()=> {
this.updateVisibleRows();
this.synchronizeScrollX();
if (this.shouldSnapToBottom()) {
this.autoScroll = true;
} else {
// If user scrolls away from bottom, disable auto-scroll.
// Auto-scroll will be re-enabled if user scrolls to bottom again.
this.autoScroll = false;
}
this.processingScroll = false;
});
}
},
shouldSnapToBottom() {
return this.scrollable.scrollTop >= (this.scrollable.scrollHeight - this.scrollable.offsetHeight - AUTO_SCROLL_TRIGGER_HEIGHT);
},
scrollToBottom() {
this.scrollable.scrollTop = this.scrollable.scrollHeight;
},
synchronizeScrollX() {
this.headersHolderEl.scrollLeft = this.scrollable.scrollLeft;
},
filterChanged(columnKey) {
this.table.filteredRows.setColumnFilter(columnKey, this.filters[columnKey]);
},
clearFilter(columnKey) {
this.filters[columnKey] = '';
this.table.filteredRows.setColumnFilter(columnKey, '');
},
rowsAdded(rows) {
let sizingRow;
if (Array.isArray(rows)) {
sizingRow = rows[0];
} else {
sizingRow = rows;
}
if (!this.sizingRows[sizingRow.objectKeyString]) {
this.sizingRows[sizingRow.objectKeyString] = sizingRow;
this.$nextTick().then(this.calculateColumnWidths);
}
if (!this.updatingView) {
this.updatingView = true;
requestAnimationFrame(()=> {
this.updateVisibleRows();
if (this.autoScroll) {
this.$nextTick().then(this.scrollToBottom);
}
this.updatingView = false;
});
}
},
rowsRemoved(rows) {
if (!this.updatingView) {
this.updatingView = true;
requestAnimationFrame(()=> {
this.updateVisibleRows();
this.updatingView = false;
});
}
},
exportAsCSV() {
const headerKeys = Object.keys(this.headers);
const justTheData = this.table.filteredRows.getRows()
.map(row => row.getFormattedDatum(this.headers));
this.csvExporter.export(justTheData, {
filename: this.table.domainObject.name + '.csv',
headers: headerKeys
});
},
outstandingRequests(loading) {
this.loading = loading;
},
calculateTableSize() {
this.setSizingTableWidth();
this.$nextTick().then(this.calculateColumnWidths);
},
pollForResize() {
let el = this.$el;
let width = el.clientWidth;
let height = el.clientHeight;
this.resizePollHandle = setInterval(() => {
if (el.clientWidth !== width || el.clientHeight !== height) {
this.calculateTableSize();
width = el.clientWidth;
height = el.clientHeight;
}
}, RESIZE_POLL_INTERVAL);
},
updateConfiguration(configuration) {
this.configuration = configuration;
this.updateHeaders();
},
addObject() {
this.updateHeaders();
},
removeObject(objectIdentifier) {
let objectKeyString = this.openmct.objects.makeKeyString(objectIdentifier);
delete this.sizingRows[objectKeyString];
this.updateHeaders();
}
},
created() {
this.filterChanged = _.debounce(this.filterChanged, 500);
},
mounted() {
this.table.on('object-added', this.addObject);
this.table.on('object-removed', this.removeObject);
this.table.on('outstanding-requests', this.outstandingRequests);
this.table.filteredRows.on('add', this.rowsAdded);
this.table.filteredRows.on('remove', this.rowsRemoved);
this.table.filteredRows.on('sort', this.updateVisibleRows);
this.table.filteredRows.on('filter', this.updateVisibleRows);
//Default sort
this.sortOptions = this.table.filteredRows.sortBy();
this.scrollable = this.$el.querySelector('.js-telemetry-table__body-w');
this.sizingTable = this.$el.querySelector('.js-telemetry-table__sizing');
this.headersHolderEl = this.$el.querySelector('.js-table__headers-w');
this.table.configuration.on('change', this.updateConfiguration);
this.calculateTableSize();
this.pollForResize();
this.table.initialize();
},
destroyed() {
this.table.off('object-added', this.addObject);
this.table.off('object-removed', this.removeObject);
this.table.off('outstanding-requests', this.outstandingRequests);
this.table.filteredRows.off('add', this.rowsAdded);
this.table.filteredRows.off('remove', this.rowsRemoved);
this.table.filteredRows.off('sort', this.updateVisibleRows);
this.table.filteredRows.off('filter', this.updateVisibleRows);
this.table.configuration.off('change', this.updateConfiguration);
clearInterval(this.resizePollHandle);
this.table.configuration.destroy();
this.table.destroy();
}
}
</script>

View File

@@ -1,11 +0,0 @@
<div class="grid-properties">
<!--form class="form" -->
<ul class="l-inspector-part">
<h2>Table Columns</h2>
<li class="grid-row" v-for="(title, key) in headers">
<div class="grid-cell label" title="Show or Hide Column"><label :for="key + 'ColumnControl'">{{title}}</label></div>
<div class="grid-cell value"><input type="checkbox" :id="key + 'ColumnControl'" :checked="configuration.hiddenColumns[key] !== true" @change="toggleColumn(key)"></div>
</li>
</ul>
<!--/form -->
</div>

View File

@@ -1,64 +0,0 @@
<div class="tabular-holder l-sticky-headers has-control-bar l-telemetry-table" :class="{'loading': loading}">
<div class="l-control-bar">
<a class="s-button t-export icon-download labeled"
v-on:click="exportAsCSV()"
title="Export This View's Data">
Export As CSV
</a>
</div>
<!-- Headers table -->
<div class="mct-table-headers-w">
<table class="mct-table l-tabular-headers filterable" :style="{ 'max-width': totalWidth + 'px'}">
<thead>
<tr>
<th v-for="(title, key, headerIndex) in headers"
v-on:click="sortBy(key)"
:class="['sortable', sortOptions.key === key ? 'sort' : '', sortOptions.direction].join(' ')"
:style="{ width: columnWidths[headerIndex], 'max-width': columnWidths[headerIndex]}">{{title}}</th>
</tr>
<tr class="s-filters">
<th v-for="(title, key, headerIndex) in headers"
:style="{
width: columnWidths[headerIndex],
'max-width': columnWidths[headerIndex],
}">
<div class="holder l-filter flex-elem grows" :class="{active: filters[key]}">
<input type="text" v-model="filters[key]" v-on:input="filterChanged(key)" />
<a class="clear-icon clear-input icon-x-in-circle" :class="{show: filters[key]}" @click="clearFilter(key)"></a>
</div>
</th>
</tr>
</thead>
</table>
</div>
<!-- Content table -->
<div @scroll="scroll" class="l-tabular-body t-scrolling vscroll--persist">
<div class="mct-table-scroll-forcer"
:style="{
width: totalWidth
}"></div>
<table class="mct-table js-telemetry-table" :style="{ height: totalHeight + 'px', 'max-width': totalWidth + 'px'}">
<tbody>
<telemetry-table-row v-for="(row, rowIndex) in visibleRows"
:headers="headers"
:columnWidths="columnWidths"
:rowIndex="rowIndex"
:rowOffset="rowOffset"
:rowHeight="rowHeight"
:row="row"
>
</telemetry-table-row>
</tbody>
</table>
</div>
<!-- Sizing table -->
<table class="mct-sizing-table t-sizing-table js-sizing-table" :style="{width: calcTableWidth}">
<tr>
<th v-for="(title, key, headerIndex) in headers">{{title}}</th>
</tr>
<telemetry-table-row v-for="(sizingRowData, objectKeyString) in sizingRows"
:headers="headers"
:row="sizingRowData">
</telemetry-table-row>
</table>
</div>

View File

@@ -1,6 +1,6 @@
@import "constants";
// Mixins
// Functions
@function pullForward($c: $colorBodyBg, $p: 20%) {
// For dark interfaces, lighter things come forward - opposite for light interfaces
@return darken($c, $p);
@@ -12,6 +12,7 @@
}
// Global
$fontBaseSize: 12px;
$colorBodyBg: #fcfcfc;
$colorBodyFg: #666;
$colorGenBg: #fff;
@@ -19,6 +20,7 @@ $colorStatusBarBg: #000;
$colorStatusBarFg: #999;
$colorStatusBarFgHov: #aaa;
$colorKey: #0099cc;
$colorKeyFilter: brightness(0.9) sepia(1) hue-rotate(145deg) saturate(6);
$colorKeySelectedBg: $colorKey;
$colorKeyFg: #fff;
$colorKeyHov: #00c0f6;
@@ -36,7 +38,8 @@ $overlayCr: 11px;
$shdwTextSubtle: rgba(black, 0.2) 0 1px 2px;
// Buttons and Controls
$colorBtnBg: pullForward($colorBodyBg, $contrastRatioPercent);
$btnPad: $interiorMargin, $interiorMargin * 1.25;
$colorBtnBg: #aaaaaa;
$colorBtnBgHov: pullForward($colorBtnBg, $hoverRatioPercent);
$colorBtnFg: #fff;
$colorBtnFgHov: $colorBtnFg;
@@ -46,6 +49,9 @@ $colorBtnMajorBg: $colorKey;
$colorBtnMajorBgHov: $colorKeyHov;
$colorBtnMajorFg: $colorKeyFg;
$colorBtnMajorFgHov: pushBack($colorBtnMajorFg, $hoverRatioPercent);
$colorBtnCautionBg: #f16f6f;
$colorBtnCautionBgHov: #f1504e;
$colorBtnCautionFg: $colorBtnFg;
$colorClickIcon: $colorKey;
$colorClickIconHov: $colorKeyHov;
$colorToggleIcon: rgba($colorClickIcon, 0.5);
@@ -68,6 +74,7 @@ $sliderKnobR: 2px;
$timeControllerToiLineColor: $colorBodyFg;
$timeControllerToiLineColorHov: #0052b5;
$colorTransLucBg: #666; // Used as a visual blocking element over variable backgrounds, like imagery
$createBtnTextTransform: uppercase;
// Foundation Colors
$colorAlt1: #776ba2;
@@ -90,17 +97,23 @@ $colorTick: rgba(black, 0.2);
$colorSelectableSelectedPrimary: $colorKey;
$colorSelectableHov: rgba($colorBodyFg, 0.4);
// Menu colors
// Menus
$colorMenuBg: pushBack($colorBodyBg, 10%);
$colorMenuFg: pullForward($colorMenuBg, 70%);
$colorMenuIc: $colorKey;
$colorMenuHovBg: pullForward($colorMenuBg, $hoverRatioPercent);
$colorMenuHovFg: $colorMenuFg;
$colorMenuHovIc: $colorMenuIc;
$colorMenuHovBg: $colorMenuIc; //pullForward($colorMenuBg, $hoverRatioPercent);
$colorMenuHovFg: $colorMenuBg;
$colorMenuHovIc: $colorMenuBg;
$shdwMenu: rgba(black, 0.5) 0 1px 5px;
$shdwMenuText: none;
$colorCreateMenuLgIcon: $colorKey;
$colorCreateMenuText: $colorBodyFg;
$menuItemPad: ($interiorMargin, nth($btnPad, 2));
// Palettes and Swatches
$paletteItemBorderOuterColorSelected: black;
$paletteItemBorderInnerColorSelected: white;
$paletteItemBorderInnerColor: rgba($paletteItemBorderOuterColorSelected, 0.3);
// Form colors
$colorCheck: $colorKey;
@@ -238,13 +251,14 @@ $scrollbarThumbColorMenuHov: pullForward($scrollbarThumbColorMenu, 2%);
// Splitter
$splitterD: 7px;
$splitterHandleD: 2px;
$splitterHandleHitMargin: 4px;
$splitterGrippyD: ($splitterHandleD - 4, 75px, 50px); // thickness, length, min-length
$colorSplitterBaseBg: $colorBodyBg;
$colorSplitterBg: pullForward($colorSplitterBaseBg, 20%);
$colorSplitterFg: $colorBodyBg;
$colorSplitterHover: $colorKey; // pullForward($colorSplitterBg, $hoverRatioPercent * 2);
$colorSplitterActive: $colorKey;
$splitterBtnD: (16px, 35px);
$splitterBtnD: (16px, 35px); // height, width
$splitterBtnColorBg: #eee;
$splitterBtnColorFg: #999;
$splitterBtnColorHoverBg: rgba($colorKey, 1);
@@ -272,12 +286,6 @@ $colorCalCellSelectedBg: $colorItemTreeSelectedBg;
$colorCalCellSelectedFg: $colorItemTreeSelectedFg;
$colorCalCellInMonthBg: pullForward($colorMenuBg, 5%);
// Palettes
$colorPaletteFg: pullForward($colorMenuBg, 30%);
$colorPaletteSelected: #333;
$shdwPaletteFg: none;
$shdwPaletteSelected: inset 0 0 0 1px #fff;
// About Screen
$colorAboutLink: #84b3ff;
@@ -288,3 +296,16 @@ $colorLoadingBg: rgba($colorLoadingFg, 0.1);
// Transitions
$transIn: all 50ms ease-in;
$transOut: all 250ms ease-out;
// Discrete items, like Notebook entries, Widget rules
@mixin discreteItem() {
background: rgba($colorBodyFg,0.1);
border: 1px solid $colorInteriorBorder;
border-radius: $controlCr;
.c-input-inline:hover {
background: $colorBodyBg;
}
}
@mixin discreteItemInnerElem() {
border: 1px solid $colorBodyBg;
border-radius: $controlCr; }

View File

@@ -15,10 +15,17 @@ $interiorMarginLg: 10px;
$inputTextPTopBtm: 2px;
$inputTextPLeftRight: 5px;
$inputTextP: $inputTextPTopBtm $inputTextPLeftRight;
$menuLineH: 1.5rem;
$treeItemIndent: 16px;
$treeTypeIconW: 18px;
/*************** Items */
$itemPadLR: 5px;
$ueBrowseGridItemLg: 200px;
/*************** Tabular */
$tabularHeaderH: 22px;
$tabularTdPadLR: $itemPadLR;
$tabularTdPadTB: 2px;
/************************** VISUAL */
@@ -89,7 +96,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';
@@ -106,6 +113,7 @@ $glyph-icon-frame-show: '\e1045';
$glyph-icon-frame-hide: '\e1046';
$glyph-icon-import: '\e1047';
$glyph-icon-export: '\e1048';
$glyph-icon-font-size: '\e1049';
$glyph-icon-activity: '\e1100';
$glyph-icon-activity-mode: '\e1101';
$glyph-icon-autoflow-tabular: '\e1102';
@@ -137,4 +145,38 @@ $glyph-icon-timer: '\e1127';
$glyph-icon-topic: '\e1128';
$glyph-icon-box-with-dashed-lines: '\e1129';
$glyph-icon-summary-widget: '\e1130';
$glyph-icon-notebook: '\e1131';
$glyph-icon-notebook: '\e1131';
/************************** GLYPHS AS DATA URI */
// Only objects have been converted, for use in Create menu and folder views
$bg-icon-activity: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23666666' d='M288 32H160l160 160H174.872C152.74 153.742 111.377 128 64 128H0v256h64c47.377 0 88.74-25.742 110.872-64H320L160 480h128l224-224L288 32z'/%3e%3c/svg%3e");
$bg-icon-activity-mode: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23666666' d='M256 0C148.6 0 56.6 66.2 18.6 160H64c28.4 0 54 12.4 71.5 32H256l-96-96h128l160 160-160 160H160l96-96H135.5C118 339.6 92.4 352 64 352H18.6c38 93.8 129.9 160 237.4 160 141.4 0 256-114.6 256-256S397.4 0 256 0z'/%3e%3c/svg%3e");
$bg-icon-autoflow-tabular: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23666666' d='M96 0C43.2 0 0 43.2 0 96v320c0 52.8 43.2 96 96 96h32V0H96zM192 0h128v512H192zM416 0h-32v352h128V96c0-52.8-43.2-96-96-96z'/%3e%3c/svg%3e");
$bg-icon-clock: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23666666' d='M256 0C114.62 0 0 114.62 0 256s114.62 256 256 256 256-114.62 256-256S397.38 0 256 0zm135 345a36 36 0 0 1-31.21 18 35.83 35.83 0 0 1-18-4.83l-110.85-64-.14-.08q-.6-.35-1.19-.73l-.43-.28-.93-.64-.63-.45-.63-.48-.85-.67-.32-.27c-.36-.3-.72-.61-1.07-.92a35.76 35.76 0 0 1-6.52-7.9c-.14-.23-.27-.47-.41-.7s-.29-.49-.43-.74a35.75 35.75 0 0 1-3.58-9.59v-.06c-.1-.46-.19-.92-.27-1.38 0-.14-.05-.28-.08-.42-.06-.35-.11-.71-.15-1.07s-.07-.52-.1-.79-.05-.51-.07-.77l-.09-1.12v-.52-1.39V81a36 36 0 0 1 36-36 36 36 0 0 1 36 36v161.22l92.85 53.61A36 36 0 0 1 391 345z' fill='%2300a14b'/%3e%3c/svg%3e");
$bg-icon-database: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23666666' d='M256 256C114.615 256 0 213.019 0 160v256c0 53.019 114.615 96 256 96s256-42.981 256-96V160c0 53.019-114.615 96-256 96z'/%3e%3cellipse fill='%23666666' cx='256' cy='96' rx='256' ry='96'/%3e%3c/svg%3e");
$bg-icon-database-query: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23666666' d='M341.76 409.643C316.369 423.871 287.118 432 256 432c-97.047 0-176-78.953-176-176S158.953 80 256 80s176 78.953 176 176c0 31.118-8.129 60.369-22.357 85.76l95.846 95.846C509.747 430.661 512 423.429 512 416V96c0-53.019-114.615-96-256-96S0 42.981 0 96v320c0 53.019 114.615 96 256 96 63.055 0 120.774-8.554 165.388-22.73l-79.628-79.627z'/%3e%3cpath fill='%23666666' d='M176 256c0 44.112 35.888 80 80 80s80-35.888 80-80-35.888-80-80-80-80 35.888-80 80z'/%3e%3c/svg%3e");
$bg-icon-dataset: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23666666' d='M448 96H288l-54.6-54.6-18.7-18.7C202.2 10.2 177.6 0 160 0H32C14.4 0 0 14.4 0 32v192c0-35.2 28.8-64 64-64h384c35.2 0 64 28.8 64 64v-64c0-35.2-28.8-64-64-64zM448 224H64c-35.2 0-64 28.8-64 64v160c0 35.2 28.8 64 64 64h384c35.2 0 64-28.8 64-64V288c0-35.2-28.8-64-64-64zM160 448H96V288h64v160zm128 0h-64V288h64v160zm128 0h-64V288h64v160z'/%3e%3c/svg%3e");
$bg-icon-datatable: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23666666' d='M256 256C114.6 256 0 213 0 160v256c0 53 114.6 96 256 96s256-43 256-96V160c0 53-114.6 96-256 96zm192 31.5v128c-18.3 7.8-39.9 14.4-64 19.7v-128c24.1-5.3 45.7-11.9 64-19.7zm-320 19.7v128c-24.1-5.2-45.7-11.9-64-19.7v-128c18.3 7.8 39.9 14.4 64 19.7zM192 445V317c20.5 2 41.9 3 64 3s43.5-1.1 64-3v128c-20.5 2-41.9 3-64 3s-43.5-1.1-64-3z'/%3e%3cellipse fill='%23666666' cx='256' cy='96' rx='256' ry='96'/%3e%3c/svg%3e");
$bg-icon-dictionary: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23666666' d='M416 320c52.8 0 96-43.2 96-96V96c0-52.8-43.2-96-96-96v160l-64-32-64 32V0H96C43.2 0 0 43.2 0 96v320c0 52.8 43.2 96 96 96h320c52.8 0 96-43.2 96-96v-96c0 52.8-43.2 96-96 96H96v-96h320z'/%3e%3c/svg%3e");
$bg-icon-folder: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23666666' d='M448 96H288l-54.6-54.6-18.7-18.7C202.2 10.2 177.6 0 160 0H32C14.4 0 0 14.4 0 32v192c0-35.2 28.8-64 64-64h384c35.2 0 64 28.8 64 64v-64c0-35.2-28.8-64-64-64zM448 224H64c-35.2 0-64 28.8-64 64v160c0 35.2 28.8 64 64 64h384c35.2 0 64-28.8 64-64V288c0-35.2-28.8-64-64-64z'/%3e%3c/svg%3e");
$bg-icon-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23666666' d='M448 0H64C28.8 0 0 28.8 0 64v384c0 35.2 28.8 64 64 64h384c35.2 0 64-28.8 64-64V64c0-35.2-28.8-64-64-64zm0 448H64V64h384v384z'/%3e%3cpath fill='%23666666' d='M160 128l-64 64v224h320V256l-64-64-64 64z'/%3e%3c/svg%3e");
$bg-icon-layout: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23666666' d='M224 0H96C43.2 0 0 43.2 0 96v320c0 52.8 43.2 96 96 96h128V0zM416 0H288v288.832h224V96c0-52.8-43.2-96-96-96zM288 512h128c52.8 0 96-43.2 96-96v-64.832H288V512z'/%3e%3c/svg%3e");
$bg-icon-object: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='none' d='M256 96L76.8 208 256 320l179.2-112z'/%3e%3cpath fill='%23666666' d='M256 512l256-160V160L255.99 0 0 160v192l256 160zm0-416l179.2 112L256 320 76.8 208 256 96z'/%3e%3c/svg%3e");
$bg-icon-object-unknown: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23666666' d='M255-1L-1 159v192l256 160 256-160V159L255-1zm37.7 430.6c-10.6 10.4-23 15.4-38 15.4-15.6 0-28.1-4.9-38.1-14.8-10-10-14.8-22.4-14.8-38.1 0-15.2 5.1-27.6 15.5-38.1s22.6-15.6 37.4-15.6c14.8 0 27.1 5.2 37.8 16 10.7 10.8 15.9 23.2 15.9 38-.1 14.5-5.4 27-15.7 37.2zm26.4-156.3c-11.8 5.9-18.7 11-21.7 16.2-1.8 3.1-3 7.4-3.7 13.4v20.5H213v-22.1c0-20.1 2.2-34.9 6.5-44 4-8.6 11.3-15.1 22.4-20l17.4-7.7c16-7.1 24.1-17.6 24.1-31.4 0-8-3-15.2-8.6-20.9-5.6-5.6-12.8-8.6-20.8-8.6-12 0-27.2 5-31.4 28.7l-1.1 6.1H148l.7-8.1c2-22.3 8.5-41.2 19.4-56.1 9.8-13.5 22.8-24.3 38.5-32.3 15.7-8 32.3-12 49.1-12 30.3 0 55.1 9.7 75.7 29.8 20.6 20 30.6 44 30.6 73.6 0 35.4-14.4 60.7-42.9 74.9z'/%3e%3c/svg%3e");
$bg-icon-packet: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='none' d='M256 96L76.8 208 256 320l179.2-112z'/%3e%3cpath fill='%23666666' d='M256 0L0 160v256c0 52.8 43.2 96 96 96h320c52.8 0 96-43.2 96-96V160L256 0zm0 96l179.2 112L256 320 76.8 208 256 96z'/%3e%3c/svg%3e");
$bg-icon-page: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23666666' d='M352 256c-52.8 0-96-43.2-96-96V0H96C43.2 0 0 43.2 0 96v320c0 52.8 43.2 96 96 96h320c52.8 0 96-43.2 96-96V256H352z'/%3e%3cpath fill='%23666666' d='M384 192h128L320 0v128c0 35.2 28.8 64 64 64z'/%3e%3c/svg%3e");
$bg-icon-plot-overlay: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23666666' d='M415 0H97C43.65 0 0 43.65 0 97v203.41c7.09 9.32 12.83 14.17 16 15.42 7.14-2.81 27.22-23.77 46.48-73C83.71 188.64 120.64 124 176 124c26.2 0 50.71 14.58 72.85 43.34 18.67 24.25 32.42 54.46 40.67 75.54 19.26 49.19 39.34 70.15 46.48 73 7.14-2.81 27.22-23.77 46.48-73C403.71 188.64 440.64 124 496 124a69.55 69.55 0 0 1 16 1.87V97c0-53.35-43.65-97-97-97z'/%3e%3cpath fill='%23666666' d='M496 196.17c-7.14 2.81-27.22 23.76-46.48 73C428.29 323.36 391.36 388 336 388c-26.2 0-50.71-14.58-72.85-43.34-18.67-24.25-32.42-54.46-40.67-75.54-19.26-49.19-39.34-70.15-46.48-73-7.14 2.81-27.22 23.76-46.48 73C108.29 323.36 71.36 388 16 388a69.56 69.56 0 0 1-16-1.87V415c0 53.35 43.65 97 97 97h318c53.35 0 97-43.65 97-97V211.59c-7.09-9.32-12.83-14.17-16-15.42z'/%3e%3c/svg%3e");
$bg-icon-plot-stacked: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23666666' d='M44.8 156c12.49 0 24.48-13.26 42.76-35.09 22.71-27.14 51-60.91 98-60.91 22.32 0 43.31 7.73 62.4 23 14.34 11.45 25.58 25.21 36.46 38.53C303.63 145 314 156 326.4 156H512V97c0-53.35-43.65-97-97-97H97C43.65 0 0 43.65 0 97v59h44.8z'/%3e%3cpath fill='%23666666' d='M264.75 205.2c-14.12-11.32-25.26-25-36-38.14C211 145.32 199.37 132 185.6 132c-12.53 0-24.54 13.27-42.83 35.12-22.7 27.12-51 60.88-98 60.88H0v56h185.6c22 0 42.77 7.67 61.65 22.8 14.12 11.32 25.26 25 36 38.14C301 366.68 312.63 380 326.4 380c12.53 0 24.54-13.27 42.83-35.12 22.7-27.12 51-60.88 98-60.88H512v-56H326.4c-22.03 0-42.77-7.67-61.65-22.8z'/%3e%3cpath fill='%23666666' d='M467.2 356c-12.49 0-24.48 13.26-42.76 35.09-22.71 27.14-51 60.91-98 60.91-22.32 0-43.31-7.73-62.4-23-14.34-11.45-25.58-25.21-36.46-38.53C208.37 367 198 356 185.6 356H0v59c0 53.35 43.65 97 97 97h318c53.35 0 97-43.65 97-97v-59h-44.8z'/%3e%3c/svg%3e");
$bg-icon-session: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23666666' d='M317.8 262.2c3.3 2.1 6.6 4.3 9.6 6.8l60.2 48.2c14.8 11.9 41.9 11.9 56.7 0l67.6-54c.1-2.4.1-4.7.1-7.1 0-26.1-3.9-51.2-11.1-74.9L423.5 243c-29.1 23.3-70.1 29.6-105.7 19.2zM124.3 317.1l60.2-48.2c29-23.2 70-29.6 105.6-19.2-3.3-2.1-6.6-4.3-9.6-6.8l-60.2-48.2c-14.8-11.9-41.9-11.9-56.7 0L103.5 243c-20 16-45.7 24-71.5 24-10.8 0-21.5-1.4-31.9-4.2v.8c2.5 1.7 5 3.4 7.3 5.3l60.2 48.2c14.9 11.9 41.9 11.9 56.7 0z'/%3e%3cpath fill='%23666666' d='M60.3 189.1l60.2-48.2c40.1-32.1 102.8-32.1 142.9 0l60.2 48.2c14.8 11.9 41.9 11.9 56.7 0l90.5-72.4C425.2 46.5 346 0 256 0 136.7 0 36.4 81.6 8 192.1c15.4 8.8 38.9 7.8 52.3-3zM344.5 371l-60.2-48.2c-14.8-11.9-41.9-11.9-56.7 0L167.5 371c-20 16-45.7 24-71.5 24-23.9 0-47.7-6.9-67.1-20.7C71.7 456.1 157.3 512 256 512s184.3-55.9 227.1-137.7c-40.2 28.7-99.9 27.6-138.6-3.3z'/%3e%3c/svg%3e");
$bg-icon-tabular: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23666666' d='M448 0H64C28.8 0 0 28.8 0 64v384c0 35.2 28.8 64 64 64h384c35.2 0 64-28.8 64-64V64c0-35.2-28.8-64-64-64zM320 224H192v-96h128v96zm-128 32h128v96H192v-96zm-32 96H32v-96h128v96zm0-224v96H32v-96h128zM64 480c-8.5 0-16.5-3.3-22.6-9.4S32 456.5 32 448v-64h128v96H64zm128 0v-96h128v96H192zm288-32c0 8.5-3.3 16.5-9.4 22.6S456.5 480 448 480h-96v-96h128v64zm0-96H352v-96h128v96zm0-128H352v-96h128v96z'/%3e%3c/svg%3e");
$bg-icon-tabular-lad: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23666666' d='M448 0H64C28.7.1.1 28.7 0 64v384c.1 35.3 28.7 63.9 64 64h384c35.3-.1 63.9-28.7 64-64V64c-.1-35.3-28.7-63.9-64-64zM32 128h128v96H32v-96zm0 128h128v96H32v-96zm32 224c-17.6-.1-31.9-14.4-32-32v-64h128v96H64zm128 0v-96h128v96H192zm288-32c-.1 17.6-14.4 31.9-32 32h-96v-96h128v64zm0-192v96H192v-96h32v-32h-32v-96h288v96h-32v32h32z'/%3e%3cpath fill='%23666666' d='M391.2 273.7L336 246.1V160c0-8.8-7.2-16-16-16s-16 7.2-16 16v105.9l72.8 36.4c7.9 4 17.5.8 21.5-7.2 4-7.8.8-17.5-7.1-21.4z'/%3e%3c/svg%3e");
$bg-icon-tabular-lad-set: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23666666' d='M64 384V96c-35.3.1-63.9 28.7-64 64v288c.1 35.3 28.7 63.9 64 64h288c35.3-.1 63.9-28.7 64-64H128c-35.3-.1-63.9-28.7-64-64z'/%3e%3cpath fill='%23666666' d='M448 0H160c-35.3.1-63.9 28.7-64 64v288c.1 35.3 28.7 63.9 64 64h288c35.3-.1 63.9-28.7 64-64V64c-.1-35.3-28.7-63.9-64-64zM128 96h96v64h-96V96zm0 96h96v96h-96v-96zm32 192c-17.6-.1-31.9-14.4-32-32v-32h96v64h-64zm96 0v-64h96v64h-96zm224-32c-.1 17.6-14.4 31.9-32 32h-64v-64h96v32zm0-64H256V96h224v192z'/%3e%3cpath fill='%23666666' d='M416 240c8.8 0 16-7.2 16-16 0-6.9-4.4-13-10.9-15.2L384 196.5V144c0-8.8-7.2-16-16-16s-16 7.2-16 16v75.5l58.9 19.6c1.7.6 3.4.9 5.1.9z'/%3e%3c/svg%3e");
$bg-icon-tabular-realtime: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23666666' d='M0 64v384c0 35.2 28.8 64 64 64h288c35.2 0 64-28.8 64-64V340c-19.8 7.8-41.4 12-64 12-35.4 0-68.4-10.5-96-28.6V352h-96v-96h35.3c-5.2-10.1-9.4-20.8-12.6-32H160v-96h22.7C203.6 54.2 271.6 0 352 0H64C28.8 0 0 28.8 0 64zm288 320h96v64c0 8.5-3.3 16.5-9.4 22.6S360.5 480 352 480h-64v-96zm-160 96H64c-8.5 0-16.5-3.3-22.6-9.4S32 456.5 32 448v-64h96v96zm0-128H32v-96h96v96zm32 32h96v96h-96v-96zm-32-160H32v-96h96v96z'/%3e%3cpath fill='%23666666' d='M192 160c0 88.4 71.6 160 160 160s160-71.6 160-160S440.4 0 352 0 192 71.6 192 160zm49.7 39.8L227 187.5c-1.4-6.4-2.3-12.9-2.7-19.6 15.1-.1 30.1-5 41.9-14.8l39.6-33c7.5-6.2 21.1-6.2 28.6 0l39.6 33c2.8 2.3 5.7 4.3 8.8 6.1-23-11.7-52.7-9.2-72.8 7.5l-39.6 33c-7.6 6.3-21.2 6.3-28.7.1zM352 288c-36.7 0-69.7-15.4-93-40.1 14.2-.6 28.1-5.5 39.2-14.7l39.6-33c7.5-6.2 21.1-6.2 28.6 0l39.6 33c11 9.2 25 14.1 39.2 14.7-23.5 24.7-56.5 40.1-93.2 40.1zm125.9-151.3c1.4 7.5 2.1 15.3 2.1 23.3 0 9.4-1 18.6-3 27.5l-14.7 12.3c-7.5 6.2-21.1 6.2-28.6 0l-39.6-33c-2.8-2.3-5.7-4.3-8.8-6.1 23 11.7 52.7 9.2 72.8-7.5l19.8-16.5zM352 32c46.4 0 87.1 24.7 109.5 61.7l-31.2 26c-7.5 6.2-21.1 6.2-28.6 0l-39.6-33c-23.6-19.7-60.6-19.7-84.3 0l-39.6 33c-2.5 2.1-5.7 3.5-9.1 4.2C244.7 70.8 293.8 32 352 32z'/%3e%3c/svg%3e");
$bg-icon-tabular-scrolling: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23666666' d='M32 0C14.4 0 0 14.4 0 32v96h224V0H32zM512 128V32c0-17.6-14.4-32-32-32H288v128h224zM0 192v96c0 17.6 14.4 32 32 32h192V192H0zM480 320c17.6 0 32-14.4 32-32v-96H288v128h192zM256 512L128 384h256z'/%3e%3c/svg%3e");
$bg-icon-telemetry: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23666666' d='M16 315.83c7.14-2.81 27.22-23.77 46.48-73C83.71 188.64 120.64 124 176 124c26.2 0 50.71 14.58 72.85 43.34 18.67 24.25 32.42 54.46 40.67 75.54 19.26 49.19 39.34 70.15 46.48 73 7.14-2.81 27.22-23.77 46.48-73 18.7-47.75 49.57-103.57 94.47-116.23A255.87 255.87 0 0 0 256 0C114.62 0 0 114.62 0 256a257.18 257.18 0 0 0 5 50.52c4.77 5.39 8.61 8.37 11 9.31z'/%3e%3cpath fill='%23666666' d='M496 196.17c-7.14 2.81-27.22 23.76-46.48 73C428.29 323.36 391.36 388 336 388c-26.2 0-50.71-14.58-72.85-43.34-18.67-24.25-32.42-54.46-40.67-75.54-19.26-49.19-39.34-70.15-46.48-73-7.14 2.81-27.22 23.76-46.48 73-18.7 47.75-49.57 103.57-94.47 116.23A255.87 255.87 0 0 0 256 512c141.38 0 256-114.62 256-256a257.18 257.18 0 0 0-5-50.52c-4.77-5.39-8.61-8.37-11-9.31z'/%3e%3c/svg%3e");
$bg-icon-timeline: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23666666' d='M128 128h192v64H128zM192 224h192v64H192zM160 320h192v64H160z'/%3e%3cpath fill='%23666666' d='M416 0h-64v96h63.8c.1 0 .1.1.2.2v319.7c0 .1-.1.1-.2.2H352v96h64c52.8 0 96-43.2 96-96V96c0-52.8-43.2-96-96-96zM96 415.8V96.2c0-.1.1-.1.2-.2H160V0H96C43.2 0 0 43.2 0 96v320c0 52.8 43.2 96 96 96h64v-96H96.2c-.1 0-.2-.1-.2-.2z'/%3e%3c/svg%3e");
$bg-icon-timer: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23666666' d='M288 73.3V32.01a32 32 0 0 0-32-32h-64a32 32 0 0 0-32 32V73.3C67.48 100.84 0 186.54 0 288.01c0 123.71 100.29 224 224 224s224-100.29 224-224c0-101.48-67.5-187.2-160-214.71zm-54 224.71l-131.88 105.5A167.4 167.4 0 0 1 56 288.01c0-92.64 75.36-168 168-168 3.36 0 6.69.11 10 .31v177.69z'/%3e%3c/svg%3e");
$bg-icon-topic: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23666666' d='M227.18 238.32l43.15-43.15a25.18 25.18 0 0 1 35.36 0l43.15 43.15a94.42 94.42 0 0 0 35.18 22.25V174.5l-28.82-28.82a95.11 95.11 0 0 0-134.35 0l-43.15 43.15a25.18 25.18 0 0 1-35.36 0L128 174.5v86.07a95.11 95.11 0 0 0 99.18-22.25z'/%3e%3cpath fill='%23666666' d='M252.82 273.68l-43.15 43.15a25.18 25.18 0 0 1-35.36 0l-43.15-43.15c-1-1-2.1-2-3.18-3v98.68a95.11 95.11 0 0 0 131.18-3l43.15-43.15a25.18 25.18 0 0 1 35.36 0l43.15 43.15c1 1 2.1 2 3.18 3v-98.68a95.11 95.11 0 0 0-131.18 3z'/%3e%3cpath fill='%23666666' d='M416 0h-64v96h63.83l.17.17v319.66l-.17.17H352v96h64c52.8 0 96-43.2 96-96V96c0-52.8-43.2-96-96-96zM160 416H96.17l-.17-.17V96.17l.17-.17H160V0H96C43.2 0 0 43.2 0 96v320c0 52.8 43.2 96 96 96h64v-96z'/%3e%3c/svg%3e");
$bg-icon-box-with-dashed-lines: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23666666' d='M0 192h64v128H0zM64 64.11l.11-.11H160V0H64A64.19 64.19 0 0 0 0 64v96h64V64.11zM64 447.89V352H0v96a64.19 64.19 0 0 0 64 64h96v-64H64.11zM192 0h128v64H192zM448 447.89l-.11.11H352v64h96a64.19 64.19 0 0 0 64-64v-96h-64v95.89zM448 0h-96v64h95.89l.11.11V160h64V64a64.19 64.19 0 0 0-64-64zM448 192h64v128h-64zM192 448h128v64H192zM128 128h256v256H128z'/%3e%3c/svg%3e");
$bg-icon-summary-widget: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23666666' d='M448 0H64C28.8 0 0 28.8 0 64v384c0 35.2 28.8 64 64 64h384c35.2 0 64-28.8 64-64V64c0-35.2-28.8-64-64-64zm-24.1 305.2l-41.3 71.6-94.8-65.8 9.6 115h-82.7l9.6-115-94.8 65.8-41.3-71.6L192.5 256 88.1 206.8l41.3-71.6 94.8 65.8-9.6-115h82.7l-9.6 115 94.8-65.8 41.3 71.6L319.5 256l104.4 49.2z'/%3e%3c/svg%3e");
$bg-icon-notebook: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath fill='%23666666' d='M448 55.4c0-39.9-27.7-63.7-61.5-52.7L0 128h448V55.4zM448 160H0v288c0 35.2 28.8 64 64 64h384c35.2 0 64-28.8 64-64V224c0-35.2-28.8-64-64-64zm-32 256H224V256h192v160z'/%3e%3c/svg%3e");

View File

@@ -0,0 +1,552 @@
/*****************************************************************************
* 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.
*****************************************************************************/
// VERSION MANUALLY RESTORED FROM VUE-LAYOUT
/******************************************************** PLACEHOLDERS */
@mixin cControl() {
$fs: 1em;
@include userSelectNone();
display: inline-flex;
align-items: center;
justify-content: center;
overflow: hidden;
&:before,
&:after {
font-family: symbolsfont;
display: block;
flex: 0 0 auto;
}
&:after {
font-size: 0.8em;
}
[class*="__label"] {
@include ellipsize();
display: block;
line-height: $fs; // Remove effect on top and bottom padding
font-size: $fs;
}
}
@mixin cButton() {
@include cControl();
background: $colorBtnBg;
border-radius: $controlCr;
color: $colorBtnFg;
cursor: pointer;
padding: nth($btnPad, 1) nth($btnPad, 2);
&:hover {
background: $colorBtnBgHov;
color: $colorBtnFgHov;
}
&[class*="--major"] {
background: $colorBtnMajorBg;
color: $colorBtnMajorFg;
&:hover {
background: $colorBtnMajorBgHov;
color: $colorBtnMajorFgHov;
}
}
&[class*='--caution'] {
background: $colorBtnCautionBg;
color: $colorBtnCautionFg;
&:hover {
background: $colorBtnCautionBgHov;
}
}
}
@mixin cClickIcon() {
// A clickable element that just includes the icon, no background
// Padding is included to facilitate a bigger hit area
// Make the icon bigger relative to its container
@include cControl();
$pLR: 3px;
$pTB: 3px;
border-radius: $controlCr;
color: $colorKey;
cursor: pointer;
padding: $pTB $pLR ;
&:hover {
background: rgba($colorKey, 0.2);
}
&:before,
*:before {
// *:before handles any nested containers that may contain glyph elements
// Needed for c-togglebutton.
font-size: 1.3em;
}
}
@mixin cCtrlWrapper {
// Provides a wrapper around buttons and other controls
// Contains control and provides positioning context for contained menu/palette.
// Wraps --menu elements, contains button and menu
overflow: visible;
.c-menu {
// Default position of contained menu
top: 100%; left: 0;
}
&[class*='--menus-up'] {
.c-menu {
top: auto; bottom: 100%;
}
}
&[class*='--menus-left'] {
.c-menu {
left: auto; right: 0;
}
}
}
/********* Buttons */
// Optionally can include icon in :before via markup
.c-button,
.c-button--menu,
button {
@include cButton();
}
.c-button--menu {
$m: $interiorMargin;
&:before,
> * {
margin-right: $m;
}
&:after {
content: $glyph-icon-arrow-down;
font-family: symbolsfont;
opacity: 0.5;
}
}
/********* Icon Buttons */
.c-click-icon {
@include cClickIcon();
&--menu {
&:after {
content: $glyph-icon-arrow-down;
font-family: symbolsfont;
font-size: 0.6em;
margin-left: floor($interiorMarginSm * 0.8);
opacity: 0.5;
}
}
&--swatched {
// Color control, show swatch element
display: flex;
flex-flow: column nowrap;
align-items: center;
justify-content: center;
> [class*='swatch'] {
box-shadow: inset rgba(black, 0.2) 0 0 1px;
flex: 0 0 auto;
height: 4px;
width: 100%;
margin-top: 1px;
}
&:before {
// Reduce size of icon to make a bit of room
flex: 1 1 auto;
font-size: 1.1em;
}
}
}
/******************************************************** DISCLOSURE CONTROLS */
/********* Disclosure Button */
// Provides a downward arrow icon that when clicked displays a context menu
// Always placed AFTER an element
.c-disclosure-button {
@include cClickIcon();
margin-left: $interiorMarginSm;
&:before {
content: $glyph-icon-arrow-down;
font-family: symbolsfont;
font-size: 0.7em;
}
}
/********* Disclosure Triangle */
// Provides an arrow icon that when clicked expands an element to reveal its contents.
// Used in tree items. Always placed BEFORE an element.
.c-disclosure-triangle {
$d: 12px;
display: flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
width: $d;
position: relative;
&.is-enabled:before {
$s: .65;
content: $glyph-icon-arrow-right-equilateral;
display: block;
font-family: symbolsfont;
font-size: 1rem * $s;
position: absolute;
transform-origin: floor(($d / 2) * $s); // This is slightly better than 'center'
transition: transform 100ms ease-in-out;
}
&--expanded {
&:before {
transform: rotate(90deg);
}
}
}
/******************************************************** FORM ELEMENTS */
/********* 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;
padding-right: 0;
overflow: hidden;
transition: all 250ms ease;
white-space: nowrap;
&:not(:focus) {
text-overflow: ellipsis;
}
&:hover,
&:focus {
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;
}
}
.c-labeled-input {
// An input used in the Toolbar
// Assumes label is before the input
@include cControl();
input {
margin-left: $interiorMarginSm;
}
}
/******************************************************** HYPERLINKS AND HYPERLINK BUTTONS */
.c-hyperlink {
&--link {
color: $colorKey;
}
&--button {
@include cButton();
}
}
/******************************************************** MENUS */
@mixin menuOuter() {
border-radius: $basicCr;
background: $colorMenuBg;
text-shadow: $shdwMenuText;
padding: $interiorMarginSm;
box-shadow: $shdwMenu;
display: block;
position: absolute;
z-index: 70;
}
@mixin menuInner() {
color: $colorMenuFg;
li {
@include cControl();
justify-content: start;
color: $colorMenuFg;
display: flex;
padding: nth($menuItemPad, 1) nth($menuItemPad, 2);
transition: $transIn;
white-space: nowrap;
&:hover {
background: $colorMenuHovBg;
color: $colorMenuHovFg;
&:before {
color: $colorMenuHovIc;
}
}
&:before {
color: $colorMenuIc;
font-size: 1em;
margin-right: $interiorMargin;
}
}
}
.c-menu {
@include menuOuter();
@include menuInner();
}
.c-super-menu {
// Two column layout, menu items on left with detail of hover element on right
@include menuOuter();
display: flex;
padding: $interiorMarginLg;
flex-direction: row;
> [class*="__"] {
$m: $interiorMarginLg;
flex: 1 1 50%;
&:first-child {
margin-right: $m;
}
&:last-child {
border-left: 1px solid $colorInteriorBorder;
padding-left: $m;
}
}
&__menu {
@include menuInner();
overflow: auto;
ul {
margin-right: $interiorMarginSm; // Fend off scrollbar
}
li {
border-radius: $controlCr;
}
}
&__item-description {
display: flex;
flex-direction: column;
justify-content: stretch;
.l-item-description {
&__icon,
&__description {
//flex: 1 1 50%;
}
&__name,
&__description {
margin-top: $interiorMarginLg;
}
&__icon {
min-height: 20%;
margin: 10% 25%;
}
&__name {
flex: 0 0 auto;
font-size: 1.25em;
}
&__description {
font-size: $fontBaseSize;
}
}
}
}
/******************************************************** PALETTES */
.c-palette {
display: flex;
flex-flow: column nowrap;
&__items {
flex: 1 1 auto;
display: grid;
grid-template-columns: repeat(10, [col] auto );
grid-gap: 1px;
}
&__item {
$d: 16px;
border: 1px solid transparent;
cursor: pointer;
width: 16px; height: 16px;
transition: $transOut;
&:hover {
transition: $transIn;
$o: 0.7;
border-color: rgba($paletteItemBorderOuterColorSelected, $o);
box-shadow: inset rgba($paletteItemBorderInnerColorSelected, $o) 0 0 0 1px;
}
&.is-selected {
border-color: $paletteItemBorderOuterColorSelected !important;
border-width: 2px;
box-shadow: inset rgba($paletteItemBorderInnerColorSelected, 1) 0 0 0 1px;
}
}
&__item-none {
flex: 0 0 auto;
display: flex;
align-items: center;
margin-bottom: $interiorMarginSm;
.c-palette__item {
@include noColor();
border-color: $paletteItemBorderInnerColor;
margin-right: $interiorMarginSm;
}
}
}
/******************************************************** TOOLBAR */
.c-ctrl-wrapper {
@include cCtrlWrapper();
}
.c-toolbar,
.c-toolbar .c-ctrl-wrapper {
display: flex;
align-items: stretch;
}
.c-toolbar {
height: 24px; // Need to standardize the height
.c-click-icon {
@include cControl();
$pLR: $interiorMargin - 1;
$pTB: 2px;
color: $colorBodyFg;
padding: $pTB $pLR;
&--swatched {
padding-bottom: floor($pTB / 2);
width: 2em; // Standardize the width
}
&[class*='--caution'] {
&:before {
color: $colorBtnCautionBg;
}
&:hover {
background: rgba($colorBtnCautionBgHov, 0.2);
:before {
color: $colorBtnCautionBgHov;
}
}
}
}
.c-labeled-input {
font-size: 0.9em;
input[type='number'] {
width: 40px; // Number input sucks and must have size set using this method
}
+ .c-labeled-input {
margin-left: $interiorMargin;
}
}
}
/********* Button Sets */
.c-button-set {
// When one set is adjacent to another, provides a divider between
display: inline-flex;
flex: 0 0 auto;
> * {
// Assume buttons are immediate descendants
flex: 0 0 auto;
+ * {
// margin-left: $interiorMarginSm;
}
}
+ .c-button-set {
$m: $interiorMargin;
$b: 1px;
&:before {
content: '';
display: block;
width: $m + $b; // Allow for border
border-right: $b solid $colorInteriorBorder;
margin-right: $m;
}
}
&[class*='--strip'] {
// Buttons are smashed together with minimal margin
// c-buttons don't have border-radius between buttons, creates a tool-button-strip look
// c-click-icons get grouped more closely together
&[class^="c-button"] {
// Only apply the following to buttons that have background, eg. c-button
border-radius: 0;
+ * {
margin-left: 1px;
}
&:first-child {
border-top-left-radius: $controlCr;
border-bottom-left-radius: $controlCr;
}
&:last-child {
border-top-right-radius: $controlCr;
border-bottom-right-radius: $controlCr;
}
}
}
}

View File

@@ -28,7 +28,9 @@
}
/******************************* RESETS */
* {
*,
:before,
:after {
box-sizing: border-box;
}
@@ -41,6 +43,15 @@ div {
display: contents;
}
.u-space {
// Provides a separator space between elements
&--right {
+ [class*="__"] {
margin-left: $interiorMarginLg !important;
}
}
}
/******************************* BROWSER ELEMENTS */
body.desktop {
::-webkit-scrollbar {
@@ -78,6 +89,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;
@@ -95,8 +113,9 @@ body, html {
body {
-webkit-font-smoothing: subpixel-antialiased;
-moz-osx-font-smoothing: grayscale;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 13px;
font-size: $fontBaseSize;
font-weight: normal;
background-color: $colorBodyBg;
color: $colorBodyFg;
@@ -146,6 +165,59 @@ ol, ul {
padding-left: 0;
}
table {
border-spacing: 0;
border-collapse: collapse;
}
li {
list-style-type: none;
margin: 0;
padding: 0;
}
/******************************************************** HAS */
// Local Controls: Controls placed in proximity to or overlaid on components and views
body.desktop .has-local-controls {
// Provides hover ability to show local controls
&:hover [class*="local-controls--hidden"] {
transition: opacity 50ms ease-in-out;
opacity: 1;
pointer-events: inherit;
}
[class*="local-controls--hidden"] {
transition: opacity 500ms ease-in-out;
opacity: 0;
pointer-events: none;
}
}
//[class*="local-controls"] {
// // An explicit outer holder for controls. Typically placed in upper right.
// //font-size: 0.7rem;
// display: flex;
// align-items: center;
// justify-content: flex-end;
//
//
// &.h-local-controls-overlay-content {
// // Imagery controls
// $p: $interiorMargin;
// position: absolute;
// top: $p; right: $p;
// z-index: 2;
// }
//.l-btn-set,
//.s-button {
// &:not(:first-child) {
// margin-left: $interiorMargin;
// }
//}
//}
/************************** LEGACY */
mct-container {
@@ -218,21 +290,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;
@@ -315,3 +372,8 @@ a.disabled {
.t-imagery {
display: contents;
}
.t-frame-outer {
min-width: 200px;
min-height: 200px;
}

View File

@@ -1,24 +1,3 @@
/*****************************************************************************
* 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.
*****************************************************************************/
@mixin glyphBefore($unicode, $family: 'symbolsfont') {
&:before {
content: $unicode;
@@ -33,16 +12,21 @@
}
}
[class*="icon-"].labeled {
// Moved from .s-button and generalized
&:before {
// Fend off label from icon when it's included
margin-right: $interiorMarginSm;
@mixin glyphBg($glyphUrl) {
background-image: $glyphUrl;
background-position: center;
background-size: contain;
background-repeat: no-repeat;
}
[class*="icon-"] {
&:before, &:after
{
-webkit-font-smoothing: antialiased;
}
}
/************************** 16 PX CLASSES */
.icon-alert-rect { @include glyphBefore($glyph-icon-alert-rect); }
.icon-alert-triangle { @include glyphBefore($glyph-icon-alert-triangle); }
.icon-arrow-down { @include glyphBefore($glyph-icon-arrow-down); }
@@ -107,7 +91,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); }
@@ -124,6 +108,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); }
@@ -162,3 +147,36 @@
.icon-folder-12px { @include glyphBefore($glyph-icon-folder,'symbolsfont-12px'); }
.icon-list-view-12px { @include glyphBefore($glyph-icon-list-view,'symbolsfont-12px'); }
.icon-grippy-12px { @include glyphBefore($glyph-icon-grippy,'symbolsfont-12px'); }
/************************** GLYPH BG CLASSES */
.bg-icon-activity { @include glyphBg($bg-icon-activity); }
.bg-icon-activity-mode { @include glyphBg($bg-icon-activity-mode); }
.bg-icon-autoflow-tabular { @include glyphBg($bg-icon-autoflow-tabular); }
.bg-icon-clock { @include glyphBg($bg-icon-clock); }
.bg-icon-database { @include glyphBg($bg-icon-database); }
.bg-icon-database-query { @include glyphBg($bg-icon-database-query); }
.bg-icon-dataset { @include glyphBg($bg-icon-dataset); }
.bg-icon-datatable { @include glyphBg($bg-icon-datatable); }
.bg-icon-dictionary { @include glyphBg($bg-icon-dictionary); }
.bg-icon-folder { @include glyphBg($bg-icon-folder); }
.bg-icon-image { @include glyphBg($bg-icon-image); }
.bg-icon-layout { @include glyphBg($bg-icon-layout); }
.bg-icon-object { @include glyphBg($bg-icon-object); }
.bg-icon-object-unknown { @include glyphBg($bg-icon-object-unknown); }
.bg-icon-packet { @include glyphBg($bg-icon-packet); }
.bg-icon-page { @include glyphBg($bg-icon-page); }
.bg-icon-plot-overlay { @include glyphBg($bg-icon-plot-overlay); }
.bg-icon-plot-stacked { @include glyphBg($bg-icon-plot-stacked); }
.bg-icon-session { @include glyphBg($bg-icon-session); }
.bg-icon-tabular { @include glyphBg($bg-icon-tabular); }
.bg-icon-tabular-lad { @include glyphBg($bg-icon-tabular-lad); }
.bg-icon-tabular-lad-set { @include glyphBg($bg-icon-tabular-lad-set); }
.bg-icon-tabular-realtime { @include glyphBg($bg-icon-tabular-realtime); }
.bg-icon-tabular-scrolling { @include glyphBg($bg-icon-tabular-scrolling); }
.bg-icon-telemetry { @include glyphBg($bg-icon-telemetry); }
.bg-icon-timeline { @include glyphBg($bg-icon-timeline); }
.bg-icon-timer { @include glyphBg($bg-icon-timer); }
.bg-icon-topic { @include glyphBg($bg-icon-topic); }
.bg-icon-box-with-dashed-lines { @include glyphBg($bg-icon-box-with-dashed-lines); }
.bg-icon-summary-widget { @include glyphBg($bg-icon-summary-widget); }
.bg-icon-notebook { @include glyphBg($bg-icon-notebook); }

View File

@@ -20,6 +20,8 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
// VERSION MANUALLY RESTORED FROM VUE-LAYOUT
/************************** VISUALS */
@mixin ancillaryIcon($d, $c) {
// Used for small icons used in combination with larger icons,
@@ -53,6 +55,37 @@
background-size: $bgsize $bgsize;
}
@mixin noColor() {
// A "no fill/stroke" selection option. Used in palettes.
$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;
}
@mixin bgTicks($c: $colorBodyFg, $repeatDir: 'x') {
$deg: 90deg;
@if ($repeatDir != 'x') {
$deg: 0deg;
$repeatDir: repeat-y;
} @else {
$repeatDir: repeat-x;
}
background-image: linear-gradient($deg,
$c 1px, transparent 1px,
transparent 100%
);
background-repeat: $repeatDir;
}
@mixin bgVertStripes($c: yellow, $a: 0.1, $d: 40px) {
@include background-image(linear-gradient(-90deg,
rgba($c, $a) 0%, rgba($c, $a) 50%,
@@ -62,6 +95,41 @@
background-size: $d $d;
}
/************************** LAYOUT */
@mixin abs($m: 0) {
position: absolute;
top: $m; right: $m; bottom: $m; left: $m;
}
@mixin gridTwoColumn() {
display: grid;
grid-row-gap: 0;
grid-template-columns: 1fr 2fr;
align-items: start;
[class*="header"] {
border-radius: $smallCr;
background-color: $colorInspectorSectionHeaderBg;
color: $colorInspectorSectionHeaderFg;
margin: 0 0 $interiorMarginSm 0;
padding: $interiorMarginSm $interiorMargin;
&:not(:first-child) {
// Allow multiple headers within a component
margin-top: $interiorMarginLg;
}
}
[class*="span-all"],
[class*="header"] {
@include gridTwoColumnSpanCols();
}
}
@mixin gridTwoColumnSpanCols() {
grid-column: 1 / 3;
}
/************************** TEXT */
@mixin ellipsize() {
overflow: hidden;
@@ -80,13 +148,10 @@
}
/************************** CONTROLS, BUTTONS */
@mixin nice-input($bg: $colorInputBg, $fg: $colorInputFg, $shdw: rgba(black, 0.5) 0 0px 2px) {
@mixin input-base() {
appearance: none;
background: $bg;
border: none;
color: $fg;
border-radius: $controlCr;
box-shadow: inset $shdw;
outline: none;
&:focus {
@@ -99,6 +164,13 @@
}
}
@mixin nice-input($bg: $colorInputBg, $fg: $colorInputFg, $shdw: rgba(black, 0.5) 0 0px 2px) {
@include input-base();
background: $bg;
color: $fg;
box-shadow: inset $shdw;
}
@mixin button($bg: $colorBtnBg, $fg: $colorBtnFg, $radius: $controlCr, $shdw: none) {
background: $bg;
color: $fg;
@@ -106,6 +178,63 @@
box-shadow: $shdw;
}
@mixin wrappedInput() {
// An input that is wrapped. Optionally includes a __label or icon element.
// Based on .c-search.
@include nice-input();
display: flex;
align-items: center;
padding-left: 4px;
padding-right: 4px;
&:before,
[class*='__label'] {
opacity: 0.5;
}
&:before {
// Adds an icon. Content defined in class.
direction: rtl; // Aligns glyph to right-hand side of container, for transition
display: block;
font-family: symbolsfont;
flex: 0 0 auto;
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,
input[type='text'],
input[type='search'],
input[type='number'] {
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
}
&.is-active {
&:before {
padding: 2px 0px;
width: 0px;
}
}
}
/************************** MATH */
@function percentToDecimal($p) {
@return $p / 100%;

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

@@ -56,7 +56,7 @@
//
//!********************************* VIEWS *!
@import "../styles/fixed-position";
@import "../styles/lists/tabular";
//@import "../styles/lists/tabular";
@import "../styles/plots/plots-main";
@import "../styles/plots/legend";
@import "../styles/iframe";
@@ -64,7 +64,7 @@
@import "../styles/items/item";
@import "../styles/mobile/item";
@import "../styles/table";
@import "../styles/notebook/notebook";
//@import "../styles/notebook/notebook";
//
//!********************************* TO BE MOVED *!
@import "../styles/autoflow";
@@ -75,4 +75,5 @@
//!********************************* APP STARTUP *!
//@import "../styles/app-start";
@import "../styles/conductor/time-conductor-snow";
@import "../styles/conductor/time-conductor-snow";
//@import "../styles/notebook/notebook-snow";

View File

@@ -0,0 +1,385 @@
/*********************************************** NOTEBOOK */
@import "sass-base.scss";
.c-notebook {
//@include test(orange);
display: flex;
flex-direction: column;
overflow: hidden;
position: absolute;
top: 0px;
right: 0px;
bottom: 0px;
left: 0px;
> [class*="__"] + [class*="__"] {
margin-top: $interiorMargin;
}
&__head,
&__controls,
&__drag-area,
&__entries {
display: flex;
flex-wrap: nowrap;
}
&__head,
&__drag-area,
&__controls {
flex: 0 0 auto;
}
&__head {
[class*="__"] + [class*="__"] {
margin-left: $interiorMargin;
}
}
&__search {
flex: 1 1 auto;
}
&__drag-area {
background: rgba($colorKey, 0.1);
border: 1px dashed rgba($colorKey, 0.7);
border-radius: $controlCr;
color: rgba($colorBodyFg, 0.7);
padding: 10px;
cursor: pointer;
&:before {
margin-right: 7px !important;
}
[class*="__label"] {
font-style: italic;
@include ellipsize();
}
&:hover {
background: rgba($colorKey, 0.2);
color: $colorBodyFg;
}
&.drag-active,
&.is-active {
// Not currently working
border-color: $colorKey;
}
body.mobile & {
display: none;
}
}
&__entries {
flex-direction: column;
flex: 1 1 auto;
padding-right: $interiorMarginSm;
overflow-x: hidden;
overflow-y: scroll;
[class*="__entry"] + [class*="__entry"] {
margin-top: $interiorMarginSm;
}
}
}
/****************************** ENTRIES */
.c-ne {
// A Notebook entry
$p: $interiorMarginSm;
@include discreteItem();
display: flex;
//flex-wrap: wrap;
padding: $interiorMarginSm $interiorMarginSm $interiorMarginSm $interiorMargin;
&__time,
&__text,
&__local-controls {
padding-top: $p;
padding-bottom: $p;
}
&__time,
&__embed__time {
opacity: 0.7;
}
&__time-and-content {
display: flex;
flex: 1 1 auto;
flex-wrap: wrap;
}
&__time {
flex: 0 1 auto;
min-width: 130px;
margin-right: $interiorMarginLg;
* {
white-space: nowrap;
}
}
&__content {
flex: 1 1 auto;
> [class*="__"] + [class*="__"] {
margin-top: $interiorMarginSm;
}
}
&__text {
min-height: 24px; // Needed in Firefox when field is blank
white-space: pre-wrap;
&.is-blank-notebook-entry {
&:not(:focus):before {
content: 'Blank entry';
font-style: italic;
opacity: 0.5;
}
}
}
&__embeds {
flex-wrap: wrap;
> [class*="__embed"] {
margin: 0 $interiorMarginSm $interiorMarginSm 0;
}
}
}
/****************************** EMBEDS */
@mixin snapThumb() {
// LEGACY: TODO: refactor when .snap-thumb in New Entry dialog is refactored
$d: 50px;
border: 1px solid $colorInteriorBorder;
cursor: pointer;
width: $d;
height: $d;
border-radius: 5px;
overflow: hidden;
img {
height: 100%;
width: 100%;
}
}
.snap-thumb {
// LEGACY,
@include snapThumb();
}
.c-ne__embed {
@include discreteItemInnerElem();
display: inline-flex;
flex: 0 0 auto;
padding: $interiorMargin;
[class*="__"] + [class*="__"] {
margin-left: $interiorMargin;
}
&__info {
display: flex;
flex-direction: column;
a {
color: $colorKey;
}
}
&__name,
&__link {
// Holds __link and __context-available
display: flex;
align-items: center;
}
&__link {
&:before {
display: block;
font-size: 0.85em;
margin-right: $interiorMarginSm;
}
}
&__context-available {
font-size: 0.7em;
margin-left: $interiorMarginSm;
}
&__snap-thumb {
@include snapThumb();
}
}
/****************************** SNAPSHOTTING */
// LEGACY: TODO: refactor these names
.t-contents,
.snap-annotation {
overflow: hidden;
}
.s-status-taking-snapshot,
.overlay.snapshot {
// Handle overflow-y issues with tables and html2canvas
.l-sticky-headers .l-tabular-body { overflow: auto; }
}
/****************************** PAINTERRO OVERRIDES */
.annotation-dialog .abs.editor {
border-radius: 0;
}
#snap-annotation {
$m: $interiorMargin;
display: flex;
flex-direction: column;
position: absolute;
top: $m; right: 0; bottom: $m; left: 0; // LEGACY, deal with .editor border-radius clipping stuff
}
#snap-annotation-wrapper,
#snap-annotation-bar {
position: relative;
top: auto; right: auto; bottom: auto; left: auto;
}
#snap-annotation-wrapper {
background: rgba($colorBodyFg, 0.1);
border: 1px solid $colorInteriorBorder;
order: 2;
flex: 10 0 auto;
}
#snap-annotation-bar {
// Holds tool buttons, color selectors, etc.
$h: 22px;
$fs: 0.8rem;
order: 1;
flex: 0 0 auto;
height: auto;
background-color: transparent !important;
margin-bottom: $interiorMargin;
> div > span {
display: flex;
align-items: center;
font-size: $fs;
}
> div,
> div > span,
.ptro-icon-btn,
.ptro-named-btn,
.ptro-color-btn,
.ptro-bordered-btn,
.ptro-tool-ctl-name,
.ptro-color-btn,
.tool-controls,
.ptro-input {
// Lot of resets for crappy CSS in Painterro
&:first-child {
margin-left: 0 !important;
}
display: inline-block;
font-family: inherit;
font-size: $fs !important;
height: $h !important;
margin: 0 0 0 5px;
position: relative;
width: auto !important;
line-height: $h !important;
top: auto;
right: auto;
bottom: auto;
left: auto;
vertical-align: top;
}
.ptro-tool-ctl-name {
border-radius: 0;
background: none;
top: auto;
font-family: inherit;
padding: 0;
}
.ptro-color-btn {
width: $h !important;
}
.ptro-color-control,
.ptro-icon-btn,
.ptro-named-btn {
// Buttons in toolbar. Why the f* they're named like this is a mystery
background-color: $colorBtnBg;
color: $colorBtnFg;
padding: 0 $interiorMargin;
&:hover {
background: $colorBtnBgHov;
color: $colorBtnFgHov;
}
i {
display: contents;
font-size: $fs * 1.2;
line-height: inherit;
}
}
.ptro-color-active-control {
background: $colorBtnMajorBg !important;
color: $colorBtnMajorFg !important;
}
.ptro-info,
.ptro-btn-color-checkers-bar,
*[title="Font name"],
*[title="Stroke color"],
*[title="Stroke width"],
*[data-id="fontName"],
*[data-id="fontStrokeSize"],
*[data-id="stroke"] {
display: none;
}
}
/****************************** MOBILE */
body.mobile {
.c-notebook__drag-area { display: none; }
.c-notebook__entry {
[class*="local-controls"] {
display: none;
}
}
&.phone.portrait {
.c-notebook__head,
.c-notebook__entry {
flex-direction: column;
> [class*="__"] + [class*="__"] {
margin-left: 0;
margin-top: $interiorMargin;
}
}
.c-notebook__entry {
[class*="text"] {
min-height: 0;
padding: 0;
pointer-events: none;
}
}
}
}

View File

@@ -30,7 +30,8 @@
.child-frame {
.has-control-bar {
$btnExportH: $btnFrameH;
.l-control-bar {
.l-control-bar,
.c-control-bar {
display: none;
}
.l-view-section {

View File

@@ -0,0 +1,29 @@
<template>
<div class="c-menu">
<ul>
<li v-for="item in contextMenuItems"
:class="item.class"
:title="item.title">
{{ item.name }}
</li>
</ul>
</div>
</template>
<script>
export default {
data: function () {
return {
contextMenuItems: [
{name: 'Open In New Tab', class: 'icon-new-window', title: 'Open in a new browser tab'},
{name: 'Preview', class: 'hide-in-t-main-view icon-eye-open', title: 'Preview in large dialog'},
{name: 'Edit Properties...', class: 'major icon-pencil', title: 'Edit properties of this object.'},
{name: 'Duplicate', class: 'icon-duplicate', title: 'Duplicate object to another location.'},
{name: 'Create Link', class: 'icon-link', title: 'Create Link to object in another location.'},
{name: 'Export as JSON', class: 'icon-export', title: 'Export as JSON'},
{name: 'Import from JSON', class: 'icon-import', title: 'Import from JSON'}
]
}
}
}
</script>

View File

@@ -0,0 +1,98 @@
<template>
<div class="c-ctrl-wrapper">
<div class="c-create-button c-button--menu c-button--major icon-plus"
@click="toggleCreateMenu">
<span class="c-button__label">Create</span>
</div>
<div class="c-create-menu c-super-menu"
v-if="showCreateMenu">
<div class="c-super-menu__menu">
<ul>
<li v-for="(item, index) in items"
:key="index"
:class="item.class"
:title="item.title"
@mouseover="showItemDescription(item)">
{{ item.name }}
</li>
</ul>
</div>
<div class="c-super-menu__item-description">
<div :class="['l-item-description__icon', 'bg-' + selectedMenuItem.class]"></div>
<div class="l-item-description__name">{{selectedMenuItem.name}}</div>
<div class="l-item-description__description">{{selectedMenuItem.title}}</div>
</div>
</div>
</div>
</template>
<style lang="scss">
@import "~styles/sass-base";
.c-create-button,
.c-create-menu {
font-size: 1.1em;
}
.c-create-button .c-button__label {
text-transform: $createBtnTextTransform;
}
.c-create-menu {
max-height: 80vh;
max-width: 500px;
min-height: 250px;
z-index: 70;
[class*="__icon"] {
filter: $colorKeyFilter;
}
[class*="__item-description"] {
min-width: 200px;
}
}
</style>
<script>
export default {
inject: ['openmct'],
props: {
showCreateMenu: {
type: Boolean,
default: false
}
},
methods: {
toggleCreateMenu: function () {
this.showCreateMenu = !this.showCreateMenu;
},
showItemDescription: function (menuItem) {
this.selectedMenuItem = menuItem;
}
},
data: function() {
let items = [];
this.openmct.types.listKeys().forEach(key => {
let menuItem = openmct.types.get(key).definition;
if (menuItem.creatable) {
let menuItemTemplate = {
name: menuItem.name,
class: menuItem.cssClass,
title: menuItem.description
};
items.push(menuItemTemplate);
}
});
return {
items: items,
selectedMenuItem: {}
}
}
}
</script>

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

@@ -28,7 +28,6 @@
<style lang="scss">
@import "~styles/sass-base";
$hitMargin: 4px;
/**************************** BASE - MOBILE AND DESKTOP */
.l-multipane {
display: flex;
@@ -86,6 +85,11 @@
transition: opacity 150ms ease;
opacity: 0;
pointer-events: none;
> * {
min-width: 0 !important;
min-height: 0 !important;
}
}
}
@@ -110,7 +114,6 @@
padding: $interiorMargin;
pointer-events: inherit;
transition: opacity 250ms ease 250ms;
overflow: auto;
.l-pane__contents {
// Don't pad all nested __contents
@@ -149,7 +152,7 @@
&__collapse-button {
$m: 2px;
$h: nth($splitterBtnD, 1) - ($m * 2);
$h: 12px;
color: $splitterBtnColorFg;
flex: 0 0 nth($splitterBtnD, 1);
font-size: $h * .9;
@@ -159,27 +162,24 @@
&:after {
// Close icon
background: $splitterBtnColorFg;
border-radius: 2px;
color: $splitterBtnColorBg;
content: $glyph-icon-minus;
background: $colorBtnBg;
border-radius: $smallCr;
color: $colorBtnFg;
content: $glyph-icon-arrow-right-equilateral;
display: block;
font-family: symbolsfont;
font-size: .8em;
height: $h;
line-height: $h;
padding: 0 7px;
font-size: 6px;
line-height: 90%;
padding: 3px 15px;
position: absolute;
right: $m;
top: $m;
text-align: right;
transition: $transOut;
z-index: -1;
}
&:hover {
background: rgba(black, 0.1);
//color: $splitterBtnColorHoverFg;
&:after {
background: $splitterBtnColorHoverBg;
color: $splitterBtnColorHoverFg;
@@ -192,6 +192,7 @@
// Name of the pane
@include ellipsize();
display: block;
padding-right: nth($splitterBtnD, 2) + $interiorMargin; // Force label to ellipsis
text-transform: uppercase;
transform-origin: top left;
flex: 1 0 90%;
@@ -200,6 +201,10 @@
&--resizing {
// User is dragging the handle and resizing a pane
@include userSelectNone();
+ .l-pane {
@include userSelectNone();
}
}
&[class*="--collapsed"] {
@@ -216,10 +221,6 @@
background: $splitterBtnColorFg;
color: $splitterBtnColorBg;
&:after {
content: $glyph-icon-plus;
}
&:hover {
background: $splitterBtnColorHoverBg !important;
}
@@ -241,9 +242,9 @@
&:before {
// Extended hit area
top: 0;
right: $hitMargin * -1;
right: $splitterHandleHitMargin * -1;
bottom: 0;
left: $hitMargin * -1;
left: $splitterHandleHitMargin * -1;
}
}
@@ -267,7 +268,6 @@
right: auto;
transform: translateX(-50%);
width: auto;
}
}
}
@@ -278,6 +278,14 @@
left: 0;
transform: translateX(floor($splitterHandleD / -2)); // Center over the pane edge
}
&[class*="--collapsed"] {
> .l-pane__collapse-button {
&:after {
transform: translateX(-50%) scaleX(-1);
}
}
}
}
/************************** Horizontal Splitter After */
@@ -286,6 +294,12 @@
right: 0;
transform: translateX(floor($splitterHandleD / 2));
}
&:not([class*="--collapsed"]) {
> .l-pane__collapse-button:after {
transform: scaleX(-1);
}
}
}
}
@@ -299,25 +313,43 @@
&:before {
// Extended hit area
left: 0;
top: $hitMargin * -1;
top: $splitterHandleHitMargin * -1;
right: 0;
bottom: $hitMargin * -1;
bottom: $splitterHandleHitMargin * -1;
}
}
/************************** Vertical Splitter Before */
// Pane collapses downward
&[class*="-before"] {
> .l-pane__handle {
top: 0;
transform: translateY(floor($splitterHandleD / -2)); // Center over the pane edge
transform: translateY(floor($splitterHandleD / -1));
}
> .l-pane__collapse-button:after {
content: $glyph-icon-arrow-down;
}
&.l-pane--collapsed {
> .l-pane__collapse-button:after {
transform: scaleY(-1);
}
}
}
/************************** Vertical Splitter After */
// Pane collapses upward. Not sure we'll ever use this...
&[class*="-after"] {
> .l-pane__handle {
bottom: 0;
transform: translateY(floor($splitterHandleD / 2)); // Center over the pane edge
transform: translateY(floor($splitterHandleD / 1));
}
&:not(.l-pane--collapsed) > .l-pane__collapse-button {
&:after {
transform: scaleY(-1);
}
}
}
}

View File

@@ -1,56 +1,29 @@
<template>
<div class="c-search"
:class="{ 'is-active': active === true }">
<input class="c-search__input" type="search"
v-bind:value="searchInput"
@input="handleInput($event)"/>
<input class="c-search__input"
tabindex="10000"
type="search"
v-bind="$attrs"
v-bind:value="value"
v-on="inputListeners"/>
<a class="c-search__clear-input icon-x-in-circle"
v-on:click="clearInput"></a>
</div>
</template>
<style lang="scss">
@import "~styles/sass-base";;
@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; // 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: 0 1 100%;
padding-left: 2px !important;
padding-right: 2px !important;
min-width: 10px; // Must be set to allow input to collapse below browser min
}
&__clear-input {
@@ -58,11 +31,6 @@
}
&.is-active {
&:before {
padding: 2px 0px;
width: 0px;
}
.c-search__clear-input {
display: block;
}
@@ -71,26 +39,36 @@
</style>
<script>
/* Emits input and clear events */
export default {
inheritAttrs: false,
props: {
value: String
},
computed: {
inputListeners: function () {
let vm = this;
return Object.assign({},
this.$listeners,
{
input: function (event) {
vm.$emit('input', event.target.value);
vm.active = (event.target.value.length > 0);
}
}
)
}
},
data: function() {
return {
searchInput: '',
active: false
}
},
methods: {
handleInput(e) {
// Grab input as the user types it
// and set 'active' based on input length > 0
this.searchInput = e.target.value;
this.active = (this.searchInput.length > 0);
},
clearInput() {
// Clear the user's input and set 'active' to false
this.searchInput = '';
this.value = '';
this.$emit('clear','');
this.active = false;
}
}

View File

@@ -1,133 +0,0 @@
<template>
<div class="c-splitter" :class="{
'c-splitter-vertical' : align === 'vertical',
'c-splitter-horizontal' : align === 'horizontal',
'c-splitter-collapse-left' : collapse === 'to-left',
'c-splitter-collapse-right' : collapse === 'to-right',
}">
<a class="c-splitter__btn"></a>
</div>
</template>
<style lang="scss">
@import "~styles/constants";
@import "~styles/constants-snow";
@import "~styles/mixins";
@import "~styles/glyphs";
$c: #06f;
$size: $splitterHandleD;
$margin: 0px;
$hitMargin: 4px;
.c-splitter {
background: $colorSplitterBg;
transition: $transOut;
&:before {
// Bigger hit area
//@include test();
content: '';
display: block;
position: absolute;
z-index: 1;
}
&:active, &:hover {
transition: $transIn;
}
&:active {
background: $colorSplitterActive;
}
&:hover {
background: $colorSplitterHover;
}
&-vertical {
cursor: col-resize;
width: $size;
margin: 0 $margin;
&:before {
top: 0;
right: $hitMargin * -1;
bottom: 0;
left: $hitMargin * -1;
}
}
&-horizontal {
cursor: row-resize;
height: $size;
margin: $margin 0;
&:before {
top: $hitMargin * -1;
right: 0;
bottom: $hitMargin * -1;
left: 0;
}
}
}
.c-splitter__btn {
// Collapse button
background: $colorSplitterBg;
display: none; // Only display if splitter is collapsible, see below
width: $splitterD;
height: 40px;
transition: $transOut;
&:active, &:hover {
transition: $transIn;
}
&:hover {
background: $colorSplitterHover;
}
&:active {
background: $colorSplitterActive;
}
[class*="collapse"] & {
display: flex;
align-items: center;
justify-content: center;
position: absolute;
z-index: 3;
&:before {
color: $colorSplitterFg;
display: block;
font-size: 0.8em;
font-family: symbolsfont;
}
}
[class*="collapse-left"] & {
border-bottom-left-radius: $controlCr;
right: 0;
&:before {
content: $glyph-icon-arrow-left;
}
}
[class*="collapse-right"] & {
border-bottom-right-radius: $controlCr;
left: 0;
&:before {
content: $glyph-icon-arrow-right;
}
}
}
</style>
<script>
export default {
props: {
align: String,
collapse: String
}
}
</script>

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

@@ -1,43 +1,12 @@
<template>
<span class="c-view-control"
<span class="c-disclosure-triangle"
:class="{
'c-view-control--expanded' : expanded,
'c-disclosure-triangle--expanded' : expanded,
'is-enabled' : enabled
}"
@click="toggle"></span>
</template>
<style lang="scss">
@import "~styles/sass-base";;
.c-view-control {
$d: 12px;
display: flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
width: $d;
position: relative;
&.is-enabled:before {
$s: .65;
content: $glyph-icon-arrow-right-equilateral;
display: block;
font-family: symbolsfont;
font-size: 1rem * $s;
position: absolute;
transform-origin: floor(($d / 2) * $s); // This is slightly better than 'center'
transition: transform 100ms ease-in-out;
}
&--expanded {
&:before {
transform: rotate(90deg);
}
}
}
</style>
<script>
export default {
props: {

View File

@@ -0,0 +1,33 @@
<template>
<div></div>
</template>
<script>
export default {
inject: ['openmct'],
mounted() {
let openmct = this.openmct;
let $injector = openmct.$injector;
let angular = openmct.$angular;
let templateLinker = $injector.get('templateLinker');
let templateMap = {};
$injector.get('templates[]').forEach((t) => {
templateMap[t.key] = templateMap[t.key] || t;
});
let $rootScope = $injector.get('$rootScope');
this.$scope = $rootScope.$new();
templateLinker.link(
this.$scope,
angular.element(this.$el),
templateMap.elementsPool
);
},
destroyed() {
this.$scope.$destroy();
}
}
</script>

View File

@@ -0,0 +1,167 @@
<template>
<multipane class="c-inspector"
type="vertical">
<pane class="c-inspector__properties">
<properties></properties>
<location></location>
<inspector-view></inspector-view>
</pane>
<pane class="c-inspector__elements"
handle="before"
label="Elements">
<elements></elements>
</pane>
</multipane>
</template>
<style lang="scss">
@import "~styles/sass-base";
.c-inspector {
min-width: 150px;
> [class*="__"] {
min-height: 50px;
+ [class*="__"] {
// Margin between elements
margin-top: $interiorMargin;
}
> .l-pane__contents {
overflow: auto;
> * {
// Fend off scrollbar
margin-right: $interiorMarginSm;
}
}
}
&__elements {
// LEGACY TODO: Refactor when markup is updated, fix scrolling
// so that only tree holder handles overflow
height: 200px;
.tree-item {
.t-object-label {
// Elements pool is a flat list, so don't indent items.
left: 0;
}
}
}
/************************************************************** LEGACY */
// TODO: refactor when markup can be converted
.inspector-location {
display: inline-block;
.location-item {
$h: 1.2em;
box-sizing: border-box;
cursor: pointer;
display: inline-block;
line-height: $h;
position: relative;
padding: 2px 4px;
.t-object-label {
.t-item-icon {
height: $h;
margin-right: $interiorMarginSm;
}
}
&:hover {
background: $colorItemTreeHoverBg;
color: $colorItemTreeHoverFg;
.icon {
color: $colorItemTreeIconHover;
}
}
}
&:not(.last) .t-object-label .t-title-label:after {
color: pushBack($colorInspectorFg, 15%);
content: '\e904';
display: inline-block;
font-family: symbolsfont;
font-size: 8px;
font-style: normal !important;
line-height: inherit;
margin-left: $interiorMarginSm;
width: 4px;
}
}
}
.c-properties {
@include gridTwoColumn();
+ .c-properties {
// Margin between components
margin-top: $interiorMarginLg;
}
&__section,
&__row {
display: contents;
}
&__row + &__row {
> [class*="__"] {
// Row borders, effected via border-top on child elements of the row
border-top: 1px solid $colorInspectorSectionHeaderBg;
}
}
&__header {
font-size: .85em;
text-transform: uppercase;
}
&__label,
&__value {
padding: 3px $interiorMarginLg 3px 0;
}
&__label {
color: $colorInspectorPropName;
&[title] {
// When a cell has a title, assume it's helpful text
cursor: help;
}
}
&__value {
color: $colorInspectorPropVal;
word-break: break-all;
&:first-child {
// If there is no preceding .label element, make value span columns
@include gridTwoColumnSpanCols();
}
}
}
</style>
<script>
import multipane from '../controls/multipane.vue';
import pane from '../controls/pane.vue';
import Elements from './Elements.vue';
import Location from './Location.vue';
import Properties from './Properties.vue';
import InspectorView from './InspectorView.vue';
export default {
inject: ['openmct'],
components: {
multipane,
pane,
Elements,
Properties,
Location,
InspectorView
}
}
</script>

View File

@@ -0,0 +1,32 @@
<template>
<div class="c-properties"></div>
</template>
<style>
</style>
<script>
export default {
inject: ['openmct'],
mounted() {
this.openmct.selection.on('change', this.updateSelection);
this.updateSelection(this.openmct.selection.get());
},
destroyed() {
this.openmct.selection.off('change', this.updateSelection);
},
methods: {
updateSelection(selection) {
if (this.selectedView && this.selectedView.destroy) {
this.selectedView.destroy();
}
this.$el.innerHTML = '';
this.selectedView = this.openmct.inspectorViews.get(selection);
if (!this.selectedView) {
return;
}
this.selectedView.show(this.$el);
}
}
}
</script>

View File

@@ -0,0 +1,42 @@
<template>
<div class="c-properties c-properties--location">
<div class="c-properties__header" title="The location of this linked object.">Location</div>
<ul class="c-properties__section">
<li class="c-properties__row">
<div class="c-properties__label">This Link</div>
<div class="c-properties__value">TODO</div>
</li>
<li class="c-properties__row">
<div class="c-properties__label">Original</div>
<div class="c-properties__value">TODO</div>
</li>
</ul>
</div>
</template>
<script>
export default {
inject: ['openmct'],
data() {
return {
domainObject: {}
}
},
mounted() {
this.openmct.selection.on('change', this.updateSelection);
this.updateSelection(this.openmct.selection.get());
},
beforeDestroy() {
this.openmct.selection.off('change', this.updateSelection);
},
methods: {
updateSelection(selection) {
if (selection.length === 0) {
this.domainObject = {};
return;
}
this.domainObject = selection[0].context.item;
}
}
}
</script>

View File

@@ -0,0 +1,96 @@
<template>
<div class="c-properties c-properties--properties">
<div class="c-properties__header">Properties</div>
<ul class="c-properties__section">
<li class="c-properties__row">
<div class="c-properties__label">Title</div>
<div class="c-properties__value">{{ domainObject.name }}</div>
</li>
<li class="c-properties__row">
<div class="c-properties__label">Type</div>
<div class="c-properties__value">{{ typeName }}</div>
</li>
<li class="c-properties__row" v-if="domainObject.created">
<div class="c-properties__label">Created</div>
<div class="c-properties__value">{{ domainObject.created }}</div>
</li>
<li class="c-properties__row" v-if="domainObject.modified">
<div class="c-properties__label">Modified</div>
<div class="c-properties__value">{{ domainObject.modified }}</div>
</li>
<li class="c-properties__row"
v-for="prop in typeProperties"
:key="prop.name">
<div class="c-properties__label">{{ prop.name }}</div>
<div class="c-properties__value">{{ prop.value }}</div>
</li>
</ul>
</div>
</template>
<script>
export default {
inject: ['openmct'],
data() {
return {
domainObject: {}
}
},
computed: {
type() {
return this.openmct.types.get(this.domainObject.type);
},
typeName() {
if (!this.type) {
return `Unknown: ${this.domainObject.type}`;
}
return this.type.definition.name;
},
typeProperties() {
if (!this.type) {
return [];
}
let definition = this.type.definition;
if (!definition.form || definition.form.length === 0) {
return [];
}
return definition.form
.map((field) => {
let path = field.property
if (typeof path === 'string') {
path = [path];
}
return {
name: field.name,
path
};
})
.filter(field => Array.isArray(field.path))
.map((field) => {
return {
name: field.name,
value: field.path.reduce((object, field) => {
return object[field];
}, this.domainObject)
};
});
}
},
mounted() {
this.openmct.selection.on('change', this.updateSelection);
this.updateSelection(this.openmct.selection.get());
},
beforeDestroy() {
this.openmct.selection.off('change', this.updateSelection);
},
methods: {
updateSelection(selection) {
if (selection.length === 0) {
this.domainObject = {};
return;
}
this.domainObject = selection[0].context.item;
}
}
}
</script>

View File

@@ -0,0 +1,177 @@
<template>
<div class="l-browse-bar">
<div class="l-browse-bar__start">
<a class="l-browse-bar__nav-to-parent-button c-click-icon icon-pointer-left"></a>
<div class="l-browse-bar__object-name--w"
:class="type.cssClass">
<span
class="l-browse-bar__object-name c-input-inline"
v-on:blur="updateName"
contenteditable>
{{ domainObject.name }}
</span>
</div>
<div class="l-browse-bar__context-actions c-disclosure-button"></div>
</div>
<div class="l-browse-bar__end">
<div class="l-browse-bar__view-switcher c-menu-button--w c-menu-button--menus-left"
v-if="views.length > 1">
<div class="c-button--menu"
:class="currentView.cssClass"
title="Switch view type"
@click="toggleViewMenu">
<span class="c-button__label">
{{ currentView.name }}
</span>
</div>
<div class="c-menu" v-show="showViewMenu">
<ul>
<li v-for="(view, index) in views"
@click="setView(view)"
:key="index"
:class="view.cssClass"
:title="view.name">
{{ view.name }}
</li>
</ul>
</div>
</div>
<!-- Action buttons -->
<div class="l-browse-bar__actions">
<div class="l-browse-bar__action c-button icon-eye-open" title="Preview"></div>
<div class="l-browse-bar__action c-button icon-notebook" title="New Notebook entry"></div>
<div class="l-browse-bar__action c-button c-button--major icon-pencil" title="Edit"></div>
</div>
</div>
</div>
</template>
<script>
export default {
inject: ['openmct'],
methods: {
toggleViewMenu: function (event) {
event.stopPropagation();
this.showViewMenu = !this.showViewMenu;
},
updateName: function (event) {
// TODO: handle isssues with contenteditable text escaping.
if (event.target.innerText !== this.domainObject.name) {
this.openmct.objects.mutate(this.domainObject, 'name', event.target.innerText);
}
},
setView: function (view) {
this.viewKey = view.key;
this.openmct.router.updateParams({
view: this.viewKey
});
}
},
data: function () {
return {
showViewMenu: false,
domainObject: {},
viewKey: undefined
}
},
computed: {
currentView() {
return this.views.filter(v => v.key === this.viewKey)[0] || {};
},
views() {
return this
.openmct
.objectViews
.get(this.domainObject)
.map((p) => {
return {
key: p.key,
cssClass: p.cssClass,
name: p.name
};
});
},
type() {
let objectType = this.openmct.types.get(this.domainObject.type);
if (!objectType) {
return {}
}
return objectType.definition;
}
},
mounted: function () {
document.addEventListener('click', () => {
if (this.showViewMenu) {
this.showViewMenu = false;
}
});
}
}
</script>
<style lang="scss">
@import "~styles/sass-base";
/******************************* START */
.l-browse-bar {
display: flex;
align-items: center;
justify-content: space-between;
[class*="__"] {
// Removes extraneous horizontal white space
display: inline-flex;
}
&__start {
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
}
&__end {
display: flex;
align-items: center;
flex: 0 0 auto;
[class*="__"] + [class*="__"] {
margin-left: $interiorMarginSm;
}
}
&__nav-to-parent-button,
&__disclosure-button {
flex: 0 0 auto;
}
&__nav-to-parent-button {
// This is an icon-button
$p: $interiorMarginLg;
margin-right: $interiorMargin;
padding-left: $p;
padding-right: $p;
}
&__object-name--w {
align-items: center;
display: flex;
flex: 0 1 auto;
min-width: 0;
&:before {
// Icon
opacity: 0.5;
margin-right: $interiorMargin;
}
}
&__object-name {
flex: 0 1 auto;
}
}
</style>

View File

@@ -0,0 +1,15 @@
<template>
<div></div>
</template>
<style lang="scss">
</style>
<script>
export default {
<mct-include key="'conductor'" class="abs holder flex-elem flex-fixed l-flex-row l-time-conductor-holder"></mct-include>
}
</script>

View File

@@ -1,8 +1,12 @@
<template>
<div class="l-shell">
<div class="l-shell__head">
[ Create Button ]
[ App Logo ]
<CreateButton class="l-shell__create-button"></CreateButton>
<div class="l-shell__controls">
<a class="c-click-icon icon-new-window" title="Open in a new browser tab"></a>
<a class="c-click-icon icon-fullscreen-collapse" title="Enable full screen mode"></a>
</div>
<div class="l-shell__app-logo">[ App Logo ]</div>
</div>
<multipane class="l-shell__main"
type="horizontal">
@@ -14,17 +18,24 @@
<search class="c-search--major" ref="shell-search"></search>
</div>
<div class="l-shell__tree">
<mct-tree :nodes="treeRoots"></mct-tree>
<mct-tree></mct-tree>
</div>
</pane>
<pane class="l-shell__pane-main">
<div class="l-shell__main-container" ref="mainContainer"></div>
<pane class="l-shell__pane-main"
:class="{ 'is-editing' : true }">
<browse-bar class="l-shell__main-view-browse-bar"
ref="browseBar"></browse-bar>
<toolbar class="l-shell__toolbar"></toolbar>
<object-view class="l-shell__main-container"
ref="browseObject"></object-view>
<mct-template template-key="conductor"
class="l-shell__time-conductor"></mct-template>
</pane>
<pane class="l-shell__pane-inspector l-pane--holds-multipane"
handle="before"
label="Inspect"
collapsable>
<MctInspector ref="inspector"></MctInspector>
<Inspector ref="inspector"></Inspector>
</pane>
</multipane>
<div class="l-shell__status">
@@ -72,10 +83,11 @@
}
}
&__head,
&__pane-inspector {
body.mobile & {
display: none;
&__pane-main,
&__pane-tree {
> .l-pane__contents {
display: flex;
flex-flow: column nowrap;
}
}
@@ -98,13 +110,53 @@
}
}
/********** MAIN AREA */
&__head,
&__pane-inspector {
body.mobile & {
display: none;
}
}
&__head,
&__status {
flex: 0 0 auto;
display: flex;
}
/******************************* HEAD */
&__main-view-browse-bar {
flex: 0 0 auto;
}
&__head {
align-items: center;
justify-content: space-between;
border-bottom: 1px solid $colorInteriorBorder;
padding: $interiorMargin;
> [class*="__"] + [class*="__"] {
margin-left: $interiorMargin;
}
}
&__create-button,
&__app-logo {
flex: 0 0 auto;
}
&__controls {
flex: 1 1 100%;
display: flex;
justify-content: flex-end;
margin-right: 2.5%;
}
/******************************* MAIN AREA */
&__main-container {
// Wrapper for main views
font-size: 16px; // TEMP FOR LEGACY STYLING
flex: 1 1 auto;
overflow: auto;
position: absolute;
top: $interiorMargin; right: $interiorMarginLg; bottom: $interiorMargin; left: $interiorMarginLg;
//font-size: 16px; // TEMP FOR LEGACY STYLING
}
&__tree {
@@ -115,24 +167,12 @@
&__time-conductor {
border-top: 1px solid $colorInteriorBorder;
min-height: 50px;
padding: $interiorMarginLg;
flex: 0 0 auto;
padding: $interiorMargin;
}
body.desktop & {
/********** HEAD AND STATUS */
&__head,
&__status {
display: block;
flex: 0 1 auto;
}
&__head {
border-bottom: 1px solid $colorInteriorBorder;
height: 40px;
padding: $interiorMarginLg;
}
&__pane-tree,
&__pane-inspector {
max-width: 30%;
@@ -145,28 +185,43 @@
&__pane-inspector {
width: 200px;
}
&__toolbar {
flex: 0 0 auto;
margin-bottom: $interiorMargin;
}
}
}
</style>
<script>
import MctInspector from './MctInspector.vue';
import MctMain from './MctMain.vue';
import Inspector from '../inspector/Inspector.vue';
import MctStatus from './MctStatus.vue';
import MctTree from './mct-tree.vue';
import ObjectView from './ObjectView.vue';
import MctTemplate from '../legacy/mct-template.vue';
import ContextMenu from '../controls/ContextMenu.vue';
import CreateButton from '../controls/CreateButton.vue';
import search from '../controls/search.vue';
import multipane from '../controls/multipane.vue';
import pane from '../controls/pane.vue';
import BrowseBar from './BrowseBar.vue';
import Toolbar from './Toolbar.vue';
export default {
components: {
MctInspector,
MctMain,
Inspector,
MctStatus,
MctTree,
ObjectView,
'mct-template': MctTemplate,
ContextMenu,
CreateButton,
search,
multipane,
pane
pane,
BrowseBar,
Toolbar
}
}
</script>

View File

@@ -1,202 +0,0 @@
<template>
<multipane class="c-inspector"
type="vertical">
<pane class="c-inspector__properties">
<div ref="properties"></div>
</pane>
<pane class="l-pane c-inspector__elements"
handle="before"
label="Elements">
<div ref="elements">c-inspector__elements 1</div>
</pane>
</multipane>
</template>
<style lang="scss">
@import "~styles/sass-base";
@mixin grid-two-column() {
display: grid;
grid-row-gap: 0;
grid-template-columns: 1fr 2fr;
align-items: start;
}
@mixin grid-two-column-span-cols() {
grid-column: 1 / 3;
}
.c-inspector {
min-width: 150px;
> [class*="__"] {
min-height: 50px;
&:not(:last-child) {
margin-bottom: $interiorMargin;
}
> .l-pane__contents > * {
// Provide margin against scrollbar
// TODO: move this into pane.vue
margin-right: $interiorMarginSm;
}
}
&__elements {
height: 200px;
}
.l-inspector-part {
display: contents; // Legacy
}
h2 {
// Legacy, somewhat
@include grid-two-column-span-cols;
border-radius: $smallCr;
background-color: $colorInspectorSectionHeaderBg;
color: $colorInspectorSectionHeaderFg;
font-size: .85em;
font-weight: normal;
margin: $interiorMarginLg 0 $interiorMarginSm 0;
padding: $interiorMarginSm $interiorMargin;
text-transform: uppercase;
&.first {
margin-top: 0;
}
}
.grid-properties {
.label {
color: $colorInspectorPropName;
}
.value {
color: $colorInspectorPropVal;
word-break: break-all;
&:first-child {
// If there is no preceding .label element, make value span columns
@include grid-two-column-span-cols;
}
}
}
}
/******************************* PROPERTIES GRID */
.grid-elem {
&:not(:first-child) {
border-top: 1px solid $colorInteriorBorder;
}
&.label {
background-color: rgba(0,0,128,0.2);
}
&.value {
background-color: rgba(0,128,0,0.2);
}
}
// Properties grids
.grid-properties, // LEGACY
.l-grid-properties {
@include grid-two-column;
}
.grid-row {
display: contents;
}
.grid-span-all {
@include grid-two-column-span-cols;
}
.grid-row {
.grid-cell {
padding: 3px $interiorMarginLg 3px 0;
&[title] {
// When a cell has a title, assume it's helpful text
cursor: help;
}
}
&.force-border,
&:not(:first-of-type) {
// Row borders, effected via border-top on child elements of the row
.grid-cell {
border-top: 1px solid $colorInspectorSectionHeaderBg;
}
}
/************************************************************** LEGACY STYLES */
.tree {
.grid-properties {
margin-left: $treeItemIndent + $interiorMarginLg;
}
}
.inspector-location {
display: inline-block;
.location-item {
$h: 1.2em;
box-sizing: border-box;
cursor: pointer;
display: inline-block;
line-height: $h;
position: relative;
padding: 2px 4px;
.t-object-label {
.t-item-icon {
height: $h;
margin-right: $interiorMarginSm;
}
}
&:hover {
background: $colorItemTreeHoverBg;
color: $colorItemTreeHoverFg;
.icon {
color: $colorItemTreeIconHover;
}
}
}
&:not(.last) .t-object-label .t-title-label:after {
color: pushBack($colorInspectorFg, 15%);
content: '\e904';
display: inline-block;
font-family: symbolsfont;
font-size: 8px;
font-style: normal !important;
line-height: inherit;
margin-left: $interiorMarginSm;
width: 4px;
}
}
// Elements pool
.holder-elements {
.current-elements {
position: relative;
.tree-item {
.t-object-label {
// Elements pool is a flat list, so don't indent items.
/*font-size: 0.75rem;*/
left: 0;
}
}
}
}
}
</style>
<script>
import multipane from '../controls/multipane.vue';
import pane from '../controls/pane.vue';
export default {
components: {
multipane,
pane
}
}
</script>

View File

@@ -1,21 +0,0 @@
<template>
<mct-representation key='browse-object'></mct-representation>
</template>
<style lang="scss">
.MCT_Main {
position: absolute;
right: 100px;
top: 0px;
bottom: 20px;
left: 100px;
background: green;
}
</style>
<script>
export default {
}
</script>

View File

@@ -0,0 +1,75 @@
<template>
</template>
<style lang="scss">
.c-object-view {
display: contents;
}
</style>
<script>
import _ from "lodash"
export default {
inject: ["openmct"],
props: {
view: String,
object: Object
},
destroyed() {
this.clear();
},
watch: {
view(newView, oldView) {
this.viewKey = newView;
this.debounceUpdateView();
},
object(newObject, oldObject) {
this.currentObject = newObject;
this.debounceUpdateView();
}
},
created() {
this.debounceUpdateView = _.debounce(this.updateView, 10);
},
mounted() {
this.currentObject = this.object;
this.updateView();
},
methods: {
clear() {
if (this.currentView) {
this.currentView.destroy();
this.$el.innerHTML = '';
}
delete this.viewContainer;
delete this.currentView;
},
updateView() {
this.clear();
if (!this.currentObject) {
return;
}
this.viewContainer = document.createElement('div');
this.viewContainer.classList.add('c-object-view');
this.$el.append(this.viewContainer);
let provider = this.openmct.objectViews.getByProviderKey(this.viewKey);
if (!provider) {
provider = this.openmct.objectViews.get(this.currentObject)[0];
if (!provider) {
return;
}
}
this.currentView = provider.view(this.currentObject);
this.currentView.show(this.viewContainer);
},
show(object, viewKey) {
this.currentObject = object;
this.viewKey = viewKey;
this.updateView();
}
}
}
</script>

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

@@ -10,9 +10,8 @@
<style lang="scss">
@import "~styles/sass-base";
// TODO: make sure hit area encompasses all of the tree item; currently is just the text
.c-tree {
@include userSelectNone();
overflow-x: hidden;
overflow-y: auto;
height: 100%;
@@ -70,7 +69,8 @@
&:before {
// Type icon
display: inline-block;
display: block;
flex: 0 0 auto;
font-size: 1.3em;
margin-right: $interiorMarginSm;
}

Some files were not shown because too many files have changed in this diff Show More