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,46 +1,54 @@
import React from 'react'; import React from 'react';
import '../css/custom.css'; import '../css/custom.css';
import Layout from '@theme/Layout'; import Layout from '@theme/Layout';
import { TooltipProvider } from '../../../../platform/ui-next/src/components/Tooltip'; import BrowserOnly from '@docusaurus/BrowserOnly';
// 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';
/** /**
* Components List page that displays all available UI components * Components List page that displays all available UI components
*/ */
export default function ComponentsList() { export default function ComponentsList() {
return ( return (
<Layout <Layout title="Components" description="OHIF Viewer Components">
title="Components" <BrowserOnly fallback={<></>}>
description="OHIF Viewer Components" {() => {
> // Dynamically require all sub-components to avoid SSR issues
const { TooltipProvider } =
require('../../../ui-next/src/components/Tooltip');
const {
Card,
CardHeader,
CardTitle,
CardDescription,
} = require('../../../ui-next/src/components/Card');
const { Icons } = require('../../../ui-next/src/components/Icons');
// 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> <TooltipProvider>
<div className="text-foreground min-h-screen bg-black"> <div className="text-foreground min-h-screen bg-black">
<div className="mx-auto my-4 max-w-5xl pt-4 pb-6"> <div className="mx-auto my-4 max-w-5xl pt-4 pb-6">
@ -54,7 +62,7 @@ export default function ComponentsList() {
<CardHeader> <CardHeader>
<CardTitle className="text-foreground text-xl"> <CardTitle className="text-foreground text-xl">
<Icons.ColorChange className="h-12 w-12" /> <Icons.ColorChange className="h-12 w-12" />
Colors & Typography Colors &amp; Typography
</CardTitle> </CardTitle>
<CardDescription className="text-lg"> <CardDescription className="text-lg">
Color Palette and Typography Guidelines Color Palette and Typography Guidelines
@ -89,19 +97,21 @@ export default function ComponentsList() {
Patterns Patterns
</CardTitle> </CardTitle>
<CardDescription className="text-lg"> <CardDescription className="text-lg">
Component-Based Layout Examples ComponentBased Layout Examples
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
</Card> </Card>
</a> </a>
</div> </div>
<h1 className="text-foreground ml-6 mb-6 text-5xl">Components</h1> <h1 className="text-foreground ml-6 mb-6 text-5xl">
Components
</h1>
{/* <AllinOneMenuShowcase /> */} <AllinOneMenuShowcase />
<ButtonShowcase /> <ButtonShowcase />
<CheckboxShowcase /> <CheckboxShowcase />
{/* <CinePlayerShowcase /> */} <CinePlayerShowcase />
<ComboboxShowcase /> <ComboboxShowcase />
<DataRowShowcase /> <DataRowShowcase />
<DialogShowcase /> <DialogShowcase />
@ -124,6 +134,9 @@ export default function ComponentsList() {
</div> </div>
</div> </div>
</TooltipProvider> </TooltipProvider>
);
}}
</BrowserOnly>
</Layout> </Layout>
); );
} }

View File

@ -1,150 +1,119 @@
// import React from 'react'; import React from 'react';
// import * as AllInOneMenu from '../../../../../platform/ui-next/src/components/AllInOneMenu'; import useIsBrowser from '@docusaurus/useIsBrowser';
// import { Switch } from '../../../../../platform/ui-next/src/components/Switch'; import ShowcaseRow from './ShowcaseRow';
// import ShowcaseRow from './ShowcaseRow';
/** /**
* PureUI mock of the inapp Window/Level menu. * PureUI mock of the inapp Window/Level menu.
* Clickable, but all actions are inert. * Clickable, but all actions are inert.
*/ */
export default function AllinOneMenuShowcase() { export default function AllinOneMenuShowcase() {
const isBrowser = useIsBrowser();
if (!isBrowser) {
return null; return null;
} }
// /* Helpers to build common static items so the JSX stays concise */ const {
// const renderColorLUTItems = () => default: AllInOneMenu,
// [ IconMenu,
// 'Grayscale', SubMenu,
// 'X Ray', ItemPanel,
// 'HSV', Item,
// 'Hot Iron', DividerItem,
// 'Red Hot', HorizontalDirection,
// 'S PET', VerticalDirection,
// 'Perfusion', } = require('../../../../ui-next/src/components/AllInOneMenu');
// 'Rainbow', const { Switch } = require('../../../../ui-next/src/components/Switch');
// 'SUV',
// 'GE 256',
// 'GE',
// 'Siemens',
// ].map(name => (
// <AllInOneMenu.Item
// key={name}
// label={name}
// />
// ));
// const renderWindowPresetItems = () => const renderColorLUTItems = () =>
// [ [
// { desc: 'Soft tissue', wl: '400 / 40' }, 'Grayscale',
// { desc: 'Lung', wl: '1500 / -600' }, 'X Ray',
// { desc: 'Liver', wl: '150 / 90' }, 'HSV',
// { desc: 'Bone', wl: '2500 / 480' }, 'Hot Iron',
// { desc: 'Brain', wl: '80 / 40' }, 'Red Hot',
// ].map(p => ( 'S PET',
// <AllInOneMenu.Item 'Perfusion',
// key={p.desc} 'Rainbow',
// label={p.desc} 'SUV',
// secondaryLabel={p.wl} 'GE 256',
// /> 'GE',
// )); 'Siemens',
].map(name => <Item key={name} label={name} />);
// return ( const renderWindowPresetItems = () =>
// <ShowcaseRow [
// title="All In One Menu" { desc: 'Soft tissue', wl: '400 / 40' },
// 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." { desc: 'Lung', wl: '1500 / -600' },
// code={`<AllInOneMenu.IconMenu icon="viewport-window-level" menuStyle={{ width: 212, maxHeight: 500 }}> { desc: 'Liver', wl: '150 / 90' },
// <AllInOneMenu.ItemPanel label="Display"> { desc: 'Bone', wl: '2500 / 480' },
// <AllInOneMenu.Item label="Display Color bar" rightIcon={<Switch disabled />} /> { desc: 'Brain', wl: '80 / 40' },
// <AllInOneMenu.DividerItem /> ].map(p => (
// <AllInOneMenu.SubMenu itemLabel="Color LUT" itemIcon="icon-color-lut"> <Item key={p.desc} label={p.desc} secondaryLabel={p.wl} />
// <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>
// <span className="text-muted-foreground ml-3 text-sm"> return (
// Click the icon to explore an example <ShowcaseRow
// </span> title="All In One Menu"
// </div> description="A structured menu that consolidates controls and actions."
// </ShowcaseRow> 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 React, { useState } from 'react';
// import CinePlayer from '../../../../ui-next/src/components/CinePlayer/CinePlayer'; import useIsBrowser from '@docusaurus/useIsBrowser';
// import ShowcaseRow from './ShowcaseRow'; import ShowcaseRow from './ShowcaseRow';
/** /**
* CinePlayerShowcase displays a playable/pausable cine player with FPS control. * CinePlayerShowcase displays a playable/pausable cine player with FPS control.
*/ */
export default function CinePlayerShowcase() { export default function CinePlayerShowcase() {
const [isPlaying, setIsPlaying] = useState(false);
const [fps, setFps] = useState(24);
const isBrowser = useIsBrowser();
// If not browser, return null to avoid SSR parse errors
if (!isBrowser) {
return null; return null;
} }
// const [isPlaying, setIsPlaying] = useState(false);
// const [fps, setFps] = useState(24);
// return ( const { default: CinePlayer } = require('../../../../ui-next/src/components/CinePlayer/CinePlayer');
// <ShowcaseRow
// title="Cine Player" return (
// description="Play, pause, scrub through dynamic image series and adjust FPS." <ShowcaseRow
// code={` title="Cine Player"
// <CinePlayer description="Play, pause, scrub through dynamic image series and adjust FPS."
// className="w-[300px]" code={`
// isPlaying={isPlaying} <CinePlayer
// frameRate={fps} className="w-[300px]"
// onPlayPauseChange={setIsPlaying} isPlaying={isPlaying}
// onFrameRateChange={setFps} frameRate={fps}
// onClose={() => console.log('close clicked')} onPlayPauseChange={setIsPlaying}
// /> onFrameRateChange={setFps}
// `} onClose={() => console.log('close clicked')}
// > />
// <CinePlayer `}
// className="w-[300px]" >
// isPlaying={isPlaying} <CinePlayer
// frameRate={fps} className="w-[300px]"
// onPlayPauseChange={setIsPlaying} isPlaying={isPlaying}
// onFrameRateChange={setFps} frameRate={fps}
// onClose={() => console.log('close clicked')} onPlayPauseChange={setIsPlaying}
// /> onFrameRateChange={setFps}
// </ShowcaseRow> 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" description="Dropdown menu provides a flexible list of options that can open from buttons or other elements"
code={` code={`
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger> <DropdownMenuTrigger asChild>
<Button>Open Basic</Button> <Button>Open Basic</Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent> <DropdownMenuContent>

View File

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

View File

@ -7,6 +7,7 @@ export default function Patterns() {
return ( return (
<Layout> <Layout>
<div>
<h1>Patterns</h1> <h1>Patterns</h1>
<button <button
className="bg-slate-400" className="bg-slate-400"
@ -26,6 +27,7 @@ export default function Patterns() {
> >
{'tmtv'} {'tmtv'}
</button> </button>
</div>
</Layout> </Layout>
); );
} }