@crazyhappyone/auto-graph 0.1.3 → 0.1.4

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/index.js CHANGED
@@ -96,6 +96,9 @@ function applyLayoutConstraints(input) {
96
96
  const nodeById = new Map(input.nodes.map((node) => [node.id, node]));
97
97
  applyFixedPositionLocks(input.nodes, boxes, locks, diagnostics);
98
98
  applyExactPositions(input.constraints, boxes, locks, diagnostics, nodeById);
99
+ if (input.distributeContainedChildren) {
100
+ yieldFixedPositionLocks(input, boxes, locks);
101
+ }
99
102
  applyContainment(input.constraints, boxes, locks, diagnostics, false);
100
103
  applyRelative(input.constraints, boxes, locks, diagnostics);
101
104
  applyAlign(input.constraints, boxes, locks, diagnostics);
@@ -109,6 +112,13 @@ function applyLayoutConstraints(input) {
109
112
  );
110
113
  applyContainment(input.constraints, boxes, locks, diagnostics, true);
111
114
  applyDistributeContained(input, boxes, locks, diagnostics);
115
+ if (input.distributeContainedChildren) {
116
+ const diagBefore = diagnostics.length;
117
+ applyContainment(input.constraints, boxes, locks, diagnostics, true);
118
+ applyDistributeContained(input, boxes, locks, diagnostics);
119
+ dedupReplayDiagnostics(diagnostics, diagBefore);
120
+ }
121
+ removeResolvedConstraintDiagnostics(input.constraints, boxes, diagnostics);
112
122
  reportOverlaps(boxes, diagnostics, containmentOverlapKeys(input.constraints));
113
123
  reportIntraContainerOverflow(input, boxes, diagnostics);
114
124
  return { boxes, locks, diagnostics };
@@ -154,6 +164,62 @@ function applyFixedPositionLocks(nodes, boxes, locks, diagnostics) {
154
164
  locks.set(node.id, { nodeId: node.id, source: "fixed-position" });
155
165
  }
156
166
  }
167
+ function dedupReplayDiagnostics(diagnostics, keepUpTo) {
168
+ const seen = /* @__PURE__ */ new Set();
169
+ for (let i = 0; i < keepUpTo && i < diagnostics.length; i += 1) {
170
+ const d = diagnostics[i];
171
+ if (d === void 0) continue;
172
+ seen.add(diagnosticFingerprint(d));
173
+ }
174
+ for (let i = diagnostics.length - 1; i >= keepUpTo; i -= 1) {
175
+ const d = diagnostics[i];
176
+ if (d === void 0) continue;
177
+ const fp = diagnosticFingerprint(d);
178
+ if (seen.has(fp)) {
179
+ diagnostics.splice(i, 1);
180
+ } else {
181
+ seen.add(fp);
182
+ }
183
+ }
184
+ }
185
+ function diagnosticFingerprint(d) {
186
+ const nodeId = typeof d.detail?.nodeId === "string" ? d.detail.nodeId : "";
187
+ const containerId = typeof d.detail?.containerId === "string" ? d.detail.containerId : "";
188
+ return `${d.code}|${nodeId}|${containerId}`;
189
+ }
190
+ function yieldFixedPositionLocks(input, boxes, locks) {
191
+ for (const c of input.constraints) {
192
+ if (c.kind !== "containment") continue;
193
+ const container = boxes.get(c.containerId);
194
+ if (container === void 0) continue;
195
+ const content = contentBox(container, c.padding);
196
+ const mainAxis = input.direction === "LR" || input.direction === "RL" ? "width" : "height";
197
+ const crossAxis = mainAxis === "width" ? "height" : "width";
198
+ let eligible = 0;
199
+ for (const childId of c.childIds) {
200
+ const box = boxes.get(childId);
201
+ if (box === void 0) continue;
202
+ const lock = locks.get(childId);
203
+ if (lock?.source === "exact-position") continue;
204
+ const fits = box[mainAxis] <= content[mainAxis] && box[crossAxis] <= content[crossAxis];
205
+ if (fits) {
206
+ eligible += 1;
207
+ }
208
+ }
209
+ if (eligible < 2) continue;
210
+ for (const childId of c.childIds) {
211
+ const lock = locks.get(childId);
212
+ if (lock?.source === "fixed-position") {
213
+ const box = boxes.get(childId);
214
+ if (box === void 0) continue;
215
+ const fits = box[mainAxis] <= content[mainAxis] && box[crossAxis] <= content[crossAxis];
216
+ if (fits) {
217
+ locks.delete(childId);
218
+ }
219
+ }
220
+ }
221
+ }
222
+ }
157
223
  function applyExactPositions(constraints, boxes, locks, diagnostics, nodeById) {
158
224
  for (const constraint of constraints) {
159
225
  if (constraint.kind !== "exact-position") {
@@ -226,7 +292,7 @@ function applyContainment(constraints, boxes, locks, diagnostics, reportOverflow
226
292
  code: "constraints.locked-target-not-moved",
227
293
  message: `Locked child ${childId} was not moved into containment.`,
228
294
  path: ["constraints", constraint.id ?? constraint.containerId],
229
- detail: { nodeId: childId }
295
+ detail: { nodeId: childId, containerId: constraint.containerId }
230
296
  });
231
297
  if (!isInside(child, content)) {
232
298
  diagnostics.push({
@@ -378,6 +444,60 @@ function repairOverlaps(input, boxes, locks, diagnostics, siblingPairs) {
378
444
  }
379
445
  reportOverlaps(boxes, diagnostics, ignoredPairs);
380
446
  }
447
+ function removeResolvedConstraintDiagnostics(constraints, boxes, diagnostics) {
448
+ for (let i = diagnostics.length - 1; i >= 0; i -= 1) {
449
+ const d = diagnostics[i];
450
+ if (d === void 0) continue;
451
+ if (d.code === "constraints.overlap.unresolved") {
452
+ const aId = d.detail?.firstId;
453
+ const bId = d.detail?.secondId;
454
+ if (typeof aId !== "string" || typeof bId !== "string") continue;
455
+ const a = boxes.get(aId);
456
+ const b = boxes.get(bId);
457
+ if (a !== void 0 && b !== void 0 && !intersectsAabb(a, b)) {
458
+ diagnostics.splice(i, 1);
459
+ }
460
+ continue;
461
+ }
462
+ if (d.code === "constraints.containment.impossible" || d.code === "constraints.locked-target-not-moved" && typeof d.message === "string" && d.message.includes("not moved into containment")) {
463
+ const nodeId = d.detail?.nodeId;
464
+ if (typeof nodeId !== "string") continue;
465
+ const child = boxes.get(nodeId);
466
+ if (child === void 0) continue;
467
+ const diagContainerId = typeof d.detail?.containerId === "string" ? d.detail.containerId : void 0;
468
+ let resolved = false;
469
+ for (const c of constraints) {
470
+ if (c.kind !== "containment") continue;
471
+ if (!c.childIds.includes(nodeId)) continue;
472
+ if (diagContainerId !== void 0 && c.containerId !== diagContainerId) {
473
+ continue;
474
+ }
475
+ const container = boxes.get(c.containerId);
476
+ if (container === void 0) continue;
477
+ const content = contentBox(container, c.padding);
478
+ if (isInside(child, content)) {
479
+ diagnostics.splice(i, 1);
480
+ resolved = true;
481
+ }
482
+ break;
483
+ }
484
+ if (!resolved && diagContainerId !== void 0) {
485
+ for (const c of constraints) {
486
+ if (c.kind !== "containment") continue;
487
+ if (c.containerId !== diagContainerId) continue;
488
+ if (!c.childIds.includes(nodeId)) continue;
489
+ const container = boxes.get(c.containerId);
490
+ if (container === void 0) continue;
491
+ const content = contentBox(container, c.padding);
492
+ if (isInside(child, content)) {
493
+ diagnostics.splice(i, 1);
494
+ }
495
+ break;
496
+ }
497
+ }
498
+ }
499
+ }
500
+ }
381
501
  function reportOverlaps(boxes, diagnostics, ignoredPairs = /* @__PURE__ */ new Set()) {
382
502
  const ids = [...boxes.keys()].sort();
383
503
  const reported = new Set(
@@ -685,6 +805,12 @@ function applyDistributeContained(input, boxes, locks, diagnostics) {
685
805
  continue;
686
806
  }
687
807
  if (locks.has(childId)) {
808
+ const lock = locks.get(childId);
809
+ if (lock?.source === "fixed-position") {
810
+ unlocked.push({ id: childId, box });
811
+ locks.delete(childId);
812
+ continue;
813
+ }
688
814
  diagnostics.push({
689
815
  severity: "warning",
690
816
  code: "constraints.locked-target-not-moved",
@@ -743,6 +869,7 @@ function applyDistributeContained(input, boxes, locks, diagnostics) {
743
869
  });
744
870
  }
745
871
  boxes.set(child.id, clamped);
872
+ locks.delete(child.id);
746
873
  pos = clamped[axis] + clamped[mainSize] + minGap;
747
874
  }
748
875
  diagnostics.push({
@@ -3988,7 +4115,7 @@ function routeEdge(input) {
3988
4115
  const rerouted = greedyRerouteAroundObstacles(
3989
4116
  bestPoints2,
3990
4117
  allObstacles,
3991
- Math.min(maxAttempts, 3)
4118
+ maxAttempts
3992
4119
  );
3993
4120
  const reroutedAvoidsEndpointInteriors = !routeIntersectsEndpointInteriors(
3994
4121
  rerouted,
@@ -4750,9 +4877,7 @@ function solveDiagram(diagram, options = {}) {
4750
4877
  options?.overlapSpacing ?? 40,
4751
4878
  Math.max(0, options?.minLaneGutter ?? 0)
4752
4879
  );
4753
- if (swimlaneContracts.layouts.size > 0) {
4754
- removeResolvedOverlapDiagnostics(diagnostics, constrained.boxes);
4755
- }
4880
+ removeResolvedOverlapDiagnostics(diagnostics, constrained.boxes);
4756
4881
  diagnostics.push(...swimlaneContracts.diagnostics);
4757
4882
  const coordinatedNodes = coordinateNodes(
4758
4883
  styledNodes,
@@ -6698,7 +6823,8 @@ function coordinateEdges(edges, nodes, coordinatedNodes, obstacles, softObstacle
6698
6823
  ...softObstacles,
6699
6824
  ...routeTextObstacles
6700
6825
  ],
6701
- hardObstacles
6826
+ hardObstacles,
6827
+ ...options.maxRoutingAttempts === void 0 ? {} : { maxRoutingAttempts: options.maxRoutingAttempts }
6702
6828
  });
6703
6829
  diagnostics.push(
6704
6830
  ...route.diagnostics.map((diagnostic) => ({
@@ -7181,7 +7307,8 @@ function edgeLabelAnchor(edge, layout2, edges, obstacleBoxes, placedLabelBoxes,
7181
7307
  for (const candidate of edgeLabelAnchorCandidates(
7182
7308
  edge.points,
7183
7309
  placement,
7184
- layout2
7310
+ layout2,
7311
+ baseOffset
7185
7312
  )) {
7186
7313
  const labelBox = {
7187
7314
  x: candidate.x - layout2.box.width / 2,
@@ -7213,8 +7340,8 @@ function edgeLabelAnchor(edge, layout2, edges, obstacleBoxes, placedLabelBoxes,
7213
7340
  }
7214
7341
  return placement;
7215
7342
  }
7216
- function edgeLabelAnchorCandidates(points, placement, layout2) {
7217
- const segment = labelSegmentOnPolyline(points);
7343
+ function edgeLabelAnchorCandidates(points, placement, layout2, baseOffset = 10) {
7344
+ const segment = labelSegmentOnPolyline(points, baseOffset);
7218
7345
  if (segment === void 0) {
7219
7346
  return [placement];
7220
7347
  }
@@ -7264,7 +7391,7 @@ function edgeLabelAnchorCandidates(points, placement, layout2) {
7264
7391
  }, 0);
7265
7392
  if (totalLen > 200) {
7266
7393
  for (const ratio of [0.25, 0.75]) {
7267
- const qp = labelPlacementAtRatio(points, ratio, totalLen);
7394
+ const qp = labelPlacementAtRatio(points, ratio, totalLen, baseOffset);
7268
7395
  if (qp !== void 0) {
7269
7396
  candidates.push(qp);
7270
7397
  const qTargetDist = totalLen * ratio;
@@ -7350,7 +7477,7 @@ function labelSegmentOnPolyline(points, baseOffset = 10) {
7350
7477
  if (last === void 0) {
7351
7478
  return void 0;
7352
7479
  }
7353
- const offset = labelOffset2(last);
7480
+ const offset = labelOffset2(last, baseOffset);
7354
7481
  return {
7355
7482
  start: last.start,
7356
7483
  end: last.end,
@@ -7372,7 +7499,7 @@ function nonZeroSegments2(points) {
7372
7499
  }
7373
7500
  return segments;
7374
7501
  }
7375
- function labelPlacementAtRatio(points, ratio, totalLength) {
7502
+ function labelPlacementAtRatio(points, ratio, totalLength, baseOffset = 10) {
7376
7503
  if (points.length < 2 || ratio < 0 || ratio > 1) {
7377
7504
  return void 0;
7378
7505
  }
@@ -7390,7 +7517,10 @@ function labelPlacementAtRatio(points, ratio, totalLength) {
7390
7517
  }
7391
7518
  if (travelled + segLen >= targetDist) {
7392
7519
  const t = (targetDist - travelled) / segLen;
7393
- const offset = labelOffset2({ start: prev, end: curr, length: segLen });
7520
+ const offset = labelOffset2(
7521
+ { start: prev, end: curr, length: segLen },
7522
+ baseOffset
7523
+ );
7394
7524
  return {
7395
7525
  x: prev.x + (curr.x - prev.x) * t + offset.x,
7396
7526
  y: prev.y + (curr.y - prev.y) * t + offset.y