@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 @@
|
|
|
1
|
+
export { IntersectionsMatrix } from "./IntersectionsMatrix";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import type { SetsChartProps } from "./SetsChart.types";
|
|
2
|
+
export declare const SetsChart: ({ mode, sets, intersections, width, yScale, transform, labels, hoveredSet, hoveredIntersection, hoverAreaWidth, axisLabel, onSetHover, }: SetsChartProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,54 @@
|
|
|
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_BEFORE_LEFT_BAR, SET_BAR_HEIGHT, SET_LABEL_SIZE, TEXT_GAP, } from "../../constants";
|
|
6
|
+
import { SetsChartAxis } from "../SetsChartAxis";
|
|
7
|
+
import styles from "./SetsChart.module.css";
|
|
8
|
+
export const SetsChart = ({ mode, sets, intersections, width, yScale, transform, labels, hoveredSet, hoveredIntersection, hoverAreaWidth, axisLabel, onSetHover, }) => {
|
|
9
|
+
const hoveredElementsSet = useMemo(() => {
|
|
10
|
+
const elements = hoveredIntersection?.elements || hoveredSet?.elements;
|
|
11
|
+
return elements ? new Set(elements) : null;
|
|
12
|
+
}, [hoveredIntersection, hoveredSet]);
|
|
13
|
+
const setOverlappingColumns = useMemo(() => {
|
|
14
|
+
const result = {};
|
|
15
|
+
sets.forEach((set) => {
|
|
16
|
+
result[set.name] = intersections.filter((intersection) => intersection.sets.includes(set.name));
|
|
17
|
+
});
|
|
18
|
+
return result;
|
|
19
|
+
}, [sets, intersections]);
|
|
20
|
+
const xScale = useMemo(() => {
|
|
21
|
+
const maxSetSize = sets.length > 0 ? Math.max(...sets.map((set) => set.size)) : 0;
|
|
22
|
+
return scaleLinear().domain([0, maxSetSize]).range([0, width]);
|
|
23
|
+
}, [width, sets]);
|
|
24
|
+
const rootStyle = {
|
|
25
|
+
"--set-label-size": SET_LABEL_SIZE,
|
|
26
|
+
};
|
|
27
|
+
return (_jsxs("g", { transform: transform, style: rootStyle, role: "listbox", "aria-label": "Set bars", children: [_jsx(SetsChartAxis, { width: width, label: axisLabel }), sets.map((set, i) => {
|
|
28
|
+
const { name, size, elements } = set;
|
|
29
|
+
const label = labels[i];
|
|
30
|
+
const height = yScale.bandwidth();
|
|
31
|
+
const y = (yScale(name) ?? 0) + height / 2 - SET_BAR_HEIGHT / 2;
|
|
32
|
+
const lineY = (yScale(name) ?? 0) + height;
|
|
33
|
+
let barSize = size;
|
|
34
|
+
if (mode === "distinctIntersection" &&
|
|
35
|
+
(hoveredIntersection || hoveredSet)) {
|
|
36
|
+
if (hoveredIntersection) {
|
|
37
|
+
const containsSet = hoveredIntersection?.sets.includes(name);
|
|
38
|
+
barSize = containsSet ? hoveredIntersection?.size : 0;
|
|
39
|
+
}
|
|
40
|
+
else if (hoveredSet && setOverlappingColumns[hoveredSet.name]) {
|
|
41
|
+
const hoveredSetOverlappingColumns = new Set(setOverlappingColumns[hoveredSet.name]);
|
|
42
|
+
barSize = setOverlappingColumns[set.name]
|
|
43
|
+
.filter((item) => hoveredSetOverlappingColumns.has(item))
|
|
44
|
+
.reduce((acc, item) => acc + item.size, 0);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
barSize = hoveredElementsSet
|
|
49
|
+
? elements.filter((el) => hoveredElementsSet.has(el)).length
|
|
50
|
+
: size;
|
|
51
|
+
}
|
|
52
|
+
return (_jsxs("g", { children: [_jsx("rect", { className: classNames(styles.setsBarBackground), x: 0, y: y, width: width, height: SET_BAR_HEIGHT }), _jsx("rect", { className: classNames(styles.setsBarDimmed), x: width - xScale(size), y: y, width: xScale(size), height: SET_BAR_HEIGHT }), _jsx("rect", { className: classNames(styles.setsBar), x: width - xScale(barSize), y: y, width: xScale(barSize), height: SET_BAR_HEIGHT }), i !== sets.length - 1 && (_jsx("line", { className: classNames(styles.gridLine), x1: 0, x2: hoverAreaWidth, y1: lineY, y2: lineY })), _jsx("text", { className: classNames(styles.setsLabel), x: width + TEXT_GAP, y: (yScale(name) ?? 0) + height / 2, children: label }), _jsx("rect", { className: classNames(styles.hoverBar), "data-reltio-id": "set-hover-area", role: "option", "aria-selected": hoveredSet === set, x: -GAP_BEFORE_LEFT_BAR + 1, y: yScale(name), width: hoverAreaWidth - 2, height: height, tabIndex: 0, "aria-label": `Set: ${name}, size: ${size}`, onMouseEnter: () => onSetHover(set), onMouseLeave: () => onSetHover(null), onFocus: () => onSetHover(set), onBlur: () => onSetHover(null) })] }, `set-group-${name}`));
|
|
53
|
+
})] }));
|
|
54
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import styleInject from 'style-inject';
|
|
2
|
+
import json from './SetsChart.module.css.json';
|
|
3
|
+
styleInject(`
|
|
4
|
+
.SetsChart_setsBarBackground__L29wd {
|
|
5
|
+
fill: var(--reltio-color-brand-blue);
|
|
6
|
+
opacity: 0.08;
|
|
7
|
+
transition:
|
|
8
|
+
x ease-in-out 0.3s,
|
|
9
|
+
width ease-in-out 0.3s;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.SetsChart_setsBarDimmed__L29wd {
|
|
13
|
+
fill: var(--reltio-color-border-3);
|
|
14
|
+
transition:
|
|
15
|
+
x ease-in-out 0.3s,
|
|
16
|
+
width ease-in-out 0.3s;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.SetsChart_setsBar__L29wd {
|
|
20
|
+
fill: var(--reltio-color-brand-blue);
|
|
21
|
+
transition:
|
|
22
|
+
x ease-in-out 0.3s,
|
|
23
|
+
width ease-in-out 0.3s;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.SetsChart_hoverBar__L29wd {
|
|
27
|
+
fill: transparent;
|
|
28
|
+
stroke-width: 1px;
|
|
29
|
+
cursor: pointer;
|
|
30
|
+
stroke: var(--reltio-color-brand-blue);
|
|
31
|
+
opacity: 0;
|
|
32
|
+
transition: opacity ease-in-out 0.3s;
|
|
33
|
+
outline: none;
|
|
34
|
+
|
|
35
|
+
&:hover,
|
|
36
|
+
&:focus-visible {
|
|
37
|
+
opacity: 1;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.SetsChart_setsLabel__L29wd {
|
|
42
|
+
font-size: var(--set-label-size);
|
|
43
|
+
fill: var(--reltio-color-text);
|
|
44
|
+
text-anchor: start;
|
|
45
|
+
dominant-baseline: middle;
|
|
46
|
+
transition: all ease-in-out 0.3s;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.SetsChart_gridLine__L29wd {
|
|
50
|
+
stroke: var(--reltio-color-border-black);
|
|
51
|
+
opacity: 0.04;
|
|
52
|
+
stroke-width: 1px;
|
|
53
|
+
}
|
|
54
|
+
`);
|
|
55
|
+
export default json;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "setsBarBackground": "SetsChart_setsBarBackground__L29wd", "setsBarDimmed": "SetsChart_setsBarDimmed__L29wd", "setsBar": "SetsChart_setsBar__L29wd", "hoverBar": "SetsChart_hoverBar__L29wd", "setsLabel": "SetsChart_setsLabel__L29wd", "gridLine": "SetsChart_gridLine__L29wd" }
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ScaleBand } from "d3-scale";
|
|
2
|
+
import type { DataSet, Intersection, SetOverlapChartMode } from "../../SetOverlapChart.types";
|
|
3
|
+
export type SetsChartProps = {
|
|
4
|
+
mode: SetOverlapChartMode;
|
|
5
|
+
sets: DataSet[];
|
|
6
|
+
intersections: Intersection[];
|
|
7
|
+
width: number;
|
|
8
|
+
yScale: ScaleBand<string>;
|
|
9
|
+
labels: string[];
|
|
10
|
+
transform?: string;
|
|
11
|
+
hoverAreaWidth: number;
|
|
12
|
+
hoveredSet?: DataSet | null;
|
|
13
|
+
hoveredIntersection?: Intersection | null;
|
|
14
|
+
axisLabel?: string;
|
|
15
|
+
onSetHover: (set: DataSet | null) => void;
|
|
16
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SetsChart } from "./SetsChart";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SetsChart } from "./SetsChart";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { classNames } from "../../../../utils/classNames";
|
|
3
|
+
import styles from "./SetsChartAxis.module.css";
|
|
4
|
+
const AXIS_LABEL_OFFSET = 12;
|
|
5
|
+
const AXIS_TICK_LABEL_OFFSET = 9;
|
|
6
|
+
const LABEL_OFFSET = AXIS_LABEL_OFFSET + AXIS_TICK_LABEL_OFFSET;
|
|
7
|
+
export const SetsChartAxis = ({ width, label }) => {
|
|
8
|
+
return (_jsxs("g", { children: [_jsxs("g", { transform: "translate(0,0)", children: [_jsx("line", { y2: -6, className: classNames(styles.axisTick) }), _jsx("text", { x: 0, y: -AXIS_TICK_LABEL_OFFSET, className: classNames(styles.axisTickLabel), children: "100%" })] }), _jsxs("g", { transform: `translate(${width},0)`, children: [_jsx("line", { y2: -6, className: classNames(styles.axisTick) }), _jsx("text", { x: 0, y: -AXIS_TICK_LABEL_OFFSET, className: classNames(styles.axisTickLabel), children: "0%" })] }), label && (_jsx("text", { className: classNames(styles.axisLabel), transform: `translate(${width / 2},${-LABEL_OFFSET})`, children: label }))] }));
|
|
9
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import styleInject from 'style-inject';
|
|
2
|
+
import json from './SetsChartAxis.module.css.json';
|
|
3
|
+
styleInject(`
|
|
4
|
+
.SetsChartAxis_axisTick__L29wd {
|
|
5
|
+
stroke: var(--reltio-color-border-black);
|
|
6
|
+
stroke-width: 1px;
|
|
7
|
+
opacity: 0.54;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.SetsChartAxis_axisTickLabel__L29wd {
|
|
11
|
+
font-size: 10px;
|
|
12
|
+
fill: var(--reltio-color-text);
|
|
13
|
+
opacity: 0.54;
|
|
14
|
+
text-anchor: middle;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.SetsChartAxis_axisLabel__L29wd {
|
|
18
|
+
fill: var(--reltio-color-text);
|
|
19
|
+
opacity: 0.54;
|
|
20
|
+
font-size: 12px;
|
|
21
|
+
text-anchor: middle;
|
|
22
|
+
}
|
|
23
|
+
`);
|
|
24
|
+
export default json;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "axisTick": "SetsChartAxis_axisTick__L29wd", "axisTickLabel": "SetsChartAxis_axisTickLabel__L29wd", "axisLabel": "SetsChartAxis_axisLabel__L29wd" }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SetsChartAxis } from "./SetsChartAxis";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SetsChartAxis } from "./SetsChartAxis";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const TOP_GAP = 24;
|
|
2
|
+
export declare const TEXT_GAP = 16;
|
|
3
|
+
export declare const GAP_BETWEEN_TABLE_AND_CHART = 8;
|
|
4
|
+
export declare const GAP_BEFORE_LEFT_BAR = 12;
|
|
5
|
+
export declare const AXIS_TICK_LABEL_SIZE = "12px";
|
|
6
|
+
export declare const SET_LABEL_SIZE = "13px";
|
|
7
|
+
export declare const SET_BAR_HEIGHT = 12;
|
|
8
|
+
export declare const MATRIX_ROW_HEIGHT = 29;
|
|
9
|
+
export declare const MIN_MATRIX_COLUMN_WIDTH = 28;
|
|
10
|
+
export declare const MIN_INTERSECTION_CHART_HEIGHT = 120;
|
|
11
|
+
export declare const MIN_SETS_CHART_WIDTH = 100;
|
|
12
|
+
export declare const MAX_SETS_CHART_WIDTH = 200;
|
|
13
|
+
export declare const MAX_CHARACTERS = 20;
|
|
14
|
+
export declare const MIN_CHARACTERS = 8;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Gaps
|
|
2
|
+
export const TOP_GAP = 24;
|
|
3
|
+
export const TEXT_GAP = 16;
|
|
4
|
+
export const GAP_BETWEEN_TABLE_AND_CHART = 8;
|
|
5
|
+
export const GAP_BEFORE_LEFT_BAR = 12;
|
|
6
|
+
// Sizes
|
|
7
|
+
export const AXIS_TICK_LABEL_SIZE = "12px";
|
|
8
|
+
export const SET_LABEL_SIZE = "13px";
|
|
9
|
+
export const SET_BAR_HEIGHT = 12;
|
|
10
|
+
export const MATRIX_ROW_HEIGHT = 29;
|
|
11
|
+
export const MIN_MATRIX_COLUMN_WIDTH = 28;
|
|
12
|
+
export const MIN_INTERSECTION_CHART_HEIGHT = 120;
|
|
13
|
+
export const MIN_SETS_CHART_WIDTH = 100;
|
|
14
|
+
export const MAX_SETS_CHART_WIDTH = 200;
|
|
15
|
+
export const MAX_CHARACTERS = 20;
|
|
16
|
+
export const MIN_CHARACTERS = 8;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { DataSet } from "./SetOverlapChart.types";
|
|
2
|
+
export declare const measureText: (text: string, fontSize: string) => number;
|
|
3
|
+
export declare const truncateLabels: (sets: DataSet[], maxLabelsWidth: number) => {
|
|
4
|
+
labels: string[];
|
|
5
|
+
maxWidth: number;
|
|
6
|
+
};
|
|
7
|
+
export declare const createMatrixXScale: (intersectionCount: number, width: number) => import("d3-scale").ScaleBand<string>;
|
|
8
|
+
export declare const createMatrixYScale: (setNames: string[], height: number) => import("d3-scale").ScaleBand<string>;
|
|
9
|
+
export declare const getEmptyGridDimensions: (width: number, height: number) => {
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
left: number;
|
|
13
|
+
};
|
|
14
|
+
export declare const createEmptyGridXScale: (width: number) => import("d3-scale").ScaleBand<string>;
|
|
15
|
+
export declare const createEmptyGridYScale: (height: number) => import("d3-scale").ScaleLinear<number, number, never>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { scaleBand, scaleLinear } from "d3-scale";
|
|
2
|
+
import { MAX_CHARACTERS, MIN_CHARACTERS, SET_LABEL_SIZE, TOP_GAP, } from "./constants";
|
|
3
|
+
const ctxCache = new Map();
|
|
4
|
+
const getContext = (fontSize) => {
|
|
5
|
+
const cached = ctxCache.get(fontSize);
|
|
6
|
+
if (cached)
|
|
7
|
+
return cached;
|
|
8
|
+
if (typeof OffscreenCanvas !== "undefined") {
|
|
9
|
+
const ctx = new OffscreenCanvas(0, 0).getContext("2d");
|
|
10
|
+
if (ctx) {
|
|
11
|
+
ctx.font = `${fontSize} "Inter", sans-serif`;
|
|
12
|
+
ctxCache.set(fontSize, ctx);
|
|
13
|
+
return ctx;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return null;
|
|
17
|
+
};
|
|
18
|
+
export const measureText = (text, fontSize) => {
|
|
19
|
+
const ctx = getContext(fontSize);
|
|
20
|
+
if (ctx)
|
|
21
|
+
return ctx.measureText(text).width;
|
|
22
|
+
return text.length * Number.parseFloat(fontSize) * 0.6;
|
|
23
|
+
};
|
|
24
|
+
export const truncateLabels = (sets, maxLabelsWidth) => {
|
|
25
|
+
const newTruncatedLabels = [];
|
|
26
|
+
let maxWidth = 0;
|
|
27
|
+
sets.forEach(({ name }) => {
|
|
28
|
+
let truncatedText = name.length > MAX_CHARACTERS
|
|
29
|
+
? `${name.slice(0, MAX_CHARACTERS)}...`
|
|
30
|
+
: name;
|
|
31
|
+
let textWidth = measureText(truncatedText, SET_LABEL_SIZE);
|
|
32
|
+
while (textWidth > maxLabelsWidth &&
|
|
33
|
+
truncatedText.length > MIN_CHARACTERS) {
|
|
34
|
+
truncatedText = `${truncatedText.slice(0, -4)}...`;
|
|
35
|
+
textWidth = measureText(truncatedText, SET_LABEL_SIZE);
|
|
36
|
+
}
|
|
37
|
+
newTruncatedLabels.push(truncatedText);
|
|
38
|
+
maxWidth = Math.max(maxWidth, textWidth);
|
|
39
|
+
});
|
|
40
|
+
return { labels: newTruncatedLabels, maxWidth };
|
|
41
|
+
};
|
|
42
|
+
export const createMatrixXScale = (intersectionCount, width) => scaleBand([0, width])
|
|
43
|
+
.domain(Array.from({ length: intersectionCount }, (_, i) => String(i)))
|
|
44
|
+
.paddingInner(0.2);
|
|
45
|
+
export const createMatrixYScale = (setNames, height) => scaleBand([0, height]).domain(setNames);
|
|
46
|
+
export const getEmptyGridDimensions = (width, height) => ({
|
|
47
|
+
width: width * 0.9,
|
|
48
|
+
height: height - TOP_GAP * 2,
|
|
49
|
+
left: (width - width * 0.9) / 2,
|
|
50
|
+
});
|
|
51
|
+
export const createEmptyGridXScale = (width) => scaleBand().domain([]).range([0, width]);
|
|
52
|
+
export const createEmptyGridYScale = (height) => scaleLinear().domain([0, 1000]).range([height, 0]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SetOverlapChart } from "./SetOverlapChart";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { DataSet, Intersection } from "./SetOverlapChart.types";
|
|
2
|
+
type Props = {
|
|
3
|
+
width: number;
|
|
4
|
+
height: number;
|
|
5
|
+
intersections: Intersection[];
|
|
6
|
+
sets: DataSet[];
|
|
7
|
+
};
|
|
8
|
+
export declare const useSetOverlapChartSizes: ({ width, height, intersections, sets, }: Props) => {
|
|
9
|
+
labels: string[];
|
|
10
|
+
svgWidth: number;
|
|
11
|
+
svgHeight: number;
|
|
12
|
+
intersectionsChartWidth: number;
|
|
13
|
+
intersectionsChartHeight: number;
|
|
14
|
+
matrixY: number;
|
|
15
|
+
matrixHeight: number;
|
|
16
|
+
leftPadding: number;
|
|
17
|
+
setsChartWidth: number;
|
|
18
|
+
};
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { GAP_BEFORE_LEFT_BAR, GAP_BETWEEN_TABLE_AND_CHART, MATRIX_ROW_HEIGHT, MAX_SETS_CHART_WIDTH, MIN_INTERSECTION_CHART_HEIGHT, MIN_MATRIX_COLUMN_WIDTH, MIN_SETS_CHART_WIDTH, TEXT_GAP, TOP_GAP, } from "./constants";
|
|
3
|
+
import { truncateLabels } from "./helpers";
|
|
4
|
+
export const useSetOverlapChartSizes = ({ width, height, intersections, sets, }) => useMemo(() => {
|
|
5
|
+
const minMatrixWidth = intersections.length * MIN_MATRIX_COLUMN_WIDTH * 1.2;
|
|
6
|
+
const matrixHeight = sets.length * MATRIX_ROW_HEIGHT;
|
|
7
|
+
const availableLabelsWidth = width -
|
|
8
|
+
minMatrixWidth -
|
|
9
|
+
TEXT_GAP * 2 -
|
|
10
|
+
GAP_BEFORE_LEFT_BAR -
|
|
11
|
+
MIN_SETS_CHART_WIDTH;
|
|
12
|
+
const { labels, maxWidth: labelsWidth } = truncateLabels(sets, availableLabelsWidth);
|
|
13
|
+
const availableSetsChartWidth = width - minMatrixWidth - TEXT_GAP * 2 - GAP_BEFORE_LEFT_BAR - labelsWidth;
|
|
14
|
+
const setsChartWidth = Math.min(Math.max(availableSetsChartWidth, MIN_SETS_CHART_WIDTH), MAX_SETS_CHART_WIDTH);
|
|
15
|
+
const leftPadding = setsChartWidth + labelsWidth + TEXT_GAP * 2 + GAP_BEFORE_LEFT_BAR;
|
|
16
|
+
const intersectionsChartWidth = Math.max(minMatrixWidth, width - leftPadding);
|
|
17
|
+
const intersectionsChartHeight = Math.max(MIN_INTERSECTION_CHART_HEIGHT, height - TOP_GAP - matrixHeight - GAP_BETWEEN_TABLE_AND_CHART);
|
|
18
|
+
const svgWidth = leftPadding + intersectionsChartWidth;
|
|
19
|
+
const svgHeight = intersectionsChartHeight +
|
|
20
|
+
matrixHeight +
|
|
21
|
+
TOP_GAP +
|
|
22
|
+
GAP_BETWEEN_TABLE_AND_CHART;
|
|
23
|
+
const matrixY = svgHeight - matrixHeight - 1;
|
|
24
|
+
return {
|
|
25
|
+
labels,
|
|
26
|
+
svgWidth,
|
|
27
|
+
svgHeight,
|
|
28
|
+
intersectionsChartWidth,
|
|
29
|
+
intersectionsChartHeight,
|
|
30
|
+
matrixY,
|
|
31
|
+
matrixHeight,
|
|
32
|
+
leftPadding,
|
|
33
|
+
setsChartWidth,
|
|
34
|
+
};
|
|
35
|
+
}, [width, height, intersections, sets]);
|
package/charts/index.d.ts
CHANGED
package/charts/index.js
CHANGED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { AppSelectorProps } from "./AppSelector.types";
|
|
2
|
+
/** Navigation popover for switching between Reltio platform applications.
|
|
3
|
+
* Displays apps grouped by category in a grid layout.
|
|
4
|
+
*/
|
|
5
|
+
export declare const AppSelector: ({ apps, env, tenant, className, positionArea, ...rest }: AppSelectorProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button } from "../../components/Button";
|
|
3
|
+
import { Divider } from "../../components/Divider";
|
|
4
|
+
import { Popover } from "../../components/Popover";
|
|
5
|
+
import { Applications } from "../../icons/Applications";
|
|
6
|
+
import { Link } from "../../icons/Link";
|
|
7
|
+
import { classNames } from "../../utils/classNames";
|
|
8
|
+
import styles from "./AppSelector.module.css";
|
|
9
|
+
const DEFAULT_CATEGORY = "Applications";
|
|
10
|
+
/** Navigation popover for switching between Reltio platform applications.
|
|
11
|
+
* Displays apps grouped by category in a grid layout.
|
|
12
|
+
*/
|
|
13
|
+
export const AppSelector = ({ apps, env, tenant, className, positionArea = "right span-top", ...rest }) => {
|
|
14
|
+
const validApps = apps.filter((app) => app.name && app.uri);
|
|
15
|
+
const groups = Object.groupBy(validApps, ({ category }) => category || DEFAULT_CATEGORY);
|
|
16
|
+
return (_jsx("nav", { className: classNames(className), "aria-label": "Applications", ...rest, children: _jsx(Popover, { trigger: _jsx(Button, { variant: "text", "aria-label": "Applications", children: _jsx(Applications, {}) }), positionArea: positionArea, children: _jsx("div", { className: classNames(styles.content), children: Object.entries(groups).map(([category, apps]) => (_jsxs("div", { className: classNames(styles.group), children: [_jsx(Divider, { className: classNames(styles.divider), children: category }), _jsx("div", { className: classNames(styles.grid), children: apps?.map((app) => (_jsxs("a", { href: resolveUri(app.uri, env, tenant), target: "_blank", rel: "noopener noreferrer", className: classNames(styles.app), children: [app.icon ? (_jsx("img", { src: app.icon, alt: "", className: classNames(styles.appIcon) })) : (_jsx(Link, { size: "xlarge" })), app.name] }, app.name))) })] }, category))) }) }) }));
|
|
17
|
+
};
|
|
18
|
+
const resolveUri = (uri, env, tenant) => uri
|
|
19
|
+
?.replaceAll("${environment}", String(env))
|
|
20
|
+
.replaceAll("${tenant}", String(tenant));
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import styleInject from 'style-inject';
|
|
2
|
+
import json from './AppSelector.module.css.json';
|
|
3
|
+
styleInject(`
|
|
4
|
+
.AppSelector_content__L29wd {
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: column;
|
|
7
|
+
gap: 16px;
|
|
8
|
+
padding: 16px;
|
|
9
|
+
width: 316px;
|
|
10
|
+
max-height: 485px;
|
|
11
|
+
overflow-y: auto;
|
|
12
|
+
box-sizing: border-box;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.AppSelector_group__L29wd {
|
|
16
|
+
display: flex;
|
|
17
|
+
flex-direction: column;
|
|
18
|
+
gap: 12px;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.AppSelector_grid__L29wd {
|
|
22
|
+
display: grid;
|
|
23
|
+
grid-template-columns: repeat(3, 1fr);
|
|
24
|
+
column-gap: 16px;
|
|
25
|
+
row-gap: 24px;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.AppSelector_divider__L29wd {
|
|
29
|
+
margin-left: 12px;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.AppSelector_app__L29wd {
|
|
33
|
+
display: flex;
|
|
34
|
+
flex-direction: column;
|
|
35
|
+
align-items: center;
|
|
36
|
+
gap: 8px;
|
|
37
|
+
padding: 8px 0;
|
|
38
|
+
border-radius: 12px;
|
|
39
|
+
text-align: center;
|
|
40
|
+
font-size: 13px;
|
|
41
|
+
line-height: 1.3;
|
|
42
|
+
color: var(--reltio-color-text);
|
|
43
|
+
text-decoration: none;
|
|
44
|
+
cursor: pointer;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.AppSelector_app__L29wd:hover {
|
|
48
|
+
background: var(--reltio-color-bg-transparent-1);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.AppSelector_appIcon__L29wd {
|
|
52
|
+
width: 48px;
|
|
53
|
+
height: 48px;
|
|
54
|
+
object-fit: contain;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.AppSelector_error__L29wd {
|
|
58
|
+
margin-bottom: -8px;
|
|
59
|
+
}
|
|
60
|
+
`);
|
|
61
|
+
export default json;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "content": "AppSelector_content__L29wd", "group": "AppSelector_group__L29wd", "grid": "AppSelector_grid__L29wd", "divider": "AppSelector_divider__L29wd", "app": "AppSelector_app__L29wd", "appIcon": "AppSelector_appIcon__L29wd", "error": "AppSelector_error__L29wd" }
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { HtmlProps } from "../../utils/types";
|
|
2
|
+
export type AppEntry = {
|
|
3
|
+
/** Application display name. Apps without a name are ignored. */
|
|
4
|
+
name?: string;
|
|
5
|
+
/** URL opened in a new tab on click. Apps without a URI are ignored. */
|
|
6
|
+
uri?: string;
|
|
7
|
+
/** Absolute URL to the application icon (SVG). Falls back to a generic link icon. */
|
|
8
|
+
icon?: string;
|
|
9
|
+
/** Application category for grouping in navigation.
|
|
10
|
+
* @default "Applications"
|
|
11
|
+
*/
|
|
12
|
+
category?: string;
|
|
13
|
+
};
|
|
14
|
+
export type AppSelectorProps = HtmlProps<"div", {
|
|
15
|
+
/** List of apps to display, grouped by category.
|
|
16
|
+
* Apps missing `name` or `uri` are silently ignored.
|
|
17
|
+
* The list of available apps for a given tenant
|
|
18
|
+
* can be retrieved from Reltio Config Service.
|
|
19
|
+
*/
|
|
20
|
+
apps: AppEntry[];
|
|
21
|
+
/** Environment identifier substituted into URI templates (`${environment}`). */
|
|
22
|
+
env?: string;
|
|
23
|
+
/** Tenant identifier substituted into URI templates (`${tenant}`). */
|
|
24
|
+
tenant?: string;
|
|
25
|
+
/** CSS `position-area` value controlling popover placement relative to the trigger.
|
|
26
|
+
* @default "right span-top"
|
|
27
|
+
*/
|
|
28
|
+
positionArea?: string;
|
|
29
|
+
}>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./AppSelector";
|
|
@@ -5,5 +5,8 @@ import type { ButtonProps } from "./Button.types";
|
|
|
5
5
|
* A flexible, accessible button component that supports multiple variants
|
|
6
6
|
* (filled, outlined, text), color options (primary, secondary, inherited),
|
|
7
7
|
* sizes, states, and can render as either a button or anchor element.
|
|
8
|
+
*
|
|
9
|
+
* Automatically switches to circular icon-only layout when children
|
|
10
|
+
* is a single React component element.
|
|
8
11
|
*/
|
|
9
12
|
export declare const Button: ({ variant, color, size, disabled, fullWidth, children, className, href, type, ...rest }: ButtonProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useRef } from "react";
|
|
2
|
+
import React, { useEffect, useRef } from "react";
|
|
3
3
|
import { classNames } from "../../utils/classNames";
|
|
4
4
|
import styles from "./Button.module.css";
|
|
5
5
|
/**
|
|
@@ -8,6 +8,9 @@ import styles from "./Button.module.css";
|
|
|
8
8
|
* A flexible, accessible button component that supports multiple variants
|
|
9
9
|
* (filled, outlined, text), color options (primary, secondary, inherited),
|
|
10
10
|
* sizes, states, and can render as either a button or anchor element.
|
|
11
|
+
*
|
|
12
|
+
* Automatically switches to circular icon-only layout when children
|
|
13
|
+
* is a single React component element.
|
|
11
14
|
*/
|
|
12
15
|
export const Button = ({ variant = "filled", color = "inherited", size = "medium", disabled = false, fullWidth = false, children, className, href, type = "button", ...rest }) => {
|
|
13
16
|
const buttonRef = useRef(null);
|
|
@@ -19,8 +22,11 @@ export const Button = ({ variant = "filled", color = "inherited", size = "medium
|
|
|
19
22
|
buttonRef.current.blur();
|
|
20
23
|
}
|
|
21
24
|
}, [disabled]);
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
const isIconOnly = React.Children.count(children) === 1 &&
|
|
26
|
+
React.isValidElement(children) &&
|
|
27
|
+
typeof children.type !== "string" &&
|
|
28
|
+
children.type !== React.Fragment;
|
|
29
|
+
const composedClassName = classNames(styles.root, styles[variant], styles[color], styles[size], disabled && styles.disabled, fullWidth && styles.fullWidth, isIconOnly && styles.iconOnly, className);
|
|
24
30
|
// Render as anchor if href is provided
|
|
25
31
|
if (href) {
|
|
26
32
|
return (_jsx("a", { ref: buttonRef, href: disabled ? undefined : href, className: composedClassName, "aria-disabled": disabled ? true : undefined, ...rest, children: children }));
|
|
@@ -115,20 +115,20 @@ styleInject(`
|
|
|
115
115
|
/* Size: Small */
|
|
116
116
|
.Button_small__L29wd {
|
|
117
117
|
min-height: 32px;
|
|
118
|
-
padding: 8px
|
|
118
|
+
padding: 8px 12px;
|
|
119
119
|
font-size: 0.875rem;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
/* Size: Medium */
|
|
123
123
|
.Button_medium__L29wd {
|
|
124
124
|
min-height: 40px;
|
|
125
|
-
padding: 12px
|
|
125
|
+
padding: 12px 18px;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
/* Size: Large */
|
|
129
129
|
.Button_large__L29wd {
|
|
130
130
|
min-height: 48px;
|
|
131
|
-
padding: 16px
|
|
131
|
+
padding: 16px 24px;
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
/* State: Disabled */
|
|
@@ -137,6 +137,12 @@ styleInject(`
|
|
|
137
137
|
cursor: not-allowed;
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
/* Icon Only */
|
|
141
|
+
.Button_iconOnly__L29wd {
|
|
142
|
+
aspect-ratio: 1;
|
|
143
|
+
padding: 0;
|
|
144
|
+
}
|
|
145
|
+
|
|
140
146
|
/* Full Width */
|
|
141
147
|
.Button_fullWidth__L29wd {
|
|
142
148
|
width: 100%;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{ "root": "Button_root__L29wd", "filled": "Button_filled__L29wd", "outlined": "Button_outlined__L29wd", "text": "Button_text__L29wd", "primary": "Button_primary__L29wd", "inherited": "Button_inherited__L29wd", "small": "Button_small__L29wd", "medium": "Button_medium__L29wd", "large": "Button_large__L29wd", "disabled": "Button_disabled__L29wd", "fullWidth": "Button_fullWidth__L29wd" }
|
|
1
|
+
{ "root": "Button_root__L29wd", "filled": "Button_filled__L29wd", "outlined": "Button_outlined__L29wd", "text": "Button_text__L29wd", "primary": "Button_primary__L29wd", "inherited": "Button_inherited__L29wd", "small": "Button_small__L29wd", "medium": "Button_medium__L29wd", "large": "Button_large__L29wd", "disabled": "Button_disabled__L29wd", "iconOnly": "Button_iconOnly__L29wd", "fullWidth": "Button_fullWidth__L29wd" }
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { classNames } from "../../utils/classNames";
|
|
3
|
+
import styles from "./Divider.module.css";
|
|
4
|
+
export const Divider = ({ align = "start", children, className, ...rest }) => {
|
|
5
|
+
return (_jsx("div", { role: "separator", className: classNames(styles.root, children ? styles.labeled : styles.plain, children ? styles[align] : undefined, className), ...rest, children: children }));
|
|
6
|
+
};
|