From a5f2b06e52bda1504a1c4c32a4039d3caacb01a0 Mon Sep 17 00:00:00 2001 From: Bryan Date: Fri, 17 Feb 2023 16:26:03 -0800 Subject: [PATCH] feat: Toggle view panels via new menu buttons (#401) * add toggle view panel icons --- package-lock.json | 14 +-- package.json | 2 +- src/components/menus/ViewMenu.svelte | 89 ++++++++++++++-- src/components/ui/CssGrid.svelte | 10 +- src/components/ui/PlanGrid.svelte | 33 ++++-- src/components/ui/ToggleableIcon.svelte | 11 ++ src/routes/plans/[id]/+page.svelte | 41 +++++++- src/stores/views.ts | 62 +++++++++++- src/types/view.ts | 16 +++ src/utilities/view.ts | 128 +++++++++++++++++++++++- svelte.config.js | 9 ++ 11 files changed, 381 insertions(+), 34 deletions(-) create mode 100644 src/components/ui/ToggleableIcon.svelte diff --git a/package-lock.json b/package-lock.json index 52e8592f16..ccb521c07e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "MIT", "dependencies": { "@fontsource/jetbrains-mono": "^4.5.11", - "@nasa-jpl/stellar": "^1.0.24", + "@nasa-jpl/stellar": "^1.0.25", "@sveltejs/adapter-node": "^1.1.3", "@sveltejs/kit": "^1.1.1", "ag-grid-community": "^29.0.0", @@ -660,9 +660,9 @@ } }, "node_modules/@nasa-jpl/stellar": { - "version": "1.0.24", - "resolved": "https://registry.npmjs.org/@nasa-jpl/stellar/-/stellar-1.0.24.tgz", - "integrity": "sha512-BWpT/r9k3Y/JoCoZ0x0PKqpoQFRUXpzu1dJaU8yEXMNnhU+B+mMWZWsei5oldYKKlPCTUC6Utc2+Qump5ExbOQ==" + "version": "1.0.25", + "resolved": "https://registry.npmjs.org/@nasa-jpl/stellar/-/stellar-1.0.25.tgz", + "integrity": "sha512-EBLCbCLl18YaOJRYSYcAOfMJdEnBs07dTWftzrp/pOBO9D4Jtjmy6O+fHQc+lhFOW/4hXhAUa2Mds01BUTNjWg==" }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", @@ -6867,9 +6867,9 @@ } }, "@nasa-jpl/stellar": { - "version": "1.0.24", - "resolved": "https://registry.npmjs.org/@nasa-jpl/stellar/-/stellar-1.0.24.tgz", - "integrity": "sha512-BWpT/r9k3Y/JoCoZ0x0PKqpoQFRUXpzu1dJaU8yEXMNnhU+B+mMWZWsei5oldYKKlPCTUC6Utc2+Qump5ExbOQ==" + "version": "1.0.25", + "resolved": "https://registry.npmjs.org/@nasa-jpl/stellar/-/stellar-1.0.25.tgz", + "integrity": "sha512-EBLCbCLl18YaOJRYSYcAOfMJdEnBs07dTWftzrp/pOBO9D4Jtjmy6O+fHQc+lhFOW/4hXhAUa2Mds01BUTNjWg==" }, "@nodelib/fs.scandir": { "version": "2.1.5", diff --git a/package.json b/package.json index 1c8047ff53..58cdd0f2b0 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ }, "dependencies": { "@fontsource/jetbrains-mono": "^4.5.11", - "@nasa-jpl/stellar": "^1.0.24", + "@nasa-jpl/stellar": "^1.0.25", "@sveltejs/adapter-node": "^1.1.3", "@sveltejs/kit": "^1.1.1", "ag-grid-community": "^29.0.0", diff --git a/src/components/menus/ViewMenu.svelte b/src/components/menus/ViewMenu.svelte index ee36a83cfa..a763d59ce4 100644 --- a/src/components/menus/ViewMenu.svelte +++ b/src/components/menus/ViewMenu.svelte @@ -1,21 +1,51 @@ -
+
+
+ toggleView('left', !leftPanelIsOn)}> + + + + + + toggleView('left-split', !leftSplitPanelIsOn)}> + + + + + +
|
+ toggleView('bottom', !bottomPanelIsOn)}> + + + + + +
|
+ toggleView('right-split', !rightSplitPanelIsOn)}> + + + + + + toggleView('right', !rightPanelIsOn)}> + + + + + +
Save Save as Reset to default @@ -70,6 +132,19 @@ position: relative; } + .toggles { + --aerie-menu-item-padding: 0px; + align-items: center; + display: grid; + grid-template-columns: repeat(7, min-content); + justify-content: space-between; + margin: 8px 8px 0; + } + + .toggle-divider { + opacity: 0.6; + } + .view-menu hr { background-color: var(--st-gray-20); border: 0; diff --git a/src/components/ui/CssGrid.svelte b/src/components/ui/CssGrid.svelte index cbe65f70f0..6cb1e0a3a8 100644 --- a/src/components/ui/CssGrid.svelte +++ b/src/components/ui/CssGrid.svelte @@ -2,7 +2,7 @@ - + {#if !leftHidden} {#if leftSplit} - +
@@ -79,7 +90,7 @@ {/if} {#if middleSplit} - +
@@ -95,10 +106,10 @@ {/if} {#if !rightHidden} - + {#if rightSplit} - +
diff --git a/src/components/ui/ToggleableIcon.svelte b/src/components/ui/ToggleableIcon.svelte new file mode 100644 index 0000000000..33d12de6ad --- /dev/null +++ b/src/components/ui/ToggleableIcon.svelte @@ -0,0 +1,11 @@ + + + + +{#if isOn} + +{:else} + +{/if} diff --git a/src/routes/plans/[id]/+page.svelte b/src/routes/plans/[id]/+page.svelte index 240dc6dbc7..ff11927960 100644 --- a/src/routes/plans/[id]/+page.svelte +++ b/src/routes/plans/[id]/+page.svelte @@ -59,8 +59,15 @@ simulationStatus, spans, } from '../../../stores/simulation'; - import { initializeView, resetOriginalView, resetView, view } from '../../../stores/views'; - import type { ViewSaveEvent } from '../../../types/view'; + import { + initializeView, + resetOriginalView, + resetView, + view, + viewTogglePanel, + viewUpdateGrid, + } from '../../../stores/views'; + import type { ViewSaveEvent, ViewToggleEvent } from '../../../types/view'; import { createActivitiesMap } from '../../../utilities/activities'; import effects from '../../../utilities/effects'; import { isSaveEvent } from '../../../utilities/keyboardEvents'; @@ -184,9 +191,30 @@ } } + function onToggleView(event: CustomEvent) { + const { detail } = event; + viewTogglePanel(detail); + } + function onResetView() { resetView(); } + + function onChangeColumnSizes(event: CustomEvent) { + viewUpdateGrid({ columnSizes: event.detail }); + } + + function onChangeLeftRowSizes(event: CustomEvent) { + viewUpdateGrid({ leftRowSizes: event.detail }); + } + + function onChangeMiddleRowSizes(event: CustomEvent) { + viewUpdateGrid({ middleRowSizes: event.detail }); + } + + function onChangeRightRowSizes(event: CustomEvent) { + viewUpdateGrid({ rightRowSizes: event.detail }); + } @@ -262,12 +290,19 @@ on:createView={onCreateView} on:editView={onEditView} on:saveView={onSaveView} + on:toggleView={onToggleView} on:resetView={onResetView} /> - + diff --git a/src/stores/views.ts b/src/stores/views.ts index d4307633fc..1d8a5ee820 100644 --- a/src/stores/views.ts +++ b/src/stores/views.ts @@ -1,9 +1,10 @@ import { isEqual } from 'lodash-es'; import { derived, get, writable, type Writable } from 'svelte/store'; -import type { View, ViewActivityTable, ViewGrid } from '../types/view'; +import type { View, ViewActivityTable, ViewGrid, ViewToggleEvent } from '../types/view'; import { getTarget } from '../utilities/generic'; import gql from '../utilities/gql'; import { TimelineLockStatus } from '../utilities/timeline'; +import { createColumnSizes, createRowSizes, parseColumnSizes } from '../utilities/view'; import { gqlSubscribable } from './subscribable'; /* Subscriptions. */ @@ -144,6 +145,65 @@ export function viewSetSelectedTimeline(timelineId: number | null): void { selectedTimelineId.set(timelineId); } +export function viewTogglePanel(event: ViewToggleEvent) { + const { state, type } = event; + const currentView = get(view); + const grid = currentView?.definition?.plan?.grid ?? { + columnSizes: '', + leftHidden: false, + rightHidden: false, + }; + + const { columnSizes, leftHidden, rightHidden } = grid; + + const { col1, col2, col3 } = parseColumnSizes(columnSizes, leftHidden, rightHidden); + switch (type) { + case 'left': { + viewUpdateGrid({ + columnSizes: createColumnSizes({ col1, col2, col3 }, !state, rightHidden), + leftHidden: !state, + leftRowSizes: createRowSizes({}, false), + leftSplit: false, + }); + break; + } + case 'left-split': { + viewUpdateGrid({ + columnSizes: createColumnSizes({ col1, col2, col3 }, !state, rightHidden), + leftHidden: !state, + leftRowSizes: createRowSizes({}, state), + leftSplit: state, + }); + break; + } + case 'bottom': { + viewUpdateGrid({ + middleRowSizes: createRowSizes({ row1: '2fr', row2: '1fr' }, state), + middleSplit: state, + }); + break; + } + case 'right': { + viewUpdateGrid({ + columnSizes: createColumnSizes({ col1, col2, col3 }, leftHidden, !state), + rightHidden: !state, + rightRowSizes: createRowSizes({}, false), + rightSplit: false, + }); + break; + } + case 'right-split': { + viewUpdateGrid({ + columnSizes: createColumnSizes({ col1, col2, col3 }, leftHidden, !state), + rightHidden: !state, + rightRowSizes: createRowSizes({}, state), + rightSplit: state, + }); + break; + } + } +} + export function viewUpdateActivityTables(update: Partial, activityTableId: number): void { view.update(currentView => ({ ...currentView, diff --git a/src/types/view.ts b/src/types/view.ts index 77c32186b6..cfd5ff6542 100644 --- a/src/types/view.ts +++ b/src/types/view.ts @@ -8,6 +8,11 @@ export type ViewActivityTable = { }; export type ViewSaveEvent = Partial; +export type ViewToggleType = 'left' | 'left-split' | 'bottom' | 'right' | 'right-split'; +export type ViewToggleEvent = { + state: boolean; + type: ViewToggleType; +}; export type ViewIFrame = { id: number; @@ -37,6 +42,17 @@ export type ViewGridComponent = export type ViewGridSection = 'LeftBottom' | 'LeftTop' | 'MiddleBottom' | 'RightBottom' | 'RightTop'; +export type ViewGridColumns = { + col1?: string; + col2?: string; + col3?: string; +}; + +export type ViewGridRows = { + row1?: string; + row2?: string; +}; + export type ViewGrid = { columnSizes: string; leftComponentBottom: ViewGridComponent; diff --git a/src/utilities/view.ts b/src/utilities/view.ts index 7d10f5b56c..fab549fd9f 100644 --- a/src/utilities/view.ts +++ b/src/utilities/view.ts @@ -1,7 +1,7 @@ import type { ActivityType } from '../types/activity'; import type { ResourceType } from '../types/simulation'; import type { ActivityLayer, Axis, LineLayer, Row, XRangeLayer } from '../types/timeline'; -import type { View } from '../types/view'; +import type { View, ViewGridColumns, ViewGridRows } from '../types/view'; /** * Generates a default generic UI view. @@ -204,6 +204,48 @@ export function generateDefaultView(activityTypes: ActivityType[] = [], resource sortIndex: null, width: 280, }, + { + aggFunc: null, + colId: 'anchor_id', + flex: null, + hide: true, + pinned: null, + pivot: false, + pivotIndex: null, + rowGroup: false, + rowGroupIndex: null, + sort: null, + sortIndex: null, + width: 200, + }, + { + aggFunc: null, + colId: 'anchored_to_start', + flex: null, + hide: true, + pinned: null, + pivot: false, + pivotIndex: null, + rowGroup: false, + rowGroupIndex: null, + sort: null, + sortIndex: null, + width: 200, + }, + { + aggFunc: null, + colId: 'start_offset', + flex: null, + hide: true, + pinned: null, + pivot: false, + pivotIndex: null, + rowGroup: false, + rowGroupIndex: null, + sort: null, + sortIndex: null, + width: 200, + }, ], id: 0, }, @@ -213,7 +255,7 @@ export function generateDefaultView(activityTypes: ActivityType[] = [], resource leftComponentBottom: 'SimulationPanel', leftComponentTop: 'ActivityTypesPanel', leftHidden: false, - leftRowSizes: '1fr 3px 1fr', + leftRowSizes: '1fr', leftSplit: false, middleComponentBottom: 'ActivityTablePanel', middleRowSizes: '2fr 3px 1fr', @@ -221,7 +263,7 @@ export function generateDefaultView(activityTypes: ActivityType[] = [], resource rightComponentBottom: 'TimelineEditorPanel', rightComponentTop: 'ActivityFormPanel', rightHidden: false, - rightRowSizes: '1fr 3px 1fr', + rightRowSizes: '1fr', rightSplit: false, }, iFrames: [ @@ -324,3 +366,83 @@ export function generateDefaultView(activityTypes: ActivityType[] = [], resource updated_at: now, }; } + +export function parseColumnSizes( + columnSizes = '1fr 3px 3fr 3px 1fr', + leftHidden: boolean, + rightHidden: boolean, +): ViewGridColumns | null { + console.log(leftHidden, rightHidden); + if (!leftHidden && !rightHidden) { + const matches = columnSizes.match( + new RegExp(`^(?\\S+)\\s(!?\\S+)\\s(?\\S+)\\s(!?\\S+)\\s(?\\S+)$`, 'i'), + ); + if (matches) { + const { groups: { col1, col2, col3 } = {} } = matches; + return { + col1, + col2, + col3, + }; + } + } else if (leftHidden && rightHidden) { + const matches = columnSizes.match(new RegExp(`^\\s(?\\S+)$`, 'i')); + if (matches) { + const { groups: { col2 } = {} } = matches; + return { + col2, + }; + } + } else if (!leftHidden && rightHidden) { + const matches = columnSizes.match(new RegExp(`^(?\\S+)\\s(?!\\S+)\\s(?\\S+)$`, 'i')); + if (matches) { + const { groups: { col1, col2 } = {} } = matches; + return { + col1, + col2, + }; + } + } else if (leftHidden && !rightHidden) { + const matches = columnSizes.match(new RegExp(`^(?\\S+)\\s(?!\\S+)\\s(?\\S+)$`, 'i')); + if (matches) { + const { groups: { col2, col3 } = {} } = matches; + return { + col2, + col3, + }; + } + } + + return { + col1: '1fr', + col2: '3fr', + col3: '1fr', + }; +} + +export function createColumnSizes( + { col1 = '1fr', col2 = '3fr', col3 = '1fr' }: ViewGridColumns, + leftHidden: boolean, + rightHidden: boolean, +): string { + const gutterSize = '3px'; + + if (leftHidden && rightHidden) { + return col2; + } else if (!leftHidden && rightHidden) { + return `${col1} ${gutterSize} ${col2}`; + } else if (leftHidden && !rightHidden) { + return `${col2} ${gutterSize} ${col3}`; + } + + return `${col1} ${gutterSize} ${col2} ${gutterSize} ${col3}`; +} + +export function createRowSizes({ row1 = '1fr', row2 = '1fr' }: ViewGridRows, colSplit: boolean): string { + const gutterSize = '3px'; + if (colSplit) { + return `${row1} ${gutterSize} ${row2}`; + } + + return '1fr'; +} diff --git a/svelte.config.js b/svelte.config.js index a05df7e131..94a165d3d3 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -9,6 +9,15 @@ const config = { base: '', }, }, + onwarn(warning, defaultHandler) { + // don't warn on components containing only global styles + if (warning.code === 'vite-plugin-svelte-css-no-scopable-elements') { + return; + } + + // handle all other warnings normally + defaultHandler(warning); + }, preprocess: preprocess(), };