@opentrace/components 0.1.1-rc.62 → 0.1.1-rc.77

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.
@@ -5605,9 +5605,10 @@ function useGraphInstance({
5605
5605
  const unmountedRef = t.useRef(false);
5606
5606
  const requestIdRef = t.useRef(0);
5607
5607
  const [layoutReady, setLayoutReady] = t.useState(false);
5608
+ const flatMode = layoutConfig.flatMode ?? false;
5608
5609
  const structuralTypes = t.useMemo(
5609
- () => new Set(layoutConfig.structuralTypes),
5610
- [layoutConfig.structuralTypes]
5610
+ () => flatMode ? /* @__PURE__ */ new Set() : new Set(layoutConfig.structuralTypes),
5611
+ [layoutConfig.structuralTypes, flatMode]
5611
5612
  );
5612
5613
  t.useEffect(() => {
5613
5614
  graph.clear();
@@ -5671,7 +5672,7 @@ function useGraphInstance({
5671
5672
  });
5672
5673
  }
5673
5674
  const { assignments: communityAssignments } = communityData;
5674
- if (communityAssignments) {
5675
+ if (communityAssignments && !flatMode) {
5675
5676
  const communityGroups = /* @__PURE__ */ new Map();
5676
5677
  for (const node of allNodes) {
5677
5678
  const cid = communityAssignments[node.id];
@@ -5719,7 +5720,7 @@ function useGraphInstance({
5719
5720
  const nodeIds = allNodes.map((n2) => n2.id);
5720
5721
  const simLinks = [];
5721
5722
  for (const link of allLinks) {
5722
- if (link.label !== layoutConfig.layoutEdgeType) continue;
5723
+ if (!flatMode && link.label !== layoutConfig.layoutEdgeType) continue;
5723
5724
  const source = endpointId(link.source);
5724
5725
  const target = endpointId(link.target);
5725
5726
  if (nodeIdSet.has(source) && nodeIdSet.has(target)) {
@@ -5760,7 +5761,7 @@ function useGraphInstance({
5760
5761
  console.log(`[graph] layout computed for ${pos.size} nodes`);
5761
5762
  }
5762
5763
  const { assignments: assignments2 } = communityData;
5763
- if (assignments2 && Object.keys(assignments2).length > 0) {
5764
+ if (!flatMode && assignments2 && Object.keys(assignments2).length > 0) {
5764
5765
  applySpacing(pos, assignments2, 40, 100, 50, 0.5);
5765
5766
  const sizeMap = /* @__PURE__ */ new Map();
5766
5767
  for (const sn of serializedNodes) {
@@ -7195,6 +7196,68 @@ function LayoutPipeline({
7195
7196
  }, [layoutReady, optimizeTick, layoutConfig, start, stop, sigma]);
7196
7197
  return null;
7197
7198
  }
7199
+ let cached$1 = null;
7200
+ function resolveLabelColor() {
7201
+ const root = document.documentElement;
7202
+ const key = `${root.dataset.theme ?? ""}_${root.dataset.mode ?? ""}`;
7203
+ if (cached$1 && cached$1.key === key) return cached$1.fg;
7204
+ const style = getComputedStyle(root);
7205
+ const fg = style.getPropertyValue("--foreground").trim() || "#e2e8f0";
7206
+ cached$1 = { fg, key };
7207
+ return fg;
7208
+ }
7209
+ let renderedBoxes = [];
7210
+ let hoverBoxes = [];
7211
+ function resetLabelGrid() {
7212
+ renderedBoxes = [];
7213
+ }
7214
+ function resetHoverGrid() {
7215
+ hoverBoxes = [];
7216
+ }
7217
+ function overlaps(boxes, x, y, w2, h2) {
7218
+ for (const box of boxes) {
7219
+ if (x < box.x + box.w && x + w2 > box.x && y < box.y + box.h && y + h2 > box.y) {
7220
+ return true;
7221
+ }
7222
+ }
7223
+ return false;
7224
+ }
7225
+ function overlapsExistingHover(x, y, w2, h2) {
7226
+ return overlaps(hoverBoxes, x, y, w2, h2);
7227
+ }
7228
+ function pushHoverBox(x, y, w2, h2) {
7229
+ hoverBoxes.push({ x, y, w: w2, h: h2 });
7230
+ }
7231
+ function drawNodeLabel(context, data, settings) {
7232
+ if (!data.label) return;
7233
+ const extras = data;
7234
+ if (extras.highlighted) return;
7235
+ const size = settings.labelSize;
7236
+ const font = settings.labelFont;
7237
+ const weight = settings.labelWeight;
7238
+ const fg = resolveLabelColor();
7239
+ context.font = `${weight} ${size}px ${font}`;
7240
+ const textWidth = context.measureText(data.label).width;
7241
+ const x = data.x + data.size + 3;
7242
+ const y = data.y + size / 3;
7243
+ const boxX = x - 1;
7244
+ const boxY = y - size;
7245
+ const boxW = textWidth + 2;
7246
+ const boxH = size + 4;
7247
+ if (overlaps(renderedBoxes, boxX, boxY, boxW, boxH)) {
7248
+ return;
7249
+ }
7250
+ renderedBoxes.push({ x: boxX, y: boxY, w: boxW, h: boxH });
7251
+ context.save();
7252
+ context.strokeStyle = "rgba(0,0,0,0.7)";
7253
+ context.lineWidth = 3;
7254
+ context.lineJoin = "round";
7255
+ context.strokeText(data.label, x, y);
7256
+ context.restore();
7257
+ context.font = `${weight} ${size}px ${font}`;
7258
+ context.fillStyle = fg;
7259
+ context.fillText(data.label, x, y);
7260
+ }
7198
7261
  let _hoveredNodeKey = null;
7199
7262
  function setHoveredNodeKey(key) {
7200
7263
  _hoveredNodeKey = key;
@@ -7213,15 +7276,73 @@ function resolveThemeColors() {
7213
7276
  };
7214
7277
  return cached;
7215
7278
  }
7279
+ let tooltipBuffer = [];
7280
+ let tooltipContext = null;
7281
+ let flushScheduled = false;
7282
+ function flushTooltips() {
7283
+ const ctx = tooltipContext;
7284
+ if (!ctx || tooltipBuffer.length === 0) return;
7285
+ const colors = resolveThemeColors();
7286
+ for (const cmd of tooltipBuffer) {
7287
+ const radius = 4;
7288
+ ctx.fillStyle = colors.bg;
7289
+ ctx.shadowOffsetX = 0;
7290
+ ctx.shadowOffsetY = 2;
7291
+ ctx.shadowBlur = 8;
7292
+ ctx.shadowColor = "rgba(0,0,0,0.5)";
7293
+ ctx.beginPath();
7294
+ ctx.roundRect(cmd.x, cmd.y, cmd.w, cmd.h, radius);
7295
+ ctx.fill();
7296
+ if (cmd.isHovered) {
7297
+ ctx.shadowBlur = 0;
7298
+ ctx.strokeStyle = cmd.color;
7299
+ ctx.lineWidth = 2;
7300
+ ctx.stroke();
7301
+ }
7302
+ ctx.shadowOffsetX = 0;
7303
+ ctx.shadowOffsetY = 0;
7304
+ ctx.shadowBlur = 0;
7305
+ const textX = cmd.x + 4;
7306
+ const centerY = cmd.y + cmd.h / 2;
7307
+ if (cmd.label) {
7308
+ ctx.fillStyle = colors.fg;
7309
+ ctx.font = `${cmd.weight} ${cmd.labelSize}px ${cmd.font}`;
7310
+ ctx.fillText(
7311
+ cmd.label,
7312
+ textX,
7313
+ centerY + (cmd.subtitle ? -cmd.lineHeight / 2 + cmd.labelSize * 0.35 : cmd.labelSize / 3)
7314
+ );
7315
+ }
7316
+ if (cmd.subtitle) {
7317
+ ctx.fillStyle = colors.muted;
7318
+ ctx.font = `${cmd.smallSize}px ${cmd.font}`;
7319
+ ctx.fillText(
7320
+ cmd.subtitle,
7321
+ textX,
7322
+ centerY + cmd.lineHeight / 2
7323
+ );
7324
+ }
7325
+ }
7326
+ tooltipBuffer = [];
7327
+ tooltipContext = null;
7328
+ }
7329
+ let lastHoverFrameTime = 0;
7216
7330
  function drawNodeHover(context, data, settings) {
7217
- const key = data.key;
7218
- if (key && _hoveredNodeKey !== null && key !== _hoveredNodeKey) return;
7331
+ const now = performance.now();
7332
+ if (now - lastHoverFrameTime > 8) {
7333
+ resetHoverGrid();
7334
+ tooltipBuffer = [];
7335
+ lastHoverFrameTime = now;
7336
+ }
7337
+ tooltipContext = context;
7338
+ const extras = data;
7339
+ const key = extras.key;
7340
+ const isActuallyHovered = key != null && _hoveredNodeKey != null && key === _hoveredNodeKey;
7341
+ const isSelected = extras.borderSize != null && extras.borderSize > 0;
7219
7342
  const size = settings.labelSize;
7220
7343
  const font = settings.labelFont;
7221
7344
  const weight = settings.labelWeight;
7222
- const colors = resolveThemeColors();
7223
7345
  const PADDING = 4;
7224
- const extras = data;
7225
7346
  const label = typeof data.label === "string" ? data.label : typeof extras._originalLabel === "string" ? extras._originalLabel : "";
7226
7347
  const nodeType = extras.nodeType;
7227
7348
  const communityName = extras._communityName;
@@ -7235,63 +7356,58 @@ function drawNodeHover(context, data, settings) {
7235
7356
  subtitleWidth = context.measureText(subtitle).width;
7236
7357
  }
7237
7358
  const textWidth = Math.max(labelWidth, subtitleWidth);
7238
- if (textWidth === 0 && !subtitle) {
7239
- context.beginPath();
7240
- context.arc(data.x, data.y, data.size + PADDING, 0, Math.PI * 2);
7241
- context.closePath();
7242
- context.fillStyle = colors.bg;
7243
- context.shadowBlur = 10;
7244
- context.shadowColor = "rgba(0,0,0,0.5)";
7245
- context.fill();
7246
- context.shadowBlur = 0;
7247
- context.beginPath();
7248
- context.arc(data.x, data.y, data.size, 0, Math.PI * 2);
7249
- context.closePath();
7250
- context.fillStyle = data.color;
7251
- context.fill();
7252
- return;
7253
- }
7254
7359
  const boxWidth = Math.round(textWidth + 8);
7255
7360
  const lineHeight = size + 2;
7256
7361
  const lines = subtitle ? 2 : 1;
7257
7362
  const boxHeight = Math.round(lineHeight * lines + PADDING * 2);
7258
- const nodeRadius = Math.max(data.size, size / 2) + PADDING;
7259
- const angleRadian = Math.asin(Math.min(1, boxHeight / 2 / nodeRadius));
7260
- const xDelta = Math.sqrt(
7261
- Math.abs(Math.pow(nodeRadius, 2) - Math.pow(boxHeight / 2, 2))
7262
- );
7263
- context.fillStyle = colors.bg;
7264
- context.shadowOffsetX = 0;
7265
- context.shadowOffsetY = 2;
7266
- context.shadowBlur = 10;
7267
- context.shadowColor = "rgba(0,0,0,0.5)";
7268
- context.beginPath();
7269
- context.moveTo(data.x + xDelta, data.y + boxHeight / 2);
7270
- context.lineTo(data.x + nodeRadius + boxWidth, data.y + boxHeight / 2);
7271
- context.lineTo(data.x + nodeRadius + boxWidth, data.y - boxHeight / 2);
7272
- context.lineTo(data.x + xDelta, data.y - boxHeight / 2);
7273
- context.arc(data.x, data.y, nodeRadius, angleRadian, -angleRadian);
7274
- context.closePath();
7275
- context.fill();
7276
- context.shadowOffsetX = 0;
7277
- context.shadowOffsetY = 0;
7278
- context.shadowBlur = 0;
7279
- const textX = data.x + nodeRadius + 4;
7280
- if (label) {
7281
- context.fillStyle = colors.fg;
7282
- context.font = `${weight} ${size}px ${font}`;
7283
- context.fillText(label, textX, data.y + (subtitle ? -1 : size / 3));
7284
- }
7285
- if (subtitle) {
7286
- context.fillStyle = colors.muted;
7287
- context.font = `${smallSize}px ${font}`;
7288
- context.fillText(subtitle, textX, data.y + lineHeight - 2);
7363
+ const tooltipGap = 4;
7364
+ const tx = data.x + data.size + tooltipGap;
7365
+ const ty = data.y - boxHeight / 2;
7366
+ const hasTooltip = textWidth > 0 || subtitle;
7367
+ let drawTooltip = hasTooltip;
7368
+ const pad = 8;
7369
+ const cullX = data.x - data.size - pad;
7370
+ const cullY = data.y - Math.max(boxHeight / 2, data.size) - pad;
7371
+ const cullW = data.size * 2 + boxWidth + tooltipGap + pad * 2;
7372
+ const cullH = Math.max(boxHeight, data.size * 2) + pad * 2;
7373
+ if (hasTooltip && !isActuallyHovered && !isSelected) {
7374
+ if (overlapsExistingHover(cullX, cullY, cullW, cullH)) {
7375
+ drawTooltip = false;
7376
+ } else {
7377
+ pushHoverBox(cullX, cullY, cullW, cullH);
7378
+ }
7379
+ } else if (hasTooltip) {
7380
+ pushHoverBox(cullX, cullY, cullW, cullH);
7289
7381
  }
7290
7382
  context.beginPath();
7291
7383
  context.arc(data.x, data.y, data.size, 0, Math.PI * 2);
7292
7384
  context.closePath();
7293
7385
  context.fillStyle = data.color;
7294
7386
  context.fill();
7387
+ if (drawTooltip) {
7388
+ tooltipBuffer.push({
7389
+ x: tx,
7390
+ y: ty,
7391
+ w: boxWidth,
7392
+ h: boxHeight,
7393
+ label,
7394
+ subtitle,
7395
+ labelSize: size,
7396
+ smallSize,
7397
+ font,
7398
+ weight,
7399
+ lineHeight,
7400
+ color: data.color,
7401
+ isHovered: isActuallyHovered
7402
+ });
7403
+ }
7404
+ if (!flushScheduled) {
7405
+ flushScheduled = true;
7406
+ queueMicrotask(() => {
7407
+ flushScheduled = false;
7408
+ flushTooltips();
7409
+ });
7410
+ }
7295
7411
  }
7296
7412
  function SigmaRefCapture({
7297
7413
  onReady
@@ -7304,6 +7420,14 @@ function SigmaRefCapture({
7304
7420
  onReady(sigma);
7305
7421
  }
7306
7422
  }, [sigma, onReady]);
7423
+ t.useEffect(() => {
7424
+ const s = sigma;
7425
+ const handler = () => resetLabelGrid();
7426
+ s.on("beforeRender", handler);
7427
+ return () => {
7428
+ s.off("beforeRender", handler);
7429
+ };
7430
+ }, [sigma]);
7307
7431
  return null;
7308
7432
  }
7309
7433
  function GraphEventHandler({
@@ -7557,6 +7681,7 @@ const GraphCanvas = t.memo(
7557
7681
  labelFont: LABEL_FONT,
7558
7682
  labelColor: { color: LABEL_COLOR },
7559
7683
  labelSize: LABEL_SIZE,
7684
+ defaultDrawNodeLabel: drawNodeLabel,
7560
7685
  defaultDrawNodeHover: drawNodeHover,
7561
7686
  allowInvalidContainer: true,
7562
7687
  zIndex: zIndexEnabled,
@@ -9119,6 +9244,9 @@ exports.NODE_SIZE_MIN = NODE_SIZE_MIN;
9119
9244
  exports.NODE_SIZE_MULTIPLIERS = NODE_SIZE_MULTIPLIERS;
9120
9245
  exports.ZOOM_SIZE_EXPONENT = ZOOM_SIZE_EXPONENT;
9121
9246
  exports.drawNodeHover = drawNodeHover;
9247
+ exports.drawNodeLabel = drawNodeLabel;
9248
+ exports.flushTooltips = flushTooltips;
9249
+ exports.resetLabelGrid = resetLabelGrid;
9122
9250
  exports.setHoveredNodeKey = setHoveredNodeKey;
9123
9251
  exports.useGraphInstance = useGraphInstance;
9124
9252
  exports.useGraphVisuals = useGraphVisuals;