@uipath/apollo-react 3.45.3 → 3.45.5

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.
@@ -28,6 +28,7 @@ __webpack_require__.d(__webpack_exports__, {
28
28
  });
29
29
  const jsx_runtime_namespaceObject = require("react/jsx-runtime");
30
30
  const index_cjs_namespaceObject = require("../../../icons/index.cjs");
31
+ const external_utils_index_cjs_namespaceObject = require("../../../utils/index.cjs");
31
32
  const external_layouts_index_cjs_namespaceObject = require("../../../layouts/index.cjs");
32
33
  const react_cjs_namespaceObject = require("../../../xyflow/react.cjs");
33
34
  const components_index_cjs_namespaceObject = require("../../../../material/components/index.cjs");
@@ -177,19 +178,25 @@ const ResourceNode = /*#__PURE__*/ (0, external_react_namespaceObject.memo)(({ d
177
178
  data
178
179
  ]);
179
180
  const toolbarConfig = (0, external_react_namespaceObject.useMemo)(()=>{
180
- if ('view' === mode) return null;
181
+ if ('view' === mode || data.isPlaceholder && (!isSuggestion || !suggestionId)) return null;
181
182
  if (isSuggestion && suggestionId) {
182
183
  if ('0.0.1' === suggestionGroupVersion) return null;
183
184
  const rejectAction = {
184
185
  id: 'reject-suggestion',
185
- icon: 'close',
186
+ icon: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_utils_index_cjs_namespaceObject.NodeIcon, {
187
+ icon: "X",
188
+ size: 14
189
+ }),
186
190
  label: suggestTranslations.reject,
187
191
  disabled: false,
188
192
  onAction: ()=>handleActOnSuggestion(suggestionId, 'reject')
189
193
  };
190
194
  const acceptAction = {
191
195
  id: 'accept-suggestion',
192
- icon: 'check',
196
+ icon: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_utils_index_cjs_namespaceObject.NodeIcon, {
197
+ icon: "check",
198
+ size: 14
199
+ }),
193
200
  label: suggestTranslations.accept,
194
201
  disabled: false,
195
202
  onAction: ()=>handleActOnSuggestion(suggestionId, 'accept')
@@ -228,7 +235,10 @@ const ResourceNode = /*#__PURE__*/ (0, external_react_namespaceObject.memo)(({ d
228
235
  };
229
236
  const removeAction = {
230
237
  id: 'remove',
231
- icon: 'delete',
238
+ icon: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_utils_index_cjs_namespaceObject.NodeIcon, {
239
+ icon: "trash",
240
+ size: 14
241
+ }),
232
242
  label: translations?.remove ?? '',
233
243
  disabled: false,
234
244
  onAction: handleClickRemove
@@ -1 +1 @@
1
- {"version":3,"file":"ResourceNode.d.ts","sourceRoot":"","sources":["../../../../../src/canvas/components/AgentCanvas/nodes/ResourceNode.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAY,MAAM,0CAA0C,CAAC;AAGpF,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,yBAAyB,EAE9B,KAAK,wBAAwB,EAC7B,KAAK,sBAAsB,EAC5B,MAAM,gBAAgB,CAAC;AAexB,UAAU,iBAAkB,SAAQ,SAAS,CAAC,qBAAqB,CAAC;IAClE,IAAI,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,yBAAyB,KAAK,IAAI,CAAC;IAC7E,SAAS,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,yBAAyB,KAAK,IAAI,CAAC;IAC9E,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,yBAAyB,KAAK,IAAI,CAAC;IACpF,kBAAkB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,yBAAyB,KAAK,IAAI,CAAC;IACvF,cAAc,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,yBAAyB,KAAK,IAAI,CAAC;IACnF,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,yBAAyB,KAAK,IAAI,CAAC;IACjF,aAAa,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,yBAAyB,KAAK,IAAI,CAAC;IAClF,gBAAgB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,yBAAyB,KAAK,IAAI,CAAC;IACrF,kBAAkB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,yBAAyB,KAAK,IAAI,CAAC;IACvF,gBAAgB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,YAAY,CAAC,EAAE,wBAAwB,CAAC;IACxC,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IAEhD,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,eAAO,MAAM,YAAY,0RAoBpB,iBAAiB,6CAqcrB,CAAC"}
1
+ {"version":3,"file":"ResourceNode.d.ts","sourceRoot":"","sources":["../../../../../src/canvas/components/AgentCanvas/nodes/ResourceNode.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,SAAS,EAAY,MAAM,0CAA0C,CAAC;AAGpF,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,yBAAyB,EAE9B,KAAK,wBAAwB,EAC7B,KAAK,sBAAsB,EAC5B,MAAM,gBAAgB,CAAC;AAexB,UAAU,iBAAkB,SAAQ,SAAS,CAAC,qBAAqB,CAAC;IAClE,IAAI,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,yBAAyB,KAAK,IAAI,CAAC;IAC7E,SAAS,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,yBAAyB,KAAK,IAAI,CAAC;IAC9E,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,yBAAyB,KAAK,IAAI,CAAC;IACpF,kBAAkB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,yBAAyB,KAAK,IAAI,CAAC;IACvF,cAAc,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,yBAAyB,KAAK,IAAI,CAAC;IACnF,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,yBAAyB,KAAK,IAAI,CAAC;IACjF,aAAa,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,yBAAyB,KAAK,IAAI,CAAC;IAClF,gBAAgB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,yBAAyB,KAAK,IAAI,CAAC;IACrF,kBAAkB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,yBAAyB,KAAK,IAAI,CAAC;IACvF,gBAAgB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,YAAY,CAAC,EAAE,wBAAwB,CAAC;IACxC,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IAEhD,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,eAAO,MAAM,YAAY,0RAoBpB,iBAAiB,6CAocrB,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { McpIcon, MemoryIcon } from "../../../icons/index.js";
3
+ import { NodeIcon } from "../../../utils/index.js";
3
4
  import { Row } from "../../../layouts/index.js";
4
5
  import { Position } from "../../../xyflow/react.js";
5
6
  import { ApIcon } from "../../../../material/components/index.js";
@@ -149,19 +150,25 @@ const ResourceNode = /*#__PURE__*/ memo(({ data, selected, id, mode, hasError =
149
150
  data
150
151
  ]);
151
152
  const toolbarConfig = useMemo(()=>{
152
- if ('view' === mode) return null;
153
+ if ('view' === mode || data.isPlaceholder && (!isSuggestion || !suggestionId)) return null;
153
154
  if (isSuggestion && suggestionId) {
154
155
  if ('0.0.1' === suggestionGroupVersion) return null;
155
156
  const rejectAction = {
156
157
  id: 'reject-suggestion',
157
- icon: 'close',
158
+ icon: /*#__PURE__*/ jsx(NodeIcon, {
159
+ icon: "X",
160
+ size: 14
161
+ }),
158
162
  label: suggestTranslations.reject,
159
163
  disabled: false,
160
164
  onAction: ()=>handleActOnSuggestion(suggestionId, 'reject')
161
165
  };
162
166
  const acceptAction = {
163
167
  id: 'accept-suggestion',
164
- icon: 'check',
168
+ icon: /*#__PURE__*/ jsx(NodeIcon, {
169
+ icon: "check",
170
+ size: 14
171
+ }),
165
172
  label: suggestTranslations.accept,
166
173
  disabled: false,
167
174
  onAction: ()=>handleActOnSuggestion(suggestionId, 'accept')
@@ -200,7 +207,10 @@ const ResourceNode = /*#__PURE__*/ memo(({ data, selected, id, mode, hasError =
200
207
  };
201
208
  const removeAction = {
202
209
  id: 'remove',
203
- icon: 'delete',
210
+ icon: /*#__PURE__*/ jsx(NodeIcon, {
211
+ icon: "trash",
212
+ size: 14
213
+ }),
204
214
  label: translations?.remove ?? '',
205
215
  disabled: false,
206
216
  onAction: handleClickRemove
@@ -33,15 +33,24 @@ var __webpack_require__ = {};
33
33
  var __webpack_exports__ = {};
34
34
  __webpack_require__.r(__webpack_exports__);
35
35
  __webpack_require__.d(__webpack_exports__, {
36
+ computeSankeyDimensions: ()=>computeSankeyDimensions,
36
37
  ApSankeyDiagram: ()=>ApSankeyDiagram
37
38
  });
38
39
  const jsx_runtime_namespaceObject = require("react/jsx-runtime");
40
+ const Add_namespaceObject = require("@mui/icons-material/Add");
41
+ var Add_default = /*#__PURE__*/ __webpack_require__.n(Add_namespaceObject);
42
+ const FitScreen_namespaceObject = require("@mui/icons-material/FitScreen");
43
+ var FitScreen_default = /*#__PURE__*/ __webpack_require__.n(FitScreen_namespaceObject);
44
+ const Remove_namespaceObject = require("@mui/icons-material/Remove");
45
+ var Remove_default = /*#__PURE__*/ __webpack_require__.n(Remove_namespaceObject);
39
46
  const material_namespaceObject = require("@mui/material");
40
47
  const styles_namespaceObject = require("@mui/material/styles");
41
48
  const apollo_core_namespaceObject = require("@uipath/apollo-core");
42
49
  var apollo_core_default = /*#__PURE__*/ __webpack_require__.n(apollo_core_namespaceObject);
43
50
  const external_d3_sankey_namespaceObject = require("d3-sankey");
44
51
  const external_d3_scale_chromatic_namespaceObject = require("d3-scale-chromatic");
52
+ const external_d3_selection_namespaceObject = require("d3-selection");
53
+ const external_d3_zoom_namespaceObject = require("d3-zoom");
45
54
  const external_react_namespaceObject = require("react");
46
55
  var external_react_default = /*#__PURE__*/ __webpack_require__.n(external_react_namespaceObject);
47
56
  const SankeyContainer = (0, styles_namespaceObject.styled)('div')({
@@ -51,9 +60,7 @@ const SankeyContainer = (0, styles_namespaceObject.styled)('div')({
51
60
  width: '100%',
52
61
  height: '600px',
53
62
  '& svg': {
54
- display: 'block',
55
- width: '100%',
56
- height: '100%'
63
+ display: 'block'
57
64
  }
58
65
  });
59
66
  const StyledSankeyNode = (0, styles_namespaceObject.styled)('rect')({
@@ -121,9 +128,66 @@ const TooltipValue = (0, styles_namespaceObject.styled)('span')({
121
128
  color: 'var(--color-foreground-emp)',
122
129
  fontWeight: apollo_core_default().FontFamily.FontWeightSemibold
123
130
  });
131
+ const ZoomControls = (0, styles_namespaceObject.styled)('div')({
132
+ position: 'absolute',
133
+ bottom: apollo_core_default().Spacing.SpacingM,
134
+ right: apollo_core_default().Spacing.SpacingM,
135
+ display: 'flex',
136
+ gap: '1px',
137
+ backgroundColor: apollo_core_default().Colors.ColorGray100,
138
+ borderRadius: apollo_core_default().Border.BorderRadiusM,
139
+ overflow: 'hidden',
140
+ boxShadow: apollo_core_default().Shadow.ShadowDp2
141
+ });
124
142
  const DIAGRAM_MARGIN_LEFT = 5;
125
143
  const DIAGRAM_MARGIN_RIGHT = 120;
126
- const DIAGRAM_MARGIN_VERTICAL = 5;
144
+ const DIAGRAM_MARGIN_TOP = 5;
145
+ const DIAGRAM_MARGIN_BOTTOM = 40;
146
+ const ZOOM_SCALE_EXTENT_MIN = 0.5;
147
+ const ZOOM_SCALE_EXTENT_MAX = 2;
148
+ function computeSankeyDimensions(data, nodePadding, minNodeHeight, minColumnWidth, marginLeft, marginRight, marginTop, marginBottom) {
149
+ if (!data || 0 === data.nodes.length) return {
150
+ minWidth: 0,
151
+ minHeight: 0
152
+ };
153
+ const outgoing = new Map();
154
+ const incoming = new Map();
155
+ for (const node of data.nodes){
156
+ outgoing.set(node.id, []);
157
+ incoming.set(node.id, []);
158
+ }
159
+ for (const link of data.links){
160
+ outgoing.get(link.source)?.push(link.target);
161
+ incoming.get(link.target)?.push(link.source);
162
+ }
163
+ const depth = new Map();
164
+ const sources = data.nodes.filter((n)=>(incoming.get(n.id)?.length ?? 0) === 0);
165
+ const queue = [];
166
+ for (const s of sources){
167
+ depth.set(s.id, 0);
168
+ queue.push(s.id);
169
+ }
170
+ const maxIterations = data.nodes.length * data.links.length + data.nodes.length;
171
+ let iterations = 0;
172
+ while(queue.length > 0 && iterations++ < maxIterations){
173
+ const id = queue.shift();
174
+ const d = depth.get(id);
175
+ for (const target of outgoing.get(id) ?? [])if (!depth.has(target) || depth.get(target) < d + 1) {
176
+ depth.set(target, d + 1);
177
+ queue.push(target);
178
+ }
179
+ }
180
+ const columnCounts = new Map();
181
+ for (const d of depth.values())columnCounts.set(d, (columnCounts.get(d) ?? 0) + 1);
182
+ const columnCount = columnCounts.size;
183
+ const maxNodesPerColumn = Math.max(0, ...columnCounts.values());
184
+ const minWidth = columnCount * minColumnWidth + marginLeft + marginRight;
185
+ const minHeight = maxNodesPerColumn * (minNodeHeight + nodePadding) - nodePadding + marginTop + marginBottom;
186
+ return {
187
+ minWidth,
188
+ minHeight
189
+ };
190
+ }
127
191
  const truncateToFit = (text, maxWidth, ctx)=>{
128
192
  if (maxWidth <= 0) return '';
129
193
  if (ctx.measureText(text).width <= maxWidth) return text;
@@ -138,9 +202,13 @@ const truncateToFit = (text, maxWidth, ctx)=>{
138
202
  return 0 === lo ? ellipsis : text.substring(0, lo) + ellipsis;
139
203
  };
140
204
  const ApSankeyDiagram = /*#__PURE__*/ external_react_default().forwardRef((props, ref)=>{
141
- const { data, nodeAlignment = 'justify', nodePadding = 16, nodeWidth = 24, style, className, colorScheme = external_d3_scale_chromatic_namespaceObject.schemeTableau10, ariaLabel = 'Sankey diagram', onLinkClick, onNodeClick } = props;
205
+ const { data, nodeAlignment = 'justify', nodePadding = 16, nodeWidth = 24, style, className, colorScheme = external_d3_scale_chromatic_namespaceObject.schemeTableau10, ariaLabel = 'Sankey diagram', onLinkClick, onNodeClick, minNodeHeight = 36, minColumnWidth = 140 } = props;
142
206
  const containerRef = (0, external_react_namespaceObject.useRef)(null);
143
207
  const svgRef = (0, external_react_namespaceObject.useRef)(null);
208
+ const zoomGroupRef = (0, external_react_namespaceObject.useRef)(null);
209
+ const zoomBehaviorRef = (0, external_react_namespaceObject.useRef)(null);
210
+ const zoomTransformRef = (0, external_react_namespaceObject.useRef)(external_d3_zoom_namespaceObject.zoomIdentity);
211
+ const hasUserZoomedRef = (0, external_react_namespaceObject.useRef)(false);
144
212
  const [containerWidth, setContainerWidth] = (0, external_react_namespaceObject.useState)(0);
145
213
  const [containerHeight, setContainerHeight] = (0, external_react_namespaceObject.useState)(0);
146
214
  const [selectedLinkIndex, setSelectedLinkIndex] = (0, external_react_namespaceObject.useState)(null);
@@ -162,6 +230,20 @@ const ApSankeyDiagram = /*#__PURE__*/ external_react_default().forwardRef((props
162
230
  }, []);
163
231
  const actualWidth = containerWidth || 1200;
164
232
  const actualHeight = containerHeight || 600;
233
+ const { minWidth, minHeight } = (0, external_react_namespaceObject.useMemo)(()=>{
234
+ if (!data || 0 === data.nodes.length) return {
235
+ minWidth: 0,
236
+ minHeight: 0
237
+ };
238
+ return computeSankeyDimensions(data, nodePadding, minNodeHeight, minColumnWidth, DIAGRAM_MARGIN_LEFT, DIAGRAM_MARGIN_RIGHT, DIAGRAM_MARGIN_TOP, DIAGRAM_MARGIN_BOTTOM);
239
+ }, [
240
+ data,
241
+ nodePadding,
242
+ minNodeHeight,
243
+ minColumnWidth
244
+ ]);
245
+ const svgWidth = Math.max(actualWidth, minWidth);
246
+ const svgHeight = Math.max(actualHeight, minHeight);
165
247
  const nodeColorMap = (0, external_react_namespaceObject.useMemo)(()=>{
166
248
  const map = new Map();
167
249
  if (!data) return map;
@@ -180,11 +262,11 @@ const ApSankeyDiagram = /*#__PURE__*/ external_react_default().forwardRef((props
180
262
  const sankeyGenerator = (0, external_d3_sankey_namespaceObject.sankey)().nodeId((d)=>d.id).nodeAlign(alignmentFn).nodeWidth(nodeWidth).nodePadding(nodePadding).extent([
181
263
  [
182
264
  DIAGRAM_MARGIN_LEFT,
183
- DIAGRAM_MARGIN_VERTICAL
265
+ DIAGRAM_MARGIN_TOP
184
266
  ],
185
267
  [
186
- actualWidth - DIAGRAM_MARGIN_RIGHT,
187
- actualHeight - DIAGRAM_MARGIN_VERTICAL
268
+ svgWidth - DIAGRAM_MARGIN_RIGHT,
269
+ svgHeight - DIAGRAM_MARGIN_BOTTOM
188
270
  ]
189
271
  ]);
190
272
  const graph = {
@@ -201,8 +283,8 @@ const ApSankeyDiagram = /*#__PURE__*/ external_react_default().forwardRef((props
201
283
  nodeAlignment,
202
284
  nodeWidth,
203
285
  nodePadding,
204
- actualWidth,
205
- actualHeight
286
+ svgWidth,
287
+ svgHeight
206
288
  ]);
207
289
  const linkPaths = (0, external_react_namespaceObject.useMemo)(()=>{
208
290
  if (!sankeyGraph) return [];
@@ -237,6 +319,8 @@ const ApSankeyDiagram = /*#__PURE__*/ external_react_default().forwardRef((props
237
319
  targetId: targetNode.id,
238
320
  sourceLabel: sourceNode.label,
239
321
  targetLabel: targetNode.label,
322
+ sourceX,
323
+ targetX,
240
324
  centerX,
241
325
  centerY
242
326
  };
@@ -291,18 +375,84 @@ const ApSankeyDiagram = /*#__PURE__*/ external_react_default().forwardRef((props
291
375
  nodeColorMap,
292
376
  colorScheme
293
377
  ]);
378
+ const fitToView = (0, external_react_namespaceObject.useCallback)(()=>{
379
+ const svg = svgRef.current;
380
+ const zoomBehavior = zoomBehaviorRef.current;
381
+ if (!svg || !zoomBehavior || 0 === svgWidth || 0 === svgHeight) return;
382
+ hasUserZoomedRef.current = false;
383
+ const cw = containerWidth || 1;
384
+ const ch = containerHeight || 1;
385
+ const scale = 0.95 * Math.min(cw / svgWidth, ch / svgHeight);
386
+ const tx = (cw - svgWidth * scale) / 2;
387
+ const ty = (ch - svgHeight * scale) / 2;
388
+ const t = external_d3_zoom_namespaceObject.zoomIdentity.translate(tx, ty).scale(scale);
389
+ (0, external_d3_selection_namespaceObject.select)(svg).transition().duration(300).call(zoomBehavior.transform, t);
390
+ }, [
391
+ containerWidth,
392
+ containerHeight,
393
+ svgWidth,
394
+ svgHeight
395
+ ]);
396
+ (0, external_react_namespaceObject.useEffect)(()=>{
397
+ const svg = svgRef.current;
398
+ const zoomGroup = zoomGroupRef.current;
399
+ if (!svg || !zoomGroup) return;
400
+ const zoomBehavior = (0, external_d3_zoom_namespaceObject.zoom)().scaleExtent([
401
+ ZOOM_SCALE_EXTENT_MIN,
402
+ ZOOM_SCALE_EXTENT_MAX
403
+ ]).filter((event)=>{
404
+ if ('wheel' === event.type) return event.ctrlKey || event.metaKey;
405
+ return !event.button;
406
+ }).on('zoom', (event)=>{
407
+ const t = event.transform;
408
+ zoomTransformRef.current = t;
409
+ zoomGroup.setAttribute('transform', `translate(${t.x},${t.y}) scale(${t.k})`);
410
+ if (event.sourceEvent) hasUserZoomedRef.current = true;
411
+ });
412
+ zoomBehaviorRef.current = zoomBehavior;
413
+ (0, external_d3_selection_namespaceObject.select)(svg).call(zoomBehavior);
414
+ return ()=>{
415
+ (0, external_d3_selection_namespaceObject.select)(svg).on('.zoom', null);
416
+ zoomBehaviorRef.current = null;
417
+ };
418
+ }, []);
419
+ (0, external_react_namespaceObject.useEffect)(()=>{
420
+ if (!sankeyGraph || 0 === containerWidth || 0 === containerHeight) return;
421
+ if (hasUserZoomedRef.current) return;
422
+ fitToView();
423
+ }, [
424
+ sankeyGraph,
425
+ containerWidth,
426
+ containerHeight,
427
+ fitToView
428
+ ]);
429
+ const handleZoomIn = (0, external_react_namespaceObject.useCallback)(()=>{
430
+ const svg = svgRef.current;
431
+ const zoomBehavior = zoomBehaviorRef.current;
432
+ if (!svg || !zoomBehavior) return;
433
+ (0, external_d3_selection_namespaceObject.select)(svg).transition().duration(200).call(zoomBehavior.scaleBy, 1.3);
434
+ }, []);
435
+ const handleZoomOut = (0, external_react_namespaceObject.useCallback)(()=>{
436
+ const svg = svgRef.current;
437
+ const zoomBehavior = zoomBehaviorRef.current;
438
+ if (!svg || !zoomBehavior) return;
439
+ (0, external_d3_selection_namespaceObject.select)(svg).transition().duration(200).call(zoomBehavior.scaleBy, 1 / 1.3);
440
+ }, []);
294
441
  const handleLinkMouseEnter = (0, external_react_namespaceObject.useCallback)((index, centerX, centerY)=>{
295
442
  if (selectedLinkIndex === index) return;
296
443
  const svgRect = svgRef.current?.getBoundingClientRect();
297
444
  if (svgRect) {
445
+ const t = zoomTransformRef.current;
446
+ const screenX = svgRect.left + centerX * t.k + t.x;
447
+ const screenY = svgRect.top + centerY * t.k + t.y;
298
448
  const virtualAnchor = {
299
449
  getBoundingClientRect: ()=>({
300
- x: svgRect.left + centerX,
301
- y: svgRect.top + centerY,
302
- left: svgRect.left + centerX,
303
- top: svgRect.top + centerY,
304
- right: svgRect.left + centerX,
305
- bottom: svgRect.top + centerY,
450
+ x: screenX,
451
+ y: screenY,
452
+ left: screenX,
453
+ top: screenY,
454
+ right: screenX,
455
+ bottom: screenY,
306
456
  width: 0,
307
457
  height: 0,
308
458
  toJSON: ()=>{}
@@ -339,90 +489,121 @@ const ApSankeyDiagram = /*#__PURE__*/ external_react_default().forwardRef((props
339
489
  ref: containerRef,
340
490
  style: style,
341
491
  className: className,
342
- role: "img",
492
+ role: "figure",
343
493
  "aria-label": ariaLabel,
344
494
  children: [
345
- /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("svg", {
495
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("svg", {
346
496
  ref: svgRef,
347
- width: actualWidth,
348
- height: actualHeight,
497
+ width: svgWidth,
498
+ height: svgHeight,
499
+ children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("g", {
500
+ ref: zoomGroupRef,
501
+ children: [
502
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("defs", {
503
+ children: linkPaths.map((linkData)=>/*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("linearGradient", {
504
+ id: linkData.gradientId,
505
+ gradientUnits: "userSpaceOnUse",
506
+ x1: linkData.sourceX,
507
+ y1: 0,
508
+ x2: linkData.targetX,
509
+ y2: 0,
510
+ children: [
511
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("stop", {
512
+ offset: "0%",
513
+ stopColor: linkData.sourceColor
514
+ }),
515
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("stop", {
516
+ offset: "100%",
517
+ stopColor: linkData.targetColor
518
+ })
519
+ ]
520
+ }, linkData.gradientId))
521
+ }),
522
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("g", {
523
+ children: linkPaths.map((linkData, index)=>{
524
+ const isConnectedToHoveredNode = connectedLinkIndices.has(index);
525
+ let opacity = 0.6;
526
+ if (selectedLinkIndex === index) opacity = 1;
527
+ else if (hoveredNodeId) opacity = isConnectedToHoveredNode ? 1 : 0.3;
528
+ return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(StyledSankeyLink, {
529
+ d: linkData.path,
530
+ stroke: linkData.color ? linkData.color : `url(#${linkData.gradientId})`,
531
+ strokeWidth: linkData.strokeWidth,
532
+ fill: "none",
533
+ opacity: opacity,
534
+ onMouseEnter: ()=>handleLinkMouseEnter(index, linkData.centerX, linkData.centerY),
535
+ onMouseLeave: handleLinkMouseLeave,
536
+ onClick: (e)=>onLinkClick?.(linkData.originalLink, e),
537
+ "aria-label": `Link from ${linkData.sourceLabel} to ${linkData.targetLabel}`
538
+ }, `link-${index}`);
539
+ })
540
+ }),
541
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("g", {
542
+ children: nodeData.map((nodeItem)=>{
543
+ const nodeOpacity = hoveredNodeId ? hoveredNodeId === nodeItem.node.id ? 1 : 0.4 : 1;
544
+ return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("g", {
545
+ children: [
546
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(StyledSankeyNode, {
547
+ x: nodeItem.x,
548
+ y: nodeItem.y,
549
+ width: nodeItem.width,
550
+ height: nodeItem.height,
551
+ fill: nodeItem.color,
552
+ opacity: nodeOpacity,
553
+ onMouseEnter: ()=>handleNodeMouseEnter(nodeItem.node.id),
554
+ onMouseLeave: handleNodeMouseLeave,
555
+ onClick: (e)=>onNodeClick?.(nodeItem.node, e)
556
+ }),
557
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)(StyledSankeyNodeLabel, {
558
+ x: nodeItem.labelX,
559
+ y: nodeItem.labelY,
560
+ dy: "0.35em",
561
+ textAnchor: nodeItem.labelAnchor,
562
+ children: [
563
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("title", {
564
+ children: nodeItem.fullLabel
565
+ }),
566
+ nodeItem.displayLabel
567
+ ]
568
+ }),
569
+ nodeItem.value > 0 && /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(StyledSankeyNodeValue, {
570
+ x: nodeItem.labelX,
571
+ y: nodeItem.labelY + parseInt(apollo_core_default().Spacing.SpacingM, 10),
572
+ dy: "0.35em",
573
+ textAnchor: nodeItem.labelAnchor,
574
+ children: nodeItem.value
575
+ })
576
+ ]
577
+ }, `node-${nodeItem.node.id}`);
578
+ })
579
+ })
580
+ ]
581
+ })
582
+ }),
583
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)(ZoomControls, {
349
584
  children: [
350
- /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("defs", {
351
- children: linkPaths.map((linkData)=>/*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("linearGradient", {
352
- id: linkData.gradientId,
353
- gradientUnits: "userSpaceOnUse",
354
- x1: "0%",
355
- y1: "0%",
356
- x2: "100%",
357
- y2: "0%",
358
- children: [
359
- /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("stop", {
360
- offset: "0%",
361
- stopColor: linkData.sourceColor
362
- }),
363
- /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("stop", {
364
- offset: "100%",
365
- stopColor: linkData.targetColor
366
- })
367
- ]
368
- }, linkData.gradientId))
585
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(material_namespaceObject.IconButton, {
586
+ size: "small",
587
+ onClick: handleZoomIn,
588
+ "aria-label": "Zoom in",
589
+ children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(Add_default(), {
590
+ fontSize: "small"
591
+ })
369
592
  }),
370
- /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("g", {
371
- children: linkPaths.map((linkData, index)=>{
372
- const isConnectedToHoveredNode = connectedLinkIndices.has(index);
373
- let opacity = 0.6;
374
- if (selectedLinkIndex === index) opacity = 1;
375
- else if (hoveredNodeId) opacity = isConnectedToHoveredNode ? 1 : 0.3;
376
- return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(StyledSankeyLink, {
377
- d: linkData.path,
378
- stroke: linkData.color ? linkData.color : `url(#${linkData.gradientId})`,
379
- strokeWidth: linkData.strokeWidth,
380
- fill: "none",
381
- opacity: opacity,
382
- onMouseEnter: ()=>handleLinkMouseEnter(index, linkData.centerX, linkData.centerY),
383
- onMouseLeave: handleLinkMouseLeave,
384
- onClick: (e)=>onLinkClick?.(linkData.originalLink, e),
385
- "aria-label": `Link from ${linkData.sourceLabel} to ${linkData.targetLabel}`
386
- }, `link-${index}`);
593
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(material_namespaceObject.IconButton, {
594
+ size: "small",
595
+ onClick: handleZoomOut,
596
+ "aria-label": "Zoom out",
597
+ children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(Remove_default(), {
598
+ fontSize: "small"
387
599
  })
388
600
  }),
389
- /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("g", {
390
- children: nodeData.map((nodeItem)=>{
391
- const nodeOpacity = hoveredNodeId ? hoveredNodeId === nodeItem.node.id ? 1 : 0.4 : 1;
392
- return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)("g", {
393
- children: [
394
- /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(StyledSankeyNode, {
395
- x: nodeItem.x,
396
- y: nodeItem.y,
397
- width: nodeItem.width,
398
- height: nodeItem.height,
399
- fill: nodeItem.color,
400
- opacity: nodeOpacity,
401
- onMouseEnter: ()=>handleNodeMouseEnter(nodeItem.node.id),
402
- onMouseLeave: handleNodeMouseLeave,
403
- onClick: (e)=>onNodeClick?.(nodeItem.node, e)
404
- }),
405
- /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)(StyledSankeyNodeLabel, {
406
- x: nodeItem.labelX,
407
- y: nodeItem.labelY,
408
- dy: "0.35em",
409
- textAnchor: nodeItem.labelAnchor,
410
- children: [
411
- /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("title", {
412
- children: nodeItem.fullLabel
413
- }),
414
- nodeItem.displayLabel
415
- ]
416
- }),
417
- nodeItem.value > 0 && /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(StyledSankeyNodeValue, {
418
- x: nodeItem.labelX,
419
- y: nodeItem.labelY + parseInt(apollo_core_default().Spacing.SpacingM, 10),
420
- dy: "0.35em",
421
- textAnchor: nodeItem.labelAnchor,
422
- children: nodeItem.value
423
- })
424
- ]
425
- }, `node-${nodeItem.node.id}`);
601
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(material_namespaceObject.IconButton, {
602
+ size: "small",
603
+ onClick: fitToView,
604
+ "aria-label": "Fit to view",
605
+ children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(FitScreen_default(), {
606
+ fontSize: "small"
426
607
  })
427
608
  })
428
609
  ]
@@ -512,8 +693,10 @@ const ApSankeyDiagram = /*#__PURE__*/ external_react_default().forwardRef((props
512
693
  });
513
694
  ApSankeyDiagram.displayName = 'ApSankeyDiagram';
514
695
  exports.ApSankeyDiagram = __webpack_exports__.ApSankeyDiagram;
696
+ exports.computeSankeyDimensions = __webpack_exports__.computeSankeyDimensions;
515
697
  for(var __webpack_i__ in __webpack_exports__)if (-1 === [
516
- "ApSankeyDiagram"
698
+ "ApSankeyDiagram",
699
+ "computeSankeyDimensions"
517
700
  ].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
518
701
  Object.defineProperty(exports, '__esModule', {
519
702
  value: true
@@ -1,4 +1,8 @@
1
1
  import React from 'react';
2
- import type { ApSankeyDiagramProps } from './ApSankeyDiagram.types';
2
+ import type { ApSankeyDiagramProps, SankeyData } from './ApSankeyDiagram.types';
3
+ export declare function computeSankeyDimensions(data: SankeyData, nodePadding: number, minNodeHeight: number, minColumnWidth: number, marginLeft: number, marginRight: number, marginTop: number, marginBottom: number): {
4
+ minWidth: number;
5
+ minHeight: number;
6
+ };
3
7
  export declare const ApSankeyDiagram: React.ForwardRefExoticComponent<ApSankeyDiagramProps & React.RefAttributes<HTMLDivElement>>;
4
8
  //# sourceMappingURL=ApSankeyDiagram.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ApSankeyDiagram.d.ts","sourceRoot":"","sources":["../../../../src/material/components/ap-sankey-diagram/ApSankeyDiagram.tsx"],"names":[],"mappings":"AAeA,OAAO,KAON,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,oBAAoB,EAA0B,MAAM,yBAAyB,CAAC;AA4J5F,eAAO,MAAM,eAAe,6FAsb3B,CAAC"}
1
+ {"version":3,"file":"ApSankeyDiagram.d.ts","sourceRoot":"","sources":["../../../../src/material/components/ap-sankey-diagram/ApSankeyDiagram.tsx"],"names":[],"mappings":"AAoBA,OAAO,KAON,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EACV,oBAAoB,EACpB,UAAU,EAGX,MAAM,yBAAyB,CAAC;AAyJjC,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,UAAU,EAChB,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GACnB;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAmDzC;AAuBD,eAAO,MAAM,eAAe,6FAqjB3B,CAAC"}
@@ -1,9 +1,14 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
- import { Paper, Popper } from "@mui/material";
2
+ import Add from "@mui/icons-material/Add";
3
+ import FitScreen from "@mui/icons-material/FitScreen";
4
+ import Remove from "@mui/icons-material/Remove";
5
+ import { IconButton, Paper, Popper } from "@mui/material";
3
6
  import { styled } from "@mui/material/styles";
4
7
  import apollo_core from "@uipath/apollo-core";
5
8
  import { sankey, sankeyCenter, sankeyJustify, sankeyLeft, sankeyLinkHorizontal, sankeyRight } from "d3-sankey";
6
9
  import { schemeTableau10 } from "d3-scale-chromatic";
10
+ import { select as external_d3_selection_select } from "d3-selection";
11
+ import { zoom, zoomIdentity } from "d3-zoom";
7
12
  import react, { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
8
13
  const SankeyContainer = styled('div')({
9
14
  position: 'relative',
@@ -12,9 +17,7 @@ const SankeyContainer = styled('div')({
12
17
  width: '100%',
13
18
  height: '600px',
14
19
  '& svg': {
15
- display: 'block',
16
- width: '100%',
17
- height: '100%'
20
+ display: 'block'
18
21
  }
19
22
  });
20
23
  const StyledSankeyNode = styled('rect')({
@@ -82,9 +85,66 @@ const TooltipValue = styled('span')({
82
85
  color: 'var(--color-foreground-emp)',
83
86
  fontWeight: apollo_core.FontFamily.FontWeightSemibold
84
87
  });
88
+ const ZoomControls = styled('div')({
89
+ position: 'absolute',
90
+ bottom: apollo_core.Spacing.SpacingM,
91
+ right: apollo_core.Spacing.SpacingM,
92
+ display: 'flex',
93
+ gap: '1px',
94
+ backgroundColor: apollo_core.Colors.ColorGray100,
95
+ borderRadius: apollo_core.Border.BorderRadiusM,
96
+ overflow: 'hidden',
97
+ boxShadow: apollo_core.Shadow.ShadowDp2
98
+ });
85
99
  const DIAGRAM_MARGIN_LEFT = 5;
86
100
  const DIAGRAM_MARGIN_RIGHT = 120;
87
- const DIAGRAM_MARGIN_VERTICAL = 5;
101
+ const DIAGRAM_MARGIN_TOP = 5;
102
+ const DIAGRAM_MARGIN_BOTTOM = 40;
103
+ const ZOOM_SCALE_EXTENT_MIN = 0.5;
104
+ const ZOOM_SCALE_EXTENT_MAX = 2;
105
+ function computeSankeyDimensions(data, nodePadding, minNodeHeight, minColumnWidth, marginLeft, marginRight, marginTop, marginBottom) {
106
+ if (!data || 0 === data.nodes.length) return {
107
+ minWidth: 0,
108
+ minHeight: 0
109
+ };
110
+ const outgoing = new Map();
111
+ const incoming = new Map();
112
+ for (const node of data.nodes){
113
+ outgoing.set(node.id, []);
114
+ incoming.set(node.id, []);
115
+ }
116
+ for (const link of data.links){
117
+ outgoing.get(link.source)?.push(link.target);
118
+ incoming.get(link.target)?.push(link.source);
119
+ }
120
+ const depth = new Map();
121
+ const sources = data.nodes.filter((n)=>(incoming.get(n.id)?.length ?? 0) === 0);
122
+ const queue = [];
123
+ for (const s of sources){
124
+ depth.set(s.id, 0);
125
+ queue.push(s.id);
126
+ }
127
+ const maxIterations = data.nodes.length * data.links.length + data.nodes.length;
128
+ let iterations = 0;
129
+ while(queue.length > 0 && iterations++ < maxIterations){
130
+ const id = queue.shift();
131
+ const d = depth.get(id);
132
+ for (const target of outgoing.get(id) ?? [])if (!depth.has(target) || depth.get(target) < d + 1) {
133
+ depth.set(target, d + 1);
134
+ queue.push(target);
135
+ }
136
+ }
137
+ const columnCounts = new Map();
138
+ for (const d of depth.values())columnCounts.set(d, (columnCounts.get(d) ?? 0) + 1);
139
+ const columnCount = columnCounts.size;
140
+ const maxNodesPerColumn = Math.max(0, ...columnCounts.values());
141
+ const minWidth = columnCount * minColumnWidth + marginLeft + marginRight;
142
+ const minHeight = maxNodesPerColumn * (minNodeHeight + nodePadding) - nodePadding + marginTop + marginBottom;
143
+ return {
144
+ minWidth,
145
+ minHeight
146
+ };
147
+ }
88
148
  const truncateToFit = (text, maxWidth, ctx)=>{
89
149
  if (maxWidth <= 0) return '';
90
150
  if (ctx.measureText(text).width <= maxWidth) return text;
@@ -99,9 +159,13 @@ const truncateToFit = (text, maxWidth, ctx)=>{
99
159
  return 0 === lo ? ellipsis : text.substring(0, lo) + ellipsis;
100
160
  };
101
161
  const ApSankeyDiagram = /*#__PURE__*/ react.forwardRef((props, ref)=>{
102
- const { data, nodeAlignment = 'justify', nodePadding = 16, nodeWidth = 24, style, className, colorScheme = schemeTableau10, ariaLabel = 'Sankey diagram', onLinkClick, onNodeClick } = props;
162
+ const { data, nodeAlignment = 'justify', nodePadding = 16, nodeWidth = 24, style, className, colorScheme = schemeTableau10, ariaLabel = 'Sankey diagram', onLinkClick, onNodeClick, minNodeHeight = 36, minColumnWidth = 140 } = props;
103
163
  const containerRef = useRef(null);
104
164
  const svgRef = useRef(null);
165
+ const zoomGroupRef = useRef(null);
166
+ const zoomBehaviorRef = useRef(null);
167
+ const zoomTransformRef = useRef(zoomIdentity);
168
+ const hasUserZoomedRef = useRef(false);
105
169
  const [containerWidth, setContainerWidth] = useState(0);
106
170
  const [containerHeight, setContainerHeight] = useState(0);
107
171
  const [selectedLinkIndex, setSelectedLinkIndex] = useState(null);
@@ -123,6 +187,20 @@ const ApSankeyDiagram = /*#__PURE__*/ react.forwardRef((props, ref)=>{
123
187
  }, []);
124
188
  const actualWidth = containerWidth || 1200;
125
189
  const actualHeight = containerHeight || 600;
190
+ const { minWidth, minHeight } = useMemo(()=>{
191
+ if (!data || 0 === data.nodes.length) return {
192
+ minWidth: 0,
193
+ minHeight: 0
194
+ };
195
+ return computeSankeyDimensions(data, nodePadding, minNodeHeight, minColumnWidth, DIAGRAM_MARGIN_LEFT, DIAGRAM_MARGIN_RIGHT, DIAGRAM_MARGIN_TOP, DIAGRAM_MARGIN_BOTTOM);
196
+ }, [
197
+ data,
198
+ nodePadding,
199
+ minNodeHeight,
200
+ minColumnWidth
201
+ ]);
202
+ const svgWidth = Math.max(actualWidth, minWidth);
203
+ const svgHeight = Math.max(actualHeight, minHeight);
126
204
  const nodeColorMap = useMemo(()=>{
127
205
  const map = new Map();
128
206
  if (!data) return map;
@@ -141,11 +219,11 @@ const ApSankeyDiagram = /*#__PURE__*/ react.forwardRef((props, ref)=>{
141
219
  const sankeyGenerator = sankey().nodeId((d)=>d.id).nodeAlign(alignmentFn).nodeWidth(nodeWidth).nodePadding(nodePadding).extent([
142
220
  [
143
221
  DIAGRAM_MARGIN_LEFT,
144
- DIAGRAM_MARGIN_VERTICAL
222
+ DIAGRAM_MARGIN_TOP
145
223
  ],
146
224
  [
147
- actualWidth - DIAGRAM_MARGIN_RIGHT,
148
- actualHeight - DIAGRAM_MARGIN_VERTICAL
225
+ svgWidth - DIAGRAM_MARGIN_RIGHT,
226
+ svgHeight - DIAGRAM_MARGIN_BOTTOM
149
227
  ]
150
228
  ]);
151
229
  const graph = {
@@ -162,8 +240,8 @@ const ApSankeyDiagram = /*#__PURE__*/ react.forwardRef((props, ref)=>{
162
240
  nodeAlignment,
163
241
  nodeWidth,
164
242
  nodePadding,
165
- actualWidth,
166
- actualHeight
243
+ svgWidth,
244
+ svgHeight
167
245
  ]);
168
246
  const linkPaths = useMemo(()=>{
169
247
  if (!sankeyGraph) return [];
@@ -198,6 +276,8 @@ const ApSankeyDiagram = /*#__PURE__*/ react.forwardRef((props, ref)=>{
198
276
  targetId: targetNode.id,
199
277
  sourceLabel: sourceNode.label,
200
278
  targetLabel: targetNode.label,
279
+ sourceX,
280
+ targetX,
201
281
  centerX,
202
282
  centerY
203
283
  };
@@ -252,18 +332,84 @@ const ApSankeyDiagram = /*#__PURE__*/ react.forwardRef((props, ref)=>{
252
332
  nodeColorMap,
253
333
  colorScheme
254
334
  ]);
335
+ const fitToView = useCallback(()=>{
336
+ const svg = svgRef.current;
337
+ const zoomBehavior = zoomBehaviorRef.current;
338
+ if (!svg || !zoomBehavior || 0 === svgWidth || 0 === svgHeight) return;
339
+ hasUserZoomedRef.current = false;
340
+ const cw = containerWidth || 1;
341
+ const ch = containerHeight || 1;
342
+ const scale = 0.95 * Math.min(cw / svgWidth, ch / svgHeight);
343
+ const tx = (cw - svgWidth * scale) / 2;
344
+ const ty = (ch - svgHeight * scale) / 2;
345
+ const t = zoomIdentity.translate(tx, ty).scale(scale);
346
+ external_d3_selection_select(svg).transition().duration(300).call(zoomBehavior.transform, t);
347
+ }, [
348
+ containerWidth,
349
+ containerHeight,
350
+ svgWidth,
351
+ svgHeight
352
+ ]);
353
+ useEffect(()=>{
354
+ const svg = svgRef.current;
355
+ const zoomGroup = zoomGroupRef.current;
356
+ if (!svg || !zoomGroup) return;
357
+ const zoomBehavior = zoom().scaleExtent([
358
+ ZOOM_SCALE_EXTENT_MIN,
359
+ ZOOM_SCALE_EXTENT_MAX
360
+ ]).filter((event)=>{
361
+ if ('wheel' === event.type) return event.ctrlKey || event.metaKey;
362
+ return !event.button;
363
+ }).on('zoom', (event)=>{
364
+ const t = event.transform;
365
+ zoomTransformRef.current = t;
366
+ zoomGroup.setAttribute('transform', `translate(${t.x},${t.y}) scale(${t.k})`);
367
+ if (event.sourceEvent) hasUserZoomedRef.current = true;
368
+ });
369
+ zoomBehaviorRef.current = zoomBehavior;
370
+ external_d3_selection_select(svg).call(zoomBehavior);
371
+ return ()=>{
372
+ external_d3_selection_select(svg).on('.zoom', null);
373
+ zoomBehaviorRef.current = null;
374
+ };
375
+ }, []);
376
+ useEffect(()=>{
377
+ if (!sankeyGraph || 0 === containerWidth || 0 === containerHeight) return;
378
+ if (hasUserZoomedRef.current) return;
379
+ fitToView();
380
+ }, [
381
+ sankeyGraph,
382
+ containerWidth,
383
+ containerHeight,
384
+ fitToView
385
+ ]);
386
+ const handleZoomIn = useCallback(()=>{
387
+ const svg = svgRef.current;
388
+ const zoomBehavior = zoomBehaviorRef.current;
389
+ if (!svg || !zoomBehavior) return;
390
+ external_d3_selection_select(svg).transition().duration(200).call(zoomBehavior.scaleBy, 1.3);
391
+ }, []);
392
+ const handleZoomOut = useCallback(()=>{
393
+ const svg = svgRef.current;
394
+ const zoomBehavior = zoomBehaviorRef.current;
395
+ if (!svg || !zoomBehavior) return;
396
+ external_d3_selection_select(svg).transition().duration(200).call(zoomBehavior.scaleBy, 1 / 1.3);
397
+ }, []);
255
398
  const handleLinkMouseEnter = useCallback((index, centerX, centerY)=>{
256
399
  if (selectedLinkIndex === index) return;
257
400
  const svgRect = svgRef.current?.getBoundingClientRect();
258
401
  if (svgRect) {
402
+ const t = zoomTransformRef.current;
403
+ const screenX = svgRect.left + centerX * t.k + t.x;
404
+ const screenY = svgRect.top + centerY * t.k + t.y;
259
405
  const virtualAnchor = {
260
406
  getBoundingClientRect: ()=>({
261
- x: svgRect.left + centerX,
262
- y: svgRect.top + centerY,
263
- left: svgRect.left + centerX,
264
- top: svgRect.top + centerY,
265
- right: svgRect.left + centerX,
266
- bottom: svgRect.top + centerY,
407
+ x: screenX,
408
+ y: screenY,
409
+ left: screenX,
410
+ top: screenY,
411
+ right: screenX,
412
+ bottom: screenY,
267
413
  width: 0,
268
414
  height: 0,
269
415
  toJSON: ()=>{}
@@ -300,90 +446,121 @@ const ApSankeyDiagram = /*#__PURE__*/ react.forwardRef((props, ref)=>{
300
446
  ref: containerRef,
301
447
  style: style,
302
448
  className: className,
303
- role: "img",
449
+ role: "figure",
304
450
  "aria-label": ariaLabel,
305
451
  children: [
306
- /*#__PURE__*/ jsxs("svg", {
452
+ /*#__PURE__*/ jsx("svg", {
307
453
  ref: svgRef,
308
- width: actualWidth,
309
- height: actualHeight,
454
+ width: svgWidth,
455
+ height: svgHeight,
456
+ children: /*#__PURE__*/ jsxs("g", {
457
+ ref: zoomGroupRef,
458
+ children: [
459
+ /*#__PURE__*/ jsx("defs", {
460
+ children: linkPaths.map((linkData)=>/*#__PURE__*/ jsxs("linearGradient", {
461
+ id: linkData.gradientId,
462
+ gradientUnits: "userSpaceOnUse",
463
+ x1: linkData.sourceX,
464
+ y1: 0,
465
+ x2: linkData.targetX,
466
+ y2: 0,
467
+ children: [
468
+ /*#__PURE__*/ jsx("stop", {
469
+ offset: "0%",
470
+ stopColor: linkData.sourceColor
471
+ }),
472
+ /*#__PURE__*/ jsx("stop", {
473
+ offset: "100%",
474
+ stopColor: linkData.targetColor
475
+ })
476
+ ]
477
+ }, linkData.gradientId))
478
+ }),
479
+ /*#__PURE__*/ jsx("g", {
480
+ children: linkPaths.map((linkData, index)=>{
481
+ const isConnectedToHoveredNode = connectedLinkIndices.has(index);
482
+ let opacity = 0.6;
483
+ if (selectedLinkIndex === index) opacity = 1;
484
+ else if (hoveredNodeId) opacity = isConnectedToHoveredNode ? 1 : 0.3;
485
+ return /*#__PURE__*/ jsx(StyledSankeyLink, {
486
+ d: linkData.path,
487
+ stroke: linkData.color ? linkData.color : `url(#${linkData.gradientId})`,
488
+ strokeWidth: linkData.strokeWidth,
489
+ fill: "none",
490
+ opacity: opacity,
491
+ onMouseEnter: ()=>handleLinkMouseEnter(index, linkData.centerX, linkData.centerY),
492
+ onMouseLeave: handleLinkMouseLeave,
493
+ onClick: (e)=>onLinkClick?.(linkData.originalLink, e),
494
+ "aria-label": `Link from ${linkData.sourceLabel} to ${linkData.targetLabel}`
495
+ }, `link-${index}`);
496
+ })
497
+ }),
498
+ /*#__PURE__*/ jsx("g", {
499
+ children: nodeData.map((nodeItem)=>{
500
+ const nodeOpacity = hoveredNodeId ? hoveredNodeId === nodeItem.node.id ? 1 : 0.4 : 1;
501
+ return /*#__PURE__*/ jsxs("g", {
502
+ children: [
503
+ /*#__PURE__*/ jsx(StyledSankeyNode, {
504
+ x: nodeItem.x,
505
+ y: nodeItem.y,
506
+ width: nodeItem.width,
507
+ height: nodeItem.height,
508
+ fill: nodeItem.color,
509
+ opacity: nodeOpacity,
510
+ onMouseEnter: ()=>handleNodeMouseEnter(nodeItem.node.id),
511
+ onMouseLeave: handleNodeMouseLeave,
512
+ onClick: (e)=>onNodeClick?.(nodeItem.node, e)
513
+ }),
514
+ /*#__PURE__*/ jsxs(StyledSankeyNodeLabel, {
515
+ x: nodeItem.labelX,
516
+ y: nodeItem.labelY,
517
+ dy: "0.35em",
518
+ textAnchor: nodeItem.labelAnchor,
519
+ children: [
520
+ /*#__PURE__*/ jsx("title", {
521
+ children: nodeItem.fullLabel
522
+ }),
523
+ nodeItem.displayLabel
524
+ ]
525
+ }),
526
+ nodeItem.value > 0 && /*#__PURE__*/ jsx(StyledSankeyNodeValue, {
527
+ x: nodeItem.labelX,
528
+ y: nodeItem.labelY + parseInt(apollo_core.Spacing.SpacingM, 10),
529
+ dy: "0.35em",
530
+ textAnchor: nodeItem.labelAnchor,
531
+ children: nodeItem.value
532
+ })
533
+ ]
534
+ }, `node-${nodeItem.node.id}`);
535
+ })
536
+ })
537
+ ]
538
+ })
539
+ }),
540
+ /*#__PURE__*/ jsxs(ZoomControls, {
310
541
  children: [
311
- /*#__PURE__*/ jsx("defs", {
312
- children: linkPaths.map((linkData)=>/*#__PURE__*/ jsxs("linearGradient", {
313
- id: linkData.gradientId,
314
- gradientUnits: "userSpaceOnUse",
315
- x1: "0%",
316
- y1: "0%",
317
- x2: "100%",
318
- y2: "0%",
319
- children: [
320
- /*#__PURE__*/ jsx("stop", {
321
- offset: "0%",
322
- stopColor: linkData.sourceColor
323
- }),
324
- /*#__PURE__*/ jsx("stop", {
325
- offset: "100%",
326
- stopColor: linkData.targetColor
327
- })
328
- ]
329
- }, linkData.gradientId))
542
+ /*#__PURE__*/ jsx(IconButton, {
543
+ size: "small",
544
+ onClick: handleZoomIn,
545
+ "aria-label": "Zoom in",
546
+ children: /*#__PURE__*/ jsx(Add, {
547
+ fontSize: "small"
548
+ })
330
549
  }),
331
- /*#__PURE__*/ jsx("g", {
332
- children: linkPaths.map((linkData, index)=>{
333
- const isConnectedToHoveredNode = connectedLinkIndices.has(index);
334
- let opacity = 0.6;
335
- if (selectedLinkIndex === index) opacity = 1;
336
- else if (hoveredNodeId) opacity = isConnectedToHoveredNode ? 1 : 0.3;
337
- return /*#__PURE__*/ jsx(StyledSankeyLink, {
338
- d: linkData.path,
339
- stroke: linkData.color ? linkData.color : `url(#${linkData.gradientId})`,
340
- strokeWidth: linkData.strokeWidth,
341
- fill: "none",
342
- opacity: opacity,
343
- onMouseEnter: ()=>handleLinkMouseEnter(index, linkData.centerX, linkData.centerY),
344
- onMouseLeave: handleLinkMouseLeave,
345
- onClick: (e)=>onLinkClick?.(linkData.originalLink, e),
346
- "aria-label": `Link from ${linkData.sourceLabel} to ${linkData.targetLabel}`
347
- }, `link-${index}`);
550
+ /*#__PURE__*/ jsx(IconButton, {
551
+ size: "small",
552
+ onClick: handleZoomOut,
553
+ "aria-label": "Zoom out",
554
+ children: /*#__PURE__*/ jsx(Remove, {
555
+ fontSize: "small"
348
556
  })
349
557
  }),
350
- /*#__PURE__*/ jsx("g", {
351
- children: nodeData.map((nodeItem)=>{
352
- const nodeOpacity = hoveredNodeId ? hoveredNodeId === nodeItem.node.id ? 1 : 0.4 : 1;
353
- return /*#__PURE__*/ jsxs("g", {
354
- children: [
355
- /*#__PURE__*/ jsx(StyledSankeyNode, {
356
- x: nodeItem.x,
357
- y: nodeItem.y,
358
- width: nodeItem.width,
359
- height: nodeItem.height,
360
- fill: nodeItem.color,
361
- opacity: nodeOpacity,
362
- onMouseEnter: ()=>handleNodeMouseEnter(nodeItem.node.id),
363
- onMouseLeave: handleNodeMouseLeave,
364
- onClick: (e)=>onNodeClick?.(nodeItem.node, e)
365
- }),
366
- /*#__PURE__*/ jsxs(StyledSankeyNodeLabel, {
367
- x: nodeItem.labelX,
368
- y: nodeItem.labelY,
369
- dy: "0.35em",
370
- textAnchor: nodeItem.labelAnchor,
371
- children: [
372
- /*#__PURE__*/ jsx("title", {
373
- children: nodeItem.fullLabel
374
- }),
375
- nodeItem.displayLabel
376
- ]
377
- }),
378
- nodeItem.value > 0 && /*#__PURE__*/ jsx(StyledSankeyNodeValue, {
379
- x: nodeItem.labelX,
380
- y: nodeItem.labelY + parseInt(apollo_core.Spacing.SpacingM, 10),
381
- dy: "0.35em",
382
- textAnchor: nodeItem.labelAnchor,
383
- children: nodeItem.value
384
- })
385
- ]
386
- }, `node-${nodeItem.node.id}`);
558
+ /*#__PURE__*/ jsx(IconButton, {
559
+ size: "small",
560
+ onClick: fitToView,
561
+ "aria-label": "Fit to view",
562
+ children: /*#__PURE__*/ jsx(FitScreen, {
563
+ fontSize: "small"
387
564
  })
388
565
  })
389
566
  ]
@@ -472,4 +649,4 @@ const ApSankeyDiagram = /*#__PURE__*/ react.forwardRef((props, ref)=>{
472
649
  });
473
650
  });
474
651
  ApSankeyDiagram.displayName = 'ApSankeyDiagram';
475
- export { ApSankeyDiagram };
652
+ export { ApSankeyDiagram, computeSankeyDimensions };
@@ -28,5 +28,7 @@ export interface ApSankeyDiagramProps {
28
28
  onLinkClick?: (link: SankeyLink, event: React.MouseEvent) => void;
29
29
  colorScheme?: string[];
30
30
  ariaLabel?: string;
31
+ minNodeHeight?: number;
32
+ minColumnWidth?: number;
31
33
  }
32
34
  //# sourceMappingURL=ApSankeyDiagram.types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ApSankeyDiagram.types.d.ts","sourceRoot":"","sources":["../../../../src/material/components/ap-sankey-diagram/ApSankeyDiagram.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAK3C,MAAM,WAAW,UAAU;IAEzB,EAAE,EAAE,MAAM,CAAC;IAEX,KAAK,EAAE,MAAM,CAAC;IAEd,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAKD,MAAM,WAAW,UAAU;IAEzB,MAAM,EAAE,MAAM,CAAC;IAEf,MAAM,EAAE,MAAM,CAAC;IAEf,KAAK,EAAE,MAAM,CAAC;IAEd,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAKD,MAAM,WAAW,UAAU;IAEzB,KAAK,EAAE,UAAU,EAAE,CAAC;IAEpB,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;AAKD,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAC;AAKpE,MAAM,WAAW,oBAAoB;IAEnC,IAAI,EAAE,UAAU,CAAC;IAEjB,aAAa,CAAC,EAAE,aAAa,CAAC;IAE9B,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,KAAK,CAAC,EAAE,aAAa,CAAC;IAEtB,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IAElE,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IAElE,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IAEvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
1
+ {"version":3,"file":"ApSankeyDiagram.types.d.ts","sourceRoot":"","sources":["../../../../src/material/components/ap-sankey-diagram/ApSankeyDiagram.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAK3C,MAAM,WAAW,UAAU;IAEzB,EAAE,EAAE,MAAM,CAAC;IAEX,KAAK,EAAE,MAAM,CAAC;IAEd,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAKD,MAAM,WAAW,UAAU;IAEzB,MAAM,EAAE,MAAM,CAAC;IAEf,MAAM,EAAE,MAAM,CAAC;IAEf,KAAK,EAAE,MAAM,CAAC;IAEd,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAKD,MAAM,WAAW,UAAU;IAEzB,KAAK,EAAE,UAAU,EAAE,CAAC;IAEpB,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;AAKD,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAC;AAKpE,MAAM,WAAW,oBAAoB;IAEnC,IAAI,EAAE,UAAU,CAAC;IAEjB,aAAa,CAAC,EAAE,aAAa,CAAC;IAE9B,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,KAAK,CAAC,EAAE,aAAa,CAAC;IAEtB,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IAElE,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;IAElE,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IAEvB,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uipath/apollo-react",
3
- "version": "3.45.3",
3
+ "version": "3.45.5",
4
4
  "description": "Apollo Design System - React component library with Material UI theming",
5
5
  "repository": {
6
6
  "type": "git",
@@ -175,6 +175,8 @@
175
175
  "d3-sankey": "^0.12.3",
176
176
  "d3-scale": "^4.0.2",
177
177
  "d3-scale-chromatic": "^3.1.0",
178
+ "d3-selection": "^3.0.0",
179
+ "d3-zoom": "^3.0.0",
178
180
  "date-fns": "^4.1.0",
179
181
  "debounce": "^3.0.0",
180
182
  "html-to-image": "^1.11.11",
@@ -198,8 +200,8 @@
198
200
  "use-sync-external-store": "^1.2.0",
199
201
  "zod": "^4.3.5",
200
202
  "zustand": "^5.0.9",
201
- "@uipath/apollo-core": "5.7.0",
202
- "@uipath/apollo-wind": "0.10.0"
203
+ "@uipath/apollo-wind": "0.10.0",
204
+ "@uipath/apollo-core": "5.7.0"
203
205
  },
204
206
  "devDependencies": {
205
207
  "@lingui/cli": "^5.6.1",
@@ -221,6 +223,8 @@
221
223
  "@types/d3-sankey": "^0.12.4",
222
224
  "@types/d3-scale": "^4.0.8",
223
225
  "@types/d3-scale-chromatic": "^3.0.3",
226
+ "@types/d3-selection": "^3.0.11",
227
+ "@types/d3-zoom": "^3.0.8",
224
228
  "@types/lodash": "^4.17.21",
225
229
  "@types/luxon": "^3.7.1",
226
230
  "@types/mdast": "^4.0.4",