updates
This commit is contained in:
parent
9ff4ece541
commit
e91811ff23
@ -6,6 +6,7 @@ import { HangingProtocolService, CommandsManager } from '@ohif/core';
|
||||
import { useAppConfig } from '@state';
|
||||
import ViewerHeader from './ViewerHeader';
|
||||
import SidePanelWithServices from '../Components/SidePanelWithServices';
|
||||
import { Onboarding } from '@ohif/ui-next';
|
||||
|
||||
function ViewerLayout({
|
||||
// From Extension Module Params
|
||||
@ -155,7 +156,7 @@ function ViewerLayout({
|
||||
) : null}
|
||||
</React.Fragment>
|
||||
</div>
|
||||
|
||||
<Onboarding />
|
||||
<InvestigationalUseDialog dialogConfiguration={appConfig?.investigationalUseDialog} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -298,27 +298,97 @@ window.config = {
|
||||
steps: [
|
||||
{
|
||||
id: 'scroll',
|
||||
text: 'You can scroll through the images using the mouse wheel.',
|
||||
text: 'You can scroll through the images using the mouse wheel or scrollbar.',
|
||||
attachTo: {
|
||||
element: '.viewport-element',
|
||||
element: '.scroll',
|
||||
on: 'left-start',
|
||||
},
|
||||
advanceOn: {
|
||||
selector: '.cornerstone-viewport-element',
|
||||
event: 'CORNERSTONE_STACK_VIEWPORT_SCROLL',
|
||||
},
|
||||
/**
|
||||
buttons: [
|
||||
{
|
||||
text: 'Next',
|
||||
action() {
|
||||
this.next();
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Skip',
|
||||
action() {
|
||||
this.cancel();
|
||||
},
|
||||
},
|
||||
],
|
||||
*/
|
||||
},
|
||||
{
|
||||
id: 'zoom',
|
||||
text: 'You can zoom the images using the right click.',
|
||||
attachTo: {
|
||||
element: '.viewport-element',
|
||||
on: 'left-start',
|
||||
},
|
||||
advanceOn: {
|
||||
selector: '.cornerstone-viewport-element',
|
||||
event: 'CORNERSTONE_CAMERA_MODIFIED',
|
||||
},
|
||||
/**
|
||||
buttons: [
|
||||
{
|
||||
text: 'Next',
|
||||
action() {
|
||||
this.next();
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Skip',
|
||||
action() {
|
||||
this.cancel();
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Back',
|
||||
action() {
|
||||
this.back();
|
||||
},
|
||||
},
|
||||
],
|
||||
*/
|
||||
},
|
||||
{
|
||||
id: 'pan',
|
||||
text: 'You can pan the images using the middle click.',
|
||||
attachTo: {
|
||||
element: '.viewport-element',
|
||||
on: 'left-start',
|
||||
},
|
||||
advanceOn: {
|
||||
selector: '.cornerstone-viewport-element',
|
||||
event: 'CORNERSTONE_TOOLS_MOUSE_DRAG',
|
||||
},
|
||||
/**
|
||||
buttons: [
|
||||
{
|
||||
text: 'Next',
|
||||
action() {
|
||||
this.next();
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Skip',
|
||||
action() {
|
||||
this.cancel();
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Back',
|
||||
action() {
|
||||
this.back();
|
||||
},
|
||||
},
|
||||
],
|
||||
*/
|
||||
},
|
||||
{
|
||||
id: 'windowing',
|
||||
@ -327,14 +397,58 @@ window.config = {
|
||||
element: '.viewport-element',
|
||||
on: 'left-start',
|
||||
},
|
||||
/**
|
||||
buttons: [
|
||||
{
|
||||
text: 'Next',
|
||||
action() {
|
||||
this.next();
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Skip',
|
||||
action() {
|
||||
this.cancel();
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Back',
|
||||
action() {
|
||||
this.back();
|
||||
},
|
||||
},
|
||||
],
|
||||
*/
|
||||
},
|
||||
{
|
||||
id: 'length',
|
||||
text: 'You can measure the length of a region using the Length tool.',
|
||||
attachTo: {
|
||||
element: '.MeasurementTools-split-button-primary',
|
||||
element: '[data-cy="MeasurementTools-split-button-primary"]',
|
||||
on: 'bottom',
|
||||
},
|
||||
/**
|
||||
buttons: [
|
||||
{
|
||||
text: 'Next',
|
||||
action() {
|
||||
this.next();
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Skip',
|
||||
action() {
|
||||
this.cancel();
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Back',
|
||||
action() {
|
||||
this.back();
|
||||
},
|
||||
},
|
||||
],
|
||||
*/
|
||||
},
|
||||
{
|
||||
id: 'drawAnnotation',
|
||||
@ -343,6 +457,28 @@ window.config = {
|
||||
element: '.viewport-element',
|
||||
on: 'left-start',
|
||||
},
|
||||
/**
|
||||
buttons: [
|
||||
{
|
||||
text: 'Next',
|
||||
action() {
|
||||
this.next();
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Skip',
|
||||
action() {
|
||||
this.cancel();
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Back',
|
||||
action() {
|
||||
this.back();
|
||||
},
|
||||
},
|
||||
],
|
||||
*/
|
||||
},
|
||||
{
|
||||
id: 'trackMeasurement',
|
||||
@ -351,6 +487,28 @@ window.config = {
|
||||
element: '[data-cy="prompt-begin-tracking-yes-btn"]',
|
||||
on: 'bottom',
|
||||
},
|
||||
/**
|
||||
buttons: [
|
||||
{
|
||||
text: 'Next',
|
||||
action() {
|
||||
this.next();
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Skip',
|
||||
action() {
|
||||
this.cancel();
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Back',
|
||||
action() {
|
||||
this.back();
|
||||
},
|
||||
},
|
||||
],
|
||||
*/
|
||||
},
|
||||
{
|
||||
id: 'openMeasurementPanel',
|
||||
@ -359,6 +517,28 @@ window.config = {
|
||||
element: '#trackedMeasurements-btn',
|
||||
on: 'left-start',
|
||||
},
|
||||
/**
|
||||
buttons: [
|
||||
{
|
||||
text: 'Next',
|
||||
action() {
|
||||
this.next();
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Skip',
|
||||
action() {
|
||||
this.cancel();
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Back',
|
||||
action() {
|
||||
this.back();
|
||||
},
|
||||
},
|
||||
],
|
||||
*/
|
||||
},
|
||||
{
|
||||
id: 'scrollAwayFromMeasurement',
|
||||
@ -367,6 +547,28 @@ window.config = {
|
||||
element: '.viewport-element',
|
||||
on: 'left-start',
|
||||
},
|
||||
/**
|
||||
buttons: [
|
||||
{
|
||||
text: 'Next',
|
||||
action() {
|
||||
this.next();
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Skip',
|
||||
action() {
|
||||
this.cancel();
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Back',
|
||||
action() {
|
||||
this.back();
|
||||
},
|
||||
},
|
||||
],
|
||||
*/
|
||||
},
|
||||
{
|
||||
id: 'jumpToMeasurement',
|
||||
@ -375,6 +577,28 @@ window.config = {
|
||||
element: '[data-cy="measurement-item"]',
|
||||
on: 'left-start',
|
||||
},
|
||||
/**
|
||||
buttons: [
|
||||
{
|
||||
text: 'Next',
|
||||
action() {
|
||||
this.next();
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Skip',
|
||||
action() {
|
||||
this.cancel();
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Back',
|
||||
action() {
|
||||
this.back();
|
||||
},
|
||||
},
|
||||
],
|
||||
*/
|
||||
},
|
||||
{
|
||||
id: 'changeLayout',
|
||||
@ -383,6 +607,28 @@ window.config = {
|
||||
element: '[data-cy="Layout"]',
|
||||
on: 'bottom',
|
||||
},
|
||||
/**
|
||||
buttons: [
|
||||
{
|
||||
text: 'Next',
|
||||
action() {
|
||||
this.next();
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Skip',
|
||||
action() {
|
||||
this.cancel();
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Back',
|
||||
action() {
|
||||
this.back();
|
||||
},
|
||||
},
|
||||
],
|
||||
*/
|
||||
},
|
||||
{
|
||||
id: 'selectLayout',
|
||||
@ -391,6 +637,28 @@ window.config = {
|
||||
element: '[data-cy="MPR"]',
|
||||
on: 'left-start',
|
||||
},
|
||||
/**
|
||||
buttons: [
|
||||
{
|
||||
text: 'Complete',
|
||||
action() {
|
||||
this.complete();
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Skip',
|
||||
action() {
|
||||
this.cancel();
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'Back',
|
||||
action() {
|
||||
this.back();
|
||||
},
|
||||
},
|
||||
],
|
||||
*/
|
||||
},
|
||||
],
|
||||
tourOptions: {},
|
||||
|
||||
@ -24,13 +24,14 @@ import {
|
||||
UserAuthenticationProvider,
|
||||
ToolboxProvider,
|
||||
} from '@ohif/ui';
|
||||
import { ThemeWrapper as ThemeWrapperNext, OnboardingProvider } from '@ohif/ui-next';
|
||||
import { ThemeWrapper as ThemeWrapperNext } from '@ohif/ui-next';
|
||||
// Viewer Project
|
||||
// TODO: Should this influence study list?
|
||||
import { AppConfigProvider } from '@state';
|
||||
import createRoutes from './routes';
|
||||
import appInit from './appInit.js';
|
||||
import OpenIdConnectRoutes from './utils/OpenIdConnectRoutes';
|
||||
import { ShepherdJourneyProvider } from 'react-shepherd';
|
||||
|
||||
let commandsManager: CommandsManager,
|
||||
extensionManager: ExtensionManager,
|
||||
@ -99,7 +100,6 @@ function App({
|
||||
cineService,
|
||||
userAuthenticationService,
|
||||
customizationService,
|
||||
onboardingService,
|
||||
} = servicesManager.services;
|
||||
|
||||
const providers = [
|
||||
@ -116,7 +116,7 @@ function App({
|
||||
[SnackbarProvider, { service: uiNotificationService }],
|
||||
[DialogProvider, { service: uiDialogService }],
|
||||
[ModalProvider, { service: uiModalService, modal: Modal }],
|
||||
[OnboardingProvider],
|
||||
[ShepherdJourneyProvider],
|
||||
];
|
||||
|
||||
// Loop through and register each of the service providers registered with the ServiceProvidersManager.
|
||||
|
||||
@ -34,6 +34,7 @@ import {
|
||||
import { Types } from '@ohif/ui';
|
||||
|
||||
import i18n from '@ohif/i18n';
|
||||
import { Onboarding } from '@ohif/ui-next';
|
||||
|
||||
const PatientInfoVisibility = Types.PatientInfoVisibility;
|
||||
|
||||
@ -411,7 +412,7 @@ function WorkList({
|
||||
disabled={!isValidMode}
|
||||
startIconTooltip={
|
||||
!isValidMode ? (
|
||||
<div className="font-inter flex w-[206px] whitespace-normal text-left text-xs font-normal text-white ">
|
||||
<div className="font-inter flex w-[206px] whitespace-normal text-left text-xs font-normal text-white">
|
||||
{invalidModeDescription}
|
||||
</div>
|
||||
) : null
|
||||
@ -534,6 +535,7 @@ function WorkList({
|
||||
WhiteLabeling={appConfig.whiteLabeling}
|
||||
showPatientInfo={PatientInfoVisibility.DISABLED}
|
||||
/>
|
||||
<Onboarding />
|
||||
<InvestigationalUseDialog dialogConfiguration={appConfig?.investigationalUseDialog} />
|
||||
<div className="ohif-scrollbar ohif-scrollbar-stable-gutter flex grow flex-col overflow-y-auto sm:px-5">
|
||||
<StudyListFilter
|
||||
|
||||
23
platform/ui-next/src/components/Onboarding/Onboarding.css
Normal file
23
platform/ui-next/src/components/Onboarding/Onboarding.css
Normal file
@ -0,0 +1,23 @@
|
||||
.shepherd-element {
|
||||
@apply !bg-primary-dark !rounded-lg !p-2;
|
||||
}
|
||||
|
||||
.shepherd-text {
|
||||
@apply text-[11px] !text-white;
|
||||
}
|
||||
|
||||
.shepherd-footer {
|
||||
@apply !bg-primary-dark !rounded-b-lg;
|
||||
}
|
||||
|
||||
.shepherd-button {
|
||||
@apply !bg-primary-main hover:!bg-customblue-80 active:!bg-customblue-40 !box-content !inline-flex !h-[15px] !min-w-[60px] !flex-row !items-center !justify-center !gap-[5px] !whitespace-nowrap !rounded !px-[10px] !text-center !font-sans !text-[11px] !font-semibold !leading-[1.2] !text-white !outline-none !transition !duration-300 !ease-in-out focus:!outline-none;
|
||||
}
|
||||
|
||||
.shepherd-arrow::before {
|
||||
@apply !bg-primary-dark;
|
||||
}
|
||||
|
||||
.shepherd-element[data-popper-placement^='left'] > .shepherd-arrow {
|
||||
right: -5px !important;
|
||||
}
|
||||
65
platform/ui-next/src/components/Onboarding/Onboarding.tsx
Normal file
65
platform/ui-next/src/components/Onboarding/Onboarding.tsx
Normal file
@ -0,0 +1,65 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useShepherd } from 'react-shepherd';
|
||||
import { StepOptions, TourOptions } from 'shepherd.js';
|
||||
import { useLocation } from 'react-router';
|
||||
import 'shepherd.js/dist/css/shepherd.css';
|
||||
import './Onboarding.css';
|
||||
|
||||
const hasTourBeenShown = (tourId: string) => {
|
||||
const shownTours = JSON.parse(localStorage.getItem('shownTours')) || [];
|
||||
return shownTours.includes(tourId);
|
||||
};
|
||||
const markTourAsShown = (tourId: string) => {
|
||||
const shownTours = JSON.parse(localStorage.getItem('shownTours')) || [];
|
||||
if (!shownTours.includes(tourId)) {
|
||||
shownTours.push(tourId);
|
||||
localStorage.setItem('shownTours', JSON.stringify(shownTours));
|
||||
}
|
||||
};
|
||||
|
||||
const Onboarding = () => {
|
||||
const Shepherd = useShepherd();
|
||||
const location = useLocation();
|
||||
const tours = window.config.tours as Array<{
|
||||
id: string;
|
||||
route: string;
|
||||
tourOptions: TourOptions;
|
||||
steps: StepOptions[];
|
||||
}>;
|
||||
useEffect(() => {
|
||||
if (!tours) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const pathname = location.pathname;
|
||||
const matchingTour = tours.find(tour => tour.route === pathname);
|
||||
|
||||
if (matchingTour) {
|
||||
const startTour = () => {
|
||||
const tourInstance = new Shepherd.Tour(matchingTour.tourOptions);
|
||||
matchingTour.steps.forEach(step => tourInstance.addStep(step));
|
||||
|
||||
const observer = new MutationObserver(() => {
|
||||
const firstStepElement = document.querySelector(
|
||||
matchingTour.steps[0].attachTo.element as string
|
||||
);
|
||||
if (firstStepElement) {
|
||||
observer.disconnect();
|
||||
tourInstance.start();
|
||||
markTourAsShown(matchingTour.id);
|
||||
}
|
||||
});
|
||||
|
||||
if (!hasTourBeenShown(matchingTour.id)) {
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
setTimeout(() => observer.disconnect(), 5000);
|
||||
}
|
||||
};
|
||||
|
||||
startTour();
|
||||
}
|
||||
}, [Shepherd, tours, location.pathname]);
|
||||
return null;
|
||||
};
|
||||
|
||||
export { Onboarding };
|
||||
3
platform/ui-next/src/components/Onboarding/index.ts
Normal file
3
platform/ui-next/src/components/Onboarding/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { Onboarding } from './Onboarding';
|
||||
|
||||
export { Onboarding };
|
||||
@ -28,6 +28,7 @@ import ThumbnailList from './ThumbnailList';
|
||||
import PanelSection from './PanelSection';
|
||||
import DisplaySetMessageListTooltip from './DisplaySetMessageListTooltip';
|
||||
import { Toolbox, ToolboxUI } from './Toolbox';
|
||||
import { Onboarding } from './Onboarding';
|
||||
|
||||
export {
|
||||
Button,
|
||||
@ -71,4 +72,5 @@ export {
|
||||
DisplaySetMessageListTooltip,
|
||||
Toolbox,
|
||||
ToolboxUI,
|
||||
Onboarding,
|
||||
};
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
import React, { createContext, useContext } from 'react';
|
||||
import { ShepherdJourneyProvider, useShepherd } from 'react-shepherd';
|
||||
import { StepOptions, TourOptions } from 'shepherd.js';
|
||||
import 'shepherd.js/dist/css/shepherd.css';
|
||||
|
||||
const OnboardingProviderContext = createContext(null);
|
||||
const { Provider } = OnboardingProviderContext;
|
||||
|
||||
export const useOnboardingProvider = () => useContext(OnboardingProviderContext);
|
||||
|
||||
const ShepherdController = ({
|
||||
tours,
|
||||
}: {
|
||||
tours: Array<{
|
||||
tourOptions: TourOptions;
|
||||
steps: StepOptions[];
|
||||
route: string;
|
||||
id: string;
|
||||
}>;
|
||||
}) => {
|
||||
const Shepherd = useShepherd();
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const OnboardingProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const tours = window?.config?.tours;
|
||||
if (!tours) {
|
||||
return <>{children}</>;
|
||||
}
|
||||
return (
|
||||
<ShepherdJourneyProvider>
|
||||
<Provider value={{}}>
|
||||
<ShepherdController tours={tours} />
|
||||
{children}
|
||||
</Provider>
|
||||
</ShepherdJourneyProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default OnboardingProvider;
|
||||
@ -1,5 +1,3 @@
|
||||
import NotificationProvider, { useNotification } from './NotificationProvider';
|
||||
import OnboardingProvider, { useOnboardingProvider } from './OnboardingProvider';
|
||||
|
||||
export { useNotification, NotificationProvider };
|
||||
export { useOnboardingProvider, OnboardingProvider };
|
||||
|
||||
@ -33,14 +33,10 @@ import {
|
||||
DisplaySetMessageListTooltip,
|
||||
Toolbox,
|
||||
ToolboxUI,
|
||||
Onboarding,
|
||||
} from './components';
|
||||
|
||||
import {
|
||||
useNotification,
|
||||
NotificationProvider,
|
||||
OnboardingProvider,
|
||||
useOnboardingProvider,
|
||||
} from './contextProviders';
|
||||
import { useNotification, NotificationProvider } from './contextProviders';
|
||||
|
||||
export {
|
||||
// components
|
||||
@ -81,6 +77,5 @@ export {
|
||||
DisplaySetMessageListTooltip,
|
||||
Toolbox,
|
||||
ToolboxUI,
|
||||
OnboardingProvider,
|
||||
useOnboardingProvider,
|
||||
Onboarding,
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user