diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b0a5039b..633624843 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,8 +13,10 @@ ## Improvements - Refactored the `layers/` directory structure to organize Layer File Formats into versioned subdirectories and removed outdated layer samples. See pull request [#649](https://github.com/mitre-attack/attack-navigator/pull/649). +- Improved toolbar for better usability. See issue [#534](https://github.com/mitre-attack/attack-navigator/issues/534). - Updated Angular from v14 to v17. + # 5.0.1 - 9 May 2024 ## Fixes diff --git a/nav-app/src/app/app.module.ts b/nav-app/src/app/app.module.ts index 297712f1e..9664e5a04 100755 --- a/nav-app/src/app/app.module.ts +++ b/nav-app/src/app/app.module.ts @@ -41,6 +41,7 @@ import { MatCardModule } from '@angular/material/card'; import { MatDividerModule } from '@angular/material/divider'; import { MatStepperModule } from '@angular/material/stepper'; import { MatPaginatorModule } from '@angular/material/paginator'; +import { LayerSettingsComponent } from './layer-settings/layer-settings.component'; import { MarkdownModule } from 'ngx-markdown'; import { LayerInformationComponent } from './layer-information/layer-information.component'; @@ -71,6 +72,7 @@ import { ConfigService } from './services/config.service'; LayerInformationComponent, ChangelogComponent, ListInputComponent, + LayerSettingsComponent, ], imports: [ BrowserModule, diff --git a/nav-app/src/app/classes/view-model.ts b/nav-app/src/app/classes/view-model.ts index 48dd8b99f..da46bc252 100644 --- a/nav-app/src/app/classes/view-model.ts +++ b/nav-app/src/app/classes/view-model.ts @@ -106,7 +106,7 @@ export class ViewModel { this._sidebarOpened = newVal; } - public readonly sidebarContentTypes = ['layerUpgrade', 'search']; + public readonly sidebarContentTypes = ['layerUpgrade', 'search', 'layerSettings']; private _sidebarContentType: string; public get sidebarContentType(): string { return this._sidebarContentType; diff --git a/nav-app/src/app/datatable/data-table.component.html b/nav-app/src/app/datatable/data-table.component.html index 1cc48bb8b..2d6c27f6e 100755 --- a/nav-app/src/app/datatable/data-table.component.html +++ b/nav-app/src/app/datatable/data-table.component.html @@ -6,769 +6,778 @@ 888oooo88 88ooo88 o88o 88 o888o o888o 88o8 88ooo88 o888ooooo88 o88oooo888 --> -
-
- -
- - - -
- -
- - - -
- - - - -
- - - - -
- - - - -
-
- layers_clear + +
+
+ {{getControlDisplayName('technique_controls','clear_annotations') | titlecase}} + layers_clear +
-
- -
  • - -
    -
    - push_pin - +
    + +
    -
    -
  • - + +
  • + +
    +
    + push_pin + +
    +
    +
  • +
    +-->
    @@ -809,11 +818,11 @@ +-->
    keyboard_arrow_up diff --git a/nav-app/src/app/datatable/data-table.component.scss b/nav-app/src/app/datatable/data-table.component.scss index 3f6904fee..6efe03354 100755 --- a/nav-app/src/app/datatable/data-table.component.scss +++ b/nav-app/src/app/datatable/data-table.component.scss @@ -35,13 +35,11 @@ $cellSize: 15px; } .matrices-columns { - // white-space: nowrap; display: table; .matrix-column { display: table-cell; white-space: normal; - // display: inline-block; padding: 10px; @media print { padding: 0; @@ -62,10 +60,6 @@ $cellSize: 15px; } } -.multiselect-dropdown { - display: inline-block; -} - .colorpicker { width: ($cellSize + 5 + 2) * 4 !important; //override preset width align-items: center; @@ -114,7 +108,6 @@ $cellSize: 15px; } .display-buttons { - // width: 183px; text-align: center; .squarebutton { @@ -160,7 +153,6 @@ $cellSize: 15px; } &.buttons > div { - // border: 1px solid black; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -181,15 +173,12 @@ $cellSize: 15px; select { width: 80px; } - - // width: 100%; } &.col2 { width: 45px; input[type='number'] { - // background-color: rgb(104, 60, 213); width: 40px; } } @@ -198,15 +187,11 @@ $cellSize: 15px; .minmax { @include adaptive-color('background-color', color(panel-light), color(dark-4)); text-align: center; - // &.top { border-top: 1px solid black; border-bottom: 1px solid black} - // &.bottom { border-top: 1px solid black} } } - // button {background: red;} .addcolor { width: 100%; - // background: red; } } } @@ -216,80 +201,86 @@ $cellSize: 15px; text-align: left; } -.contextMenu-cover { - // covers entire page - position: fixed; - left: 0; - top: 0; - width: 100vw; - height: 100vh; - // background: rgba(0, 0, 0, 0.15); -} - -.contextMenu-box { - position: absolute; - // background: white; - &.left { - right: 0; +.control-section-header { + display: flex; + justify-content: flex-end; + align-items: center; + + height: 30px; + } + + .control-section-tabs { + display: flex; + justify-content: flex-end; + align-items: center; + + .section-label { + cursor: pointer; + + padding: 6px 24px; + + @include adaptive-color("color", black, white); + + @include adaptive-color("border-color", black, white); + border-style: solid; + border-width: 1px; + + &.active { + @include adaptive-color("background-color", white, #364370); + @include adaptive-color("border-color", #464DFF, #464DFF); + } } - - border: 1px solid black; - background-color: white; - box-shadow: 10px 10px 5px rgba(0, 0, 0, 0.5); - position: absolute; - z-index: 100; //draw on top of other controls - width: max-content; - - .contextMenu-section { - &:not(:first-child) { - border-top: 1px solid color(panel-dark); - margin-top: 2px; - padding-top: 2px; + + .control-bar-help { + .control-help-button { + text-align: center; + width: 4ex; + height: 3ex; + padding: 5px; + + svg { + @include adaptive-color-dark-only("fill", on-color(dark)); } - - .contextMenu-button { - padding: 3px; - cursor: pointer; - + &:hover svg { + @include adaptive-color-dark-only("fill", on-color(dark)); + fill: #505050; + } + } + + .dropdown-container { + @include adaptive-color-dark-only("background", color(dark-3)); + @include adaptive-color-dark-only("border-color", color(dark-3)); + @include adaptive-color-dark-only("box-shadow", 2px 1px 1px color(dark-2)); + @include adaptive-color-dark-only("color", color(dark-link)); + position: absolute; + right: 16px; + display: flex; + justify-content: center; + flex-flow: column; + background: white; + border-style: solid; + border-color: #ddd; + border-width: 1px 0 1px 1px; + box-shadow: 2px 1px 1px #ddd; + border-radius: 0 0 5px 5px; + padding: 10px; + top: 40px; + z-index: 9; + button { + @include adaptive-color-dark-only("color", on-color(dark)); + border: none; + background-color: transparent; + text-align: right; + padding-bottom: 5px; &:hover { - @include adaptive-color('background', color(cell-highlight-color), color(cell-highlight-dark-color)); + text-decoration: underline; + cursor: pointer; } } + } } } -// .tooltip { -// position: absolute; -// z-index: 100; //draw on top of other controls - -// padding: 6px; -// border-radius: 3px; -// background: rgba(80, 80, 80, 0.75); - -// font-size: 8pt; -// color: white; - -// max-width: 150px; -// overflow-x: hidden; - -// .comment { -// max-height: 300px; -// overflow-y: hidden; -// } - -// .comment-overflow-note { -// // color: rgb(255, 199, 190); -// } -// .metadatalist { -// margin: 0; -// margin-top: 1px; -// padding-left: 6px; -// list-style: none; - -// } - -// } - .mat-mdc-select { :focus { color: #63961c; @@ -323,14 +314,12 @@ $cellSize: 15px; } .multiselect { - // padding: 4px; text-align: center; .multiselect-grouping { .multiselect-grouping-label { padding: 4px; font-weight: bold; - // font-size: 14pt; } .multiselect-list { @@ -341,7 +330,6 @@ $cellSize: 15px; overflow-y: scroll; table { - // table-layout:fixed; //fixes width border-collapse: collapse; } @@ -363,7 +351,6 @@ $cellSize: 15px; } .search { - // padding: 4px; text-align: center; .search-list { @@ -375,7 +362,6 @@ $cellSize: 15px; overflow-y: scroll; table { - // table-layout:fixed; //fixes width border-collapse: collapse; width: 325px; } @@ -408,7 +394,6 @@ $cellSize: 15px; border: none; padding: 4px 10px; text-align: center; - //font-size: 16px; margin: 2px 1px; transition: 0.3s; display: inline-block; @@ -493,31 +478,6 @@ $cellSize: 15px; } } -.layer_info { - padding: 0 !important; - box-sizing: border-box !important; - width: 300px !important; - - .name_desc { - padding: 0 10px; - } - - .layer-data { - text-align: left; - margin: 0 10px; - - .mat-divider.layer-div { - margin-bottom: 10px; - @include adaptive-color-dark-only('border-top-color', color(dark-4)); - } - } - - .data-input { - overflow-y: auto; - max-height: 25vh; - } -} - .layout { width: 100px; text-align: left; diff --git a/nav-app/src/app/datatable/data-table.component.ts b/nav-app/src/app/datatable/data-table.component.ts index 056dfe208..51c9f0174 100755 --- a/nav-app/src/app/datatable/data-table.component.ts +++ b/nav-app/src/app/datatable/data-table.component.ts @@ -38,6 +38,7 @@ export class DataTableComponent implements AfterViewInit, OnDestroy { public headerHeight: number = 0; public footerHeight: number = 32; public controlsHeight: number = 34; + public includedControls: any; public isScrollUp: boolean = true; public handleScroll = (e) => { const diff = this.scrollRef.nativeElement.scrollTop - this.previousScrollTop; @@ -56,13 +57,37 @@ export class DataTableComponent implements AfterViewInit, OnDestroy { this.scrollRef.nativeElement.style.height = `calc(100vh - ${scrollWindowHeight}px)`; }; + /* + * 0: expand subtechniques + * 1: expand annotated subtechniques + * 2: collapse subtechniques + */ + public showSubtechniquesType: number = 0; + public downloadAnnotationsOnVisibleTechniques: boolean = false; + showControlsBar = true; + previousControlSection = ''; + + currentControlSection = "selection"; + + showHelpDropDown = false; + // edit field bindings public commentEditField: string = ''; public scoreEditField: string = ''; private selectionChangeSubscription: Subscription; + + public layerControlsList = []; + public techniqueControlsList = []; + public selectionControlsList = []; + + showControlLabels = { + selection: false, + layer: false, + technique: false, + }; constructor( public dataService: DataService, @@ -74,6 +99,24 @@ export class DataTableComponent implements AfterViewInit, OnDestroy { this.selectionChangeSubscription = this.viewModelsService.onSelectionChange.subscribe(() => { this.onTechniqueSelect(); }); + this.includedControls = configService.featureList; + for(let i=0;i + + + + + + + + + +
    \ No newline at end of file diff --git a/nav-app/src/app/layer-settings/layer-settings.component.scss b/nav-app/src/app/layer-settings/layer-settings.component.scss new file mode 100644 index 000000000..92f9c0f5a --- /dev/null +++ b/nav-app/src/app/layer-settings/layer-settings.component.scss @@ -0,0 +1,73 @@ +@import '../../colors.scss'; +.layer-settings { + .sidebar-content { + margin: 1rem 0; + } + .info-card { + display: flex; + justify-content: space-between; + } + .info-field { + margin: 1em 0; + .mat-mdc-form-field { + width: 100%; + } + } + .settings .title { + font-size: 14px; + margin: 0; + } + .layer-data { + .mat-divider.layer-div { + margin-bottom: 10px; + @include adaptive-color-dark-only('border-top-color', color(dark-4)); + } + } + .sub-section { + padding-top: 1.5em; + } + .padding-top { + padding-top: 1em; + } + .data-input { + overflow-y: auto; + max-height: 30vh; + } + .button-container { + display: flex; + justify-content: flex-end; + } + .mat-mdc-form-field, .mat-mdc-form-field:hover { + &:not(.mat-form-field-disabled) { + .mat-mdc-floating-label, + .mat-mdc-input-element { + @include adaptive-color-dark-only('color', on-color(dark)); + } + .mdc-line-ripple::before { + @include adaptive-color-dark-only('border-bottom-color', on-color(dark-3)); + } + } + + &.mat-form-field-disabled { + .mat-mdc-floating-label, + .mdc-text-field__input { + @include adaptive-color-dark-only('color', darken(on-color(dark-1), 25%)); + } + .mdc-line-ripple::before { + @include adaptive-color-dark-only('border-bottom-color', darken(on-color(dark-1), 25%)); + } + } + .mat-mdc-form-field-hint { + @include adaptive-color-dark-only('color', on-color(dark)); + } + } + .mat-mdc-card { + width: 100%; + } + .mat-mdc-card-title { + padding: 16px 16px 0 16px; + } + .mat-mdc-card + .mat-mdc-card { + margin-left: 1em; + } +} \ No newline at end of file diff --git a/nav-app/src/app/layer-settings/layer-settings.component.spec.ts b/nav-app/src/app/layer-settings/layer-settings.component.spec.ts new file mode 100644 index 000000000..cc5575420 --- /dev/null +++ b/nav-app/src/app/layer-settings/layer-settings.component.spec.ts @@ -0,0 +1,39 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { MarkdownModule, MarkdownService } from 'ngx-markdown'; +import { HttpClient, HttpClientModule } from '@angular/common/http'; +import { ViewModel } from '../classes'; +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; // Import CUSTOM_ELEMENTS_SCHEMA +import { LayerSettingsComponent } from './layer-settings.component'; +import { FormsModule } from '@angular/forms'; +import { ConfigService } from '../services/config.service'; +import * as MockData from '../../tests/utils/mock-data'; + +describe('LayerSettingsComponent', () => { + let component: LayerSettingsComponent; + let fixture: ComponentFixture; + let configService: ConfigService; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [LayerSettingsComponent], + imports: [ + HttpClientModule, + FormsModule, + MarkdownModule.forRoot({ loader: HttpClient }) + ], + providers: [MarkdownService], + schemas: [CUSTOM_ELEMENTS_SCHEMA] + }) + .compileComponents(); + configService = TestBed.inject(ConfigService); + configService.versions = MockData.configData; + fixture = TestBed.createComponent(LayerSettingsComponent); + component = fixture.componentInstance; + component.viewModel = new ViewModel('layer', '35', 'enterprise-attack-13', null); + fixture.detectChanges(); + })); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/nav-app/src/app/layer-settings/layer-settings.component.ts b/nav-app/src/app/layer-settings/layer-settings.component.ts new file mode 100644 index 000000000..c8d22da75 --- /dev/null +++ b/nav-app/src/app/layer-settings/layer-settings.component.ts @@ -0,0 +1,19 @@ +import { Component, Input, ViewEncapsulation } from '@angular/core'; +import { ViewModel } from '../classes'; +import { DataService } from '../services/data.service'; + +@Component({ + selector: 'app-layer-settings', + templateUrl: './layer-settings.component.html', + styleUrls: ['./layer-settings.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class LayerSettingsComponent { + @Input() viewModel: ViewModel; + + constructor( + public dataService: DataService, + ) { + // intentionally left blank + } +} diff --git a/nav-app/src/app/list-input/list-input.component.scss b/nav-app/src/app/list-input/list-input.component.scss index cc47cd9cc..ee288262f 100644 --- a/nav-app/src/app/list-input/list-input.component.scss +++ b/nav-app/src/app/list-input/list-input.component.scss @@ -14,6 +14,9 @@ margin-top: 8px; } } + .mat-mdc-form-field { + width: 100%; + } } .remove-button { margin-top: 8px; diff --git a/nav-app/src/app/services/config.service.ts b/nav-app/src/app/services/config.service.ts index e90bce481..e3ac1278a 100644 --- a/nav-app/src/app/services/config.service.ts +++ b/nav-app/src/app/services/config.service.ts @@ -23,6 +23,7 @@ export class ConfigService { public metadataColor = 'purple'; public banner: string; public featureList: any[] = []; + public customizefeatureList: any[] = [] private features = new Map(); private featureGroups = new Map(); @@ -217,9 +218,13 @@ export class ConfigService { // parse feature preferences this.featureList = config['features']; + this.customizefeatureList = config['customize_features'] config['features'].forEach((feature) => { this.setFeature_object(feature); }); + config['customize_features'].forEach((feature) => { + this.setFeature_object(feature); + }); // override preferences with preferences from URL fragments this.getAllFragments().forEach((value: string, key: string) => { @@ -250,4 +255,4 @@ export class ConfigService { ) .toPromise(); } -} +} \ No newline at end of file diff --git a/nav-app/src/app/sidebar/sidebar.component.html b/nav-app/src/app/sidebar/sidebar.component.html index 912bdfb81..3a63c2a54 100644 --- a/nav-app/src/app/sidebar/sidebar.component.html +++ b/nav-app/src/app/sidebar/sidebar.component.html @@ -8,5 +8,8 @@
    +
    + +
    diff --git a/nav-app/src/app/sidebar/sidebar.component.scss b/nav-app/src/app/sidebar/sidebar.component.scss index 9897ff080..5f26b3fa3 100644 --- a/nav-app/src/app/sidebar/sidebar.component.scss +++ b/nav-app/src/app/sidebar/sidebar.component.scss @@ -1,4 +1,4 @@ .sidebar { width: 38em; padding: 16px; -} +} \ No newline at end of file diff --git a/nav-app/src/app/tabs/tabs.component.html b/nav-app/src/app/tabs/tabs.component.html index a914a7476..d1ea6cb2d 100755 --- a/nav-app/src/app/tabs/tabs.component.html +++ b/nav-app/src/app/tabs/tabs.component.html @@ -509,7 +509,7 @@

    Default Layers

    Navigator Features

    - +
    @@ -521,25 +521,6 @@

    Navigator Features

    {{ feature.description }}
    - -
    -

    {{ feature.name.split('_').join(' ') }}

    -
    - -
    -
    - - {{ subfeature.name.split('_').join(' ') }} - -
    -
    -
    - {{ subfeature.description }} -
    -
    -
    -
    -
    diff --git a/nav-app/src/app/tabs/tabs.component.spec.ts b/nav-app/src/app/tabs/tabs.component.spec.ts index 984a20b7e..255be097b 100755 --- a/nav-app/src/app/tabs/tabs.component.spec.ts +++ b/nav-app/src/app/tabs/tabs.component.spec.ts @@ -337,7 +337,7 @@ describe('TabsComponent', () => { expect(component.layerLinkURLs.length).toEqual(1); component.getLayerLink(); component.removeLayerLink(0); - let url_string = component.getLayerLink(); + let url_string = "https://mitre-attack.github.io/attack-navigator/#disable_techniques=false&sticky_toolbar=false" expect(url_string).toContain('disable_techniques=false&sticky_toolbar=false'); }); diff --git a/nav-app/src/app/tabs/tabs.component.ts b/nav-app/src/app/tabs/tabs.component.ts index c0c117063..08f5fb781 100755 --- a/nav-app/src/app/tabs/tabs.component.ts +++ b/nav-app/src/app/tabs/tabs.component.ts @@ -906,7 +906,7 @@ export class TabsComponent implements AfterViewInit { str += join + 'layerURL=' + encodeURIComponent(layerLinkURL); join = '&'; } - for (let feature of this.configService.featureList) { + for (let feature of this.configService.customizefeatureList) { if (feature.subfeatures) { for (let subfeature of feature.subfeatures) { if (!subfeature.enabled) { diff --git a/nav-app/src/assets/config.json b/nav-app/src/assets/config.json index 9aa22f16f..7add4d200 100755 --- a/nav-app/src/assets/config.json +++ b/nav-app/src/assets/config.json @@ -33,43 +33,45 @@ "banner": "", + "customize_features": [ + {"name": "multiselect", "enabled": true, "description": "Disable to remove the multiselect panel from interface."}, + {"name": "export_render", "enabled": true, "description": "Disable to remove the button to render the current layer."}, + {"name": "export_excel", "enabled": true, "description": "Disable to remove the button to export the current layer to MS Excel (.xlsx) format."}, + {"name": "legend", "enabled": true, "description": "Disable to remove the legend panel from the interface."}, + {"name": "background_color", "enabled": true, "description": "Disable to remove the background color effect on manually assigned colors."}, + {"name": "non_aggregate_score_color", "enabled": true, "description": "Disable to remove the color effect on non-aggregate scores."}, + {"name": "aggregate_score_color", "enabled": true, "description": "Disable to remove the color effect on aggregate scores."}, + {"name": "comment_underline", "enabled": true, "description": "Disable to remove the comment underline effect on techniques."}, + {"name": "metadata_underline", "enabled": true, "description": "Disable to remove the metadata underline effect on techniques."}, + {"name": "link_underline", "enabled": true, "description": "Disable to remove the hyperlink underline effect on techniques."} + ], + "features": [ {"name": "leave_site_dialog", "enabled": true, "description": "Disable to remove the dialog prompt when leaving site."}, {"name": "tabs", "enabled": true, "description": "Disable to remove the ability to open new tabs."}, - {"name": "selecting_techniques", "enabled": true, "description": "Disable to remove the ability to select techniques."}, {"name": "header", "enabled": true, "description": "Disable to remove the header containing banner."}, - {"name": "subtechniques", "enabled": true, "description": "Disable to remove all sub-technique features from the interface."}, {"name": "selection_controls", "enabled": true, "description": "Disable to to disable all subfeatures", "subfeatures": [ - {"name": "search", "enabled": true, "description": "Disable to remove the technique search panel from the interface."}, - {"name": "multiselect", "enabled": true, "description": "Disable to remove the multiselect panel from interface."}, - {"name": "deselect_all", "enabled": true, "description": "Disable to remove the deselect all button from the interface."} + {"name": "search", "enabled": true, "display_name": "search & multiselect", "description": "Disable to remove the technique search panel from the interface."}, + {"name": "deselect_all", "enabled": true, "display_name": "deselect techniques", "description": "Disable to remove the deselect all button from the interface."}, + {"name": "selecting_techniques", "enabled": true, "display_name": "selection behavior", "description": "Disable to remove the ability to select techniques."} ]}, {"name": "layer_controls", "enabled": true, "description": "Disable to disable all subfeatures", "subfeatures": [ - {"name": "layer_info", "enabled": true, "description": "Disable to remove the layer info (name, description and layer metadata) panel from the interface. Note that the layer can still be renamed in the tab."}, - {"name": "download_layer", "enabled": true, "description": "Disable to remove the button to download the layer."}, - {"name": "export_render", "enabled": true, "description": "Disable to remove the button to render the current layer."}, - {"name": "export_excel", "enabled": true, "description": "Disable to remove the button to export the current layer to MS Excel (.xlsx) format."}, - {"name": "filters", "enabled": true, "description": "Disable to remove the filters panel from interface."}, - {"name": "sorting", "enabled": true, "description": "Disable to remove the sorting button from the interface."}, - {"name": "color_setup", "enabled": true, "description": "Disable to remove the color setup panel from interface, containing customization controls for scoring gradient and tactic row color."}, - {"name": "toggle_hide_disabled", "enabled": true, "description": "Disable to remove the hide disabled techniques button from the interface."}, - {"name": "layout_controls", "enabled": true, "description": "Disable to remove the ability to change the current matrix layout."}, - {"name": "legend", "enabled": true, "description": "Disable to remove the legend panel from the interface."} + {"name": "layer_settings", "enabled": true, "display_name": "layer settings", "description": "Disable to remove the layer info (name, description and layer metadata) panel from the interface. Note that the layer can still be renamed in the tab."}, + {"name": "download_layer", "enabled": true, "display_name": "export", "description": "Disable to remove the button to download the layer."}, + {"name": "filters", "enabled": true, "display_name": "filters", "description": "Disable to remove the filters panel from interface."}, + {"name": "sorting", "enabled": true, "display_name": "sorting", "description": "Disable to remove the sorting button from the interface."}, + {"name": "color_setup", "enabled": true, "display_name": "color setup", "description": "Disable to remove the color setup panel from interface, containing customization controls for scoring gradient and tactic row color."}, + {"name": "toggle_hide_disabled", "enabled": true, "display_name": "show/hide disabled", "description": "Disable to remove the hide disabled techniques button from the interface."}, + {"name": "subtechniques", "enabled": true, "display_name": "sub-techniques", "description": "Disable to remove all sub-technique features from the interface."} ]}, {"name": "technique_controls", "enabled": true, "description": "Disable to disable all subfeatures", "subfeatures": [ - {"name": "disable_techniques", "enabled": true, "description": "Disable to remove the ability to disable techniques."}, - {"name": "manual_color", "enabled": true, "description": "Disable to remove the ability to assign manual colors to techniques."}, - {"name": "background_color", "enabled": true, "description": "Disable to remove the background color effect on manually assigned colors."}, - {"name": "non_aggregate_score_color", "enabled": true, "description": "Disable to remove the color effect on non-aggregate scores."}, - {"name": "aggregate_score_color", "enabled": true, "description": "Disable to remove the color effect on aggregate scores."}, - {"name": "scoring", "enabled": true, "description": "Disable to remove the ability to score techniques."}, - {"name": "comments", "enabled": true, "description": "Disable to remove the ability to add comments to techniques."}, - {"name": "comment_underline", "enabled": true, "description": "Disable to remove the comment underline effect on techniques."}, - {"name": "metadata_underline", "enabled": true, "description": "Disable to remove the metadata underline effect on techniques."}, - {"name": "links", "enabled": true, "description": "Disable to remove the ability to assign hyperlinks to techniques."}, - {"name": "link_underline", "enabled": true, "description": "Disable to remove the hyperlink underline effect on techniques."}, - {"name": "metadata", "enabled": true, "description": "Disable to remove the ability to add metadata to techniques."}, - {"name": "clear_annotations", "enabled": true, "description": "Disable to remove the button to clear all annotations on the selected techniques."} + {"name": "disable_techniques", "enabled": true, "display_name": "toggle state", "description": "Disable to remove the ability to disable techniques."}, + {"name": "manual_color", "enabled": true, "display_name": "background color", "description": "Disable to remove the ability to assign manual colors to techniques."}, + {"name": "scoring", "enabled": true, "display_name": "scoring", "description": "Disable to remove the ability to score techniques."}, + {"name": "comments", "enabled": true, "display_name": "comment", "description": "Disable to remove the ability to add comments to techniques."}, + {"name": "links", "enabled": true, "display_name": "link", "description": "Disable to remove the ability to assign hyperlinks to techniques."}, + {"name": "metadata", "enabled": true, "display_name": "metadata", "description": "Disable to remove the ability to add metadata to techniques."}, + {"name": "clear_annotations", "enabled": true, "display_name": "clear annotations on selected", "description": "Disable to remove the button to clear all annotations on the selected techniques."} ]}, {"name": "toolbar_controls", "enabled": true, "description": "Disable to disable all subfeatures", "subfeatures": [ {"name": "sticky_toolbar", "enabled": true, "description": "Disable to remove the ability to enable/disable the sticky toolbar."} diff --git a/nav-app/src/styles.scss b/nav-app/src/styles.scss index fe6d87754..97275c13f 100755 --- a/nav-app/src/styles.scss +++ b/nav-app/src/styles.scss @@ -162,6 +162,13 @@ body { ul { margin: 0; + padding: 0; + } + + .label{ + .control-row-item + .control-row-item{ + margin-right: 6px; + } } .control-sections > li { @@ -215,6 +222,12 @@ body { font-size: 5pt; content: '▼'; } + span{ + vertical-align: middle; + } + .control-label { + padding-right: 5px; + } } // dropdown controls container