@lunit/oui 2.4.8 → 2.4.10
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/dist/presentations/AnalysisBar/AnalysisBar.js +5 -5
- package/dist/presentations/AnalysisBar/AnalysisBar.styled.js +1 -1
- package/dist/presentations/AnalysisBar/AnalysisBar.utils.d.ts +13 -0
- package/dist/presentations/AnalysisBar/AnalysisBar.utils.js +41 -0
- package/dist/presentations/Threshold/Threshold.js +11 -10
- package/dist/presentations/ToggleControl/ToggleControl.js +1 -1
- package/package.json +2 -2
|
@@ -2,14 +2,14 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
2
2
|
import { findIndex as _findIndex } from 'lodash';
|
|
3
3
|
import { Box } from '@mui/material';
|
|
4
4
|
import { AnalysisBarSegmentContainer, AnalysisBarOuter, AxisLabels, GraphAxisLabel, GraphScoreIndicator, GraphSegment, } from './AnalysisBar.styled';
|
|
5
|
-
import { parseStringValToNumber } from './AnalysisBar.utils';
|
|
5
|
+
import { parseStringValToNumber, normalizeSegmentWidths } from './AnalysisBar.utils';
|
|
6
6
|
import HeatmapGraphSVG from './HeatmapGraphIcon/HeatmapGraphSVG';
|
|
7
7
|
const Segments = ({ segments, isCutoff }) => {
|
|
8
8
|
if (!segments) {
|
|
9
9
|
return null;
|
|
10
10
|
}
|
|
11
11
|
const validSegments = segments.filter((segment) => segment.width > 0);
|
|
12
|
-
const combinedWidth = segments.
|
|
12
|
+
const combinedWidth = segments.reduce((sum, segment) => sum + segment.width, 0);
|
|
13
13
|
const onePercentSegmentIndex = _findIndex(segments, (segment) => segment.width > 0 && segment.width <= 1);
|
|
14
14
|
if (segments?.length === 1 && segments[0].color === 'jet') {
|
|
15
15
|
return (_jsx(Box, { sx: { width: '100%', height: '14px', transform: 'scaleY(0.8)' }, children: _jsx(HeatmapGraphSVG, {}) }));
|
|
@@ -18,7 +18,7 @@ const Segments = ({ segments, isCutoff }) => {
|
|
|
18
18
|
if (segment.color === 'jet') {
|
|
19
19
|
return _jsx(HeatmapGraphSVG, { width: `${segment.width}%` });
|
|
20
20
|
}
|
|
21
|
-
return (_jsx(GraphSegment, { color: segment.color, width: segment.width, isFirstSegment: i === 0, isLastSegment: i ===
|
|
21
|
+
return (_jsx(GraphSegment, { color: segment.color, width: segment.width, isFirstSegment: i === 0, isLastSegment: i === validSegments.length - 1, isCutoff: isCutoff, combinedWidth: combinedWidth, isNextSegment: onePercentSegmentIndex >= 0 && i === onePercentSegmentIndex + 1, isOnePercentSegment: segment.width <= 1 }, i));
|
|
22
22
|
}) }));
|
|
23
23
|
};
|
|
24
24
|
const AnalysisBar = ({ componentType = 'analysisBar', isCutoff = false, expressionValue, caption, expressionLevels = [], expressionLabels = [], segments, isDimmed = false, }) => {
|
|
@@ -26,8 +26,8 @@ const AnalysisBar = ({ componentType = 'analysisBar', isCutoff = false, expressi
|
|
|
26
26
|
throw new Error("The 'componentType' prop is required.");
|
|
27
27
|
if (!expressionLevels.length)
|
|
28
28
|
throw new Error("The 'expressionLevels' prop must contain at least one item.");
|
|
29
|
-
const
|
|
30
|
-
return (_jsxs(AnalysisBarOuter, { className: "analysis-bar", isDimmed: isDimmed, children: [_jsxs(AnalysisBarSegmentContainer, { children: [expressionValue && (_jsx(GraphScoreIndicator, { value: expressionValue, isCutoff: isCutoff, isJet:
|
|
29
|
+
const normalizedSegments = normalizeSegmentWidths(segments, isCutoff);
|
|
30
|
+
return (_jsxs(AnalysisBarOuter, { className: "analysis-bar", isDimmed: isDimmed, children: [_jsxs(AnalysisBarSegmentContainer, { children: [expressionValue && (_jsx(GraphScoreIndicator, { value: expressionValue, isCutoff: isCutoff, isJet: normalizedSegments?.[0]?.color === 'jet' })), _jsx(Segments, { segments: normalizedSegments, isCutoff: isCutoff })] }), (expressionLevels.length > 0 || caption) && (_jsxs(AxisLabels, { children: [expressionLevels.length > 0 &&
|
|
31
31
|
expressionLevels.map((val, i) => {
|
|
32
32
|
return (_jsx(GraphAxisLabel, { label: expressionLabels[i] || val, isCutoff: isCutoff, leftPos: parseStringValToNumber(val) }, i));
|
|
33
33
|
}), caption && (_jsx(GraphAxisLabel, { label: caption, isCutoff: isCutoff, leftPos: 50 }))] }))] }));
|
|
@@ -105,7 +105,7 @@ const segmentStyle = (color, width, isFirstSegment, isLastSegment, isCutoff, com
|
|
|
105
105
|
borderBottomRightRadius: isLastSegment ? '2px' : '0px',
|
|
106
106
|
width: `${width}%`,
|
|
107
107
|
};
|
|
108
|
-
if (isOnePercentSegment) {
|
|
108
|
+
if (isOnePercentSegment && isCutoff) {
|
|
109
109
|
return {
|
|
110
110
|
...baseStyle,
|
|
111
111
|
width: '12px',
|
|
@@ -1,3 +1,16 @@
|
|
|
1
1
|
export declare function parseStringValToNumber(val: string): number;
|
|
2
2
|
export declare function clampNumericalPercentage(value: number): number;
|
|
3
3
|
export declare function clampBarLabelPosition(value: number): number;
|
|
4
|
+
/**
|
|
5
|
+
* @description normalizeSegmentWidths is a function that normalizes the width of the segments in the analysis bar.
|
|
6
|
+
* @param segments - The segments to normalize.
|
|
7
|
+
* @param isCutoff - Whether the analysis bar is a cutoff.
|
|
8
|
+
* @returns The normalized segments.
|
|
9
|
+
*/
|
|
10
|
+
export declare function normalizeSegmentWidths(segments: {
|
|
11
|
+
color: 'jet' | string;
|
|
12
|
+
width: number;
|
|
13
|
+
}[], isCutoff?: boolean): {
|
|
14
|
+
color: 'jet' | string;
|
|
15
|
+
width: number;
|
|
16
|
+
}[];
|
|
@@ -16,3 +16,44 @@ export function clampBarLabelPosition(value) {
|
|
|
16
16
|
return 1;
|
|
17
17
|
return value;
|
|
18
18
|
}
|
|
19
|
+
const round = (value) => Math.round(value * 10) / 10;
|
|
20
|
+
/**
|
|
21
|
+
* @description normalizeSegmentWidths is a function that normalizes the width of the segments in the analysis bar.
|
|
22
|
+
* @param segments - The segments to normalize.
|
|
23
|
+
* @param isCutoff - Whether the analysis bar is a cutoff.
|
|
24
|
+
* @returns The normalized segments.
|
|
25
|
+
*/
|
|
26
|
+
export function normalizeSegmentWidths(segments, isCutoff) {
|
|
27
|
+
if (isCutoff)
|
|
28
|
+
return segments;
|
|
29
|
+
let validSegments = segments
|
|
30
|
+
.map((segment) => ({ ...segment, width: round(segment.width) }))
|
|
31
|
+
.filter((segment) => segment.width > 0);
|
|
32
|
+
if (validSegments.length === 0)
|
|
33
|
+
return [];
|
|
34
|
+
const normalize = (segments) => {
|
|
35
|
+
const total = segments.reduce((sum, s) => sum + s.width, 0);
|
|
36
|
+
if (total === 0 || Math.abs(total - 100) < 0.1)
|
|
37
|
+
return segments;
|
|
38
|
+
const normalized = segments.map((segment) => ({
|
|
39
|
+
...segment,
|
|
40
|
+
width: round((segment.width / total) * 100),
|
|
41
|
+
}));
|
|
42
|
+
const normalizedTotal = normalized.reduce((sum, s) => sum + s.width, 0);
|
|
43
|
+
const difference = 100 - normalizedTotal;
|
|
44
|
+
if (normalized.length > 0 && Math.abs(difference) > 0.01) {
|
|
45
|
+
const lastIndex = normalized.length - 1;
|
|
46
|
+
normalized[lastIndex] = {
|
|
47
|
+
...normalized[lastIndex],
|
|
48
|
+
width: round(normalized[lastIndex].width + difference),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return normalized;
|
|
52
|
+
};
|
|
53
|
+
validSegments = normalize(validSegments);
|
|
54
|
+
const filtered = validSegments.filter((segment) => segment.width > 0);
|
|
55
|
+
if (filtered.length < validSegments.length && filtered.length > 0) {
|
|
56
|
+
return normalize(filtered);
|
|
57
|
+
}
|
|
58
|
+
return filtered;
|
|
59
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from 'react';
|
|
2
|
+
import { useState, useMemo, useCallback } from 'react';
|
|
3
3
|
import { v4 as uuidV4 } from 'uuid';
|
|
4
4
|
import { Box } from '@mui/material';
|
|
5
5
|
import { BaseSlider, Container } from './Threshold.styled';
|
|
@@ -20,22 +20,23 @@ const Threshold = ({ componentType = 'threshold', title, totalScore, totalCells,
|
|
|
20
20
|
if (!options.length)
|
|
21
21
|
throw new Error("The 'options' prop must contain at least one item.");
|
|
22
22
|
const [isThresholdChanged, setIsThresholdChanged] = useState(false);
|
|
23
|
-
const
|
|
23
|
+
const selectedOptionValue = useMemo(() => {
|
|
24
|
+
return (options.find((option) => option.title === thresholdState?.selectedItem)?.value ||
|
|
25
|
+
options[0].value);
|
|
26
|
+
}, [options, thresholdState?.selectedItem]);
|
|
24
27
|
const presetValue = getPresetValue(options, thresholdState?.selectedItem);
|
|
25
|
-
const handleDropdownChange = (event) => {
|
|
28
|
+
const handleDropdownChange = useCallback((event) => {
|
|
26
29
|
onChangeDropdown(event);
|
|
27
|
-
const newOption = options.find((option) => option.title === event.target.value)?.value;
|
|
28
|
-
setSelectedOptionValue(newOption);
|
|
29
30
|
setIsThresholdChanged(false);
|
|
30
|
-
};
|
|
31
|
-
const handleSliderChange = (event, value) => {
|
|
31
|
+
}, [onChangeDropdown]);
|
|
32
|
+
const handleSliderChange = useCallback((event, value) => {
|
|
32
33
|
onChangeSlider(event, value);
|
|
33
34
|
setIsThresholdChanged(value !== selectedOptionValue);
|
|
34
|
-
};
|
|
35
|
-
const handleUpdateConfiguration = () => {
|
|
35
|
+
}, [onChangeSlider, selectedOptionValue]);
|
|
36
|
+
const handleUpdateConfiguration = useCallback(() => {
|
|
36
37
|
onClickUpdateConfiguration();
|
|
37
38
|
setIsThresholdChanged(false);
|
|
38
|
-
};
|
|
39
|
+
}, [onClickUpdateConfiguration]);
|
|
39
40
|
return (_jsxs(Container, { children: [_jsx(Score, { ...totalScore }), _jsx(Statistic, { ...totalCells }), statisticItems.map((item) => {
|
|
40
41
|
return (_jsx(Statistic, { ...item }, uuidV4()));
|
|
41
42
|
}), _jsx(HorizonDivider, { componentType: "divider" }), _jsx(PresentationTypography, { size: "medium", children: title }), _jsx(Dropdown, { value: thresholdState?.selectedItem, onChange: handleDropdownChange, sx: { width: '258px' }, children: options.map((option) => {
|
|
@@ -22,6 +22,6 @@ const ToggleControl = ({ componentType = 'toggle', title, size = 'medium', symbo
|
|
|
22
22
|
return (_jsxs(ToggleControlOuter, { "data-testid": `toggle-control-${title?.replace(/\s/g, '-')}`, hasBar: !!bar || !!content.length, style: { marginBottom: isParent ? '4px' : '0' }, children: [_jsxs(ToggleControlContainer, { className: "toggle-inner-container", size: size, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: '6px' }, className: "toggle-label", children: [!!tooltip.length && (_jsx(Tooltip, { arrow: true, size: "large", title: _jsx(Box, { sx: { display: 'flex', flexDirection: 'column', gap: '8px' }, children: tooltip.map((item) => (_jsxs(Box, { children: [item.componentType === 'freeText' && (_jsx(FreeText, { componentType: item.componentType, size: item.size, content: item.content })), item.componentType === 'statistic' && (_jsx(Statistic, { componentType: item.componentType, title: item.title, size: item.size, layout: item.layout, symbol: item.symbol, color: item.color }))] }, uuidV4()))) }), children: _jsx(Box, { sx: {
|
|
23
23
|
display: 'flex',
|
|
24
24
|
alignItems: 'center',
|
|
25
|
-
}, children: _jsx(Information, {}) }) })), symbol && (_jsx(PresentationSymbolIcon, { symbol: symbol, color: color, isDimmed: isDimmed || !checked })), _jsx(PresentationTypography, { size: size, isDimmed: isDimmed || !checked, children: title })] }), _jsx(StyledToggle, { disabled: disabled || isDimmed, checked: checked, toggleColor: theme.palette.lunitTeal[70], onChange: onChange })] }), content.length > 0 && (_jsx(Box, { sx: { display: 'flex', flexDirection: 'column', gap: '8px' }, children: content.map((item) => (_jsx(Statistic, { ...item, isDimmed: isDimmed || !checked }, uuidV4()))) }, uuidV4())), bar && (_jsx(Box, { sx: { padding: '0 8px', fontSize: '14px', lineHeight: '8px' }, children: _jsx(AnalysisBar, { isDimmed: isDimmed || !checked, componentType: bar.componentType, expressionValue: bar.expressionValue, expressionLevels: bar.expressionLevels, expressionLabels: bar.expressionLabels, caption: bar.caption, segments: bar.segments }) }))] }));
|
|
25
|
+
}, children: _jsx(Information, {}) }) })), symbol && (_jsx(PresentationSymbolIcon, { symbol: symbol, color: color, isDimmed: isDimmed || !checked })), _jsx(PresentationTypography, { size: size, isDimmed: isDimmed || !checked, children: title })] }), _jsx(StyledToggle, { disabled: disabled || isDimmed, checked: checked, toggleColor: theme.palette.lunitTeal[70], onChange: onChange })] }), content.length > 0 && (_jsx(Box, { sx: { display: 'flex', flexDirection: 'column', gap: '8px' }, children: content.map((item) => (_jsx(Statistic, { ...item, isDimmed: isDimmed || !checked }, uuidV4()))) }, uuidV4())), bar && (_jsx(Box, { sx: { padding: '0 8px', fontSize: '14px', lineHeight: '8px', width: '100%' }, children: _jsx(AnalysisBar, { isDimmed: isDimmed || !checked, componentType: bar.componentType, expressionValue: bar.expressionValue, expressionLevels: bar.expressionLevels, expressionLabels: bar.expressionLabels, caption: bar.caption, segments: bar.segments }) }))] }));
|
|
26
26
|
};
|
|
27
27
|
export default ToggleControl;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunit/oui",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.10",
|
|
4
4
|
"validate-branch-name": {
|
|
5
5
|
"pattern": "^(main|develop)|(feature|fix|release|hotfix|qe)/.+$",
|
|
6
6
|
"errorMsg": "The branch name is not correct. Please check the pattern. (ex. feature/add-something)"
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"@emotion/react": "^11.11.1",
|
|
12
12
|
"@emotion/styled": "^11.11.0",
|
|
13
13
|
"@lunit/design-system-icons": "^2.1.0",
|
|
14
|
-
"@lunit/design-system-logo": "
|
|
14
|
+
"@lunit/design-system-logo": "1.1.1",
|
|
15
15
|
"@mui/icons-material": "^5.15.2",
|
|
16
16
|
"@mui/lab": "^5.0.0-alpha.158",
|
|
17
17
|
"@mui/material": "^5.15.2",
|