From 752f899f3b24ff0eea7c10ba4ec670fa245d3587 Mon Sep 17 00:00:00 2001 From: roby Date: Thu, 5 Mar 2026 15:53:38 -0700 Subject: [PATCH] Firefly-1924: Add Legend support --- src/firefly/js/drawingLayers/Catalog.js | 3 +- src/firefly/js/drawingLayers/HiPSMOC.js | 7 +- .../js/drawingLayers/hpx/HpxCatalog.js | 3 +- src/firefly/js/ui/QuantityInputField.jsx | 6 + src/firefly/js/ui/ToolbarButton.jsx | 151 +++++++++--------- src/firefly/js/visualize/HiPSMocUtil.js | 7 +- src/firefly/js/visualize/draw/DrawLayer.js | 2 + src/firefly/js/visualize/iv/ImageViewer.jsx | 5 +- .../js/visualize/iv/ImageViewerDecorate.jsx | 14 +- .../js/visualize/saga/CoverageWatcher.js | 12 +- src/firefly/js/visualize/task/PlotHipsTask.js | 4 +- src/firefly/js/visualize/ui/CoveraeViewer.jsx | 2 + .../js/visualize/ui/DrawLayerItemView.jsx | 46 +++++- .../js/visualize/ui/DrawLayerUIComponents.jsx | 22 ++- .../js/visualize/ui/MultiImageViewer.jsx | 58 +++---- .../js/visualize/ui/MultiImageViewerView.jsx | 122 ++++++-------- .../js/visualize/ui/MultiItemViewerView.jsx | 78 ++++----- ...lbarView.jsx => RightSidePvDecoration.jsx} | 36 +++-- src/firefly/js/visualize/ui/SmallLegend.jsx | 113 +++++++++++++ .../js/visualize/ui/TargetHiPSPanel.jsx | 15 +- .../visualize/ui/TargetHipsPanelToolbar.jsx | 20 +-- 21 files changed, 442 insertions(+), 284 deletions(-) rename src/firefly/js/visualize/ui/{VisInlineToolbarView.jsx => RightSidePvDecoration.jsx} (69%) create mode 100644 src/firefly/js/visualize/ui/SmallLegend.jsx diff --git a/src/firefly/js/drawingLayers/Catalog.js b/src/firefly/js/drawingLayers/Catalog.js index 5f3467693a..897f9767b0 100644 --- a/src/firefly/js/drawingLayers/Catalog.js +++ b/src/firefly/js/drawingLayers/Catalog.js @@ -78,7 +78,7 @@ const pointBehavior= (catalogType) => function creator(initPayload, presetDefaults={}) { - const {catalogId, tableData, tableMeta, title, catalogType= CatalogType.POINT, + const {catalogId, tableData, tableMeta, title, shortTitle, catalogType= CatalogType.POINT, selectInfo, columns, tableRequest, highlightedRow, color, angleInRadian=false, tableCanControlColor:inTableCanControlColor, symbol, size, tbl_id, dataTooBigForSelection=false, tableSelection, layersPanelLayoutId, @@ -104,6 +104,7 @@ function creator(initPayload, presetDefaults={}) { const helpText= `Click on ${(catalogType===CatalogType.REGION) ? 'region' : 'point'} to highlight`; const options= { catalogType, + shortTitle, layersPanelLayoutId, hasPerPlotData:false, isPointData: pointBehavior(catalogType), diff --git a/src/firefly/js/drawingLayers/HiPSMOC.js b/src/firefly/js/drawingLayers/HiPSMOC.js index f4cd13e6aa..2758e10ed9 100644 --- a/src/firefly/js/drawingLayers/HiPSMOC.js +++ b/src/firefly/js/drawingLayers/HiPSMOC.js @@ -178,6 +178,7 @@ function creator(initPayload) { dl.requestedStyle= drawingDef.style; + dl.shortTitle= initPayload.shortTitle; dl.mocFitsInfo = mocFitsInfo; dl.mocTable= undefined; dl.rootTitle= dl.title; @@ -282,7 +283,7 @@ function getLayerChanges(drawLayer, action) { return {title: tObj, updateStatusAry}; case DrawLayerCntlr.MODIFY_CUSTOM_FIELD: - const {fillStyle, targetPlotId, mocTable, autoUsesOnlyOutline=false} = action.payload.changes; + const {fillStyle, targetPlotId, mocTable} = action.payload.changes; if (fillStyle && targetPlotId) { @@ -295,7 +296,7 @@ function getLayerChanges(drawLayer, action) { let newStyle; const newMocObj = {...drawLayer.mocObj}; if (requestedStyle===Style.AUTO) { - const {style: s, color:newColor} = getAutoDrawStyle(primePlot(visRoot(),targetPlotId), drawingDef.color, autoUsesOnlyOutline); + const {style: s, color:newColor} = getAutoDrawStyle(primePlot(visRoot(),targetPlotId), drawingDef.color, action.payload.changes.autoUsesOnlyOutline); newDrawingDef.color= newColor; newMocObj.color= newColor; newStyle= s; @@ -317,7 +318,7 @@ function getLayerChanges(drawLayer, action) { const mocCsys= getMetaEntry(mocTable,'COORDSYS')?.trim().toUpperCase().startsWith('G') ? CoordinateSys.GALACTIC : CoordinateSys.EQ_J2000; const mocObj = createMocObj(drawLayer, mocTiles, mocCsys); - return {mocTable, mocObj, mocCsys, title: getTitle(drawLayer, visiblePlotIdAry), autoUsesOnlyOutline}; + return {mocTable, mocObj, mocCsys, title: getTitle(drawLayer, visiblePlotIdAry), autoUsesOnlyOutline:action.payload.changes.autoUsesOnlyOutline}; } break; diff --git a/src/firefly/js/drawingLayers/hpx/HpxCatalog.js b/src/firefly/js/drawingLayers/hpx/HpxCatalog.js index a31b51e1e6..2a755af2c0 100644 --- a/src/firefly/js/drawingLayers/hpx/HpxCatalog.js +++ b/src/firefly/js/drawingLayers/hpx/HpxCatalog.js @@ -55,7 +55,7 @@ export default {factoryDef, TYPE_ID}; // every draw layer must default export wi function creator(initPayload, presetDefaults={}) { const {catalogId, - highlightedRow, color, title, + highlightedRow, color, title, shortTitle, tableCanControlColor= true, symbol, size, tbl_id, layersPanelLayoutId, @@ -83,6 +83,7 @@ function creator(initPayload, presetDefaults={}) { hasPerPlotData: true, isPointData:true, supportSubgroups: Boolean(table.tableMeta && table.tableMeta[SUBGROUP]), + shortTitle, layersPanelLayoutId, }; diff --git a/src/firefly/js/ui/QuantityInputField.jsx b/src/firefly/js/ui/QuantityInputField.jsx index 2981f2958b..26967ec8e5 100644 --- a/src/firefly/js/ui/QuantityInputField.jsx +++ b/src/firefly/js/ui/QuantityInputField.jsx @@ -113,6 +113,7 @@ export function QuantityInputFieldView({min, max, sx, slotProps, inputStyle = {} ? `Valid range: ${formatQuantity(min, unit)} - ${formatQuantity(max, unit)}` : ''; + return ( { initialState: normalizeInitState(props?.initialState, quantityBaseUnit, convertQuantityUnits), }); + if (viewProps.value && !viewProps.displayValue) { + // eslint-disable-next-line react-hooks/immutability + viewProps.displayValue = convertQuantityUnits(viewProps.value, viewProps.quantityBaseUnit, viewProps.unit); + } + const handleOnChange = (ev, quantityInfoUpdate) => { const { value, displayValue, unit=quantityBaseUnit, validator, nullAllowed=false, min, max } = { ...viewProps, ...quantityInfoUpdate }; diff --git a/src/firefly/js/ui/ToolbarButton.jsx b/src/firefly/js/ui/ToolbarButton.jsx index 0ea8b22546..3476b09411 100644 --- a/src/firefly/js/ui/ToolbarButton.jsx +++ b/src/firefly/js/ui/ToolbarButton.jsx @@ -56,91 +56,91 @@ function makeImage(icon,style={},className='') { * @param props.lastTextItem * @param props.ref * @param props.style - a style to apply - * @return {object} - */ +* @return {object} +*/ export function ToolbarButton(props) { - const { - icon,text='',badgeCount=0,badgeAlert=false, enabled=true, visible=true, - imageStyle={}, iconButtonSize, shortcutKey='', color='neutral', variant='plain', - disableHiding, active, sx, CheckboxOnIcon, CheckboxOffIcon, value, - useDropDownIndicator= false, hasCheckBox=false, checkBoxOn=false, pressed=false, - component, slotProps={}, dropPosition={}, dropDownCB, onClick, ref:fRef} = props; - checkProps(props, ToolbarButton); +const { + icon,text='',badgeCount=0,badgeAlert=false, enabled=true, visible=true, + imageStyle={}, iconButtonSize, shortcutKey='', color='neutral', variant='plain', buttonSize='md', + disableHiding, active, sx, CheckboxOnIcon, CheckboxOffIcon, value, + useDropDownIndicator= false, hasCheckBox=false, checkBoxOn=false, pressed=false, + component, slotProps={}, dropPosition={}, dropDownCB, onClick, ref:fRef} = props; +checkProps(props, ToolbarButton); - const tip= props.tip || props.title || ''; - const buttonPressed= pressed || active; - const {current:divElementRef}= useRef({divElement:undefined}); - useImperativeHandle(fRef, () => divElementRef.divElement); - const setupRef = useCallback((c) => divElementRef.divElement= c, [divElementRef]); +const tip= props.tip || props.title || ''; +const buttonPressed= pressed || active; +const {current:divElementRef}= useRef({divElement:undefined}); +useImperativeHandle(fRef, () => divElementRef.divElement); +const setupRef = useCallback((c) => divElementRef.divElement= c, [divElementRef]); - const handleClick= useCallback((ev) => { - onClick?.(divElementRef.divElement,ev); - dropDownCB ? dropDownCB(divElementRef.divElement) : dispatchHideDialog(DROP_DOWN_KEY); - },[onClick,dropDownCB,divElementRef.divElement]); +const handleClick= useCallback((ev) => { + onClick?.(divElementRef.divElement,ev); + dropDownCB ? dropDownCB(divElementRef.divElement) : dispatchHideDialog(DROP_DOWN_KEY); +},[onClick,dropDownCB,divElementRef.divElement]); - useEffect( () => { - const {cnrl,meta,key,hasShortcut}= getShortCutInfo(shortcutKey); - if (!hasShortcut) return; - const listener= (ev) => { - if (cnrl && !ev.ctrlKey) return; - if (meta && !ev.metaKey) return; - ev.key===key && handleClick(); - }; - window.document.addEventListener('keydown', listener); - return () => window.document.removeEventListener('keydown', listener); - }); - if (!visible) return false; - const allowInput= disableHiding?'allow-input':'normal-button-hide'; +useEffect( () => { + const {cnrl,meta,key,hasShortcut}= getShortCutInfo(shortcutKey); + if (!hasShortcut) return; + const listener= (ev) => { + if (cnrl && !ev.ctrlKey) return; + if (meta && !ev.metaKey) return; + ev.key===key && handleClick(); + }; + window.document.addEventListener('keydown', listener); + return () => window.document.removeEventListener('keydown', listener); +}); +if (!visible) return false; +const allowInput= disableHiding?'allow-input':'normal-button-hide'; - const image= makeImage(icon,imageStyle,allowInput); - const iSize= iconButtonSize ? {'--IconButton-size': iconButtonSize} : {}; +const image= makeImage(icon,imageStyle,allowInput); +const iSize= iconButtonSize ? {'--IconButton-size': iconButtonSize} : {}; - // const image= icon ? : undefined; - const useIconButton= icon && !text; - // const dropDownIndicator= useDropDownIndicator ? makeImage(DROP_DOWN_ICON,undefined,allowInput) : undefined; - const dropDownIndicator= useDropDownIndicator ? : undefined; +// const image= icon ? : undefined; +const useIconButton= icon && !text; +// const dropDownIndicator= useDropDownIndicator ? makeImage(DROP_DOWN_ICON,undefined,allowInput) : undefined; +const dropDownIndicator= useDropDownIndicator ? : undefined; - // +// - const tbCheckBoxProps= slotProps.tbCheckBox ?? {}; - const iconButton= slotProps.iconButton ?? {}; +const tbCheckBoxProps= slotProps.tbCheckBox ?? {}; +const iconButton= slotProps.iconButton ?? {}; - const b= ( - - - - {useIconButton ? - ( ( - {minHeight:'unset', minWidth:'unset', p:1/4, backgroundColor:'transparent', - '& svg' : { - color: enabled? - theme.vars.palette[color]?.plainColor : - theme.vars.palette[color]?.softDisabledColor, - }, - opacity: enabled ? '1' : '0.3', - ...makeBorder(active,theme,color), - ...iSize, - ['&[aria-pressed="true"]']: { - ...theme.variants.outlinedActive.neutral, - borderColor: theme.vars.palette.neutral.outlinedHoverBorder, - }, - ...iconButton?.sx - }), +const b= ( + + + + {useIconButton ? + ( ( + {minHeight:'unset', minWidth:'unset', p:1/4, backgroundColor:'transparent', + '& svg' : { + color: enabled? + theme.vars.palette[color]?.plainColor : + theme.vars.palette[color]?.softDisabledColor, + }, + opacity: enabled ? '1' : '0.3', + ...makeBorder(active,theme,color), + ...iSize, + ['&[aria-pressed="true"]']: { + ...theme.variants.outlinedActive.neutral, + borderColor: theme.vars.palette.neutral.outlinedHoverBorder, + }, + ...iconButton?.sx + }), - className:'ff-toolbar-iconbutton ' + allowInput, - value, - component, - variant:'soft', color:'neutral' , - 'aria-pressed': buttonPressed ? 'true' : 'false', - 'aria-label':tip, onClick:handleClick, disabled:!enabled}}> - {image} - ) : - (Boolean(text || icon || shortcutKey || image) &&