diff --git a/src/firefly/html/css/global.css b/src/firefly/html/css/global.css index a7314c9c6..229873115 100644 --- a/src/firefly/html/css/global.css +++ b/src/firefly/html/css/global.css @@ -308,30 +308,6 @@ div.rootStyle img { /*-------------------< loading mask ---------------------*/ -.mask-only { - position: absolute; - width: 100%; - height: 100%; - background-color: var(--joy-palette-background-backdrop); - backdrop-filter: blur(8px); -} - -.loading-mask { - position: absolute; - width: 100%; - height: 100%; -} - -.loading-mask::before { - content: ''; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - background-color: var(--joy-palette-background-backdrop); - backdrop-filter: blur(8px); -} @-webkit-keyframes spin { from { transform: rotate(0deg);} to {transform: rotate(359deg);} @@ -353,23 +329,6 @@ div.rootStyle img { animation-iteration-count: infinite; animation-timing-function: linear; } - -.loading-mask::after { - content: ""; - position: absolute; - border-width: 3px; - border-style: solid; - border-color: transparent rgb(255, 255, 255) rgb(255, 255, 255); - border-radius: 50%; - width: 24px; - height: 24px; - top: calc(50% - 12px); - left: calc(50% - 12px); - animation-name: spin; - animation-duration: 2s; - animation-iteration-count: infinite; - animation-timing-function: linear; -} /*------------------- loading mask >---------------------*/ .lost-connection--small { diff --git a/src/firefly/js/charts/ui/PlotlyWrapper.jsx b/src/firefly/js/charts/ui/PlotlyWrapper.jsx index c94b96b93..d0abcbd2d 100644 --- a/src/firefly/js/charts/ui/PlotlyWrapper.jsx +++ b/src/firefly/js/charts/ui/PlotlyWrapper.jsx @@ -14,6 +14,7 @@ import {logger} from '../../util/Logger.js'; import BrowserInfo from '../../util/BrowserInfo.js'; import Enum from 'enum'; import {showPlotLySaveDialog} from 'firefly/charts/ui/PlotlySaveDialog.jsx'; +import {Skeleton} from '@mui/joy'; const PLOTLY_BASE_ID= 'plotly-plot'; const MASKING_DELAY= 400; @@ -397,7 +398,7 @@ export class PlotlyWrapper extends Component { return (
- {showMask &&
} + {showMask &&
}
); } diff --git a/src/firefly/js/core/ComponentCntlr.js b/src/firefly/js/core/ComponentCntlr.js index bc16f2e2e..337ced0c0 100644 --- a/src/firefly/js/core/ComponentCntlr.js +++ b/src/firefly/js/core/ComponentCntlr.js @@ -3,7 +3,7 @@ */ import {flux} from './ReduxFlux'; -import {get} from 'lodash'; +import {get, isNil} from 'lodash'; import update from 'immutability-helper'; import {REINIT_APP} from './AppDataCntlr.js'; @@ -168,6 +168,14 @@ const hideAllDialogsChange= function(state) { const changeComponentState= function(state, action) { const {componentId, componentState} = action.payload; if (!componentId) {return state;} + + if (isNil(componentState)) { + return update(state, + { + [COMPONENT_KEY]: {[componentId]: {$set: undefined}} + }); + } + if (!state[COMPONENT_KEY][componentId]) { state = update(state, { diff --git a/src/firefly/js/core/background/BackgroundCntlr.js b/src/firefly/js/core/background/BackgroundCntlr.js index c934a87b3..e7b1aef08 100644 --- a/src/firefly/js/core/background/BackgroundCntlr.js +++ b/src/firefly/js/core/background/BackgroundCntlr.js @@ -203,7 +203,7 @@ function bgPackage(action) { } } } else { - showInfoPopup(jobInfo?.error); + jobInfo?.error && showInfoPopup(jobInfo.error); } }; doPackageRequest({dlRequest, searchRequest, selectInfo:selectionInfo, bgKey, onComplete}); diff --git a/src/firefly/js/core/background/BackgroundMonitor.css b/src/firefly/js/core/background/BackgroundMonitor.css deleted file mode 100644 index 1feb99690..000000000 --- a/src/firefly/js/core/background/BackgroundMonitor.css +++ /dev/null @@ -1,45 +0,0 @@ -/* - * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt - */ - - -.JobInfo__items--link { - height: 16px; - margin-left: 3px; -} -.JobInfo__items--link:hover { - cursor: pointer; -} - - -.BgMaskPanel__msg { - text-align: center; - margin: 7px 5px; - font-size: larger; - font-style: italic; -} - - -.BgMaskPanel { - position: relative; - width: 100%; - height: 100%; - box-sizing: border-box; - border: 1px solid rgba(0, 0, 0, 0.3); - display: flex; - justify-content: center; -} - -.BgMaskPanel__content { - margin-top: 95px; - position: relative; - color: white; - display: flex; - flex-direction: column; -} - -.BgMaskPanel__actions { - display: inline-flex; - align-items: center; - margin-top: 3px; -} \ No newline at end of file diff --git a/src/firefly/js/core/background/BackgroundMonitor.jsx b/src/firefly/js/core/background/BackgroundMonitor.jsx index e4a30ad99..4b739f7c5 100644 --- a/src/firefly/js/core/background/BackgroundMonitor.jsx +++ b/src/firefly/js/core/background/BackgroundMonitor.jsx @@ -29,7 +29,6 @@ import CANCEL from 'html/images/stop.gif'; import DOWNLOAED from 'html/images/blue_check-on_10x10.gif'; import FAILED from 'html/images/exclamation16x16.gif'; import INFO from 'html/images/info-icon.png'; -import './BackgroundMonitor.css'; import CompleteButton from 'firefly/ui/CompleteButton'; diff --git a/src/firefly/js/core/background/BgMaskPanel.jsx b/src/firefly/js/core/background/BgMaskPanel.jsx index 7ef2042c4..b815d15d3 100644 --- a/src/firefly/js/core/background/BgMaskPanel.jsx +++ b/src/firefly/js/core/background/BgMaskPanel.jsx @@ -1,15 +1,14 @@ import React, {useEffect} from 'react'; -import PropTypes from 'prop-types'; -import {Button, Stack} from '@mui/joy'; -import shallowequal from 'shallowequal'; +import {bool, string, func, object, element} from 'prop-types'; +import {Button, LinearProgress, Skeleton, Stack, Typography} from '@mui/joy'; import {dispatchJobAdd, dispatchJobCancel} from './BackgroundCntlr.js'; import {dispatchComponentStateChange, getComponentState} from '../ComponentCntlr.js'; -import {useStoreConnector} from '../../ui/SimpleComponent.jsx'; +import {useStoreConnector, Slot} from '../../ui/SimpleComponent.jsx'; import {Logger} from '../../util/Logger.js'; import {showJobInfo} from './JobInfo.jsx'; import {getJobInfo} from './BackgroundUtil.js'; -import INFO from 'html/images/info-icon.png'; +import {InfoButton} from 'firefly/visualize/ui/Buttons.jsx'; const logger = Logger('BgMaskPanel'); @@ -23,16 +22,13 @@ const logger = Logger('BgMaskPanel'); */ -export const BgMaskPanel = React.memo(({componentKey, onMaskComplete, style={}}) => { - - const {inProgress, jobInfo} = useStoreConnector((oldState={}) => { - const {inProgress:oProg, jobInfo:oInfo} = oldState; - const {inProgress=false, jobId} = getComponentState(componentKey); - const jobInfo = getJobInfo(jobId); - if (oProg === inProgress && shallowequal(oInfo, jobInfo)) return oldState; - return {inProgress, jobInfo}; - }); +export const BgMaskPanel = React.memo(({componentKey, onMaskComplete, mask, showError= true, ...props}) => { + const inProgress = useStoreConnector(() => getComponentState(componentKey)?.inProgress || false); + const jobInfo = useStoreConnector(() => { + const {jobId} = getComponentState(componentKey); + return jobId && getJobInfo(jobId); + }); const sendToBg = () => { if (jobInfo) { dispatchComponentStateChange(componentKey, {inProgress:false}); @@ -46,16 +42,12 @@ export const BgMaskPanel = React.memo(({componentKey, onMaskComplete, style={}}) } }; - const showInfo = () => { - jobInfo && showJobInfo(jobInfo.jobId); - }; - const msg = jobInfo?.errorSummary?.message || jobInfo?.jobInfo?.progressDesc || 'Working...'; - const Options = () => ( (inProgress || true) && + const Options = () => ( - - + + ); @@ -69,39 +61,42 @@ export const BgMaskPanel = React.memo(({componentKey, onMaskComplete, style={}}) logger.debug(inProgress ? 'show' : 'hide'); if (inProgress) { return ( -
-
-
-
-
-
{msg}
- -
- -
-
-
+ + + ); - } else if (errorInJob) { + } else if (errorInJob && showError) { return ( -
-
-
-
-
-
{jobInfo.phase}
- -
-
{msg}
-
-
-
+ ); } else return null; }); +function MaskP({msg, jobInfo, children, mask=, ...props}) { + const showInfo = () => { + showJobInfo(jobInfo.jobId); + }; + + return ( + + {mask} + + + {msg} + + + {children} + + + + + ); +} + BgMaskPanel.propTypes = { - componentKey: PropTypes.string.isRequired, // key used to identify this background job - style: PropTypes.object, // used for overriding default styling - onMaskComplete: PropTypes.func + componentKey: string.isRequired, // key used to identify this background job + style: object, // used for overriding default styling + onMaskComplete: func, + showError: bool, + mask: element }; diff --git a/src/firefly/js/core/background/JobInfo.jsx b/src/firefly/js/core/background/JobInfo.jsx index d7713accf..0baa8a44d 100644 --- a/src/firefly/js/core/background/JobInfo.jsx +++ b/src/firefly/js/core/background/JobInfo.jsx @@ -9,12 +9,17 @@ import {dispatchShowDialog} from '../ComponentCntlr.js'; import {HelpIcon} from '../../ui/HelpIcon.jsx'; import {CollapsibleItem, CollapsibleGroup} from 'firefly/ui/panel/CollapsiblePanel.jsx'; import {uwsJobInfo} from 'firefly/rpc/SearchServicesJson.js'; -import {Box, Button, Stack} from '@mui/joy'; +import {Box, Button, Skeleton, Stack} from '@mui/joy'; import {OverflowMarker} from 'firefly/tables/ui/TablePanel.jsx'; -const popupSx = {justifyContent: 'space-between', resize: 'both', overflow: 'auto', - minHeight: 200, minWidth: 450}; +const popupSx = { + justifyContent: 'space-between', + resize: 'both', + overflow: 'auto', + minHeight: 200, minWidth: 450, + width: '45vh', height: '45wh' +}; export function showJobInfo(jobId) { const ID = 'show-job-info'; @@ -36,7 +41,7 @@ export async function showUwsJob({jobUrl, jobId}) { const mask = ( -
+ ); diff --git a/src/firefly/js/tables/TableUtil.js b/src/firefly/js/tables/TableUtil.js index ee84e266b..1ab0d8fe1 100644 --- a/src/firefly/js/tables/TableUtil.js +++ b/src/firefly/js/tables/TableUtil.js @@ -1121,38 +1121,39 @@ export function tableDetailsView(tbl_id, highlightedRow, details_tbl_id) { * @memberof firefly.util.table * @func calcColumnWidths */ -export function getColMaxValues(columns, dataAry, - { - maxAryWidth = Number.MAX_SAFE_INTEGER, - maxColWidth = Number.MAX_SAFE_INTEGER, - useWidth = true, - }={}) { - return columns.map( (cv, idx) => { - - const width = useWidth? cv.prefWidth || cv.width : 0; - if (width) { - return 'O'.repeat(width); // O is a good reference for average width of a character - } +export function getColMaxValues(columns, dataAry, opt) { + return columns.map( (cv, idx) => getColMaxVal(cv, idx, dataAry, opt)); +} + +export function getColMaxVal(col, columnIndex, dataAry, + { + maxAryWidth = Number.MAX_SAFE_INTEGER, + maxColWidth = Number.MAX_SAFE_INTEGER, + useWidth = true, + }={}) { + const width = useWidth? col.prefWidth || col.width : 0; + if (width) { + return 'O'.repeat(width); // O is a good reference for average width of a character + } - let maxVal = ''; + let maxVal = ''; - // the 4 headers - [cv.label || cv.name, cv.units, getTypeLabel(cv), cv.nullString].forEach( (v) => { - if (v?.length > maxVal.length) maxVal = v; - }); + // the 4 headers + [col.label || col.name, col.units, getTypeLabel(col), col.nullString].forEach( (v) => { + if (v?.length > maxVal.length) maxVal = v; + }); - // the data - dataAry.forEach((row) => { - const v = formatValue(columns[idx], row[idx]); - if (v.length > maxVal.length) maxVal = v; - }); + // the data + dataAry.forEach((row) => { + const v = formatValue(col, row[columnIndex]); + if (v.length > maxVal.length) maxVal = v; + }); - // limits - if (cv.arraySize && maxVal.length > maxAryWidth) maxVal = maxVal.substr(0, maxAryWidth); - if (maxVal.length > maxColWidth) maxVal = maxVal.substr(0, maxColWidth); + // limits + if (col.arraySize && maxVal.length > maxAryWidth) maxVal = maxVal.substr(0, maxAryWidth); + if (maxVal.length > maxColWidth) maxVal = maxVal.substr(0, maxColWidth); - return maxVal; - }); + return maxVal; } /** @@ -1442,10 +1443,12 @@ export function hasAuxData(tbl_id) { /** * @param tbl_id ID of the table + * @param tableModel or, the tableModel itself. * @return TBL_STATE of the table. */ -export function getTableState(tbl_id) { - const {error, status, isFetching, totalRows, filters, sqlFilter} = getTblById(tbl_id) || {}; +export function getTableState(tbl_id, tableModel={}) { + const {error, status, isFetching, totalRows, request={}} = getTblById(tbl_id) || tableModel; + const {filters, sqlFilter} = request; if (error) return TBL_STATE.ERROR; if (isFetching) return TBL_STATE.LOADING; @@ -1488,7 +1491,7 @@ export function parseError(error) { const [_, type, cause] = error?.cause.match(/(.+?):(.+)/) || []; return {message, type, cause}; } else { - const [_, error, cause] = message.match(/(.+?):(.+)/) || []; // formatted error messages; 'error:cause' + const [_, error, cause] = message?.match(/(.+?):(.+)/) || []; // formatted error messages; 'error:cause' return {message: error || message, cause}; } } diff --git a/src/firefly/js/tables/ui/AddOrUpdateColumn.jsx b/src/firefly/js/tables/ui/AddOrUpdateColumn.jsx index e8d6f276e..c92d23368 100644 --- a/src/firefly/js/tables/ui/AddOrUpdateColumn.jsx +++ b/src/firefly/js/tables/ui/AddOrUpdateColumn.jsx @@ -4,7 +4,7 @@ import React, {useCallback, useState} from 'react'; import PropTypes from 'prop-types'; -import {Button, Stack, Link, Sheet, Typography, Box} from '@mui/joy'; +import {Button, Stack, Link, Sheet, Typography, Box, Skeleton} from '@mui/joy'; import {delay} from 'lodash'; import {UCDList} from '../../voAnalyzer/VoConst.js'; @@ -12,7 +12,7 @@ import {SqlTableFilter, code} from './FilterEditor.jsx'; import {addOrUpdateColumn, deleteColumn} from '../../rpc/SearchServicesJson.js'; import {getGroupFields, validateFieldGroup, getFieldVal} from '../../fieldGroup/FieldGroupUtils.js'; import {ValidationField} from '../../ui/ValidationField.jsx'; -import {getAllColumns, getColumn, getTblById} from '../TableUtil.js'; +import {getAllColumns, getColumn, getTableUiById, getTblById} from '../TableUtil.js'; import {showPopup, showInfoPopup, showYesNoPopup} from '../../ui/PopupUtil.jsx'; import {HelpIcon} from '../../ui/HelpIcon.jsx'; import {ToolbarButton} from '../../ui/ToolbarButton.jsx'; @@ -68,9 +68,8 @@ export const AddOrUpdateColumn = React.memo(({tbl_ui_id, tbl_id, hidePopup, edit setIsWorking(true); addOrUpdateColumn(request, params).then( () => { hidePopup?.(); - dispatchTableUiUpdate( {tbl_ui_id, columns:[], columnWidths: undefined, scrollLeft:100000}); // reset columns and scroll to the right-most of the table so the added column is visible + reloadTable(tbl_ui_id, request, editColName, params.cname); onChange?.(); - reloadTable(request,editColName, params.cname); }).catch( (err) => { showInfoPopup(parseError(err, params), 'Add Column Failed'); }).finally(() => setIsWorking(false)); @@ -83,9 +82,7 @@ export const AddOrUpdateColumn = React.memo(({tbl_ui_id, tbl_id, hidePopup, edit const {request} = getTblById(tbl_id); deleteColumn(request, editColName).then( () => { hidePopup?.(); - dispatchTableUiUpdate( {tbl_ui_id, columns:[], columnWidths: undefined}); // reset columns and scroll to the right-most of the table so the added column is visible - // dispatchTableUiUpdate() - reloadTable(request,editColName); + reloadTable(tbl_ui_id, request, editColName); onChange?.(); }).catch( (err) => showInfoPopup(parseError(err), 'Delete Column Failed')); } @@ -98,14 +95,14 @@ export const AddOrUpdateColumn = React.memo(({tbl_ui_id, tbl_id, hidePopup, edit const DelBtn = (); return ( - a':{width:12}, label:{width:80}, input:{width:265} }}> - {isWorking &&
} + {isWorking && } c.name === 'ROW_IDX'); + columns.splice(colIdx, 0, {}); + columnWidths.splice(colIdx, 0, -1); + // for now, we always add to the end; we can keep all previous values intact + scrollLeft = 100000; // scroll to the right-most of the table so the added column is visible + } else { + // updating a column + const colIdx = columns.findIndex((c) => c.name === editColName); + if (del) { + columns.splice(colIdx, 1); + columnWidths.splice(colIdx, 1); + } // if editColumn is in sort or filter, clear it. + const {sortInfo, filters, inclCols, sqlFilter} = request; + if (sortInfo) { if (sortInfo.match(/[A-Z],(.+)/)?.[1]?.replaceAll('"','').split(',')?.includes(editColName)) { Reflect.deleteProperty(request,'sortInfo'); @@ -166,6 +183,7 @@ function reloadTable(request, editColName, newColName) { Reflect.deleteProperty(request,'sqlFilter'); } } + // if editColumn is in the 'select' portion, remove it. if (inclCols) { const cols = inclCols.split(','); @@ -174,7 +192,9 @@ function reloadTable(request, editColName, newColName) { } } } + dispatchTableFetch(request); + dispatchTableUiUpdate( {tbl_ui_id, columns, columnWidths, scrollLeft}); // update columns and scroll } export const AddColumnBtn = ({tbl_ui_id, tbl_id}) => ( diff --git a/src/firefly/js/tables/ui/BasicTableView.jsx b/src/firefly/js/tables/ui/BasicTableView.jsx index 9887a99a4..d48602360 100644 --- a/src/firefly/js/tables/ui/BasicTableView.jsx +++ b/src/firefly/js/tables/ui/BasicTableView.jsx @@ -4,22 +4,25 @@ import React, {useCallback, useEffect} from 'react'; import {Box, Typography} from '@mui/joy'; -import PropTypes, {func} from 'prop-types'; +import {arrayOf, array, bool, func, instanceOf, number, object, objectOf, shape, string} from 'prop-types'; import {Column, Table} from 'fixed-data-table-2'; import {wrapResizer} from '../../ui/SizeMeConfig.js'; import {get, set, isEmpty, isUndefined, omitBy, pick} from 'lodash'; -import {calcColumnWidths, getCellValue, getColMaxValues, getColumns, getProprietaryInfo, getTableState, getTableUiById, getTblById, hasRowAccess, isClientTable, tableTextView, TBL_STATE, uniqueTblUiId} from '../TableUtil.js'; +import {getCellValue, getColMaxVal, getColMaxValues, getColumns, getProprietaryInfo, getTableState, getTableUiById, getTblById, hasRowAccess, isClientTable, tableTextView, TBL_STATE, uniqueTblUiId} from '../TableUtil.js'; import {SelectInfo} from '../SelectInfo.js'; import {FilterInfo} from '../FilterInfo.js'; import {SortInfo} from '../SortInfo.js'; -import {CellWrapper, getPxWidth, HeaderCell, headerLevel, headerStyle, makeDefaultRenderer, SelectableCell, SelectableHeader} from './TableRenderer.js'; +import {CellWrapper, getPxWidth, HeaderCell, headerStyle, makeDefaultRenderer, SelectableCell, SelectableHeader} from './TableRenderer.js'; import {useStoreConnector} from '../../ui/SimpleComponent.jsx'; import {dispatchTableUiUpdate, TBL_UI_UPDATE} from '../TablesCntlr.js'; import {Logger} from '../../util/Logger.js'; import 'fixed-data-table-2/dist/fixed-data-table.css'; import './TablePanel.css'; +import {updateSet} from 'firefly/util/WebUtil.js'; +import {TableMask} from 'firefly/ui/panel/MaskPanel.jsx'; +import {TableErrorMsg} from 'firefly/tables/ui/TablePanel.jsx'; const logger = Logger('Tables').tag('BasicTable'); const noDataMsg = 'No Data Found'; @@ -86,8 +89,11 @@ const BasicTableViewInternal = React.memo((props) => { if (isSingleColumnTable(columns) && (!columnWidths || columnWidths[0]!==calcWidth)) { // set 1st (only visible) column's width to table's width minus scrollbar's width (15px) changes.columnWidths = [calcWidth, ...Array(columns.length - 1).fill(0)]; + } else if(!columnWidths) { + changes.columnWidths = columnWidthsInPixel(columns, data); + } else if (columnWidths.some?.((w) => w < 0)) { // at least one column needs width calc + changes.columnWidths = columnWidths.map((w, idx) => w >0 ? w : colWidthInPixel( getColMaxVal(columns[idx], idx,data), columns[idx])); } - else if(!columnWidths) changes.columnWidths = columnWidthsInPixel(columns, data); } if (adjScrollTop !== scrollTop) changes.scrollTop = adjScrollTop; if (adjScrollLeft !== scrollLeft) changes.scrollLeft = adjScrollLeft; @@ -104,11 +110,10 @@ const BasicTableViewInternal = React.memo((props) => { const rowClassNameGetter = highlightedRowHandler || defHighlightedRowHandler(tbl_id, hlRowIdx, startIdx); - const tstate = getTableState(tbl_id); + const tstate = getTableState(tbl_id, {error}); // tableModel is used when tbl_id is not defined. logger.debug(`render.. state:[${tstate}] -- ${tbl_id}`); - - if (tstate === TBL_STATE.ERROR) return
{error}
; - if (tstate === TBL_STATE.LOADING || isEmpty(columnWidths)) return
; + if (tstate === TBL_STATE.ERROR) return ; + if (tstate === TBL_STATE.LOADING || isEmpty(columnWidths)) return ; const content = () => { if (textView) { @@ -160,41 +165,41 @@ const BasicTableViewInternal = React.memo((props) => { BasicTableViewInternal.propTypes = { - tbl_ui_id: PropTypes.string, - columns: PropTypes.arrayOf(PropTypes.object), - data: PropTypes.arrayOf(PropTypes.array), - hlRowIdx: PropTypes.number, - selectInfoCls: PropTypes.instanceOf(SelectInfo), - filterInfo: PropTypes.string, - sortInfo: PropTypes.string, - selectable: PropTypes.bool, - showUnits: PropTypes.bool, - showTypes: PropTypes.bool, - showFilters: PropTypes.bool, - showHeader: PropTypes.bool, - textView: PropTypes.bool, - rowHeight: PropTypes.number, - rowHeightGetter: PropTypes.func, // params: rowData and columnWidths, returns height - showMask: PropTypes.bool, - currentPage: PropTypes.number, - startIdx: PropTypes.number, - error: PropTypes.string, - size: PropTypes.object.isRequired, - highlightedRowHandler: PropTypes.func, - onRowDoubleClick: PropTypes.func, - renderers: PropTypes.objectOf( - PropTypes.shape({ - cellRenderer: PropTypes.func, - headRenderer: PropTypes.func + tbl_ui_id: string, + columns: arrayOf(object), + data: arrayOf(array), + hlRowIdx: number, + selectInfoCls: instanceOf(SelectInfo), + filterInfo: string, + sortInfo: string, + selectable: bool, + showUnits: bool, + showTypes: bool, + showFilters: bool, + showHeader: bool, + textView: bool, + rowHeight: number, + rowHeightGetter: func, // params: rowData and columnWidths, returns height + showMask: bool, + currentPage: number, + startIdx: number, + error: string, + size: object.isRequired, + highlightedRowHandler: func, + onRowDoubleClick: func, + renderers: objectOf( + shape({ + cellRenderer: func, + headRenderer: func }) ), - callbacks: PropTypes.shape({ - onRowHighlight: PropTypes.func, - onRowSelect: PropTypes.func, - onSelectAll: PropTypes.func, - onSort: PropTypes.func, - onFilter: PropTypes.func, - onGotoPage: PropTypes.func + callbacks: shape({ + onRowHighlight: func, + onRowSelect: func, + onSelectAll: func, + onSort: func, + onFilter: func, + onGotoPage: func }) }; @@ -226,11 +231,10 @@ function doScrollEnd(scrollLeft, scrollTop) { } function doColumnResize(newColumnWidth, columnKey) { - const {columnWidths={}, tbl_ui_id} = this; - dispatchTableUiUpdate({ - tbl_ui_id, - columnWidths: Object.assign({}, columnWidths, {[columnKey]: newColumnWidth}) - }); + const {columnWidths=[], tbl_ui_id} = this; + if(columnKey >= 0 && columnKey < columnWidths.length) { + dispatchTableUiUpdate({ tbl_ui_id, columnWidths: updateSet(columnWidths, columnKey, newColumnWidth)}); + } } function doKeyDown(e) { @@ -328,20 +332,21 @@ function correctScrollLeftIfNeeded(totalColWidths, scrollLeft, width, triggeredB return scrollLeft; } -function columnWidthsInPixel(columns, data, minWidth=45) { +function columnWidthsInPixel(columns, data, minWidth) { const maxVals = getColMaxValues(columns, data, {maxColWidth: 100, maxAryWidth: 30}); + return maxVals.map((text, idx) => colWidthInPixel(text, columns[idx]), minWidth); +} - const paddings = 8; - return maxVals.map((text, idx) => { - const header = columns[idx].label || columns[idx].name; - const style = header === text ? headerStyle : {fontSize:12}; - text = text.replace(/[^a-zA-Z0-9]/g, 'O'); // some non-alphanum values can be very narrow. use 'O' in place of them. - const pxNum = getPxWidth({text, ...style}) + paddings; - return pxNum < minWidth ? minWidth : pxNum; - }); +function colWidthInPixel(text, col, minWidth=45, paddings=8) { + const header = col.label || col.name; + const style = header === text ? headerStyle : {fontSize:12}; + text = text.replace(/[^a-zA-Z0-9]/g, 'O'); // some non-alphanum values can be very narrow. use 'O' in place of them. + const pxNum = getPxWidth({text, ...style}) + paddings; + return pxNum < minWidth ? minWidth : pxNum; } + function defHighlightedRowHandler(tbl_id, hlRowIdx, startIdx) { const tableModel = getTblById(tbl_id); diff --git a/src/firefly/js/tables/ui/TablePanel.jsx b/src/firefly/js/tables/ui/TablePanel.jsx index 3dbd51d57..c7e844bc3 100644 --- a/src/firefly/js/tables/ui/TablePanel.jsx +++ b/src/firefly/js/tables/ui/TablePanel.jsx @@ -3,7 +3,7 @@ */ import React, {useEffect} from 'react'; -import {Box, Stack, Typography, Sheet, Divider, ChipDelete, Tooltip, Skeleton, Button} from '@mui/joy'; +import {Box, Stack, Typography, Sheet, ChipDelete, Tooltip, Button} from '@mui/joy'; import PropTypes, {object, shape} from 'prop-types'; import {defer, truncate, get, set} from 'lodash'; import {getAppOptions, getSearchActions} from '../../core/AppDataCntlr.js'; @@ -33,6 +33,7 @@ import {AddColumnBtn} from './AddOrUpdateColumn.jsx'; import WarningIcon from '@mui/icons-material/WarningAmberRounded'; import {PropertySheetAsTable} from 'firefly/tables/ui/PropertySheet'; import {META} from '../TableRequestUtil.js'; +import {TableMask} from 'firefly/ui/panel/MaskPanel.jsx'; const logger = Logger('Tables').tag('TablePanel'); @@ -428,26 +429,28 @@ function NotReady({showTitle, tbl_id, title, removable, backgroundable, error}) dispatchTableFetch(JSON.parse(prevReq)); }; if (error) { - const {message, type, cause} = parseError(error); - return ( - - {message} - { cause && ( - - - Cause: - {type && [{type}]} - - {cause} - - )} - {prevReq && } - - ); + return ; } else { - return ; + return ; } } } - +export function TableErrorMsg({error, prevReq, reloadTable, ...props}) { + const {message, type, cause} = parseError(error); + return ( + + {message} + { cause && ( + + + Cause: + {type && [{type}]} + + {cause} + + )} + {prevReq && } + + ); +} diff --git a/src/firefly/js/ui/DownloadDialog.jsx b/src/firefly/js/ui/DownloadDialog.jsx index 3db66bad8..149f47848 100644 --- a/src/firefly/js/ui/DownloadDialog.jsx +++ b/src/firefly/js/ui/DownloadDialog.jsx @@ -190,18 +190,8 @@ export function DownloadOptionPanel ({groupKey, cutoutSize, help_id, children, s // const showWarnings = hasProprietaryData(getTblById(tbl_id)); // it feature is not working correctly - const maskStyle = { - position: 'absolute', - top:-26, - bottom:-4, - right:-4, - left:-4, - width:undefined, - height:undefined, - backgroundColor: 'rgba(0,0,0,0.2)' - }; - const maskPanel = hideDownloadDialog()}/>; + const maskPanel = (hideDownloadDialog()}/>); const saveAsProps = { initialState: { diff --git a/src/firefly/js/ui/FileUploadDropdown.jsx b/src/firefly/js/ui/FileUploadDropdown.jsx index 7ff50f864..b9961d887 100644 --- a/src/firefly/js/ui/FileUploadDropdown.jsx +++ b/src/firefly/js/ui/FileUploadDropdown.jsx @@ -2,10 +2,9 @@ * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt */ -import {Box} from '@mui/joy'; +import {Box, Skeleton} from '@mui/joy'; import React, {useState} from 'react'; import {FormPanel} from './FormPanel.jsx'; -import {dispatchHideDropDown} from '../core/LayoutCntlr.js'; import {FileUploadViewPanel, resultFail} from '../visualize/ui/FileUploadViewPanel.jsx'; import {getAppOptions} from 'firefly/api/ApiUtil.js'; import DialogRootContainer from 'firefly/ui/DialogRootContainer.jsx'; @@ -15,7 +14,6 @@ import {resultSuccess} from 'firefly/ui/FileUploadProcessor'; import {FieldGroup} from 'firefly/ui/FieldGroup'; import {DATA_LINK_TABLES, IMAGES, MOC_TABLES, REGIONS, SPECTRUM_TABLES, TABLES, UWS} from 'firefly/ui/FileUploadUtil'; -const maskWrapper= { position:'absolute', left:0, top:0, width:'100%', height:'100%' }; const panelKey = 'FileUploadAnalysis'; const defaultAcceptList = [ @@ -40,7 +38,7 @@ export const FileUploadDropdown= ({sx, onCancel, onSubmit=resultSuccess, keepSta const [doMask, changeMasking]= useState(() => false); const helpId = getAppOptions()?.uploadPanelHelpId ?? 'basics.searching'; return ( - + - {doMask &&
} + {doMask && } // zIndex:10 because '.file-drop-zone' raises plane to z-index:10. ); }; diff --git a/src/firefly/js/ui/ThemeSetup.js b/src/firefly/js/ui/ThemeSetup.js index 16f47fdfb..b4e871692 100644 --- a/src/firefly/js/ui/ThemeSetup.js +++ b/src/firefly/js/ui/ThemeSetup.js @@ -83,6 +83,11 @@ export function defaultTheme() { defaultProps: { size:'sm', } + }, + JoySkeleton: { + defaultProps: { + animation: 'wave', + } } } }); diff --git a/src/firefly/js/ui/WorkspaceDropdown.jsx b/src/firefly/js/ui/WorkspaceDropdown.jsx index 5a5b18fa1..319f9af8c 100644 --- a/src/firefly/js/ui/WorkspaceDropdown.jsx +++ b/src/firefly/js/ui/WorkspaceDropdown.jsx @@ -8,6 +8,7 @@ import PropTypes from 'prop-types'; import {FormPanel} from './FormPanel.jsx'; import {dispatchHideDropDown} from '../core/LayoutCntlr.js'; import {WorkspaceViewPanel, resultSuccess, resultFail} from '../visualize/ui/WorkspaceViewPanel.jsx'; +import {Skeleton, Stack} from '@mui/joy'; const dropdownName = 'WorkspaceDropDownCmd'; @@ -36,7 +37,7 @@ export class WorkspaceDropdown extends PureComponent { render() { return ( -
+ - {this.state.doMask &&
} -
+ {this.state.doMask && } +
); } } diff --git a/src/firefly/js/ui/WorkspaceSelectPane.jsx b/src/firefly/js/ui/WorkspaceSelectPane.jsx index eb82509d5..d0ce616f4 100644 --- a/src/firefly/js/ui/WorkspaceSelectPane.jsx +++ b/src/firefly/js/ui/WorkspaceSelectPane.jsx @@ -15,7 +15,7 @@ import {WorkspaceSave} from './WorkspaceViewer.jsx'; import {useStoreConnector} from './SimpleComponent.jsx'; import {dispatchWorkspaceUpdate, getWorkspaceErrorMsg} from '../visualize/WorkspaceCntlr.js'; import {getWorkspaceConfig} from '../visualize/WorkspaceCntlr.js'; -import {Stack, Typography, Box} from '@mui/joy'; +import {Stack, Typography, Box, Skeleton} from '@mui/joy'; export const LOCALFILE = 'isLocal'; export const WORKSPACE = 'isWs'; @@ -85,7 +85,7 @@ function ShowWorkspace({wsSelect}) { const wsList = useStoreConnector(getWorkspaceList); const isUpdating = useStoreConnector(isAccessWorkspace); - const content = isUpdating ? + const content = isUpdating ? : isEmpty(wsList) ? {'Workspace access error: ' + getWorkspaceErrorMsg()} : ; diff --git a/src/firefly/js/ui/WorkspaceViewer.jsx b/src/firefly/js/ui/WorkspaceViewer.jsx index ac46b0849..93557013a 100644 --- a/src/firefly/js/ui/WorkspaceViewer.jsx +++ b/src/firefly/js/ui/WorkspaceViewer.jsx @@ -1,4 +1,4 @@ -import {Button, Stack} from '@mui/joy'; +import {Button, Skeleton, Stack} from '@mui/joy'; import React, {memo} from 'react'; import PropTypes from 'prop-types'; import {get, truncate} from 'lodash'; @@ -459,7 +459,7 @@ function showWorkspaceAsPopup({onComplete, value, fieldKey=workspaceUploadDef.fi files={newList} keepSelect={true} initialState={{value, validator: isWsFolder(false)}}/> - {showMask &&
} + {showMask && } }> diff --git a/src/firefly/js/ui/dynamic/DLGeneratedDropDown.js b/src/firefly/js/ui/dynamic/DLGeneratedDropDown.js index 42afba4f1..adea36206 100644 --- a/src/firefly/js/ui/dynamic/DLGeneratedDropDown.js +++ b/src/firefly/js/ui/dynamic/DLGeneratedDropDown.js @@ -1,4 +1,4 @@ -import {Box, Link, Sheet, Stack, Typography} from '@mui/joy'; +import {Box, Link, Sheet, Skeleton, Stack, Typography} from '@mui/joy'; import {isArray, isEmpty} from 'lodash'; import {bool, func, object, shape, string} from 'prop-types'; import React, {useEffect, useState} from 'react'; @@ -451,7 +451,7 @@ function DLGeneratedTableSearch({currentTblId, qAna, groupKey, initArgs, sideBar const NotLoaded= ({regHasUrl,regLoaded, url}) => ( (regHasUrl || !regLoaded) ? - (
) : + ( ) : ( {`No collections to load from: ${url}`} ) diff --git a/src/firefly/js/ui/panel/MaskPanel.jsx b/src/firefly/js/ui/panel/MaskPanel.jsx new file mode 100644 index 000000000..984ac9d01 --- /dev/null +++ b/src/firefly/js/ui/panel/MaskPanel.jsx @@ -0,0 +1,52 @@ +import React from 'react'; +import {Sheet, Skeleton, Stack} from '@mui/joy'; + +export function TableMask({cols=5, withToolbar=true, ...props}) { + return ( + + {withToolbar && } + + {[...Array(cols).keys()].map((value) => ( + + ))} + + + {[...Array(cols).keys()].map((value) => ( + + ))} + + + ); +} + +export function GridMask({rows=3, cols=3, ...props}) { + return ( + + {[...Array(rows).keys()].map((idx) => ( + + {[...Array(cols).keys()].map((idx) => ( + + ))} + + ))} + + ); +} + +export function FormMask({rows=10, sx, ...props}) { + return ( + + + {[...Array(rows).keys()].map((value) => ( + + ))} + + + {[...Array(rows).keys()].map((value) => ( + + ))} + + + ); +} + diff --git a/src/firefly/js/ui/tap/ObjectIDSearch.jsx b/src/firefly/js/ui/tap/ObjectIDSearch.jsx index d44328cf7..f8b0fe50f 100644 --- a/src/firefly/js/ui/tap/ObjectIDSearch.jsx +++ b/src/firefly/js/ui/tap/ObjectIDSearch.jsx @@ -1,4 +1,4 @@ -import {Box, Chip, Stack, Typography} from '@mui/joy'; +import {Box, Chip, Skeleton, Stack, Typography} from '@mui/joy'; import React, {useContext, useEffect, useState} from 'react'; import {FieldGroupCtx} from 'firefly/ui/FieldGroup'; import {ConstraintContext} from 'firefly/ui/tap/Constraints'; @@ -128,7 +128,9 @@ export function ObjectIDSearch({cols, capabilities, tableName, columnsModel}) { return ( - + + {working && } + Performs an exact match on the ID(s) provided, not a spatial search in the neighborhood of the designated objects. {!canUpload && This search uses "Select IN" style SQL as this service does not support uploads.} } - {working &&
} diff --git a/src/firefly/js/ui/tap/TapViewType.jsx b/src/firefly/js/ui/tap/TapViewType.jsx index 77f57972f..81dbd3f3d 100644 --- a/src/firefly/js/ui/tap/TapViewType.jsx +++ b/src/firefly/js/ui/tap/TapViewType.jsx @@ -1,4 +1,4 @@ -import {Box, Button, Divider, Sheet, Stack, Tooltip, Typography} from '@mui/joy'; +import {Box, Button, Divider, Sheet, Skeleton, Stack, Tooltip, Typography} from '@mui/joy'; import {truncate} from 'lodash'; import {bool, string, func, object, shape} from 'prop-types'; import React, {Fragment, useContext, useEffect, useRef, useState} from 'react'; @@ -25,6 +25,7 @@ import { import MoreVertRoundedIcon from '@mui/icons-material/MoreVertRounded'; import './TableSelectViewPanel.css'; +import {TableMask} from 'firefly/ui/panel/MaskPanel.jsx'; const SCHEMA_TIP= 'Select a table collection (TAP ‘schema’); type to search the schema names and descriptions.'; @@ -111,7 +112,7 @@ function AdqlUI({serviceUrl, serviceLabel, servicesShowing, setServicesShowing,
- :
+ : } ); @@ -399,7 +400,7 @@ function BasicUI(props) { capabilities, obsCoreMetadataModel, sx:{mt:1}, tableName:getTapBrowserState().tableName}}/> - :
+ : } @@ -414,7 +415,7 @@ function BasicUI(props) { columnsModel={columnsModel} /> - :
+ : } diff --git a/src/firefly/js/visualize/iv/ImageViewerStatus.jsx b/src/firefly/js/visualize/iv/ImageViewerStatus.jsx index 89f252ec8..fd831a1f9 100644 --- a/src/firefly/js/visualize/iv/ImageViewerStatus.jsx +++ b/src/firefly/js/visualize/iv/ImageViewerStatus.jsx @@ -1,7 +1,7 @@ /* * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt */ -import {Box, Card, Typography} from '@mui/joy'; +import {Box, Card, Skeleton, Typography} from '@mui/joy'; import React, {useEffect, memo, useState} from 'react'; import PropTypes from 'prop-types'; import {CompleteButton} from '../../ui/CompleteButton.jsx'; @@ -60,7 +60,7 @@ export const ImageViewerStatus= memo( return ( - {working && showing.maskShowing &&
} + {working && showing.maskShowing &&
} { showing.messageShowing && !useMessageAlpha ? statusText : { ...statusText, backgroundColor: ctxBG(theme,65)} }}> diff --git a/src/firefly/js/visualize/ui/CatalogConstraintsPanel.jsx b/src/firefly/js/visualize/ui/CatalogConstraintsPanel.jsx index e3a9b4593..6227369c7 100644 --- a/src/firefly/js/visualize/ui/CatalogConstraintsPanel.jsx +++ b/src/firefly/js/visualize/ui/CatalogConstraintsPanel.jsx @@ -2,7 +2,7 @@ * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt */ -import {Box, Button, Skeleton, Stack, Typography} from '@mui/joy'; +import {Box, Button, Stack, Typography} from '@mui/joy'; import React, {PureComponent, memo} from 'react'; import PropTypes from 'prop-types'; import {isEmpty, merge, isNil, isArray, cloneDeep, has} from 'lodash'; @@ -21,6 +21,7 @@ import {useFieldGroupConnector} from '../../ui/FieldGroupConnector.jsx'; const sqlConstraintsCol = {name: 'constraints', idx: 1, type: 'char', width: 10}; import '../../tables/ui/TablePanel.css'; +import {TableMask} from 'firefly/ui/panel/MaskPanel.jsx'; /* * update short_dd to be one of ['short', 'long', ''] based on if 'showForm' is true or not @@ -128,7 +129,7 @@ export class CatalogConstraintsPanel extends PureComponent { ); }; - if (isEmpty(tableModel) || !tableModel.tbl_id.startsWith(catname)) return ; + if (isEmpty(tableModel) || !tableModel.tbl_id.startsWith(catname)) return ; return ( diff --git a/src/firefly/js/visualize/ui/ColorBandPanel.jsx b/src/firefly/js/visualize/ui/ColorBandPanel.jsx index e3c5ca859..ea628c368 100644 --- a/src/firefly/js/visualize/ui/ColorBandPanel.jsx +++ b/src/firefly/js/visualize/ui/ColorBandPanel.jsx @@ -2,7 +2,7 @@ * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt */ -import {Box, Stack, Typography} from '@mui/joy'; +import {Box, Skeleton, Stack, Typography} from '@mui/joy'; import React, {memo, useState, useEffect, useRef} from 'react'; import PropTypes from 'prop-types'; import {debounce, get} from 'lodash'; @@ -99,6 +99,7 @@ export const ColorBandPanel= memo(({fields,plot,band, groupKey}) => { return ( + {doMask && } {dataHistUrl && setExit(true)} />} @@ -114,7 +115,6 @@ export const ColorBandPanel= memo(({fields,plot,band, groupKey}) => { - {doMask && } ); }); diff --git a/src/firefly/js/visualize/ui/ColorTableDropDownView.jsx b/src/firefly/js/visualize/ui/ColorTableDropDownView.jsx index 5f17831aa..e5f899931 100644 --- a/src/firefly/js/visualize/ui/ColorTableDropDownView.jsx +++ b/src/firefly/js/visualize/ui/ColorTableDropDownView.jsx @@ -23,7 +23,7 @@ import {RangeSliderView} from '../../ui/RangeSliderView.jsx'; import DialogRootContainer from 'firefly/ui/DialogRootContainer.jsx'; import {dispatchHideDialog, dispatchShowDialog} from 'firefly/core/ComponentCntlr.js'; import {DROP_DOWN_KEY} from 'firefly/ui/DropDownToolbarButton.jsx'; -import {Typography, Box, Stack, Divider, IconButton} from '@mui/joy'; +import {Typography, Box, Stack, Divider, IconButton, Skeleton} from '@mui/joy'; // import LockOpenTwoToneIcon from '@mui/icons-material/LockOpenTwoTone'; // import LockTwoToneIcon from '@mui/icons-material/LockTwoTone'; import LockIcon from '@mui/icons-material/Lock'; @@ -100,7 +100,7 @@ const handleColorChange= (plot,cbarId, bias= .5, contrast= 1) => { const makeMask= () => (
-
+
( color: 'white', width: 150, fontSize: '12pt', - textAlign: 'center' + textAlign: 'center', + zIndex: 10 }}> Loading Advanced Options
@@ -261,7 +262,7 @@ const AdvancedColorPanel= ({allowPopout}) => { defaultValue:contrastInt, slideValue:contrastInt, handleChange:(v) => changeBiasContrastColor(colorTableId, bias,v/10)}} /> :
} - {!allLoaded && makeMask() } + { (true || !allLoaded) && makeMask() } ); @@ -310,7 +311,7 @@ const AdvancedColorPanel= ({allowPopout}) => { )) } - {!allLoaded && makeMask() } + {(true || !allLoaded) && makeMask() } ); diff --git a/src/firefly/js/visualize/ui/ImageSearchPanelV2.jsx b/src/firefly/js/visualize/ui/ImageSearchPanelV2.jsx index 7a5dc3b84..abbfb8e2f 100644 --- a/src/firefly/js/visualize/ui/ImageSearchPanelV2.jsx +++ b/src/firefly/js/visualize/ui/ImageSearchPanelV2.jsx @@ -47,6 +47,7 @@ import VisUtil from '../VisUtil'; import {getWorkspaceConfig} from '../WorkspaceCntlr.js'; import {getImageMasterData} from './AllImageSearchConfig.js'; import {FD_KEYS, FG_KEYS} from './UIConst'; +import {GridMask} from 'firefly/ui/panel/MaskPanel.jsx'; var imageMasterData; // latest imageMasterData retrieved from server const scrollDivId = 'ImageSearchScroll'; @@ -248,11 +249,7 @@ function ImageSearchPanelV2 ({archiveName='Search', title='Image Search', multiS
); } else { - return ( - -
- - ); + return (); } } diff --git a/src/firefly/js/visualize/ui/IrsaCatalogSearch.jsx b/src/firefly/js/visualize/ui/IrsaCatalogSearch.jsx index a5e1f28b9..ffd3c96ae 100644 --- a/src/firefly/js/visualize/ui/IrsaCatalogSearch.jsx +++ b/src/firefly/js/visualize/ui/IrsaCatalogSearch.jsx @@ -24,6 +24,7 @@ import {getAppOptions} from '../../core/AppDataCntlr.js'; import {PREF_KEY} from 'firefly/tables/TablePref.js'; import {useStoreConnector} from 'firefly/ui/SimpleComponent.jsx'; import {OptionListField} from 'firefly/ui/OptionListField.jsx'; +import {GridMask} from 'firefly/ui/panel/MaskPanel.jsx'; /** * group key for fieldgroup comp @@ -90,7 +91,7 @@ function ProjectPart() { } }, [valC]); // if catalog changes, select first table of that catalog - if (!(catmaster && valP && valC)) return ; + if (!(catmaster && valP && valC)) return ; const optProjects = getProjectOptions(catmaster); const optCatalogs = getSubProjectOptions(catmaster, valP); @@ -170,7 +171,7 @@ function PositionPart() { const optCatTable = getCatalogOptions(catmaster, valP, valC); const selCatTable = optCatTable?.find((c) => c.value === valT); - if (!(catmaster && valP && valC && selCatTable)) return ; + if (!(catmaster && valP && valC && selCatTable)) return ; const POS_COL = cols.findIndex((c) => c?.name?.toLowerCase() === 'pos'); @@ -197,7 +198,7 @@ function TableConstraint() { const cattableValue = useStoreConnector(() => getFieldVal(irsaCatalogGroupKey, 'cattable')); const ddform = useStoreConnector(() => getFieldVal(irsaCatalogGroupKey, 'ddform', 'true')); - if (!(masterTableInfo && valP && valC)) return ; + if (!(masterTableInfo && valP && valC)) return ; const {catmaster} = masterTableInfo; const catTable = getCatalogOptions(catmaster, valP, valC);