diff --git a/src/firefly/java/edu/caltech/ipac/firefly/data/ServerRequest.java b/src/firefly/java/edu/caltech/ipac/firefly/data/ServerRequest.java index e4d04db86c..53266e2039 100644 --- a/src/firefly/java/edu/caltech/ipac/firefly/data/ServerRequest.java +++ b/src/firefly/java/edu/caltech/ipac/firefly/data/ServerRequest.java @@ -3,6 +3,7 @@ */ package edu.caltech.ipac.firefly.data; +import edu.caltech.ipac.firefly.util.MathUtil; import edu.caltech.ipac.util.StringUtils; import edu.caltech.ipac.visualize.plot.ResolvedWorldPt; import edu.caltech.ipac.visualize.plot.WorldPt; @@ -276,6 +277,10 @@ public double getDoubleParam(String key) { return StringUtils.getDouble(getParam(key)); } + public double getDoubleParam(String key, double def) { + return StringUtils.getDouble(getParam(key), def); + } + public float getFloatParam(String key) { return StringUtils.getFloat(getParam(key)); } @@ -294,6 +299,15 @@ public WorldPt getWorldPtParam(String key) { return wpt; } + public double getSizeParam(String key, MathUtil.Units units) { + double sizeInDeg = getDoubleParam(key); // client sends size in degrees + return MathUtil.convert(MathUtil.Units.DEGREE, units, sizeInDeg); + } + + public double getSizeParam(String key) { + return getSizeParam(key, MathUtil.Units.DEGREE); + } + //==================================================================== // //==================================================================== diff --git a/src/firefly/js/charts/ChartsCntlr.js b/src/firefly/js/charts/ChartsCntlr.js index 43a39a6013..340931a38b 100644 --- a/src/firefly/js/charts/ChartsCntlr.js +++ b/src/firefly/js/charts/ChartsCntlr.js @@ -86,6 +86,7 @@ function reducers() { * @param {string} [p.viewerId] – viewer where chart will be displayed * @param {boolean} [p.deletable] - is the chart deletable, if undefined: single chart in a group is not deletable, multiple are deletable * @param {string} [p.help_id] - help id, if undefined, no help icon shows up + * @param {boolean} [p.activateViewer] - activate the chart viewer (tab) when chart is added * @param {Function} [p.dispatcher=flux.process] - only for special dispatching uses such as remote * @public * @function dispatchChartAdd diff --git a/src/firefly/js/core/LayoutCntlr.js b/src/firefly/js/core/LayoutCntlr.js index a3fee5f642..32dc4a62a6 100644 --- a/src/firefly/js/core/LayoutCntlr.js +++ b/src/firefly/js/core/LayoutCntlr.js @@ -18,7 +18,8 @@ import { TBL_RESULTS_ADDED, TBL_RESULTS_REMOVE, TABLE_REMOVE, TABLE_SPACE_PATH, } from '../tables/TablesCntlr.js'; import {CHART_ADD, CHART_REMOVE, CHART_SPACE_PATH} from '../charts/ChartsCntlr.js'; import { - DEFAULT_FITS_VIEWER_ID, getMultiViewRoot, getViewer, PINNED_CHART_VIEWER_ID, REPLACE_VIEWER_ITEMS + DEFAULT_FITS_VIEWER_ID, + DEFAULT_PLOT2D_VIEWER_ID, getMultiViewRoot, getViewer, PINNED_CHART_VIEWER_ID, REPLACE_VIEWER_ITEMS } from '../visualize/MultiViewCntlr.js'; import {COMMAND, getMenu, REINIT_APP} from './AppDataCntlr.js'; import {getDefaultChartProps} from '../charts/ChartUtil.js'; @@ -66,6 +67,8 @@ export const REMOVE_CELL = `${LAYOUT_PATH}.removeCell`; export const ENABLE_SPECIAL_VIEWER= `${LAYOUT_PATH}.enableSpecialViewer`; +/*------------------ Layout Constants ---------------------------- */ + export const TRIVIEW_ICov_Ch_T= 'TRIVIEW_ICov_Ch_T'; //top left: image/cov, top right: charts, bottom: tables export const TRIVIEW_I_ChCov_T= 'TRIVIEW_I_ChCov_T';//top left: image, top right: charts/cov, bottom: tables export const BIVIEW_ICov_Ch= 'BIVIEW_ICov_Ch'; //left: image/cov, right: charts @@ -73,8 +76,15 @@ export const BIVIEW_I_ChCov= 'BIVIEW_I_ChCov'; //left: image, right: charts/cov export const BIVIEW_T_IChCov= 'BIVIEW_T_IChCov'; //left: tables, right: image/charts/cov export const BIVIEW_IChCov_T= 'BIVIEW_IChCov_T'; //left: image/charts/cov, right: tables - - +/* IDs of all the tabs other than TablesContainer inside the "Results" panel */ +export const TAB_IDS = { + ACTIVE_CHART: 'activeCharts', + PINNED_CHART: 'pinnedCharts', + COVERAGE: 'coverage', + DP: 'meta', + PINNED_IMAGE: 'fits', + PROPERTY_SHEET: 'rowDetails', +}; /*---------------------------- Reducers ----------------------------*/ @@ -435,7 +445,9 @@ export function dropDownHandler(layoutInfo, action) { // calculate dropDown when new UI elements are added or removed from results switch (action.type) { case CHART_ADD: - return smartMerge(layoutInfo, {dropDown: resultsViewDropdown}); + const {viewerId, activateViewer} = action.payload; + const updates = activateViewer ? getChartViewerLayout(viewerId) : {}; + return smartMerge(layoutInfo, {...updates, dropDown: resultsViewDropdown}); case TBL_RESULTS_ADDED: case TBL_RESULTS_ACTIVE: case TABLE_LOADED: @@ -571,3 +583,15 @@ function getColFitIdx(gridView, row, testIdx, gridColumns, testWidth) { export const MENU_TAB_NODES = 'menuTabNodes'; export const getMenuTabNodes = () => getLayouInfo()?.[MENU_TAB_NODES] ?? {}; export const dispatchUpdateMenuTabNodes = (menuTabNodes) => dispatchUpdateLayoutInfo({[MENU_TAB_NODES]: menuTabNodes}); + +// get LayoutInfo based on the viewerId of the chart add action +const getChartViewerLayout = (viewerId) => { + switch (viewerId) { + case DEFAULT_PLOT2D_VIEWER_ID: + return {rightSide: {selectedTab: TAB_IDS.ACTIVE_CHART}}; + case PINNED_CHART_VIEWER_ID: + return {rightSide: {selectedTab: TAB_IDS.PINNED_CHART}}; + default: + return {}; + } +}; \ No newline at end of file diff --git a/src/firefly/js/core/background/BackgroundUtil.js b/src/firefly/js/core/background/BackgroundUtil.js index 539f2efa8c..03381d1881 100644 --- a/src/firefly/js/core/background/BackgroundUtil.js +++ b/src/firefly/js/core/background/BackgroundUtil.js @@ -318,15 +318,20 @@ function getMimeLoader(mimeType, href) { } } +const handleLayoutChanges = (jobInfo) => { + showJobMonitor(false); + const {submitTo} = getMetadata({jobInfo}); + if (submitTo) dispatchFormSubmit({submitTo}); // if this is a routed app, submit the form to update the route +}; + export function loadTableResult({jobInfo, request, href}) { - const {submitTo, tbl_id} = getMetadata({jobInfo}); + const {tbl_id} = getMetadata({jobInfo}); const tblRequest= makeFileRequest(null, href, null, {tbl_id}); copyRequestOptions(request, tblRequest); const {tbl_ui_id} = getTableUiByTblId(tbl_id) || {}; // re-use existing table UI if exists dispatchTableSearch(tblRequest, {tbl_ui_id}); - showJobMonitor(false); - if (submitTo) dispatchFormSubmit({submitTo}); // if this is a routed app, submit the form to update the route + handleLayoutChanges(jobInfo); } export function getMetadata({jobInfo, resultIdx=0}) { @@ -350,5 +355,6 @@ export function loadImageResult({jobInfo, href}) { wpRequest.setPlotGroupId(viewerId); const plotId = `${href.replace('.', '_')}-${jobInfo.jobId}`; dispatchPlotImage({plotId, wpRequest, viewerId}); + handleLayoutChanges(jobInfo); } diff --git a/src/firefly/js/core/background/JobInfo.jsx b/src/firefly/js/core/background/JobInfo.jsx index 0350f9db8b..e74249f375 100644 --- a/src/firefly/js/core/background/JobInfo.jsx +++ b/src/firefly/js/core/background/JobInfo.jsx @@ -121,18 +121,22 @@ export function JobProgress({jobInfo, ...props}) { if (!isActive(jobInfo)) return null; const pct = getJobPctComplete(jobInfo); - const lpProps = pct >= 0 ? {determinate:true, value:pct} : {size:'md'}; + const lpProps = pct >= 0 ? {determinate:true, value:pct} : {}; return ( - - - Progress: - + + + Progress: + + {msg} + + + {elapsed} + - - {elapsed} — - {msg} - - + + ); } @@ -155,7 +159,7 @@ function JobInfoDetails({jobInfo={}}) { - + diff --git a/src/firefly/js/templates/fireflyviewer/TriViewPanel.jsx b/src/firefly/js/templates/fireflyviewer/TriViewPanel.jsx index f2e2506d44..63d6a4da07 100644 --- a/src/firefly/js/templates/fireflyviewer/TriViewPanel.jsx +++ b/src/firefly/js/templates/fireflyviewer/TriViewPanel.jsx @@ -12,11 +12,11 @@ import React, {memo, useContext} from 'react'; import {getExpandedChartProps} from '../../charts/ChartsCntlr.js'; import {allowPinnedCharts} from '../../charts/ChartUtil.js'; import {ActiveChartsPanel} from '../../charts/ui/ChartsContainer.jsx'; -import {dispatchUpdateLayoutInfo, getLayouInfo, getResultCounts, LO_VIEW} from '../../core/LayoutCntlr.js'; +import {dispatchUpdateLayoutInfo, getLayouInfo, getResultCounts, LO_VIEW, TAB_IDS} from '../../core/LayoutCntlr.js'; import {TablesContainer} from '../../tables/ui/TablesContainer.jsx'; import {AppInitLoadingMessage} from '../../ui/AppInitLoadingMessage.jsx'; import {AppPropertiesCtx} from '../../ui/AppPropertiesCtx.jsx'; -import {Tab, Tabs} from '../../ui/panel/TabPanel.jsx'; +import {Tab, TabPanel} from '../../ui/panel/TabPanel.jsx'; import {useStoreConnector} from '../../ui/SimpleComponent.jsx'; import {DEFAULT_PLOT2D_VIEWER_ID, PINNED_CHART_VIEWER_ID} from '../../visualize/MultiViewCntlr.js'; import { @@ -94,21 +94,14 @@ TriViewPanel.propTypes = { let lastSelected; -const ACTIVE_CHART_TAB_ID= 'activeCharts'; -const PINNED_CHART_TAB_ID= 'pinnedCharts'; -const COVERAGE_TAB_ID= 'coverage'; -const DP_TAB_ID='meta'; -const PINNED_IMAGE_TAB_ID='fits'; -const PROPERTY_SHEET_TAB_ID='rowDetails'; - function getSelectedTab(idObj,coverageRight,showXyPlots,showFits,anyTables,anyPinnedCharts) { const tabIds= Object.entries(idObj).filter(([,v]) => v).map(([k]) => k); if (tabIds.includes(lastSelected)) return lastSelected; if (!anyTables) { - if (showFits) return PINNED_IMAGE_TAB_ID; - if (anyPinnedCharts) return PINNED_CHART_TAB_ID; + if (showFits) return TAB_IDS.PINNED_IMAGE; + if (anyPinnedCharts) return TAB_IDS.PINNED_CHART; } - return coverageRight ? COVERAGE_TAB_ID : showXyPlots ? ACTIVE_CHART_TAB_ID : PINNED_CHART_TAB_ID; + return coverageRight ? TAB_IDS.COVERAGE : showXyPlots ? TAB_IDS.ACTIVE_CHART : TAB_IDS.PINNED_CHART; } function makeKey(idObj) { @@ -121,7 +114,7 @@ function makeKey(idObj) { function RightSide({expanded, closeable, showXyPlots, showMeta, showFits, dataProductTableId, coverageRight, imagesWithCharts }) { - + const selectedTab = useStoreConnector(()=>getLayouInfo()?.rightSide?.selectedTab); const onTabSelect = (id) => { lastSelected= id; dispatchUpdateLayoutInfo({rightSide:{selectedTab:id}}); @@ -144,32 +137,32 @@ function RightSide({expanded, closeable, showXyPlots, showMeta, showFits, dataPr const anyTables= Boolean(tableCnt); const anyPinnedCharts= Boolean(pinChartCnt); const idObj= { - [ACTIVE_CHART_TAB_ID]: anyTables && showXyPlots, - [PINNED_CHART_TAB_ID]: allowPinnedCharts() && showPinnedTab, - [COVERAGE_TAB_ID]: anyTables && (imagesWithCharts || coverageRight), - [DP_TAB_ID]: imagesWithCharts && showMeta, - [PINNED_IMAGE_TAB_ID]: imagesWithCharts && showFits, - [PROPERTY_SHEET_TAB_ID]:anyTables, + [TAB_IDS.ACTIVE_CHART]: anyTables && showXyPlots, + [TAB_IDS.PINNED_CHART]: allowPinnedCharts() && showPinnedTab, + [TAB_IDS.COVERAGE]: anyTables && (imagesWithCharts || coverageRight), + [TAB_IDS.DP]: imagesWithCharts && showMeta, + [TAB_IDS.PINNED_IMAGE]: imagesWithCharts && showFits, + [TAB_IDS.PROPERTY_SHEET]:anyTables, }; const style= {height: '100%'}; const defaultSelected= getSelectedTab(idObj,coverageRight,showXyPlots,showFits,anyTables,anyPinnedCharts); - if (idObj[PINNED_IMAGE_TAB_ID] && Object.values(idObj).filter( (v) => v).length===1) { - return makeFitsPinnedTab({id:PINNED_IMAGE_TAB_ID,asTab:false}); + if (idObj[TAB_IDS.PINNED_IMAGE] && Object.values(idObj).filter( (v) => v).length===1) { + return makeFitsPinnedTab({id:TAB_IDS.PINNED_IMAGE,asTab:false}); } return( - - {idObj[ACTIVE_CHART_TAB_ID] && makeActiveChartTab({activeLabel, chartExpandedMode, closeable, asTab:true, id:ACTIVE_CHART_TAB_ID}) } - {idObj[PINNED_CHART_TAB_ID] && makePinnedChartTab({pinnedLabel, chartExpandedMode, closeable, asTab:true, id:PINNED_CHART_TAB_ID}) } - {idObj[COVERAGE_TAB_ID] && makeCoverageTab({id:COVERAGE_TAB_ID})} - {idObj[DP_TAB_ID] && makeMultiProductViewerTab({dataProductTableId,id:DP_TAB_ID})} - {idObj[PINNED_IMAGE_TAB_ID] && makeFitsPinnedTab({id:PINNED_IMAGE_TAB_ID,asTab:true})} - {idObj[PROPERTY_SHEET_TAB_ID] && makePropertySheetTab({id:PROPERTY_SHEET_TAB_ID})} - + + {idObj[TAB_IDS.ACTIVE_CHART] && makeActiveChartTab({activeLabel, chartExpandedMode, closeable, asTab:true, id:TAB_IDS.ACTIVE_CHART}) } + {idObj[TAB_IDS.PINNED_CHART] && makePinnedChartTab({pinnedLabel, chartExpandedMode, closeable, asTab:true, id:TAB_IDS.PINNED_CHART}) } + {idObj[TAB_IDS.COVERAGE] && makeCoverageTab({id:TAB_IDS.COVERAGE})} + {idObj[TAB_IDS.DP] && makeMultiProductViewerTab({dataProductTableId,id:TAB_IDS.DP})} + {idObj[TAB_IDS.PINNED_IMAGE] && makeFitsPinnedTab({id:TAB_IDS.PINNED_IMAGE,asTab:true})} + {idObj[TAB_IDS.PROPERTY_SHEET] && makePropertySheetTab({id:TAB_IDS.PROPERTY_SHEET})} + ); } diff --git a/src/firefly/js/ui/TargetPanel.jsx b/src/firefly/js/ui/TargetPanel.jsx index e9fb7c9fa1..dd12f3ff8c 100644 --- a/src/firefly/js/ui/TargetPanel.jsx +++ b/src/firefly/js/ui/TargetPanel.jsx @@ -29,7 +29,7 @@ const simbadThenNed= 'simbadthenned'; const TargetPanelView = (props) =>{ const {showHelp, feedback, valid, message, onChange, value, button, slotProps, fieldKey, children, resolver, showResolveSourceOp= true, showExample= true, - label= LABEL_DEFAULT, + label= LABEL_DEFAULT, inputFieldLabel, targetPanelExampleRow1, targetPanelExampleRow2, connectedMarker=false, placeholderHighlight=true, examples, onUnmountCB, sx}= props; @@ -41,6 +41,7 @@ const TargetPanelView = (props) =>{ const positionField = ( onChange(ev.target.value, TARGET), endDecorator, @@ -74,6 +75,7 @@ const TargetPanelView = (props) =>{ TargetPanelView.propTypes = { label : string, + inputFieldLabel: string, sx: object, valid : bool.isRequired, showHelp : bool.isRequired, diff --git a/src/firefly/js/ui/dynamic/EmbeddedPositionSearchPanel.jsx b/src/firefly/js/ui/dynamic/EmbeddedPositionSearchPanel.jsx index e8184f497e..97cab9aba6 100644 --- a/src/firefly/js/ui/dynamic/EmbeddedPositionSearchPanel.jsx +++ b/src/firefly/js/ui/dynamic/EmbeddedPositionSearchPanel.jsx @@ -452,14 +452,15 @@ function ConeOp({slotProps,nullAllowed}) { }= slotProps.sizeInput ?? {}; const { targetKey=DEF_TARGET_PANEL_KEY, + inputFieldLabel, targetPanelExampleRow1, - targetPanelExampleRow2 + targetPanelExampleRow2, }= slotProps.targetPanel ?? {}; return ( ); - } + const selectedTab = useStoreConnector(() => getLayouInfo()?.images?.selectedTab); const onTabSelect = (id) => dispatchUpdateLayoutInfo({images:{selectedTab:id}}); const key= (showCoverage&&coverageSide==='LEFT') +'--'+ showFits +'--' + showMeta; + const defaultSelected=getDefSelected(showCoverage,showFits,showMeta,dataProductTableId); + + if (imageExpandedMode) { + return ( ); + } if (showCoverage || showFits || showMeta) { return ( - - { showCoverage && coverageSide==='LEFT' && makeCoverageTab({id:'coverage'}) } - { showMeta && makeMultiProductViewerTab({dataProductTableId,id:'meta'}) } - { showFits && makeFitsPinnedTab({id:'fits',asTab:true}) } - + + { showCoverage && coverageSide==='LEFT' && makeCoverageTab({id:TAB_IDS.COVERAGE}) } + { showMeta && makeMultiProductViewerTab({dataProductTableId,id:TAB_IDS.DP}) } + { showFits && makeFitsPinnedTab({id:TAB_IDS.PINNED_IMAGE,asTab:true}) } + ); } @@ -125,24 +134,24 @@ TriViewImageSection.propTypes= { closeable: PropTypes.bool, dataProductTableId: PropTypes.string, chartMetaId: PropTypes.string, - selectedTab: PropTypes.oneOf(['fits', 'meta', 'coverage']), + selectedTab: PropTypes.oneOf([TAB_IDS.PINNED_IMAGE, TAB_IDS.DP, TAB_IDS.COVERAGE]), coverageSide: PropTypes.string, style: PropTypes.object }; function getDefSelected(showCoverage, showFits, showMeta, tbl_id) { - if (showFits) return 'fits'; + if (showFits) return TAB_IDS.PINNED_IMAGE; if (showMeta) { - if (!showCoverage || isObsCoreLike(tbl_id)) return 'meta'; + if (!showCoverage || isObsCoreLike(tbl_id)) return TAB_IDS.DP; const sdAry= getServiceDescriptors(tbl_id); if (sdAry) { - if (sdAry.some((sd) => isDataLinkServiceDesc(sd) )) return 'meta'; // treat just like an obbscore table - return 'coverage'; // probably a catalog with some secondary data products, a pretty common case + if (sdAry.some((sd) => isDataLinkServiceDesc(sd) )) return TAB_IDS.DP; // treat just like an obbscore table + return TAB_IDS.COVERAGE; // probably a catalog with some secondary data products, a pretty common case } - return 'meta'; // probably not obscore but some other data product table (like an upload of images) + return TAB_IDS.DP; // probably not obscore but some other data product table (like an upload of images) } - if (showCoverage) return 'coverage'; + if (showCoverage) return TAB_IDS.COVERAGE; } export function startImagesLayoutWatcher() { @@ -233,12 +242,12 @@ function handleNewTable(layoutInfo, action) { const showCoverage= hasCoverageTable(tblList)|| hasCoverageData(tbl_id) || isOrbitalPathTable(tbl_id) || isCatalog(tbl_id); if (isMeta || showTables ) { - if (!showFits) selectedTab = 'coverage'; + if (!showFits) selectedTab = TAB_IDS.COVERAGE; showFits= showFits || shouldShowFits(); } if (isMeta && showTables) { showImages = true; - selectedTab = 'meta'; + selectedTab = TAB_IDS.DP; showMeta = true; dataProductTableId = tbl_id; } @@ -306,11 +315,11 @@ function onNewImage(layoutInfo, action) { const {viewerId} = action.payload || {}; if (viewerId === META_VIEWER_ID) { // select meta tab when new images are added to meta image group. - selectedTab = 'meta'; + selectedTab = TAB_IDS.DP; showMeta = true; } else if (viewerId === DEFAULT_FITS_VIEWER_ID) { // select fits tab when new images are added to default group. - selectedTab = 'fits'; + selectedTab = TAB_IDS.PINNED_IMAGE; showFits = true; }