Skip to content

Commit fa46264

Browse files
authored
feat(ui): add drag-to-resize on lineage DAG nodes (#5831)
Signed-off-by: CTC97 <ctcurnin@gmail.com> Signed-off-by: CTC97 <connor.curnin@translucent.co>
1 parent ba4054a commit fa46264

2 files changed

Lines changed: 61 additions & 41 deletions

File tree

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

Lines changed: 60 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
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'
88
import { type Column } from '@api/client'
99
import ModelNodeHeaderHandles from './ModelNodeHeaderHandles'
1010
import ModelColumns from './ModelColumns'
@@ -82,6 +82,8 @@ export default function ModelNode({
8282
)
8383

8484
const [isMouseOver, setIsMouseOver] = useState(false)
85+
const headerRef = useRef<HTMLDivElement>(null)
86+
const columnsWrapperRef = useRef<HTMLDivElement>(null)
8587

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

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)