diff --git a/frontend/context/HoverMetadataContext.ts b/frontend/context/HoverMetadataContext.ts new file mode 100644 index 0000000..f5409e0 --- /dev/null +++ b/frontend/context/HoverMetadataContext.ts @@ -0,0 +1,12 @@ +import { DotMetadata } from '@/models/combinedDefinition' +import React from 'react' + +export type HoverDotMetadataContextProps = { + hoverDotMetadata: DotMetadata | null + setHoverDotMetadata: React.Dispatch> +} + +export const HoverDotMetadataContext = React.createContext({ + hoverDotMetadata: null, + setHoverDotMetadata: () => {}, +}) diff --git a/frontend/pages/DefinitionList/Show.tsx b/frontend/pages/DefinitionList/Show.tsx index c26566c..f825356 100644 --- a/frontend/pages/DefinitionList/Show.tsx +++ b/frontend/pages/DefinitionList/Show.tsx @@ -17,6 +17,8 @@ import { MetadataDialog } from './components/MetadataDialog' import type { DialogProps } from './components/dialog' import { RecentModulesContext } from '@/context/RecentModulesContext' import { Module } from '@/models/module' +import { HoverDotMetadataContext } from '@/context/HoverMetadataContext' +import { DotMetadata } from '@/models/combinedDefinition' export const Show: React.FC = () => { const [selectedDefinitionIds, setSelectedDefinitionIds] = useBitIdHash() @@ -26,6 +28,7 @@ export const Show: React.FC = () => { concentrate: false, onlyModule: false, }) + const [hoverDotMetadata, setHoverDotMetadata] = useState(null) const { data: combinedDefinition, isLoading, @@ -40,45 +43,47 @@ export const Show: React.FC = () => { return ( - - - - - - - {isLoading ? ( - - - - ) : !combinedDefinition ? ( - -

No data

-
- ) : ( - - - - - )} -
-
+ + + + + + + + {isLoading ? ( + + + + ) : !combinedDefinition ? ( + +

No data

+
+ ) : ( + + + + + )} +
+
+
) diff --git a/frontend/pages/DefinitionList/components/DefinitionGraph/ScrollableSvg.tsx b/frontend/pages/DefinitionList/components/DefinitionGraph/ScrollableSvg.tsx index 23e1a08..b53a56b 100644 --- a/frontend/pages/DefinitionList/components/DefinitionGraph/ScrollableSvg.tsx +++ b/frontend/pages/DefinitionList/components/DefinitionGraph/ScrollableSvg.tsx @@ -1,4 +1,4 @@ -import React, { FC, useCallback, useEffect, useRef, useState } from 'react' +import React, { FC, useCallback, useContext, useEffect, useRef, useState } from 'react' import { ReactSVGPanZoom, TOOL_NONE, TOOL_PAN } from 'react-svg-pan-zoom' import { ReactSvgPanZoomLoader } from 'react-svg-pan-zoom-loader' import styled from 'styled-components' @@ -11,6 +11,8 @@ import { extractSvgSize, getClosestAndSmallestElement, toSVGPoint } from '@/util import { DialogProps } from '../dialog' import type { Tool, Value } from 'react-svg-pan-zoom' +import { HoverDotMetadataContext } from '@/context/HoverMetadataContext' +import { color } from '@/constants/theme' type Props = { combinedDefinition: CombinedDefinition @@ -41,7 +43,7 @@ export const ScrollableSvg: FC = ({ combinedDefinition, setVisibleDialog const [value, setValue] = useState({} as Value) // NOTE: react-svg-pan-zoom supported blank object as a initial value. but types is not supported. const [tool, setTool] = useState(TOOL_PAN) - const [hoverMetadata, setHoverMetadata] = useState(null) + const { hoverDotMetadata, setHoverDotMetadata } = useContext(HoverDotMetadataContext) const [svg, setSvg] = useState('') const svgSize = extractSvgSize(svg) @@ -77,9 +79,9 @@ export const ScrollableSvg: FC = ({ combinedDefinition, setVisibleDialog } const onClickGeometry = (event: MouseEvent) => { - if (hoverMetadata) { + if (hoverDotMetadata) { event.preventDefault() - setVisibleDialog({ type: 'metadataDialog', metadata: hoverMetadata, left: event.clientX, top: event.clientY }) + setVisibleDialog({ type: 'metadataDialog', metadata: hoverDotMetadata, left: event.clientX, top: event.clientY }) } } @@ -88,12 +90,12 @@ export const ScrollableSvg: FC = ({ combinedDefinition, setVisibleDialog return () => { document.removeEventListener('click', onClickGeometry) } - }, [tool, hoverMetadata, setVisibleDialog]) + }, [tool, hoverDotMetadata, setVisibleDialog]) // On hover .node, .edge, .cluster useEffect(() => { if (tool !== TOOL_NONE) { - setHoverMetadata(null) + setHoverDotMetadata(null) return } @@ -102,9 +104,9 @@ export const ScrollableSvg: FC = ({ combinedDefinition, setVisibleDialog if (element) { const metadata = combinedDefinition.dotMetadata.find(({ id }) => element.id === id) - setHoverMetadata(metadata ?? null) + setHoverDotMetadata(metadata ?? null) } else { - setHoverMetadata(null) + setHoverDotMetadata(null) } } @@ -136,7 +138,7 @@ export const ScrollableSvg: FC = ({ combinedDefinition, setVisibleDialog } } - setHoverMetadata((prev) => findNewMetadata(prev)) + setHoverDotMetadata((prev) => findNewMetadata(prev)) setVisibleDialog((prev) => { if (prev?.type === 'metadataDialog') { const newMetadata = findNewMetadata(prev.metadata) @@ -150,12 +152,16 @@ export const ScrollableSvg: FC = ({ combinedDefinition, setVisibleDialog return prev } }) - }, [combinedDefinition.dotMetadata, setVisibleDialog, setHoverMetadata]) + }, [combinedDefinition.dotMetadata, setVisibleDialog, setHoverDotMetadata]) + + useEffect(() => { + console.log(hoverDotMetadata?.id) + }, [hoverDotMetadata]) if (!svg) return null return ( - + ( @@ -198,7 +204,10 @@ const Wrapper = styled.div<{ $idOnHover: string | undefined }>` props.$idOnHover && ` #${props.$idOnHover} { - stroke-width: 3; + ellipse { + stroke-width: 4; + stroke: ${color.DANGER}; + } } cursor: pointer; diff --git a/frontend/pages/DefinitionList/components/DefinitionSources/DefinitionSources.tsx b/frontend/pages/DefinitionList/components/DefinitionSources/DefinitionSources.tsx index a2ac1b1..dcec61e 100644 --- a/frontend/pages/DefinitionList/components/DefinitionSources/DefinitionSources.tsx +++ b/frontend/pages/DefinitionList/components/DefinitionSources/DefinitionSources.tsx @@ -1,4 +1,4 @@ -import { FC, useCallback, useContext, useMemo, useState } from 'react' +import { FC, useCallback, useContext, useEffect, useMemo, useState, createRef } from 'react' import styled from 'styled-components' import { Link } from '@/components/Link' @@ -8,7 +8,6 @@ import { Cluster, EmptyTableBody, FaCircleInfoIcon, - FaCopyIcon, FaPencilIcon, Table, TableReel, @@ -26,6 +25,8 @@ import { RecentModulesContext } from '@/context/RecentModulesContext' import { SourceModulesComboBox } from '@/components/SourceModulesComboBox' import { UpdateSourceModulesButton } from '@/components/UpdateSourceModulesButton' import { SourceMemoInput } from '@/components/SourceMemoInput' +import React from 'react' +import { HoverDotMetadataContext } from '@/context/HoverMetadataContext' const sortTypes = ['asc', 'desc', 'none'] as const @@ -38,16 +39,48 @@ type SortState = { type DefinitionSourceTrProps = { source: Source + combinedDefinition: CombinedDefinition mutateCombinedDefinition: () => void } -const DefinitionSourceTr: FC = ({ source, mutateCombinedDefinition }) => { +// Return tr +const isTr = (event: MouseEvent, trRef: HTMLTableRowElement): boolean => { + const tr = (event.target as HTMLElement).closest('tr') + + return tr === trRef +} + +const DefinitionSourceTr: FC = ({ source, combinedDefinition, mutateCombinedDefinition }) => { + const ref = createRef() const { recentModules, setRecentModules } = useContext(RecentModulesContext) + const { setHoverDotMetadata } = useContext(HoverDotMetadataContext) const [editingMemo, setEditingMemo] = useState(false) const [editingModules, setEditingModules] = useState(false) + // On hover .node, .edge, .cluster + useEffect(() => { + if (!combinedDefinition || !ref.current) return + + const currentRef = ref.current + + const onMouseMove = (event: MouseEvent) => { + if (!isTr(event, currentRef)) return + + const dotMetadata = combinedDefinition.dotMetadata.find((d) => d.type === 'source' && d.sourceName === source.sourceName) + + console.log(`set ${dotMetadata?.id}`) + setHoverDotMetadata(dotMetadata ?? null) + } + + document.addEventListener('mousemove', onMouseMove) + + return () => { + document.removeEventListener('mousemove', onMouseMove) + } + }, [combinedDefinition?.dotMetadata, setHoverDotMetadata]) + return ( - + {source.sourceName} @@ -187,6 +220,7 @@ export const DefinitionSources: FC = ({ combinedDefiniti ))}