@reltio/design 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/charts/SetOverlapChart/SetOverlapChart.d.ts +33 -0
- package/charts/SetOverlapChart/SetOverlapChart.js +79 -0
- package/charts/SetOverlapChart/SetOverlapChart.module.css.d.ts +2 -0
- package/charts/SetOverlapChart/SetOverlapChart.module.css.js +36 -0
- package/charts/SetOverlapChart/SetOverlapChart.module.css.json +1 -0
- package/charts/SetOverlapChart/SetOverlapChart.types.d.ts +117 -0
- package/charts/SetOverlapChart/SetOverlapChart.types.js +1 -0
- package/charts/SetOverlapChart/components/IntersectionsChart/IntersectionsChart.d.ts +2 -0
- package/charts/SetOverlapChart/components/IntersectionsChart/IntersectionsChart.js +36 -0
- package/charts/SetOverlapChart/components/IntersectionsChart/IntersectionsChart.module.css.d.ts +2 -0
- package/charts/SetOverlapChart/components/IntersectionsChart/IntersectionsChart.module.css.js +40 -0
- package/charts/SetOverlapChart/components/IntersectionsChart/IntersectionsChart.module.css.json +1 -0
- package/charts/SetOverlapChart/components/IntersectionsChart/IntersectionsChart.types.d.ts +15 -0
- package/charts/SetOverlapChart/components/IntersectionsChart/IntersectionsChart.types.js +1 -0
- package/charts/SetOverlapChart/components/IntersectionsChart/helpers.d.ts +4 -0
- package/charts/SetOverlapChart/components/IntersectionsChart/helpers.js +4 -0
- package/charts/SetOverlapChart/components/IntersectionsChart/index.d.ts +1 -0
- package/charts/SetOverlapChart/components/IntersectionsChart/index.js +1 -0
- package/charts/SetOverlapChart/components/IntersectionsChartAxis/IntersectionsChartAxis.d.ts +2 -0
- package/charts/SetOverlapChart/components/IntersectionsChartAxis/IntersectionsChartAxis.js +30 -0
- package/charts/SetOverlapChart/components/IntersectionsChartAxis/IntersectionsChartAxis.module.css.d.ts +2 -0
- package/charts/SetOverlapChart/components/IntersectionsChartAxis/IntersectionsChartAxis.module.css.js +36 -0
- package/charts/SetOverlapChart/components/IntersectionsChartAxis/IntersectionsChartAxis.module.css.json +1 -0
- package/charts/SetOverlapChart/components/IntersectionsChartAxis/IntersectionsChartAxis.types.d.ts +8 -0
- package/charts/SetOverlapChart/components/IntersectionsChartAxis/IntersectionsChartAxis.types.js +1 -0
- package/charts/SetOverlapChart/components/IntersectionsChartAxis/helpers.d.ts +1 -0
- package/charts/SetOverlapChart/components/IntersectionsChartAxis/helpers.js +3 -0
- package/charts/SetOverlapChart/components/IntersectionsChartAxis/index.d.ts +1 -0
- package/charts/SetOverlapChart/components/IntersectionsChartAxis/index.js +1 -0
- package/charts/SetOverlapChart/components/IntersectionsMatrix/IntersectionsMatrix.d.ts +2 -0
- package/charts/SetOverlapChart/components/IntersectionsMatrix/IntersectionsMatrix.js +13 -0
- package/charts/SetOverlapChart/components/IntersectionsMatrix/IntersectionsMatrix.module.css.d.ts +2 -0
- package/charts/SetOverlapChart/components/IntersectionsMatrix/IntersectionsMatrix.module.css.js +29 -0
- package/charts/SetOverlapChart/components/IntersectionsMatrix/IntersectionsMatrix.module.css.json +1 -0
- package/charts/SetOverlapChart/components/IntersectionsMatrix/IntersectionsMatrix.types.d.ts +22 -0
- package/charts/SetOverlapChart/components/IntersectionsMatrix/IntersectionsMatrix.types.js +1 -0
- package/charts/SetOverlapChart/components/IntersectionsMatrix/helpers.d.ts +7 -0
- package/charts/SetOverlapChart/components/IntersectionsMatrix/helpers.js +25 -0
- package/charts/SetOverlapChart/components/IntersectionsMatrix/index.d.ts +1 -0
- package/charts/SetOverlapChart/components/IntersectionsMatrix/index.js +1 -0
- package/charts/SetOverlapChart/components/SetsChart/SetsChart.d.ts +2 -0
- package/charts/SetOverlapChart/components/SetsChart/SetsChart.js +54 -0
- package/charts/SetOverlapChart/components/SetsChart/SetsChart.module.css.d.ts +2 -0
- package/charts/SetOverlapChart/components/SetsChart/SetsChart.module.css.js +55 -0
- package/charts/SetOverlapChart/components/SetsChart/SetsChart.module.css.json +1 -0
- package/charts/SetOverlapChart/components/SetsChart/SetsChart.types.d.ts +16 -0
- package/charts/SetOverlapChart/components/SetsChart/SetsChart.types.js +1 -0
- package/charts/SetOverlapChart/components/SetsChart/index.d.ts +1 -0
- package/charts/SetOverlapChart/components/SetsChart/index.js +1 -0
- package/charts/SetOverlapChart/components/SetsChartAxis/SetsChartAxis.d.ts +2 -0
- package/charts/SetOverlapChart/components/SetsChartAxis/SetsChartAxis.js +9 -0
- package/charts/SetOverlapChart/components/SetsChartAxis/SetsChartAxis.module.css.d.ts +2 -0
- package/charts/SetOverlapChart/components/SetsChartAxis/SetsChartAxis.module.css.js +24 -0
- package/charts/SetOverlapChart/components/SetsChartAxis/SetsChartAxis.module.css.json +1 -0
- package/charts/SetOverlapChart/components/SetsChartAxis/SetsChartAxis.types.d.ts +4 -0
- package/charts/SetOverlapChart/components/SetsChartAxis/SetsChartAxis.types.js +1 -0
- package/charts/SetOverlapChart/components/SetsChartAxis/index.d.ts +1 -0
- package/charts/SetOverlapChart/components/SetsChartAxis/index.js +1 -0
- package/charts/SetOverlapChart/constants.d.ts +14 -0
- package/charts/SetOverlapChart/constants.js +16 -0
- package/charts/SetOverlapChart/helpers.d.ts +15 -0
- package/charts/SetOverlapChart/helpers.js +52 -0
- package/charts/SetOverlapChart/index.d.ts +2 -0
- package/charts/SetOverlapChart/index.js +1 -0
- package/charts/SetOverlapChart/useSetOverlapChartSizes.d.ts +19 -0
- package/charts/SetOverlapChart/useSetOverlapChartSizes.js +35 -0
- package/charts/index.d.ts +1 -0
- package/charts/index.js +1 -0
- package/components/AppSelector/AppSelector.d.ts +5 -0
- package/components/AppSelector/AppSelector.js +20 -0
- package/components/AppSelector/AppSelector.module.css.d.ts +2 -0
- package/components/AppSelector/AppSelector.module.css.js +61 -0
- package/components/AppSelector/AppSelector.module.css.json +1 -0
- package/components/AppSelector/AppSelector.types.d.ts +29 -0
- package/components/AppSelector/AppSelector.types.js +1 -0
- package/components/AppSelector/index.d.ts +2 -0
- package/components/AppSelector/index.js +1 -0
- package/components/Button/Button.d.ts +3 -0
- package/components/Button/Button.js +9 -3
- package/components/Button/Button.module.css.js +9 -3
- package/components/Button/Button.module.css.json +1 -1
- package/components/Divider/Divider.d.ts +2 -0
- package/components/Divider/Divider.js +6 -0
- package/components/Divider/Divider.module.css.d.ts +2 -0
- package/components/Divider/Divider.module.css.js +40 -0
- package/components/Divider/Divider.module.css.json +1 -0
- package/components/Divider/Divider.types.d.ts +11 -0
- package/components/Divider/Divider.types.js +1 -0
- package/components/Divider/index.d.ts +2 -0
- package/components/Divider/index.js +1 -0
- package/components/Popover/Popover.d.ts +36 -0
- package/components/Popover/Popover.js +62 -0
- package/components/Popover/Popover.module.css.d.ts +2 -0
- package/components/Popover/Popover.module.css.js +62 -0
- package/components/Popover/Popover.module.css.json +1 -0
- package/components/Popover/Popover.types.d.ts +18 -0
- package/components/Popover/Popover.types.js +1 -0
- package/components/Popover/index.d.ts +2 -0
- package/components/Popover/index.js +1 -0
- package/components/index.d.ts +1 -0
- package/components/index.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { SetOverlapChartProps } from "./SetOverlapChart.types";
|
|
2
|
+
/**
|
|
3
|
+
* UpSet-style set overlap chart built with custom SVG and d3-scale.
|
|
4
|
+
*
|
|
5
|
+
* Visualizes how records overlap across multiple sets (source systems)
|
|
6
|
+
* using three coordinated sub-charts: vertical intersection bars at the
|
|
7
|
+
* top, a dot matrix in the middle, and horizontal set bars on the left.
|
|
8
|
+
*
|
|
9
|
+
* Supports two combination modes — `"intersection"` (overlapping counts,
|
|
10
|
+
* element-level hover) and `"distinctIntersection"` (mutually exclusive
|
|
11
|
+
* counts, structural hover). Each mode requires differently shaped data
|
|
12
|
+
* despite sharing the same TypeScript types.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* const sets = [
|
|
17
|
+
* { name: "SAP", size: 6, elements: ["r1", "r2"] },
|
|
18
|
+
* { name: "Oracle", size: 4, elements: ["r1", "r3"] },
|
|
19
|
+
* ];
|
|
20
|
+
* const intersections = [
|
|
21
|
+
* { sets: ["SAP"], size: 6, elements: ["r1", "r2"] },
|
|
22
|
+
* { sets: ["SAP", "Oracle"], size: 2, elements: ["r1"] },
|
|
23
|
+
* ];
|
|
24
|
+
*
|
|
25
|
+
* <SetOverlapChart
|
|
26
|
+
* sets={sets}
|
|
27
|
+
* intersections={intersections}
|
|
28
|
+
* mode="intersection"
|
|
29
|
+
* style={{ width: 900, height: 500 }}
|
|
30
|
+
* />
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare const SetOverlapChart: ({ intersections, sets, mode, intersectionChartAxisLabel, setsChartAxisLabel, loading, error, className, style, ...rest }: SetOverlapChartProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { classNames } from "../../utils/classNames";
|
|
4
|
+
import { IntersectionsChart } from "./components/IntersectionsChart";
|
|
5
|
+
import { IntersectionsChartAxis } from "./components/IntersectionsChartAxis";
|
|
6
|
+
import { IntersectionsMatrix } from "./components/IntersectionsMatrix";
|
|
7
|
+
import { SetsChart } from "./components/SetsChart";
|
|
8
|
+
import { GAP_BEFORE_LEFT_BAR, TOP_GAP } from "./constants";
|
|
9
|
+
import { createEmptyGridXScale, createEmptyGridYScale, createMatrixXScale, createMatrixYScale, getEmptyGridDimensions, } from "./helpers";
|
|
10
|
+
import styles from "./SetOverlapChart.module.css";
|
|
11
|
+
import { useSetOverlapChartSizes } from "./useSetOverlapChartSizes";
|
|
12
|
+
/**
|
|
13
|
+
* UpSet-style set overlap chart built with custom SVG and d3-scale.
|
|
14
|
+
*
|
|
15
|
+
* Visualizes how records overlap across multiple sets (source systems)
|
|
16
|
+
* using three coordinated sub-charts: vertical intersection bars at the
|
|
17
|
+
* top, a dot matrix in the middle, and horizontal set bars on the left.
|
|
18
|
+
*
|
|
19
|
+
* Supports two combination modes — `"intersection"` (overlapping counts,
|
|
20
|
+
* element-level hover) and `"distinctIntersection"` (mutually exclusive
|
|
21
|
+
* counts, structural hover). Each mode requires differently shaped data
|
|
22
|
+
* despite sharing the same TypeScript types.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```tsx
|
|
26
|
+
* const sets = [
|
|
27
|
+
* { name: "SAP", size: 6, elements: ["r1", "r2"] },
|
|
28
|
+
* { name: "Oracle", size: 4, elements: ["r1", "r3"] },
|
|
29
|
+
* ];
|
|
30
|
+
* const intersections = [
|
|
31
|
+
* { sets: ["SAP"], size: 6, elements: ["r1", "r2"] },
|
|
32
|
+
* { sets: ["SAP", "Oracle"], size: 2, elements: ["r1"] },
|
|
33
|
+
* ];
|
|
34
|
+
*
|
|
35
|
+
* <SetOverlapChart
|
|
36
|
+
* sets={sets}
|
|
37
|
+
* intersections={intersections}
|
|
38
|
+
* mode="intersection"
|
|
39
|
+
* style={{ width: 900, height: 500 }}
|
|
40
|
+
* />
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export const SetOverlapChart = ({ intersections = [], sets = [], mode = "intersection", intersectionChartAxisLabel, setsChartAxisLabel, loading = false, error, className, style, ...rest }) => {
|
|
44
|
+
const containerRef = useRef(null);
|
|
45
|
+
const [hoveredSet, setHoveredSet] = useState(null);
|
|
46
|
+
const [hoveredIntersection, setHoveredIntersection] = useState(null);
|
|
47
|
+
const [[availableWidth, availableHeight], setAvailableSize] = useState([
|
|
48
|
+
0, 0,
|
|
49
|
+
]);
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
const container = containerRef.current;
|
|
52
|
+
if (!container)
|
|
53
|
+
return;
|
|
54
|
+
const updateSize = () => {
|
|
55
|
+
setAvailableSize([container.clientWidth, container.clientHeight]);
|
|
56
|
+
};
|
|
57
|
+
updateSize();
|
|
58
|
+
const resizeObserver = new ResizeObserver(updateSize);
|
|
59
|
+
resizeObserver.observe(container);
|
|
60
|
+
return () => resizeObserver.disconnect();
|
|
61
|
+
}, []);
|
|
62
|
+
const hasValidData = intersections.length > 0 && sets.length > 0 && !error;
|
|
63
|
+
const { labels, svgWidth, svgHeight, intersectionsChartWidth, intersectionsChartHeight, matrixY, matrixHeight, setsChartWidth, leftPadding, } = useSetOverlapChartSizes({
|
|
64
|
+
width: availableWidth,
|
|
65
|
+
height: availableHeight,
|
|
66
|
+
intersections,
|
|
67
|
+
sets,
|
|
68
|
+
});
|
|
69
|
+
const matrixXScale = useMemo(() => createMatrixXScale(intersections.length, intersectionsChartWidth), [intersections.length, intersectionsChartWidth]);
|
|
70
|
+
const matrixYScale = useMemo(() => createMatrixYScale(sets.map((set) => set.name), matrixHeight), [sets, matrixHeight]);
|
|
71
|
+
const emptyGrid = getEmptyGridDimensions(availableWidth, availableHeight);
|
|
72
|
+
const emptyXScale = useMemo(() => createEmptyGridXScale(emptyGrid.width), [emptyGrid.width]);
|
|
73
|
+
const emptyYScale = useMemo(() => createEmptyGridYScale(emptyGrid.height), [emptyGrid.height]);
|
|
74
|
+
const chartDescription = hasValidData
|
|
75
|
+
? `Set overlap chart showing overlaps across ${sets.length} sets and ${intersections.length} intersections`
|
|
76
|
+
: "Empty set overlap chart";
|
|
77
|
+
const overlay = useMemo(() => error ? (_jsx("div", { className: classNames(styles.overlay, styles.errorOverlay), children: error })) : loading ? (_jsx("div", { className: classNames(styles.overlay, styles.loadingOverlay), children: "Loading" })) : !hasValidData ? (_jsx("div", { className: classNames(styles.overlay), children: "No data" })) : null, [error, loading, hasValidData]);
|
|
78
|
+
return (_jsxs("div", { ref: containerRef, className: classNames(styles.root, className), style: style, ...rest, children: [overlay, hasValidData ? (_jsxs("svg", { className: classNames(styles.svg), width: svgWidth, height: svgHeight, role: "img", "aria-label": chartDescription, children: [_jsx(IntersectionsMatrix, { xScale: matrixXScale, yScale: matrixYScale, intersections: intersections, sets: sets, transform: `translate(${leftPadding},${matrixY})`, hoveredIntersection: hoveredIntersection }), _jsx(SetsChart, { mode: mode, yScale: matrixYScale, width: setsChartWidth, sets: sets, labels: labels, intersections: intersections, hoveredSet: hoveredSet, hoveredIntersection: hoveredIntersection, hoverAreaWidth: svgWidth, transform: `translate(${GAP_BEFORE_LEFT_BAR},${matrixY})`, onSetHover: setHoveredSet, axisLabel: setsChartAxisLabel }), _jsx(IntersectionsChart, { mode: mode, xScale: matrixXScale, width: intersectionsChartWidth, height: intersectionsChartHeight, intersections: intersections, transform: `translate(${leftPadding},${TOP_GAP})`, matrixHeight: matrixHeight, hoveredSet: hoveredSet, hoveredIntersection: hoveredIntersection, onIntersectionHover: setHoveredIntersection, axisLabel: intersectionChartAxisLabel })] })) : (_jsx("svg", { className: classNames(styles.svg), width: availableWidth, height: availableHeight, role: "img", "aria-label": chartDescription, children: _jsx("g", { transform: `translate(${emptyGrid.left},${TOP_GAP})`, children: _jsx(IntersectionsChartAxis, { xScale: emptyXScale, yScale: emptyYScale, width: emptyGrid.width, height: emptyGrid.height }) }) }))] }));
|
|
79
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import styleInject from 'style-inject';
|
|
2
|
+
import json from './SetOverlapChart.module.css.json';
|
|
3
|
+
styleInject(`
|
|
4
|
+
.SetOverlapChart_root__L29wd {
|
|
5
|
+
position: relative;
|
|
6
|
+
display: flex;
|
|
7
|
+
overflow: auto;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.SetOverlapChart_svg__L29wd {
|
|
11
|
+
flex-shrink: 0;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.SetOverlapChart_overlay__L29wd {
|
|
15
|
+
position: absolute;
|
|
16
|
+
inset: 0;
|
|
17
|
+
display: flex;
|
|
18
|
+
align-items: center;
|
|
19
|
+
justify-content: center;
|
|
20
|
+
color: var(--reltio-color-text-secondary);
|
|
21
|
+
font-size: 14px;
|
|
22
|
+
z-index: 1;
|
|
23
|
+
pointer-events: none;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.SetOverlapChart_errorOverlay__L29wd {
|
|
27
|
+
color: var(--reltio-color-error-font);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.SetOverlapChart_loadingOverlay__L29wd {
|
|
31
|
+
background-color: var(--reltio-color-bg-white);
|
|
32
|
+
opacity: 0.7;
|
|
33
|
+
pointer-events: all;
|
|
34
|
+
}
|
|
35
|
+
`);
|
|
36
|
+
export default json;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "root": "SetOverlapChart_root__L29wd", "svg": "SetOverlapChart_svg__L29wd", "overlay": "SetOverlapChart_overlay__L29wd", "errorOverlay": "SetOverlapChart_errorOverlay__L29wd", "loadingOverlay": "SetOverlapChart_loadingOverlay__L29wd" }
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { HtmlProps } from "../../utils/types";
|
|
2
|
+
/**
|
|
3
|
+
* Props for the SetOverlapChart component.
|
|
4
|
+
*
|
|
5
|
+
* Renders an UpSet-style set overlap visualization as a custom SVG
|
|
6
|
+
* (no ECharts). The chart is composed of three coordinated sub-charts:
|
|
7
|
+
* vertical intersection bars, a dot matrix, and horizontal set bars.
|
|
8
|
+
*
|
|
9
|
+
* The two supported modes (`"intersection"` and `"distinctIntersection"`)
|
|
10
|
+
* require **different data shapes** despite sharing the same TypeScript
|
|
11
|
+
* types — see `SetOverlapChartMode` and individual prop docs for details.
|
|
12
|
+
*/
|
|
13
|
+
export type SetOverlapChartProps = HtmlProps<"div", {
|
|
14
|
+
/**
|
|
15
|
+
* Intersection columns to visualize. Each entry becomes a vertical bar
|
|
16
|
+
* in the intersection chart and a column of dots in the matrix.
|
|
17
|
+
*
|
|
18
|
+
* **Data requirements vary by mode:**
|
|
19
|
+
* - `"intersection"` — `elements` must be populated with record identifiers
|
|
20
|
+
* - `"distinctIntersection"` — `elements` should be empty (`[]`)
|
|
21
|
+
*/
|
|
22
|
+
intersections: Intersection[];
|
|
23
|
+
/**
|
|
24
|
+
* Sets to be visualized. Each entry becomes a horizontal
|
|
25
|
+
* bar in the set chart and a row of dots in the matrix.
|
|
26
|
+
*
|
|
27
|
+
* **Data requirements vary by mode:**
|
|
28
|
+
* - `"intersection"` — `elements` must be populated with record identifiers
|
|
29
|
+
* - `"distinctIntersection"` — `elements` should be empty (`[]`)
|
|
30
|
+
*/
|
|
31
|
+
sets: DataSet[];
|
|
32
|
+
/**
|
|
33
|
+
* Combination semantics for intersection columns.
|
|
34
|
+
* Controls how hover highlighting is computed and what data fields are used.
|
|
35
|
+
*
|
|
36
|
+
* - `"intersection"` — overlapping counts; hover uses element-level filtering
|
|
37
|
+
* - `"distinctIntersection"` — mutually exclusive counts; hover uses structural matching
|
|
38
|
+
*
|
|
39
|
+
* The two modes are **not interchangeable** with the same data.
|
|
40
|
+
* @default "intersection"
|
|
41
|
+
*/
|
|
42
|
+
mode?: SetOverlapChartMode;
|
|
43
|
+
/**
|
|
44
|
+
* Label text rendered alongside the intersection chart Y-axis.
|
|
45
|
+
* Examples: `"Profile count"`, `"Record count"`
|
|
46
|
+
*/
|
|
47
|
+
intersectionChartAxisLabel?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Label text rendered alongside the set chart X-axis.
|
|
50
|
+
* Examples: `"Set Size"`, `"Source system"`
|
|
51
|
+
*/
|
|
52
|
+
setsChartAxisLabel?: string;
|
|
53
|
+
/**
|
|
54
|
+
* Shows a loading overlay on the chart.
|
|
55
|
+
* - When data is empty: loading overlay on blank area
|
|
56
|
+
* - When data is present: loading overlay on rendered chart
|
|
57
|
+
* @default false
|
|
58
|
+
*/
|
|
59
|
+
loading?: boolean;
|
|
60
|
+
/** Error message. When set, replaces the chart with centered error text. */
|
|
61
|
+
error?: string;
|
|
62
|
+
}>;
|
|
63
|
+
/**
|
|
64
|
+
* A single intersection column in the chart plot.
|
|
65
|
+
*
|
|
66
|
+
* Represents a combination of one or more sets. In `INTERSECTION` mode
|
|
67
|
+
* the `elements` array drives hover highlighting; in `DISTINCT_INTERSECTION`
|
|
68
|
+
* mode only `sets` property is used. Identity is derived from array position.
|
|
69
|
+
*/
|
|
70
|
+
export type Intersection = {
|
|
71
|
+
/** Names of the sets that participate in this intersection (e.g. `["SAP", "Oracle"]`). */
|
|
72
|
+
sets: string[];
|
|
73
|
+
/** Number of records in this intersection. Determines bar height. */
|
|
74
|
+
size: number;
|
|
75
|
+
/**
|
|
76
|
+
* Record identifiers belonging to this intersection.
|
|
77
|
+
* - **`"intersection"` mode** — must be populated; used for element-level hover filtering
|
|
78
|
+
* - **`"distinctIntersection"` mode** — should be `[]`; hover uses structural matching instead
|
|
79
|
+
*/
|
|
80
|
+
elements: string[];
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* A single set in the chart plot.
|
|
84
|
+
*
|
|
85
|
+
* Each set appears as a row in the matrix and a horizontal bar in the
|
|
86
|
+
* set chart. The `name` must match the strings used in `Intersection.sets`.
|
|
87
|
+
*/
|
|
88
|
+
export type DataSet = {
|
|
89
|
+
/** Display name and join key — must match values in `Intersection.sets`. */
|
|
90
|
+
name: string;
|
|
91
|
+
/** Total number of records in this set. Determines bar width. */
|
|
92
|
+
size: number;
|
|
93
|
+
/**
|
|
94
|
+
* Record identifiers belonging to this set.
|
|
95
|
+
* - **`"intersection"` mode** — must be populated; used for element-level hover filtering
|
|
96
|
+
* - **`"distinctIntersection"` mode** — should be `[]`; hover uses structural matching instead
|
|
97
|
+
*/
|
|
98
|
+
elements: string[];
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Combination semantics for the UpSet plot.
|
|
102
|
+
*
|
|
103
|
+
* Given a combination mask over sets [A, B, C] that selects A and B:
|
|
104
|
+
*
|
|
105
|
+
* - **`"intersection"`** — Inclusive intersection semantics.
|
|
106
|
+
* `⋂ selectedSets` — counts elements in A ∩ B, regardless of C.
|
|
107
|
+
* An element in A ∩ B ∩ C is counted in the A+B column AND the A+B+C
|
|
108
|
+
* column (overlapping). Hover computation is O(E_i × E_h) per bar —
|
|
109
|
+
* may cause lag with large data.
|
|
110
|
+
*
|
|
111
|
+
* - **`"distinctIntersection"`** — Exact-region semantics.
|
|
112
|
+
* `(⋂ selectedSets) \ (⋃ nonSelectedSets)` — counts elements in
|
|
113
|
+
* A ∩ B ∩ ¬C. An element is counted only in the column that matches
|
|
114
|
+
* its precise set membership. Hover computation is O(S) per bar,
|
|
115
|
+
* independent of dataset size.
|
|
116
|
+
*/
|
|
117
|
+
export type SetOverlapChartMode = "distinctIntersection" | "intersection";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import type { IntersectionsChartProps } from "./IntersectionsChart.types";
|
|
2
|
+
export declare const IntersectionsChart: ({ mode, xScale, width, height, transform, matrixHeight, intersections, hoveredSet, hoveredIntersection, axisLabel, onIntersectionHover, }: IntersectionsChartProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { scaleLinear } from "d3-scale";
|
|
3
|
+
import { useMemo } from "react";
|
|
4
|
+
import { classNames } from "../../../../utils/classNames";
|
|
5
|
+
import { GAP_BETWEEN_TABLE_AND_CHART } from "../../constants";
|
|
6
|
+
import { IntersectionsChartAxis } from "../IntersectionsChartAxis";
|
|
7
|
+
import { getBarHeight, getBarY } from "./helpers";
|
|
8
|
+
import styles from "./IntersectionsChart.module.css";
|
|
9
|
+
export const IntersectionsChart = ({ mode, xScale, width, height, transform, matrixHeight, intersections, hoveredSet, hoveredIntersection, axisLabel, onIntersectionHover, }) => {
|
|
10
|
+
const yScale = useMemo(() => {
|
|
11
|
+
const intersectionsSizes = intersections.map((intersection) => intersection.size);
|
|
12
|
+
const domain = [0, Math.max(0, ...intersectionsSizes)];
|
|
13
|
+
return scaleLinear([height, 0]).domain(domain);
|
|
14
|
+
}, [intersections, height]);
|
|
15
|
+
const hoveredElementsSet = useMemo(() => {
|
|
16
|
+
const elements = hoveredIntersection?.elements || hoveredSet?.elements;
|
|
17
|
+
return elements ? new Set(elements) : null;
|
|
18
|
+
}, [hoveredIntersection, hoveredSet]);
|
|
19
|
+
return (_jsxs("g", { transform: transform, role: "listbox", "aria-label": "Intersection bars", children: [_jsx(IntersectionsChartAxis, { xScale: xScale, yScale: yScale, width: width, height: height, axisLabel: axisLabel }), intersections.map((intersection, i) => {
|
|
20
|
+
const { elements, size } = intersection;
|
|
21
|
+
const isHoveredColumn = hoveredIntersection === intersection;
|
|
22
|
+
const x = xScale(String(i)) ?? 0;
|
|
23
|
+
const y = yScale(size);
|
|
24
|
+
const barWidth = xScale.bandwidth();
|
|
25
|
+
let barY = hoveredElementsSet
|
|
26
|
+
? yScale(elements.filter((el) => hoveredElementsSet.has(el)).length)
|
|
27
|
+
: y;
|
|
28
|
+
if (mode === "distinctIntersection" &&
|
|
29
|
+
hoveredSet &&
|
|
30
|
+
intersection.sets.includes(hoveredSet.name)) {
|
|
31
|
+
barY = y;
|
|
32
|
+
}
|
|
33
|
+
const hoverLabel = `Intersection: ${intersection.sets.join(" ∩ ")}, size: ${size}`;
|
|
34
|
+
return (_jsxs("g", { children: [_jsx("title", { children: intersection.sets.join(" ∩ ") }), _jsx("rect", { className: classNames(styles.barBackground), x: x, y: y, width: barWidth, height: height - y }), _jsx("text", { className: classNames(styles.barLabel), x: x + barWidth / 2, y: y - 4, children: size }), _jsx("rect", { className: classNames(styles.bar), x: x, y: getBarY(mode, barY, y, isHoveredColumn), width: barWidth, height: getBarHeight(mode, height, barY, y, isHoveredColumn) }), _jsx("rect", { className: classNames(styles.hoverBar), "data-reltio-id": "intersection-hover-area", role: "option", "aria-selected": isHoveredColumn, x: x, y: y, width: barWidth, height: height - y + matrixHeight + GAP_BETWEEN_TABLE_AND_CHART - 1, tabIndex: 0, "aria-label": hoverLabel, onMouseEnter: () => onIntersectionHover(intersection), onMouseLeave: () => onIntersectionHover(null), onFocus: () => onIntersectionHover(intersection), onBlur: () => onIntersectionHover(null) })] }, intersection.sets.join(",")));
|
|
35
|
+
})] }));
|
|
36
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import styleInject from 'style-inject';
|
|
2
|
+
import json from './IntersectionsChart.module.css.json';
|
|
3
|
+
styleInject(`
|
|
4
|
+
.IntersectionsChart_bar__L29wd {
|
|
5
|
+
fill: var(--reltio-color-brand-blue);
|
|
6
|
+
transition:
|
|
7
|
+
y ease-in-out 0.3s,
|
|
8
|
+
height ease-in-out 0.3s;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.IntersectionsChart_barBackground__L29wd {
|
|
12
|
+
fill: var(--reltio-color-brand-blue);
|
|
13
|
+
opacity: 0.2;
|
|
14
|
+
transition:
|
|
15
|
+
y ease-in-out 0.3s,
|
|
16
|
+
height ease-in-out 0.3s;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.IntersectionsChart_barLabel__L29wd {
|
|
20
|
+
fill: var(--reltio-color-text);
|
|
21
|
+
font-size: 12px;
|
|
22
|
+
text-anchor: middle;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.IntersectionsChart_hoverBar__L29wd {
|
|
26
|
+
fill: transparent;
|
|
27
|
+
stroke: var(--reltio-color-brand-blue);
|
|
28
|
+
stroke-width: 1px;
|
|
29
|
+
cursor: pointer;
|
|
30
|
+
transition: opacity ease-in-out 0.3s;
|
|
31
|
+
opacity: 0;
|
|
32
|
+
outline: none;
|
|
33
|
+
|
|
34
|
+
&:hover,
|
|
35
|
+
&:focus-visible {
|
|
36
|
+
opacity: 1;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
`);
|
|
40
|
+
export default json;
|
package/charts/SetOverlapChart/components/IntersectionsChart/IntersectionsChart.module.css.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "bar": "IntersectionsChart_bar__L29wd", "barBackground": "IntersectionsChart_barBackground__L29wd", "barLabel": "IntersectionsChart_barLabel__L29wd", "hoverBar": "IntersectionsChart_hoverBar__L29wd" }
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ScaleBand } from "d3-scale";
|
|
2
|
+
import type { DataSet, Intersection, SetOverlapChartMode } from "../../SetOverlapChart.types";
|
|
3
|
+
export type IntersectionsChartProps = {
|
|
4
|
+
mode: SetOverlapChartMode;
|
|
5
|
+
xScale: ScaleBand<string>;
|
|
6
|
+
width: number;
|
|
7
|
+
height: number;
|
|
8
|
+
transform?: string;
|
|
9
|
+
matrixHeight: number;
|
|
10
|
+
intersections: Intersection[];
|
|
11
|
+
hoveredSet?: DataSet | null;
|
|
12
|
+
hoveredIntersection?: Intersection | null;
|
|
13
|
+
axisLabel?: string;
|
|
14
|
+
onIntersectionHover: (intersection: Intersection | null) => void;
|
|
15
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { SetOverlapChartMode } from "../../SetOverlapChart.types";
|
|
2
|
+
declare const getBarHeight: (mode: SetOverlapChartMode, height: number, barY: number, y: number, isHoveredColumn: boolean) => number;
|
|
3
|
+
declare const getBarY: (mode: SetOverlapChartMode, barY: number, y: number, isHoveredColumn: boolean) => number;
|
|
4
|
+
export { getBarHeight, getBarY };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
const shouldShowFixedBar = (mode, isHoveredColumn) => mode === "distinctIntersection" && isHoveredColumn;
|
|
2
|
+
const getBarHeight = (mode, height, barY, y, isHoveredColumn) => height - (shouldShowFixedBar(mode, isHoveredColumn) ? y : barY);
|
|
3
|
+
const getBarY = (mode, barY, y, isHoveredColumn) => (shouldShowFixedBar(mode, isHoveredColumn) ? y : barY);
|
|
4
|
+
export { getBarHeight, getBarY };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { IntersectionsChart } from "./IntersectionsChart";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { IntersectionsChart } from "./IntersectionsChart";
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
import { classNames } from "../../../../utils/classNames";
|
|
4
|
+
import { AXIS_TICK_LABEL_SIZE } from "../../constants";
|
|
5
|
+
import { calculateTicksWidth } from "./helpers";
|
|
6
|
+
import styles from "./IntersectionsChartAxis.module.css";
|
|
7
|
+
export const IntersectionsChartAxis = ({ xScale, yScale, width, height, axisLabel, }) => {
|
|
8
|
+
const ticks = useMemo(() => {
|
|
9
|
+
const maxValue = yScale.domain()[1];
|
|
10
|
+
return yScale
|
|
11
|
+
.copy()
|
|
12
|
+
.nice()
|
|
13
|
+
.ticks()
|
|
14
|
+
.filter((tick) => Number.isInteger(tick) && tick <= maxValue);
|
|
15
|
+
}, [yScale]);
|
|
16
|
+
const ticksWidth = useMemo(() => calculateTicksWidth(ticks), [ticks]);
|
|
17
|
+
const rootStyle = {
|
|
18
|
+
"--axis-tick-label-size": AXIS_TICK_LABEL_SIZE,
|
|
19
|
+
};
|
|
20
|
+
return (_jsxs("g", { style: rootStyle, children: [xScale.domain().map((d) => {
|
|
21
|
+
const x = (xScale(d) ?? 0) + xScale.bandwidth() / 2;
|
|
22
|
+
return (_jsx("line", { className: classNames(styles.verticalGrid), x1: x, x2: x, y1: 0, y2: height }, `v-grid-${d}`));
|
|
23
|
+
}), ticks.map((tick) => {
|
|
24
|
+
const y = yScale(tick);
|
|
25
|
+
return (_jsx("line", { className: classNames(styles.horizontalGrid), x1: 0, x2: width, y1: y, y2: y }, `h-grid-${tick}`));
|
|
26
|
+
}), _jsx("g", { children: ticks.map((tick) => {
|
|
27
|
+
const y = yScale(tick);
|
|
28
|
+
return (_jsxs("g", { transform: `translate(0,${y})`, children: [_jsx("line", { className: classNames(styles.axis), x1: -4, x2: 0, y1: 0, y2: 0 }), _jsx("text", { className: classNames(styles.axisTickLabel), x: -8, y: 4, children: tick })] }, `x-tick-${tick}`));
|
|
29
|
+
}) }), _jsx("line", { className: classNames(styles.axis), x1: 0, x2: 0, y1: 0, y2: height }), _jsx("line", { className: classNames(styles.axis), x1: 0, x2: width, y1: height, y2: height }), axisLabel && (_jsx("text", { className: classNames(styles.axisLabel), transform: `translate(${-ticksWidth - 12},${height / 2}) rotate(-90)`, children: axisLabel }))] }));
|
|
30
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import styleInject from 'style-inject';
|
|
2
|
+
import json from './IntersectionsChartAxis.module.css.json';
|
|
3
|
+
styleInject(`
|
|
4
|
+
.IntersectionsChartAxis_axis__L29wd {
|
|
5
|
+
stroke: var(--reltio-color-border-black);
|
|
6
|
+
stroke-width: 1px;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.IntersectionsChartAxis_axisLabel__L29wd {
|
|
10
|
+
fill: var(--reltio-color-text);
|
|
11
|
+
opacity: 0.54;
|
|
12
|
+
font-size: 12px;
|
|
13
|
+
text-anchor: middle;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.IntersectionsChartAxis_axisTickLabel__L29wd {
|
|
17
|
+
font-size: var(--axis-tick-label-size);
|
|
18
|
+
fill: var(--reltio-color-text);
|
|
19
|
+
opacity: 0.54;
|
|
20
|
+
text-anchor: end;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.IntersectionsChartAxis_verticalGrid__L29wd {
|
|
24
|
+
stroke: var(--reltio-color-border-black);
|
|
25
|
+
opacity: 0.04;
|
|
26
|
+
stroke-width: 1px;
|
|
27
|
+
stroke-dasharray: 2;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.IntersectionsChartAxis_horizontalGrid__L29wd {
|
|
31
|
+
stroke: var(--reltio-color-border-black);
|
|
32
|
+
opacity: 0.04;
|
|
33
|
+
stroke-width: 1px;
|
|
34
|
+
}
|
|
35
|
+
`);
|
|
36
|
+
export default json;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "axis": "IntersectionsChartAxis_axis__L29wd", "axisLabel": "IntersectionsChartAxis_axisLabel__L29wd", "axisTickLabel": "IntersectionsChartAxis_axisTickLabel__L29wd", "verticalGrid": "IntersectionsChartAxis_verticalGrid__L29wd", "horizontalGrid": "IntersectionsChartAxis_horizontalGrid__L29wd" }
|
package/charts/SetOverlapChart/components/IntersectionsChartAxis/IntersectionsChartAxis.types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const calculateTicksWidth: (ticks: number[]) => number;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { IntersectionsChartAxis } from "./IntersectionsChartAxis";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { IntersectionsChartAxis } from "./IntersectionsChartAxis";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
import { classNames } from "../../../../utils/classNames";
|
|
4
|
+
import { getMatrixData } from "./helpers";
|
|
5
|
+
import styles from "./IntersectionsMatrix.module.css";
|
|
6
|
+
export const IntersectionsMatrix = ({ xScale, yScale, intersections, sets, transform, hoveredIntersection, }) => {
|
|
7
|
+
const { circles, lines } = useMemo(() => getMatrixData(xScale, yScale, intersections, sets), [xScale, yScale, intersections, sets]);
|
|
8
|
+
const hoveredIndex = hoveredIntersection
|
|
9
|
+
? intersections.indexOf(hoveredIntersection)
|
|
10
|
+
: -1;
|
|
11
|
+
const checkDimmed = (i) => hoveredIntersection && hoveredIndex !== i;
|
|
12
|
+
return (_jsxs("g", { transform: transform, children: [circles.map(({ cx, cy, isPresent, intersectionIndex }) => (_jsx("circle", { cx: cx, cy: cy, r: 8, className: classNames(styles.circle, isPresent && styles.activeCircle, isPresent && checkDimmed(intersectionIndex) && styles.dimmedCircle) }, `matrix-circle-${cx}-${cy}`))), lines.map(({ x, y1, y2, intersectionIndex }) => (_jsx("line", { x1: x, y1: y1, x2: x, y2: y2, className: classNames(styles.link, checkDimmed(intersectionIndex) && styles.dimmedLine) }, `matrix-line-${intersectionIndex}`)))] }));
|
|
13
|
+
};
|
package/charts/SetOverlapChart/components/IntersectionsMatrix/IntersectionsMatrix.module.css.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import styleInject from 'style-inject';
|
|
2
|
+
import json from './IntersectionsMatrix.module.css.json';
|
|
3
|
+
styleInject(`
|
|
4
|
+
.IntersectionsMatrix_circle__L29wd {
|
|
5
|
+
stroke-width: 2px;
|
|
6
|
+
fill: var(--reltio-color-surface-3);
|
|
7
|
+
stroke: var(--reltio-color-surface-3);
|
|
8
|
+
transition: fill ease-in-out 0.3s;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.IntersectionsMatrix_activeCircle__L29wd {
|
|
12
|
+
fill: var(--reltio-color-brand-blue);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.IntersectionsMatrix_dimmedCircle__L29wd {
|
|
16
|
+
fill: var(--reltio-color-border-3);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.IntersectionsMatrix_link__L29wd {
|
|
20
|
+
stroke: var(--reltio-color-brand-blue);
|
|
21
|
+
stroke-width: 2px;
|
|
22
|
+
transition: stroke ease-in-out 0.3s;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.IntersectionsMatrix_dimmedLine__L29wd {
|
|
26
|
+
stroke: var(--reltio-color-border-3);
|
|
27
|
+
}
|
|
28
|
+
`);
|
|
29
|
+
export default json;
|
package/charts/SetOverlapChart/components/IntersectionsMatrix/IntersectionsMatrix.module.css.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "circle": "IntersectionsMatrix_circle__L29wd", "activeCircle": "IntersectionsMatrix_activeCircle__L29wd", "dimmedCircle": "IntersectionsMatrix_dimmedCircle__L29wd", "link": "IntersectionsMatrix_link__L29wd", "dimmedLine": "IntersectionsMatrix_dimmedLine__L29wd" }
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ScaleBand } from "d3-scale";
|
|
2
|
+
import type { DataSet, Intersection } from "../../SetOverlapChart.types";
|
|
3
|
+
export type IntersectionsMatrixProps = {
|
|
4
|
+
xScale: ScaleBand<string>;
|
|
5
|
+
yScale: ScaleBand<string>;
|
|
6
|
+
intersections: Intersection[];
|
|
7
|
+
sets: DataSet[];
|
|
8
|
+
transform: string;
|
|
9
|
+
hoveredIntersection: Intersection | null;
|
|
10
|
+
};
|
|
11
|
+
export type IntersectionCircle = {
|
|
12
|
+
cx: number;
|
|
13
|
+
cy: number;
|
|
14
|
+
isPresent: boolean;
|
|
15
|
+
intersectionIndex: number;
|
|
16
|
+
};
|
|
17
|
+
export type IntersectionLine = {
|
|
18
|
+
x: number;
|
|
19
|
+
y1: number;
|
|
20
|
+
y2: number;
|
|
21
|
+
intersectionIndex: number;
|
|
22
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ScaleBand } from "d3-scale";
|
|
2
|
+
import type { DataSet, Intersection } from "../../SetOverlapChart.types";
|
|
3
|
+
import type { IntersectionCircle, IntersectionLine } from "./IntersectionsMatrix.types";
|
|
4
|
+
export declare const getMatrixData: (xScale: ScaleBand<string>, yScale: ScaleBand<string>, intersections: Intersection[], sets: DataSet[]) => {
|
|
5
|
+
circles: IntersectionCircle[];
|
|
6
|
+
lines: IntersectionLine[];
|
|
7
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export const getMatrixData = (xScale, yScale, intersections, sets) => {
|
|
2
|
+
const circles = intersections.flatMap((intersection, i) => sets.map((set) => ({
|
|
3
|
+
cx: (xScale(String(i)) ?? 0) + xScale.bandwidth() / 2,
|
|
4
|
+
cy: (yScale(set.name) ?? 0) + yScale.bandwidth() / 2,
|
|
5
|
+
isPresent: intersection.sets.includes(set.name),
|
|
6
|
+
intersectionIndex: i,
|
|
7
|
+
})));
|
|
8
|
+
const lines = intersections.reduce((acc, intersection, i) => {
|
|
9
|
+
const activeSetIndices = intersection.sets
|
|
10
|
+
.map((name) => sets.findIndex((set) => set.name === name))
|
|
11
|
+
.filter((i) => i >= 0)
|
|
12
|
+
.sort((a, b) => a - b);
|
|
13
|
+
if (activeSetIndices.length > 1) {
|
|
14
|
+
const firstIdx = activeSetIndices[0];
|
|
15
|
+
const lastIdx = activeSetIndices[activeSetIndices.length - 1];
|
|
16
|
+
const x = (xScale(String(i)) ?? 0) + xScale.bandwidth() / 2;
|
|
17
|
+
const y1 = (yScale(sets[firstIdx].name) ?? 0) + yScale.bandwidth() / 2;
|
|
18
|
+
const y2 = (yScale(sets[lastIdx].name) ?? 0) + yScale.bandwidth() / 2;
|
|
19
|
+
acc.push({ x, y1, y2, intersectionIndex: i });
|
|
20
|
+
return acc;
|
|
21
|
+
}
|
|
22
|
+
return acc;
|
|
23
|
+
}, []);
|
|
24
|
+
return { circles, lines };
|
|
25
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { IntersectionsMatrix } from "./IntersectionsMatrix";
|