Skip to content

Commit 6c616fa

Browse files
fix(f-canvas): scope layer ordering to its own containers
The first cut of `[fLayers]` made every layer container `position: absolute` with a `[style.z-index]` binding, which changed the containing block for the absolutely-positioned `.f-node` / `.f-group` descendants and broke positioning across many examples and consumer apps. Switch the strategy: keep the original SCSS shape (only `.f-connections-container` stays `position: absolute`, as it has been since before v18.6) and add `isolation: isolate` to each of the three containers so the per-element z-indices defined in `f-flow.component.scss` (`.f-node` 4, `.f-connection-content` 3, `.f-group` 1) stay scoped to their own layer instead of competing across layers. The visible stacking order between the three layers is now driven by the order the containers appear in the DOM — the template iterates `resolvedLayers()` via `@for` / `@switch` — not by per-container z-index. The `groupsZIndex` / `connectionsZIndex` / `nodesZIndex` computeds and the `[style.z-index]` bindings are removed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 4f02fe4 commit 6c616fa

3 files changed

Lines changed: 40 additions & 25 deletions

File tree

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
11
<ng-container>
2-
<div #fGroupsContainer class="f-groups-container" [style.z-index]="groupsZIndex()">
3-
<ng-content select="[fGroup], [fGroups]" />
4-
</div>
5-
<div #fConnectionsContainer class="f-connections-container" [style.z-index]="connectionsZIndex()">
6-
<ng-content select="f-snap-connection" />
7-
<ng-content select="f-connection, [fConnections]" />
8-
<ng-content select="f-connection-for-create" />
9-
</div>
10-
11-
<div #fNodesContainer class="f-nodes-container" [style.z-index]="nodesZIndex()">
12-
<ng-content select="[fNode], [fNodes]" />
13-
</div>
2+
@for (layer of resolvedLayers(); track layer) {
3+
@switch (layer) {
4+
@case ('groups') {
5+
<div #fGroupsContainer class="f-groups-container">
6+
<ng-content select="[fGroup], [fGroups]" />
7+
</div>
8+
}
9+
@case ('connections') {
10+
<div #fConnectionsContainer class="f-connections-container">
11+
<ng-content select="f-snap-connection" />
12+
<ng-content select="f-connection, [fConnections]" />
13+
<ng-content select="f-connection-for-create" />
14+
</div>
15+
}
16+
@case ('nodes') {
17+
<div #fNodesContainer class="f-nodes-container">
18+
<ng-content select="[fNode], [fNodes]" />
19+
</div>
20+
}
21+
}
22+
}
1423
</ng-container>

libs/f-flow/src/f-canvas/f-canvas.component.scss

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,21 @@
1414
}
1515
}
1616

17-
// Each layer container is absolutely positioned so its [style.z-index]
18-
// (set by FCanvasComponent based on `fLayers` / `withFCanvas({ layers })`)
19-
// participates in stacking and creates a context for its descendants.
17+
// Each layer container is its own stacking context (`isolation: isolate`)
18+
// so the per-element z-indices set in `f-flow.component.scss` on `.f-node`,
19+
// `.f-group`, and `.f-connection-content` stay scoped to their layer
20+
// instead of competing across layers. The actual stacking order between
21+
// the three layers is controlled by their DOM order in the template
22+
// (driven by `[fLayers]` / `withFCanvas({ layers })`), not by z-index.
2023
.f-groups-container,
2124
.f-connections-container,
2225
.f-nodes-container {
26+
isolation: isolate;
27+
}
28+
29+
// Connections-container stays absolutely positioned to preserve the
30+
// containing block its descendant SVGs rely on (matches every release
31+
// before v18.6).
32+
.f-connections-container {
2333
position: absolute;
2434
}

libs/f-flow/src/f-canvas/f-canvas.component.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@ export class FCanvasComponent extends FCanvasBase implements OnInit, OnDestroy {
9393
*/
9494
public readonly fLayers = input<EFCanvasLayer[] | undefined>(undefined);
9595

96+
/**
97+
* Final layer order rendered in the template. The three sibling
98+
* containers are emitted in this order, which (combined with
99+
* `isolation: isolate` on each of them) is what visually stacks
100+
* groups, connections, and nodes — no per-container z-index involved.
101+
*/
96102
protected readonly resolvedLayers = computed<EFCanvasLayer[]>(() => {
97103
const fromInput = this.fLayers();
98104
if (fromInput && fromInput.length > 0) {
@@ -105,16 +111,6 @@ export class FCanvasComponent extends FCanvasBase implements OnInit, OnDestroy {
105111
return [...F_DEFAULT_LAYER_ORDER];
106112
});
107113

108-
protected readonly groupsZIndex = computed(() => this._zIndexFor(EFCanvasLayer.GROUPS));
109-
protected readonly connectionsZIndex = computed(() => this._zIndexFor(EFCanvasLayer.CONNECTIONS));
110-
protected readonly nodesZIndex = computed(() => this._zIndexFor(EFCanvasLayer.NODES));
111-
112-
private _zIndexFor(layer: EFCanvasLayer): number {
113-
// +1 so every layer has a non-zero z-index and creates a real
114-
// stacking context for its descendants.
115-
return this.resolvedLayers().indexOf(layer) + 1;
116-
}
117-
118114
public override fGroupsContainer =
119115
viewChild.required<ElementRef<HTMLElement>>('fGroupsContainer');
120116
public override fNodesContainer = viewChild.required<ElementRef<HTMLElement>>('fNodesContainer');

0 commit comments

Comments
 (0)