Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,7 @@ export const CheckResultWarnings = observer(function CheckResultWarnings({
if (!feature) {
return null
}
let row = 0
const featureLayout = display.getFeatureLayoutPosition(feature)
if (featureLayout) {
row = featureLayout.layoutRow + featureLayout.featureRow
}
const row = display.getRowForFeature(feature) ?? 0
const top = row * apolloRowHeight
const height = apolloRowHeight
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { type Coord, useStyles } from '../../util/displayUtils'
import type { LinearApolloDisplay as LinearApolloDisplayI } from '../stateModel'

import { CheckResultWarnings } from './CheckResultWarnings'
import { OverlayCanvas } from './OverlayCanvas'

interface LinearApolloDisplayProps {
model: LinearApolloDisplayI
Expand All @@ -29,18 +30,12 @@ export const LinearApolloDisplay = observer(function LinearApolloDisplay(
const {
loading,
contextMenuItems: getContextMenuItems,
cursor,
featuresHeight,
featuresHeight: getFeaturesHeight,
isShown,
onMouseDown,
onMouseLeave,
onMouseMove,
onMouseUp,
regionCannotBeRendered,
session,
setCanvas,
setCollaboratorCanvas,
setOverlayCanvas,
setTheme,
} = model
const { classes } = useStyles()
Expand All @@ -55,115 +50,100 @@ export const LinearApolloDisplay = observer(function LinearApolloDisplay(
if (!isShown) {
return null
}
const featuresHeight = getFeaturesHeight(lgv.assemblyNames[0])
return (
<>
<div
className={classes.canvasContainer}
style={{
width: lgv.dynamicBlocks.totalWidthPx,
height: featuresHeight,
}}
onContextMenu={(event) => {
event.preventDefault()
if (contextMenuItems.length > 0) {
// There's already a context menu open, so close it
setContextMenuItems([])
} else {
const coord: [number, number] = [event.clientX, event.clientY]
setContextCoord(coord)
setContextMenuItems(getContextMenuItems(event))
}
}}
>
{session.isLocked ? (
<div className={classes.locked} data-testid="lock-icon">
<LockIcon />
</div>
) : null}
{loading ? (
<div className={classes.loading}>
<CircularProgress size="18px" />
</div>
) : null}
{/* This type is wrong in @jbrowse/core */}
{/* eslint-disable-next-line @typescript-eslint/no-unnecessary-condition */}
{message ? (
<Alert
severity="warning"
classes={{ message: classes.ellipses }}
slotProps={{ root: { className: classes.center } }}
>
<Tooltip title={message}>
<div>{message}</div>
</Tooltip>
</Alert>
) : (
// Promise.resolve() in these 3 callbacks is to avoid infinite rendering loop
// https://github.com/mobxjs/mobx/issues/3728#issuecomment-1715400931
<>
<canvas
ref={async (node: HTMLCanvasElement) => {
await Promise.resolve()
setCollaboratorCanvas(node)
}}
width={lgv.dynamicBlocks.totalWidthPx}
height={featuresHeight}
className={classes.canvas}
data-testid="collaboratorCanvas"
/>
<canvas
ref={async (node: HTMLCanvasElement) => {
await Promise.resolve()
setCanvas(node)
}}
width={lgv.dynamicBlocks.totalWidthPx}
height={featuresHeight}
className={classes.canvas}
data-testid="canvas"
/>
<canvas
ref={async (node: HTMLCanvasElement) => {
await Promise.resolve()
setOverlayCanvas(node)
}}
width={lgv.dynamicBlocks.totalWidthPx}
height={featuresHeight}
onMouseMove={onMouseMove}
onMouseLeave={onMouseLeave}
onMouseDown={onMouseDown}
onMouseUp={onMouseUp}
className={classes.canvas}
style={{ cursor: cursor ?? 'default' }}
data-testid="overlayCanvas"
/>
<CheckResultWarnings display={model} />
<Menu
open={contextMenuItems.length > 0}
onMenuItemClick={(_, callback) => {
callback()
setContextMenuItems([])
}}
onClose={() => {
setContextMenuItems([])
}}
slotProps={{
transition: {
onExit: () => {
setContextMenuItems([])
},
<div
className={classes.canvasContainer}
style={{
width: lgv.dynamicBlocks.totalWidthPx,
height: featuresHeight,
}}
onContextMenu={(event) => {
event.preventDefault()
if (contextMenuItems.length > 0) {
// There's already a context menu open, so close it
setContextMenuItems([])
} else {
const coord: [number, number] = [event.clientX, event.clientY]
setContextCoord(coord)
setContextMenuItems(getContextMenuItems(event))
}
}}
>
{session.isLocked ? (
<div className={classes.locked} data-testid="lock-icon">
<LockIcon />
</div>
) : null}
{loading ? (
<div className={classes.loading}>
<CircularProgress size="18px" />
</div>
) : null}
{/* This type is wrong in @jbrowse/core */}
{/* eslint-disable-next-line @typescript-eslint/no-unnecessary-condition */}
{message ? (
<Alert
severity="warning"
classes={{ message: classes.ellipses }}
slotProps={{ root: { className: classes.center } }}
>
<Tooltip title={message}>
<div>{message}</div>
</Tooltip>
</Alert>
) : (
// Promise.resolve() in these 2 callbacks is to avoid infinite rendering loop
// https://github.com/mobxjs/mobx/issues/3728#issuecomment-1715400931
<>
<canvas
ref={async (node: HTMLCanvasElement) => {
await Promise.resolve()
setCollaboratorCanvas(node)
}}
width={lgv.dynamicBlocks.totalWidthPx}
height={featuresHeight}
className={classes.canvas}
data-testid="collaboratorCanvas"
/>
<canvas
ref={async (node: HTMLCanvasElement) => {
await Promise.resolve()
setCanvas(node)
}}
width={lgv.dynamicBlocks.totalWidthPx}
height={featuresHeight}
className={classes.canvas}
data-testid="canvas"
/>
<OverlayCanvas model={model} />
<CheckResultWarnings display={model} />
<Menu
open={contextMenuItems.length > 0}
onMenuItemClick={(_, callback) => {
callback()
setContextMenuItems([])
}}
onClose={() => {
setContextMenuItems([])
}}
slotProps={{
transition: {
onExit: () => {
setContextMenuItems([])
},
}}
anchorReference="anchorPosition"
anchorPosition={
contextCoord
? { top: contextCoord[1], left: contextCoord[0] }
: undefined
}
menuItems={contextMenuItems}
/>
</>
)}
</div>
</>
},
}}
anchorReference="anchorPosition"
anchorPosition={
contextCoord
? { top: contextCoord[1], left: contextCoord[0] }
: undefined
}
menuItems={contextMenuItems}
/>
</>
)}
</div>
)
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/* eslint-disable @typescript-eslint/unbound-method */

/* eslint-disable @typescript-eslint/no-misused-promises */

import { getContainingView } from '@jbrowse/core/util'
import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
import { observer } from 'mobx-react'
import React, { useState } from 'react'

import { type Coord, useStyles } from '../../util/displayUtils'
import type { LinearApolloDisplay as LinearApolloDisplayI } from '../stateModel'

import { Tooltip as LinearApolloDisplayTooltip } from './Tooltip'

interface LinearApolloDisplayProps {
model: LinearApolloDisplayI
}

export const OverlayCanvas = observer(function OverlayCanvas(
props: LinearApolloDisplayProps,
) {
const { model } = props
const {
cursor,
featuresHeight: getFeaturesHeight,
isShown,
onMouseDown,
onMouseLeave,
onMouseMove,
onMouseUp,
session,
setOverlayCanvas,
} = model
const { classes } = useStyles()
const lgv = getContainingView(model) as unknown as LinearGenomeViewModel

const [mouseCoord, setMouseCoord] = useState<Coord>()
if (!isShown) {
return null
}
const featuresHeight = getFeaturesHeight(lgv.assemblyNames[0])
// Promise.resolve() in this callback is to avoid infinite rendering loop
// https://github.com/mobxjs/mobx/issues/3728#issuecomment-1715400931
return (
<>
<canvas
ref={async (node: HTMLCanvasElement) => {
await Promise.resolve()
setOverlayCanvas(node)
}}
width={lgv.dynamicBlocks.totalWidthPx}
height={featuresHeight}
onMouseMove={(...args) => {
const [event] = args
setMouseCoord([event.clientX, event.clientY])
onMouseMove(...args)
}}
onMouseLeave={(...args) => {
setMouseCoord(undefined)
onMouseLeave(...args)
}}
onMouseDown={onMouseDown}
onMouseUp={onMouseUp}
className={classes.canvas}
style={{ cursor: cursor ?? 'default' }}
data-testid="overlayCanvas"
/>
<LinearApolloDisplayTooltip
mouseCooordinate={mouseCoord}
session={session}
/>
</>
)
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { BaseTooltip } from '@jbrowse/core/ui'
import { observer } from 'mobx-react'
import React from 'react'

import type { ApolloSessionModel } from '../../session'
import type { Coord } from '../../util/displayUtils'

interface LinearApolloDisplayProps {
mouseCooordinate: Coord | undefined
session: ApolloSessionModel
}

export const Tooltip = observer(function Tooltip(
props: LinearApolloDisplayProps,
) {
const { mouseCooordinate, session } = props
const { apolloHoveredFeature } = session

if (!(mouseCooordinate && apolloHoveredFeature)) {
return
}
const [x, y] = mouseCooordinate
const { feature } = apolloHoveredFeature
const { attributes, min, max } = feature
const location = `Loc: ${min + 1}..${max}`
const featureType = `Type: ${feature.type}`
const featureName = attributes.get('gff_name')?.find((name) => name !== '')
return (
<BaseTooltip clientPoint={{ x, y }} placement="top-start">
{featureType}
<br />
{featureName ? (
<>
{featureName}
<br />
</>
) : null}
{location}
</BaseTooltip>
)
})
Loading
Loading