@pyreon/flow 0.10.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,32 +1,19 @@
1
- import type { VNodeChild } from '@pyreon/core'
2
- import { signal } from '@pyreon/reactivity'
3
- import {
4
- getEdgePath,
5
- getHandlePosition,
6
- getSmartHandlePositions,
7
- getWaypointPath,
8
- } from '../edges'
9
- import type {
10
- Connection,
11
- FlowInstance,
12
- FlowNode,
13
- NodeComponentProps,
14
- } from '../types'
15
- import { Position } from '../types'
1
+ import type { VNodeChild } from "@pyreon/core"
2
+ import { signal } from "@pyreon/reactivity"
3
+ import { getEdgePath, getHandlePosition, getSmartHandlePositions, getWaypointPath } from "../edges"
4
+ import type { Connection, FlowInstance, FlowNode, NodeComponentProps } from "../types"
5
+ import { Position } from "../types"
16
6
 
17
7
  // ─── Node type registry ──────────────────────────────────────────────────────
18
8
 
19
- type NodeTypeMap = Record<
20
- string,
21
- (props: NodeComponentProps<any>) => VNodeChild
22
- >
9
+ type NodeTypeMap = Record<string, (props: NodeComponentProps<any>) => VNodeChild>
23
10
 
24
11
  /**
25
12
  * Default node renderer — simple labeled box.
26
13
  */
27
14
  function DefaultNode(props: NodeComponentProps) {
28
- const borderColor = props.selected ? '#3b82f6' : '#ddd'
29
- const cursor = props.dragging ? 'grabbing' : 'grab'
15
+ const borderColor = props.selected ? "#3b82f6" : "#ddd"
16
+ const cursor = props.dragging ? "grabbing" : "grab"
30
17
  return (
31
18
  <div
32
19
  style={`padding: 8px 16px; background: white; border: 2px solid ${borderColor}; border-radius: 6px; font-size: 13px; min-width: 80px; text-align: center; cursor: ${cursor}; user-select: none;`}
@@ -51,8 +38,8 @@ interface ConnectionState {
51
38
 
52
39
  const emptyConnection: ConnectionState = {
53
40
  active: false,
54
- sourceNodeId: '',
55
- sourceHandleId: '',
41
+ sourceNodeId: "",
42
+ sourceHandleId: "",
56
43
  sourcePosition: Position.Right,
57
44
  sourceX: 0,
58
45
  sourceY: 0,
@@ -91,7 +78,7 @@ interface DragState {
91
78
 
92
79
  const emptyDrag: DragState = {
93
80
  active: false,
94
- nodeId: '',
81
+ nodeId: "",
95
82
  startX: 0,
96
83
  startY: 0,
97
84
  startPositions: new Map(),
@@ -123,11 +110,11 @@ function EdgeLayer(props: {
123
110
  <marker
124
111
  id="flow-arrowhead"
125
112
  {...{
126
- markerWidth: '10',
127
- markerHeight: '7',
128
- refX: '10',
129
- refY: '3.5',
130
- orient: 'auto',
113
+ markerWidth: "10",
114
+ markerHeight: "7",
115
+ refX: "10",
116
+ refY: "3.5",
117
+ orient: "auto",
131
118
  }}
132
119
  >
133
120
  <polygon points="0 0, 10 3.5, 0 7" fill="#999" />
@@ -143,10 +130,7 @@ function EdgeLayer(props: {
143
130
  const targetW = targetNode.width ?? 150
144
131
  const targetH = targetNode.height ?? 40
145
132
 
146
- const { sourcePosition, targetPosition } = getSmartHandlePositions(
147
- sourceNode,
148
- targetNode,
149
- )
133
+ const { sourcePosition, targetPosition } = getSmartHandlePositions(sourceNode, targetNode)
150
134
 
151
135
  const sourcePos = getHandlePosition(
152
136
  sourcePosition,
@@ -172,7 +156,7 @@ function EdgeLayer(props: {
172
156
  waypoints: edge.waypoints,
173
157
  })
174
158
  : getEdgePath(
175
- edge.type ?? 'bezier',
159
+ edge.type ?? "bezier",
176
160
  sourcePos.x,
177
161
  sourcePos.y,
178
162
  sourcePosition,
@@ -188,10 +172,7 @@ function EdgeLayer(props: {
188
172
  const CustomEdge = edge.type && edgeTypes?.[edge.type]
189
173
  if (CustomEdge) {
190
174
  return (
191
- <g
192
- key={edge.id}
193
- onClick={() => edge.id && instance.selectEdge(edge.id)}
194
- >
175
+ <g key={edge.id} onClick={() => edge.id && instance.selectEdge(edge.id)}>
195
176
  <CustomEdge
196
177
  edge={edge}
197
178
  sourceX={sourcePos.x}
@@ -209,11 +190,11 @@ function EdgeLayer(props: {
209
190
  <path
210
191
  d={path}
211
192
  fill="none"
212
- stroke={isSelected ? '#3b82f6' : '#999'}
213
- stroke-width={isSelected ? '2' : '1.5'}
193
+ stroke={isSelected ? "#3b82f6" : "#999"}
194
+ stroke-width={isSelected ? "2" : "1.5"}
214
195
  marker-end="url(#flow-arrowhead)"
215
- class={edge.animated ? 'pyreon-flow-edge-animated' : ''}
216
- style={`pointer-events: stroke; cursor: pointer; ${edge.style ?? ''}`}
196
+ class={edge.animated ? "pyreon-flow-edge-animated" : ""}
197
+ style={`pointer-events: stroke; cursor: pointer; ${edge.style ?? ""}`}
217
198
  onClick={() => {
218
199
  if (edge.id) instance.selectEdge(edge.id)
219
200
  instance._emit.edgeClick(edge)
@@ -237,7 +218,7 @@ function EdgeLayer(props: {
237
218
  <path
238
219
  d={
239
220
  getEdgePath(
240
- 'bezier',
221
+ "bezier",
241
222
  conn.sourceX,
242
223
  conn.sourceY,
243
224
  conn.sourcePosition,
@@ -272,13 +253,7 @@ function NodeLayer(props: {
272
253
  position: Position,
273
254
  ) => void
274
255
  }): VNodeChild {
275
- const {
276
- instance,
277
- nodeTypes,
278
- draggingNodeId,
279
- onNodePointerDown,
280
- onHandlePointerDown,
281
- } = props
256
+ const { instance, nodeTypes, draggingNodeId, onNodePointerDown, onHandlePointerDown } = props
282
257
 
283
258
  return () => {
284
259
  const nodes = instance.nodes()
@@ -290,14 +265,13 @@ function NodeLayer(props: {
290
265
  {nodes.map((node) => {
291
266
  const isSelected = selectedIds.includes(node.id)
292
267
  const isDragging = dragId === node.id
293
- const NodeComponent =
294
- (node.type && nodeTypes[node.type]) || nodeTypes.default!
268
+ const NodeComponent = (node.type && nodeTypes[node.type]) || nodeTypes.default!
295
269
 
296
270
  return (
297
271
  <div
298
272
  key={node.id}
299
- class={`pyreon-flow-node ${node.class ?? ''} ${isSelected ? 'selected' : ''} ${isDragging ? 'dragging' : ''}`}
300
- style={`position: absolute; transform: translate(${node.position.x}px, ${node.position.y}px); z-index: ${isDragging ? 1000 : isSelected ? 100 : 0}; ${node.style ?? ''}`}
273
+ class={`pyreon-flow-node ${node.class ?? ""} ${isSelected ? "selected" : ""} ${isDragging ? "dragging" : ""}`}
274
+ style={`position: absolute; transform: translate(${node.position.x}px, ${node.position.y}px); z-index: ${isDragging ? 1000 : isSelected ? 100 : 0}; ${node.style ?? ""}`}
301
275
  data-nodeid={node.id}
302
276
  onClick={(e: MouseEvent) => {
303
277
  e.stopPropagation()
@@ -311,22 +285,17 @@ function NodeLayer(props: {
311
285
  onPointerDown={(e: PointerEvent) => {
312
286
  // Check if clicking a handle
313
287
  const target = e.target as HTMLElement
314
- const handle = target.closest('.pyreon-flow-handle')
288
+ const handle = target.closest(".pyreon-flow-handle")
315
289
  if (handle) {
316
- const hType =
317
- handle.getAttribute('data-handletype') ?? 'source'
318
- const hId = handle.getAttribute('data-handleid') ?? 'source'
290
+ const hType = handle.getAttribute("data-handletype") ?? "source"
291
+ const hId = handle.getAttribute("data-handleid") ?? "source"
319
292
  const hPos =
320
- (handle.getAttribute('data-handleposition') as Position) ??
321
- Position.Right
293
+ (handle.getAttribute("data-handleposition") as Position) ?? Position.Right
322
294
  onHandlePointerDown(e, node.id, hType, hId, hPos)
323
295
  return
324
296
  }
325
297
  // Otherwise start dragging node
326
- if (
327
- node.draggable !== false &&
328
- instance.config.nodesDraggable !== false
329
- ) {
298
+ if (node.draggable !== false && instance.config.nodesDraggable !== false) {
330
299
  onNodePointerDown(e, node)
331
300
  }
332
301
  }}
@@ -350,7 +319,7 @@ function NodeLayer(props: {
350
319
  type EdgeTypeMap = Record<
351
320
  string,
352
321
  (props: {
353
- edge: import('../types').FlowEdge
322
+ edge: import("../types").FlowEdge
354
323
  sourceX: number
355
324
  sourceY: number
356
325
  targetX: number
@@ -409,7 +378,7 @@ export function Flow(props: FlowComponentProps): VNodeChild {
409
378
  y: null,
410
379
  })
411
380
 
412
- const draggingNodeId = () => (dragState().active ? dragState().nodeId : '')
381
+ const draggingNodeId = () => (dragState().active ? dragState().nodeId : "")
413
382
 
414
383
  // ── Node dragging ──────────────────────────────────────────────────────
415
384
 
@@ -447,9 +416,7 @@ export function Flow(props: FlowComponentProps): VNodeChild {
447
416
 
448
417
  instance._emit.nodeDragStart(node)
449
418
 
450
- const container = (e.currentTarget as HTMLElement).closest(
451
- '.pyreon-flow',
452
- ) as HTMLElement
419
+ const container = (e.currentTarget as HTMLElement).closest(".pyreon-flow") as HTMLElement
453
420
  if (container) container.setPointerCapture(e.pointerId)
454
421
  }
455
422
 
@@ -470,13 +437,7 @@ export function Flow(props: FlowComponentProps): VNodeChild {
470
437
 
471
438
  const w = node.width ?? 150
472
439
  const h = node.height ?? 40
473
- const handlePos = getHandlePosition(
474
- position,
475
- node.position.x,
476
- node.position.y,
477
- w,
478
- h,
479
- )
440
+ const handlePos = getHandlePosition(position, node.position.x, node.position.y, w, h)
480
441
 
481
442
  connectionState.set({
482
443
  active: true,
@@ -489,9 +450,7 @@ export function Flow(props: FlowComponentProps): VNodeChild {
489
450
  currentY: handlePos.y,
490
451
  })
491
452
 
492
- const container = (e.target as HTMLElement).closest(
493
- '.pyreon-flow',
494
- ) as HTMLElement
453
+ const container = (e.target as HTMLElement).closest(".pyreon-flow") as HTMLElement
495
454
  if (container) container.setPointerCapture(e.pointerId)
496
455
  }
497
456
 
@@ -503,10 +462,7 @@ export function Flow(props: FlowComponentProps): VNodeChild {
503
462
 
504
463
  const delta = -e.deltaY * 0.001
505
464
  const newZoom = Math.min(
506
- Math.max(
507
- instance.viewport.peek().zoom * (1 + delta),
508
- instance.config.minZoom ?? 0.1,
509
- ),
465
+ Math.max(instance.viewport.peek().zoom * (1 + delta), instance.config.minZoom ?? 0.1),
510
466
  instance.config.maxZoom ?? 4,
511
467
  )
512
468
 
@@ -535,8 +491,8 @@ export function Flow(props: FlowComponentProps): VNodeChild {
535
491
  if (instance.config.pannable === false) return
536
492
 
537
493
  const target = e.target as HTMLElement
538
- if (target.closest('.pyreon-flow-node')) return
539
- if (target.closest('.pyreon-flow-handle')) return
494
+ if (target.closest(".pyreon-flow-node")) return
495
+ if (target.closest(".pyreon-flow-handle")) return
540
496
 
541
497
  // Shift+drag on empty space → selection box
542
498
  if (e.shiftKey && instance.config.multiSelect !== false) {
@@ -681,11 +637,10 @@ export function Flow(props: FlowComponentProps): VNodeChild {
681
637
  if (conn.active) {
682
638
  // Check if we released over a handle target
683
639
  const target = e.target as HTMLElement
684
- const handle = target.closest('.pyreon-flow-handle')
640
+ const handle = target.closest(".pyreon-flow-handle")
685
641
  if (handle) {
686
- const targetNodeId =
687
- handle.closest('.pyreon-flow-node')?.getAttribute('data-nodeid') ?? ''
688
- const targetHandleId = handle.getAttribute('data-handleid') ?? 'target'
642
+ const targetNodeId = handle.closest(".pyreon-flow-node")?.getAttribute("data-nodeid") ?? ""
643
+ const targetHandleId = handle.getAttribute("data-handleid") ?? "target"
689
644
 
690
645
  if (targetNodeId && targetNodeId !== conn.sourceNodeId) {
691
646
  const connection: Connection = {
@@ -699,12 +654,8 @@ export function Flow(props: FlowComponentProps): VNodeChild {
699
654
  instance.addEdge({
700
655
  source: connection.source,
701
656
  target: connection.target,
702
- ...(connection.sourceHandle != null
703
- ? { sourceHandle: connection.sourceHandle }
704
- : {}),
705
- ...(connection.targetHandle != null
706
- ? { targetHandle: connection.targetHandle }
707
- : {}),
657
+ ...(connection.sourceHandle != null ? { sourceHandle: connection.sourceHandle } : {}),
658
+ ...(connection.targetHandle != null ? { targetHandle: connection.targetHandle } : {}),
708
659
  })
709
660
  }
710
661
  }
@@ -719,31 +670,31 @@ export function Flow(props: FlowComponentProps): VNodeChild {
719
670
  // ── Keyboard ───────────────────────────────────────────────────────────
720
671
 
721
672
  const handleKeyDown = (e: KeyboardEvent) => {
722
- if (e.key === 'Delete' || e.key === 'Backspace') {
673
+ if (e.key === "Delete" || e.key === "Backspace") {
723
674
  const target = e.target as HTMLElement
724
- if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA') return
675
+ if (target.tagName === "INPUT" || target.tagName === "TEXTAREA") return
725
676
  instance.pushHistory()
726
677
  instance.deleteSelected()
727
678
  }
728
- if (e.key === 'Escape') {
679
+ if (e.key === "Escape") {
729
680
  instance.clearSelection()
730
681
  connectionState.set({ ...emptyConnection })
731
682
  }
732
- if (e.key === 'a' && (e.metaKey || e.ctrlKey)) {
683
+ if (e.key === "a" && (e.metaKey || e.ctrlKey)) {
733
684
  e.preventDefault()
734
685
  instance.selectAll()
735
686
  }
736
- if (e.key === 'c' && (e.metaKey || e.ctrlKey)) {
687
+ if (e.key === "c" && (e.metaKey || e.ctrlKey)) {
737
688
  instance.copySelected()
738
689
  }
739
- if (e.key === 'v' && (e.metaKey || e.ctrlKey)) {
690
+ if (e.key === "v" && (e.metaKey || e.ctrlKey)) {
740
691
  instance.paste()
741
692
  }
742
- if (e.key === 'z' && (e.metaKey || e.ctrlKey) && !e.shiftKey) {
693
+ if (e.key === "z" && (e.metaKey || e.ctrlKey) && !e.shiftKey) {
743
694
  e.preventDefault()
744
695
  instance.undo()
745
696
  }
746
- if (e.key === 'z' && (e.metaKey || e.ctrlKey) && e.shiftKey) {
697
+ if (e.key === "z" && (e.metaKey || e.ctrlKey) && e.shiftKey) {
747
698
  e.preventDefault()
748
699
  instance.redo()
749
700
  }
@@ -759,10 +710,7 @@ export function Flow(props: FlowComponentProps): VNodeChild {
759
710
  e.preventDefault()
760
711
  const t1 = e.touches[0]!
761
712
  const t2 = e.touches[1]!
762
- lastTouchDist = Math.hypot(
763
- t2.clientX - t1.clientX,
764
- t2.clientY - t1.clientY,
765
- )
713
+ lastTouchDist = Math.hypot(t2.clientX - t1.clientX, t2.clientY - t1.clientY)
766
714
  lastTouchCenter = {
767
715
  x: (t1.clientX + t2.clientX) / 2,
768
716
  y: (t1.clientY + t2.clientY) / 2,
@@ -832,12 +780,12 @@ export function Flow(props: FlowComponentProps): VNodeChild {
832
780
  resizeObserver.observe(el)
833
781
  }
834
782
 
835
- const containerStyle = `position: relative; width: 100%; height: 100%; overflow: hidden; outline: none; touch-action: none; ${props.style ?? ''}`
783
+ const containerStyle = `position: relative; width: 100%; height: 100%; overflow: hidden; outline: none; touch-action: none; ${props.style ?? ""}`
836
784
 
837
785
  return (
838
786
  <div
839
787
  ref={containerRef}
840
- class={`pyreon-flow ${props.class ?? ''}`}
788
+ class={`pyreon-flow ${props.class ?? ""}`}
841
789
  style={containerStyle}
842
790
  tabIndex={0}
843
791
  onWheel={handleWheel}
@@ -1,11 +1,11 @@
1
- import type { VNodeChild } from '@pyreon/core'
2
- import type { HandleProps } from '../types'
1
+ import type { VNodeChild } from "@pyreon/core"
2
+ import type { HandleProps } from "../types"
3
3
 
4
4
  const positionOffset: Record<string, string> = {
5
- top: 'top: -4px; left: 50%; transform: translateX(-50%);',
6
- right: 'right: -4px; top: 50%; transform: translateY(-50%);',
7
- bottom: 'bottom: -4px; left: 50%; transform: translateX(-50%);',
8
- left: 'left: -4px; top: 50%; transform: translateY(-50%);',
5
+ top: "top: -4px; left: 50%; transform: translateX(-50%);",
6
+ right: "right: -4px; top: 50%; transform: translateY(-50%);",
7
+ bottom: "bottom: -4px; left: 50%; transform: translateX(-50%);",
8
+ left: "left: -4px; top: 50%; transform: translateY(-50%);",
9
9
  }
10
10
 
11
11
  /**
@@ -26,13 +26,13 @@ const positionOffset: Record<string, string> = {
26
26
  * ```
27
27
  */
28
28
  export function Handle(props: HandleProps): VNodeChild {
29
- const { type, position, id, style = '' } = props
29
+ const { type, position, id, style = "" } = props
30
30
  const posStyle = positionOffset[position] ?? positionOffset.bottom
31
31
  const baseStyle = `position: absolute; ${posStyle} width: 8px; height: 8px; background: #555; border: 2px solid white; border-radius: 50%; cursor: crosshair; z-index: 1; ${style}`
32
32
 
33
33
  return (
34
34
  <div
35
- class={`pyreon-flow-handle pyreon-flow-handle-${type} ${props.class ?? ''}`}
35
+ class={`pyreon-flow-handle pyreon-flow-handle-${type} ${props.class ?? ""}`}
36
36
  style={baseStyle}
37
37
  data-handletype={type}
38
38
  data-handleid={id ?? type}
@@ -1,5 +1,5 @@
1
- import type { VNodeChild } from '@pyreon/core'
2
- import type { FlowInstance, MiniMapProps } from '../types'
1
+ import type { VNodeChild } from "@pyreon/core"
2
+ import type { FlowInstance, MiniMapProps } from "../types"
3
3
 
4
4
  /**
5
5
  * Miniature overview of the flow diagram showing all nodes
@@ -12,14 +12,12 @@ import type { FlowInstance, MiniMapProps } from '../types'
12
12
  * </Flow>
13
13
  * ```
14
14
  */
15
- export function MiniMap(
16
- props: MiniMapProps & { instance?: FlowInstance },
17
- ): VNodeChild {
15
+ export function MiniMap(props: MiniMapProps & { instance?: FlowInstance }): VNodeChild {
18
16
  const {
19
17
  width = 200,
20
18
  height = 150,
21
- nodeColor = '#e2e8f0',
22
- maskColor = 'rgba(0, 0, 0, 0.08)',
19
+ nodeColor = "#e2e8f0",
20
+ maskColor = "rgba(0, 0, 0, 0.08)",
23
21
  instance,
24
22
  } = props
25
23
 
@@ -29,8 +27,7 @@ export function MiniMap(
29
27
 
30
28
  return () => {
31
29
  const nodes = instance.nodes()
32
- if (nodes.length === 0)
33
- return <div class="pyreon-flow-minimap" style={containerStyle} />
30
+ if (nodes.length === 0) return <div class="pyreon-flow-minimap" style={containerStyle} />
34
31
 
35
32
  // Calculate graph bounds
36
33
  let minX = Number.POSITIVE_INFINITY
@@ -78,23 +75,10 @@ export function MiniMap(
78
75
  }
79
76
 
80
77
  return (
81
- <div
82
- class="pyreon-flow-minimap"
83
- style={containerStyle}
84
- onClick={handleClick}
85
- >
86
- <svg
87
- role="img"
88
- aria-label="minimap"
89
- width={String(width)}
90
- height={String(height)}
91
- >
78
+ <div class="pyreon-flow-minimap" style={containerStyle} onClick={handleClick}>
79
+ <svg role="img" aria-label="minimap" width={String(width)} height={String(height)}>
92
80
  {/* Mask outside viewport */}
93
- <rect
94
- width={String(width)}
95
- height={String(height)}
96
- fill={maskColor}
97
- />
81
+ <rect width={String(width)} height={String(height)} fill={maskColor} />
98
82
 
99
83
  {/* Nodes */}
100
84
  {nodes.map((node) => {
@@ -102,8 +86,7 @@ export function MiniMap(
102
86
  const h = (node.height ?? 40) * scale
103
87
  const x = (node.position.x - minX + padding) * scale
104
88
  const y = (node.position.y - minY + padding) * scale
105
- const color =
106
- typeof nodeColor === 'function' ? nodeColor(node) : nodeColor
89
+ const color = typeof nodeColor === "function" ? nodeColor(node) : nodeColor
107
90
 
108
91
  return (
109
92
  <rect
@@ -1,5 +1,5 @@
1
- import type { VNodeChild } from '@pyreon/core'
2
- import type { FlowInstance } from '../types'
1
+ import type { VNodeChild } from "@pyreon/core"
2
+ import type { FlowInstance } from "../types"
3
3
 
4
4
  export interface NodeResizerProps {
5
5
  nodeId: string
@@ -14,28 +14,28 @@ export interface NodeResizerProps {
14
14
  showEdgeHandles?: boolean
15
15
  }
16
16
 
17
- type ResizeDirection = 'nw' | 'ne' | 'sw' | 'se' | 'n' | 's' | 'e' | 'w'
17
+ type ResizeDirection = "nw" | "ne" | "sw" | "se" | "n" | "s" | "e" | "w"
18
18
 
19
19
  const directionCursors: Record<ResizeDirection, string> = {
20
- nw: 'nw-resize',
21
- ne: 'ne-resize',
22
- sw: 'sw-resize',
23
- se: 'se-resize',
24
- n: 'n-resize',
25
- s: 's-resize',
26
- e: 'e-resize',
27
- w: 'w-resize',
20
+ nw: "nw-resize",
21
+ ne: "ne-resize",
22
+ sw: "sw-resize",
23
+ se: "se-resize",
24
+ n: "n-resize",
25
+ s: "s-resize",
26
+ e: "e-resize",
27
+ w: "w-resize",
28
28
  }
29
29
 
30
30
  const directionPositions: Record<ResizeDirection, string> = {
31
- nw: 'top: -4px; left: -4px;',
32
- ne: 'top: -4px; right: -4px;',
33
- sw: 'bottom: -4px; left: -4px;',
34
- se: 'bottom: -4px; right: -4px;',
35
- n: 'top: -4px; left: 50%; transform: translateX(-50%);',
36
- s: 'bottom: -4px; left: 50%; transform: translateX(-50%);',
37
- e: 'right: -4px; top: 50%; transform: translateY(-50%);',
38
- w: 'left: -4px; top: 50%; transform: translateY(-50%);',
31
+ nw: "top: -4px; left: -4px;",
32
+ ne: "top: -4px; right: -4px;",
33
+ sw: "bottom: -4px; left: -4px;",
34
+ se: "bottom: -4px; right: -4px;",
35
+ n: "top: -4px; left: 50%; transform: translateX(-50%);",
36
+ s: "bottom: -4px; left: 50%; transform: translateX(-50%);",
37
+ e: "right: -4px; top: 50%; transform: translateY(-50%);",
38
+ w: "left: -4px; top: 50%; transform: translateY(-50%);",
39
39
  }
40
40
 
41
41
  /**
@@ -67,8 +67,8 @@ export function NodeResizer(props: NodeResizerProps): VNodeChild {
67
67
  } = props
68
68
 
69
69
  const directions: ResizeDirection[] = showEdgeHandles
70
- ? ['nw', 'ne', 'sw', 'se', 'n', 's', 'e', 'w']
71
- : ['nw', 'ne', 'sw', 'se']
70
+ ? ["nw", "ne", "sw", "se", "n", "s", "e", "w"]
71
+ : ["nw", "ne", "sw", "se"]
72
72
 
73
73
  const createHandler = (dir: ResizeDirection) => {
74
74
  let startX = 0
@@ -112,19 +112,19 @@ export function NodeResizer(props: NodeResizerProps): VNodeChild {
112
112
  let newY = startNodeY
113
113
 
114
114
  // Horizontal
115
- if (dir === 'e' || dir === 'se' || dir === 'ne') {
115
+ if (dir === "e" || dir === "se" || dir === "ne") {
116
116
  newW = Math.max(minWidth, startWidth + dx)
117
117
  }
118
- if (dir === 'w' || dir === 'sw' || dir === 'nw') {
118
+ if (dir === "w" || dir === "sw" || dir === "nw") {
119
119
  newW = Math.max(minWidth, startWidth - dx)
120
120
  newX = startNodeX + startWidth - newW
121
121
  }
122
122
 
123
123
  // Vertical
124
- if (dir === 's' || dir === 'se' || dir === 'sw') {
124
+ if (dir === "s" || dir === "se" || dir === "sw") {
125
125
  newH = Math.max(minHeight, startHeight + dy)
126
126
  }
127
- if (dir === 'n' || dir === 'ne' || dir === 'nw') {
127
+ if (dir === "n" || dir === "ne" || dir === "nw") {
128
128
  newH = Math.max(minHeight, startHeight - dy)
129
129
  newY = startNodeY + startHeight - newH
130
130
  }
@@ -1,8 +1,8 @@
1
- import type { VNodeChild } from '@pyreon/core'
1
+ import type { VNodeChild } from "@pyreon/core"
2
2
 
3
3
  export interface NodeToolbarProps {
4
4
  /** Position relative to node — default: 'top' */
5
- position?: 'top' | 'bottom' | 'left' | 'right'
5
+ position?: "top" | "bottom" | "left" | "right"
6
6
  /** Offset from node in px — default: 8 */
7
7
  offset?: number
8
8
  /** Only show when node is selected — default: true */
@@ -15,10 +15,10 @@ export interface NodeToolbarProps {
15
15
  }
16
16
 
17
17
  const positionStyles: Record<string, string> = {
18
- top: 'bottom: 100%; left: 50%; transform: translateX(-50%);',
19
- bottom: 'top: 100%; left: 50%; transform: translateX(-50%);',
20
- left: 'right: 100%; top: 50%; transform: translateY(-50%);',
21
- right: 'left: 100%; top: 50%; transform: translateY(-50%);',
18
+ top: "bottom: 100%; left: 50%; transform: translateX(-50%);",
19
+ bottom: "top: 100%; left: 50%; transform: translateX(-50%);",
20
+ left: "right: 100%; top: 50%; transform: translateY(-50%);",
21
+ right: "left: 100%; top: 50%; transform: translateY(-50%);",
22
22
  }
23
23
 
24
24
  /**
@@ -41,33 +41,24 @@ const positionStyles: Record<string, string> = {
41
41
  * ```
42
42
  */
43
43
  export function NodeToolbar(props: NodeToolbarProps): VNodeChild {
44
- const {
45
- position = 'top',
46
- offset = 8,
47
- showOnSelect = true,
48
- selected = false,
49
- children,
50
- } = props
44
+ const { position = "top", offset = 8, showOnSelect = true, selected = false, children } = props
51
45
 
52
46
  if (showOnSelect && !selected) return null
53
47
 
54
48
  const posStyle = positionStyles[position] ?? positionStyles.top
55
49
  const marginProp =
56
- position === 'top'
50
+ position === "top"
57
51
  ? `margin-bottom: ${offset}px;`
58
- : position === 'bottom'
52
+ : position === "bottom"
59
53
  ? `margin-top: ${offset}px;`
60
- : position === 'left'
54
+ : position === "left"
61
55
  ? `margin-right: ${offset}px;`
62
56
  : `margin-left: ${offset}px;`
63
57
 
64
- const baseStyle = `position: absolute; ${posStyle} ${marginProp} z-index: 10; display: flex; gap: 4px; background: white; border: 1px solid #ddd; border-radius: 6px; padding: 4px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); ${props.style ?? ''}`
58
+ const baseStyle = `position: absolute; ${posStyle} ${marginProp} z-index: 10; display: flex; gap: 4px; background: white; border: 1px solid #ddd; border-radius: 6px; padding: 4px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); ${props.style ?? ""}`
65
59
 
66
60
  return (
67
- <div
68
- class={`pyreon-flow-node-toolbar ${props.class ?? ''}`}
69
- style={baseStyle}
70
- >
61
+ <div class={`pyreon-flow-node-toolbar ${props.class ?? ""}`} style={baseStyle}>
71
62
  {children}
72
63
  </div>
73
64
  )
@@ -1,11 +1,11 @@
1
- import type { VNodeChild } from '@pyreon/core'
2
- import type { PanelProps } from '../types'
1
+ import type { VNodeChild } from "@pyreon/core"
2
+ import type { PanelProps } from "../types"
3
3
 
4
4
  const positionStyles: Record<string, string> = {
5
- 'top-left': 'top: 10px; left: 10px;',
6
- 'top-right': 'top: 10px; right: 10px;',
7
- 'bottom-left': 'bottom: 10px; left: 10px;',
8
- 'bottom-right': 'bottom: 10px; right: 10px;',
5
+ "top-left": "top: 10px; left: 10px;",
6
+ "top-right": "top: 10px; right: 10px;",
7
+ "bottom-left": "bottom: 10px; left: 10px;",
8
+ "bottom-right": "bottom: 10px; right: 10px;",
9
9
  }
10
10
 
11
11
  /**
@@ -21,12 +21,12 @@ const positionStyles: Record<string, string> = {
21
21
  * ```
22
22
  */
23
23
  export function Panel(props: PanelProps): VNodeChild {
24
- const { position = 'top-left', style = '', children } = props
25
- const posStyle = positionStyles[position] ?? positionStyles['top-left']
24
+ const { position = "top-left", style = "", children } = props
25
+ const posStyle = positionStyles[position] ?? positionStyles["top-left"]
26
26
  const baseStyle = `position: absolute; ${posStyle} z-index: 5; ${style}`
27
27
 
28
28
  return (
29
- <div class={`pyreon-flow-panel ${props.class ?? ''}`} style={baseStyle}>
29
+ <div class={`pyreon-flow-panel ${props.class ?? ""}`} style={baseStyle}>
30
30
  {children}
31
31
  </div>
32
32
  )