@elaraai/east-ui-components 1.0.13 → 1.0.14
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/collections/schematic/camera.d.ts +175 -0
- package/dist/collections/schematic/camera.d.ts.map +1 -0
- package/dist/collections/schematic/index.d.ts.map +1 -1
- package/dist/collections/schematic/paint.d.ts +91 -0
- package/dist/collections/schematic/paint.d.ts.map +1 -1
- package/dist/index.cjs +536 -197
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +536 -197
- package/dist/index.js.map +1 -1
- package/dist/theme/global-css.d.ts.map +1 -1
- package/dist/theme/slot-recipes/schematic.d.ts +1 -1
- package/dist/theme/slot-recipes/schematic.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -1187,8 +1187,13 @@ const globalCss = react.defineGlobalStyles({
|
|
|
1187
1187
|
},
|
|
1188
1188
|
/* ─── Drag & drop stages (drag-drop-visuals) ─────────────────────────
|
|
1189
1189
|
* The DragLayerProvider drives these via data attributes; surfaces get
|
|
1190
|
-
* the treatment for free by registering cells/sinks.
|
|
1191
|
-
|
|
1190
|
+
* the treatment for free by registering cells/sinks.
|
|
1191
|
+
*
|
|
1192
|
+
* Scoped `:not([data-scope])` so it dims only our own drag origins —
|
|
1193
|
+
* Ark/Zag widgets (Slider, etc.) set `data-dragging` on themselves for
|
|
1194
|
+
* their own drag state and carry a `data-scope`; without this exclusion
|
|
1195
|
+
* a slider goes to 40% opacity while its thumb is dragged. */
|
|
1196
|
+
"[data-dragging]:not([data-scope])": {
|
|
1192
1197
|
opacity: "0.4"
|
|
1193
1198
|
},
|
|
1194
1199
|
"[data-drag-ghost]": {
|
|
@@ -3008,6 +3013,7 @@ const schematicSlotRecipe = react.defineSlotRecipe({
|
|
|
3008
3013
|
"root",
|
|
3009
3014
|
"canvas",
|
|
3010
3015
|
"grid",
|
|
3016
|
+
"cardLayer",
|
|
3011
3017
|
"underlay",
|
|
3012
3018
|
"zone",
|
|
3013
3019
|
"zoneLabel",
|
|
@@ -3085,6 +3091,15 @@ const schematicSlotRecipe = react.defineSlotRecipe({
|
|
|
3085
3091
|
linear-gradient(to right, color-mix(in oklch, {colors.border.subtle} 22%, transparent) 1px, transparent 1px),
|
|
3086
3092
|
linear-gradient(to bottom, color-mix(in oklch, {colors.border.subtle} 22%, transparent) 1px, transparent 1px)`
|
|
3087
3093
|
},
|
|
3094
|
+
/* Holds the live `--cam-ppu` / `--cam-tx` / `--cam-ty` custom
|
|
3095
|
+
* properties (written by applyCamera each frame). Transparent and
|
|
3096
|
+
* click-through; each card re-enables its own pointer events so the
|
|
3097
|
+
* full-cover layer never swallows canvas clicks (issue #57). */
|
|
3098
|
+
cardLayer: {
|
|
3099
|
+
position: "absolute",
|
|
3100
|
+
inset: "0",
|
|
3101
|
+
pointerEvents: "none"
|
|
3102
|
+
},
|
|
3088
3103
|
/* Links draw in pixel space; tone → token mapping lives here so
|
|
3089
3104
|
* data only ever names a tone. */
|
|
3090
3105
|
underlay: {
|
|
@@ -3281,6 +3296,7 @@ const schematicSlotRecipe = react.defineSlotRecipe({
|
|
|
3281
3296
|
item: {
|
|
3282
3297
|
position: "absolute",
|
|
3283
3298
|
transform: "translate(-50%, -50%)",
|
|
3299
|
+
pointerEvents: "auto",
|
|
3284
3300
|
display: "flex",
|
|
3285
3301
|
flexDirection: "column",
|
|
3286
3302
|
gap: "2px",
|
|
@@ -3476,14 +3492,24 @@ const schematicSlotRecipe = react.defineSlotRecipe({
|
|
|
3476
3492
|
minHeight: "0",
|
|
3477
3493
|
overflowY: "auto",
|
|
3478
3494
|
paddingBottom: "{spacing.2}",
|
|
3479
|
-
lineHeight: "1.7"
|
|
3480
|
-
|
|
3495
|
+
lineHeight: "1.7",
|
|
3496
|
+
// Row height shared by the sticky zone headers (their stacked `top`
|
|
3497
|
+
// offset is a multiple of this) so an ancestor chain pins flush.
|
|
3498
|
+
"--nav-row-h": "22px"
|
|
3499
|
+
},
|
|
3500
|
+
/* Zone headers pin to the top of the scroll area like a tree view: each
|
|
3501
|
+
* depth level sticks at `top = depth × --nav-row-h` (set inline), so the
|
|
3502
|
+
* area (and its ancestors) stays visible while you scroll its items.
|
|
3503
|
+
* The opaque surface + z-index occlude the items scrolling underneath. */
|
|
3481
3504
|
navZone: {
|
|
3482
3505
|
display: "flex",
|
|
3483
3506
|
alignItems: "center",
|
|
3484
3507
|
gap: "{spacing.1}",
|
|
3485
|
-
|
|
3486
|
-
paddingRight: "{spacing.3}"
|
|
3508
|
+
height: "var(--nav-row-h)",
|
|
3509
|
+
paddingRight: "{spacing.3}",
|
|
3510
|
+
position: "sticky",
|
|
3511
|
+
background: "bg.surface",
|
|
3512
|
+
zIndex: "1"
|
|
3487
3513
|
},
|
|
3488
3514
|
navCaret: {
|
|
3489
3515
|
background: "transparent",
|
|
@@ -3530,6 +3556,9 @@ const schematicSlotRecipe = react.defineSlotRecipe({
|
|
|
3530
3556
|
border: "none",
|
|
3531
3557
|
paddingY: "2px",
|
|
3532
3558
|
paddingRight: "{spacing.3}",
|
|
3559
|
+
// Keep the auto-scroll-to-selected item clear of the pinned zone
|
|
3560
|
+
// headers (up to two ancestor levels).
|
|
3561
|
+
scrollMarginTop: "calc(var(--nav-row-h) * 2)",
|
|
3533
3562
|
fontFamily: "mono",
|
|
3534
3563
|
fontSize: "11px",
|
|
3535
3564
|
letterSpacing: "0.06em",
|
|
@@ -61620,6 +61649,83 @@ function multiSelect(arr, left, right, n2, compare) {
|
|
|
61620
61649
|
stack.push(left, mid, mid, right);
|
|
61621
61650
|
}
|
|
61622
61651
|
}
|
|
61652
|
+
const IDENTITY = { zoom: 1, tx: 0, ty: 0 };
|
|
61653
|
+
function project(x2, y2, cam) {
|
|
61654
|
+
return { sx: x2 * cam.ppu + cam.tx, sy: y2 * cam.ppu + cam.ty };
|
|
61655
|
+
}
|
|
61656
|
+
function cardTranslateCss(x2, y2) {
|
|
61657
|
+
return `translate(calc(var(--cam-ppu) * ${x2} * 1px + var(--cam-tx)), calc(var(--cam-ppu) * ${y2} * 1px + var(--cam-ty)))`;
|
|
61658
|
+
}
|
|
61659
|
+
function cardWidthCss(width) {
|
|
61660
|
+
return `calc(var(--cam-ppu) * ${width} * 1px)`;
|
|
61661
|
+
}
|
|
61662
|
+
function viewportWorldBbox(cam, width, height) {
|
|
61663
|
+
return {
|
|
61664
|
+
minX: -cam.tx / cam.ppu,
|
|
61665
|
+
minY: -cam.ty / cam.ppu,
|
|
61666
|
+
maxX: (width - cam.tx) / cam.ppu,
|
|
61667
|
+
maxY: (height - cam.ty) / cam.ppu
|
|
61668
|
+
};
|
|
61669
|
+
}
|
|
61670
|
+
function bboxOverlaps(a2, b2) {
|
|
61671
|
+
return a2.maxX >= b2.minX && a2.minX <= b2.maxX && a2.maxY >= b2.minY && a2.minY <= b2.maxY;
|
|
61672
|
+
}
|
|
61673
|
+
function pointsBbox(pts) {
|
|
61674
|
+
if (pts.length === 0) return null;
|
|
61675
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
61676
|
+
for (const p2 of pts) {
|
|
61677
|
+
if (p2.x < minX) minX = p2.x;
|
|
61678
|
+
if (p2.y < minY) minY = p2.y;
|
|
61679
|
+
if (p2.x > maxX) maxX = p2.x;
|
|
61680
|
+
if (p2.y > maxY) maxY = p2.y;
|
|
61681
|
+
}
|
|
61682
|
+
return { minX, minY, maxX, maxY };
|
|
61683
|
+
}
|
|
61684
|
+
function zoomAbout(prev, factor, px, py, min2, max2) {
|
|
61685
|
+
const zoom = Math.max(min2, Math.min(max2, prev.zoom * factor));
|
|
61686
|
+
if (zoom === 1) return IDENTITY;
|
|
61687
|
+
const k2 = zoom / prev.zoom;
|
|
61688
|
+
return { zoom, tx: px - k2 * (px - prev.tx), ty: py - k2 * (py - prev.ty) };
|
|
61689
|
+
}
|
|
61690
|
+
function nextMode(mode, event) {
|
|
61691
|
+
switch (event) {
|
|
61692
|
+
case "pointerDown":
|
|
61693
|
+
return "panning";
|
|
61694
|
+
case "wheel":
|
|
61695
|
+
return "wheeling";
|
|
61696
|
+
case "zoom":
|
|
61697
|
+
return "wheeling";
|
|
61698
|
+
case "flyStart":
|
|
61699
|
+
return "flying";
|
|
61700
|
+
case "pointerUp":
|
|
61701
|
+
return mode === "panning" ? "idle" : mode;
|
|
61702
|
+
case "flyEnd":
|
|
61703
|
+
return mode === "flying" ? "idle" : mode;
|
|
61704
|
+
case "cancel":
|
|
61705
|
+
return "idle";
|
|
61706
|
+
}
|
|
61707
|
+
}
|
|
61708
|
+
function cancelsFly(mode, event) {
|
|
61709
|
+
return mode === "flying" && (event === "pointerDown" || event === "wheel" || event === "zoom" || event === "cancel");
|
|
61710
|
+
}
|
|
61711
|
+
function makeRafCoalescer(schedule, cancel) {
|
|
61712
|
+
let handle2 = null;
|
|
61713
|
+
return {
|
|
61714
|
+
request(run) {
|
|
61715
|
+
if (handle2 !== null) return;
|
|
61716
|
+
handle2 = schedule(() => {
|
|
61717
|
+
handle2 = null;
|
|
61718
|
+
run();
|
|
61719
|
+
});
|
|
61720
|
+
},
|
|
61721
|
+
cancel() {
|
|
61722
|
+
if (handle2 !== null) {
|
|
61723
|
+
cancel(handle2);
|
|
61724
|
+
handle2 = null;
|
|
61725
|
+
}
|
|
61726
|
+
}
|
|
61727
|
+
};
|
|
61728
|
+
}
|
|
61623
61729
|
const css = (c2, a2 = 1) => a2 >= 1 ? `rgb(${c2[0]},${c2[1]},${c2[2]})` : `rgba(${c2[0]},${c2[1]},${c2[2]},${a2})`;
|
|
61624
61730
|
function toneRGB(p2, tone, kind) {
|
|
61625
61731
|
switch (tone) {
|
|
@@ -61700,6 +61806,7 @@ function arcFromBulge(p1, p2, bulge) {
|
|
|
61700
61806
|
const mx = (p1.x + p2.x) / 2, my = (p1.y + p2.y) / 2;
|
|
61701
61807
|
const d2 = chord / 2 / Math.tan(theta / 2);
|
|
61702
61808
|
const cx2 = mx - dy / chord * d2, cy = my + dx / chord * d2;
|
|
61809
|
+
if (!Number.isFinite(radius) || !Number.isFinite(d2) || !Number.isFinite(cx2) || !Number.isFinite(cy)) return null;
|
|
61703
61810
|
return {
|
|
61704
61811
|
cx: cx2,
|
|
61705
61812
|
cy,
|
|
@@ -61727,22 +61834,76 @@ function traceVertices(ctx, pts, closed) {
|
|
|
61727
61834
|
ctx.closePath();
|
|
61728
61835
|
}
|
|
61729
61836
|
}
|
|
61837
|
+
const MARKER_LABEL_FONT = '600 10px ui-monospace, "SF Mono", Menlo, monospace';
|
|
61838
|
+
const MARKER_DOT_RADIUS = 5;
|
|
61839
|
+
const MARKER_DOT_HIT_RADIUS = 10;
|
|
61840
|
+
const MARKER_PIN_PAD_X = 6, MARKER_PIN_DOT_W = 7, MARKER_PIN_GAP = 4, MARKER_PIN_H = 16;
|
|
61841
|
+
function markerHitbox(item, tier, cam, measure) {
|
|
61842
|
+
const { sx, sy } = project(item.x, item.y, cam);
|
|
61843
|
+
if (tier === "dot") return { kind: "circle", cx: sx, cy: sy, r: MARKER_DOT_HIT_RADIUS };
|
|
61844
|
+
const tw = measure(item.label);
|
|
61845
|
+
const w2 = MARKER_PIN_PAD_X + MARKER_PIN_DOT_W + MARKER_PIN_GAP + tw + MARKER_PIN_PAD_X;
|
|
61846
|
+
return { kind: "rect", left: sx - w2 / 2, top: sy - MARKER_PIN_H / 2, w: w2, h: MARKER_PIN_H };
|
|
61847
|
+
}
|
|
61848
|
+
function markerHit(box, sx, sy) {
|
|
61849
|
+
return box.kind === "circle" ? Math.hypot(sx - box.cx, sy - box.cy) <= box.r : sx >= box.left && sx <= box.left + box.w && sy >= box.top && sy <= box.top + box.h;
|
|
61850
|
+
}
|
|
61851
|
+
function hatchSpacing(rawSpacing) {
|
|
61852
|
+
return Number.isFinite(rawSpacing) ? Math.max(1, rawSpacing) : 1;
|
|
61853
|
+
}
|
|
61854
|
+
function hatchSweepBounds(x2, y2, w2, h2, width, height, dx, dy, diag, spacing) {
|
|
61855
|
+
const nx = -dy, ny = dx;
|
|
61856
|
+
if (Math.abs(nx) <= 1e-6) return { oStart: -diag, oEnd: diag };
|
|
61857
|
+
const cx0 = Math.max(x2, 0), cy0 = Math.max(y2, 0);
|
|
61858
|
+
const cx1 = Math.min(x2 + w2, width), cy1 = Math.min(y2 + h2, height);
|
|
61859
|
+
const cs = [cx0 * nx + cy0 * ny, cx1 * nx + cy0 * ny, cx0 * nx + cy1 * ny, cx1 * nx + cy1 * ny];
|
|
61860
|
+
const oOfC = (c2) => (c2 - x2 * nx - (y2 - diag * dy) * ny) / nx;
|
|
61861
|
+
const oA = oOfC(Math.min(...cs)), oB = oOfC(Math.max(...cs));
|
|
61862
|
+
return {
|
|
61863
|
+
oStart: Math.max(-diag, -diag + Math.floor((Math.min(oA, oB) + diag) / spacing) * spacing - spacing),
|
|
61864
|
+
oEnd: Math.min(diag, Math.max(oA, oB) + spacing)
|
|
61865
|
+
};
|
|
61866
|
+
}
|
|
61867
|
+
function zoneWorldBbox(zone, geom) {
|
|
61868
|
+
const bb = { minX: zone.x, minY: zone.y, maxX: zone.x + zone.width, maxY: zone.y + zone.height };
|
|
61869
|
+
if (geom !== void 0 && geom.type === "circle") {
|
|
61870
|
+
const cx2 = zone.x + zone.width / 2, cy = zone.y + zone.height / 2, r2 = geom.value.radius;
|
|
61871
|
+
bb.minX = Math.min(bb.minX, cx2 - r2);
|
|
61872
|
+
bb.minY = Math.min(bb.minY, cy - r2);
|
|
61873
|
+
bb.maxX = Math.max(bb.maxX, cx2 + r2);
|
|
61874
|
+
bb.maxY = Math.max(bb.maxY, cy + r2);
|
|
61875
|
+
} else if (geom !== void 0 && geom.type !== "rect") {
|
|
61876
|
+
const g2 = pointsBbox(geom.value.vertices);
|
|
61877
|
+
if (g2 !== null) {
|
|
61878
|
+
bb.minX = Math.min(bb.minX, g2.minX);
|
|
61879
|
+
bb.minY = Math.min(bb.minY, g2.minY);
|
|
61880
|
+
bb.maxX = Math.max(bb.maxX, g2.maxX);
|
|
61881
|
+
bb.maxY = Math.max(bb.maxY, g2.maxY);
|
|
61882
|
+
}
|
|
61883
|
+
}
|
|
61884
|
+
return bb;
|
|
61885
|
+
}
|
|
61730
61886
|
function paintSchematic(input) {
|
|
61731
61887
|
var _a2, _b, _c, _d;
|
|
61732
61888
|
const { ctx, value, cam, width, height, visibleItems, tiers, selected, centers, palette: p2 } = input;
|
|
61733
61889
|
const wx = (x2) => x2 * cam.ppu + cam.tx;
|
|
61734
61890
|
const wy = (y2) => y2 * cam.ppu + cam.ty;
|
|
61735
61891
|
const ppu = cam.ppu;
|
|
61736
|
-
|
|
61892
|
+
const viewBbox = viewportWorldBbox(cam, width, height);
|
|
61893
|
+
ctx.save();
|
|
61894
|
+
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
61895
|
+
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
|
61896
|
+
ctx.restore();
|
|
61737
61897
|
ctx.lineJoin = "round";
|
|
61738
61898
|
for (const zone of value.zones) {
|
|
61899
|
+
const geom = getSomeorUndefined(zone.geometry);
|
|
61900
|
+
if (!bboxOverlaps(zoneWorldBbox(zone, geom), viewBbox)) continue;
|
|
61739
61901
|
const pattern = zone.pattern;
|
|
61740
61902
|
const patternTone = (pattern.value.tone.type === "some" ? pattern.value.tone.value.type : void 0) ?? "muted";
|
|
61741
61903
|
const tint = resolveTint(p2, getSomeorUndefined(zone.color), (_a2 = getSomeorUndefined(zone.tone)) == null ? void 0 : _a2.type, toneRGB(p2, patternTone, "zone"));
|
|
61742
61904
|
const zbg = getSomeorUndefined(zone.bg);
|
|
61743
61905
|
const zFillAlpha = getSomeorUndefined(zone.fillOpacity) ?? 0.15;
|
|
61744
61906
|
const zWeight = getSomeorUndefined(zone.weight);
|
|
61745
|
-
const geom = getSomeorUndefined(zone.geometry);
|
|
61746
61907
|
const x2 = wx(zone.x), y2 = wy(zone.y), w2 = zone.width * ppu, h2 = zone.height * ppu;
|
|
61747
61908
|
const fillShape = () => {
|
|
61748
61909
|
if (zbg === void 0) return;
|
|
@@ -61790,8 +61951,8 @@ function paintSchematic(input) {
|
|
|
61790
61951
|
}
|
|
61791
61952
|
}
|
|
61792
61953
|
} else if (pattern.type === "hatch") {
|
|
61793
|
-
const spacing = pattern.value.spacing
|
|
61794
|
-
const angle = (pattern.value.angle
|
|
61954
|
+
const spacing = hatchSpacing(getSomeorUndefined(pattern.value.spacing) ?? 8);
|
|
61955
|
+
const angle = (getSomeorUndefined(pattern.value.angle) ?? 45) * Math.PI / 180;
|
|
61795
61956
|
ctx.save();
|
|
61796
61957
|
ctx.beginPath();
|
|
61797
61958
|
ctx.rect(x2, y2, w2, h2);
|
|
@@ -61808,8 +61969,9 @@ function paintSchematic(input) {
|
|
|
61808
61969
|
ctx.lineWidth = zWeight ?? 1;
|
|
61809
61970
|
const dx = Math.cos(angle), dy = Math.sin(angle);
|
|
61810
61971
|
const diag = Math.hypot(w2, h2);
|
|
61972
|
+
const { oStart, oEnd } = hatchSweepBounds(x2, y2, w2, h2, width, height, dx, dy, diag, spacing);
|
|
61811
61973
|
ctx.beginPath();
|
|
61812
|
-
for (let o2 =
|
|
61974
|
+
for (let o2 = oStart; o2 < oEnd; o2 += spacing) {
|
|
61813
61975
|
ctx.moveTo(x2 + o2, y2 - diag * dy);
|
|
61814
61976
|
ctx.lineTo(x2 + o2 + dx * 2 * diag, y2 - diag * dy + dy * 2 * diag);
|
|
61815
61977
|
}
|
|
@@ -61843,6 +62005,8 @@ function paintSchematic(input) {
|
|
|
61843
62005
|
for (const link2 of value.links) {
|
|
61844
62006
|
const from = centers.get(link2.from), to = centers.get(link2.to);
|
|
61845
62007
|
if (!from || !to) continue;
|
|
62008
|
+
const linkBbox = pointsBbox([from, ...link2.via, to]);
|
|
62009
|
+
if (linkBbox !== null && !bboxOverlaps(linkBbox, viewBbox)) continue;
|
|
61846
62010
|
const anchors = [from, ...link2.via, to].map((q) => ({ x: wx(q.x), y: wy(q.y) }));
|
|
61847
62011
|
const corner = link2.route.type === "orthogonal" ? link2.route.value.corner.type === "some" ? link2.route.value.corner.value : 8 : 0;
|
|
61848
62012
|
const pts = link2.route.type === "orthogonal" ? orthogonalize(anchors) : anchors;
|
|
@@ -61916,7 +62080,7 @@ function paintSchematic(input) {
|
|
|
61916
62080
|
const x2 = wx(item.x), y2 = wy(item.y);
|
|
61917
62081
|
if (tier === "dot") {
|
|
61918
62082
|
ctx.beginPath();
|
|
61919
|
-
ctx.arc(x2, y2,
|
|
62083
|
+
ctx.arc(x2, y2, MARKER_DOT_RADIUS, 0, Math.PI * 2);
|
|
61920
62084
|
ctx.fillStyle = tint;
|
|
61921
62085
|
ctx.fill();
|
|
61922
62086
|
ctx.lineWidth = 1.5;
|
|
@@ -61931,11 +62095,10 @@ function paintSchematic(input) {
|
|
|
61931
62095
|
}
|
|
61932
62096
|
continue;
|
|
61933
62097
|
}
|
|
61934
|
-
ctx.font =
|
|
61935
|
-
const
|
|
61936
|
-
|
|
61937
|
-
const
|
|
61938
|
-
const left = x2 - w2 / 2, top = y2 - h2 / 2;
|
|
62098
|
+
ctx.font = MARKER_LABEL_FONT;
|
|
62099
|
+
const box = markerHitbox(item, "label", cam, (t4) => ctx.measureText(t4).width);
|
|
62100
|
+
if (box.kind !== "rect") continue;
|
|
62101
|
+
const { left, top, w: w2, h: h2 } = box;
|
|
61939
62102
|
ctx.beginPath();
|
|
61940
62103
|
(_d = ctx.roundRect) == null ? void 0 : _d.call(ctx, left, top, w2, h2, h2 / 2);
|
|
61941
62104
|
if (!ctx.roundRect) ctx.rect(left, top, w2, h2);
|
|
@@ -61945,12 +62108,12 @@ function paintSchematic(input) {
|
|
|
61945
62108
|
ctx.strokeStyle = isSel ? css(p2.fg) : css(p2.borderStrong);
|
|
61946
62109
|
ctx.stroke();
|
|
61947
62110
|
ctx.beginPath();
|
|
61948
|
-
ctx.arc(left +
|
|
62111
|
+
ctx.arc(left + MARKER_PIN_PAD_X + MARKER_PIN_DOT_W / 2, y2, MARKER_PIN_DOT_W / 2, 0, Math.PI * 2);
|
|
61949
62112
|
ctx.fillStyle = tint;
|
|
61950
62113
|
ctx.fill();
|
|
61951
62114
|
ctx.fillStyle = css(p2.fg);
|
|
61952
62115
|
ctx.textBaseline = "middle";
|
|
61953
|
-
ctx.fillText(item.label, left +
|
|
62116
|
+
ctx.fillText(item.label, left + MARKER_PIN_PAD_X + MARKER_PIN_DOT_W + MARKER_PIN_GAP, y2);
|
|
61954
62117
|
}
|
|
61955
62118
|
}
|
|
61956
62119
|
const TOKENS = {
|
|
@@ -61997,7 +62160,6 @@ const SchematicPaletteProbe = React.memo(function SchematicPaletteProbe2({ onRes
|
|
|
61997
62160
|
});
|
|
61998
62161
|
library$1.add(icons$2);
|
|
61999
62162
|
const schematicEqual = east.equalFor(internal.Schematic.Types.Schematic);
|
|
62000
|
-
const IDENTITY = { zoom: 1, tx: 0, ty: 0 };
|
|
62001
62163
|
const MAX_ZOOM = 40;
|
|
62002
62164
|
const LOD_CARD_PPU = 30;
|
|
62003
62165
|
const LOD_LABEL_PPU = 16;
|
|
@@ -62102,7 +62264,7 @@ function declutterTiers(items, tree, baseLod, ppu, selected) {
|
|
|
62102
62264
|
if (selected !== null && tiers.has(selected)) tiers.set(selected, baseLod);
|
|
62103
62265
|
return tiers;
|
|
62104
62266
|
}
|
|
62105
|
-
const EastChakraSchematic = React.memo(function EastChakraSchematic2({ value }) {
|
|
62267
|
+
const EastChakraSchematic = React.memo(function EastChakraSchematic2({ value, storageKey }) {
|
|
62106
62268
|
const styles2 = react.useSlotRecipe({ key: "schematic" })();
|
|
62107
62269
|
const { width: W2, height: H } = value.extent;
|
|
62108
62270
|
const onSelectFn = React.useMemo(() => getSomeorUndefined(value.onSelect), [value.onSelect]);
|
|
@@ -62112,14 +62274,17 @@ const EastChakraSchematic = React.memo(function EastChakraSchematic2({ value })
|
|
|
62112
62274
|
const showMinimap = getSomeorUndefined(value.minimap) ?? value.items.length >= MINIMAP_AUTO;
|
|
62113
62275
|
const fixedHeight = getSomeorUndefined(value.height);
|
|
62114
62276
|
const [selected, setSelected] = React.useState(null);
|
|
62115
|
-
const [view, setView] = React.useState(IDENTITY);
|
|
62116
62277
|
const [query, setQuery] = React.useState("");
|
|
62117
62278
|
const [openZone, setOpenZone] = React.useState(null);
|
|
62118
|
-
const
|
|
62279
|
+
const { state: persisted, setState: setPersisted } = usePersistedState(storageKey, { navCollapsed: false });
|
|
62280
|
+
const navCollapsed = persisted.navCollapsed;
|
|
62281
|
+
const setNavCollapsed = React.useCallback((next) => setPersisted((prev) => ({ ...prev, navCollapsed: next })), [setPersisted]);
|
|
62119
62282
|
const [palette, setPalette] = React.useState(null);
|
|
62120
62283
|
const navTreeRef = React.useRef(null);
|
|
62121
62284
|
const canvasRef = React.useRef(null);
|
|
62122
62285
|
const drawRef = React.useRef(null);
|
|
62286
|
+
const cardLayerRef = React.useRef(null);
|
|
62287
|
+
const gridRef = React.useRef(null);
|
|
62123
62288
|
const [size, setSize] = React.useState(null);
|
|
62124
62289
|
React.useLayoutEffect(() => {
|
|
62125
62290
|
const el = canvasRef.current;
|
|
@@ -62130,10 +62295,30 @@ const EastChakraSchematic = React.memo(function EastChakraSchematic2({ value })
|
|
|
62130
62295
|
ro.observe(el);
|
|
62131
62296
|
return () => ro.disconnect();
|
|
62132
62297
|
}, []);
|
|
62298
|
+
const [dpr, setDpr] = React.useState(() => typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1);
|
|
62299
|
+
React.useEffect(() => {
|
|
62300
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
62301
|
+
let cleanup = () => {
|
|
62302
|
+
};
|
|
62303
|
+
const subscribe2 = () => {
|
|
62304
|
+
const mql = window.matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);
|
|
62305
|
+
const onChange2 = () => {
|
|
62306
|
+
cleanup();
|
|
62307
|
+
setDpr(window.devicePixelRatio || 1);
|
|
62308
|
+
subscribe2();
|
|
62309
|
+
};
|
|
62310
|
+
mql.addEventListener("change", onChange2);
|
|
62311
|
+
cleanup = () => mql.removeEventListener("change", onChange2);
|
|
62312
|
+
};
|
|
62313
|
+
subscribe2();
|
|
62314
|
+
return () => cleanup();
|
|
62315
|
+
}, []);
|
|
62316
|
+
const cameraRef = React.useRef(IDENTITY);
|
|
62317
|
+
const modeRef = React.useRef("idle");
|
|
62318
|
+
const animRef = React.useRef(null);
|
|
62319
|
+
const [cameraSnapshot, setCameraSnapshot] = React.useState(IDENTITY);
|
|
62133
62320
|
const fit = size !== null ? Math.min(size.w / W2, size.h / H) : 0;
|
|
62134
|
-
const ppu = fit *
|
|
62135
|
-
const wx = (x2) => x2 * ppu + view.tx;
|
|
62136
|
-
const wy = (y2) => y2 * ppu + view.ty;
|
|
62321
|
+
const ppu = fit * cameraSnapshot.zoom;
|
|
62137
62322
|
const centers = React.useMemo(() => {
|
|
62138
62323
|
const out = /* @__PURE__ */ new Map();
|
|
62139
62324
|
for (const item of value.items) out.set(item.key, { x: item.x, y: item.y });
|
|
@@ -62166,12 +62351,12 @@ const EastChakraSchematic = React.memo(function EastChakraSchematic2({ value })
|
|
|
62166
62351
|
const visibleItems = React.useMemo(() => {
|
|
62167
62352
|
if (size === null || ppu === 0) return [];
|
|
62168
62353
|
return index2.search({
|
|
62169
|
-
minX: -
|
|
62170
|
-
minY: -
|
|
62171
|
-
maxX: (size.w -
|
|
62172
|
-
maxY: (size.h -
|
|
62354
|
+
minX: -cameraSnapshot.tx / ppu,
|
|
62355
|
+
minY: -cameraSnapshot.ty / ppu,
|
|
62356
|
+
maxX: (size.w - cameraSnapshot.tx) / ppu,
|
|
62357
|
+
maxY: (size.h - cameraSnapshot.ty) / ppu
|
|
62173
62358
|
}).map((b2) => b2.item);
|
|
62174
|
-
}, [index2, size, ppu,
|
|
62359
|
+
}, [index2, size, ppu, cameraSnapshot]);
|
|
62175
62360
|
const nav = React.useMemo(() => buildNavTree(value.zones, value.items), [value.zones, value.items]);
|
|
62176
62361
|
const zoneDisplay = React.useMemo(() => {
|
|
62177
62362
|
const counts = /* @__PURE__ */ new Map();
|
|
@@ -62197,106 +62382,15 @@ const EastChakraSchematic = React.memo(function EastChakraSchematic2({ value })
|
|
|
62197
62382
|
const indexOf = new Map(ordered.map((item, i2) => [item.key, i2]));
|
|
62198
62383
|
return { ordered, indexOf };
|
|
62199
62384
|
}, [nav]);
|
|
62200
|
-
const animRef = React.useRef(null);
|
|
62201
|
-
const viewRef = React.useRef(view);
|
|
62202
|
-
viewRef.current = view;
|
|
62203
|
-
const flyTo = React.useCallback((rect) => {
|
|
62204
|
-
if (size === null || fit === 0) return;
|
|
62205
|
-
const pad2 = 1.18;
|
|
62206
|
-
const zoom = Math.min(MAX_ZOOM, Math.min(size.w / (rect.w * pad2 * fit), size.h / (rect.h * pad2 * fit)));
|
|
62207
|
-
const target = {
|
|
62208
|
-
zoom,
|
|
62209
|
-
tx: size.w / 2 - (rect.x + rect.w / 2) * fit * zoom,
|
|
62210
|
-
ty: size.h / 2 - (rect.y + rect.h / 2) * fit * zoom
|
|
62211
|
-
};
|
|
62212
|
-
const from = viewRef.current;
|
|
62213
|
-
const start = performance.now();
|
|
62214
|
-
if (animRef.current !== null) cancelAnimationFrame(animRef.current);
|
|
62215
|
-
const step = (now) => {
|
|
62216
|
-
const t4 = Math.min(1, (now - start) / 350);
|
|
62217
|
-
const e3 = 1 - Math.pow(1 - t4, 3);
|
|
62218
|
-
setView({
|
|
62219
|
-
zoom: from.zoom + (target.zoom - from.zoom) * e3,
|
|
62220
|
-
tx: from.tx + (target.tx - from.tx) * e3,
|
|
62221
|
-
ty: from.ty + (target.ty - from.ty) * e3
|
|
62222
|
-
});
|
|
62223
|
-
if (t4 < 1) animRef.current = requestAnimationFrame(step);
|
|
62224
|
-
};
|
|
62225
|
-
animRef.current = requestAnimationFrame(step);
|
|
62226
|
-
}, [size, fit]);
|
|
62227
|
-
React.useEffect(() => () => {
|
|
62228
|
-
if (animRef.current !== null) cancelAnimationFrame(animRef.current);
|
|
62229
|
-
}, []);
|
|
62230
|
-
const flyToItem = React.useCallback((item) => {
|
|
62231
|
-
flyTo({ x: item.x - 4, y: item.y - 3, w: 8, h: 6 });
|
|
62232
|
-
setSelected(item.key);
|
|
62233
|
-
setOpenZone(nav.zoneOf.get(item.key) ?? null);
|
|
62234
|
-
if (onSelectFn) queueMicrotask(() => onSelectFn(item.key));
|
|
62235
|
-
}, [flyTo, nav.zoneOf, onSelectFn]);
|
|
62236
|
-
const stepSelection = React.useCallback((delta) => {
|
|
62237
|
-
const { ordered, indexOf } = itemOrder;
|
|
62238
|
-
if (ordered.length === 0) return;
|
|
62239
|
-
const idx = selected !== null ? indexOf.get(selected) ?? -1 : -1;
|
|
62240
|
-
flyToItem(ordered[(idx + delta + ordered.length) % ordered.length]);
|
|
62241
|
-
}, [itemOrder, selected, flyToItem]);
|
|
62242
|
-
const flyToZone = React.useCallback((zone) => {
|
|
62243
|
-
flyTo({ x: zone.x, y: zone.y, w: zone.width, h: zone.height });
|
|
62244
|
-
setOpenZone(zone.key);
|
|
62245
|
-
}, [flyTo]);
|
|
62246
|
-
React.useEffect(() => {
|
|
62247
|
-
const el = canvasRef.current;
|
|
62248
|
-
if (!el) return;
|
|
62249
|
-
const onWheel = (e3) => {
|
|
62250
|
-
e3.preventDefault();
|
|
62251
|
-
const rect = el.getBoundingClientRect();
|
|
62252
|
-
const px = e3.clientX - rect.left, py = e3.clientY - rect.top;
|
|
62253
|
-
setView((prev) => {
|
|
62254
|
-
const zoom = Math.max(1, Math.min(MAX_ZOOM, prev.zoom * Math.exp(-e3.deltaY * 15e-4)));
|
|
62255
|
-
const k2 = zoom / prev.zoom;
|
|
62256
|
-
const next = { zoom, tx: px - k2 * (px - prev.tx), ty: py - k2 * (py - prev.ty) };
|
|
62257
|
-
return zoom === 1 ? IDENTITY : next;
|
|
62258
|
-
});
|
|
62259
|
-
};
|
|
62260
|
-
el.addEventListener("wheel", onWheel, { passive: false });
|
|
62261
|
-
return () => el.removeEventListener("wheel", onWheel);
|
|
62262
|
-
}, []);
|
|
62263
|
-
const panRef = React.useRef(null);
|
|
62264
|
-
const movedRef = React.useRef(false);
|
|
62265
|
-
const onCanvasPointerDown = React.useCallback((e3) => {
|
|
62266
|
-
if (e3.button !== 0) return;
|
|
62267
|
-
if (e3.target.closest("button") !== null) return;
|
|
62268
|
-
panRef.current = { x: e3.clientX, y: e3.clientY, tx: viewRef.current.tx, ty: viewRef.current.ty };
|
|
62269
|
-
movedRef.current = false;
|
|
62270
|
-
e3.currentTarget.setPointerCapture(e3.pointerId);
|
|
62271
|
-
}, []);
|
|
62272
|
-
const onCanvasPointerMove = React.useCallback((e3) => {
|
|
62273
|
-
const pan = panRef.current;
|
|
62274
|
-
if (!pan) return;
|
|
62275
|
-
if (Math.abs(e3.clientX - pan.x) > 4 || Math.abs(e3.clientY - pan.y) > 4) movedRef.current = true;
|
|
62276
|
-
setView((prev) => ({ ...prev, tx: pan.tx + e3.clientX - pan.x, ty: pan.ty + e3.clientY - pan.y }));
|
|
62277
|
-
}, []);
|
|
62278
|
-
const onCanvasPointerUp = React.useCallback(() => {
|
|
62279
|
-
panRef.current = null;
|
|
62280
|
-
}, []);
|
|
62281
|
-
const resetView = React.useCallback(() => setView(IDENTITY), []);
|
|
62282
|
-
const zoomBy = React.useCallback((factor) => {
|
|
62283
|
-
if (size === null) return;
|
|
62284
|
-
const px = size.w / 2, py = size.h / 2;
|
|
62285
|
-
setView((prev) => {
|
|
62286
|
-
const zoom = Math.max(1, Math.min(MAX_ZOOM, prev.zoom * factor));
|
|
62287
|
-
const k2 = zoom / prev.zoom;
|
|
62288
|
-
return zoom === 1 ? IDENTITY : { zoom, tx: px - k2 * (px - prev.tx), ty: py - k2 * (py - prev.ty) };
|
|
62289
|
-
});
|
|
62290
|
-
}, [size]);
|
|
62291
62385
|
const [currentZone, setCurrentZone] = React.useState(void 0);
|
|
62292
62386
|
React.useEffect(() => {
|
|
62293
62387
|
const SPY_ENTER = 0.3, SPY_STAY = 0.18, SPY_MARGIN = 0.15;
|
|
62294
|
-
if (size === null || ppu === 0 ||
|
|
62388
|
+
if (size === null || ppu === 0 || cameraSnapshot.zoom <= 1.05) {
|
|
62295
62389
|
setCurrentZone(void 0);
|
|
62296
62390
|
return;
|
|
62297
62391
|
}
|
|
62298
|
-
const vx0 = -
|
|
62299
|
-
const vx1 = (size.w -
|
|
62392
|
+
const vx0 = -cameraSnapshot.tx / ppu, vy0 = -cameraSnapshot.ty / ppu;
|
|
62393
|
+
const vx1 = (size.w - cameraSnapshot.tx) / ppu, vy1 = (size.h - cameraSnapshot.ty) / ppu;
|
|
62300
62394
|
const viewArea = (vx1 - vx0) * (vy1 - vy0);
|
|
62301
62395
|
const scoreOf = (z) => {
|
|
62302
62396
|
const ix = Math.max(0, Math.min(vx1, z.x + z.width) - Math.max(vx0, z.x));
|
|
@@ -62320,73 +62414,321 @@ const EastChakraSchematic = React.memo(function EastChakraSchematic2({ value })
|
|
|
62320
62414
|
}
|
|
62321
62415
|
return bestScore >= SPY_ENTER ? best : void 0;
|
|
62322
62416
|
});
|
|
62323
|
-
}, [size, ppu,
|
|
62417
|
+
}, [size, ppu, cameraSnapshot, value.zones]);
|
|
62324
62418
|
const lod = ppu >= LOD_CARD_PPU ? "card" : ppu >= LOD_LABEL_PPU ? "label" : "dot";
|
|
62325
62419
|
const centerTree = React.useMemo(() => buildCenterTree(visibleItems), [visibleItems]);
|
|
62326
62420
|
const tiers = React.useMemo(() => declutterTiers(visibleItems, centerTree, lod, ppu, selected), [visibleItems, centerTree, lod, ppu, selected]);
|
|
62327
62421
|
const lowerQuery = query.trim().toLowerCase();
|
|
62328
62422
|
const searchHits = React.useMemo(() => lowerQuery === "" ? [] : value.items.filter((i2) => i2.key.toLowerCase().includes(lowerQuery) || i2.label.toLowerCase().includes(lowerQuery)).slice(0, 12), [lowerQuery, value.items]);
|
|
62329
62423
|
const scaleLen = ppu > 0 ? niceScaleLength(ppu, 100) : 0;
|
|
62330
|
-
React.
|
|
62331
|
-
|
|
62332
|
-
|
|
62333
|
-
|
|
62334
|
-
|
|
62424
|
+
const valueRef = React.useRef(value);
|
|
62425
|
+
valueRef.current = value;
|
|
62426
|
+
const sizeRef = React.useRef(size);
|
|
62427
|
+
sizeRef.current = size;
|
|
62428
|
+
const fitRef = React.useRef(fit);
|
|
62429
|
+
fitRef.current = fit;
|
|
62430
|
+
const dprRef = React.useRef(dpr);
|
|
62431
|
+
dprRef.current = dpr;
|
|
62432
|
+
const paletteRef = React.useRef(palette);
|
|
62433
|
+
paletteRef.current = palette;
|
|
62434
|
+
const visibleItemsRef = React.useRef(visibleItems);
|
|
62435
|
+
visibleItemsRef.current = visibleItems;
|
|
62436
|
+
const tiersRef = React.useRef(tiers);
|
|
62437
|
+
tiersRef.current = tiers;
|
|
62438
|
+
const selectedRef = React.useRef(selected);
|
|
62439
|
+
selectedRef.current = selected;
|
|
62440
|
+
const centersRef = React.useRef(centers);
|
|
62441
|
+
centersRef.current = centers;
|
|
62442
|
+
const openZoneRef = React.useRef(openZone);
|
|
62443
|
+
openZoneRef.current = openZone;
|
|
62444
|
+
const lastPaintRef = React.useRef(null);
|
|
62445
|
+
const applyCamera = React.useCallback(() => {
|
|
62446
|
+
const sz = sizeRef.current;
|
|
62447
|
+
if (sz === null) return;
|
|
62448
|
+
const cam = cameraRef.current;
|
|
62449
|
+
const ppuLive = fitRef.current * cam.zoom;
|
|
62450
|
+
const dprLive = dprRef.current;
|
|
62451
|
+
const layer = cardLayerRef.current;
|
|
62452
|
+
if (layer !== null) {
|
|
62453
|
+
layer.style.setProperty("--cam-ppu", String(ppuLive));
|
|
62454
|
+
layer.style.setProperty("--cam-tx", `${cam.tx}px`);
|
|
62455
|
+
layer.style.setProperty("--cam-ty", `${cam.ty}px`);
|
|
62456
|
+
}
|
|
62457
|
+
const grid = gridRef.current;
|
|
62458
|
+
if (grid !== null) {
|
|
62459
|
+
const len = ppuLive > 0 ? niceScaleLength(ppuLive, 100) : 0;
|
|
62460
|
+
if (len > 0) {
|
|
62461
|
+
const major = len * ppuLive, minor = major / 5;
|
|
62462
|
+
grid.style.backgroundSize = `${major}px ${major}px, ${major}px ${major}px, ${minor}px ${minor}px, ${minor}px ${minor}px`;
|
|
62463
|
+
grid.style.backgroundPosition = `${cam.tx}px 0, 0 ${cam.ty}px, ${cam.tx}px 0, 0 ${cam.ty}px`;
|
|
62464
|
+
}
|
|
62465
|
+
}
|
|
62466
|
+
const canvas = drawRef.current, pal = paletteRef.current;
|
|
62467
|
+
if (canvas === null || pal === null) return;
|
|
62468
|
+
const last = lastPaintRef.current;
|
|
62469
|
+
if (last !== null && last.zoom === cam.zoom && last.tx === cam.tx && last.ty === cam.ty && last.vis === visibleItemsRef.current && last.tiers === tiersRef.current && last.sel === selectedRef.current && last.pal === pal && last.w === sz.w && last.h === sz.h && last.dpr === dprLive && last.val === valueRef.current) return;
|
|
62470
|
+
lastPaintRef.current = {
|
|
62471
|
+
zoom: cam.zoom,
|
|
62472
|
+
tx: cam.tx,
|
|
62473
|
+
ty: cam.ty,
|
|
62474
|
+
vis: visibleItemsRef.current,
|
|
62475
|
+
tiers: tiersRef.current,
|
|
62476
|
+
sel: selectedRef.current,
|
|
62477
|
+
pal,
|
|
62478
|
+
w: sz.w,
|
|
62479
|
+
h: sz.h,
|
|
62480
|
+
dpr: dprLive,
|
|
62481
|
+
val: valueRef.current
|
|
62482
|
+
};
|
|
62483
|
+
const bw = Math.round(sz.w * dprLive), bh = Math.round(sz.h * dprLive);
|
|
62335
62484
|
if (canvas.width !== bw || canvas.height !== bh) {
|
|
62336
62485
|
canvas.width = bw;
|
|
62337
62486
|
canvas.height = bh;
|
|
62338
62487
|
}
|
|
62339
62488
|
const ctx = canvas.getContext("2d");
|
|
62340
62489
|
if (ctx === null) return;
|
|
62341
|
-
ctx.setTransform(
|
|
62490
|
+
ctx.setTransform(dprLive, 0, 0, dprLive, 0, 0);
|
|
62342
62491
|
paintSchematic({
|
|
62343
62492
|
ctx,
|
|
62344
|
-
value,
|
|
62345
|
-
cam: { ppu, tx:
|
|
62346
|
-
width:
|
|
62347
|
-
height:
|
|
62348
|
-
visibleItems,
|
|
62349
|
-
tiers,
|
|
62350
|
-
selected,
|
|
62351
|
-
centers,
|
|
62352
|
-
palette
|
|
62493
|
+
value: valueRef.current,
|
|
62494
|
+
cam: { ppu: ppuLive, tx: cam.tx, ty: cam.ty },
|
|
62495
|
+
width: sz.w,
|
|
62496
|
+
height: sz.h,
|
|
62497
|
+
visibleItems: visibleItemsRef.current,
|
|
62498
|
+
tiers: tiersRef.current,
|
|
62499
|
+
selected: selectedRef.current,
|
|
62500
|
+
centers: centersRef.current,
|
|
62501
|
+
palette: pal
|
|
62353
62502
|
});
|
|
62354
|
-
}, [
|
|
62355
|
-
const
|
|
62356
|
-
|
|
62357
|
-
|
|
62358
|
-
|
|
62359
|
-
|
|
62360
|
-
|
|
62361
|
-
|
|
62503
|
+
}, []);
|
|
62504
|
+
const coalescerRef = React.useRef(null);
|
|
62505
|
+
if (coalescerRef.current === null && typeof requestAnimationFrame !== "undefined") {
|
|
62506
|
+
coalescerRef.current = makeRafCoalescer(requestAnimationFrame, cancelAnimationFrame);
|
|
62507
|
+
}
|
|
62508
|
+
const requestRender = React.useCallback(() => {
|
|
62509
|
+
const run = () => {
|
|
62510
|
+
applyCamera();
|
|
62511
|
+
const cam = cameraRef.current;
|
|
62512
|
+
setCameraSnapshot((prev) => prev.zoom === cam.zoom && prev.tx === cam.tx && prev.ty === cam.ty ? prev : { zoom: cam.zoom, tx: cam.tx, ty: cam.ty });
|
|
62513
|
+
};
|
|
62514
|
+
const cz = coalescerRef.current;
|
|
62515
|
+
if (cz !== null) cz.request(run);
|
|
62516
|
+
else run();
|
|
62517
|
+
}, [applyCamera]);
|
|
62518
|
+
const cancelFlyRaf = React.useCallback(() => {
|
|
62519
|
+
if (animRef.current !== null) {
|
|
62520
|
+
cancelAnimationFrame(animRef.current);
|
|
62521
|
+
animRef.current = null;
|
|
62522
|
+
}
|
|
62523
|
+
}, []);
|
|
62524
|
+
const transition = React.useCallback((event) => {
|
|
62525
|
+
if (cancelsFly(modeRef.current, event)) cancelFlyRaf();
|
|
62526
|
+
modeRef.current = nextMode(modeRef.current, event);
|
|
62527
|
+
}, [cancelFlyRaf]);
|
|
62528
|
+
const flyTo = React.useCallback((rect) => {
|
|
62529
|
+
if (sizeRef.current === null || fitRef.current === 0) return;
|
|
62530
|
+
const pad2 = 1.18;
|
|
62531
|
+
const frame = () => {
|
|
62532
|
+
const sz = sizeRef.current, ft = fitRef.current;
|
|
62533
|
+
if (sz === null || ft === 0) return null;
|
|
62534
|
+
const zoom = Math.min(MAX_ZOOM, Math.min(sz.w / (rect.w * pad2 * ft), sz.h / (rect.h * pad2 * ft)));
|
|
62535
|
+
return {
|
|
62536
|
+
zoom,
|
|
62537
|
+
tx: sz.w / 2 - (rect.x + rect.w / 2) * ft * zoom,
|
|
62538
|
+
ty: sz.h / 2 - (rect.y + rect.h / 2) * ft * zoom
|
|
62539
|
+
};
|
|
62540
|
+
};
|
|
62541
|
+
const from = { ...cameraRef.current };
|
|
62542
|
+
const start = performance.now();
|
|
62543
|
+
if (animRef.current !== null) cancelAnimationFrame(animRef.current);
|
|
62544
|
+
transition("flyStart");
|
|
62545
|
+
const step = (now) => {
|
|
62546
|
+
const t4 = Math.min(1, (now - start) / 350);
|
|
62547
|
+
const e3 = 1 - Math.pow(1 - t4, 3);
|
|
62548
|
+
const target = frame();
|
|
62549
|
+
if (target !== null) {
|
|
62550
|
+
cameraRef.current = {
|
|
62551
|
+
zoom: from.zoom + (target.zoom - from.zoom) * e3,
|
|
62552
|
+
tx: from.tx + (target.tx - from.tx) * e3,
|
|
62553
|
+
ty: from.ty + (target.ty - from.ty) * e3
|
|
62554
|
+
};
|
|
62555
|
+
applyCamera();
|
|
62556
|
+
const cam = cameraRef.current;
|
|
62557
|
+
setCameraSnapshot({ zoom: cam.zoom, tx: cam.tx, ty: cam.ty });
|
|
62558
|
+
}
|
|
62559
|
+
if (t4 < 1) {
|
|
62560
|
+
animRef.current = requestAnimationFrame(step);
|
|
62561
|
+
} else {
|
|
62562
|
+
animRef.current = null;
|
|
62563
|
+
transition("flyEnd");
|
|
62564
|
+
}
|
|
62565
|
+
};
|
|
62566
|
+
animRef.current = requestAnimationFrame(step);
|
|
62567
|
+
}, [applyCamera, transition]);
|
|
62568
|
+
const zoneOverrideRef = React.useRef(null);
|
|
62569
|
+
const flyToItem = React.useCallback((item) => {
|
|
62570
|
+
flyTo({ x: item.x - 4, y: item.y - 3, w: 8, h: 6 });
|
|
62571
|
+
setSelected(item.key);
|
|
62572
|
+
zoneOverrideRef.current = null;
|
|
62573
|
+
setOpenZone(nav.zoneOf.get(item.key) ?? null);
|
|
62574
|
+
if (onSelectFn) queueMicrotask(() => onSelectFn(item.key));
|
|
62575
|
+
}, [flyTo, nav.zoneOf, onSelectFn]);
|
|
62576
|
+
const stepSelection = React.useCallback((delta) => {
|
|
62577
|
+
const { ordered, indexOf } = itemOrder;
|
|
62578
|
+
if (ordered.length === 0) return;
|
|
62579
|
+
const idx = selected !== null ? indexOf.get(selected) ?? -1 : -1;
|
|
62580
|
+
flyToItem(ordered[(idx + delta + ordered.length) % ordered.length]);
|
|
62581
|
+
}, [itemOrder, selected, flyToItem]);
|
|
62582
|
+
const flyToZone = React.useCallback((zone) => {
|
|
62583
|
+
flyTo({ x: zone.x, y: zone.y, w: zone.width, h: zone.height });
|
|
62584
|
+
zoneOverrideRef.current = null;
|
|
62585
|
+
setOpenZone(zone.key);
|
|
62586
|
+
}, [flyTo]);
|
|
62587
|
+
React.useEffect(() => {
|
|
62588
|
+
const el = canvasRef.current;
|
|
62589
|
+
if (!el) return;
|
|
62590
|
+
const onWheel = (e3) => {
|
|
62591
|
+
e3.preventDefault();
|
|
62592
|
+
const rect = el.getBoundingClientRect();
|
|
62593
|
+
const px = e3.clientX - rect.left, py = e3.clientY - rect.top;
|
|
62594
|
+
transition("wheel");
|
|
62595
|
+
cameraRef.current = zoomAbout(cameraRef.current, Math.exp(-e3.deltaY * 15e-4), px, py, 1, MAX_ZOOM);
|
|
62596
|
+
requestRender();
|
|
62597
|
+
};
|
|
62598
|
+
el.addEventListener("wheel", onWheel, { passive: false });
|
|
62599
|
+
return () => el.removeEventListener("wheel", onWheel);
|
|
62600
|
+
}, [transition, requestRender]);
|
|
62601
|
+
const panRef = React.useRef(null);
|
|
62602
|
+
const movedRef = React.useRef(false);
|
|
62603
|
+
const pickAt = React.useCallback((host, clientX, clientY) => {
|
|
62604
|
+
var _a2, _b, _c;
|
|
62605
|
+
const sz = sizeRef.current;
|
|
62606
|
+
if (sz === null) return false;
|
|
62607
|
+
const cam = cameraRef.current;
|
|
62608
|
+
const ppuLive = fitRef.current * cam.zoom;
|
|
62609
|
+
if (ppuLive === 0) return false;
|
|
62610
|
+
const rect = host.getBoundingClientRect();
|
|
62611
|
+
const sx = clientX - rect.left, sy = clientY - rect.top;
|
|
62612
|
+
const wxp = (sx - cam.tx) / ppuLive, wyp = (sy - cam.ty) / ppuLive;
|
|
62613
|
+
const select = (key) => {
|
|
62614
|
+
setSelected(key);
|
|
62615
|
+
if (onSelectFn) queueMicrotask(() => onSelectFn(key));
|
|
62616
|
+
};
|
|
62617
|
+
for (const it of visibleItems) {
|
|
62362
62618
|
if ((tiers.get(it.key) ?? lod) !== "card") continue;
|
|
62363
62619
|
const fp = getSomeorUndefined(it.footprint);
|
|
62364
62620
|
if (fp === void 0 || fp.type === "rect") continue;
|
|
62365
62621
|
const hit = fp.type === "circle" ? Math.hypot(wxp - it.x, wyp - it.y) <= fp.value.radius : pointInPolygon(wxp, wyp, fp.value.vertices);
|
|
62366
62622
|
if (hit) {
|
|
62367
|
-
|
|
62368
|
-
|
|
62369
|
-
|
|
62623
|
+
select(it.key);
|
|
62624
|
+
return true;
|
|
62625
|
+
}
|
|
62626
|
+
}
|
|
62627
|
+
if (typeof document !== "undefined") {
|
|
62628
|
+
const cardKey = (_b = (_a2 = document.elementFromPoint(clientX, clientY)) == null ? void 0 : _a2.closest("[data-card-key]")) == null ? void 0 : _b.getAttribute("data-card-key");
|
|
62629
|
+
if (cardKey !== null && cardKey !== void 0) {
|
|
62630
|
+
select(cardKey);
|
|
62631
|
+
return true;
|
|
62370
62632
|
}
|
|
62371
62633
|
}
|
|
62634
|
+
const ctx = ((_c = drawRef.current) == null ? void 0 : _c.getContext("2d")) ?? null;
|
|
62635
|
+
if (ctx !== null) {
|
|
62636
|
+
ctx.save();
|
|
62637
|
+
ctx.font = MARKER_LABEL_FONT;
|
|
62638
|
+
}
|
|
62639
|
+
const camScreen = { ppu: ppuLive, tx: cam.tx, ty: cam.ty };
|
|
62372
62640
|
let best = null, bestD = Infinity;
|
|
62373
62641
|
for (const it of visibleItems) {
|
|
62374
62642
|
const tier = tiers.get(it.key) ?? lod;
|
|
62375
62643
|
if (tier === "card") continue;
|
|
62376
|
-
const
|
|
62377
|
-
|
|
62378
|
-
|
|
62644
|
+
const box = markerHitbox(it, tier, camScreen, (t4) => ctx !== null ? ctx.measureText(t4).width : t4.length * 6);
|
|
62645
|
+
if (!markerHit(box, sx, sy)) continue;
|
|
62646
|
+
const d2 = Math.hypot(it.x * ppuLive + cam.tx - sx, it.y * ppuLive + cam.ty - sy);
|
|
62647
|
+
if (d2 < bestD) {
|
|
62379
62648
|
best = it;
|
|
62380
62649
|
bestD = d2;
|
|
62381
62650
|
}
|
|
62382
62651
|
}
|
|
62383
|
-
if (
|
|
62384
|
-
|
|
62652
|
+
if (ctx !== null) ctx.restore();
|
|
62653
|
+
if (best !== null) {
|
|
62654
|
+
flyToItem(best);
|
|
62655
|
+
return true;
|
|
62656
|
+
}
|
|
62657
|
+
return false;
|
|
62658
|
+
}, [tiers, lod, visibleItems, onSelectFn, flyToItem]);
|
|
62659
|
+
const onCanvasPointerDown = React.useCallback((e3) => {
|
|
62660
|
+
if (e3.button !== 0) return;
|
|
62661
|
+
if (e3.target.closest("button") !== null) return;
|
|
62662
|
+
transition("pointerDown");
|
|
62663
|
+
panRef.current = { x: e3.clientX, y: e3.clientY, tx: cameraRef.current.tx, ty: cameraRef.current.ty };
|
|
62664
|
+
movedRef.current = false;
|
|
62665
|
+
e3.currentTarget.setPointerCapture(e3.pointerId);
|
|
62666
|
+
}, [transition]);
|
|
62667
|
+
const onCanvasPointerMove = React.useCallback((e3) => {
|
|
62668
|
+
const pan = panRef.current;
|
|
62669
|
+
if (!pan) return;
|
|
62670
|
+
if (Math.abs(e3.clientX - pan.x) > 4 || Math.abs(e3.clientY - pan.y) > 4) movedRef.current = true;
|
|
62671
|
+
cameraRef.current = { ...cameraRef.current, tx: pan.tx + e3.clientX - pan.x, ty: pan.ty + e3.clientY - pan.y };
|
|
62672
|
+
requestRender();
|
|
62673
|
+
}, [requestRender]);
|
|
62674
|
+
const endPan = React.useCallback(() => {
|
|
62675
|
+
if (panRef.current === null) return false;
|
|
62676
|
+
panRef.current = null;
|
|
62677
|
+
transition("pointerUp");
|
|
62678
|
+
return true;
|
|
62679
|
+
}, [transition]);
|
|
62680
|
+
const onCanvasPointerUp = React.useCallback((e3) => {
|
|
62681
|
+
const wasPress = endPan();
|
|
62682
|
+
if (!wasPress || movedRef.current) return;
|
|
62683
|
+
if (!pickAt(e3.currentTarget, e3.clientX, e3.clientY)) setSelected(null);
|
|
62684
|
+
}, [endPan, pickAt]);
|
|
62685
|
+
const onCanvasPointerCancel = React.useCallback(() => {
|
|
62686
|
+
movedRef.current = false;
|
|
62687
|
+
endPan();
|
|
62688
|
+
}, [endPan]);
|
|
62689
|
+
const resetView = React.useCallback(() => {
|
|
62690
|
+
transition("cancel");
|
|
62691
|
+
cameraRef.current = IDENTITY;
|
|
62692
|
+
requestRender();
|
|
62693
|
+
}, [transition, requestRender]);
|
|
62694
|
+
const zoomBy = React.useCallback((factor) => {
|
|
62695
|
+
const sz = sizeRef.current;
|
|
62696
|
+
if (sz === null) return;
|
|
62697
|
+
transition("zoom");
|
|
62698
|
+
cameraRef.current = zoomAbout(cameraRef.current, factor, sz.w / 2, sz.h / 2, 1, MAX_ZOOM);
|
|
62699
|
+
requestRender();
|
|
62700
|
+
}, [transition, requestRender]);
|
|
62701
|
+
const minimapDragRef = React.useRef(false);
|
|
62702
|
+
const minimapJump = React.useCallback((el, clientX, clientY) => {
|
|
62703
|
+
const sz = sizeRef.current;
|
|
62704
|
+
if (sz === null) return;
|
|
62705
|
+
const rect = el.getBoundingClientRect();
|
|
62706
|
+
const cx2 = (clientX - rect.left) / rect.width * W2;
|
|
62707
|
+
const cy = (clientY - rect.top) / rect.height * H;
|
|
62708
|
+
const cam = cameraRef.current;
|
|
62709
|
+
cameraRef.current = { ...cam, tx: sz.w / 2 - cx2 * fitRef.current * cam.zoom, ty: sz.h / 2 - cy * fitRef.current * cam.zoom };
|
|
62710
|
+
requestRender();
|
|
62711
|
+
}, [W2, H, requestRender]);
|
|
62712
|
+
React.useEffect(() => () => {
|
|
62713
|
+
var _a2;
|
|
62714
|
+
(_a2 = coalescerRef.current) == null ? void 0 : _a2.cancel();
|
|
62715
|
+
if (animRef.current !== null) cancelAnimationFrame(animRef.current);
|
|
62716
|
+
}, [animRef]);
|
|
62717
|
+
React.useLayoutEffect(() => {
|
|
62718
|
+
applyCamera();
|
|
62719
|
+
}, [applyCamera, value, visibleItems, tiers, selected, palette, size, dpr, fit, cameraSnapshot]);
|
|
62385
62720
|
const toggleZone = React.useCallback((key) => {
|
|
62386
|
-
|
|
62721
|
+
const collapsing = openZoneRef.current === key;
|
|
62722
|
+
zoneOverrideRef.current = collapsing ? key : null;
|
|
62723
|
+
setOpenZone(collapsing ? nav.parentOf.get(key) ?? null : key);
|
|
62387
62724
|
}, [nav.parentOf]);
|
|
62388
62725
|
React.useEffect(() => {
|
|
62389
|
-
if (currentZone
|
|
62726
|
+
if (currentZone === void 0) return;
|
|
62727
|
+
if (zoneOverrideRef.current !== null) {
|
|
62728
|
+
if (zoneOverrideRef.current === currentZone) return;
|
|
62729
|
+
zoneOverrideRef.current = null;
|
|
62730
|
+
}
|
|
62731
|
+
setOpenZone(currentZone);
|
|
62390
62732
|
}, [currentZone]);
|
|
62391
62733
|
const openPath = React.useMemo(() => {
|
|
62392
62734
|
const path2 = /* @__PURE__ */ new Set();
|
|
@@ -62411,7 +62753,15 @@ const EastChakraSchematic = React.memo(function EastChakraSchematic2({ value })
|
|
|
62411
62753
|
react.Box,
|
|
62412
62754
|
{
|
|
62413
62755
|
css: styles2.navZone,
|
|
62414
|
-
style: {
|
|
62756
|
+
style: {
|
|
62757
|
+
paddingLeft: `${8 + depth * 14}px`,
|
|
62758
|
+
// Pin this header below its ancestors' pinned headers
|
|
62759
|
+
// (each level offset by one row) so the whole path stays
|
|
62760
|
+
// visible while scrolling the zone's items; shallower
|
|
62761
|
+
// levels stack above deeper ones.
|
|
62762
|
+
top: `calc(var(--nav-row-h) * ${depth})`,
|
|
62763
|
+
zIndex: 30 - depth
|
|
62764
|
+
},
|
|
62415
62765
|
...node2.zone.key === currentZone ? { "data-current": "" } : {},
|
|
62416
62766
|
children: [
|
|
62417
62767
|
expandable ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -62500,32 +62850,20 @@ const EastChakraSchematic = React.memo(function EastChakraSchematic2({ value })
|
|
|
62500
62850
|
onPointerDown: onCanvasPointerDown,
|
|
62501
62851
|
onPointerMove: onCanvasPointerMove,
|
|
62502
62852
|
onPointerUp: onCanvasPointerUp,
|
|
62853
|
+
onPointerCancel: onCanvasPointerCancel,
|
|
62854
|
+
onLostPointerCapture: onCanvasPointerCancel,
|
|
62503
62855
|
onDoubleClick: resetView,
|
|
62504
62856
|
children: [
|
|
62505
|
-
showGrid && size !== null &&
|
|
62506
|
-
const major = scaleLen * ppu;
|
|
62507
|
-
const minor = major / 5;
|
|
62508
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
62509
|
-
react.Box,
|
|
62510
|
-
{
|
|
62511
|
-
css: styles2.grid,
|
|
62512
|
-
style: {
|
|
62513
|
-
backgroundSize: `${major}px ${major}px, ${major}px ${major}px, ${minor}px ${minor}px, ${minor}px ${minor}px`,
|
|
62514
|
-
backgroundPosition: `${view.tx}px 0, 0 ${view.ty}px, ${view.tx}px 0, 0 ${view.ty}px`
|
|
62515
|
-
}
|
|
62516
|
-
}
|
|
62517
|
-
);
|
|
62518
|
-
})(),
|
|
62857
|
+
showGrid && size !== null && /* @__PURE__ */ jsxRuntime.jsx(react.Box, { ref: gridRef, css: styles2.grid }),
|
|
62519
62858
|
size !== null && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
62520
62859
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
62521
62860
|
"canvas",
|
|
62522
62861
|
{
|
|
62523
62862
|
ref: drawRef,
|
|
62524
|
-
onClick: onCanvasClick,
|
|
62525
62863
|
style: { position: "absolute", inset: 0, width: "100%", height: "100%" }
|
|
62526
62864
|
}
|
|
62527
62865
|
),
|
|
62528
|
-
visibleItems.map((item) => {
|
|
62866
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.Box, { ref: cardLayerRef, css: styles2.cardLayer, children: visibleItems.map((item) => {
|
|
62529
62867
|
if ((tiers.get(item.key) ?? lod) !== "card") return null;
|
|
62530
62868
|
const tone = statusTone(item.status);
|
|
62531
62869
|
const isSelected = selected === item.key;
|
|
@@ -62539,17 +62877,14 @@ const EastChakraSchematic = React.memo(function EastChakraSchematic2({ value })
|
|
|
62539
62877
|
react.Box,
|
|
62540
62878
|
{
|
|
62541
62879
|
css: styles2.item,
|
|
62880
|
+
"data-card-key": item.key,
|
|
62542
62881
|
...isSelected ? { "data-selected": "" } : {},
|
|
62543
62882
|
style: {
|
|
62544
|
-
left:
|
|
62545
|
-
top:
|
|
62546
|
-
|
|
62547
|
-
|
|
62548
|
-
onClick: () => {
|
|
62549
|
-
setSelected(item.key);
|
|
62550
|
-
if (onSelectFn) queueMicrotask(() => onSelectFn(item.key));
|
|
62883
|
+
left: 0,
|
|
62884
|
+
top: 0,
|
|
62885
|
+
transform: `${cardTranslateCss(item.x, item.y)} translate(-50%, -50%)`,
|
|
62886
|
+
...typeof width === "number" ? { width: cardWidthCss(width) } : {}
|
|
62551
62887
|
},
|
|
62552
|
-
onPointerDown: (e3) => e3.stopPropagation(),
|
|
62553
62888
|
children: [
|
|
62554
62889
|
/* @__PURE__ */ jsxRuntime.jsxs(react.Box, { css: styles2.itemHead, children: [
|
|
62555
62890
|
iconDef !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(react.Box, { as: "span", css: styles2.itemIcon, children: /* @__PURE__ */ jsxRuntime.jsx(FontAwesomeIcon, { icon: iconDef }) }),
|
|
@@ -62563,7 +62898,7 @@ const EastChakraSchematic = React.memo(function EastChakraSchematic2({ value })
|
|
|
62563
62898
|
},
|
|
62564
62899
|
item.key
|
|
62565
62900
|
);
|
|
62566
|
-
}),
|
|
62901
|
+
}) }),
|
|
62567
62902
|
/* @__PURE__ */ jsxRuntime.jsxs(react.Box, { css: styles2.controls, children: [
|
|
62568
62903
|
/* @__PURE__ */ jsxRuntime.jsx(react.Box, { as: "button", css: styles2.controlButton, "aria-label": "Zoom in", title: "Zoom in (scroll)", onClick: () => zoomBy(1.5), children: /* @__PURE__ */ jsxRuntime.jsx(FontAwesomeIcon, { icon: faPlus }) }),
|
|
62569
62904
|
/* @__PURE__ */ jsxRuntime.jsx(react.Box, { as: "button", css: styles2.controlButton, "aria-label": "Zoom out", title: "Zoom out (scroll)", onClick: () => zoomBy(1 / 1.5), children: /* @__PURE__ */ jsxRuntime.jsx(FontAwesomeIcon, { icon: faMinus }) }),
|
|
@@ -62607,16 +62942,20 @@ const EastChakraSchematic = React.memo(function EastChakraSchematic2({ value })
|
|
|
62607
62942
|
style: { width: 150, height: 150 * H / W2 },
|
|
62608
62943
|
onPointerDown: (e3) => {
|
|
62609
62944
|
e3.stopPropagation();
|
|
62610
|
-
const
|
|
62611
|
-
|
|
62612
|
-
|
|
62613
|
-
|
|
62614
|
-
|
|
62615
|
-
|
|
62616
|
-
|
|
62617
|
-
|
|
62618
|
-
|
|
62619
|
-
|
|
62945
|
+
const el = e3.currentTarget;
|
|
62946
|
+
el.setPointerCapture(e3.pointerId);
|
|
62947
|
+
transition("cancel");
|
|
62948
|
+
minimapDragRef.current = true;
|
|
62949
|
+
minimapJump(el, e3.clientX, e3.clientY);
|
|
62950
|
+
},
|
|
62951
|
+
onPointerMove: (e3) => {
|
|
62952
|
+
if (!minimapDragRef.current) return;
|
|
62953
|
+
e3.stopPropagation();
|
|
62954
|
+
minimapJump(e3.currentTarget, e3.clientX, e3.clientY);
|
|
62955
|
+
},
|
|
62956
|
+
onPointerUp: (e3) => {
|
|
62957
|
+
minimapDragRef.current = false;
|
|
62958
|
+
e3.currentTarget.releasePointerCapture(e3.pointerId);
|
|
62620
62959
|
},
|
|
62621
62960
|
children: [
|
|
62622
62961
|
value.zones.map((zone) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -62637,8 +62976,8 @@ const EastChakraSchematic = React.memo(function EastChakraSchematic2({ value })
|
|
|
62637
62976
|
{
|
|
62638
62977
|
css: styles2.minimapViewport,
|
|
62639
62978
|
style: {
|
|
62640
|
-
left: `${Math.max(0, -
|
|
62641
|
-
top: `${Math.max(0, -
|
|
62979
|
+
left: `${Math.max(0, -cameraSnapshot.tx / ppu / W2 * 100)}%`,
|
|
62980
|
+
top: `${Math.max(0, -cameraSnapshot.ty / ppu / H * 100)}%`,
|
|
62642
62981
|
width: `${Math.min(100, size.w / ppu / W2 * 100)}%`,
|
|
62643
62982
|
height: `${Math.min(100, size.h / ppu / H * 100)}%`
|
|
62644
62983
|
}
|