/** * @upsetjs/react * https://github.com/upsetjs/upsetjs * * Copyright (c) 2021 Samuel Gratzl */ import type { ISetLike, UpSetQueries } from '@upsetjs/model'; import React, { PropsWithChildren } from 'react'; import { clsx } from '../../utils'; import type { VennDiagramStyleInfo } from '../derive/deriveVennStyleDependent'; import type { ITextArcSlice } from '../layout/interfaces'; import type { UpSetSelection } from '../../components/interfaces'; import type { VennDiagramDataInfo } from '../derive/deriveVennDataDependent'; import { generateArcSlicePath } from '../layout/generate'; import type { VennDiagramSizeInfo } from '../derive/deriveVennSizeDependent'; import { mergeColor } from '../../components/utils'; import SelectionPattern from './SelectionPattern'; function sliceRotate(slice: ITextArcSlice, center: { cx: number; cy: number }) { if (slice.text.x === center.cx) { return 0; } if (slice.text.x > center.cx) { return slice.text.y <= center.cy ? 60 : -60; } return slice.text.y <= center.cy ? -60 : 60; } function generateTitle( d: ISetLike, s: number, sName: string | undefined, secondary: boolean, qs: number[], queries: UpSetQueries, data: VennDiagramDataInfo, cx: number ) { const dc = data.format(d.cardinality); const baseName = !sName ? d.name : `${d.name} ∩ ${sName}`; const baseCardinality = !sName ? dc : `${data.format(s)}/${dc}`; if (qs.length === 0) { return { tooltip: `${baseName}: ${baseCardinality}`, title: d.type === 'set' ? ( <> {d.name} {baseCardinality} ) : ( baseCardinality ), }; } if (qs.length === 1 && !secondary && !sName) { return { tooltip: `${d.name} ∩ ${queries[0].name}: ${data.format(qs[0])}/${dc}`, title: d.type === 'set' ? ( <> {d.name} {`${data.format(qs[0])}/${dc}`} ) : ( `${data.format(qs[0])}/${dc}` ), }; } const queryLine = ( {queries.map((q, i) => ( {'⬤'} {` ${data.format(qs[i])}/${dc}${i < queries.length - 1 ? ' ' : ''}`} ))} ); return { tooltip: `${baseName}: ${baseCardinality}\n${queries .map((q, i) => `${d.name} ∩ ${q.name}: ${data.format(qs[i])}/${dc}`) .join('\n')}`, title: d.type === 'set' ? ( <> {d.name} {baseCardinality} {queryLine} ) : ( <> {baseCardinality} {queryLine} ), }; } export default function VennArcSliceSelection({ slice, d, i, data, style, elemOverlap, selected, selectionName, h, queries, size, fill, qs, }: PropsWithChildren<{ slice: ITextArcSlice; i: number; d: ISetLike; selected: boolean; elemOverlap: null | ((s: ISetLike) => number); selectionName?: string; style: VennDiagramStyleInfo; data: VennDiagramDataInfo; size: VennDiagramSizeInfo; fill: boolean; queries: UpSetQueries; qs: readonly ((s: ISetLike) => number)[]; h: UpSetSelection; }>) { const p = generateArcSlicePath(slice, data.sets.d); const rotate = sliceRotate(slice, size.area); const o = elemOverlap ? elemOverlap(d) : 0; const fillFullSelection = (o === d.cardinality && d.cardinality > 0) || selected; const className = clsx( `arc-${style.id}`, o === 0 && !selected && `${fill ? 'fillPrimary' : 'arcP'}-${style.id}`, fillFullSelection && `fillSelection-${style.id}`, style.classNames.set ); const id = `upset-${style.id}-${i}`; const secondary = elemOverlap != null || h.onMouseLeave != null; const qsOverlaps = qs.map((q) => q(d)); const { title, tooltip } = generateTitle(d, o, selectionName, secondary, qsOverlaps, queries, data, slice.text.x); return ( 0 && o < d.cardinality ? `url(#${id})` : !fillFullSelection || !style.selectionColor ? d.color : undefined )} > {style.tooltips && {tooltip}} {title} ); }