@designa8/vibedesign-stripe-deepseek-85ea 0.1.2 → 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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @designa8/vibedesign-stripe-deepseek-85ea
2
2
 
3
- Complete design system generated by Atoms Design, version **0.1.2**.
3
+ Complete design system generated by Atoms Design, version **0.1.4**.
4
4
  Tokens, atoms, molecules, organisms, and templates — import from the package root;
5
5
  never hardcode visual values in consumer code.
6
6
 
package/dist/index.cjs CHANGED
@@ -11854,7 +11854,7 @@ var RoutingNode = React60.forwardRef(
11854
11854
  ...rest
11855
11855
  }, ref) => {
11856
11856
  const glyph5 = icon ?? defaultIconByType[type];
11857
- const flagged = selected || active;
11857
+ const highlighted = selected || active;
11858
11858
  return /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(
11859
11859
  "div",
11860
11860
  {
@@ -11863,6 +11863,7 @@ var RoutingNode = React60.forwardRef(
11863
11863
  style: { width: NODE_WIDTH, ...style },
11864
11864
  "data-node-type": type,
11865
11865
  "data-selected": selected || void 0,
11866
+ "data-active": active || void 0,
11866
11867
  ...rest,
11867
11868
  children: [
11868
11869
  /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(
@@ -11871,8 +11872,8 @@ var RoutingNode = React60.forwardRef(
11871
11872
  className: cn52(
11872
11873
  "flex items-stretch overflow-hidden rounded-md border bg-surface",
11873
11874
  "transition duration-150 ease-out",
11874
- disabled ? "border-hairline-soft opacity-50" : unreachable ? "border-dashed border-semantic-warning" : selected || active ? "border-primary ring-2 ring-primary/25" : conflict ? "border-semantic-error/50" : "border-hairline",
11875
- dragging ? "shadow-lg" : flagged ? "shadow-md" : "shadow-sm",
11875
+ disabled ? "border-hairline-soft opacity-50" : selected && unreachable ? "border-dashed border-semantic-warning ring-2 ring-primary" : selected ? "border-primary ring-2 ring-primary" : unreachable ? "border-dashed border-semantic-warning" : active ? "border-primary ring-2 ring-primary/25" : conflict ? "border-semantic-error/50" : "border-hairline",
11876
+ dragging ? "shadow-lg" : highlighted ? "shadow-md" : "shadow-sm",
11876
11877
  disabled ? "" : "group-hover/node:border-hairline-strong group-hover/node:shadow-md"
11877
11878
  ),
11878
11879
  children: [
@@ -11928,7 +11929,7 @@ var RoutingNode = React60.forwardRef(
11928
11929
  "aria-hidden": "true",
11929
11930
  className: cn52(
11930
11931
  "absolute -left-xs top-1/2 size-sm -translate-y-1/2 rounded-full border-2 transition-colors duration-150 ease-out",
11931
- flagged ? "border-primary bg-primary" : "border-hairline-strong bg-surface"
11932
+ highlighted ? "border-primary bg-primary" : "border-hairline-strong bg-surface"
11932
11933
  )
11933
11934
  }
11934
11935
  ),
@@ -11938,7 +11939,7 @@ var RoutingNode = React60.forwardRef(
11938
11939
  "aria-hidden": "true",
11939
11940
  className: cn52(
11940
11941
  "absolute -right-xs top-1/2 size-sm -translate-y-1/2 rounded-full border-2 transition-colors duration-150 ease-out",
11941
- flagged ? "border-primary bg-primary" : "border-hairline-strong bg-surface"
11942
+ highlighted ? "border-primary bg-primary" : "border-hairline-strong bg-surface"
11942
11943
  )
11943
11944
  }
11944
11945
  ),
@@ -11961,7 +11962,7 @@ var RoutingNode = React60.forwardRef(
11961
11962
  size: "inline",
11962
11963
  disabled,
11963
11964
  "aria-label": ariaLabel ?? `${eyebrowByType2[type]}, ${title}`,
11964
- "aria-current": selected || void 0,
11965
+ "aria-pressed": selected,
11965
11966
  onClick: onNodeSelect,
11966
11967
  className: "absolute inset-0 rounded-md focus-visible:ring-focus-blue focus-visible:ring-offset-0",
11967
11968
  children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { className: "sr-only", children: title })
@@ -12106,6 +12107,10 @@ var SCENE_H = 342;
12106
12107
  var PLANE_H = 372;
12107
12108
  var PLANE_PAD = 18;
12108
12109
  var PANEL_W = 296;
12110
+ var SNAP = 8;
12111
+ var MARQUEE_MIN = 4;
12112
+ var clampTo = (value, min, max) => Math.min(Math.max(value, min), max);
12113
+ var boxesIntersect = (a, b) => a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y;
12109
12114
  var DEFAULT_NODES = [
12110
12115
  {
12111
12116
  id: "source",
@@ -12218,6 +12223,7 @@ var DEFAULT_SAMPLES = [
12218
12223
  ]
12219
12224
  }
12220
12225
  ];
12226
+ var DEFAULT_SELECTION = ["brand"];
12221
12227
  var computeIssues = (nodes, edges) => {
12222
12228
  const issues = [];
12223
12229
  const seenFields = /* @__PURE__ */ new Map();
@@ -12256,6 +12262,7 @@ var computeIssues = (nodes, edges) => {
12256
12262
  });
12257
12263
  return issues;
12258
12264
  };
12265
+ var sameIds = (a, b) => a.length === b.length && a.every((id, index) => id === b[index]);
12259
12266
  var PaymentRoutingCanvas = React62.forwardRef(
12260
12267
  ({
12261
12268
  nodes,
@@ -12263,6 +12270,9 @@ var PaymentRoutingCanvas = React62.forwardRef(
12263
12270
  onNodesChange,
12264
12271
  onEdgesChange,
12265
12272
  onNodeSelect,
12273
+ selectedNodeIds,
12274
+ defaultSelectedNodeIds,
12275
+ onSelectionChange,
12266
12276
  sampleTransaction,
12267
12277
  onSimulationStep,
12268
12278
  onValidationChange,
@@ -12273,7 +12283,7 @@ var PaymentRoutingCanvas = React62.forwardRef(
12273
12283
  const graphEdges = edges ?? DEFAULT_EDGES;
12274
12284
  const samples = sampleTransaction ?? DEFAULT_SAMPLES;
12275
12285
  const [localNodes, setLocalNodes] = React62.useState(incomingNodes);
12276
- const [selectedId, setSelectedId] = React62.useState("brand");
12286
+ const [panelNodeId, setPanelNodeId] = React62.useState("brand");
12277
12287
  const [panelOpen, setPanelOpen] = React62.useState(true);
12278
12288
  const [zoom, setZoom] = React62.useState(1);
12279
12289
  const [fit, setFit] = React62.useState(1);
@@ -12291,15 +12301,42 @@ var PaymentRoutingCanvas = React62.forwardRef(
12291
12301
  const [replaying, setReplaying] = React62.useState(false);
12292
12302
  const [focusedId, setFocusedId] = React62.useState(null);
12293
12303
  const [anchor, setAnchor] = React62.useState({ top: 0, right: 16 });
12304
+ const controlledSelection = selectedNodeIds !== void 0;
12305
+ const [internalSelection, setInternalSelection] = React62.useState(
12306
+ () => defaultSelectedNodeIds ?? DEFAULT_SELECTION
12307
+ );
12308
+ const selection = controlledSelection ? selectedNodeIds : internalSelection;
12309
+ const selectionRef = React62.useRef(selection);
12310
+ selectionRef.current = selection;
12311
+ const onSelectionChangeRef = React62.useRef(onSelectionChange);
12312
+ onSelectionChangeRef.current = onSelectionChange;
12313
+ const [marqueeBox, setMarqueeBox] = React62.useState(null);
12314
+ const [marqueeIds, setMarqueeIds] = React62.useState(null);
12315
+ const marqueeRef = React62.useRef(null);
12316
+ const [modifierHeld, setModifierHeld] = React62.useState(false);
12294
12317
  const rootRef = React62.useRef(null);
12295
12318
  const planeRef = React62.useRef(null);
12296
12319
  const triggerRef = React62.useRef(null);
12297
12320
  const nodeRefs = React62.useRef({});
12298
12321
  const suppressClick = React62.useRef(false);
12299
12322
  const nodeDragActive = React62.useRef(false);
12323
+ const nodesRef = React62.useRef(localNodes);
12324
+ nodesRef.current = localNodes;
12300
12325
  React62.useEffect(() => {
12301
12326
  setLocalNodes(incomingNodes);
12302
12327
  }, [incomingNodes]);
12328
+ React62.useEffect(() => {
12329
+ const syncModifier = (event) => setModifierHeld(Boolean(event.metaKey || event.ctrlKey));
12330
+ const releaseModifier = () => setModifierHeld(false);
12331
+ window.addEventListener("keydown", syncModifier);
12332
+ window.addEventListener("keyup", syncModifier);
12333
+ window.addEventListener("blur", releaseModifier);
12334
+ return () => {
12335
+ window.removeEventListener("keydown", syncModifier);
12336
+ window.removeEventListener("keyup", syncModifier);
12337
+ window.removeEventListener("blur", releaseModifier);
12338
+ };
12339
+ }, []);
12303
12340
  React62.useEffect(() => {
12304
12341
  const plane = planeRef.current;
12305
12342
  if (!plane) return;
@@ -12367,56 +12404,127 @@ var PaymentRoutingCanvas = React62.forwardRef(
12367
12404
  x: node.x + (side === "out" ? NODE_W : 0),
12368
12405
  y: node.y + (node.h ?? NODE_H) / 2
12369
12406
  });
12370
- const focusedNode = selectedId ? nodeById.get(selectedId) : void 0;
12407
+ const focusedNode = panelNodeId ? nodeById.get(panelNodeId) : void 0;
12408
+ const selectionSet = React62.useMemo(() => new Set(selection), [selection]);
12409
+ const marqueeSet = React62.useMemo(() => new Set(marqueeIds ?? []), [marqueeIds]);
12410
+ const commitSelection = React62.useCallback(
12411
+ (next) => {
12412
+ const unique = next.filter((id, index) => next.indexOf(id) === index);
12413
+ if (sameIds(unique, selectionRef.current)) return;
12414
+ selectionRef.current = unique;
12415
+ if (!controlledSelection) setInternalSelection(unique);
12416
+ onSelectionChangeRef.current?.(unique);
12417
+ },
12418
+ [controlledSelection]
12419
+ );
12420
+ const openNodePanel = (node) => {
12421
+ commitSelection([node.id]);
12422
+ setPanelNodeId(node.id);
12423
+ setPanelOpen(true);
12424
+ onNodeSelect?.(node);
12425
+ };
12371
12426
  const selectNode = (node) => {
12372
12427
  if (suppressClick.current) {
12373
12428
  suppressClick.current = false;
12374
12429
  return;
12375
12430
  }
12376
- setSelectedId(node.id);
12377
- setPanelOpen(true);
12378
- onNodeSelect?.(node);
12431
+ openNodePanel(node);
12379
12432
  };
12380
12433
  const closePanel = () => {
12381
- const previous = selectedId;
12434
+ const previous = panelNodeId;
12382
12435
  setPanelOpen(false);
12383
- setSelectedId(null);
12436
+ setPanelNodeId(null);
12384
12437
  onNodeSelect?.(null);
12385
12438
  if (previous) nodeRefs.current[previous]?.querySelector("button")?.focus();
12386
12439
  };
12440
+ const clearSelection = () => {
12441
+ commitSelection([]);
12442
+ if (panelOpen) {
12443
+ setPanelOpen(false);
12444
+ setPanelNodeId(null);
12445
+ onNodeSelect?.(null);
12446
+ }
12447
+ };
12448
+ const nudgeSelection = (key) => {
12449
+ const ids = new Set(selectionRef.current);
12450
+ if (ids.size === 0) return;
12451
+ const dx = key === "ArrowLeft" ? -SNAP : key === "ArrowRight" ? SNAP : 0;
12452
+ const dy = key === "ArrowUp" ? -SNAP : key === "ArrowDown" ? SNAP : 0;
12453
+ if (!dx && !dy) return;
12454
+ const next = nodesRef.current.map(
12455
+ (node) => ids.has(node.id) ? { ...node, x: node.x + dx, y: node.y + dy } : node
12456
+ );
12457
+ setLocalNodes(next);
12458
+ onNodesChange?.(next);
12459
+ onEdgesChange?.(graphEdges);
12460
+ };
12387
12461
  const beginDrag = (event, node) => {
12388
12462
  if (event.button !== 0) return;
12389
12463
  event.stopPropagation();
12464
+ suppressClick.current = false;
12390
12465
  nodeDragActive.current = true;
12466
+ const additive = event.metaKey || event.ctrlKey;
12467
+ const current = selectionRef.current;
12468
+ const isIn = current.includes(node.id);
12469
+ let dragIds;
12470
+ let deselectOnClick = false;
12471
+ let openOnClick = false;
12472
+ if (additive) {
12473
+ if (isIn) {
12474
+ dragIds = current;
12475
+ deselectOnClick = true;
12476
+ } else {
12477
+ dragIds = [...current, node.id];
12478
+ commitSelection(dragIds);
12479
+ }
12480
+ } else if (isIn && current.length > 1) {
12481
+ dragIds = current;
12482
+ openOnClick = true;
12483
+ } else {
12484
+ dragIds = [node.id];
12485
+ if (!(current.length === 1 && current[0] === node.id)) commitSelection([node.id]);
12486
+ openOnClick = true;
12487
+ }
12488
+ const dragged = new Set(dragIds);
12489
+ const origins = /* @__PURE__ */ new Map();
12490
+ nodesRef.current.forEach((item) => {
12491
+ if (dragged.has(item.id)) origins.set(item.id, { x: item.x, y: item.y });
12492
+ });
12391
12493
  const startX = event.clientX;
12392
12494
  const startY = event.clientY;
12393
12495
  const scale2 = fit * zoom || 1;
12394
12496
  let moved = false;
12497
+ let finalNodes = null;
12395
12498
  const move = (moveEvent) => {
12396
- const dx = (moveEvent.clientX - startX) / scale2;
12397
- const dy = (moveEvent.clientY - startY) / scale2;
12398
- if (!moved && Math.abs(dx) + Math.abs(dy) < 4) return;
12499
+ const rawX = (moveEvent.clientX - startX) / scale2;
12500
+ const rawY = (moveEvent.clientY - startY) / scale2;
12501
+ if (!moved && Math.abs(rawX) + Math.abs(rawY) < 4) return;
12399
12502
  moved = true;
12400
- setLocalNodes(
12401
- (current) => current.map(
12402
- (item) => item.id === node.id ? {
12403
- ...item,
12404
- x: Math.round((node.x + dx) / 8) * 8,
12405
- y: Math.round((node.y + dy) / 8) * 8
12406
- } : item
12407
- )
12408
- );
12503
+ const dx = Math.round(rawX / SNAP) * SNAP;
12504
+ const dy = Math.round(rawY / SNAP) * SNAP;
12505
+ const next = nodesRef.current.map((item) => {
12506
+ const origin = origins.get(item.id);
12507
+ return origin ? { ...item, x: origin.x + dx, y: origin.y + dy } : item;
12508
+ });
12509
+ finalNodes = next;
12510
+ setLocalNodes(next);
12409
12511
  };
12410
12512
  const end = () => {
12411
12513
  nodeDragActive.current = false;
12412
12514
  window.removeEventListener("pointermove", move);
12413
- if (!moved) return;
12414
12515
  suppressClick.current = true;
12415
- setLocalNodes((current) => {
12416
- onNodesChange?.(current);
12417
- onEdgesChange?.(graphEdges);
12418
- return current;
12419
- });
12516
+ if (moved) {
12517
+ if (finalNodes) {
12518
+ onNodesChange?.(finalNodes);
12519
+ onEdgesChange?.(graphEdges);
12520
+ }
12521
+ return;
12522
+ }
12523
+ if (deselectOnClick) {
12524
+ commitSelection(selectionRef.current.filter((id) => id !== node.id));
12525
+ return;
12526
+ }
12527
+ if (openOnClick) openNodePanel(node);
12420
12528
  };
12421
12529
  window.addEventListener("pointermove", move);
12422
12530
  window.addEventListener("pointerup", end, { once: true });
@@ -12427,16 +12535,106 @@ var PaymentRoutingCanvas = React62.forwardRef(
12427
12535
  const startX = event.clientX;
12428
12536
  const startY = event.clientY;
12429
12537
  const origin = pan;
12538
+ let moved = false;
12430
12539
  const move = (moveEvent) => {
12431
- setPan({
12432
- x: origin.x + (moveEvent.clientX - startX),
12433
- y: origin.y + (moveEvent.clientY - startY)
12434
- });
12540
+ const dx = moveEvent.clientX - startX;
12541
+ const dy = moveEvent.clientY - startY;
12542
+ if (!moved && Math.abs(dx) + Math.abs(dy) < 4) return;
12543
+ moved = true;
12544
+ setPan({ x: origin.x + dx, y: origin.y + dy });
12545
+ };
12546
+ const end = () => {
12547
+ window.removeEventListener("pointermove", move);
12548
+ if (!moved) clearSelection();
12435
12549
  };
12436
- const end = () => window.removeEventListener("pointermove", move);
12437
12550
  window.addEventListener("pointermove", move);
12438
12551
  window.addEventListener("pointerup", end, { once: true });
12439
12552
  };
12553
+ const beginMarquee = (event) => {
12554
+ if (event.button !== 0) return;
12555
+ if (nodeDragActive.current) return;
12556
+ const plane = planeRef.current;
12557
+ if (!plane) return;
12558
+ const box = plane.getBoundingClientRect();
12559
+ if (!box.width || !box.height) return;
12560
+ const gestureScale = fit * zoom || 1;
12561
+ const originX = clampTo(event.clientX - box.left, 0, box.width);
12562
+ const originY = clampTo(event.clientY - box.top, 0, box.height);
12563
+ const gesture = {
12564
+ originX,
12565
+ originY,
12566
+ box: { left: box.left, top: box.top, width: box.width, height: box.height },
12567
+ offsetX: PLANE_PAD + pan.x,
12568
+ offsetY: Math.max((planeHeight - SCENE_H * gestureScale) / 2, 0) + pan.y,
12569
+ scale: gestureScale,
12570
+ base: selectionRef.current,
12571
+ rect: { x: originX, y: originY, w: 0, h: 0 }
12572
+ };
12573
+ marqueeRef.current = gesture;
12574
+ setMarqueeBox(gesture.rect);
12575
+ setMarqueeIds([]);
12576
+ const hitsFor = (rect) => {
12577
+ const scene = {
12578
+ x: (rect.x - gesture.offsetX) / gesture.scale,
12579
+ y: (rect.y - gesture.offsetY) / gesture.scale,
12580
+ w: rect.w / gesture.scale,
12581
+ h: rect.h / gesture.scale
12582
+ };
12583
+ return nodesRef.current.filter(
12584
+ (node) => boxesIntersect(scene, {
12585
+ x: node.x,
12586
+ y: node.y,
12587
+ w: NODE_W,
12588
+ h: node.h ?? NODE_H
12589
+ })
12590
+ ).map((node) => node.id);
12591
+ };
12592
+ const cleanup = () => {
12593
+ window.removeEventListener("pointermove", move);
12594
+ window.removeEventListener("pointerup", end);
12595
+ window.removeEventListener("pointercancel", cancel);
12596
+ window.removeEventListener("keydown", onKeyDown);
12597
+ };
12598
+ const settle = () => {
12599
+ cleanup();
12600
+ marqueeRef.current = null;
12601
+ setMarqueeBox(null);
12602
+ setMarqueeIds(null);
12603
+ };
12604
+ function cancel() {
12605
+ settle();
12606
+ }
12607
+ function onKeyDown(keyEvent) {
12608
+ if (keyEvent.key !== "Escape") return;
12609
+ keyEvent.preventDefault();
12610
+ cancel();
12611
+ }
12612
+ function move(moveEvent) {
12613
+ const x = clampTo(moveEvent.clientX - gesture.box.left, 0, gesture.box.width);
12614
+ const y = clampTo(moveEvent.clientY - gesture.box.top, 0, gesture.box.height);
12615
+ const rect = {
12616
+ x: Math.min(gesture.originX, x),
12617
+ y: Math.min(gesture.originY, y),
12618
+ w: Math.abs(x - gesture.originX),
12619
+ h: Math.abs(y - gesture.originY)
12620
+ };
12621
+ gesture.rect = rect;
12622
+ setMarqueeBox(rect);
12623
+ setMarqueeIds(hitsFor(rect));
12624
+ }
12625
+ function end() {
12626
+ const rect = gesture.rect;
12627
+ settle();
12628
+ if (Math.max(rect.w, rect.h) < MARQUEE_MIN) return;
12629
+ const hits = hitsFor(rect);
12630
+ if (hits.length === 0) return;
12631
+ commitSelection([...gesture.base, ...hits]);
12632
+ }
12633
+ window.addEventListener("pointermove", move);
12634
+ window.addEventListener("pointerup", end);
12635
+ window.addEventListener("pointercancel", cancel);
12636
+ window.addEventListener("keydown", onKeyDown);
12637
+ };
12440
12638
  const moveFocus = (node, key) => {
12441
12639
  const column = localNodes.filter((item) => Math.abs(item.x - node.x) < 1).sort((a, b) => a.y - b.y);
12442
12640
  let target;
@@ -12461,7 +12659,7 @@ var PaymentRoutingCanvas = React62.forwardRef(
12461
12659
  y: Math.min(Math.max(node.y / SCENE_H * 100 + 2, 2), 82),
12462
12660
  w: 21,
12463
12661
  h: 18,
12464
- highlighted: node.id === selectedId || traversed.has(node.id)
12662
+ highlighted: selectionSet.has(node.id) || traversed.has(node.id)
12465
12663
  }));
12466
12664
  const issueByNode = (nodeId) => issues.filter((issue) => issue.nodeId === nodeId);
12467
12665
  const currentStep = steps[stepIndex];
@@ -12553,12 +12751,30 @@ var PaymentRoutingCanvas = React62.forwardRef(
12553
12751
  ref: planeRef,
12554
12752
  role: "application",
12555
12753
  "aria-label": "Payment routing graph",
12754
+ "aria-describedby": "routing-selection-status",
12556
12755
  tabIndex: 0,
12557
- onPointerDown: beginPan,
12756
+ onPointerDown: (event) => {
12757
+ if (event.metaKey || event.ctrlKey) beginMarquee(event);
12758
+ else beginPan(event);
12759
+ },
12558
12760
  onKeyDown: (event) => {
12761
+ const modifier = event.metaKey || event.ctrlKey;
12762
+ if (modifier && (event.key === "a" || event.key === "A")) {
12763
+ event.preventDefault();
12764
+ commitSelection(localNodes.map((item) => item.id));
12765
+ return;
12766
+ }
12559
12767
  if (event.key === "Escape") {
12768
+ event.preventDefault();
12769
+ if (marqueeRef.current) return;
12560
12770
  setIssuesOpen(false);
12561
- if (panelOpen) closePanel();
12771
+ clearSelection();
12772
+ return;
12773
+ }
12774
+ if (event.shiftKey && (event.key === "ArrowUp" || event.key === "ArrowDown" || event.key === "ArrowLeft" || event.key === "ArrowRight")) {
12775
+ event.preventDefault();
12776
+ nudgeSelection(event.key);
12777
+ return;
12562
12778
  }
12563
12779
  if (event.key === "+" || event.key === "=") {
12564
12780
  setZoom((current) => Math.min(current + 0.1, 1.6));
@@ -12572,7 +12788,9 @@ var PaymentRoutingCanvas = React62.forwardRef(
12572
12788
  }
12573
12789
  },
12574
12790
  className: cn54(
12575
- "relative min-h-0 min-w-0 flex-1 cursor-grab overflow-hidden bg-canvas",
12791
+ "relative min-h-0 min-w-0 flex-1 overflow-hidden bg-canvas",
12792
+ // Held Cmd / Ctrl over the background means "drag to select".
12793
+ modifierHeld || marqueeBox ? "cursor-crosshair" : "cursor-grab",
12576
12794
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-focus-blue focus-visible:ring-inset"
12577
12795
  ),
12578
12796
  style: {
@@ -12583,6 +12801,7 @@ var PaymentRoutingCanvas = React62.forwardRef(
12583
12801
  backgroundSize: "16px 16px"
12584
12802
  },
12585
12803
  children: [
12804
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("p", { id: "routing-selection-status", role: "status", "aria-live": "polite", className: "sr-only", children: selection.length === 0 ? "No nodes selected" : `${selection.length} node${selection.length === 1 ? "" : "s"} selected` }),
12586
12805
  /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)(
12587
12806
  "div",
12588
12807
  {
@@ -12627,6 +12846,7 @@ var PaymentRoutingCanvas = React62.forwardRef(
12627
12846
  (issue) => issue.kind === "no-fallback"
12628
12847
  );
12629
12848
  const active = traversed.has(node.id);
12849
+ const isSelected = selectionSet.has(node.id) || marqueeSet.has(node.id);
12630
12850
  return /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
12631
12851
  "div",
12632
12852
  {
@@ -12644,31 +12864,35 @@ var PaymentRoutingCanvas = React62.forwardRef(
12644
12864
  metric: node.metric,
12645
12865
  trailing: node.trailing,
12646
12866
  icon: node.icon,
12647
- selected: selectedId === node.id,
12867
+ selected: isSelected,
12648
12868
  active: active && !unreachable,
12649
12869
  unreachable,
12650
12870
  noFallback,
12651
12871
  conflict: conflict ? "Conflicts with Priority 2" : void 0,
12652
12872
  disabled: node.disabled,
12653
- showHint: focusedId === node.id && selectedId === node.id,
12654
- ariaLabel: `${node.type}, ${node.title}${node.metric ? `, ${node.metric}` : ""}${unreachable ? ", unreachable" : ""}`,
12873
+ showHint: focusedId === node.id && isSelected,
12874
+ ariaLabel: `${node.type}, ${node.title}${node.metric ? `, ${node.metric}` : ""}${isSelected ? ", selected" : ""}${unreachable ? ", unreachable" : ""}`,
12655
12875
  onNodeSelect: () => selectNode(node),
12656
12876
  onFocus: () => setFocusedId(node.id),
12657
12877
  onKeyDown: (event) => {
12878
+ if (event.shiftKey && (event.key === "ArrowUp" || event.key === "ArrowDown" || event.key === "ArrowLeft" || event.key === "ArrowRight")) {
12879
+ return;
12880
+ }
12658
12881
  if (event.key === "ArrowUp" || event.key === "ArrowDown" || event.key === "ArrowLeft" || event.key === "ArrowRight") {
12659
12882
  event.preventDefault();
12660
12883
  moveFocus(node, event.key);
12884
+ return;
12661
12885
  }
12662
- if (event.key === "Enter" || event.key === " ") {
12886
+ if (event.key === " " || event.shiftKey && event.key === "Enter") {
12663
12887
  event.preventDefault();
12664
- setSelectedId(node.id);
12665
- setPanelOpen(true);
12666
- onNodeSelect?.(node);
12888
+ commitSelection(
12889
+ selectionRef.current.includes(node.id) ? selectionRef.current.filter((id) => id !== node.id) : [...selectionRef.current, node.id]
12890
+ );
12891
+ return;
12667
12892
  }
12668
- if (event.key === "Escape") {
12893
+ if (event.key === "Enter") {
12669
12894
  event.preventDefault();
12670
- setIssuesOpen(false);
12671
- if (panelOpen) closePanel();
12895
+ openNodePanel(node);
12672
12896
  }
12673
12897
  }
12674
12898
  }
@@ -12680,6 +12904,25 @@ var PaymentRoutingCanvas = React62.forwardRef(
12680
12904
  ]
12681
12905
  }
12682
12906
  ),
12907
+ /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
12908
+ "div",
12909
+ {
12910
+ "aria-hidden": "true",
12911
+ "data-state": marqueeBox ? "active" : "idle",
12912
+ className: cn54(
12913
+ "pointer-events-none absolute rounded-xs border border-dashed border-primary bg-primary/10",
12914
+ "transition-opacity duration-150 ease-out",
12915
+ marqueeBox ? "opacity-100" : "opacity-0"
12916
+ ),
12917
+ style: {
12918
+ left: marqueeBox?.x ?? 0,
12919
+ top: marqueeBox?.y ?? 0,
12920
+ width: marqueeBox?.w ?? 0,
12921
+ height: marqueeBox?.h ?? 0,
12922
+ zIndex: "var(--z-sticky, 1100)"
12923
+ }
12924
+ }
12925
+ ),
12683
12926
  /* @__PURE__ */ (0, import_jsx_runtime62.jsx)("ol", { className: "sr-only", children: graphEdges.map((edge) => {
12684
12927
  const from = nodeById.get(edge.from);
12685
12928
  const to = nodeById.get(edge.to);
@@ -12704,8 +12947,7 @@ var PaymentRoutingCanvas = React62.forwardRef(
12704
12947
  onJump: (id) => {
12705
12948
  const target = localNodes.find((node) => node.title === id);
12706
12949
  if (!target) return;
12707
- setSelectedId(target.id);
12708
- setPanelOpen(true);
12950
+ openNodePanel(target);
12709
12951
  setPan({ x: PLANE_PAD - target.x, y: -target.y });
12710
12952
  }
12711
12953
  }