@01.software/sdk 0.4.3-dev.260324.6dc30aa → 0.5.0

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.
package/dist/ui/flow.js CHANGED
@@ -39,18 +39,6 @@ var __async = (__this, __arguments, generator) => {
39
39
  });
40
40
  };
41
41
 
42
- // src/ui/Flow/index.tsx
43
- import React2 from "react";
44
- import {
45
- ReactFlow,
46
- ReactFlowProvider,
47
- Background,
48
- Controls,
49
- MiniMap,
50
- MarkerType,
51
- useReactFlow
52
- } from "@xyflow/react";
53
-
54
42
  // src/ui/Flow/types.ts
55
43
  function isDynamicNode(node) {
56
44
  return node.type === "dynamic";
@@ -217,6 +205,44 @@ function useFlow(options) {
217
205
  };
218
206
  }
219
207
 
208
+ // src/ui/Flow/prefetchFlow.ts
209
+ function prefetchFlow(options) {
210
+ return __async(this, null, function* () {
211
+ var _a;
212
+ const { client, slug, id } = options;
213
+ const identifier = (_a = id != null ? id : slug) != null ? _a : "";
214
+ yield Promise.all([
215
+ client.queryClient.prefetchQuery({
216
+ queryKey: collectionKeys("flows").detail(identifier),
217
+ queryFn: () => __async(null, null, function* () {
218
+ if (id) return client.from("flows").findById(id);
219
+ const result = yield client.from("flows").find({
220
+ where: { slug: { equals: slug } },
221
+ limit: 1
222
+ });
223
+ const doc = result.docs[0];
224
+ if (!doc) throw new Error(`Flow not found: ${slug}`);
225
+ return doc;
226
+ })
227
+ }),
228
+ client.queryClient.prefetchQuery({
229
+ queryKey: collectionKeys("flow-node-types").lists(),
230
+ queryFn: () => __async(null, null, function* () {
231
+ const result = yield client.from("flow-node-types").find({ limit: 100 });
232
+ return result.docs;
233
+ })
234
+ }),
235
+ client.queryClient.prefetchQuery({
236
+ queryKey: collectionKeys("flow-edge-types").lists(),
237
+ queryFn: () => __async(null, null, function* () {
238
+ const result = yield client.from("flow-edge-types").find({ limit: 100 });
239
+ return result.docs;
240
+ })
241
+ })
242
+ ]);
243
+ });
244
+ }
245
+
220
246
  // src/ui/Flow/useFlowData.ts
221
247
  import { useMemo as useMemo2 } from "react";
222
248
  function useFlowData(options) {
@@ -389,7 +415,7 @@ function validateTemplateCode(code) {
389
415
  return !BLOCKED_PATTERNS.some((pattern) => pattern.test(code));
390
416
  }
391
417
  function compileTemplate(code, slug) {
392
- const cacheKey = `${slug}:${code.length}:${hashCode(code)}:${code.slice(0, 64)}`;
418
+ const cacheKey = `${slug}:${hashCode(code)}`;
393
419
  if (componentCache.has(cacheKey)) {
394
420
  const cached = componentCache.get(cacheKey);
395
421
  componentCache.delete(cacheKey);
@@ -443,7 +469,21 @@ function clearTemplateCache() {
443
469
  componentCache.clear();
444
470
  }
445
471
 
446
- // src/ui/Flow/index.tsx
472
+ // src/ui/Flow/FlowRenderer.tsx
473
+ import React5 from "react";
474
+ import {
475
+ ReactFlow,
476
+ ReactFlowProvider,
477
+ Background,
478
+ Controls,
479
+ MiniMap
480
+ } from "@xyflow/react";
481
+
482
+ // src/ui/Flow/node-types-factory.tsx
483
+ import React3 from "react";
484
+
485
+ // src/ui/Flow/node-renderers.tsx
486
+ import React2 from "react";
447
487
  function sanitizeUrl(url) {
448
488
  if (!url) return url;
449
489
  try {
@@ -454,11 +494,6 @@ function sanitizeUrl(url) {
454
494
  return void 0;
455
495
  }
456
496
  }
457
- function toMarkerType(value) {
458
- if (value === "arrow") return MarkerType.Arrow;
459
- if (value === "arrowclosed") return MarkerType.ArrowClosed;
460
- return void 0;
461
- }
462
497
  function renderFieldValue(key, val, fieldDef) {
463
498
  if (val == null || val === "") return null;
464
499
  const fieldType = fieldDef == null ? void 0 : fieldDef.fieldType;
@@ -540,10 +575,7 @@ function EnhancedDynamicNode({
540
575
  "div",
541
576
  {
542
577
  className: `flow-node flow-node--${typeDef.slug}${typeDef.transparentBackground ? " flow-node--transparent-bg" : ""}`,
543
- style: {
544
- width: "100%",
545
- height: "100%"
546
- }
578
+ style: { width: "100%", height: "100%" }
547
579
  },
548
580
  /* @__PURE__ */ React2.createElement(TemplateErrorBoundary, { resetKey: typeDef.template }, /* @__PURE__ */ React2.createElement(
549
581
  Component,
@@ -552,8 +584,8 @@ function EnhancedDynamicNode({
552
584
  label: data.label,
553
585
  color: typeDef.color,
554
586
  nodeTypeSlug: typeDef.slug,
555
- width: width != null ? width : typeDef.defaultSize.width,
556
- height: height != null ? height : typeDef.defaultSize.height
587
+ width: width || typeDef.defaultSize.width,
588
+ height: height || typeDef.defaultSize.height
557
589
  }
558
590
  ))
559
591
  );
@@ -579,7 +611,7 @@ function EnhancedDynamicNode({
579
611
  function DefaultFrameNode({ data }) {
580
612
  var _a, _b, _c, _d;
581
613
  const d = data;
582
- const baseColor = (_a = d.color) != null ? _a : "rgba(128,128,128,0.15)";
614
+ const baseColor = (_a = d.color) != null ? _a : "rgb(128,128,128)";
583
615
  const padding = (_b = d.padding) != null ? _b : 20;
584
616
  const borderStyle = (_c = d.borderStyle) != null ? _c : "dashed";
585
617
  const opacity = (_d = d.opacity) != null ? _d : 0.15;
@@ -613,43 +645,42 @@ function DefaultFrameNode({ data }) {
613
645
  )
614
646
  );
615
647
  }
648
+
649
+ // src/ui/Flow/node-types-factory.tsx
616
650
  function createNodeTypes(nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrapper, renderNode) {
617
651
  const types = {};
618
652
  types.dynamic = ((props) => {
619
653
  const d = props.data;
620
654
  const typeDef = nodeTypeDefsMap == null ? void 0 : nodeTypeDefsMap.get(d.nodeTypeSlug);
621
655
  const CustomRenderer = nodeRenderers == null ? void 0 : nodeRenderers[d.nodeTypeSlug];
622
- let content;
623
- if (CustomRenderer) {
624
- content = /* @__PURE__ */ React2.createElement(
625
- CustomRenderer,
626
- {
627
- id: props.id,
628
- nodeTypeSlug: d.nodeTypeSlug,
629
- label: d.label,
630
- fields: d.fields,
631
- nodeTypeDef: typeDef
632
- }
633
- );
634
- } else if (typeDef) {
635
- content = /* @__PURE__ */ React2.createElement(EnhancedDynamicNode, { data: d, typeDef, width: props.width, height: props.height });
636
- } else {
637
- content = /* @__PURE__ */ React2.createElement(DefaultDynamicNode, __spreadValues({}, props));
638
- }
656
+ const defaultRender = typeDef ? /* @__PURE__ */ React3.createElement(
657
+ EnhancedDynamicNode,
658
+ {
659
+ data: d,
660
+ typeDef,
661
+ width: props.width,
662
+ height: props.height
663
+ }
664
+ ) : /* @__PURE__ */ React3.createElement(DefaultDynamicNode, __spreadValues({}, props));
665
+ const slotProps = {
666
+ id: props.id,
667
+ nodeTypeSlug: d.nodeTypeSlug,
668
+ label: d.label,
669
+ fields: d.fields,
670
+ nodeTypeDef: typeDef,
671
+ selected: props.selected,
672
+ width: props.width,
673
+ height: props.height,
674
+ defaultRender
675
+ };
676
+ let content = CustomRenderer ? /* @__PURE__ */ React3.createElement(CustomRenderer, __spreadValues({}, slotProps)) : defaultRender;
639
677
  if (renderNode) {
640
- const slotProps = {
641
- id: props.id,
642
- nodeTypeSlug: d.nodeTypeSlug,
643
- label: d.label,
644
- fields: d.fields,
645
- nodeTypeDef: typeDef
646
- };
647
678
  const result = renderNode(slotProps, content);
648
679
  if (result !== null) content = result;
649
680
  }
650
681
  if (nodeWrapper) {
651
682
  const Wrapper = nodeWrapper;
652
- content = /* @__PURE__ */ React2.createElement(
683
+ content = /* @__PURE__ */ React3.createElement(
653
684
  Wrapper,
654
685
  {
655
686
  id: props.id,
@@ -666,7 +697,7 @@ function createNodeTypes(nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrap
666
697
  types.frame = frameRenderer ? ((props) => {
667
698
  const d = props.data;
668
699
  const Renderer = frameRenderer;
669
- return /* @__PURE__ */ React2.createElement(
700
+ return /* @__PURE__ */ React3.createElement(
670
701
  Renderer,
671
702
  {
672
703
  id: props.id,
@@ -674,20 +705,23 @@ function createNodeTypes(nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrap
674
705
  color: d.color,
675
706
  padding: d.padding,
676
707
  borderStyle: d.borderStyle,
677
- opacity: d.opacity
708
+ opacity: d.opacity,
709
+ width: props.width,
710
+ height: props.height
678
711
  }
679
712
  );
680
713
  }) : DefaultFrameNode;
681
714
  return types;
682
715
  }
683
716
  function createEdgeTypes(edgeRenderers, edgeTypeDefsMap) {
684
- if (!edgeRenderers || Object.keys(edgeRenderers).length === 0) return void 0;
717
+ if (!edgeRenderers || Object.keys(edgeRenderers).length === 0)
718
+ return void 0;
685
719
  const types = {};
686
720
  for (const [slug, Renderer] of Object.entries(edgeRenderers)) {
687
721
  types[slug] = ((props) => {
688
722
  var _a;
689
723
  const def = edgeTypeDefsMap == null ? void 0 : edgeTypeDefsMap.get(slug);
690
- return /* @__PURE__ */ React2.createElement(
724
+ return /* @__PURE__ */ React3.createElement(
691
725
  Renderer,
692
726
  {
693
727
  id: props.id,
@@ -704,22 +738,92 @@ function createEdgeTypes(edgeRenderers, edgeTypeDefsMap) {
704
738
  }
705
739
  return types;
706
740
  }
741
+
742
+ // src/ui/Flow/edge-styles.ts
743
+ import { MarkerType } from "@xyflow/react";
744
+ function toMarkerType(value) {
745
+ if (value === "arrow") return MarkerType.Arrow;
746
+ if (value === "arrowclosed") return MarkerType.ArrowClosed;
747
+ return void 0;
748
+ }
749
+ var EDGE_TYPE_MAP = {
750
+ step: "step",
751
+ smoothstep: "smoothstep",
752
+ bezier: "default",
753
+ default: "default"
754
+ };
755
+ function applyEdgeStyles(edges, edgeTypeDefsMap) {
756
+ if (!(edgeTypeDefsMap == null ? void 0 : edgeTypeDefsMap.size)) return edges;
757
+ return edges.map((edge) => {
758
+ var _a;
759
+ const slug = edge.edgeTypeSlug;
760
+ if (!slug) return edge;
761
+ const def = edgeTypeDefsMap.get(slug);
762
+ if (!def) return edge;
763
+ const styled = __spreadValues({}, edge);
764
+ if (!styled.type && def.lineStyle) {
765
+ styled.type = (_a = EDGE_TYPE_MAP[def.lineStyle]) != null ? _a : "default";
766
+ }
767
+ styled.style = __spreadValues(__spreadValues(__spreadValues({}, def.color ? { stroke: def.color } : void 0), def.strokeWidth ? { strokeWidth: def.strokeWidth } : void 0), edge.style);
768
+ if (styled.animated == null && def.animated) styled.animated = true;
769
+ if (!styled.markerStart) {
770
+ const startType = toMarkerType(def.markerStart);
771
+ if (startType) {
772
+ styled.markerStart = __spreadValues({
773
+ type: startType
774
+ }, def.color ? { color: def.color } : void 0);
775
+ }
776
+ }
777
+ if (!styled.markerEnd) {
778
+ const endType = toMarkerType(def.markerEnd);
779
+ if (endType) {
780
+ styled.markerEnd = __spreadValues({
781
+ type: endType
782
+ }, def.color ? { color: def.color } : void 0);
783
+ }
784
+ }
785
+ return styled;
786
+ });
787
+ }
788
+
789
+ // src/ui/Flow/focus-handler.tsx
790
+ import React4 from "react";
791
+ import { useReactFlow, useStoreApi } from "@xyflow/react";
792
+ function clampViewport(vp, cw, ch, extent) {
793
+ const left = -vp.x / vp.zoom;
794
+ const right = (cw - vp.x) / vp.zoom;
795
+ const top = -vp.y / vp.zoom;
796
+ const bottom = (ch - vp.y) / vp.zoom;
797
+ const dx0 = left - extent[0][0];
798
+ const dx1 = right - extent[1][0];
799
+ const dy0 = top - extent[0][1];
800
+ const dy1 = bottom - extent[1][1];
801
+ const cx = dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1);
802
+ const cy = dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1);
803
+ if (cx === 0 && cy === 0) return vp;
804
+ return { x: vp.x + cx * vp.zoom, y: vp.y + cy * vp.zoom, zoom: vp.zoom };
805
+ }
707
806
  function FocusHandler({
708
807
  bounds,
709
808
  padding,
710
809
  animation,
711
810
  mode,
712
- responsive
811
+ responsive,
812
+ extent,
813
+ clampBounds,
814
+ minZoomProp,
815
+ onInitialFit
713
816
  }) {
714
- const { fitBounds, setViewport } = useReactFlow();
715
- const containerRef = React2.useRef(null);
817
+ const { setViewport } = useReactFlow();
818
+ const store = useStoreApi();
819
+ const containerRef = React4.useRef(null);
716
820
  const boundsKey = `${bounds.x},${bounds.y},${bounds.width},${bounds.height}`;
717
- const boundsRef = React2.useRef(bounds);
821
+ const boundsRef = React4.useRef(bounds);
718
822
  boundsRef.current = bounds;
719
- const [containerSize, setContainerSize] = React2.useState({ w: 0, h: 0 });
720
- const prevBoundsKeyRef = React2.useRef(null);
721
- const prevSizeRef = React2.useRef({ w: 0, h: 0 });
722
- React2.useEffect(() => {
823
+ const [containerSize, setContainerSize] = React4.useState({ w: 0, h: 0 });
824
+ const prevBoundsKeyRef = React4.useRef(null);
825
+ const prevSizeRef = React4.useRef({ w: 0, h: 0 });
826
+ React4.useEffect(() => {
723
827
  const el = containerRef.current;
724
828
  if (!el) return;
725
829
  const observer = new ResizeObserver((entries) => {
@@ -731,7 +835,7 @@ function FocusHandler({
731
835
  observer.observe(el);
732
836
  return () => observer.disconnect();
733
837
  }, []);
734
- React2.useEffect(() => {
838
+ React4.useEffect(() => {
735
839
  if (containerSize.w === 0 || containerSize.h === 0) return;
736
840
  const prevKey = prevBoundsKeyRef.current;
737
841
  const prevSize = prevSizeRef.current;
@@ -741,70 +845,78 @@ function FocusHandler({
741
845
  const isResizeOnly = !isBoundsChange && (prevSize.w !== containerSize.w || prevSize.h !== containerSize.h);
742
846
  const isInitial = prevKey === null;
743
847
  if (isResizeOnly && !responsive) return;
744
- const duration = isInitial || isBoundsChange ? animation === true ? 300 : typeof animation === "number" ? animation : 0 : 0;
848
+ const duration = isInitial ? 0 : isBoundsChange ? animation === true ? 300 : typeof animation === "number" ? animation : 0 : 0;
745
849
  const b = boundsRef.current;
746
850
  const padX = padding * b.width;
747
851
  const padY = padding * b.height;
748
852
  const bw = b.width + padX * 2;
749
853
  const bh = b.height + padY * 2;
750
- if (mode === "cover") {
751
- const zoom = Math.max(containerSize.w / bw, containerSize.h / bh);
752
- const cx = b.x + b.width / 2;
753
- const cy = b.y + b.height / 2;
754
- const x = containerSize.w / 2 - cx * zoom;
755
- const y = containerSize.h / 2 - cy * zoom;
756
- setViewport({ x, y, zoom }, { duration });
854
+ if (bw === 0 || bh === 0) return;
855
+ const zoomFn = mode === "cover" ? Math.max : Math.min;
856
+ const zoom = zoomFn(containerSize.w / bw, containerSize.h / bh);
857
+ const centerTarget = clampBounds != null ? clampBounds : b;
858
+ const cx = centerTarget.x + centerTarget.width / 2;
859
+ const cy = centerTarget.y + centerTarget.height / 2;
860
+ const x = containerSize.w / 2 - cx * zoom;
861
+ const y = containerSize.h / 2 - cy * zoom;
862
+ if (clampBounds) {
863
+ const coverZoom = Math.max(
864
+ containerSize.w / clampBounds.width,
865
+ containerSize.h / clampBounds.height
866
+ );
867
+ store.getState().setMinZoom(Math.max(Math.min(coverZoom, zoom), minZoomProp != null ? minZoomProp : 0));
757
868
  } else {
758
- fitBounds(boundsRef.current, { padding, duration });
869
+ store.getState().setMinZoom(minZoomProp != null ? minZoomProp : 0.5);
759
870
  }
760
- }, [boundsKey, padding, animation, mode, responsive, containerSize.w, containerSize.h, fitBounds, setViewport]);
761
- return /* @__PURE__ */ React2.createElement(
871
+ let vp = { x, y, zoom };
872
+ if (isInitial || isBoundsChange) {
873
+ setViewport(vp, { duration: isInitial ? 0 : duration });
874
+ if (extent) {
875
+ const visW = containerSize.w / zoom;
876
+ const visH = containerSize.h / zoom;
877
+ onInitialFit == null ? void 0 : onInitialFit([
878
+ [Math.min(extent[0][0], cx - visW / 2), Math.min(extent[0][1], cy - visH / 2)],
879
+ [Math.max(extent[1][0], cx + visW / 2), Math.max(extent[1][1], cy + visH / 2)]
880
+ ]);
881
+ } else {
882
+ onInitialFit == null ? void 0 : onInitialFit();
883
+ }
884
+ } else {
885
+ if (extent) {
886
+ vp = clampViewport(vp, containerSize.w, containerSize.h, extent);
887
+ }
888
+ setViewport(vp, { duration });
889
+ }
890
+ }, [
891
+ boundsKey,
892
+ padding,
893
+ animation,
894
+ mode,
895
+ responsive,
896
+ containerSize.w,
897
+ containerSize.h,
898
+ extent,
899
+ setViewport,
900
+ clampBounds,
901
+ minZoomProp,
902
+ store,
903
+ onInitialFit
904
+ ]);
905
+ return /* @__PURE__ */ React4.createElement(
762
906
  "div",
763
907
  {
764
908
  ref: containerRef,
765
- style: { position: "absolute", inset: 0, pointerEvents: "none", visibility: "hidden" }
766
- }
767
- );
768
- }
769
- var EDGE_TYPE_MAP = {
770
- step: "step",
771
- smoothstep: "smoothstep",
772
- bezier: "default",
773
- default: "default"
774
- };
775
- function applyEdgeStyles(edges, edgeTypeDefsMap) {
776
- if (!(edgeTypeDefsMap == null ? void 0 : edgeTypeDefsMap.size)) return edges;
777
- return edges.map((edge) => {
778
- var _a;
779
- const slug = edge.edgeTypeSlug;
780
- if (!slug) return edge;
781
- const def = edgeTypeDefsMap.get(slug);
782
- if (!def) return edge;
783
- const styled = __spreadValues({}, edge);
784
- if (!styled.type && def.lineStyle) {
785
- styled.type = (_a = EDGE_TYPE_MAP[def.lineStyle]) != null ? _a : "default";
786
- }
787
- styled.style = __spreadValues(__spreadValues(__spreadValues({}, def.color ? { stroke: def.color } : void 0), def.strokeWidth ? { strokeWidth: def.strokeWidth } : void 0), edge.style);
788
- if (styled.animated == null && def.animated) styled.animated = true;
789
- if (!styled.markerStart) {
790
- const startType = toMarkerType(def.markerStart);
791
- if (startType) {
792
- styled.markerStart = __spreadValues({
793
- type: startType
794
- }, def.color ? { color: def.color } : void 0);
795
- }
796
- }
797
- if (!styled.markerEnd) {
798
- const endType = toMarkerType(def.markerEnd);
799
- if (endType) {
800
- styled.markerEnd = __spreadValues({
801
- type: endType
802
- }, def.color ? { color: def.color } : void 0);
909
+ style: {
910
+ position: "absolute",
911
+ inset: 0,
912
+ pointerEvents: "none",
913
+ visibility: "hidden"
803
914
  }
804
915
  }
805
- return styled;
806
- });
916
+ );
807
917
  }
918
+
919
+ // src/ui/Flow/FlowRenderer.tsx
808
920
  function FlowRenderer({
809
921
  data,
810
922
  className,
@@ -816,6 +928,10 @@ function FlowRenderer({
816
928
  interactive = false,
817
929
  fitView = true,
818
930
  onNodeClick,
931
+ onNodeDoubleClick,
932
+ onNodeContextMenu,
933
+ onNodeMouseEnter,
934
+ onNodeMouseLeave,
819
935
  onEdgeClick,
820
936
  frameRenderer,
821
937
  edgeRenderers,
@@ -833,30 +949,38 @@ function FlowRenderer({
833
949
  focusAnimation,
834
950
  focusMode = "contain",
835
951
  responsiveFit,
836
- translateExtent: translateExtentProp
952
+ translateExtent: translateExtentProp,
953
+ minZoom: minZoomProp,
954
+ maxZoom: maxZoomProp
837
955
  }) {
838
- var _a;
839
- const nodeTypeDefsMap = React2.useMemo(() => {
956
+ var _a, _b;
957
+ const nodeTypeDefsMap = React5.useMemo(() => {
840
958
  if (!(nodeTypeDefs == null ? void 0 : nodeTypeDefs.length)) return void 0;
841
959
  return new Map(nodeTypeDefs.map((d) => [d.slug, d]));
842
960
  }, [nodeTypeDefs]);
843
- const edgeTypeDefsMap = React2.useMemo(() => {
961
+ const edgeTypeDefsMap = React5.useMemo(() => {
844
962
  if (!(edgeTypeDefs == null ? void 0 : edgeTypeDefs.length)) return void 0;
845
963
  return new Map(edgeTypeDefs.map((d) => [d.slug, d]));
846
964
  }, [edgeTypeDefs]);
847
- const nodeTypes = React2.useMemo(
848
- () => createNodeTypes(nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrapper, renderNode),
965
+ const nodeTypes = React5.useMemo(
966
+ () => createNodeTypes(
967
+ nodeRenderers,
968
+ nodeTypeDefsMap,
969
+ frameRenderer,
970
+ nodeWrapper,
971
+ renderNode
972
+ ),
849
973
  [nodeRenderers, nodeTypeDefsMap, frameRenderer, nodeWrapper, renderNode]
850
974
  );
851
- const customEdgeTypes = React2.useMemo(
975
+ const customEdgeTypes = React5.useMemo(
852
976
  () => createEdgeTypes(edgeRenderers, edgeTypeDefsMap),
853
977
  [edgeRenderers, edgeTypeDefsMap]
854
978
  );
855
- const mergedCSS = React2.useMemo(() => {
979
+ const mergedCSS = React5.useMemo(() => {
856
980
  if (!(nodeTypeDefs == null ? void 0 : nodeTypeDefs.length)) return "";
857
981
  return nodeTypeDefs.filter((d) => d.customCSS).map((d) => d.customCSS).join("\n");
858
982
  }, [nodeTypeDefs]);
859
- const styledEdges = React2.useMemo(() => {
983
+ const styledEdges = React5.useMemo(() => {
860
984
  var _a2;
861
985
  let edges = applyEdgeStyles((_a2 = data == null ? void 0 : data.edges) != null ? _a2 : [], edgeTypeDefsMap);
862
986
  if (edgeRenderers) {
@@ -870,64 +994,98 @@ function FlowRenderer({
870
994
  }
871
995
  return edges;
872
996
  }, [data == null ? void 0 : data.edges, edgeTypeDefsMap, edgeRenderers]);
997
+ const translateExtent = React5.useMemo(() => {
998
+ if (translateExtentProp) return translateExtentProp;
999
+ const es = clampBounds != null ? clampBounds : bounds;
1000
+ if (!es) return void 0;
1001
+ const ep = clampBounds ? 0 : focusPadding != null ? focusPadding : 0.1;
1002
+ return [
1003
+ [es.x - ep * es.width, es.y - ep * es.height],
1004
+ [es.x + es.width * (1 + ep), es.y + es.height * (1 + ep)]
1005
+ ];
1006
+ }, [translateExtentProp, clampBounds, bounds, focusPadding]);
1007
+ const boundsKey = bounds ? `${bounds.x},${bounds.y},${bounds.width},${bounds.height}` : "";
1008
+ const extentReadyRef = React5.useRef(false);
1009
+ const expandedExtentRef = React5.useRef(void 0);
1010
+ const prevBoundsKeyRef = React5.useRef(boundsKey);
1011
+ if (prevBoundsKeyRef.current !== boundsKey) {
1012
+ prevBoundsKeyRef.current = boundsKey;
1013
+ extentReadyRef.current = false;
1014
+ expandedExtentRef.current = void 0;
1015
+ }
1016
+ const [, rerender] = React5.useReducer((x) => x + 1, 0);
1017
+ const handleInitialFit = React5.useCallback(
1018
+ (expandedExtent) => {
1019
+ extentReadyRef.current = true;
1020
+ expandedExtentRef.current = expandedExtent;
1021
+ rerender();
1022
+ },
1023
+ []
1024
+ );
1025
+ const activeExtent = !bounds || extentReadyRef.current ? (_a = expandedExtentRef.current) != null ? _a : translateExtent : void 0;
873
1026
  if (!data) return null;
874
1027
  const resolvedDefaultViewport = defaultViewportProp != null ? defaultViewportProp : !fitView && data.viewport ? data.viewport : void 0;
875
- const pad = focusPadding != null ? focusPadding : 0.1;
876
- const extentSource = clampBounds != null ? clampBounds : bounds;
877
- const extentPad = clampBounds ? 0 : pad;
878
- const translateExtent = translateExtentProp != null ? translateExtentProp : extentSource ? [
879
- [extentSource.x - extentPad * extentSource.width, extentSource.y - extentPad * extentSource.height],
880
- [
881
- extentSource.x + extentSource.width * (1 + extentPad),
882
- extentSource.y + extentSource.height * (1 + extentPad)
883
- ]
884
- ] : void 0;
885
- return /* @__PURE__ */ React2.createElement(ReactFlowProvider, null, /* @__PURE__ */ React2.createElement(
1028
+ return /* @__PURE__ */ React5.createElement(ReactFlowProvider, null, /* @__PURE__ */ React5.createElement(
886
1029
  "div",
887
1030
  {
888
1031
  className,
889
- style: __spreadValues({ width: "100%", height: "100%", background: "transparent" }, style)
1032
+ style: __spreadValues({
1033
+ width: "100%",
1034
+ height: "100%",
1035
+ background: "transparent"
1036
+ }, style)
890
1037
  },
891
- /* @__PURE__ */ React2.createElement(
1038
+ /* @__PURE__ */ React5.createElement(
892
1039
  ReactFlow,
893
1040
  {
894
- nodes: (_a = data.nodes) != null ? _a : [],
1041
+ nodes: (_b = data.nodes) != null ? _b : [],
895
1042
  edges: styledEdges,
896
1043
  nodeTypes,
897
1044
  edgeTypes: customEdgeTypes,
898
1045
  defaultViewport: resolvedDefaultViewport,
899
1046
  fitView: bounds ? false : fitView,
900
- translateExtent,
1047
+ translateExtent: activeExtent,
901
1048
  onNodeClick,
1049
+ onNodeDoubleClick,
1050
+ onNodeContextMenu,
1051
+ onNodeMouseEnter,
1052
+ onNodeMouseLeave,
902
1053
  onEdgeClick,
903
1054
  onMoveEnd: onViewportChange ? ((_, vp) => {
904
1055
  onViewportChange(vp);
905
1056
  }) : void 0,
906
1057
  nodesDraggable: interactive,
907
1058
  nodesConnectable: false,
908
- elementsSelectable: interactive || !!onNodeClick || !!onEdgeClick,
1059
+ elementsSelectable: interactive || !!onNodeClick || !!onNodeDoubleClick || !!onEdgeClick,
909
1060
  panOnDrag: interactive,
910
1061
  zoomOnScroll: interactive,
911
1062
  zoomOnPinch: interactive,
912
- zoomOnDoubleClick: false
1063
+ zoomOnDoubleClick: false,
1064
+ minZoom: minZoomProp,
1065
+ maxZoom: maxZoomProp,
1066
+ proOptions: { hideAttribution: true }
913
1067
  },
914
- mergedCSS && /* @__PURE__ */ React2.createElement("style", { dangerouslySetInnerHTML: { __html: mergedCSS } }),
915
- background && /* @__PURE__ */ React2.createElement(Background, null),
916
- controls && /* @__PURE__ */ React2.createElement(Controls, null),
917
- minimap && /* @__PURE__ */ React2.createElement(
1068
+ mergedCSS && /* @__PURE__ */ React5.createElement("style", { dangerouslySetInnerHTML: { __html: mergedCSS } }),
1069
+ background && /* @__PURE__ */ React5.createElement(Background, null),
1070
+ controls && /* @__PURE__ */ React5.createElement(Controls, null),
1071
+ minimap && /* @__PURE__ */ React5.createElement(
918
1072
  MiniMap,
919
1073
  {
920
1074
  nodeColor: minimapNodeColor
921
1075
  }
922
1076
  ),
923
- bounds && /* @__PURE__ */ React2.createElement(
1077
+ bounds && /* @__PURE__ */ React5.createElement(
924
1078
  FocusHandler,
925
1079
  {
926
1080
  bounds,
927
1081
  padding: focusPadding != null ? focusPadding : 0.1,
928
1082
  animation: focusAnimation != null ? focusAnimation : true,
929
1083
  mode: focusMode,
930
- responsive: responsiveFit != null ? responsiveFit : true
1084
+ responsive: responsiveFit != null ? responsiveFit : true,
1085
+ extent: translateExtent,
1086
+ clampBounds,
1087
+ minZoomProp,
1088
+ onInitialFit: handleInitialFit
931
1089
  }
932
1090
  ),
933
1091
  children
@@ -945,6 +1103,7 @@ export {
945
1103
  getNodeBounds,
946
1104
  isDynamicNode,
947
1105
  isFrameNode,
1106
+ prefetchFlow,
948
1107
  useFlow,
949
1108
  useFlowData
950
1109
  };