diff --git a/src/Filter.tsx b/src/Filter.tsx index 5d616ad..b531c3e 100644 --- a/src/Filter.tsx +++ b/src/Filter.tsx @@ -1,6 +1,7 @@ import React, { ChangeEvent, useState } from 'react'; import { FilterAlt as FilterIcon } from '@mui/icons-material'; +import TextFormatIcon from '@mui/icons-material/TextFormat'; import { FormControl, FormHelperText, @@ -13,10 +14,18 @@ import { type Props = { handleSetFilters: (filters: Set) => void; filters: Set; + handleSetMatchFullWord: () => void; + matchFullWord: boolean; }; -export default function Filters({ handleSetFilters, filters }: Props) { +export default function Filters({ + handleSetFilters, + filters, + handleSetMatchFullWord, + matchFullWord, +}: Props) { const [query, setQuery] = useState(''); + const handleSearch = () => { if (query) { const newSet = new Set(filters); @@ -42,11 +51,24 @@ export default function Filters({ handleSetFilters, filters }: Props) { value={query} onChange={handleChangeQuery} endAdornment={ - - - - - + <> + + + + + + + + + + + } label="Filter..." aria-describedby="helper-text" diff --git a/src/Sizer.tsx b/src/Sizer.tsx index fe59c84..9d8f222 100644 --- a/src/Sizer.tsx +++ b/src/Sizer.tsx @@ -23,6 +23,7 @@ type Props = { maxWeight: number; settings: SettingsProps; filters: Set; + matchFullWord: boolean; }; export default function Sizer({ @@ -33,6 +34,7 @@ export default function Sizer({ minWeight, maxWeight, filters, + matchFullWord, }: Props) { const [nodeSize, setNodeSize] = useState(1); const [edgeSize, setEdgeSize] = useState(1); @@ -130,7 +132,7 @@ export default function Sizer({ console.warn(`caught error: ${e}`); } } - }, [fontSize, nodeSize, edgeSize, cy, settings, filters]); + }, [fontSize, nodeSize, edgeSize, cy, settings, filters, matchFullWord]); const edgesDisabled = (n: number) => !settings[SHOW_EDGES_KEY] || n === 0; const nodesDisabled = !settings[SHOW_NODES_KEY] || settings[SHOW_LABELS_KEY]; diff --git a/src/View.tsx b/src/View.tsx index 86fdd0a..950952c 100644 --- a/src/View.tsx +++ b/src/View.tsx @@ -2,19 +2,15 @@ import React, { useEffect, useState } from 'react'; import CytoscapeComponent from 'react-cytoscapejs'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; -import SaveIcon from '@mui/icons-material/Save'; import { Accordion, AccordionDetails, AccordionSummary, Checkbox, Chip, - Fab, FormControlLabel, FormGroup, Grid, - IconButton, - Modal, } from '@mui/material'; import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; @@ -59,14 +55,6 @@ const selectTextOutlineColor = _.memoize(function (ele: NodeSingular) { return 'gray'; }); -const convertToString = (obj?: any): string => { - return JSON.stringify(obj, null, 2); -}; - -const convertFromString = (obj?: any): any => { - return JSON.parse(obj); -}; - export const SHOW_NODES_KEY = 'showNodes'; export const SHOW_PARENT_NODES_KEY = 'showParentNodes'; export const SHOW_EDGES_KEY = 'showEdges'; @@ -105,6 +93,7 @@ const View = ({ graph }: Props) => { const [minWeight, setMinWeight] = useState(Math.min(...weights)); const [maxWeight, setMaxWeight] = useState(Math.max(...weights)); const [filters, setFilters] = useState>(new Set()); + const [matchFullWord, setMatchFullWord] = useState(false); const stylesheet = [ { @@ -178,6 +167,23 @@ const View = ({ graph }: Props) => { setFilters(newSet); }; + const handleSetMatchFullWord = (): void => { + setMatchFullWord(!matchFullWord); + }; + + const showElement = (ele: Cytoscape.NodeSingular): void => { + ele.style({ visibility: 'visible' }); + + // if node is a child + // todo: do not assume just one parent + const category = ele.parent(); + category.style({ visibility: 'visible' }); + + // if node is a parent + const children = ele.children(); + children.style({ visibility: 'visible' }); + }; + useEffect(() => { // anything in here is fired on component mount. if (cyHandle) { @@ -326,17 +332,15 @@ const View = ({ graph }: Props) => { // bring back relevant nodes cyHandle.nodes().forEach((ele) => { const cleanName = ele.data('name').trim().toLowerCase(); - if (cleanName.includes(f)) { - ele.style({ visibility: 'visible' }); - - // if node is a child - // todo: do not assume just one parent - const category = ele.parent(); - category.style({ visibility: 'visible' }); - - // if node is a parent - const children = ele.children(); - children.style({ visibility: 'visible' }); + if (matchFullWord) { + if (cleanName === f) { + showElement(ele); + } + } else { + // partial match + if (cleanName.includes(f)) { + showElement(ele); + } } }); cyHandle.edges().style({ visibility: 'visible' }); @@ -356,11 +360,10 @@ const View = ({ graph }: Props) => { console.warn(`caught error: ${e}`); } } - return () => { // anything in here is fired on component unmount. }; - }, [settings, cyHandle, filters]); + }, [settings, cyHandle, filters, matchFullWord]); // keydown listener for deleting elements useEffect(() => { @@ -443,7 +446,12 @@ const View = ({ graph }: Props) => { - +
{Array.from(filters).map((f) => ( { maxWeight={maxWeight} settings={settings} filters={filters} + matchFullWord={matchFullWord} />