ai-design-system 0.1.114 → 0.1.115

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.
@@ -7,7 +7,7 @@ import {
7
7
  type Node,
8
8
  useReactFlow,
9
9
  } from "@xyflow/react";
10
- import { useCallback, useEffect } from "react";
10
+ import { useCallback, useEffect, useRef } from "react";
11
11
  import { Canvas } from "@/components/ai-elements/canvas";
12
12
  import { Connection as ConnectionLine } from "@/components/ai-elements/connection";
13
13
  import { Controls } from "@/components/ai-elements/controls";
@@ -58,6 +58,36 @@ function WorkflowCanvasInner({
58
58
  className,
59
59
  }: WorkflowCanvasProps) {
60
60
  const { fitView } = useReactFlow();
61
+ const containerRef = useRef<HTMLDivElement>(null);
62
+ const wasZeroSizeRef = useRef<boolean>(true);
63
+
64
+ // Automatically fit view when the canvas container transitions from hidden (0x0)
65
+ // to visible (e.g. mobile Overview icon clicked or tab activated)
66
+ useEffect(() => {
67
+ const el = containerRef.current;
68
+ if (!el || typeof ResizeObserver === "undefined") return;
69
+
70
+ const observer = new ResizeObserver((entries) => {
71
+ for (const entry of entries) {
72
+ const { width, height } = entry.contentRect;
73
+ if (width > 0 && height > 0) {
74
+ if (wasZeroSizeRef.current) {
75
+ wasZeroSizeRef.current = false;
76
+ // Delay slightly so xyflow's internal node measurements can settle
77
+ const timer = setTimeout(() => {
78
+ fitView({ padding: 0.2, duration: 300 });
79
+ }, 60);
80
+ return () => clearTimeout(timer);
81
+ }
82
+ } else {
83
+ wasZeroSizeRef.current = true;
84
+ }
85
+ }
86
+ });
87
+
88
+ observer.observe(el);
89
+ return () => observer.disconnect();
90
+ }, [fitView]);
61
91
 
62
92
  // Opt-in re-fit, e.g. after the device-preview toolbar's Route select
63
93
  // switches to "All" and the node count/extent changes underneath the
@@ -67,20 +97,16 @@ function WorkflowCanvasInner({
67
97
  // every such change would override the user's own pan/zoom constantly.
68
98
  useEffect(() => {
69
99
  if (fitViewSignal === undefined) return;
70
- // A plain synchronous call, and even a double-rAF, both raced xyflow's
71
- // own internal node measurement — verified live in both cases: the
72
- // resulting scale was far smaller than the two device-preview nodes'
73
- // true combined width needed (computed against their real DOM rects:
74
- // ~1.2 would tightly fit them, but fitView produced ~0.57 and one node
75
- // still landed partly outside the pane). xyflow measures each node via
76
- // ResizeObserver, which fires as a separate, unsynchronized callback —
77
- // not guaranteed to have run even two animation frames after the nodes
78
- // prop change lands. A short real-time delay is the standard, if
79
- // inelegant, workaround for this exact class of race.
80
- const timer = setTimeout(() => {
100
+ const timer1 = setTimeout(() => {
81
101
  fitView({ padding: 0.2, duration: 300 });
82
102
  }, 50);
83
- return () => clearTimeout(timer);
103
+ const timer2 = setTimeout(() => {
104
+ fitView({ padding: 0.2, duration: 300 });
105
+ }, 200);
106
+ return () => {
107
+ clearTimeout(timer1);
108
+ clearTimeout(timer2);
109
+ };
84
110
  }, [fitViewSignal, fitView]);
85
111
 
86
112
  useEffect(() => {
@@ -105,6 +131,7 @@ function WorkflowCanvasInner({
105
131
 
106
132
  return (
107
133
  <div
134
+ ref={containerRef}
108
135
  className={className}
109
136
  data-testid="workflow-canvas"
110
137
  style={{
package/dist/index.cjs CHANGED
@@ -16232,12 +16232,42 @@ function WorkflowCanvasInner({
16232
16232
  className
16233
16233
  }) {
16234
16234
  const { fitView } = react.useReactFlow();
16235
+ const containerRef = React3.useRef(null);
16236
+ const wasZeroSizeRef = React3.useRef(true);
16237
+ React3.useEffect(() => {
16238
+ const el = containerRef.current;
16239
+ if (!el || typeof ResizeObserver === "undefined") return;
16240
+ const observer = new ResizeObserver((entries) => {
16241
+ for (const entry of entries) {
16242
+ const { width, height } = entry.contentRect;
16243
+ if (width > 0 && height > 0) {
16244
+ if (wasZeroSizeRef.current) {
16245
+ wasZeroSizeRef.current = false;
16246
+ const timer = setTimeout(() => {
16247
+ fitView({ padding: 0.2, duration: 300 });
16248
+ }, 60);
16249
+ return () => clearTimeout(timer);
16250
+ }
16251
+ } else {
16252
+ wasZeroSizeRef.current = true;
16253
+ }
16254
+ }
16255
+ });
16256
+ observer.observe(el);
16257
+ return () => observer.disconnect();
16258
+ }, [fitView]);
16235
16259
  React3.useEffect(() => {
16236
16260
  if (fitViewSignal === void 0) return;
16237
- const timer = setTimeout(() => {
16261
+ const timer1 = setTimeout(() => {
16238
16262
  fitView({ padding: 0.2, duration: 300 });
16239
16263
  }, 50);
16240
- return () => clearTimeout(timer);
16264
+ const timer2 = setTimeout(() => {
16265
+ fitView({ padding: 0.2, duration: 300 });
16266
+ }, 200);
16267
+ return () => {
16268
+ clearTimeout(timer1);
16269
+ clearTimeout(timer2);
16270
+ };
16241
16271
  }, [fitViewSignal, fitView]);
16242
16272
  React3.useEffect(() => {
16243
16273
  const handleKeyDown = (event) => {
@@ -16260,6 +16290,7 @@ function WorkflowCanvasInner({
16260
16290
  return /* @__PURE__ */ jsxRuntime.jsx(
16261
16291
  "div",
16262
16292
  {
16293
+ ref: containerRef,
16263
16294
  className,
16264
16295
  "data-testid": "workflow-canvas",
16265
16296
  style: {