From e9ce5780cce37500e7f2587295098021909d3fb8 Mon Sep 17 00:00:00 2001 From: iVy Deliz Date: Fri, 8 Nov 2024 16:22:34 -0700 Subject: [PATCH] Add "Scroll to Activity" to activity directive table context menu (#1433) --- .../activity/ActivityDirectivesTable.svelte | 28 ++++++++++++++++++- .../ActivityDirectivesTablePanel.svelte | 10 ++++++- .../timeline/TimelineViewControls.svelte | 18 +++--------- .../ui/DataGrid/BulkActionDataGrid.svelte | 4 ++- src/utilities/timeline.ts | 11 ++++++++ 5 files changed, 54 insertions(+), 17 deletions(-) diff --git a/src/components/activity/ActivityDirectivesTable.svelte b/src/components/activity/ActivityDirectivesTable.svelte index 0e9647c9b4..f283e8cb90 100644 --- a/src/components/activity/ActivityDirectivesTable.svelte +++ b/src/components/activity/ActivityDirectivesTable.svelte @@ -14,6 +14,9 @@ import BulkActionDataGrid from '../ui/DataGrid/BulkActionDataGrid.svelte'; import type DataGrid from '../ui/DataGrid/DataGrid.svelte'; import DataGridActions from '../ui/DataGrid/DataGridActions.svelte'; + import ContextMenuItem from '../context-menu/ContextMenuItem.svelte'; + import ContextMenuSeparator from '../context-menu/ContextMenuSeparator.svelte'; + import { createEventDispatcher } from 'svelte'; export let activityDirectives: ActivityDirective[] = []; export let activityDirectiveErrorRollupsMap: Record | undefined = undefined; @@ -22,10 +25,15 @@ export let dataGrid: DataGrid | undefined = undefined; export let plan: Plan | null; export let selectedActivityDirectiveId: ActivityDirectiveId | null = null; + export let bulkSelectedActivityDirectiveIds: ActivityDirectiveId[] = []; export let planReadOnly: boolean = false; export let user: User | null; export let filterExpression: string = ''; + const dispatch = createEventDispatcher<{ + scrollTimelineToTime: number; + }>(); + type ActivityDirectiveWithErrorCounts = ActivityDirective & { errorCounts?: ActivityErrorCounts }; type CellRendererParams = { deleteActivityDirective: (activity: ActivityDirective) => void; @@ -129,11 +137,22 @@ function getRowId(activityDirective: ActivityDirective): ActivityDirectiveId { return activityDirective.id; } + + function scrollTimelineToActivityDirective() { + const directiveId = + (bulkSelectedActivityDirectiveIds.length > 0 && bulkSelectedActivityDirectiveIds[0]) ?? + selectedActivityDirectiveId; + const directive = activityDirectives.find(item => item.id === directiveId) ?? null; + if (directive?.start_time_ms !== undefined && directive?.start_time_ms !== null) { + dispatch('scrollTimelineToTime', directive.start_time_ms); + } + } +> + + {#if bulkSelectedActivityDirectiveIds.length === 1} + Scroll to Activity + + {/if} + + diff --git a/src/components/activity/ActivityDirectivesTablePanel.svelte b/src/components/activity/ActivityDirectivesTablePanel.svelte index 9dfc2087a3..6a90ff23c9 100644 --- a/src/components/activity/ActivityDirectivesTablePanel.svelte +++ b/src/components/activity/ActivityDirectivesTablePanel.svelte @@ -14,7 +14,7 @@ import { InvalidDate } from '../../constants/time'; import { activityDirectivesMap, selectActivity, selectedActivityDirectiveId } from '../../stores/activities'; import { activityErrorRollupsMap } from '../../stores/errors'; - import { plan, planReadOnly } from '../../stores/plan'; + import { maxTimeRange, plan, planReadOnly, viewTimeRange } from '../../stores/plan'; import { plugins } from '../../stores/plugins'; import { view, viewTogglePanel, viewUpdateActivityDirectivesTable } from '../../stores/views'; import type { ActivityDirective } from '../../types/activity'; @@ -30,6 +30,8 @@ import Panel from '../ui/Panel.svelte'; import ActivityDirectivesTable from './ActivityDirectivesTable.svelte'; import ActivityTableMenu from './ActivityTableMenu.svelte'; + import { get } from 'svelte/store'; + import { getTimeRangeCenteredAroundTime } from '../../utilities/timeline'; export let gridSection: ViewGridSection; export let user: User | null; @@ -364,6 +366,11 @@ viewUpdateActivityDirectivesTable({ autoSizeColumns: 'off' }); } } + + function scrollTimelineToTime({ detail }: CustomEvent) { + const centeredTimeRange = getTimeRangeCenteredAroundTime(detail, get(viewTimeRange), get(maxTimeRange)); + viewTimeRange.set(centeredTimeRange); + } @@ -417,6 +424,7 @@ on:gridSizeChanged={onGridSizeChangedDebounced} on:rowDoubleClicked={onRowDoubleClicked} on:selectionChanged={onSelectionChanged} + on:scrollTimelineToTime={scrollTimelineToTime} /> diff --git a/src/components/timeline/TimelineViewControls.svelte b/src/components/timeline/TimelineViewControls.svelte index 903ff1fb15..a3f38f823d 100644 --- a/src/components/timeline/TimelineViewControls.svelte +++ b/src/components/timeline/TimelineViewControls.svelte @@ -27,13 +27,8 @@ } from '../../stores/simulation'; import { timelineInteractionMode, timelineLockStatus, viewIsModified } from '../../stores/views'; import type { TimeRange } from '../../types/timeline'; - import { - getActivityDirectiveStartTimeMs, - getDoyTimeFromInterval, - getIntervalInMs, - getUnixEpochTime, - } from '../../utilities/time'; - import { TimelineLockStatus } from '../../utilities/timeline'; + import { getActivityDirectiveStartTimeMs, getDoyTimeFromInterval, getUnixEpochTime } from '../../utilities/time'; + import { getTimeRangeCenteredAroundTime, TimelineLockStatus } from '../../utilities/timeline'; import { showFailureToast, showSuccessToast } from '../../utilities/toast'; import { tooltip } from '../../utilities/tooltip'; import Input from '../form/Input.svelte'; @@ -217,13 +212,8 @@ function scrollToSelection() { const time = getSelectionTime(); if (!isNaN(time) && (time < viewTimeRange.start || time > viewTimeRange.end)) { - const midSpan = time + getIntervalInMs($selectedSpan?.duration) / 2; - const start = Math.max(maxTimeRange.start, midSpan - viewDuration / 2); - const end = Math.min(maxTimeRange.end, midSpan + viewDuration / 2); - dispatch('viewTimeRangeChanged', { - end, - start, - }); + const centeredTimeRange = getTimeRangeCenteredAroundTime(time, viewTimeRange, maxTimeRange); + dispatch('viewTimeRangeChanged', centeredTimeRange); } } diff --git a/src/components/ui/DataGrid/BulkActionDataGrid.svelte b/src/components/ui/DataGrid/BulkActionDataGrid.svelte index 1c46d4fa69..13efd9996e 100644 --- a/src/components/ui/DataGrid/BulkActionDataGrid.svelte +++ b/src/components/ui/DataGrid/BulkActionDataGrid.svelte @@ -33,6 +33,7 @@ export let pluralItemDisplayText: string = ''; export let scrollToSelection: boolean = false; export let selectedItemId: RowId | null = null; + export let selectedItemIds: RowId[] = []; export let showContextMenu: boolean = true; export let singleItemDisplayText: string = ''; export let suppressDragLeaveHidesColumns: boolean = true; @@ -48,7 +49,6 @@ let isFiltered: boolean = false; let deletePermission: boolean = true; - let selectedItemIds: RowId[] = []; $: if (typeof hasDeletePermission === 'function' && user) { if (selectedItemIds.length > 0) { @@ -172,6 +172,8 @@ > {#if showContextMenu} + + Bulk Actions Select All {isFiltered ? 'Visible ' : ''}{pluralItemDisplayText} diff --git a/src/utilities/timeline.ts b/src/utilities/timeline.ts index d46d5d2e1e..0a437891f7 100644 --- a/src/utilities/timeline.ts +++ b/src/utilities/timeline.ts @@ -428,6 +428,17 @@ export function getUniqueColorForLineLayer(row?: Row): string { return color; } +export function getTimeRangeCenteredAroundTime( + time: number, + currentTimeRange: TimeRange, + maxTimeRange: TimeRange, +): TimeRange { + const padding = (currentTimeRange.end - currentTimeRange.start) / 2; + const start = Math.max(maxTimeRange.start, time - padding); + const end = Math.min(maxTimeRange.end, time + padding); + return { end, start }; +} + /** * Returns a new vertical guide */