Skip to content

Commit 7c547ff

Browse files
committed
feat(ui): add drag-to-resize on lineage DAG nodes
Signed-off-by: CTC97 <ctcurnin@gmail.com>
1 parent 86d6d42 commit 7c547ff

2 files changed

Lines changed: 62 additions & 41 deletions

File tree

web/client/src/library/components/graph/ModelNode.tsx

Lines changed: 61 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { isNil, isArrayNotEmpty, isNotNil, toID, isFalse } from '@utils/index'
22
import clsx from 'clsx'
3-
import { useMemo, useCallback, useState } from 'react'
3+
import { useMemo, useCallback, useState, useRef } from 'react'
44
import { ModelType } from '@api/client'
55
import { useLineageFlow } from './context'
66
import { type GraphNodeData } from './help'
7-
import { Position, type NodeProps } from 'reactflow'
7+
import { Position, type NodeProps, NodeResizeControl } from 'reactflow'
8+
import '@reactflow/node-resizer/dist/style.css'
89
import { type Column } from '@api/client'
910
import ModelNodeHeaderHandles from './ModelNodeHeaderHandles'
1011
import ModelColumns from './ModelColumns'
@@ -82,6 +83,8 @@ export default function ModelNode({
8283
)
8384

8485
const [isMouseOver, setIsMouseOver] = useState(false)
86+
const headerRef = useRef<HTMLDivElement>(null)
87+
const columnsWrapperRef = useRef<HTMLDivElement>(null)
8588

8689
const handleClick = useCallback(
8790
(e: React.MouseEvent) => {
@@ -153,7 +156,7 @@ export default function ModelNode({
153156
onMouseEnter={() => setIsMouseOver(true)}
154157
onMouseLeave={() => setIsMouseOver(false)}
155158
className={clsx(
156-
'text-xs font-semibold border-4',
159+
'text-xs font-semibold border-4 relative',
157160
isMouseOver ? 'z-50' : 'z-1',
158161
showColumns ? 'rounded-xl' : 'rounded-2xl',
159162
(hasHighlightedNodes ? isHighlightedNode : isActiveNode) || isMainNode
@@ -181,46 +184,64 @@ export default function ModelNode({
181184
? 'ring-8 ring-neutral-50'
182185
: isSelected && 'ring-8 ring-secondary-50 dark:ring-primary-50',
183186
)}
184-
style={{
185-
maxWidth: isNil(nodeData.width)
186-
? 'auto'
187-
: `${nodeData.width as number}px`,
188-
}}
187+
style={{ width: '100%' }}
189188
>
190-
<ModelNodeHeaderHandles
191-
id={id}
192-
type={nodeData.type}
193-
label={nodeData.label}
194-
isSelected={isSelected}
195-
isDraggable={true}
196-
className={clsx(
197-
'bg-theme-lighter',
198-
showColumns ? 'rounded-t-[8px]' : 'rounded-xl',
199-
)}
200-
hasLeft={targetPosition === Position.Left && isNil(lineageCache)}
201-
hasRight={sourcePosition === Position.Right && isNil(lineageCache)}
202-
handleClick={isInteractive ? handleClick : undefined}
203-
handleSelect={
204-
mainNode === id ||
205-
isCTE ||
206-
hasHighlightedNodes ||
207-
isNotNil(lineageCache)
208-
? undefined
209-
: handleSelect
210-
}
211-
count={hasHighlightedNodes ? undefined : columns.length}
189+
<NodeResizeControl
190+
minWidth={150}
191+
minHeight={36}
192+
position="bottom-right"
193+
style={{
194+
background: 'transparent',
195+
border: 'none',
196+
width: 10,
197+
height: 10,
198+
}}
199+
onResize={(_, params) => {
200+
const headerH = headerRef.current?.offsetHeight ?? 0
201+
const available = Math.max(0, params.height - headerH)
202+
if (columnsWrapperRef.current) {
203+
columnsWrapperRef.current.style.height = `${available}px`
204+
}
205+
}}
212206
/>
213-
{showColumns && (
214-
<ModelColumns
215-
className="nowheel rounded-b-lg bg-theme-lighter text-xs"
216-
nodeId={id}
217-
columns={columns}
218-
disabled={shouldDisableColumns}
219-
withHandles={true}
220-
withSource={true}
221-
withDescription={false}
222-
maxHeight="10rem"
207+
<div ref={headerRef}>
208+
<ModelNodeHeaderHandles
209+
id={id}
210+
type={nodeData.type}
211+
label={nodeData.label}
212+
isSelected={isSelected}
213+
isDraggable={true}
214+
className={clsx(
215+
'bg-theme-lighter',
216+
showColumns ? 'rounded-t-[8px]' : 'rounded-xl',
217+
)}
218+
hasLeft={targetPosition === Position.Left && isNil(lineageCache)}
219+
hasRight={sourcePosition === Position.Right && isNil(lineageCache)}
220+
handleClick={isInteractive ? handleClick : undefined}
221+
handleSelect={
222+
mainNode === id ||
223+
isCTE ||
224+
hasHighlightedNodes ||
225+
isNotNil(lineageCache)
226+
? undefined
227+
: handleSelect
228+
}
229+
count={hasHighlightedNodes ? undefined : columns.length}
223230
/>
231+
</div>
232+
{showColumns && (
233+
<div ref={columnsWrapperRef} style={{ height: '10rem', overflow: 'hidden' }}>
234+
<ModelColumns
235+
className="nowheel rounded-b-lg bg-theme-lighter text-xs h-full"
236+
nodeId={id}
237+
columns={columns}
238+
disabled={shouldDisableColumns}
239+
withHandles={true}
240+
withSource={true}
241+
withDescription={false}
242+
maxHeight="100%"
243+
/>
244+
</div>
224245
)}
225246
</div>
226247
)

web/client/src/library/components/graph/ModelNodeHeaderHandles.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ export default function ModelNodeHeaderHandles({
8585
)}
8686
onClick={handleClick}
8787
>
88-
{truncate(decodeURI(label), 50, 20)}
88+
{decodeURI(label)}
8989
</span>
9090
{isNotNil(count) && (
9191
<span className="flex justify-between ml-2 mr-1 px-2 rounded-full bg-neutral-10">

0 commit comments

Comments
 (0)