Skip to content

Commit 75769ce

Browse files
fix(library): stop resize cursors on borders a node can't resize (#818)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent cec0a4c commit 75769ce

51 files changed

Lines changed: 603 additions & 184 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@tumaet/apollon": patch
3+
---
4+
5+
Fixed resize handles across every node. Borders that can't resize no longer show a resize cursor — a class's height is driven by its attributes and methods, so its top and bottom borders used to offer a vertical-resize cursor and a drag that did nothing. Content-sized nodes keep their corner handles and resize only along the axis that can change. And on filled nodes such as activity swimlanes, the edge you drag to resize is now grabbable instead of hiding behind the node — previously only the corners worked.

library/eslint.config.mjs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,22 @@ export default [
4040
"error",
4141
{ argsIgnorePattern: "^_", varsIgnorePattern: "^_" },
4242
],
43-
// The library styles with Base UI + raw CSS + --apollon-* tokens.
44-
// It must never depend on MUI or a CSS-in-JS runtime (Emotion).
43+
// `paths`: React Flow's NodeResizer renders controls on axes a node has
44+
// pinned, so nodes must reach it through the wrapper that knows the
45+
// difference. `patterns`: the library styles with Base UI + raw CSS +
46+
// --apollon-* tokens, and must never depend on MUI or a CSS-in-JS
47+
// runtime (Emotion).
4548
"no-restricted-imports": [
4649
"error",
4750
{
51+
paths: [
52+
{
53+
name: "@xyflow/react",
54+
importNames: ["NodeResizer"],
55+
message:
56+
"Import NodeResizer from @/nodes/wrappers. React Flow's renders resize controls on axes a node has pinned, which offers a cursor and a drag that do nothing.",
57+
},
58+
],
4859
patterns: [
4960
{
5061
group: ["@mui/*", "@mui"],

library/lib/components/wrapper/FeedbackDropzone.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,13 @@ interface Props {
99
elementId: string
1010
elementType?: string
1111
asElement?: "g" | "div" | "path"
12-
className?: string
1312
}
1413

1514
export const FeedbackDropzone: React.FC<Props> = ({
1615
children,
1716
elementId,
1817
elementType,
1918
asElement = "g",
20-
className,
2119
}) => {
2220
const { mode, readonly } = useMetadataStore(
2321
useShallow((store) => ({
@@ -94,7 +92,6 @@ export const FeedbackDropzone: React.FC<Props> = ({
9492
onDragLeave={handleDragLeave}
9593
onDragOver={handleDragOver}
9694
style={hoverStyle}
97-
className={className}
9895
>
9996
{children}
10097
</div>

library/lib/nodes/TitleAndDescriptionNode.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { NodeProps, NodeResizer, type Node } from "@xyflow/react"
2-
import { DefaultNodeWrapper } from "@/nodes/wrappers"
1+
import { NodeProps, type Node } from "@xyflow/react"
2+
import { DefaultNodeWrapper, NodeResizer } from "@/nodes/wrappers"
33
import { TitleAndDescriptionSVG } from "@/components"
44

55
type Props = Node<{
@@ -30,11 +30,7 @@ export function TitleAndDesctiption({
3030
title={title}
3131
description={description || ""}
3232
/>
33-
<NodeResizer
34-
isVisible
35-
minHeight={200}
36-
handleStyle={{ width: 8, height: 8 }}
37-
/>
33+
<NodeResizer isVisible minHeight={200} />
3834
</DefaultNodeWrapper>
3935
)
4036
}

library/lib/nodes/activityDiagram/Activity.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { NodeProps, NodeResizer, type Node } from "@xyflow/react"
1+
import { NodeProps, type Node } from "@xyflow/react"
22
import { usePopoverAnchor } from "@/hooks/usePopoverAnchor"
3-
import { DefaultNodeWrapper } from "../wrappers"
3+
import { DefaultNodeWrapper, NodeResizer } from "../wrappers"
44
import { useHandleOnResize } from "@/hooks"
55
import { DefaultNodeProps } from "@/types"
66
import { PopoverManager } from "@/components/popovers/PopoverManager"
@@ -31,7 +31,6 @@ export function Activity({
3131
onResize={onResize}
3232
minHeight={50}
3333
minWidth={50}
34-
handleStyle={{ width: 8, height: 8 }}
3534
/>
3635
<div ref={anchorRef}>
3736
<ActivitySVG

library/lib/nodes/activityDiagram/ActivityActionNode.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { NodeProps, NodeResizer, type Node } from "@xyflow/react"
1+
import { NodeProps, type Node } from "@xyflow/react"
22
import { usePopoverAnchor } from "@/hooks/usePopoverAnchor"
3-
import { DefaultNodeWrapper } from "../wrappers"
3+
import { DefaultNodeWrapper, NodeResizer } from "../wrappers"
44
import { useHandleOnResize } from "@/hooks"
55
import { DefaultNodeProps } from "@/types"
66
import { PopoverManager } from "@/components/popovers/PopoverManager"
@@ -31,7 +31,6 @@ export function ActivityActionNode({
3131
onResize={onResize}
3232
minHeight={50}
3333
minWidth={50}
34-
handleStyle={{ width: 8, height: 8 }}
3534
/>
3635
<div ref={anchorRef}>
3736
<ActivityActionNodeSVG

library/lib/nodes/activityDiagram/ActivityForkNode.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { NodeProps, NodeResizer, type Node } from "@xyflow/react"
1+
import { NodeProps, type Node } from "@xyflow/react"
22
import { usePopoverAnchor } from "@/hooks/usePopoverAnchor"
3-
import { DefaultNodeWrapper, HandleId } from "../wrappers"
3+
import { DefaultNodeWrapper, HandleId, NodeResizer } from "../wrappers"
44
import { useHandleOnResize } from "@/hooks"
55
import { DefaultNodeProps } from "@/types"
66
import { PopoverManager } from "@/components/popovers/PopoverManager"
@@ -34,15 +34,13 @@ export function ActivityForkNode({
3434
HandleId.BottomLeft,
3535
HandleId.BottomRight,
3636
]}
37-
className="vertically-not-resizable"
3837
>
3938
<NodeToolbar elementId={id} />
4039
<NodeResizer
4140
isVisible={isDiagramModifiable}
4241
onResize={onResize}
4342
minWidth={20}
4443
maxWidth={20}
45-
handleStyle={{ width: 8, height: 8 }}
4644
/>
4745
<div ref={anchorRef}>
4846
<ActivityForkNodeSVG

library/lib/nodes/activityDiagram/ActivityForkNodeHorizontal.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { NodeProps, NodeResizer, type Node } from "@xyflow/react"
1+
import { NodeProps, type Node } from "@xyflow/react"
22
import { usePopoverAnchor } from "@/hooks/usePopoverAnchor"
3-
import { DefaultNodeWrapper, HandleId } from "../wrappers"
3+
import { DefaultNodeWrapper, HandleId, NodeResizer } from "../wrappers"
44
import { useHandleOnResize } from "@/hooks"
55
import { DefaultNodeProps } from "@/types"
66
import { PopoverManager } from "@/components/popovers/PopoverManager"
@@ -34,15 +34,13 @@ export function ActivityForkNodeHorizontal({
3434
HandleId.RightTop,
3535
HandleId.RightBottom,
3636
]}
37-
className="horizontally-not-resizable"
3837
>
3938
<NodeToolbar elementId={id} />
4039
<NodeResizer
4140
isVisible={isDiagramModifiable}
4241
onResize={onResize}
4342
minHeight={20}
4443
maxHeight={20}
45-
handleStyle={{ width: 8, height: 8 }}
4644
/>
4745
<div ref={anchorRef}>
4846
<ActivityForkNodeHorizontalSVG

library/lib/nodes/activityDiagram/ActivityMergeNode.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1-
import { NodeProps, NodeResizer, type Node } from "@xyflow/react"
1+
import { NodeProps, type Node } from "@xyflow/react"
22
import { usePopoverAnchor } from "@/hooks/usePopoverAnchor"
3-
import { DefaultNodeWrapper, FOUR_WAY_HANDLES_PRESET } from "../wrappers"
3+
import {
4+
DefaultNodeWrapper,
5+
FOUR_WAY_HANDLES_PRESET,
6+
NodeResizer,
7+
} from "../wrappers"
48
import { useHandleOnResize } from "@/hooks"
59
import { DefaultNodeProps } from "@/types"
610
import { PopoverManager } from "@/components/popovers/PopoverManager"
@@ -37,7 +41,6 @@ export function ActivityMergeNode({
3741
onResize={onResize}
3842
minHeight={50}
3943
minWidth={50}
40-
handleStyle={{ width: 8, height: 8 }}
4144
/>
4245
<div ref={anchorRef}>
4346
<ActivityMergeNodeSVG

library/lib/nodes/activityDiagram/ActivityObjectNode.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { NodeProps, NodeResizer, type Node } from "@xyflow/react"
1+
import { NodeProps, type Node } from "@xyflow/react"
22
import { usePopoverAnchor } from "@/hooks/usePopoverAnchor"
3-
import { DefaultNodeWrapper } from "../wrappers"
3+
import { DefaultNodeWrapper, NodeResizer } from "../wrappers"
44
import { useHandleOnResize } from "@/hooks"
55
import { DefaultNodeProps } from "@/types"
66
import { PopoverManager } from "@/components/popovers/PopoverManager"
@@ -32,7 +32,6 @@ export function ActivityObjectNode({
3232
onResize={onResize}
3333
minHeight={50}
3434
minWidth={50}
35-
handleStyle={{ width: 8, height: 8 }}
3635
/>
3736
<div ref={anchorRef}>
3837
<ActivityObjectNodeSVG

0 commit comments

Comments
 (0)