diff --git a/js/components/preview/molecule/moleculeView/moleculeView.js b/js/components/preview/molecule/moleculeView/moleculeView.js index 5796dd8e3..bddc6328d 100644 --- a/js/components/preview/molecule/moleculeView/moleculeView.js +++ b/js/components/preview/molecule/moleculeView/moleculeView.js @@ -667,21 +667,6 @@ const MoleculeView = memo( ); }; - // useEffect(() => { - // if (!proteinData) { - // dispatch(getProteinData(data)).then(i => { - // if (i && i.length > 0) { - // const proteinData = i[0]; - // data.proteinData = proteinData; - // const result = - // data.proteinData && - // (data.proteinData.diff_info || data.proteinData.event_info || data.proteinData.sigmaa_info); - // setHasMap(result); - // } - // }); - // } - // }, [data, dispatch, proteinData]); - // componentDidMount useEffect(() => { dispatch(getMolImage(data.id, MOL_TYPE.HIT, imageWidth, imageHeight)).then(i => { @@ -823,9 +808,6 @@ const MoleculeView = memo( }; const addNewSurface = () => { - // if (selectMoleculeSite) { - // selectMoleculeSite(data.site); - // } dispatch( withDisabledMoleculeNglControlButton(currentID, 'surface', async () => { await dispatch(addSurface(stage, data, colourToggle)); @@ -912,9 +894,6 @@ const MoleculeView = memo( }; const addNewVector = () => { - // if (selectMoleculeSite) { - // selectMoleculeSite(data.site); - // } dispatch( withDisabledMoleculeNglControlButton(currentID, 'vector', async () => { await dispatch(addVector(stage, data)); diff --git a/js/components/preview/molecule/observationCmpList.js b/js/components/preview/molecule/observationCmpList.js new file mode 100644 index 000000000..afe0ceb23 --- /dev/null +++ b/js/components/preview/molecule/observationCmpList.js @@ -0,0 +1,1319 @@ +/** + * Created by abradley on 14/03/2018. + */ +import { + Grid, + Chip, + Tooltip, + makeStyles, + CircularProgress, + Divider, + Typography, + IconButton, + ButtonGroup +} from '@material-ui/core'; +import React, { useState, useEffect, useCallback, memo, useRef, useContext, useMemo } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import MoleculeView from './moleculeView'; +import { colourList } from './utils/color'; +import { MoleculeListSortFilterDialog, filterMolecules, getAttrDefinition } from './moleculeListSortFilterDialog'; +import InfiniteScroll from 'react-infinite-scroller'; +import { Button } from '../../common/Inputs/Button'; +import { Panel } from '../../common/Surfaces/Panel'; +import { ComputeSize } from '../../../utils/computeSize'; +import { moleculeProperty } from './helperConstants'; +import { VIEWS } from '../../../constants/constants'; +import { NglContext } from '../../nglView/nglProvider'; +import classNames from 'classnames'; +import { + addVector, + removeVector, + addHitProtein, + removeHitProtein, + addComplex, + removeComplex, + addSurface, + removeSurface, + addDensity, + removeDensity, + addLigand, + removeLigand, + initializeMolecules, + applyDirectSelection, + addQuality, + removeQuality, + withDisabledMoleculesNglControlButtons, + removeSelectedTypesInHitNavigator, + selectAllHits, + autoHideTagEditorDialogsOnScroll +} from './redux/dispatchActions'; +import { DEFAULT_FILTER, PREDEFINED_FILTERS } from '../../../reducers/selection/constants'; +import { Edit, FilterList } from '@material-ui/icons'; +import { getLHSCompoundsList, selectAllMoleculeList, selectJoinedMoleculeList } from './redux/selectors'; +import { MOL_ATTRIBUTES } from './redux/constants'; +import { + setFilter, + setMolListToEdit, + setNextXMolecules, + setMoleculeForTagEdit +} from '../../../reducers/selection/actions'; +import { initializeFilter } from '../../../reducers/selection/dispatchActions'; +import * as listType from '../../../constants/listTypes'; +import { useRouteMatch } from 'react-router-dom'; +import { setSortDialogOpen, setSearchStringOfHitNavigator } from './redux/actions'; +import { AlertModal } from '../../common/Modal/AlertModal'; +import { + setSelectedAllByType, + setDeselectedAllByType, + setTagEditorOpen, + setIsTagGlobalEdit +} from '../../../reducers/selection/actions'; +import { TagEditor } from '../tags/modal/tagEditor'; +import { getMoleculeForId, selectTag } from '../tags/redux/dispatchActions'; +import SearchField from '../../common/Components/SearchField'; +import useDisableNglControlButtons from './useDisableNglControlButtons'; +import GroupNglControlButtonsContext from './groupNglControlButtonsContext'; +import { extractTargetFromURLParam } from '../utils'; +import { LoadingContext } from '../../loading'; +import { DJANGO_CONTEXT } from '../../../utils/djangoContext'; +import ObservationCmpView from './observationCmpView'; + +const useStyles = makeStyles(theme => ({ + container: { + minHeight: '100px', + height: '100%', + width: 'inherit', + color: theme.palette.black + }, + noOfSelectedHits: { + marginLeft: '5px' + }, + gridItemHeader: { + height: '32px', + fontSize: '8px', + color: '#7B7B7B' + }, + gridItemHeaderVert: { + transform: 'rotate(-90deg)', + height: 'fit-content' + }, + gridItemHeaderHoriz: { + width: 'fit-content' + }, + gridItemList: { + overflow: 'auto', + height: `calc(99% - ${theme.spacing(6)}px - ${theme.spacing(2)}px)` + }, + centered: { + display: 'flex', + alignItems: 'center', + justifyContent: 'center' + }, + button: { + minWidth: 'unset' + }, + buttonActive: { + border: 'solid 1px #009000', + color: '#009000', + '&:hover': { + backgroundColor: '#E3EEDA', + borderColor: '#003f00', + color: '#003f00' + } + }, + paddingProgress: { + padding: theme.spacing(1) + }, + filterSection: { + marginTop: theme.spacing(1), + marginBottom: theme.spacing(1) + }, + filterTitle: { + transform: 'rotate(-90deg)' + }, + molHeader: { + marginLeft: 19, + width: 'inherit' + }, + rightBorder: { + borderRight: '1px solid', + borderRightColor: theme.palette.background.divider, + fontWeight: 'bold', + paddingLeft: theme.spacing(1) / 2, + paddingRight: theme.spacing(1) / 2, + paddingTop: theme.spacing(1), + paddingBottom: theme.spacing(1), + fontSize: 8, + width: 25, + textAlign: 'center', + '&:last-child': { + borderRight: 'none', + width: 32 + } + }, + contButtonsMargin: { + marginTop: theme.spacing(1) / 2, + marginBottom: theme.spacing(1) / 2, + marginLeft: theme.spacing(2) + }, + contColButton: { + minWidth: 'fit-content', + paddingLeft: theme.spacing(1) / 4, + paddingRight: theme.spacing(1) / 4, + paddingBottom: 0, + paddingTop: 0, + fontWeight: 'bold', + fontSize: 9, + borderRadius: 0, + borderColor: theme.palette.primary.main, + backgroundColor: theme.palette.primary.light, + '&:hover': { + backgroundColor: theme.palette.primary.light + // color: theme.palette.primary.contrastText + }, + '&:disabled': { + borderRadius: 0, + borderColor: 'white' + } + }, + contColButtonUnselected: { + minWidth: 'fit-content', + paddingLeft: theme.spacing(1) / 4, + paddingRight: theme.spacing(1) / 4, + paddingBottom: 0, + paddingTop: 0, + fontWeight: 'bold', + fontSize: 9, + borderRadius: 0, + borderColor: theme.palette.primary.main, + backgroundColor: theme.palette.primary.light, + '&:hover': { + backgroundColor: theme.palette.primary.light + // color: theme.palette.primary.contrastText + }, + '&:disabled': { + borderRadius: 0, + borderColor: 'white', + color: 'white' + } + }, + + contColButtonSelected: { + backgroundColor: theme.palette.primary.main, + color: theme.palette.primary.contrastText, + '&:hover': { + backgroundColor: theme.palette.primary.main + // color: theme.palette.black + } + }, + contColButtonHalfSelected: { + backgroundColor: theme.palette.primary.semidark, + color: theme.palette.primary.contrastText, + '&:hover': { + backgroundColor: theme.palette.primary.semidark + //color: theme.palette.black + } + }, + formControl: { + color: 'inherit', + margin: theme.spacing(1), + width: 87 + // fontSize: '1.2rem' + }, + select: { + color: 'inherit', + fill: 'inherit', + '&:hover:not(.Mui-disabled):before': { + borderColor: 'inherit' + }, + '&:before': { + borderColor: 'inherit' + }, + '&:not(.Mui-disabled)': { + fill: theme.palette.white + } + }, + selectIcon: { + fill: 'inherit' + }, + search: { + width: 116 + }, + total: { + ...theme.typography.button, + color: theme.palette.primary.main, + fontStyle: 'italic' + } +})); +let selectedDisplayHits = false; + +export const ObservationCmpList = memo(({ hideProjects }) => { + const classes = useStyles(); + const dispatch = useDispatch(); + let match = useRouteMatch(); + // let target = match && match.params && match.params.target; + let target = match && match.params && extractTargetFromURLParam(match.params[0]); + + const nextXMolecules = useSelector(state => state.selectionReducers.nextXMolecules); + const [selectAllHitsPressed, setSelectAllHitsPressed] = useState(false); + const [selectDisplayedHitsPressed, setSelectDisplayedHitsPressed] = useState(false); + const moleculesPerPage = 30; + const [currentPage, setCurrentPage] = useState(0); + const searchString = useSelector(state => state.previewReducers.molecule.searchStringLHS); + // const [searchString, setSearchString] = useState(null); + const [sortDialogAnchorEl, setSortDialogAnchorEl] = useState(null); + const oldUrl = useRef(''); + const setOldUrl = url => { + oldUrl.current = url; + }; + const list_type = listType.MOLECULE; + const imgHeight = 49; + const imgWidth = 150; + const sortDialogOpen = useSelector(state => state.previewReducers.molecule.sortDialogOpen); + const filter = useSelector(state => state.selectionReducers.filter); + const getJoinedMoleculeList = useSelector(state => selectJoinedMoleculeList(state)); + const allMoleculesList = useSelector(state => selectAllMoleculeList(state)); + + const selectedAll = useRef(false); + + const proteinList = useSelector(state => state.selectionReducers.proteinList); + const complexList = useSelector(state => state.selectionReducers.complexList); + const fragmentDisplayList = useSelector(state => state.selectionReducers.fragmentDisplayList); + const surfaceList = useSelector(state => state.selectionReducers.surfaceList); + const densityList = useSelector(state => state.selectionReducers.densityList); + const densityListCustom = useSelector(state => state.selectionReducers.densityListCustom); + const qualityList = useSelector(state => state.selectionReducers.qualityList); + const vectorOnList = useSelector(state => state.selectionReducers.vectorOnList); + const informationList = useSelector(state => state.selectionReducers.informationList); + const isTagEditorOpen = useSelector(state => state.selectionReducers.tagEditorOpened); + const molForTagEditId = useSelector(state => state.selectionReducers.molForTagEdit); + const moleculesToEditIds = useSelector(state => state.selectionReducers.moleculesToEdit); + const isGlobalEdit = useSelector(state => state.selectionReducers.isGlobalEdit); + + const object_selection = useSelector(state => state.selectionReducers.mol_group_selection); + + const all_mol_lists = useSelector(state => state.apiReducers.all_mol_lists); + const directDisplay = useSelector(state => state.apiReducers.direct_access); + const directAccessProcessed = useSelector(state => state.apiReducers.direct_access_processed); + const tags = useSelector(state => state.apiReducers.tagList); + const noTagsReceived = useSelector(state => state.apiReducers.noTagsReceived); + const categories = useSelector(state => state.apiReducers.categoryList); + + const lhsCompoundsList = useSelector(state => getLHSCompoundsList(state)); + + const proteinsHasLoaded = useSelector(state => state.nglReducers.proteinsHasLoaded); + + const [predefinedFilter, setPredefinedFilter] = useState(filter !== undefined ? filter.predefined : DEFAULT_FILTER); + + const isActiveFilter = !!(filter || {}).active; + + const { getNglView } = useContext(NglContext); + const { moleculesAndTagsAreLoading } = useContext(LoadingContext); + const majorViewStage = getNglView(VIEWS.MAJOR_VIEW) && getNglView(VIEWS.MAJOR_VIEW).stage; + + const filterRef = useRef(); + const tagEditorRef = useRef(); + const scrollBarRef = useRef(); + const [tagEditorAnchorEl, setTagEditorAnchorEl] = useState(null); + + if (directDisplay && directDisplay.target) { + target = directDisplay.target; + } + + let selectedMolecule = []; + // TODO: Reset Infinity scroll + /*useEffect(() => { + // setCurrentPage(0); + }, [object_selection]);*/ + + let filteredLHSCompoundsList = useMemo(() => { + return lhsCompoundsList; + }, [lhsCompoundsList]); + + let compoundMolecules = useMemo(() => { + const compoundMolecules = {}; + + filteredLHSCompoundsList.forEach(compound => { + const molsForCmp = allMoleculesList.filter(molecule => molecule.cmpd === compound.id); + compoundMolecules[compound.id] = molsForCmp.sort((a, b) => { + if (a.code < b.code) { + return -1; + } + if (a.code > b.code) { + return 1; + } + return 0; + }); + }); + + return compoundMolecules; + }, [filteredLHSCompoundsList, allMoleculesList]); + + let joinedMoleculeLists = useMemo(() => { + // const searchedString = currentActionList.find(action => action.type === 'SEARCH_STRING_HIT_NAVIGATOR'); + if (searchString) { + return allMoleculesList.filter(molecule => molecule.code.toLowerCase().includes(searchString.toLowerCase())); + // } else if (searchedString) { + // return getJoinedMoleculeList.filter(molecule => + // molecule.protein_code.toLowerCase().includes(searchedString.searchStringHitNavigator.toLowerCase()) + // ); + } else { + return getJoinedMoleculeList; + } + }, [getJoinedMoleculeList, allMoleculesList, searchString]); + + const addSelectedMoleculesFromUnselectedSites = useCallback( + (joinedMoleculeLists, list) => { + const addedMols = [...joinedMoleculeLists]; + const onlyAlreadySelected = []; + list?.forEach(moleculeID => { + const foundJoinedMolecule = addedMols.find(mol => mol.id === moleculeID); + if (!foundJoinedMolecule) { + const molecule = allMoleculesList.find(mol => mol.id === moleculeID); + if (molecule) { + addedMols.push(molecule); + onlyAlreadySelected.push(molecule); + } + } + }); + + const result = [...onlyAlreadySelected, ...joinedMoleculeLists]; + return result; + }, + [allMoleculesList] + ); + + //the dependencies which are marked by compiler as unnecessary are actually necessary because without them the memo returns + //old joinedMoleculeLists in situation where we want to preserve molecule in view which shouldn't be there + //but want to remove it after the tag editor dialog is closed + // eslint-disable-next-line react-hooks/exhaustive-deps + joinedMoleculeLists = useMemo(() => addSelectedMoleculesFromUnselectedSites(joinedMoleculeLists, proteinList), [ + addSelectedMoleculesFromUnselectedSites, + joinedMoleculeLists, + proteinList, + molForTagEditId, + isTagEditorOpen, + moleculesToEditIds + ]); + // eslint-disable-next-line react-hooks/exhaustive-deps + joinedMoleculeLists = useMemo(() => addSelectedMoleculesFromUnselectedSites(joinedMoleculeLists, complexList), [ + addSelectedMoleculesFromUnselectedSites, + joinedMoleculeLists, + complexList, + molForTagEditId, + isTagEditorOpen, + moleculesToEditIds + ]); + joinedMoleculeLists = useMemo( + () => addSelectedMoleculesFromUnselectedSites(joinedMoleculeLists, fragmentDisplayList), + // eslint-disable-next-line react-hooks/exhaustive-deps + [ + addSelectedMoleculesFromUnselectedSites, + joinedMoleculeLists, + fragmentDisplayList, + molForTagEditId, + isTagEditorOpen, + moleculesToEditIds + ] + ); + // eslint-disable-next-line react-hooks/exhaustive-deps + joinedMoleculeLists = useMemo(() => addSelectedMoleculesFromUnselectedSites(joinedMoleculeLists, surfaceList), [ + addSelectedMoleculesFromUnselectedSites, + joinedMoleculeLists, + surfaceList, + molForTagEditId, + isTagEditorOpen, + moleculesToEditIds + ]); + // eslint-disable-next-line react-hooks/exhaustive-deps + joinedMoleculeLists = useMemo(() => addSelectedMoleculesFromUnselectedSites(joinedMoleculeLists, densityList), [ + addSelectedMoleculesFromUnselectedSites, + joinedMoleculeLists, + densityList, + molForTagEditId, + isTagEditorOpen, + moleculesToEditIds + ]); + // eslint-disable-next-line react-hooks/exhaustive-deps + joinedMoleculeLists = useMemo(() => addSelectedMoleculesFromUnselectedSites(joinedMoleculeLists, vectorOnList), [ + addSelectedMoleculesFromUnselectedSites, + joinedMoleculeLists, + vectorOnList, + molForTagEditId, + isTagEditorOpen, + moleculesToEditIds + ]); + + if (isActiveFilter) { + joinedMoleculeLists = filterMolecules(joinedMoleculeLists, filter); + } + + const loadNextMolecules = () => { + setCurrentPage(currentPage + 1); + }; + + if (molForTagEditId && !joinedMoleculeLists.some(m => m.id === molForTagEditId)) { + const tagEditMol = dispatch(getMoleculeForId(molForTagEditId)); + if (tagEditMol) { + // joinedMoleculeLists = [tagEditMol, ...joinedMoleculeLists]; + joinedMoleculeLists.push(tagEditMol); + joinedMoleculeLists.sort((a, b) => { + if (a.code < b.code) { + return -1; + } + if (a.code > b.code) { + return 1; + } + return 0; + }); + } + } + + if (moleculesToEditIds && moleculesToEditIds.length > 0 && isGlobalEdit) { + moleculesToEditIds.forEach(mid => { + if (!joinedMoleculeLists.some(m => m.id === mid)) { + const tagEditMol = dispatch(getMoleculeForId(mid)); + if (tagEditMol) { + joinedMoleculeLists.push(tagEditMol); + } + } + }); + joinedMoleculeLists.sort((a, b) => { + if (a.code < b.code) { + return -1; + } + if (a.code > b.code) { + return 1; + } + return 0; + }); + } + + const listItemOffset = (currentPage + 1) * moleculesPerPage + nextXMolecules; + const canLoadMore = listItemOffset < joinedMoleculeLists.length; + + const wereMoleculesInitialized = useRef(false); + + useEffect(() => { + if ((proteinsHasLoaded === true || proteinsHasLoaded === null) && all_mol_lists.length > 0) { + if (!directAccessProcessed && directDisplay && directDisplay.molecules && directDisplay.molecules.length > 0) { + dispatch(applyDirectSelection(majorViewStage)); + wereMoleculesInitialized.current = true; + } + if ( + majorViewStage && + all_mol_lists && + hideProjects && + target !== undefined && + wereMoleculesInitialized.current === false && + tags && + tags.length > 0 && + categories && + categories.length > 0 + ) { + dispatch(initializeFilter(object_selection, joinedMoleculeLists)); + dispatch(initializeMolecules(majorViewStage)); + wereMoleculesInitialized.current = true; + } + if ( + majorViewStage && + all_mol_lists && + target !== undefined && + wereMoleculesInitialized.current === false && + noTagsReceived + ) { + dispatch(initializeFilter(object_selection, joinedMoleculeLists)); + dispatch(initializeMolecules(majorViewStage)); + wereMoleculesInitialized.current = true; + } + } + }, [ + list_type, + majorViewStage, + dispatch, + hideProjects, + target, + proteinsHasLoaded, + joinedMoleculeLists, + all_mol_lists, + directDisplay, + directAccessProcessed, + object_selection, + tags, + categories, + noTagsReceived + ]); + + const joinedMoleculeListsCopy = useMemo(() => [...joinedMoleculeLists], [joinedMoleculeLists]); + + // useEffect(() => { + // if (!joinedMoleculeListsCopy.length) { + // dispatch(setSortDialogOpen(false)); + // } + // }, [dispatch, joinedMoleculeListsCopy.length]); + + const handleFilterChange = filter => { + const filterSet = Object.assign({}, filter); + for (let attr of MOL_ATTRIBUTES) { + if (filterSet.filter[attr.key].priority === undefined || filterSet.filter[attr.key].priority === '') { + filterSet.filter[attr.key].priority = 0; + } + } + dispatch(setFilter(filterSet)); + }; + + const allSelectedMolecules = useMemo( + () => + allMoleculesList.filter(molecule => moleculesToEditIds.includes(molecule.id) || molecule.id === molForTagEditId), + [allMoleculesList, moleculesToEditIds, molForTagEditId] + ); + + let currentMolecules = joinedMoleculeLists.slice(0, listItemOffset); + if ( + fragmentDisplayList.length === 0 && + proteinList.length === 0 && + complexList.length === 0 && + surfaceList.length === 0 && + densityList.length === 0 && + vectorOnList.length === 0 + ) { + if (allSelectedMolecules.length === 0) { + selectedDisplayHits = false; + } + } else { + if (allSelectedMolecules.length === 0) { + selectedDisplayHits = false; + } else { + if (allSelectedMolecules.length !== 0) { + for (let i = 0; i < allSelectedMolecules.length; i++) { + const selectedMolecule = allSelectedMolecules[i]; + if ( + fragmentDisplayList.includes(selectedMolecule.id) || + proteinList.includes(selectedMolecule.id) || + complexList.includes(selectedMolecule.id) || + surfaceList.includes(selectedMolecule.id) || + densityList.includes(selectedMolecule.id) || + vectorOnList.includes(selectedMolecule.id) + ) { + selectedDisplayHits = true; + } else { + selectedDisplayHits = false; + break; + } + } + if (selectedDisplayHits) { + const notSelectedMols = []; + const danglingFrags = fragmentDisplayList.filter( + id => !allSelectedMolecules.filter(m => m.id === id).length > 0 + ); + if (danglingFrags && danglingFrags.length > 0) { + notSelectedMols.push(danglingFrags); + } + const danglingProteins = proteinList.filter(id => !allSelectedMolecules.filter(m => m.id === id).length > 0); + if (danglingProteins && danglingProteins.length > 0) { + notSelectedMols.push(danglingProteins); + } + const danglingComplexes = complexList.filter(id => !allSelectedMolecules.filter(m => m.id === id).length > 0); + if (danglingComplexes && danglingComplexes.length > 0) { + notSelectedMols.push(danglingComplexes); + } + const danglingSurfaces = surfaceList.filter(id => !allSelectedMolecules.filter(m => m.id === id).length > 0); + if (danglingSurfaces && danglingSurfaces.length > 0) { + notSelectedMols.push(danglingSurfaces); + } + const danglingDensities = densityList.filter(id => !allSelectedMolecules.filter(m => m.id === id).length > 0); + if (danglingDensities && danglingDensities.length > 0) { + notSelectedMols.push(danglingDensities); + } + const danglingVectors = vectorOnList.filter(id => !allSelectedMolecules.filter(m => m.id === id).length > 0); + if (danglingVectors && danglingVectors.length > 0) { + notSelectedMols.push(danglingVectors); + } + if (notSelectedMols && notSelectedMols.length > 0) { + selectedDisplayHits = false; + } + } + } + } + } + + joinedMoleculeListsCopy.map(data => { + if (fragmentDisplayList.includes(data.id)) { + selectedMolecule.push(data); + } + if (proteinList.includes(data.id)) { + selectedMolecule.push(data); + } + if (complexList.includes(data.id)) { + selectedMolecule.push(data); + } + if (surfaceList.includes(data.id)) { + selectedMolecule.push(data); + } + if (densityList.includes(data.id)) { + selectedMolecule.push(data); + } + if (vectorOnList.includes(data.id)) { + selectedMolecule.push(data); + } + }); + const uniqueSelectedMoleculeForHitNavigator = [...new Set(selectedMolecule)]; + + const newMolsToEdit = []; + currentMolecules.forEach(cm => { + if (moleculesToEditIds.includes(cm.id)) { + newMolsToEdit.push(cm.id); + } + }); + if (newMolsToEdit.length !== moleculesToEditIds.length) { + dispatch(setMolListToEdit(newMolsToEdit)); + } + + const changePredefinedFilter = event => { + let newFilter = Object.assign({}, filter); + + const preFilterKey = event.target.value; + setPredefinedFilter(preFilterKey); + + if (preFilterKey !== 'none') { + newFilter.active = true; + newFilter.predefined = preFilterKey; + Object.keys(PREDEFINED_FILTERS[preFilterKey].filter).forEach(attr => { + const maxValue = PREDEFINED_FILTERS[preFilterKey].filter[attr]; + newFilter.filter[attr].maxValue = maxValue; + newFilter.filter[attr].max = newFilter.filter[attr].max < maxValue ? maxValue : newFilter.filter[attr].max; + }); + dispatch(setFilter(newFilter)); + } else { + // close filter dialog options + setSortDialogAnchorEl(null); + dispatch(setSortDialogOpen(false)); + // reset filter + dispatch(setFilter(undefined)); + newFilter = dispatch(initializeFilter(object_selection, joinedMoleculeLists)); + } + // currently do not filter molecules by excluding them + /*setFilteredCount(getFilteredMoleculesCount(getListedMolecules(object_selection, cached_mol_lists), newFilter)); + handleFilterChange(newFilter);*/ + }; + + const joinedGivenMatch = useCallback( + givenList => { + return givenList.filter(element => allSelectedMolecules.filter(element2 => element2.id === element).length > 0) + .length; + }, + [allSelectedMolecules] + ); + + const joinedLigandMatchLength = useMemo(() => joinedGivenMatch(fragmentDisplayList), [ + fragmentDisplayList, + joinedGivenMatch + ]); + const joinedProteinMatchLength = useMemo(() => joinedGivenMatch(proteinList), [proteinList, joinedGivenMatch]); + const joinedComplexMatchLength = useMemo(() => joinedGivenMatch(complexList), [complexList, joinedGivenMatch]); + + const changeButtonClassname = (givenList = [], matchListLength) => { + if (!matchListLength) { + return false; + } else if (allSelectedMolecules.length === matchListLength) { + return true; + } + return null; + }; + + const isLigandOn = changeButtonClassname(fragmentDisplayList, joinedLigandMatchLength); + const isProteinOn = changeButtonClassname(proteinList, joinedProteinMatchLength); + const isComplexOn = changeButtonClassname(complexList, joinedComplexMatchLength); + + const addType = { + ligand: addLigand, + protein: addHitProtein, + complex: addComplex, + surface: addSurface, + quality: addQuality, + density: addDensity, + vector: addVector + }; + + const removeType = { + ligand: removeLigand, + protein: removeHitProtein, + complex: removeComplex, + surface: removeSurface, + quality: removeQuality, + density: removeDensity, + vector: removeVector + }; + + // TODO: "currentMolecules" do not need to correspondent to selections in {type}List + // TODO: so this could lead to inconsistend behaviour while scrolling + // TODO: maybe change "currentMolecules.forEach" to "{type}List.forEach" + + const removeSelectedType = (type, skipTracking = false) => { + if (type === 'ligand') { + allSelectedMolecules.forEach(molecule => { + dispatch(removeType[type](majorViewStage, molecule, skipTracking)); + }); + } else { + allSelectedMolecules.forEach(molecule => { + dispatch(removeType[type](majorViewStage, molecule, colourList[molecule.id % colourList.length], skipTracking)); + }); + } + + selectedAll.current = false; + }; + + const removeSelectedTypes = useCallback( + (skipMolecules = [], skipTracking = false) => { + dispatch(removeSelectedTypesInHitNavigator(skipMolecules, majorViewStage, skipTracking)); + }, + [dispatch, majorViewStage] + ); + + const selectMoleculeTags = moleculeTagsSet => { + const moleculeTags = tags.filter(tag => moleculeTagsSet.includes(tag.id)); + moleculeTags.forEach(tag => { + dispatch(selectTag(tag)); + }); + }; + + const addNewType = (type, skipTracking = false) => { + dispatch( + withDisabledMoleculesNglControlButtons( + allSelectedMolecules.map(molecule => molecule.id), + type, + async () => { + const promises = []; + + if (type === 'ligand') { + allSelectedMolecules.forEach(molecule => { + //selectMoleculeTags(molecule.tags_set); + + promises.push( + dispatch( + addType[type]( + majorViewStage, + molecule, + colourList[molecule.id % colourList.length], + false, + true, + skipTracking + ) + ) + ); + }); + } else { + allSelectedMolecules.forEach(molecule => { + //selectMoleculeTags(molecule.tags_set); + promises.push( + dispatch( + addType[type](majorViewStage, molecule, colourList[molecule.id % colourList.length], skipTracking) + ) + ); + }); + } + + await Promise.all(promises); + } + ) + ); + }; + + const ucfirst = string => { + return string.charAt(0).toUpperCase() + string.slice(1); + }; + + const onButtonToggle = (type, calledFromSelectAll = false) => { + setLastProcessedLPCType(type); + if (calledFromSelectAll === true && selectedAll.current === true) { + // REDO + if (eval('is' + ucfirst(type) + 'On') === false) { + addNewType(type, true); + } + } else if (calledFromSelectAll && selectedAll.current === false) { + removeSelectedType(type, true); + } else if (!calledFromSelectAll) { + if (eval('is' + ucfirst(type) + 'On') === false) { + let molecules = getSelectedMoleculesByType(type, true); + if (molecules && molecules.length > 100) { + setIsOpenLPCAlert(true); + } else { + dispatch(setSelectedAllByType(type, molecules)); + addNewType(type, true); + } + } else { + let molecules = getSelectedMoleculesByType(type, false); + dispatch(setDeselectedAllByType(type, molecules)); + removeSelectedType(type, true); + } + } + }; + + const getSelectedMoleculesByType = (type, isAdd) => { + switch (type) { + case 'ligand': + return isAdd ? getMoleculesToSelect(fragmentDisplayList) : getMoleculesToDeselect(fragmentDisplayList); + case 'protein': + return isAdd ? getMoleculesToSelect(proteinList) : getMoleculesToDeselect(proteinList); + case 'complex': + return isAdd ? getMoleculesToSelect(complexList) : getMoleculesToDeselect(complexList); + default: + return null; + } + }; + + const getMoleculesToSelect = list => { + let molecules = allSelectedMolecules.filter(m => !list.includes(m.id)); + return molecules; + }; + + const getMoleculesToDeselect = list => { + let molecules = allSelectedMolecules.filter(m => list.includes(m.id)); + return molecules; + }; + + const actions = [ + { + // setSearchString(value); + dispatch(setSearchStringOfHitNavigator(value)); + }} + disabled={false || (getJoinedMoleculeList && getJoinedMoleculeList.length === 0)} + // searchString={filterSearchString?.searchStringHitNavigator ?? ''} + searchString={searchString ?? ''} + placeholder="Search" + />, + + { + if (isTagEditorOpen === false) { + setTagEditorAnchorEl(event.currentTarget); + dispatch(setIsTagGlobalEdit(true)); + dispatch(setTagEditorOpen(true)); + } else { + setTagEditorAnchorEl(null); + dispatch(setIsTagGlobalEdit(false)); + dispatch(setTagEditorOpen(false)); + } + }} + > + + + + , + { + if (sortDialogOpen === false) { + setSortDialogAnchorEl(event.currentTarget); + dispatch(setSortDialogOpen(true)); + } else { + setSortDialogAnchorEl(null); + dispatch(setSortDialogOpen(false)); + } + }} + color={'inherit'} + disabled={/*!joinedMoleculeListsCopy.length || */ predefinedFilter !== 'none'} + > + + + + + ]; + + const [isOpenAlert, setIsOpenAlert] = useState(false); + const [isOpenLPCAlert, setIsOpenLPCAlert] = useState(false); + const [lastProcessedLPCType, setLastProcessedLPCType] = useState(null); + + const groupNglControlButtonsDisabledState = useDisableNglControlButtons(allSelectedMolecules); + + const anyControlButtonDisabled = Object.values(groupNglControlButtonsDisabledState).some(buttonState => buttonState); + + const containsAtLeastOne = (list, molsList) => { + for (const mol in molsList) { + if (list.includes(mol.id)) { + return true; + } + } + + return false; + }; + + return ( + + { + dispatch(setNextXMolecules(joinedMoleculeLists?.length || 0)); + setIsOpenAlert(false); + }} + handleOnCancel={() => { + setIsOpenAlert(false); + }} + /> + { + let molecules = getSelectedMoleculesByType(lastProcessedLPCType, true); + dispatch(setSelectedAllByType(lastProcessedLPCType, molecules)); + addNewType(lastProcessedLPCType, true); + setIsOpenLPCAlert(false); + }} + handleOnCancel={() => { + setIsOpenLPCAlert(false); + }} + /> + {isTagEditorOpen && ( + + )} + {sortDialogOpen && ( + + )} +
+ {isActiveFilter && ( + <> +
+ + + + Filters + + + + + {filter.priorityOrder.map(attr => ( + + + + + + ))} + + + +
+ + + )} +
+ + + + {/* Tooltip should not have disabled element as a direct child */} + <> + + + + + {/* Tooltip should not have disabled element as a direct child */} + <> + + + + + {/* Tooltip should not have disabled element as a direct child */} + <> + {/* C stands for contacts now */} + + + + + + { + + + + + + } + {selectedDisplayHits === true ? ( + + + + + + ) : ( + + + + + + )} + + {`Selected: ${ + allSelectedMolecules ? allSelectedMolecules.length : 0 + }`} + + + + + {/* Header */} + + + {Object.keys(moleculeProperty).map(key => ( + + {moleculeProperty[key]} + + ))} + + + + {console.log('tagEditorRef', tagEditorRef)} + {currentMolecules.length > 0 && ( + <> + + + dispatch( + autoHideTagEditorDialogsOnScroll({ + tagEditorRef, + scrollBarRef + }) + ) + } + pageStart={0} + loadMore={loadNextMolecules} + hasMore={canLoadMore} + loader={ +
+ + + +
+ } + useWindow={false} + > + {/* + {currentMolecules.map((data, index, array) => { + const selected = allSelectedMolecules.some(molecule => molecule.id === data.id); + const isTagEditorInvokedByMolecule = data.id === molForTagEditId; + + return ( + 0 && array[index - 1]} + nextItemData={index < array?.length && array[index + 1]} + setRef={setTagEditorAnchorEl} + removeSelectedTypes={removeSelectedTypes} + L={fragmentDisplayList.includes(data.id)} + P={proteinList.includes(data.id)} + C={complexList.includes(data.id)} + S={surfaceList.includes(data.id)} + D={densityList.includes(data.id)} + D_C={densityListCustom.includes(data.id)} + Q={qualityList.includes(data.id)} + V={vectorOnList.includes(data.id)} + I={informationList.includes(data.id)} + eventInfo={data?.proteinData?.event_info || null} + sigmaaInfo={data?.proteinData?.sigmaa_info || null} + diffInfo={data?.proteinData?.diff_info || null} + isTagEditorInvokedByMolecule={isTagEditorInvokedByMolecule} + isTagEditorOpen={isTagEditorInvokedByMolecule && isTagEditorOpen} + selected={selected} + disableL={selected && groupNglControlButtonsDisabledState.ligand} + disableP={selected && groupNglControlButtonsDisabledState.protein} + disableC={selected && groupNglControlButtonsDisabledState.complex} + /> + ); + })} + */} + {filteredLHSCompoundsList.map((data, index, array) => { + const molsForCmp = compoundMolecules[data.id]; + + return ( + + ); + })} +
+
+ + + + {`Total ${joinedMoleculeLists?.length}`} + + + + + + + + + + + + )} + {moleculesAndTagsAreLoading && ( + + + + + + )} +
+
+ ); +}); diff --git a/js/components/preview/molecule/observationCmpView/index.js b/js/components/preview/molecule/observationCmpView/index.js new file mode 100644 index 000000000..fa26e46a5 --- /dev/null +++ b/js/components/preview/molecule/observationCmpView/index.js @@ -0,0 +1 @@ +export { default } from './observationCmpView'; diff --git a/js/components/preview/molecule/observationCmpView/observationCmpView.js b/js/components/preview/molecule/observationCmpView/observationCmpView.js new file mode 100644 index 000000000..a58bce78a --- /dev/null +++ b/js/components/preview/molecule/observationCmpView/observationCmpView.js @@ -0,0 +1,1252 @@ +/** + * Created by abradley on 14/03/2018. + */ + +import React, { memo, useEffect, useState, useRef, useContext, useCallback } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { Button, Grid, makeStyles, Tooltip, IconButton, Popper, Item, CircularProgress } from '@material-ui/core'; +import { Panel } from '../../../common'; +import { MyLocation, Warning, Assignment, AssignmentTurnedIn } from '@material-ui/icons'; +import SVGInline from 'react-svg-inline'; +import classNames from 'classnames'; +import { VIEWS } from '../../../../constants/constants'; +import { NGL_PARAMS, COMMON_PARAMS } from '../../../nglView/constants'; +import { NglContext } from '../../../nglView/nglProvider'; +import { + addVector, + removeVector, + addHitProtein, + removeHitProtein, + addComplex, + removeComplex, + addSurface, + removeSurface, + addDensity, + addDensityCustomView, + removeDensity, + addLigand, + removeLigand, + getMolImage, + removeQuality, + addQuality, + getQualityInformation, + getDensityMapData, + getProteinData, + withDisabledMoleculeNglControlButton, + getCategoryById +} from '../redux/dispatchActions'; +import { + setSelectedAll, + setDeselectedAll, + setMoleculeForTagEdit, + setTagEditorOpen, + appendToMolListToEdit, + removeFromMolListToEdit +} from '../../../../reducers/selection/actions'; +import { moleculeProperty } from '../helperConstants'; +import { centerOnLigandByMoleculeID } from '../../../../reducers/ngl/dispatchActions'; +import { SvgTooltip } from '../../../common'; +import { MOL_TYPE } from '../redux/constants'; +import { DensityMapsModal } from '../modals/densityMapsModal'; +import { getRandomColor } from '../utils/color'; +import { DEFAULT_TAG_COLOR, getAllTagsForMol } from '../../tags/utils/tagUtils'; +import MoleculeSelectCheckbox from './moleculeSelectCheckbox'; +import useClipboard from 'react-use-clipboard'; +import Popover from '@mui/material/Popover'; +import Typography from '@mui/material/Typography'; +import { Edit } from '@material-ui/icons'; +import { DJANGO_CONTEXT } from '../../../../utils/djangoContext'; +import { getFontColorByBackgroundColor } from '../../../../utils/colors'; + +const useStyles = makeStyles(theme => ({ + container: { + padding: theme.spacing(1) / 4, + color: 'black', + height: 54 + }, + contButtonsMargin: { + margin: theme.spacing(1) / 2, + width: 'inherit', + marginTop: 0 + }, + contColButton: { + minWidth: 'fit-content', + paddingLeft: theme.spacing(1) / 4, + paddingRight: theme.spacing(1) / 4, + paddingBottom: 0, + paddingTop: 0, + fontWeight: 'bold', + fontSize: 9, + borderRadius: 0, + borderColor: theme.palette.primary.main, + backgroundColor: theme.palette.primary.light, + '&:hover': { + backgroundColor: theme.palette.primary.light + // color: theme.palette.primary.contrastText + }, + '&:disabled': { + borderRadius: 0, + borderColor: 'white' + } + }, + contColButtonSelected: { + backgroundColor: theme.palette.primary.main, + color: theme.palette.primary.contrastText, + '&:hover': { + backgroundColor: theme.palette.primary.main + // color: theme.palette.black + } + }, + contColButtonHalfSelected: { + backgroundColor: theme.palette.primary.semidark, + color: theme.palette.primary.contrastText, + '&:hover': { + backgroundColor: theme.palette.primary.semidark + // color: theme.palette.black + } + }, + detailsCol: { + border: 'solid 1px', + borderColor: theme.palette.background.divider, + borderStyle: 'solid none solid solid', + width: 'inherit' + }, + image: { + border: 'solid 1px', + borderColor: theme.palette.background.divider, + borderStyle: 'solid solid solid none', + position: 'relative' + }, + imageMargin: { + marginTop: theme.spacing(1), + marginBottom: theme.spacing(1) + }, + rightBorder: { + borderRight: '1px solid', + borderRightColor: theme.palette.background.divider, + fontWeight: 'bold', + fontSize: 11, + paddingLeft: theme.spacing(1) / 2, + paddingRight: theme.spacing(1) / 2, + paddingBottom: theme.spacing(1) / 4, + width: 25, + textAlign: 'center', + '&:last-child': { + borderRight: 'none', + width: 32 + } + }, + fullHeight: { + height: '100%' + }, + site: { + width: theme.spacing(3), + textAlign: 'center', + backgroundColor: theme.palette.background.default, + border: `solid 1px`, + borderColor: theme.palette.background.divider, + paddingBottom: theme.spacing(1) / 2 + }, + qualityLabel: { + paddingLeft: theme.spacing(1) / 4, + paddingRight: theme.spacing(1) / 4 + }, + matchingValue: { + backgroundColor: theme.palette.success.lighter + }, + unmatchingValue: { + backgroundColor: theme.palette.error.lighter + }, + moleculeTitleLabel: { + ...theme.typography.button, + overflow: 'hidden', + whiteSpace: 'nowrap', + textOverflow: 'ellipsis', + lineHeight: '1.45' + }, + checkbox: { + padding: 0 + }, + rank: { + fontStyle: 'italic', + fontSize: 7 + }, + myLocation: { + width: 10.328, + height: 15 + }, + myLocationButton: { + minWidth: 'fit-content', + paddingLeft: theme.spacing(1) / 4, + paddingRight: theme.spacing(1) / 4, + paddingBottom: 0, + paddingTop: 0, + fontWeight: 'bold', + fontSize: 9, + borderRadius: 0, + borderStyle: 'none', + borderColor: theme.palette.white, + '&:disabled': { + borderRadius: 0, + borderStyle: 'none', + borderColor: theme.palette.white + } + }, + arrows: { + height: '100%', + border: 'solid 1px', + borderColor: theme.palette.background.divider, + borderStyle: 'solid solid solid solid' + }, + arrow: { + width: 12, + height: 15 + }, + invisArrow: { + width: 12, + height: 15, + visibility: 'hidden' + }, + warningIcon: { + padding: 0, + color: theme.palette.warning.darkLight, + '&:hover': { + color: theme.palette.warning.dark + } + }, + tagIcon: { + padding: 0, + color: theme.palette.primary.main, + '&:hover': { + color: theme.palette.primary.dark + } + }, + copyIcon: { + padding: 0, + color: theme.palette.success.main, + '&:hover': { + color: theme.palette.success.dark + } + }, + tooltip: { + backgroundColor: theme.palette.white + }, + imageActions: { + position: 'absolute', + top: 0, + left: 0 + }, + imageTagActions: { + position: 'absolute', + top: 0, + right: 0 + }, + tagPopover: { + height: '10px', + width: '220px', + padding: '0px', + fontSize: '9px', + borderRadius: '6px', + textAlign: 'center', + verticalAlign: 'center', + paddingBottom: '14px' + }, + tagPopoverSingle: { + height: '10px', + width: '18px', + padding: '0px', + fontSize: '9px', + borderRadius: '7px', + verticalAlign: 'center', + paddingBottom: '14px', + paddingLeft: '2px', + paddingRight: '3px', + textAlign: 'center' + }, + popover: { + paddingLeft: '5px', + fontSize: '10px', + borderRadius: '5px', + border: '0px black solid', + paddingRight: '5px', + minWidth: '35px', + textAlign: 'center', + verticalAlign: 'center' + }, + editButtonIcon: { + width: '0.7em', + height: '0.7em', + padding: '0px', + marginLeft: '11px', + border: 'solid 1px black', + borderRadius: '5px' + }, + gridTagsPopover: { + width: '400px' + }, + paper: { + maxHeight: 343, + height: 'auto', + overflowY: 'auto', + top: '50%', + left: '50%' + //transform: 'translate(86%, 0%)' + }, + buttonLoadingOverlay: { + position: 'absolute', + width: '11px !important', + height: '11px !important' + }, + buttonSelectedLoadingOverlay: { + color: theme.palette.primary.contrastText + } +})); + +export const img_data_init = ` + + + '`; + +const ObservationCmpView = memo( + ({ + imageHeight, + imageWidth, + data, + index, + setRef, + L, + P, + C, + S, + D, + D_C, + Q, + V, + I, + selected, + disableL, + disableP, + disableC, + observations + }) => { + // const [countOfVectors, setCountOfVectors] = useState('-'); + // const [cmpds, setCmpds] = useState('-'); + const selectedAll = useRef(false); + const ref = useRef(null); + const currentID = (data && data.id) || undefined; + const classes = useStyles(); + + const dispatch = useDispatch(); + const target_on_name = useSelector(state => state.apiReducers.target_on_name); + const filter = useSelector(state => state.selectionReducers.filter); + const [img_data, setImg_data] = useState(img_data_init); + + const viewParams = useSelector(state => state.nglReducers.viewParams); + const tagList = useSelector(state => state.apiReducers.tagList); + const tagEditorOpen = useSelector(state => state.selectionReducers.tagEditorOpened); + + const [tagEditModalOpenNew, setTagEditModalOpenNew] = useState(tagEditorOpen); + + const { getNglView } = useContext(NglContext); + const stage = getNglView(VIEWS.MAJOR_VIEW) && getNglView(VIEWS.MAJOR_VIEW).stage; + + const isLigandOn = L; + const isProteinOn = P; + const isComplexOn = C; + const isSurfaceOn = S; + const isDensityOn = D; + const isDensityCustomOn = D_C; + const isQualityOn = Q; + const isVectorOn = V; + const hasAdditionalInformation = I; + + const [isCopied, setCopied] = useClipboard(data.smiles, { successDuration: 5000 }); + + const [hasMap, setHasMap] = useState(); + + const hasAllValuesOn = isLigandOn && isProteinOn && isComplexOn; + const hasSomeValuesOn = !hasAllValuesOn && (isLigandOn || isProteinOn || isComplexOn); + + let warningIconVisible = viewParams[COMMON_PARAMS.warningIcon] === true && hasAdditionalInformation === true; + let isWireframeStyle = viewParams[NGL_PARAMS.contour_DENSITY]; + + const disableMoleculeNglControlButtons = + useSelector(state => state.previewReducers.molecule.disableNglControlButtons[currentID]) || {}; + + const colourToggle = getRandomColor(data); + + const [densityModalOpen, setDensityModalOpen] = useState(false); + const [moleculeTooltipOpen, setMoleculeTooltipOpen] = useState(false); + const [tagPopoverOpen, setTagPopoverOpen] = useState(null); + + const moleculeImgRef = useRef(null); + + const open = tagPopoverOpen ? true : false; + + const getDataForTagsTooltip = () => { + const assignedTags = getAllTagsForMol(data, tagList); + return assignedTags; + }; + + useEffect(() => { + setTagEditModalOpenNew(tagEditorOpen); + }, [tagEditorOpen]); + + const handlePopoverOpen = event => { + setTagPopoverOpen(event.currentTarget); + }; + + const handlePopoverClose = () => { + setTagPopoverOpen(null); + }; + + const resolveTagBackgroundColor = tag => { + let color = DEFAULT_TAG_COLOR; + + if (tag.colour && tag.colour !== '') { + color = tag.colour; + } else { + const category = dispatch(getCategoryById(tag.category)); + if (category) { + color = `#${category.colour}`; + } + } + + return color; + }; + + const resolveTagForegroundColor = tag => { + const bgColor = resolveTagBackgroundColor(tag); + return getFontColorByBackgroundColor(bgColor); + }; + + const generateTagPopover = () => { + const allData = getDataForTagsTooltip(); + const sortedData = [...allData].sort((a, b) => a.tag.localeCompare(b.tag)); + + const modifiedObjects = sortedData.map(obj => { + const tagNameShortLength = 2; + if (obj.tag.length > tagNameShortLength) { + return { ...obj, tag: obj.tag.slice(0, tagNameShortLength) }; + } + return obj; + }); + + const allTagsLength = allData.length > 9 ? 9 : allData.length; + const popperPadding = allTagsLength > 1 ? 250 : 420; + + return modifiedObjects?.length > 0 ? ( +
+ + {modifiedObjects.length < 2 ? ( + + {modifiedObjects.map((item, index) => + index < allTagsLength ? ( + +
{item.tag}
+
+ ) : ( +
+ ) + )} + {DJANGO_CONTEXT['username'] === 'NOT_LOGGED_IN' ? ( +
+ ) : ( +
+ + { + if (tagEditModalOpenNew) { + setTagEditModalOpenNew(false); + dispatch(setTagEditorOpen(!tagEditModalOpenNew)); + dispatch(setMoleculeForTagEdit(null)); + } else { + setTagEditModalOpenNew(true); + dispatch(setMoleculeForTagEdit(data.id)); + dispatch(setTagEditorOpen(true)); + if (setRef) { + setRef(ref.current); + } + } + }} + style={{ padding: '0px', paddingBottom: '3px', marginRight: '5px', position: 'right' }} + > + + + + + +
+ )} +
+ ) : ( + +
+ {modifiedObjects.map((item, index) => + index < allTagsLength ? ( + +
{item.tag}
+
+ ) : ( +
+ ) + )} +
+
+ {DJANGO_CONTEXT['username'] === 'NOT_LOGGED_IN' ? ( +
+ ) : ( + { + if (tagEditModalOpenNew) { + setTagEditModalOpenNew(false); + dispatch(setTagEditorOpen(!tagEditModalOpenNew)); + dispatch(setMoleculeForTagEdit(null)); + } else { + setTagEditModalOpenNew(true); + dispatch(setMoleculeForTagEdit(data.id)); + dispatch(setTagEditorOpen(true)); + if (setRef) { + setRef(ref.current); + } + } + }} + style={{ padding: '0px', paddingBottom: '3px', cursor: 'pointer' }} + > + + + + + )} +
+
+ )} +
+ {tagEditorOpen === false ? ( + + + + + {sortedData.map((item, index) => ( + +
{item.tag}
+
+ ))} +
+
+
+
+ ) : ( +
+ )} +
+ ) : DJANGO_CONTEXT['username'] === 'NOT_LOGGED_IN' ? ( +
+ ) : ( + { + if (tagEditModalOpenNew) { + setTagEditModalOpenNew(false); + dispatch(setTagEditorOpen(!tagEditModalOpenNew)); + dispatch(setMoleculeForTagEdit(null)); + } else { + setTagEditModalOpenNew(true); + dispatch(setMoleculeForTagEdit(data.id)); + dispatch(setTagEditorOpen(true)); + if (setRef) { + setRef(ref.current); + } + } + }} + style={{ padding: '0px', paddingBottom: '3px', cursor: 'pointer' }} + > + + + + + ); + }; + + // componentDidMount + useEffect(() => { + dispatch(getMolImage(data.id, MOL_TYPE.HIT, imageWidth, imageHeight)).then(i => { + setImg_data(i); + }); + }, [data.id, data.smiles, imageHeight, imageWidth, dispatch]); + + useEffect(() => { + dispatch(getQualityInformation(data)); + }, [data, dispatch]); + + const svg_image = ( + + ); + // Here add the logic that updates this based on the information + // const refinement = ; + const selected_style = { + backgroundColor: colourToggle + }; + const not_selected_style = {}; + const current_style = + isLigandOn || isProteinOn || isComplexOn || isSurfaceOn || isDensityOn || isVectorOn + ? selected_style + : not_selected_style; + + const addNewLigand = (skipTracking = false) => { + // if (selectMoleculeSite) { + // selectMoleculeSite(data.site); + // } + dispatch( + withDisabledMoleculeNglControlButton(currentID, 'ligand', async () => { + await dispatch(addLigand(stage, data, colourToggle, false, true, skipTracking)); + }) + ); + }; + + const removeSelectedLigand = (skipTracking = false) => { + dispatch(removeLigand(stage, data, skipTracking)); + selectedAll.current = false; + }; + + const [loadingAll, setLoadingAll] = useState(false); + const [loadingLigand, setLoadingLigand] = useState(false); + const onLigand = calledFromSelectAll => { + setLoadingLigand(true); + if (calledFromSelectAll === true && selectedAll.current === true) { + if (isLigandOn === false) { + addNewLigand(calledFromSelectAll); + } + } else if (calledFromSelectAll && selectedAll.current === false) { + removeSelectedLigand(calledFromSelectAll); + } else if (!calledFromSelectAll) { + if (isLigandOn === false) { + addNewLigand(); + } else { + removeSelectedLigand(); + } + } + setLoadingLigand(false); + }; + + const removeSelectedProtein = (skipTracking = false) => { + dispatch(removeHitProtein(stage, data, colourToggle, skipTracking)); + selectedAll.current = false; + }; + + const addNewProtein = (skipTracking = false) => { + // if (selectMoleculeSite) { + // selectMoleculeSite(data.site); + // } + dispatch( + withDisabledMoleculeNglControlButton(currentID, 'protein', async () => { + await dispatch(addHitProtein(stage, data, colourToggle, true, skipTracking)); + }) + ); + }; + + const [loadingProtein, setLoadingProtein] = useState(false); + const onProtein = calledFromSelectAll => { + setLoadingProtein(true); + if (calledFromSelectAll === true && selectedAll.current === true) { + if (isProteinOn === false) { + addNewProtein(calledFromSelectAll); + } + } else if (calledFromSelectAll && selectedAll.current === false) { + removeSelectedProtein(calledFromSelectAll); + } else if (!calledFromSelectAll) { + if (isProteinOn === false) { + addNewProtein(); + } else { + removeSelectedProtein(); + } + } + setLoadingProtein(false); + }; + + const removeSelectedComplex = (skipTracking = false) => { + dispatch(removeComplex(stage, data, colourToggle, skipTracking)); + selectedAll.current = false; + }; + + const addNewComplex = (skipTracking = false) => { + dispatch( + withDisabledMoleculeNglControlButton(currentID, 'complex', async () => { + await dispatch(addComplex(stage, data, colourToggle, skipTracking)); + }) + ); + }; + + const [loadingComplex, setLoadingComplex] = useState(false); + const onComplex = calledFromSelectAll => { + setLoadingComplex(true); + if (calledFromSelectAll === true && selectedAll.current === true) { + if (isComplexOn === false) { + addNewComplex(calledFromSelectAll); + } + } else if (calledFromSelectAll && selectedAll.current === false) { + removeSelectedComplex(calledFromSelectAll); + } else if (!calledFromSelectAll) { + if (isComplexOn === false) { + addNewComplex(); + } else { + removeSelectedComplex(); + } + } + setLoadingComplex(false); + }; + + const removeSelectedSurface = () => { + dispatch(removeSurface(stage, data, colourToggle)); + }; + + const addNewSurface = () => { + dispatch( + withDisabledMoleculeNglControlButton(currentID, 'surface', async () => { + await dispatch(addSurface(stage, data, colourToggle)); + }) + ); + }; + + const [loadingSurface, setLoadingSurface] = useState(false); + const onSurface = () => { + setLoadingSurface(true); + if (isSurfaceOn === false) { + addNewSurface(); + } else { + removeSelectedSurface(); + } + setLoadingSurface(false); + }; + + const removeSelectedDensity = () => { + dispatch(removeDensity(stage, data, colourToggle, false)); + }; + + const addNewDensityCustom = async () => { + dispatch( + withDisabledMoleculeNglControlButton(currentID, 'density', async () => { + await dispatch(addDensityCustomView(stage, data, colourToggle, isWireframeStyle)); + }) + ); + }; + + const addNewDensity = async () => { + dispatch( + withDisabledMoleculeNglControlButton(currentID, 'ligand', async () => { + await dispatch( + withDisabledMoleculeNglControlButton(currentID, 'density', async () => { + await dispatch(addDensity(stage, data, colourToggle, isWireframeStyle)); + }) + ); + }) + ); + }; + + const [loadingDensity, setLoadingDensity] = useState(false); + const onDensity = () => { + setLoadingDensity(true); + if (isDensityOn === false && isDensityCustomOn === false) { + dispatch(getDensityMapData(data)).then(r => { + if (r) { + dispatch(setDensityModalOpen(true)); + } else { + addNewDensity(); + } + }); + } else if (isDensityCustomOn === false) { + addNewDensityCustom(); + } else { + removeSelectedDensity(); + } + setLoadingDensity(false); + }; + + const removeSelectedQuality = () => { + dispatch(removeQuality(stage, data, colourToggle)); + }; + + const addNewQuality = () => { + dispatch( + withDisabledMoleculeNglControlButton(currentID, 'ligand', async () => { + await dispatch(addQuality(stage, data, colourToggle)); + }) + ); + }; + + const onQuality = () => { + if (isQualityOn === false) { + addNewQuality(); + } else { + removeSelectedQuality(); + } + }; + + const removeSelectedVector = () => { + dispatch(removeVector(stage, data)); + }; + + const addNewVector = () => { + dispatch( + withDisabledMoleculeNglControlButton(currentID, 'vector', async () => { + await dispatch(addVector(stage, data)); + }) + ); + }; + + const [loadingVector, setLoadingVector] = useState(false); + const onVector = () => { + setLoadingVector(true); + if (isVectorOn === false) { + addNewVector(); + } else { + removeSelectedVector(); + } + setLoadingVector(false); + }; + + const setCalledFromAll = () => { + let isSelected = selectedAll.current === true; + if (isSelected) { + dispatch(setSelectedAll(data, true, true, true)); + } else { + dispatch(setDeselectedAll(data, isLigandOn, isProteinOn, isComplexOn)); + } + }; + + /** + * Check if given molecule is matching current filter + * @param Object item - item.name is attribute name, item.value is its value + * @return boolean + */ + const isMatchingValue = item => { + let match = false; + if (!(item.value < filter.filter[item.name].minValue || item.value > filter.filter[item.name].maxValue)) { + match = true; + } + return match; + }; + + /** + * Get css class for value regarding to its filter match + * @param Object item - item.name is attribute name, item.value is its value + * @return string - css class + */ + const getValueMatchingClass = item => { + let cssClass = ''; + if (filter && filter.predefined !== 'none') { + cssClass = isMatchingValue(item) ? classes.matchingValue : classes.unmatchingValue; + } + return cssClass; + }; + + const scrollToElement = element => { + element.scrollIntoView({ + behavior: 'auto', + block: 'nearest', + inline: 'nearest' + }); + }; + + let moleculeTitle = data?.code.replace(new RegExp(`${target_on_name}-`, 'i'), ''); + + const moleculeLPCControlButtonDisabled = ['ligand', 'protein', 'complex'].some( + type => disableMoleculeNglControlButtons[type] + ); + + const groupMoleculeLPCControlButtonDisabled = disableL || disableP || disableC; + + return ( + <> + + {/* Site number */} + + + { + const result = e.target.checked; + if (result) { + dispatch(appendToMolListToEdit(currentID)); + } else { + dispatch(removeFromMolListToEdit(currentID)); + } + }} + /> + + + {index + 1}. + + + + {/* Title label */} + + +
{moleculeTitle}
+
+ {generateTagPopover()} +
+ {/* Control Buttons A, L, C, V */} + + + + + + + + + + + + + + + + + + + + + + + + + {/* C stands for contacts now */} + + + + + + + + + + + + + + + + + + + + + {/* + + {getCalculatedProps().map(item => ( + + + {item.name === moleculeProperty.mw && Math.round(item.value)} + {item.name === moleculeProperty.logP && Math.round(item.value) } + {item.name === moleculeProperty.tpsa && Math.round(item.value)} + {item.name !== moleculeProperty.mw && + item.name !== moleculeProperty.logP && + item.name !== moleculeProperty.tpsa && + item.value} + + + ))} + + */} +
+ {/* Image */} +
setMoleculeTooltipOpen(true)} + onMouseLeave={() => setMoleculeTooltipOpen(false)} + ref={moleculeImgRef} + > + {svg_image} +
+ {moleculeTooltipOpen && ( + + + {!isCopied ? : } + + + )} + {warningIconVisible && ( + + onQuality()}> + + + + )} +
+
+
+ + + + ); + } +); + +ObservationCmpView.displayName = 'ObservationCmpView'; +export default ObservationCmpView; diff --git a/js/components/preview/molecule/redux/selectors.js b/js/components/preview/molecule/redux/selectors.js index d6dc54691..96e6e8129 100644 --- a/js/components/preview/molecule/redux/selectors.js +++ b/js/components/preview/molecule/redux/selectors.js @@ -6,6 +6,7 @@ const getTagFilteringMode = state => state.selectionReducers.tagFilteringMode; const getNoTagsReceived = state => state.apiReducers.noTagsReceived; const getDisplayAllMolecules = state => state.selectionReducers.displayAllMolecules; const getDisplayUntaggedMolecules = state => state.selectionReducers.displayUntaggedMolecules; +export const getLHSCompoundsList = state => state.apiReducers.lhs_compounds_list; export const selectJoinedMoleculeList = createSelector( getAllMolecules, diff --git a/js/components/preview/tags/utils/tagUtils.js b/js/components/preview/tags/utils/tagUtils.js index 221fd0548..f78117ccb 100644 --- a/js/components/preview/tags/utils/tagUtils.js +++ b/js/components/preview/tags/utils/tagUtils.js @@ -130,44 +130,6 @@ export const getAllTagsForMol = (mol, tagList) => { return result; }; -// export const getCategoryIds = () => { -// const result = []; - -// let categoryObject = { -// id: CATEGORY_ID[CATEGORY_TYPE.SITE], -// category: CATEGORY_TYPE['SITE'], -// colour: '00CC00', -// description: null -// }; -// result.push({ ...categoryObject }); - -// categoryObject = { -// id: CATEGORY_ID[CATEGORY_TYPE.SERIES], -// category: CATEGORY_TYPE['SERIES'], -// colour: '00CC00', -// description: null -// }; -// result.push({ ...categoryObject }); - -// categoryObject = { -// id: CATEGORY_ID[CATEGORY_TYPE.FORUM], -// category: CATEGORY_TYPE['FORUM'], -// colour: '00CC00', -// description: null -// }; -// result.push({ ...categoryObject }); - -// categoryObject = { -// id: CATEGORY_ID[CATEGORY_TYPE.OTHER], -// category: CATEGORY_TYPE['OTHER'], -// colour: '00CC00', -// description: null -// }; -// result.push({ ...categoryObject }); - -// return result; -// }; - export const getDefaultTagDiscoursePostText = tag => { return `This post for tag ${tag.tag} is here to discuss its contents.`; };