@gravity-ui/page-constructor 8.15.0 → 8.17.0

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.
Files changed (129) hide show
  1. package/README.md +31 -9
  2. package/build/cjs/constructor-items.d.ts +1 -1
  3. package/build/cjs/demo/ComponentMap/ComponentMap.css +55 -0
  4. package/build/cjs/demo/ComponentMap/ComponentMap.d.ts +6 -0
  5. package/build/cjs/demo/ComponentMap/ComponentMap.js +286 -0
  6. package/build/cjs/demo/ComponentMap/ComponentMap.js.map +1 -0
  7. package/build/cjs/demo/ComponentMap/draw.d.ts +23 -0
  8. package/build/cjs/demo/ComponentMap/draw.js +134 -0
  9. package/build/cjs/demo/ComponentMap/draw.js.map +1 -0
  10. package/build/cjs/demo/ComponentMap/edgeGeometry.d.ts +20 -0
  11. package/build/cjs/demo/ComponentMap/edgeGeometry.js +146 -0
  12. package/build/cjs/demo/ComponentMap/edgeGeometry.js.map +1 -0
  13. package/build/cjs/demo/ComponentMap/graph.d.ts +9 -0
  14. package/build/cjs/demo/ComponentMap/graph.js +68 -0
  15. package/build/cjs/demo/ComponentMap/graph.js.map +1 -0
  16. package/build/cjs/demo/ComponentMap/layout.d.ts +12 -0
  17. package/build/cjs/demo/ComponentMap/layout.js +149 -0
  18. package/build/cjs/demo/ComponentMap/layout.js.map +1 -0
  19. package/build/cjs/demo/ComponentMap/model.d.ts +55 -0
  20. package/build/cjs/demo/ComponentMap/model.js +3 -0
  21. package/build/cjs/demo/ComponentMap/model.js.map +1 -0
  22. package/build/cjs/demo/ComponentMap/viewport.d.ts +7 -0
  23. package/build/cjs/demo/ComponentMap/viewport.js +41 -0
  24. package/build/cjs/demo/ComponentMap/viewport.js.map +1 -0
  25. package/build/cjs/demo/HeroAndMedia.stories.js +12 -23
  26. package/build/cjs/demo/HeroAndMedia.stories.js.map +1 -1
  27. package/build/cjs/demo/TokenizationHeader.stories.d.ts +13 -0
  28. package/build/cjs/demo/TokenizationHeader.stories.js +47 -0
  29. package/build/cjs/demo/TokenizationHeader.stories.js.map +1 -0
  30. package/build/cjs/demo/TokenizationHero.stories.d.ts +9 -0
  31. package/build/cjs/demo/TokenizationHero.stories.js +20 -0
  32. package/build/cjs/demo/TokenizationHero.stories.js.map +1 -0
  33. package/build/cjs/demo/tokenizationLanding.d.ts +2 -0
  34. package/build/cjs/demo/tokenizationLanding.js +21 -0
  35. package/build/cjs/demo/tokenizationLanding.js.map +1 -0
  36. package/build/cjs/models/constructor-items/sub-blocks.d.ts +3 -0
  37. package/build/cjs/models/constructor-items/sub-blocks.js.map +1 -1
  38. package/build/cjs/sub-blocks/LayoutItem/LayoutItem.css +15 -0
  39. package/build/cjs/sub-blocks/LayoutItem/LayoutItem.d.ts +1 -1
  40. package/build/cjs/sub-blocks/LayoutItem/LayoutItem.js +12 -2
  41. package/build/cjs/sub-blocks/LayoutItem/LayoutItem.js.map +1 -1
  42. package/build/cjs/sub-blocks/LayoutItem/schema.d.ts +10 -0
  43. package/build/cjs/sub-blocks/LayoutItem/schema.js +10 -0
  44. package/build/cjs/sub-blocks/LayoutItem/schema.js.map +1 -1
  45. package/build/docs/INDEX.md +191 -0
  46. package/build/docs/blocks/Banner.md +59 -0
  47. package/build/docs/blocks/CardLayout.md +79 -0
  48. package/build/docs/blocks/Companies.md +67 -0
  49. package/build/docs/blocks/ContentLayout.md +54 -0
  50. package/build/docs/blocks/ExtendedFeatures.md +74 -0
  51. package/build/docs/blocks/FilterBlock.md +112 -0
  52. package/build/docs/blocks/FoldableList.md +78 -0
  53. package/build/docs/blocks/Footer.md +110 -0
  54. package/build/docs/blocks/Form.md +89 -0
  55. package/build/docs/blocks/Header.md +70 -0
  56. package/build/docs/blocks/HeaderSlider.md +70 -0
  57. package/build/docs/blocks/Hero.md +67 -0
  58. package/build/docs/blocks/Icons.md +61 -0
  59. package/build/docs/blocks/Info.md +73 -0
  60. package/build/docs/blocks/Map.md +81 -0
  61. package/build/docs/blocks/Media.md +63 -0
  62. package/build/docs/blocks/PromoFeaturesBlock.md +74 -0
  63. package/build/docs/blocks/Questions.md +78 -0
  64. package/build/docs/blocks/Share.md +45 -0
  65. package/build/docs/blocks/Slider.md +89 -0
  66. package/build/docs/blocks/SliderOld.md +63 -0
  67. package/build/docs/blocks/Table.md +64 -0
  68. package/build/docs/blocks/Tabs.md +92 -0
  69. package/build/docs/guides/data-preparation-ru.md +257 -0
  70. package/build/docs/guides/data-preparation.md +259 -0
  71. package/build/docs/sub-blocks/BackgroundCard.md +63 -0
  72. package/build/docs/sub-blocks/BannerCard.md +57 -0
  73. package/build/docs/sub-blocks/BasicCard.md +60 -0
  74. package/build/docs/sub-blocks/Content.md +58 -0
  75. package/build/docs/sub-blocks/Divider.md +47 -0
  76. package/build/docs/sub-blocks/HubspotForm.md +47 -0
  77. package/build/docs/sub-blocks/ImageCard.md +59 -0
  78. package/build/docs/sub-blocks/LayoutItem.md +75 -0
  79. package/build/docs/sub-blocks/MediaCard.md +66 -0
  80. package/build/docs/sub-blocks/PriceCard.md +60 -0
  81. package/build/docs/sub-blocks/PriceDetailed.md +74 -0
  82. package/build/docs/sub-blocks/Quote.md +74 -0
  83. package/build/esm/constructor-items.d.ts +1 -1
  84. package/build/esm/demo/ComponentMap/ComponentMap.css +55 -0
  85. package/build/esm/demo/ComponentMap/ComponentMap.d.ts +7 -0
  86. package/build/esm/demo/ComponentMap/ComponentMap.js +283 -0
  87. package/build/esm/demo/ComponentMap/ComponentMap.js.map +1 -0
  88. package/build/esm/demo/ComponentMap/draw.d.ts +23 -0
  89. package/build/esm/demo/ComponentMap/draw.js +130 -0
  90. package/build/esm/demo/ComponentMap/draw.js.map +1 -0
  91. package/build/esm/demo/ComponentMap/edgeGeometry.d.ts +20 -0
  92. package/build/esm/demo/ComponentMap/edgeGeometry.js +136 -0
  93. package/build/esm/demo/ComponentMap/edgeGeometry.js.map +1 -0
  94. package/build/esm/demo/ComponentMap/graph.d.ts +9 -0
  95. package/build/esm/demo/ComponentMap/graph.js +62 -0
  96. package/build/esm/demo/ComponentMap/graph.js.map +1 -0
  97. package/build/esm/demo/ComponentMap/layout.d.ts +12 -0
  98. package/build/esm/demo/ComponentMap/layout.js +144 -0
  99. package/build/esm/demo/ComponentMap/layout.js.map +1 -0
  100. package/build/esm/demo/ComponentMap/model.d.ts +55 -0
  101. package/build/esm/demo/ComponentMap/model.js +2 -0
  102. package/build/esm/demo/ComponentMap/model.js.map +1 -0
  103. package/build/esm/demo/ComponentMap/viewport.d.ts +7 -0
  104. package/build/esm/demo/ComponentMap/viewport.js +34 -0
  105. package/build/esm/demo/ComponentMap/viewport.js.map +1 -0
  106. package/build/esm/demo/HeroAndMedia.stories.js +1 -12
  107. package/build/esm/demo/HeroAndMedia.stories.js.map +1 -1
  108. package/build/esm/demo/TokenizationHeader.stories.d.ts +13 -0
  109. package/build/esm/demo/TokenizationHeader.stories.js +43 -0
  110. package/build/esm/demo/TokenizationHeader.stories.js.map +1 -0
  111. package/build/esm/demo/TokenizationHero.stories.d.ts +9 -0
  112. package/build/esm/demo/TokenizationHero.stories.js +16 -0
  113. package/build/esm/demo/TokenizationHero.stories.js.map +1 -0
  114. package/build/esm/demo/tokenizationLanding.d.ts +2 -0
  115. package/build/esm/demo/tokenizationLanding.js +16 -0
  116. package/build/esm/demo/tokenizationLanding.js.map +1 -0
  117. package/build/esm/models/constructor-items/sub-blocks.d.ts +3 -0
  118. package/build/esm/models/constructor-items/sub-blocks.js.map +1 -1
  119. package/build/esm/sub-blocks/LayoutItem/LayoutItem.css +15 -0
  120. package/build/esm/sub-blocks/LayoutItem/LayoutItem.d.ts +1 -1
  121. package/build/esm/sub-blocks/LayoutItem/LayoutItem.js +14 -4
  122. package/build/esm/sub-blocks/LayoutItem/LayoutItem.js.map +1 -1
  123. package/build/esm/sub-blocks/LayoutItem/schema.d.ts +10 -0
  124. package/build/esm/sub-blocks/LayoutItem/schema.js +10 -0
  125. package/build/esm/sub-blocks/LayoutItem/schema.js.map +1 -1
  126. package/package.json +4 -2
  127. package/schema/index.js +1 -1
  128. package/server/models/constructor-items/sub-blocks.d.ts +3 -0
  129. package/widget/index.js +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"draw.js","sourceRoot":"../../../../src","sources":["demo/ComponentMap/draw.ts"],"names":[],"mappings":"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","sourcesContent":["import {getEdgeGeometry, orderEdgesForInteraction} from './edgeGeometry';\nimport {getEdgeKey} from './graph';\nimport {NODE_LABEL_FONT} from './layout';\nimport type {\n ComponentMapEmphasis,\n ComponentMapLayout,\n LayoutEdge,\n LayoutNode,\n Point,\n ViewportSize,\n ViewportTransform,\n} from './model';\n\nexport const PALETTE = {\n bandHeading: '#4d5562',\n edge: '#7d8798',\n label: '#1f2733',\n nodeFill: {\n block: '#d9e8ff',\n 'sub-block': '#e5ddff',\n component: '#e4ebf2',\n },\n nodeOutline: '#748094',\n selectedFill: '#ffbe5c',\n selectedOutline: '#9a5700',\n} as const;\n\nconst NODE_RADIUS = 10;\nconst NODE_DIMMED_OPACITY = 0.16;\nconst EDGE_DIMMED_OPACITY = 0.1;\nconst EDGE_WIDTH = 1.5;\nconst INTERACTIVE_EDGE_WIDTH = 3;\nconst INTERACTIVE_EDGE_COLOR = '#ffbe5c';\n\nexport interface ComponentMapDrawOptions {\n viewport: ViewportSize;\n devicePixelRatio: number;\n selectedNodeId?: string;\n hoveredEdgeKey?: string;\n selectedEdgeKeys: ReadonlySet<string>;\n emphasis: ComponentMapEmphasis;\n}\n\ninterface DrawEdgeOptions {\n active: boolean;\n hovered: boolean;\n selected: boolean;\n}\n\nfunction roundedRect(\n context: CanvasRenderingContext2D,\n x: number,\n y: number,\n width: number,\n height: number,\n radius: number,\n) {\n const right = x + width;\n const bottom = y + height;\n\n context.beginPath();\n context.moveTo(x + radius, y);\n context.lineTo(right - radius, y);\n context.quadraticCurveTo(right, y, right, y + radius);\n context.lineTo(right, bottom - radius);\n context.quadraticCurveTo(right, bottom, right - radius, bottom);\n context.lineTo(x + radius, bottom);\n context.quadraticCurveTo(x, bottom, x, bottom - radius);\n context.lineTo(x, y + radius);\n context.quadraticCurveTo(x, y, x + radius, y);\n context.closePath();\n}\n\nfunction drawArrowhead(context: CanvasRenderingContext2D, arrowhead: [Point, Point, Point]) {\n context.beginPath();\n context.moveTo(arrowhead[0].x, arrowhead[0].y);\n context.lineTo(arrowhead[1].x, arrowhead[1].y);\n context.lineTo(arrowhead[2].x, arrowhead[2].y);\n context.closePath();\n context.fill();\n}\n\nfunction drawEdge(\n canvasContext: CanvasRenderingContext2D,\n source: LayoutNode,\n target: LayoutNode,\n {active, hovered, selected}: DrawEdgeOptions,\n) {\n const context = canvasContext;\n const {start, end, firstControl, secondControl, arrowhead} = getEdgeGeometry(source, target);\n const color = selected || hovered ? INTERACTIVE_EDGE_COLOR : PALETTE.edge;\n\n context.save();\n context.globalAlpha = selected || active ? 1 : EDGE_DIMMED_OPACITY;\n context.strokeStyle = color;\n context.fillStyle = color;\n context.lineWidth = selected || hovered ? INTERACTIVE_EDGE_WIDTH : EDGE_WIDTH;\n context.beginPath();\n context.moveTo(start.x, start.y);\n context.bezierCurveTo(\n firstControl.x,\n firstControl.y,\n secondControl.x,\n secondControl.y,\n end.x,\n end.y,\n );\n context.stroke();\n drawArrowhead(context, arrowhead);\n context.restore();\n}\n\nfunction drawNode(\n canvasContext: CanvasRenderingContext2D,\n node: LayoutNode,\n active: boolean,\n selected: boolean,\n) {\n const context = canvasContext;\n const {x, y, width, height} = node.rect;\n\n context.save();\n context.globalAlpha = active ? 1 : NODE_DIMMED_OPACITY;\n context.fillStyle = selected ? PALETTE.selectedFill : PALETTE.nodeFill[node.kind];\n context.strokeStyle = selected ? PALETTE.selectedOutline : PALETTE.nodeOutline;\n context.lineWidth = selected ? 3 : 1.5;\n roundedRect(context, x, y, width, height, NODE_RADIUS);\n context.fill();\n context.stroke();\n context.restore();\n}\n\nfunction drawLabel(canvasContext: CanvasRenderingContext2D, node: LayoutNode, active: boolean) {\n const context = canvasContext;\n const {x, y, width, height} = node.rect;\n\n context.save();\n context.globalAlpha = active ? 1 : NODE_DIMMED_OPACITY;\n context.fillStyle = PALETTE.label;\n context.font = NODE_LABEL_FONT;\n context.textAlign = 'center';\n context.textBaseline = 'middle';\n context.fillText(node.label, x + width / 2, y + height / 2);\n context.restore();\n}\n\nexport function drawComponentMap(\n canvasContext: CanvasRenderingContext2D,\n layout: ComponentMapLayout,\n transform: ViewportTransform,\n options: ComponentMapDrawOptions,\n) {\n const context = canvasContext;\n const {viewport, devicePixelRatio, selectedNodeId, hoveredEdgeKey, selectedEdgeKeys, emphasis} =\n options;\n const hasEmphasis = emphasis.mode !== 'none';\n\n context.resetTransform();\n context.scale(devicePixelRatio, devicePixelRatio);\n context.clearRect(0, 0, viewport.width, viewport.height);\n context.translate(transform.x, transform.y);\n context.scale(transform.scale, transform.scale);\n\n context.save();\n context.fillStyle = PALETTE.bandHeading;\n context.font = '700 18px sans-serif';\n context.textAlign = 'left';\n context.textBaseline = 'top';\n for (const band of layout.bands) {\n context.fillText(band.label, band.bounds.x, band.bounds.y);\n }\n context.restore();\n\n const edgeState = ({source, target}: LayoutEdge) => {\n const key = getEdgeKey(source.id, target.id);\n return {\n key,\n selected: selectedEdgeKeys.has(key),\n hovered: key === hoveredEdgeKey,\n };\n };\n const orderedEdges = orderEdgesForInteraction(layout.edges, hoveredEdgeKey, selectedEdgeKeys);\n\n for (const edge of orderedEdges) {\n const {source, target} = edge;\n const {key, selected, hovered} = edgeState(edge);\n const active =\n emphasis.mode === 'none' ||\n (emphasis.mode === 'selection' && emphasis.activeEdgeIds.has(key));\n drawEdge(context, source, target, {active, hovered, selected});\n }\n\n for (const node of layout.nodes) {\n const active = !hasEmphasis || emphasis.activeNodeIds.has(node.id);\n drawNode(\n context,\n node,\n active,\n emphasis.mode === 'selection' && node.id === selectedNodeId,\n );\n }\n\n for (const node of layout.nodes) {\n const active = !hasEmphasis || emphasis.activeNodeIds.has(node.id);\n drawLabel(context, node, active);\n }\n}\n"]}
@@ -0,0 +1,20 @@
1
+ import type { LayoutEdge, LayoutNode, Point } from "./model.js";
2
+ export declare const ARROW_SIZE = 12;
3
+ export declare const EDGE_CURVE_SEGMENTS = 24;
4
+ export declare const EDGE_HIT_TOLERANCE = 6;
5
+ export declare const SAME_GROUP_ARC_MIN_OUTSET = 64;
6
+ export declare const SAME_GROUP_ARC_SPAN_FACTOR = 0.25;
7
+ export interface EdgeGeometry {
8
+ start: Point;
9
+ end: Point;
10
+ firstControl: Point;
11
+ secondControl: Point;
12
+ arrowhead: [Point, Point, Point];
13
+ }
14
+ export declare function getSameGroupArcOutset(source: LayoutNode, target: LayoutNode): number;
15
+ export declare function getEdgeGeometry(source: LayoutNode, target: LayoutNode): EdgeGeometry;
16
+ export declare function getCubicBezierPoint(geometry: EdgeGeometry, ratio: number): Point;
17
+ export declare function getEdgeDistance(point: Point, geometry: EdgeGeometry): number;
18
+ export declare const getEdgeHitTolerance: (scale: number) => number;
19
+ export declare function orderEdgesForInteraction(edges: LayoutEdge[], hoveredEdgeKey: string | undefined, selectedEdgeKeys: ReadonlySet<string>): LayoutEdge[];
20
+ export declare function hitTestEdge(edges: LayoutEdge[], point: Point, tolerance: number): LayoutEdge | undefined;
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getEdgeHitTolerance = exports.SAME_GROUP_ARC_SPAN_FACTOR = exports.SAME_GROUP_ARC_MIN_OUTSET = exports.EDGE_HIT_TOLERANCE = exports.EDGE_CURVE_SEGMENTS = exports.ARROW_SIZE = void 0;
4
+ exports.getSameGroupArcOutset = getSameGroupArcOutset;
5
+ exports.getEdgeGeometry = getEdgeGeometry;
6
+ exports.getCubicBezierPoint = getCubicBezierPoint;
7
+ exports.getEdgeDistance = getEdgeDistance;
8
+ exports.orderEdgesForInteraction = orderEdgesForInteraction;
9
+ exports.hitTestEdge = hitTestEdge;
10
+ const graph_1 = require("./graph.js");
11
+ exports.ARROW_SIZE = 12;
12
+ exports.EDGE_CURVE_SEGMENTS = 24;
13
+ exports.EDGE_HIT_TOLERANCE = 6;
14
+ exports.SAME_GROUP_ARC_MIN_OUTSET = 64;
15
+ exports.SAME_GROUP_ARC_SPAN_FACTOR = 0.25;
16
+ const distance = (left, right) => Math.hypot(left.x - right.x, left.y - right.y);
17
+ const getNodeCenter = (node) => ({
18
+ x: node.rect.x + node.rect.width / 2,
19
+ y: node.rect.y + node.rect.height / 2,
20
+ });
21
+ function getSameGroupArcOutset(source, target) {
22
+ const sourceCenter = getNodeCenter(source);
23
+ const targetCenter = getNodeCenter(target);
24
+ return Math.max(exports.SAME_GROUP_ARC_MIN_OUTSET, Math.abs(targetCenter.y - sourceCenter.y) * exports.SAME_GROUP_ARC_SPAN_FACTOR);
25
+ }
26
+ function distanceToSegment(point, start, end) {
27
+ const deltaX = end.x - start.x;
28
+ const deltaY = end.y - start.y;
29
+ const lengthSquared = deltaX * deltaX + deltaY * deltaY;
30
+ if (lengthSquared === 0) {
31
+ return distance(point, start);
32
+ }
33
+ const ratio = Math.max(0, Math.min(1, ((point.x - start.x) * deltaX + (point.y - start.y) * deltaY) / lengthSquared));
34
+ return distance(point, { x: start.x + ratio * deltaX, y: start.y + ratio * deltaY });
35
+ }
36
+ function triangleSign(point, first, second) {
37
+ return ((point.x - second.x) * (first.y - second.y) - (first.x - second.x) * (point.y - second.y));
38
+ }
39
+ function isInsideTriangle(point, [first, second, third]) {
40
+ const firstSign = triangleSign(point, first, second);
41
+ const secondSign = triangleSign(point, second, third);
42
+ const thirdSign = triangleSign(point, third, first);
43
+ const hasNegative = firstSign < 0 || secondSign < 0 || thirdSign < 0;
44
+ const hasPositive = firstSign > 0 || secondSign > 0 || thirdSign > 0;
45
+ return !(hasNegative && hasPositive);
46
+ }
47
+ function getEdgeGeometry(source, target) {
48
+ const sourceCenter = getNodeCenter(source);
49
+ const targetCenter = getNodeCenter(target);
50
+ let start;
51
+ let end;
52
+ let firstControl;
53
+ let secondControl;
54
+ if (source.kind === target.kind) {
55
+ start = { x: source.rect.x + source.rect.width, y: sourceCenter.y };
56
+ end = { x: target.rect.x + target.rect.width, y: targetCenter.y };
57
+ const arcX = Math.max(start.x, end.x) + getSameGroupArcOutset(source, target);
58
+ firstControl = { x: arcX, y: start.y };
59
+ secondControl = { x: arcX, y: end.y };
60
+ }
61
+ else {
62
+ const direction = targetCenter.x >= sourceCenter.x ? 1 : -1;
63
+ start = {
64
+ x: sourceCenter.x + (source.rect.width / 2) * direction,
65
+ y: sourceCenter.y,
66
+ };
67
+ end = {
68
+ x: targetCenter.x - (target.rect.width / 2) * direction,
69
+ y: targetCenter.y,
70
+ };
71
+ const controlX = (start.x + end.x) / 2;
72
+ firstControl = { x: controlX, y: start.y };
73
+ secondControl = { x: controlX, y: end.y };
74
+ }
75
+ const angle = Math.atan2(end.y - secondControl.y, end.x - secondControl.x);
76
+ const arrowhead = [
77
+ end,
78
+ {
79
+ x: end.x - exports.ARROW_SIZE * Math.cos(angle - Math.PI / 6),
80
+ y: end.y - exports.ARROW_SIZE * Math.sin(angle - Math.PI / 6),
81
+ },
82
+ {
83
+ x: end.x - exports.ARROW_SIZE * Math.cos(angle + Math.PI / 6),
84
+ y: end.y - exports.ARROW_SIZE * Math.sin(angle + Math.PI / 6),
85
+ },
86
+ ];
87
+ return { start, end, firstControl, secondControl, arrowhead };
88
+ }
89
+ function getCubicBezierPoint(geometry, ratio) {
90
+ const inverse = 1 - ratio;
91
+ return {
92
+ x: inverse ** 3 * geometry.start.x +
93
+ 3 * inverse ** 2 * ratio * geometry.firstControl.x +
94
+ 3 * inverse * ratio ** 2 * geometry.secondControl.x +
95
+ ratio ** 3 * geometry.end.x,
96
+ y: inverse ** 3 * geometry.start.y +
97
+ 3 * inverse ** 2 * ratio * geometry.firstControl.y +
98
+ 3 * inverse * ratio ** 2 * geometry.secondControl.y +
99
+ ratio ** 3 * geometry.end.y,
100
+ };
101
+ }
102
+ function getEdgeDistance(point, geometry) {
103
+ if (isInsideTriangle(point, geometry.arrowhead)) {
104
+ return 0;
105
+ }
106
+ let minimum = Number.POSITIVE_INFINITY;
107
+ let previous = geometry.start;
108
+ for (let index = 1; index <= exports.EDGE_CURVE_SEGMENTS; index += 1) {
109
+ const current = getCubicBezierPoint(geometry, index / exports.EDGE_CURVE_SEGMENTS);
110
+ minimum = Math.min(minimum, distanceToSegment(point, previous, current));
111
+ previous = current;
112
+ }
113
+ for (let index = 0; index < geometry.arrowhead.length; index += 1) {
114
+ minimum = Math.min(minimum, distanceToSegment(point, geometry.arrowhead[index], geometry.arrowhead[(index + 1) % geometry.arrowhead.length]));
115
+ }
116
+ return minimum;
117
+ }
118
+ const getEdgeHitTolerance = (scale) => exports.EDGE_HIT_TOLERANCE / Math.max(scale, Number.EPSILON);
119
+ exports.getEdgeHitTolerance = getEdgeHitTolerance;
120
+ function orderEdgesForInteraction(edges, hoveredEdgeKey, selectedEdgeKeys) {
121
+ const priority = (edge) => {
122
+ const key = (0, graph_1.getEdgeKey)(edge.source.id, edge.target.id);
123
+ if (selectedEdgeKeys.has(key)) {
124
+ return 2;
125
+ }
126
+ return key === hoveredEdgeKey ? 1 : 0;
127
+ };
128
+ return edges
129
+ .map((edge, index) => ({ edge, index, priority: priority(edge) }))
130
+ .sort((left, right) => left.priority - right.priority || left.index - right.index)
131
+ .map(({ edge }) => edge);
132
+ }
133
+ function hitTestEdge(edges, point, tolerance) {
134
+ let closest;
135
+ let closestDistance = Number.POSITIVE_INFINITY;
136
+ for (let index = edges.length - 1; index >= 0; index -= 1) {
137
+ const edge = edges[index];
138
+ const edgeDistance = getEdgeDistance(point, getEdgeGeometry(edge.source, edge.target));
139
+ if (edgeDistance <= tolerance && (!closest || edgeDistance < closestDistance)) {
140
+ closest = edge;
141
+ closestDistance = edgeDistance;
142
+ }
143
+ }
144
+ return closest;
145
+ }
146
+ //# sourceMappingURL=edgeGeometry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edgeGeometry.js","sourceRoot":"../../../../src","sources":["demo/ComponentMap/edgeGeometry.ts"],"names":[],"mappings":"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","sourcesContent":["import {getEdgeKey} from './graph';\nimport type {LayoutEdge, LayoutNode, Point} from './model';\n\nexport const ARROW_SIZE = 12;\nexport const EDGE_CURVE_SEGMENTS = 24;\nexport const EDGE_HIT_TOLERANCE = 6;\nexport const SAME_GROUP_ARC_MIN_OUTSET = 64;\nexport const SAME_GROUP_ARC_SPAN_FACTOR = 0.25;\n\nexport interface EdgeGeometry {\n start: Point;\n end: Point;\n firstControl: Point;\n secondControl: Point;\n arrowhead: [Point, Point, Point];\n}\n\nconst distance = (left: Point, right: Point) => Math.hypot(left.x - right.x, left.y - right.y);\n\nconst getNodeCenter = (node: LayoutNode): Point => ({\n x: node.rect.x + node.rect.width / 2,\n y: node.rect.y + node.rect.height / 2,\n});\n\nexport function getSameGroupArcOutset(source: LayoutNode, target: LayoutNode): number {\n const sourceCenter = getNodeCenter(source);\n const targetCenter = getNodeCenter(target);\n return Math.max(\n SAME_GROUP_ARC_MIN_OUTSET,\n Math.abs(targetCenter.y - sourceCenter.y) * SAME_GROUP_ARC_SPAN_FACTOR,\n );\n}\n\nfunction distanceToSegment(point: Point, start: Point, end: Point) {\n const deltaX = end.x - start.x;\n const deltaY = end.y - start.y;\n const lengthSquared = deltaX * deltaX + deltaY * deltaY;\n if (lengthSquared === 0) {\n return distance(point, start);\n }\n const ratio = Math.max(\n 0,\n Math.min(1, ((point.x - start.x) * deltaX + (point.y - start.y) * deltaY) / lengthSquared),\n );\n return distance(point, {x: start.x + ratio * deltaX, y: start.y + ratio * deltaY});\n}\n\nfunction triangleSign(point: Point, first: Point, second: Point) {\n return (\n (point.x - second.x) * (first.y - second.y) - (first.x - second.x) * (point.y - second.y)\n );\n}\n\nfunction isInsideTriangle(point: Point, [first, second, third]: [Point, Point, Point]) {\n const firstSign = triangleSign(point, first, second);\n const secondSign = triangleSign(point, second, third);\n const thirdSign = triangleSign(point, third, first);\n const hasNegative = firstSign < 0 || secondSign < 0 || thirdSign < 0;\n const hasPositive = firstSign > 0 || secondSign > 0 || thirdSign > 0;\n return !(hasNegative && hasPositive);\n}\n\nexport function getEdgeGeometry(source: LayoutNode, target: LayoutNode): EdgeGeometry {\n const sourceCenter = getNodeCenter(source);\n const targetCenter = getNodeCenter(target);\n let start: Point;\n let end: Point;\n let firstControl: Point;\n let secondControl: Point;\n\n if (source.kind === target.kind) {\n start = {x: source.rect.x + source.rect.width, y: sourceCenter.y};\n end = {x: target.rect.x + target.rect.width, y: targetCenter.y};\n const arcX = Math.max(start.x, end.x) + getSameGroupArcOutset(source, target);\n firstControl = {x: arcX, y: start.y};\n secondControl = {x: arcX, y: end.y};\n } else {\n const direction = targetCenter.x >= sourceCenter.x ? 1 : -1;\n start = {\n x: sourceCenter.x + (source.rect.width / 2) * direction,\n y: sourceCenter.y,\n };\n end = {\n x: targetCenter.x - (target.rect.width / 2) * direction,\n y: targetCenter.y,\n };\n const controlX = (start.x + end.x) / 2;\n firstControl = {x: controlX, y: start.y};\n secondControl = {x: controlX, y: end.y};\n }\n const angle = Math.atan2(end.y - secondControl.y, end.x - secondControl.x);\n const arrowhead: [Point, Point, Point] = [\n end,\n {\n x: end.x - ARROW_SIZE * Math.cos(angle - Math.PI / 6),\n y: end.y - ARROW_SIZE * Math.sin(angle - Math.PI / 6),\n },\n {\n x: end.x - ARROW_SIZE * Math.cos(angle + Math.PI / 6),\n y: end.y - ARROW_SIZE * Math.sin(angle + Math.PI / 6),\n },\n ];\n\n return {start, end, firstControl, secondControl, arrowhead};\n}\n\nexport function getCubicBezierPoint(geometry: EdgeGeometry, ratio: number): Point {\n const inverse = 1 - ratio;\n return {\n x:\n inverse ** 3 * geometry.start.x +\n 3 * inverse ** 2 * ratio * geometry.firstControl.x +\n 3 * inverse * ratio ** 2 * geometry.secondControl.x +\n ratio ** 3 * geometry.end.x,\n y:\n inverse ** 3 * geometry.start.y +\n 3 * inverse ** 2 * ratio * geometry.firstControl.y +\n 3 * inverse * ratio ** 2 * geometry.secondControl.y +\n ratio ** 3 * geometry.end.y,\n };\n}\n\nexport function getEdgeDistance(point: Point, geometry: EdgeGeometry): number {\n if (isInsideTriangle(point, geometry.arrowhead)) {\n return 0;\n }\n\n let minimum = Number.POSITIVE_INFINITY;\n let previous = geometry.start;\n for (let index = 1; index <= EDGE_CURVE_SEGMENTS; index += 1) {\n const current = getCubicBezierPoint(geometry, index / EDGE_CURVE_SEGMENTS);\n minimum = Math.min(minimum, distanceToSegment(point, previous, current));\n previous = current;\n }\n\n for (let index = 0; index < geometry.arrowhead.length; index += 1) {\n minimum = Math.min(\n minimum,\n distanceToSegment(\n point,\n geometry.arrowhead[index],\n geometry.arrowhead[(index + 1) % geometry.arrowhead.length],\n ),\n );\n }\n return minimum;\n}\n\nexport const getEdgeHitTolerance = (scale: number) =>\n EDGE_HIT_TOLERANCE / Math.max(scale, Number.EPSILON);\n\nexport function orderEdgesForInteraction(\n edges: LayoutEdge[],\n hoveredEdgeKey: string | undefined,\n selectedEdgeKeys: ReadonlySet<string>,\n): LayoutEdge[] {\n const priority = (edge: LayoutEdge) => {\n const key = getEdgeKey(edge.source.id, edge.target.id);\n if (selectedEdgeKeys.has(key)) {\n return 2;\n }\n return key === hoveredEdgeKey ? 1 : 0;\n };\n\n return edges\n .map((edge, index) => ({edge, index, priority: priority(edge)}))\n .sort((left, right) => left.priority - right.priority || left.index - right.index)\n .map(({edge}) => edge);\n}\n\nexport function hitTestEdge(\n edges: LayoutEdge[],\n point: Point,\n tolerance: number,\n): LayoutEdge | undefined {\n let closest: LayoutEdge | undefined;\n let closestDistance = Number.POSITIVE_INFINITY;\n for (let index = edges.length - 1; index >= 0; index -= 1) {\n const edge = edges[index];\n const edgeDistance = getEdgeDistance(point, getEdgeGeometry(edge.source, edge.target));\n if (edgeDistance <= tolerance && (!closest || edgeDistance < closestDistance)) {\n closest = edge;\n closestDistance = edgeDistance;\n }\n }\n return closest;\n}\n"]}
@@ -0,0 +1,9 @@
1
+ import type { ComponentMapData, ComponentMapEmphasis } from "./model.js";
2
+ export declare const getEdgeKey: (source: string, target: string) => string;
3
+ interface ComponentMapEmphasisRequest {
4
+ selectedNodeId?: string;
5
+ searchQuery?: string;
6
+ }
7
+ export declare function getRelatedNodeIds(data: ComponentMapData, selectedId: string): Set<string>;
8
+ export declare function getEmphasis(data: ComponentMapData, { selectedNodeId, searchQuery }?: ComponentMapEmphasisRequest): ComponentMapEmphasis;
9
+ export {};
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getEdgeKey = void 0;
4
+ exports.getRelatedNodeIds = getRelatedNodeIds;
5
+ exports.getEmphasis = getEmphasis;
6
+ const getEdgeKey = (source, target) => `${source}\0${target}`;
7
+ exports.getEdgeKey = getEdgeKey;
8
+ const normalizeSearchValue = (value) => value.trim().toLowerCase();
9
+ function createAdjacency(data) {
10
+ const incoming = new Map();
11
+ const outgoing = new Map();
12
+ for (const { source, target } of data.edges) {
13
+ const outgoingTargets = outgoing.get(source) ?? [];
14
+ outgoingTargets.push(target);
15
+ outgoing.set(source, outgoingTargets);
16
+ const incomingSources = incoming.get(target) ?? [];
17
+ incomingSources.push(source);
18
+ incoming.set(target, incomingSources);
19
+ }
20
+ return { incoming, outgoing };
21
+ }
22
+ function visit(start, adjacency, result) {
23
+ const queue = [start];
24
+ while (queue.length) {
25
+ const current = queue.shift();
26
+ for (const next of adjacency.get(current) ?? []) {
27
+ if (!result.has(next)) {
28
+ result.add(next);
29
+ queue.push(next);
30
+ }
31
+ }
32
+ }
33
+ }
34
+ function getRelatedNodeIds(data, selectedId) {
35
+ if (!data.nodes.some(({ id }) => id === selectedId)) {
36
+ return new Set();
37
+ }
38
+ const { incoming, outgoing } = createAdjacency(data);
39
+ const ancestorIds = new Set([selectedId]);
40
+ const descendantIds = new Set([selectedId]);
41
+ visit(selectedId, incoming, ancestorIds);
42
+ visit(selectedId, outgoing, descendantIds);
43
+ return new Set([...ancestorIds, ...descendantIds]);
44
+ }
45
+ function getEmphasis(data, { selectedNodeId, searchQuery = '' } = {}) {
46
+ const normalizedQuery = normalizeSearchValue(searchQuery);
47
+ if (normalizedQuery) {
48
+ return {
49
+ mode: 'search',
50
+ activeNodeIds: new Set(data.nodes
51
+ .filter(({ label }) => normalizeSearchValue(label).includes(normalizedQuery))
52
+ .map(({ id }) => id)),
53
+ activeEdgeIds: new Set(),
54
+ };
55
+ }
56
+ if (!selectedNodeId) {
57
+ return { mode: 'none', activeNodeIds: new Set(), activeEdgeIds: new Set() };
58
+ }
59
+ const activeNodeIds = getRelatedNodeIds(data, selectedNodeId);
60
+ return {
61
+ mode: 'selection',
62
+ activeNodeIds,
63
+ activeEdgeIds: new Set(data.edges
64
+ .filter(({ source, target }) => activeNodeIds.has(source) && activeNodeIds.has(target))
65
+ .map(({ source, target }) => (0, exports.getEdgeKey)(source, target))),
66
+ };
67
+ }
68
+ //# sourceMappingURL=graph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph.js","sourceRoot":"../../../../src","sources":["demo/ComponentMap/graph.ts"],"names":[],"mappings":"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","sourcesContent":["import type {ComponentMapData, ComponentMapEmphasis} from './model';\n\nexport const getEdgeKey = (source: string, target: string) => `${source}\\0${target}`;\n\ninterface ComponentMapEmphasisRequest {\n selectedNodeId?: string;\n searchQuery?: string;\n}\n\nconst normalizeSearchValue = (value: string) => value.trim().toLowerCase();\n\nfunction createAdjacency(data: ComponentMapData) {\n const incoming = new Map<string, string[]>();\n const outgoing = new Map<string, string[]>();\n\n for (const {source, target} of data.edges) {\n const outgoingTargets = outgoing.get(source) ?? [];\n outgoingTargets.push(target);\n outgoing.set(source, outgoingTargets);\n\n const incomingSources = incoming.get(target) ?? [];\n incomingSources.push(source);\n incoming.set(target, incomingSources);\n }\n\n return {incoming, outgoing};\n}\n\nfunction visit(start: string, adjacency: Map<string, string[]>, result: Set<string>) {\n const queue = [start];\n while (queue.length) {\n const current = queue.shift() as string;\n for (const next of adjacency.get(current) ?? []) {\n if (!result.has(next)) {\n result.add(next);\n queue.push(next);\n }\n }\n }\n}\n\nexport function getRelatedNodeIds(data: ComponentMapData, selectedId: string): Set<string> {\n if (!data.nodes.some(({id}) => id === selectedId)) {\n return new Set();\n }\n\n const {incoming, outgoing} = createAdjacency(data);\n const ancestorIds = new Set([selectedId]);\n const descendantIds = new Set([selectedId]);\n\n visit(selectedId, incoming, ancestorIds);\n visit(selectedId, outgoing, descendantIds);\n\n return new Set([...ancestorIds, ...descendantIds]);\n}\n\nexport function getEmphasis(\n data: ComponentMapData,\n {selectedNodeId, searchQuery = ''}: ComponentMapEmphasisRequest = {},\n): ComponentMapEmphasis {\n const normalizedQuery = normalizeSearchValue(searchQuery);\n if (normalizedQuery) {\n return {\n mode: 'search',\n activeNodeIds: new Set(\n data.nodes\n .filter(({label}) => normalizeSearchValue(label).includes(normalizedQuery))\n .map(({id}) => id),\n ),\n activeEdgeIds: new Set(),\n };\n }\n\n if (!selectedNodeId) {\n return {mode: 'none', activeNodeIds: new Set(), activeEdgeIds: new Set()};\n }\n\n const activeNodeIds = getRelatedNodeIds(data, selectedNodeId);\n return {\n mode: 'selection',\n activeNodeIds,\n activeEdgeIds: new Set(\n data.edges\n .filter(\n ({source, target}) => activeNodeIds.has(source) && activeNodeIds.has(target),\n )\n .map(({source, target}) => getEdgeKey(source, target)),\n ),\n };\n}\n"]}
@@ -0,0 +1,12 @@
1
+ import type { ComponentMapData, ComponentMapLayout, LayoutNode, Point } from "./model.js";
2
+ export declare const NODE_MIN_WIDTH = 208;
3
+ export declare const NODE_HEIGHT = 48;
4
+ export declare const NODE_LABEL_FONT = "600 13px sans-serif";
5
+ export declare const NODE_LABEL_PADDING = 16;
6
+ export declare const VERTICAL_GAP = 32;
7
+ export declare const GROUP_GAP = 112;
8
+ export declare const BAND_HEADER_HEIGHT = 36;
9
+ export declare const WORLD_PADDING = 56;
10
+ export declare const KIND_ORDER: readonly ["block", "sub-block", "component"];
11
+ export declare function layoutComponentMap(data: ComponentMapData): ComponentMapLayout;
12
+ export declare function hitTest(layoutOrNodes: ComponentMapLayout | LayoutNode[], point: Point): LayoutNode | undefined;
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KIND_ORDER = exports.WORLD_PADDING = exports.BAND_HEADER_HEIGHT = exports.GROUP_GAP = exports.VERTICAL_GAP = exports.NODE_LABEL_PADDING = exports.NODE_LABEL_FONT = exports.NODE_HEIGHT = exports.NODE_MIN_WIDTH = void 0;
4
+ exports.layoutComponentMap = layoutComponentMap;
5
+ exports.hitTest = hitTest;
6
+ const edgeGeometry_1 = require("./edgeGeometry.js");
7
+ exports.NODE_MIN_WIDTH = 208;
8
+ exports.NODE_HEIGHT = 48;
9
+ exports.NODE_LABEL_FONT = '600 13px sans-serif';
10
+ exports.NODE_LABEL_PADDING = 16;
11
+ exports.VERTICAL_GAP = 32;
12
+ exports.GROUP_GAP = 112;
13
+ exports.BAND_HEADER_HEIGHT = 36;
14
+ exports.WORLD_PADDING = 56;
15
+ exports.KIND_ORDER = ['block', 'sub-block', 'component'];
16
+ const BAND_LABELS = {
17
+ block: 'Blocks',
18
+ 'sub-block': 'Sub-blocks',
19
+ component: 'Components',
20
+ };
21
+ function sortByLabel(nodes) {
22
+ return [...nodes].sort((left, right) => left.label.localeCompare(right.label));
23
+ }
24
+ function createLabelMeasurer() {
25
+ if (typeof CanvasRenderingContext2D !== 'undefined' && typeof document !== 'undefined') {
26
+ try {
27
+ const context = document.createElement('canvas').getContext('2d');
28
+ if (context) {
29
+ context.font = exports.NODE_LABEL_FONT;
30
+ return (label) => context.measureText(label).width;
31
+ }
32
+ }
33
+ catch {
34
+ // Use deterministic metrics when Canvas is unavailable (for example during SSR).
35
+ }
36
+ }
37
+ return (label) => Array.from(label).length * 7;
38
+ }
39
+ const getNodesHeight = (count) => count === 0 ? 0 : count * exports.NODE_HEIGHT + (count - 1) * exports.VERTICAL_GAP;
40
+ function measureGroup(kind, nodes, measureLabel) {
41
+ const sortedNodes = sortByLabel(nodes);
42
+ const nodeWidth = Math.max(exports.NODE_MIN_WIDTH, ...sortedNodes.map(({ label }) => measureLabel(label) + exports.NODE_LABEL_PADDING * 2));
43
+ return {
44
+ kind,
45
+ label: BAND_LABELS[kind],
46
+ nodes: sortedNodes,
47
+ nodeWidth,
48
+ visibleHeight: exports.BAND_HEADER_HEIGHT + getNodesHeight(sortedNodes.length),
49
+ };
50
+ }
51
+ function createBand(group, x, y) {
52
+ return {
53
+ kind: group.kind,
54
+ label: group.label,
55
+ bounds: {
56
+ x,
57
+ y,
58
+ width: group.nodeWidth,
59
+ height: group.visibleHeight,
60
+ },
61
+ nodes: group.nodes.map((node, index) => ({
62
+ ...node,
63
+ rect: {
64
+ x,
65
+ y: y + exports.BAND_HEADER_HEIGHT + index * (exports.NODE_HEIGHT + exports.VERTICAL_GAP),
66
+ width: group.nodeWidth,
67
+ height: exports.NODE_HEIGHT,
68
+ },
69
+ })),
70
+ };
71
+ }
72
+ function resolveEdges(edges, nodeById) {
73
+ return edges.map(({ source, target }) => {
74
+ const sourceNode = nodeById.get(source);
75
+ const targetNode = nodeById.get(target);
76
+ if (!sourceNode || !targetNode) {
77
+ throw new Error(`Component map edge references unknown node: ${source} -> ${target}`);
78
+ }
79
+ return { source: sourceNode, target: targetNode };
80
+ });
81
+ }
82
+ function layoutComponentMap(data) {
83
+ const nodesByKind = new Map(exports.KIND_ORDER.map((kind) => [kind, []]));
84
+ for (const node of data.nodes) {
85
+ nodesByKind.get(node.kind)?.push(node);
86
+ }
87
+ const measureLabel = createLabelMeasurer();
88
+ const measuredGroups = exports.KIND_ORDER.map((kind) => measureGroup(kind, nodesByKind.get(kind) ?? [], measureLabel));
89
+ const maximumGroupHeight = Math.max(...measuredGroups.map(({ visibleHeight }) => visibleHeight));
90
+ const commonCenterY = exports.WORLD_PADDING + maximumGroupHeight / 2;
91
+ const provisionalBands = measuredGroups.map((group) => createBand(group, 0, commonCenterY - group.visibleHeight / 2));
92
+ const provisionalNodes = provisionalBands.flatMap(({ nodes }) => nodes);
93
+ const provisionalNodeById = new Map(provisionalNodes.map((node) => [node.id, node]));
94
+ const provisionalEdges = resolveEdges(data.edges, provisionalNodeById);
95
+ const corridorByKind = new Map(exports.KIND_ORDER.map((kind) => [kind, 0]));
96
+ for (const edge of provisionalEdges) {
97
+ if (edge.source.kind === edge.target.kind) {
98
+ corridorByKind.set(edge.source.kind, Math.max(corridorByKind.get(edge.source.kind) ?? 0, (0, edgeGeometry_1.getSameGroupArcOutset)(edge.source, edge.target) + edgeGeometry_1.ARROW_SIZE));
99
+ }
100
+ }
101
+ let nextGroupX = exports.WORLD_PADDING;
102
+ const bands = provisionalBands.map((band, index) => {
103
+ const x = nextGroupX;
104
+ const placedBand = {
105
+ ...band,
106
+ bounds: { ...band.bounds, x },
107
+ nodes: band.nodes.map((node) => ({
108
+ ...node,
109
+ rect: { ...node.rect, x },
110
+ })),
111
+ };
112
+ nextGroupX =
113
+ x +
114
+ band.bounds.width +
115
+ (corridorByKind.get(band.kind) ?? 0) +
116
+ (index === provisionalBands.length - 1 ? 0 : exports.GROUP_GAP);
117
+ return placedBand;
118
+ });
119
+ const nodes = bands.flatMap((band) => band.nodes);
120
+ const nodeById = new Map(nodes.map((node) => [node.id, node]));
121
+ const edges = resolveEdges(data.edges, nodeById);
122
+ const rightmostBand = bands[bands.length - 1];
123
+ const rightmost = rightmostBand.bounds.x +
124
+ rightmostBand.bounds.width +
125
+ (corridorByKind.get(rightmostBand.kind) ?? 0);
126
+ return {
127
+ bands,
128
+ nodes,
129
+ edges,
130
+ bounds: {
131
+ x: 0,
132
+ y: 0,
133
+ width: rightmost + exports.WORLD_PADDING,
134
+ height: maximumGroupHeight + exports.WORLD_PADDING * 2,
135
+ },
136
+ };
137
+ }
138
+ function hitTest(layoutOrNodes, point) {
139
+ const nodes = Array.isArray(layoutOrNodes) ? layoutOrNodes : layoutOrNodes.nodes;
140
+ for (let index = nodes.length - 1; index >= 0; index -= 1) {
141
+ const node = nodes[index];
142
+ const { x, y, width, height } = node.rect;
143
+ if (point.x >= x && point.x <= x + width && point.y >= y && point.y <= y + height) {
144
+ return node;
145
+ }
146
+ }
147
+ return undefined;
148
+ }
149
+ //# sourceMappingURL=layout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.js","sourceRoot":"../../../../src","sources":["demo/ComponentMap/layout.ts"],"names":[],"mappings":"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","sourcesContent":["import {ARROW_SIZE, getSameGroupArcOutset} from './edgeGeometry';\nimport type {\n ComponentMapData,\n ComponentMapEdge,\n ComponentMapLayout,\n ComponentMapNode,\n ComponentMapNodeKind,\n LayoutBand,\n LayoutEdge,\n LayoutNode,\n Point,\n} from './model';\n\nexport const NODE_MIN_WIDTH = 208;\nexport const NODE_HEIGHT = 48;\nexport const NODE_LABEL_FONT = '600 13px sans-serif';\nexport const NODE_LABEL_PADDING = 16;\nexport const VERTICAL_GAP = 32;\nexport const GROUP_GAP = 112;\nexport const BAND_HEADER_HEIGHT = 36;\nexport const WORLD_PADDING = 56;\nexport const KIND_ORDER = ['block', 'sub-block', 'component'] as const;\n\nconst BAND_LABELS: Record<ComponentMapNodeKind, string> = {\n block: 'Blocks',\n 'sub-block': 'Sub-blocks',\n component: 'Components',\n};\n\nfunction sortByLabel(nodes: ComponentMapNode[]) {\n return [...nodes].sort((left, right) => left.label.localeCompare(right.label));\n}\n\nfunction createLabelMeasurer() {\n if (typeof CanvasRenderingContext2D !== 'undefined' && typeof document !== 'undefined') {\n try {\n const context = document.createElement('canvas').getContext('2d');\n if (context) {\n context.font = NODE_LABEL_FONT;\n return (label: string) => context.measureText(label).width;\n }\n } catch {\n // Use deterministic metrics when Canvas is unavailable (for example during SSR).\n }\n }\n\n return (label: string) => Array.from(label).length * 7;\n}\n\ninterface MeasuredGroup {\n kind: ComponentMapNodeKind;\n label: string;\n nodes: ComponentMapNode[];\n nodeWidth: number;\n visibleHeight: number;\n}\n\nconst getNodesHeight = (count: number) =>\n count === 0 ? 0 : count * NODE_HEIGHT + (count - 1) * VERTICAL_GAP;\n\nfunction measureGroup(\n kind: ComponentMapNodeKind,\n nodes: ComponentMapNode[],\n measureLabel: (label: string) => number,\n): MeasuredGroup {\n const sortedNodes = sortByLabel(nodes);\n const nodeWidth = Math.max(\n NODE_MIN_WIDTH,\n ...sortedNodes.map(({label}) => measureLabel(label) + NODE_LABEL_PADDING * 2),\n );\n return {\n kind,\n label: BAND_LABELS[kind],\n nodes: sortedNodes,\n nodeWidth,\n visibleHeight: BAND_HEADER_HEIGHT + getNodesHeight(sortedNodes.length),\n };\n}\n\nfunction createBand(group: MeasuredGroup, x: number, y: number): LayoutBand {\n return {\n kind: group.kind,\n label: group.label,\n bounds: {\n x,\n y,\n width: group.nodeWidth,\n height: group.visibleHeight,\n },\n nodes: group.nodes.map((node, index) => ({\n ...node,\n rect: {\n x,\n y: y + BAND_HEADER_HEIGHT + index * (NODE_HEIGHT + VERTICAL_GAP),\n width: group.nodeWidth,\n height: NODE_HEIGHT,\n },\n })),\n };\n}\n\nfunction resolveEdges(\n edges: ComponentMapEdge[],\n nodeById: ReadonlyMap<string, LayoutNode>,\n): LayoutEdge[] {\n return edges.map(({source, target}) => {\n const sourceNode = nodeById.get(source);\n const targetNode = nodeById.get(target);\n if (!sourceNode || !targetNode) {\n throw new Error(`Component map edge references unknown node: ${source} -> ${target}`);\n }\n return {source: sourceNode, target: targetNode};\n });\n}\n\nexport function layoutComponentMap(data: ComponentMapData): ComponentMapLayout {\n const nodesByKind = new Map<ComponentMapNodeKind, ComponentMapNode[]>(\n KIND_ORDER.map((kind) => [kind, []]),\n );\n for (const node of data.nodes) {\n nodesByKind.get(node.kind)?.push(node);\n }\n\n const measureLabel = createLabelMeasurer();\n const measuredGroups = KIND_ORDER.map((kind) =>\n measureGroup(kind, nodesByKind.get(kind) ?? [], measureLabel),\n );\n const maximumGroupHeight = Math.max(...measuredGroups.map(({visibleHeight}) => visibleHeight));\n const commonCenterY = WORLD_PADDING + maximumGroupHeight / 2;\n const provisionalBands = measuredGroups.map((group) =>\n createBand(group, 0, commonCenterY - group.visibleHeight / 2),\n );\n const provisionalNodes = provisionalBands.flatMap(({nodes}) => nodes);\n const provisionalNodeById = new Map(provisionalNodes.map((node) => [node.id, node]));\n const provisionalEdges = resolveEdges(data.edges, provisionalNodeById);\n const corridorByKind = new Map<ComponentMapNodeKind, number>(\n KIND_ORDER.map((kind) => [kind, 0]),\n );\n for (const edge of provisionalEdges) {\n if (edge.source.kind === edge.target.kind) {\n corridorByKind.set(\n edge.source.kind,\n Math.max(\n corridorByKind.get(edge.source.kind) ?? 0,\n getSameGroupArcOutset(edge.source, edge.target) + ARROW_SIZE,\n ),\n );\n }\n }\n\n let nextGroupX = WORLD_PADDING;\n const bands = provisionalBands.map((band, index) => {\n const x = nextGroupX;\n const placedBand: LayoutBand = {\n ...band,\n bounds: {...band.bounds, x},\n nodes: band.nodes.map((node) => ({\n ...node,\n rect: {...node.rect, x},\n })),\n };\n nextGroupX =\n x +\n band.bounds.width +\n (corridorByKind.get(band.kind) ?? 0) +\n (index === provisionalBands.length - 1 ? 0 : GROUP_GAP);\n return placedBand;\n });\n const nodes = bands.flatMap((band) => band.nodes);\n const nodeById = new Map(nodes.map((node) => [node.id, node]));\n const edges = resolveEdges(data.edges, nodeById);\n const rightmostBand = bands[bands.length - 1];\n const rightmost =\n rightmostBand.bounds.x +\n rightmostBand.bounds.width +\n (corridorByKind.get(rightmostBand.kind) ?? 0);\n\n return {\n bands,\n nodes,\n edges,\n bounds: {\n x: 0,\n y: 0,\n width: rightmost + WORLD_PADDING,\n height: maximumGroupHeight + WORLD_PADDING * 2,\n },\n };\n}\n\nexport function hitTest(\n layoutOrNodes: ComponentMapLayout | LayoutNode[],\n point: Point,\n): LayoutNode | undefined {\n const nodes = Array.isArray(layoutOrNodes) ? layoutOrNodes : layoutOrNodes.nodes;\n\n for (let index = nodes.length - 1; index >= 0; index -= 1) {\n const node = nodes[index];\n const {x, y, width, height} = node.rect;\n if (point.x >= x && point.x <= x + width && point.y >= y && point.y <= y + height) {\n return node;\n }\n }\n\n return undefined;\n}\n"]}
@@ -0,0 +1,55 @@
1
+ export type ComponentMapNodeKind = 'block' | 'sub-block' | 'component';
2
+ export interface ComponentMapNode {
3
+ id: string;
4
+ kind: ComponentMapNodeKind;
5
+ exportName: string;
6
+ label: string;
7
+ }
8
+ export interface ComponentMapEdge {
9
+ source: string;
10
+ target: string;
11
+ }
12
+ export interface ComponentMapData {
13
+ nodes: ComponentMapNode[];
14
+ edges: ComponentMapEdge[];
15
+ }
16
+ export interface Point {
17
+ x: number;
18
+ y: number;
19
+ }
20
+ export interface Rect extends Point {
21
+ width: number;
22
+ height: number;
23
+ }
24
+ export interface LayoutNode extends ComponentMapNode {
25
+ rect: Rect;
26
+ }
27
+ export interface LayoutBand {
28
+ kind: ComponentMapNodeKind;
29
+ label: string;
30
+ bounds: Rect;
31
+ nodes: LayoutNode[];
32
+ }
33
+ export interface LayoutEdge {
34
+ source: LayoutNode;
35
+ target: LayoutNode;
36
+ }
37
+ export interface ComponentMapLayout {
38
+ nodes: LayoutNode[];
39
+ bands: LayoutBand[];
40
+ edges: LayoutEdge[];
41
+ bounds: Rect;
42
+ }
43
+ export interface ViewportSize {
44
+ width: number;
45
+ height: number;
46
+ }
47
+ export interface ViewportTransform extends Point {
48
+ scale: number;
49
+ }
50
+ export type ComponentMapEmphasisMode = 'none' | 'selection' | 'search';
51
+ export interface ComponentMapEmphasis {
52
+ mode: ComponentMapEmphasisMode;
53
+ activeNodeIds: Set<string>;
54
+ activeEdgeIds: Set<string>;
55
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.js","sourceRoot":"../../../../src","sources":["demo/ComponentMap/model.ts"],"names":[],"mappings":"","sourcesContent":["export type ComponentMapNodeKind = 'block' | 'sub-block' | 'component';\n\nexport interface ComponentMapNode {\n id: string;\n kind: ComponentMapNodeKind;\n exportName: string;\n label: string;\n}\n\nexport interface ComponentMapEdge {\n source: string;\n target: string;\n}\n\nexport interface ComponentMapData {\n nodes: ComponentMapNode[];\n edges: ComponentMapEdge[];\n}\n\nexport interface Point {\n x: number;\n y: number;\n}\n\nexport interface Rect extends Point {\n width: number;\n height: number;\n}\n\nexport interface LayoutNode extends ComponentMapNode {\n rect: Rect;\n}\n\nexport interface LayoutBand {\n kind: ComponentMapNodeKind;\n label: string;\n bounds: Rect;\n nodes: LayoutNode[];\n}\n\nexport interface LayoutEdge {\n source: LayoutNode;\n target: LayoutNode;\n}\n\nexport interface ComponentMapLayout {\n nodes: LayoutNode[];\n bands: LayoutBand[];\n edges: LayoutEdge[];\n bounds: Rect;\n}\n\nexport interface ViewportSize {\n width: number;\n height: number;\n}\n\nexport interface ViewportTransform extends Point {\n scale: number;\n}\n\nexport type ComponentMapEmphasisMode = 'none' | 'selection' | 'search';\n\nexport interface ComponentMapEmphasis {\n mode: ComponentMapEmphasisMode;\n activeNodeIds: Set<string>;\n activeEdgeIds: Set<string>;\n}\n"]}
@@ -0,0 +1,7 @@
1
+ import type { Point, Rect, ViewportSize, ViewportTransform } from "./model.js";
2
+ export declare const VIEWPORT_PADDING = 24;
3
+ export declare const MINIMUM_VIEWPORT_SCALE = 0.01;
4
+ export declare const worldToScreen: (point: Point, transform: ViewportTransform) => Point;
5
+ export declare const screenToWorld: (point: Point, transform: ViewportTransform) => Point;
6
+ export declare function fitToViewport(bounds: Rect, viewport: ViewportSize, padding?: number): ViewportTransform;
7
+ export declare function zoomAtPoint(transform: ViewportTransform, factor: number, point: Point, minScale: number, maxScale: number): ViewportTransform;
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.screenToWorld = exports.worldToScreen = exports.MINIMUM_VIEWPORT_SCALE = exports.VIEWPORT_PADDING = void 0;
4
+ exports.fitToViewport = fitToViewport;
5
+ exports.zoomAtPoint = zoomAtPoint;
6
+ exports.VIEWPORT_PADDING = 24;
7
+ exports.MINIMUM_VIEWPORT_SCALE = 0.01;
8
+ const worldToScreen = (point, transform) => ({
9
+ x: point.x * transform.scale + transform.x,
10
+ y: point.y * transform.scale + transform.y,
11
+ });
12
+ exports.worldToScreen = worldToScreen;
13
+ const screenToWorld = (point, transform) => ({
14
+ x: (point.x - transform.x) / transform.scale,
15
+ y: (point.y - transform.y) / transform.scale,
16
+ });
17
+ exports.screenToWorld = screenToWorld;
18
+ function fitToViewport(bounds, viewport, padding = exports.VIEWPORT_PADDING) {
19
+ const availableWidth = Math.max(0, viewport.width - padding * 2);
20
+ const availableHeight = Math.max(0, viewport.height - padding * 2);
21
+ const widthScale = bounds.width > 0 ? availableWidth / bounds.width : 1;
22
+ const heightScale = bounds.height > 0 ? availableHeight / bounds.height : 1;
23
+ // React postpones fitting until it has a positive canvas size. Keep this pure fallback
24
+ // invertible as well for callers that receive an empty or padding-sized viewport.
25
+ const scale = Math.max(exports.MINIMUM_VIEWPORT_SCALE, Math.min(1, widthScale, heightScale));
26
+ return {
27
+ scale,
28
+ x: viewport.width / 2 - (bounds.x + bounds.width / 2) * scale,
29
+ y: viewport.height / 2 - (bounds.y + bounds.height / 2) * scale,
30
+ };
31
+ }
32
+ function zoomAtPoint(transform, factor, point, minScale, maxScale) {
33
+ const world = (0, exports.screenToWorld)(point, transform);
34
+ const scale = Math.min(maxScale, Math.max(minScale, transform.scale * factor));
35
+ return {
36
+ scale,
37
+ x: point.x - world.x * scale,
38
+ y: point.y - world.y * scale,
39
+ };
40
+ }
41
+ //# sourceMappingURL=viewport.js.map