@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.
Files changed (102) hide show
  1. package/charts/SetOverlapChart/SetOverlapChart.d.ts +33 -0
  2. package/charts/SetOverlapChart/SetOverlapChart.js +79 -0
  3. package/charts/SetOverlapChart/SetOverlapChart.module.css.d.ts +2 -0
  4. package/charts/SetOverlapChart/SetOverlapChart.module.css.js +36 -0
  5. package/charts/SetOverlapChart/SetOverlapChart.module.css.json +1 -0
  6. package/charts/SetOverlapChart/SetOverlapChart.types.d.ts +117 -0
  7. package/charts/SetOverlapChart/SetOverlapChart.types.js +1 -0
  8. package/charts/SetOverlapChart/components/IntersectionsChart/IntersectionsChart.d.ts +2 -0
  9. package/charts/SetOverlapChart/components/IntersectionsChart/IntersectionsChart.js +36 -0
  10. package/charts/SetOverlapChart/components/IntersectionsChart/IntersectionsChart.module.css.d.ts +2 -0
  11. package/charts/SetOverlapChart/components/IntersectionsChart/IntersectionsChart.module.css.js +40 -0
  12. package/charts/SetOverlapChart/components/IntersectionsChart/IntersectionsChart.module.css.json +1 -0
  13. package/charts/SetOverlapChart/components/IntersectionsChart/IntersectionsChart.types.d.ts +15 -0
  14. package/charts/SetOverlapChart/components/IntersectionsChart/IntersectionsChart.types.js +1 -0
  15. package/charts/SetOverlapChart/components/IntersectionsChart/helpers.d.ts +4 -0
  16. package/charts/SetOverlapChart/components/IntersectionsChart/helpers.js +4 -0
  17. package/charts/SetOverlapChart/components/IntersectionsChart/index.d.ts +1 -0
  18. package/charts/SetOverlapChart/components/IntersectionsChart/index.js +1 -0
  19. package/charts/SetOverlapChart/components/IntersectionsChartAxis/IntersectionsChartAxis.d.ts +2 -0
  20. package/charts/SetOverlapChart/components/IntersectionsChartAxis/IntersectionsChartAxis.js +30 -0
  21. package/charts/SetOverlapChart/components/IntersectionsChartAxis/IntersectionsChartAxis.module.css.d.ts +2 -0
  22. package/charts/SetOverlapChart/components/IntersectionsChartAxis/IntersectionsChartAxis.module.css.js +36 -0
  23. package/charts/SetOverlapChart/components/IntersectionsChartAxis/IntersectionsChartAxis.module.css.json +1 -0
  24. package/charts/SetOverlapChart/components/IntersectionsChartAxis/IntersectionsChartAxis.types.d.ts +8 -0
  25. package/charts/SetOverlapChart/components/IntersectionsChartAxis/IntersectionsChartAxis.types.js +1 -0
  26. package/charts/SetOverlapChart/components/IntersectionsChartAxis/helpers.d.ts +1 -0
  27. package/charts/SetOverlapChart/components/IntersectionsChartAxis/helpers.js +3 -0
  28. package/charts/SetOverlapChart/components/IntersectionsChartAxis/index.d.ts +1 -0
  29. package/charts/SetOverlapChart/components/IntersectionsChartAxis/index.js +1 -0
  30. package/charts/SetOverlapChart/components/IntersectionsMatrix/IntersectionsMatrix.d.ts +2 -0
  31. package/charts/SetOverlapChart/components/IntersectionsMatrix/IntersectionsMatrix.js +13 -0
  32. package/charts/SetOverlapChart/components/IntersectionsMatrix/IntersectionsMatrix.module.css.d.ts +2 -0
  33. package/charts/SetOverlapChart/components/IntersectionsMatrix/IntersectionsMatrix.module.css.js +29 -0
  34. package/charts/SetOverlapChart/components/IntersectionsMatrix/IntersectionsMatrix.module.css.json +1 -0
  35. package/charts/SetOverlapChart/components/IntersectionsMatrix/IntersectionsMatrix.types.d.ts +22 -0
  36. package/charts/SetOverlapChart/components/IntersectionsMatrix/IntersectionsMatrix.types.js +1 -0
  37. package/charts/SetOverlapChart/components/IntersectionsMatrix/helpers.d.ts +7 -0
  38. package/charts/SetOverlapChart/components/IntersectionsMatrix/helpers.js +25 -0
  39. package/charts/SetOverlapChart/components/IntersectionsMatrix/index.d.ts +1 -0
  40. package/charts/SetOverlapChart/components/IntersectionsMatrix/index.js +1 -0
  41. package/charts/SetOverlapChart/components/SetsChart/SetsChart.d.ts +2 -0
  42. package/charts/SetOverlapChart/components/SetsChart/SetsChart.js +54 -0
  43. package/charts/SetOverlapChart/components/SetsChart/SetsChart.module.css.d.ts +2 -0
  44. package/charts/SetOverlapChart/components/SetsChart/SetsChart.module.css.js +55 -0
  45. package/charts/SetOverlapChart/components/SetsChart/SetsChart.module.css.json +1 -0
  46. package/charts/SetOverlapChart/components/SetsChart/SetsChart.types.d.ts +16 -0
  47. package/charts/SetOverlapChart/components/SetsChart/SetsChart.types.js +1 -0
  48. package/charts/SetOverlapChart/components/SetsChart/index.d.ts +1 -0
  49. package/charts/SetOverlapChart/components/SetsChart/index.js +1 -0
  50. package/charts/SetOverlapChart/components/SetsChartAxis/SetsChartAxis.d.ts +2 -0
  51. package/charts/SetOverlapChart/components/SetsChartAxis/SetsChartAxis.js +9 -0
  52. package/charts/SetOverlapChart/components/SetsChartAxis/SetsChartAxis.module.css.d.ts +2 -0
  53. package/charts/SetOverlapChart/components/SetsChartAxis/SetsChartAxis.module.css.js +24 -0
  54. package/charts/SetOverlapChart/components/SetsChartAxis/SetsChartAxis.module.css.json +1 -0
  55. package/charts/SetOverlapChart/components/SetsChartAxis/SetsChartAxis.types.d.ts +4 -0
  56. package/charts/SetOverlapChart/components/SetsChartAxis/SetsChartAxis.types.js +1 -0
  57. package/charts/SetOverlapChart/components/SetsChartAxis/index.d.ts +1 -0
  58. package/charts/SetOverlapChart/components/SetsChartAxis/index.js +1 -0
  59. package/charts/SetOverlapChart/constants.d.ts +14 -0
  60. package/charts/SetOverlapChart/constants.js +16 -0
  61. package/charts/SetOverlapChart/helpers.d.ts +15 -0
  62. package/charts/SetOverlapChart/helpers.js +52 -0
  63. package/charts/SetOverlapChart/index.d.ts +2 -0
  64. package/charts/SetOverlapChart/index.js +1 -0
  65. package/charts/SetOverlapChart/useSetOverlapChartSizes.d.ts +19 -0
  66. package/charts/SetOverlapChart/useSetOverlapChartSizes.js +35 -0
  67. package/charts/index.d.ts +1 -0
  68. package/charts/index.js +1 -0
  69. package/components/AppSelector/AppSelector.d.ts +5 -0
  70. package/components/AppSelector/AppSelector.js +20 -0
  71. package/components/AppSelector/AppSelector.module.css.d.ts +2 -0
  72. package/components/AppSelector/AppSelector.module.css.js +61 -0
  73. package/components/AppSelector/AppSelector.module.css.json +1 -0
  74. package/components/AppSelector/AppSelector.types.d.ts +29 -0
  75. package/components/AppSelector/AppSelector.types.js +1 -0
  76. package/components/AppSelector/index.d.ts +2 -0
  77. package/components/AppSelector/index.js +1 -0
  78. package/components/Button/Button.d.ts +3 -0
  79. package/components/Button/Button.js +9 -3
  80. package/components/Button/Button.module.css.js +9 -3
  81. package/components/Button/Button.module.css.json +1 -1
  82. package/components/Divider/Divider.d.ts +2 -0
  83. package/components/Divider/Divider.js +6 -0
  84. package/components/Divider/Divider.module.css.d.ts +2 -0
  85. package/components/Divider/Divider.module.css.js +40 -0
  86. package/components/Divider/Divider.module.css.json +1 -0
  87. package/components/Divider/Divider.types.d.ts +11 -0
  88. package/components/Divider/Divider.types.js +1 -0
  89. package/components/Divider/index.d.ts +2 -0
  90. package/components/Divider/index.js +1 -0
  91. package/components/Popover/Popover.d.ts +36 -0
  92. package/components/Popover/Popover.js +62 -0
  93. package/components/Popover/Popover.module.css.d.ts +2 -0
  94. package/components/Popover/Popover.module.css.js +62 -0
  95. package/components/Popover/Popover.module.css.json +1 -0
  96. package/components/Popover/Popover.types.d.ts +18 -0
  97. package/components/Popover/Popover.types.js +1 -0
  98. package/components/Popover/index.d.ts +2 -0
  99. package/components/Popover/index.js +1 -0
  100. package/components/index.d.ts +1 -0
  101. package/components/index.js +1 -0
  102. 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,2 @@
1
+ import json from './SetOverlapChart.module.css.json';
2
+ export default json;
@@ -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,2 @@
1
+ import json from './IntersectionsChart.module.css.json';
2
+ export default json;
@@ -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;
@@ -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,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,2 @@
1
+ import type { IntersectionsChartAxisProps } from "./IntersectionsChartAxis.types";
2
+ export declare const IntersectionsChartAxis: ({ xScale, yScale, width, height, axisLabel, }: IntersectionsChartAxisProps) => import("react/jsx-runtime").JSX.Element;
@@ -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,2 @@
1
+ import json from './IntersectionsChartAxis.module.css.json';
2
+ export default json;
@@ -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" }
@@ -0,0 +1,8 @@
1
+ import type { ScaleBand, ScaleLinear } from "d3-scale";
2
+ export type IntersectionsChartAxisProps = {
3
+ xScale: ScaleBand<string>;
4
+ yScale: ScaleLinear<number, number>;
5
+ width: number;
6
+ height: number;
7
+ axisLabel?: string;
8
+ };
@@ -0,0 +1 @@
1
+ export declare const calculateTicksWidth: (ticks: number[]) => number;
@@ -0,0 +1,3 @@
1
+ import { AXIS_TICK_LABEL_SIZE } from "../../constants";
2
+ import { measureText } from "../../helpers";
3
+ export const calculateTicksWidth = (ticks) => ticks.reduce((maxWidth, tick) => Math.max(maxWidth, measureText(String(tick), AXIS_TICK_LABEL_SIZE)), 0);
@@ -0,0 +1 @@
1
+ export { IntersectionsChartAxis } from "./IntersectionsChartAxis";
@@ -0,0 +1 @@
1
+ export { IntersectionsChartAxis } from "./IntersectionsChartAxis";
@@ -0,0 +1,2 @@
1
+ import type { IntersectionsMatrixProps } from "./IntersectionsMatrix.types";
2
+ export declare const IntersectionsMatrix: ({ xScale, yScale, intersections, sets, transform, hoveredIntersection, }: IntersectionsMatrixProps) => import("react/jsx-runtime").JSX.Element;
@@ -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
+ };
@@ -0,0 +1,2 @@
1
+ import json from './IntersectionsMatrix.module.css.json';
2
+ export default json;
@@ -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;
@@ -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,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";