display: grid;
grid-column-gap: $interiorMargin;
grid-template-columns: 20px max-content 1fr;
grid-row-gap: $interiorMargin;
margin-top: $interiorMarginLg;
width: max-content;
.c-form__row {
display: contents;
}
}
}
.c-form-row {
align-items: start;
&__label,
&__state-indicator {
flex: 0 0 auto;
padding: 2px 0;
}
&__label {
width: 30%;
min-width: 100px;
order: 1;
}
&__state-indicator {
order: 2;
width: 30px;
text-align: center;
&.invalid,
&.invalid.req {
@include validationState($glyph-icon-x, $colorFormInvalid);
}
&.valid,
&.valid.req {
@include validationState($glyph-icon-check, $colorFormValid);
}
&.req {
@include validationState($glyph-icon-asterisk, $colorFormRequired);
}
}
&__controls {
flex: 1 1 auto;
order: 3;
}
}
.c-form-control {
&--locator > .c-tree {
height: 100%;
}
&--clock-display-format-fields {
display: flex;
> * {
flex: 0 0 auto;
+ * {
margin-left: $interiorMargin;
}
}
}
&--datetime {
$size: max-content;
display: grid;
grid-template-columns: repeat(5, $size);
grid-template-rows: $size;
grid-row-gap: 3px;
grid-column-gap: $interiorMargin;
align-items: stretch;
.hint {
align-self: center;
opacity: 0.7;
}
}
}
.l-input-lg {
input[type='text'],
input[type='search'],
input[type='number'],
textarea[type='text'] {
width: 100%;
}
}
.l-input-sm {
input[type='text'],
input[type='search'],
input[type='number'] {
width: 50px;
}
}
/***************************************************** LEGACY */
.section-header {
border-radius: $basicCr;
color: lighten($colorBodyFg, 20%);
font-size: inherit;
margin: $interiorMargin 0;
padding: $formTBPad $formLRPad;
text-transform: uppercase;
.view-control {
display: inline-block;
margin-right: $interiorMargin;
width: 1em;
height: 1em;
}
}
.form {
color: $colorFormText;
height: 100%;
width: 100%;
.l-form-section {
margin-bottom: $interiorMarginLg * 2;
position: relative;
&.grows {
.l-section-body,
.form-row {
flex: 1 1 auto;
.wrapper {
height: 100%;
}
}
}
}
.form-row {
$m: $interiorMargin;
box-sizing: border-box;
border-top: 1px solid $colorFormLines;
padding: $formTBPad 0;
position: relative;
&.first {
border-top: none;
}
> .label,
> .controls {
box-sizing: border-box;
font-size: 0.8rem;
}
> .label {
// Only style this way for immediate children of .form-row; prevents problems when .label is used in .controls section of a form
min-width: 120px;
order: 1;
position: relative;
width: $formLabelW;
}
.value {
color: $colorInputFg;
}
.controls {
order: 9;
position: relative;
flex: 1 1 auto;
.l-composite-control {
&.l-checkbox {
display: inline-block;
line-height: $formRowCtrlsH;
margin-right: 5px;
}
}
.l-input-lg {
// LEGACY FORM SUPPORT
input[type='text'],
input[type='search'],
input[type='number'] {
width: 100%;
}
}
select {
margin-right: $interiorMargin;
}
}
.hint,
.field-hints {
color: $colorFieldHint;
}
}
}
.selector-list {
// Displays tree view in dialogs
@include nice-input();
padding: $interiorMargin;
position: relative;
min-height: 0; // Chrome 73 overflow bug fix
height: 100%;
> .wrapper {
$p: $interiorMargin;
box-sizing: border-box;
overflow: auto;
}
}
.l-controls-first .form .form-row,
.form .form-row.l-controls-first {
> .label,
> .controls {
line-height: inherit;
min-height: inherit;
}
> .label {
flex: 1 1 auto;
min-width: 0;
width: auto;
order: 2;
}
> .control,
> .controls {
flex: 0 0 auto;
margin-right: $interiorMargin;
order: 1;
}
}
.l-controls-under.l-flex-row {
// Change to use column layout
flex-direction: column;
.flex-elem {
margin-bottom: $interiorMarginLg;
}
}
.l-composite-control {
vertical-align: middle;
&:not(.l-inline) {
margin-bottom: $interiorMargin;
}
&.l-inline {
display: inline-block;
}
&.l-checkbox {
.composite-control-label {
line-height: 18px;
}
}
}
/********* COMPACT FORM */
// ul > li > label, control
// Make a new UL for each form section
// Allow control-first, controls-below
// 3/8/19: Used by Summary Widgets edit UI
.l-compact-form .tree ul li,
.l-compact-form ul li {
padding: 2px 0;
}
.l-compact-form {
$h: $btnStdH;
$labelW: 40%;
$minW: $labelW;
ul {
li {
display: flex;
align-items: stretch;
padding: $interiorMargin 0;
label,
.control {
display: flex;
}
label {
line-height: $h;
width: $labelW;
}
.controls {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
flex-grow: 1;
margin-left: $interiorMargin;
min-height: $h;
line-height: $h;
input[type='text'],
input[type='search'],
input[type='number'],
button,
select {
min-height: $h;
}
> * + * {
margin-left: $interiorMarginSm;
}
}
&.connects-to-previous {
padding-top: 0;
}
&.section-header {
margin-top: $interiorMarginLg;
border-top: 1px solid $colorFormLines;
}
&.controls-first {
.control {
flex-grow: 0;
margin-right: $interiorMargin;
min-width: 0;
order: 1;
width: auto;
}
label {
flex-grow: 1;
order: 2;
width: auto;
}
}
&.controls-under {
display: block;
.control,
label {
display: block;
width: auto;
}
ul li {
border-top: none !important;
padding: 0;
}
}
}
}
}
/******** VALIDATION */
.form-error {
// Block element that visually flags an error and contains a message
background-color: $colorFormFieldErrorBg;
color: $colorFormFieldErrorFg;
border-radius: $basicCr;
display: block;
padding: 1px 6px;
&:before {
content: $glyph-icon-alert-triangle;
display: inline-block;
font-family: symbolsfont;
margin-right: $interiorMarginSm;
}
}
body.desktop .form-row > .label {
&:after {
position: absolute;
right: $interiorMargin;
height: 100%;
line-height: 200%;
}
}
.req {
color: $colorFormRequired;
}
================================================
FILE: src/styles/_global.scss
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
@use 'sass:math';
/******************************************************** RESETS */
*,
:before,
:after {
box-sizing: border-box;
}
div {
position: relative;
}
/******************************************************** UTILITIES */
.u-contents {
display: contents;
}
.u-menu-to {
&--left {
.c-menu {
left: auto !important;
right: 0;
}
}
&--center {
.c-menu {
left: 50% !important;
transform: translateX(-50%);
}
}
}
.u-space {
// Provides a separator space between elements
&--right {
+ [class*='__'] {
margin-left: $interiorMarginLg !important;
}
}
}
.u-flex-spreader {
// Pushes against elements in a flex layout to spread them out
flex: 1 1 auto;
}
.visually-hidden {
// Provides a way to add accessible text to elements
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
/******************************************************** BROWSER ELEMENTS */
body.desktop {
::-webkit-scrollbar {
box-sizing: border-box;
box-shadow: inset $scrollbarTrackShdw;
background-color: $scrollbarTrackColorBg;
height: $scrollbarTrackSize;
width: $scrollbarTrackSize;
}
::-webkit-scrollbar-thumb {
box-sizing: border-box;
background: $scrollbarThumbColor;
&:hover {
background: $scrollbarThumbColorHov;
}
}
::-webkit-scrollbar-corner {
background: transparent;
}
.c-menu ::-webkit-scrollbar-thumb {
background: $scrollbarThumbColorMenu;
&:hover {
background: $scrollbarThumbColorMenuHov;
}
}
div,
ul,
span {
// Firefox
scrollbar-color: $scrollbarThumbColor $scrollbarTrackColorBg;
scrollbar-width: thin;
}
}
/******************************************************** FONTS */
@mixin fontAndSize() {
@each $size in $listFontSizes {
&[data-font-size='#{$size}'] {
font-size: #{$size}px;
// Set row heights in telemetry tables
tr {
min-height: #{$size + ($tabularTdPadTB * 2)};
}
}
}
&[data-font*='bold'] {
font-weight: bold;
}
&[data-font*='narrow'] {
font-family: 'Arial Narrow', sans-serif;
}
&[data-font*='monospace'] {
font-family: 'Andale Mono', sans-serif;
}
}
.u-style-receiver {
@include fontAndSize();
}
/******************************************************** HTML ENTITIES */
a {
color: $colorA;
cursor: pointer;
text-decoration: none;
&:focus {
outline: none !important;
}
}
body,
html {
height: $bodySize;
width: 100%;
}
#openmct-app {
@include abs();
}
body {
-webkit-font-smoothing: subpixel-antialiased;
-moz-osx-font-smoothing: grayscale;
@include bodyFont($fontBaseSize);
// background-color: $colorBodyBg;
background: $bodyBg;
background-size: $bodyBgSize;
color: $colorBodyFg;
}
em {
font-style: normal;
}
p {
margin-bottom: $interiorMarginLg;
}
ol,
ul {
list-style: none;
margin: 0;
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
[class*='local-controls--hidden'] {
transition: opacity 500ms ease-in-out;
opacity: 0;
pointer-events: none;
}
// Look down up to two levels and display hidden LC's on hover
&:hover {
> [class*='local-controls--hidden'],
> * > [class*='local-controls--hidden'],
> * > * > [class*='local-controls--hidden'] {
transition: opacity 50ms ease-in-out;
opacity: 1;
pointer-events: inherit;
}
}
}
/******************************************************** ICON BACKGROUNDS */
// Used with elements that utilize an SVG background element where specific coloring is needed
.u-icon-bg-color {
// Messages and notifications
&-info {
@include glyphBg($bg-icon-info);
filter: $colorStatusInfoFilter;
}
&-alert {
@include glyphBg($bg-icon-alert-rect);
filter: $colorStatusAlertFilter;
}
&-error {
@include glyphBg($bg-icon-alert-triangle);
filter: $colorStatusErrorFilter;
}
}
/******************************************************** SELECTION AND EDITING */
// Provides supporting styles for Display Layouts and augmented legacy Fixed Position view
.c-grid,
.c-grid__x,
.c-grid__y {
@include abs();
}
.c-grid .c-grid {
pointer-events: none;
&__x {
@include bgTicks($editUIGridColorFg, 'x');
}
&__y {
@include bgTicks($editUIGridColorFg, 'y');
}
}
/*************************** SELECTION */
.u-inspectable {
&:hover {
box-shadow: $browseSelectableShdwHov;
}
}
/**************************** EDITING */
.is-editing {
.is-moveable {
cursor: move;
}
.u-links {
// Applied in markup to objects that provide links. Disable while editing.
pointer-events: none;
}
}
::placeholder {
opacity: 0.7;
font-style: italic;
}
/******************************************************** STATES */
@mixin spinner($b: 5, $c: $colorKey) {
animation-name: rotation-centered;
animation-duration: 0.5s;
animation-iteration-count: infinite;
animation-timing-function: linear;
border-radius: 100%;
box-sizing: border-box;
border-color: rgba($c, 0.25);
border-top-color: rgba($c, 1);
border-style: solid;
border-width: $b;
display: block;
position: absolute;
left: 50%;
top: 50%;
transform-origin: center;
transform: translate(-50%, -50%);
}
@keyframes geartooth {
to { transform: translate(-50%, -50%) rotate(50deg); }
}
@mixin gearSpinner($anim: "geartooth", $animDur: 0.25s, $steps: 6, $color: "#ffffff") {
@include absCenter();
@include bgDashedCircle($color: $color);
animation: #{$anim} $animDur steps($steps) infinite;
background-size: contain;
background-position: center;
background-repeat: no-repeat;
width: 100%;
height: 100%;
}
.gear-spinner {
@include gearSpinner($color: $colorKey);
}
.wait-spinner {
@include spinner($waitSpinnerBorderW, $colorKey);
pointer-events: none;
z-index: 2;
&.inline {
display: inline-block !important;
margin-right: $interiorMargin;
position: relative !important;
vertical-align: middle;
}
}
.loading {
// Can be applied to any block element with height and width
pointer-events: none;
&:before,
&:after {
content: '';
}
&:before {
@include spinner($waitSpinnerBorderW, $colorLoadingFg);
height: $waitSpinnerD;
width: $waitSpinnerD;
z-index: 10;
}
&:after {
@include abs();
background: $colorLoadingBg;
display: block;
z-index: 9;
}
&.c-tree__item {
$d: $waitSpinnerTreeD;
$spinnerL: 19 + math.div($d, 2);
display: flex;
align-items: center;
margin-left: $treeNavArrowD + $interiorMargin;
min-height: 5px + $d;
.c-tree__item__label {
font-style: italic;
margin-left: $interiorMargin;
opacity: 0.6;
}
&:before {
left: auto;
top: auto;
transform: translate(0);
height: $d;
width: $d;
border-width: 3px;
//left: $spinnerL;
position: relative;
}
&:after {
display: none;
}
}
&.c-loading--overlay {
@include abs();
}
}
[aria-disabled='true'],
*[disabled],
.disabled {
opacity: $controlDisabledOpacity;
cursor: not-allowed !important;
pointer-events: none !important;
}
/******************************************************** RESPONSIVE CONTAINERS */
@mixin responsiveContainerWidths($dimension) {
// 3/21/22: `--width-less-than*` classes set in ObjectView.vue
.--show-if-less-than-#{$dimension} {
// Hide anything that displays within a given width by default.
// `display` property must be set within a more specific class
// for the particular item to be displayed.
display: none !important;
}
.--width-less-than-#{$dimension} {
.--hide-if-less-than-#{$dimension} {
display: none;
}
}
}
//.--hide-by-default { display: none !important; }
@include responsiveContainerWidths('220');
@include responsiveContainerWidths('600');
.u-fade-truncate,
.u-fade-truncate--lg {
&:after {
display: block;
position: absolute;
top: 0;
bottom: 0;
content: '';
right: 0;
width: $fadeTruncateW * 1.5;
z-index: 2;
}
&.--no-sep {
border-right: none;
}
}
.u-fade-truncate--lg {
flex-basis: 100% !important;
}
================================================
FILE: src/styles/_glyphs.scss
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
@font-face {
// Use https://icomoon.io/app with `Icomoon.Open MCT Symbols 2018.json` to generate font files
font-family: 'symbolsfont';
src: url('./fonts/Open-MCT-Symbols-16px.woff') format('woff'),
url('./fonts/Open-MCT-Symbols-16px.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
// Use https://icomoon.io/app with icomoon-project-Open-MCT-Symbols-12px.json to generate font files
font-family: 'symbolsfont-12px';
src: url('./fonts/Open-MCT-Symbols-12px.woff') format('woff'),
url('./fonts/Open-MCT-Symbols-12px.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
/************************** 16 PX CLASSES */
.icon-alert-rect {
@include glyphBefore($glyph-icon-alert-rect);
}
.icon-alert-triangle {
@include glyphBefore($glyph-icon-alert-triangle);
}
.icon-arrow-up {
@include glyphBefore($glyph-icon-arrow-up);
}
.icon-arrow-double-up {
@include glyphBefore($glyph-icon-arrow-double-up);
}
.icon-arrow-tall-up {
@include glyphBefore($glyph-icon-arrow-tall-up);
}
.icon-arrow-right {
@include glyphBefore($glyph-icon-arrow-right);
}
.icon-arrow-right-equilateral {
@include glyphBefore($glyph-icon-arrow-right-equilateral);
}
.icon-arrow-down {
@include glyphBefore($glyph-icon-arrow-down);
}
.icon-arrow-double-down {
@include glyphBefore($glyph-icon-arrow-double-down);
}
.icon-arrow-tall-down {
@include glyphBefore($glyph-icon-arrow-tall-down);
}
.icon-arrow-left {
@include glyphBefore($glyph-icon-arrow-left);
}
.icon-asterisk {
@include glyphBefore($glyph-icon-asterisk);
}
.icon-bell {
@include glyphBefore($glyph-icon-bell);
}
.icon-box-round-corners {
@include glyphBefore($glyph-icon-box-round-corners);
}
.icon-box-with-arrow {
@include glyphBefore($glyph-icon-box-with-arrow);
}
.icon-check {
@include glyphBefore($glyph-icon-check);
}
.icon-connectivity {
@include glyphBefore($glyph-icon-connectivity);
}
.icon-database-in-brackets {
@include glyphBefore($glyph-icon-database-in-brackets);
}
.icon-eye-open {
@include glyphBefore($glyph-icon-eye-open);
}
.icon-gear {
@include glyphBefore($glyph-icon-gear);
}
.icon-gear-after {
@include glyphAfter($glyph-icon-gear);
}
.icon-hourglass {
@include glyphBefore($glyph-icon-hourglass);
}
.icon-info {
@include glyphBefore($glyph-icon-info);
}
.icon-link {
@include glyphBefore($glyph-icon-link);
}
.icon-lock {
@include glyphBefore($glyph-icon-lock);
}
.icon-minus {
@include glyphBefore($glyph-icon-minus);
}
.icon-people {
@include glyphBefore($glyph-icon-people);
}
.icon-person {
@include glyphBefore($glyph-icon-person);
}
.icon-plus {
@include glyphBefore($glyph-icon-plus);
}
.icon-plus-in-rect {
@include glyphBefore($glyph-icon-plus-in-rect);
}
.icon-trash {
@include glyphBefore($glyph-icon-trash);
}
.icon-x {
@include glyphBefore($glyph-icon-x);
}
.icon-brackets {
@include glyphBefore($glyph-icon-brackets);
}
.icon-crosshair {
@include glyphBefore($glyph-icon-crosshair);
}
.icon-grippy {
@include glyphBefore($glyph-icon-grippy);
}
.icon-grid {
@include glyphBefore($glyph-icon-grid);
}
.icon-grippy-ew {
@include glyphBefore($glyph-icon-grippy-ew);
}
.icon-columns {
@include glyphBefore($glyph-icon-columns);
}
.icon-rows {
@include glyphBefore($glyph-icon-rows);
}
.icon-filter {
@include glyphBefore($glyph-icon-filter);
}
.icon-filter-outline {
@include glyphBefore($glyph-icon-filter-outline);
}
.icon-suitcase {
@include glyphBefore($glyph-icon-suitcase);
}
.icon-cursor-lock {
@include glyphBefore($glyph-icon-cursor-lock);
}
.icon-flag {
@include glyphBefore($glyph-icon-flag);
}
.icon-eye-disabled {
@include glyphBefore($glyph-icon-eye-disabled);
}
.icon-notebook-page {
@include glyphBefore($glyph-icon-notebook-page);
}
.icon-unlocked {
@include glyphBefore($glyph-icon-unlocked);
}
.icon-circle {
@include glyphBefore($glyph-icon-circle);
}
.icon-draft {
@include glyphBefore($glyph-icon-draft);
}
.icon-question-mark {
@include glyphBefore($glyph-icon-question-mark);
}
.icon-circle-slash {
@include glyphBefore($glyph-icon-circle-slash);
}
.icon-status-poll-check {
@include glyphBefore($glyph-icon-status-poll-check);
}
.icon-status-poll-caution {
@include glyphBefore($glyph-icon-status-poll-caution);
}
.icon-status-poll-circle-slash {
@include glyphBefore($glyph-icon-status-poll-circle-slash);
}
.icon-status-poll-question-mark {
@include glyphBefore($glyph-icon-status-poll-question-mark);
}
.icon-status-poll-edit {
@include glyphBefore($glyph-icon-status-poll-edit);
}
.icon-stale {
@include glyphBefore($glyph-icon-stale);
}
.icon-arrows-right-left {
@include glyphBefore($glyph-icon-arrows-right-left);
}
.icon-arrows-up-down {
@include glyphBefore($glyph-icon-arrows-up-down);
}
.icon-bullet {
@include glyphBefore($glyph-icon-bullet);
}
.icon-calendar {
@include glyphBefore($glyph-icon-calendar);
}
.icon-chain-links {
@include glyphBefore($glyph-icon-chain-links);
}
.icon-download {
@include glyphBefore($glyph-icon-download);
}
.icon-duplicate {
@include glyphBefore($glyph-icon-duplicate);
}
.icon-folder-new {
@include glyphBefore($glyph-icon-folder-new);
}
.icon-fullscreen-collapse {
@include glyphBefore($glyph-icon-fullscreen-collapse);
}
.icon-fullscreen-expand {
@include glyphBefore($glyph-icon-fullscreen-expand);
}
.icon-layers {
@include glyphBefore($glyph-icon-layers);
}
.icon-line-horz {
@include glyphBefore($glyph-icon-line-horz);
}
.icon-magnify {
@include glyphBefore($glyph-icon-magnify);
}
.icon-magnify-in {
@include glyphBefore($glyph-icon-magnify-in);
}
.icon-magnify-out {
@include glyphBefore($glyph-icon-magnify-out);
}
.icon-menu-hamburger {
@include glyphBefore($glyph-icon-menu-hamburger);
}
.icon-move {
@include glyphBefore($glyph-icon-move);
}
.icon-new-window {
@include glyphBefore($glyph-icon-new-window);
}
.icon-paint-bucket {
@include glyphBefore($glyph-icon-paint-bucket);
}
.icon-pencil {
@include glyphBefore($glyph-icon-pencil);
}
.icon-pencil-in-brackets {
@include glyphBefore($glyph-icon-pencil-in-brackets);
}
.icon-play {
@include glyphBefore($glyph-icon-play);
}
.icon-pause {
@include glyphBefore($glyph-icon-pause);
}
.icon-plot-resource {
@include glyphBefore($glyph-icon-plot-resource);
}
.icon-pointer-left {
@include glyphBefore($glyph-icon-pointer-left);
}
.icon-pointer-right {
@include glyphBefore($glyph-icon-pointer-right);
}
.icon-refresh {
@include glyphBefore($glyph-icon-refresh);
}
.icon-save {
@include glyphBefore($glyph-icon-save);
}
.icon-save-as {
@include glyphBefore($glyph-icon-save-as);
}
.icon-sine {
@include glyphBefore($glyph-icon-sine);
}
.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);
}
.icon-resync {
@include glyphBefore($glyph-icon-resync);
}
.icon-reset {
@include glyphBefore($glyph-icon-reset);
}
.icon-x-in-circle {
@include glyphBefore($glyph-icon-x-in-circle);
}
.icon-brightness {
@include glyphBefore($glyph-icon-brightness);
}
.icon-contrast {
@include glyphBefore($glyph-icon-contrast);
}
.icon-expand {
@include glyphBefore($glyph-icon-expand);
}
.icon-list-view {
@include glyphBefore($glyph-icon-list-view);
}
.icon-grid-snap-to {
@include glyphBefore($glyph-icon-grid-snap-to);
}
.icon-grid-snap-no {
@include glyphBefore($glyph-icon-grid-snap-no);
}
.icon-frame-show {
@include glyphBefore($glyph-icon-frame-show);
}
.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-clear-data {
@include glyphBefore($glyph-icon-clear-data);
}
.icon-history {
@include glyphBefore($glyph-icon-history);
}
.icon-arrow-nav-to-parent {
@include glyphBefore($glyph-icon-arrow-nav-to-parent);
}
.icon-crosshair-in-circle {
@include glyphBefore($glyph-icon-crosshair-in-circle);
}
.icon-target {
@include glyphBefore($glyph-icon-target);
}
.icon-items-collapse {
@include glyphBefore($glyph-icon-items-collapse);
}
.icon-items-expand {
@include glyphBefore($glyph-icon-items-expand);
}
.icon-3-dots {
@include glyphBefore($glyph-icon-3-dots);
}
.icon-grid-on {
@include glyphBefore($glyph-icon-grid-on);
}
.icon-grid-off {
@include glyphBefore($glyph-icon-grid-off);
}
.icon-camera {
@include glyphBefore($glyph-icon-camera);
}
.icon-folders-collapse {
@include glyphBefore($glyph-icon-folders-collapse);
}
.icon-multiline {
@include glyphBefore($glyph-icon-multiline);
}
.icon-singleline {
@include glyphBefore($glyph-icon-singleline);
}
.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);
}
.icon-clock {
@include glyphBefore($glyph-icon-clock);
}
.icon-database {
@include glyphBefore($glyph-icon-database);
}
.icon-database-query {
@include glyphBefore($glyph-icon-database-query);
}
.icon-dataset {
@include glyphBefore($glyph-icon-dataset);
}
.icon-datatable {
@include glyphBefore($glyph-icon-datatable);
}
.icon-dictionary {
@include glyphBefore($glyph-icon-dictionary);
}
.icon-folder {
@include glyphBefore($glyph-icon-folder);
}
.icon-image {
@include glyphBefore($glyph-icon-image);
}
.icon-layout {
@include glyphBefore($glyph-icon-layout);
}
.icon-object {
@include glyphBefore($glyph-icon-object);
}
.icon-object-unknown {
@include glyphBefore($glyph-icon-object-unknown);
}
.icon-packet {
@include glyphBefore($glyph-icon-packet);
}
.icon-page {
@include glyphBefore($glyph-icon-page);
}
.icon-plot-overlay {
@include glyphBefore($glyph-icon-plot-overlay);
}
.icon-plot-stacked {
@include glyphBefore($glyph-icon-plot-stacked);
}
.icon-session {
@include glyphBefore($glyph-icon-session);
}
.icon-tabular {
@include glyphBefore($glyph-icon-tabular);
}
.icon-tabular-lad {
@include glyphBefore($glyph-icon-tabular-lad);
}
.icon-tabular-lad-set {
@include glyphBefore($glyph-icon-tabular-lad-set);
}
.icon-tabular-realtime {
@include glyphBefore($glyph-icon-tabular-realtime);
}
.icon-tabular-scrolling {
@include glyphBefore($glyph-icon-tabular-scrolling);
}
.icon-telemetry {
@include glyphBefore($glyph-icon-telemetry);
}
.icon-timeline {
@include glyphBefore($glyph-icon-timeline);
}
.icon-timer {
@include glyphBefore($glyph-icon-timer);
}
.icon-topic {
@include glyphBefore($glyph-icon-topic);
}
.icon-box-with-dashed-lines {
@include glyphBefore($glyph-icon-box-with-dashed-lines);
}
.icon-summary-widget {
@include glyphBefore($glyph-icon-summary-widget);
}
.icon-notebook {
@include glyphBefore($glyph-icon-notebook);
}
.icon-tabs-view {
@include glyphBefore($glyph-icon-tabs-view);
}
.icon-flexible-layout {
@include glyphBefore($glyph-icon-flexible-layout);
}
.icon-generator-telemetry {
@include glyphBefore($glyph-icon-generator-telemetry);
}
.icon-generator-events {
@include glyphBefore($glyph-icon-generator-events);
}
.icon-gauge {
@include glyphBefore($glyph-icon-gauge);
}
.icon-spectra {
@include glyphBefore($glyph-icon-spectra);
}
.icon-spectra-telemetry {
@include glyphBefore($glyph-icon-spectra-telemetry);
}
.icon-command {
@include glyphBefore($glyph-icon-command);
}
.icon-conditional {
@include glyphBefore($glyph-icon-conditional);
}
.icon-condition-widget {
@include glyphBefore($glyph-icon-condition-widget);
}
.icon-alphanumeric {
@include glyphBefore($glyph-icon-alphanumeric);
}
.icon-image-telemetry {
@include glyphBefore($glyph-icon-image-telemetry);
}
.icon-telemetry-aggregate {
@include glyphBefore($glyph-icon-telemetry-aggregate);
}
.icon-bar-chart {
@include glyphBefore($glyph-icon-bar-chart);
}
.icon-map {
@include glyphBefore($glyph-icon-map);
}
.icon-plan {
@include glyphBefore($glyph-icon-plan);
}
.icon-timelist {
@include glyphBefore($glyph-icon-timelist);
}
.icon-notebook-shift-log {
@include glyphBefore($glyph-icon-notebook-shift-log);
}
.icon-derived-telemetry {
@include glyphBefore($glyph-icon-derived-telemetry);
}
/************************** 12 PX CLASSES */
// TODO: sync with 16px redo as of 10/25/18
.icon-filter-12px {
@include glyphBefore($glyph-icon-filter, 'symbolsfont-12px');
}
.icon-filter-outline-12px {
@include glyphBefore($glyph-icon-filter-outline, 'symbolsfont-12px');
}
.icon-crosshair-12px {
@include glyphBefore($glyph-icon-crosshair, 'symbolsfont-12px');
}
.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-alert-rect {
@include glyphBg($bg-icon-alert-rect);
}
.bg-icon-alert-triangle {
@include glyphBg($bg-icon-alert-triangle);
}
.bg-icon-bell {
@include glyphBg($bg-icon-bell);
}
.bg-icon-info {
@include glyphBg($bg-icon-info);
}
.bg-icon-plus {
@include glyphBg($bg-icon-plus);
}
.bg-icon-grippy-ew {
@include glyphBg($bg-icon-grippy-ew);
}
.bg-icon-chain-links {
@include glyphBg($bg-icon-chain-links);
}
.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-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-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);
}
.bg-icon-tabs-view {
@include glyphBg($bg-icon-tabs-view);
}
.bg-icon-flexible-layout {
@include glyphBg($bg-icon-flexible-layout);
}
.bg-icon-generator-telemetry {
@include glyphBg($bg-icon-generator-telemetry);
}
.bg-icon-generator-events {
@include glyphBg($bg-icon-generator-events);
}
.bg-icon-gauge {
@include glyphBg($bg-icon-gauge);
}
.bg-icon-spectra {
@include glyphBg($bg-icon-spectra);
}
.bg-icon-spectra-telemetry {
@include glyphBg($bg-icon-spectra-telemetry);
}
.bg-icon-command {
@include glyphBg($bg-icon-command);
}
.bg-icon-conditional {
@include glyphBg($bg-icon-conditional);
}
.bg-icon-condition-widget {
@include glyphBg($bg-icon-condition-widget);
}
.bg-icon-bar-chart {
@include glyphBg($bg-icon-bar-chart);
}
.bg-icon-map {
@include glyphBg($bg-icon-map);
}
.bg-icon-plan {
@include glyphBg($bg-icon-plan);
}
.bg-icon-timelist {
@include glyphBg($bg-icon-timelist);
}
.bg-icon-plot-scatter {
@include glyphBg($bg-icon-plot-scatter);
}
.bg-icon-notebook-shift-log {
@include glyphBg($bg-icon-notebook-shift-log);
}
.bg-icon-telemetry-aggregate {
@include glyphBg($bg-icon-telemetry-aggregate);
}
.bg-icon-trash {
@include glyphBg($bg-icon-trash);
}
.bg-icon-eye-open {
@include glyphBg($bg-icon-eye-open);
}
.bg-icon-camera {
@include glyphBg($bg-icon-camera);
}
.bg-icon-derived-telemetry {
@include glyphBg($bg-icon-derived-telemetry);
}
/************************** COLOR-ABLE BG SVG GLYPHS */
@mixin bgDashedCircle($r: 14, $sw: 5, $sda: "6.2 6.2", $color: "#000000") {
// Renders a 6 segmented dashed circle as a background-image using SVG
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg fill='none' width='32' height='32' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='16' cy='16' r='#{$r}' stroke='%23#{str-slice(#{$color}, 2)}' stroke-width='#{$sw}' stroke-dasharray='#{$sda}'/%3e%3c/svg%3e");
}
@mixin bgCheckMark($color: "#000000") {
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg width='21' height='20' viewBox='0 0 21 20' fill='none' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M21 6.04102L7.04102 20H6.95898L0 13.041V6.95898L7 13.959L20.959 0H21V6.04102Z' fill='%23#{str-slice(#{$color}, 2)}'/%3e%3c/svg%3e ");
}
@mixin bgCircleSlash($color: "#000000") {
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg width='20' height='20' viewBox='0 0 20 20' fill='none' xmlns='http://www.w3.org/2000/svg'%3e%3cpath fill-rule='evenodd' clip-rule='evenodd' d='M10 0C15.5228 0 20 4.47715 20 10C20 15.5228 15.5228 20 10 20C4.47715 20 0 15.5228 0 10C0 4.47715 4.47715 0 10 0ZM6.7627 16.0654C7.72753 16.5815 8.82935 16.875 10 16.875C13.797 16.875 16.875 13.797 16.875 10C16.875 8.82935 16.5815 7.72753 16.0654 6.7627L6.7627 16.0654ZM10 3.125C6.20304 3.125 3.125 6.20304 3.125 10C3.125 11.1702 3.41794 12.2718 3.93359 13.2363L13.2363 3.93359C12.2718 3.41794 11.1702 3.125 10 3.125Z' fill='%23#{str-slice(#{$color}, 2)}'/%3e%3c/svg%3e");
}
@mixin bgSkip($color: "#000000") {
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg width='20' height='20' viewBox='0 0 20 20' fill='none' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12 3C14.7614 3 17 5.23858 17 8V12H20L15 17L10 12H13V8C13 7.44772 12.5523 7 12 7H8C7.44772 7 7 7.44772 7 8V11C7 13.7616 4.76097 15.9998 2 16H0V12H2C2.55243 11.9998 3 11.5519 3 11V8C3 5.23858 5.23858 3 8 3H12Z' fill='%23#{str-slice(#{$color}, 2)}'/%3e%3c/svg%3e ");
}
================================================
FILE: src/styles/_legacy-messages.scss
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
@use 'sass:math';
/******************************************************************* MESSAGES */
.w-message-contents {
flex: 1 1 auto;
display: flex;
flex-direction: column;
> * + * {
margin-bottom: $interiorMargin;
}
.message-body {
flex: 1 1 100%;
}
}
// Singleton in an overlay dialog
.t-message-single .l-message,
.t-message-single.l-message {
$iconW: $messageListIconD;
&:before {
font-size: $iconW;
width: $iconW + 2;
}
.title {
font-size: 1.2em;
}
}
// Singleton inline in a view
.t-message-inline .l-message,
.t-message-inline.l-message {
border-radius: $controlCr;
&.message-severity-info {
background-color: rgba($colorInfo, 0.3);
}
&.message-severity-alert {
background-color: rgba($colorWarningLo, 0.3);
}
&.message-severity-error {
background-color: rgba($colorWarningHi, 0.3);
}
.w-message-contents.l-message-body-only {
.message-body {
margin-top: $interiorMargin;
}
}
}
// In a list
.c-overlay__messages {
//@include abs();
display: flex;
flex-direction: column;
overflow: auto;
padding-right: $interiorMargin;
> div,
> span {
margin-bottom: $interiorMargin;
}
.w-messages {
flex: 1 1 100%;
overflow-y: auto;
padding-right: $interiorMargin;
}
// Each message
.c-message {
@include discreteItem();
flex: 0 0 auto;
margin-bottom: $interiorMarginSm;
.hint,
.bottom-bar {
text-align: left;
}
}
}
@include phonePortrait {
.t-message-single .l-message,
.t-message-single.l-message {
flex-direction: column;
&:before {
margin-right: 0;
margin-bottom: $interiorMarginLg;
text-align: center;
width: 100%;
}
.bottom-bar {
text-align: center;
.s-button {
display: block;
width: 100%;
}
}
}
}
body.desktop .t-message-list {
.w-message-contents {
padding-right: $interiorMargin;
}
}
// Alert elements in views
@mixin sUnSynced {
$c: $colorPausedBg;
border: 1px solid $c;
}
.s-unsynced {
@include sUnsynced();
}
.s-status-timeconductor-unsynced {
// Plot areas
.gl-plot .gl-plot-display-area {
@include sUnsynced();
}
// Object headers
.object-header {
.t-object-alert {
display: inline;
}
}
}
================================================
FILE: src/styles/_legacy-plots.scss
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
/********************************************************************* PLOTS */
mct-plot {
display: contents;
}
/*********************** STACKED PLOT LAYOUT */
.is-editing {
.gl-plot.child-frame {
@include hover {
background: rgba($editUIColorBg, 0.1);
box-shadow: inset rgba($editUIColorBg, 0.3) 0 0 0 1px;
}
&[s-selected] {
background: rgba($editUIColorBg, 0.2);
box-shadow: inset rgba($editUIColorBg, 0.8) 0 0 0 1px;
z-index: 2;
}
}
.plot-wrapper-axis-and-display-area {
pointer-events: none;
}
}
.c-plot,
.gl-plot {
.s-status-taking-snapshot & {
.c-control-bar {
display: none;
}
.gl-plot-x-label__select,
.gl-plot-y-label__select {
display: none;
}
}
/*********************** MISSING ITEM INDICATORS */
.is-status__indicator {
font-size: 0.8em;
}
}
.c-plot {
@include abs($mainViewPad);
display: flex;
overflow: hidden;
min-height: $plotMinH;
.c-control-bar {
flex: 0 0 auto;
margin-bottom: $interiorMargin;
}
.l-view-section {
display: flex;
flex: 1 1 auto;
flex-direction: column;
overflow-y: auto;
overflow-x: hidden;
}
&.is-stale {
@include isStaleHolder();
}
.c-plot--stacked-container {
border: 1px solid transparent;
display: flex;
flex: 1 1 auto;
flex-direction: column;
min-height: $plotMinH;
overflow: hidden;
&[s-selected] {
.is-editing & {
border: $editMarqueeBorder;
}
}
}
&--stacked {
min-height: auto !important;
.child-frame {
.has-control-bar {
.c-control-bar {
// Hides buttons per plot element in a stacked plot
display: none;
}
}
mct-plot {
display: flex;
flex: 1 1 auto;
height: 100%;
position: relative;
}
flex: 1 1 auto;
}
.s-status-timeconductor-unsynced .holder-plot {
.t-object-alert.t-alert-unsynced {
display: block;
}
}
}
.--width-less-than-600 & {
.c-control-bar {
display: none;
}
}
}
.gl-plot {
display: flex;
flex: 1 1 auto;
/*********************** AXIS AND DISPLAY AREA */
.plot-wrapper-axis-and-display-area {
position: relative;
flex: 1 1 auto;
overflow: hidden;
//min-height: $plotMinH;
}
.gl-plot-wrapper-display-area-and-x-axis {
// Holds the plot area and the X-axis only
position: absolute;
top: nth($plotDisplayArea, 1);
right: 0;
bottom: 0;
left: nth($plotDisplayArea, 4);
.gl-plot-display-area {
overflow: hidden;
position: absolute;
top: 0;
right: 0;
bottom: nth($plotDisplayArea, 3);
left: 0;
}
.gl-plot-chart-area,
.gl-plot-chart-wrapper {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.alt-pressed {
// When alt is being pressed and user is hovering over the plot, set the cursor
@include cursorGrab();
}
.gl-plot-axis-area.gl-plot-x {
top: auto;
right: 0;
bottom: 0;
left: 0;
height: $plotXBarH;
width: auto;
overflow: hidden;
}
}
.gl-plot-axis-area {
position: absolute;
&.gl-plot-y {
top: nth($plotDisplayArea, 1);
right: auto;
bottom: nth($plotDisplayArea, 3);
left: 0;
width: $plotYBarW;
&:hover {
.gl-plot-y-label__select {
display: block;
}
}
}
&.gl-plot-x {
@include hover {
.gl-plot-x-label__select {
display: block;
}
}
}
}
.gl-plot-coords {
// This does not appear to be in use in Open MCT
box-sizing: border-box;
border-radius: $controlCr;
background: black;
padding: 2px 5px;
position: absolute;
top: nth($plotDisplayArea, 1) + $interiorMarginLg;
right: auto;
bottom: auto;
left: nth($plotDisplayArea, 4) + $interiorMarginLg;
z-index: 10;
&:empty {
display: none;
}
}
.gl-plot-ticks,
.gl-plot-tick-wrapper {
height: 100%;
}
.gl-plot-label,
.l-plot-label {
position: absolute;
text-align: center;
&.gl-plot-x-label,
&.l-plot-x-label {
top: auto;
right: 0;
bottom: 0;
left: 0;
height: auto;
}
&.gl-plot-y-label {
display: flex;
left: 0;
right: auto;
bottom: 0;
text-orientation: mixed;
writing-mode: vertical-lr;
//z-index allows clicking on visibility icon
z-index: 2;
.icon-gear-after:after {
// Icon denoting configurability
margin-top: $interiorMargin; // Uses margin-top due to writing-mode
}
.icon-eye-open:before,
.icon-eye-disabled:before {
padding-top: 5px;
}
.plot-series-color-swatch {
@include colorSwatch();
display: inline-block;
flex: 0 0 auto;
margin-bottom: $interiorMargin; // Uses margin-bottom due to writing-mode
}
}
}
.gl-plot-y-label-swatch-container {
display: flex;
flex-direction: row;
overflow: auto;
}
.plot-yaxis-right {
&.gl-plot-y {
margin-left: 100%;
}
.gl-plot-label {
&.gl-plot-y-label {
left: auto;
right: 0;
}
}
.gl-plot-y-label__select {
left: 0;
right: auto;
}
}
.gl-plot-x-label__select {
position: absolute;
left: 50%;
bottom: 0;
transform: translateX(-50%);
z-index: 10;
}
.gl-plot-y-label__select {
position: absolute;
bottom: 2%;
transform: translateY(-50%);
left: 0;
z-index: 10;
}
.gl-plot-x-options,
.gl-plot-y-options {
$h: 24px;
position: absolute;
height: $h;
min-height: $h;
z-index: 2;
}
.gl-plot-x-options {
transform: translateX(-50%);
bottom: 0;
left: 50%;
}
.gl-plot-y-options {
transform: translateY(-50%);
min-width: 150px; // Need this due to enclosure of .select
top: 50%;
left: $plotYLabelW + $interiorMargin * 2;
}
.t-plot-display-controls {
position: absolute;
top: $interiorMargin;
right: $interiorMargin;
}
.gl-plot-hash {
position: absolute;
opacity: $opacityPlotHash;
&.hash-v {
border-right: 1px $colorPlotHash $stylePlotHash;
height: 100%;
}
&.hash-h {
border-bottom: 1px $colorPlotHash $stylePlotHash;
width: 100%;
}
}
&__local-controls {
// Plot local controls
$m: $interiorMargin;
display: flex;
align-items: center;
position: absolute;
top: $m;
left: $m;
z-index: 9;
&__reset {
transition: right 100ms;
top: $m;
right: $m;
}
&__zoom-and-guides {
top: $m;
right: $m;
}
.c-button {
box-shadow: $colorLocalControlOvrBg 0 0 0 2px;
}
}
.l-state-indicators {
color: $colorPausedBg;
position: absolute;
cursor: help;
font-size: 1.2em;
bottom: $interiorMarginSm;
left: $interiorMarginSm;
z-index: 2;
> * + * {
margin-left: $interiorMarginSm;
}
.t-alert-unsynced {
display: none;
}
}
}
.gl-plot-display-area,
.plot-display-area {
@if $colorPlotBg != none {
background-color: $colorPlotBg;
}
cursor: crosshair;
border: 1px solid $colorPlotAreaBorder;
}
.tick {
position: absolute;
border: 0 $colorPlotHash solid;
&.tick-x {
border-right-width: 1px;
height: 100%; // Assumption is that the tick will be in a holder that will set it's height;
}
}
.gl-plot-tick,
.tick-label {
@include reverseEllipsis();
font-size: 0.7rem;
position: absolute;
&.gl-plot-x-tick-label,
&.tick-label-x {
right: auto;
bottom: auto;
left: auto;
height: auto;
width: 20%;
margin-left: -10%;
text-align: center;
}
&.gl-plot-y-tick-label,
&.tick-label-y {
top: auto;
height: 1em;
width: auto;
margin-bottom: -0.5em;
text-align: right;
}
}
.gl-plot-tick {
&.gl-plot-x-tick-label {
top: $interiorMarginSm;
}
&.gl-plot-y-tick-label {
right: $interiorMarginSm;
left: auto;
}
}
.plot-yaxis-right {
.gl-plot-tick {
&.gl-plot-y-tick-label {
left: $interiorMarginSm;
right: auto;
}
}
}
.tick-label {
&.tick-label-x {
top: 0;
}
&.tick-label-y {
right: 0;
left: 0;
}
}
.export-plot {
$bg: white;
$fg: black;
$gry: #999;
background: $bg !important;
z-index: -10;
.l-view-section {
.s-status-timeconductor-unsynced .holder-plot {
.t-object-alert.t-alert-unsynced {
display: none;
}
}
}
.gl-plot-display-area {
background: none !important;
border-color: $gry !important;
.gl-plot-local-controls,
.h-local-controls {
opacity: 0;
}
}
.gl-plot {
color: $fg;
.gl-plot-hash {
opacity: 0.1;
border-color: $fg;
}
}
table {
thead {
border-bottom: none;
th {
background: #eee;
border-left-color: $bg;
color: #666;
}
tr {
border: none;
}
}
tbody {
tr {
border-top: 1px solid #ccc;
}
td {
color: $fg;
}
}
}
}
/****************** _LEGEND.SCSS */
.gl-plot-legend,
.c-plot-legend {
overflow: hidden;
flex: 0 0 auto; // Prevents clipping for all legend placements (top, bottom, etc.)
&__wrapper {
// Holds view-control and both collapsed and expanded legends
flex: 1 1 auto;
height: 100%;
overflow: auto;
padding: 2px;
}
&__view-control {
padding-top: 4px;
margin-right: $interiorMarginSm;
}
&__header {
@include propertiesHeader();
margin-bottom: $interiorMarginSm;
}
.--width-less-than-600 & {
&.is-legend-hidden {
display: none;
}
}
}
.c-plot--stacked {
.is-legend-hidden {
// Always show the legend in a stacked plot
display: flex !important;
}
}
.gl-plot-legend {
display: flex;
align-items: flex-start;
table {
table-layout: fixed;
th,
td {
@include ellipsize(); // Note: this won't work if table-layout uses anything other than fixed.
padding: 1px 3px; // Tighter than standard tabular padding
}
}
}
*[class*='-legend'] {
&.hover-on-plot {
// User is hovering over the plot to get a value at a point
.hover-value-enabled {
background-color: $legendHoverValueBg;
border-radius: $smallCr;
padding: 0 $interiorMarginSm;
&.value-to-display-min:before {
content: 'MIN ';
}
&.value-to-display-max:before {
content: 'MAX ';
}
}
}
}
/***************** GENERAL STYLES, ALL STATES */
.plot-legend-item,
.plot-series-limit-label {
// General styles for legend items, both expanded and collapsed legend states
> * + * {
margin-left: $interiorMarginSm;
}
&.is-stale {
@include isStaleElement();
}
.plot-series-color-swatch {
@include colorSwatch();
display: inline-block;
flex: 0 0 auto;
}
.plot-series-name {
display: inline;
@include ellipsize();
}
.plot-series-value {
@include ellipsize();
@include isLimit();
}
}
.plot-series-swatch-and-name {
display: flex;
flex: 0 1 auto;
align-items: center;
> * + * {
margin-left: $interiorMarginSm;
}
}
.plot-wrapper-expanded-legend {
flex: 1 1 auto;
}
.plot-legend-top .gl-plot-legend {
margin-bottom: $interiorMargin;
}
.plot-legend-bottom .gl-plot-legend {
margin-top: $interiorMargin;
}
.plot-legend-left .gl-plot-legend {
margin-right: $interiorMargin;
}
.plot-legend-right .gl-plot-legend {
margin-left: $interiorMargin;
}
.gl-plot,
.c-plot {
&.plot-legend-collapsed .icon-cursor-lock::before {
padding-right: 5px;
}
&.plot-legend-expanded .icon-cursor-lock::before {
padding-right: 5px;
}
&.plot-legend-top .gl-plot-legend {
margin-bottom: $interiorMargin;
}
&.plot-legend-bottom .gl-plot-legend {
margin-top: $interiorMargin;
}
&.plot-legend-right .gl-plot-legend {
margin-left: $interiorMargin;
}
&.plot-legend-left .gl-plot-legend {
margin-right: $interiorMargin;
}
/***************** GENERAL STYLES, COLLAPSED */
&.plot-legend-collapsed {
// .plot-legend-item is a span of spans.
.plot-legend-item {
border-radius: $smallCr;
display: flex;
justify-content: stretch;
.plot-series-swatch-and-name,
.plot-series-value {
@include ellipsize();
flex: 1 1 auto;
}
.plot-series-value {
text-align: left;
}
}
}
/***************** GENERAL STYLES, EXPANDED */
&.plot-legend-expanded {
.gl-plot-legend {
max-height: 70%;
}
.plot-wrapper-expanded-legend {
overflow-y: auto;
table thead th {
background: $legendTableHeadBg;
}
}
}
/***************** TOP OR BOTTOM */
&.plot-legend-top,
&.plot-legend-bottom,
&.plot-legend-hidden {
// General styles when legend is on the top or bottom
// -hidden included for legacy plots
flex-direction: column;
&.plot-legend-collapsed {
// COLLAPSED ON TOP OR BOTTOM
.plot-wrapper-collapsed-legend {
display: flex;
flex: 1 1 auto;
overflow: hidden;
> .plot-legend-item + .plot-legend-item {
// Space between plot items
margin-left: $interiorMarginLg;
}
}
}
}
/***************** LEFT OR RIGHT */
&.plot-legend-left,
&.plot-legend-right {
// General styles when legend is on left or right
.gl-plot-legend {
max-height: inherit;
}
&.plot-legend-expanded {
// EXPANDED, ON EITHER SIDE
.gl-plot-legend {
width: $plotLegendWidthExpanded;
}
}
&.plot-legend-collapsed {
// COLLAPSED, ON EITHER SIDE
.gl-plot-legend {
width: $plotLegendWidthCollapsed;
}
.plot-wrapper-collapsed-legend {
display: flex;
flex-flow: column nowrap;
min-width: 0;
flex: 1 1 auto;
overflow-y: auto;
> * + * {
// Space between plot items
margin-top: $interiorMarginSm;
}
}
.plot-legend-item {
margin-bottom: $interiorMarginSm;
margin-left: 0;
flex-wrap: nowrap;
.plot-series-swatch-and-name {
@include ellipsize();
flex: 0 1 auto;
min-width: 20%;
}
.plot-series-value {
flex: 0 1 auto;
width: auto;
}
}
}
}
/***************** ON BOTTOM OR RIGHT */
&.plot-legend-right,
&.plot-legend-bottom {
.gl-plot-legend {
order: 2;
}
.plot-wrapper-axis-and-display-area {
order: 1;
}
}
}
/***************** STACKED PLOT LEGEND OVERRIDES */
.c-plot--stacked {
// Always show the legend on top, ignore any position setting
.c-plot,
.gl-plot {
flex-direction: column !important;
.c-plot-legend,
.gl-plot-legend {
margin: 0;
margin-bottom: $interiorMargin;
order: 1 !important;
width: 100% !important;
.plot-wrapper-collapsed-legend {
flex-direction: row !important;
}
}
.plot-wrapper-axis-and-display-area {
order: 2 !important;
}
}
}
/***************** BAR GRAPHS */
.c-bar-chart {
flex: 1 1 auto;
overflow: hidden;
}
/***************** SCATTER PLOTS */
.c-scatter-chart {
flex: 1 1 auto;
overflow: hidden;
}
/***************** CURSOR GUIDES */
[class*='c-cursor-guide'] {
box-shadow: $shdwCursorGuide;
background-color: $colorCursorGuide;
display: none; // Displayed when an element with has-cursor-guides gets a hover; see below
pointer-events: none;
position: absolute;
}
.has-cursor-guides:hover [class*='c-cursor-guide'] {
display: block;
}
.c-cursor-guide {
&--h {
height: 1px;
left: 0;
right: 0;
}
&--v {
width: 1px;
top: 0;
bottom: 0;
}
}
.s-status-timeconductor-unsynced {
.t-alert-unsynced {
display: inline-block !important;
}
}
/*********************** CURSOR LOCK INDICATOR */
[class*='c-state-indicator__alert-cursor-lock'] {
display: none;
}
[class*='is-cursor-locked'] {
background: rgba($colorInfo, 0.1);
[class*='c-state-indicator__alert-cursor-lock'] {
@include userSelectNone();
color: $colorInfo;
display: block;
margin-right: $interiorMarginSm;
&[class*='--verbose'] {
padding: $interiorMarginSm;
}
}
}
================================================
FILE: src/styles/_legacy.scss
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
@use 'sass:math';
/*********************************************************************** CLOCKS AND TIMERS */
.c-clock,
.c-timer {
display: flex;
align-items: center;
font-size: 1.25em;
overflow: hidden;
> * {
flex: 0 0 auto;
display: flex;
align-items: center;
}
.c-frame & {
// When in a Display or Flexible Layout
@include abs();
padding: $interiorMargin;
}
}
.c-clock {
> * + * {
margin-left: $interiorMargin;
}
&__timezone-selection .c-menu {
// Menu for selecting timezones in properties dialog
max-height: 200px;
}
}
.c-timer {
$ctrlW: 22px;
&__controls {
font-size: 1rem !important;
margin-right: 0;
min-width: 0;
overflow: hidden;
@include transition($prop: width, $dur: $transOutTime);
width: 0;
.c-icon-button:before {
font-size: 1em;
}
}
&__direction {
font-size: 0.7em !important;
margin-right: $interiorMargin;
}
&__ng-controller {
font-size: 0;
width: 0;
}
&:hover {
.c-timer__controls {
@include transition(
$prop: width,
$dur: $transOutTime
); // On purpose: want this to take a bit longer
margin-right: $interiorMargin;
width: $ctrlW * 2;
}
&.is-stopped .c-timer__controls {
width: $ctrlW;
}
}
&__direction,
&__value {
opacity: 0.5;
}
&.is-started {
.c-timer {
&__direction,
&__value {
opacity: 1;
}
}
}
}
/*********************************************************************** SUMMARY WIDGETS */
/************************* WIDGET OBJECT */
@mixin cSummaryWidget() {
box-shadow: $shdwBtns;
border-radius: $basicCr;
border-style: solid;
border-width: 1px;
cursor: default;
display: inline-flex;
align-items: center;
justify-content: center;
&[href] {
cursor: pointer;
}
&__icon {
// Hide the icon holder element. Selector below shows this once 'icon-*' is added.
display: none;
font-size: 0.9em;
}
&__label {
@include ellipsize();
}
[class*='icon-'] {
// When 'icon-*' is added, show this element and add margin
display: block;
margin-right: $interiorMarginSm;
}
}
.c-summary-widget,
.c-sw {
@include cSummaryWidget();
padding: $interiorMarginLg $interiorMarginLg * 2;
&--thumb {
max-width: 30%;
padding: $interiorMarginSm $interiorMargin;
}
}
.widget-edit-holder {
// Hide edit area when in browse mode
display: none;
}
.widget-rule-header {
display: flex;
align-items: center;
> * + * {
margin-left: $interiorMargin;
}
}
[class*='action-buttons-wrapper'] {
white-space: nowrap;
line-height: $btnStdH;
}
.widget-rules-wrapper,
.widget-rule-content,
.w-widget-test-data-content {
min-height: 0;
height: 0;
opacity: 0;
overflow: hidden;
pointer-events: none;
}
.widget-rules-wrapper {
flex: 1 1 auto !important;
}
.widget-rule-content.expanded {
overflow: visible !important;
min-height: 50px;
height: auto;
margin-top: $interiorMargin;
opacity: 1;
pointer-events: inherit;
}
.w-widget-test-data-content {
.l-enable {
padding: $interiorMargin 0;
}
.w-widget-test-data-items {
max-height: 20vh;
overflow-y: scroll !important;
padding-right: $interiorMargin;
}
}
.l-widget-thumb-wrapper,
.l-compact-form label {
$ruleLabelW: 40%;
$ruleLabelMaxW: 150px;
display: flex;
max-width: $ruleLabelMaxW;
width: $ruleLabelW;
}
.js-summary-widget__message {
display: none;
}
/**************\ EDITING A WIDGET */
.w-summary-widget {
// Classes for editor layout while editing a widget
// This selector is ugly and brittle, but needed to prevent interface from showing when widget is in a layout
// being edited.
@include abs();
display: flex;
flex-direction: column;
> .l-summary-widget {
// Main view of the summary widget
// Give some airspace and center the widget in the area
margin: 30px auto;
}
.widget-edit-holder {
display: flex; // Overrides `display: none` during Browse mode
flex: 1 1 auto;
overflow: hidden;
.flex-accordion-holder {
// Needed because otherwise accordion elements "creep" when contents expand and contract
display: block !important;
}
&.expanded-widget-test-data {
.w-widget-test-data-content {
min-height: 50px;
height: auto;
opacity: 1;
pointer-events: inherit;
}
&:not(.expanded-widget-rules) {
// Test data is expanded and rules are collapsed
// Make text data take up all the vertical space
.flex-accordion-holder {
display: flex;
}
}
}
&.expanded-widget-rules {
.widget-rules-wrapper {
min-height: 50px;
height: 100%; // Fix for Chrome 73 scrolling bug
opacity: 1;
pointer-events: inherit;
}
}
}
&.s-status-no-data {
.widget-edit-holder {
opacity: 0.3;
pointer-events: none;
}
.js-summary-widget__message {
display: flex;
}
}
.l-compact-form {
// Overrides on .l-compact-form
ul {
&:last-child {
margin: 0;
}
li {
&:not(.widget-rule-header) {
&:not(.connects-to-previous) {
border-top: 1px solid $colorFormLines;
}
}
&.connects-to-previous {
padding: $interiorMargin 0;
}
> label {
display: block; // Needed to align text to right
text-align: right;
width: 90px;
flex: 0 0 auto;
}
.controls {
display: flex;
flex-wrap: wrap;
align-items: center;
align-content: stretch;
> * + * {
margin-left: $interiorMarginSm;
}
}
}
}
&.s-widget-test-data-item {
// Single line of ul li label span, etc.
ul {
li {
border: none !important;
> label {
display: inline-block;
width: auto;
text-align: left;
}
}
}
}
}
.t-condition .controls {
> * {
margin-bottom: $interiorMargin;
}
}
}
.widget-edit-holder {
font-size: 0.8rem;
}
.widget-rules-wrapper {
// Wrapper area that holds n rules
box-sizing: border-box;
overflow-y: scroll;
padding-right: $interiorMargin;
}
.l-widget-rule,
.l-widget-test-data-item {
box-sizing: border-box;
margin-bottom: $interiorMarginSm;
padding: $interiorMargin $interiorMarginLg;
}
.rule-title {
flex: 0 1 auto;
color: pullForward($colorBodyFg, 50%);
}
.rule-description {
flex: 1 1 auto;
@include ellipsize();
color: pushBack($colorBodyFg, 20%);
}
.s-widget-rule,
.s-widget-test-data-item {
background-color: rgba($colorBodyFg, 0.1);
border-radius: $basicCr;
}
.c-sw-edit {
padding: $interiorMargin;
&__ui {
display: flex;
flex-direction: column;
&__header {
border-top: 1px solid $colorInteriorBorder;
display: flex;
align-items: center;
margin: $interiorMargin 0;
padding: $interiorMargin 0;
text-transform: uppercase;
> * + * {
margin-left: $interiorMarginSm;
}
}
}
}
.c-sw-rule {
&__grippy-wrapper {
$d: 8px;
flex: 0 0 auto;
cursor: move;
width: $d;
height: $d;
transform: translateY(-1px);
}
&__grippy {
@include grippy($c: $colorItemTreeVC, $dir: 'y');
@include abs();
}
}
/******************************************************************* CHANNEL SELECTOR */
.channel-selector {
.line {
margin-bottom: $interiorMargin;
min-height: $formInputH;
}
.treeview {
$myBg: darken($colorBodyBg, 2%);
@include reactive-input();
min-height: 300px;
max-height: 400px;
overflow: auto;
padding: $interiorMargin;
}
.btns-add-remove {
margin-top: 150px;
.s-button {
display: block;
margin-bottom: $interiorMargin;
text-align: center;
}
}
}
/******************************************************************* AUTOFLOW TABULAR */
// NOT UNIT TESTED AS OF 3/12/19
.autoflow {
$headerH: $formInputH;
$colMargin: $interiorMargin;
$colW: 225px;
$valW: 70px;
$valPad: 5px;
$rowH: 15px;
font-size: 0.75rem;
&:hover {
.l-autoflow-header .s-button.change-column-width {
opacity: 1;
}
}
.l-autoflow-header {
bottom: auto;
height: $headerH;
line-height: $headerH;
min-width: $colW;
.t-last-update {
overflow: hidden;
}
.s-button.change-column-width {
@include transition($prop: opacity, $dur: $transOutTime);
opacity: 0;
}
.l-filter {
display: block;
margin-right: $interiorMargin;
input.t-filter-input {
width: 150px;
}
}
}
.l-autoflow-items {
overflow-x: scroll;
overflow-y: hidden;
top: $headerH + $interiorMargin * 2;
white-space: nowrap;
.l-autoflow-col {
box-sizing: border-box;
border-left: 1px solid $colorInteriorBorder;
display: inline-block;
padding-left: $colMargin;
padding-right: $colMargin;
vertical-align: top;
width: $colW;
.l-autoflow-row {
box-sizing: border-box;
border-bottom: 1px solid rgba(#fff, 0.05);
display: block;
height: $rowH;
line-height: $rowH;
margin-bottom: 1px;
margin-top: 1px;
position: relative;
&:first-child {
border-top: none;
}
&:hover {
background: rgba(#fff, 0.1);
}
.l-autoflow-item.r {
color: lighten($colorBodyFg, 10%);
}
&.first-in-group {
border-top: 1px solid lighten($colorInteriorBorder, 20%);
}
.l-autoflow-item {
display: block;
&.l {
float: none;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: auto;
}
&.r {
border-radius: $smallCr;
float: right;
margin-left: $interiorMargin;
padding-left: $valPad;
padding-right: $valPad;
text-align: right;
}
}
}
&:first-child {
border-left: none;
padding-left: 0;
}
}
}
}
.frame {
&.child-frame.panel {
.autoflow .l-autoflow-header .l-filter {
display: none;
}
}
}
/******************************************************************* INDICATORS */
/* Indicators are generally only displayed in the ue-bottom-bar element of the main interface */
.h-indicator,
mct-indicators mct-include {
display: inline; // Fallback for display: contents
display: contents;
}
/************************************************* DATETIME UI */
@mixin complexFieldHolder($myW) {
width: $myW + $interiorMargin;
input[type='text'] {
width: $myW;
}
}
.complex.datetime {
span {
display: inline-block;
margin-right: $interiorMargin;
}
.fields {
margin-top: $interiorMarginSm 0;
padding: $interiorMarginSm 0;
}
.date {
@include complexFieldHolder(80px);
}
.time.md {
@include complexFieldHolder(60px);
}
.time.sm {
@include complexFieldHolder(40px);
}
}
/************************************************* INFO BUBBLES */
.l-infobubble-wrapper {
$arwSize: 5px;
box-shadow: rgba(black, 0.4) 0 1px 5px;
position: relative;
z-index: 50;
.l-infobubble {
display: inline-block;
min-width: $bubbleMinW;
max-width: $bubbleMaxW;
padding: 5px 10px;
&:before {
content: '';
position: absolute;
width: 0;
height: 0;
}
table {
width: 100%;
tr {
td {
padding: 2px 0;
vertical-align: top;
&.label {
padding-right: $interiorMargin * 2;
white-space: nowrap;
}
&.value {
//word-wrap: break-word; // Doesn't work in ?
word-break: break-all;
}
&.align-wrap {
white-space: normal;
}
}
}
}
.title {
@include ellipsize();
margin-bottom: $interiorMargin;
}
}
&.arw-down {
margin-bottom: $arwSize * 2;
.l-infobubble::before {
left: 50%;
top: 100%;
margin-left: -1 * $arwSize;
border-left: $arwSize solid transparent;
border-right: $arwSize solid transparent;
border-top: ($arwSize * 1.5) solid $colorInfoBubbleBg;
}
}
.arw {
z-index: 2;
}
&.arw-up .arw.arw-down,
&.arw-down .arw.arw-up {
display: none;
}
}
body.desktop {
.l-infobubble {
&.arw-left {
margin-left: $bubbleArwSize * 2;
&:before {
@include triangle('left', $bubbleArwSize, 1.5, $colorInfoBubbleBg);
right: 100%;
}
}
&.arw-right {
margin-right: $bubbleArwSize * 2;
&:before {
@include triangle('right', $bubbleArwSize, 1.5, $colorInfoBubbleBg);
left: 100%;
}
}
&.arw-top {
&:before {
top: $bubbleArwSize * 2;
}
}
&.arw-btm {
&:before {
bottom: $bubbleArwSize * 2;
}
}
}
}
.l-thumbsbubble-wrapper {
.arw-up {
@include triangle('up', $bubbleArwSize, 1.5, $colorThumbsBubbleBg);
}
.arw-down {
@include triangle('down', $bubbleArwSize, 1.5, $colorThumbsBubbleBg);
}
}
.s-infobubble {
$emFg: darken($colorInfoBubbleFg, 20%);
border-radius: $basicCr;
box-shadow: rgba(black, 0.4) 0 1px 5px;
background: $colorInfoBubbleBg;
color: $colorInfoBubbleFg;
font-size: 0.8rem;
.title {
color: $emFg;
font-weight: bold;
}
table {
tr {
td {
border: none;
border-top: 1px solid darken($colorInfoBubbleBg, 10%) !important;
font-size: 0.9em;
}
&:first-child td {
border-top: none !important;
}
}
}
&:first-child td {
border-top: none;
}
.label {
color: lighten($emFg, 30%);
}
.value {
color: $emFg;
}
}
.s-thumbsbubble {
background: $colorThumbsBubbleBg;
color: $colorThumbsBubbleFg;
}
/***************************************************************** SPLITTERS */
.splitter {
display: block;
position: absolute;
z-index: 3;
&:after {
// The handle
content: '';
pointer-events: none;
@include abs(0);
background: $colorSplitterBg;
display: block;
z-index: 4;
}
&:active {
&:after {
background-color: $colorSplitterActive !important;
}
}
@if $colorSplitterHover != 'none' {
&:not(:active) {
&:hover {
&:after {
background-color: $colorSplitterHover !important;
@include transition($prop: background-color, $dur: 150ms);
}
}
}
}
}
.split-layout {
$inset: $splitterHandleHitMargin;
&.horizontal {
// Slides vertically up and down, splitting the element horizontally
overflow: hidden; // Suppress overall scroll; each internal pane handles its own overflow
.pane {
left: 0;
right: 0;
&.top {
bottom: auto;
}
&.bottom {
top: auto;
}
}
> .splitter {
cursor: row-resize;
left: 0;
right: 0;
height: $splitterHandleD;
&:after {
top: $inset;
bottom: $inset;
}
}
}
&.vertical {
// Slides horizontally left to right, splitting the element vertically
.pane {
top: 0;
bottom: 0;
&.left {
right: auto;
}
&.right {
left: auto;
}
}
> .splitter {
cursor: col-resize;
top: 0;
bottom: 0;
width: $splitterHandleD;
&:after {
left: $inset;
right: $inset;
//width: $splitterHandleD;
}
&.flush-right {
width: ceil(math.div($splitterHandleD, 2));
&:after {
width: $splitterHandleD;
left: auto;
right: 0;
}
&.edge-shdw {
background-image: linear-gradient(
90deg,
rgba(black, 0) 40%,
rgba(black, 0.05) 70%,
rgba(black, 0.2) 100%
);
}
}
}
}
}
/******************************************************************* FLEX STYLES */
.l-flex-row,
.l-flex-col {
display: flex;
flex-wrap: nowrap;
.flex-elem {
min-height: 0; // Needed to allow element to shrink within parent
position: relative;
&:not(.grows) {
flex: 0 0 auto;
&.flex-can-shrink {
flex: 0 1 auto;
}
}
&.grows {
flex: 1 1 auto;
}
&.contents-align-right {
text-align: right;
}
}
.flex-container {
// Apply to wrapping elements, mct-includes, etc.
display: flex;
flex-wrap: nowrap;
flex: 1 1 auto;
min-height: 0;
}
}
.l-flex-row {
flex-direction: row;
&.flex-elem {
flex: 1 1 auto;
}
> .flex-elem {
min-width: 0;
&.holder:not(:last-child) {
margin-right: $interiorMargin;
}
}
.flex-container {
flex-direction: row;
}
}
.l-flex-col {
flex-direction: column;
> .flex-elem {
min-height: 0;
&.holder:not(:last-child) {
margin-bottom: $interiorMarginLg;
}
}
&.l-flex-accordion .flex-accordion-holder {
display: flex;
flex-direction: column;
}
.flex-container {
flex-direction: column;
}
}
.flex-fixed {
flex: 0 0 auto;
}
.flex-justify-end {
justify-content: flex-end;
}
/******************************************************************* GRID STYLES */
.grid-two-column,
.grid-properties {
display: grid;
grid-row-gap: 0;
grid-template-columns: 1fr 2fr;
}
.grid-span-all,
.grid-two-column-span-cols {
grid-column: 1 / 3;
}
.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);
}
}
.grid-row {
display: contents;
}
.grid-row {
.grid-cell {
padding: 3px $interiorMarginLg 3px 0;
&[title]:not([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;
}
}
}
/******************************************************************* ABOUT SCREEN */
.l-about {
&.abs {
overflow: auto;
}
$contentH: 200px;
.l-splash {
position: relative;
height: 45%;
}
.l-content {
position: relative;
margin-top: $interiorMarginLg;
}
}
.s-about {
line-height: 120%;
a {
color: $colorAboutLink;
}
h1,
h2,
h3 {
color: pullForward($colorBodyFg, 20%);
margin-bottom: 1em;
}
h1 {
font-size: 2.25em;
}
h2 {
border-top: 1px solid $colorInteriorBorder;
font-size: 1.5em;
margin-top: 2em;
padding-top: 1em;
}
h3 {
margin-top: 2em;
}
.s-description,
.s-button {
line-height: 2em;
}
.l-licenses-software {
.l-license-software {
border-top: 1px solid $colorInteriorBorder;
padding: 0.5em 0;
&:first-child {
border-top: none;
}
em {
color: pushBack($colorBodyFg, 20%);
}
h3 {
font-size: 1.25em;
}
.s-license-text {
font-size: 0.9em;
}
}
}
}
/******************************************************************* STARTUP / SPLASH SCREEN */
@mixin splashElem($m: 20%) {
top: $m;
right: $m * 1.25;
bottom: $m;
left: $m * 1.25;
}
.l-splash,
.l-splash:before,
.l-splash:after {
background-position: center;
background-repeat: no-repeat;
position: absolute;
}
.l-splash {
background-size: cover;
top: 0;
right: 0;
bottom: 0;
left: 0;
&:before,
&:after {
background-size: contain;
content: '';
}
&:before {
// NASA logo, dude
$w: 5%;
$m: 10px;
background-image: url('../ui/layout/assets/images/logo-nasa.svg');
top: $m;
right: auto;
bottom: auto;
left: $m;
height: auto;
width: $w * 2;
padding-bottom: $w;
padding-top: $w;
}
&:after {
// App logo
top: 0;
right: 15%;
bottom: 0;
left: 15%;
}
}
.l-splash-holder {
// Main outer holder for splash.
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 10000;
opacity: 1;
.l-splash {
// The splash element.
@include splashElem();
}
}
@media only screen and (max-device-width: 767px) {
.l-splash-holder .l-splash {
@include splashElem(0);
border-radius: 0;
box-shadow: none;
}
}
@media only screen and (max-device-width: 767px) and (orientation: portrait) {
.l-splash-holder .l-splash {
&:before {
// Make the NASA logo a bit bigger when we're in portrait mode.
$w: 12%;
width: $w * 2;
padding-bottom: $w;
padding-top: $w;
}
}
}
/******************************************************************* VARIOUS */
.c-overlay mct-include {
display: inline; // Fallback for display: contents
display: contents;
}
mct-container {
display: block;
}
.overlay {
.outer-holder {
background: $colorMenuBg;
color: $colorMenuFg !important;
}
}
.t-popup {
z-index: 75;
}
.form .form-row {
.label {
color: $colorMenuFg !important;
}
.selector-list {
@include reactive-input();
background: $colorInputBg !important;
color: $colorInputFg !important;
}
}
.ui-symbol.view-control {
display: block;
transform-origin: center center;
&:before {
content: $glyph-icon-arrow-right-equilateral;
}
&.expanded {
transform: rotate(90deg);
}
}
.t-frame-outer {
min-width: 200px;
min-height: 200px;
}
.l-iframe {
iframe {
display: block;
height: 100%;
width: 100%;
border: none;
}
}
// Alert elements in views
.s-unsynced {
@include sUnsynced();
}
.s-status-timeconductor-unsynced {
// Plot areas
.gl-plot .gl-plot-display-area {
@include sUnsynced();
}
// Object headers
.object-header {
.t-object-alert {
display: inline;
}
}
}
.abs {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
height: auto;
width: auto;
}
.code {
font-family: 'Lucida Console', monospace;
font-size: 0.7em;
line-height: 150%;
white-space: pre;
}
.codehilite {
@extend .code;
background-color: rgba($colorBodyFg, 0.1);
padding: 1em;
}
.s-status-missing {
// Labels. Expects .s-status-missing to be applied to mct-representation that contains
.t-object-label .t-item-icon:before {
content: $glyph-icon-object-unknown;
}
// Item, grid item. Expects .s-status-missing to be applied to mct-representation that contains .item.grid-item
.item .t-item-icon-glyph:before {
content: $glyph-icon-object-unknown;
}
// Object header. Expects .s-status-missing to be applied to mct-representation.object-header
&.object-header {
.type-icon:before {
content: $glyph-icon-object-unknown;
}
}
// Tree item. Expects .s-status-missing to be applied to .tree-item,
// and mct-representation.search-item
&.tree-item,
&.search-item {
> .rep-object-label .t-item-icon:before {
content: $glyph-icon-object-unknown;
}
}
}
.align-right {
text-align: right;
}
.centered {
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%);
box-shadow: inset rgba(black, 0.3) 0 0 0 1px;
background-repeat: no-repeat;
background-size: contain;
}
.scrolling,
.scroll {
overflow: auto;
}
.vscroll {
overflow-x: hidden;
overflow-y: auto;
&.scroll-pad {
padding-right: $interiorMargin;
}
}
.vscroll--persist {
overflow-x: hidden;
overflow-y: scroll;
}
.slidable {
cursor: move; // Fallback
cursor: grab;
cursor: -moz-grab;
cursor: -webkit-grab;
&.horz {
cursor: col-resize;
}
&.vert {
cursor: row-resize;
}
}
.no-margin {
margin: 0;
}
.ds {
box-shadow: rgba(#000, 0.7) 0 4px 10px 2px;
}
.capitalize {
text-transform: capitalize;
}
.hide,
.hidden,
.t-main-view .hide-in-t-main-view {
display: none !important;
}
.hide-nice {
opacity: 0;
pointer-events: none;
}
.invisible {
display: block;
visibility: hidden;
height: 0;
padding: 0;
border: 0;
margin: 0 !important;
transform: scale(0);
pointer-events: none;
position: absolute;
}
.sep {
color: rgba(#fff, 0.2);
}
.comma-list span {
&:not(:first-child) {
&:before {
content: ', ';
}
}
}
================================================
FILE: src/styles/_limits.scss
================================================
$plotLimitLineSize: 1px;
$plotLimitDashWidthOffset: 10px;
$lineBlocker: $colorPlotLimitLineBg;
$plotLimitDashWidthSeverity: 50px;
$plotLimitDashWidthCritical: $plotLimitDashWidthSeverity - $plotLimitDashWidthOffset;
$plotLimitDashWidthDistress: $plotLimitDashWidthCritical - $plotLimitDashWidthOffset;
$plotLimitDashWidthWarning: $plotLimitDashWidthDistress - $plotLimitDashWidthOffset;
$plotLimitDashWidthWatch: $plotLimitDashWidthWarning - $plotLimitDashWidthOffset;
@mixin plotLimitLine($c, $breakPerc) {
background: $lineBlocker
linear-gradient(90deg, $c $breakPerc, transparent $breakPerc, transparent 100%) repeat-x;
}
@mixin plotLimitDirectionGradient($c, $deg: 0deg) {
background: linear-gradient($deg, $c, transparent);
}
@mixin plotLimitLineUpper($c) {
$breakPerc: 80%;
@include plotLimitLine($c: $c, $breakPerc: $breakPerc);
}
@mixin plotLimitLineLower($c) {
$breakPerc: 30%;
@include plotLimitLine($c: $c, $breakPerc: $breakPerc);
}
.c-plot-limit-line {
box-shadow: $lineBlocker 0 0 0 2px;
height: $plotLimitLineSize;
width: 100%;
position: absolute;
z-index: 1;
// Colors and directions
&--purple.c-plot-limit-line--upr {
@include plotLimitLineUpper($colorLimitPurpleIc);
}
&--purple.c-plot-limit-line--lwr {
@include plotLimitLineLower($colorLimitPurpleIc);
}
&--red.c-plot-limit-line--upr {
@include plotLimitLineUpper($colorLimitRedIc);
}
&--red.c-plot-limit-line--lwr {
@include plotLimitLineLower($colorLimitRedIc);
}
&--orange.c-plot-limit-line--upr {
@include plotLimitLineUpper($colorLimitOrangeIc);
}
&--orange.c-plot-limit-line--lwr {
@include plotLimitLineLower($colorLimitOrangeIc);
}
&--yellow.c-plot-limit-line--upr {
@include plotLimitLineUpper($colorLimitYellowIc);
}
&--yellow.c-plot-limit-line--lwr {
@include plotLimitLineLower($colorLimitYellowIc);
}
&--cyan.c-plot-limit-line--upr {
@include plotLimitLineUpper($colorLimitCyanIc);
}
&--cyan.c-plot-limit-line--lwr {
@include plotLimitLineLower($colorLimitCyanIc);
}
// Severities
&--severe {
background-size: $plotLimitDashWidthSeverity 100% !important;
}
&--critical {
background-size: $plotLimitDashWidthCritical 100% !important;
}
&--distress {
background-size: $plotLimitDashWidthDistress 100% !important;
}
&--warning {
background-size: $plotLimitDashWidthWarning 100% !important;
}
&--watch {
background-size: $plotLimitDashWidthWatch 100% !important;
}
}
.c-plot-limit {
// Holds both label and directional gradient
$labelCr: $basicCr;
display: flex;
position: absolute;
width: 100%;
z-index: 0;
&__label {
border-width: 1px 1px 0 0;
border-style: solid;
border-radius: 0 $labelCr 0 0;
display: flex;
flex: 0 0 auto;
align-items: center;
padding: 2px 4px;
transform: translateY(-100%);
> * + * {
margin-left: $interiorMarginSm;
}
}
&.--align-label-right {
justify-content: flex-end;
.c-plot-limit__label {
border-radius: $labelCr 0 0 0;
border-width: 1px 0 0 1px;
}
}
&.--align-label-below {
.c-plot-limit__label {
border-radius: 0 0 $labelCr 0;
border-width: 0 1px 1px 0;
transform: translateY(0);
}
&.--align-label-right {
.c-plot-limit__label {
border-radius: 0 0 0 $labelCr;
border-width: 0 0 1px 1px;
}
}
}
[class*='icon'] {
&:before {
display: block;
font-family: symbolsfont;
font-size: 0.9em;
}
}
&__series-color-swatch {
@include colorSwatch();
display: block;
flex: 0 0 auto;
}
&:before {
// Direction gradient
content: '';
display: block;
position: absolute;
left: 0;
right: 0;
height: 100%;
opacity: 0.2;
}
&--upr:before {
transform: translateY(-100%);
}
&--lwr:before {
transform: scaleY(-1); // This inverts the gradient direction
}
// Label styling
&--purple [class*='label'] {
background-color: $colorLimitPurpleBg;
border-color: $colorLimitPurpleIc;
color: $colorLimitPurpleFg;
}
&--red [class*='label'] {
background-color: $colorLimitRedBg;
border-color: $colorLimitRedIc;
color: $colorLimitRedFg;
}
&--orange [class*='label'] {
background-color: $colorLimitOrangeBg;
border-color: $colorLimitOrangeIc;
color: $colorLimitOrangeFg;
}
&--yellow [class*='label'] {
background-color: $colorLimitYellowBg;
border-color: $colorLimitYellowIc;
color: $colorLimitYellowFg;
}
&--cyan [class*='label'] {
background-color: $colorLimitCyanBg;
border-color: $colorLimitCyanIc;
color: $colorLimitCyanFg;
}
// Directional gradients
&--purple:before {
@include plotLimitDirectionGradient($c: $colorLimitPurpleIc);
}
&--red:before {
@include plotLimitDirectionGradient($c: $colorLimitRedIc);
}
&--orange:before {
@include plotLimitDirectionGradient($c: $colorLimitOrangeIc);
}
&--yellow:before {
@include plotLimitDirectionGradient($c: $colorLimitYellowIc);
}
&--cyan:before {
@include plotLimitDirectionGradient($c: $colorLimitCyanIc);
}
}
// Severity icons
.c-plot-limit__label .c-plot-limit__severity-icon:before {
.c-plot-limit--severe & {
content: $glyph-icon-alert-triangle;
}
.c-plot-limit--critical & {
content: $glyph-icon-alert-rect;
}
.c-plot-limit--distress & {
content: $glyph-icon-bell;
}
.c-plot-limit--warning & {
content: $glyph-icon-asterisk;
}
.c-plot-limit--watch & {
content: $glyph-icon-eye-open;
}
}
// Direction icons
.c-plot-limit__label .c-plot-limit__direction-icon:before {
.c-plot-limit--upr & {
content: $glyph-icon-arrow-up;
}
.c-plot-limit--lwr & {
content: $glyph-icon-arrow-down;
}
}
================================================
FILE: src/styles/_mixins.scss
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
@use 'sass:math';
/************************** GLYPHS */
@mixin glyphBefore($unicode, $family: 'symbolsfont') {
&:before {
content: $unicode;
font-family: $family;
}
}
@mixin glyphAfter($unicode, $family: 'symbolsfont') {
&:after {
content: $unicode;
font-family: $family;
}
}
@mixin glyphBg($glyphUrl) {
background-image: $glyphUrl;
background-position: center;
background-size: contain;
background-repeat: no-repeat;
}
[class*='icon-'] {
&:before {
-webkit-font-smoothing: antialiased;
}
}
/************************** EFFECTS */
@mixin flash(
$animName: flash,
$dur: 500ms,
$dir: alternate,
$iter: 20,
$prop: background,
$valStart: rgba($colorOk, 1),
$valEnd: rgba($colorOk, 0)
) {
@keyframes #{$animName} {
0% {
#{$prop}: $valStart;
}
100% {
#{$prop}: $valEnd;
}
}
animation-name: $animName;
animation-duration: $dur;
animation-direction: $dir;
animation-iteration-count: $iter;
animation-timing-function: ease-out;
}
@mixin mixedBg() {
$c1: nth($mixedSettingBg, 1);
$c2: nth($mixedSettingBg, 2);
$mixedBgD: $mixedSettingBgSize $mixedSettingBgSize;
@include bgStripes2Color($c1, $c2, $bgSize: $mixedBgD);
}
@mixin pulse($animName: pulse, $dur: 500ms, $iteration: infinite, $opacity0: 0.5, $opacity100: 1) {
@keyframes #{$animName} {
0% {
opacity: $opacity0;
}
100% {
opacity: $opacity100;
}
}
animation-name: $animName;
animation-duration: $dur;
animation-direction: alternate;
animation-iteration-count: $iteration;
animation-timing-function: ease-in-out;
}
@mixin pulseProp(
$animName: pulseProp,
$dur: 500ms,
$iter: 5,
$prop: opacity,
$valStart: 0,
$valEnd: 1
) {
@keyframes #{$animName} {
0% {
#{$prop}: $valStart;
}
100% {
#{$prop}: $valEnd;
}
}
animation-name: $animName;
animation-duration: $dur;
animation-direction: alternate;
animation-iteration-count: $iter;
animation-timing-function: ease-in-out;
}
@mixin transition($prop: all, $dur: $transInTime, $timing: ease-in-out, $delay: 0ms) {
transition-property: $prop;
transition-duration: $dur;
transition-timing-function: $timing;
transition-delay: $delay;
}
/************************** VISUALS */
@mixin ancillaryIcon($d, $c) {
// Used for small icons used in combination with larger icons,
// like the link and alert icons in tree items.
color: $c;
font-size: $d;
line-height: $d;
height: $d;
width: $d;
}
@mixin appearanceNone() {
-moz-appearance: none;
-webkit-appearance: none;
appearance: none;
&:focus {
outline: none;
}
}
@mixin isAlias() {
&:after {
color: $colorIconAlias;
content: $glyph-icon-link;
display: block;
font-family: symbolsfont;
position: absolute;
text-shadow: rgba(black, 0.5) 0 1px 4px;
top: auto;
left: 0;
bottom: 10%;
right: auto;
transform-origin: left bottom;
transform: scale(0.5);
}
}
@mixin isStatus($absPos: false, $glyph: '', $color: $colorBodyFg) {
// Supports CSS classing as follows:
// is-status--missing, is-status--suspect, etc.
// Common styles to be applied to tree items, object labels, grid and list item views
.is-status__indicator {
display: block; // Set to display: none in status.scss
text-shadow: $colorBodyBg 0 0 2px;
font-family: symbolsfont;
@if $absPos {
position: absolute;
z-index: 3;
}
&:before {
color: $color;
content: $glyph;
}
}
}
@mixin isStaleGlyph() {
content: $glyph-icon-stale;
display: block;
font-family: symbolsfont;
font-style: normal;
pointer-events: none;
}
@mixin isStaleHolder() {
// Applied to objects that frame content, like Display Layout frames, plots, etc.
border-radius: 3px;
border: 2px solid rgba($colorTelemStale, 0.8) !important;
&:before {
@include isStaleGlyph();
color: $colorTelemStale;
position: absolute;
bottom: 5px;
left: 3px;
width: 12px;
z-index: 10;
}
}
@mixin isStaleElement() {
// Applied directly to values, like LAD Table cells, alphanumerics, plot legend items
background: $colorTelemStale !important;
color: $colorTelemStaleFg !important;
font-style: italic;
}
@mixin isLimit() {
&[class*='is-limit'] {
&:before {
display: inline-block;
font-family: symbolsfont;
margin-right: $interiorMarginSm;
}
}
&.is-limit--lwr:before {
content: $glyph-icon-arrow-down;
}
&.is-limit--upr:before {
content: $glyph-icon-arrow-up;
}
&.is-limit--purple {
background: $colorLimitPurpleBg !important;
color: $colorLimitPurpleFg !important;
}
&.is-limit--red {
background: $colorLimitRedBg !important;
color: $colorLimitRedFg !important;
}
&.is-limit--orange {
background: $colorLimitOrangeBg !important;
color: $colorLimitOrangeFg !important;
}
&.is-limit--yellow {
background: $colorLimitYellowBg !important;
color: $colorLimitYellowFg !important;
}
&.is-limit--cyan {
background: $colorLimitCyanBg !important;
color: $colorLimitCyanFg !important;
}
}
@mixin bgDiagonalStripes($c: yellow, $a: 0.1, $d: 40px) {
background-image: linear-gradient(
-45deg,
rgba($c, $a) 25%,
transparent 25%,
transparent 50%,
rgba($c, $a) 50%,
rgba($c, $a) 75%,
transparent 75%,
transparent 100%
)
repeat;
background-size: $d $d;
}
@mixin bgStripes($c: yellow, $a: 0.1, $bgsize: 5px, $angle: 90deg) {
background: linear-gradient(
$angle,
rgba($c, $a) 25%,
transparent 25%,
transparent 50%,
rgba($c, $a) 50%,
rgba($c, $a) 75%,
transparent 75%,
transparent 100%
)
repeat;
background-size: $bgsize $bgsize;
}
@mixin bgStripes2Color($c1, $c2, $bgSize, $angle: -45deg) {
background: linear-gradient(
-45deg,
$c1 0%,
$c1 25%,
$c2 25%,
$c2 50%,
$c1 50%,
$c1 75%,
$c2 75%,
$c2 100%
)
repeat;
background-size: $bgSize;
}
@mixin bgCheckerboard($c: $colorBodyFg, $opacity: 0.3, $size: 32px, $imp: false) {
$color: rgba($c, $opacity);
$bgPos: floor(math.div($size, 2));
$impStr: null;
@if $imp {
$impStr: !important;
}
background-image:
linear-gradient(45deg, $color 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, $color 75%),
linear-gradient(45deg, transparent 75%, $color 75%),
linear-gradient(45deg, $color 25%, transparent 25%) $impStr;
background-size: $size $size;
background-position:
0 0,
0 0,
-1 * $bgPos -1 * $bgPos,
$bgPos $bgPos;
}
@mixin disabled() {
opacity: $controlDisabledOpacity;
pointer-events: none !important;
cursor: default !important;
}
@mixin grippy($c: rgba(black, 0.5), $dir: 'x') {
$deg: 90deg;
$bgSize: 3px 100%;
@if $dir != 'x' {
// Grippy texture runs 'vertically'
$deg: 0deg;
$bgSize: 100% 3px;
}
background: linear-gradient($deg, $c 1px, transparent 1px, transparent 100%) repeat;
background-size: $bgSize;
}
@mixin colorSwatch() {
border-radius: 30%;
border: 1px solid $colorBodyBg;
height: $plotSwatchD;
width: $plotSwatchD;
}
@mixin dropDownArrowBg() {
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10'%3e%3cpath fill='%23#{svgColorFromHex($colorSelectArw)}' d='M5 5l5-5H0z'/%3e%3c/svg%3e");
background-repeat: no-repeat, no-repeat;
background-position:
right 0.4em top 80%,
0 0;
}
@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 sliderTrack($bg: $scrollbarTrackColorBg) {
border-radius: 2px;
box-sizing: border-box;
background-color: $bg;
}
@mixin triangle($dir: 'left', $size: 5px, $ratio: 1, $color: red) {
width: 0;
height: 0;
$slopedB: math.div($size, $ratio) solid transparent;
$straightB: $size solid $color;
@if $dir == 'up' {
border-left: $slopedB;
border-right: $slopedB;
border-bottom: $straightB;
} @else if $dir == 'right' {
border-top: $slopedB;
border-bottom: $slopedB;
border-left: $straightB;
} @else if $dir == 'down' {
border-left: $slopedB;
border-right: $slopedB;
border-top: $straightB;
} @else {
border-top: $slopedB;
border-bottom: $slopedB;
border-right: $straightB;
}
}
@mixin bgVertStripes($c: yellow, $a: 0.1, $d: 40px) {
background-image: linear-gradient(
-90deg,
rgba($c, $a) 0%,
rgba($c, $a) 50%,
transparent 50%,
transparent 100%
);
background-repeat: repeat;
background-size: $d $d;
}
/************************** LAYOUT */
@mixin abs($m: 0) {
position: absolute;
top: $m;
right: $m;
bottom: $m;
left: $m;
}
@mixin absCenter() {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
transform-origin: center center;
}
@mixin propertiesHeader() {
border-radius: $smallCr;
background-color: $colorInspectorSectionHeaderBg;
color: $colorInspectorSectionHeaderFg;
font-weight: normal;
padding: $interiorMarginSm $interiorMargin;
text-transform: uppercase;
}
@mixin modalFullScreen() {
// Optional modifier that makes a c-menu more mobile-friendly
position: fixed;
border-radius: 0;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
/************************** TEXT */
@mixin ellipsize() {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
@mixin fadeTruncate($color: $colorBodyBg, $angle: 90deg) {
background-image: linear-gradient($angle, transparent 0%, $color 100%);
}
@mixin reverseEllipsis() {
@include ellipsize();
direction: ltr;
unicode-bidi: bidi-override;
}
/************************** CONTROLS, BUTTONS, INPUTS */
@mixin hover {
body.desktop & {
&:hover {
@content;
}
}
}
@mixin htmlInputReset() {
@include appearanceNone();
background: transparent;
border: none;
border-radius: 0;
outline: none;
text-align: inherit;
&:focus {
outline: 0;
}
}
@mixin input-base() {
@include htmlInputReset();
border-radius: $controlCr;
&.error {
background: $colorFormFieldErrorBg;
color: $colorFormFieldErrorFg;
}
}
@mixin nice-input(
$bg: $colorInputBg,
$fg: $colorInputFg,
$shdw: inset rgba(black, 0.5) 0 0 2px 1px
) {
@include input-base();
background: $bg;
color: $fg;
box-shadow: $shdw;
}
@mixin reactive-input($bg: $colorInputBg, $fg: $colorInputFg) {
@include input-base();
background: $bg;
box-shadow: $shdwInput;
color: $fg;
&:focus {
box-shadow: $shdwInputFoc;
}
&::selection {
background: rgba($colorKeySelectedBg, 0.5);
}
}
@mixin inlineInput() {
@include reactive-input($bg: transparent);
box-shadow: none;
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;
}
}
@mixin button($bg: $colorBtnBg, $fg: $colorBtnFg, $radius: $controlCr, $shdw: none) {
background: $bg;
color: $fg;
border-radius: $radius;
box-shadow: $shdw;
}
@mixin cControl() {
$fs: 1em;
@include userSelectNone();
display: inline-flex;
align-items: center;
justify-content: center;
line-height: $fs; // Remove effect on top and bottom padding
overflow: hidden;
&:before,
&:after {
font-family: symbolsfont;
display: block;
flex: 0 0 auto;
}
&:before {
font-size: 0.9em;
}
&:after {
font-size: 0.8em;
}
[class*='__label'] {
@include ellipsize();
display: block;
font-size: $fs;
}
&[class*='icon'] > [class*='__label'] {
// When button holds both an icon and a label, provide margin between them.
margin-left: $interiorMarginSm;
}
}
@mixin cControlHov($styleConst: $shdwBtnHov) {
transition: box-shadow $transOutTime;
@include hover() {
transition: box-shadow $transInTime;
box-shadow: $styleConst !important;
}
}
@function cButtonPadding($padding: $interiorMargin, $compact: false) {
@if $compact {
@return floor(math.div($padding, 1.5)) $padding;
} @else {
@return $padding floor($padding * 1.25);
}
}
@mixin cButtonLayout() {
$pad: $interiorMargin;
padding: cButtonPadding($pad);
&:after,
> * + * {
margin-left: $interiorMarginSm;
}
&[class*='--compact'] {
//padding: floor(math.div($pad, 1.5)) $pad;
padding: cButtonPadding($pad, true);
}
}
@mixin cButton() {
@include cControl();
@include cControlHov();
@include themedButton();
@include cButtonLayout();
border-radius: $controlCr;
color: $colorBtnFg;
cursor: pointer;
&[class*='--major'],
&[class*='is-active'] {
background: $colorBtnMajorBg !important;
color: $colorBtnMajorFg !important;
}
&[class*='--caution'] {
background: $colorBtnCautionBg !important;
color: $colorBtnCautionFg !important;
}
}
@mixin cClickIcon() {
@include cControl();
@include cControlHov();
color: $colorBodyFg;
cursor: pointer;
padding: 4px; // Bigger hit area
opacity: 0.7;
transform-origin: center;
&[class*='--major'] {
color: $colorBtnMajorBg !important;
opacity: 0.8;
}
@include hover() {
transform: scale(1.1);
opacity: 1;
}
}
@mixin cClickIconButtonLayout() {
$pLR: 5px;
$pTB: 5px;
padding: $pTB $pLR;
&:before,
*:before {
// *:before handles any nested containers that may contain glyph elements
// Needed for c-togglebutton.
font-size: 1.15em;
}
}
@mixin cClickIconButton() {
// A clickable element that just includes the icon
// Background is displayed on hover
// Padding is included to facilitate a bigger hit area
// Make the icon bigger relative to its container
@include cControl();
@include cControlHov();
@include cClickIconButtonLayout();
background: none;
color: $colorClickIconButton;
box-shadow: none;
cursor: pointer;
border-radius: $controlCr;
&[class*='--major'] {
color: $colorBtnMajorBg !important;
}
}
@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-bottom'] {
.c-menu {
top: auto;
bottom: 100%;
}
}
&[class*='--menus-down'] {
.c-menu {
top: auto;
bottom: 100%;
}
}
&[class*='--menus-right'] {
.c-menu {
left: 0;
right: auto;
}
}
&[class*='--menus-left'],
&[class*='menus-to-left'] {
.c-menu {
left: auto;
right: 0;
}
}
}
@mixin cArrowButtonBase($colorBg: transparent, $colorFg: $colorBtnFg, $filterHov: $filterHov) {
// Copied from branch new-tree-refactor
background: $colorBg;
&:before {
// Arrow shape
border-style: solid;
content: '';
display: block;
position: absolute;
left: 50%;
top: 50%;
transform-origin: center;
}
&:before {
border-color: $colorFg;
}
&--up,
&--prev {
&:before {
transform: translate(-30%, -50%) rotate(135deg);
}
}
&--down,
&--next {
&:before {
transform: translate(-70%, -50%) rotate(-45deg);
}
}
}
@mixin cArrowButtonSizing($dimOuter: 48px) {
height: $dimOuter;
width: $dimOuter;
$dimInner: floor($dimOuter * 0.6);
$borderW: floor($dimInner * 0.3);
$backOffsetW: floor($dimInner * 0.4);
&:before {
height: $dimInner;
width: $dimInner;
}
&:before {
// Arrow shape
border-width: 0 $borderW $borderW 0;
}
}
@mixin cArrowButton() {
@include cArrowButtonBase();
@include cArrowButtonSizing();
}
@mixin hasMenu() {
&:after {
content: $glyph-icon-arrow-down;
font-family: symbolsfont;
font-size: 0.7em;
margin-left: floor($interiorMarginSm * 0.8);
opacity: 0.5;
}
}
@mixin cSelect($bg, $fg, $arwClr, $shdw) {
$svgArwClr: str-slice(
inspect($arwClr),
2,
str-length(inspect($arwClr))
); // Remove initial # in color value
background: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10'%3e%3cpath fill='%23#{$svgArwClr}' d='M5 5l5-5H0z'/%3e%3c/svg%3e"),
$bg;
color: $fg;
box-shadow: $shdw;
}
@mixin smallerControlButtons() {
.c-click-icon,
.c-button,
.c-icon-button {
// Shrink buttons a bit when they appear in containers
font-size: 0.9em;
padding: 4px;
}
}
@mixin wrappedInput() {
// An input that is wrapped. Optionally includes a __label or icon element.
// Based on .c-search.
@include nice-input($shdw: $shdwInput);
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%;
}
@function decimalToPercent($d) {
@return percentage($d);
}
/************************** UTILITIES */
@mixin browserPrefix($prop, $val) {
#{$prop}: $val;
-ms-#{$prop}: $val;
-moz-#{$prop}: $val;
-webkit-#{$prop}: $val;
}
@mixin userSelectNone() {
@include browserPrefix(user-select, none);
}
@mixin cursorGrab() {
cursor: grab;
cursor: -webkit-grab;
&:active {
cursor: grabbing;
cursor: -webkit-grabbing;
}
}
@function svgColorFromHex($hexColor) {
// Remove initial # in color value
@return str-slice(inspect($hexColor), 2, str-length(inspect($hexColor)));
}
@mixin test($c: deeppink, $a: 0.3) {
background: rgba($c, $a) !important;
background-color: rgba($c, $a) !important;
}
@mixin sUnsynced {
$c: $colorPausedBg;
border: 1px solid $c;
}
// @mixin telemetryView(){
// border: 1px solid $colorBodyFg;
// border-radius: $controlCr;
// }
================================================
FILE: src/styles/_status.scss
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
/*************************************************** MIXINS */
@mixin statusStyle($bg, $fg, $imp: false) {
$impStr: null;
@if $imp {
$impStr: !important;
}
background: $bg $impStr;
background-color: $bg $impStr;
color: $fg $impStr;
}
@mixin statusIcon($ic, $glyph: null, $imp: false) {
$impStr: null;
@if $imp {
$impStr: !important;
}
&:before {
color: $ic;
display: inline-block;
font-family: symbolsfont;
font-size: 0.8em;
margin-right: $interiorMarginSm;
@if $glyph != null {
content: $glyph $impStr;
}
}
}
@mixin statusStyleCombined($bg, $fg, $ic) {
@include statusStyle($bg, $fg, $imp: true);
@include statusIcon($ic);
}
@mixin elementStatusColors($c) {
// Sets bg and icon colors for elements
&:before {
color: $c !important;
}
}
@mixin indicatorStatusColors($c) {
&:before,
.c-indicator__count {
color: $c;
}
}
@mixin uIndicator($bg, $fg, $glyph) {
background: $bg;
color: $fg;
&[class*='--with-icon'] {
&:before {
color: $fg;
display: inline-block;
font-family: symbolsfont;
margin-right: $interiorMarginSm;
@if $glyph != null {
content: $glyph;
}
}
}
&[class*='--block'] {
border-radius: $controlCr;
display: inline-block;
padding: 2px $interiorMargin;
}
}
/*************************************************** STYLES */
tr {
&.is-limit--yellow {
@include statusStyle($colorLimitYellowBg, $colorLimitYellowFg);
td:first-child {
@include statusIcon($colorLimitYellowIc, $glyph-icon-alert-rect);
}
td {
color: $colorLimitYellowFg;
}
}
&.is-limit--red {
@include statusStyle($colorLimitRedBg, $colorLimitRedFg);
td:first-child {
@include statusIcon($colorLimitRedIc, $glyph-icon-alert-triangle);
}
td {
color: $colorLimitRedFg;
}
}
&.is-limit--upr {
td:first-child:before {
content: $glyph-icon-arrow-up !important;
}
}
&.is-limit--lwr {
td:first-child:before {
content: $glyph-icon-arrow-down !important;
}
}
}
/*************************************************** STATUS */
[class*='s-status-icon'] {
&:before {
font-family: symbolsfont;
margin-right: $interiorMargin;
}
}
.s-status-warning-hi,
.s-status-icon-warning-hi {
@include elementStatusColors($colorWarningHi);
}
.s-status-warning-lo,
.s-status-icon-warning-lo {
@include elementStatusColors($colorWarningLo);
}
.s-status-diagnostic,
.s-status-icon-diagnostic {
@include elementStatusColors($colorDiagnostic);
}
.s-status-info,
.s-status-icon-info {
@include elementStatusColors($colorInfo);
}
.s-status-ok,
.s-status-icon-ok {
@include elementStatusColors($colorOk);
}
.s-status-icon-warning-hi:before {
content: $glyph-icon-alert-triangle;
}
.s-status-icon-warning-lo:before {
content: $glyph-icon-alert-rect;
}
.s-status-icon-diagnostic:before {
content: $glyph-icon-eye-open;
}
.s-status-icon-info:before {
content: $glyph-icon-info;
}
.s-status-icon-ok:before {
content: $glyph-icon-check;
}
/*************************************************** INDICATOR COLORING */
.c-indicator {
&.s-status-info {
@include indicatorStatusColors($colorInfo);
}
&.s-status-disabled {
@include indicatorStatusColors($colorIndicatorDisabled);
}
&.s-status-available {
@include indicatorStatusColors($colorIndicatorAvailable);
}
&.s-status-on,
&.s-status-enabled {
@include indicatorStatusColors($colorIndicatorOn);
}
&.s-status-off {
@include indicatorStatusColors($colorIndicatorOff);
}
&.s-status-caution,
&.s-status-warning,
&.s-status-alert {
@include indicatorStatusColors($colorStatusAlert);
}
&.s-status-error {
@include indicatorStatusColors($colorStatusError);
}
}
.s-status {
&--partial {
// Partially completed things, such as a file downloading or process that's running
background-color: $colorStatusPartialBg;
}
&--complete {
// Completed things, such as a file downloaded or process that's finished
background-color: $colorStatusCompleteBg;
}
}
.u-alert {
@include uIndicator($colorAlert, $colorAlertFg, $glyph-icon-alert-triangle);
}
.u-error {
@include uIndicator($colorError, $colorErrorFg, $glyph-icon-alert-triangle);
}
.is-status {
&__indicator {
display: none; // Default state; is set to block when within an actual is-status class
}
&--missing {
@include isStatus($glyph: $glyph-icon-alert-triangle, $color: $colorAlert);
}
&--suspect {
@include isStatus($glyph: $glyph-icon-alert-rect, $color: $colorWarningLo);
}
}
.is-event {
&--purple {
background-color: $colorEventPurpleBg !important;
color: $colorEventPurpleFg !important;
}
&--red {
background-color: $colorEventRedBg !important;
color: $colorEventRedFg !important;
}
&--orange {
background-color: $colorEventOrangeBg !important;
color: $colorEventOrangeFg !important;
}
&--yellow {
background-color: $colorEventYellowBg !important;
color: $colorEventYellowFg !important;
}
}
================================================
FILE: src/styles/_table.scss
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
/******************************************************** TABLE */
table {
$minW: 50px;
width: 100%;
thead {
th {
background: $colorTabHeaderBg;
+ th {
border-left: 1px solid $colorTabHeaderBorder;
}
}
}
tbody {
tr + tr {
border-top: 1px solid $colorTabBorder;
}
}
th,
td {
white-space: nowrap;
min-width: $minW;
padding: $tabularTdPadTB $tabularTdPadLR;
}
td {
vertical-align: top;
}
}
.is-editing {
td.is-selectable {
&:hover {
background: rgba($editUIColorBg, 0.1);
box-shadow: inset rgba($editUIColorBg, 0.8) 0 0 0 1px;
}
&[s-selected] {
background: $editUIColorBg !important;
border: 1px solid $editUIColorFg !important;
color: $editUIColorFg !important;
box-shadow: $editFrameSelectedShdw;
z-index: 2;
}
}
}
/******************************************************** C-TABLE */
div.c-table {
// When c-table is used as a wrapper element in more complex table views
height: 100%;
}
.c-table-wrapper {
// Wraps .c-control-bar and .c-table
display: flex;
flex-direction: column;
overflow: hidden;
// Using absolute here because sizing table can't calc width properly if padding is used
$p: $mainViewPad;
position: absolute;
top: $p;
right: $p;
bottom: $p;
left: $p;
> .c-table {
height: auto;
flex: 1 1 auto;
}
&.is-stale {
@include isStaleHolder();
}
.--width-less-than-600 & {
&:not(.is-paused) {
.c-table-control-bar {
display: none;
}
}
.c-table-control-bar {
.c-icon-button,
.c-click-icon,
.c-button {
&__label {
display: none;
}
}
}
}
}
.c-table-control-bar {
display: flex;
flex: 0 0 auto;
//margin-bottom: $interiorMarginSm; // This approach to allow margin to go away when control bar is hidden
padding: $interiorMarginSm 0;
> * + * {
margin-left: $interiorMarginSm;
}
}
.c-table {
// Can be used by any type of table, scrolling, LAD, etc.
$min-w: 50px;
width: 100%;
&__headers-w {
flex: 0 0 auto;
}
/******************************* ELEMENTS */
thead tr,
&.c-table__headers {
background: $colorTabHeaderBg;
th {
&:not(:first-child) {
border-left: 1px solid $colorTabHeaderBorder;
}
}
}
tbody,
&__body {
tr:not(.c-table__group-header) + tr:not(.c-table__group-header) {
border-top: 1px solid $colorTabBorder;
}
transition: $transOut;
}
&__selectable-row {
cursor: pointer;
&:hover {
background: $colorListItemBgHov;
filter: $filterHov;
transition: $transIn;
}
}
&__group-header {
// tr element found in LAD Table Sets
border-top: 1px solid $colorTabHeaderBorder;
background: $colorTabGroupHeaderBg;
td {
color: $colorTabGroupHeaderFg;
}
}
&--sortable {
.is-sorting {
&:after {
color: $colorIconAlias;
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-lad-table-wrapper {
width: 100%;
height: 100%;
padding: $mainViewPad;
&.is-stale {
@include isStaleHolder();
}
}
.c-lad-table {
&.fixed-layout {
table-layout: fixed;
td {
overflow: hidden;
text-overflow: ellipsis;
}
}
th,
td {
width: 33%; // Needed to prevent size jumping as values dynamically update
overflow: hidden;
text-overflow: ellipsis;
}
tbody tr {
&:hover {
background: $colorItemTreeHoverBg;
}
}
td {
user-select: none; // Table supports context-click to display Actions menu, don't allow text selection.
&.is-stale {
@include isStaleElement();
}
}
}
/************************************** TABLE AND SUMMARY VIEWS */
// Displays summary values above a table.
.c-table-and-summary {
height: 100%;
width: 100%;
overflow: auto;
display: flex;
flex-direction: column;
> * + * {
margin-top: $interiorMargin;
}
&__summary {
display: flex;
justify-items: stretch;
> * + * {
margin-left: 1px;
}
}
&__summary-item {
background: $colorSummaryBg;
color: $colorSummaryFg;
flex: 1 1 auto;
padding: $interiorMargin $interiorMarginLg;
em {
font-weight: bold;
color: $colorSummaryFgEm;
}
}
}
================================================
FILE: src/styles/fonts/Open MCT Symbols 12px.json
================================================
{
"metadata": {
"name": "Open MCT Symbols 12px",
"lastOpened": 0,
"created": 1561483556329
},
"iconSets": [
{
"selection": [
{
"order": 12,
"id": 10,
"name": "icon12-filter",
"prevSize": 12,
"code": 59686,
"tempChar": ""
},
{
"order": 14,
"id": 11,
"name": "icon12-filter-outline",
"prevSize": 12,
"code": 59687,
"tempChar": ""
},
{
"order": 9,
"id": 6,
"name": "icon12-crosshair",
"prevSize": 12,
"code": 59696,
"tempChar": ""
},
{
"order": 11,
"id": 8,
"name": "icon12-grippy",
"prevSize": 12,
"code": 59697,
"tempChar": ""
},
{
"order": 10,
"id": 7,
"name": "icon12-list-view",
"prevSize": 12,
"code": 921666,
"tempChar": ""
},
{
"order": 6,
"id": 3,
"prevSize": 12,
"code": 921865,
"name": "icon12-folder",
"tempChar": ""
}
],
"id": 0,
"metadata": {
"name": "Open MCT Symbols 12px",
"importSize": {
"width": 384,
"height": 384
},
"designer": "Charles Hacskaylo"
},
"height": 1024,
"prevSize": 12,
"icons": [
{
"id": 10,
"paths": [
"M853.333 0h-682.667c-94.135 0.302-170.364 76.532-170.667 170.638l-0 0.029v682.667c0.302 94.135 76.532 170.364 170.638 170.667l0.029 0h256v-341.333l-341.333-341.333h853.333l-341.333 341.333 1.067 341.333h254.933c94.135-0.302 170.364-76.532 170.667-170.638l0-0.029v-682.667c-0.302-94.135-76.532-170.364-170.638-170.667l-0.029-0z"
],
"attrs": [],
"grid": 0,
"tags": ["icon12-filter"]
},
{
"id": 11,
"paths": [
"M853.333 0h-682.667c-94.135 0.302-170.364 76.532-170.667 170.638l-0 0.029v682.667c0.302 94.135 76.532 170.364 170.638 170.667l0.029 0h682.667c94.135-0.302 170.364-76.532 170.667-170.638l0-0.029v-682.667c-0.302-94.135-76.532-170.364-170.638-170.667l-0.029-0zM170.933 853.333h-0.267v-512l256 256v256zM853.067 853.333h-255.2l-0.533-256 256-256v511.733zM853.333 341.333h-682.667v-170.4h682.667z"
],
"attrs": [],
"grid": 0,
"tags": ["icon12-filter-outline"]
},
{
"id": 6,
"paths": [
"M597.333 0h-170.667v256h170.667v-256z",
"M1024 426.667h-256v170.667h256v-170.667z",
"M597.333 768h-170.667v256h170.667v-256z",
"M256 426.667h-256v170.667h256v-170.667z"
],
"attrs": [],
"grid": 0,
"tags": ["icon12-crosshair"]
},
{
"id": 8,
"paths": [
"M186.347 232.64c0 51.458-41.715 93.173-93.173 93.173s-93.173-41.715-93.173-93.173c0-51.458 41.715-93.173 93.173-93.173s93.173 41.715 93.173 93.173z",
"M186.347 511.867c0 51.458-41.715 93.173-93.173 93.173s-93.173-41.715-93.173-93.173c0-51.458 41.715-93.173 93.173-93.173s93.173 41.715 93.173 93.173z",
"M186.347 791.36c0 51.458-41.715 93.173-93.173 93.173s-93.173-41.715-93.173-93.173c0-51.458 41.715-93.173 93.173-93.173s93.173 41.715 93.173 93.173z",
"M465.573 93.173c0 51.458-41.715 93.173-93.173 93.173s-93.173-41.715-93.173-93.173c0-51.458 41.715-93.173 93.173-93.173s93.173 41.715 93.173 93.173z",
"M465.573 372.4c0 51.458-41.715 93.173-93.173 93.173s-93.173-41.715-93.173-93.173c0-51.458 41.715-93.173 93.173-93.173s93.173 41.715 93.173 93.173z",
"M379.028 558.728c51.328 3.652 89.978 48.223 86.325 99.551s-48.223 89.978-99.551 86.325c-51.328-3.652-89.978-48.223-86.325-99.551s48.223-89.978 99.551-86.325z",
"M379.017 837.96c51.328 3.652 89.978 48.223 86.325 99.551s-48.223 89.978-99.551 86.325c-51.328-3.652-89.978-48.223-86.325-99.551s48.223-89.978 99.551-86.325z",
"M744.773 232.64c0 51.458-41.715 93.173-93.173 93.173s-93.173-41.715-93.173-93.173c0-51.458 41.715-93.173 93.173-93.173s93.173 41.715 93.173 93.173z",
"M744.773 511.867c0 51.458-41.715 93.173-93.173 93.173s-93.173-41.715-93.173-93.173c0-51.458 41.715-93.173 93.173-93.173s93.173 41.715 93.173 93.173z",
"M744.773 791.36c0 51.458-41.715 93.173-93.173 93.173s-93.173-41.715-93.173-93.173c0-51.458 41.715-93.173 93.173-93.173s93.173 41.715 93.173 93.173z"
],
"attrs": [],
"width": 745,
"grid": 0,
"tags": ["icon12-grippy"]
},
{
"id": 7,
"paths": [
"M0 0h1024v170.667h-1024v-170.667z",
"M0 426.667h1024v170.667h-1024v-170.667z",
"M0 853.333h1024v170.667h-1024v-170.667z"
],
"attrs": [],
"grid": 0,
"tags": ["icon12-list-view"]
},
{
"id": 3,
"paths": [
"M938.667 170.667h-341.333l-110.32-110.32c-33.2-33.2-98.667-60.347-145.68-60.347h-256c-47.073 0.136-85.197 38.26-85.333 85.32l-0 341.346c0.136-47.073 38.26-85.197 85.32-85.333l853.346-0c47.073 0.136 85.197 38.26 85.333 85.32l0-170.654c-0.136-47.073-38.26-85.197-85.32-85.333z",
"M85.333 426.667h853.333c47.128 0 85.333 38.205 85.333 85.333v426.667c0 47.128-38.205 85.333-85.333 85.333h-853.333c-47.128 0-85.333-38.205-85.333-85.333v-426.667c0-47.128 38.205-85.333 85.333-85.333z"
],
"attrs": [],
"grid": 0,
"tags": ["icon12-folder"]
}
],
"invisible": false,
"colorThemes": [],
"colorThemeIdx": 0
}
],
"preferences": {
"showGlyphs": true,
"showCodes": true,
"showQuickUse": true,
"showQuickUse2": true,
"showSVGs": true,
"fontPref": {
"prefix": "openmct-symbols-",
"metadata": {
"fontFamily": "Open-MCT-Symbols-12px",
"majorVersion": 1,
"minorVersion": 0
},
"metrics": {
"emSize": 1024,
"baseline": 6.25,
"whitespace": 50
},
"embed": false,
"noie8": true,
"ie7": false,
"showMetadata": false,
"includeMetadata": false,
"showMetrics": true
},
"imagePref": {
"prefix": "icon-",
"png": true,
"useClassSelector": true,
"color": 0,
"bgColor": 16777215
},
"historySize": 100,
"gridSize": 16
},
"uid": -1
}
================================================
FILE: src/styles/fonts/Open MCT Symbols 16px.json
================================================
{
"metadata": {
"name": "Open MCT Symbols 16px",
"lastOpened": 0,
"created": 1726681576505
},
"iconSets": [
{
"selection": [
{
"order": 77,
"id": 47,
"name": "icon-alert-rect-v2",
"prevSize": 16,
"code": 59648,
"tempChar": ""
},
{
"order": 76,
"id": 48,
"name": "icon-alert-triangle-v2",
"prevSize": 16,
"code": 59649,
"tempChar": ""
},
{
"order": 20,
"id": 112,
"name": "icon-arrow-up",
"prevSize": 16,
"code": 59650,
"tempChar": ""
},
{
"order": 36,
"id": 94,
"name": "icon-arrow-double-up",
"prevSize": 16,
"code": 59651,
"tempChar": ""
},
{
"order": 143,
"id": 96,
"name": "icon-arrow-tall-up",
"prevSize": 16,
"code": 59652,
"tempChar": ""
},
{
"order": 21,
"id": 111,
"name": "icon-arrow-right",
"prevSize": 16,
"code": 59653,
"tempChar": ""
},
{
"order": 102,
"id": 18,
"name": "icon-arrow-right-equilateral",
"prevSize": 16,
"code": 59654,
"tempChar": ""
},
{
"order": 19,
"id": 113,
"name": "icon-arrow-down",
"prevSize": 16,
"code": 59655,
"tempChar": ""
},
{
"order": 37,
"id": 93,
"name": "icon-arrow-double-down",
"prevSize": 16,
"code": 59656,
"tempChar": ""
},
{
"order": 35,
"id": 95,
"name": "icon-arrow-tall-down",
"prevSize": 16,
"code": 59657,
"tempChar": ""
},
{
"order": 18,
"id": 114,
"name": "icon-arrow-left",
"prevSize": 16,
"code": 59658,
"tempChar": ""
},
{
"order": 17,
"id": 115,
"name": "icon-asterisk",
"prevSize": 16,
"code": 59659,
"tempChar": ""
},
{
"order": 128,
"id": 59,
"name": "icon-bell",
"prevSize": 16,
"code": 59660,
"tempChar": ""
},
{
"order": 123,
"id": 103,
"name": "icon-box-round-corners",
"prevSize": 16,
"code": 59661,
"tempChar": ""
},
{
"order": 129,
"id": 58,
"name": "icon-box-with-arrow-cursor",
"prevSize": 16,
"code": 59662,
"tempChar": ""
},
{
"order": 142,
"id": 120,
"name": "icon-check",
"prevSize": 16,
"code": 59663,
"tempChar": ""
},
{
"order": 25,
"id": 107,
"name": "icon-connectivity",
"prevSize": 16,
"code": 59664,
"tempChar": ""
},
{
"order": 44,
"id": 84,
"name": "icon-database-in-brackets",
"prevSize": 16,
"code": 59665,
"tempChar": ""
},
{
"order": 75,
"id": 49,
"name": "icon-eye-open",
"prevSize": 16,
"code": 59666,
"tempChar": ""
},
{
"order": 5,
"id": 129,
"name": "icon-gear",
"prevSize": 16,
"code": 59667,
"tempChar": ""
},
{
"order": 66,
"id": 60,
"name": "icon-hourglass",
"prevSize": 16,
"code": 59668,
"tempChar": ""
},
{
"order": 65,
"id": 61,
"name": "icon-info",
"prevSize": 16,
"code": 59669,
"tempChar": ""
},
{
"order": 53,
"id": 75,
"name": "icon-link",
"prevSize": 16,
"code": 59670,
"tempChar": ""
},
{
"order": 42,
"id": 86,
"name": "icon-lock",
"prevSize": 16,
"code": 59671,
"tempChar": ""
},
{
"order": 49,
"id": 79,
"name": "icon-minus",
"prevSize": 16,
"code": 59672,
"tempChar": ""
},
{
"order": 22,
"id": 110,
"name": "icon-people",
"prevSize": 16,
"code": 59673,
"tempChar": ""
},
{
"order": 46,
"id": 82,
"name": "icon-person",
"prevSize": 16,
"code": 59674,
"tempChar": ""
},
{
"order": 38,
"id": 92,
"name": "icon-plus",
"prevSize": 16,
"code": 59675,
"tempChar": ""
},
{
"order": 164,
"id": 137,
"name": "icon-plus-in-rect",
"prevSize": 16,
"code": 59676,
"tempChar": ""
},
{
"order": 6,
"id": 128,
"name": "icon-trash",
"prevSize": 16,
"code": 59677,
"tempChar": ""
},
{
"order": 140,
"id": 122,
"name": "icon-x-heavy",
"prevSize": 16,
"code": 59678,
"tempChar": ""
},
{
"order": 78,
"id": 46,
"name": "icon-brackets",
"prevSize": 16,
"code": 59679,
"tempChar": ""
},
{
"order": 93,
"id": 27,
"name": "icon-crosshair",
"prevSize": 16,
"code": 59680,
"tempChar": ""
},
{
"order": 91,
"id": 31,
"name": "icon-grippy",
"prevSize": 16,
"code": 59681,
"tempChar": ""
},
{
"order": 121,
"id": 118,
"name": "icon-grid",
"prevSize": 16,
"code": 59682,
"tempChar": ""
},
{
"order": 154,
"id": 136,
"name": "icon-grippy-ew",
"prevSize": 16,
"code": 59683,
"tempChar": ""
},
{
"order": 152,
"id": 135,
"name": "icon-columns",
"prevSize": 16,
"code": 59684,
"tempChar": ""
},
{
"order": 153,
"id": 134,
"name": "icon-rows",
"prevSize": 16,
"code": 59685,
"tempChar": ""
},
{
"order": 162,
"id": 140,
"name": "icon-filter",
"prevSize": 16,
"code": 59686,
"tempChar": ""
},
{
"order": 161,
"id": 139,
"name": "icon-filter-outline",
"prevSize": 16,
"code": 59687,
"tempChar": ""
},
{
"order": 155,
"id": 142,
"name": "icon-suitcase",
"prevSize": 16,
"code": 59688,
"tempChar": ""
},
{
"order": 169,
"id": 145,
"name": "icon-cursor-locked",
"prevSize": 16,
"code": 59689,
"tempChar": ""
},
{
"order": 176,
"id": 150,
"name": "icon-flag",
"prevSize": 16,
"code": 59690,
"tempChar": ""
},
{
"order": 177,
"id": 152,
"name": "icon-eye-disabled",
"prevSize": 16,
"code": 59691,
"tempChar": ""
},
{
"order": 179,
"id": 153,
"name": "icon-notebook-page",
"prevSize": 16,
"code": 59692,
"tempChar": ""
},
{
"order": 186,
"id": 160,
"name": "icon-unlocked",
"prevSize": 16,
"code": 59693,
"tempChar": ""
},
{
"order": 197,
"id": 169,
"name": "icon-circle",
"prevSize": 16,
"code": 59694,
"tempChar": ""
},
{
"order": 201,
"id": 173,
"name": "icon-draft",
"prevSize": 16,
"code": 59695,
"tempChar": ""
},
{
"order": 212,
"id": 183,
"name": "icon-circle-slash",
"prevSize": 16,
"code": 59696,
"tempChar": ""
},
{
"order": 213,
"id": 182,
"name": "icon-question-mark",
"prevSize": 16,
"code": 59697,
"tempChar": ""
},
{
"order": 206,
"id": 179,
"name": "icon-status-poll-check",
"prevSize": 16,
"code": 59698,
"tempChar": ""
},
{
"order": 207,
"id": 178,
"name": "icon-status-poll-caution",
"prevSize": 16,
"code": 59699,
"tempChar": ""
},
{
"order": 210,
"id": 180,
"name": "icon-status-poll-circle-slash",
"prevSize": 16,
"code": 59700,
"tempChar": ""
},
{
"order": 211,
"id": 181,
"name": "icon-status-poll-question-mark",
"prevSize": 16,
"code": 59701,
"tempChar": ""
},
{
"order": 209,
"id": 176,
"name": "icon-status-poll-edit",
"prevSize": 16,
"code": 59702,
"tempChar": ""
},
{
"order": 215,
"id": 185,
"name": "icon-stale",
"prevSize": 16,
"code": 59703,
"tempChar": ""
},
{
"order": 27,
"id": 105,
"name": "icon-arrows-right-left",
"prevSize": 16,
"code": 59904,
"tempChar": ""
},
{
"order": 26,
"id": 106,
"name": "icon-arrows-up-down",
"prevSize": 16,
"code": 59905,
"tempChar": ""
},
{
"order": 68,
"id": 56,
"name": "icon-bullet",
"prevSize": 16,
"code": 59906,
"tempChar": ""
},
{
"order": 150,
"id": 133,
"prevSize": 16,
"code": 59907,
"name": "icon-calendar",
"tempChar": ""
},
{
"order": 45,
"id": 83,
"name": "icon-chain-links",
"prevSize": 16,
"code": 59908,
"tempChar": ""
},
{
"order": 73,
"id": 51,
"name": "icon-download",
"prevSize": 16,
"code": 59909,
"tempChar": ""
},
{
"order": 39,
"id": 91,
"name": "icon-duplicate",
"prevSize": 16,
"code": 59910,
"tempChar": ""
},
{
"order": 50,
"id": 78,
"name": "icon-folder-new",
"prevSize": 16,
"code": 59911,
"tempChar": ""
},
{
"order": 138,
"id": 124,
"name": "icon-fullscreen-collapse",
"prevSize": 16,
"code": 59912,
"tempChar": ""
},
{
"order": 139,
"id": 123,
"name": "icon-fullscreen-expand",
"prevSize": 16,
"code": 59913,
"tempChar": ""
},
{
"order": 122,
"id": 104,
"name": "icon-layers",
"prevSize": 16,
"code": 59914,
"tempChar": ""
},
{
"order": 151,
"id": 102,
"name": "icon-line-horz",
"prevSize": 16,
"code": 59915,
"tempChar": ""
},
{
"order": 100,
"id": 20,
"name": "icon-magnify",
"prevSize": 16,
"code": 59916,
"tempChar": ""
},
{
"order": 99,
"id": 21,
"name": "icon-magnify-in",
"prevSize": 16,
"code": 59917,
"tempChar": ""
},
{
"order": 101,
"id": 19,
"name": "icon-magnify-out-v2",
"prevSize": 16,
"code": 59918,
"tempChar": ""
},
{
"order": 103,
"id": 17,
"name": "icon-menu",
"prevSize": 16,
"code": 59919,
"tempChar": ""
},
{
"order": 124,
"id": 89,
"name": "icon-move",
"prevSize": 16,
"code": 59920,
"tempChar": ""
},
{
"order": 7,
"id": 127,
"name": "icon-new-window",
"prevSize": 16,
"code": 59921,
"tempChar": ""
},
{
"order": 63,
"id": 63,
"name": "icon-paint-bucket-v2",
"prevSize": 16,
"code": 59922,
"tempChar": ""
},
{
"order": 15,
"id": 117,
"name": "icon-pencil",
"prevSize": 16,
"code": 59923,
"tempChar": ""
},
{
"order": 54,
"id": 72,
"name": "icon-pencil-edit-in-place",
"prevSize": 16,
"code": 59924,
"tempChar": ""
},
{
"order": 40,
"id": 90,
"name": "icon-play",
"prevSize": 16,
"code": 59925,
"tempChar": ""
},
{
"order": 125,
"id": 88,
"name": "icon-pause",
"prevSize": 16,
"code": 59926,
"tempChar": ""
},
{
"order": 119,
"id": 13,
"name": "icon-plot-resource",
"prevSize": 16,
"code": 59927,
"tempChar": ""
},
{
"order": 48,
"id": 80,
"name": "icon-pointer-left",
"prevSize": 16,
"code": 59928,
"tempChar": ""
},
{
"order": 47,
"id": 81,
"name": "icon-pointer-right",
"prevSize": 16,
"code": 59929,
"tempChar": ""
},
{
"order": 85,
"id": 37,
"name": "icon-refresh",
"prevSize": 16,
"code": 59930,
"tempChar": ""
},
{
"order": 55,
"id": 71,
"name": "icon-save",
"prevSize": 16,
"code": 59931,
"tempChar": ""
},
{
"order": 56,
"id": 70,
"name": "icon-save-as",
"prevSize": 16,
"code": 59932,
"tempChar": ""
},
{
"order": 58,
"id": 68,
"name": "icon-sine",
"prevSize": 16,
"code": 59933,
"tempChar": ""
},
{
"order": 113,
"id": 5,
"name": "icon-font",
"prevSize": 16,
"code": 59934,
"tempChar": ""
},
{
"order": 41,
"id": 87,
"name": "icon-thumbs-strip",
"prevSize": 16,
"code": 59935,
"tempChar": ""
},
{
"order": 146,
"id": 99,
"name": "icon-two-parts-both",
"prevSize": 16,
"code": 59936,
"tempChar": ""
},
{
"order": 145,
"id": 98,
"name": "icon-two-parts-one-only",
"prevSize": 16,
"code": 59937,
"tempChar": ""
},
{
"order": 82,
"id": 40,
"name": "icon-resync",
"prevSize": 16,
"code": 59938,
"tempChar": ""
},
{
"order": 86,
"id": 36,
"name": "icon-reset",
"prevSize": 16,
"code": 59939,
"tempChar": ""
},
{
"order": 61,
"id": 65,
"name": "icon-x-in-circle",
"prevSize": 16,
"code": 59940,
"tempChar": ""
},
{
"order": 84,
"id": 38,
"name": "icon-brightness",
"prevSize": 16,
"code": 59941,
"tempChar": ""
},
{
"order": 83,
"id": 39,
"name": "icon-contrast",
"prevSize": 16,
"code": 59942,
"tempChar": ""
},
{
"order": 87,
"id": 35,
"name": "icon-expand",
"prevSize": 16,
"code": 59943,
"tempChar": ""
},
{
"order": 89,
"id": 33,
"name": "icon-list-view",
"prevSize": 16,
"code": 59944,
"tempChar": ""
},
{
"order": 133,
"id": 28,
"name": "icon-grid-snap-to",
"prevSize": 16,
"code": 59945,
"tempChar": ""
},
{
"order": 132,
"id": 29,
"name": "icon-grid-snap-no",
"prevSize": 16,
"code": 59946,
"tempChar": ""
},
{
"order": 94,
"id": 26,
"name": "icon-frame-show",
"prevSize": 16,
"code": 59947,
"tempChar": ""
},
{
"order": 95,
"id": 25,
"name": "icon-frame-hide",
"prevSize": 16,
"code": 59948,
"tempChar": ""
},
{
"order": 97,
"id": 23,
"name": "icon-import",
"prevSize": 16,
"code": 59949,
"tempChar": ""
},
{
"order": 96,
"id": 24,
"name": "icon-export",
"prevSize": 16,
"code": 59950,
"tempChar": ""
},
{
"order": 194,
"id": 4,
"name": "icon-font-size",
"prevSize": 16,
"code": 59951,
"tempChar": ""
},
{
"order": 163,
"id": 141,
"name": "icon-clear-data",
"prevSize": 16,
"code": 59952,
"tempChar": ""
},
{
"order": 173,
"id": 149,
"name": "icon-history",
"prevSize": 16,
"code": 59953,
"tempChar": ""
},
{
"order": 181,
"id": 158,
"name": "icon-arrow-up-to-parent",
"prevSize": 16,
"code": 59954,
"tempChar": ""
},
{
"order": 184,
"id": 159,
"name": "icon-crosshair-in-circle",
"prevSize": 16,
"code": 59955,
"tempChar": ""
},
{
"order": 185,
"id": 161,
"name": "icon-target",
"prevSize": 16,
"code": 59956,
"tempChar": ""
},
{
"order": 187,
"id": 163,
"name": "icon-items-collapse",
"prevSize": 16,
"code": 59957,
"tempChar": ""
},
{
"order": 188,
"id": 162,
"name": "icon-items-expand",
"prevSize": 16,
"code": 59958,
"tempChar": ""
},
{
"order": 190,
"id": 164,
"name": "icon-3-dots",
"prevSize": 16,
"code": 59959,
"tempChar": ""
},
{
"order": 193,
"id": 165,
"name": "icon-grid-on",
"prevSize": 16,
"code": 59960,
"tempChar": ""
},
{
"order": 192,
"id": 166,
"name": "icon-grid-off",
"prevSize": 16,
"code": 59961,
"tempChar": ""
},
{
"order": 191,
"id": 167,
"name": "icon-camera",
"prevSize": 16,
"code": 59962,
"tempChar": ""
},
{
"order": 196,
"id": 168,
"name": "icon-folders-collapse",
"prevSize": 16,
"code": 59963,
"tempChar": ""
},
{
"order": 216,
"id": 187,
"name": "icon-multiline",
"prevSize": 16,
"code": 59964,
"tempChar": ""
},
{
"order": 217,
"id": 186,
"name": "icon-singleline",
"prevSize": 16,
"code": 59965,
"tempChar": ""
},
{
"order": 144,
"id": 97,
"name": "icon-activity",
"prevSize": 16,
"code": 60160,
"tempChar": ""
},
{
"order": 104,
"id": 16,
"name": "icon-activity-mode",
"prevSize": 16,
"code": 60161,
"tempChar": ""
},
{
"order": 137,
"id": 125,
"name": "icon-autoflow-tabular",
"prevSize": 16,
"code": 60162,
"tempChar": ""
},
{
"order": 115,
"id": 3,
"name": "icon-clock",
"prevSize": 16,
"code": 60163,
"tempChar": ""
},
{
"order": 2,
"id": 132,
"name": "icon-database",
"prevSize": 16,
"code": 60164,
"tempChar": ""
},
{
"order": 3,
"id": 131,
"name": "icon-database-query",
"prevSize": 16,
"code": 60165,
"tempChar": ""
},
{
"order": 67,
"id": 57,
"name": "icon-dataset",
"prevSize": 16,
"code": 60166,
"tempChar": ""
},
{
"order": 59,
"id": 67,
"name": "icon-datatable",
"prevSize": 16,
"code": 60167,
"tempChar": ""
},
{
"order": 136,
"id": 126,
"name": "icon-dictionary",
"prevSize": 16,
"code": 60168,
"tempChar": ""
},
{
"order": 51,
"id": 77,
"name": "icon-folder",
"prevSize": 16,
"code": 60169,
"tempChar": ""
},
{
"order": 147,
"id": 100,
"name": "icon-image",
"prevSize": 16,
"code": 60170,
"tempChar": ""
},
{
"order": 4,
"id": 130,
"name": "icon-layout",
"prevSize": 16,
"code": 60171,
"tempChar": ""
},
{
"order": 24,
"id": 108,
"name": "icon-object",
"prevSize": 16,
"code": 60172,
"tempChar": ""
},
{
"order": 52,
"id": 76,
"name": "icon-object-unknown",
"prevSize": 16,
"code": 60173,
"tempChar": ""
},
{
"order": 105,
"id": 15,
"name": "icon-packet",
"prevSize": 16,
"code": 60174,
"tempChar": ""
},
{
"order": 126,
"id": 74,
"name": "icon-page",
"prevSize": 16,
"code": 60175,
"tempChar": ""
},
{
"order": 130,
"id": 44,
"name": "icon-plot-overlay",
"prevSize": 16,
"code": 60176,
"tempChar": ""
},
{
"order": 80,
"id": 42,
"name": "icon-plot-stacked",
"prevSize": 16,
"code": 60177,
"tempChar": ""
},
{
"order": 134,
"id": 14,
"name": "icon-session",
"prevSize": 16,
"code": 60178,
"tempChar": ""
},
{
"order": 109,
"id": 9,
"name": "icon-tabular",
"prevSize": 16,
"code": 60179,
"tempChar": ""
},
{
"order": 107,
"id": 11,
"name": "icon-tabular-lad",
"prevSize": 16,
"code": 60180,
"tempChar": ""
},
{
"order": 106,
"id": 12,
"name": "icon-tabular-lad-set",
"prevSize": 16,
"code": 60181,
"tempChar": ""
},
{
"order": 70,
"id": 54,
"name": "icon-tabular-realtime",
"prevSize": 16,
"code": 60182,
"tempChar": ""
},
{
"order": 60,
"id": 66,
"name": "icon-tabular-scrolling",
"prevSize": 16,
"code": 60183,
"tempChar": ""
},
{
"order": 131,
"id": 43,
"name": "icon-telemetry",
"prevSize": 16,
"code": 60184,
"tempChar": ""
},
{
"order": 202,
"id": 10,
"name": "icon-timeline",
"prevSize": 16,
"code": 60185,
"tempChar": ""
},
{
"order": 81,
"id": 41,
"name": "icon-timer",
"prevSize": 16,
"code": 60186,
"tempChar": ""
},
{
"order": 69,
"id": 55,
"name": "icon-topic",
"prevSize": 16,
"code": 60187,
"tempChar": ""
},
{
"order": 79,
"id": 45,
"name": "icon-box-with-dashed-lines-v2",
"prevSize": 16,
"code": 60188,
"tempChar": ""
},
{
"order": 90,
"id": 32,
"name": "icon-summary-widget",
"prevSize": 16,
"code": 60189,
"tempChar": ""
},
{
"order": 92,
"id": 30,
"name": "icon-notebook",
"prevSize": 16,
"code": 60190,
"tempChar": ""
},
{
"order": 168,
"id": 0,
"name": "icon-tabs-view",
"prevSize": 16,
"code": 60191,
"tempChar": ""
},
{
"order": 117,
"id": 1,
"name": "icon-flexible-layout",
"prevSize": 16,
"code": 60192,
"tempChar": ""
},
{
"order": 166,
"id": 144,
"name": "icon-generator-sine",
"prevSize": 16,
"code": 60193,
"tempChar": ""
},
{
"order": 167,
"id": 143,
"name": "icon-generator-event",
"prevSize": 16,
"code": 60194,
"tempChar": ""
},
{
"order": 165,
"id": 138,
"name": "icon-gauge-v2",
"prevSize": 16,
"code": 60195,
"tempChar": ""
},
{
"order": 170,
"id": 148,
"name": "icon-spectra",
"prevSize": 16,
"code": 60196,
"tempChar": ""
},
{
"order": 171,
"id": 147,
"name": "icon-telemetry-spectra",
"prevSize": 16,
"code": 60197,
"tempChar": ""
},
{
"order": 172,
"id": 146,
"name": "icon-pushbutton",
"prevSize": 16,
"code": 60198,
"tempChar": ""
},
{
"order": 174,
"id": 151,
"name": "icon-conditional",
"prevSize": 16,
"code": 60199,
"tempChar": ""
},
{
"order": 178,
"id": 154,
"name": "icon-condition-widget",
"prevSize": 16,
"code": 60200,
"tempChar": ""
},
{
"order": 180,
"id": 155,
"name": "icon-alphanumeric",
"prevSize": 16,
"code": 60201,
"tempChar": ""
},
{
"order": 183,
"id": 156,
"name": "icon-image-telemetry",
"prevSize": 16,
"code": 60202,
"tempChar": ""
},
{
"order": 198,
"id": 170,
"name": "icon-telemetry-aggregate",
"prevSize": 16,
"code": 60203,
"tempChar": ""
},
{
"order": 199,
"id": 172,
"name": "icon-bar-graph",
"prevSize": 16,
"code": 60204,
"tempChar": ""
},
{
"order": 200,
"id": 171,
"name": "icon-map",
"prevSize": 16,
"code": 60205,
"tempChar": ""
},
{
"order": 203,
"id": 174,
"name": "icon-plan",
"prevSize": 16,
"code": 60206,
"tempChar": ""
},
{
"order": 204,
"id": 175,
"name": "icon-timelist",
"prevSize": 16,
"code": 60207,
"tempChar": ""
},
{
"order": 205,
"id": 176,
"name": "icon-plot-scatter",
"prevSize": 16,
"code": 60208,
"tempChar": ""
},
{
"order": 218,
"id": 184,
"name": "icon-notebook-restricted",
"prevSize": 16,
"code": 60209,
"tempChar": ""
}
],
"id": 0,
"metadata": {
"name": "Open MCT Symbols 16px",
"importSize": {
"width": 576,
"height": 512
},
"designer": "Charles Hacskaylo"
},
"height": 1024,
"prevSize": 16,
"icons": [
{
"id": 47,
"paths": [
"M896 0h-768c-70.6 0.2-127.8 57.4-128 128v768c0.2 70.6 57.4 127.8 128 128h768c70.6-0.2 127.8-57.4 128-128v-768c-0.2-70.6-57.4-127.8-128-128zM576 896h-128v-128h128v128zM597.8 512l-37.8 192h-96l-37.8-192v-384h171.8v384z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-alert-rect-v2"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 48,
"paths": [
"M998.2 848.8l-422.6-739.6c-35-61.2-92-61.2-127 0l-422.8 739.6c-35 61.2-6 111.2 64.4 111.2h843.4c70.6 0 99.6-50 64.6-111.2zM576 896h-128v-128h128v128zM597.8 512l-37.8 192h-96l-37.8-192v-256h171.8v256z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-alert-triangle-v2"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 112,
"paths": [
"M512 256l-512 512h1024z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-arrow-up"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 94,
"paths": [
"M510 510l512 512h-1024z",
"M510-2l512 512h-1024z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-arrow-double-up"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 96,
"paths": [
"M512 0l512 1024h-1024z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-arrow-tall-up"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 111,
"paths": [
"M768 512l-512-512v1024z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-arrow-right"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 18,
"paths": [
"M962 512l-896 512v-1024z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-arrow-right-equilateral"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 113,
"paths": [
"M512 768l512-512h-1024z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-arrow-down"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 93,
"paths": [
"M510 510l-512-512h1024z",
"M510 1022l-512-512h1024z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-arrow-double-down"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 95,
"paths": [
"M512 1024l-512-1024h1024z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-arrow-tall-down"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 114,
"paths": [
"M256 512l512 512v-1024z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-arrow-left"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 115,
"paths": [
"M1004.166 340.458l-97.522-168.916-330.534 229.414 33.414-400.956h-195.048l33.414 400.956-330.534-229.414-97.522 168.916 363.944 171.542-363.944 171.542 97.522 168.916 330.534-229.414-33.414 400.956h195.048l-33.414-400.956 330.534 229.414 97.522-168.916-363.944-171.542z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-asterisk"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 59,
"paths": [
"M512 1024c106 0 192-86 192-192h-384c0 106 86 192 192 192z",
"M896 448v-64c0-212-172-384-384-384s-384 172-384 384v64c0 70.6-57.4 128-128 128v128h1024v-128c-70.6 0-128-57.4-128-128z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-bell"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 103,
"paths": [
"M1024 832c0 105.6-86.4 192-192 192h-640c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h640c105.6 0 192 86.4 192 192v640z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-box-round-corners"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 58,
"paths": [
"M894-2h-768c-70.4 0-128 57.6-128 128v768c0 70.4 57.6 128 128 128h400c-2.2-3.8-4-7.6-5.8-11.4l-255.2-576.8c-21.4-48.4-10.8-105 26.6-142.4 24.4-24.4 57.2-37.4 90.4-37.4 17.4 0 35.2 3.6 51.8 11l576.6 255.4c4 1.8 7.8 3.8 11.4 5.8v-400.2c0.2-70.4-57.4-128-127.8-128z",
"M958.6 637.4l-576.6-255.4 255.4 576.6 64.6-128.6 192 192 128-128-192-192z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-box-with-arrow-cursor"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 120,
"paths": [
"M1024 0l-640 640-384-384v384l384 384 640-640z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-check"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 107,
"paths": [
"M704 576c0 70.4-57.6 128-128 128h-128c-70.4 0-128-57.6-128-128v-128c0-70.4 57.6-128 128-128h128c70.4 0 128 57.6 128 128v128z",
"M1024 512l-192-320v640z",
"M0 512l192-320v640z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-connectivity"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 84,
"paths": [
"M768 352c0 53.019-114.615 96-256 96s-256-42.981-256-96c0-53.019 114.615-96 256-96s256 42.981 256 96z",
"M768 672v-256c0 53-114.6 96-256 96s-256-43-256-96v256c0 53 114.6 96 256 96s256-43 256-96z",
"M832 0h-128v192h127.6c0.2 0 0.2 0.2 0.4 0.4v639.4c0 0.2-0.2 0.2-0.4 0.4h-127.6v192h128c105.6 0 192-86.4 192-192v-640.2c0-105.6-86.4-192-192-192z",
"M192 831.6v-639.4c0-0.2 0.2-0.2 0.4-0.4h127.6v-191.8h-128c-105.6 0-192 86.4-192 192v640c0 105.6 86.4 192 192 192h128v-192h-127.6c-0.2 0-0.4-0.2-0.4-0.4z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-database-in-brackets"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 49,
"paths": [
"M512 116.4c-245.8 0-452.2 168-510.8 395.6 58.6 227.4 265 395.6 510.8 395.6s452.2-168 510.8-395.6c-58.6-227.4-265-395.6-510.8-395.6zM829.2 588.4c-22.6 34.4-50.6 64.8-83 90.4-32.8 25.8-69 45.6-108 59.4-40.4 14.2-82.8 21.4-126 21.4s-85.8-7.2-126-21.4c-39-13.8-75.4-33.8-108-59.4-32.4-25.6-60.4-55.8-83-90.4-15.8-24-28.8-49.6-38.6-76.4 10-26.8 23-52.4 38.6-76.4 22.6-34.4 50.6-64.8 83-90.4 32.8-25.8 69-45.6 108-59.4 40.4-14.2 82.8-21.4 126-21.4s85.8 7.2 126 21.4c39 13.8 75.4 33.8 108 59.4 32.4 25.6 60.4 55.8 83 90.4 15.8 24 28.8 49.6 38.6 76.4-9.8 26.8-22.8 52.4-38.6 76.4z",
"M704 512c0 106.039-85.961 192-192 192s-192-85.961-192-192c0-106.039 85.961-192 192-192s192 85.961 192 192z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-eye-open-v2"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 129,
"paths": [
"M1024 576v-128l-140.976-35.244c-8.784-32.922-21.818-64.106-38.504-92.918l74.774-124.622-90.51-90.51-124.622 74.774c-28.812-16.686-59.996-29.72-92.918-38.504l-35.244-140.976h-128l-35.244 140.976c-32.922 8.784-64.106 21.818-92.918 38.504l-124.622-74.774-90.51 90.51 74.774 124.622c-16.686 28.812-29.72 59.996-38.504 92.918l-140.976 35.244v128l140.976 35.244c8.784 32.922 21.818 64.106 38.504 92.918l-74.774 124.622 90.51 90.51 124.622-74.774c28.812 16.686 59.996 29.72 92.918 38.504l35.244 140.976h128l35.244-140.976c32.922-8.784 64.106-21.818 92.918-38.504l124.622 74.774 90.51-90.51-74.774-124.622c16.686-28.812 29.72-59.996 38.504-92.918l140.976-35.244zM704 512c0 106.038-85.962 192-192 192s-192-85.962-192-192 85.962-192 192-192 192 85.962 192 192z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-gear"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 60,
"paths": [
"M1024 0h-1024c0 282.8 229.2 512 512 512s512-229.2 512-512zM512 384c-102.6 0-199-40-271.6-112.4-41.2-41.2-72-90.2-90.8-143.6h724.6c-18.8 53.4-49.6 102.4-90.8 143.6-72.4 72.4-168.8 112.4-271.4 112.4z",
"M512 512c-282.8 0-512 229.2-512 512h1024c0-282.8-229.2-512-512-512z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-hourglass"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 61,
"paths": [
"M512 0c-282.8 0-512 229.2-512 512s229.2 512 512 512 512-229.2 512-512-229.2-512-512-512zM512 128c70.6 0 128 57.4 128 128s-57.4 128-128 128c-70.6 0-128-57.4-128-128s57.4-128 128-128zM704 832h-384v-128h64v-256h256v256h64v128z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-info"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 75,
"paths": [
"M1024 512l-512-512v307.2l-512 204.8v256h512v256z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-link-v2"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 86,
"paths": [
"M702 384h-62v-128c0-141.385-114.615-256-256-256s-256 114.615-256 256v0 128h-64c-35.301 0.113-63.887 28.699-64 63.989v512.011c0.113 35.301 28.699 63.887 63.989 64h638.011c35.301-0.113 63.887-28.699 64-63.989v-512.011c-0.113-35.301-28.699-63.887-63.989-64h-0.011zM256 384v-128c0-70.692 57.308-128 128-128s128 57.308 128 128v0 128z"
],
"attrs": [
{}
],
"grid": 16,
"tags": [
"icon-lock"
],
"isMulticolor": false,
"isMulticolor2": false,
"colorPermutations": {
"12552552551": [
{}
]
},
"width": 768
},
{
"id": 79,
"paths": [
"M960 640c35.2 0 64-28.8 64-64v-128c0-35.2-28.8-64-64-64h-896c-35.2 0-64 28.8-64 64v128c0 35.2 28.8 64 64 64h896z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-minus"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 110,
"paths": [
"M704 320h64c70.4 0 128-57.6 128-128v-64c0-70.4-57.6-128-128-128h-64c-70.4 0-128 57.6-128 128v64c0 70.4 57.6 128 128 128z",
"M256 320h64c70.4 0 128-57.6 128-128v-64c0-70.4-57.6-128-128-128h-64c-70.4 0-128 57.6-128 128v64c0 70.4 57.6 128 128 128z",
"M832 384h-192c-34.908 0-67.716 9.448-96 25.904 57.278 33.324 96 95.404 96 166.096v448h384v-448c0-105.6-86.4-192-192-192z",
"M384 384h-192c-105.6 0-192 86.4-192 192v448h576v-448c0-105.6-86.4-192-192-192z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-people"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 82,
"paths": [
"M768 256c0 105.6-86.4 192-192 192h-128c-105.6 0-192-86.4-192-192v-64c0-105.6 86.4-192 192-192h128c105.6 0 192 86.4 192 192v64z",
"M64 1024v-192c0-140.8 115.2-256 256-256h384c140.8 0 256 115.2 256 256v192z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-person"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 92,
"paths": [
"M960 384h-330v-320c0-35.2-28.8-64-64-64h-108c-35.2 0-64 28.8-64 64v320h-330c-35.2 0-64 28.8-64 64v128c0 35.2 28.8 64 64 64h330v320c0 35.2 28.8 64 64 64h108c35.2 0 64-28.8 64-64v-320h330c35.2 0 64-28.8 64-64v-128c0-35.2-28.8-64-64-64z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-plus"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 137,
"paths": [
"M830 0h-636c-106.6 0-194 87.2-194 194v636c0 106.8 87.4 194 194 194h636c106.6 0 194-87.2 194-194v-636c0-106.8-87.4-194-194-194zM896 608c0 17.673-14.327 32-32 32v0h-224v224c0 17.673-14.327 32-32 32v0h-192c-17.673 0-32-14.327-32-32v0-224h-224c-17.673 0-32-14.327-32-32v0-192c0-17.673 14.327-32 32-32v0h224v-224c0-17.673 14.327-32 32-32v0h192c17.673 0 32 14.327 32 32v0 224h224c17.673 0 32 14.327 32 32v0z"
],
"attrs": [
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-plus-in-rect"
],
"colorPermutations": {
"12552552551": [
{}
]
}
},
{
"id": 128,
"paths": [
"M832 128h-192.36v-64c0-35.2-28.8-64-64-64h-128c-35.2 0-64 28.8-64 64v64h-191.64c-105.6 0-192 72-192 160s0 160 0 160h64v384c0 105.6 86.4 192 192 192h512c105.6 0 192-86.4 192-192v-384h64c0 0 0-72 0-160s-86.4-160-192-160zM320 832h-128v-384h128v384zM576 832h-128v-384h128v384zM832 832h-128v-384h128v384z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-trash"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 122,
"paths": [
"M704 512l301.332 301.332c24.89 24.89 24.89 65.62 0 90.51l-101.49 101.49c-24.89 24.89-65.62 24.89-90.51 0l-301.332-301.332c0 0-301.332 301.332-301.332 301.332-24.89 24.89-65.62 24.89-90.51 0l-101.49-101.49c-24.89-24.89-24.89-65.62 0-90.51l301.332-301.332c0 0-301.332-301.332-301.332-301.332-24.89-24.89-24.89-65.62 0-90.51l101.49-101.49c24.89-24.89 65.62-24.89 90.51 0l301.332 301.332c0 0 301.332-301.332 301.332-301.332 24.89-24.89 65.62-24.89 90.51 0l101.49 101.49c24.89 24.89 24.89 65.62 0 90.51 0 0-301.332 301.332-301.332 301.332z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-x-heavy"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 46,
"paths": [
"M832 0h-192v192h191.66l0.34 0.34v639.32l-0.34 0.34h-191.66v192h192c105.6 0 192-86.4 192-192v-640c0-105.6-86.4-192-192-192z",
"M384 832h-191.66l-0.34-0.34v-639.32l0.34-0.34h191.66v-192h-192c-105.6 0-192 86.4-192 192v640c0 105.6 86.4 192 192 192h192v-192z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-brackets"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 27,
"paths": [
"M574-2h-128v320h128v-320z",
"M1022 446h-320v128h320v-128z",
"M574 702h-128v320h128v-320z",
"M318 446h-320v128h320v-128z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-crosshair"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 31,
"paths": [
"M365.4 182.8c0 40.427-32.773 73.2-73.2 73.2s-73.2-32.773-73.2-73.2c0-40.427 32.773-73.2 73.2-73.2s73.2 32.773 73.2 73.2z",
"M365.4 402.2c0 40.427-32.773 73.2-73.2 73.2s-73.2-32.773-73.2-73.2c0-40.427 32.773-73.2 73.2-73.2s73.2 32.773 73.2 73.2z",
"M365.4 621.8c0 40.427-32.773 73.2-73.2 73.2s-73.2-32.773-73.2-73.2c0-40.427 32.773-73.2 73.2-73.2s73.2 32.773 73.2 73.2z",
"M365.4 841.2c0 40.427-32.773 73.2-73.2 73.2s-73.2-32.773-73.2-73.2c0-40.427 32.773-73.2 73.2-73.2s73.2 32.773 73.2 73.2z",
"M584.8 73.2c0 40.427-32.773 73.2-73.2 73.2s-73.2-32.773-73.2-73.2c0-40.427 32.773-73.2 73.2-73.2s73.2 32.773 73.2 73.2z",
"M584.8 292.6c0 40.427-32.773 73.2-73.2 73.2s-73.2-32.773-73.2-73.2c0-40.427 32.773-73.2 73.2-73.2s73.2 32.773 73.2 73.2z",
"M584.8 512c0 40.427-32.773 73.2-73.2 73.2s-73.2-32.773-73.2-73.2c0-40.427 32.773-73.2 73.2-73.2s73.2 32.773 73.2 73.2z",
"M584.8 731.4c0 40.427-32.773 73.2-73.2 73.2s-73.2-32.773-73.2-73.2c0-40.427 32.773-73.2 73.2-73.2s73.2 32.773 73.2 73.2z",
"M584.8 950.8c0 40.427-32.773 73.2-73.2 73.2s-73.2-32.773-73.2-73.2c0-40.427 32.773-73.2 73.2-73.2s73.2 32.773 73.2 73.2z",
"M804.2 182.8c0 40.427-32.773 73.2-73.2 73.2s-73.2-32.773-73.2-73.2c0-40.427 32.773-73.2 73.2-73.2s73.2 32.773 73.2 73.2z",
"M804.2 402.2c0 40.427-32.773 73.2-73.2 73.2s-73.2-32.773-73.2-73.2c0-40.427 32.773-73.2 73.2-73.2s73.2 32.773 73.2 73.2z",
"M804.2 621.8c0 40.427-32.773 73.2-73.2 73.2s-73.2-32.773-73.2-73.2c0-40.427 32.773-73.2 73.2-73.2s73.2 32.773 73.2 73.2z",
"M804.2 841.2c0 40.427-32.773 73.2-73.2 73.2s-73.2-32.773-73.2-73.2c0-40.427 32.773-73.2 73.2-73.2s73.2 32.773 73.2 73.2z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-grippy-v2"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 118,
"paths": [
"M0 576v256c0 105.6 86.4 192 192 192h256v-448h-448z",
"M448 0h-256c-105.6 0-192 86.4-192 192v256h448v-448z",
"M832 0h-256v448h448v-256c0-105.6-86.4-192-192-192z",
"M576 1024h256c105.6 0 192-86.4 192-192v-256h-448v448z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-grid-v2"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 136,
"paths": [
"M704 0h128v1024h-128v-1024z",
"M448 0h128v1024h-128v-1024z",
"M192 0h128v1024h-128v-1024z"
],
"attrs": [
{},
{},
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-grippy-ew"
],
"colorPermutations": {
"12552552551": [
{},
{},
{}
]
}
},
{
"id": 135,
"paths": [
"M0 0h256v1024h-256v-1024z",
"M384 0h256v1024h-256v-1024z",
"M768 0h256v1024h-256v-1024z"
],
"attrs": [
{},
{},
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-columns"
],
"colorPermutations": {
"12552552551": [
{},
{},
{}
]
}
},
{
"id": 134,
"paths": [
"M0 0h1024v256h-1024v-256z",
"M0 384h1024v256h-1024v-256z",
"M0 768h1024v256h-1024v-256z"
],
"attrs": [
{},
{},
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-rows"
],
"colorPermutations": {
"12552552551": [
{},
{},
{}
]
}
},
{
"id": 140,
"paths": [
"M896 0h-768c-70.601 0.227-127.773 57.399-128 127.978l-0 0.022v768c0.227 70.601 57.399 127.773 127.978 128l0.022 0h256v-512l-192-192h640l-192 192v512h256c70.601-0.227 127.773-57.399 128-127.978l0-0.022v-768c-0.227-70.601-57.399-127.773-127.978-128l-0.022-0z"
],
"attrs": [
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-filter"
],
"colorPermutations": {
"12552552551": [
{}
]
}
},
{
"id": 139,
"paths": [
"M896 0h-768c-70.601 0.227-127.773 57.399-128 127.978l-0 0.022v768c0.227 70.601 57.399 127.773 127.978 128l0.022 0h768c70.601-0.227 127.773-57.399 128-127.978l0-0.022v-768c-0.227-70.601-57.399-127.773-127.978-128l-0.022-0zM896 895.8h-256v-383.8l192-192h-640l192 192v384h-256v-767.8h768z"
],
"attrs": [
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-filter-outline"
],
"colorPermutations": {
"12552552551": [
{}
]
}
},
{
"id": 142,
"paths": [
"M768 128c-0.080-70.66-57.34-127.92-127.993-128l-256.007-0c-70.66 0.080-127.92 57.34-128 127.993l-0 0.007v128h-64v768h640v-768h-64zM384 128.12l0.12-0.12 255.88 0.12v127.88h-256z",
"M0 320v640c0.102 35.305 28.695 63.898 63.99 64l0.010 0h64v-768h-64c-35.305 0.102-63.898 28.695-64 63.99l-0 0.010z",
"M960 256h-64v768h64c35.305-0.102 63.898-28.695 64-63.99l0-0.010v-640c-0.102-35.305-28.695-63.898-63.99-64l-0.010-0z"
],
"attrs": [
{},
{},
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-suitcase"
],
"colorPermutations": {
"12552552551": [
{},
{},
{}
]
}
},
{
"id": 145,
"paths": [
"M704 320h-64v-64c0-141.385-114.615-256-256-256s-256 114.615-256 256v0 64h-64c-35.301 0.113-63.887 28.699-64 63.989l-0 0.011v576c0.113 35.301 28.699 63.887 63.989 64l0.011 0h640c35.301-0.113 63.887-28.699 64-63.989l0-0.011v-576c-0.113-35.301-28.699-63.887-63.989-64l-0.011-0zM256 256c0-70.692 57.308-128 128-128s128 57.308 128 128v0 64h-256zM533.4 896l-128-128-43 85-170.4-383.6 383.6 170.2-85 43 128 128z"
],
"attrs": [
{}
],
"width": 768,
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-cursor-locked"
],
"colorPermutations": {
"12552552551": [
{}
]
}
},
{
"id": 150,
"paths": [
"M192 640h832l-192-320 192-320h-896c-70.606 0.215-127.785 57.394-128 127.979l-0 0.021v896h192z"
],
"attrs": [
{}
],
"grid": 16,
"tags": [
"icon-flag"
],
"isMulticolor": false,
"isMulticolor2": false,
"colorPermutations": {
"12552552551": [
{}
]
}
},
{
"id": 152,
"paths": [
"M209.46 608.68q-7.46-9.86-14.26-20.28c-14.737-21.984-27.741-47.184-37.759-73.847l-0.841-2.553c11.078-29.259 24.068-54.443 39.51-77.869l-0.91 1.469c23.221-34.963 50.705-64.8 82.207-89.793l0.793-0.607c57.663-45.719 130.179-75.053 209.311-79.947l1.069-0.053 114.48-140.88c-27.366-5.017-58.869-7.898-91.041-7.92l-0.019-0c-245.8 0-452.2 168-510.8 395.6 21.856 82.93 60.906 154.847 113.325 214.773l-0.525-0.613z",
"M814.76 415.080q7.52 10 14.44 20.52c14.737 21.984 27.741 47.184 37.759 73.847l0.841 2.553c-10.859 29.216-23.863 54.416-39.447 77.748l0.847-1.348c-23.221 34.963-50.705 64.8-82.207 89.793l-0.793 0.607c-57.762 45.834-130.437 75.216-209.743 80.049l-1.057 0.051-114.46 140.86c27.346 4.988 58.817 7.84 90.955 7.84 0.037 0 0.074-0 0.111-0l-0.005 0c245.8 0 452.2-168 510.8-395.6-21.856-82.93-60.906-154.847-113.325-214.773l0.525 0.613z",
"M832 0l-832 1024h192l832-1024h-192z"
],
"attrs": [
{},
{},
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-eye-disabled"
],
"colorPermutations": {
"12552552551": [
{},
{},
{}
]
}
},
{
"id": 153,
"paths": [
"M830 62h-830l-4 702c0 106.6 87.4 194 194 194h640c106.6 0 194-87.4 194-194v-508c0-106.8-87.4-194-194-194zM832 446l-384 384-192-192v-256l192 192 384-384v256z"
],
"attrs": [
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-notebook-page"
],
"colorPermutations": {
"12552552551": [
{}
]
}
},
{
"id": 160,
"paths": [
"M768 0c-141.339 0.114-255.886 114.661-256 255.989l-0 0.011v128h-448c-35.301 0.113-63.887 28.699-64 63.989l-0 0.011v512c0.113 35.301 28.699 63.887 63.989 64l0.011 0h638c35.301-0.113 63.887-28.699 64-63.989l0-0.011v-512c-0.113-35.301-28.699-63.887-63.989-64l-0.011-0h-62v-128c0-70.692 57.308-128 128-128s128 57.308 128 128v0 128h128v-128c-0.114-141.339-114.661-255.886-255.989-256l-0.011-0z"
],
"attrs": [
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-unlocked"
],
"colorPermutations": {
"12552552551": [
{}
]
}
},
{
"id": 169,
"paths": [
"M1024 512c0 282.77-229.23 512-512 512s-512-229.23-512-512c0-282.77 229.23-512 512-512s512 229.23 512 512z"
],
"attrs": [
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-circle"
],
"colorPermutations": {
"12552552551": [
{}
]
}
},
{
"id": 173,
"paths": [
"M876.34 635.58l-49.9 49.88-19.26 19.5-26 8.7-423.040 144.2 144.2-423.28 8.84-25.78 150-149.88-85.6-149.78c-34.92-61.12-92-61.12-127 0l-422.78 739.72c-34.94 61.14-5.92 111.14 64.48 111.14h843.44c70.4 0 99.42-50 64.48-111.14z",
"M973.18 242.84c-19.32-19.3-40.66-34.62-60.16-43.16-34.42-15.12-52.38-4.54-60.1 3.16l-258.12 258.12-82.8 243.040 243-82.8 3.36-3.4 254.76-254.76c4.94-4.94 10.88-13.88 10.88-28.3 0-25.34-19.5-60.56-50.82-91.9zM631 619.82l-34.88-34.86 34.64-101.6 9.24-3.36h32v64h64v32l-3.42 9.26z"
],
"attrs": [
{},
{}
],
"grid": 16,
"tags": [
"icon-draft"
],
"isMulticolor": false,
"isMulticolor2": false,
"colorPermutations": {
"12552552551": [
{},
{}
]
}
},
{
"id": 183,
"paths": [
"M512 0c-282.78 0-512 229.22-512 512s229.22 512 512 512 512-229.22 512-512-229.22-512-512-512zM263.1 263.1c66.48-66.48 154.88-103.1 248.9-103.1 66.74 0 130.64 18.48 185.9 52.96l-484.94 484.94c-34.5-55.24-52.96-119.16-52.96-185.9 0-94.020 36.62-182.42 103.1-248.9zM760.9 760.9c-66.48 66.48-154.88 103.1-248.9 103.1-66.74 0-130.64-18.48-185.9-52.96l484.94-484.94c34.5 55.24 52.96 119.16 52.96 185.9 0 94.020-36.62 182.42-103.1 248.9z"
],
"attrs": [
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-circle-slash"
],
"colorPermutations": {
"12552552551": [
{}
]
}
},
{
"id": 182,
"paths": [
"M136.86 52.26c54.080-34.82 120.58-52.26 199.44-52.26 103.6 0 189.7 24.76 258.24 74.28s102.82 122.88 102.82 220.060c0 59.6-14.86 109.8-44.58 150.6-17.38 24.76-50.76 56.4-100.14 94.9l-48.68 37.82c-26.54 20.64-44.14 44.7-52.82 72.2-5.5 17.44-8.46 44.48-8.92 81.14h-186.4c2.74-77.48 10.060-131 21.94-160.58s42.5-63.62 91.88-102.12l50.060-39.2c16.46-12.38 29.72-25.9 39.78-40.58 18.28-25.2 27.42-52.96 27.42-83.22 0-34.84-10.18-66.6-30.52-95.24-20.36-28.64-57.52-42.98-111.48-42.98s-90.68 17.66-112.88 52.96c-22.18 35.32-33.26 71.98-33.26 110.040h-198.76c5.5-130.64 51.12-223.24 136.86-277.82zM251.020 825.24h205.62v198.74h-205.62v-198.74z"
],
"attrs": [
{}
],
"width": 697,
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-question-mark"
],
"colorPermutations": {
"12552552551": [
{}
]
}
},
{
"id": 179,
"paths": [
"M512 0c-282.76 0-512 214.9-512 480 0 92.26 27.8 178.44 75.92 251.6l-75.92 292.4 313.5-101.42c61.040 24.1 128.12 37.42 198.5 37.42 282.76 0 512-214.9 512-480s-229.24-480-512-480zM768 448l-320 320-192-192v-192l192 192 320-320v192z"
],
"attrs": [
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-status-poll-check"
],
"colorPermutations": {
"12552552551": [
{}
]
}
},
{
"id": 178,
"paths": [
"M512 0c-282.76 0-512 214.9-512 480 0 92.26 27.8 178.44 75.92 251.6l-75.92 292.4 313.5-101.42c61.040 24.1 128.12 37.42 198.5 37.42 282.76 0 512-214.9 512-480s-229.24-480-512-480zM781.36 704h-538.72c-44.96 0-63.5-31.94-41.2-70.98l270-472.48c22.3-39.040 58.82-39.040 81.12 0l269.98 472.48c22.3 39.040 3.78 70.98-41.2 70.98z",
"M457.14 417.86l24.2 122.64h61.32l24.2-122.64v-163.5h-109.72v163.5z",
"M471.12 581.36h81.76v81.76h-81.76v-81.76z"
],
"attrs": [
{},
{},
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-status-poll-caution"
],
"colorPermutations": {
"12552552551": [
{},
{},
{}
]
}
},
{
"id": 180,
"paths": [
"M391.18 668.7c35.72 22.98 77.32 35.3 120.82 35.3 59.84 0 116.080-23.3 158.4-65.6 42.3-42.3 65.6-98.56 65.6-158.4 0-43.5-12.32-85.080-35.3-120.82l-309.52 309.52z",
"M512 256c-59.84 0-116.080 23.3-158.4 65.6-42.3 42.3-65.6 98.56-65.6 158.4 0 43.5 12.32 85.080 35.3 120.82l309.52-309.52c-35.72-22.98-77.32-35.3-120.82-35.3z",
"M512 0c-282.76 0-512 214.9-512 480 0 92.26 27.8 178.44 75.92 251.6l-75.92 292.4 313.5-101.42c61.040 24.1 128.12 37.42 198.5 37.42 282.76 0 512-214.9 512-480s-229.24-480-512-480zM512 800c-176.74 0-320-143.26-320-320s143.26-320 320-320 320 143.26 320 320-143.26 320-320 320z"
],
"attrs": [
{},
{},
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-status-poll-circle-slash"
],
"colorPermutations": {
"12552552551": [
{},
{},
{}
]
}
},
{
"id": 181,
"paths": [
"M512 0c-282.76 0-512 214.9-512 480 0 92.26 27.8 178.44 75.92 251.6l-75.92 292.4 313.5-101.42c61.040 24.1 128.12 37.42 198.5 37.42 282.76 0 512-214.9 512-480s-229.24-480-512-480zM579.020 832h-141.36v-136.64h141.36v136.64zM713.84 433.9c-11.94 17.020-34.9 38.78-68.84 65.24l-33.48 26c-18.24 14.18-30.34 30.74-36.32 49.64-3.78 11.98-5.82 30.58-6.14 55.8h-128.12c1.88-53.26 6.92-90.060 15.080-110.4 8.18-20.34 29.22-43.74 63.16-70.22l34.42-26.94c11.3-8.52 20.42-17.8 27.34-27.9 12.56-17.34 18.86-36.4 18.86-57.2 0-23.94-7-45.78-20.98-65.48-14-19.7-39.54-29.54-76.64-29.54s-62.34 12.14-77.6 36.4c-15.24 24.28-22.88 49.48-22.88 75.64h-136.64c3.78-89.84 35.14-153.5 94.080-191.020 37.18-23.94 82.9-35.94 137.12-35.94 71.22 0 130.42 17.020 177.54 51.060s70.68 84.48 70.68 151.3c0 40.98-10.22 75.5-30.66 103.54z"
],
"attrs": [
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-status-poll-question-mark"
],
"colorPermutations": {
"12552552551": [
{}
]
}
},
{
"id": 176,
"paths": [
"M1000.080 334.64l-336.6 336.76-20.52 6.88-450.96 153.72 160.68-471.52 332.34-332.34c-54.040-18.2-112.28-28.14-173.020-28.14-282.76 0-512 214.9-512 480 0 92.26 27.8 178.44 75.92 251.6l-75.92 292.4 313.5-101.42c61.040 24.1 128.12 37.42 198.5 37.42 282.76 0 512-214.9 512-480 0-50.68-8.4-99.5-23.92-145.36z",
"M408.42 395.24l-2.16 6.3-111.7 327.9 334.12-113.86 4.62-4.68 350.28-350.28c6.8-6.78 14.96-19.1 14.96-38.9 0-34.86-26.82-83.28-69.88-126.38-26.54-26.54-55.9-47.6-82.7-59.34-47.34-20.8-72.020-6.24-82.64 4.36l-354.9 354.88zM470.56 421.42h44v88h88v44l-4.7 12.72-139.68 47.54-47.94-47.94 47.6-139.72 12.72-4.6z"
],
"attrs": [
{},
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-status-poll-edit"
],
"colorPermutations": {
"12552552551": [
{},
{}
]
}
},
{
"id": 186,
"paths": [
"M832 0h-640c-105.6 0-192 86.4-192 192v640c0 105.6 86.4 192 192 192h640c105.6 0 192-86.4 192-192v-640c0-105.6-86.4-192-192-192zM681.38 365.14c0.68-20.46-2.22-37.7-8.7-51.68-6.5-13.98-15.7-25.4-27.64-34.28-11.94-8.86-26.1-15.18-42.48-18.94-16.38-3.74-33.78-5.62-52.2-5.62-15.020 0-30.2 1.54-45.54 4.6s-29.16 8.18-41.44 15.36-22.18 16.56-29.68 28.14c-7.52 11.62-11.26 25.94-11.26 42.98s6.66 32.6 19.96 44.52c13.3 11.94 29.34 21.84 48.1 29.68 18.76 7.86 38.020 14 57.82 18.42 19.78 4.44 35.82 8.020 48.1 10.74 28.66 7.52 54.92 16.22 78.8 26.1 23.88 9.9 44.52 22.68 61.92 38.38s30.86 34.8 40.42 57.32c9.54 22.52 14.32 50.16 14.32 82.9 0 43.68-9.040 80.86-27.12 111.56s-41.28 55.62-69.6 74.7c-28.32 19.1-60.22 32.92-95.7 41.44s-70.62 12.8-105.42 12.8c-102.34 0-178.6-20.8-228.74-62.44-50.16-41.6-75.22-107.1-75.22-196.5h152.5c-1.38 25.94 1.7 47.58 9.22 64.98 7.5 17.4 18.42 31.22 32.74 41.44 14.32 10.24 31.38 17.58 51.18 22 19.78 4.44 41.28 6.66 64.48 6.66 16.38 0 32.74-2.040 49.12-6.14s31.22-10.24 44.52-18.42c13.3-8.18 24.22-18.76 32.76-31.72 8.52-12.94 12.8-28.66 12.8-47.080s-5.46-32.24-16.38-43.5c-10.92-11.26-25.080-20.98-42.48-29.16s-37.2-15.36-59.36-21.5c-22.18-6.14-44.52-12.62-67.040-19.44-23.2-6.82-45.72-15-67.54-24.56-21.84-9.54-41.44-21.82-58.84-36.84-17.4-15-31.38-33.42-41.96-55.26-10.58-21.82-15.86-48.44-15.86-79.82 0-40.94 8.52-75.74 25.58-104.4 17.040-28.66 39.22-52.020 66.52-70.1 27.28-18.080 58.16-31.38 92.62-39.92 34.44-8.52 69.42-12.8 104.9-12.8 37.52 0 72.82 4.26 105.92 12.8 33.080 8.54 62.080 22.36 87 41.44 24.9 19.1 44.68 43.5 59.36 73.18 14.66 29.68 22 65.68 22 107.98h-152.5z"
],
"attrs": [
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-stale"
],
"colorPermutations": {
"12552552551": [
{}
]
}
},
{
"id": 105,
"paths": [
"M1024 512l-448 512v-1024z",
"M448 0l-448 512 448 512z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-arrows-right-left"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 106,
"paths": [
"M512 0l512 448h-1024z",
"M0 576l512 448 512-448z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-arrows-up-down"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 56,
"paths": [
"M832 752c0 44-36 80-80 80h-480c-44 0-80-36-80-80v-480c0-44 36-80 80-80h480c44 0 80 36 80 80v480z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-bullet"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 133,
"paths": [
"M896 0h-768c-70.4 0-128 57.6-128 128v768c0 70.4 57.6 128 128 128h768c70.4 0 128-57.6 128-128v-768c0-70.4-57.6-128-128-128zM640 448h-256v-192h256v192zM384 512h256v192h-256v-192zM320 704h-256v-192h256v192zM320 256v192h-256v-192h256zM128 960c-17 0-33-6.6-45.2-18.8s-18.8-28.2-18.8-45.2v-128h256v192h-192zM384 960v-192h256v192h-256zM960 896c0 17-6.6 33-18.8 45.2s-28.2 18.8-45.2 18.8h-192v-192h256v128zM960 704h-256v-192h256v192zM960 448h-256v-192h256v192z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-calendar"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 83,
"paths": [
"M958.4 65.6c-43.8-43.8-101-65.6-158.4-65.6s-114.6 21.8-158.4 65.6l-128 128c-74 74-85.4 187-34 273l-12.8 12.8c-35.4-20.8-75-31.4-114.8-31.4-57.4 0-114.6 21.8-158.4 65.6l-128 128c-87.4 87.4-87.4 229.4 0 316.8 43.8 43.8 101 65.6 158.4 65.6s114.6-21.8 158.4-65.6l128-128c74-74 85.4-187 34-273l12.8-12.8c35.2 21 75 31.6 114.6 31.6 57.4 0 114.6-21.8 158.4-65.6l128-128c87.6-87.6 87.6-229.6 0.2-317zM419.8 739.8l-128 128c-18 18.2-42.2 28.2-67.8 28.2s-49.8-10-67.8-28.2c-37.4-37.4-37.4-98.4 0-135.8l128-128c18.2-18.2 42.2-28.2 67.8-28.2 5.6 0 11.2 0.6 16.8 1.4l-55.6 55.6c-10.4 10.4-16.2 24.2-16.2 38.8s5.8 28.6 16.2 38.8c10.4 10.4 24.2 16.2 38.8 16.2s28.6-5.8 38.8-16.2l55.6-55.6c5.4 30.4-3.6 62.2-26.6 85zM867.8 291.8l-128 128c-18 18.2-42.2 28.2-67.8 28.2-5.6 0-11.2-0.6-16.8-1.4l55.6-55.6c10.4-10.4 16.2-24.2 16.2-38.8s-5.8-28.6-16.2-38.8c-10.4-10.4-24.2-16.2-38.8-16.2s-28.6 5.8-38.8 16.2l-55.6 55.6c-5.2-29.8 3.6-61.6 26.6-84.6l128-128c18-18.4 42.2-28.4 67.8-28.4s49.8 10 67.8 28.2c37.6 37.4 37.6 98.2 0 135.6z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-chain-links"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 51,
"paths": [
"M832 576v255.66l-0.34 0.34-639.66-0.34v-255.66h-192v256c0 105.6 86.4 192 192 192h640c105.6 0 192-86.4 192-192v-256h-192z",
"M512 640l448-448h-256v-192h-384v192h-256l448 448z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-download"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 91,
"paths": [
"M640 256v-128c0-70.4-57.6-128-128-128h-384c-70.4 0-128 57.6-128 128v384c0 70.4 57.6 128 128 128h128v-139.6c0-134.8 109.6-244.4 244.4-244.4h139.6z",
"M896 384h-384c-70.4 0-128 57.6-128 128v384c0 70.4 57.6 128 128 128h384c70.4 0 128-57.6 128-128v-384c0-70.4-57.6-128-128-128z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-duplicate"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 78,
"paths": [
"M896 192h-320c-16.4-16.4-96.8-96.8-109.2-109.2l-37.4-37.4c-25-25-74.2-45.4-109.4-45.4h-256c-35.2 0-64 28.8-64 64v384c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v-128c0-70.4-57.6-128-128-128z",
"M896 448h-768c-70.4 0-128 57.6-128 128v320c0 70.4 57.6 128 128 128h768c70.4 0 128-57.6 128-128v-320c0-70.4-57.6-128-128-128zM704 800h-128v128h-128v-128h-128v-128h128v-128h128v128h128v128z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-folder-new-v2.5"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 124,
"paths": [
"M191.656 832c0.118 0.1 0.244 0.224 0.344 0.344v191.656h192v-192c0-105.6-86.4-192-192-192h-192v192h191.656z",
"M192 191.656c-0.1 0.118-0.224 0.244-0.344 0.344h-191.656v192h192c105.6 0 192-86.4 192-192v-192h-192v191.656z",
"M832 384h192v-192h-191.656c-0.118-0.1-0.244-0.226-0.344-0.344v-191.656h-192v192c0 105.6 86.4 192 192 192z",
"M832 832.344c0.1-0.118 0.224-0.244 0.344-0.344h191.656v-192h-192c-105.6 0-192 86.4-192 192v192h192v-191.656z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-fullscreen-collapse"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 123,
"paths": [
"M192.344 832c-0.118-0.1-0.244-0.224-0.344-0.344v-191.656h-192v192c0 105.6 86.4 192 192 192h192v-192h-191.656z",
"M192 192.344c0.1-0.118 0.224-0.244 0.344-0.344h191.656v-192h-192c-105.6 0-192 86.4-192 192v192h192v-191.656z",
"M832 0h-192v192h191.656c0.118 0.1 0.244 0.226 0.344 0.344v191.656h192v-192c0-105.6-86.4-192-192-192z",
"M832 831.656c-0.1 0.118-0.224 0.244-0.344 0.344h-191.656v192h192c105.6 0 192-86.4 192-192v-192h-192v191.656z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-fullscreen-expand"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 104,
"paths": [
"M1024 384l-512-384-512 384 512 384z",
"M512 896l-426.666-320-85.334 64 512 384 512-384-85.334-64z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-layers"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 102,
"paths": [
"M64 576c-35.346 0-64-28.654-64-64s28.654-64 64-64h896c35.346 0 64 28.654 64 64s-28.654 64-64 64h-896z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-line-horz"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 20,
"paths": [
"M1024 896l-256.8-256.8c42.4-66.6 65-144 64.8-223.2 0-229.8-186.2-416-416-416s-416 186.2-416 416 186.2 416 416 416c79 0.2 156.4-22.4 223.2-64.8l256.8 256.8 128-128zM212.4 619.6c-112.4-112.4-112.4-294.8 0-407.2s294.8-112.4 407.2 0 112.4 294.8 0 407.2c-54 54-127.2 84.4-203.6 84.4-76.4 0.2-149.8-30.2-203.6-84.4z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-magnify-v2"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 21,
"paths": [
"M1024 896l-256.86-256.86c40.681-62.963 64.861-139.898 64.861-222.481 0-0.232-0-0.464-0.001-0.696l0 0.036c0-229.76-186.24-416-416-416s-416 186.24-416 416 186.24 416 416 416c0.196 0 0.427 0.001 0.659 0.001 82.583 0 159.518-24.18 224.112-65.846l-1.631 0.985 256.86 256.86zM212.36 619.64c-52.114-52.117-84.346-124.114-84.346-203.64 0-159.058 128.942-288 288-288s288 128.942 288 288c0 159.058-128.942 288-288 288-0.005 0-0.010-0-0.014-0l0.001 0c-0.242 0.001-0.529 0.001-0.815 0.001-79.271 0-151.010-32.251-202.811-84.348l-0.013-0.014z",
"M224 352h384v128h-384v-128z",
"M352 224h128v384h-128v-384z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-magnify-in-v2"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 19,
"paths": [
"M767.2 639.2c42.4-66.6 65-144 64.8-223.2 0-229.8-186.2-416-416-416s-416 186.2-416 416 186.2 416 416 416c79 0.2 156.4-22.4 223.2-64.8l256.8 256.8 128-128-256.8-256.8zM619.6 619.6c-54 54-127.2 84.4-203.6 84.4-76.4 0.2-149.8-30.2-203.6-84.4-112.4-112.4-112.4-294.8 0-407.2s294.8-112.4 407.2 0c112.4 112.4 112.4 294.8 0 407.2z",
"M224 352h384v128h-384v-128z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-magnify-out-v2"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 17,
"paths": [
"M0 128h1024v128h-1024v-128z",
"M0 448h1024v128h-1024v-128z",
"M0 768h1024v128h-1024v-128z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-menu-v2.2"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 89,
"paths": [
"M293.4 512l218.6-218.6 256 256v-421.4c0-70.4-57.6-128-128-128h-512c-70.4 0-128 57.6-128 128v512c0 70.4 57.6 128 128 128h421.4l-256-256z",
"M1024 448h-128v320l-384-384-128 128 384 384h-320v128h576z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-move"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 127,
"paths": [
"M448 0v128h320l-384 384 128 128 384-384v320h128v-576z",
"M576 674.274v157.382c-0.1 0.118-0.226 0.244-0.344 0.344h-383.312c-0.118-0.1-0.244-0.226-0.344-0.344v-383.312c0.1-0.118 0.226-0.244 0.344-0.344h157.382l192-192h-349.726c-105.6 0-192 86.4-192 192v384c0 105.6 86.4 192 192 192h384c105.6 0 192-86.4 192-192v-349.726l-192 192z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-new-window"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 63,
"paths": [
"M544 224v224c0 88.4-71.6 160-160 160s-160-71.6-160-160v-97.2l-197.4 196.4c-50 50-12.4 215.2 112.4 340s290 162.4 340 112.4l417-423.6-352-352z",
"M896 1024c70.6 0 128-57.4 128-128 0-108.6-128-192-128-192s-128 83.4-128 192c0 70.6 57.4 128 128 128z",
"M384 512c-35.4 0-64-28.6-64-64v-384c0-35.4 28.6-64 64-64s64 28.6 64 64v384c0 35.4-28.6 64-64 64z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-paint-bucket-v2"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 117,
"paths": [
"M922.344 101.68c-38.612-38.596-81.306-69.232-120.304-86.324-68.848-30.25-104.77-9.078-120.194 6.344l-516.228 516.216-3.136 9.152-162.482 476.932 485.998-165.612 6.73-6.806 509.502-509.506c9.882-9.866 21.768-27.77 21.768-56.578 0.002-50.71-38.996-121.148-101.654-183.818zM237.982 855.66l-69.73-69.728 69.25-203.228 18.498-6.704h64v128h128v64l-6.846 18.506-203.172 69.154z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-pencil"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 72,
"paths": [
"M922.4 101.6c-38.6-38.6-81.4-69.2-120.4-86.2-68.8-30.2-104.8-9-120.2 6.4l-516.2 516.2-3.2 9.2-162.4 476.8 486-165.6 516.2-516.4c9.8-9.8 21.8-27.8 21.8-56.6 0-50.6-39-121-101.6-183.8zM238 855.6l-69.8-69.6 69.2-203.2 18.4-6.8h64v128h128v64l-6.8 18.6-203 69z",
"M0 0v512l128-128v-256h256l128-128z",
"M1024 1024v-512l-128 128v256h-256l-128 128z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-pencil-edit-in-place"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 90,
"paths": [
"M1024 512l-1024 512v-1024z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-play"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 88,
"paths": [
"M126-2h256v1024h-256v-1024z",
"M638-2h256v1024h-256v-1024z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-pause"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 13,
"paths": [
"M255.8 704c0.2 0 0.2 0 0 0l0.2-128c0-70.6 57.4-128 128-128h255.8c0 0 0 0 0.2-0.2v-127.8c0-70.6 57.4-128 128-128h143.6c-93.8-117-238-192-399.6-192-282.8 0-512 229.2-512 512 0 68 13.2 132.8 37.2 192h218.6z",
"M768.2 320c-0.2 0-0.2 0 0 0l-0.2 128c0 70.6-57.4 128-128 128h-255.8c0 0 0 0-0.2 0.2v127.8c0 70.6-57.4 128-128 128h-143.6c93.8 117 238 192 399.6 192 282.8 0 512-229.2 512-512 0-68-13.2-132.8-37.2-192h-218.6z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-plot-resource"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 80,
"paths": [
"M766 1024l-256-512 256-512h-256l-256 512 256 512z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-pointer-left"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 81,
"paths": [
"M254 0l256 512-256 512h256l256-512-256-512z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-pointer-right"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 37,
"paths": [
"M1024 460.8v-460.8l-175.8 175.8c-85.2-69.6-190.8-107.6-302-107.6-127.6 0-247.6 49.8-338 140s-140 210.4-140 338 49.8 247.6 140 338 210.4 140 338 140 247.6-49.8 338-140c74-74 120.8-167.8 135-269.6h-138.6c-32 155.4-169.8 272.8-334.6 272.8-188.2 0-341.4-153.2-341.4-341.4s153.4-341.2 341.6-341.2c76.8 0 147.6 25.4 204.8 68.2l-187.8 187.8h460.8z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-refresh-v1.5"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 71,
"paths": [
"M192.2 576c-0.2 0-0.2 0 0 0l-0.2 448h640v-447.8c0 0 0 0-0.2-0.2h-639.6z",
"M978.8 210.8l-165.4-165.4c-25-25-74.2-45.4-109.4-45.4h-576c-70.4 0-128 57.6-128 128v768c0 70.4 57.6 128 128 128v-448c0-35.2 28.8-64 64-64h640c35.2 0 64 28.8 64 64v448c70.4 0 128-57.6 128-128v-576c0-35.2-20.4-84.4-45.2-109.2zM704 256c0 35.2-28.8 64-64 64h-448c-35.2 0-64-28.8-64-64v-192h320v192h128v-192h128v192z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-save-v2"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 70,
"paths": [
"M978.8 338.8l-64-64c24.8 24.8 45.2 74 45.2 109.2v448c0 70.4-57.6 128-128 128h-640c-18.8 0-36.6-4.2-52.6-11.4 20.2 44.4 65 75.4 116.6 75.4h640c70.4 0 128-57.6 128-128v-448c0-35.2-20.4-84.4-45.2-109.2z",
"M704 896v-319.8c0 0 0 0-0.2-0.2h-511.6l-0.2 320h512z",
"M192 512h512c35.2 0 64 28.8 64 64v320c70.4 0 128-57.6 128-128v-448c0-35.2-20.4-84.4-45.2-109.2l-165.4-165.4c-25-25-74.2-45.4-109.4-45.4h-448c-70.4 0-128 57.6-128 128v640c0 70.4 57.6 128 128 128v-320c0-35.2 28.8-64 64-64zM128 64h192v192h128v-192h128v192c0 35.2-28.8 64-64 64h-320c-35.2 0-64-28.8-64-64v-192z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-save-as"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 68,
"paths": [
"M1024 512c-1.8-7.2-3.4-14.4-5.2-21.8-20.2-86.2-53.4-209.4-98.4-307.2-22.4-49-45.4-86.6-70.2-115.2-48.6-56-98.4-67.8-131.8-67.8-33.2 0-83.2 11.8-131.8 67.8-24.6 28.6-47.6 66.2-70 115.2-44.8 97.8-78.2 221-98.4 307.2-21.8 93-46.6 175.4-72 238.4-16.4 40.6-30.4 66.4-40.8 82.8-10.4-16.2-24.4-42.2-40.8-82.8-23.2-58-46.2-132.4-66.6-216.6h-198c1.8 7.2 3.4 14.4 5.2 21.8 20.2 86.2 53.4 209.4 98.4 307.2 22.4 49 45.4 86.6 70.2 115.2 48.6 56 98.6 67.8 131.8 67.8s83.2-11.8 131.8-67.8c24.8-28.6 47.6-66.2 70.2-115.2 44.8-97.8 78.2-221 98.4-307.2 21.8-93 46.6-175.4 72-238.4 16.4-40.6 30.4-66.4 40.8-82.8 10.4 16.2 24.4 42.2 40.8 82.8 23.4 57.8 46.4 132.4 66.8 216.4h197.6z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-sine"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 5,
"paths": [
"M800 1024h224l-384-1024h-256l-384 1024h224l84-224h408zM380 608l132-352 132 352z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-font"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 87,
"paths": [
"M448 382c0 35.2-28.8 64-64 64h-320c-35.2 0-64-28.8-64-64v-320c0-35.2 28.8-64 64-64h320c35.2 0 64 28.8 64 64v320z",
"M1024 382c0 35.2-28.8 64-64 64h-320c-35.2 0-64-28.8-64-64v-320c0-35.2 28.8-64 64-64h320c35.2 0 64 28.8 64 64v320z",
"M448 958c0 35.2-28.8 64-64 64h-320c-35.2 0-64-28.8-64-64v-320c0-35.2 28.8-64 64-64h320c35.2 0 64 28.8 64 64v320z",
"M1024 958c0 35.2-28.8 64-64 64h-320c-35.2 0-64-28.8-64-64v-320c0-35.2 28.8-64 64-64h320c35.2 0 64 28.8 64 64v320z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-thumbs-strip"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 99,
"paths": [
"M896 0h-768c-70.4 0-128 57.6-128 128v768c0 70.4 57.6 128 128 128h768c70.4 0 128-57.6 128-128v-768c0-70.4-57.6-128-128-128zM128 128h320v768h-320v-768zM896 896h-320v-768h320v768z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-two-parts-both"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 98,
"paths": [
"M896 0h-768c-70.4 0-128 57.6-128 128v768c0 70.4 57.6 128 128 128h768c70.4 0 128-57.6 128-128v-768c0-70.4-57.6-128-128-128zM896 896h-320v-768h320v768z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-two-parts-one-only"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 40,
"paths": [
"M795.2 164.8c-79.8-65.2-178.8-100.8-283.2-100.8-119.6 0-232.2 46.6-316.8 131.2-69.4 69.4-113.2 157.4-126.6 252.8h130c29.6-145.8 158.8-256 313.4-256 72 0 138.4 23.8 192 64l-176 176h432v-432l-164.8 164.8z",
"M512 832c-72 0-138.4-23.8-192-64l176-176h-432v432l164.8-164.8c79.8 65.2 178.8 100.8 283.2 100.8 119.6 0 232.2-46.6 316.8-131.2 69.4-69.4 113.2-157.4 126.6-252.8h-130c-29.6 145.8-158.8 256-313.4 256z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-resync"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 36,
"paths": [
"M460.8 460.8l-187.8-187.8c57.2-42.8 128-68.2 204.8-68.2 188.2 0 341.6 153.2 341.6 341.4s-153.2 341.2-341.4 341.2c-165 0-302.8-117.6-334.6-273h-138.4c14.2 101.8 61 195.6 135 269.6 90.2 90.2 210.4 140 338 140s247.6-49.8 338-140 140-210.4 140-338-49.8-247.6-140-338-210.4-140-338-140c-111.4 0-217 38-302 107.6l-176-175.6v460.8h460.8z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-reset"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 65,
"paths": [
"M512 0c-282.8 0-512 229.2-512 512s229.2 512 512 512 512-229.2 512-512-229.2-512-512-512zM832 704l-128 128-192-192-192 192-128-128 192-192-192-192 128-128 192 192 192-192 128 128-192 192 192 192z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-x-in-circle"
],
"isMulticolor": false,
"isMulticolor2": false,
"colorPermutations": {
"12552552551": []
}
},
{
"id": 38,
"paths": [
"M253.414 318.061l-155.172-116.384c-50.233 66.209-85.127 146.713-97.91 234.39l-0.333 2.781 191.919 27.434c8.145-56.552 29.998-106.879 62.068-149.006l-0.573 0.784z",
"M191.98 557.717l-191.919 27.434c13.115 90.459 48.009 170.963 99.174 238.453l-0.931-1.281 155.111-116.384c-31.476-41.347-53.309-91.675-61.231-146.504l-0.204-1.719z",
"M466.283 191.98l-27.434-191.919c-90.459 13.115-170.963 48.009-238.453 99.174l1.281-0.931 116.384 155.111c41.347-31.476 91.675-53.309 146.504-61.231l1.719-0.204z",
"M822.323 98.242c-66.209-50.233-146.713-85.127-234.39-97.91l-2.781-0.333-27.434 191.919c56.552 8.145 106.879 29.998 149.006 62.068l-0.784-0.573z",
"M832.020 466.283l191.919-27.434c-13.115-90.459-48.009-170.963-99.174-238.453l0.931 1.281-155.111 116.384c31.476 41.347 53.309 91.675 61.231 146.504l0.204 1.719z",
"M201.677 925.758c66.209 50.233 146.713 85.127 234.39 97.91l2.781 0.333 27.434-191.919c-56.552-8.145-106.879-29.998-149.006-62.068l0.784 0.573z",
"M770.586 705.939l155.131 116.343c50.233-66.209 85.127-146.713 97.91-234.39l0.333-2.781-191.919-27.434c-8.125 56.564-29.966 106.906-62.028 149.049l0.574-0.786z",
"M557.717 832.020l27.434 191.919c90.459-13.115 170.963-48.009 238.453-99.174l-1.281 0.931-116.384-155.111c-41.347 31.476-91.675 53.309-146.504 61.231l-1.719 0.204z",
"M770.586 512c0 142.813-115.773 258.586-258.586 258.586s-258.586-115.773-258.586-258.586c0-142.813 115.773-258.586 258.586-258.586s258.586 115.773 258.586 258.586z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-brightness"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 39,
"paths": [
"M512 0c-282.78 0-512 229.24-512 512s229.22 512 512 512 512-229.24 512-512-229.22-512-512-512zM783.52 783.52c-69.111 69.481-164.785 112.481-270.502 112.481-0.358 0-0.716-0-1.074-0.001l0.055 0v-768c212.070 0.010 383.982 171.929 383.982 384 0 106.034-42.977 202.031-112.462 271.52l0-0z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-contrast"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 35,
"paths": [
"M960 0c0 0 0 0 0 0h-320v128h165.4l-210.6 210.8c-25 25-25 65.6 0 90.6 12.4 12.4 28.8 18.8 45.2 18.8s32.8-6.2 45.2-18.8l210.8-210.8v165.4h128v-384h-64z",
"M896 805.4l-210.8-210.6c-25-25-65.6-25-90.6 0s-25 65.6 0 90.6l210.8 210.6h-165.4v128h384v-384h-128v165.4z",
"M218.6 128h165.4v-128h-320c0 0 0 0 0 0h-64v384h128v-165.4l210.8 210.8c12.4 12.4 28.8 18.8 45.2 18.8s32.8-6.2 45.2-18.8c25-25 25-65.6 0-90.6l-210.6-210.8z",
"M338.8 594.8l-210.8 210.6v-165.4h-128v384h384v-128h-165.4l210.8-210.8c25-25 25-65.6 0-90.6-25.2-24.8-65.6-24.8-90.6 0.2z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-expand"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 33,
"paths": [
"M0 64h1024v128h-1024v-128z",
"M0 320h1024v128h-1024v-128z",
"M0 576h1024v128h-1024v-128z",
"M0 832h1024v128h-1024v-128z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-list-view"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 28,
"paths": [
"M382 830h448v-448h-448v448zM510 510h192v192h-192v-192z",
"M-2 574h320v64h-320v-64z",
"M894 574h128v64h-128v-64z",
"M574-2h64v320h-64v-320z",
"M574 894h64v128h-64v-128z",
"M574 574h64v64h-64v-64z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-grid-snap-to"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 29,
"paths": [
"M768 576h192v64h-192v-64z",
"M256 576h192v64h-192v-64z",
"M0 576h192v64h-192v-64z",
"M640 512h-64v64h-64v64h64v64h64v-64h64v-64h-64z",
"M576 256h64v192h-64v-192z",
"M576 0h64v192h-64v-192z",
"M576 768h64v192h-64v-192z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-grid-snap-no"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 26,
"paths": [
"M0 64v896h1024v-896h-1024zM896 832h-768v-640h768v640z",
"M192 256h384v128h-384v-128z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-frame-show"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 25,
"paths": [
"M128 190h420l104-128h-652v802.4l128-157.4z",
"M896 830h-420l-104 128h652v-802.4l-128 157.4z",
"M832-2l-832 1024h192l832-1024z",
"M392 382l104-128h-304v128z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-frame-hide"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 23,
"paths": [
"M832 192.4v639.4c0 0.2-0.2 0.2-0.4 0.4h-319.6v192h320c105.6 0 192-86.4 192-192v-640.2c0-105.6-86.4-192-192-192h-320v192h319.6c0.2 0 0.4 0.2 0.4 0.4z",
"M192 704v192l384-384-384-384v192h-192v384z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-import"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 24,
"paths": [
"M192 831.66v-639.32l0.34-0.34h319.66v-192h-320c-105.6 0-192 86.4-192 192v640c0 105.6 86.4 192 192 192h320v-192h-319.66z",
"M1024 512l-384-384v192h-192v384h192v192l384-384z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-export"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 4,
"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": [
{},
{}
],
"grid": 16,
"tags": [
"icon-font-size-alt1"
],
"width": 1504,
"isMulticolor": false,
"isMulticolor2": false,
"colorPermutations": {
"12552552551": [
{},
{}
]
}
},
{
"id": 141,
"paths": [
"M632 312l-120 120-120-120-80 80 120 120-120 120 80 80 120-120 120 120 80-80-120-120 120-120-80-80z",
"M512 0c-282.76 0-512 86-512 192v640c0 106 229.24 192 512 192s512-86 512-192v-640c0-106-229.24-192-512-192zM512 832c-176.731 0-320-143.269-320-320s143.269-320 320-320c176.731 0 320 143.269 320 320v0c0 176.731-143.269 320-320 320v0z"
],
"attrs": [
{},
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-clear-data"
],
"colorPermutations": {
"12552552551": [
{},
{}
]
}
},
{
"id": 149,
"paths": [
"M576 64c-247.4 0-448 200.6-448 448h-128l192 192 192-192h-128c0-85.4 33.2-165.8 93.8-226.2 60.4-60.6 140.8-93.8 226.2-93.8s165.8 33.2 226.2 93.8c60.6 60.4 93.8 140.8 93.8 226.2s-33.2 165.8-93.8 226.2c-60.4 60.6-140.8 93.8-226.2 93.8s-165.8-33.2-226.2-93.8l-90.6 90.6c81 81 193 131.2 316.8 131.2 247.4 0 448-200.6 448-448s-200.6-448-448-448z",
"M576 272c-26.6 0-48 21.4-48 48v211.8l142 142c9.4 9.4 21.6 14 34 14s24.6-4.6 34-14c18.8-18.8 18.8-49.2 0-67.8l-114-114v-172c0-26.6-21.4-48-48-48z"
],
"attrs": [
{},
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-history"
],
"colorPermutations": {
"12552552551": [
{},
{}
]
}
},
{
"id": 158,
"paths": [
"M643.427 825.261c-81.955-0.697-148.179-67.065-148.642-149.010l-0-0.044v-395.828l296.871 247.393v-197.914l-395.828-329.857-395.828 328.62v197.502l296.871-246.156v396.241c0 190.905 155.239 346.556 346.144 346.968l412.321 0.825 0.412-197.914z"
],
"attrs": [
{}
],
"width": 1056,
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-arrow-up-to-parent"
],
"colorPermutations": {
"12552552551": [
{}
]
}
},
{
"id": 159,
"paths": [
"M512 0c-282.8 0-512 229.2-512 512s229.2 512 512 512 512-229.2 512-512-229.2-512-512-512zM783.6 783.6c-54.634 54.8-125.77 93.12-205.322 106.874l-2.278 0.326v-250.8h-128v250.8c-161.302-28.062-286.738-153.497-314.468-312.5l-0.332-2.3h250.8v-128h-250.8c28.062-161.302 153.497-286.738 312.5-314.468l2.3-0.332v250.8h128v-250.8c161.302 28.062 286.738 153.497 314.468 312.5l0.332 2.3h-250.8v128h250.8c-14.080 81.83-52.4 152.966-107.191 207.591l-0.009 0.009z"
],
"attrs": [
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-crosshair-in-circle"
],
"colorPermutations": {
"12552552551": [
{}
]
}
},
{
"id": 161,
"paths": [
"M512 384c70.692 0 128 57.308 128 128s-57.308 128-128 128c-70.692 0-128-57.308-128-128v0c0.114-70.647 57.353-127.886 127.989-128l0.011-0zM512 256c-141.385 0-256 114.615-256 256s114.615 256 256 256c141.385 0 256-114.615 256-256v0c-0.114-141.339-114.661-255.886-255.989-256l-0.011-0z",
"M512 128c211.87 0.128 383.575 171.912 383.575 383.8 0 211.967-171.833 383.8-383.8 383.8s-383.8-171.833-383.8-383.8c0-105.99 42.963-201.945 112.425-271.4l-0 0c69.21-69.437 164.944-112.401 270.713-112.401 0.312 0 0.624 0 0.936 0.001l-0.048-0zM512 0c-282.8 0-512 229.2-512 512s229.2 512 512 512 512-229.2 512-512-229.2-512-512-512z"
],
"attrs": [
{},
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-target"
],
"colorPermutations": {
"12552552551": [
{},
{}
]
}
},
{
"id": 163,
"paths": [
"M45.2 658.8h229.6l-274.8 274.6 90.6 90.6 274.6-274.8v229.6h128v-448h-448v128z",
"M1024 90.6l-90.6-90.6-274.6 274.8v-229.6h-128v448h448v-128h-229.6l274.8-274.6z"
],
"attrs": [
{},
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-items-collapse"
],
"colorPermutations": {
"12552552551": [
{},
{}
]
}
},
{
"id": 162,
"paths": [
"M448 896h-229.4l274.6-274.8-90.4-90.4-274.8 274.6v-229.4h-128v448h448v-128z",
"M530.8 402.8l90.4 90.4 274.8-274.6v229.4h128v-448h-448v128h229.4l-274.6 274.8z"
],
"attrs": [
{},
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-items-expand"
],
"colorPermutations": {
"12552552551": [
{},
{}
]
}
},
{
"id": 164,
"paths": [
"M256 512c0 70.692-57.308 128-128 128s-128-57.308-128-128c0-70.692 57.308-128 128-128s128 57.308 128 128z",
"M640 512c0 70.692-57.308 128-128 128s-128-57.308-128-128c0-70.692 57.308-128 128-128s128 57.308 128 128z",
"M1024 512c0 70.692-57.308 128-128 128s-128-57.308-128-128c0-70.692 57.308-128 128-128s128 57.308 128 128z"
],
"attrs": [
{},
{},
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-3-dots"
],
"colorPermutations": {
"12552552551": [
{},
{},
{}
]
}
},
{
"id": 165,
"paths": [
"M1024 384v-128h-256v-256h-128v256h-256v-256h-128v256h-256v128h256v256h-256v128h256v256h128v-256h256v256h128v-256h256v-128h-256v-256zM640 640h-256v-256h256z"
],
"attrs": [
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-grid-on"
],
"colorPermutations": {
"12552552551": [
{}
]
}
},
{
"id": 166,
"paths": [
"M256 551.4l128-157.6v-9.8h8l104-128h-112v-256h-128v256h-256v128h256v167.4z",
"M184 640h-184v128h80l104-128z",
"M768 472.6l-128 157.6v9.8h-8l-104 128h112v256h128v-256h256v-128h-256v-167.4z",
"M840 384h184v-128h-80l-104 128z",
"M832 0l-832 1024h192l832-1024h-192z"
],
"attrs": [
{},
{},
{},
{},
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-grid-off"
],
"colorPermutations": {
"12552552551": [
{},
{},
{},
{},
{}
]
}
},
{
"id": 167,
"paths": [
"M896 256h-128l-128-256h-256l-128 256h-128c-70.601 0.227-127.773 57.399-128 127.978l-0 0.022v512c0.227 70.601 57.399 127.773 127.978 128l0.022 0h768c70.601-0.227 127.773-57.399 128-127.978l0-0.022v-512c-0.227-70.601-57.399-127.773-127.978-128l-0.022-0zM512 864c-141.385 0-256-114.615-256-256s114.615-256 256-256c141.385 0 256 114.615 256 256v0c0 141.385-114.615 256-256 256v0z"
],
"attrs": [
{}
],
"grid": 16,
"tags": [
"icon-camera"
],
"isMulticolor": false,
"isMulticolor2": false,
"colorPermutations": {
"12552552551": [
{}
]
}
},
{
"id": 168,
"paths": [
"M896 320v448c-0.215 70.606-57.394 127.785-127.979 128l-0.021 0h-576c0.215 70.606 57.394 127.785 127.979 128l0.021 0h576c70.606-0.215 127.785-57.394 128-127.979l0-0.021v-448c-0.215-70.606-57.394-127.785-127.979-128l-0.021-0z",
"M832 704v-448c-0.215-70.606-57.394-127.785-127.979-128l-0.021-0h-192l-101.5-82.74c-24.88-24.9-74.040-45.26-109.24-45.26h-237.26c-35.305 0.102-63.898 28.695-64 63.99l-0 0.010v640c0.215 70.606 57.394 127.785 127.979 128l0.021 0h576c70.606-0.215 127.785-57.394 128-127.979l0-0.021zM128 644v-516l256 260z"
],
"attrs": [
{},
{}
],
"grid": 16,
"tags": [
"icon-folders-collapse"
],
"isMulticolor": false,
"isMulticolor2": false,
"colorPermutations": {
"12552552551": [
{},
{}
]
}
},
{
"id": 188,
"paths": [
"M832.4 128.6c22.8 0 38 11.8 45 19 7 7 19 22.4 19 45v640c0 22.8-11.8 38-19 45-7 7-22.4 19-45 19h-640c-22.8 0-38-11.8-45-19-7-7-19-22.4-19-45v-640c0-22.8 11.8-38 19-45 7-7 22.4-19 45-19h640zM832.4 0.6h-640c-105.6 0-192 86.4-192 192v640c0 105.6 86.4 192 192 192h640c105.6 0 192-86.4 192-192v-640c0-105.6-86.4-192-192-192v0z",
"M256.4 320.6h512v128h-512v-128z",
"M384.4 576.6h384v128h-384v-128z"
],
"attrs": [
{},
{},
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-multiline"
],
"colorPermutations": {
"12552552551": [
{},
{},
{}
]
}
},
{
"id": 187,
"paths": [
"M832.4 0.6h-640c-105.6 0-192 86.4-192 192v640c0 105.6 86.4 192 192 192h640c105.6 0 192-86.4 192-192v-640c0-105.6-86.4-192-192-192zM896.4 448.6h-512v128h512v256c0 22.8-11.8 38-19 45-7 7-22.4 19-45 19h-640c-22.8 0-38-11.8-45-19-7-7-19-22.4-19-45v-640c0-22.8 11.8-38 19-45 7-7 22.4-19 45-19h640c22.8 0 38 11.8 45 19 7 7 19 22.4 19 45v256z"
],
"attrs": [
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-singleline"
],
"colorPermutations": {
"12552552551": [
{}
]
}
},
{
"id": 97,
"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"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-activity"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 16,
"paths": [
"M512 0c-214.8 0-398.8 132.4-474.8 320h90.8c56.8 0 108 24.8 143 64h241l-192-192h256l320 320-320 320h-256l192-192h-241c-35 39.2-86.2 64-143 64h-90.8c76 187.6 259.8 320 474.8 320 282.8 0 512-229.2 512-512s-229.2-512-512-512z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-activity-mode"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 125,
"paths": [
"M192 0c-105.6 0-192 86.4-192 192v640c0 105.6 86.4 192 192 192h64v-1024h-64z",
"M384 0h256v1024h-256v-1024z",
"M832 0h-64v704h256v-512c0-105.6-86.4-192-192-192z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-autoflow-tabular"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 3,
"paths": [
"M512 0c-282.8 0-512 229.2-512 512s229.2 512 512 512 512-229.2 512-512-229.2-512-512-512zM782 690c-12.8 22.2-36.6 36-62.4 36-12.6 0-25-3.4-36-9.6l-222-128.2c-0.8-0.4-1.6-1-2.4-1.4l-0.8-0.6-1.8-1.2-2.4-2-1.8-1.4-0.6-0.6c-0.8-0.6-1.4-1.2-2.2-1.8v0c-5-4.6-9.4-10-13-15.8-0.2-0.4-0.6-1-0.8-1.4s-0.6-1-0.8-1.4c-3.2-6-5.8-12.4-7.2-19.2v-0.2c-0.2-1-0.4-1.8-0.6-2.8 0-0.2 0-0.6-0.2-0.8-0.2-0.6-0.2-1.4-0.2-2.2s-0.2-1-0.2-1.6 0-1-0.2-1.6-0.2-1.6-0.2-2.2c0-0.4 0-0.6 0-1 0-1 0-1.8 0-2.8 0 0 0-0.2 0-0.4v-363.8c0-39.8 32.2-72 72-72s72 32.2 72 72v322.4l185.8 107.2c34.2 20 45.8 64 26 98.4z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-clock-v1.5"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 132,
"paths": [
"M1024 192c0 106.039-229.23 192-512 192s-512-85.961-512-192c0-106.039 229.23-192 512-192s512 85.961 512 192z",
"M512 512c-282.77 0-512-85.962-512-192v512c0 106.038 229.23 192 512 192s512-85.962 512-192v-512c0 106.038-229.23 192-512 192z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-database"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 131,
"paths": [
"M683.52 819.286c-50.782 28.456-109.284 44.714-171.52 44.714-194.094 0-352-157.906-352-352s157.906-352 352-352 352 157.906 352 352c0 62.236-16.258 120.738-44.714 171.52l191.692 191.692c8.516-13.89 13.022-28.354 13.022-43.212v-640c0-106.038-229.23-192-512-192s-512 85.962-512 192v640c0 106.038 229.23 192 512 192 126.11 0 241.548-17.108 330.776-45.46l-159.256-159.254z",
"M352 512c0 88.224 71.776 160 160 160s160-71.776 160-160-71.776-160-160-160-160 71.776-160 160z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-database-query"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 57,
"paths": [
"M896 192h-320c-16.4-16.4-96.8-96.8-109.2-109.2l-37.4-37.4c-25-25-74.2-45.4-109.4-45.4h-256c-35.2 0-64 28.8-64 64v384c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v-128c0-70.4-57.6-128-128-128z",
"M896 448h-768c-70.4 0-128 57.6-128 128v320c0 70.4 57.6 128 128 128h768c70.4 0 128-57.6 128-128v-320c0-70.4-57.6-128-128-128zM320 896h-128v-320h128v320zM576 896h-128v-320h128v320zM832 896h-128v-320h128v320z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-dataset"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 67,
"paths": [
"M1024 192c0 106.039-229.23 192-512 192s-512-85.961-512-192c0-106.039 229.23-192 512-192s512 85.961 512 192z",
"M512 512c-282.8 0-512-86-512-192v512c0 106 229.2 192 512 192s512-86 512-192v-512c0 106-229.2 192-512 192zM896 575v256c-36.6 15.6-79.8 28.8-128 39.4v-256c48.2-10.6 91.4-23.8 128-39.4zM256 614.4v256c-48.2-10.4-91.4-23.8-128-39.4v-256c36.6 15.6 79.8 28.8 128 39.4zM384 890v-256c41 4 83.8 6 128 6s87-2.2 128-6v256c-41 4-83.8 6-128 6s-87-2.2-128-6z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-datatable"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 126,
"paths": [
"M832 640c105.6 0 192-86.4 192-192v-256c0-105.6-86.4-192-192-192v320l-128-64-128 64v-320h-384c-105.6 0-192 86.4-192 192v640c0 105.6 86.4 192 192 192h640c105.6 0 192-86.4 192-192v-192c0 105.6-86.4 192-192 192h-640v-192h640z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-dictionary"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 77,
"paths": [
"M896 192h-320c-16.4-16.4-96.8-96.8-109.2-109.2l-37.4-37.4c-25-25-74.2-45.4-109.4-45.4h-256c-35.2 0-64 28.8-64 64v384c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v-128c0-70.4-57.6-128-128-128z",
"M896 448h-768c-70.4 0-128 57.6-128 128v320c0 70.4 57.6 128 128 128h768c70.4 0 128-57.6 128-128v-320c0-70.4-57.6-128-128-128z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-folder-v2.5"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 100,
"paths": [
"M896 0h-768c-70.4 0-128 57.6-128 128v768c0 70.4 57.6 128 128 128h768c70.4 0 128-57.6 128-128v-768c0-70.4-57.6-128-128-128zM896 896h-768v-768h768v768z",
"M320 256l-128 128v448h640v-320l-128-128-128 128z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-image"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 130,
"paths": [
"M448 0h-256c-105.6 0-192 86.4-192 192v640c0 105.6 86.4 192 192 192h256v-1024z",
"M832 0h-256v577.664h448v-385.664c0-105.6-86.4-192-192-192z",
"M576 1024h256c105.6 0 192-86.4 192-192v-129.664h-448v321.664z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-layout"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 108,
"paths": [
"M512 1024l512-320v-384l-512.020-320-511.98 320v384l512 320zM512 192l358.4 224-358.4 224-358.4-224 358.4-224z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-object"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 76,
"paths": [
"M511.98 0l-511.98 320v384l512 320 512-320v-384l-512.020-320zM586.22 896h-141.36v-136.64h141.36v136.64zM721.040 497.9c-11.94 17.020-34.9 38.78-68.84 65.24l-33.48 26c-18.24 14.18-30.34 30.74-36.32 49.64-3.78 11.98-5.82 30.58-6.14 55.8h-128.12c1.88-53.26 6.92-90.060 15.080-110.4 8.18-20.34 29.22-43.74 63.16-70.22l34.42-26.94c11.3-8.52 20.42-17.8 27.34-27.9 12.56-17.34 18.86-36.4 18.86-57.2 0-23.94-7-45.78-20.98-65.48-14-19.7-39.54-29.54-76.64-29.54s-62.34 12.14-77.6 36.4c-15.24 24.28-22.88 49.48-22.88 75.64h-136.64c3.78-89.84 35.14-153.5 94.080-191.020 37.18-23.94 82.9-35.94 137.12-35.94 71.22 0 130.42 17.020 177.54 51.060s70.68 84.48 70.68 151.3c0 40.98-10.22 75.5-30.66 103.54z"
],
"attrs": [
{}
],
"grid": 16,
"tags": [
"icon-object-unknown"
],
"isMulticolor": false,
"isMulticolor2": false,
"colorPermutations": {
"12552552551": [
{}
]
}
},
{
"id": 15,
"paths": [
"M512 0l-512 320v512c0 105.6 86.4 192 192 192h640c105.6 0 192-86.4 192-192v-512l-512-320zM512 192l358.4 224-358.4 224-358.4-224 358.4-224z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-packet"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 74,
"paths": [
"M704 512c-105.6 0-192-86.4-192-192v-320h-320c-105.6 0-192 86.4-192 192v640c0 105.6 86.4 192 192 192h640c105.6 0 192-86.4 192-192v-320h-320z",
"M768 384h256l-384-384v256c0 70.4 57.6 128 128 128z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-page"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 44,
"paths": [
"M830 0h-636c-106.7 0-194 87.3-194 194v406.82c14.18 18.64 25.66 28.34 32 30.84 14.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 42.46-108.48 116.32-237.76 227.040-237.76 11.355 0.003 22.389 1.366 32.952 3.936l-0.952-0.196v-57.74c0-106.7-87.3-194-194-194z",
"M992 392.34c-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-42.46 108.48-116.32 237.76-227.040 237.76-11.355-0.003-22.389-1.367-32.952-3.936l0.952 0.196v57.74c0 106.7 87.3 194 194 194h636c106.7 0 194-87.3 194-194v-406.82c-14.18-18.64-25.66-28.34-32-30.84z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-plot-overlay"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 42,
"paths": [
"M89.6 312c24.98 0 48.96-26.52 85.52-70.18 45.42-54.28 102-121.82 196-121.82 44.64 0 86.62 15.46 124.8 46 28.68 22.9 51.16 50.42 72.92 77.060 38.42 46.94 59.16 68.94 83.96 68.94h371.2v-118c0-106.7-87.3-194-194-194h-636c-106.7 0-194 87.3-194 194v118h89.6z",
"M529.5 410.4c-28.24-22.64-50.52-50-72-76.28-35.5-43.48-58.76-70.12-86.3-70.12-25.060 0-49.080 26.54-85.66 70.24-45.4 54.24-102 121.76-196 121.76h-89.54v112h371.2c44 0 85.54 15.34 123.3 45.6 28.24 22.64 50.52 50 72 76.28 35.5 43.48 58.76 70.12 86.3 70.12 25.060 0 49.080-26.54 85.66-70.24 45.4-54.24 102-121.76 196-121.76h89.54v-112h-371.2c-44.060 0-85.54-15.34-123.3-45.6z",
"M934.4 712c-24.98 0-48.96 26.52-85.52 70.18-45.42 54.28-102 121.82-196 121.82-44.64 0-86.62-15.46-124.8-46-28.68-22.9-51.16-50.42-72.92-77.060-38.42-46.94-59.16-68.94-83.96-68.94h-371.2v118c0 106.7 87.3 194 194 194h636c106.7 0 194-87.3 194-194v-118h-89.6z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-plot-stacked"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 14,
"paths": [
"M635.6 524.4c6.6 4.2 13.2 8.6 19.2 13.6l120.4 96.4c29.6 23.8 83.8 23.8 113.4 0l135.2-108c0.2-4.8 0.2-9.4 0.2-14.2 0-52.2-7.8-102.4-22.2-149.8l-154.8 123.6c-58.2 46.6-140.2 59.2-211.4 38.4z",
"M248.6 634.2l120.4-96.4c58-46.4 140-59.2 211.2-38.4-6.6-4.2-13.2-8.6-19.2-13.6l-120.4-96.4c-29.6-23.8-83.8-23.8-113.4 0l-120.2 96.6c-40 32-91.4 48-143 48-21.6 0-43-2.8-63.8-8.4 0 0.6 0 1.2 0 1.6 5 3.4 10 6.8 14.6 10.6l120.4 96.4c29.8 23.8 83.8 23.8 113.4 0z",
"M120.6 378.2l120.4-96.4c80.2-64.2 205.6-64.2 285.8 0l120.4 96.4c29.6 23.8 83.8 23.8 113.4 0l181-144.8c-91.2-140.4-249.6-233.4-429.6-233.4-238.6 0-439.2 163.2-496 384.2 30.8 17.6 77.8 15.6 104.6-6z",
"M689 742l-120.4-96.4c-29.6-23.8-83.8-23.8-113.4 0l-120.2 96.4c-40 32-91.4 48-143 48-47.8 0-95.4-13.8-134.2-41.4 85.6 163.6 256.8 275.4 454.2 275.4s368.6-111.8 454.2-275.4c-80.4 57.4-199.8 55.2-277.2-6.6z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-session-v2.5"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 9,
"paths": [
"M896 0h-768c-70.4 0-128 57.6-128 128v768c0 70.4 57.6 128 128 128h768c70.4 0 128-57.6 128-128v-768c0-70.4-57.6-128-128-128zM640 448h-256v-192h256v192zM384 512h256v192h-256v-192zM320 704h-256v-192h256v192zM320 256v192h-256v-192h256zM128 960c-17 0-33-6.6-45.2-18.8s-18.8-28.2-18.8-45.2v-128h256v192h-192zM384 960v-192h256v192h-256zM960 896c0 17-6.6 33-18.8 45.2s-28.2 18.8-45.2 18.8h-192v-192h256v128zM960 704h-256v-192h256v192zM960 448h-256v-192h256v192z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-tabular"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 11,
"paths": [
"M896 0h-768c-70.6 0.2-127.8 57.4-128 128v768c0.2 70.6 57.4 127.8 128 128h768c70.6-0.2 127.8-57.4 128-128v-768c-0.2-70.6-57.4-127.8-128-128zM64 256h256v192h-256v-192zM64 512h256v192h-256v-192zM128 960c-35.2-0.2-63.8-28.8-64-64v-128h256v192h-192zM384 960v-192h256v192h-256zM960 896c-0.2 35.2-28.8 63.8-64 64h-192v-192h256v128zM960 512v192h-576v-192h64v-64h-64v-192h576v192h-64v64h64z",
"M782.4 547.4l-110.4-55.2v-172.2c0-17.6-14.4-32-32-32s-32 14.4-32 32v211.8l145.6 72.8c15.8 8 35 1.6 43-14.4 8-15.6 1.6-35-14.2-42.8v0z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-tabular-lad"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 12,
"paths": [
"M128 768v-576c-70.6 0.2-127.8 57.4-128 128v576c0.2 70.6 57.4 127.8 128 128h576c70.6-0.2 127.8-57.4 128-128h-576c-70.6-0.2-127.8-57.4-128-128z",
"M896 0h-576c-70.6 0.2-127.8 57.4-128 128v576c0.2 70.6 57.4 127.8 128 128h576c70.6-0.2 127.8-57.4 128-128v-576c-0.2-70.6-57.4-127.8-128-128zM256 192h192v128h-192v-128zM256 384h192v192h-192v-192zM320 768c-35.2-0.2-63.8-28.8-64-64v-64h192v128h-128zM512 768v-128h192v128h-192zM960 704c-0.2 35.2-28.8 63.8-64 64h-128v-128h192v64zM960 576h-448v-384h448v384z",
"M832 480c17.6 0 32-14.4 32-32 0-13.8-8.8-26-21.8-30.4l-74.2-24.6v-105c0-17.6-14.4-32-32-32s-32 14.4-32 32v151l117.8 39.2c3.4 1.2 6.8 1.8 10.2 1.8z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-tabular-lad-set"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 54,
"paths": [
"M896 0h-768c-70.606 0.215-127.785 57.394-128 127.979l-0 0.021v768c0.215 70.606 57.394 127.785 127.979 128l0.021 0h768c70.606-0.215 127.785-57.394 128-127.979l0-0.021v-768c-0.215-70.606-57.394-127.785-127.979-128l-0.021-0zM448 292l25.060 25.32c7.916 7.922 18.856 12.822 30.94 12.822s23.023-4.9 30.94-12.822l0-0 75.5-76.3c29.97-30.338 71.571-49.128 117.56-49.128s87.59 18.79 117.544 49.112l0.016 0.016 50.44 50.98v152.2c-24.111-8.83-44.678-22.255-61.542-39.342l-0.018-0.018-75.5-76.3c-7.916-7.922-18.856-12.822-30.94-12.822s-23.023 4.9-30.94 12.822l-0 0-75.5 76.3c-29.971 30.343-71.575 49.137-117.568 49.137-20.084 0-39.331-3.584-57.137-10.146l1.145 0.369v-152.2zM320 960h-192c-35.26-0.214-63.786-28.74-64-63.98l-0-0.020v-128h256v192zM320 704h-256v-192h256v192zM320 448h-256v-192h256v192zM640 960h-256v-192h256v192zM448 636.62v-174.5c1.88 1.74 3.74 3.5 5.56 5.34l75.5 76.3c7.916 7.922 18.856 12.822 30.94 12.822s23.023-4.9 30.94-12.822l0-0 75.5-76.3c29.966-30.333 71.56-49.119 117.542-49.119 43.28 0 82.673 16.643 112.128 43.879l-0.11-0.1v174.5c-1.88-1.74-3.74-3.5-5.56-5.34l-75.5-76.3c-7.916-7.922-18.856-12.822-30.94-12.822s-23.023 4.9-30.94 12.822l-0 0-75.5 76.3c-29.966 30.333-71.56 49.119-117.542 49.119-43.28 0-82.673-16.643-112.128-43.879l0.11 0.1zM960 896c-0.214 35.26-28.74 63.786-63.98 64l-0.020 0h-192v-192h256v128z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-tabular-realtime-v2"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 66,
"paths": [
"M896 0h-768c-70.606 0.215-127.785 57.394-128 127.979l-0 0.021v768c0.215 70.606 57.394 127.785 127.979 128l0.021 0h768c70.606-0.215 127.785-57.394 128-127.979l0-0.021v-768c-0.215-70.606-57.394-127.785-127.979-128l-0.021-0zM768 256v192h-192v-192zM576 512h192v192h-192zM512 704h-192v-192h192zM512 256v192h-192v-192zM64 256h192v192h-192zM64 512h192v192h-192zM128 960c-35.255-0.225-63.775-28.745-64-63.978l-0-0.022v-128h192v192zM320 960v-192h192v192zM704 960h-128v-192h192v192zM941.14 941.14c-11.511 11.644-27.483 18.856-45.139 18.86l-64.001 0v-64h128c-0.004 17.657-7.216 33.629-18.854 45.134l-0.006 0.006zM960 768h-128v-512h128z"
],
"attrs": [
{}
],
"grid": 16,
"tags": [
"icon-tabular-scrolling"
],
"isMulticolor": false,
"isMulticolor2": false,
"colorPermutations": {
"12552552551": [
{}
]
}
},
{
"id": 43,
"paths": [
"M32 631.66c14.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.225 0l0.011-0c-282.76 0-512 229.24-512 512-0 0.032-0 0.070-0 0.108 0 35.719 3.641 70.587 10.572 104.254l-0.572-3.323c9.54 10.78 17.22 16.74 22 18.62z",
"M992 392.34c-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.225-0l-0.011 0c282.76 0 512-229.24 512-512 0-0.032 0-0.070 0-0.108 0-35.719-3.641-70.587-10.572-104.254l0.572 3.323c-9.54-10.78-17.22-16.74-22-18.62z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-telemetry-v2"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 10,
"paths": [
"M832 0h-640c-105.6 0-192 86.4-192 192v640c0 105.6 86.4 192 192 192h640c105.6 0 192-86.4 192-192v-640c0-105.6-86.4-192-192-192zM128 320v-128h256v128zM256 448h384v128h-384zM896 832h-448v-128h448zM896 576h-128v-128h128zM896 320h-384v-128h384z"
],
"attrs": [
{}
],
"grid": 16,
"tags": [
"icon-timeline"
],
"isMulticolor": false,
"isMulticolor2": false,
"colorPermutations": {
"12552552551": [
{}
]
}
},
{
"id": 41,
"paths": [
"M640 146.6v-82.58c0-35.346-28.654-64-64-64v0h-128c-35.346 0-64 28.654-64 64v0 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.42zM532 596.020l-263.76 211c-57.105-59.935-92.24-141.25-92.24-230.772 0-0.080 0-0.16 0-0.24l-0 0.012c0-185.28 150.72-336 336-336 6.72 0 13.38 0.22 20 0.62v355.38z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-timer-v1.5"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 55,
"paths": [
"M454.36 476.64l86.3-86.3c9.088-8.965 21.577-14.502 35.36-14.502s26.272 5.537 35.366 14.507l-0.006-0.006 86.3 86.3c19.328 19.358 42.832 34.541 69.047 44.082l1.313 0.418v-172.14l-57.64-57.64c-34.408-34.33-81.9-55.558-134.35-55.558s-99.943 21.228-134.354 55.562l0.004-0.004-86.3 86.3c-9.088 8.965-21.577 14.502-35.36 14.502s-26.272-5.537-35.366-14.507l0.006 0.006-28.68-28.66v172.14c19.045 7.022 41.040 11.084 63.984 11.084 52.463 0 99.966-21.239 134.379-55.587l-0.003 0.003z",
"M505.64 547.36l-86.3 86.3c-9.088 8.965-21.577 14.502-35.36 14.502s-26.272-5.537-35.366-14.507l0.006 0.006-86.3-86.3c-2-2-4.2-4-6.36-6v197.36c33.664 30.721 78.65 49.537 128.031 49.537 52.44 0 99.923-21.22 134.333-55.541l-0.004 0.004 86.3-86.3c9.088-8.965 21.577-14.502 35.36-14.502s26.272 5.537 35.366 14.507l-0.006-0.006 86.3 86.3c2 2 4.2 4 6.36 6v-197.36c-33.664-30.721-78.65-49.537-128.031-49.537-52.44 0-99.923 21.22-134.333 55.541l0.004-0.004z",
"M832 0h-128v192h127.66l0.34 0.34v639.32l-0.34 0.34h-127.66v192h128c105.6 0 192-86.4 192-192v-640c0-105.6-86.4-192-192-192z",
"M320 832h-127.66l-0.34-0.34v-639.32l0.34-0.34h127.66v-192h-128c-105.6 0-192 86.4-192 192v640c0 105.6 86.4 192 192 192h128v-192z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-topic-v2.5"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 45,
"paths": [
"M0 384h128v256h-128v-256z",
"M128 128.22l0.22-0.22h191.78v-128h-192c-70.606 0.215-127.785 57.394-128 127.979l-0 0.021v192h128v-191.78z",
"M128 895.78v-191.78h-128v192c0.215 70.606 57.394 127.785 127.979 128l0.021 0h192v-128h-191.78z",
"M384 0h256v128h-256v-128z",
"M896 895.78l-0.22 0.22h-191.78v128h192c70.606-0.215 127.785-57.394 128-127.979l0-0.021v-192h-128v191.78z",
"M896 0h-192v128h191.78l0.22 0.22v191.78h128v-192c-0.215-70.606-57.394-127.785-127.979-128l-0.021-0z",
"M896 384h128v256h-128v-256z",
"M384 896h256v128h-256v-128z",
"M256 256h512v512h-512v-512z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-box-with-dashed-lines-v2"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 32,
"paths": [
"M896 0h-768c-70.4 0-128 57.6-128 128v768c0 70.4 57.6 128 128 128h768c70.4 0 128-57.6 128-128v-768c0-70.4-57.6-128-128-128zM847.8 610.4l-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"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-summary-widget"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 30,
"paths": [
"M896 110.8c0-79.8-55.4-127.4-123-105.4l-773 250.6h896v-145.2z",
"M896 320h-896v576c0 70.4 57.6 128 128 128h768c70.4 0 128-57.6 128-128v-448c0-70.4-57.6-128-128-128zM832 832h-384v-320h384v320z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-notebook"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 0,
"paths": [
"M0 896c0.227 70.601 57.399 127.773 127.978 128l0.022 0h768c70.601-0.227 127.773-57.399 128-127.978l0-0.022v-608h-512l-50.2-225.6c-7.6-34.2-42.6-62.4-77.8-62.4h-256c-70.601 0.227-127.773 57.399-128 127.978l-0 0.022zM832 768h-640v-256h640z",
"M480 0c35.2 0 70.2 28.2 77.8 62.4l36 161.6h430.2v-96c-0.227-70.601-57.399-127.773-127.978-128l-0.022-0z"
],
"attrs": [
{},
{}
],
"grid": 16,
"tags": [
"icon-tabs-view"
],
"isMulticolor": false,
"isMulticolor2": false,
"colorPermutations": {
"12552552551": [
{},
{}
]
}
},
{
"id": 1,
"paths": [
"M0 832c0 105.6 86.4 192 192 192h64v-576h-256z",
"M0 192v128h256v-320h-64c-105.6 0-192 86.4-192 192z",
"M768 1024h64c105.6 0 192-86.4 192-192v-128h-256z",
"M384 0h256v1024h-256v-1024z",
"M832 0h-64v576h256v-384c0-105.6-86.4-192-192-192z"
],
"attrs": [],
"grid": 16,
"tags": [
"icon-flexible-layout"
],
"colorPermutations": {
"12552552551": []
}
},
{
"id": 144,
"paths": [
"M152 473.8c10.8-4.2 40.8-35.6 69.8-109.4 31.8-81.4 87.2-178.4 170.2-178.4 39.4 0 76 21.8 109.2 65 28 36.4 48.8 81.6 61 113.4 29 73.8 59 105.2 69.8 109.4 10.8-4.2 40.8-35.6 69.8-109.4s74.2-155.4 141.6-174.4c-67.89-114.467-190.82-190-331.391-190-0.003 0-0.007 0-0.010 0l0.001-0c-212 0-384 172-384 384 0.017 26.829 2.71 53.018 7.827 78.329l-0.427-2.529c7.2 8 13 12.6 16.6 14z",
"M884.6 477c7.235-27.919 11.392-59.972 11.4-92.995l0-0.005c-0.017-26.829-2.71-53.018-7.827-78.329l0.427 2.529c-7.2-8-13-12.6-16.6-14-10.8 4.2-40.8 35.6-69.8 109.4-21.8 55.8-54.6 119-100 153.2z",
"M512 640l135-59c-4.485 0.614-9.689 0.977-14.972 1l-0.028 0c-39.4 0-76-21.8-109.2-65-28-36.4-48.8-81.6-61-113.4-29-73.8-59-105.2-69.8-109.4-10.8 4.2-40.8 35.6-69.8 109.4-16.4 42.2-39.2 88.4-68.8 123.2z",
"M1024 480l-512 224-512-224v320l512 224 512-224v-320z"
],
"attrs": [
{},
{},
{},
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-generator-sine"
],
"colorPermutations": {
"12552552551": [
{},
{},
{},
{}
]
}
},
{
"id": 143,
"paths": [
"M320 192h384v64h-384v-64z",
"M320 448h384v64h-384v-64z",
"M320 320h320v64h-320v-64z",
"M256 128.2h512v399.8l128-56v-344c-0.227-70.601-57.399-127.773-127.978-128l-0.022-0h-512c-70.601 0.227-127.773 57.399-128 127.978l-0 0.022v344l128 56z",
"M658.2 576h-292.4l146.2 64 146.2-64z",
"M512 704l-512-224v320l512 224 512-224v-320l-512 224z"
],
"attrs": [
{},
{},
{},
{},
{},
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-generator-event"
],
"colorPermutations": {
"12552552551": [
{},
{},
{},
{},
{},
{}
]
}
},
{
"id": 138,
"paths": [
"M512 0c-282.8 0-512 229.2-512 512 0 226.4 147 418.4 350.6 486l257.4-486v503c236.8-45 416-253 416-503 0-282.8-229.2-512-512-512zM754.8 527.8c-58.967-68.597-145.842-111.772-242.8-111.772s-183.833 43.176-242.445 111.35l-0.355 0.422-146-125c8.6-10 17.4-19.6 26.8-28.8 92.628-92.679 220.619-150.006 362-150.006s269.372 57.326 361.997 150.003l0.003 0.003c9.4 9.2 18.2 18.8 26.8 28.8z"
],
"attrs": [
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-gauge-v2"
],
"colorPermutations": {
"12552552551": [
{}
]
}
},
{
"id": 148,
"paths": [
"M768 704h-512l102.4-179.2-358.4 51.2v254c0 106.6 87.4 194 194 194h636c106.8 0 194-87.4 194-194v-62l-325.8-186.2z",
"M830 0h-636c-106.6 0-194 87.2-194 194v318l400-60.2 112-195.8 109.8 192h402.2v-254c-0.227-107.052-86.948-193.773-193.978-194l-0.022-0z",
"M1024 640v-64l-384-64 384 128z"
],
"attrs": [
{},
{},
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-spectra"
],
"colorPermutations": {
"12552552551": [
{},
{},
{}
]
}
},
{
"id": 147,
"paths": [
"M512 256l109.8 192h398.2c-31.4-252.6-247-448-508-448-282.8 0-512 229.2-512 512l400-60.2z",
"M768 704h-512l102.4-179.2-354.4 50.6c31.2 252.8 246.8 448.6 508 448.6 201.6 0 376-116.6 459.6-286l-273.4-156.2z",
"M640 512l384 128v-64l-384-64z"
],
"attrs": [
{},
{},
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-telemetry-spectra"
],
"colorPermutations": {
"12552552551": [
{},
{},
{}
]
}
},
{
"id": 146,
"paths": [
"M370.2 459.4c9.326 8.53 19.666 16.261 30.729 22.914l0.871 0.486c-11.077-19.209-17.664-42.221-17.8-66.76l-0-0.040c0-39.6 17.8-77.6 50.2-107.4 37-34 87.4-52.6 141.8-52.6 40.2 0 78.2 10.2 110.2 29.2-8.918-15.653-19.693-29.040-32.268-40.482l-0.132-0.118c-37-34-87.4-52.6-141.8-52.6s-104.8 18.6-141.8 52.6c-32.4 29.8-50.2 67.8-50.2 107.4s17.8 77.6 50.2 107.4z",
"M885.4 269.6c-40.6-154.6-192.4-269.6-373.4-269.6s-332.8 115-373.4 269.6c-86 80-138.6 187.8-138.6 306.4 0 247.4 229.2 448 512 448s512-200.6 512-448c0-118.6-52.6-226.4-138.6-306.4zM512 128c141.2 0 256 100.4 256 224s-114.8 224-256 224-256-100.4-256-224 114.8-224 256-224zM512 832c-175.4 0-318.4-127.8-320-285.4 68.8 94.8 186.4 157.4 320 157.4s251.2-62.6 320-157.4c-1.6 157.6-144.6 285.4-320 285.4z"
],
"attrs": [
{},
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-pushbutton"
],
"colorPermutations": {
"12552552551": [
{},
{}
]
}
},
{
"id": 151,
"paths": [
"M512 0c-282.76 0-512 229.24-512 512s229.24 512 512 512 512-229.24 512-512-229.24-512-512-512zM512 768l-384-256 384-256 384 256z"
],
"attrs": [
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-conditional"
],
"colorPermutations": {
"12552552551": [
{}
]
}
},
{
"id": 154,
"paths": [
"M832 0h-640c-105.6 0-192 86.4-192 192v640c0 105.6 86.4 192 192 192h640c105.6 0 192-86.4 192-192v-640c0-105.6-86.4-192-192-192zM512 768l-384-256 384-256 384 256z"
],
"attrs": [
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-condition-widget"
],
"colorPermutations": {
"12552552551": [
{}
]
}
},
{
"id": 155,
"paths": [
"M535.6 530.6c-8.4 1.6-17.2 3-26.2 4s-18.2 2.4-27.2 4c-10.196 1.861-18.808 4.010-27.21 6.633l1.61-0.433c-8.609 2.674-16.105 6.348-22.89 10.987l0.29-0.187c-6.693 4.517-12.283 10.107-16.663 16.585l-0.137 0.215c-4.6 6.8-7.4 15.6-8.8 26s-0.4 18.4 2.4 25.2c2.746 6.688 7.224 12.195 12.881 16.122l0.119 0.078c5.967 4.053 13.057 6.94 20.704 8.161l0.296 0.039c7.592 1.527 16.319 2.4 25.25 2.4 0.123 0 0.246-0 0.369-0l-0.019 0c22.2 0 39.6-3.6 52.6-11s23.2-16.2 30.2-26.4c6.273-8.873 11.271-19.191 14.426-30.285l0.174-0.715c1.853-6.809 3.601-15.41 4.855-24.169l0.145-1.231 5.2-41.6c-5.4 4.217-11.723 7.564-18.583 9.689l-0.417 0.111c-6.489 2.241-14.362 4.255-22.444 5.662l-0.956 0.138z",
"M1024 384v-192h-152l24-192h-192l-24 192h-256l24-192h-192l-24 192h-232v192h208l-32 256h-176v192h152l-24 192h192l24-192h256l-24 192h192l24-192h232v-192h-208l32-256zM702.8 411.8l-26.4 211.8c-2.231 15.809-3.537 34.122-3.6 52.727l-0 0.073c0 16.8 2.2 29.4 6.4 37.8h-113.4c-1.342-5.556-2.338-12.122-2.781-18.84l-0.019-0.36c-0.261-3.524-0.409-7.634-0.409-11.778 0-2.962 0.076-5.907 0.226-8.832l-0.017 0.41c-18.663 17.401-41.395 30.694-66.597 38.289l-1.203 0.311c-22.627 6.956-48.639 10.974-75.586 11l-0.014 0c-0.764 0.011-1.666 0.018-2.569 0.018-18.098 0-35.598-2.563-52.156-7.345l1.325 0.328c-15.991-4.512-29.851-12.090-41.545-22.122l0.145 0.122c-11.233-9.982-19.792-22.733-24.624-37.192l-0.176-0.608c-5.2-15.2-6.4-33.4-3.8-54.4s9.4-42.2 19.4-57.2c9.524-14.399 21.535-26.346 35.532-35.512l0.468-0.288c13.387-8.662 28.922-15.533 45.512-19.765l1.088-0.235c13.436-3.792 30.801-7.554 48.47-10.41l2.93-0.39c17-2.6 33.8-4.6 50.4-6.2 16.628-1.527 31.69-4.070 46.349-7.643l-2.149 0.443c13-3 23.6-7.6 31.6-13.6s12.6-15 13.6-26.4 0.8-21.8-2.4-28.8c-2.849-6.902-7.542-12.56-13.468-16.517l-0.132-0.083c-6.217-4.011-13.604-6.78-21.543-7.774l-0.257-0.026c-7.897-1.277-17-2.007-26.274-2.007-0.537 0-1.073 0.002-1.609 0.007l0.082-0.001c-22 0-40 4.6-53.8 14.2s-23 25.2-28 47.2h-111.8c4.8-26.2 14.2-48 27.8-65.4 13.475-16.978 29.89-30.968 48.574-41.377l0.826-0.423c18.192-10.038 39.297-17.806 61.619-22.175l1.381-0.225c20.488-4.162 44.053-6.563 68.171-6.6l0.029-0c21.8 0.005 43.239 1.532 64.222 4.479l-2.422-0.279c20.641 2.809 39.324 8.783 56.401 17.461l-1.001-0.461c15.909 8.108 28.858 20.031 37.967 34.601l0.233 0.399c9 15 12.2 34.8 9 59.6z"
],
"attrs": [
{},
{}
],
"grid": 16,
"tags": [
"icon-alphanumeric"
],
"isMulticolor": false,
"isMulticolor2": false,
"colorPermutations": {
"12552552551": [
{},
{}
]
}
},
{
"id": 156,
"paths": [
"M512 0c-282.8 0-512 229.2-512 512s229.2 512 512 512 512-229.2 512-512-229.2-512-512-512zM783.6 783.6c-69.581 69.675-165.757 112.776-272 112.776-212.298 0-384.4-172.102-384.4-384.4s172.102-384.4 384.4-384.4c212.298 0 384.4 172.102 384.4 384.4 0 0.008-0 0.017-0 0.025l0-0.001c0.001 0.264 0.001 0.575 0.001 0.887 0 105.769-42.964 201.503-112.391 270.703l-0.010 0.010z",
"M704 384l-128 128-192-192-192 192c0 176.731 143.269 320 320 320s320-143.269 320-320v0z"
],
"attrs": [
{},
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-image-telemetry"
],
"colorPermutations": {
"12552552551": [
{},
{}
]
}
},
{
"id": 170,
"paths": [
"M78 395.44c14-41.44 37.48-100.8 69.2-148.36 38.62-57.78 82.38-87.080 130.14-87.080s91.5 29.3 130 87.080c31.72 47.56 55.14 106.92 69.2 148.36 30.88 90.96 63.12 134.98 78 146.54 14.94-11.56 47.2-55.58 78-146.54 14-41.44 37.48-100.8 69.22-148.36q27.8-41.7 59.12-63.5c-75.7-111.377-201.81-183.58-344.783-183.58-0.034 0-0.068 0-0.103 0l0.006-0c-229.76 0-416 186.24-416 416-0 0.071-0 0.156-0 0.24 0 39.119 5.396 76.977 15.484 112.871l-0.704-2.931c16.78-21.74 40.4-63.34 63.22-130.74z",
"M754 436.56c-14 41.44-37.48 100.8-69.2 148.36-38.56 57.78-82.32 87.080-130 87.080s-91.5-29.3-130-87.080c-31.72-47.56-55.14-106.92-69.2-148.36-30.88-90.96-63.14-134.98-78-146.54-14.94 11.56-47.2 55.58-78 146.54-14.38 41.44-37.8 100.8-69.6 148.36q-27.8 41.7-59.12 63.5c75.7 111.378 201.81 183.58 344.783 183.58 0.119 0 0.237-0 0.356-0l-0.019 0c229.76 0 416-186.24 416-416 0-0.071 0-0.156 0-0.24 0-39.119-5.396-76.977-15.484-112.871l0.704 2.931c-16.78 21.74-40.4 63.34-63.22 130.74z",
"M921.56 334.62c4.098 24.449 6.44 52.617 6.44 81.332 0 0.017-0 0.034-0 0.051l0-0.003c0 0.095 0 0.208 0 0.32 0 282.593-229.087 511.68-511.68 511.68-0.113 0-0.225-0-0.338-0l0.018 0c-0.014 0-0.031 0-0.048 0-28.716 0-56.884-2.342-84.325-6.845l2.993 0.405c72.483 63.623 168.109 102.44 272.802 102.44 0.203 0 0.406-0 0.61-0l-0.031 0c229.76 0 416-186.24 416-416 0-0.172 0-0.375 0-0.578 0-104.692-38.817-200.319-102.844-273.271l0.404 0.47z"
],
"attrs": [
{},
{},
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-telemetry-aggregate"
],
"colorPermutations": {
"12552552551": [
{},
{},
{}
]
}
},
{
"id": 172,
"paths": [
"M832 0h-640c-105.6 0-192 86.4-192 192v640c0 105.6 86.4 192 192 192h640c105.6 0 192-86.4 192-192v-640c0-105.6-86.4-192-192-192zM267.64 896h-139.64v-448h139.64zM477.1 896h-139.64v-768h139.64zM686.54 896h-139.64v-320h139.64zM896 896h-139.64v-640h139.64z"
],
"attrs": [
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-bar-graph"
],
"colorPermutations": {
"12552552551": [
{}
]
}
},
{
"id": 171,
"paths": [
"M896 65.4l-128 62.6v896l128-62.6c70.4-34.42 128-120.2 128-190.6v-640c0-70.4-57.6-99.82-128-65.4z",
"M320 912l387.2 96.8v-896l-387.2-96.8v896z",
"M259.2 0.8l-3.2-0.8-128 62.6c-70.4 34.42-128 120.2-128 190.6v640c0 70.4 57.6 99.82 128 65.4l128-62.6 3.2 0.8z"
],
"attrs": [
{},
{},
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-map"
],
"colorPermutations": {
"12552552551": [
{},
{},
{}
]
}
},
{
"id": 174,
"paths": [
"M256 192v-64c0.215-70.606 57.394-127.785 127.979-128l0.021-0h256c70.606 0.215 127.785 57.394 128 127.979l0 0.021v64z",
"M832 128v128h-640v-128c-105.6 0-192 86.4-192 192v512c0 105.6 86.4 192 192 192h640c105.6 0 192-86.4 192-192v-512c0-105.6-86.4-192-192-192zM128 576v-128h256v128zM640 832h-384v-128h384zM896 832h-128v-128h128zM896 576h-384v-128h384z"
],
"attrs": [
{},
{}
],
"grid": 16,
"tags": [
"icon-plan"
],
"isMulticolor": false,
"isMulticolor2": false,
"colorPermutations": {
"12552552551": [
{},
{}
]
}
},
{
"id": 175,
"paths": [
"M896 0h-768c-70.606 0.215-127.785 57.394-128 127.979l-0 0.021v768c0.215 70.606 57.394 127.785 127.979 128l0.021 0h768c70.606-0.215 127.785-57.394 128-127.979l0-0.021v-768c-0.215-70.606-57.394-127.785-127.979-128l-0.021-0zM426.94 533.46c-8.054 15.864-24.249 26.545-42.938 26.545-7.823 0-15.209-1.871-21.734-5.191l0.273 0.126-154.54-77.28v-221.66c0-26.51 21.49-48 48-48s48 21.49 48 48v0 162.34l101.46 50.72c15.864 8.054 26.545 24.249 26.545 42.938 0 7.823-1.871 15.209-5.191 21.734l0.126-0.273zM896 896h-320v-128h320zM896 704h-320v-128h320zM896 512h-320v-128h320zM896 320h-320v-128h320z"
],
"attrs": [
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-timelist"
],
"colorPermutations": {
"12552552551": [
{}
]
}
},
{
"id": 185,
"paths": [
"M192 0c-105.6 0-192 86.4-192 192v640c0 105.6 86.4 192 192 192h640c105.6 0 192-86.4 192-192v-640c0-105.6-86.4-192-192-192zM128 352c0-53.019 42.981-96 96-96s96 42.981 96 96c0 53.019-42.981 96-96 96v0c-53.019 0-96-42.981-96-96v0zM288 832c-53.019 0-96-42.981-96-96s42.981-96 96-96c53.019 0 96 42.981 96 96v0c0 53.019-42.981 96-96 96v0zM544 640c-53.019 0-96-42.981-96-96s42.981-96 96-96c53.019 0 96 42.981 96 96v0c0 53.019-42.981 96-96 96v0zM544 320c-53.019 0-96-42.981-96-96s42.981-96 96-96c53.019 0 96 42.981 96 96v0c0 53.019-42.981 96-96 96v0zM800 832c-53.019 0-96-42.981-96-96s42.981-96 96-96c53.019 0 96 42.981 96 96v0c0 53.019-42.981 96-96 96v0z"
],
"attrs": [
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-plot-scatter"
],
"colorPermutations": {
"12552552551": [
{}
]
}
},
{
"id": 184,
"paths": [
"M896 110.72c0-79.9-55.38-127.32-123.080-105.36l-772.92 250.64h896v-145.28z",
"M896 320h-896v576c0 70.4 57.6 128 128 128h768c70.4 0 128-57.6 128-128v-448c0-70.4-57.6-128-128-128zM256 832h-128v-128h128v128zM256 640h-128v-128h128v128zM896 832h-512v-128h512v128zM896 640h-512v-128h512v128z"
],
"attrs": [
{},
{}
],
"isMulticolor": false,
"isMulticolor2": false,
"grid": 16,
"tags": [
"icon-notebook-restricted"
],
"colorPermutations": {
"12552552551": [
{},
{}
]
}
}
],
"invisible": false,
"colorThemes": [
[
[
0,
0,
0,
1
],
[
255,
255,
255,
1
]
]
],
"colorThemeIdx": 0
}
],
"preferences": {
"showGlyphs": true,
"showCodes": true,
"showQuickUse": true,
"showQuickUse2": true,
"showSVGs": true,
"fontPref": {
"prefix": "openmct-symbols-",
"metadata": {
"fontFamily": "Open-MCT-Symbols-16px",
"majorVersion": 5,
"minorVersion": 1,
"designer": "Charles Hacskaylo",
"description": "Change to 5% baseline height"
},
"metrics": {
"emSize": 1024,
"baseline": 10,
"whitespace": 0
},
"embed": false,
"noie8": true,
"ie7": false,
"resetPoint": 59904,
"showSelector": true,
"showVersion": true,
"autoHost": false,
"selector": "",
"classSelector": ".icon",
"showMetrics": true,
"showMetadata": true
},
"imagePref": {
"prefix": "icon-",
"png": true,
"useClassSelector": true,
"color": 0,
"bgColor": 16777215
},
"historySize": 50,
"gridSize": 16,
"quickUsageToken": {
"OpenMCTSymbols": "ZTA5ZDc2NTE1MTc5NWM5Njk2NGE1MmQ0NTNiYjI1MmIjMSMxNTY1Mzc3OTA4IyMj"
}
},
"uid": -1
}
================================================
FILE: src/styles/notebook.scss
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
/*********************************************** NOTEBOOK */
@mixin searchHighlight {
background: rgba($colorBtnSelectedBg, 0.4);
color: $colorBtnSelectedFg;
font-weight: bold;
}
.c-notebook {
$headerFontSize: 1.3em;
display: flex;
flex-direction: column;
flex: 1 1 auto;
overflow: hidden;
height: 100%;
/****************************** CONTENT */
&__body {
// Holds __nav and __page-view
display: flex;
flex: 1 1 auto;
overflow: hidden;
}
&__nav {
flex: 0 0 auto;
* {
overflow: hidden;
}
}
.c-sidebar {
.c-sidebar__pane {
flex-basis: 50%;
}
}
body.mobile & {
.c-list-button,
&-snapshot-menubutton {
display: none;
}
}
/****************************** CONTENT */
&__contents {
width: 70%;
}
&__page-view {
// Holds __header, __drag-area and __entries
display: flex;
flex: 1 1 auto;
flex-direction: column;
width: 100%;
> * {
flex: 0 0 auto;
+ * {
margin-top: $interiorMargin;
}
}
}
> * + * {
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;
}
&__page-locked-message,
&__drag-area {
border-radius: $controlCr;
padding: 10px;
&:before {
margin-right: 7px !important;
}
}
&__drag-area {
background: rgba($colorKey, 0.02);
border: 1px dashed rgba($colorKey, 0.7);
color: $colorKey;
cursor: pointer;
justify-content: center;
[class*='__label'] {
font-style: italic;
@include ellipsize();
}
&:hover {
background: rgba($colorBodyFg, 0.2);
}
&.drag-active,
&.is-active {
// Not currently working
border-color: $colorKey;
}
body.mobile & {
display: none;
}
}
/***** PAGE VIEW */
&__page-view {
&__header {
display: flex;
flex-wrap: wrap; // Allows wrapping in mobile portrait and narrow placements
line-height: 220%;
> * {
flex: 0 0 auto;
}
}
&__path {
flex: 1 1 auto;
margin: 0 $interiorMargin;
overflow: hidden;
white-space: nowrap;
font-size: $headerFontSize;
> * {
// Section
flex: 0 0 auto;
+ * {
// Page
display: inline;
flex: 1 1 auto;
@include ellipsize();
}
}
}
}
&__entries {
flex-direction: column;
flex: 1 1 auto;
overflow-x: hidden;
overflow-y: scroll;
@include desktop() {
padding-right: $interiorMarginSm; // Scrollbar kickoff
}
[class*='__entry'] + [class*='__entry'] {
margin-top: $interiorMarginSm;
}
.commit-button {
@include cButton();
position: absolute;
right: 5px;
bottom: 5px;
}
}
/***** SEARCH RESULTS */
&__search-results {
display: flex;
flex: 1 1 auto;
flex-direction: column;
overflow-y: auto;
> * + * {
margin-top: 5px;
}
&__header {
font-size: $headerFontSize;
flex: 0 0 auto;
}
.c-notebook__entries {
flex: 1 1 auto;
}
.c-ne {
flex-direction: column;
> * + * {
margin-top: $interiorMargin;
}
}
}
/***** RESTRICTED NOTEBOOK */
&__page-locked-message {
background: rgba($colorAlert, 0.2);
display: flex;
padding: 5px;
> * + * {
margin-left: $interiorMargin;
}
[class*='icon'] {
flex: 0 0 auto;
}
[class*='__message'] {
flex: 1 1 auto;
}
}
&__commit-entries-control {
display: flex;
justify-content: flex-end;
}
}
.is-notebook-default,
.is-status--notebook-default {
&:after {
color: $colorFilter;
font-family: symbolsfont;
font-size: 0.9em;
}
&.c-list__item:after {
content: $glyph-icon-notebook-page;
flex: 1 0 auto;
text-align: right;
}
}
/****************************** ENTRIES */
.c-ne {
// A Notebook entry
$p: $interiorMarginSm;
@include discreteItem();
cursor: pointer;
display: flex;
padding: $interiorMarginSm $interiorMarginSm $interiorMarginSm $interiorMargin;
@include hover() {
background: $colorDiscreteItemBgHov;
}
&.is-selected {
background: rgba($colorKey, 0.1);
.c-ne__text {
.c-notebook--page-unlocked & {
cursor: text;
@include hover() {
&:not(.locked) {
background: $colorInputBgHov;
}
}
}
}
}
&__text,
&__local-controls {
padding-top: $p;
padding-bottom: $p;
}
&__creator,
&__embed__time {
opacity: 0.9;
}
&__time-and-creator-and-delete,
&__text,
&__input {
padding: $p;
}
&__time-and-creator-and-delete {
display: flex;
align-items: center;
> * + * {
float: right;
margin-left: auto;
}
}
&__time-and-creator {
color: $colorA;
}
&__creator [class*='icon'] {
font-size: 0.95em;
}
&__time-and-content {
display: block;
flex: 1 1 auto;
overflow: visible;
> * + * {
margin-top: $interiorMarginSm;
}
}
&__time {
* {
white-space: nowrap;
}
}
&__content {
flex-direction: column;
flex: 1 1 auto;
margin-right: $interiorMarginSm;
margin-top: $interiorMargin;
> [class*='__'] + [class*='__'] {
margin-top: $interiorMarginSm;
}
}
&__text,
&__input {
color: $colorBodyFgEm !important;
}
&__text {
min-height: 22px; // Needed in Firefox when field is blank
white-space: normal;
.search-highlight {
@include searchHighlight();
}
// Resets and styling for markdown
h1,
h2,
h3,
h4,
h5 {
margin: unset !important;
padding: unset !important;
}
a {
text-decoration: underline;
}
em {
font-style: italic;
}
> * {
&:not(:first-child) {
margin-top: $interiorMarginSm;
}
}
p,
blockquote,
pre {
margin: 0;
&:not(:first-child) {
margin-top: $interiorMarginLg;
}
}
blockquote {
$m: 16px;
margin-left: $m;
margin-right: $m;
}
ul,
ol {
padding: 0 0 0 16px;
}
ul {
list-style: square;
}
ol {
list-style: decimal;
}
li {
list-style-type: inherit;
}
table {
width: auto;
}
th,
td {
border: 1px solid rgba($colorBodyFg, 0.7);
}
th {
background: rgba($colorBodyFg, 0.2);
}
}
&__input {
// Textarea
//@include inlineInput;
@include reactive-input();
padding-left: $p;
padding-right: $p;
overflow: unset;
margin-bottom: $interiorMargin;
min-height: 5rem;
resize: vertical;
width: 100%;
}
&__save-button {
display: flex;
justify-content: flex-end;
.c-button {
$lrP: 15px;
padding-left: $lrP;
padding-right: $lrP;
}
}
&__section-and-page {
// Shown when c-ne within search results
background: rgba($colorBodyFg, 0.1); //$colorInteriorBorder;
border-radius: $controlCr;
display: inline-flex;
align-items: center;
align-self: flex-start;
padding: $interiorMargin;
.search-highlight {
@include searchHighlight();
}
> * + * {
margin-left: $interiorMargin;
}
[class*='icon'] {
font-size: 0.8em;
opacity: 0.7;
}
}
&__remove {
float: right;
}
}
/****************************** EMBEDS */
@mixin snapThumb() {
// LEGACY: TODO: refactor when .snap-thumb in New Entry dialog is refactored
$d: 40px;
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__embeds-wrapper {
max-height: 75px;
padding-left: $interiorMargin;
padding-top: $interiorMargin;
display: flex;
}
.c-ne__embed {
@include discreteItemInnerElem();
display: inline-flex;
flex: 0 0 auto;
padding: $interiorMargin;
border: 1px solid $colorInteriorBorderNotebook;
&__info {
display: flex;
flex-direction: column;
margin-left: $interiorMargin;
a {
color: $colorKey;
}
}
&__name,
&__link {
// Holds __link and __context-available
display: flex;
align-items: center;
}
&__link {
flex: 1 1 auto;
&:before {
display: block;
font-size: 1em;
margin-right: $interiorMarginSm;
}
}
&__context-available {
font-size: 0.7em;
margin-left: $interiorMarginSm;
}
&__snap-thumb {
@include snapThumb();
}
&__actions {
margin: $interiorMarginSm;
}
&__actions-menu {
width: 55vh;
max-width: 500px;
height: 130px;
z-index: 70;
[class*='__icon'] {
filter: $colorKeyFilter;
margin: 0%;
height: 4vh;
}
[class*='__item-description'] {
min-width: 200px;
}
}
}
/****************************** 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
background: $colorBodyBg; // Prevent html2canvas from using white background
color: $colorBodyFg;
padding: $interiorMarginSm !important; // Prevents items from going right to the edge of the image
.l-sticky-headers .l-tabular-body {
overflow: auto;
}
.l-browse-bar {
display: none; // Suppress browse-bar when snapshotting from view-large overlay
+ * {
margin-top: 0 !important; // Remove margin from any following elements
}
}
* {
box-shadow: none !important; // Prevent html2canvas problems with box-shadow
}
}
.c-notebook-snapshot {
flex: 1 1 auto;
display: flex;
flex-direction: column;
> * + * {
margin-top: $interiorMargin;
}
&__header {
flex: 0 0 auto;
}
&__image {
background-size: contain;
background-repeat: no-repeat;
background-position: center center;
flex: 1 1 auto;
}
}
/****************************** SNAPSHOT CONTAINER */
.c-snapshots-h {
// Is hidden when the parent div l-shell__drawer is collapsed, so no worries about padding, etc.
display: flex;
flex-direction: column;
overflow: hidden;
padding: $interiorMargin $interiorMarginLg;
> * + * {
margin-top: $interiorMargin;
}
.l-browse-bar {
flex: 0 0 auto;
}
.c-snapshots-h__title {
display: flex;
}
.c-snapshots {
flex: 1 1 auto;
}
}
.c-snapshots {
flex-wrap: wrap;
overflow: auto;
.c-snapshot {
margin: 0 $interiorMarginSm $interiorMarginSm 0;
}
.hint {
font-size: 1.25em;
font-style: italic;
opacity: 0.7;
padding: $interiorMarginLg;
text-align: center;
}
}
/****************************** PAINTERRO OVERRIDES */
.annotation-dialog .abs.editor {
border-radius: 0;
}
#snap-annotation {
$m: 0; //$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;
$m: $interiorMarginSm;
display: flex;
align-items: center;
height: $h + ($m * 2) !important;
margin-bottom: $interiorMarginLg;
order: 1;
flex: 0 0 auto;
background-color: transparent !important;
padding: $interiorMarginSm;
> div {
display: contents;
> * + * {
margin-left: $interiorMargin !important;
}
}
.ptro-tool-controls {
display: flex;
margin-left: $interiorMarginLg !important;
> * + * {
margin-left: $interiorMargin !important;
}
}
.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
font-family: inherit;
font-size: $fs !important;
height: $h !important;
margin: 0;
position: relative;
line-height: $h !important;
}
.ptro-tool-ctl-name {
border-radius: 0;
background: none;
color: $colorBodyFg;
top: auto;
font-family: inherit;
padding: 0;
}
.ptro-color-btn {
width: $h !important;
}
.ptro-check,
.ptro-color-control,
.ptro-icon-btn,
.ptro-named-btn {
// Buttons in toolbar
border-radius: $smallCr;
box-shadow: rgba($colorBtnFg, 0.3) 0 0 0 1px;
color: $colorBtnFg !important;
padding: 1px $interiorMargin;
&:hover {
background: $colorBtnBgHov;
color: $colorBtnFgHov;
}
i {
display: contents;
font-size: $fs * 1.2;
line-height: inherit;
}
}
.ptro-color-control,
.ptro-icon-btn,
.ptro-named-btn {
// Buttons in toolbar
background-color: $colorBtnBg;
}
.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;
height: fit-content;
}
}
&.phone.portrait {
.c-notebook__head,
.c-notebook__entry,
.c-ne__time-and-content {
flex-direction: column;
> [class*='__'] + [class*='__'] {
margin-left: 0;
margin-top: $interiorMargin;
}
}
.c-notebook__entry {
[class*='text'] {
min-height: 0;
pointer-events: none;
}
}
}
}
/****************************** INDICATOR */
.c-indicator.has-new-snapshot {
$c: $colorOk;
@include pulseProp(
$animName: flashSnapshot,
$dur: 500ms,
$iter: infinite,
$prop: background,
$valStart: rgba($c, 0.4),
$valEnd: rgba($c, 0)
);
}
/****************************** RESTRICTED NOTEBOOK / SHIFT LOG */
.c-notebook--restricted {
.c-notebook__pages {
.c-list__item {
// Can display lock icon when a page is committed.
&:before {
$s: 0.8em;
color: $colorAlert;
display: block;
font-size: $s;
width: $s;
margin-right: $interiorMarginSm;
}
&:not([class*='lock']) {
&:before {
content: '';
}
}
}
}
}
.c-list__item {
&__name:focus {
text-overflow: clip;
}
}
================================================
FILE: src/styles/plotly.scss
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
/***************** PLOTLY OVERRIDES */
.plot-container.plotly {
.bglayer .bg {
fill: $colorPlotBg !important;
fill-opacity: 1 !important;
stroke: $colorInteriorBorder;
stroke-width: 1 !important;
}
.cartesianlayer {
.gridlayer {
.x,
.y {
path {
opacity: $opacityPlotHash;
stroke: $colorPlotHash !important;
}
}
}
path.xy2-y {
stroke: $colorPlotHash !important; // Using this instead of $colorPlotAreaBorder because that is an rgba
opacity: $opacityPlotHash !important;
}
}
.xtick,
.ytick,
[class^='g-'] text[class*='title'] {
// Matches
text {
fill: $colorPlotFg !important;
font-size: 12px !important;
}
}
}
================================================
FILE: src/styles/vendor/normalize-min.scss
================================================
/*! normalize.css v1.1.2 | MIT License | git.io/normalize */
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
nav,
section,
summary {
display: block;
}
audio,
canvas,
video {
display: inline-block;
*display: inline;
*zoom: 1;
}
audio:not([controls]) {
display: none;
height: 0;
}
[hidden] {
display: none;
}
html {
font-size: 100%;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
html,
button,
input,
select,
textarea {
font-family: sans-serif;
}
body {
margin: 0;
}
a:focus {
outline: thin dotted;
}
a:active,
a:hover {
outline: 0;
}
h1 {
font-size: 2em;
margin: 0.67em 0;
}
h2 {
font-size: 1.5em;
margin: 0.83em 0;
}
h3 {
font-size: 1.17em;
margin: 1em 0;
}
h4 {
font-size: 1em;
margin: 1.33em 0;
}
h5 {
font-size: 0.83em;
margin: 1.67em 0;
}
h6 {
font-size: 0.67em;
margin: 2.33em 0;
}
abbr[title] {
border-bottom: 1px dotted;
}
b,
strong {
font-weight: bold;
}
blockquote {
margin: 1em 40px;
}
dfn {
font-style: italic;
}
hr {
-moz-box-sizing: content-box;
box-sizing: content-box;
height: 0;
}
mark {
background: #ff0;
color: #000;
}
p,
pre {
margin: 1em 0;
}
code,
kbd,
pre,
samp {
font-family: monospace, serif;
_font-family: 'courier new', monospace;
font-size: 1em;
}
pre {
white-space: pre;
white-space: pre-wrap;
word-wrap: break-word;
}
q {
quotes: none;
}
q:before,
q:after {
content: '';
content: none;
}
small {
font-size: 80%;
}
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
dl,
menu,
ol,
ul {
margin: 1em 0;
}
dd {
margin: 0 0 0 40px;
}
menu,
ol,
ul {
padding: 0 0 0 40px;
}
nav ul,
nav ol {
list-style: none;
list-style-image: none;
}
img {
border: 0;
-ms-interpolation-mode: bicubic;
}
svg:not(:root) {
overflow: hidden;
}
figure {
margin: 0;
}
form {
margin: 0;
}
fieldset {
border: 1px solid silver;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
legend {
border: 0;
padding: 0;
white-space: normal;
*margin-left: -7px;
}
button,
input,
select,
textarea {
font-size: 100%;
margin: 0;
vertical-align: baseline;
*vertical-align: middle;
}
button,
input {
line-height: normal;
}
button,
select {
text-transform: none;
}
button,
html input[type='button'],
input[type='reset'],
input[type='submit'] {
-webkit-appearance: button;
cursor: pointer;
*overflow: visible;
}
button[disabled],
html input[disabled] {
cursor: default;
}
input[type='checkbox'],
input[type='radio'] {
box-sizing: border-box;
padding: 0;
*height: 13px;
*width: 13px;
}
input[type='search'] {
-webkit-appearance: textfield;
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box;
box-sizing: content-box;
}
input[type='search']::-webkit-search-cancel-button,
input[type='search']::-webkit-search-decoration {
-webkit-appearance: none;
}
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
textarea {
overflow: auto;
vertical-align: top;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
================================================
FILE: src/styles/vue-styles.scss
================================================
@import '../api/overlays/components/dialog-component.scss';
@import '../api/overlays/components/overlay-component.scss';
@import '../api/tooltips/components/tooltip-component.scss';
@import '../plugins/condition/components/conditionals.scss';
@import '../plugins/comps/components/comps.scss';
@import '../plugins/conditionWidget/components/condition-widget.scss';
@import '../plugins/condition/components/inspector/conditional-styles.scss';
@import '../plugins/displayLayout/components/box-and-line-views';
@import '../plugins/displayLayout/components/display-layout.scss';
@import '../plugins/displayLayout/components/edit-marquee.scss';
@import '../plugins/displayLayout/components/image-view.scss';
@import '../plugins/displayLayout/components/layout-frame.scss';
@import '../plugins/displayLayout/components/telemetry-view.scss';
@import '../plugins/displayLayout/components/text-view.scss';
@import '../plugins/events/components/events-view.scss';
@import '../plugins/filters/components/filters-view.scss';
@import '../plugins/filters/components/global-filters.scss';
@import '../plugins/flexibleLayout/components/flexible-layout.scss';
@import '../plugins/folderView/components/grid-view.scss';
@import '../plugins/folderView/components/list-item.scss';
@import '../plugins/imagery/components/imagery-view.scss';
@import '../plugins/imagery/components/Compass/compass.scss';
@import '../plugins/telemetryTable/components/table-row.scss';
@import '../plugins/telemetryTable/components/table-footer-indicator.scss';
@import '../plugins/tabs/components/tabs.scss';
@import '../plugins/telemetryTable/components/table.scss';
@import '../plugins/timeConductor/conductor.scss';
@import '../plugins/timeConductor/conductor-axis.scss';
@import '../plugins/timeConductor/conductor-mode-icon.scss';
@import '../plugins/timeConductor/date-picker.scss';
@import '../plugins/timeline/timeline.scss';
@import '../plugins/timelist/timelist.scss';
@import '../plugins/plan/plan';
@import '../plugins/viewDatumAction/components/metadata-list.scss';
@import '../ui/components/object-frame.scss';
@import '../ui/components/object-label.scss';
@import '../ui/components/progress-bar.scss';
@import '../ui/components/search.scss';
@import '../ui/components/swim-lane/swimlane.scss';
@import '../plugins/inspectorViews/annotations/tags/tags.scss';
@import '../ui/components/toggle-switch.scss';
@import '../ui/components/timesystem-axis.scss';
@import '../ui/components/List/list-view.scss';
@import '../plugins/inspectorViews/elements/elements.scss';
@import '../ui/inspector/inspector.scss';
@import '../plugins/inspectorViews/properties/location.scss';
@import '../ui/layout/app-logo.scss';
@import '../ui/layout/create-button.scss';
@import '../ui/layout/layout.scss';
@import '../ui/layout/mct-tree.scss';
@import '../ui/layout/search/search.scss';
@import '../ui/layout/pane.scss';
@import '../ui/layout/recent-objects.scss';
@import '../ui/layout/status-bar/indicators.scss';
@import '../ui/layout/status-bar/notification-banner.scss';
@import '../ui/preview/preview.scss';
@import '../ui/toolbar/components/toolbar-checkbox.scss';
@import './notebook.scss';
@import '../plugins/notebook/components/sidebar.scss';
@import '../plugins/gauge/gauge.scss';
@import '../plugins/faultManagement/fault-manager.scss';
@import '../plugins/operatorStatus/operator-status.scss';
@import '../plugins/userIndicator/user-indicator.scss';
@import '../plugins/inspectorDataVisualization/inspector-data-visualization.scss';
#splash-screen {
display: none;
}
================================================
FILE: src/tools/url.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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 url handling.
*/
/**
* Convert the current URL parameters to an array of strings.
* @param {import('../../openmct').OpenMCT} openmct
* @returns {Array} newTabParams
*/
export function paramsToArray(openmct, customUrlParams = {}) {
let urlParams = openmct.router.getParams();
// Merge the custom URL parameters with the current URL parameters.
Object.entries(customUrlParams).forEach((param) => {
const [key, value] = param;
urlParams[key] = value;
});
if (urlParams['tc.mode'] === 'fixed') {
delete urlParams['tc.startDelta'];
delete urlParams['tc.endDelta'];
} else if (urlParams['tc.mode'] === 'local') {
delete urlParams['tc.startBound'];
delete urlParams['tc.endBound'];
}
return Object.entries(urlParams).map(([key, value]) => `${key}=${value}`);
}
export function identifierToString(openmct, objectPath) {
return '#/browse/' + openmct.objects.getRelativePath(objectPath);
}
/**
* @param {import('../../openmct').OpenMCT} openmct
* @param {Array} objectPath
* @param {any} customUrlParams
* @returns {string} url
*/
export function objectPathToUrl(openmct, objectPath, customUrlParams = {}) {
let url = identifierToString(openmct, objectPath);
let urlParams = paramsToArray(openmct, customUrlParams);
if (urlParams.length) {
url += '?' + urlParams.join('&');
}
return url;
}
================================================
FILE: src/tools/urlSpec.js
================================================
import { createOpenMct, resetApplicationState } from '../utils/testing.js';
import { identifierToString, objectPathToUrl, paramsToArray } from './url.js';
describe('the url tool', function () {
let openmct;
let mockObjectPath;
beforeEach((done) => {
mockObjectPath = [
{
name: 'mock folder',
type: 'fake-folder',
identifier: {
key: 'mock-folder',
namespace: ''
}
},
{
name: 'mock parent folder',
type: 'fake-folder',
identifier: {
key: 'mock-parent-folder',
namespace: ''
}
}
];
openmct = createOpenMct();
openmct.on('start', () => {
openmct.router.setPath('/browse/mine?testParam1=testValue1');
done();
});
openmct.startHeadless();
});
afterEach(() => {
return resetApplicationState(openmct);
});
describe('paramsToArray', () => {
it('exists', () => {
expect(paramsToArray).toBeDefined();
});
it('can construct an array properly from query parameters', () => {
const arrayOfParams = paramsToArray(openmct);
expect(arrayOfParams.length).toBeDefined();
expect(arrayOfParams.length).toBeGreaterThan(0);
});
});
describe('identifierToString', () => {
it('exists', () => {
expect(identifierToString).toBeDefined();
});
it('can construct a String properly from a path', () => {
const constructedString = identifierToString(openmct, mockObjectPath);
expect(constructedString).toEqual('#/browse/mock-parent-folder/mock-folder');
});
});
describe('objectPathToUrl', () => {
it('exists', () => {
expect(objectPathToUrl).toBeDefined();
});
it('can construct URL properly from a path', () => {
const constructedURL = objectPathToUrl(openmct, mockObjectPath);
expect(constructedURL).toContain('#/browse/mock-parent-folder/mock-folder');
});
it('can take params to set a custom url', () => {
const customParams = {
'tc.startBound': 1669911059,
'tc.endBound': 1669911082,
'tc.mode': 'fixed'
};
const constructedURL = objectPathToUrl(openmct, mockObjectPath, customParams);
expect(constructedURL).toContain('tc.startBound=1669911059&tc.endBound=1669911082');
expect(constructedURL).toContain('tc.mode=fixed');
});
});
});
================================================
FILE: src/ui/color/Color.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
/**
* A representation of a color that allows conversions between different
* formats.
*
* @constructor
*/
/**
* A representation of a color that allows conversions between different
* formats.
*
* @constructor
*/
function Color(integerArray) {
this.integerArray = integerArray;
}
Color.fromHexString = function (hexString) {
if (!/#([0-9a-fA-F]{2}){2}/.test(hexString)) {
throw new Error(
'Invalid input "' + hexString + '". Hex string must be in CSS format e.g. #00FF00'
);
}
return new Color([
parseInt(hexString.slice(1, 3), 16),
parseInt(hexString.slice(3, 5), 16),
parseInt(hexString.slice(5, 7), 16)
]);
};
/**
* Return color as a three element array of RGB values, where each value
* is a integer in the range of 0-255.
*
* @return {number[]} the color, as integer RGB values
*/
Color.prototype.asIntegerArray = function () {
return this.integerArray.map(function (c) {
return c;
});
};
/**
* Return color as a string using #-prefixed six-digit RGB hex notation
* (e.g. #FF0000). See http://www.w3.org/TR/css3-color/#rgb-color.
*
* @return {string} the color, as a style-friendly string
*/
Color.prototype.asHexString = function () {
return (
'#' +
this.integerArray
.map(function (c) {
return (c < 16 ? '0' : '') + c.toString(16);
})
.join('')
);
};
/**
* Return color as a RGBA float array.
*
* This format is present specifically to support use with
* WebGL, which expects colors of that form.
*
* @return {number[]} the color, as floating-point RGBA values
*/
Color.prototype.asRGBAArray = function () {
return this.integerArray
.map(function (c) {
return c / 255.0;
})
.concat([1]);
};
Color.prototype.equalTo = function (otherColor) {
return this.asHexString() === otherColor.asHexString();
};
export default Color;
================================================
FILE: src/ui/color/ColorHelper.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
export const COLOR_PALETTE = [
[0x43, 0xb0, 0xff],
[0xf0, 0x60, 0x00],
[0x00, 0x70, 0x40],
[0xfb, 0x49, 0x49],
[0xc8, 0x00, 0xcf],
[0x55, 0x77, 0xf2],
[0xff, 0xa6, 0x3d],
[0x05, 0xa3, 0x00],
[0xf0, 0x00, 0x6c],
[0xac, 0x54, 0xae],
[0x23, 0xa9, 0xdb],
[0xc7, 0xbe, 0x52],
[0x5a, 0xbd, 0x56],
[0xad, 0x50, 0x72],
[0x94, 0x25, 0xea],
[0x21, 0x87, 0x82],
[0x8f, 0x6e, 0x47],
[0xf0, 0x59, 0xcb],
[0x34, 0xb6, 0x7d],
[0x7f, 0x52, 0xff],
[0x46, 0xc7, 0xc0],
[0xa1, 0x8c, 0x1c],
[0x95, 0xb1, 0x26],
[0xff, 0x84, 0x9e],
[0xb7, 0x79, 0xe7],
[0x8c, 0xc9, 0xfd],
[0xdb, 0xaa, 0x6e],
[0x93, 0xb5, 0x77],
[0xff, 0xbc, 0xda],
[0xd3, 0xb6, 0xde]
];
export function isDefaultColor(color) {
const a = color.asIntegerArray();
return COLOR_PALETTE.some(function (b) {
return a[0] === b[0] && a[1] === b[1] && a[2] === b[2];
});
}
================================================
FILE: src/ui/color/ColorPalette.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
/**
* A color palette stores a set of colors and allows for different
* methods of color allocation.
*
* @constructor
*/
import Color from './Color.js';
import { COLOR_PALETTE, isDefaultColor } from './ColorHelper.js';
/**
* A color palette stores a set of colors and allows for different
* methods of color allocation.
*
* @constructor
*/
function ColorPalette() {
const allColors = (this.allColors = COLOR_PALETTE.map(function (color) {
return new Color(color);
}));
this.colorGroups = [[], [], []];
for (let i = 0; i < allColors.length; i++) {
this.colorGroups[i % 3].push(allColors[i]);
}
this.reset();
}
/**
*
*/
ColorPalette.prototype.groups = function () {
return this.colorGroups;
};
ColorPalette.prototype.reset = function () {
this.availableColors = this.allColors.slice();
};
ColorPalette.prototype.remove = function (color) {
this.availableColors = this.availableColors.filter(function (c) {
return !c.equalTo(color);
});
};
ColorPalette.prototype.return = function (color) {
if (isDefaultColor(color)) {
this.availableColors.unshift(color);
}
};
ColorPalette.prototype.getByHexString = function (hexString) {
const color = Color.fromHexString(hexString);
return color;
};
/**
* @returns {Color} the next unused color in the palette. If all colors
* have been allocated, it will wrap around.
*/
ColorPalette.prototype.getNextColor = function () {
if (!this.availableColors.length) {
console.warn('Color Palette empty, reusing colors!');
this.reset();
}
return this.availableColors.shift();
};
export default ColorPalette;
================================================
FILE: src/ui/color/ColorSwatch.vue
================================================
================================================
FILE: src/ui/components/COMPONENTS.md
================================================
# Components
Components in this folder are intended for reuse in other parts of the
application. In order for components to be reused, they must not depend on
parent styling, and they should have minimum internal state.
================================================
FILE: src/ui/components/ContextMenuDropDown.vue
================================================
================================================
FILE: src/ui/components/List/ListHeader.vue
================================================
{{ title }}
{{ title }}
================================================
FILE: src/ui/components/List/ListItem.vue
================================================
{{ itemValue.text }}
================================================
FILE: src/ui/components/List/ListView.vue
================================================
================================================
FILE: src/ui/components/List/list-view.scss
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2025, 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.
*****************************************************************************/
/******************************* LIST VIEW */
.c-list-view {
tbody tr {
background: $colorListItemBg;
}
td {
$p: $interiorMargin;
@include ellipsize();
line-height: 120%; // Needed for icon alignment
max-width: 0;
padding-top: $p;
padding-bottom: $p;
width: 25%;
}
&--selectable {
body.desktop & {
tbody tr {
cursor: pointer;
&:hover {
background: $colorListItemBgHov;
}
}
}
}
&--sticky-header {
thead tr {
position: -webkit-sticky;
position: sticky;
top: 0;
z-index: 2;
}
}
.is-object-type-folder & {
tbody { font-size: 1.1em; }
}
}
================================================
FILE: src/ui/components/ObjectFrame.vue
================================================
================================================
FILE: src/ui/components/ObjectLabel.vue
================================================
{{ domainObject.name }}
================================================
FILE: src/ui/components/ObjectPath.vue
================================================
================================================
FILE: src/ui/components/ObjectPathString.vue
================================================
{{ orderedPathStr }}
================================================
FILE: src/ui/components/ObjectView.vue
================================================
================================================
FILE: src/ui/components/ProgressBar.vue
================================================
{{ progressPerc }}% complete.
{{ progressText }}
================================================
FILE: src/ui/components/SearchComponent.vue
================================================
================================================
FILE: src/ui/components/TimeSystemAxis.vue
================================================
{{ formattedAheadBehindDuration }}
================================================
FILE: src/ui/components/ToggleSwitch.vue
================================================
================================================
FILE: src/ui/components/ViewControl.vue
================================================
================================================
FILE: src/ui/components/object-frame.scss
================================================
.c-so-view {
display: flex;
flex-direction: column;
height: 100%;
// &__container{
// display: contents;
// }
/*************************** HEADER */
&__header {
flex: 0 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: $interiorMarginSm;
overflow: hidden;
padding: 3px;
@include smallerControlButtons; // Make button in frame headers a bit smaller
.c-object-label {
font-size: 1.05em;
min-width: 20%;
&__type-icon {
opacity: $objectLabelTypeIconOpacity;
}
&__name {
color: $objectLabelNameColorFg;
}
}
}
&:not(.c-so-view--no-frame) {
border: $browseFrameBorder;
@include browseFrameBorder;
padding: $interiorMarginObjectFrameVertical $interiorMarginObjectFrameHorizontal;
.is-editing & {
background: rgba($colorBodyBg, 0.8);
@include browseFrameBorder;
}
}
/*************************** FRAME CONTROLS */
&__frame-controls {
display: flex;
flex: 0 1 auto;
overflow: hidden;
&__btns,
&__more {
flex: 0 0 auto;
}
.--width-less-than-220 &,
.--width-less-than-600 & {
[class*='__label'] {
// button labels
display: none !important;
}
}
}
/*************************** HIDDEN FRAME */
&--no-frame {
> .c-so-view__header {
visibility: hidden;
pointer-events: none;
position: absolute;
top: 0;
right: 0;
bottom: auto;
left: 0;
z-index: 10;
.c-object-label {
visibility: hidden;
}
.c-so-view__frame-controls {
background: $frameControlsColorBg;
border-radius: $controlCr;
box-shadow: $frameControlsShdw;
padding: 1px;
pointer-events: all;
.c-icon-button {
color: $frameControlsColorFg;
&:hover {
background: rgba($frameControlsColorFg, 0.3);
}
}
&__btns {
display: none;
}
&:hover {
[class*='__btns'] {
display: block;
}
}
[class*='__label'] {
// button labels
display: none;
}
}
}
&.c-so-view--flexible-layout,
&.c-so-view--layout {
// For sub-layouts with hidden frames, completely hide the header to avoid overlapping buttons
> .c-so-view__header {
display: none;
}
}
/* HOVERS */
&:hover {
> .c-so-view__header {
visibility: visible;
}
}
&[class*='is-status'] {
border: $borderMissing;
}
}
/*************************** OBJECT VIEW */
&__object-view {
flex: 1 1 auto;
height: 0; // Chrome 73 overflow bug fix
overflow: auto;
//To accommodate independent time conductor controls
display: flex;
flex-direction: column;
.u-fills-container {
// Expand component types that fill a container
@include abs();
}
}
&.has-complex-content {
> .c-so-view__view-large {
display: block;
}
}
&.is-status--missing {
border: $borderMissing;
}
// Leave for debugging
//&.--width-less-than-600 { background: rgba(orange, 0.2) !important; }
//&.--width-less-than-220 { background: rgba(red, 0.2) !important; }
}
.l-angular-ov-wrapper {
// This element is the recipient for object styling; cannot be display: contents
overflow: hidden;
display: block;
height: 100%;
}
================================================
FILE: src/ui/components/object-label.scss
================================================
.c-object-label {
// tag and draggable element that holds type icon and name.
// Used mostly in trees and lists
@include ellipsize();
display: flex;
align-items: center;
flex: 0 1 auto;
> * + * {
margin-left: $interiorMargin;
}
&.is-status--draft {
.c-object-label__type-icon {
&:after {
color: $colorStatusAlert;
font-family: symbolsfont;
content: $glyph-icon-draft;
margin-left: $interiorMarginSm;
}
}
}
&__name {
@include ellipsize();
color: $objectLabelNameColorFg;
display: inline;
padding: 1px 0;
}
&__type-icon {
// Type icon. Must be an HTML entity to allow inclusion of alias indicator.
display: block;
flex: 0 0 auto;
font-size: 1.1em;
opacity: $objectLabelTypeIconOpacity;
}
.is-status__indicator {
position: absolute;
right: -3px;
top: -3px;
transform: scale(0.5);
}
&.is-status--missing,
&.is-status--suspect {
[class*='__type-icon'] {
&:before,
&:after {
opacity: $opacityMissing;
}
}
}
&.is-status--notebook-default {
&:after {
content: $glyph-icon-notebook-page;
display: block;
margin-left: $interiorMargin;
}
}
&.is-status--current {
&:after {
content: $glyph-icon-asterisk;
display: block;
margin-left: $interiorMargin;
font-family: symbolsfont;
}
}
}
.c-tree .c-object-label {
border-radius: $controlCr;
padding: $interiorMarginSm 1px;
> * + * {
margin-left: $interiorMarginSm;
}
&__name {
display: inline;
width: 100%;
}
&__type-icon {
color: $colorItemTreeIcon;
font-size: 1.25em;
margin-right: $interiorMarginSm;
opacity: 1;
min-width: $treeTypeIconW;
}
}
================================================
FILE: src/ui/components/progress-bar.scss
================================================
/******************************************************** PROGRESS BAR */
@keyframes progressIndeterminate {
0% {
transform:scaleX(0);
}
90% {
transform:scaleX(1);
opacity: 1;
}
100% {
opacity: 0;
}
}
.c-progress-bar {
background: $colorProgressBarHolder;
display: block;
min-height: $progressBarMinH;
overflow: hidden;
width: 100%;
&__bar {
background: $colorProgressBar;
transform-origin: left;
&.--indeterminate {
@include abs();
animation: progressIndeterminate 1.5s ease-in infinite;
}
}
}
================================================
FILE: src/ui/components/search.scss
================================================
@mixin visibleRegexButton {
opacity: 1;
padding: 1px 3px;
min-width: 24px;
}
.c-search {
@include wrappedInput();
padding-top: 2px;
padding-bottom: 2px;
&:before {
// Mag glass icon
content: $glyph-icon-magnify;
body.mobile & { // Make search icon stand out in mobile
opacity: 1;
}
}
&__use-regex {
// Button
$c: $colorBodyFg;
background: rgba($c, 0.2);
border: 1px solid rgba($c, 0.3);
color: $c;
border-radius: $controlCr;
font-weight: bold;
letter-spacing: 1px;
font-size: 0.8em;
margin-left: $interiorMarginSm;
min-width: 0;
opacity: 0;
order: 2;
overflow: hidden;
padding: 1px 0;
transform-origin: left;
@include transition($prop: min-width, $dur: $transOutTime);
width: 0;
&.is-active {
$c: $colorBtnActiveBg;
@include visibleRegexButton();
background: rgba($c, 0.3);
border-color: $c;
color: $c;
}
}
&__clear-input {
display: none;
order: 99;
padding: 1px 0;
body.mobile & {
display: block;
}
}
&.is-active {
body.mobile & { // In mobile, persist the expanded search bar instead of collapsing upon clicking away
background-color: rgba($colorHeadFg, 0.2) !important;
width: 50vw !important;
}
.c-search__use-regex {
margin-left: 0;
}
&:before {
width: 0;
body.mobile & {
width: auto;
}
}
input[type='text'],
input[type='search'] {
margin-left: 0;
}
@include hover {
.c-search__clear-input {
display: block;
}
}
}
input[type='text'],
input[type='search'] {
margin-left: $interiorMargin;
order: 3;
text-align: left;
}
@include hover {
.c-search__use-regex {
@include visibleRegexButton();
}
}
}
================================================
FILE: src/ui/components/swim-lane/SwimLane.vue
================================================
================================================
FILE: src/ui/components/swim-lane/swimlane.scss
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
@use 'sass:math';
.c-swimlane {
$handleSize: 1px;
$handleMargin: 3px;
$handleHitSize: $handleMargin * 2 + $handleSize;
display: grid;
grid-template-columns: 100px 100px 1fr;
grid-column-gap: 1px;
grid-row-gap: 1px; // Used for grid within a swimlane for Plan views
min-height: max-content; // Plan and Gantt views: must use max-content to prevent swimlane from collapsing
width: 100%;
.is-object-type-time-strip & {
min-height: $btnStdH;
}
&__time-axis {
flex: 0 0 auto;
height: 32px;
overflow: visible;
}
&.is-status--draft {
background: $colorTimeStripDraftBg;
}
&__lane-label {
background: $colorTimeStripLabelBg;
color: $colorBodyFg;
overflow: visible;
padding: $interiorMarginSm $interiorMargin;
}
&__handle {
$size: $handleSize;
$margin: $handleMargin;
z-index: 2;
@include resizeHandleStyle($size, $margin);
@include abs();
display: none; // Set to display: block in .is-editing section below
//z-index: 1000;
&.vertical {
// Vertical resizing uses c-fl-frame__resize-handle
}
&.horizontal {
// Resizes in X dimension
left: auto;
right: math.floor($handleHitSize * -0.5);
width: $handleHitSize;
}
}
&__lane-object {
background: rgba(black, 0.1);
height: 100%;
.c-plan {
display: contents;
}
@include smallerControlButtons;
}
&__lane-label-button-h {
// Holds swimlane button(s)
flex: 1 1 auto;
text-align: right;
}
.--span-cols {
grid-column: span 2;
}
// Yet more brittle special case selecting...
.is-object-type-plan,
.is-object-type-gantt-chart {
display: contents;
}
.is-editing & {
//grid-column-gap: $handleHitSize;
.c-swimlane__handle {
display: block;
}
}
}
================================================
FILE: src/ui/components/timesystem-axis.scss
================================================
@use 'sass:math';
.c-timesystem-axis {
$h: 30px;
height: $h;
&__ticks {
// Ticks SVG
$lineC: $colorInteriorBorder;
text-rendering: geometricPrecision;
width: 100%;
height: 100%;
.domain {
display: none;
}
.tick {
line {
stroke: $lineC;
}
text {
// Tick labels
fill: $colorBodyFg;
paint-order: stroke;
font-weight: bold;
}
}
}
/******************************************** LINES */
$mbMarkerW: 16px;
$mbMarkerH: math.floor(math.div($mbMarkerW, 2));
&__line-wrapper,
&__mb-line,
&__ahead-behind-line {
pointer-events: none;
position: absolute;
z-index: 10;
&.hidden {
display: none;
}
}
&__line-wrapper {
position: absolute;
left: 0;
top: 20px; // This must be kept in parity with JS constant TIME_AXIS_LINE_Y
right: 0;
overflow: hidden;
}
&__mb-line {
$c: $colorTimeRealtimeBtnBgMajor;
$w: 15px;
$wHalf: math.round(math.div($w, 2));
$transform: translateX(($wHalf - 1) * -1);
border-right: 2px dashed $c;
opacity: 0.5;
pointer-events: none;
width: 1px;
z-index: 10;
&:before,
&:after {
content: '';
display: block;
position: absolute;
width: 0;
height: 0;
transform: $transform;
border-left: $mbMarkerH solid transparent;
border-right: $mbMarkerH solid transparent;
border-top: $mbMarkerH solid $c;
}
&:after {
bottom: 0;
transform: $transform rotate(180deg);
}
}
&__ahead-behind-line {
$lineOffset: 1px;
position: absolute;
top: 0;
bottom: 0;
z-index: 0;
.c-timesystem-axis__ahead-behind-connector {
// SVG that holds "connector" polygon
height: $mbMarkerH;
position: absolute;
width: 100%;
top: 0;
}
&.--ahead {
$c: $colorABAhead;
border-right: 2px dashed $c;
left: 0;
.c-timesystem-axis__ahead-behind-connector {
right: $lineOffset;
polygon {
fill: $c;
}
&--behind {
display: none;
}
}
}
&.--behind {
$c: $colorABBehind;
border-left: 2px dashed $c;
right: 0;
.c-timesystem-axis__ahead-behind-connector {
left: $lineOffset;
polygon {
fill: $c;
}
&--ahead {
display: none;
}
}
}
}
}
.c-ta-abi {
// .c-timesystem-axis ahead-behind-indicator
$m: 0;
$p: 3px;
background: rgba($colorBodyBg, 0.7);
display: flex;
flex-direction: row;
font-size: 0.9em;
align-items: center;
gap: $interiorMarginSm;
position: absolute;
left: $m;
top: $m;
padding: $p;
white-space: nowrap;
&__icon {
order: 1;
.--behind & {
order: 2;
}
}
&__connector {
order: 2;
.--behind & {
order: 1;
}
&:before {
$s: 4.5px;
content: '';
height: 0;
width: 0;
display: block;
border: $s solid transparent;
.--ahead & {
$c: $colorABAhead;
border-top-color: $c;
border-right-color: $c;
}
.--behind & {
$c: $colorABBehind;
border-top-color: $c;
border-left-color: $c;
}
}
}
&__text {
order: 3;
&:before {
content: 'AHEAD ';
}
.--behind & {
&:before {
content: 'BEHIND ';
}
}
}
&.--ahead {
color: $colorABAhead;
}
&.--behind {
color: $colorABBehind;
}
}
================================================
FILE: src/ui/components/toggle-switch.scss
================================================
@use 'sass:math';
@mixin toggleSwitch($d: 12px, $m: 2px, $bg: $colorBtnBg) {
$br: math.div($d, 1.5);
.c-toggle-switch__slider {
background: $bg;
border-radius: $br;
height: $d + ($m * 2);
width: $d * 2 + $m * 2;
&:before {
// Knob
border-radius: floor($br * 0.8);
box-shadow: rgba(black, 0.4) 0 0 2px;
height: $d;
width: $d;
top: $m;
left: $m;
right: auto;
}
}
}
.c-toggle-switch {
cursor: pointer;
display: inline-flex;
gap: $interiorMarginSm;
align-items: center;
vertical-align: middle;
&__control,
&__label {
flex: 0 0 auto;
}
&__control {
cursor: pointer;
overflow: hidden;
display: block;
}
&__slider {
// Sits within __switch
display: inline-block;
position: relative;
&:before {
// Knob
background: $colorBtnFg; // TODO: make discrete theme constants for these colors
content: '';
display: block;
position: absolute;
transition: transform 100ms ease-in-out;
}
}
&__label {
white-space: nowrap;
}
input {
opacity: 0;
width: 0;
height: 0;
&:checked {
+ .c-toggle-switch__slider {
background: $colorKey; // TODO: make discrete theme constants for these colors
&:before {
transform: translateX(100%);
}
}
}
}
@include toggleSwitch();
}
.c-toggle-switch--mini {
@include toggleSwitch($d: 9px, $m: 0px);
}
================================================
FILE: src/ui/composables/alignmentContext.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
/* eslint-disable func-style */
import { reactive } from 'vue';
/** @type {Map} */
const alignmentMap = new Map();
/**
* Manages alignment for multiple y axes given an object path.
* This is a Vue composition API utility function.
* @param {Object} targetObject - The target to attach the event listener to.
* @param {ObjectPath} objectPath - The path of the target object.
* @param {import('../../../openmct.js').OpenMCT} openmct - The open mct API.
* @returns {Object} An object containing alignment data and methods to update, remove, and reset alignment.
*/
export function useAlignment(targetObject, objectPath, openmct) {
/**
* Get the alignment key for the given path.
* @returns {string|undefined} The alignment key if found, otherwise undefined.
*/
const getAlignmentKeyForPath = () => {
const keys = Array.from(alignmentMap.keys());
return objectPath
.map((domainObject) => openmct.objects.makeKeyString(domainObject.identifier))
.reverse()
.find((keyString) => keys.includes(keyString));
};
// Use the furthest ancestor's alignment if it exists, otherwise, use your own
let alignmentKey =
getAlignmentKeyForPath() || openmct.objects.makeKeyString(targetObject.identifier);
if (!alignmentMap.has(alignmentKey)) {
alignmentMap.set(
alignmentKey,
reactive({
leftWidth: 0,
rightWidth: 0,
multiple: false,
axes: {}
})
);
}
/**
* Reset any alignment data for the given key.
*/
const reset = () => {
const key = getAlignmentKeyForPath();
if (key && alignmentMap.has(key)) {
alignmentMap.delete(key);
}
};
/**
* Given the axes ids and widths, calculate the max left and right widths and whether or not multiple left axes exist.
*/
const processAlignment = () => {
const alignment = alignmentMap.get(alignmentKey);
const axesKeys = Object.keys(alignment.axes);
const leftAxes = axesKeys.filter((axis) => axis <= 2);
const rightAxes = axesKeys.filter((axis) => axis > 2);
alignment.leftWidth = leftAxes.reduce((sum, axis) => sum + (alignment.axes[axis] || 0), 0);
alignment.rightWidth = rightAxes.reduce((sum, axis) => sum + (alignment.axes[axis] || 0), 0);
alignment.multiple = leftAxes.length > 1;
};
/**
* @typedef {Object} RemoveParams
* @property {number} yAxisId - The ID of the y-axis to remove.
* @property {ObjectPath} [updateObjectPath] - The path of the object to update.
*/
/**
* Unregister y-axis from width calculations.
* @param {RemoveParams} param0 - The object containing yAxisId and updateObjectPath.
*/
const remove = ({ yAxisId, updateObjectPath } = {}) => {
const key = getAlignmentKeyForPath();
if (key) {
const alignment = alignmentMap.get(alignmentKey);
if (alignment.axes[yAxisId] !== undefined) {
delete alignment.axes[yAxisId];
}
processAlignment();
}
};
/**
* @typedef {Object} UpdateParams
* @property {number} width - The width of the y-axis.
* @property {number} yAxisId - The ID of the y-axis to update.
* @property {ObjectPath} [updateObjectPath] - The path of the object to update.
*/
/**
* Update widths of a y axis given the id and path. The path is used to determine which ancestor should hold the alignment.
* @param {UpdateParams} param0 - The object containing width, yAxisId, and updateObjectPath.
*/
const update = ({ width, yAxisId, updateObjectPath } = {}) => {
const key = getAlignmentKeyForPath();
if (key) {
const alignment = alignmentMap.get(alignmentKey);
if (alignment.axes[yAxisId] === undefined || width > alignment.axes[yAxisId]) {
alignment.axes[yAxisId] = width;
}
processAlignment();
}
};
return { alignment: alignmentMap.get(alignmentKey), update, remove, reset };
}
/**
* @typedef {import('../../api/objects/ObjectAPI.js').DomainObject[]} ObjectPath
*/
/**
* @typedef {Object} Alignment
* @property {number} leftWidth - The total width of the left axes.
* @property {number} rightWidth - The total width of the right axes.
* @property {boolean} multiple - Indicates if there are multiple left axes.
* @property {Object.} axes - A map of axis IDs to their widths.
*/
================================================
FILE: src/ui/composables/edit.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
import { ref } from 'vue';
import { useEventEmitter } from './event.js';
/**
* Provides a reactive `isEditing` property that reflects the current editing state of the
* application.
* @param {import('openmct').OpenMCT} openmct the Open MCT API
* @returns {{
* isEditing: import('vue').Ref
* }}
*/
export function useIsEditing(openmct) {
const isEditing = ref(openmct.editor.isEditing());
useEventEmitter(openmct.editor, 'isEditing', (_isEditing) => {
isEditing.value = _isEditing;
});
return { isEditing };
}
================================================
FILE: src/ui/composables/event.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
/* eslint-disable func-style */
import { isRef, onBeforeMount, onBeforeUnmount, onMounted, watch } from 'vue';
/**
* Registers an event listener on the specified target and automatically removes it when the
* component is unmounted.
* This is a Vue composition API utility function.
* @param {EventTarget} target - The target to attach the event listener to.
* @param {string} event - The name of the event to listen for.
* @param {Function} handler - The callback function to execute when the event is triggered.
*/
export function useEventListener(target, event, handler) {
const addListener = (el) => {
if (el) {
el.addEventListener(event, handler);
}
};
const removeListener = (el) => {
if (el) {
el.removeEventListener(event, handler);
}
};
// If target is a reactive ref, watch it for changes
if (isRef(target)) {
watch(
target,
(newTarget, oldTarget) => {
if (newTarget !== oldTarget) {
removeListener(oldTarget);
addListener(newTarget);
}
},
{ immediate: true }
);
} else {
// Otherwise use lifecycle hooks to add/remove listener
onMounted(() => addListener(target));
onBeforeUnmount(() => removeListener(target));
}
}
/**
* Registers an event listener on the specified EventEmitter instance and automatically removes it
* when the component is unmounted.
* This is a Vue composition API utility function.
* @param {import('eventemitter3').EventEmitter} emitter - The EventEmitter instance to attach the event listener to.
* @param {string} event - The name of the event to listen for.
* @param {Function} callback - The callback function to execute when the event is triggered.
*/
export function useEventEmitter(emitter, event, callback) {
onBeforeMount(() => emitter.on(event, callback));
onBeforeUnmount(() => emitter.off(event, callback));
}
================================================
FILE: src/ui/composables/resize.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
/* eslint-disable func-style */
import { onBeforeUnmount, reactive } from 'vue';
import throttle from '../../utils/throttle.js';
import { useEventListener } from './event.js';
/**
* A composable which provides a function to begin observing the size of the passed-in element,
* and a reactive object containing the width and height of the observed element. The ResizeObserver
* is automatically disconnected before the component is unmounted.
* @returns {{size: {width: number, height: number}, startObserving: (element: HTMLElement) => void}}
*/
export function useResizeObserver() {
const size = reactive({ width: 0, height: 0 });
let observer;
const startObserving = (element) => {
if (!element) {
return;
}
observer = new ResizeObserver((entries) => {
if (entries[0]) {
const { width, height } = entries[0].contentRect;
size.width = width;
size.height = height;
}
});
observer.observe(element);
};
onBeforeUnmount(() => {
if (observer) {
observer.disconnect();
}
});
return { size, startObserving };
}
/**
* A composable function which can be used to listen to and handle window resize events.
* Throttles the resize event to prevent performance issues.
* @param {number} [throttleMs=100] The number of milliseconds to throttle the resize event.
* @returns {{ windowSize: { width: number, height: number } }}
*/
export function useWindowResize(throttleMs = 100) {
const windowSize = reactive({ width: window.innerWidth, height: window.innerHeight });
const handleResize = throttle(() => {
windowSize.width = window.innerWidth;
windowSize.height = window.innerHeight;
}, throttleMs);
useEventListener(window, 'resize', handleResize);
return { windowSize };
}
================================================
FILE: src/ui/inspector/InspectorDetailsSpec.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
import { createOpenMct, resetApplicationState } from 'utils/testing';
import { nextTick } from 'vue';
const INSPECTOR_SELECTOR_PREFIX = '.c-inspect-properties__';
describe('the inspector', () => {
let appHolder;
let openmct;
let folderItem;
let selection;
beforeEach((done) => {
folderItem = {
name: 'folder',
type: 'folder',
createdBy: 'John Q',
modifiedBy: 'Public',
id: 'mock-folder-key',
identifier: {
namespace: '',
key: 'mock-folder-key'
},
notes: 'This object should have some notes',
created: 1592851063871
};
selection = [
{
context: {
item: folderItem
}
}
];
appHolder = document.createElement('div');
openmct = createOpenMct();
openmct.on('start', done);
openmct.start(appHolder);
});
afterEach(() => {
return resetApplicationState(openmct);
});
it('displays default details for selection', async () => {
openmct.selection.select(selection);
await nextTick();
const details = getDetails();
const [title, type, createdBy, modifiedBy, notes, timestamp] = details;
expect(title.name).toEqual('Title');
expect(title.value.toLowerCase()).toEqual(folderItem.name);
expect(type.name).toEqual('Type');
expect(type.value.toLowerCase()).toEqual(folderItem.type);
expect(createdBy.name).toEqual('Created By');
expect(createdBy.value).toEqual(folderItem.createdBy);
expect(modifiedBy.name).toEqual('Modified By');
expect(modifiedBy.value).toEqual(folderItem.modifiedBy);
expect(notes.value).toEqual('This object should have some notes');
expect(timestamp.name).toEqual('Created');
expect(new Date(timestamp.value).toString()).toEqual(new Date(folderItem.created).toString());
});
it('details show modified date', async () => {
const modifiedTimestamp = folderItem.created + 1000;
folderItem.modified = modifiedTimestamp;
openmct.selection.select(selection);
await nextTick();
const details = getDetails();
const timestamp = details[details.length - 1];
expect(timestamp.name).toEqual('Modified');
expect(new Date(timestamp.value).toString()).toEqual(new Date(folderItem.modified).toString());
});
it('displays custom details if provided through context', async () => {
const NAME_PREFIX = 'Custom Name';
const VALUE_PREFIX = 'Custom Value';
const indexes = [0, 1, 2, 3, 4, 5, 6, 7, 8];
const customDetails = indexes.map((index) => {
return {
name: `${NAME_PREFIX} ${index}`,
value: `${VALUE_PREFIX} ${index}`
};
});
selection[0].context.details = customDetails;
openmct.selection.select(selection);
await nextTick();
const details = getDetails();
expect(details.length).toEqual(customDetails.length);
details.forEach((detail, index) => {
expect(detail.name).toEqual(customDetails[index].name);
expect(detail.value).toEqual(customDetails[index].value);
});
});
function getDetailsElements() {
const inspectorDetailsSection = appHolder.querySelector(`${INSPECTOR_SELECTOR_PREFIX}section`);
const details = inspectorDetailsSection.querySelectorAll(`${INSPECTOR_SELECTOR_PREFIX}row`);
return details;
}
function getDetails() {
const detailsElements = getDetailsElements();
const details = Array.from(detailsElements).map((element) => {
return {
name: getText(element, 'label'),
value: getText(element, 'value')
};
});
return details;
}
function getText(element, selectorSuffix) {
return element
.querySelector(`${INSPECTOR_SELECTOR_PREFIX}${selectorSuffix}`)
.textContent.trim();
}
});
================================================
FILE: src/ui/inspector/InspectorPanel.vue
================================================
================================================
FILE: src/ui/inspector/InspectorStylesSpec.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
import mount from 'utils/mount';
import { createOpenMct, resetApplicationState } from 'utils/testing';
import { mockLocalStorage } from 'utils/testing/mockLocalStorage';
import { nextTick } from 'vue';
import StylesView from '@/plugins/condition/components/inspector/StylesView.vue';
import SavedStylesView from '../../plugins/inspectorViews/styles/SavedStylesView.vue';
import stylesManager from '../../plugins/inspectorViews/styles/StylesManager.js';
import {
mockMultiSelectionMixedStyles,
mockMultiSelectionNonSpecificStyles,
mockMultiSelectionSameStyles,
mockStyle,
mockTelemetryTableSelection
} from './InspectorStylesSpecMocks.js';
describe('the inspector', () => {
let openmct;
let selection;
let stylesViewComponent;
let savedStylesViewComponent;
mockLocalStorage();
beforeEach((done) => {
openmct = createOpenMct();
spyOn(openmct.objects, 'save').and.returnValue(Promise.resolve(true));
openmct.on('start', done);
openmct.startHeadless();
});
afterEach(() => {
return resetApplicationState(openmct);
});
it('should allow a style to be saved', () => {
selection = mockTelemetryTableSelection;
stylesViewComponent = createViewComponent(StylesView, selection, openmct);
savedStylesViewComponent = createViewComponent(SavedStylesView, selection, openmct);
expect(savedStylesViewComponent.$refs.root.savedStyles.length).toBe(0);
stylesViewComponent.$refs.root.saveStyle(mockStyle);
expect(savedStylesViewComponent.$refs.root.savedStyles.length).toBe(1);
});
it('should display all saved styles', async () => {
selection = mockTelemetryTableSelection;
stylesViewComponent = createViewComponent(StylesView, selection, openmct);
savedStylesViewComponent = createViewComponent(SavedStylesView, selection, openmct);
expect(savedStylesViewComponent.$refs.root.savedStyles.length).toBe(0);
stylesViewComponent.$refs.root.saveStyle(mockStyle);
await nextTick();
expect(savedStylesViewComponent.$refs.root.savedStyles.length).toBe(1);
});
xit('should allow a saved style to be applied', () => {
spyOn(openmct.editor, 'isEditing').and.returnValue(true);
selection = mockTelemetryTableSelection;
stylesViewComponent = createViewComponent(StylesView, selection, openmct);
savedStylesViewComponent = createViewComponent(SavedStylesView, selection, openmct);
stylesViewComponent.$refs.root.saveStyle(mockStyle);
return stylesViewComponent.$nextTick().then(() => {
const styleSelectorComponent = savedStylesViewComponent.$refs.root.$refs.root;
styleSelectorComponent.selectStyle();
return savedStylesViewComponent.$nextTick().then(() => {
const styleEditorComponent = stylesViewComponent.$refs.root.$refs.styleEditor;
const styles = styleEditorComponent.$children.filter(
(component) => component.options.value === mockStyle.color
);
expect(styles.length).toBe(3);
});
});
});
it('should allow a saved style to be deleted', () => {
selection = mockTelemetryTableSelection;
stylesViewComponent = createViewComponent(StylesView, selection, openmct);
savedStylesViewComponent = createViewComponent(SavedStylesView, selection, openmct);
stylesViewComponent.$refs.root.saveStyle(mockStyle);
expect(savedStylesViewComponent.$refs.root.savedStyles.length).toBe(1);
savedStylesViewComponent.$refs.root.deleteStyle(0);
expect(savedStylesViewComponent.$refs.root.savedStyles.length).toBe(0);
});
it('should prevent a style from being saved when the number of saved styles is at the limit', () => {
spyOn(SavedStylesView.methods, 'showLimitReachedDialog').and.callThrough();
selection = mockTelemetryTableSelection;
stylesViewComponent = createViewComponent(StylesView, selection, openmct);
savedStylesViewComponent = createViewComponent(SavedStylesView, selection, openmct);
for (let i = 1; i <= 20; i++) {
stylesViewComponent.$refs.root.saveStyle(mockStyle);
}
expect(SavedStylesView.methods.showLimitReachedDialog).not.toHaveBeenCalled();
expect(savedStylesViewComponent.$refs.root.savedStyles.length).toBe(20);
stylesViewComponent.$refs.root.saveStyle(mockStyle);
expect(SavedStylesView.methods.showLimitReachedDialog).toHaveBeenCalled();
expect(savedStylesViewComponent.$refs.root.savedStyles.length).toBe(20);
});
it('should allow styles from multi-selections to be saved', async () => {
spyOn(openmct.editor, 'isEditing').and.returnValue(true);
selection = mockMultiSelectionSameStyles;
stylesViewComponent = createViewComponent(StylesView, selection, openmct);
savedStylesViewComponent = createViewComponent(SavedStylesView, selection, openmct);
await nextTick();
const styleEditorComponent = stylesViewComponent.$refs.root.$refs.styleEditor;
const saveStyleButton = styleEditorComponent.$refs.saveStyleButton;
expect(saveStyleButton).not.toBe(undefined);
saveStyleButton.$refs.button.click();
expect(savedStylesViewComponent.$refs.root.$data.savedStyles.length).toBe(1);
});
it('should prevent mixed styles from being saved', async () => {
spyOn(openmct.editor, 'isEditing').and.returnValue(true);
selection = mockMultiSelectionMixedStyles;
stylesViewComponent = createViewComponent(StylesView, selection, openmct);
savedStylesViewComponent = createViewComponent(SavedStylesView, selection, openmct);
await nextTick();
const styleEditorComponent = stylesViewComponent.$refs.root.$refs.styleEditor;
const saveStyleButton = styleEditorComponent.$refs.saveStyleButton;
// Saving should not be enabled, thus the button ref should be undefined
expect(saveStyleButton).toBe(undefined);
});
it('should prevent non-specific styles from being saved', async () => {
spyOn(openmct.editor, 'isEditing').and.returnValue(true);
selection = mockMultiSelectionNonSpecificStyles;
stylesViewComponent = createViewComponent(StylesView, selection, openmct);
savedStylesViewComponent = createViewComponent(SavedStylesView, selection, openmct);
await nextTick();
const styleEditorComponent = stylesViewComponent.$refs.root.$refs.styleEditor;
const saveStyleButton = styleEditorComponent.$refs.saveStyleButton;
// Saving should not be enabled, thus the button ref should be undefined
expect(saveStyleButton).toBe(undefined);
});
function createViewComponent(component) {
const element = document.createElement('div');
const child = document.createElement('div');
element.appendChild(child);
const config = {
provide: {
openmct,
selection,
stylesManager
},
components: {},
template: `<${component.name} ref="root"/>`
};
config.components[component.name] = component;
const { vNode } = mount(config, {
element
});
return vNode.componentInstance;
}
});
================================================
FILE: src/ui/inspector/InspectorStylesSpecMocks.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
export const mockTelemetryTableSelection = [
[
{
context: {
item: {
configuration: {},
type: 'table',
identifier: {
key: 'mock-telemetry-table-1',
namespace: ''
}
}
}
}
]
];
export const mockStyle = {
backgroundColor: '#ff0000',
border: '#ff0000',
color: '#ff0000'
};
const mockDisplayLayoutPath = {
context: {
item: {
identifier: {
key: '6af3200d-928b-4ff0-8ed0-b94a0e6752d1',
namespace: ''
},
type: 'layout',
configuration: {
items: [
{
id: 'dd3202e5-40d0-4112-8951-00f0f1ed6a29',
type: 'text-view',
fontSize: 'default',
font: 'default'
},
{
id: 'b522d636-90b2-4f5f-9588-2a0345c30f87',
type: 'text-view',
fontSize: 'default',
font: 'default'
},
{
id: '537b7596-b442-44fe-b464-07f56bdc67c8',
type: 'text-view',
fontSize: 'default',
font: 'default'
},
{
id: '3f17162f-a822-4e39-8332-6aa39b79d022',
type: 'text-view',
fontSize: 'default',
font: 'default'
},
{
id: 'c1c5acd8-a14b-450c-8c94-ce0075dd9912',
type: 'text-view',
fontSize: '8',
font: 'monospace-bold'
}
],
objectStyles: {
'dd3202e5-40d0-4112-8951-00f0f1ed6a29': {
staticStyle: {
style: {
backgroundColor: '#0000ff',
border: '1px solid #0000ff'
}
}
},
'b522d636-90b2-4f5f-9588-2a0345c30f87': {
staticStyle: {
style: {
backgroundColor: '#ff0000',
border: '1px solid #ff0000'
}
}
},
'537b7596-b442-44fe-b464-07f56bdc67c8': {
staticStyle: {
style: {
backgroundColor: '#ff0000',
border: '1px solid #ff0000'
}
}
},
'3f17162f-a822-4e39-8332-6aa39b79d022': {
staticStyle: {
style: {
backgroundColor: '#0000ff',
border: '1px solid #0000ff',
color: '#0000ff'
}
}
},
'c1c5acd8-a14b-450c-8c94-ce0075dd9912': {
staticStyle: {
style: {
backgroundColor: '#0000ff',
border: '1px solid #0000ff',
color: '#0000ff'
}
}
}
}
}
},
supportsMultiSelect: true
}
};
const mockTextBox1Path = {
context: {
index: 0,
layoutItem: {
id: 'dd3202e5-40d0-4112-8951-00f0f1ed6a29',
type: 'text-view',
fontSize: 'default',
font: 'default'
}
}
};
const mockTextBox2Path = {
context: {
index: 1,
layoutItem: {
id: 'b522d636-90b2-4f5f-9588-2a0345c30f87',
type: 'text-view',
fontSize: 'default',
font: 'default'
}
}
};
const mockTextBox3Path = {
context: {
index: 2,
layoutItem: {
id: '537b7596-b442-44fe-b464-07f56bdc67c8',
type: 'text-view',
fontSize: 'default',
font: 'default'
}
}
};
const mockTextBox4Path = {
context: {
index: 3,
layoutItem: {
id: '3f17162f-a822-4e39-8332-6aa39b79d022',
type: 'text-view',
fontSize: 'default',
font: 'default'
}
}
};
const mockTextBox5Path = {
context: {
index: 4,
layoutItem: {
id: 'c1c5acd8-a14b-450c-8c94-ce0075dd9912',
type: 'text-view',
fontSize: '8',
font: 'default-bold'
}
}
};
export const mockMultiSelectionSameStyles = [
[mockTextBox2Path, mockDisplayLayoutPath],
[mockTextBox3Path, mockDisplayLayoutPath]
];
export const mockMultiSelectionMixedStyles = [
[mockTextBox1Path, mockDisplayLayoutPath],
[mockTextBox2Path, mockDisplayLayoutPath]
];
export const mockMultiSelectionNonSpecificStyles = [
[mockTextBox4Path, mockDisplayLayoutPath],
[mockTextBox5Path, mockDisplayLayoutPath]
];
================================================
FILE: src/ui/inspector/InspectorTabs.vue
================================================
================================================
FILE: src/ui/inspector/InspectorViews.vue
================================================
================================================
FILE: src/ui/inspector/ObjectName.vue
================================================
================================================
FILE: src/ui/inspector/inspector.scss
================================================
.c-inspector {
display: flex;
flex: 1 1 auto;
gap: $interiorMarginSm;
flex-direction: column;
overflow: hidden;
&__selected,
&__multiple-selected {
@include headerFont(1.1em);
padding: $interiorMarginSm 0;
}
&__multiple-selected {
$p: $interiorMarginLg;
background: rgba($colorWarningLo, 0.3);
border-radius: $basicCr;
display: inline-block;
font-style: italic;
padding-left: $p;
padding-right: $p;
}
&__selected {
.c-object-label__type-icon {
opacity: $objectLabelTypeIconOpacity;
}
&--non-domain-object .c-object-label__name {
font-style: italic;
}
}
&__tabs {
flex: 0 0 auto;
font-size: 11px;
text-transform: uppercase;
&.c-tabs {
flex-wrap: nowrap;
}
.c-tab {
background: $colorTabBg;
color: $colorTabFg;
padding: $interiorMargin;
&:not(.is-current) {
overflow: hidden;
&:after {
background-image: linear-gradient(90deg, transparent 0%, rgba($colorTabBg, 1) 70%);
content: '';
display: block;
position: absolute;
right: 0;
height: 100%;
width: 15px;
z-index: 1;
}
}
&.is-current {
background: $colorTabCurrentBg;
color: $colorTabCurrentFg;
padding-right: $interiorMargin + 3;
}
&__name {
overflow: hidden;
}
}
}
&__content {
flex: 1 1 auto;
display: flex;
flex-direction: column;
overflow: auto;
}
&__elements {
height: 200px; // Initial height
.tree-item {
.t-object-label {
// Elements pool is a flat list, so don't indent items.
left: 0;
}
}
}
&__saved-styles {
height: 300px;
}
.c-color-swatch {
$d: 12px;
display: block;
flex: 0 0 auto;
width: $d;
height: $d;
}
.c-tree {
// When a tree is in the Inspector, remove scrolling and right pad
overflow: visible;
padding-right: 0;
}
textarea {
// When a textarea is in the Inspector, only allow vertical resize
resize: vertical;
}
/************************************************************** LEGACY */
.l-inspector-part {
display: contents;
}
h2 {
@include propertiesHeader();
font-size: 0.65rem;
grid-column: 1 / 3;
margin: $interiorMargin 0;
&.--first {
margin-top: 0;
}
}
.c-tree .grid-properties {
margin-left: $treeItemIndent;
}
.l-multipane {
.l-pane {
min-height: 50px;
}
}
}
.c-inspect-properties,
.c-inspect-tags {
[class*='header'] {
@include propertiesHeader();
flex: 0 0 auto;
//font-size: 0.85em;
}
}
.c-inspect-properties,
.c-inspect-styles {
[class*='header'] {
@include propertiesHeader();
flex: 0 0 auto;
font-size: 11px;
text-transform: uppercase;
&:not(:first-child) {
// Allow multiple headers within a component
margin-top: $interiorMarginLg;
}
}
}
/********************************************* INSPECTOR PROPERTIES TAB */
.c-inspect-properties {
display: grid;
grid-row-gap: $interiorMarginSm;
grid-template-columns: 1fr 2fr;
align-items: start;
min-width: 150px;
[class*='span-all'],
[class*='header'] {
grid-column: 1 / 3;
}
+ .c-inspect-properties {
margin-top: $interiorMarginLg;
}
&__section,
&__row {
display: contents;
}
&__row + &__row,
&__section + &__section {
[class*='__label'],
[class*='__value'] {
// Row borders, effected via border-top on child elements of the row
border-top: 1px solid $colorInteriorBorder;
}
}
&__label,
&__value {
padding: 3px $interiorMarginLg 3px 0;
}
&__label,
&__hint {
color: $colorInspectorPropName;
&[title]:not([title='']) {
// When a cell has a title, assume it's helpful text
cursor: help;
}
}
&__value {
color: $colorInspectorPropVal;
&:first-child {
// If there is no preceding .label element, make value span columns
grid-column: 1 / 3;
}
.hint {
color: $colorBodyFg
}
}
}
.is-editing {
.c-inspect-properties {
&__value,
&__label {
line-height: 160%; // Prevent buttons/selects from overlapping when wrapping
}
}
.grid-row--pad-label-for-button {
// Add extra space at the top of the label grid cell because there's a button to the right
[class*='label'] {
line-height: 1.8em;
}
}
.c-location {
// Always make the location element span columns
grid-column: 1 / 3;
}
}
/********************************************* INSPECTOR PROPERTIES TAB */
.c-saved-style {
cursor: default;
}
/********************************************* LEGACY SUPPORT */
.c-inspector {
// FilterField.vue
.u-contents + .u-contents {
li.grid-row > * {
border-top: 1px solid $colorInspectorSectionHeaderBg;
}
}
.grid-row + .grid-row {
> * {
border-top: 1px solid $colorInspectorSectionHeaderBg;
}
}
.grid-row .label {
color: $colorInspectorPropName;
}
.grid-row .value {
color: $colorInspectorPropVal;
word-break: break-all;
&:first-child {
// If there is no preceding .label element, make value span columns
grid-column: 1 / 3;
}
}
}
================================================
FILE: src/ui/layout/AboutDialog.vue
================================================
Open MCT
Open MCT, Copyright © 2014-2024, 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 included with this distribution or
click here for third party licensing information .
Version Information
================================================
FILE: src/ui/layout/AppLayout.vue
================================================
================================================
FILE: src/ui/layout/AppLogo.vue
================================================
================================================
FILE: src/ui/layout/BrowseBar.vue
================================================
================================================
FILE: src/ui/layout/Container.js
================================================
import { v4 as uuid } from 'uuid';
class Container {
constructor(size) {
this.id = uuid();
this.frames = [];
this.size = size;
}
}
export default Container;
================================================
FILE: src/ui/layout/CreateButton.vue
================================================
================================================
FILE: src/ui/layout/Frame.js
================================================
import { v4 as uuid } from 'uuid';
class Frame {
constructor(domainObjectIdentifier, size) {
this.id = uuid();
this.domainObjectIdentifier = domainObjectIdentifier;
this.size = size;
this.noFrame = false;
}
}
export default Frame;
================================================
FILE: src/ui/layout/LayoutSpec.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
import mount from 'utils/mount';
import { createOpenMct, resetApplicationState } from 'utils/testing';
import { nextTick } from 'vue';
import Layout from './AppLayout.vue';
describe('Open MCT Layout:', () => {
let openmct;
let element;
let components;
beforeEach((done) => {
openmct = createOpenMct();
openmct.on('start', done);
// to silence error from BrowseBar.vue
spyOn(openmct.objectViews, 'get').and.callFake(() => []);
openmct.startHeadless();
});
afterEach(() => {
return resetApplicationState(openmct);
});
describe('the pane:', () => {
it('is displayed on layout load', async () => {
await createLayout();
await nextTick();
Object.entries(components).forEach(([name, component]) => {
expect(component.pane).toBeTruthy();
expect(isCollapsed(component.pane)).toBeFalse();
});
});
it('is collapsed on layout load if specified by a hide param', async () => {
setHideParams();
await createLayout();
await nextTick();
await nextTick();
Object.entries(components).forEach(([name, component]) => {
expect(isCollapsed(component.pane)).toBeTrue();
});
});
it('on toggle collapses if expanded', async () => {
await createLayout();
await nextTick();
toggleCollapseButtons();
await nextTick();
Object.entries(components).forEach(([name, component]) => {
expect(openmct.router.getSearchParam(component.param)).toEqual('true');
expect(isCollapsed(component.pane)).toBeTrue();
});
});
it('on toggle expands if collapsed', async () => {
setHideParams();
await createLayout();
await nextTick();
toggleExpandButtons();
Object.entries(components).forEach(([name, component]) => {
expect(openmct.router.getSearchParam(component.param)).not.toEqual('true');
expect(isCollapsed(component.pane)).toBeFalse();
});
});
});
// eslint-disable-next-line require-await
async function createLayout() {
const el = document.createElement('div');
const child = document.createElement('div');
el.appendChild(child);
const { vNode } = mount(
{
el,
components: {
Layout
},
provide: {
openmct
},
template: ` `
},
{
element: el
}
);
element = vNode.el;
setComponents();
}
function setComponents() {
components = {
tree: {
param: 'hideTree',
pane: element.querySelector('.l-shell__pane-tree'),
collapseButton: element.querySelector('.l-shell__pane-tree .l-pane__collapse-button'),
expandButton: element.querySelector('.l-shell__pane-tree .l-pane__expand-button')
},
inspector: {
param: 'hideInspector',
pane: element.querySelector('.l-shell__pane-inspector'),
collapseButton: element.querySelector('.l-shell__pane-inspector .l-pane__collapse-button'),
expandButton: element.querySelector('.l-shell__pane-inspector .l-pane__expand-button')
}
};
}
function isCollapsed(el) {
return el.classList.contains('l-pane--collapsed');
}
function setHideParams() {
Object.entries(components).forEach(([name, component]) => {
openmct.router.setSearchParam(component.param, true);
});
}
function toggleCollapseButtons() {
Object.entries(components).forEach(([name, component]) => {
component.collapseButton.click();
});
}
function toggleExpandButtons() {
Object.entries(components).forEach(([name, component]) => {
component.expandButton.click();
});
}
});
================================================
FILE: src/ui/layout/MctTree.vue
================================================
Searching...
No results found
================================================
FILE: src/ui/layout/MultipaneContainer.vue
================================================
================================================
FILE: src/ui/layout/PaneContainer.vue
================================================
================================================
FILE: src/ui/layout/RecentObjectsList.vue
================================================
================================================
FILE: src/ui/layout/RecentObjectsListItem.vue
================================================
{{ domainObject.name }}
================================================
FILE: src/ui/layout/ResizeHandle/ResizeHandle.vue
================================================
================================================
FILE: src/ui/layout/TreeItem.vue
================================================
================================================
FILE: src/ui/layout/ViewSwitcher.vue
================================================
================================================
FILE: src/ui/layout/app-logo.scss
================================================
.l-shell__app-logo {
cursor: pointer;
width: 70px;
height: 20px;
background: url('assets/images/logo-openmct.svg') center no-repeat;
}
================================================
FILE: src/ui/layout/create-button.scss
================================================
.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;
width: 500px;
min-height: 250px;
z-index: 70;
[class*='__icon'] {
filter: $colorKeyFilter;
}
[class*='__item-description'] {
min-width: 200px;
}
}
================================================
FILE: src/ui/layout/layout.scss
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
/******************************* SHELL */
.l-shell {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: flex;
flex-flow: column nowrap;
overflow: hidden;
&__drawer {
background: $drawerBg;
display: flex;
flex-direction: column;
height: 0;
min-height: 0;
max-height: 15%;
overflow: hidden;
@include transition(min-height);
&.is-expanded {
min-height: 100px;
}
}
&__pane-tree {
width: 40%;
[class*='collapse-button'] {
// For mobile, collapse button becomes menu icon
body.mobile & {
@include cClickIconButton();
color: $colorKey !important;
position: absolute;
right: -18px;
top: $interiorMarginSm;
transform: translateX(100%);
width: $mobileMenuIconD;
z-index: 2;
&:before {
content: $glyph-icon-menu-hamburger;
}
}
}
}
&__pane-tree,
&__pane-inspector {
.l-pane__contents {
display: flex;
flex-flow: column nowrap;
overflow-x: hidden;
}
}
&__pane-tree {
.l-pane__contents {
> * {
flex: 0 0 auto;
+ * {
margin-top: $interiorMargin;
}
}
}
}
&__pane-main {
.l-pane__header {
display: none;
}
.l-pane__contents {
.l-shell__main-.l-shell__main-view-browse-bar {
position: relative;
}
// Using `position: absolute` due to flex having repaint issues with the Time Conductor, #5247
.l-shell__time-conductor,
.l-shell__main-container {
position: absolute;
left: 0;
right: 0;
height: auto;
width: auto;
}
// Using `position: absolute` due to flex having repaint issues with the Time Conductor, #5247
.l-shell__main-container {
top: $shellMainBrowseBarH + $interiorMarginLg;
bottom: $shellTimeConductorH + $interiorMargin;
&.--has-toolbar {
.is-editing & {
top: $shellToolBarH + $shellMainBrowseBarH + $interiorMarginLg !important;
}
}
> .c-object-view {
border: 1px dashed transparent;
.is-editing & {
border-color: rgba($editUIAreaBaseColor, 0.6);
}
&[s-selected] {
.is-editing & {
border-color: $editUIAreaBaseColor
}
}
}
}
@include phonePortrait() {
.l-shell__main-container {
bottom: $shellTimeConductorMobileH + $interiorMargin;
}
}
.l-shell__time-conductor {
bottom: 0;
}
}
}
body.mobile & {
&__pane-main,
&__pane-tree {
padding: $interiorMarginLg;
}
&__pane-tree {
background: linear-gradient(90deg, transparent 70%, rgba(black, 0.2) 99%, rgba(black, 0.3));
.l-pane__header {
// Hide all buttons except the collapse button
> :not(.l-pane__collapse-button) {
display: none;
}
}
[class*='expand-button'] {
display: none;
}
&[class*='--collapsed'] {
[class*='collapse-button'] {
right: -8px;
}
}
}
}
body.phone.portrait & {
&__pane-tree {
width: calc(100% - #{$mobileMenuIconD + (2 * nth($shellPanePad, 2))});
+ .l-pane {
// Hide pane-main when this pane is expanded
opacity: 0;
pointer-events: none;
}
&[class*='--collapsed'] + .l-pane {
// Show pane-main when tree is collapsed
opacity: 1;
pointer-events: inherit;
transition: opacity 250ms ease 250ms;
}
}
}
//&__head,
&__pane-inspector {
body.mobile & {
display: none;
}
}
&__status {
flex: 0 0 auto;
display: flex;
}
/******************************* HEAD */
&__main-view-browse-bar {
flex: 0 0 auto;
margin-bottom: $interiorMargin; // Needs some additional visual separation
}
&__head {
body.mobile & {
.c-create-button,
.c-create-menu,
.c-indicator,
.c-icon-button {
// Making status area visible, but hiding some widgets for mobile.
display: none;
}
}
}
body.mobile & .l-shell__main-view-browse-bar {
margin-left: $mobileMenuIconD; // Make room for the hamburger!
.c-button[class*='__actions__edit'] {
display: none; // Hide the main view edit button when in mobile context
}
}
&__head {
align-items: center;
background: $colorHeadBg;
display: grid;
grid-template-columns: min-content 1fr 3fr repeat(4, min-content);
grid-column-gap: $interiorMargin;
padding: $interiorMargin $interiorMargin + 2;
.l-shell__head__button {
color: $colorBtnMajorBg;
flex: 0 0 auto;
font-size: 0.9em;
}
&-section {
// Subdivides elements across the head
display: flex;
}
&--expanded {
.c-indicator__label {
transition: none !important;
}
}
}
&__controls {
$brdr: 1px solid $colorInteriorBorder;
border-right: $brdr;
border-left: $brdr;
align-items: start;
$p: $interiorMarginSm;
padding-left: $p;
padding-right: $p;
}
&__create-button,
&__app-logo {
flex: 0 0 auto;
}
&__create-button {
body.mobile & {
// Fixes weird gap in mobile status area
margin-right: 0px;
}
}
&__indicators {
// Style as multiline by default
display: flex;
flex-wrap: wrap;
font-size: 11px;
min-height: 25px;
justify-content: flex-end;
.l-shell__head--indicators-single-line & {
flex-wrap: nowrap;
justify-content: flex-start; // Overflow detection doesn't work with flex-end.
overflow: hidden;
> *:first-child {
margin-left: auto; // Mimics justify-content: flex-end when in single line mode.
}
}
}
/******************************* MAIN AREA */
&__main-container {
// Wrapper for main views
display: flex;
flex-direction: column;
flex: 1 1 auto !important;
height: 100%; // Chrome 73 overflow bug fix
overflow: auto;
> * + * {
margin-top: $interiorMargin;
}
> .c-object-view {
flex: 1 1 auto;
overflow: auto;
}
}
&__tree {
// Tree component within __pane-tree
flex: 1 1 auto !important;
}
&__main {
> .l-pane {
padding: nth($shellPanePad, 1) 0;
}
}
body.desktop & {
&__main {
// Top and bottom padding in container that holds tree, __pane-main and Inspector
padding: nth($shellPanePad, 1) 0;
min-height: 0;
> .l-pane {
padding-top: 0;
padding-bottom: 0;
}
.l-pane__expand-button__label {
// Add a plus icon before the label
&:before {
font-family: symbolsfont;
content: $glyph-icon-plus;
display: inline-block;
}
}
}
&__pane-tree,
&__pane-inspector {
max-width: 70%;
}
&__pane-tree {
width: 300px;
padding-left: nth($shellPanePad, 2);
}
&__pane-inspector {
width: 200px;
padding-right: nth($shellPanePad, 2);
}
}
&__toolbar {
// Toolbar in the main shell, used by Display Layouts
$p: $interiorMargin;
background: $editUIBaseColor;
border-radius: $basicCr;
height: $p + 24px; // Need to standardize the height
justify-content: space-between;
padding: $p;
z-index: 2;
}
&__resizing {
iframe {
pointer-events: none;
}
}
}
.c-object-view {
display: block;
height: 100%;
overflow: auto;
&.is-stale {
@include isStaleHolder();
}
}
/************************** BROWSE BAR */
.l-browse-bar {
display: flex;
align-items: center;
justify-content: space-between;
> * + * {
margin-left: $interiorMarginSm;
}
&__start,
&__end,
&__actions {
display: flex;
align-items: center;
}
&__actions,
&__end {
.c-button {
&[class*='icon-']:before {
min-width: 1em;
text-align: center;
}
}
> * + * {
margin-left: $interiorMarginSm;
}
}
&__start {
flex: 1 1 auto;
min-width: 0; // Forces interior to compress when pushed on
[class*='button'] {
flex: 0 0 auto;
}
}
&__end {
flex: 0 0 auto;
}
&__nav-to-parent-button,
&__disclosure-button {
//flex: 0 0 auto;
}
&__nav-to-parent-button {
// This is an icon-button
margin-right: $interiorMargin;
.is-editing & {
display: none;
}
}
&__object-name--w,
&__object-name {
flex: 0 1 auto;
}
.c-object-label__type-icon {
opacity: $objectLabelTypeIconOpacity;
}
&__object-name--w {
@include headerFont(1.5em);
min-width: 0;
.is-status__indicator {
right: -5px !important;
top: -4px !important;
}
}
&__object-details {
opacity: 0.5;
}
}
/************************** DRAWER */
.c-drawer {
/* Sliding overlay or push element to contain things
* Designed for mobile and compact desktop scenarios
* Variations:
* --overlays: position absolute, overlays neighboring elements
* --push: position relative, pushs/collapses neighboring elements
* --align-left, align-top: opens from left or top respectively
* &.is-expanded: applied when expanded.
*/
$transProps: width, min-width, height, min-height;
min-height: 0;
min-width: 0;
overflow: hidden;
&:not(.is-expanded) {
// When collapsed, hide internal elements
> * {
display: none;
}
}
&.c-drawer--align-left {
@include transition($prop: $transProps, $dur: $transOutTime);
height: 100%;
}
&.c-drawer--align-top {
@include transition($prop: $transProps, $dur: $transOutTime);
}
&.c-drawer--overlays {
position: absolute;
z-index: 3;
&.is-expanded {
// Height and width must be set per usage
&.c-drawer--align-left {
box-shadow: rgba(black, 0.7) 3px 0 20px;
}
&.c-drawer--align-top {
box-shadow: rgba(black, 0.7) 0 3px 20px;
}
}
}
&.c-drawer--push {
position: relative;
&.is-expanded {
// Height and width must be set per usage
&.c-drawer--align-left {
box-shadow: rgba(black, 0.2) 3px 0 20px;
margin-right: $interiorMarginLg;
}
&.c-drawer--align-top {
box-shadow: rgba(black, 0.2) 0 3px 20px;
margin-bottom: $interiorMarginLg; // Not sure this is desired here
}
}
}
}
================================================
FILE: src/ui/layout/mct-tree.scss
================================================
@use 'sass:math';
.c-tree-and-search {
display: flex;
flex-direction: column;
flex: 1 1 auto;
overflow: auto;
> * + * {
margin-top: $interiorMargin;
}
&__search {
flex: 0 0 auto;
}
&__no-results {
font-style: italic;
opacity: 0.6;
}
&__tree {
flex: 1 1 auto;
height: 0; // Chrome 73 overflow bug fix
}
.c-tree {
flex: 1 1 auto;
overflow: hidden;
transition: all;
.c-tree__item-h {
width: 100%;
}
&__scrollable {
overflow: auto;
padding-right: $interiorMargin;
}
&__item--empty {
// Styling for empty tree items
// Indent should allow for c-nav view-control width and icon spacing
font-style: italic;
padding: $interiorMarginSm * 2 1px;
opacity: 0.7;
pointer-events: none;
&:before {
content: '';
display: inline-block;
width: $treeNavArrowD + $interiorMarginLg;
}
}
}
}
.c-tree,
.c-list {
@include userSelectNone();
overflow-x: hidden;
overflow-y: auto;
.icon-arrow-nav-to-parent {
visibility: hidden;
&.is-enabled {
visibility: visible;
}
}
li {
position: relative;
&[class*='__item-h'] {
display: block;
width: 100%;
}
+ li {
margin-top: 1px;
}
}
&__item {
display: flex;
align-items: center;
cursor: pointer;
line-height: 110%;
padding: $interiorMarginSm $interiorMargin;
transition: background 150ms ease;
&__type-icon {
color: $colorItemTreeIcon;
}
@include hover {
background: $colorItemTreeHoverBg;
}
&.is-navigated-object,
&.is-selected {
background: $colorItemTreeSelectedBg;
[class*='__name'] {
color: $colorItemTreeSelectedFg;
}
}
&.is-targeted-item {
$c: $colorBodyFg;
@include pulseProp(
$animName: flashTarget,
$dur: 500ms,
$iter: 8,
$prop: background,
$valStart: rgba($c, 0.4),
$valEnd: rgba($c, 0)
);
}
&.is-new {
animation-name: animTemporaryHighlight;
animation-timing-function: ease-out;
animation-duration: 3s;
animation-iteration-count: 1;
}
&.is-context-clicked {
box-shadow: inset $colorItemTreeSelectedBg 0 0 0 1px;
}
.icon-arrow-nav-to-parent {
visibility: hidden;
&.is-enabled {
visibility: visible;
}
}
}
}
.c-tree {
.c-tree {
margin-left: 15px;
}
&__item {
border-radius: $smallCr;
[class*='view-control'] {
padding: 2px 10px;
}
> * + * {
margin-left: ceil(math.div($interiorMarginSm, 2));
}
@include hover {
background: $colorItemTreeHoverBg;
}
// Object labels in trees
&__label {
flex: 1 1 auto;
}
&.is-alias {
// Object is an alias to an original.
[class*='__type-icon'] {
@include isAlias();
}
}
body.mobile & {
@include button($bg: $colorMobilePaneLeftTreeItemBg, $fg: $colorMobilePaneLeftTreeItemFg);
height: $mobileTreeItemH;
margin-bottom: $interiorMarginSm;
[class*='view-control'] {
width: ceil($mobileTreeItemH * 0.5);
}
}
&.is-navigated-object,
&.is-selected {
background: $colorItemTreeSelectedBg;
[class*='__label'],
[class*='__name'] {
color: $colorItemTreeSelectedFg;
}
[class*='__type-icon']:before {
color: $colorItemTreeSelectedIcon;
}
}
}
}
.is-editing .is-navigated-object {
a[class*='__item__label'] {
[class*='__name'] {
font-style: italic;
}
}
}
.c-tree {
&__item {
body.mobile & {
@include button($bg: $colorMobilePaneLeftTreeItemBg, $fg: $colorMobilePaneLeftTreeItemFg);
height: $mobileTreeItemH;
margin-bottom: $interiorMarginSm;
[class*='view-control'] {
width: ceil($mobileTreeItemH * 0.5);
}
}
}
.c-tree {
margin-left: $treeItemIndent;
}
}
.c-list {
&__item {
border-radius: $smallCr;
&__name {
$p: $interiorMarginSm;
@include ellipsize();
padding-bottom: $p;
padding-top: $p;
}
}
}
.c-nav {
$dimension: $treeNavArrowD;
&__up,
&__down {
flex: 0 0 auto;
height: $dimension;
width: $dimension;
visibility: hidden;
position: relative;
text-align: center;
&.is-enabled {
visibility: visible;
}
&:before {
// Nav arrow
$dimension: 9px;
$width: 3px;
border: solid $colorItemTreeVC;
border-width: 0 $width $width 0;
content: '';
display: block;
position: absolute;
left: 50%;
top: 50%;
height: $dimension;
width: $dimension;
}
@include desktop {
&:hover:before {
border-color: $colorItemTreeHoverFg;
}
}
}
&__up:before {
transform: translate(-30%, -50%) rotate(135deg);
}
&__down:before {
transform: translate(-70%, -50%) rotate(-45deg);
}
}
.c-selector {
&.c-tree-and-search {
background: rgba($colorFormLines, 0.1);
border-radius: $basicCr;
padding: 2px;
height: 100%;
min-height: 150px;
}
}
================================================
FILE: src/ui/layout/pane.scss
================================================
@use 'sass:math';
/**************************** BASE - MOBILE AND DESKTOP */
.l-multipane {
display: flex;
flex: 1 1 auto;
overflow: hidden;
&--horizontal,
> .l-pane {
flex-flow: row nowrap;
}
&--vertical,
> .l-pane {
flex-flow: column nowrap;
}
&--vertical {
height: 100%;
}
}
.l-pane {
backface-visibility: hidden;
display: flex;
min-width: 0px;
min-height: 0px;
opacity: 1;
pointer-events: inherit;
&__handle,
&__label {
// __handle and __label don't appear in mobile
display: none;
}
&__header {
display: flex;
align-items: center;
@include desktop() {
margin-bottom: $interiorMargin;
}
}
&--reacts {
// This is the pane that doesn't hold the handle
// It reacts to other panes that are able to resize
flex: 1 1 0;
}
&--collapsed {
flex-basis: 0px !important;
transition: all 350ms ease;
.l-pane__contents {
transition: opacity 150ms ease;
opacity: 0;
pointer-events: none;
overflow: hidden; // Prevents toolbar from extending into Inspector
> * {
min-width: 0 !important;
min-height: 0 !important;
}
}
}
&[class*='--horizontal'] {
padding-left: $interiorMargin;
padding-right: $interiorMargin;
&.l-pane--collapsed {
padding-left: 0 !important;
padding-right: 0 !important;
}
}
&[class*='--vertical'] {
padding-top: $interiorMargin;
padding-bottom: $interiorMargin;
min-height: 30px; // For Recents holder
&.l-pane--collapsed {
padding-top: 0 !important;
padding-bottom: 0 !important;
}
}
/************************ CONTENTS */
&__contents {
flex: 1 1 100%;
opacity: 1;
overflow-x: hidden;
overflow-y: auto;
pointer-events: inherit;
transition: opacity 250ms ease 250ms;
.l-pane__contents {
// Don't pad all nested __contents
padding: 0;
}
}
/************************************************ DESKTOP STYLES */
body.desktop & {
&__handle {
background-color: $colorSplitterBg;
display: block;
position: absolute;
@include transition(background-color, $transOutTime);
&:before {
// Extended hit area
content: '';
display: block;
position: absolute;
z-index: -1;
}
&:hover {
background-color: $colorSplitterHover;
@include transition(background-color);
}
}
&__header {
font-size: 11px;
}
&__label {
// Name of the pane
@include ellipsize();
@include userSelectNone();
color: $splitterBtnLabelColorFg;
display: block;
text-transform: uppercase;
flex: 1 1 auto;
}
[class*='expand-button'] {
display: none; // Hidden by default
background-color: $splitterCollapsedBtnColorBg;
color: $splitterCollapsedBtnColorFg;
&:before {
// '+' icon
font-size: 0.8em;
margin-bottom: $interiorMarginSm; // margin-bottom is needed for Tree and Inspector
margin-right: $interiorMarginSm; // margin-right and margin-left are needed for Recent Objects
margin-left: $interiorMarginSm;
}
&:hover {
background-color: $splitterCollapsedBtnColorBgHov;
color: $splitterCollapsedBtnColorFgHov;
@include transition(background-color);
}
}
&--resizing {
// User is dragging the handle and resizing a pane
@include userSelectNone();
+ .l-pane {
@include userSelectNone();
}
.l-pane {
&__handle {
background-color: $colorSplitterHover;
}
}
}
&[class*='--collapsed'] {
/********************************* STYLES FOR DESKTOP COLLAPSED PANES, ALL ORIENTATIONS */
$d: nth($splitterBtnD, 1);
flex-basis: $d;
min-width: $d;
min-height: $d;
> .l-pane__handle {
display: none;
}
[class*='collapse-button'] {
display: none;
}
[class*='expand-button'] {
display: block;
}
}
&[class*='--collapsed'] { // For Recent Objects Button
&.collapse-horizontal {
[class*='expand-button'] {
display: block;
position: absolute;
top: 0;
width: 100%;
border-top-right-radius: $controlCr;
border-top-left-radius: $controlCr;
}
}
[class*='expand-button'] {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
height: auto;
width: 100%;
padding: $interiorMarginSm 1px;
font-size: 11px;
[class*='label'] {
text-orientation: mixed;
text-transform: uppercase;
writing-mode: horizontal-tb;
}
}
}
&[class*='--horizontal'] {
> .l-pane__handle {
cursor: col-resize;
top: 0;
bottom: 0;
width: $splitterHandleD;
&:before {
// Extended hit area
top: 0;
right: $splitterHandleHitMargin * -1;
bottom: 0;
left: $splitterHandleHitMargin * -1;
}
}
.l-pane__collapse-button {
&:before {
content: $glyph-icon-line-horz;
}
}
&[class*='--collapsed'] {
/************************ COLLAPSED HORIZONTAL SPLITTER, EITHER DIRECTION */
[class*='__header'] {
display: none;
}
[class*='expand-button'] {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
height: auto;
width: 100%;
padding: $interiorMargin 1px;
font-size: 11px;
[class*='label'] {
text-orientation: mixed;
text-transform: uppercase;
writing-mode: vertical-lr;
}
}
}
/************************** Horizontal Splitter Before */
// Example: Inspector pane
&[class*='-before'] {
margin-left: nth($shellPanePad, 2);
padding-left: nth($shellPanePad, 2);
> .l-pane__handle {
left: 0;
transform: translateX(floor(math.div($splitterHandleD, -2))); // Center over the pane edge
}
[class*='expand-button'] {
border-top-left-radius: $controlCr;
border-bottom-left-radius: $controlCr;
}
}
/************************** Horizontal Splitter After */
// Example: Tree pane and Recent Objects
&[class*='-after'] {
margin-right: nth($shellPanePad, 2);
padding-right: nth($shellPanePad, 2);
> .l-pane__handle {
right: 0;
transform: translateX(floor(math.div($splitterHandleD, 2)));
}
[class*='expand-button'] {
border-top-right-radius: $controlCr;
border-bottom-right-radius: $controlCr;
}
}
}
&[class*='--vertical'] {
// l-pane--vertical
> .l-pane__handle {
cursor: row-resize;
left: 0;
right: 0;
height: $splitterHandleD;
&:before {
// Extended hit area
left: 0;
top: $splitterHandleHitMargin * -1;
right: 0;
bottom: $splitterHandleHitMargin * -1;
}
}
/************************** Vertical Splitter Before */
// Pane collapses downward. Used by Recent Objects in Tree
&[class*='-before'] {
$m: $interiorMarginLg;
margin-top: $m;
padding-top: $m;
> .l-pane__handle {
top: 0;
transform: translateY(floor(math.div($splitterHandleD, -1)));
}
.l-pane__collapse-button:before {
content: $glyph-icon-line-horz;
}
&.l-pane--collapsed {
> .l-pane__collapse-button {
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(math.div($splitterHandleD, 1)));
}
&:not(.l-pane--collapsed) > .l-pane__collapse-button {
&:after {
transform: scaleY(-1);
}
}
}
}
} // Ends .body.desktop
} // Ends .l-pane
================================================
FILE: src/ui/layout/recent-objects.scss
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
.c-recentobjects-listitem {
display: flex;
padding: $interiorMargin $interiorMarginSm;
align-items: flex-start;
> * + * {
margin-left: $interiorMarginSm;
}
+ .c-recentobjects-listitem {
border-top: 1px solid $colorInteriorBorder;
}
&.is-alias {
// Object is an alias to an original.
[class~='recent-object-icon'] {
@include isAlias();
&:after {
bottom: 20%;
}
}
}
&__object-path {
padding: 0 $interiorMarginSm;
}
&__target-button {
opacity: 0;
}
&__type-icon,
&__more-options-button {
flex: 0 0 auto;
}
&__type-icon {
color: $colorItemTreeIcon;
font-size: 1.25em;
// TEMP: uses object-label component, hide label part
.c-object-label__name {
display: none;
}
}
&__more-options-button {
display: none; // TEMP until enabled
}
&__body {
flex: 1 1 auto;
padding-top: 2px; // Align with type icon
> * + * {
margin-top: $interiorMarginSm;
}
.c-location {
font-size: 0.9em;
opacity: 0.8;
&__item {
> * + * {
background: blue !important;
}
}
}
}
&__tags {
display: flex;
> * + * {
margin-left: $interiorMargin;
}
}
&__title {
border-radius: $basicCr;
color: $colorItemTreeFg;
cursor: pointer;
padding: $interiorMarginSm;
&:hover {
background-color: $colorItemTreeHoverBg;
}
}
.c-tag {
font-size: 0.9em;
}
}
.c-recentobjects-listitem:hover .c-recentobjects-listitem__target-button {
opacity: 100;
}
================================================
FILE: src/ui/layout/search/AnnotationSearchResult.vue
================================================
================================================
FILE: src/ui/layout/search/GrandSearch.vue
================================================
================================================
FILE: src/ui/layout/search/GrandSearchSpec.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
import mount from 'utils/mount';
import { createOpenMct, resetApplicationState } from 'utils/testing';
import { nextTick } from 'vue';
import ExampleTagsPlugin from '../../../../example/exampleTags/plugin.js';
import DisplayLayoutPlugin from '../../../plugins/displayLayout/plugin.js';
import GrandSearch from './GrandSearch.vue';
describe('GrandSearch', () => {
let openmct;
let grandSearchComponent;
let viewContainer;
let parent;
let sharedWorkerToRestore;
let mockDomainObject;
let mockAnnotationObject;
let mockDisplayLayout;
let mockFolderObject;
let mockAnotherFolderObject;
let mockTopObject;
let originalRouterPath;
let mockNewObject;
let mockObjectProvider;
let _destroy;
beforeEach((done) => {
openmct = createOpenMct();
originalRouterPath = openmct.router.path;
openmct.router.path = [mockDisplayLayout];
openmct.editor.edit();
openmct.install(new ExampleTagsPlugin());
openmct.install(new DisplayLayoutPlugin());
const availableTags = openmct.annotation.getAvailableTags();
mockDomainObject = {
type: 'notebook',
name: 'fooRabbitNotebook',
location: 'fooNameSpace:topObject',
identifier: {
key: 'some-object',
namespace: 'fooNameSpace'
},
configuration: {
entries: {
someSection: {
somePage: [
{
id: 'fooBarEntry',
text: 'Foo Bar Text'
}
]
}
}
}
};
mockTopObject = {
type: 'root',
name: 'Top Folder',
composition: [],
identifier: {
key: 'topObject',
namespace: 'fooNameSpace'
}
};
mockAnotherFolderObject = {
type: 'folder',
name: 'Another Test Folder',
composition: [],
location: 'fooNameSpace:topObject',
identifier: {
key: 'someParent',
namespace: 'fooNameSpace'
}
};
mockFolderObject = {
type: 'folder',
name: 'Test Folder',
composition: [],
location: 'fooNameSpace:someParent',
identifier: {
key: 'someFolder',
namespace: 'fooNameSpace'
}
};
mockDisplayLayout = {
type: 'layout',
name: 'Bar Layout',
composition: [],
identifier: {
key: 'some-layout',
namespace: 'fooNameSpace'
},
configuration: {
items: [],
layoutGrid: [10, 10]
}
};
mockAnnotationObject = {
type: 'annotation',
name: 'Some Notebook Annotation',
annotationType: openmct.annotation.ANNOTATION_TYPES.NOTEBOOK,
tags: [availableTags[0].id, availableTags[1].id],
identifier: {
key: 'anAnnotationKey',
namespace: 'fooNameSpace'
},
targets: [
{
keyString: 'fooNameSpace:some-object',
entryId: 'fooBarEntry'
}
]
};
mockNewObject = {
type: 'folder',
name: 'New Apple Test Folder',
composition: [],
location: 'fooNameSpace:topObject',
identifier: {
key: 'newApple',
namespace: 'fooNameSpace'
}
};
openmct.router.isNavigatedObject = jasmine.createSpy().and.returnValue(false);
mockObjectProvider = jasmine.createSpyObj('mock object provider', [
'create',
'update',
'get',
'supportsSearchType',
'search'
]);
// eslint-disable-next-line require-await
mockObjectProvider.get = async (identifier) => {
if (identifier.key === mockDomainObject.identifier.key) {
return mockDomainObject;
} else if (identifier.key === mockAnnotationObject.identifier.key) {
return mockAnnotationObject;
} else if (identifier.key === mockDisplayLayout.identifier.key) {
return mockDisplayLayout;
} else if (identifier.key === mockFolderObject.identifier.key) {
return mockFolderObject;
} else if (identifier.key === mockAnotherFolderObject.identifier.key) {
return mockAnotherFolderObject;
} else if (identifier.key === mockTopObject.identifier.key) {
return mockTopObject;
} else if (identifier.key === mockNewObject.identifier.key) {
return mockNewObject;
} else {
return null;
}
};
mockObjectProvider.create.and.returnValue(Promise.resolve(true));
mockObjectProvider.update.and.returnValue(Promise.resolve(true));
openmct.objects.addProvider('fooNameSpace', mockObjectProvider);
const mockViewProvider = jasmine.createSpyObj('mock view provider', ['key', 'view', 'canView']);
openmct.objectViews.addProvider(mockViewProvider);
openmct.on('start', async () => {
// use local worker
sharedWorkerToRestore = openmct.objects.inMemorySearchProvider.worker;
openmct.objects.inMemorySearchProvider.worker = null;
await openmct.objects.inMemorySearchProvider.index(mockTopObject);
await openmct.objects.inMemorySearchProvider.index(mockDomainObject);
await openmct.objects.inMemorySearchProvider.index(mockDisplayLayout);
await openmct.objects.inMemorySearchProvider.index(mockFolderObject);
await openmct.objects.inMemorySearchProvider.index(mockAnnotationObject);
parent = document.createElement('div');
document.body.appendChild(parent);
viewContainer = document.createElement('div');
parent.append(viewContainer);
const { vNode, destroy } = mount(
{
components: {
GrandSearch
},
provide: {
openmct
},
template: ' '
},
{
element: viewContainer
}
);
grandSearchComponent = vNode.componentInstance;
_destroy = destroy;
await nextTick();
done();
});
openmct.startHeadless();
});
afterEach(() => {
openmct.objects.inMemorySearchProvider.worker = sharedWorkerToRestore;
openmct.router.path = originalRouterPath;
_destroy();
return resetApplicationState(openmct);
});
it('should render an object search result', async () => {
await grandSearchComponent.$refs.root.searchEverything('foo');
await nextTick();
const searchResults = document.querySelectorAll(
'[aria-label="fooRabbitNotebook notebook result"]'
);
expect(searchResults.length).toBe(1);
expect(searchResults[0].innerText).toContain('Rabbit');
});
it('should render an object search result if new object added', async () => {
delete mockObjectProvider.supportsSearchType;
delete mockObjectProvider.search;
const composition = openmct.composition.get(mockFolderObject);
composition.add(mockNewObject);
// after adding, need to wait a beat for the folder to be indexed
await nextTick();
await grandSearchComponent.$refs.root.searchEverything('apple');
await nextTick();
const searchResults = document.querySelectorAll(
'[aria-label="New Apple Test Folder folder result"]'
);
expect(searchResults.length).toBe(1);
expect(searchResults[0].innerText).toContain('Apple');
});
it('should not use InMemorySearch provider if object provider provides search', async () => {
// eslint-disable-next-line require-await
mockObjectProvider.search.and.callFake((query, abortSignal, searchType) => {
if (searchType === openmct.objects.SEARCH_TYPES.OBJECTS) {
return [mockNewObject];
} else {
return [];
}
});
mockObjectProvider.supportsSearchType.and.callFake((someType) => {
return true;
});
const composition = openmct.composition.get(mockFolderObject);
composition.add(mockNewObject);
await grandSearchComponent.$refs.root.searchEverything('apple');
await nextTick();
const searchResults = document.querySelectorAll(
'[aria-label="New Apple Test Folder folder result"]'
);
// This will be of length 2 (doubles) if we're incorrectly searching with InMemorySearchProvider as well
expect(searchResults.length).toBe(1);
expect(searchResults[0].innerText).toContain('Apple');
});
it('should render an annotation search result', async () => {
await grandSearchComponent.$refs.root.searchEverything('S');
await nextTick();
const annotationResults = document.querySelectorAll('[aria-label="Annotation Search Result"]');
expect(annotationResults.length).toBe(1);
expect(annotationResults[0].innerText).toContain('Driving');
});
it('should render no annotation search results if no match', async () => {
await grandSearchComponent.$refs.root.searchEverything('Qbert');
await nextTick();
const annotationResults = document.querySelectorAll('[aria-label="Annotation Search Result"]');
expect(annotationResults.length).toBe(0);
});
it('should preview object search results in edit mode if object clicked', async () => {
await grandSearchComponent.$refs.root.searchEverything('Folder');
grandSearchComponent.$refs.root.openmct.router.path = [mockDisplayLayout];
await nextTick();
const folderResult = document.querySelector('[name="Test Folder"]');
expect(folderResult).not.toBeNull();
folderResult.click();
const previewWindow = document.querySelector('.js-preview-window');
expect(previewWindow.innerText).toContain('Snapshot');
});
it('should preview annotation search results in edit mode if annotation clicked', async () => {
await grandSearchComponent.$refs.root.searchEverything('Dri');
grandSearchComponent.$refs.root.openmct.router.path = [mockDisplayLayout];
await nextTick();
const annotationResults = document.querySelectorAll('[aria-label="Annotation Search Result"]');
expect(annotationResults.length).toBe(1);
expect(annotationResults[0].innerText).toContain('Driving');
annotationResults[0].click();
const previewWindow = document.querySelector('.js-preview-window');
expect(previewWindow.innerText).toContain('Snapshot');
});
});
================================================
FILE: src/ui/layout/search/ObjectSearchResult.vue
================================================
================================================
FILE: src/ui/layout/search/SearchResultsDropDown.vue
================================================
================================================
FILE: src/ui/layout/search/search.scss
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
/******************************* EXPANDED SEARCH 2022 */
.c-gsearch {
.l-shell__head & {
// Search input in the shell head
.c-search {
background: rgba($colorHeadFg, 0.2);
box-shadow: none;
flex: 1 1 auto;
body.mobile & {
@include phonePortrait() {
// This logic of expanding the search input upon click only happens in mobile portrait mode
background: $colorHeadBg;
width: 15%;
&:hover {
// When clicked, expand the search bar
background-color: rgba($colorHeadFg, 0.2);
width: 50vw;
transition: width 120ms;
}
}
}
}
}
&__dropdown {
@include menuOuter();
display: flex;
flex-direction: column;
padding: $interiorMarginLg;
min-width: 500px;
max-height: 500px;
top: $formInputH;
z-index: 60;
body.mobile & {
// Makes search in mobile look less like an overlay, and more fullscreen.
position: absolute;
top: 25px;
left: -12px;
height: calc(100vh - 22px);
min-width: 100vw;
max-height: none;
border-radius: 0px;
box-shadow: none;
}
}
&__results,
&__results-section {
flex: 1 1 auto;
}
&__results {
// Holds n __results-sections
padding-right: $interiorMargin; // Fend off scrollbar
overflow-y: auto;
> * + * {
margin-top: $interiorMarginLg;
}
body.mobile & {
// Add a margin to results so we have room for the close button
margin-right: 20px;
}
}
&__results-section {
> * + * {
margin-top: $interiorMarginSm;
}
}
&__results-section-title {
@include propertiesHeader();
}
&__result-pane-msg {
> * + * {
margin-top: $interiorMargin;
}
}
&__results-close {
// Close button that appears for mobile only
display: none;
body.mobile & {
display: block;
}
}
body.mobile & {
width: 50vw;
@include phonePortrait() {
// This logic only appears for a mobile portrait mode
width: 100%;
}
}
}
.c-gsearch-result {
display: flex;
padding: $interiorMarginSm 0;
> * + * {
margin-left: $interiorMargin;
}
+ .c-gsearch-result {
border-top: 1px solid $colorInteriorBorder;
}
&__type-icon,
&__more-options-button {
flex: 0 0 auto;
}
&__type-icon {
color: $colorItemTreeIcon;
font-size: 1.5em;
// TEMP: uses object-label component, hide label part
.c-object-label__name {
display: none;
}
}
&__more-options-button {
display: none; // TEMP until enabled
}
&__body {
flex: 1 1 auto;
> * + * {
margin-top: $interiorMarginSm;
}
.c-location {
color: $colorBodyFg;
font-size: 0.9em;
opacity: 0.8;
}
}
&__tags {
display: flex;
> * + * {
margin-left: $interiorMargin;
}
}
&__title {
border-radius: $basicCr;
color: pullForward($colorBodyFg, 30%);
cursor: pointer;
font-size: 1.15em;
padding: 3px $interiorMarginSm;
&:hover {
background-color: $colorItemTreeHoverBg;
}
}
.c-tag {
font-size: 0.9em;
}
}
================================================
FILE: src/ui/layout/status-bar/NotificationBanner.vue
================================================
{{ activeModel.message }}
{{ getLinkProps.text }}
================================================
FILE: src/ui/layout/status-bar/StatusIndicators.vue
================================================
================================================
FILE: src/ui/layout/status-bar/indicators.scss
================================================
.c-indicator {
@include cControl();
@include cClickIconButtonLayout();
border-radius: $controlCr;
overflow: visible;
position: relative;
text-transform: uppercase;
button {
text-transform: uppercase;
}
&.no-minify {
// For items that cannot be minified
display: flex;
flex-flow: row nowrap;
align-items: center;
> *,
&:before {
flex: 1 1 auto;
}
&:before {
margin-right: $interiorMarginSm;
}
}
&:not(.no-minify) {
&:before {
margin-right: 0 !important;
}
}
}
.c-indicator__label {
// Label element. Appears as a hover bubble element when Indicators are minified;
// Appears as an inline element when not.
display: inline-block;
transition: none;
white-space: nowrap;
a,
button,
.s-button,
.c-button {
// Make in label look like buttons
@include transition(background-color);
background-color: transparent;
border: 1px solid rgba($colorIndicatorMenuFg, 0.8);
border-radius: $controlCr;
box-sizing: border-box;
color: inherit;
font-size: inherit;
height: auto;
line-height: normal;
padding: 0 2px;
@include hover {
background-color: rgba($colorIndicatorMenuFg, 0.1);
border-color: rgba($colorIndicatorMenuFg, 0.75);
color: $colorIndicatorMenuFgHov;
}
}
[class*='icon-'] {
// If any elements within label include the class 'icon-*' then deal with their :before's
&:before {
font-size: 0.8em;
margin-right: $interiorMarginSm;
}
}
}
.c-indicator__count {
display: none; // Only displays when Indicator is minified, see below
}
[class*='minify-indicators'] {
// All styles for minified Indicators should go in here
.c-indicator:not(.no-minify) {
border: 1px solid transparent; // Hack to make minified sizing work in Safari. Have no idea why this works.
overflow: visible;
transition: transform;
@include hover() {
background: $colorIndicatorBgHov;
transition: transform 250ms ease-in 200ms; // Go-away transition
.c-indicator__label {
box-shadow: $colorIndicatorMenuBgShdw;
transform: scale(1);
overflow: visible;
transition: transform 100ms ease-out 100ms; // Appear transition
}
}
.c-indicator__label {
transition: transform 250ms ease-in 200ms; // Go-away transition
background: $colorIndicatorMenuBg;
color: $colorIndicatorMenuFg;
border-radius: $controlCr;
right: 0;
top: 130%;
padding: $interiorMargin $interiorMargin;
position: absolute;
transform-origin: 90% 0;
transform: scale(0);
overflow: hidden;
z-index: 50;
&:before {
// Infobubble-style arrow element
content: '';
display: block;
position: absolute;
bottom: 100%;
right: 8px;
@include triangle('up', $size: 4px, $ratio: 1, $color: $colorIndicatorMenuBg);
}
}
.c-indicator__count {
display: inline-block;
margin-left: $interiorMarginSm;
}
}
}
/* Mobile */
// Hide the clock indicator when we're phone portrait
body.phone.portrait {
.c-indicator.t-indicator-clock {
display: none;
}
}
================================================
FILE: src/ui/layout/status-bar/notification-banner.scss
================================================
@mixin statusBannerColors($bg, $fg: $colorStatusFg) {
$bgPb: 10%;
$bgPbD: 10%;
background-color: darken($bg, $bgPb);
color: $fg;
&:hover {
background-color: darken($bg, $bgPb - $bgPbD);
}
.s-action {
background-color: darken($bg, $bgPb + $bgPbD);
&:hover {
background-color: darken($bg, $bgPb);
}
}
}
.c-message-banner {
$closeBtnSize: 7px;
border-radius: $controlCr;
@include statusBannerColors($colorStatusDefault, $colorStatusFg);
cursor: pointer;
display: flex;
align-items: center;
left: 50%;
top: 50%;
max-width: 50%;
max-height: 25px;
padding: $interiorMarginSm $interiorMargin $interiorMarginSm $interiorMarginLg;
position: absolute;
transform: translate(-50%, -50%);
z-index: 2;
> * + * {
margin-left: $interiorMargin;
}
&.ok {
@include statusBannerColors($colorOk, $colorOkFg);
}
&.info {
@include statusBannerColors($colorInfo, $colorInfoFg);
}
&.caution,
&.warning,
&.alert {
@include statusBannerColors($colorWarningLo, $colorWarningLoFg);
}
&.error {
@include statusBannerColors($colorWarningHi, $colorWarningHiFg);
}
&__message {
@include ellipsize();
flex: 1 1 auto;
}
&__progress-bar {
height: 7px;
width: 70px;
// Only show the progress bar
.c-progress-bar {
&__text {
display: none;
}
}
}
&__close-button {
font-size: 1.25em;
}
}
================================================
FILE: src/ui/mixins/context-menu-gesture.js
================================================
import { toRaw } from 'vue';
export default {
inject: ['openmct'],
props: {
objectPath: {
type: Array,
default() {
return [];
}
}
},
data() {
return {
contextClickActive: false
};
},
mounted() {
this.unobserveObjects = {};
//TODO: touch support
this.$nextTick(() => {
this.$refs.root.addEventListener('contextmenu', this.showContextMenu);
});
function updateObject(oldObject, newObject) {
const rawNewObject = toRaw(newObject);
const rawOldObject = toRaw(oldObject);
Object.assign(rawOldObject, rawNewObject);
}
this.objectPath.forEach((object) => {
if (object) {
const key = this.openmct.objects.makeKeyString(object.identifier);
this.unobserveObjects[key] = this.openmct.objects.observe(
object,
'*',
updateObject.bind(this, object)
);
}
});
},
beforeUnmount() {
this.removeListeners();
this.$refs.root.removeEventListener('contextMenu', this.showContextMenu);
},
methods: {
removeListeners() {
Object.values(this.unobserveObjects).forEach((unobserve) => unobserve());
this.unobserveObjects = {};
},
showContextMenu(event) {
if (this.readOnly) {
return;
}
event.preventDefault();
event.stopPropagation();
let actionsCollection = this.openmct.actions.getActionsCollection(toRaw(this.objectPath));
let actions = actionsCollection.getVisibleActions();
let sortedActions = this.openmct.actions._groupAndSortActions(actions);
const menuOptions = {
onDestroy: this.onContextMenuDestroyed,
label: this.objectPath[0].name
};
const menuItems = this.openmct.menus.actionsToMenuItems(
sortedActions,
actionsCollection.objectPath,
actionsCollection.view
);
this.openmct.menus.showMenu(event.clientX, event.clientY, menuItems, menuOptions);
this.contextClickActive = true;
this.$emit('context-click-active', true);
},
onContextMenuDestroyed() {
this.contextClickActive = false;
this.$emit('context-click-active', false);
}
}
};
================================================
FILE: src/ui/mixins/object-link.js
================================================
import { objectPathToUrl } from '../../tools/url.js';
export default {
inject: ['openmct'],
props: {
objectPath: {
type: Array,
default() {
return [];
}
}
},
computed: {
objectLink() {
if (!this.objectPath.length) {
return;
}
if (this.navigateToPath) {
return '#' + this.navigateToPath;
}
const url = objectPathToUrl(this.openmct, this.objectPath);
return url;
}
}
};
================================================
FILE: src/ui/mixins/staleness-mixin.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
import { isProxy, toRaw } from 'vue';
import { isIdentifier } from '@/api/objects/object-utils';
import StalenessUtils from '@/utils/staleness';
export default {
data() {
return {
staleObjects: [],
stalenessSubscription: {},
compositionObjectMap: new Map(),
setupClockChanged: false
};
},
computed: {
isStale() {
return this.staleObjects.length !== 0;
}
},
methods: {
getSubscriptionId(domainObject) {
// Only extract the identifier if it is not already an identifier
const identifier = isIdentifier(domainObject) ? domainObject : domainObject.identifier;
return this.openmct?.objects.makeKeyString(identifier);
},
setupClockChangedEvent(callback) {
this.setupClockChanged = true;
this.compositionIteratorCallback = this.compositionIterator(callback);
this.openmct.time.on('clockChanged', this.compositionIteratorCallback);
},
addToCompositionMap(id, domainObject) {
if (!this.compositionObjectMap.get(id)) {
this.compositionObjectMap.set(id, domainObject);
}
},
compositionIterator(callback) {
return () => {
this.staleObjects = [];
for (const [, object] of this.compositionObjectMap) {
let domainObject = object;
if (isProxy(domainObject)) {
domainObject = toRaw(object);
}
if (callback && typeof callback === 'function') {
callback(domainObject);
}
}
};
},
subscribeToStaleness(domainObjectList, callback) {
if (domainObjectList === null || domainObjectList === undefined) {
return;
}
if (!Array.isArray(domainObjectList)) {
domainObjectList = [domainObjectList];
}
domainObjectList.forEach((domainObject) => {
if (isProxy(domainObject)) {
domainObject = toRaw(domainObject);
}
const id = this.getSubscriptionId(domainObject);
this.addToCompositionMap(id, domainObject);
this.setupStalenessUtils(domainObject);
this.requestStaleness(domainObject, callback);
this.setupStalenessSubscription(domainObject, callback);
});
},
triggerUnsubscribeFromStaleness(domainObjectList, callback) {
if (domainObjectList === null || domainObjectList === undefined) {
return;
}
if (!Array.isArray(domainObjectList)) {
domainObjectList = [domainObjectList];
}
domainObjectList.forEach((domainObject) => {
if (isProxy(domainObject)) {
domainObject = toRaw(domainObject);
}
const id = this.getSubscriptionId(domainObject);
if (!this.stalenessSubscription[id]) {
return;
}
if (this.staleObjects.length !== 0) {
this.clearStaleness(id);
}
this.teardownStalenessSubscription(domainObject);
this.teardownStalenessUtils(domainObject);
delete this.stalenessSubscription[id];
});
if (callback && typeof callback === 'function') {
callback();
}
},
setupStalenessUtils(domainObject) {
const id = this.getSubscriptionId(domainObject);
if (this.stalenessSubscription[id]) {
return;
}
this.stalenessSubscription[id] = {};
this.stalenessSubscription[id].stalenessUtils = new StalenessUtils(
this.openmct,
domainObject
);
},
teardownStalenessUtils(domainObject) {
const id = this.getSubscriptionId(domainObject);
const { stalenessUtils } = this.stalenessSubscription[id];
if (stalenessUtils) {
stalenessUtils.destroy();
delete this.stalenessSubscription[id].stalenessUtils;
}
},
setupStalenessSubscription(domainObject, callback) {
const id = this.getSubscriptionId(domainObject);
this.stalenessSubscription[id].unsubscribe = this.openmct.telemetry.subscribeToStaleness(
domainObject,
(stalenessResponse) => {
this.handleStalenessResponse(id, stalenessResponse, callback);
}
);
},
teardownStalenessSubscription(domainObject) {
const id = this.getSubscriptionId(domainObject);
const { unsubscribe } = this.stalenessSubscription[id];
if (unsubscribe) {
unsubscribe();
delete this.stalenessSubscription[id].unsubscribe;
}
},
resubscribeToStaleness(domainObject, callback, unsubscribeCallback) {
const id = this.getSubscriptionId(domainObject);
this.stalenessSubscription[id].resubscribe = () => {
this.staleObjects = [];
this.triggerUnsubscribeFromStaleness(domainObject, unsubscribeCallback);
this.setupStalenessSubscription(domainObject, callback);
};
},
async requestStaleness(domainObject, callback) {
const id = this.getSubscriptionId(domainObject);
const stalenessResponse = await this.openmct.telemetry.isStale(domainObject);
if (stalenessResponse !== undefined) {
this.handleStalenessResponse(id, stalenessResponse, callback);
}
},
handleStalenessResponse(id, stalenessResponse, callback) {
if (!id) {
id = Object.keys(this.stalenessSubscription)[0];
}
if (this.stalenessSubscription[id].stalenessUtils.shouldUpdateStaleness(stalenessResponse)) {
if (callback && typeof callback === 'function') {
callback(stalenessResponse);
} else {
this.addOrRemoveStaleObject(id, stalenessResponse);
}
}
},
clearStaleness(id) {
const stalenessResponse = { isStale: false };
if (!id) {
id = Object.keys(this.stalenessSubscription)[0];
}
this.addOrRemoveStaleObject(id, stalenessResponse);
},
addOrRemoveStaleObject(id, stalenessResponse) {
const index = this.staleObjects.indexOf(id);
if (stalenessResponse.isStale) {
if (index === -1) {
this.staleObjects.push(id);
}
} else {
if (index !== -1) {
this.staleObjects.splice(index, 1);
}
}
}
},
unmounted() {
let compositionObjects = [];
for (const [, object] of this.compositionObjectMap) {
compositionObjects.push(object);
}
this.triggerUnsubscribeFromStaleness(compositionObjects);
if (this.setupClockChanged) {
this.openmct.time.off('clockChanged', this.compositionIteratorCallback);
this.setupClockChanged = false;
}
}
};
================================================
FILE: src/ui/mixins/toggle-mixin.js
================================================
export default {
data() {
return {
open: false
};
},
methods: {
toggle(event) {
if (this.open) {
if (this.isOpening) {
// Prevent document event handler from closing immediately
// after opening. Can't use stopPropagation because that
// would break other menus with similar behavior.
this.isOpening = false;
return;
}
document.removeEventListener('click', this.toggle);
this.open = false;
} else {
document.addEventListener('click', this.toggle);
this.open = true;
this.isOpening = true;
}
}
},
unmounted() {
document.removeEventListener('click', this.toggle);
}
};
================================================
FILE: src/ui/preview/PreviewAction.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
import { EventEmitter } from 'eventemitter3';
import mount from 'utils/mount';
import PreviewContainer from './PreviewContainer.vue';
const PREVIEW_ACTION_KEY = 'preview';
class PreviewAction extends EventEmitter {
constructor(openmct) {
super();
/**
* Metadata
*/
this.name = 'View';
this.key = PREVIEW_ACTION_KEY;
this.description = 'View in large dialog';
this.cssClass = 'icon-items-expand';
this.group = 'windowing';
this.priority = 1;
/**
* Dependencies
*/
this._openmct = openmct;
if (PreviewAction.isVisible === undefined) {
PreviewAction.isVisible = false;
}
}
invoke(objectPath, viewOptions) {
const { vNode, destroy } = mount(
{
components: {
PreviewContainer
},
provide: {
openmct: this._openmct,
objectPath: objectPath
},
data() {
return {
viewOptions
};
},
template: ' '
},
{
app: this._openmct.app
}
);
const overlay = this._openmct.overlays.overlay({
element: vNode.el,
size: 'large',
autoHide: false,
buttons: [
{
label: 'Done',
callback: () => {
overlay.dismiss();
}
}
],
onDestroy: () => {
PreviewAction.isVisible = false;
destroy();
this.emit('isVisible', false);
overlay.dismiss();
}
});
PreviewAction.isVisible = true;
this.emit('isVisible', true);
}
appliesTo(objectPath, view = {}) {
const parentElement = view.parentElement;
const isObjectView = parentElement && parentElement.classList.contains('js-object-view');
return (
!PreviewAction.isVisible &&
!this._openmct.router.isNavigatedObject(objectPath) &&
!isObjectView
);
}
_preventPreview(objectPath) {
const noPreviewTypes = ['folder'];
return noPreviewTypes.includes(objectPath[0].type);
}
}
export { PREVIEW_ACTION_KEY };
export default PreviewAction;
================================================
FILE: src/ui/preview/PreviewContainer.vue
================================================
================================================
FILE: src/ui/preview/PreviewHeader.vue
================================================
================================================
FILE: src/ui/preview/ViewHistoricalDataAction.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
import PreviewAction from './PreviewAction.js';
const VIEW_HISTORICAL_DATA_ACTION_KEY = 'viewHistoricalData';
class ViewHistoricalDataAction extends PreviewAction {
constructor(openmct) {
super(openmct);
this.name = 'View Historical Data';
this.key = VIEW_HISTORICAL_DATA_ACTION_KEY;
this.description = 'View Historical Data in a Table or Plot';
this.cssClass = 'icon-eye-open';
this.hideInDefaultMenu = true;
}
appliesTo(objectPath, view = {}) {
let viewContext = view.getViewContext && view.getViewContext();
return (
objectPath.length && viewContext && viewContext.row && viewContext.row.viewHistoricalData
);
}
}
export { VIEW_HISTORICAL_DATA_ACTION_KEY };
export default ViewHistoricalDataAction;
================================================
FILE: src/ui/preview/plugin.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
import PreviewAction from './PreviewAction.js';
import ViewHistoricalDataAction from './ViewHistoricalDataAction.js';
export default function () {
return function (openmct) {
openmct.actions.register(new PreviewAction(openmct));
openmct.actions.register(new ViewHistoricalDataAction(openmct));
};
}
================================================
FILE: src/ui/preview/preview.scss
================================================
.l-preview-window {
display: flex;
flex-direction: column;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
> * + * {
margin-top: $interiorMargin;
}
&__object-name {
flex: 0 0 auto;
}
&__object-view {
flex: 1 1 auto;
height: 100%; // Chrome 73
overflow: auto;
> div:not([class]) {
// Target an immediate child div without a class and make it display: contents
display: contents;
}
}
}
================================================
FILE: src/ui/registries/InspectorViewRegistry.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
const DEFAULT_VIEW_PRIORITY = 0;
/**
* A InspectorViewRegistry maintains the definitions for views
* that may occur in the inspector.
*/
export default class InspectorViewRegistry {
constructor() {
/** @type {Record} */
this.providers = {};
}
/**
*
* @param {DomainObject} selection the object to be viewed
* @returns {ViewProvider[]} any providers
* which can provide views of this object
* @private for platform-internal use
*/
get(selection) {
function byPriority(providerA, providerB) {
const priorityA = providerA.priority?.() ?? DEFAULT_VIEW_PRIORITY;
const priorityB = providerB.priority?.() ?? DEFAULT_VIEW_PRIORITY;
return priorityB - priorityA;
}
return this.#getAllProviders()
.filter((provider) => provider.canView(selection))
.map((provider) => {
const view = provider.view(selection);
view.key = provider.key;
view.name = provider.name;
view.glyph = provider.glyph;
return view;
})
.sort(byPriority);
}
/**
* Registers a new inspector view provider.
*
* @param {ViewProvider} provider the provider for this view
*/
addProvider(provider) {
const key = provider.key;
const name = provider.name;
if (key === undefined) {
throw "View providers must have a unique 'key' property defined";
}
if (name === undefined) {
throw "View providers must have a 'name' property defined";
}
if (this.providers[key] !== undefined) {
console.warn(`Provider already defined for key '${key}'. Provider keys must be unique.`);
}
this.providers[key] = provider;
}
/**
* Retrieves a view provider by its key.
* @param {string} key the key of the view provider
* @returns {ViewProvider} the view provider
*/
getByProviderKey(key) {
return this.providers[key];
}
/**
* @returns {ViewProvider[]} all providers
*/
#getAllProviders() {
return Object.values(this.providers);
}
}
/**
* @typedef {import("openmct").View} View
* @typedef {import("openmct").ViewProvider} ViewProvider
* @typedef {import('openmct').DomainObject} DomainObject
*/
================================================
FILE: src/ui/registries/ToolbarRegistry.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
/**
* A ToolbarRegistry maintains the definitions for toolbars.
*
* @interface ToolbarRegistry
*/
export default class ToolbarRegistry {
constructor() {
this.providers = {};
}
/**
* Gets toolbar controls from providers which can provide a toolbar for this selection.
*
* @param {Object} selection the selection object
* @returns {Object[]} an array of objects defining controls for the toolbar
* @private for platform-internal use
*/
get(selection) {
const providers = this.getAllProviders().filter(function (provider) {
return provider.forSelection(selection);
});
const structure = [];
providers.forEach((provider) => {
provider.toolbar(selection).forEach((item) => structure.push(item));
});
return structure;
}
/**
* @private
*/
getAllProviders() {
return Object.values(this.providers);
}
/**
* @private
*/
getByProviderKey(key) {
return this.providers[key];
}
/**
* Registers a new type of toolbar.
*
* @param {module:openmct.ToolbarRegistry} provider the provider for this toolbar
* @method addProvider
*/
addProvider(provider) {
const key = provider.key;
if (key === undefined) {
throw "Toolbar providers must have a unique 'key' property defined.";
}
if (this.providers[key] !== undefined) {
console.warn("Provider already defined for key '%s'. Provider keys must be unique.", key);
}
this.providers[key] = provider;
}
}
/**
* Exposes types of toolbars in Open MCT.
*
* @interface ToolbarProvider
* @property {string} key a unique identifier for this toolbar
* @property {string} name the human-readable name of this toolbar
* @property {string} [description] a longer-form description (typically
* a single sentence or short paragraph) of this kind of toolbar
*/
/**
* Checks if this provider can supply toolbar for a selection.
*
* @method forSelection
* @param {module:openmct.selection} selection
* @returns {boolean} 'true' if the toolbar applies to the provided selection,
* otherwise 'false'.
*/
/**
* Provides controls that comprise a toolbar.
*
* @method toolbar
* @param {Object} selection the selection object
* @returns {Object[]} an array of objects defining controls for the toolbar.
*/
================================================
FILE: src/ui/registries/ViewRegistry.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
import { EventEmitter } from 'eventemitter3';
import PRIORITIES from '../../api/priority/PriorityAPI';
/**
* A ViewRegistry maintains the definitions for different kinds of views
* that may occur in different places in the user interface.
*/
export default class ViewRegistry extends EventEmitter {
constructor() {
super();
EventEmitter.apply(this);
/** @type {Record} */
this.providers = {};
}
/**
* for platform-internal use
* @param {import('openmct').DomainObject} item the object to be viewed
* @param {import('openmct').ObjectPath} objectPath - The current contextual object path of the view object
* @returns {ViewProvider[]} a list of providers that can provide views for this object, sorted by
* descending priority
*/
get(item, objectPath) {
if (objectPath === undefined) {
throw 'objectPath must be provided to get applicable views for an object';
}
function byPriority(providerA, providerB) {
let priorityA = providerA.priority ? providerA.priority(item) : PRIORITIES.DEFAULT;
let priorityB = providerB.priority ? providerB.priority(item) : PRIORITIES.DEFAULT;
return priorityB - priorityA;
}
return this.getAllProviders()
.filter(function (provider) {
return provider.canView(item, objectPath);
})
.sort(byPriority);
}
/**
* @private
*/
getAllProviders() {
return Object.values(this.providers);
}
/**
* Register a new type of view.
*
* @param {ViewProvider} provider the provider for this view
*/
addProvider(provider) {
const key = provider.key;
if (key === undefined) {
throw "View providers must have a unique 'key' property defined";
}
if (this.providers[key] !== undefined) {
console.warn(`Provider already defined for key '${key}'. Provider keys must be unique.`);
}
const wrappedView = provider.view.bind(provider);
provider.view = (domainObject, objectPath) => {
const viewObject = wrappedView(domainObject, objectPath);
const wrappedShow = viewObject.show.bind(viewObject);
viewObject.key = key; // provide access to provider key on view object
viewObject.show = (element, isEditing, viewOptions) => {
viewObject.parentElement = element.parentElement;
wrappedShow(element, isEditing, viewOptions);
};
return viewObject;
};
this.providers[key] = provider;
}
/**
* Returns the view provider by key
* @param {string} key
* @returns {ViewProvider}
*/
getByProviderKey(key) {
return this.providers[key];
}
/**
* Used internally to support seamless usage of new views with old
* views.
* @private
*/
getByVPID(vpid) {
return this.providers.filter(function (p) {
return p.vpid === vpid;
})[0];
}
}
/**
* @typedef {import('openmct').DomainObject} DomainObject
* @typedef {import('openmct').ObjectPath} ObjectPath
*/
/**
* @typedef {Object} ViewOptions
* @property {() => void} [renderWhenVisible]
* This function can be used for all rendering logic that would otherwise be executed within a
* `requestAnimationFrame` call. When called, `renderWhenVisible` will either execute the provided
* function immediately (via `requestAnimationFrame`) if the view is currently visible, or defer its
* execution until the view becomes visible.
*
* Additionally, `renderWhenVisible` returns a boolean value indicating whether the provided
* function was executed immediately (`true`) or deferred (`false`).
* Monitoring of visibility begins after the first call to `renderWhenVisible` is made.
*/
/**
* @typedef {Object} View
* A View is used to provide displayable content, and to react to
* associated life cycle events.
* @property {(container: HTMLElement, isEditing: boolean | undefined, viewOptions: ViewOptions | undefined) => void} show
* Populate the supplied DOM element with the contents of this view.
* View implementations should use this method to attach any
* listeners or acquire other resources that are necessary to keep
* the contents of this view up-to-date.
*
* - `container`: The DOM element where the view should be rendered.
* - `isEditing`: Indicates whether the view is in editing mode.
* - `viewOptions`: An object with configuration options for the view.
* @property {() => void} destroy - Release any resources associated with this view.
* View implementations should use this method to detach any listeners or release other resources
* that are no longer necessary once a view is no longer used.
* @property {() => { item: DomainObject, isMultiSelectEvent: boolean }} [getSelectionContext]
* A function that returns the selection context of the view.
* View implementations may use this method to customize the selection context.
*/
/**
* Exposes types of views in Open MCT.
*
* @typedef {Object} ViewProvider
* @property {string} key - The unique key that identifies this view
* @property {string} name - The name of the view
* @property {string} [cssClass] - The CSS class to apply to labels for this view (to add icons,
* for instance)
* @property {(domainObject: DomainObject, objectPath: ObjectPath) => boolean} canView
* Returns true if this provider is able to supply views for the given {@link DomainObject}.
*
* When called by Open MCT, this may include additional arguments
* which are on the path to the object to be viewed; for instance,
* when viewing "A Folder" within "My Items", this method will be
* invoked with "A Folder" (as a {@link DomainObject}) as the first argument.
* @property {(domainObject: DomainObject, objectPath: ObjectPath) => boolean} [canEdit]
* An optional function that defines whether or not this view can be used to edit a given object.
* If not provided, will default to `false` and the view will not support editing.
* @property {(domainObject: DomainObject, objectPath: ObjectPath) => View} view A function that
* provides a view for the provided domain object.
* @property {(domainObject: DomainObject) => number} [priority]
* A function that returns the priority of the view. The more positive the value, the higher the
* priority. Similarly, the more negative the value, the lower the priority.
*
* If not provided, the default priority of 100 will be used. This value is used to sort the views
* by descending priority if there are multiple views that can be shown for a given object.
*/
================================================
FILE: src/ui/router/ApplicationRouter.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
import { EventEmitter } from 'eventemitter3';
import LocationBar from 'location-bar';
import _ from 'lodash';
class ApplicationRouter extends EventEmitter {
/**
* events
* change:params -> notify listeners w/ new, old, and changed.
* change:path -> notify listeners w/ new, old paths.
*
* methods:
* update(path, params) -> updates path and params at the same time. Only
* updates specified params, other params are not modified.
* updateParams(newParams) -> update only specified params, leaving rest
* intact. Does not modify path.
* updatePath(path);
*
* route(path, handler);
* start(); Start routing.
* @param {import('../../../openmct').OpenMCT} openmct
*/
constructor(openmct) {
super();
this.locationBar = new LocationBar();
this.openmct = openmct;
this.routes = [];
this.started = false;
this.path = null;
this.setHash = _.debounce(this.setHash.bind(this), 300);
openmct.once('destroy', () => {
this.destroy();
});
}
// Public Methods
destroy() {
this.locationBar.stop();
}
/**
* Delete a given query parameter from current url
*
* @param {string} paramName name of searchParam to delete from current url searchParams
*/
deleteSearchParam(paramName) {
let url = this.getHashRelativeURL();
url.searchParams.delete(paramName);
this.setLocationFromUrl();
}
/**
* object for accessing all current search parameters
*
* @returns {URLSearchParams} A {@link https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/entries|URLSearchParams}
*/
getAllSearchParams() {
return this.getHashRelativeURL()?.searchParams;
}
/**
* Uniquely identifies a domain object.
*
* @typedef CurrentLocation
* @property {URL} url current url location
* @property {string} path current url location pathname
* @property {string} getQueryString a function which returns url search query
* @property {Object} params object representing url searchParams
*/
/**
* object for accessing current url location and search params
*
* @returns {CurrentLocation} A {@link CurrentLocation}
*/
getCurrentLocation() {
return this.currentLocation;
}
/**
* Get current location URL Object
*
* @returns {URL} current url location
*/
getHashRelativeURL() {
return this.getCurrentLocation()?.url;
}
/**
* Get current location URL Object searchParams
*
* @returns {Object} object representing current url searchParams
*/
getParams() {
return this.currentLocation.params;
}
/**
* Get a value of given param from current url searchParams
*
* @returns {string} value of paramName from current url searchParams
*/
getSearchParam(paramName) {
return this.getAllSearchParams()?.get(paramName);
}
/**
* Navigate to given hash, update current location object, and notify listeners about location change
*
* @param {string} hash The URL hash to navigate to in the form of "#/browse/mine/{keyString}/{keyString}".
* Should not include any params.
*/
navigate(hash) {
this.handleLocationChange(hash.substring(1));
}
/**
* Check if a given object and current location object are same
*
* @param {Array} objectPath Object path of a given Domain Object
*
* @returns {boolean}
*/
isNavigatedObject(objectPath) {
let targetObject = objectPath[0];
let navigatedObject = this.path[0];
if (!targetObject.identifier) {
return false;
}
return this.openmct.objects.areIdsEqual(targetObject.identifier, navigatedObject.identifier);
}
/**
* Add routes listeners
*
* @param {RegExp} matcher Regex to match value in url
* @param {Function} callback function called when found match in url
*/
route(matcher, callback) {
this.routes.push({
matcher,
callback
});
}
/**
* Set url hash using path and queryString
*
* @param {string} path path for url
* @param {string} queryString queryString for url
*/
set(path, queryString) {
this.setHash(`${path}?${queryString}`);
}
/**
* Will replace all current search parameters with the ones defined in urlSearchParams
*/
setAllSearchParams() {
this.setLocationFromUrl();
}
/**
* To force update url based on value in currentLocation object
*/
setLocationFromUrl() {
this.updateTimeSettings();
}
/**
* Set url hash using path
*
* @param {string} path path for url
*/
setPath(path) {
this.handleLocationChange(path.substring(1));
}
/**
* Update param value from current url searchParams
*
* @param {string} paramName param name from current url searchParams
* @param {string} paramValue param value from current url searchParams
*/
setSearchParam(paramName, paramValue) {
let url = this.getHashRelativeURL();
url.searchParams.set(paramName, paramValue);
this.setLocationFromUrl();
}
/**
* start application routing, should be done after handlers are registered.
*/
start() {
if (this.started) {
throw new Error('Router already started!');
}
this.started = true;
this.locationBar.onChange((p) => this.hashChanged(p));
this.locationBar.start({
root: location.pathname
});
}
/**
* Set url hash using path and searchParams object
*
* @param {string} path path for url
* @param {string} params oject representing searchParams key/value
*/
update(path, params) {
let searchParams = this.currentLocation.url.searchParams;
for (let [key, value] of Object.entries(params)) {
if (typeof value === 'undefined') {
searchParams.delete(key);
} else {
searchParams.set(key, value);
}
}
this.set(path, searchParams.toString());
}
/**
* Update route params. Takes an object of updates. New parameters
*/
updateParams(updateParams) {
let searchParams = this.currentLocation.url.searchParams;
Object.entries(updateParams).forEach(([key, value]) => {
if (typeof value === 'undefined') {
searchParams.delete(key);
} else {
searchParams.set(key, value);
}
});
this.setQueryString(searchParams.toString());
}
/**
* To force update url based on value in currentLocation object
*/
updateTimeSettings() {
const hash = `${this.currentLocation.path}?${this.currentLocation.getQueryString()}`;
this.setHash(hash);
}
// Private Methods
/**
* @private
* Create currentLocation object
*
* @param {string} pathString USVString representing relative URL.
*
* @returns {CurrentLocation} A {@link CurrentLocation}
*/
createLocation(pathString) {
if (pathString[0] !== '/') {
pathString = '/' + pathString;
}
let url = new URL(pathString, `${location.protocol}//${location.host}${location.pathname}`);
return {
url: url,
path: url.pathname,
getQueryString: () => url.search.replace(/^\?/, ''),
params: paramsToObject(url.searchParams)
};
}
/**
* @private
* Compare new and old path and on change emit event 'change:path'
*
* @param {string} newPath new path of url
* @param {string} oldPath old path of url
* @returns {boolean} true if path changed, false otherwise
*/
doPathChange(newPath, oldPath) {
if (newPath === oldPath) {
return false;
}
let route = this.routes.filter((r) => r.matcher.test(newPath))[0];
if (route) {
route.callback(newPath, route.matcher.exec(newPath), this.currentLocation.params);
}
this.openmct.telemetry.abortAllRequests();
this.emit('change:path', newPath, oldPath);
return true;
}
/**
* @private
* Compare new and old params and on change emit event 'change:params'
*
* @param {Object} newParams new params of url
* @param {Object} oldParams old params of url
* @returns {boolean} true if params changed, false otherwise
*/
doParamsChange(newParams, oldParams) {
if (_.isEqual(newParams, oldParams)) {
return false;
}
let changedParams = {};
Object.entries(newParams).forEach(([key, value]) => {
if (value !== oldParams[key]) {
changedParams[key] = value;
}
});
Object.keys(oldParams).forEach((key) => {
if (!Object.prototype.hasOwnProperty.call(newParams, key)) {
changedParams[key] = undefined;
}
});
this.emit('change:params', newParams, oldParams, changedParams);
return true;
}
/**
* @private
* On location change, update currentLocation object and emit appropriate events
*
* @param {string} pathString USVString representing relative URL.
*/
handleLocationChange(pathString) {
let oldLocation = this.currentLocation;
let newLocation = this.createLocation(pathString);
this.currentLocation = newLocation;
if (!oldLocation) {
this.doPathChange(newLocation.path, null);
this.doParamsChange(newLocation.params, {});
return;
}
const pathChanged = this.doPathChange(newLocation.path, oldLocation.path);
const paramsChanged = this.doParamsChange(newLocation.params, oldLocation.params, pathChanged);
if (pathChanged || paramsChanged) {
// If either path or parameters have changed, we update the URL in the address bar.
this.set(newLocation.path, newLocation.getQueryString());
}
}
/**
* @private
* On hash changed, update currentLocation object and emit appropriate events
*
* @param {string} hash new hash for url
*/
hashChanged(hash) {
this.emit('change:hash', hash);
this.handleLocationChange(hash);
}
/**
* @private
* Set new hash for url
*
* @param {string} hash new hash for url
*/
setHash(hash) {
location.hash = '#' + hash.replace(/#/g, '');
}
/**
* @private
* Set queryString part of current url
*
* @param {string} queryString queryString part of url
*/
setQueryString(queryString) {
this.handleLocationChange(`${this.currentLocation.path}?${queryString}`);
}
}
/**
* Convert searchParams into Object
*
* @param {URLSearchParams} searchParams queryString part of url
*
* @returns {Object}
*/
function paramsToObject(searchParams) {
let params = {};
for (let [key, value] of searchParams.entries()) {
if (params[key]) {
if (!Array.isArray(params[key])) {
params[key] = [params[key]];
}
params[key].push(value);
} else {
params[key] = value;
}
}
return params;
}
export default ApplicationRouter;
================================================
FILE: src/ui/router/ApplicationRouterSpec.js
================================================
import { createOpenMct, resetApplicationState } from 'utils/testing';
let openmct;
let element;
let child;
let appHolder;
let resolveFunction;
xdescribe('Application router utility functions', () => {
beforeEach((done) => {
appHolder = document.createElement('div');
appHolder.style.width = '640px';
appHolder.style.height = '480px';
openmct = createOpenMct();
openmct.install(openmct.plugins.MyItems());
element = document.createElement('div');
child = document.createElement('div');
element.appendChild(child);
openmct.on('start', () => {
resolveFunction = () => {
const success = window.location.hash !== null && window.location.hash !== '';
if (success) {
done();
}
};
openmct.router.on('change:hash', resolveFunction);
// We have a debounce set to 300ms on setHash, so if we don't flush,
// the above resolve function sometimes doesn't fire due to a race condition.
openmct.router.setHash.flush();
openmct.router.setLocationFromUrl();
});
openmct.start(appHolder);
document.body.append(appHolder);
});
afterEach(() => {
openmct.router.removeListener('change:hash', resolveFunction);
appHolder.remove();
return resetApplicationState(openmct);
});
it('has initial hash when loaded', () => {
const success = window.location.hash !== null;
expect(success).toBe(true);
});
it('The setSearchParam function sets an individual search parameter in the window location hash', () => {
openmct.router.setSearchParam('testParam1', 'testValue1');
const searchParams = openmct.router.getAllSearchParams();
expect(searchParams.get('testParam1')).toBe('testValue1');
});
it('The deleteSearchParam function deletes an individual search parameter in the window location hash', () => {
openmct.router.deleteSearchParam('testParam');
const searchParams = openmct.router.getAllSearchParams();
expect(searchParams.get('testParam')).toBe(null);
});
it('The setSearchParam function sets a multiple individual search parameters in the window location hash', () => {
openmct.router.setSearchParam('testParam1', 'testValue1');
openmct.router.setSearchParam('testParam2', 'testValue2');
const searchParams = openmct.router.getAllSearchParams();
expect(searchParams.get('testParam1')).toBe('testValue1');
expect(searchParams.get('testParam2')).toBe('testValue2');
});
it('The setAllSearchParams function replaces all search parameters in the window location hash', () => {
openmct.router.setSearchParam('testParam2', 'updatedtestValue2');
openmct.router.setSearchParam('newTestParam3', 'newTestValue3');
const searchParams = openmct.router.getAllSearchParams();
expect(searchParams.get('testParam2')).toBe('updatedtestValue2');
expect(searchParams.get('newTestParam3')).toBe('newTestValue3');
});
it('The doPathChange function triggers aborting all requests when doing a path change', () => {
const abortSpy = spyOn(openmct.telemetry, 'abortAllRequests');
openmct.router.doPathChange('newPath', 'oldPath');
expect(abortSpy).toHaveBeenCalledTimes(1);
});
});
================================================
FILE: src/ui/router/Browse.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
class Browse {
/**
* @type {number}
*/
#navigateCall = 0;
/**
* @type {Object?}
*/
#browseObject = null;
/**
* @type {Function | undefined}
*/
#unobserve = undefined;
/**
* @type {string | undefined}
*/
#currentObjectPath = undefined;
/**
* @type {boolean}
*/
#isRoutingInProgress = false;
/**
* @type {import('../../../openmct').OpenMCT}
*/
#openmct;
/**
*
* @param {import('../../../openmct').OpenMCT} openmct
*/
constructor(openmct) {
this.#openmct = openmct;
this.#openmct.router.route(/^\/browse\/?$/, this.#navigateToFirstChildOfRoot.bind(this));
this.#openmct.router.route(/^\/browse\/(.*)$/, this.#handleBrowseRoute.bind(this));
this.#openmct.router.on('change:params', this.#onParamsChanged.bind(this));
}
#onParamsChanged(newParams, oldParams, changed) {
if (this.#isRoutingInProgress) {
return;
}
if (changed.view && this.#browseObject) {
const provider = this.#openmct.objectViews.getByProviderKey(changed.view);
this.#viewObject(this.#browseObject, provider);
}
}
#viewObject(object, viewProvider) {
this.#currentObjectPath = this.#openmct.router.path;
this.#openmct.layout.$refs.browseObject.show(
object,
viewProvider.key,
true,
this.#currentObjectPath
);
this.#openmct.layout.$refs.browseBar.domainObject = object;
this.#openmct.layout.$refs.browseBar.viewKey = viewProvider.key;
}
#handleBrowseObjectUpdate(newObject) {
this.#openmct.layout.$refs.browseBar.domainObject = newObject;
if (typeof newObject.name === 'string' && newObject.name !== document.title) {
document.title = newObject.name;
}
}
async #navigateToPath(path, currentViewKey) {
this.#navigateCall++;
const currentNavigation = this.#navigateCall;
if (this.#unobserve) {
this.#unobserve();
this.#unobserve = undefined;
}
path = decodeURIComponent(path);
if (!Array.isArray(path)) {
path = path.split('/');
}
let objects = await this.#pathToObjects(path);
if (currentNavigation !== this.#navigateCall) {
return; // Prevent race.
}
this.#isRoutingInProgress = false;
objects = objects.reverse();
this.#openmct.router.path = objects;
this.#browseObject = objects[0];
this.#openmct.router.emit('afterNavigation');
this.#openmct.layout.$refs.browseBar.domainObject = this.#browseObject;
if (!this.#browseObject) {
this.#openmct.layout.$refs.browseObject.clear();
return;
}
document.title = this.#browseObject.name; //change document title to current object in main view
this.#unobserve = this.#openmct.objects.observe(
this.#browseObject,
'*',
this.#handleBrowseObjectUpdate.bind(this)
);
if (!currentViewKey) {
currentViewKey = this.#getPreferredViewForObjectType(this.#browseObject);
}
const currentProvider = this.#openmct.objectViews.getByProviderKey(currentViewKey);
if (currentProvider && currentProvider.canView(this.#browseObject, this.#openmct.router.path)) {
this.#viewObject(this.#browseObject, currentProvider);
return;
}
const routerPath = this.#openmct.router.path;
const retrievedObjectViews = this.#openmct.objectViews.get(this.#browseObject, routerPath);
const defaultProvider = retrievedObjectViews?.[0];
if (defaultProvider) {
this.#openmct.router.updateParams({ view: defaultProvider.key });
} else {
this.#openmct.router.updateParams({ view: undefined });
this.#openmct.layout.$refs.browseObject.clear();
}
}
#pathToObjects(path) {
return Promise.all(
path.map((keyString) => {
const identifier = this.#openmct.objects.parseKeyString(keyString);
return this.#openmct.objects.supportsMutation(identifier)
? this.#openmct.objects.getMutable(identifier)
: this.#openmct.objects.get(identifier);
})
);
}
#getPreferredViewForObjectType(obj) {
const storedViewPrefs =
JSON.parse(window.localStorage.getItem('openmct-stored-view-prefs')) || {};
return storedViewPrefs[obj.type] ? storedViewPrefs[obj.type] : undefined;
}
async #navigateToFirstChildOfRoot() {
try {
const rootObject = await this.#openmct.objects.get('ROOT');
const composition = this.#openmct.composition.get(rootObject);
if (!composition) {
return;
}
const children = await composition.load();
const lastChild = children[children.length - 1];
if (lastChild) {
const lastChildId = this.#openmct.objects.makeKeyString(lastChild.identifier);
this.#openmct.router.setPath(`#/browse/${lastChildId}`);
}
} catch (e) {
console.error(e);
}
}
#clearMutationListeners() {
if (this.#openmct.router.path) {
this.#openmct.router.path.forEach((pathObject) => {
if (pathObject.isMutable) {
this.#openmct.objects.destroyMutable(pathObject);
}
});
}
}
#handleBrowseRoute(path, results, params) {
this.#isRoutingInProgress = true;
const navigatePath = results[1];
this.#clearMutationListeners();
this.#navigateToPath(navigatePath, params.view);
}
}
export default Browse;
================================================
FILE: src/ui/toolbar/ToolbarContainer.vue
================================================
================================================
FILE: src/ui/toolbar/components/ToolbarButton.vue
================================================
================================================
FILE: src/ui/toolbar/components/ToolbarCheckbox.vue
================================================
================================================
FILE: src/ui/toolbar/components/ToolbarColorPicker.vue
================================================
================================================
FILE: src/ui/toolbar/components/ToolbarInput.vue
================================================
================================================
FILE: src/ui/toolbar/components/ToolbarMenu.vue
================================================
================================================
FILE: src/ui/toolbar/components/ToolbarSelectMenu.vue
================================================
================================================
FILE: src/ui/toolbar/components/ToolbarSeparator.vue
================================================
================================================
FILE: src/ui/toolbar/components/ToolbarToggleButton.vue
================================================
================================================
FILE: src/ui/toolbar/components/toolbar-checkbox.scss
================================================
.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: $colorKeyBg;
&:before {
color: $colorKeyFg;
content: $glyph-icon-check;
font-family: symbolsfont;
font-size: 0.6em;
}
}
&:not(:disabled) + label {
cursor: pointer;
}
&:disabled + label {
opacity: 0.5;
}
}
}
================================================
FILE: src/utils/agent/Agent.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
/**
* The query service handles calls for browser and userAgent
* info using a comparison between the userAgent and key
* device names
* @constructor
* @param window the browser object model
*/
export default class Agent {
constructor(window) {
const userAgent = window.navigator.userAgent;
const matches = userAgent.match(/iPad|iPhone|Android/i) || [];
this.userAgent = userAgent;
this.mobileName = matches[0];
this.window = window;
this.touchEnabled = window.ontouchstart !== undefined;
}
/**
* Check if the user is on a mobile device.
* @returns {boolean} true on mobile
*/
isMobile() {
return Boolean(this.mobileName);
}
/**
* Check if the user is on a phone-sized mobile device.
* @returns {boolean} true on a phone
*/
isPhone() {
if (this.isMobile()) {
if (this.isAndroidTablet()) {
return false;
} else if (this.mobileName === 'iPad') {
return false;
} else {
return true;
}
} else {
return false;
}
}
/**
* Check if the user is on a tablet sized android device
* @returns {boolean | undefined} true on an android tablet
*/
isAndroidTablet() {
if (this.mobileName === 'Android') {
if (this.isPortrait() && this.window.innerWidth >= 768) {
return true;
} else if (this.isLandscape() && this.window.innerHeight >= 768) {
return true;
}
} else {
return false;
}
}
/**
* Check if the user is on a tablet-sized mobile device.
* @returns {boolean | undefined} true on a tablet
*/
isTablet() {
return (
(this.isMobile() && !this.isPhone() && this.mobileName !== 'Android') ||
(this.isMobile() && this.isAndroidTablet())
);
}
/**
* Check if the user's device is in a portrait-style
* orientation (display width is narrower than display height.)
* @returns {boolean} true in portrait mode
*/
isPortrait() {
const { screen } = this.window;
const hasScreenOrientation =
screen && Object.prototype.hasOwnProperty.call(screen, 'orientation');
const hasWindowOrientation = Object.prototype.hasOwnProperty.call(this.window, 'orientation');
if (hasScreenOrientation) {
return screen.orientation.type.includes('portrait');
} else if (hasWindowOrientation) {
// Use window.orientation API if available (e.g. Safari mobile)
// which returns [-90, 0, 90, 180] based on device orientation.
const { orientation } = this.window;
return Math.abs(orientation / 90) % 2 === 0;
} else {
return this.window.innerWidth < this.window.innerHeight;
}
}
/**
* Check if the user's device is in a landscape-style
* orientation (display width is greater than display height.)
* @returns {boolean} true in landscape mode
*/
isLandscape() {
return !this.isPortrait();
}
/**
* Check if the user's device supports a touch interface.
* @returns {boolean} true if touch is supported
*/
isTouch() {
return this.touchEnabled;
}
/**
* Check if the user agent matches a certain named device,
* as indicated by checking for a case-insensitive substring
* match.
* @param {string} name the name to check for
* @returns {boolean} true if the user agent includes that name
*/
isBrowser(name) {
name = name.toLowerCase();
return this.userAgent.toLowerCase().indexOf(name) !== -1;
}
}
================================================
FILE: src/utils/agent/AgentSpec.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
import Agent from './Agent.js';
const TEST_USER_AGENTS = {
DESKTOP:
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.89 Safari/537.36',
IPAD: 'Mozilla/5.0 (iPad; CPU OS 7_0 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53',
IPHONE:
'Mozilla/5.0 (iPhone; CPU iPhone OS 7_0 like Mac OS X; en-us) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53'
};
describe('The Agent', function () {
let testWindow;
let agent;
beforeEach(function () {
testWindow = {
innerWidth: 640,
innerHeight: 480,
navigator: {
userAgent: TEST_USER_AGENTS.DESKTOP
}
};
});
it('recognizes desktop devices as non-mobile', function () {
testWindow.navigator.userAgent = TEST_USER_AGENTS.DESKTOP;
agent = new Agent(testWindow);
expect(agent.isMobile()).toBeFalsy();
expect(agent.isPhone()).toBeFalsy();
expect(agent.isTablet()).toBeFalsy();
});
it('detects iPhones', function () {
testWindow.navigator.userAgent = TEST_USER_AGENTS.IPHONE;
agent = new Agent(testWindow);
expect(agent.isMobile()).toBeTruthy();
expect(agent.isPhone()).toBeTruthy();
expect(agent.isTablet()).toBeFalsy();
});
it('detects iPads', function () {
testWindow.navigator.userAgent = TEST_USER_AGENTS.IPAD;
agent = new Agent(testWindow);
expect(agent.isMobile()).toBeTruthy();
expect(agent.isPhone()).toBeFalsy();
expect(agent.isTablet()).toBeTruthy();
});
it('detects display orientation by innerHeight and innerWidth', function () {
agent = new Agent(testWindow);
testWindow.innerWidth = 1024;
testWindow.innerHeight = 400;
expect(agent.isPortrait()).toBeFalsy();
expect(agent.isLandscape()).toBeTruthy();
testWindow.innerWidth = 400;
testWindow.innerHeight = 1024;
expect(agent.isPortrait()).toBeTruthy();
expect(agent.isLandscape()).toBeFalsy();
});
it('detects display orientation by screen.orientation', function () {
agent = new Agent(testWindow);
testWindow.screen = {
orientation: {
type: 'landscape-primary'
}
};
expect(agent.isPortrait()).toBeFalsy();
expect(agent.isLandscape()).toBeTruthy();
testWindow.screen = {
orientation: {
type: 'portrait-primary'
}
};
expect(agent.isPortrait()).toBeTruthy();
expect(agent.isLandscape()).toBeFalsy();
});
it('detects display orientation by window.orientation', function () {
agent = new Agent(testWindow);
testWindow.orientation = 90;
expect(agent.isPortrait()).toBeFalsy();
expect(agent.isLandscape()).toBeTruthy();
testWindow.orientation = 0;
expect(agent.isPortrait()).toBeTruthy();
expect(agent.isLandscape()).toBeFalsy();
});
it('detects touch support', function () {
testWindow.ontouchstart = null;
expect(new Agent(testWindow).isTouch()).toBe(true);
delete testWindow.ontouchstart;
expect(new Agent(testWindow).isTouch()).toBe(false);
});
it('allows for checking browser type', function () {
testWindow.navigator.userAgent = 'Chromezilla Safarifox';
agent = new Agent(testWindow);
expect(agent.isBrowser('Chrome')).toBe(true);
expect(agent.isBrowser('Firefox')).toBe(false);
});
});
================================================
FILE: src/utils/clipboard.js
================================================
class Clipboard {
updateClipboard(newClip) {
// return promise
return navigator.clipboard.writeText(newClip);
}
readClipboard() {
// return promise
return navigator.clipboard.readText();
}
}
export default new Clipboard();
================================================
FILE: src/utils/clock/DefaultClock.js
================================================
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2024, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web 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 Web 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.
*****************************************************************************/
import { EventEmitter } from 'eventemitter3';
/**
* A {@link openmct.TimeAPI.Clock} that updates the temporal bounds of the
* application based on values provided by a ticking clock.
* @constructor
*/
export default class DefaultClock extends EventEmitter {
constructor() {
super();
this.key = 'clock';
this.cssClass = 'icon-clock';
this.name = 'Clock';
this.description = 'A default clock for openmct.';
}
tick(tickValue) {
this.emit('tick', tickValue);
this.lastTick = tickValue;
}
/**
* Register a listener for the clock. When it ticks, the
* clock will provide the time from the configured endpoint
*
* @override
* @param {string | symbol} event the event to listen for
* @param {Function} fn the function to call when the event is emitted
* @param {*} [context] the context to use for the function call
* @returns {this} a function for deregistering the provided listener
*/
on(event, fn, context) {
super.on(event, fn, context);
if (this.listeners(event).length === 1) {
this.start();
}
return this;
}
/**
* Register a listener for the clock. When it ticks, the
* clock will provide the current local system time
*
* @override
* @param {string | symbol} event the event to listen for
* @param {Function} [fn] the function to call when the event is emitted
* @param {*} [context] the context to use for the function call
* @param {boolean} [once]
* @returns {this}
*/
off(event, fn, context, once) {
super.off(event, fn, context, once);
if (this.listeners(event).length === 0) {
this.stop();
}
return this;
}
stop() {
throw new Error("Method 'stop()' must be implemented.");
}
start() {
throw new Error("Method 'start()' must be implemented.");
}
/**
* @returns {number} The most recent value provided for a clock tick
*/
currentValue() {
return this.lastTick;
}
}
================================================
FILE: src/utils/constants.js
================================================
export const SupportedViewTypes = [
'plot-stacked',
'plot-overlay',
'bar-graph.view',
'time-strip.view',
'example.imagery',
'timelist.view'
];
================================================
FILE: src/utils/debounce.js
================================================
export default function debounce(func, delay) {
let debounceTimer;
return function (...args) {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => func(...args), delay);
};
}
================================================
FILE: src/utils/duration.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
const ONE_SECOND = 1000;
const ONE_MINUTE = 60 * ONE_SECOND;
const ONE_HOUR = ONE_MINUTE * 60;
const ONE_DAY = ONE_HOUR * 24;
function normalizeAge(num) {
const hundredtized = num * 100;
const isWhole = hundredtized % 100 === 0;
return isWhole ? hundredtized / 100 : num;
}
function padLeadingZeros(num, numOfLeadingZeros) {
return num.toString().padStart(numOfLeadingZeros, '0');
}
function toDoubleDigits(num) {
return padLeadingZeros(num, 2);
}
function toTripleDigits(num) {
return padLeadingZeros(num, 3);
}
function addTimeSuffix(value, suffix) {
return typeof value === 'number' && value > 0 ? `${value + suffix}` : '';
}
export function millisecondsToDHMS(numericDuration) {
const ms = numericDuration || 0;
const dhms = [
addTimeSuffix(Math.floor(normalizeAge(ms / ONE_DAY)), 'd'),
addTimeSuffix(Math.floor(normalizeAge((ms % ONE_DAY) / ONE_HOUR)), 'h'),
addTimeSuffix(Math.floor(normalizeAge((ms % ONE_HOUR) / ONE_MINUTE)), 'm'),
addTimeSuffix(Math.floor(normalizeAge((ms % ONE_MINUTE) / ONE_SECOND)), 's'),
addTimeSuffix(Math.floor(normalizeAge(ms % ONE_SECOND)), 'ms')
]
.filter(Boolean)
.join(' ');
return `${dhms ? '+' : ''} ${dhms}`;
}
/**
*
* @param {number} value
* @param {Object} options
* @param {boolean | undefined} options.excludeMilliSeconds
* @param {boolean | undefined} options.useDayFormat
* @returns {string}
*/
export function getPreciseDuration(
value,
{ excludeMilliSeconds, useDayFormat } = {
excludeMilliSeconds: null,
useDayFormat: null
}
) {
let preciseDuration;
const ms = value || 0;
const duration = [
Math.floor(normalizeAge(ms / ONE_DAY)),
toDoubleDigits(Math.floor(normalizeAge((ms % ONE_DAY) / ONE_HOUR))),
toDoubleDigits(Math.floor(normalizeAge((ms % ONE_HOUR) / ONE_MINUTE))),
toDoubleDigits(Math.floor(normalizeAge((ms % ONE_MINUTE) / ONE_SECOND)))
];
if (!excludeMilliSeconds) {
duration.push(toTripleDigits(Math.floor(normalizeAge(ms % ONE_SECOND))));
}
if (useDayFormat) {
// Format days as XD
const days = duration.shift();
if (days > 0) {
preciseDuration = `${days}D ${duration.join(':')}`;
} else {
preciseDuration = duration.join(':');
}
} else {
const days = toDoubleDigits(duration.shift());
duration.unshift(days);
preciseDuration = duration.join(':');
}
return preciseDuration;
}
================================================
FILE: src/utils/encoding.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
export function encode_url(url) {
return url ? encodeURI(url) : url;
}
================================================
FILE: src/utils/mount.js
================================================
import { createApp, defineComponent } from 'vue';
/**
* @typedef {import('vue').Component} Component
*/
/**
* Mounts a Vue component to a DOM element.
* @param {Component | any} component
* @param {Object} [options={}] the options object
* @param {Object} [options.props] the props for the component
* @param {Object} [options.children] the children for the component
* @param {HTMLElement} [options.element] the element to mount the component to
* @returns {Object}
*/
export default function mount(component, { props, children, element } = {}) {
let el = element;
if (!el) {
el = document.createElement('div');
}
/** @type {Component | any} */
let vueComponent = defineComponent(component);
let app = createApp(vueComponent);
let mountedComponentInstance = app.mount(el);
// eslint-disable-next-line func-style
const destroy = () => {
app.unmount();
};
return {
vNode: {
componentInstance: mountedComponentInstance,
el: mountedComponentInstance.$el
},
destroy,
el
};
}
================================================
FILE: src/utils/raf.js
================================================
export default function raf(callback) {
let rendering = false;
return (...args) => {
if (!rendering) {
rendering = true;
requestAnimationFrame(() => {
callback(...args);
rendering = false;
});
}
};
}
================================================
FILE: src/utils/rafSpec.js
================================================
import raf from './raf.js';
describe('The raf utility function', () => {
it('Throttles function calls that arrive in quick succession using Request Animation Frame', () => {
const unthrottledFunction = jasmine.createSpy('unthrottledFunction');
const throttledCallback = jasmine.createSpy('throttledCallback');
const throttledFunction = raf(throttledCallback);
for (let i = 0; i < 10; i++) {
unthrottledFunction();
throttledFunction();
}
return new Promise((resolve) => {
requestAnimationFrame(resolve);
}).then(() => {
expect(unthrottledFunction).toHaveBeenCalledTimes(10);
expect(throttledCallback).toHaveBeenCalledTimes(1);
});
});
it('Only invokes callback once per animation frame', () => {
const throttledCallback = jasmine.createSpy('throttledCallback');
const throttledFunction = raf(throttledCallback);
for (let i = 0; i < 10; i++) {
throttledFunction();
}
return new Promise((resolve) => {
requestAnimationFrame(resolve);
})
.then(() => {
return new Promise((resolve) => {
requestAnimationFrame(resolve);
});
})
.then(() => {
expect(throttledCallback).toHaveBeenCalledTimes(1);
});
});
it('Invokes callback again if called in subsequent animation frame', () => {
const throttledCallback = jasmine.createSpy('throttledCallback');
const throttledFunction = raf(throttledCallback);
for (let i = 0; i < 10; i++) {
throttledFunction();
}
return new Promise((resolve) => {
requestAnimationFrame(resolve);
})
.then(() => {
for (let i = 0; i < 10; i++) {
throttledFunction();
}
return new Promise((resolve) => {
requestAnimationFrame(resolve);
});
})
.then(() => {
expect(throttledCallback).toHaveBeenCalledTimes(2);
});
});
});
================================================
FILE: src/utils/random.js
================================================
/**
* Generates a pseudo-random number based on a seed.
*
* @param {number} seed - The seed value to generate the random number.
* @returns {number} A pseudo-random number between 0 (inclusive) and 1 (exclusive).
*/
function seededRandom(seed = Date.now()) {
const x = Math.sin(seed) * 10000;
return x - Math.floor(x);
}
export { seededRandom };
================================================
FILE: src/utils/sanitization.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
function filter__proto__(key, value) {
if (key !== '__proto__') {
return value;
}
}
export { filter__proto__ };
================================================
FILE: src/utils/staleness.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
class StalenessUtils {
constructor(openmct, domainObject) {
this.openmct = openmct;
this.domainObject = domainObject;
this.metadata = this.openmct.telemetry.getMetadata(domainObject);
this.lastStalenessResponseTime = 0;
this.setTimeSystem(this.openmct.time.getTimeSystem());
this.watchTimeSystem();
}
shouldUpdateStaleness(stalenessResponse, id) {
const stalenessResponseTime = this.parseTime(stalenessResponse);
// Accept latest staleness updates from staleness provider,
// regardless of whether the update occurred within time conductor bounds.
if (stalenessResponseTime > this.lastStalenessResponseTime) {
this.lastStalenessResponseTime = stalenessResponseTime;
return true;
} else {
return false;
}
}
watchTimeSystem() {
this.openmct.time.on('timeSystem', this.setTimeSystem, this);
}
unwatchTimeSystem() {
this.openmct.time.off('timeSystem', this.setTimeSystem, this);
}
setTimeSystem(timeSystem) {
this.timeSystem = timeSystem;
}
parseTime(stalenessResponse) {
const metadataValue = this.metadata.value(this.timeSystem.key) ?? {
format: this.timeSystem.key
};
const valueFormatter = this.openmct.telemetry.getValueFormatter(metadataValue);
const stalenessDatum = {
...stalenessResponse,
source: stalenessResponse[this.timeSystem.key]
};
return valueFormatter.parse(stalenessDatum);
}
destroy() {
this.unwatchTimeSystem();
}
}
export default StalenessUtils;
================================================
FILE: src/utils/template/templateHelpers.js
================================================
export function convertTemplateToHTML(templateString) {
const parser = new DOMParser();
const doc = parser.parseFromString(templateString, 'text/html');
// Create a document fragment to hold the parsed content
const fragment = document.createDocumentFragment();
// Append nodes from the parsed content to the fragment
while (doc.body.firstChild) {
fragment.appendChild(doc.body.firstChild);
}
// Convert children of the fragment to an array and return
return Array.from(fragment.children);
}
export function toggleClass(element, className) {
if (element.classList.contains(className)) {
element.classList.remove(className);
} else {
element.classList.add(className);
}
}
================================================
FILE: src/utils/template/templateHelpersSpec.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
import { toggleClass } from '@/utils/template/templateHelpers';
const CLASS_AS_NON_EMPTY_STRING = 'class-to-toggle';
const CLASS_AS_EMPTY_STRING = '';
const CLASS_DEFAULT = CLASS_AS_NON_EMPTY_STRING;
const CLASS_SECONDARY = 'another-class-to-toggle';
const CLASS_TERTIARY = 'yet-another-class-to-toggle';
const CLASS_TO_TOGGLE = CLASS_DEFAULT;
describe('toggleClass', () => {
describe('type checking', () => {
const A_DOM_NODE = document.createElement('div');
const NOT_A_DOM_NODE = 'not-a-dom-node';
describe('errors', () => {
it('throws when "className" is an empty string', () => {
expect(() => toggleClass(A_DOM_NODE, CLASS_AS_EMPTY_STRING)).toThrow();
});
it('throws when "element" is not a DOM node', () => {
expect(() => toggleClass(NOT_A_DOM_NODE, CLASS_DEFAULT)).toThrow();
});
});
describe('success', () => {
it('does not throw when "className" is not an empty string', () => {
expect(() => toggleClass(A_DOM_NODE, CLASS_AS_NON_EMPTY_STRING)).not.toThrow();
});
it('does not throw when "element" is a DOM node', () => {
expect(() => toggleClass(A_DOM_NODE, CLASS_DEFAULT)).not.toThrow();
});
});
});
describe('adding a class', () => {
it('adds specified class to an element without any classes', () => {
// test case
const ELEMENT_WITHOUT_CLASS = document.createElement('div');
toggleClass(ELEMENT_WITHOUT_CLASS, CLASS_TO_TOGGLE);
// expected
const ELEMENT_WITHOUT_CLASS_EXPECTED = document.createElement('div');
ELEMENT_WITHOUT_CLASS_EXPECTED.classList.add(CLASS_TO_TOGGLE);
expect(ELEMENT_WITHOUT_CLASS).toEqual(ELEMENT_WITHOUT_CLASS_EXPECTED);
});
it('adds specified class to an element that already has another class', () => {
// test case
const ELEMENT_WITH_SINGLE_CLASS = document.createElement('div');
ELEMENT_WITH_SINGLE_CLASS.classList.add(CLASS_SECONDARY);
toggleClass(ELEMENT_WITH_SINGLE_CLASS, CLASS_TO_TOGGLE);
// expected
const ELEMENT_WITH_SINGLE_CLASS_EXPECTED = document.createElement('div');
ELEMENT_WITH_SINGLE_CLASS_EXPECTED.classList.add(CLASS_SECONDARY, CLASS_TO_TOGGLE);
expect(ELEMENT_WITH_SINGLE_CLASS).toEqual(ELEMENT_WITH_SINGLE_CLASS_EXPECTED);
});
it('adds specified class to an element that already has more than one other classes', () => {
// test case
const ELEMENT_WITH_MULTIPLE_CLASSES = document.createElement('div');
ELEMENT_WITH_MULTIPLE_CLASSES.classList.add(CLASS_TO_TOGGLE, CLASS_SECONDARY);
toggleClass(ELEMENT_WITH_MULTIPLE_CLASSES, CLASS_TO_TOGGLE);
// expected
const ELEMENT_WITH_MULTIPLE_CLASSES_EXPECTED = document.createElement('div');
ELEMENT_WITH_MULTIPLE_CLASSES_EXPECTED.classList.add(CLASS_SECONDARY);
expect(ELEMENT_WITH_MULTIPLE_CLASSES).toEqual(ELEMENT_WITH_MULTIPLE_CLASSES_EXPECTED);
});
});
describe('removing a class', () => {
it('removes specified class from an element that only has the specified class', () => {
// test case
const ELEMENT_WITH_ONLY_SPECIFIED_CLASS = document.createElement('div');
ELEMENT_WITH_ONLY_SPECIFIED_CLASS.classList.add(CLASS_TO_TOGGLE);
toggleClass(ELEMENT_WITH_ONLY_SPECIFIED_CLASS, CLASS_TO_TOGGLE);
// expected
const ELEMENT_WITH_ONLY_SPECIFIED_CLASS_EXPECTED = document.createElement('div');
ELEMENT_WITH_ONLY_SPECIFIED_CLASS_EXPECTED.className = '';
expect(ELEMENT_WITH_ONLY_SPECIFIED_CLASS).toEqual(ELEMENT_WITH_ONLY_SPECIFIED_CLASS_EXPECTED);
});
it('removes specified class from an element that has specified class, and others', () => {
// test case
const ELEMENT_WITH_SPECIFIED_CLASS_AND_OTHERS = document.createElement('div');
ELEMENT_WITH_SPECIFIED_CLASS_AND_OTHERS.classList.add(
CLASS_TO_TOGGLE,
CLASS_SECONDARY,
CLASS_TERTIARY
);
toggleClass(ELEMENT_WITH_SPECIFIED_CLASS_AND_OTHERS, CLASS_TO_TOGGLE);
// expected
const ELEMENT_WITH_SPECIFIED_CLASS_AND_OTHERS_EXPECTED = document.createElement('div');
ELEMENT_WITH_SPECIFIED_CLASS_AND_OTHERS_EXPECTED.classList.add(
CLASS_SECONDARY,
CLASS_TERTIARY
);
expect(ELEMENT_WITH_SPECIFIED_CLASS_AND_OTHERS).toEqual(
ELEMENT_WITH_SPECIFIED_CLASS_AND_OTHERS_EXPECTED
);
});
});
});
================================================
FILE: src/utils/testing/mockLocalStorage.js
================================================
export function mockLocalStorage() {
let store;
beforeEach(() => {
spyOn(Storage.prototype, 'getItem').and.callFake(getItem);
spyOn(Storage.prototype, 'setItem').and.callFake(setItem);
spyOn(Storage.prototype, 'removeItem').and.callFake(removeItem);
spyOn(Storage.prototype, 'clear').and.callFake(clear);
store = {};
function getItem(key) {
return store[key] || null;
}
function setItem(key, value) {
store[key] = typeof value === 'string' ? value : JSON.stringify(value);
}
function removeItem(key) {
store[key] = undefined;
delete store[key];
}
function clear() {
store = {};
}
});
afterEach(() => {
store = undefined;
});
}
================================================
FILE: src/utils/testing.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
import { MCT } from 'MCT';
import { markRaw } from 'vue';
let nativeFunctions = [];
let mockObjects = setMockObjects();
const EXAMPLE_ROLE = 'flight';
const DEFAULT_TIME_OPTIONS = {
timeSystemKey: 'utc',
bounds: {
start: 0,
end: 1
}
};
export function createOpenMct(timeSystemOptions = DEFAULT_TIME_OPTIONS) {
const openmct = markRaw(new MCT());
openmct.install(openmct.plugins.LocalStorage());
openmct.install(openmct.plugins.UTCTimeSystem());
openmct.setAssetPath('/base');
openmct.user.setActiveRole(EXAMPLE_ROLE);
const timeSystemKey = timeSystemOptions.timeSystemKey;
const start = timeSystemOptions.bounds.start;
const end = timeSystemOptions.bounds.end;
openmct.time.setTimeSystem(timeSystemKey, {
start,
end
});
return openmct;
}
export function createMouseEvent(eventName) {
return new MouseEvent(eventName, {
bubbles: true,
cancelable: true,
view: window
});
}
export function spyOnBuiltins(functionNames, object = window) {
functionNames.forEach((functionName) => {
if (nativeFunctions[functionName]) {
throw `Builtin spy function already defined for ${functionName}`;
}
nativeFunctions.push({
functionName,
object,
nativeFunction: object[functionName]
});
spyOn(object, functionName);
});
}
export function clearBuiltinSpies() {
nativeFunctions.forEach(clearBuiltinSpy);
nativeFunctions = [];
}
export function resetApplicationState(openmct) {
clearBuiltinSpies();
if (openmct) {
openmct.destroy();
}
if (window.location.hash !== '#' && window.location.hash !== '') {
window.location.hash = '#';
// Optionally wait for hashchange if necessary
return new Promise((resolve) => {
// eslint-disable-next-line func-style
const onHashChange = () => {
window.removeEventListener('hashchange', onHashChange);
resolve();
};
window.addEventListener('hashchange', onHashChange);
});
} else {
return Promise.resolve();
}
}
// required: key
// optional: element, keyCode, type
export function simulateKeyEvent(opts) {
if (!opts.key) {
console.warn('simulateKeyEvent needs a key');
return;
}
const el = opts.element || document;
const key = opts.key;
const keyCode = opts.keyCode || key;
const type = opts.type || 'keydown';
const event = new Event(type);
event.keyCode = keyCode;
event.key = key;
el.dispatchEvent(event);
}
function clearBuiltinSpy(funcDefinition) {
funcDefinition.object[funcDefinition.functionName] = funcDefinition.nativeFunction;
}
export function getLatestTelemetry(telemetry = [], opts = {}) {
let latest = [];
let timeFormat = opts.timeFormat || 'utc';
if (telemetry.length) {
latest = telemetry.reduce((prev, cur) => {
return prev[timeFormat] > cur[timeFormat] ? prev : cur;
});
}
return latest;
}
// EXAMPLE:
// getMockObjects({
// name: 'Jamie Telemetry',
// keys: ['test','other','yeah','sup'],
// format: 'local',
// telemetryConfig: {
// hints: {
// test: {
// domain: 1
// },
// other: {
// range: 2
// }
// }
// }
// })
export function getMockObjects(opts = {}) {
opts.type = opts.type || 'default';
if (opts.objectKeyStrings && !Array.isArray(opts.objectKeyStrings)) {
throw `"getMockObjects" optional parameter "objectKeyStrings" must be an array of string object keys`;
}
let requestedMocks = {};
if (!opts.objectKeyStrings) {
requestedMocks = copyObj(mockObjects[opts.type]);
} else {
opts.objectKeyStrings.forEach((objKey) => {
if (mockObjects[opts.type] && mockObjects[opts.type][objKey]) {
requestedMocks[objKey] = copyObj(mockObjects[opts.type][objKey]);
} else {
throw `No mock object for object key "${objKey}" of type "${opts.type}"`;
}
});
}
// build out custom telemetry mappings if necessary
if (requestedMocks.telemetry && opts.telemetryConfig) {
let keys = opts.telemetryConfig.keys;
let format = opts.telemetryConfig.format || 'utc';
let hints = opts.telemetryConfig.hints;
let values;
// if utc, keep default
if (format === 'utc') {
// save for later if new keys
if (keys) {
format = requestedMocks.telemetry.telemetry.values.find((vals) => vals.key === 'utc');
}
} else {
format = {
key: format,
name: 'Time',
format: format === 'local' ? 'local-format' : format,
hints: {
domain: 1
}
};
}
if (keys) {
values = keys.map((key) => ({
key,
name: key + ' attribute'
}));
values.push(format); // add time format back in
} else {
values = requestedMocks.telemetry.telemetry.values;
}
if (hints) {
for (let val of values) {
if (hints[val.key]) {
val.hints = hints[val.key];
}
}
}
requestedMocks.telemetry.telemetry.values = values;
}
// overwrite any field keys
if (opts.overwrite) {
for (let mock in requestedMocks) {
if (opts.overwrite[mock]) {
requestedMocks[mock] = Object.assign(requestedMocks[mock], opts.overwrite[mock]);
}
}
}
return requestedMocks;
}
// EXAMPLE:
// getMockTelemetry({
// name: 'My Telemetry',
// keys: ['test','other','yeah','sup'],
// count: 8,
// format: 'local'
// })
export function getMockTelemetry(opts = {}) {
let count = opts.count || 2;
let format = opts.format || 'utc';
let name = opts.name || 'Mock Telemetry Datum';
let keyCount = 2;
let keys = false;
let telemetry = [];
if (opts.keys && Array.isArray(opts.keys)) {
keyCount = opts.keys.length;
keys = opts.keys;
} else if (opts.keyCount) {
keyCount = opts.keyCount;
}
for (let i = 1; i < count + 1; i++) {
let datum = {
[format]: i,
name
};
for (let k = 1; k < keyCount + 1; k++) {
let key = keys ? keys[k - 1] : 'some-key-' + k;
let value = keys ? keys[k - 1] + ' value ' + i : 'some value ' + i + '-' + k;
datum[key] = value;
}
telemetry.push(datum);
}
return telemetry;
}
// used to inject into tests that require a render
export function renderWhenVisible(func) {
func();
return true;
}
// copy objects a bit more easily
function copyObj(obj) {
return JSON.parse(JSON.stringify(obj));
}
// add any other necessary types to this mockObjects object
function setMockObjects() {
return {
default: {
folder: {
identifier: {
namespace: '',
key: 'folder-object'
},
name: 'Test Folder Object',
type: 'folder',
composition: [],
location: 'mine'
},
ladTable: {
identifier: {
namespace: '',
key: 'lad-object'
},
type: 'LadTable',
composition: []
},
ladTableSet: {
identifier: {
namespace: '',
key: 'lad-set-object'
},
type: 'LadTableSet',
composition: []
},
telemetry: {
identifier: {
namespace: '',
key: 'telemetry-object'
},
type: 'test-telemetry-object',
name: 'Test Telemetry Object',
telemetry: {
values: [
{
key: 'name',
name: 'Name',
format: 'string'
},
{
key: 'utc',
name: 'Time',
format: 'utc',
hints: {
domain: 1
}
},
{
name: 'Some attribute 1',
key: 'some-key-1',
hints: {
range: 1
}
},
{
name: 'Some attribute 2',
key: 'some-key-2'
}
]
}
}
},
otherType: {
example: {}
}
};
}
================================================
FILE: src/utils/textHighlight/TextHighlight.vue
================================================
================================================
FILE: src/utils/throttle.js
================================================
/**
* Creates a throttled function that only invokes the provided function at most once every
* specified number of milliseconds. Subsequent calls within the waiting period will be ignored.
* @param {Function} func The function to throttle.
* @param {number} wait The number of milliseconds to wait between successive calls to the function.
* @return {Function} Returns the new throttled function.
*/
export default function throttle(func, wait) {
let timeout;
let result;
let previous = 0;
return function (...args) {
const now = new Date().getTime();
const remaining = wait - (now - previous);
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
result = func(...args);
} else if (!timeout) {
timeout = setTimeout(() => {
previous = new Date().getTime();
timeout = null;
result = func(...args);
}, remaining);
}
return result;
};
}
================================================
FILE: src/utils/useEventBus.js
================================================
import emitter from 'tiny-emitter/instance.js';
import { ref } from 'vue';
export function useEventBus() {
// Create a reactive reference to the emitter
const reactiveEmitter = ref(emitter);
// Expose the emitter's methods
const EventBus = {
$on: (...args) => reactiveEmitter.value.on(...args),
$once: (...args) => reactiveEmitter.value.once(...args),
$off: (...args) => reactiveEmitter.value.off(...args),
$emit: (...args) => reactiveEmitter.value.emit(...args)
};
return {
EventBus
};
}
================================================
FILE: src/utils/visibility/VisibilityObserver.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
/**
* Optimizes `requestAnimationFrame` calls to only execute when the element is visible in the viewport.
*/
class VisibilityObserver {
/**
* @type {HTMLElement | null}
*/
#element;
/**
* @type {IntersectionObserver | null}
*/
#observer;
/**
* @type {(() => void) | null}
*/
lastUnfiredFunc;
/**
* @type {boolean | null}
*/
isIntersecting;
/**
* @type {boolean}
*/
calledOnce;
/**
* Constructs a VisibilityObserver instance to manage visibility-based requestAnimationFrame calls.
*
* @param {HTMLElement} element - The DOM element to observe for visibility changes.
* @param {HTMLElement} rootContainer - The DOM element that is the root of the viewport.
* @throws {Error} If element is not provided.
*/
constructor(element, rootContainer) {
if (!element || !rootContainer) {
throw new Error(`VisibilityObserver must be created with an element and a rootContainer.`);
}
this.#element = element;
this.isIntersecting = true;
this.calledOnce = false;
const options = {
root: rootContainer
};
this.#observer = new IntersectionObserver(this.#observerCallback, options);
this.lastUnfiredFunc = null;
this.renderWhenVisible = this.renderWhenVisible.bind(this);
}
/**
* @returns {boolean}
*/
#inOverlay() {
return this.#element?.closest('.js-overlay');
}
#observerCallback = (entries) => {
const entry = entries[0];
if (entry && entry.target === this.#element) {
if (this.#inOverlay() && !entry.isIntersecting) {
this.isIntersecting = true;
} else {
this.isIntersecting = entry.isIntersecting;
}
if (this.isIntersecting && this.lastUnfiredFunc) {
window.requestAnimationFrame(this.lastUnfiredFunc);
this.lastUnfiredFunc = null;
}
}
};
/**
* Executes a function within requestAnimationFrame if the observed element is visible.
* If the element is not visible, the function is stored and called when the element becomes visible.
* Note that if called multiple times while not visible, only the last execution is stored and executed.
*
* @param {() => void} func - The function to execute.
* @returns {boolean} True if the function was executed immediately, false otherwise.
*/
renderWhenVisible(func) {
if (!this.calledOnce) {
this.calledOnce = true;
if (!this.#observer || !this.#element) {
this.lastUnfiredFunc = func;
return false;
}
this.#observer.observe(this.#element);
} else if (!this.isIntersecting) {
this.lastUnfiredFunc = func;
return false;
}
window.requestAnimationFrame(func);
return true;
}
/**
* Stops observing the element for visibility changes and cleans up resources to prevent memory leaks.
*/
destroy() {
if (this.#observer && this.#element) {
this.#observer.unobserve(this.#element);
}
this.#element = null;
this.isIntersecting = null;
this.#observer = null;
this.lastUnfiredFunc = null;
}
}
export default VisibilityObserver;
================================================
FILE: src/utils/vue/useDragResizer.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
import { ref } from 'vue';
/**
* @typedef {Object} DragResizerOptions the options object
* @property {number} [initialX=0] the initial x of the object to track size for
* @property {number} [initialY=0] the initial y of the object to track size for
* @property {Function} [callback] the function to call when drag is complete
*/
/**
* @typedef {Object} ReturnObject the return object
* @property {number} x the reactive horizontal size during/post drag
* @property {number} y the reactive vertical size during/post drag
* @property {function} mousedown
*/
/**
* Defines a drag resizer hook that tracks the size of an object
* in vertical and horizontal direction on drag
* @param {DragResizerOptions} [param={}] the options object
* @returns {ReturnObject}
*/
export function useDragResizer({ initialX = 0, initialY = 0, callback } = {}) {
const x = ref(initialX);
const y = ref(initialY);
const isDragging = ref(false);
let dragStartX;
let dragStartY;
let dragStartClientX;
let dragStartClientY;
/**
* Begins the tracking process for the drag resizer
* and attaches mousemove and mousedown listeners to track size after drag completion
* Attach to a mousedown event for a draggable element
* @param {*} event the mousedown event
*/
function mousedown(event) {
dragStartX = x.value;
dragStartY = y.value;
dragStartClientX = event.clientX;
dragStartClientY = event.clientY;
isDragging.value = true;
document.addEventListener('mouseup', mouseup, {
once: true,
capture: true
});
document.addEventListener('mousemove', mousemove);
event.preventDefault();
}
function mousemove(event) {
const deltaX = event.clientX - dragStartClientX;
const deltaY = event.clientY - dragStartClientY;
x.value = dragStartX + deltaX;
y.value = dragStartY + deltaY;
}
function mouseup(event) {
dragStartX = undefined;
dragStartY = undefined;
dragStartClientX = undefined;
dragStartClientY = undefined;
isDragging.value = false;
document.removeEventListener('mousemove', mousemove);
event.preventDefault();
event.stopPropagation();
callback?.();
}
return {
mousedown,
x,
y,
isDragging
};
}
================================================
FILE: src/utils/vue/useFlexContainers.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
import { computed, ref } from 'vue';
/**
* @typedef {Object} configuration
* @property {boolean} rowsLayout true if containers arranged as rows, false if columns
* @property {number} minContainerSize minimum size in pixels of a container
* @property {Function} callback function to call when container resize completes
*/
/**
* Provides a means to size a collection of containers to a total size of 100%.
* The containers will resize proportionally to fit the total size on add/remove.
* The containers will initially be sized based on their scale property.
* @param {import('vue').Ref {
return containers.value
.filter((container) => container.fixed === true)
.reduce((size, currentContainer) => size + currentContainer.size, 0);
});
function addContainer(container) {
containers.value.push(container);
sizeItems(containers.value);
roundExcess(containers.value);
callback?.();
}
function removeContainer(index) {
const isFlexContainer = !containers.value[index].fixed;
containers.value.splice(index, 1);
if (isFlexContainer) {
sizeItems(containers.value);
roundExcess(containers.value);
}
callback?.();
}
function reorderContainers(reorderPlan) {
const oldContainers = containers.value.slice();
reorderPlan.forEach((reorderEvent) => {
containers.value[reorderEvent.newIndex] = oldContainers[reorderEvent.oldIndex];
});
callback?.();
}
function setContainers(_containers) {
containers.value = _containers;
sizeItems(containers.value);
roundExcess(containers.value);
}
function startContainerResizing(index) {
const beforeContainer = getBeforeContainer(index);
const afterContainer = getAfterContainer(index);
if (beforeContainer && afterContainer && !beforeContainer.fixed && !afterContainer.fixed) {
maxMoveSize.value = beforeContainer.size + afterContainer.size;
}
}
function getBeforeContainer(index) {
return containers.value
.slice(0, index + 1)
.filter((container) => !container.fixed === true)
.at(-1);
}
function getAfterContainer(index) {
return containers.value.slice(index + 1).filter((container) => !container.fixed === true)[0];
}
function containerResizing(index, delta, event) {
const beforeContainer = getBeforeContainer(index);
const afterContainer = getAfterContainer(index);
const percentageMoved = Math.round((delta / getElSize()) * 100);
if (beforeContainer && afterContainer && !beforeContainer.fixed && !afterContainer.fixed) {
beforeContainer.size = getContainerSize(beforeContainer.size + percentageMoved);
afterContainer.size = getContainerSize(afterContainer.size - percentageMoved);
} else {
console.warn(
'Drag requires two flexible containers. Use Elements Tab in Inspector to resize.'
);
}
}
function endContainerResizing() {
callback?.();
}
function getElSize() {
const elSize = rowsLayout === true ? element.value.offsetHeight : element.value.offsetWidth;
// TODO FIXME temporary patch for timeline
const timelineHeight = 32;
return elSize - fixedContainersSize.value - timelineHeight;
}
function getContainerSize(size) {
if (size < minContainerSize) {
return minContainerSize;
} else if (size > maxMoveSize.value - minContainerSize) {
return maxMoveSize.value - minContainerSize;
} else {
return size;
}
}
/**
* Resize flexible sized items so they fit proportionally within a viewport
* 1. add size to 0 sized items based on scale proportional to total scale
* 2. resize item sizes to equal 100
* if total size < 100, resize all items
* if total size > 100, resize only items not resized in step 1 (newly added)
*
* Items may have a scale (ie. items with composition)
*
* Handles single add or removal, as well as atypical use cases,
* such as composition out of sync with containers config
* due to composition edits outside of view
*
* Typically roundExcess is called afterwards to limit pixels and percents to integers
*
* @param {*} items
*/
function sizeItems(items) {
let totalSize;
const flexItems = items.filter((item) => !item.fixed);
if (flexItems.length === 0) {
return;
}
if (flexItems.length === 1) {
flexItems[0].size = 100;
return;
}
const flexItemsWithSize = flexItems.filter((item) => item.size);
const flexItemsWithoutSize = flexItems.filter((item) => !item.size);
// total number of flexible items, adjusted by each item scale
const totalScale = flexItems.reduce((total, item) => {
const scale = item.scale ?? 1;
return total + scale;
}, 0);
flexItemsWithoutSize.forEach((item) => {
const scale = item.scale ?? 1;
item.size = Math.round((100 * scale) / totalScale);
});
totalSize = flexItems.reduce((total, item) => total + item.size, 0);
if (totalSize > 100) {
const addedSize = flexItemsWithoutSize.reduce((total, item) => total + item.size, 0);
const remainingSize = 100 - addedSize;
flexItemsWithSize.forEach((item) => {
item.size = Math.round((item.size * remainingSize) / 100);
});
} else if (totalSize < 100) {
flexItems.forEach((item) => {
item.size = Math.round((item.size * 100) / totalSize);
});
}
}
/**
*
* Rounds excess and applies to one of the items
* if an optional index is not specified, excess applied to last item
*
* @param {*} items
* @param {Number} (optional) index of the item to apply excess to in the event of rounding errors
*/
function roundExcess(items, specifiedIndex) {
const flexItems = items.filter((item) => !item.fixed);
if (!flexItems.length) {
return;
}
const totalSize = flexItems.reduce((total, item) => total + item.size, 0);
const excess = Math.round(100 - totalSize);
let index;
if (specifiedIndex !== undefined && items[specifiedIndex] && !items[specifiedIndex].fixed) {
index = specifiedIndex;
}
if (index === undefined) {
index = items.findLastIndex((item) => !item.fixed);
}
if (index > -1) {
items[index].size += excess;
}
}
function toggleFixed(index, fixed) {
let addExcessToContainer;
const remainingItems = containers.value.slice();
const container = remainingItems.splice(index, 1)[0];
if (container.fixed !== fixed) {
if (fixed) {
// toggle flex to fixed
container.size = Math.round((container.size / 100) * getElSize());
container.fixed = fixed;
sizeItems(remainingItems);
} else {
// toggle fixed to flex
addExcessToContainer = index;
container.size = Math.round((container.size * 100) / (getElSize() + container.size));
const remainingSize = 100 - container.size;
remainingItems
.filter((item) => !item.fixed)
.forEach((item) => {
item.size = Math.round((item.size * remainingSize) / 100);
});
container.fixed = fixed;
}
roundExcess(containers.value, addExcessToContainer);
callback?.();
}
}
function sizeFixedContainer(index, size) {
const container = containers.value[index];
if (container.fixed) {
container.size = size;
callback?.();
} else {
console.warn('Use view drag resizing to resize flexible containers.');
}
}
return {
addContainer,
removeContainer,
reorderContainers,
setContainers,
containers,
startContainerResizing,
containerResizing,
endContainerResizing,
toggleFixed,
sizeFixedContainer
};
}
================================================
FILE: src/utils/vue/useIsEditing.js
================================================
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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.
*****************************************************************************/
import { onBeforeUnmount, onMounted, ref } from 'vue';
export default function useIsEditing(openmct) {
const isEditing = ref(openmct.editor.isEditing());
onMounted(() => {
openmct.editor.on('isEditing', setIsEditing);
});
onBeforeUnmount(() => {
openmct.editor.off('isEditing', setIsEditing);
});
function setIsEditing(_isEditing) {
isEditing.value = _isEditing;
}
return {
isEditing
};
}
================================================
FILE: src/utils/vueWrapHtmlElement.js
================================================
import { defineComponent, h, onMounted, ref } from 'vue';
/**
* Compatibility wrapper for wrapping an HTMLElement in a Vue component.
*
* @param {HTMLElement} element
* @returns {import('vue').Component}
*/
export default function vueWrapHtmlElement(element) {
return defineComponent({
setup() {
/** @type {import('vue').Ref} */
const wrapper = ref(null);
onMounted(() => {
if (wrapper.value) {
wrapper.value.appendChild(element);
}
});
// Render function returning the wrapper div
// Use class 'u-contents' to set 'display: contents' of the parent div
return () => h('div', { ref: wrapper, class: 'u-contents' });
}
});
}
================================================
FILE: tsconfig.json
================================================
/* Note: Open MCT does not intend to support the entire Typescript ecosystem at this time.
* This file is intended to add Intellisense for IDEs like VSCode. For more information
* about Typescript, please discuss in https://github.com/nasa/openmct/discussions/4693
*/
{
"compilerOptions": {
"target": "ES6",
"baseUrl": "./",
"allowJs": true,
"checkJs": false,
"declaration": true,
"emitDeclarationOnly": true,
"declarationMap": true,
"strict": true,
"esModuleInterop": true,
"noImplicitOverride": true,
"noImplicitAny": false,
"outFile": "dist/types/index.d.ts",
"module": "NodeNext",
"moduleResolution": "NodeNext"
},
"include": [
"src/api/**/*.js"
],
"exclude": [
"**/*Spec.js"
]
}