Commit 768dffbe authored by Lauri Himanen's avatar Lauri Himanen
Browse files

Separated list of buttons into the Actions-component that is now reused by...

Separated list of buttons into the Actions-component that is now reused by Plot, Structure and DFTEntryOverview.
parent 46d8eb1d
Pipeline #92705 passed with stages
in 26 minutes and 57 seconds
/*
* Copyright The NOMAD Authors.
*
* This file is part of NOMAD. See https://nomad-lab.eu for further info.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react'
import PropTypes from 'prop-types'
import { Tooltip, IconButton, Button, Box, makeStyles } from '@material-ui/core'
import clsx from 'clsx'
export default function Actions({actions, variant, size, justifyContent, className, classes}) {
const actionsStyles = makeStyles((theme) => ({
root: {
display: 'flex',
width: '100%',
justifyContent: justifyContent
},
iconButton: {
backgroundColor: 'white',
marginRight: theme.spacing(1)
}
}))
const styles = actionsStyles(classes)
const buttonList = actions.map((value, idx) => {
return <Tooltip key={idx} title={value.tooltip}>
{variant === 'icon'
? <IconButton
size={size}
className={styles.iconButton}
onClick={value.onClick}
disabled={value.disabled}
href={value.href}>
{value.content}
</IconButton>
: <Button
variant={variant}
size={size}
className={styles.iconButton}
onClick={value.onClick}
disabled={value.disabled}
href={value.href}>
{value.content}
</Button>
}
</Tooltip>
})
return <Box className={clsx(className, styles.root)}>
{buttonList}
</Box>
}
Actions.propTypes = {
actions: PropTypes.array,
variant: PropTypes.string,
size: PropTypes.string,
justifyContent: PropTypes.string,
className: PropTypes.string,
classes: PropTypes.string
}
Actions.defaultProps = {
size: 'small',
variant: 'icon',
justifyContent: 'flex-end'
}
......@@ -17,14 +17,14 @@
*/
import React, { useContext, useState, useEffect, useMemo } from 'react'
import PropTypes from 'prop-types'
import { Tooltip, Button, Box, Card, CardContent, Grid, Typography, Link, makeStyles, Divider } from '@material-ui/core'
import { Box, Card, CardContent, Grid, Typography, Link, makeStyles, Divider } from '@material-ui/core'
import { ToggleButton, ToggleButtonGroup } from '@material-ui/lab'
import { apiContext } from '../api'
import ElectronicStructureOverview from '../visualization/ElectronicStructureOverview'
import VibrationalOverview from '../visualization/VibrationalOverview'
import { ApiDialog } from '../ApiDialogButton'
import CodeIcon from '@material-ui/icons/Code'
import Structure from '../visualization/Structure'
import Actions from '../Actions'
import Placeholder from '../visualization/Placeholder'
import Quantity from '../Quantity'
import { Link as RouterLink } from 'react-router-dom'
......@@ -33,7 +33,6 @@ import { domains } from '../domains'
import { errorContext } from '../errors'
import { authorList, convertSI, mergeObjects } from '../../utils'
import { resolveRef } from '../archive/metainfo'
import clsx from 'clsx'
import _ from 'lodash'
import {appBase, encyclopediaEnabled, normalizeDisplayValue} from '../../config'
......@@ -327,6 +326,8 @@ export default function DFTEntryOverview({data}) {
const loadingRepo = !data
const quantityProps = {data: data, loading: loadingRepo}
const domain = data.domain && domains[data.domain]
// Create toggle buttons for each structure option
const structureToggles = useMemo(() => {
if (structures) {
const toggles = []
......@@ -345,38 +346,43 @@ export default function DFTEntryOverview({data}) {
}
}
// Figure out which actions are available for this entry
const actions = useMemo(() => {
const buttons = [
{
tooltip: 'Show the API access code',
onClick: (event) => { setShowAPIDialog(!showAPIDialog) },
content: 'API access'
}
]
if (encyclopediaEnabled && data?.encyclopedia?.material?.material_id) {
buttons.push(
{
tooltip: 'View more information about this material',
content: 'Material',
href: `${appBase}/encyclopedia/#/material/${data.encyclopedia.material.material_id}`
}
)
}
return buttons
}, [data, showAPIDialog])
return (
<Grid container spacing={0} className={classes.root}>
<Grid item xs={4} className={classes.sidebar}>
<ApiDialog data={data} open={showAPIDialog} onClose={() => { setShowAPIDialog(false) }}></ApiDialog>
<Actions
className={classes.actions}
size='medium'
buttons={[
{
tooltip: 'Show the API access code',
onClick: (event) => { setShowAPIDialog(!showAPIDialog) },
icon: <CodeIcon/>,
text: 'API access'
},
{
tooltip: 'View more information about this material',
icon: <CodeIcon/>,
text: 'Material',
href: `${appBase}/encyclopedia/#/material/${data.encyclopedia.material.material_id}`
}
]}></Actions>
<Actions className={classes.actions} justifyContent='flex-start' variant='contained' size='medium' actions={actions}></Actions>
<Box className={classes.sidebarContent}>
<Typography variant="body1" className={classes.title}>Method</Typography>
<Quantity flex>
<Quantity quantity="dft.code_name" label='code name' noWrap data={data}/>
<Quantity quantity="dft.code_version" label='code version' noWrap data={data}/>
<Quantity quantity="dft.code_name" label='code name' noWrap {...quantityProps}/>
<Quantity quantity="dft.code_version" label='code version' noWrap {...quantityProps}/>
<Quantity quantity="electronic_structure_method" label='electronic structure method' loading={loading} description="The used electronic structure method." noWrap data={method}/>
<Quantity quantity="dft.xc_functional" label='xc functional family' noWrap data={data}/>
<Quantity quantity="dft.xc_functional_names" label='xc functional names' noWrap data={data}/>
<Quantity quantity="dft.basis_set" label='basis set type' noWrap data={data}/>
<Quantity quantity="dft.xc_functional" label='xc functional family' noWrap {...quantityProps}/>
<Quantity quantity="dft.xc_functional_names" label='xc functional names' noWrap {...quantityProps}/>
<Quantity quantity="dft.basis_set" label='basis set type' noWrap {...quantityProps}/>
<Quantity quantity="basis_set" label='basis set name' noWrap hideIfUnavailable data={method}/>
{method?.van_der_Waals_method && <Quantity quantity="van_der_Waals_method" label='van der Waals method' noWrap data={method}/>}
{method?.van_der_Waals_method && <Quantity quantity="van_der_Waals_method" label='van der Waals method' noWrap {...quantityProps}/>}
{method?.relativity_method && <Quantity quantity="relativity_method" label='relativity method' noWrap data={method}/>}
</Quantity>
</Box>
......@@ -414,23 +420,23 @@ export default function DFTEntryOverview({data}) {
<Box className={classes.sidebarContent}>
<Typography variant="body1" className={classes.title}>Processing information</Typography>
<Quantity column style={{maxWidth: 350}}>
<Quantity quantity="mainfile" noWrap ellipsisFront data={data} withClipboard />
<Quantity quantity="calc_id" label={`${domain ? domain.entryLabel : 'entry'} id`} noWrap withClipboard data={data} />
<Quantity quantity="encyclopedia.material.material_id" label='material id' noWrap data={data} withClipboard />
<Quantity quantity="upload_id" label='upload id' data={data} noWrap withClipboard />
<Quantity quantity="upload_time" label='upload time' noWrap data={data}>
<Quantity quantity="mainfile" noWrap ellipsisFront withClipboard {...quantityProps}/>
<Quantity quantity="calc_id" label={`${domain ? domain.entryLabel : 'entry'} id`} noWrap withClipboard {...quantityProps}/>
<Quantity quantity="encyclopedia.material.material_id" label='material id' noWrap withClipboard {...quantityProps}/>
<Quantity quantity="upload_id" label='upload id' noWrap withClipboard {...quantityProps}/>
<Quantity quantity="upload_time" label='upload time' noWrap {...quantityProps}>
<Typography noWrap>
{new Date(data.upload_time).toLocaleString()}
</Typography>
</Quantity>
<Quantity quantity="raw_id" label='raw id' noWrap hideIfUnavailable data={data} withClipboard />
<Quantity quantity="external_id" label='external id' hideIfUnavailable noWrap data={data} withClipboard />
<Quantity quantity="last_processing" label='last processing' placeholder="not processed" noWrap data={data}>
<Quantity quantity="raw_id" label='raw id' noWrap hideIfUnavailable withClipboard {...quantityProps}/>
<Quantity quantity="external_id" label='external id' hideIfUnavailable noWrap withClipboard {...quantityProps}/>
<Quantity quantity="last_processing" label='last processing' placeholder="not processed" noWrap {...quantityProps}>
<Typography noWrap>
{new Date(data.last_processing).toLocaleString()}
</Typography>
</Quantity>
<Quantity quantity="last_processing" label='processing version' noWrap placeholder="not processed" data={data}>
<Quantity quantity="last_processing" label='processing version' noWrap placeholder="not processed" {...quantityProps}>
<Typography noWrap>
{data.nomad_version}/{data.nomad_commit}
</Typography>
......@@ -439,29 +445,17 @@ export default function DFTEntryOverview({data}) {
</Box>
</Grid>
<Grid item xs={8}>
<PropertyCard
title="Material"
// actions={encyclopediaEnabled && data?.encyclopedia?.material?.material_id &&
// <Tooltip title="Show the material of this entry in the NOMAD Encyclopedia.">
// <IconButton
// size="small"
// href={`${appBase}/encyclopedia/#/material/${data.encyclopedia.material.material_id}`}
// >
// <ArrowForwardIcon/>
// </IconButton>
// </Tooltip>
// }
>
<PropertyCard title="Material">
<Grid container spacing={1}>
<Grid item xs={5}>
<Box marginTop={1}>
<Quantity column>
<Quantity quantity="formula" label='formula' noWrap data={data}/>
<Quantity quantity="dft.system" label='material type' noWrap data={data}/>
<Quantity quantity="encyclopedia.material.material_name" label='material name' noWrap data={data}/>
<Quantity quantity="formula" label='formula' noWrap {...quantityProps}/>
<Quantity quantity="dft.system" label='material type' noWrap {...quantityProps}/>
<Quantity quantity="encyclopedia.material.material_name" label='material name' noWrap {...quantityProps}/>
<Quantity row>
{materialType === 'bulk' && <Quantity quantity="dft.crystal_system" label='crystal system' noWrap data={data}/>}
{materialType === 'bulk' && <Quantity quantity="dft.spacegroup_symbol" label="spacegroup" noWrap data={data}>
{materialType === 'bulk' && <Quantity quantity="dft.crystal_system" label='crystal system' noWrap {...quantityProps}/>}
{materialType === 'bulk' && <Quantity quantity="dft.spacegroup_symbol" label="spacegroup" noWrap {...quantityProps}>
<Typography noWrap>
{normalizeDisplayValue(_.get(data, 'dft.spacegroup_symbol'))} ({normalizeDisplayValue(_.get(data, 'dft.spacegroup'))})
</Typography>
......@@ -522,43 +516,3 @@ export default function DFTEntryOverview({data}) {
DFTEntryOverview.propTypes = {
data: PropTypes.object.isRequired
}
const actionsStyles = makeStyles(theme => ({
root: {
display: 'flex',
width: '100%',
alignItems: 'flex-end'
},
iconButton: {
backgroundColor: 'white',
marginRight: theme.spacing(1)
}
}))
function Actions({buttons, size, alignItems, className, classes}) {
const styles = actionsStyles(classes)
const buttonList = buttons.map((value, idx) => {
return <Tooltip key={idx} title={value.tooltip}>
<Button
variant="contained"
size={size}
className={styles.iconButton}
onClick={value.onClick}
disabled={value.disabled}
href={value.href}>
{value.text}
</Button>
</Tooltip>
})
return <Box className={clsx(className, styles.root)}>
{buttonList}
</Box>
}
Actions.propTypes = {
buttons: PropTypes.object,
size: PropTypes.string,
alignItems: PropTypes.string,
className: PropTypes.string,
classes: PropTypes.string
}
......@@ -21,23 +21,21 @@ import { makeStyles } from '@material-ui/core/styles'
import { cloneDeep } from 'lodash'
import {
IconButton,
Tooltip,
Typography
} from '@material-ui/core'
import {
MoreVert,
Fullscreen,
FullscreenExit,
CameraAlt,
Replay
} from '@material-ui/icons'
import Floatable from './Floatable'
import Actions from '../Actions'
import Plotly from 'plotly.js-cartesian-dist-min'
import clsx from 'clsx'
import { mergeObjects } from '../../utils'
export default function Plot({data, layout, config, menu, floatTitle, capture, aspectRatio, className, classes, onRelayout, onAfterPlot, onRedraw, onRelayouting, onHover, onReset}) {
export default function Plot({data, layout, config, floatTitle, capture, aspectRatio, className, classes, onRelayout, onAfterPlot, onRedraw, onRelayouting, onHover, onReset}) {
// States
const [float, setFloat] = useState(false)
const [captureSettings, setCaptureSettings] = useState()
......@@ -77,30 +75,6 @@ export default function Plot({data, layout, config, menu, floatTitle, capture, a
const style = useStyles(classes)
// Set the final menu
const finalMenu = useMemo(() => {
let defaultMenu = {
reset: {
visible: true,
disabled: false
},
fullscreen: {
visible: true,
disabled: false
},
capture: {
visible: true,
disabled: false
},
dropdown: {
visible: false,
disabled: false,
items: undefined
}
}
return mergeObjects(menu, defaultMenu)
}, [menu])
// Set the final layout
const finalLayout = useMemo(() => {
let defaultLayout = {
......@@ -274,44 +248,19 @@ export default function Plot({data, layout, config, menu, floatTitle, capture, a
Plotly.downloadImage(canvasRef.current, captureSettings)
}, [canvasRef, captureSettings])
// List of actionable buttons for the plot
const actions = [
{tooltip: 'Reset view', onClick: handleReset, content: <Replay/>},
{tooltip: 'Toggle fullscreen', onClick: () => setFloat(!float), content: float ? <FullscreenExit/> : <Fullscreen/>},
{tooltip: 'Capture image', onClick: handleCapture, content: <CameraAlt/>}
]
return (
<Floatable className={clsx(className, style.root)} float={float} onFloat={() => setFloat(!float)} aspectRatio={aspectRatio}>
{float && <Typography variant="h6">{floatTitle}</Typography>}
<div ref={canvasRef} style={{width: '100%', height: '100%'}}></div>
<div className={style.header}>
<div className={style.spacer}></div>
{ finalMenu.reset.visible === true
? <Tooltip title="Reset view">
<IconButton size="small" className={style.iconButton} onClick={handleReset} disabled={finalMenu.reset.disabled}> <Replay />
</IconButton>
</Tooltip>
: ''
}
{ finalMenu.fullscreen.visible === true
? <Tooltip
title="Toggle fullscreen">
<IconButton size="small" className={style.iconButton} onClick={() => setFloat(!float)} disabled={finalMenu.fullscreen.disabled}>
{float ? <FullscreenExit /> : <Fullscreen />}
</IconButton>
</Tooltip>
: ''
}
{ finalMenu.capture.visible === true
? <Tooltip title="Capture image">
<IconButton size="small" className={style.iconButton} onClick={handleCapture} disabled={finalMenu.capture.disabled}>
<CameraAlt />
</IconButton>
</Tooltip>
: ''
}
{ finalMenu.dropdown.visible === true
? <Tooltip title="Options">
<IconButton size="small" className={style.iconButton} onClick={() => {}} disabled={finalMenu.dropdown.disabled}>
<MoreVert />
</IconButton>
</Tooltip>
: ''
}
<Actions actions={actions}></Actions>
</div>
</Floatable>
)
......@@ -321,7 +270,6 @@ Plot.propTypes = {
data: PropTypes.array, // Plotly.js data object
layout: PropTypes.object, // Plotly.js layout object
config: PropTypes.object, // Plotly.js config object
menu: PropTypes.object, // Menu settings
capture: PropTypes.object, // Capture settings
aspectRatio: PropTypes.number, // Fixed aspect ratio for the viewer canvas
className: PropTypes.string,
......
......@@ -23,8 +23,6 @@ import {
Checkbox,
Menu,
MenuItem,
IconButton,
Tooltip,
Typography,
FormControlLabel
} from '@material-ui/core'
......@@ -37,6 +35,7 @@ import {
} from '@material-ui/icons'
import { StructureViewer } from '@lauri-codes/materia'
import Floatable from './Floatable'
import Actions from '../Actions'
import { mergeObjects } from '../../utils'
import { withErrorHandler, ErrorCard } from '../ErrorHandler'
import _ from 'lodash'
......@@ -78,18 +77,11 @@ function Structure({className, classes, system, options, viewer, captureName, as
flexDirection: 'row',
zIndex: 1
},
spacer: {
flex: 1
},
viewerCanvas: {
flex: 1,
zIndex: 0,
minHeight: 0, // added min-height: 0 to allow the item to shrink to fit inside the container.
marginBottom: theme.spacing(2)
},
iconButton: {
backgroundColor: 'white',
marginLeft: theme.spacing(1)
}
}
})
......@@ -265,6 +257,14 @@ function Structure({className, classes, system, options, viewer, captureName, as
refViewer.current.render()
}, [])
// List of actionable buttons for the viewer
const actions = [
{tooltip: 'Reset view', onClick: handleReset, content: <Replay/>},
{tooltip: 'Toggle fullscreen', onClick: toggleFullscreen, content: fullscreen ? <FullscreenExit/> : <Fullscreen/>},
{tooltip: 'Capture image', onClick: takeScreencapture, content: <CameraAlt/>},
{tooltip: 'Options', onClick: openMenu, content: <MoreVert/>}
]
const content = <Box className={style.container}>
{showPrompt
? <ErrorCard
......@@ -277,28 +277,7 @@ function Structure({className, classes, system, options, viewer, captureName, as
{fullscreen && <Typography variant="h6">Structure</Typography>}
<div className={style.viewerCanvas} ref={measuredRef}></div>
<div className={style.header}>
<div className={style.spacer}></div>
<Tooltip title="Reset view">
<IconButton size="small" className={style.iconButton} onClick={handleReset}>
<Replay />
</IconButton>
</Tooltip>
<Tooltip
title="Toggle fullscreen">
<IconButton size="small" className={style.iconButton} onClick={toggleFullscreen}>
{fullscreen ? <FullscreenExit /> : <Fullscreen />}
</IconButton>
</Tooltip>
<Tooltip title="Capture image">
<IconButton size="small" className={style.iconButton} onClick={takeScreencapture}>
<CameraAlt />
</IconButton>
</Tooltip>
<Tooltip title="Options">
<IconButton size="small" className={style.iconButton} onClick={openMenu}>
<MoreVert />
</IconButton>
</Tooltip>
<Actions actions={actions}></Actions>
<Menu
id='settings-menu'
anchorEl={anchorEl}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment