fix(docs): Fix SSR build errors by dynamically importing UI components and resolving other issues (#4991)

This commit is contained in:
Dan Rukas 2025-04-22 13:28:49 -04:00 committed by GitHub
parent d59828ab22
commit 09ac43f297
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 341 additions and 348 deletions

View File

@ -1,129 +1,142 @@
import React from 'react';
import '../css/custom.css';
import Layout from '@theme/Layout';
import { TooltipProvider } from '../../../../platform/ui-next/src/components/Tooltip';
// Navigation card components
import { Card, CardHeader, CardTitle, CardDescription } from '../../../../platform/ui-next/src/components/Card';
import { Icons } from '../../../../platform/ui-next/src/components/Icons';
/* Showcase components (alphabetical) */
// import AllinOneMenuShowcase from './components/AllinOneMenuShowcase';
import ButtonShowcase from './components/ButtonShowcase';
import CheckboxShowcase from './components/CheckboxShowcase';
// import CinePlayerShowcase from './components/CinePlayerShowcase';
import ComboboxShowcase from './components/ComboboxShowcase';
import DataRowShowcase from './components/DataRowShowcase';
import DialogShowcase from './components/DialogShowcase';
import DropdownMenuShowcase from './components/DropdownMenuShowcase';
import HoverCardShowcase from './components/HoverCardShowcase';
import InputShowcase from './components/InputShowcase';
import LabelShowcase from './components/LabelShowcase';
import NumericMetaShowcase from './components/NumericMetaShowcase';
import PanelSectionShowcase from './components/PanelSectionShowcase';
import PopoverShowcase from './components/PopoverShowcase';
import ScrollAreaShowcase from './components/ScrollAreaShowcase';
import SelectShowcase from './components/SelectShowcase';
import SliderShowcase from './components/SliderShowcase';
import SwitchShowcase from './components/SwitchShowcase';
import TabsShowcase from './components/TabsShowcase';
import ToastShowcase from './components/ToastShowcase';
import ToolButtonShowcase from './components/ToolButtonShowcase';
import ToolButtonListShowcase from './components/ToolButtonListShowcase';
import TooltipShowcase from './components/TooltipShowcase';
import BrowserOnly from '@docusaurus/BrowserOnly';
/**
* Components List page that displays all available UI components
*/
export default function ComponentsList() {
return (
<Layout
title="Components"
description="OHIF Viewer Components"
>
<TooltipProvider>
<div className="text-foreground min-h-screen bg-black">
<div className="mx-auto my-4 max-w-5xl pt-4 pb-6">
{/* Navigation cards */}
<div className="mb-8 grid grid-cols-1 gap-5 md:grid-cols-3">
<a
href="/colors-and-type"
className="focus:ring-primary block rounded-lg text-inherit no-underline hover:no-underline focus:outline-none focus:ring-2"
>
<Card className="hover:bg-primary/30 w-full transition-colors">
<CardHeader>
<CardTitle className="text-foreground text-xl">
<Icons.ColorChange className="h-12 w-12" />
Colors & Typography
</CardTitle>
<CardDescription className="text-lg">
Color Palette and Typography Guidelines
</CardDescription>
</CardHeader>
</Card>
</a>
<a
href="/components-list"
className="focus:ring-primary block rounded-lg text-inherit no-underline hover:no-underline focus:outline-none focus:ring-2"
>
<Card className="hover:bg-primary/30 w-full transition-colors">
<CardHeader>
<CardTitle className="text-foreground text-xl">
<Icons.ColorChange className="h-12 w-12" />
Components
</CardTitle>
<CardDescription className="text-lg">
Essential UI Components with Variants
</CardDescription>
</CardHeader>
</Card>
</a>
<a
href="/patterns"
className="focus:ring-primary block rounded-lg text-inherit no-underline hover:no-underline focus:outline-none focus:ring-2"
>
<Card className="hover:bg-primary/30 w-full transition-colors">
<CardHeader>
<CardTitle className="text-foreground text-xl">
<Icons.ColorChange className="h-12 w-12" />
Patterns
</CardTitle>
<CardDescription className="text-lg">
Component-Based Layout Examples
</CardDescription>
</CardHeader>
</Card>
</a>
</div>
<Layout title="Components" description="OHIF Viewer Components">
<BrowserOnly fallback={<></>}>
{() => {
// Dynamically require all sub-components to avoid SSR issues
const { TooltipProvider } =
require('../../../ui-next/src/components/Tooltip');
<h1 className="text-foreground ml-6 mb-6 text-5xl">Components</h1>
const {
Card,
CardHeader,
CardTitle,
CardDescription,
} = require('../../../ui-next/src/components/Card');
const { Icons } = require('../../../ui-next/src/components/Icons');
{/* <AllinOneMenuShowcase /> */}
<ButtonShowcase />
<CheckboxShowcase />
{/* <CinePlayerShowcase /> */}
<ComboboxShowcase />
<DataRowShowcase />
<DialogShowcase />
<DropdownMenuShowcase />
<HoverCardShowcase />
<InputShowcase />
<LabelShowcase />
<NumericMetaShowcase />
<PanelSectionShowcase />
<PopoverShowcase />
<ScrollAreaShowcase />
<SelectShowcase />
<SliderShowcase />
<SwitchShowcase />
<TabsShowcase />
<ToastShowcase />
<ToolButtonShowcase />
<ToolButtonListShowcase />
<TooltipShowcase />
</div>
</div>
</TooltipProvider>
// Showcase modules
const AllinOneMenuShowcase = require('./components/AllinOneMenuShowcase').default;
const ButtonShowcase = require('./components/ButtonShowcase').default;
const CheckboxShowcase = require('./components/CheckboxShowcase').default;
const CinePlayerShowcase = require('./components/CinePlayerShowcase').default;
const ComboboxShowcase = require('./components/ComboboxShowcase').default;
const DataRowShowcase = require('./components/DataRowShowcase').default;
const DialogShowcase = require('./components/DialogShowcase').default;
const DropdownMenuShowcase = require('./components/DropdownMenuShowcase').default;
const HoverCardShowcase = require('./components/HoverCardShowcase').default;
const InputShowcase = require('./components/InputShowcase').default;
const LabelShowcase = require('./components/LabelShowcase').default;
const NumericMetaShowcase = require('./components/NumericMetaShowcase').default;
const PanelSectionShowcase = require('./components/PanelSectionShowcase').default;
const PopoverShowcase = require('./components/PopoverShowcase').default;
const ScrollAreaShowcase = require('./components/ScrollAreaShowcase').default;
const SelectShowcase = require('./components/SelectShowcase').default;
const SliderShowcase = require('./components/SliderShowcase').default;
const SwitchShowcase = require('./components/SwitchShowcase').default;
const TabsShowcase = require('./components/TabsShowcase').default;
const ToastShowcase = require('./components/ToastShowcase').default;
const ToolButtonShowcase = require('./components/ToolButtonShowcase').default;
const ToolButtonListShowcase = require('./components/ToolButtonListShowcase').default;
const TooltipShowcase = require('./components/TooltipShowcase').default;
return (
<TooltipProvider>
<div className="text-foreground min-h-screen bg-black">
<div className="mx-auto my-4 max-w-5xl pt-4 pb-6">
{/* Navigation cards */}
<div className="mb-8 grid grid-cols-1 gap-5 md:grid-cols-3">
<a
href="/colors-and-type"
className="focus:ring-primary block rounded-lg text-inherit no-underline hover:no-underline focus:outline-none focus:ring-2"
>
<Card className="hover:bg-primary/30 w-full transition-colors">
<CardHeader>
<CardTitle className="text-foreground text-xl">
<Icons.ColorChange className="h-12 w-12" />
Colors &amp; Typography
</CardTitle>
<CardDescription className="text-lg">
Color Palette and Typography Guidelines
</CardDescription>
</CardHeader>
</Card>
</a>
<a
href="/components-list"
className="focus:ring-primary block rounded-lg text-inherit no-underline hover:no-underline focus:outline-none focus:ring-2"
>
<Card className="hover:bg-primary/30 w-full transition-colors">
<CardHeader>
<CardTitle className="text-foreground text-xl">
<Icons.ColorChange className="h-12 w-12" />
Components
</CardTitle>
<CardDescription className="text-lg">
Essential UI Components with Variants
</CardDescription>
</CardHeader>
</Card>
</a>
<a
href="/patterns"
className="focus:ring-primary block rounded-lg text-inherit no-underline hover:no-underline focus:outline-none focus:ring-2"
>
<Card className="hover:bg-primary/30 w-full transition-colors">
<CardHeader>
<CardTitle className="text-foreground text-xl">
<Icons.ColorChange className="h-12 w-12" />
Patterns
</CardTitle>
<CardDescription className="text-lg">
ComponentBased Layout Examples
</CardDescription>
</CardHeader>
</Card>
</a>
</div>
<h1 className="text-foreground ml-6 mb-6 text-5xl">
Components
</h1>
<AllinOneMenuShowcase />
<ButtonShowcase />
<CheckboxShowcase />
<CinePlayerShowcase />
<ComboboxShowcase />
<DataRowShowcase />
<DialogShowcase />
<DropdownMenuShowcase />
<HoverCardShowcase />
<InputShowcase />
<LabelShowcase />
<NumericMetaShowcase />
<PanelSectionShowcase />
<PopoverShowcase />
<ScrollAreaShowcase />
<SelectShowcase />
<SliderShowcase />
<SwitchShowcase />
<TabsShowcase />
<ToastShowcase />
<ToolButtonShowcase />
<ToolButtonListShowcase />
<TooltipShowcase />
</div>
</div>
</TooltipProvider>
);
}}
</BrowserOnly>
</Layout>
);
}
}

View File

@ -1,150 +1,119 @@
// import React from 'react';
// import * as AllInOneMenu from '../../../../../platform/ui-next/src/components/AllInOneMenu';
// import { Switch } from '../../../../../platform/ui-next/src/components/Switch';
// import ShowcaseRow from './ShowcaseRow';
import React from 'react';
import useIsBrowser from '@docusaurus/useIsBrowser';
import ShowcaseRow from './ShowcaseRow';
/**
* PureUI mock of the inapp Window/Level menu.
* Clickable, but all actions are inert.
*/
export default function AllinOneMenuShowcase() {
return null;
}
const isBrowser = useIsBrowser();
// /* Helpers to build common static items so the JSX stays concise */
// const renderColorLUTItems = () =>
// [
// 'Grayscale',
// 'X Ray',
// 'HSV',
// 'Hot Iron',
// 'Red Hot',
// 'S PET',
// 'Perfusion',
// 'Rainbow',
// 'SUV',
// 'GE 256',
// 'GE',
// 'Siemens',
// ].map(name => (
// <AllInOneMenu.Item
// key={name}
// label={name}
// />
// ));
if (!isBrowser) {
return null;
}
// const renderWindowPresetItems = () =>
// [
// { desc: 'Soft tissue', wl: '400 / 40' },
// { desc: 'Lung', wl: '1500 / -600' },
// { desc: 'Liver', wl: '150 / 90' },
// { desc: 'Bone', wl: '2500 / 480' },
// { desc: 'Brain', wl: '80 / 40' },
// ].map(p => (
// <AllInOneMenu.Item
// key={p.desc}
// label={p.desc}
// secondaryLabel={p.wl}
// />
// ));
const {
default: AllInOneMenu,
IconMenu,
SubMenu,
ItemPanel,
Item,
DividerItem,
HorizontalDirection,
VerticalDirection,
} = require('../../../../ui-next/src/components/AllInOneMenu');
const { Switch } = require('../../../../ui-next/src/components/Switch');
// return (
// <ShowcaseRow
// title="All In One Menu"
// description="A structured, consolidated menu designed to reduce visual clutter, helping users keep their focus on the image. It supports various UI components—including Switches, Numeric Inputs, Tabs, and Sliders—for detailed settings, as well as text-based lists for actionable items."
// code={`<AllInOneMenu.IconMenu icon="viewport-window-level" menuStyle={{ width: 212, maxHeight: 500 }}>
// <AllInOneMenu.ItemPanel label="Display">
// <AllInOneMenu.Item label="Display Color bar" rightIcon={<Switch disabled />} />
// <AllInOneMenu.DividerItem />
// <AllInOneMenu.SubMenu itemLabel="Color LUT" itemIcon="icon-color-lut">
// <AllInOneMenu.ItemPanel label="Color LUTs">
// <AllInOneMenu.Item label="Preview in viewport" rightIcon={<Switch disabled />} />
// <AllInOneMenu.DividerItem />
// <AllInOneMenu.Item label="Grayscale" />
// <AllInOneMenu.Item label="X Ray" />
// <AllInOneMenu.Item label="HSV" />
// <AllInOneMenu.Item label="Hot Iron" />
// <AllInOneMenu.Item label="Red Hot" />
// <AllInOneMenu.Item label="S PET" />
// <AllInOneMenu.Item label="Perfusion" />
// <AllInOneMenu.Item label="Rainbow" />
// <AllInOneMenu.Item label="SUV" />
// <AllInOneMenu.Item label="GE 256" />
// <AllInOneMenu.Item label="GE" />
// <AllInOneMenu.Item label="Siemens" />
// </AllInOneMenu.ItemPanel>
// </AllInOneMenu.SubMenu>
// <AllInOneMenu.SubMenu itemLabel="Window Presets" itemIcon="viewport-window-level">
// <AllInOneMenu.ItemPanel label="CT Presets">
// <AllInOneMenu.Item label="Soft tissue" secondaryLabel="400 / 40" />
// <AllInOneMenu.Item label="Lung" secondaryLabel="1500 / 600" />
// <AllInOneMenu.Item label="Liver" secondaryLabel="150 / 90" />
// <AllInOneMenu.Item label="Bone" secondaryLabel="2500 / 480" />
// <AllInOneMenu.Item label="Brain" secondaryLabel="80 / 40" />
// </AllInOneMenu.ItemPanel>
// </AllInOneMenu.SubMenu>
// </AllInOneMenu.ItemPanel>
// </AllInOneMenu.IconMenu>`}
// >
// <div className="border-input/70 relative flex h-12 items-center rounded border bg-black px-4">
// <AllInOneMenu.IconMenu
// icon="viewport-window-level"
// iconClassName="text-xl text-highlight hover:bg-primary/30 cursor-pointer rounded"
// horizontalDirection={AllInOneMenu.HorizontalDirection.LeftToRight}
// verticalDirection={AllInOneMenu.VerticalDirection.TopToBottom}
// menuStyle={{ width: 212, maxHeight: 500 }}
// >
// <AllInOneMenu.ItemPanel label="Display">
// <AllInOneMenu.Item
// label="Display Color bar"
// rightIcon={
// <Switch
// checked={false}
// disabled
// className="pointer-events-none"
// />
// }
// useIconSpace={false}
// />
// <AllInOneMenu.DividerItem />
// <AllInOneMenu.SubMenu
// itemLabel="Color LUT"
// itemIcon="icon-color-lut"
// >
// <AllInOneMenu.ItemPanel
// label="Color LUTs"
// maxHeight="calc(100vh - 250px)"
// className="flex flex-col"
// >
// <AllInOneMenu.Item
// label="Preview in viewport"
// rightIcon={
// <Switch
// checked={false}
// disabled
// className="pointer-events-none"
// />
// }
// />
// <AllInOneMenu.DividerItem />
// {renderColorLUTItems()}
// </AllInOneMenu.ItemPanel>
// </AllInOneMenu.SubMenu>
// <AllInOneMenu.SubMenu
// itemLabel="Window Presets"
// itemIcon="viewport-window-level"
// >
// <AllInOneMenu.ItemPanel label="CT Presets">
// {renderWindowPresetItems()}
// </AllInOneMenu.ItemPanel>
// </AllInOneMenu.SubMenu>
// </AllInOneMenu.ItemPanel>
// </AllInOneMenu.IconMenu>
const renderColorLUTItems = () =>
[
'Grayscale',
'X Ray',
'HSV',
'Hot Iron',
'Red Hot',
'S PET',
'Perfusion',
'Rainbow',
'SUV',
'GE 256',
'GE',
'Siemens',
].map(name => <Item key={name} label={name} />);
// <span className="text-muted-foreground ml-3 text-sm">
// Click the icon to explore an example
// </span>
// </div>
// </ShowcaseRow>
// );
// }
const renderWindowPresetItems = () =>
[
{ desc: 'Soft tissue', wl: '400 / 40' },
{ desc: 'Lung', wl: '1500 / -600' },
{ desc: 'Liver', wl: '150 / 90' },
{ desc: 'Bone', wl: '2500 / 480' },
{ desc: 'Brain', wl: '80 / 40' },
].map(p => (
<Item key={p.desc} label={p.desc} secondaryLabel={p.wl} />
));
return (
<ShowcaseRow
title="All In One Menu"
description="A structured menu that consolidates controls and actions."
code={`<IconMenu icon="viewport-window-level" menuStyle={{ width: 212, maxHeight: 500 }}>…</IconMenu>`}
>
<div className="border-input/70 relative flex h-12 items-center rounded border bg-black px-4">
<IconMenu
icon="viewport-window-level"
iconClassName="text-xl text-highlight hover:bg-primary/30 cursor-pointer rounded"
horizontalDirection={HorizontalDirection.LeftToRight}
verticalDirection={VerticalDirection.TopToBottom}
menuStyle={{ width: 212, maxHeight: 500 }}
>
<ItemPanel label="Display">
<Item
label="Display Color bar"
rightIcon={
<Switch
checked={false}
disabled
className="pointer-events-none"
/>
}
useIconSpace={false}
/>
<DividerItem />
<SubMenu itemLabel="Color LUT" itemIcon="icon-color-lut">
<ItemPanel
label="Color LUTs"
maxHeight="calc(100vh - 250px)"
className="flex flex-col"
>
<Item
label="Preview in viewport"
rightIcon={
<Switch
checked={false}
disabled
className="pointer-events-none"
/>
}
/>
<DividerItem />
{renderColorLUTItems()}
</ItemPanel>
</SubMenu>
<SubMenu
itemLabel="Window Presets"
itemIcon="viewport-window-level"
>
<ItemPanel label="CT Presets">
{renderWindowPresetItems()}
</ItemPanel>
</SubMenu>
</ItemPanel>
</IconMenu>
<span className="text-muted-foreground ml-3 text-sm">
Click the icon to explore an example
</span>
</div>
</ShowcaseRow>
);
}

View File

@ -1,39 +1,45 @@
// import React, { useState } from 'react';
// import CinePlayer from '../../../../ui-next/src/components/CinePlayer/CinePlayer';
// import ShowcaseRow from './ShowcaseRow';
import React, { useState } from 'react';
import useIsBrowser from '@docusaurus/useIsBrowser';
import ShowcaseRow from './ShowcaseRow';
/**
* CinePlayerShowcase displays a playable/pausable cine player with FPS control.
*/
export default function CinePlayerShowcase() {
return null;
}
// const [isPlaying, setIsPlaying] = useState(false);
// const [fps, setFps] = useState(24);
const [isPlaying, setIsPlaying] = useState(false);
const [fps, setFps] = useState(24);
const isBrowser = useIsBrowser();
// return (
// <ShowcaseRow
// title="Cine Player"
// description="Play, pause, scrub through dynamic image series and adjust FPS."
// code={`
// <CinePlayer
// className="w-[300px]"
// isPlaying={isPlaying}
// frameRate={fps}
// onPlayPauseChange={setIsPlaying}
// onFrameRateChange={setFps}
// onClose={() => console.log('close clicked')}
// />
// `}
// >
// <CinePlayer
// className="w-[300px]"
// isPlaying={isPlaying}
// frameRate={fps}
// onPlayPauseChange={setIsPlaying}
// onFrameRateChange={setFps}
// onClose={() => console.log('close clicked')}
// />
// </ShowcaseRow>
// );
// }
// If not browser, return null to avoid SSR parse errors
if (!isBrowser) {
return null;
}
const { default: CinePlayer } = require('../../../../ui-next/src/components/CinePlayer/CinePlayer');
return (
<ShowcaseRow
title="Cine Player"
description="Play, pause, scrub through dynamic image series and adjust FPS."
code={`
<CinePlayer
className="w-[300px]"
isPlaying={isPlaying}
frameRate={fps}
onPlayPauseChange={setIsPlaying}
onFrameRateChange={setFps}
onClose={() => console.log('close clicked')}
/>
`}
>
<CinePlayer
className="w-[300px]"
isPlaying={isPlaying}
frameRate={fps}
onPlayPauseChange={setIsPlaying}
onFrameRateChange={setFps}
onClose={() => console.log('close clicked')}
/>
</ShowcaseRow>
);
}

View File

@ -18,7 +18,7 @@ export default function DropdownMenuShowcase() {
description="Dropdown menu provides a flexible list of options that can open from buttons or other elements"
code={`
<DropdownMenu>
<DropdownMenuTrigger>
<DropdownMenuTrigger asChild>
<Button>Open Basic</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
@ -78,4 +78,4 @@ export default function DropdownMenuShowcase() {
</div>
</ShowcaseRow>
);
}
}

View File

@ -1,47 +1,50 @@
import React from 'react';
import {
Tooltip,
TooltipTrigger,
TooltipContent,
TooltipProvider,
} from '../../../../../platform/ui-next/src/components/Tooltip';
import { Button } from '../../../../../platform/ui-next/src/components/Button';
import BrowserOnly from '@docusaurus/BrowserOnly';
import ShowcaseRow from './ShowcaseRow';
import { Button } from '../../../../ui-next/src/components/Button';
/**
* TooltipShowcase component displays Tooltip variants and examples
*/
export default function TooltipShowcase() {
return (
<ShowcaseRow
title="Tooltip"
description="Tooltips reveal helper text when users hover, focus, or tap an element."
code={`
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon">?</Button>
</TooltipTrigger>
<TooltipContent>
Tooltip content
</TooltipContent>
</Tooltip>
</TooltipProvider>
`}
>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
<BrowserOnly fallback={<></>}>
{() => {
const {
Tooltip,
TooltipTrigger,
TooltipContent,
TooltipProvider,
} = require('../../../../ui-next/src/components/Tooltip');
return (
<TooltipProvider>
<ShowcaseRow
title="Tooltip"
description="Tooltips reveal helper text when users hover, focus, or tap an element."
code={`
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon">?</Button>
</TooltipTrigger>
<TooltipContent>
Tooltip content
</TooltipContent>
</Tooltip>
`}
>
?
</Button>
</TooltipTrigger>
<TooltipContent>Tooltip content</TooltipContent>
</Tooltip>
</TooltipProvider>
</ShowcaseRow>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon">
?
</Button>
</TooltipTrigger>
<TooltipContent>Tooltip content</TooltipContent>
</Tooltip>
</ShowcaseRow>
</TooltipProvider>
);
}}
</BrowserOnly>
);
}
}

View File

@ -7,25 +7,27 @@ export default function Patterns() {
return (
<Layout>
<h1>Patterns</h1>
<button
className="bg-slate-400"
onClick={() => history.push('/patterns/patterns-segmentation')}
>
{'Segmentation Panel'}
</button>
<button
className="bg-slate-400"
onClick={() => history.push('/patterns/patterns-measurements')}
>
{'Measurements Panel'}
</button>
<button
className="bg-slate-400"
onClick={() => history.push('/patterns/patterns-tmtv')}
>
{'tmtv'}
</button>
<div>
<h1>Patterns</h1>
<button
className="bg-slate-400"
onClick={() => history.push('/patterns/patterns-segmentation')}
>
{'Segmentation Panel'}
</button>
<button
className="bg-slate-400"
onClick={() => history.push('/patterns/patterns-measurements')}
>
{'Measurements Panel'}
</button>
<button
className="bg-slate-400"
onClick={() => history.push('/patterns/patterns-tmtv')}
>
{'tmtv'}
</button>
</div>
</Layout>
);
}
}