Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

In-plot bookmarking #416

Merged
merged 8 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
4 changes: 2 additions & 2 deletions e2e-tests/provenance.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ test('Selection History', async ({ page }) => {
await page.getByLabel('Additional options menu').click();
await page.getByLabel('History tree sidebar').click();

const schoolIntersection = page.locator('#Subset_School > g:nth-child(3)');
const duffFanIntersection = page.locator('[id="Subset_Duff_Fan\\~\\&\\~Male"] > g:nth-child(3)');
const schoolIntersection = page.locator('#Subset_School > g:nth-child(4)');
const duffFanIntersection = page.locator('[id="Subset_Duff_Fan\\~\\&\\~Male"] > g:nth-child(4)');

// Testing history for a subset selection & deselection
await page.locator('g > circle').first().click();
Expand Down
122 changes: 114 additions & 8 deletions packages/upset/src/components/Columns/BookmarkStar.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,134 @@
import { Row } from '@visdesignlab/upset2-core';
import { FC } from 'react';
import { useRecoilValue } from 'recoil';
import StarIcon from '@mui/icons-material/Star';
import {
FC,
MouseEvent,
useContext,
useMemo,
useState,
} from 'react';
import { useRecoilValue } from 'recoil';
import { ProvenanceContext } from '../Root';
import { dimensionsSelector } from '../../atoms/dimensionsAtom';
import translate from '../../utils/transform';
import { bookmarkedColorPalette } from '../../atoms/config/currentIntersectionAtom';
import {
bookmarkedColorPalette,
bookmarkSelector,
nextColorSelector,
} from '../../atoms/config/currentIntersectionAtom';

type Props = {
row: Row;
}
row: Row;
};

const BASE_OPACITY = 0.0;
const HOVERED_OPACITY = 0.5;
const BOOKMARKED_OPACITY = 1.0;

/**
* BookmarkStar component renders a star icon that can be bookmarked.
* It changes its color and opacity based on the bookmark and hover states.
*
* @component
* @param {Props} props - The properties object.
* @param {Object} props.row - The row data for the current item.
* @returns {JSX.Element} The rendered BookmarkStar component.
*
* @example
* <BookmarkStar row={row} />
*/
export const BookmarkStar: FC<Props> = ({ row }) => {
const dimensions = useRecoilValue(dimensionsSelector);
const colorPallete = useRecoilValue(bookmarkedColorPalette);
const nextColor = useRecoilValue(nextColorSelector);
const bookmarks = useRecoilValue(bookmarkSelector);
const { actions } = useContext(ProvenanceContext);

const [hovered, setHovered] = useState(false);
const bookmarked = useMemo(() => bookmarks.find((b) => b.id === row.id), [
JakeWags marked this conversation as resolved.
Show resolved Hide resolved
bookmarks,
row.id,
]);

const color = useMemo(() => (bookmarked ? colorPallete[row.id] : nextColor), [
colorPallete,
row.id,
bookmarked,
nextColor,
]);

/**
* Calculates the opacity value based on the bookmark and hover states.
*
* @returns {number} The opacity value which can be one of the following:
* - `BOOKMARKED_OPACITY` if the item is bookmarked.
* - `HOVERED_OPACITY` if the item is hovered.
* - `BASE_OPACITY` if neither condition is met.
*
* @param {boolean} bookmarked - Indicates if the item is bookmarked.
* @param {boolean} hovered - Indicates if the item is hovered.
*/
const opacity = useMemo(() => {
if (bookmarked) {
return BOOKMARKED_OPACITY;
}

if (hovered) {
return HOVERED_OPACITY;
}

return BASE_OPACITY;
}, [bookmarked, hovered]);

const handleMouseEnter = () => {
setHovered(true);
};

const handleMouseLeave = () => {
setHovered(false);
};

/**
* Handles the click event on the bookmark star icon.
* Stops the event from propagating and adds a bookmark with the specified details.
*
* @param event - The mouse event triggered by clicking the bookmark star icon.
*/
const handleClick = (event: MouseEvent<SVGGElement, MouseEvent>) => {
event.stopPropagation();
if (bookmarked) {
actions.removeBookmark(bookmarked);
return;
JakeWags marked this conversation as resolved.
Show resolved Hide resolved
}
actions.addBookmark({
id: row.id,
label: row.elementName,
size: row.size,
type: 'intersection',
});
};

return (
<g
transform={translate(
dimensions.matrixColumn.width +
dimensions.bookmarkStar.gap,
dimensions.matrixColumn.width + dimensions.bookmarkStar.gap,
0,
)}
height={dimensions.body.rowHeight}
width={dimensions.set.width}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
onClick={(e: any) => handleClick(e)}
>
<StarIcon height={dimensions.body.rowHeight} width={dimensions.set.width} fontSize={'1em' as any} sx={{ color: colorPallete[row.id] }} />
<StarIcon
height={dimensions.body.rowHeight}
width={dimensions.set.width}
fontSize={'1em' as any}
sx={{
color,
fillOpacity: opacity,
}}
/>
</g>
);
};
4 changes: 1 addition & 3 deletions packages/upset/src/components/Rows/AggregateRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ export const AggregateRow: FC<Props> = ({ aggregateRow }) => {
const visibleSets = useRecoilValue(visibleSetSelector);
const dimensions = useRecoilValue(dimensionsSelector);
const currentIntersection = useRecoilValue(currentIntersectionSelector);
const bookmarks = useRecoilValue(bookmarkSelector);
const collapsedIds = useRecoilValue(collapsedSelector);
const { actions } = useContext(ProvenanceContext);
const selected = useRecoilValue(aggregateSelectedCount(aggregateRow));
Expand Down Expand Up @@ -140,8 +139,7 @@ export const AggregateRow: FC<Props> = ({ aggregateRow }) => {
</g>
)}
<g transform={translate(0, (['Sets', 'Overlaps'].includes(aggregateRow.aggregateBy)) ? dimensions.body.rowHeight - 5 : 0)}>
{ bookmarks.find((b) => b.id === aggregateRow.id) &&
<BookmarkStar row={aggregateRow} />}
<BookmarkStar row={aggregateRow} />
<SizeBar
row={aggregateRow}
size={aggregateRow.size}
Expand Down
3 changes: 1 addition & 2 deletions packages/upset/src/components/Rows/SubsetRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,7 @@ export const SubsetRow: FC<Props> = ({ subset }) => {
fillOpacity="0.0"
/>
<Matrix sets={visibleSets} subset={subset} />
{bookmarks.find((b) => b.id === subset.id) &&
<BookmarkStar row={subset} />}
<BookmarkStar row={subset} />
<SizeBar size={subset.size} row={subset} selected={selected} />
<AttributeBars attributes={subset.attributes} row={subset} />
</g>
Expand Down
Loading