ui(component): Tooltip component ui-next (#4635)
Co-authored-by: sedghi <ar.sedghi@gmail.com>
This commit is contained in:
parent
03ad79eaed
commit
b54f2075ec
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Icon, Tooltip, ViewportActionButton } from '@ohif/ui';
|
import { ViewportActionButton } from '@ohif/ui';
|
||||||
import { Icons } from '@ohif/ui-next';
|
import { Icons, Tooltip, TooltipTrigger, TooltipContent } from '@ohif/ui-next';
|
||||||
|
|
||||||
export default function _getStatusComponent({ isHydrated, onStatusClick }) {
|
export default function _getStatusComponent({ isHydrated, onStatusClick }) {
|
||||||
let ToolTipMessage = null;
|
let ToolTipMessage = null;
|
||||||
@ -10,7 +10,6 @@ export default function _getStatusComponent({ isHydrated, onStatusClick }) {
|
|||||||
switch (isHydrated) {
|
switch (isHydrated) {
|
||||||
case true:
|
case true:
|
||||||
StatusIcon = () => <Icons.ByName name="status-alert" />;
|
StatusIcon = () => <Icons.ByName name="status-alert" />;
|
||||||
|
|
||||||
ToolTipMessage = () => <div>This Segmentation is loaded in the segmentation panel</div>;
|
ToolTipMessage = () => <div>This Segmentation is loaded in the segmentation panel</div>;
|
||||||
break;
|
break;
|
||||||
case false:
|
case false:
|
||||||
@ -20,7 +19,6 @@ export default function _getStatusComponent({ isHydrated, onStatusClick }) {
|
|||||||
name="status-untracked"
|
name="status-untracked"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
ToolTipMessage = () => <div>Click LOAD to load RTSTRUCT.</div>;
|
ToolTipMessage = () => <div>Click LOAD to load RTSTRUCT.</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,14 +42,18 @@ export default function _getStatusComponent({ isHydrated, onStatusClick }) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{ToolTipMessage && (
|
{ToolTipMessage && (
|
||||||
<Tooltip
|
<Tooltip>
|
||||||
content={<ToolTipMessage />}
|
<TooltipTrigger asChild>
|
||||||
position="bottom-left"
|
<span>
|
||||||
>
|
<StatusArea />
|
||||||
<StatusArea />
|
</span>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent side="bottom">
|
||||||
|
<ToolTipMessage />
|
||||||
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
{!ToolTipMessage && <StatusArea />}
|
{!ToolTipMessage && <StatusArea />}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Tooltip, ViewportActionButton } from '@ohif/ui';
|
import { ViewportActionButton } from '@ohif/ui';
|
||||||
import { Icons } from '@ohif/ui-next';
|
import { Icons, Tooltip, TooltipTrigger, TooltipContent } from '@ohif/ui-next';
|
||||||
|
|
||||||
export default function _getStatusComponent({ isHydrated, onStatusClick }) {
|
export default function _getStatusComponent({ isHydrated, onStatusClick }) {
|
||||||
let ToolTipMessage = null;
|
let ToolTipMessage = null;
|
||||||
@ -10,7 +10,6 @@ export default function _getStatusComponent({ isHydrated, onStatusClick }) {
|
|||||||
switch (isHydrated) {
|
switch (isHydrated) {
|
||||||
case true:
|
case true:
|
||||||
StatusIcon = () => <Icons.ByName name="status-alert" />;
|
StatusIcon = () => <Icons.ByName name="status-alert" />;
|
||||||
|
|
||||||
ToolTipMessage = () => <div>This Segmentation is loaded in the segmentation panel</div>;
|
ToolTipMessage = () => <div>This Segmentation is loaded in the segmentation panel</div>;
|
||||||
break;
|
break;
|
||||||
case false:
|
case false:
|
||||||
@ -20,7 +19,6 @@ export default function _getStatusComponent({ isHydrated, onStatusClick }) {
|
|||||||
name="status-untracked"
|
name="status-untracked"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
ToolTipMessage = () => <div>Click LOAD to load segmentation.</div>;
|
ToolTipMessage = () => <div>Click LOAD to load segmentation.</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,14 +42,18 @@ export default function _getStatusComponent({ isHydrated, onStatusClick }) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{ToolTipMessage && (
|
{ToolTipMessage && (
|
||||||
<Tooltip
|
<Tooltip>
|
||||||
content={<ToolTipMessage />}
|
<TooltipTrigger asChild>
|
||||||
position="bottom-left"
|
<span>
|
||||||
>
|
<StatusArea />
|
||||||
<StatusArea />
|
</span>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent side="bottom">
|
||||||
|
<ToolTipMessage />
|
||||||
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
{!ToolTipMessage && <StatusArea />}
|
{!ToolTipMessage && <StatusArea />}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -5,10 +5,11 @@ import { ExtensionManager, useToolbar } from '@ohif/core';
|
|||||||
|
|
||||||
import { setTrackingUniqueIdentifiersForElement } from '../tools/modules/dicomSRModule';
|
import { setTrackingUniqueIdentifiersForElement } from '../tools/modules/dicomSRModule';
|
||||||
|
|
||||||
import { Icon, Tooltip, useViewportGrid, ViewportActionArrows } from '@ohif/ui';
|
|
||||||
import createReferencedImageDisplaySet from '../utils/createReferencedImageDisplaySet';
|
import createReferencedImageDisplaySet from '../utils/createReferencedImageDisplaySet';
|
||||||
import { usePositionPresentationStore } from '@ohif/extension-cornerstone';
|
import { usePositionPresentationStore } from '@ohif/extension-cornerstone';
|
||||||
import { Icons } from '@ohif/ui-next';
|
import { useViewportGrid, ViewportActionArrows } from '@ohif/ui';
|
||||||
|
import { Icons, Tooltip, TooltipTrigger, TooltipContent } from '@ohif/ui-next';
|
||||||
|
|
||||||
const MEASUREMENT_TRACKING_EXTENSION_ID = '@ohif/extension-measurement-tracking';
|
const MEASUREMENT_TRACKING_EXTENSION_ID = '@ohif/extension-measurement-tracking';
|
||||||
|
|
||||||
const SR_TOOLGROUP_BASE_NAME = 'SRToolGroup';
|
const SR_TOOLGROUP_BASE_NAME = 'SRToolGroup';
|
||||||
@ -467,11 +468,18 @@ function _getStatusComponent({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{ToolTipMessage && (
|
{ToolTipMessage && (
|
||||||
<Tooltip
|
<Tooltip>
|
||||||
content={<ToolTipMessage />}
|
<TooltipTrigger asChild>
|
||||||
position="bottom-left"
|
<span>
|
||||||
>
|
<StatusArea />
|
||||||
<StatusArea />
|
</span>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent
|
||||||
|
side="bottom"
|
||||||
|
align="start"
|
||||||
|
>
|
||||||
|
<ToolTipMessage />
|
||||||
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
{!ToolTipMessage && <StatusArea />}
|
{!ToolTipMessage && <StatusArea />}
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Button, PanelSection, ButtonGroup, IconButton, InputNumber, Tooltip } from '@ohif/ui';
|
import { Button, PanelSection, ButtonGroup, IconButton, InputNumber } from '@ohif/ui';
|
||||||
|
import { DoubleSlider, Icons, Tooltip, TooltipTrigger, TooltipContent } from '@ohif/ui-next';
|
||||||
import { DoubleSlider, Icons } from '@ohif/ui-next';
|
|
||||||
|
|
||||||
import { Enums } from '@cornerstonejs/core';
|
import { Enums } from '@cornerstonejs/core';
|
||||||
|
|
||||||
const controlClassNames = {
|
const controlClassNames = {
|
||||||
@ -13,16 +11,18 @@ const controlClassNames = {
|
|||||||
|
|
||||||
const Header = ({ title, tooltip }) => (
|
const Header = ({ title, tooltip }) => (
|
||||||
<div className="flex items-center space-x-1">
|
<div className="flex items-center space-x-1">
|
||||||
<Tooltip
|
<Tooltip>
|
||||||
content={<div className="text-white">{tooltip}</div>}
|
<TooltipTrigger asChild>
|
||||||
position="bottom-left"
|
<span>
|
||||||
tight={true}
|
<Icons.ByName
|
||||||
tooltipBoxClassName="max-w-xs p-2"
|
name="info-link"
|
||||||
>
|
className="text-primary-active h-[14px] w-[14px]"
|
||||||
<Icons.ByName
|
/>
|
||||||
name="info-link"
|
</span>
|
||||||
className="text-primary-active h-[14px] w-[14px]"
|
</TooltipTrigger>
|
||||||
/>
|
<TooltipContent sideOffset={4} className="max-w-xs p-2 bg-primary-dark text-white">
|
||||||
|
<div>{tooltip}</div>
|
||||||
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<span className="text-aqua-pale text-[11px] uppercase">{title}</span>
|
<span className="text-aqua-pale text-[11px] uppercase">{title}</span>
|
||||||
</div>
|
</div>
|
||||||
@ -45,9 +45,7 @@ const DynamicVolumeControls = ({
|
|||||||
onDynamicClick,
|
onDynamicClick,
|
||||||
}) => {
|
}) => {
|
||||||
const [computedView, setComputedView] = useState(false);
|
const [computedView, setComputedView] = useState(false);
|
||||||
|
|
||||||
const [computeViewMode, setComputeViewMode] = useState(Enums.DynamicOperatorType.SUM);
|
const [computeViewMode, setComputeViewMode] = useState(Enums.DynamicOperatorType.SUM);
|
||||||
|
|
||||||
const [sliderRangeValues, setSliderRangeValues] = useState([0, framesLength - 1]);
|
const [sliderRangeValues, setSliderRangeValues] = useState([0, framesLength - 1]);
|
||||||
|
|
||||||
const handleSliderChange = newValues => {
|
const handleSliderChange = newValues => {
|
||||||
@ -113,15 +111,14 @@ const DynamicVolumeControls = ({
|
|||||||
<div>
|
<div>
|
||||||
Operation Buttons (SUM, AVERAGE, SUBTRACT): Select the mathematical operation to be
|
Operation Buttons (SUM, AVERAGE, SUBTRACT): Select the mathematical operation to be
|
||||||
applied to the data set.
|
applied to the data set.
|
||||||
<br></br> Range Slider: Choose the numeric range within which the operation will be
|
<br /> Range Slider: Choose the numeric range within which the operation will be
|
||||||
performed.
|
performed.
|
||||||
<br></br>Generate Button: Execute the chosen operation on the specified range of
|
<br />Generate Button: Execute the chosen operation on the specified range of data.
|
||||||
data.{' '}
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<ButtonGroup
|
<ButtonGroup
|
||||||
className={`mt-2 w-full`}
|
className="mt-2 w-full"
|
||||||
separated={true}
|
separated={true}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
@ -190,8 +187,8 @@ function FrameControls({
|
|||||||
title="4D Controls"
|
title="4D Controls"
|
||||||
tooltip={
|
tooltip={
|
||||||
<div>
|
<div>
|
||||||
Play/Pause Button: Begin or pause the animation of the 4D visualization. <br></br> Frame
|
Play/Pause Button: Begin or pause the animation of the 4D visualization. <br /> Frame
|
||||||
Selector: Navigate through individual frames of the 4D data. <br></br> FPS (Frames Per
|
Selector: Navigate through individual frames of the 4D data. <br /> FPS (Frames Per
|
||||||
Second) Selector: Adjust the playback speed of the animation.
|
Second) Selector: Adjust the playback speed of the animation.
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@ -225,4 +222,4 @@ function FrameControls({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1,8 +1,8 @@
|
|||||||
import React, { useState, useEffect, useCallback } from 'react';
|
import React, { useState, useEffect, useCallback } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { Tooltip, ViewportActionArrows, useViewportGrid } from '@ohif/ui';
|
import { ViewportActionArrows, useViewportGrid } from '@ohif/ui';
|
||||||
import { Icons } from '@ohif/ui-next';
|
import { Icons, Tooltip, TooltipTrigger, TooltipContent } from '@ohif/ui-next';
|
||||||
|
|
||||||
import { annotation } from '@cornerstonejs/tools';
|
import { annotation } from '@cornerstonejs/tools';
|
||||||
import { useTrackedMeasurements } from './../getContextModule';
|
import { useTrackedMeasurements } from './../getContextModule';
|
||||||
@ -235,11 +235,9 @@ function TrackedCornerstoneViewport(
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="relative flex h-full w-full flex-row overflow-hidden">
|
||||||
<div className="relative flex h-full w-full flex-row overflow-hidden">
|
{getCornerstoneViewport()}
|
||||||
{getCornerstoneViewport()}
|
</div>
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -312,7 +310,7 @@ const _getArrowsComponent = (isTracked, switchMeasurement, isActiveViewport) =>
|
|||||||
<ViewportActionArrows
|
<ViewportActionArrows
|
||||||
onArrowsClick={direction => switchMeasurement(direction)}
|
onArrowsClick={direction => switchMeasurement(direction)}
|
||||||
className={isActiveViewport ? 'visible' : 'invisible group-hover/pane:visible'}
|
className={isActiveViewport ? 'visible' : 'invisible group-hover/pane:visible'}
|
||||||
></ViewportActionArrows>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -322,33 +320,23 @@ function _getStatusComponent(isTracked, t) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative">
|
<Tooltip>
|
||||||
<Tooltip
|
<TooltipTrigger asChild>
|
||||||
position="bottom-left"
|
<span>
|
||||||
content={
|
<Icons.StatusTracking className="text-aqua-pale" />
|
||||||
<div className="flex py-2">
|
</span>
|
||||||
<div className="flex pt-1">
|
</TooltipTrigger>
|
||||||
<Icons.InfoLink className="text-primary-main w-4" />
|
<TooltipContent
|
||||||
</div>
|
align="start"
|
||||||
<div className="ml-4 flex">
|
side="bottom"
|
||||||
<span className="text-common-light text-base">
|
|
||||||
{isTracked ? (
|
|
||||||
<>{t('Series is tracked and can be viewed in the measurement panel')}</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
{t(
|
|
||||||
'Measurements for untracked series will not be shown in the measurements panel'
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<Icons.StatusTracking className="text-aqua-pale" />
|
{isTracked ? (
|
||||||
</Tooltip>
|
<>{t('Series is tracked and can be viewed in the measurement panel')}</>
|
||||||
</div>
|
) : (
|
||||||
|
<>{t('Measurements for untracked series will not be shown in the measurements panel')}</>
|
||||||
|
)}
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -14,13 +14,11 @@ import { utils, hotkeys } from '@ohif/core';
|
|||||||
import publicUrl from '../../utils/publicUrl';
|
import publicUrl from '../../utils/publicUrl';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Icon,
|
|
||||||
StudyListExpandedRow,
|
StudyListExpandedRow,
|
||||||
EmptyStudies,
|
EmptyStudies,
|
||||||
StudyListTable,
|
StudyListTable,
|
||||||
StudyListPagination,
|
StudyListPagination,
|
||||||
StudyListFilter,
|
StudyListFilter,
|
||||||
TooltipClipboard,
|
|
||||||
useModal,
|
useModal,
|
||||||
AboutModal,
|
AboutModal,
|
||||||
UserPreferences,
|
UserPreferences,
|
||||||
@ -31,12 +29,20 @@ import {
|
|||||||
ButtonEnums,
|
ButtonEnums,
|
||||||
} from '@ohif/ui';
|
} from '@ohif/ui';
|
||||||
|
|
||||||
import { Header, Icons } from '@ohif/ui-next';
|
import {
|
||||||
|
Header,
|
||||||
|
Icons,
|
||||||
|
Tooltip,
|
||||||
|
TooltipTrigger,
|
||||||
|
TooltipContent,
|
||||||
|
Clipboard,
|
||||||
|
Onboarding,
|
||||||
|
ScrollArea,
|
||||||
|
} from '@ohif/ui-next';
|
||||||
|
|
||||||
import { Types } from '@ohif/ui';
|
import { Types } from '@ohif/ui';
|
||||||
|
|
||||||
import i18n from '@ohif/i18n';
|
import i18n from '@ohif/i18n';
|
||||||
import { Onboarding, ScrollArea } from '@ohif/ui-next';
|
|
||||||
|
|
||||||
const PatientInfoVisibility = Types.PatientInfoVisibility;
|
const PatientInfoVisibility = Types.PatientInfoVisibility;
|
||||||
|
|
||||||
@ -274,22 +280,37 @@ function WorkList({
|
|||||||
t('Common:localTimeFormat', 'hh:mm A')
|
t('Common:localTimeFormat', 'hh:mm A')
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const makeCopyTooltipCell = textValue => {
|
||||||
|
if (!textValue) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<span className="cursor-pointer truncate">{textValue}</span>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent side="bottom">
|
||||||
|
<div className="flex items-center justify-between gap-2">
|
||||||
|
{textValue}
|
||||||
|
<Clipboard>{textValue}</Clipboard>
|
||||||
|
</div>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
dataCY: `studyRow-${studyInstanceUid}`,
|
dataCY: `studyRow-${studyInstanceUid}`,
|
||||||
clickableCY: studyInstanceUid,
|
clickableCY: studyInstanceUid,
|
||||||
row: [
|
row: [
|
||||||
{
|
{
|
||||||
key: 'patientName',
|
key: 'patientName',
|
||||||
content: patientName ? (
|
content: patientName ? makeCopyTooltipCell(patientName) : null,
|
||||||
<TooltipClipboard>{patientName}</TooltipClipboard>
|
|
||||||
) : (
|
|
||||||
<span className="text-gray-700">(Empty)</span>
|
|
||||||
),
|
|
||||||
gridCol: 4,
|
gridCol: 4,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'mrn',
|
key: 'mrn',
|
||||||
content: <TooltipClipboard>{mrn}</TooltipClipboard>,
|
content: makeCopyTooltipCell(mrn),
|
||||||
gridCol: 3,
|
gridCol: 3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -305,7 +326,7 @@ function WorkList({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'description',
|
key: 'description',
|
||||||
content: <TooltipClipboard>{description}</TooltipClipboard>,
|
content: makeCopyTooltipCell(description),
|
||||||
gridCol: 4,
|
gridCol: 4,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -316,7 +337,7 @@ function WorkList({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'accession',
|
key: 'accession',
|
||||||
content: <TooltipClipboard>{accession}</TooltipClipboard>,
|
content: makeCopyTooltipCell(accession),
|
||||||
gridCol: 3,
|
gridCol: 3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -392,6 +413,7 @@ function WorkList({
|
|||||||
query.append('configUrl', filterValues.configUrl);
|
query.append('configUrl', filterValues.configUrl);
|
||||||
}
|
}
|
||||||
query.append('StudyInstanceUIDs', studyInstanceUid);
|
query.append('StudyInstanceUIDs', studyInstanceUid);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
mode.displayName && (
|
mode.displayName && (
|
||||||
<Link
|
<Link
|
||||||
|
|||||||
@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
title: General
|
||||||
|
---
|
||||||
|
|
||||||
|
## HTML Template Update
|
||||||
|
We have modified the `template.html` file so if you are using a custom template, you will need to update it.
|
||||||
|
|
||||||
|
Here are the key changes needed in the migration:
|
||||||
|
|
||||||
|
1. Added `window.PUBLIC_URL` declaration:
|
||||||
|
```javascript
|
||||||
|
window.PUBLIC_URL = '<%= PUBLIC_URL %>';
|
||||||
|
```
|
||||||
|
|
||||||
|
Was added before the `<!-- EXTENSIONS -->` comment block.
|
||||||
@ -1,6 +1,5 @@
|
|||||||
---
|
---
|
||||||
sidebar_position: 2
|
title: Icons
|
||||||
sidebar_label: Icons
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Migration Guide: Icon Component Updates
|
## Migration Guide: Icon Component Updates
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
---
|
||||||
|
title: Tooltip
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tooltip Updates
|
||||||
|
|
||||||
|
### Changes:
|
||||||
|
- Updated Tooltip structure to use `Tooltip`, `TooltipTrigger`, and `TooltipContent`.
|
||||||
|
- Removed deprecated `TooltipClipboard` and inline `content`/`position` properties.
|
||||||
|
|
||||||
|
### Migration Steps:
|
||||||
|
1. Replace imports:
|
||||||
|
```tsx
|
||||||
|
// Before
|
||||||
|
import { Tooltip } from '@ohif/ui';
|
||||||
|
import { TooltipClipboard } from '@ohif/ui';
|
||||||
|
|
||||||
|
// After
|
||||||
|
import { Tooltip, TooltipTrigger, TooltipContent } from '@ohif/ui-next';
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Update Tooltip usage:
|
||||||
|
```tsx
|
||||||
|
// Before
|
||||||
|
<Tooltip content={<div>Tooltip Message</div>} position="bottom-left">
|
||||||
|
<Component />
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
// After
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<Component />
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent side="bottom">
|
||||||
|
Tooltip Message
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
3. TooltipClipboard Replacement:
|
||||||
|
The `TooltipClipboard` component has been removed. Instead, use the `Clipboard` component inside `TooltipContent` for copying text functionality.
|
||||||
|
|
||||||
|
#### Before:
|
||||||
|
```tsx
|
||||||
|
<TooltipClipboard>{text}</TooltipClipboard>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### After:
|
||||||
|
```tsx
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<span className="cursor-pointer truncate">{text}</span>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent side="bottom">
|
||||||
|
<div className="flex items-center justify-between gap-2">
|
||||||
|
{text}
|
||||||
|
<Clipboard>{text}</Clipboard>
|
||||||
|
</div>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
```
|
||||||
@ -4,3 +4,6 @@ sidebar_label: 3.9 -> 3.10 beta
|
|||||||
---
|
---
|
||||||
|
|
||||||
# Migration Guide
|
# Migration Guide
|
||||||
|
|
||||||
|
|
||||||
|
## General
|
||||||
|
|||||||
55
platform/ui-next/src/components/Clipboard/Clipboard.tsx
Normal file
55
platform/ui-next/src/components/Clipboard/Clipboard.tsx
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import React, { ReactNode } from 'react';
|
||||||
|
import { Button } from '../Button';
|
||||||
|
import { Icons } from '../Icons';
|
||||||
|
|
||||||
|
interface ClipboardProps {
|
||||||
|
children: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Clipboard: React.FC<ClipboardProps> = ({ children }) => {
|
||||||
|
const [copyState, setCopyState] = React.useState<'idle' | 'success' | 'error'>('idle');
|
||||||
|
const copyText = React.useMemo(() => {
|
||||||
|
if (typeof children === 'string') {
|
||||||
|
return children.trim();
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}, [children]);
|
||||||
|
|
||||||
|
const handleCopy = React.useCallback(async () => {
|
||||||
|
if (!copyText) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(copyText);
|
||||||
|
setCopyState('success');
|
||||||
|
} catch {
|
||||||
|
setCopyState('error');
|
||||||
|
} finally {
|
||||||
|
setTimeout(() => setCopyState('idle'), 1500); // Reset state after feedback
|
||||||
|
}
|
||||||
|
}, [copyText]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleCopy();
|
||||||
|
}}
|
||||||
|
className="text-white"
|
||||||
|
title="Copy"
|
||||||
|
>
|
||||||
|
{copyState === 'idle' && <Icons.Copy className="h-6 w-6" />}
|
||||||
|
{copyState === 'success' && <Icons.FeedbackComplete className="h-6 w-6 text-white" />}
|
||||||
|
{copyState === 'error' && (
|
||||||
|
<Icons.ByName
|
||||||
|
name="Error"
|
||||||
|
className="h-6 w-6 text-white"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { Clipboard };
|
||||||
3
platform/ui-next/src/components/Clipboard/index.tsx
Normal file
3
platform/ui-next/src/components/Clipboard/index.tsx
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { Clipboard } from './Clipboard';
|
||||||
|
|
||||||
|
export { Clipboard };
|
||||||
@ -7,6 +7,7 @@ import ChevronOpen from './Sources/ChevronOpen';
|
|||||||
import Code from './Sources/Code';
|
import Code from './Sources/Code';
|
||||||
import ColorChange from './Sources/ColorChange';
|
import ColorChange from './Sources/ColorChange';
|
||||||
import Controls from './Sources/Controls';
|
import Controls from './Sources/Controls';
|
||||||
|
import Copy from './Sources/Copy';
|
||||||
import Delete from './Sources/Delete';
|
import Delete from './Sources/Delete';
|
||||||
import DicomTagBrowser from './Sources/DicomTagBrowser';
|
import DicomTagBrowser from './Sources/DicomTagBrowser';
|
||||||
import DisplayFillAndOutline from './Sources/DisplayFillAndOutline';
|
import DisplayFillAndOutline from './Sources/DisplayFillAndOutline';
|
||||||
@ -16,6 +17,7 @@ import Download from './Sources/Download';
|
|||||||
import Export from './Sources/Export';
|
import Export from './Sources/Export';
|
||||||
import EyeHidden from './Sources/EyeHidden';
|
import EyeHidden from './Sources/EyeHidden';
|
||||||
import EyeVisible from './Sources/EyeVisible';
|
import EyeVisible from './Sources/EyeVisible';
|
||||||
|
import FeedbackComplete from './Sources/FeedbackComplete';
|
||||||
import GearSettings from './Sources/GearSettings';
|
import GearSettings from './Sources/GearSettings';
|
||||||
import Hide from './Sources/Hide';
|
import Hide from './Sources/Hide';
|
||||||
import IconMPR from './Sources/IconMPR';
|
import IconMPR from './Sources/IconMPR';
|
||||||
@ -418,6 +420,7 @@ export const Icons = {
|
|||||||
Code,
|
Code,
|
||||||
ColorChange,
|
ColorChange,
|
||||||
Controls,
|
Controls,
|
||||||
|
Copy,
|
||||||
Delete,
|
Delete,
|
||||||
DicomTagBrowser,
|
DicomTagBrowser,
|
||||||
DisplayFillAndOutline,
|
DisplayFillAndOutline,
|
||||||
@ -430,6 +433,7 @@ export const Icons = {
|
|||||||
Export,
|
Export,
|
||||||
EyeHidden,
|
EyeHidden,
|
||||||
EyeVisible,
|
EyeVisible,
|
||||||
|
FeedbackComplete,
|
||||||
GearSettings,
|
GearSettings,
|
||||||
Hide,
|
Hide,
|
||||||
IconMPR,
|
IconMPR,
|
||||||
|
|||||||
44
platform/ui-next/src/components/Icons/Sources/Copy.tsx
Normal file
44
platform/ui-next/src/components/Icons/Sources/Copy.tsx
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import type { IconProps } from '../types';
|
||||||
|
|
||||||
|
export const Copy = (props: IconProps) => (
|
||||||
|
<svg
|
||||||
|
width="24px"
|
||||||
|
height="24px"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<g
|
||||||
|
id="Copy"
|
||||||
|
stroke="none"
|
||||||
|
strokeWidth="1"
|
||||||
|
fill="none"
|
||||||
|
fillRule="evenodd"
|
||||||
|
>
|
||||||
|
<rect
|
||||||
|
id="Rectangle"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
></rect>
|
||||||
|
<rect
|
||||||
|
id="Rectangle"
|
||||||
|
stroke="currentColor"
|
||||||
|
x="8.95205173"
|
||||||
|
y="4.5"
|
||||||
|
width="10"
|
||||||
|
height="10"
|
||||||
|
rx="2"
|
||||||
|
></rect>
|
||||||
|
<path
|
||||||
|
d="M7.05569885,9.5 L5.5,9.5 C4.67157288,9.5 4,10.1715729 4,11 L4,17.8271183 C4,18.6555454 4.67157288,19.3271183 5.5,19.3271183 L12.4520517,19.3271183 C13.2804789,19.3271183 13.9520517,18.6555454 13.9520517,17.8271183 L13.9520517,16.3489489 L13.9520517,16.3489489"
|
||||||
|
id="Path-4"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeLinecap="round"
|
||||||
|
></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Copy;
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import type { IconProps } from '../types';
|
||||||
|
|
||||||
|
export const FeedbackComplete = (props: IconProps) => (
|
||||||
|
<svg
|
||||||
|
width="24px"
|
||||||
|
height="24px"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<g
|
||||||
|
id="FeedbackComplete"
|
||||||
|
stroke="none"
|
||||||
|
strokeWidth="1"
|
||||||
|
fill="none"
|
||||||
|
fillRule="evenodd"
|
||||||
|
>
|
||||||
|
<rect
|
||||||
|
id="Rectangle"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
></rect>
|
||||||
|
<path
|
||||||
|
d="M6.5,12.5497794 L10.0873523,16.1371317 C10.4778766,16.527656 11.1110415,16.527656 11.5015658,16.1371317 C11.5482206,16.0904769 11.5901435,16.0393192 11.6267216,15.9844066 L17.6113609,7 L17.6113609,7"
|
||||||
|
id="Path-3"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeLinecap="round"
|
||||||
|
></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default FeedbackComplete;
|
||||||
@ -17,7 +17,7 @@ const TooltipContent = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
sideOffset={sideOffset}
|
sideOffset={sideOffset}
|
||||||
className={cn(
|
className={cn(
|
||||||
'bg-primary-dark border-secondary-light text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 overflow-hidden rounded border px-3 py-1.5 text-sm',
|
'bg-primary-dark border-secondary-light text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 overflow-hidden rounded border px-2 py-1.5 text-sm',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@ -24,7 +24,7 @@ import {
|
|||||||
DialogDescription,
|
DialogDescription,
|
||||||
} from './Dialog';
|
} from './Dialog';
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './Select';
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './Select';
|
||||||
|
import { Clipboard } from './Clipboard';
|
||||||
import { Combobox } from './Combobox';
|
import { Combobox } from './Combobox';
|
||||||
import { Popover, PopoverContent, PopoverTrigger, PopoverAnchor } from './Popover';
|
import { Popover, PopoverContent, PopoverTrigger, PopoverAnchor } from './Popover';
|
||||||
import { Calendar } from './Calendar';
|
import { Calendar } from './Calendar';
|
||||||
@ -183,4 +183,5 @@ export {
|
|||||||
CardTitle,
|
CardTitle,
|
||||||
CardDescription,
|
CardDescription,
|
||||||
CardContent,
|
CardContent,
|
||||||
|
Clipboard,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import {
|
|||||||
Tabs,
|
Tabs,
|
||||||
TabsContent,
|
TabsContent,
|
||||||
TabsList,
|
TabsList,
|
||||||
|
Clipboard,
|
||||||
TabsTrigger,
|
TabsTrigger,
|
||||||
Toggle,
|
Toggle,
|
||||||
toggleVariants,
|
toggleVariants,
|
||||||
@ -99,6 +100,7 @@ export {
|
|||||||
ThemeWrapper,
|
ThemeWrapper,
|
||||||
Calendar,
|
Calendar,
|
||||||
DatePickerWithRange,
|
DatePickerWithRange,
|
||||||
|
Clipboard,
|
||||||
// contextProviders
|
// contextProviders
|
||||||
NotificationProvider,
|
NotificationProvider,
|
||||||
useNotification,
|
useNotification,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user