docs(ui): add base for ui component showcase in docs (#4414)
This commit is contained in:
1 parent
9125cec333
commit
465df5c573
48 files changed
+5160
-4740
No files matched your search
@@ -1,3 +1,241 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap');
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
/* OHIF Theme */
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--highlight: 191 74% 63%;
|
||||
--background: 236 62% 5%;
|
||||
--foreground: 0 0% 98%;
|
||||
--card: 236 62% 5%;
|
||||
--card-foreground: 0 0% 98%;
|
||||
--popover: 219 90% 15%;
|
||||
--popover-foreground: 0 0% 98%;
|
||||
--primary: 214 98% 60%;
|
||||
--primary-foreground: 0 0% 98%;
|
||||
--secondary: 214 66% 48%;
|
||||
--secondary-foreground: 200 50% 84%;
|
||||
--muted: 234 64% 10%;
|
||||
--muted-foreground: 200 46% 65%;
|
||||
--accent: 217 79% 24%;
|
||||
--accent-foreground: 0 0% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
--border: 0 0% 14.9%;
|
||||
--input: 236 45% 21%;
|
||||
--ring: 214 98% 60%;
|
||||
--chart-1: 220 70% 50%;
|
||||
--chart-2: 160 60% 45%;
|
||||
--chart-3: 30 80% 55%;
|
||||
--chart-4: 280 65% 60%;
|
||||
--chart-5: 340 75% 55%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 0 0% 3.9%;
|
||||
--foreground: 0 0% 98%;
|
||||
--card: 0 0% 3.9%;
|
||||
--card-foreground: 0 0% 98%;
|
||||
--popover: 0 0% 3.9%;
|
||||
--popover-foreground: 0 0% 98%;
|
||||
--primary: 214 98% 60%;
|
||||
--primary-foreground: 0 0% 98%;
|
||||
--secondary: 0 0% 14.9%;
|
||||
--secondary-foreground: 0 0% 98%;
|
||||
--muted: 0 0% 14.9%;
|
||||
--muted-foreground: 0 0% 63.9%;
|
||||
--accent: 0 0% 14.9%;
|
||||
--accent-foreground: 0 0% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
--border: 0 0% 14.9%;
|
||||
--input: 236 45% 21%;
|
||||
--ring: 214 98% 60%;
|
||||
--chart-1: 220 70% 50%;
|
||||
--chart-2: 160 60% 45%;
|
||||
--chart-3: 30 80% 55%;
|
||||
--chart-4: 280 65% 60%;
|
||||
--chart-5: 340 75% 55%;
|
||||
}
|
||||
}
|
||||
|
||||
/* ORIGINAL THEME for comparison and testing
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 240 10% 3.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 240 10% 3.9%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 240 10% 3.9%;
|
||||
--primary: 240 5.9% 10%;
|
||||
--primary-foreground: 0 0% 98%;
|
||||
--secondary: 240 4.8% 95.9%;
|
||||
--secondary-foreground: 240 5.9% 10%;
|
||||
--muted: 240 4.8% 95.9%;
|
||||
--muted-foreground: 240 3.8% 46.1%;
|
||||
--accent: 240 4.8% 95.9%;
|
||||
--accent-foreground: 240 5.9% 10%;
|
||||
--destructive: 0 72.22% 50.59%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
--border: 240 5.9% 90%;
|
||||
--input: 240 5.9% 90%;
|
||||
--ring: 240 5% 64.9%;
|
||||
--radius: 0.5rem;
|
||||
|
||||
--chart-1: 12 76% 61%;
|
||||
--chart-2: 173 58% 39%;
|
||||
--chart-3: 197 37% 24%;
|
||||
--chart-4: 43 74% 66%;
|
||||
--chart-5: 27 87% 67%;
|
||||
}
|
||||
|
||||
|
||||
.dark {
|
||||
--background: 240 10% 3.9%;
|
||||
--foreground: 0 0% 98%;
|
||||
--card: 240 10% 3.9%;
|
||||
--card-foreground: 0 0% 98%;
|
||||
--popover: 240 10% 3.9%;
|
||||
--popover-foreground: 0 0% 98%;
|
||||
--primary: 0 0% 98%;
|
||||
--primary-foreground: 240 5.9% 10%;
|
||||
--secondary: 240 3.7% 15.9%;
|
||||
--secondary-foreground: 0 0% 98%;
|
||||
--muted: 240 3.7% 15.9%;
|
||||
--muted-foreground: 240 5% 64.9%;
|
||||
--accent: 240 3.7% 15.9%;
|
||||
--accent-foreground: 0 0% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 0 85.7% 97.3%;
|
||||
--border: 240 3.7% 15.9%;
|
||||
--input: 240 3.7% 15.9%;
|
||||
--ring: 240 4.9% 83.9%;
|
||||
|
||||
--chart-1: 220 70% 50%;
|
||||
--chart-2: 160 60% 45%;
|
||||
--chart-3: 30 80% 55%;
|
||||
--chart-4: 280 65% 60%;
|
||||
--chart-5: 340 75% 55%;
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
/* Theme Copy Example
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 224 71.4% 4.1%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 224 71.4% 4.1%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 224 71.4% 4.1%;
|
||||
--primary: 262.1 83.3% 57.8%;
|
||||
--primary-foreground: 210 20% 98%;
|
||||
--secondary: 220 14.3% 95.9%;
|
||||
--secondary-foreground: 220.9 39.3% 11%;
|
||||
--muted: 220 14.3% 95.9%;
|
||||
--muted-foreground: 220 8.9% 46.1%;
|
||||
--accent: 220 14.3% 95.9%;
|
||||
--accent-foreground: 220.9 39.3% 11%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 210 20% 98%;
|
||||
--border: 220 13% 91%;
|
||||
--input: 220 13% 91%;
|
||||
--ring: 262.1 83.3% 57.8%;
|
||||
--radius: 0.5rem;
|
||||
--chart-1: ;
|
||||
--chart-2: ;
|
||||
--chart-3: ;
|
||||
--chart-4: ;
|
||||
--chart-5: ;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 224 71.4% 4.1%;
|
||||
--foreground: 210 20% 98%;
|
||||
--card: 224 71.4% 4.1%;
|
||||
--card-foreground: 210 20% 98%;
|
||||
--popover: 224 71.4% 4.1%;
|
||||
--popover-foreground: 210 20% 98%;
|
||||
--primary: 263.4 70% 50.4%;
|
||||
--primary-foreground: 210 20% 98%;
|
||||
--secondary: 215 27.9% 16.9%;
|
||||
--secondary-foreground: 210 20% 98%;
|
||||
--muted: 215 27.9% 16.9%;
|
||||
--muted-foreground: 217.9 10.6% 64.9%;
|
||||
--accent: 215 27.9% 16.9%;
|
||||
--accent-foreground: 210 20% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 210 20% 98%;
|
||||
--border: 215 27.9% 16.9%;
|
||||
--input: 215 27.9% 16.9%;
|
||||
--ring: 263.4 70% 50.4%;
|
||||
--chart-1: ;
|
||||
--chart-2: ;
|
||||
--chart-3: ;
|
||||
--chart-4: ;
|
||||
--chart-5: ;
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
h2.section-header {
|
||||
@apply py-4 text-2xl font-normal text-white;
|
||||
}
|
||||
|
||||
h3.section-header {
|
||||
@apply py-3 text-xl text-white;
|
||||
}
|
||||
|
||||
.playground-row {
|
||||
@apply bg-card mb-6 flex flex-row flex-wrap rounded-md border py-10;
|
||||
}
|
||||
|
||||
.example {
|
||||
@apply flex-initial px-6;
|
||||
}
|
||||
|
||||
.example2 {
|
||||
@apply flex-initial px-4;
|
||||
}
|
||||
|
||||
/* Additional CSS edits to components */
|
||||
|
||||
/* Tooltip */
|
||||
|
||||
.TooltipContent[data-side='bottom'] {
|
||||
animation-name: slideDown;
|
||||
}
|
||||
|
||||
/* Custom CSS to hide default number input arrows */
|
||||
input[type='number']::-webkit-inner-spin-button,
|
||||
input[type='number']::-webkit-outer-spin-button {
|
||||
@apply appearance-none;
|
||||
}
|
||||
|
||||
input[type='number'] {
|
||||
-moz-appearance: textfield; /* For Firefox */
|
||||
}
|
||||
|
||||
.navbar__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.navbar__item svg {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
/* stylelint-disable docusaurus/copyright-header */
|
||||
/**
|
||||
* Any CSS included here will be global. The classic template
|
||||
@@ -98,8 +336,6 @@ html[data-theme='dark'] {
|
||||
|
||||
/* Typography updates */
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap');
|
||||
|
||||
html {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import React from 'react';
|
||||
import Layout from '@theme/Layout';
|
||||
import { useHistory } from '@docusaurus/router';
|
||||
|
||||
export default function Patterns() {
|
||||
const history = useHistory();
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<h1>Patterns</h1>
|
||||
<button
|
||||
className="bg-slate-400"
|
||||
onClick={() => history.push('/patterns/segmentation-panel')}
|
||||
>
|
||||
{'Segmentation Panel'}
|
||||
</button>
|
||||
<button
|
||||
className="bg-slate-400"
|
||||
onClick={() => history.push('/patterns/measurement-panel')}
|
||||
>
|
||||
{'Measurements Panel'}
|
||||
</button>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Button } from '../../../../ui-next/src/components/Button';
|
||||
import { Icons } from '../../../../ui-next/src/components/Icons';
|
||||
import {
|
||||
Accordion,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
AccordionContent,
|
||||
} from '../../../../ui-next/src/components/Accordion';
|
||||
import { DataRow } from '../../../../ui-next/src/components/DataRow';
|
||||
import { actionOptionsMap, dataList } from '../../../../ui-next/assets/data';
|
||||
|
||||
interface DataItem {
|
||||
id: number;
|
||||
title: string;
|
||||
description: string;
|
||||
optionalField?: string;
|
||||
colorHex?: string;
|
||||
details?: string;
|
||||
series?: string;
|
||||
}
|
||||
|
||||
interface ListGroup {
|
||||
type: string;
|
||||
items: DataItem[];
|
||||
}
|
||||
|
||||
export default function Measurements() {
|
||||
const [selectedRowId, setSelectedRowId] = useState<string | null>(null);
|
||||
const handleAction = (id: string, action: string) => {
|
||||
console.log(`Action "${action}" triggered for item with id: ${id}`);
|
||||
// Implement actual action logic here
|
||||
};
|
||||
const handleRowSelect = (id: string) => {
|
||||
setSelectedRowId(prevSelectedId => (prevSelectedId === id ? null : id));
|
||||
};
|
||||
|
||||
const organSegmentationGroup = dataList.find(
|
||||
(listGroup: ListGroup) => listGroup.type === 'Organ Segmentation'
|
||||
);
|
||||
const roiToolsGroup = dataList.find((listGroup: ListGroup) => listGroup.type === 'ROI Tools');
|
||||
|
||||
if (!organSegmentationGroup) {
|
||||
return <div className="text-red-500">Organ Segmentation data not found.</div>;
|
||||
}
|
||||
|
||||
if (!roiToolsGroup) {
|
||||
return <div className="text-red-500">ROI Tools data not found.</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="my-4 flex max-w-6xl justify-end bg-black py-6">
|
||||
{/* Simulated Panel List for "Segmentation" */}
|
||||
<div className="w-64 space-y-0">
|
||||
<Accordion
|
||||
type="multiple"
|
||||
defaultValue={['measurements-list', 'measurements-additional']}
|
||||
tabIndex={0}
|
||||
>
|
||||
{/* Segmentation Tools */}
|
||||
<AccordionItem value="measurements-list">
|
||||
<AccordionTrigger className="bg-popover hover:bg-accent text-muted-foreground my-0.5 flex h-7 w-full items-center justify-between rounded py-2 pr-1 pl-2 font-normal">
|
||||
<span>Measurements</span>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<div className="mx-2 my-0">
|
||||
<div className="text-foreground text-sm">2024-Jan-01</div>
|
||||
<div className="text-muted-foreground border-input border-b-2 pb-1 text-sm">
|
||||
Study title lorem ipsum
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex h-9 w-full items-center rounded pr-0.5">
|
||||
<div className="flex space-x-1">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
className="pl-1.5"
|
||||
>
|
||||
<Icons.Download />
|
||||
<span className="pl-1">CSV</span>
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
className="pl-0.5"
|
||||
>
|
||||
<Icons.Add />
|
||||
Create DICOM SR
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-px">
|
||||
{roiToolsGroup.items.map((item, index) => {
|
||||
const compositeId = `${roiToolsGroup.type}-${item.id}-panel`; // Ensure unique composite ID
|
||||
return (
|
||||
<DataRow
|
||||
key={`panel-${compositeId}`} // Prefix to ensure uniqueness
|
||||
number={index + 1}
|
||||
title={item.title}
|
||||
description={item.description}
|
||||
optionalField={item.optionalField}
|
||||
colorHex={item.colorHex}
|
||||
details={item.details}
|
||||
series={item.series} // Pass the new series field
|
||||
actionOptions={actionOptionsMap[roiToolsGroup.type] || ['Action']}
|
||||
onAction={(action: string) => handleAction(compositeId, action)}
|
||||
isSelected={selectedRowId === compositeId}
|
||||
onSelect={() => handleRowSelect(compositeId)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
||||
{/* Additional Findings */}
|
||||
<AccordionItem value="measurements-additional">
|
||||
<AccordionTrigger className="bg-popover hover:bg-accent text-muted-foreground my-0.5 flex h-7 w-full items-center justify-between rounded py-2 pr-1 pl-2 font-normal">
|
||||
<span>Additional Findings</span>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<div className="bg-muted mb-0.5 h-12 rounded-b pb-3"></div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { DataRow } from '../../../../ui-next/src/components/DataRow';
|
||||
import { Button } from '../../../../ui-next/src/components/Button';
|
||||
import {
|
||||
Select,
|
||||
SelectValue,
|
||||
SelectTrigger,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
} from '../../../../ui-next/src/components/Select';
|
||||
import { Icons } from '../../../../ui-next/src/components/Icons';
|
||||
import {
|
||||
Accordion,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
AccordionContent,
|
||||
} from '../../../../ui-next/src/components/Accordion';
|
||||
import { Slider } from '../../../../ui-next/src/components/Slider';
|
||||
import { Switch } from '../../../../ui-next/src/components/Switch';
|
||||
import { Label } from '../../../../ui-next/src/components/Label';
|
||||
import { Input } from '../../../../ui-next/src/components/Input';
|
||||
import { Tabs, TabsList, TabsTrigger } from '../../../../ui-next/src/components/Tabs';
|
||||
import { actionOptionsMap, dataList } from '../../../../ui-next/assets/data';
|
||||
|
||||
interface DataItem {
|
||||
id: number;
|
||||
title: string;
|
||||
description: string;
|
||||
optionalField?: string;
|
||||
colorHex?: string;
|
||||
details?: string;
|
||||
series?: string;
|
||||
}
|
||||
|
||||
interface ListGroup {
|
||||
type: string;
|
||||
items: DataItem[];
|
||||
}
|
||||
|
||||
export default function SegmentationPanel() {
|
||||
const [selectedRowId, setSelectedRowId] = useState<string | null>(null);
|
||||
const [selectedTab, setSelectedTab] = useState<string>('Fill & Outline');
|
||||
const handleAction = (id: string, action: string) => {
|
||||
console.log(`Action "${action}" triggered for item with id: ${id}`);
|
||||
// Implement actual action logic here
|
||||
};
|
||||
|
||||
// Handle row selection
|
||||
const handleRowSelect = (id: string) => {
|
||||
setSelectedRowId(prevSelectedId => (prevSelectedId === id ? null : id));
|
||||
};
|
||||
|
||||
const organSegmentationGroup = dataList.find(
|
||||
(listGroup: ListGroup) => listGroup.type === 'Organ Segmentation'
|
||||
);
|
||||
|
||||
if (!organSegmentationGroup) {
|
||||
return <div className="text-red-500">Organ Segmentation data not found.</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="my-4 flex h-full w-full max-w-6xl justify-end bg-black py-6">
|
||||
<div className="w-64 space-y-0">
|
||||
<Accordion
|
||||
type="multiple"
|
||||
defaultValue={['segmentation-tools', 'segmentation-list']}
|
||||
>
|
||||
{/* Segmentation Tools */}
|
||||
<AccordionItem value="segmentation-tools">
|
||||
<AccordionTrigger className="bg-popover hover:bg-accent text-muted-foreground my-0.5 flex h-7 w-full items-center justify-between rounded py-2 pr-1 pl-2 font-normal">
|
||||
<span>Segmentation Tools</span>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<div className="bg-muted mb-0.5 h-32 rounded-b pb-3"></div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
||||
{/* Segmentation List */}
|
||||
<AccordionItem value="segmentation-list">
|
||||
<AccordionTrigger className="bg-popover hover:bg-accent text-muted-foreground my-0.5 flex h-7 w-full items-center justify-between rounded py-2 pr-1 pl-2 font-normal">
|
||||
<span>Segmentation List</span>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<div className="mb-0">
|
||||
{/* Header Controls */}
|
||||
<div className="bg-muted flex h-10 w-full items-center space-x-1 rounded-t px-1.5">
|
||||
<Select>
|
||||
<SelectTrigger className="w-full overflow-hidden">
|
||||
<SelectValue placeholder="Segmentation 1" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="seg1">Segmentation 1</SelectItem>
|
||||
<SelectItem value="seg2">Segmentation 2</SelectItem>
|
||||
<SelectItem value="seg3">Segmentation Long Name 123</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
>
|
||||
<Icons.Info className="h-6 w-6" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Appearance Settings */}
|
||||
<AccordionItem value="segmentation-display">
|
||||
<AccordionTrigger className="bg-muted hover:bg-accent mt-0.5 flex h-7 w-full items-center justify-between rounded-b pr-1 pl-2 font-normal text-white">
|
||||
<div className="flex space-x-2">
|
||||
<Icons.Controls className="text-primary" />
|
||||
<span className="text-primary pr-1">Appearance Settings</span>
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<div className="bg-muted mb-0.5 space-y-2 rounded-b px-1.5 pt-0.5 pb-3">
|
||||
<div className="mx-1 mb-2.5 mt-1 flex items-center justify-between space-x-4">
|
||||
{/* Display Label with Selected Tab */}
|
||||
<div className="text-muted-foreground text-xs">Show: {selectedTab}</div>
|
||||
{/* Tabs Controls */}
|
||||
<Tabs
|
||||
value={selectedTab}
|
||||
onValueChange={setSelectedTab}
|
||||
>
|
||||
<TabsList>
|
||||
<TabsTrigger value="Fill & Outline">
|
||||
<Icons.DisplayFillAndOutline className="text-primary" />
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="Outline Only">
|
||||
<Icons.DisplayOutlineOnly className="text-primary" />
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="Fill Only">
|
||||
<Icons.DisplayFillOnly className="text-primary" />
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
</div>
|
||||
{/* Opacity Slider */}
|
||||
<div className="my-2 flex items-center">
|
||||
<Label className="text-muted-foreground mx-1 w-14 flex-none whitespace-nowrap text-xs">
|
||||
Opacity
|
||||
</Label>
|
||||
<Slider
|
||||
className="mx-1 flex-1"
|
||||
defaultValue={[85]}
|
||||
max={100}
|
||||
step={1}
|
||||
/>
|
||||
<Input
|
||||
className="mx-1 w-10 flex-none"
|
||||
placeholder="85"
|
||||
/>
|
||||
</div>
|
||||
{/* Border Slider */}
|
||||
<div className="my-2 flex items-center">
|
||||
<Label className="text-muted-foreground mx-1 w-14 flex-none whitespace-nowrap text-xs">
|
||||
Border
|
||||
</Label>
|
||||
<Slider
|
||||
className="mx-1 flex-1"
|
||||
defaultValue={[10]}
|
||||
max={100}
|
||||
step={1}
|
||||
/>
|
||||
<Input
|
||||
className="mx-1 w-10 flex-none"
|
||||
placeholder="2"
|
||||
/>
|
||||
</div>
|
||||
{/* Sync Changes Switch */}
|
||||
<div className="my-2 flex items-center pl-1 pb-1">
|
||||
<Switch defaultChecked />
|
||||
<Label className="text-muted-foreground mx-2 w-14 flex-none whitespace-nowrap text-xs">
|
||||
Sync changes in all viewports
|
||||
</Label>
|
||||
</div>
|
||||
<div className="border-input w-full border"></div>
|
||||
{/* Display Inactive Segmentations Switch */}
|
||||
<div className="my-2 flex items-center pl-1">
|
||||
<Switch defaultChecked />
|
||||
<Label className="text-muted-foreground mx-2 w-14 flex-none whitespace-nowrap text-xs">
|
||||
Display inactive segmentations
|
||||
</Label>
|
||||
</div>
|
||||
{/* Additional Opacity Slider */}
|
||||
<div className="my-2 flex items-center">
|
||||
<Label className="text-muted-foreground mx-1 w-14 flex-none whitespace-nowrap text-xs">
|
||||
Opacity
|
||||
</Label>
|
||||
<Slider
|
||||
className="mx-1 flex-1"
|
||||
defaultValue={[65]}
|
||||
max={100}
|
||||
step={1}
|
||||
/>
|
||||
<Input
|
||||
className="mx-1 w-10 flex-none"
|
||||
placeholder="65"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
{/* Action Buttons */}
|
||||
<div className="my-px flex h-9 w-full items-center justify-between rounded pl-0.5 pr-7">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
className="pr pl-0.5"
|
||||
>
|
||||
<Icons.Add />
|
||||
Add Segment
|
||||
</Button>
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
>
|
||||
<Icons.Hide className="h-6 w-6" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Data Rows */}
|
||||
<div className="space-y-px">
|
||||
{organSegmentationGroup.items.map((item, index) => {
|
||||
const compositeId = `${organSegmentationGroup.type}-${item.id}-panel`; // Ensure unique composite ID
|
||||
return (
|
||||
<DataRow
|
||||
key={`panel-${compositeId}`} // Prefix to ensure uniqueness
|
||||
number={index + 1}
|
||||
title={item.title}
|
||||
description={item.description}
|
||||
optionalField={item.optionalField}
|
||||
colorHex={item.colorHex}
|
||||
details={item.details}
|
||||
series={item.series}
|
||||
actionOptions={actionOptionsMap[organSegmentationGroup.type] || ['Action']}
|
||||
onAction={(action: string) => handleAction(compositeId, action)}
|
||||
isSelected={selectedRowId === compositeId}
|
||||
onSelect={() => handleRowSelect(compositeId)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import React from 'react';
|
||||
import Layout from '@theme/Layout';
|
||||
import { useHistory } from '@docusaurus/router';
|
||||
|
||||
export default function Hello() {
|
||||
const history = useHistory();
|
||||
|
||||
return (
|
||||
<Layout
|
||||
title="Hello"
|
||||
description="Hello React Page"
|
||||
>
|
||||
<button
|
||||
className="bg-slate-400"
|
||||
onClick={() => history.push('/ui-playground')}
|
||||
>
|
||||
ui-playground
|
||||
</button>
|
||||
<button
|
||||
className="bg-slate-400"
|
||||
onClick={() => history.push('/patterns')}
|
||||
>
|
||||
patterns
|
||||
</button>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,522 @@
|
||||
import React, { useState } from 'react';
|
||||
import '../css/custom.css';
|
||||
|
||||
import { Label } from '../../../ui-next/src/components/Label';
|
||||
import { Input } from '../../../ui-next/src/components/Input';
|
||||
import { Separator } from '../../../ui-next/src/components/Separator';
|
||||
import { Tabs, TabsList, TabsTrigger } from '../../../ui-next/src/components/Tabs';
|
||||
import {
|
||||
Select,
|
||||
SelectTrigger,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectValue,
|
||||
} from '../../../ui-next/src/components/Select';
|
||||
import { Button } from '../../../ui-next/src/components/Button';
|
||||
import { Switch } from '../../../ui-next/src/components/Switch';
|
||||
import { Checkbox } from '../../../ui-next/src/components/Checkbox';
|
||||
import { Toggle } from '../../../ui-next/src/components/Toggle';
|
||||
import { Slider } from '../../../ui-next/src/components/Slider';
|
||||
import { ScrollArea } from '../../../ui-next/src/components/ScrollArea';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
} from '../../../ui-next/src/components/DropdownMenu';
|
||||
import { Icons } from '../../../ui-next/src/components/Icons';
|
||||
|
||||
interface ShowcaseRowProps {
|
||||
title: string;
|
||||
description?: string;
|
||||
children: React.ReactNode;
|
||||
code: string;
|
||||
}
|
||||
|
||||
export default function ComponentShowcase() {
|
||||
return (
|
||||
<div className="bg-background text-foreground h-full">
|
||||
<div className="mx-auto my-4 max-w-6xl py-6">
|
||||
<ShowcaseRow
|
||||
title="Buttons"
|
||||
description="Various button styles and sizes"
|
||||
code={`
|
||||
<Button variant="default">Primary Button</Button>
|
||||
|
||||
<Button variant="secondary">Secondary Button</Button>
|
||||
|
||||
<Button variant="ghost">Ghost Button</Button>
|
||||
|
||||
<Button variant="ghost" size="icon">?</Button>
|
||||
|
||||
<Button variant="link">Link</Button>
|
||||
`}
|
||||
>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<Button variant="default">Primary Button</Button>
|
||||
<Button variant="secondary">Secondary Button</Button>
|
||||
<Button variant="ghost">Ghost Button</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
>
|
||||
?
|
||||
</Button>
|
||||
<Button variant="link">Link</Button>
|
||||
</div>
|
||||
</ShowcaseRow>
|
||||
|
||||
<ShowcaseRow
|
||||
title="Small Buttons"
|
||||
description="Various small button styles"
|
||||
code={`
|
||||
<Button variant="default" size="sm">Primary Button</Button>
|
||||
<Button variant="secondary" size="sm">Secondary Button</Button>
|
||||
<Button variant="ghost" size="sm">Ghost Button</Button>
|
||||
<Button variant="ghost" size="icon">?</Button>
|
||||
<Button variant="link" size="sm">Link</Button>
|
||||
`}
|
||||
>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<Button
|
||||
variant="default"
|
||||
size="sm"
|
||||
>
|
||||
Primary Button
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
>
|
||||
Secondary Button
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
>
|
||||
Ghost Button
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
>
|
||||
?
|
||||
</Button>
|
||||
<Button
|
||||
variant="link"
|
||||
size="sm"
|
||||
>
|
||||
Link
|
||||
</Button>
|
||||
</div>
|
||||
</ShowcaseRow>
|
||||
|
||||
<ShowcaseRow
|
||||
title="Button large"
|
||||
description="Various large button styles"
|
||||
code={`
|
||||
<Button variant="default" size="lg">Primary Button</Button>
|
||||
<Button variant="secondary" size="lg">Secondary Button</Button>
|
||||
<Button variant="ghost" size="lg">Ghost Button</Button>
|
||||
<Button variant="ghost" size="icon">?</Button>
|
||||
<Button variant="link" size="lg">Link</Button>
|
||||
`}
|
||||
>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<Button
|
||||
variant="default"
|
||||
size="lg"
|
||||
>
|
||||
Primary Button
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="lg"
|
||||
>
|
||||
Secondary Button
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="lg"
|
||||
>
|
||||
Ghost Button
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
>
|
||||
?
|
||||
</Button>
|
||||
<Button
|
||||
variant="link"
|
||||
size="lg"
|
||||
>
|
||||
Link
|
||||
</Button>
|
||||
</div>
|
||||
</ShowcaseRow>
|
||||
|
||||
<ShowcaseRow
|
||||
title="Input"
|
||||
description="Input field with label"
|
||||
code={`
|
||||
<div className="inline-block">
|
||||
<div className="mr-4 inline-block">
|
||||
<Label>Patient Weight</Label>
|
||||
</div>
|
||||
<div className="inline-block">
|
||||
<Input placeholder="(kg)" />
|
||||
</div>
|
||||
</div>
|
||||
`}
|
||||
>
|
||||
<div className="inline-block">
|
||||
<div className="mr-4 inline-block">
|
||||
<Label>Patient Weight</Label>
|
||||
</div>
|
||||
<div className="inline-block">
|
||||
<Input placeholder="(kg)" />
|
||||
</div>
|
||||
</div>
|
||||
</ShowcaseRow>
|
||||
|
||||
<ShowcaseRow
|
||||
title="Color swatches"
|
||||
description="Various color swatches"
|
||||
code={`
|
||||
<div className="bg-actions h-16 w-16 rounded"></div>
|
||||
<div className="bg-infosecondary h-16 w-16 rounded"></div>
|
||||
<div className="bg-highlight h-16 w-16 rounded"></div>
|
||||
<div className="h-16 w-16 rounded bg-white"></div>
|
||||
<div className="h-16 w-16 rounded bg-white"></div>
|
||||
<div className="h-16 w-16 rounded bg-white"></div>
|
||||
`}
|
||||
>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<div className="bg-actions h-16 w-16 rounded"></div>
|
||||
<div className="bg-infosecondary h-16 w-16 rounded"></div>
|
||||
<div className="bg-highlight h-16 w-16 rounded"></div>
|
||||
<div className="h-16 w-16 rounded bg-white"></div>
|
||||
<div className="h-16 w-16 rounded bg-white"></div>
|
||||
<div className="h-16 w-16 rounded bg-white"></div>
|
||||
</div>
|
||||
</ShowcaseRow>
|
||||
|
||||
<ShowcaseRow
|
||||
title="Slider"
|
||||
description="Adjustable slider component"
|
||||
code={`
|
||||
<Slider defaultValue={[50]} max={100} step={1} />
|
||||
`}
|
||||
>
|
||||
<div className="w-full max-w-sm">
|
||||
<Slider
|
||||
defaultValue={[50]}
|
||||
max={100}
|
||||
step={1}
|
||||
/>
|
||||
</div>
|
||||
</ShowcaseRow>
|
||||
|
||||
<ShowcaseRow
|
||||
title="Typography"
|
||||
description="Various text sizes"
|
||||
code={`
|
||||
<div className="text-base text-white">Standard text size (text-base) 14px</div>
|
||||
<div className="text-sm text-white">Small text size (text-sm) 13px</div>
|
||||
<div className="text-xs text-white">Extra small text size (text-xs) 12px</div>
|
||||
`}
|
||||
>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="text-base text-white">Standard text size (text-base) 14px</div>
|
||||
<div className="text-sm text-white">Small text size (text-sm) 13px</div>
|
||||
<div className="text-xs text-white">Extra small text size (text-xs) 12px</div>
|
||||
</div>
|
||||
</ShowcaseRow>
|
||||
|
||||
<ShowcaseRow
|
||||
title="Typography large"
|
||||
description="Various large text sizes"
|
||||
code={`
|
||||
<div className="text-lg text-white">Large text size (text-lg) 16px</div>
|
||||
<div className="text-xl text-white">Extra large text size (text-xl) 18px</div>
|
||||
<div className="text-2xl text-white">Double extra large text size (text-2xl) 20px</div>
|
||||
`}
|
||||
>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="text-lg text-white">Large text size (text-lg) 16px</div>
|
||||
<div className="text-xl text-white">Extra large text size (text-xl) 18px</div>
|
||||
<div className="text-2xl text-white">Double extra large text size (text-2xl) 20px</div>
|
||||
</div>
|
||||
</ShowcaseRow>
|
||||
|
||||
<ShowcaseRow
|
||||
title="Tabs"
|
||||
description="Tabs component"
|
||||
code={`
|
||||
<Tabs className="w-[400px]">
|
||||
<TabsList>
|
||||
<TabsTrigger value="circle">Circle</TabsTrigger>
|
||||
<Separator orientation="vertical" />
|
||||
<TabsTrigger value="sphere">Sphere</TabsTrigger>
|
||||
<Separator orientation="vertical" />
|
||||
<TabsTrigger value="square">Square</TabsTrigger>
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
`}
|
||||
>
|
||||
<Tabs className="w-[400px]">
|
||||
<TabsList>
|
||||
<TabsTrigger value="circle">Circle</TabsTrigger>
|
||||
<Separator orientation="vertical" />
|
||||
<TabsTrigger value="sphere">Sphere</TabsTrigger>
|
||||
<Separator orientation="vertical" />
|
||||
<TabsTrigger value="square">Square</TabsTrigger>
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
</ShowcaseRow>
|
||||
|
||||
<ShowcaseRow
|
||||
title="Select"
|
||||
description="Select component"
|
||||
code={`
|
||||
<Select>
|
||||
<SelectTrigger className="w-[180px]">
|
||||
<SelectValue placeholder="Theme" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="light">Light</SelectItem>
|
||||
<SelectItem value="dark">Dark</SelectItem>
|
||||
<SelectItem value="system">System</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
`}
|
||||
>
|
||||
<Select>
|
||||
<SelectTrigger className="w-[180px]">
|
||||
<SelectValue placeholder="Theme" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="light">Light</SelectItem>
|
||||
<SelectItem value="dark">Dark</SelectItem>
|
||||
<SelectItem value="system">System</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</ShowcaseRow>
|
||||
|
||||
<ShowcaseRow
|
||||
title="Dropdown Menu"
|
||||
description="Various dropdown menu examples"
|
||||
code={`
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger>
|
||||
<Button>Open Basic</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem>Item 1</DropdownMenuItem>
|
||||
<DropdownMenuItem>Item 2</DropdownMenuItem>
|
||||
<DropdownMenuItem>Long name Item 3</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button>Open Align Start</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start">
|
||||
<DropdownMenuItem>Item 1</DropdownMenuItem>
|
||||
<DropdownMenuItem>Item 2</DropdownMenuItem>
|
||||
<DropdownMenuItem>Long name Item 3</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button>Open Align End</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem>Item 1</DropdownMenuItem>
|
||||
<DropdownMenuItem>Item 2</DropdownMenuItem>
|
||||
<DropdownMenuItem>Long name Item 3</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button>Open Align Top</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent side="top" align="start">
|
||||
<DropdownMenuItem onSelect={() => console.debug('Item 1')}>Item 1</DropdownMenuItem>
|
||||
<DropdownMenuItem onSelect={() => console.debug('Item 2')}>Item 2</DropdownMenuItem>
|
||||
<DropdownMenuItem onSelect={() => console.debug('Item 3')}>Long name Item 3</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
`}
|
||||
>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger>
|
||||
<Button>Open Basic</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem>Item 1</DropdownMenuItem>
|
||||
<DropdownMenuItem>Item 2</DropdownMenuItem>
|
||||
<DropdownMenuItem>Long name Item 3</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button>Open Align Start</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start">
|
||||
<DropdownMenuItem>Item 1</DropdownMenuItem>
|
||||
<DropdownMenuItem>Item 2</DropdownMenuItem>
|
||||
<DropdownMenuItem>Long name Item 3</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button>Open Align End</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem>Item 1</DropdownMenuItem>
|
||||
<DropdownMenuItem>Item 2</DropdownMenuItem>
|
||||
<DropdownMenuItem>Long name Item 3</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button>Open Align Top</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
side="top"
|
||||
align="start"
|
||||
>
|
||||
<DropdownMenuItem onSelect={() => console.debug('Item 1')}>Item 1</DropdownMenuItem>
|
||||
<DropdownMenuItem onSelect={() => console.debug('Item 2')}>Item 2</DropdownMenuItem>
|
||||
<DropdownMenuItem onSelect={() => console.debug('Item 3')}>
|
||||
Long name Item 3
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</ShowcaseRow>
|
||||
|
||||
<ShowcaseRow
|
||||
title="Switch"
|
||||
description="Switch component"
|
||||
code={`
|
||||
<Switch />
|
||||
`}
|
||||
>
|
||||
<Switch />
|
||||
</ShowcaseRow>
|
||||
|
||||
<ShowcaseRow
|
||||
title="Checkbox"
|
||||
description="Checkbox with label"
|
||||
code={`
|
||||
<div className="items-top flex space-x-2">
|
||||
<Checkbox id="terms1" />
|
||||
<div className="grid gap-1.5 pt-0.5 leading-none">
|
||||
<Label>Display inactive segmentations</Label>
|
||||
</div>
|
||||
</div>
|
||||
`}
|
||||
>
|
||||
<div className="items-top flex space-x-2">
|
||||
<Checkbox id="terms1" />
|
||||
<div className="grid gap-1.5 pt-0.5 leading-none">
|
||||
<Label>Display inactive segmentations</Label>
|
||||
</div>
|
||||
</div>
|
||||
</ShowcaseRow>
|
||||
|
||||
<ShowcaseRow
|
||||
title="Toggle"
|
||||
description="Toggle component"
|
||||
code={`
|
||||
<Toggle>Hello</Toggle>
|
||||
`}
|
||||
>
|
||||
<Toggle>Hello</Toggle>
|
||||
</ShowcaseRow>
|
||||
|
||||
<ShowcaseRow
|
||||
title="Slider"
|
||||
description="Slider component"
|
||||
code={`
|
||||
<div className="w-40 px-5">
|
||||
<Slider
|
||||
className="w-full"
|
||||
defaultValue={[50]}
|
||||
max={100}
|
||||
step={1}
|
||||
/>
|
||||
</div>
|
||||
`}
|
||||
>
|
||||
<div className="w-40 px-5">
|
||||
<Slider
|
||||
className="w-full"
|
||||
defaultValue={[50]}
|
||||
max={100}
|
||||
step={1}
|
||||
/>
|
||||
</div>
|
||||
</ShowcaseRow>
|
||||
|
||||
<ShowcaseRow
|
||||
title="Scroll Area"
|
||||
description="Scroll Area component"
|
||||
code={`
|
||||
<ScrollArea className="border-input bg-background h-[150px] w-[350px] rounded-md border p-2 text-sm text-white">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
|
||||
laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat
|
||||
non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
|
||||
magna aliqua.
|
||||
</ScrollArea>
|
||||
`}
|
||||
>
|
||||
<ScrollArea className="border-input bg-background h-[150px] w-[350px] rounded-md border p-2 text-sm text-white">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
|
||||
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
|
||||
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure
|
||||
dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt
|
||||
mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed
|
||||
do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
</ScrollArea>
|
||||
</ShowcaseRow>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ShowcaseRow({ title, description, children, code }: ShowcaseRowProps) {
|
||||
const [showCode, setShowCode] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="bg-secondary/10 mb-8 rounded-lg p-6">
|
||||
<div className="mb-4 flex items-start justify-between">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold">{title}</h2>
|
||||
{description && <p className="text-muted-foreground mt-1">{description}</p>}
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setShowCode(!showCode)}
|
||||
>
|
||||
{showCode ? 'Hide Code' : 'Show Code'} <Icons.Code className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<div className="showcase-content mb-4">{children}</div>
|
||||
{showCode && (
|
||||
<pre className="mt-4 overflow-x-auto rounded-md bg-black p-4 text-sm">
|
||||
<code>{code}</code>
|
||||
</pre>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user