@lunit/oui 2.2.30 → 2.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.
@@ -1,3 +1,3 @@
1
1
  import type { AnalysisBarProps } from './AnalysisBar.types';
2
- declare const AnalysisBar: ({ componentType, expressionValue, caption, expressionLevels, segments, isDimmed, }: AnalysisBarProps) => import("react/jsx-runtime").JSX.Element;
2
+ declare const AnalysisBar: ({ componentType, expressionValue, caption, expressionLevels, expressionLabels, segments, isDimmed, }: AnalysisBarProps) => import("react/jsx-runtime").JSX.Element;
3
3
  export default AnalysisBar;
@@ -17,7 +17,7 @@ const Segments = ({ segments, adjustForOnePercent }) => {
17
17
  return (_jsx(GraphSegment, { color: segment.color, width: adjustForOnePercent ? adjustBarSegmentWidth(segment.width) : segment.width }, i));
18
18
  }) }));
19
19
  };
20
- const AnalysisBar = ({ componentType = 'analysisBar', expressionValue, caption, expressionLevels = [], segments, isDimmed = false, }) => {
20
+ const AnalysisBar = ({ componentType = 'analysisBar', expressionValue, caption, expressionLevels = [], expressionLabels = [], segments, isDimmed = false, }) => {
21
21
  if (!componentType)
22
22
  throw new Error("The 'componentType' prop is required.");
23
23
  if (!expressionLevels.length)
@@ -25,7 +25,7 @@ const AnalysisBar = ({ componentType = 'analysisBar', expressionValue, caption,
25
25
  const hasAdjustedOnePercentSegment = segments?.[0].width === 1;
26
26
  return (_jsxs(AnalysisBarOuter, { className: "analysis-bar", isDimmed: isDimmed, children: [_jsxs(AnalysisBarSegmentContainer, { children: [expressionValue && (_jsx(GraphScoreIndicator, { adjustForOnePercent: hasAdjustedOnePercentSegment, value: expressionValue, isJet: segments?.[0].color === 'jet' })), _jsx(Segments, { segments: segments, adjustForOnePercent: hasAdjustedOnePercentSegment })] }), (expressionLevels.length > 0 || caption) && (_jsxs(AxisLabels, { children: [expressionLevels.length > 0 &&
27
27
  expressionLevels.map((val, i) => {
28
- return (_jsx(GraphAxisLabel, { label: val, adjustForOnePercent: hasAdjustedOnePercentSegment, leftPos: parseStringValToNumber(val) }, i));
28
+ return (_jsx(GraphAxisLabel, { label: expressionLabels[i] || val, adjustForOnePercent: hasAdjustedOnePercentSegment, leftPos: parseStringValToNumber(val) }, i));
29
29
  }), caption && (_jsx(GraphAxisLabel, { label: caption, adjustForOnePercent: hasAdjustedOnePercentSegment, leftPos: 50 }))] }))] }));
30
30
  };
31
31
  export default AnalysisBar;
@@ -7,5 +7,6 @@ export interface AnalysisBarProps {
7
7
  width: number;
8
8
  }[];
9
9
  expressionValue?: string;
10
+ expressionLabels?: string[];
10
11
  isDimmed?: boolean;
11
12
  }
@@ -0,0 +1,3 @@
1
+ import { ModelTypeProps } from './ModelType.types';
2
+ declare const ModelType: ({ componentType, title, optionTitle, scoreItems, currentSensitivity, currentSpecificity, userModelType, options, description, onChangeDropdown, onClickUpdateConfiguration, }: ModelTypeProps) => import("react/jsx-runtime").JSX.Element;
3
+ export default ModelType;
@@ -0,0 +1,43 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { v4 as uuidV4 } from 'uuid';
4
+ import { Score } from '../Score';
5
+ import { Container } from './ModelType.styled';
6
+ import { FreeText } from '../FreeText';
7
+ import { HorizonDivider } from '../HorizonDivider';
8
+ import { PresentationTypography } from '../presentations.styled';
9
+ import { Button, Dropdown, DropdownItem, DropdownSubtitle } from '../../components';
10
+ const ModelType = ({ componentType = 'modelType', title, optionTitle, scoreItems, currentSensitivity, currentSpecificity, userModelType, options, description, onChangeDropdown, onClickUpdateConfiguration, }) => {
11
+ if (!componentType)
12
+ throw new Error("The 'componentType' prop is required.");
13
+ if (!title)
14
+ throw new Error("The 'title' prop is required.");
15
+ if (!Object.keys(scoreItems).length)
16
+ throw new Error("The 'scoreItems' prop must contain at least one item.");
17
+ if (!currentSensitivity)
18
+ throw new Error("The 'currentSensitivity' prop is required.");
19
+ if (!currentSpecificity)
20
+ throw new Error("The 'currentSpecificity' prop is required.");
21
+ if (!options.length)
22
+ throw new Error("The 'options' prop must contain at least one item.");
23
+ const [selectedItem, setSelectedItem] = useState(userModelType);
24
+ const [isModelTypeChanged, setIsModelTypeChanged] = useState(false);
25
+ const handleDropdownChange = (event) => {
26
+ onChangeDropdown(event);
27
+ const newOption = options.find((option) => option.target.annotation.dimension === event.target.value)?.target.annotation.dimension;
28
+ setSelectedItem(newOption);
29
+ setIsModelTypeChanged(userModelType !== newOption);
30
+ };
31
+ const handleUpdateConfiguration = () => {
32
+ onClickUpdateConfiguration();
33
+ setIsModelTypeChanged(false);
34
+ };
35
+ return (_jsxs(Container, { children: [scoreItems[selectedItem].map((item) => {
36
+ return (_jsx(Score, { ...item }, uuidV4()));
37
+ }), _jsx(HorizonDivider, { componentType: "divider" }), _jsx(PresentationTypography, { size: "medium-bold", children: title }), _jsx(Score, { ...currentSensitivity }), _jsx(Score, { ...currentSpecificity }), _jsxs(Dropdown, { value: selectedItem, onChange: handleDropdownChange, sx: { width: '258px' }, children: [optionTitle && _jsx(DropdownSubtitle, { label: optionTitle }), options.map((option) => {
38
+ return (_jsx(DropdownItem, { value: option.target.annotation.dimension, children: option.title }, option.target.annotation.dimension));
39
+ })] }), _jsx(Button, { id: "update-threshold-config-btn", label: "Update Configuration", sx: {
40
+ width: '100%',
41
+ }, disabled: !isModelTypeChanged, onClick: handleUpdateConfiguration }), description && description.length && (_jsx(FreeText, { componentType: "freeText", size: "small-dimmed", content: description }))] }));
42
+ };
43
+ export default ModelType;
@@ -0,0 +1,4 @@
1
+ /// <reference types="react" />
2
+ export declare const Container: import("@emotion/styled").StyledComponent<import("@mui/system").BoxOwnProps<import("@mui/material").Theme> & Omit<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
3
+ ref?: ((instance: HTMLDivElement | null) => void) | import("react").RefObject<HTMLDivElement> | null | undefined;
4
+ }, keyof import("@mui/system").BoxOwnProps<import("@mui/material").Theme>> & import("@mui/system").MUIStyledCommonProps<import("@mui/material").Theme>, {}, {}>;
@@ -0,0 +1,6 @@
1
+ import { Box, styled } from '@mui/material';
2
+ export const Container = styled(Box)({
3
+ display: 'flex',
4
+ flexDirection: 'column',
5
+ gap: '12px',
6
+ });
@@ -0,0 +1,31 @@
1
+ import { SelectChangeEvent } from '@mui/material/Select';
2
+ import { ScoreProps } from '../Score';
3
+ interface ModelTypeOption {
4
+ title: string;
5
+ target: {
6
+ visualizationType: string;
7
+ annotation: {
8
+ name: string;
9
+ dimension: string;
10
+ color: string;
11
+ };
12
+ };
13
+ sensitivity: number;
14
+ specificity: number;
15
+ }
16
+ export interface ModelTypeSpec {
17
+ componentType: 'modelType';
18
+ title: string;
19
+ optionTitle?: string;
20
+ scoreItems: Record<string, ScoreProps[]>;
21
+ currentSensitivity: ScoreProps;
22
+ currentSpecificity: ScoreProps;
23
+ options: ModelTypeOption[];
24
+ userModelType: string;
25
+ description?: string;
26
+ }
27
+ export interface ModelTypeProps extends ModelTypeSpec {
28
+ onChangeDropdown: (event: SelectChangeEvent) => void;
29
+ onClickUpdateConfiguration: () => void;
30
+ }
31
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ import ModelType from './ModelType';
2
+ import type { ModelTypeProps, ModelTypeSpec } from './ModelType.types';
3
+ export { ModelType };
4
+ export type { ModelTypeProps, ModelTypeSpec };
@@ -0,0 +1,2 @@
1
+ import ModelType from './ModelType';
2
+ export { ModelType };
@@ -0,0 +1,3 @@
1
+ import { type ModelTypeProps } from '../ModelType';
2
+ declare const ModelTypeControl: (modelType: ModelTypeProps) => import("react/jsx-runtime").JSX.Element;
3
+ export default ModelTypeControl;
@@ -0,0 +1,28 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useState } from 'react';
3
+ import { find as _find } from 'lodash';
4
+ import { ModelType } from '../ModelType';
5
+ const ModelTypeControl = (modelType) => {
6
+ const [modelTypeState, setModelTypeState] = useState(modelType);
7
+ const updateModelType = useCallback((type) => {
8
+ const currentModelType = { ...modelType };
9
+ const currentSensitivity = currentModelType.options.find((option) => option.target.annotation.dimension === type)?.sensitivity;
10
+ const currentSpecificity = currentModelType.options.find((option) => option.target.annotation.dimension === type)?.specificity;
11
+ currentModelType.currentSensitivity.metrics.value = currentSensitivity?.toString() || '0.0';
12
+ currentModelType.currentSpecificity.metrics.value = currentSpecificity?.toString() || '0.0';
13
+ setModelTypeState(currentModelType);
14
+ }, [modelType]);
15
+ useEffect(() => {
16
+ const item = _find(modelType.options, ['target.annotation.dimension', 'egfrBest']);
17
+ console.log(`default item: pixelAnnotations.${item?.target.annotation.name}.${item?.target.annotation.dimension}`);
18
+ updateModelType('egfrBest');
19
+ }, [updateModelType]);
20
+ const onChangeDropdown = (event) => {
21
+ const item = _find(modelType.options, ['target.annotation.dimension', event.target.value]);
22
+ console.log(`pixelAnnotations.${item?.target.annotation.name}.${item?.target.annotation.dimension}`);
23
+ updateModelType(event.target.value);
24
+ };
25
+ const onClickUpdateConfiguration = () => { };
26
+ return (_jsx(ModelType, { ...modelTypeState, userModelType: 'egfrBest', onChangeDropdown: onChangeDropdown, onClickUpdateConfiguration: onClickUpdateConfiguration }));
27
+ };
28
+ export default ModelTypeControl;
@@ -13,6 +13,7 @@ import { Score } from '../Score';
13
13
  import { FreeText } from '../FreeText';
14
14
  import { Histogram } from '../Histogram';
15
15
  import ThresholdControl from './ThresholdControl';
16
+ import ModelTypeControl from './ModelTypeControl';
16
17
  const Preview = ({ visualizationControls = [], analysisSummary = [] }) => {
17
18
  const [controls, setControls] = useState(visualizationControls);
18
19
  return (_jsxs(Container, { children: [_jsx(Box, { sx: { display: 'flex', flexDirection: 'column', gap: '16px', padding: '16px' }, children: visualizationControls.map((item, index) => {
@@ -55,6 +56,6 @@ const Preview = ({ visualizationControls = [], analysisSummary = [] }) => {
55
56
  });
56
57
  }
57
58
  return (_jsxs(Box, { children: [item.componentType === 'toggle' && (_jsx(ToggleControl, { ...item }, uuidV4())), item.componentType === 'toggleGroup' && (_jsx(ToggleControlGroup, { ...item }, uuidV4())), item.componentType === 'analysisBar' && (_jsx(AnalysisBar, { ...item }, uuidV4()))] }, uuidV4()));
58
- }) }), _jsx(HorizonDivider, { componentType: "divider" }), _jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: '16px', padding: '16px' }, children: [_jsx(FreeText, { componentType: "freeText", size: "medium-bold", content: "Analysis Summary" }), analysisSummary.map((item) => (_jsxs(Box, { children: [item.componentType === 'accordion' && (_jsx(Accordion, { ...item }, uuidV4())), item.componentType === 'analysisBar' && (_jsx(AnalysisBar, { ...item }, uuidV4())), item.componentType === 'statistic' && (_jsx(Statistic, { ...item }, uuidV4())), item.componentType === 'score' && (_jsx(Score, { ...item }, uuidV4())), item.componentType === 'freeText' && (_jsx(FreeText, { ...item }, uuidV4())), item.componentType === 'threshold' && (_jsx(ThresholdControl, { ...item }, uuidV4())), item.componentType === 'histogram' && (_jsx(Histogram, { ...item }, uuidV4()))] }, uuidV4())))] })] }));
59
+ }) }), _jsx(HorizonDivider, { componentType: "divider" }), _jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: '16px', padding: '16px' }, children: [_jsx(FreeText, { componentType: "freeText", size: "medium-bold", content: "Analysis Summary" }), analysisSummary.map((item) => (_jsxs(Box, { children: [item.componentType === 'accordion' && (_jsx(Accordion, { ...item }, uuidV4())), item.componentType === 'analysisBar' && (_jsx(AnalysisBar, { ...item }, uuidV4())), item.componentType === 'statistic' && (_jsx(Statistic, { ...item }, uuidV4())), item.componentType === 'score' && (_jsx(Score, { ...item }, uuidV4())), item.componentType === 'freeText' && (_jsx(FreeText, { ...item }, uuidV4())), item.componentType === 'threshold' && (_jsx(ThresholdControl, { ...item }, uuidV4())), item.componentType === 'histogram' && (_jsx(Histogram, { ...item }, uuidV4())), item.componentType === 'modelType' && (_jsx(ModelTypeControl, { ...item }, uuidV4()))] }, uuidV4())))] })] }));
59
60
  };
60
61
  export default Preview;
@@ -7,8 +7,9 @@ import { AnalysisBarProps } from '../AnalysisBar';
7
7
  import { FreeTextProps } from '../FreeText';
8
8
  import { ThresholdProps } from '../Threshold';
9
9
  import { HistogramProps } from '../Histogram';
10
+ import { ModelTypeProps } from '../ModelType';
10
11
  interface PreviewProps {
11
12
  visualizationControls: (ToggleControlProps | ToggleControlGroupProps | AnalysisBarProps)[];
12
- analysisSummary: (AccordionProps | AnalysisBarProps | StatisticProps | ScoreProps | FreeTextProps | ThresholdProps | HistogramProps)[];
13
+ analysisSummary: (AccordionProps | AnalysisBarProps | StatisticProps | ScoreProps | FreeTextProps | ThresholdProps | HistogramProps | ModelTypeProps)[];
13
14
  }
14
15
  export type { PreviewProps };
@@ -10,6 +10,6 @@ const Score = ({ componentType = 'score', title, size = 'medium', metrics }) =>
10
10
  throw new Error("The 'title' prop is required.");
11
11
  if (!metrics)
12
12
  throw new Error("The 'metrics' prop is required.");
13
- return (_jsxs(PresentationTypography, { size: size, style: { color: metrics.isFullColored ? metrics.color : 'inherit' }, children: [title, ": ", metrics.value, metrics.scoreType && (_jsx("span", { style: { color: metrics.color, marginLeft: 4 }, children: capitalize(metrics.scoreType) }))] }));
13
+ return (_jsx(PresentationTypography, { size: size, children: _jsxs("span", { style: { color: metrics.isFullColored ? metrics.color : 'inherit' }, children: [title, ": ", metrics.value, metrics.scoreType && (_jsx("span", { style: { color: metrics.color, marginLeft: 4 }, children: capitalize(metrics.scoreType) }))] }) }));
14
14
  };
15
15
  export default Score;
@@ -25,7 +25,9 @@ const Threshold = ({ componentType = 'threshold', title, totalScore, totalCells,
25
25
  const marks = getSliderMarks(presetValue);
26
26
  const handleDropdownChange = (event) => {
27
27
  onChangeDropdown(event);
28
- setSelectedOptionValue(options.find((option) => option.title === event.target.value)?.value);
28
+ const newOption = options.find((option) => option.title === event.target.value)?.value;
29
+ setSelectedOptionValue(newOption);
30
+ setIsThresholdChanged(false);
29
31
  };
30
32
  const handleSliderChange = (event, value) => {
31
33
  onChangeSlider(event, value);
@@ -22,6 +22,6 @@ const ToggleControl = ({ componentType = 'toggle', title, size = 'medium', symbo
22
22
  return (_jsxs(ToggleControlOuter, { "data-toggle-name": `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, 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' }, 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;
@@ -3,6 +3,7 @@ export * from './AnalysisBar';
3
3
  export * from './FreeText';
4
4
  export * from './Histogram';
5
5
  export * from './HorizonDivider';
6
+ export * from './ModelType';
6
7
  export * from './Preview';
7
8
  export * from './Score';
8
9
  export * from './Statistic';
@@ -3,6 +3,7 @@ export * from './AnalysisBar';
3
3
  export * from './FreeText';
4
4
  export * from './Histogram';
5
5
  export * from './HorizonDivider';
6
+ export * from './ModelType';
6
7
  export * from './Preview';
7
8
  export * from './Score';
8
9
  export * from './Statistic';
@@ -19,7 +19,7 @@ export const PresentationTypography = (props) => {
19
19
  return (_jsx(Typography, { variant: "h9", ...rest, style: { color: isDimmed ? theme.palette.neutralGrey[40] : 'inherit' } }));
20
20
  }
21
21
  // Fallback to medium size
22
- return (_jsx(Typography, { variant: "body5", ...rest, sx: { color: isDimmed ? theme.palette.neutralGrey[40] : 'inherit' } }));
22
+ return (_jsx(Typography, { variant: "body5", ...rest, style: { color: isDimmed ? theme.palette.neutralGrey[40] : 'inherit' } }));
23
23
  };
24
24
  export const PresentationSymbolIcon = ({ symbol, color = 'black', isDimmed = false, }) => {
25
25
  const symbolColor = color?.length ? color : 'black';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunit/oui",
3
- "version": "2.2.30",
3
+ "version": "2.3.0",
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)"