[Notebook] Embed enhancements (#5809)

* added new menu and actions to notebook embed as well as new information on embed

* fix method name case

* Add action messages. Fix margins

* Added bg icons. Change sizing of icons and thumbnails. Add scrolling to overflow embeds

* Rename embed wrapper

* adding dynamce class for scrolling the embeds wrapper based on need

* Add styling to embed scrolling container

* Change tag margin for better spacing between rows. Class rename. Minor styling changes to embed container. Change supermenu icon size

* Change action menu size

* Fix inner shadows. Revert tag code change. Create new theme constants. Make embed container constant

* Fix scroll and snow theme colors

* Fix overflow bug in entries and embed container. Refactor code so that containers optimize space of each entry

* Fix lint error

* Fix so embed container goes full width

* Fix input container to extend full width. Fix margin between notebook elements

* Addressed PR review comments.

* Address PR changes. Fix text overflow for long words.

* address pr review comments

* fixing tests

* first pass

* i've wasted too much time on this

Co-authored-by: Rukmini Bose <rukmini.bose15@gmail.com>
Co-authored-by: rukmini-bose <48999852+rukmini-bose@users.noreply.github.com>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
This commit is contained in:
Jamie V 2023-01-20 16:53:37 -08:00 committed by GitHub
parent d1c7d133fc
commit 20c7b23a4f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 371 additions and 141 deletions

View File

@ -24,8 +24,6 @@
This test suite is dedicated to tests which verify the basic operations surrounding Notebooks.
*/
// FIXME: Remove this eslint exception once tests are implemented
// eslint-disable-next-line no-unused-vars
const { test, expect } = require('../../../../pluginFixtures');
const { expandTreePaneItemByName, createDomainObjectWithDefaults } = require('../../../../appActions');
const nbUtils = require('../../../../helper/notebookUtils');
@ -266,70 +264,3 @@ test.describe('Notebook entry tests', () => {
test.fixme('new entries persist through navigation events without save', async ({ page }) => {});
test.fixme('previous and new entries can be deleted', async ({ page }) => {});
});
test.describe('Snapshot Menu tests', () => {
test.fixme('When no default notebook is selected, Snapshot Menu dropdown should only have a single option', async ({ page }) => {
// There should be no default notebook
// Clear default notebook if exists using `localStorage.setItem('notebook-storage', null);`
// refresh page
// Click on 'Notebook Snaphot Menu'
// 'save to Notebook Snapshots' should be only option there
});
test.fixme('When default notebook is updated selected, Snapshot Menu dropdown should list it as the newest option', async ({ page }) => {
// Create 2a notebooks
// Set Notebook A as Default
// Open Snapshot Menu and note that Notebook A is listed
// Close Snapshot Menu
// Set Default Notebook to Notebook B
// Open Snapshot Notebook and note that Notebook B is listed
// Select Default Notebook Option and verify that Snapshot is added to Notebook B
});
test.fixme('Can add Snapshots via Snapshot Menu and details are correct', async ({ page }) => {
//Note this should be a visual test, too
// Create Telemetry object
// Create A notebook with many pages and sections.
// Set page and section defaults to be between first and last of many. i.e. 3 of 5
// Navigate to Telemetry object
// Select Default Notebook Option and verify that Snapshot is added to Notebook A
// Verify Snapshot Details appear correctly
});
test.fixme('Snapshots adjust time conductor', async ({ page }) => {
// Create Telemetry object
// Set Telemetry object's timeconductor to Fixed time with Start and Endtimes are recorded
// Embed Telemetry object into notebook
// Set Time Conductor to Local clock
// Click into embedded telemetry object and verify object appears with same fixed time from record
});
});
test.describe('Snapshot Container tests', () => {
test.fixme('5 Snapshots can be added to a container', async ({ page }) => {});
test.fixme('5 Snapshots can be added to a container and Deleted with Delete All action', async ({ page }) => {});
test.fixme('A snapshot can be Deleted from Container', async ({ page }) => {});
test.fixme('A snapshot can be Previewed from Container', async ({ page }) => {});
test.fixme('A snapshot Container can be open and closed', async ({ page }) => {});
test.fixme('Can add object to Snapshot container and pull into notebook and create a new entry', async ({ page }) => {
//Create Notebook
//Create Telemetry Object
//From Telemetry Object, use 'save to Notebook Snapshots'
//Snapshots indicator should blink, click on it to view snapshots
//Navigate to Notebook
//Drag and Drop onto droppable area for new entry
//New Entry created with given snapshot added
//Snapshot removed from container?
});
test.fixme('Can add object to Snapshot container and pull into notebook and existing entry', async ({ page }) => {
//Create Notebook
//Create Telemetry Object
//From Telemetry Object, use 'save to Notebook Snapshots'
//Snapshots indicator should blink, click on it to view snapshots
//Navigate to Notebook
//Drag and Drop into exiting entry
//Existing Entry updated with given snapshot
//Snapshot removed from container?
});
test.fixme('Verify Embedded options for PNG, JPG, and Annotate work correctly', async ({ page }) => {
//Add snapshot to container
//Verify PNG, JPG, and Annotate buttons work correctly
});
});

View File

@ -0,0 +1,134 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2022, 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.
*****************************************************************************/
/*
This test suite is dedicated to tests which verify the basic operations surrounding Notebooks.
*/
const { test, expect } = require('../../../../pluginFixtures');
// const { expandTreePaneItemByName, createDomainObjectWithDefaults } = require('../../../../appActions');
// const nbUtils = require('../../../../helper/notebookUtils');
test.describe('Snapshot Menu tests', () => {
test.fixme('When no default notebook is selected, Snapshot Menu dropdown should only have a single option', async ({ page }) => {
// There should be no default notebook
// Clear default notebook if exists using `localStorage.setItem('notebook-storage', null);`
// refresh page
// Click on 'Notebook Snaphot Menu'
// 'save to Notebook Snapshots' should be only option there
});
test.fixme('When default notebook is updated selected, Snapshot Menu dropdown should list it as the newest option', async ({ page }) => {
// Create 2a notebooks
// Set Notebook A as Default
// Open Snapshot Menu and note that Notebook A is listed
// Close Snapshot Menu
// Set Default Notebook to Notebook B
// Open Snapshot Notebook and note that Notebook B is listed
// Select Default Notebook Option and verify that Snapshot is added to Notebook B
});
test.fixme('Can add Snapshots via Snapshot Menu and details are correct', async ({ page }) => {
//Note this should be a visual test, too
// Create Telemetry object
// Create A notebook with many pages and sections.
// Set page and section defaults to be between first and last of many. i.e. 3 of 5
// Navigate to Telemetry object
// Select Default Notebook Option and verify that Snapshot is added to Notebook A
// Verify Snapshot Details appear correctly
});
test.fixme('Snapshots adjust time conductor', async ({ page }) => {
// Create Telemetry object
// Set Telemetry object's timeconductor to Fixed time with Start and Endtimes are recorded
// Embed Telemetry object into notebook
// Set Time Conductor to Local clock
// Click into embedded telemetry object and verify object appears with same fixed time from record
});
});
test.describe('Snapshot Container tests', () => {
test.beforeEach(async ({ page }) => {
//Navigate to baseURL
await page.goto('./', { waitUntil: 'networkidle' });
// Create Notebook
// const notebook = await createDomainObjectWithDefaults(page, {
// type: 'Notebook',
// name: "Test Notebook"
// });
// // Create Overlay Plot
// const snapShotObject = await createDomainObjectWithDefaults(page, {
// type: 'Overlay Plot',
// name: "Dropped Overlay Plot"
// });
await page.getByRole('button', { name: ' Snapshot ' }).click();
await page.getByRole('menuitem', { name: ' Save to Notebook Snapshots' }).click();
await page.getByRole('button', { name: 'Show' }).click();
});
test.fixme('5 Snapshots can be added to a container', async ({ page }) => {});
test.fixme('5 Snapshots can be added to a container and Deleted with Delete All action', async ({ page }) => {});
test.fixme('A snapshot can be Deleted from Container with 3 dot action menu', async ({ page }) => {});
test.fixme('A snapshot can be Viewed, Annotated, display deleted, and saved from Container with 3 dot action menu', async ({ page }) => {
await page.locator('.c-snapshot.c-ne__embed').first().getByTitle('More options').click();
await page.getByRole('menuitem', { name: ' View Snapshot' }).click();
await expect(page.locator('.c-overlay__outer')).toBeVisible();
await page.getByTitle('Annotate').click();
await expect(page.locator('#snap-annotation-canvas')).toBeVisible();
await page.getByRole('button', { name: '' }).click();
// await expect(page.locator('#snap-annotation-canvas')).not.toBeVisible();
await page.getByRole('button', { name: 'Save' }).click();
await page.getByRole('button', { name: 'Done' }).click();
//await expect(await page.locator)
});
test('A snapshot can be Quick Viewed from Container with 3 dot action menu', async ({ page }) => {
await page.locator('.c-snapshot.c-ne__embed').first().getByTitle('More options').click();
await page.getByRole('menuitem', { name: 'Quick View' }).click();
await expect(page.locator('.c-overlay__outer')).toBeVisible();
});
test.fixme('A snapshot can be Navigated To from Container with 3 dot action menu', async ({ page }) => {});
test.fixme('A snapshot can be Navigated To Item in Time from Container with 3 dot action menu', async ({ page }) => {});
test.fixme('A snapshot Container can be open and closed', async ({ page }) => {});
test.fixme('Can add object to Snapshot container and pull into notebook and create a new entry', async ({ page }) => {
//Create Notebook
//Create Telemetry Object
//From Telemetry Object, use 'save to Notebook Snapshots'
//Snapshots indicator should blink, click on it to view snapshots
//Navigate to Notebook
//Drag and Drop onto droppable area for new entry
//New Entry created with given snapshot added
//Snapshot removed from container?
});
test.fixme('Can add object to Snapshot container and pull into notebook and existing entry', async ({ page }) => {
//Create Notebook
//Create Telemetry Object
//From Telemetry Object, use 'save to Notebook Snapshots'
//Snapshots indicator should blink, click on it to view snapshots
//Navigate to Notebook
//Drag and Drop into exiting entry
//Existing Entry updated with given snapshot
//Snapshot removed from container?
});
test.fixme('Verify Embedded options for PNG, JPG, and Annotate work correctly', async ({ page }) => {
//Add snapshot to container
//Verify PNG, JPG, and Annotate buttons work correctly
});
});

View File

@ -152,7 +152,7 @@ test.describe('Restricted Notebook with a page locked and with an embed @addInit
test('Allows embeds to be deleted if page unlocked @addInit', async ({ page }) => {
// Click .c-ne__embed__name .c-popup-menu-button
await page.locator('.c-ne__embed__name .c-popup-menu-button').click(); // embed popup menu
await page.locator('.c-ne__embed__name .c-icon-button').click(); // embed popup menu
const embedMenu = page.locator('body >> .c-menu');
await expect(embedMenu).toContainText('Remove This Embed');
@ -161,7 +161,7 @@ test.describe('Restricted Notebook with a page locked and with an embed @addInit
test('Disallows embeds to be deleted if page locked @addInit', async ({ page }) => {
await lockPage(page);
// Click .c-ne__embed__name .c-popup-menu-button
await page.locator('.c-ne__embed__name .c-popup-menu-button').click(); // embed popup menu
await page.locator('.c-ne__embed__name .c-icon-button').click(); // embed popup menu
const embedMenu = page.locator('body >> .c-menu');
await expect(embedMenu).not.toContainText('Remove This Embed');

View File

@ -12,14 +12,15 @@
<a
class="c-ne__embed__link"
:class="embed.cssClass"
@click="changeLocation"
@click="navigateToItemInTime"
>{{ embed.name }}</a>
<PopupMenu :popup-menu-items="popupMenuItems" />
<button
class="c-ne__embed__actions c-icon-button icon-3-dots"
title="More options"
@click.prevent.stop="showMenuItems($event)"
></button>
</div>
<div
v-if="embed.snapshot"
class="c-ne__embed__time"
>
<div class="c-ne__embed__time">
{{ createdOn }}
</div>
</div>
@ -32,17 +33,14 @@ import PreviewAction from '../../../ui/preview/PreviewAction';
import RemoveDialog from '../utils/removeDialog';
import PainterroInstance from '../utils/painterroInstance';
import SnapshotTemplate from './snapshot-template.html';
import objectPathToUrl from '@/tools/url';
import { updateNotebookImageDomainObject } from '../utils/notebook-image';
import ImageExporter from '../../../exporters/ImageExporter';
import PopupMenu from './PopupMenu.vue';
import Vue from 'vue';
export default {
components: {
PopupMenu
},
inject: ['openmct', 'snapshotContainer'],
props: {
embed: {
@ -72,7 +70,7 @@ export default {
},
data() {
return {
popupMenuItems: []
menuActions: []
};
},
computed: {
@ -88,37 +86,88 @@ export default {
watch: {
isLocked(value) {
if (value === true) {
let index = this.popupMenuItems.findIndex((item) => item.id === 'removeEmbed');
let index = this.menuActions.findIndex((item) => item.id === 'removeEmbed');
this.$delete(this.popupMenuItems, index);
this.$delete(this.menuActions, index);
}
}
},
mounted() {
this.addPopupMenuItems();
async mounted() {
this.objectPath = [];
await this.setEmbedObjectPath();
this.addMenuActions();
this.imageExporter = new ImageExporter(this.openmct);
},
methods: {
addPopupMenuItems() {
const removeEmbed = {
id: 'removeEmbed',
cssClass: 'icon-trash',
name: this.removeActionString,
callback: this.getRemoveDialog.bind(this)
};
const preview = {
id: 'preview',
cssClass: 'icon-eye-open',
name: 'Preview',
callback: this.previewEmbed.bind(this)
showMenuItems(event) {
const x = event.x;
const y = event.y;
const menuOptions = {
menuClass: 'c-ne__embed__actions-menu',
placement: this.openmct.menus.menuPlacement.TOP_RIGHT
};
this.popupMenuItems = [preview];
this.openmct.menus.showSuperMenu(x, y, this.menuActions, menuOptions);
},
addMenuActions() {
if (this.embed.snapshot) {
const viewSnapshot = {
id: 'viewSnapshot',
cssClass: 'icon-camera',
name: 'View Snapshot',
description: 'View the snapshot image taken in the form of a jpeg.',
onItemClicked: () => this.openSnapshot()
};
if (!this.isLocked) {
this.popupMenuItems.unshift(removeEmbed);
this.menuActions = [viewSnapshot];
}
const navigateToItem = {
id: 'navigateToItem',
cssClass: this.embed.cssClass,
name: 'Navigate to Item',
description: 'Navigate to the item with the current time settings.',
onItemClicked: () => this.navigateToItem()
};
const navigateToItemInTime = {
id: 'navigateToItemInTime',
cssClass: this.embed.cssClass,
name: 'Navigate to Item in Time',
description: 'Navigate to the item in its time frame when captured.',
onItemClicked: () => this.navigateToItemInTime()
};
const quickView = {
id: 'quickView',
cssClass: 'icon-eye-open',
name: 'Quick View',
description: 'Full screen overlay view of the item.',
onItemClicked: () => this.previewEmbed()
};
this.menuActions = this.menuActions.concat([quickView, navigateToItem, navigateToItemInTime]);
if (!this.isLocked) {
const removeEmbed = {
id: 'removeEmbed',
cssClass: 'icon-trash',
name: this.removeActionString,
description: 'Permanently delete this embed from this Notebook entry.',
onItemClicked: this.getRemoveDialog.bind(this)
};
this.menuActions.push(removeEmbed);
}
},
async setEmbedObjectPath() {
this.objectPath = await this.openmct.objects.getOriginalPath(this.embed.domainObject.identifier);
if (this.objectPath.length > 0 && this.objectPath[this.objectPath.length - 1].type === 'root') {
this.objectPath.pop();
}
},
annotateSnapshot() {
const annotateVue = new Vue({
@ -179,7 +228,11 @@ export default {
painterroInstance.show(object.configuration.fullSizeImageURL);
});
},
changeLocation() {
navigateToItem() {
const url = objectPathToUrl(this.openmct, this.objectPath);
this.openmct.router.navigate(url);
},
navigateToItemInTime() {
const hash = this.embed.historicLink;
const bounds = this.openmct.time.bounds();

View File

@ -31,15 +31,28 @@
@click="selectEntry($event, entry)"
>
<div class="c-ne__time-and-content">
<div class="c-ne__time-and-creator">
<span class="c-ne__created-date">{{ createdOnDate }}</span>
<span class="c-ne__created-time">{{ createdOnTime }}</span>
<div class="c-ne__time-and-creator-and-delete">
<div class="c-ne__time-and-creator">
<span class="c-ne__created-date">{{ createdOnDate }}</span>
<span class="c-ne__created-time">{{ createdOnTime }}</span>
<span
v-if="entry.createdBy"
class="c-ne__creator"
>
<span class="icon-person"></span> {{ entry.createdBy }}
</span>
</div>
<span
v-if="entry.createdBy"
class="c-ne__creator"
v-if="!readOnly && !isLocked"
class="c-ne__local-controls--hidden"
>
<span class="icon-person"></span> {{ entry.createdBy }}
<button
class="c-ne__remove c-icon-button c-icon-button--major icon-trash"
title="Delete this entry"
tabindex="-1"
@click="deleteEntry"
>
</button>
</span>
</div>
<div class="c-ne__content">
@ -94,30 +107,26 @@
</div>
</div>
<div class="c-snapshots c-ne__embeds">
<NotebookEmbed
v-for="embed in entry.embeds"
:key="embed.id"
:embed="embed"
:is-locked="isLocked"
@removeEmbed="removeEmbed"
@updateEmbed="updateEmbed"
/>
<div
:class="{'c-scrollcontainer': enableEmbedsWrapperScroll }"
>
<div
ref="embedsWrapper"
class="c-snapshots c-ne__embeds-wrapper"
>
<NotebookEmbed
v-for="embed in entry.embeds"
ref="embeds"
:key="embed.id"
:embed="embed"
:is-locked="isLocked"
@removeEmbed="removeEmbed"
@updateEmbed="updateEmbed"
/>
</div>
</div>
</div>
</div>
<div
v-if="!readOnly && !isLocked"
class="c-ne__local-controls--hidden"
>
<button
class="c-icon-button c-icon-button--major icon-trash"
title="Delete this entry"
tabindex="-1"
@click="deleteEntry"
>
</button>
</div>
<div
v-if="readOnly"
class="c-ne__section-and-page"
@ -147,6 +156,8 @@ import TextHighlight from '../../../utils/textHighlight/TextHighlight.vue';
import { createNewEmbed } from '../utils/notebook-entries';
import { saveNotebookImageDomainObject, updateNamespaceOfDomainObject } from '../utils/notebook-image';
import _ from 'lodash';
import Moment from 'moment';
const UNKNOWN_USER = 'Unknown';
@ -211,6 +222,11 @@ export default {
required: true
}
},
data() {
return {
enableEmbedsWrapperScroll: false
};
},
computed: {
createdOnDate() {
return this.formatTime(this.entry.createdOn, 'YYYY-MM-DD');
@ -246,8 +262,21 @@ export default {
}
},
mounted() {
this.manageEmbedLayout = _.debounce(this.manageEmbedLayout, 400);
if (this.$refs.embedsWrapper) {
this.embedsWrapperResizeObserver = new ResizeObserver(this.manageEmbedLayout);
this.embedsWrapperResizeObserver.observe(this.$refs.embedsWrapper);
}
this.manageEmbedLayout();
this.dropOnEntry = this.dropOnEntry.bind(this);
},
beforeDestroy() {
if (this.embedsWrapperResizeObserver) {
this.embedsWrapperResizeObserver.unobserve(this.$refs.embedsWrapper);
}
},
methods: {
async addNewEmbed(objectPath) {
const bounds = this.openmct.time.bounds();
@ -259,6 +288,8 @@ export default {
};
const newEmbed = await createNewEmbed(snapshotMeta);
this.entry.embeds.push(newEmbed);
this.manageEmbedLayout();
},
cancelEditMode(event) {
const isEditing = this.openmct.editor.isEditing();
@ -279,6 +310,17 @@ export default {
deleteEntry() {
this.$emit('deleteEntry', this.entry.id);
},
manageEmbedLayout() {
if (this.$refs.embeds) {
const embedsWrapperLength = this.$refs.embedsWrapper.clientWidth;
const embedsTotalWidth = this.$refs.embeds.reduce((total, embed) => {
return embed.$el.clientWidth + total;
}, 0);
this.enableEmbedsWrapperScroll = embedsTotalWidth > embedsWrapperLength;
}
},
async dropOnEntry($event) {
$event.stopImmediatePropagation();
@ -336,6 +378,8 @@ export default {
this.entry.embeds.splice(embedPosition, 1);
this.timestampAndUpdate();
this.manageEmbedLayout();
},
updateEmbed(newEmbed) {
this.entry.embeds.some(e => {

View File

@ -463,6 +463,8 @@ $transInBounceBig: all 300ms cubic-bezier(.2,1.6,.6,3);
$createBtnTextTransform: uppercase;
$colorDiscreteItemBg: rgba($colorBodyFg,0.1);
$colorDiscreteItemCurrentBg: rgba($colorOk,0.3);
$scrollContainer: $colorBodyBg;
;
@mixin discreteItem() {
background: $colorDiscreteItemBg;

View File

@ -467,6 +467,7 @@ $transInBounceBig: all 300ms cubic-bezier(.2,1.6,.6,3);
$createBtnTextTransform: uppercase;
$colorDiscreteItemBg: rgba($colorBodyFg,0.1);
$colorDiscreteItemCurrentBg: rgba($colorOk,0.3);
$scrollContainer: $colorBodyBg;
@mixin discreteItem() {
background: rgba($colorBodyFg,0.1);

View File

@ -463,6 +463,7 @@ $transInBounceBig: all 300ms cubic-bezier(.2,1.6,.6,3);
$createBtnTextTransform: uppercase;
$colorDiscreteItemBg: rgba($colorBodyFg,0.1);
$colorDiscreteItemCurrentBg: rgba($colorOk,0.3);
$scrollContainer: rgba(102, 102, 102, 0.1);
@mixin discreteItem() {
background: $colorDiscreteItemBg;

View File

@ -327,3 +327,6 @@ $bg-icon-timelist: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://ww
$bg-icon-plot-scatter: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cg data-name='Layer 2'%3e%3cpath d='M96 0C43.2 0 0 43.2 0 96v320c0 52.8 43.2 96 96 96h320c52.8 0 96-43.2 96-96V96c0-52.8-43.2-96-96-96ZM64 176a48 48 0 1 1 48 48 48 48 0 0 1-48-48Zm80 240a48 48 0 1 1 48-48 48 48 0 0 1-48 48Zm128-96a48 48 0 1 1 48-48 48 48 0 0 1-48 48Zm0-160a48 48 0 1 1 48-48 48 48 0 0 1-48 48Zm128 256a48 48 0 1 1 48-48 48 48 0 0 1-48 48Z' data-name='Layer 1'/%3e%3c/g%3e%3c/svg%3e");
$bg-icon-notebook-shift-log: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cpath d='M448 55.36c0-39.95-27.69-63.66-61.54-52.68L0 128h448V55.36ZM448 160H0v288c0 35.2 28.8 64 64 64h384c35.2 0 64-28.8 64-64V224c0-35.2-28.8-64-64-64ZM128 416H64v-64h64v64Zm0-96H64v-64h64v64Zm320 96H192v-64h256v64Zm0-96H192v-64h256v64Z'/%3e%3c/svg%3e");
$bg-icon-telemetry-aggregate: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3cg data-name='Layer 2'%3e%3cg data-name='Layer 3'%3e%3cpath d='M39 197.72c7-20.72 18.74-50.4 34.6-74.18C92.91 94.65 114.79 80 138.67 80s45.75 14.65 65 43.54c15.86 23.78 27.57 53.46 34.6 74.18 15.44 45.48 31.56 67.49 39 73.27 7.47-5.78 23.6-27.79 39-73.27 7-20.72 18.74-50.4 34.61-74.18q13.9-20.85 29.56-31.75A207.78 207.78 0 0 0 208 0C93.12 0 0 93.12 0 208a208.14 208.14 0 0 0 7.39 55.09c8.39-10.87 20.2-31.67 31.61-65.37Z'/%3e%3cpath d='M377 218.28c-7 20.72-18.74 50.4-34.6 74.18-19.28 28.89-41.16 43.54-65 43.54s-45.75-14.65-65-43.54c-15.86-23.78-27.57-53.46-34.6-74.18-15.44-45.48-31.57-67.49-39-73.27-7.47 5.78-23.6 27.79-39 73.27-7.19 20.72-18.9 50.4-34.8 74.18q-13.9 20.85-29.56 31.75A207.78 207.78 0 0 0 208 416c114.88 0 208-93.12 208-208a208.14 208.14 0 0 0-7.39-55.09c-8.39 10.87-20.2 31.67-31.61 65.37Z'/%3e%3cpath d='M460.78 167.31A258.4 258.4 0 0 1 464 208a255.84 255.84 0 0 1-256 256 258.4 258.4 0 0 1-40.69-3.22A207.23 207.23 0 0 0 304 512c114.88 0 208-93.12 208-208a207.23 207.23 0 0 0-51.22-136.69Z'/%3e%3c/g%3e%3c/g%3e%3c/svg%3e");
$bg-icon-trash: url("data:image/svg+xml;charset=UTF-8,%3csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='512px' height='512px' viewBox='0 0 512 512' enable-background='new 0 0 512 512' xml:space='preserve'%3e%3cpath d='M416,64h-96.18V32c0-17.6-14.4-32-32-32h-64c-17.6,0-32,14.4-32,32v32H96c-52.8,0-96,36-96,80s0,80,0,80h32v192 c0,52.8,43.2,96,96,96h256c52.8,0,96-43.2,96-96V224h32c0,0,0-36,0-80S468.8,64,416,64z M160,416H96V224h64V416z M288,416h-64V224 h64V416z M416,416h-64V224h64V416z'/%3e%3c/svg%3e");
$bg-icon-eye-open: url("data:image/svg+xml;charset=UTF-8,%3csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 512 512' style='enable-background:new 0 0 512 512;' xml:space='preserve'%3e%3cstyle type='text/css'%3e .st0%7bfill:%2300A14B;%7d %3c/style%3e%3ctitle%3eicon-eye-open-v2%3c/title%3e%3cg%3e%3cpath class='st0' d='M256,58.2c-122.9,0-226.1,84-255.4,197.8C29.9,369.7,133.1,453.8,256,453.8s226.1-84,255.4-197.8 C482.1,142.3,378.9,58.2,256,58.2z M414.6,294.2c-11.3,17.2-25.3,32.4-41.5,45.2c-16.4,12.9-34.5,22.8-54,29.7 c-20.2,7.1-41.4,10.7-63,10.7s-42.9-3.6-63-10.7c-19.5-6.9-37.7-16.9-54-29.7c-16.2-12.8-30.2-27.9-41.5-45.2 c-7.9-12-14.4-24.8-19.3-38.2c5-13.4,11.5-26.2,19.3-38.2c11.3-17.2,25.3-32.4,41.5-45.2c16.4-12.9,34.5-22.8,54-29.7 c20.2-7.1,41.4-10.7,63-10.7s42.9,3.6,63,10.7c19.5,6.9,37.7,16.9,54,29.7c16.2,12.8,30.2,27.9,41.5,45.2 c7.9,12,14.4,24.8,19.3,38.2C429,269.4,422.5,282.2,414.6,294.2z'/%3e%3ccircle class='st0' cx='256' cy='256' r='96'/%3e%3c/g%3e%3c/svg%3e");
$bg-icon-camera: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3e%3ctitle%3eicon-camera-v2%3c/title%3e%3cpath d='M448,128H384L320,0H192L128,128H64A64.2,64.2,0,0,0,0,192V448a64.2,64.2,0,0,0,64,64H448a64.2,64.2,0,0,0,64-64V192A64.2,64.2,0,0,0,448,128ZM256,432A128,128,0,1,1,384,304,128,128,0,0,1,256,432Z'/%3e%3c/svg%3e");

View File

@ -462,6 +462,15 @@ input[type=number].c-input-number--no-spinners {
}
}
.c-scrollcontainer{
@include nice-input();
margin-top: $interiorMargin;
background: $scrollContainer;
border-radius: $controlCr;
overflow: auto;
padding: $interiorMarginSm;
}
// SELECTS
select {
@include appearanceNone();

View File

@ -265,3 +265,6 @@
.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); }

View File

@ -303,11 +303,25 @@
opacity: 0.7;
}
&__time-and-creator,
&__time-and-creator-and-delete,
&__input {
padding: $p;
}
&__input{
word-break: break-word;
}
&__time-and-creator-and-delete{
display: flex;
align-items: center;
> * + *{
float: right;
margin-left: auto;
}
}
&__creator [class*='icon'] {
font-size: 0.95em;
}
@ -315,6 +329,7 @@
&__time-and-content {
display: block;
flex: 1 1 auto;
overflow: visible;
> * + * {
margin-top: $interiorMarginSm;
@ -332,9 +347,10 @@
}
&__content {
display: flex;
flex-direction: column;
flex: 1 1 auto;
margin-right: $interiorMarginSm;
margin-top: $interiorMargin;
> [class*="__"] + [class*="__"] {
margin-top: $interiorMarginSm;
@ -358,7 +374,8 @@
@include inlineInput;
padding-left: $p;
padding-right: $p;
overflow: unset;
margin-bottom: $interiorMargin;
@include hover {
&:not(:focus, .locked) {
background: rgba($colorBodyFg, 0.1);
@ -394,12 +411,17 @@
opacity: 0.7;
}
}
&__remove{
float: right;
}
}
/****************************** EMBEDS */
@mixin snapThumb() {
// LEGACY: TODO: refactor when .snap-thumb in New Entry dialog is refactored
$d: 30px;
$d: 40px;
border: 1px solid $colorInteriorBorder;
cursor: pointer;
width: $d;
@ -417,21 +439,24 @@
// 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;
[class*="__"] + [class*="__"] {
margin-left: $interiorMargin;
}
border: 1px solid $colorInteriorBorder;
&__info {
display: flex;
flex-direction: column;
margin-left: $interiorMargin;
a {
color: $colorKey;
}
@ -445,9 +470,10 @@
}
&__link {
flex: 1 1 auto;
&:before {
display: block;
font-size: 0.85em;
font-size: 1em;
margin-right: $interiorMarginSm;
}
}
@ -460,6 +486,24 @@
&__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 */
@ -700,6 +744,7 @@ body.mobile {
.c-notebook__entry {
[class*="local-controls"] {
display: none;
height: fit-content;
}
}

View File

@ -37,7 +37,7 @@
title="Add new tag"
@click="addTag"
>
<div class="c-icon-button__label">Add Tag</div>
<div class="c-icon-button__label c-tag-btn__label">Add Tag</div>
</button>
</div>
</template>

View File

@ -54,6 +54,10 @@
}
}
.c-tag-btn__label {
overflow: visible!important;
}
/******************************* HOVERS */
.has-tag-applier {
// Apply this class to all components that should trigger tag removal btn on hover