mirror of
https://github.com/nasa/openmct.git
synced 2024-12-18 20:57:53 +00:00
Merge branch 'master' into performance-throttle-1hz
This commit is contained in:
commit
21ce86b49e
4
API.md
4
API.md
@ -1315,7 +1315,7 @@ The show function is responsible for the rendering of a view. An [Intersection O
|
||||
|
||||
### Implementing Visibility-Based Rendering
|
||||
|
||||
The `renderWhenVisible` function is passed to the show function as a required part of the `viewOptions` object. This function should 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.
|
||||
The `renderWhenVisible` function is passed to the show function as part of the `viewOptions` object. 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`).
|
||||
|
||||
@ -1325,7 +1325,7 @@ Here’s the signature for the show function:
|
||||
|
||||
* `element` (HTMLElement) - The DOM element where the view should be rendered.
|
||||
* `isEditing` (boolean) - Indicates whether the view is in editing mode.
|
||||
* `viewOptions` (Object) - A required object with configuration options for the view, including:
|
||||
* `viewOptions` (Object) - An object with configuration options for the view, including:
|
||||
* `renderWhenVisible` (Function) - This function wraps the `requestAnimationFrame` and only triggers the provided render logic when the view is visible in the viewport.
|
||||
|
||||
### Example
|
||||
|
@ -247,6 +247,14 @@ test.describe('Example Imagery Object', () => {
|
||||
await page.mouse.click(canvasCenterX - 50, canvasCenterY - 50);
|
||||
await expect(page.getByText('Driving')).toBeVisible();
|
||||
await expect(page.getByText('Science')).toBeVisible();
|
||||
|
||||
// add another tag and expect it to appear without changing selection
|
||||
await page.getByRole('button', { name: /Add Tag/ }).click();
|
||||
await page.getByPlaceholder('Type to select tag').click();
|
||||
await page.getByText('Drilling').click();
|
||||
await expect(page.getByText('Driving')).toBeVisible();
|
||||
await expect(page.getByText('Science')).toBeVisible();
|
||||
await expect(page.getByText('Drilling')).toBeVisible();
|
||||
});
|
||||
|
||||
test('Can use + - buttons to zoom on the image @unstable', async ({ page }) => {
|
||||
|
@ -224,4 +224,22 @@ test.describe('Tagging in Notebooks @addInit', () => {
|
||||
// Verify the AutoComplete field is hidden
|
||||
await expect(page.locator('[placeholder="Type to select tag"]')).toBeHidden();
|
||||
});
|
||||
test('Can start to add a tag, click away, and add a tag', async ({ page }) => {
|
||||
await createNotebookEntryAndTags(page);
|
||||
|
||||
await page.getByRole('tab', { name: 'Annotations' }).click();
|
||||
|
||||
// Click on the body simulating a click outside the autocomplete)
|
||||
await page.locator('body').click();
|
||||
await page.locator(`[aria-label="Notebook Entry"]`).click();
|
||||
|
||||
await page.hover(`button:has-text("Add Tag")`);
|
||||
await page.locator(`button:has-text("Add Tag")`).click();
|
||||
|
||||
// Click inside the tag search input
|
||||
await page.locator('[placeholder="Type to select tag"]').click();
|
||||
// Select the "Driving" tag
|
||||
await page.locator('[aria-label="Autocomplete Options"] >> text=Drilling').click();
|
||||
await expect(page.getByLabel('Notebook Entries').getByText('Drilling')).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
@ -165,6 +165,29 @@ test.describe('Plot Tagging', () => {
|
||||
|
||||
await expect(page.getByText('Science')).toBeVisible();
|
||||
await expect(page.getByText('Driving')).toBeHidden();
|
||||
|
||||
// click elsewhere
|
||||
await page.locator('body').click();
|
||||
//click on tagged plot point again
|
||||
await canvas.click({
|
||||
position: {
|
||||
x: 100,
|
||||
y: 100
|
||||
}
|
||||
});
|
||||
// Add driving tag again
|
||||
await page.getByText('Annotations').click();
|
||||
await page.getByRole('button', { name: /Add Tag/ }).click();
|
||||
await page.getByPlaceholder('Type to select tag').click();
|
||||
await page.getByText('Driving').click();
|
||||
await expect(page.getByText('Science')).toBeVisible();
|
||||
await expect(page.getByText('Driving')).toBeVisible();
|
||||
|
||||
// Delete Driving again
|
||||
await page.hover('[aria-label="Tag"]:has-text("Driving")');
|
||||
await page.locator('[aria-label="Remove tag Driving"]').click();
|
||||
await expect(page.getByText('Science')).toBeVisible();
|
||||
await expect(page.getByText('Driving')).toBeHidden();
|
||||
}
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
|
@ -198,6 +198,32 @@ test.describe('Grand Search', () => {
|
||||
await expect(searchResultDropDown).toContainText('Clock A');
|
||||
});
|
||||
|
||||
test('Slowly typing after search debounce will abort requests @couchdb', async ({ page }) => {
|
||||
let requestWasAborted = false;
|
||||
await createObjectsForSearch(page);
|
||||
page.on('requestfailed', (request) => {
|
||||
// check if the request was aborted
|
||||
if (request.failure().errorText === 'net::ERR_ABORTED') {
|
||||
requestWasAborted = true;
|
||||
}
|
||||
});
|
||||
|
||||
// Intercept and delay request
|
||||
const delayInMs = 100;
|
||||
|
||||
await page.route('**', async (route, request) => {
|
||||
await new Promise((resolve) => setTimeout(resolve, delayInMs));
|
||||
route.continue();
|
||||
});
|
||||
|
||||
// Slowly type after search delay
|
||||
const searchInput = page.getByRole('searchbox', { name: 'Search Input' });
|
||||
await searchInput.pressSequentially('Clock', { delay: 200 });
|
||||
await expect(page.getByText('Clock B').first()).toBeVisible();
|
||||
|
||||
expect(requestWasAborted).toBe(true);
|
||||
});
|
||||
|
||||
test('Validate multiple objects in search results return partial matches', async ({ page }) => {
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "openmct",
|
||||
"version": "3.2.0-next",
|
||||
"version": "3.3.0-next",
|
||||
"description": "The Open MCT core platform",
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "7.22.5",
|
||||
@ -12,7 +12,7 @@
|
||||
"@types/eventemitter3": "1.2.0",
|
||||
"@types/jasmine": "5.1.2",
|
||||
"@types/lodash": "4.14.192",
|
||||
"@vue/compiler-sfc": "3.3.8",
|
||||
"@vue/compiler-sfc": "3.3.10",
|
||||
"babel-loader": "9.1.0",
|
||||
"babel-plugin-istanbul": "6.1.1",
|
||||
"codecov": "3.8.3",
|
||||
|
@ -232,6 +232,10 @@ export default class ObjectAPI {
|
||||
.get(identifier, abortSignal)
|
||||
.then((domainObject) => {
|
||||
delete this.cache[keystring];
|
||||
if (!domainObject && abortSignal.aborted) {
|
||||
// we've aborted the request
|
||||
return;
|
||||
}
|
||||
domainObject = this.applyGetInterceptors(identifier, domainObject);
|
||||
|
||||
if (this.supportsMutation(identifier)) {
|
||||
@ -791,6 +795,9 @@ export default class ObjectAPI {
|
||||
*/
|
||||
async getOriginalPath(identifier, path = [], abortSignal = null) {
|
||||
const domainObject = await this.get(identifier, abortSignal);
|
||||
if (!domainObject) {
|
||||
return [];
|
||||
}
|
||||
path.push(domainObject);
|
||||
const { location } = domainObject;
|
||||
if (location && !this.#pathContainsDomainObject(location, path)) {
|
||||
|
@ -33,8 +33,11 @@
|
||||
|
||||
<script>
|
||||
import Flatbush from 'flatbush';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import { toRaw } from 'vue';
|
||||
|
||||
import TagEditorClassNames from '../../inspectorViews/annotations/tags/TagEditorClassNames';
|
||||
|
||||
const EXISTING_ANNOTATION_STROKE_STYLE = '#D79078';
|
||||
const EXISTING_ANNOTATION_FILL_STYLE = 'rgba(202, 202, 142, 0.2)';
|
||||
const SELECTED_ANNOTATION_STROKE_COLOR = '#BD8ECC';
|
||||
@ -118,9 +121,22 @@ export default {
|
||||
document.body.removeEventListener('click', this.cancelSelection);
|
||||
},
|
||||
methods: {
|
||||
onAnnotationChange(annotations) {
|
||||
this.selectedAnnotations = annotations;
|
||||
this.$emit('annotations-changed', annotations);
|
||||
onAnnotationChange(updatedAnnotations) {
|
||||
updatedAnnotations.forEach((updatedAnnotation) => {
|
||||
// Try to find the annotation in the existing selected annotations
|
||||
const existingIndex = this.selectedAnnotations.findIndex((annotation) =>
|
||||
this.openmct.objects.areIdsEqual(annotation.identifier, updatedAnnotation.identifier)
|
||||
);
|
||||
|
||||
// If found, update it
|
||||
if (existingIndex > -1) {
|
||||
this.selectedAnnotations[existingIndex] = updatedAnnotation;
|
||||
} else {
|
||||
// If not found, add it
|
||||
this.selectedAnnotations.push(updatedAnnotation);
|
||||
}
|
||||
});
|
||||
this.$emit('annotations-changed', this.selectedAnnotations);
|
||||
},
|
||||
transformAnnotationRectangleToFlatbushRectangle(annotationRectangle) {
|
||||
let { x, y, width, height } = annotationRectangle;
|
||||
@ -164,7 +180,13 @@ export default {
|
||||
const targetDetails = [];
|
||||
annotations.forEach((annotation) => {
|
||||
annotation.targets.forEach((target) => {
|
||||
targetDetails.push(toRaw(target));
|
||||
// only add targetDetails if we haven't added it before
|
||||
const targetAlreadyAdded = targetDetails.some((targetDetail) => {
|
||||
return isEqual(targetDetail, toRaw(target));
|
||||
});
|
||||
if (!targetAlreadyAdded) {
|
||||
targetDetails.push(toRaw(target));
|
||||
}
|
||||
});
|
||||
});
|
||||
this.selectedAnnotations = annotations;
|
||||
@ -296,9 +318,13 @@ export default {
|
||||
cancelSelection(event) {
|
||||
if (this.$refs.canvas) {
|
||||
const clickedInsideCanvas = this.$refs.canvas.contains(event.target);
|
||||
// unfortunate side effect from possibly being detached from the DOM when
|
||||
// adding/deleting tags, so closest() won't work
|
||||
const clickedTagEditor = Object.values(TagEditorClassNames).some((className) => {
|
||||
return event.target.classList.contains(className);
|
||||
});
|
||||
const clickedInsideInspector = event.target.closest('.js-inspector') !== null;
|
||||
const clickedOption = event.target.closest('.js-autocomplete-options') !== null;
|
||||
if (!clickedInsideCanvas && !clickedInsideInspector && !clickedOption) {
|
||||
if (!clickedInsideCanvas && !clickedTagEditor && !clickedInsideInspector) {
|
||||
this.newAnnotationRectangle = {};
|
||||
this.selectedAnnotations = [];
|
||||
this.drawAnnotations();
|
||||
@ -345,12 +371,13 @@ export default {
|
||||
const resultIndicies = this.annotationsIndex.search(x, y, x, y);
|
||||
resultIndicies.forEach((resultIndex) => {
|
||||
const foundAnnotation = this.indexToAnnotationMap[resultIndex];
|
||||
if (foundAnnotation._deleted) {
|
||||
return;
|
||||
}
|
||||
nearbyAnnotations.push(foundAnnotation);
|
||||
});
|
||||
//show annotations if some were found
|
||||
//if everything has been deleted, don't bother with the selection
|
||||
const allAnnotationsDeleted = nearbyAnnotations.every((annotation) => annotation._deleted);
|
||||
if (allAnnotationsDeleted) {
|
||||
nearbyAnnotations = [];
|
||||
}
|
||||
const { targetDomainObjects, targetDetails } =
|
||||
this.prepareExistingAnnotationSelection(nearbyAnnotations);
|
||||
this.selectImageAnnotations({
|
||||
@ -419,6 +446,7 @@ export default {
|
||||
},
|
||||
drawAnnotations() {
|
||||
this.clearCanvas();
|
||||
let drawnRectangles = [];
|
||||
this.imageryAnnotations.forEach((annotation) => {
|
||||
if (annotation._deleted) {
|
||||
return;
|
||||
@ -426,19 +454,31 @@ export default {
|
||||
const annotationRectangle = annotation.targets.find(
|
||||
(target) => target.keyString === this.keyString
|
||||
)?.rectangle;
|
||||
const rectangleForPixelDensity = this.transformRectangleToPixelDense(annotationRectangle);
|
||||
if (this.isSelectedAnnotation(annotation)) {
|
||||
this.drawRectInCanvas(
|
||||
rectangleForPixelDensity,
|
||||
SELECTED_ANNOTATION_FILL_STYLE,
|
||||
SELECTED_ANNOTATION_STROKE_COLOR
|
||||
);
|
||||
} else {
|
||||
this.drawRectInCanvas(
|
||||
rectangleForPixelDensity,
|
||||
EXISTING_ANNOTATION_FILL_STYLE,
|
||||
EXISTING_ANNOTATION_STROKE_STYLE
|
||||
);
|
||||
|
||||
// Check if the rectangle has already been drawn
|
||||
const hasBeenDrawn = drawnRectangles.some(
|
||||
(drawnRect) =>
|
||||
drawnRect.x === annotationRectangle.x &&
|
||||
drawnRect.y === annotationRectangle.y &&
|
||||
drawnRect.width === annotationRectangle.width &&
|
||||
drawnRect.height === annotationRectangle.height
|
||||
);
|
||||
if (!hasBeenDrawn) {
|
||||
const rectangleForPixelDensity = this.transformRectangleToPixelDense(annotationRectangle);
|
||||
if (this.isSelectedAnnotation(annotation)) {
|
||||
this.drawRectInCanvas(
|
||||
rectangleForPixelDensity,
|
||||
SELECTED_ANNOTATION_FILL_STYLE,
|
||||
SELECTED_ANNOTATION_STROKE_COLOR
|
||||
);
|
||||
} else {
|
||||
this.drawRectInCanvas(
|
||||
rectangleForPixelDensity,
|
||||
EXISTING_ANNOTATION_FILL_STYLE,
|
||||
EXISTING_ANNOTATION_STROKE_STYLE
|
||||
);
|
||||
}
|
||||
drawnRectangles.push(annotationRectangle);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -35,6 +35,7 @@
|
||||
<script>
|
||||
import mount from 'utils/mount';
|
||||
|
||||
import VisibilityObserver from '../../utils/visibility/VisibilityObserver';
|
||||
import Plot from '../plot/PlotView.vue';
|
||||
import TelemetryFrame from './TelemetryFrame.vue';
|
||||
|
||||
@ -89,8 +90,9 @@ export default {
|
||||
this.clearPlots();
|
||||
|
||||
this.unregisterTimeContextList = [];
|
||||
this.elementsList = [];
|
||||
this.componentsList = [];
|
||||
this.elementsList = [];
|
||||
this.visibilityObservers = [];
|
||||
|
||||
this.telemetryKeys.forEach(async (telemetryKey) => {
|
||||
const plotObject = await this.openmct.objects.get(telemetryKey);
|
||||
@ -109,7 +111,10 @@ export default {
|
||||
return this.openmct.time.addIndependentContext(keyString, this.bounds);
|
||||
},
|
||||
renderPlot(plotObject) {
|
||||
const { vNode, destroy } = mount(
|
||||
const wrapper = document.createElement('div');
|
||||
const visibilityObserver = new VisibilityObserver(wrapper);
|
||||
|
||||
const { destroy } = mount(
|
||||
{
|
||||
components: {
|
||||
TelemetryFrame,
|
||||
@ -117,7 +122,8 @@ export default {
|
||||
},
|
||||
provide: {
|
||||
openmct: this.openmct,
|
||||
path: [plotObject]
|
||||
path: [plotObject],
|
||||
renderWhenVisible: visibilityObserver.renderWhenVisible
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@ -133,13 +139,15 @@ export default {
|
||||
</TelemetryFrame>`
|
||||
},
|
||||
{
|
||||
app: this.openmct.app
|
||||
app: this.openmct.app,
|
||||
element: wrapper
|
||||
}
|
||||
);
|
||||
|
||||
this.componentsList.push(destroy);
|
||||
this.elementsList.push(vNode.el);
|
||||
this.$refs.numericDataView.append(vNode.el);
|
||||
this.elementsList.push(wrapper);
|
||||
this.visibilityObservers.push(visibilityObserver);
|
||||
this.$refs.numericDataView.append(wrapper);
|
||||
},
|
||||
clearPlots() {
|
||||
if (this.componentsList?.length) {
|
||||
@ -152,6 +160,11 @@ export default {
|
||||
delete this.elementsList;
|
||||
}
|
||||
|
||||
if (this.visibilityObservers?.length) {
|
||||
this.visibilityObservers.forEach((visibilityObserver) => visibilityObserver.destroy());
|
||||
delete this.visibilityObservers;
|
||||
}
|
||||
|
||||
if (this.plotObjects?.length) {
|
||||
this.plotObjects = [];
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ export default {
|
||||
}
|
||||
|
||||
return this.loadedAnnotations.filter((annotation) => {
|
||||
return !annotation.tags && !annotation._deleted;
|
||||
return !annotation.tags;
|
||||
});
|
||||
},
|
||||
tagAnnotations() {
|
||||
@ -74,7 +74,7 @@ export default {
|
||||
}
|
||||
|
||||
return this.loadedAnnotations.filter((annotation) => {
|
||||
return !annotation.tags && !annotation._deleted;
|
||||
return !annotation.tags;
|
||||
});
|
||||
},
|
||||
multiSelection() {
|
||||
|
@ -35,10 +35,13 @@
|
||||
<button
|
||||
v-show="!userAddingTag && !maxTagsAdded"
|
||||
class="c-tag-applier__add-btn c-icon-button c-icon-button--major icon-plus"
|
||||
:class="TagEditorClassNames.ADD_TAG_BUTTON"
|
||||
title="Add new tag"
|
||||
@click="addTag"
|
||||
>
|
||||
<div class="c-icon-button__label c-tag-btn__label">Add Tag</div>
|
||||
<div class="c-icon-button__label c-tag-btn__label" :class="TagEditorClassNames.ADD_TAG_LABEL">
|
||||
Add Tag
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
@ -46,6 +49,7 @@
|
||||
<script>
|
||||
import { toRaw } from 'vue';
|
||||
|
||||
import TagEditorClassNames from './TagEditorClassNames';
|
||||
import TagSelection from './TagSelection.vue';
|
||||
|
||||
export default {
|
||||
@ -88,7 +92,8 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
addedTags: [],
|
||||
userAddingTag: false
|
||||
userAddingTag: false,
|
||||
TagEditorClassNames: TagEditorClassNames
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
@ -0,0 +1,9 @@
|
||||
const TagEditorClassNames = Object.freeze({
|
||||
REMOVE_TAG: 'js-remove-tag',
|
||||
AUTOCOMPLETE_INPUT: 'js-autocomplete__input',
|
||||
ADD_TAG_BUTTON: 'js-add-tag-button',
|
||||
ADD_TAG_LABEL: 'js-add-tag-label',
|
||||
TAG_OPTION: 'js-tag-option'
|
||||
});
|
||||
|
||||
export default TagEditorClassNames;
|
@ -29,7 +29,7 @@
|
||||
:model="availableTagModel"
|
||||
:place-holder-text="'Type to select tag'"
|
||||
class="c-tag-selection"
|
||||
:item-css-class="'icon-circle'"
|
||||
:item-css-class="`icon-circle ${TagEditorClassNames.TAG_OPTION}`"
|
||||
@on-change="tagSelected"
|
||||
/>
|
||||
</template>
|
||||
@ -42,6 +42,7 @@
|
||||
<button
|
||||
v-show="!readOnly"
|
||||
class="c-completed-tag-deletion c-tag__remove-btn icon-x-in-circle"
|
||||
:class="TagEditorClassNames.REMOVE_TAG"
|
||||
:style="{ textShadow: selectedBackgroundColor + ' 0 0 4px' }"
|
||||
:aria-label="`Remove tag ${selectedTagLabel}`"
|
||||
@click="removeTag"
|
||||
@ -54,6 +55,7 @@
|
||||
|
||||
<script>
|
||||
import AutoCompleteField from '../../../../api/forms/components/controls/AutoCompleteField.vue';
|
||||
import TagEditorClassNames from './TagEditorClassNames';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -88,7 +90,7 @@ export default {
|
||||
},
|
||||
emits: ['tag-removed', 'tag-added'],
|
||||
data() {
|
||||
return {};
|
||||
return { TagEditorClassNames: TagEditorClassNames };
|
||||
},
|
||||
computed: {
|
||||
availableTagModel() {
|
||||
@ -137,7 +139,6 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {},
|
||||
methods: {
|
||||
getAvailableTagByID(tagID) {
|
||||
return this.openmct.annotation.getAvailableTags().find((tag) => {
|
||||
|
@ -139,6 +139,7 @@
|
||||
@editing-entry="startTransaction"
|
||||
@delete-entry="deleteEntry"
|
||||
@update-entry="updateEntry"
|
||||
@update-annotations="loadAnnotations"
|
||||
@entry-selection="entrySelection(entry)"
|
||||
/>
|
||||
</div>
|
||||
@ -298,6 +299,12 @@ export default {
|
||||
},
|
||||
showTime() {
|
||||
mutateObject(this.openmct, this.domainObject, 'configuration.showTime', this.showTime);
|
||||
},
|
||||
notebookAnnotations: {
|
||||
handler() {
|
||||
this.filterAndSortEntries();
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
|
@ -274,7 +274,8 @@ export default {
|
||||
'change-section-page',
|
||||
'update-entry',
|
||||
'editing-entry',
|
||||
'entry-selection'
|
||||
'entry-selection',
|
||||
'update-annotations'
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
@ -638,13 +639,16 @@ export default {
|
||||
this.entry.text = restoredQuoteBrackets;
|
||||
this.timestampAndUpdate();
|
||||
},
|
||||
updateAnnotations(newAnnotations) {
|
||||
this.$emit('update-annotations', newAnnotations);
|
||||
},
|
||||
selectAndEmitEntry(event, entry) {
|
||||
selectEntry({
|
||||
element: this.$refs.entry,
|
||||
entryId: entry.id,
|
||||
domainObject: this.domainObject,
|
||||
openmct: this.openmct,
|
||||
onAnnotationChange: this.timestampAndUpdate,
|
||||
onAnnotationChange: this.updateAnnotations,
|
||||
notebookAnnotations: this.notebookAnnotations
|
||||
});
|
||||
event.stopPropagation();
|
||||
|
@ -180,7 +180,7 @@ define(['uuid'], function ({ v4: uuid }) {
|
||||
{
|
||||
check(domainObject) {
|
||||
return (
|
||||
domainObject.type === 'layout' &&
|
||||
domainObject?.type === 'layout' &&
|
||||
domainObject.configuration &&
|
||||
domainObject.configuration.layout
|
||||
);
|
||||
@ -201,7 +201,7 @@ define(['uuid'], function ({ v4: uuid }) {
|
||||
{
|
||||
check(domainObject) {
|
||||
return (
|
||||
domainObject.type === 'telemetry.fixed' &&
|
||||
domainObject?.type === 'telemetry.fixed' &&
|
||||
domainObject.configuration &&
|
||||
domainObject.configuration['fixed-display']
|
||||
);
|
||||
@ -246,7 +246,7 @@ define(['uuid'], function ({ v4: uuid }) {
|
||||
{
|
||||
check(domainObject) {
|
||||
return (
|
||||
domainObject.type === 'table' &&
|
||||
domainObject?.type === 'table' &&
|
||||
domainObject.configuration &&
|
||||
domainObject.configuration.table
|
||||
);
|
||||
|
@ -178,7 +178,9 @@
|
||||
import Flatbush from 'flatbush';
|
||||
import _ from 'lodash';
|
||||
import { useEventBus } from 'utils/useEventBus';
|
||||
import { toRaw } from 'vue';
|
||||
|
||||
import TagEditorClassNames from '../inspectorViews/annotations/tags/TagEditorClassNames';
|
||||
import XAxis from './axis/XAxis.vue';
|
||||
import YAxis from './axis/YAxis.vue';
|
||||
import MctChart from './chart/MctChart.vue';
|
||||
@ -465,9 +467,14 @@ export default {
|
||||
cancelSelection(event) {
|
||||
if (this.$refs?.plot) {
|
||||
const clickedInsidePlot = this.$refs.plot.contains(event.target);
|
||||
// unfortunate side effect from possibly being detached from the DOM when
|
||||
// adding/deleting tags, so closest() won't work
|
||||
const clickedTagEditor = Object.values(TagEditorClassNames).some((className) => {
|
||||
return event.target.classList.contains(className);
|
||||
});
|
||||
const clickedInsideInspector = event.target.closest('.js-inspector') !== null;
|
||||
const clickedOption = event.target.closest('.js-autocomplete-options') !== null;
|
||||
if (!clickedInsidePlot && !clickedInsideInspector && !clickedOption) {
|
||||
if (!clickedInsidePlot && !clickedInsideInspector && !clickedOption && !clickedTagEditor) {
|
||||
this.rectangles = [];
|
||||
this.annotationSelectionsBySeries = {};
|
||||
this.selectPlot();
|
||||
@ -937,7 +944,10 @@ export default {
|
||||
const targetDetails = [];
|
||||
const uniqueBoundsAnnotations = [];
|
||||
annotations.forEach((annotation) => {
|
||||
targetDetails.push(annotation.targets);
|
||||
// for each target, push toRaw
|
||||
annotation.targets.forEach((target) => {
|
||||
targetDetails.push(toRaw(target));
|
||||
});
|
||||
|
||||
const boundingBoxAlreadyAdded = uniqueBoundsAnnotations.some((existingAnnotation) => {
|
||||
const existingBoundingBox = Object.values(existingAnnotation.targets)[0];
|
||||
|
Loading…
Reference in New Issue
Block a user