generated from TBD54566975/tbd-project-template
-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add module tree with routing to basic decl pages (#2523)
Fixes #2487 * New tree view * Local storage persists the module expansion state and the width of the tree * Base page added for all decl types, with slightly more built out for verb and data to show how it all hooks together https://github.com/user-attachments/assets/36b9fb48-76ac-4fc7-aa34-54713675e462 Next: verb page code editor
- Loading branch information
Showing
12 changed files
with
384 additions
and
105 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import type React from 'react' | ||
import { useRef, useState } from 'react' | ||
|
||
interface ResizableHorizontalPanelsProps { | ||
leftPanelContent: React.ReactNode | ||
rightPanelContent: React.ReactNode | ||
minLeftPanelWidth?: number | ||
minRightPanelWidth?: number | ||
leftPanelWidth: number | ||
setLeftPanelWidth: (n: number) => void | ||
} | ||
|
||
export const ResizableHorizontalPanels: React.FC<ResizableHorizontalPanelsProps> = ({ | ||
leftPanelContent, | ||
rightPanelContent, | ||
minLeftPanelWidth = 100, | ||
minRightPanelWidth = 100, | ||
leftPanelWidth, | ||
setLeftPanelWidth, | ||
}) => { | ||
const containerRef = useRef<HTMLDivElement>(null) | ||
const [isDragging, setIsDragging] = useState(false) | ||
|
||
const startDragging = (e: React.MouseEvent<HTMLDivElement>) => { | ||
e.preventDefault() | ||
setIsDragging(true) | ||
} | ||
|
||
const stopDragging = () => setIsDragging(false) | ||
|
||
const onDrag = (e: React.MouseEvent<HTMLDivElement>) => { | ||
if (!isDragging || !containerRef.current) { | ||
return | ||
} | ||
const containerDims = containerRef.current.getBoundingClientRect() | ||
const newWidth = e.clientX - containerDims.x | ||
const maxWidth = containerDims.width - minRightPanelWidth | ||
if (newWidth >= minLeftPanelWidth && newWidth <= maxWidth) { | ||
setLeftPanelWidth(newWidth) | ||
} | ||
} | ||
|
||
return ( | ||
<div ref={containerRef} className='flex flex-row h-full w-full' onMouseMove={onDrag} onMouseUp={stopDragging} onMouseLeave={stopDragging}> | ||
<div style={{ width: `${leftPanelWidth}px` }} className='overflow-auto'> | ||
{leftPanelContent} | ||
</div> | ||
<div | ||
className='cursor-col-resize bg-gray-100 dark:bg-gray-900 hover:bg-indigo-600' | ||
onMouseDown={startDragging} | ||
style={{ width: '3px', cursor: 'col-resize' }} | ||
/> | ||
<div className='flex-1 overflow-auto'>{rightPanelContent}</div> | ||
</div> | ||
) | ||
} |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { useParams } from 'react-router-dom' | ||
|
||
export const ModulePanel = () => { | ||
const { moduleName } = useParams() | ||
|
||
return ( | ||
<div className='flex-1 py-2 px-4'> | ||
<p>Module: {moduleName}</p> | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,36 @@ | ||
import { useMemo } from 'react' | ||
import type React from 'react' | ||
import { useMemo, useState } from 'react' | ||
import { useSchema } from '../../api/schema/use-schema' | ||
import { ResizableHorizontalPanels } from '../../components/ResizableHorizontalPanels' | ||
import { ModulesTree } from './ModulesTree' | ||
import { moduleTreeFromSchema } from './module.utils' | ||
|
||
export const ModulesPage = () => { | ||
const treeWidthStorageKey = 'tree_w' | ||
|
||
export const ModulesPanel = () => { | ||
return ( | ||
<div className='flex-1 py-2 px-4'> | ||
<p>Content</p> | ||
</div> | ||
) | ||
} | ||
|
||
export const ModulesPage = ({ body }: { body: React.ReactNode }) => { | ||
const schema = useSchema() | ||
const tree = useMemo(() => moduleTreeFromSchema(schema?.data || []), [schema?.data]) | ||
const [treeWidth, setTreeWidth] = useState(Number(localStorage.getItem(treeWidthStorageKey)) || 300) | ||
|
||
return ( | ||
<div className='flex h-full'> | ||
<div className='w-64 h-full'> | ||
<ModulesTree modules={tree} /> | ||
</div> | ||
function setTreeWidthWithLS(newWidth: number) { | ||
localStorage.setItem(treeWidthStorageKey, `${newWidth}`) | ||
setTreeWidth(newWidth) | ||
} | ||
|
||
<div className='flex-1 py-2 px-4'> | ||
<p>Content</p> | ||
</div> | ||
</div> | ||
return ( | ||
<ResizableHorizontalPanels | ||
leftPanelContent={<ModulesTree modules={tree} />} | ||
rightPanelContent={body} | ||
leftPanelWidth={treeWidth} | ||
setLeftPanelWidth={setTreeWidthWithLS} | ||
/> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { Badge } from '../../../components/Badge' | ||
import type { Data } from '../../../protos/xyz/block/ftl/v1/schema/schema_pb' | ||
|
||
export const DataPanel = ({ value, moduleName, declName }: { value: Data; moduleName: string; declName: string }) => { | ||
return ( | ||
<div className='flex-1 py-2 px-4'> | ||
{value.export ? ( | ||
<div> | ||
<Badge name='Exported' /> | ||
</div> | ||
) : ( | ||
[] | ||
)} | ||
<div className='inline-block mr-3 align-middle'> | ||
<p> | ||
data: {moduleName}.{declName} | ||
</p> | ||
{value.comments.length > 0 ? <p className='text-xs my-1'>{value.comments}</p> : []} | ||
</div> | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { useMemo } from 'react' | ||
import { useParams } from 'react-router-dom' | ||
import { useSchema } from '../../../api/schema/use-schema' | ||
import { declFromSchema } from '../module.utils' | ||
import { DataPanel } from './DataPanel' | ||
import { VerbPanel } from './VerbPanel' | ||
|
||
export const DeclPanel = () => { | ||
const { moduleName, declCase, declName } = useParams() | ||
if (!moduleName || !declName) { | ||
// Should be impossible, but validate anyway for type safety | ||
return [] | ||
} | ||
|
||
const schema = useSchema() | ||
const decl = useMemo(() => declFromSchema(moduleName, declName, schema?.data || []), [schema?.data, moduleName, declCase, declName]) | ||
if (!decl) { | ||
return [] | ||
} | ||
|
||
const nameProps = { moduleName, declName } | ||
switch (decl.value.case) { | ||
case 'data': | ||
return <DataPanel value={decl.value.value} {...nameProps} /> | ||
case 'verb': | ||
return <VerbPanel value={decl.value.value} {...nameProps} /> | ||
} | ||
return ( | ||
<div className='flex-1 py-2 px-4'> | ||
<p> | ||
{declCase} declaration: {moduleName}.{declName} | ||
</p> | ||
</div> | ||
) | ||
} |
Oops, something went wrong.