fix(app): appearance modal provider scope, worklist preview persistence, and tag browser label overflow (#6136)

- Insert ServiceProvidersManager providers ahead of the dialog/modal
  providers in App.tsx: modal content renders as a sibling of the
  provider's children, so contexts registered via the manager (e.g.
  ActiveThemeProvider) were out of scope and the appearance modal
  crashed with 'useActiveTheme must be used within an ActiveThemeProvider'.
- Persist the worklist preview panel open/closed state in
  sessionStorage so it survives navigating into a study and back.
- Keep the DICOM tag browser instance number label on one line:
  the words truncate, the (n of total) digits never clip.
This commit is contained in:
Alireza 2026-07-10 18:52:06 -04:00 committed by GitHub
parent dc9df56be4
commit 43226d9191
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 29 additions and 8 deletions

View File

@ -165,8 +165,11 @@ const DicomTagBrowser = ({
</div>
{shouldShowInstanceList && (
<div className="mx-auto mt-0.5 flex w-1/4 flex-col">
<span className="text-muted-foreground flex h-6 items-center pb-2 text-base">
Instance Number ({instanceNumber} of {activeDisplaySet?.images?.length})
<span className="text-muted-foreground flex h-6 min-w-0 items-center whitespace-nowrap pb-2 text-base">
<span className="truncate">Instance Number</span>
<span className="shrink-0">
&nbsp;({instanceNumber} of {activeDisplaySet?.images?.length})
</span>
</span>
<Slider
value={[instanceNumber]}

View File

@ -129,12 +129,17 @@ function App({
[ShepherdJourneyProvider],
];
// Loop through and register each of the service providers registered with the ServiceProvidersManager.
const providersFromManager = Object.entries(serviceProvidersManager.providers);
// Providers registered with the ServiceProvidersManager are inserted ahead of
// the dialog/modal providers: dialog and modal content renders at those
// providers' own level (as a sibling of their children, not inside the route
// tree), so any context a registered provider supplies must already be in
// scope there.
const providersFromManager = Object.entries(serviceProvidersManager.providers).map(
([serviceName, provider]) => [provider, { service: servicesManager.services[serviceName] }]
);
if (providersFromManager.length > 0) {
providersFromManager.forEach(([serviceName, provider]) => {
providers.push([provider, { service: servicesManager.services[serviceName] }]);
});
const dialogIndex = providers.findIndex(([component]) => component === DialogProvider);
providers.splice(dialogIndex, 0, ...providersFromManager);
}
const CombinedProviders = ({ children }) => Compose({ components: providers, children });

View File

@ -9,6 +9,7 @@ import {
StudyList,
Icons,
InvestigationalUseDialog,
useSessionStorage,
type StudyRow,
type OnStudyDoubleClick,
} from '@ohif/ui-next';
@ -53,7 +54,19 @@ export default function WorkList({
const defaultSorting = useMemo(() => [{ id: 'studyDateTime', desc: true }], []);
const [selected, setSelected] = useState<StudyRow | null>(null);
const [isPreviewOpen, setPreviewOpen] = useState(true);
// Persist the preview panel open/closed state so it survives navigating
// into a study and back. The hook only handles objects, hence the wrapper.
const [previewState, updatePreviewState] = useSessionStorage({
key: 'studyList.previewOpen',
defaultValue: { open: true },
clearOnUnload: false,
});
const isPreviewOpen = previewState.open !== false;
const setPreviewOpen = useCallback(
(open: boolean) => updatePreviewState({ open }),
[updatePreviewState]
);
// `workList.onStudyDoubleClick` is the command (or command list) run when a
// study row is double-clicked — by default `launchDefaultMode`, which