Skip to content

Commit

Permalink
feat(toolkit/sashbox): prevent the splitter from overlapping overlays
Browse files Browse the repository at this point in the history
In the sashbox component, we form a new stacking context to make
nested stacking contexts local, preventing nested stacking contexts
from leaking the sashbox element boundary.

We use `0` as its z-index value. A z-index of `0` is like setting no z-index
(auto) at all, except that it still forms a stacking context. Since it has the
order `0`, it will not overlap subsequent positioned elements of the same
parent stacking context.

Our rationale for setting a z-index on the splitter:
The splitter is a positioned element; thus, it implicitly forms a stacking
context that overlaps the subsequent sash (as long as the sash is statically
positioned). But sash content may also contain positioned elements or
even have elements with a z-index set. To prevent them from overlapping
the splitter, we form a stacking context on the sash div by setting its
z-index to `0` and set the splitter’s z-index to `1`.
  • Loading branch information
danielwiehl authored and Marcarrian committed Dec 9, 2020
1 parent 0e4d4cf commit 6903256
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,25 @@ <h1>sci-sashbox</h1>
</sci-form-field>
</section>

<section class="sash-settings">
<section *ngFor="let sash of sashes; index as i">
<header>Settings of sash {{i + 1}}</header>
<sci-form-field label="Visible">
<sci-checkbox [(ngModel)]="sash.visible"></sci-checkbox>
</sci-form-field>
<section class="testing">
<header>Testing</header>

<sci-form-field label="Size">
<input [(ngModel)]="sash.size" placeholder="Value in any CSS unit or unitless as a ratio">
</sci-form-field>
<button (click)="onGlasspaneToggle()" title="Show glasspane to test that splitters do not overlap overlays.">Show glasspane</button>
</section>

<section *ngFor="let sash of sashes; index as i" class="sash-settings">
<header>Settings of sash {{i + 1}}</header>
<sci-form-field label="Visible">
<sci-checkbox [(ngModel)]="sash.visible"></sci-checkbox>
</sci-form-field>

<sci-form-field label="Size">
<input [(ngModel)]="sash.size" placeholder="Value in any CSS unit or unitless as a ratio">
</sci-form-field>

<sci-form-field label="Min size">
<input [(ngModel)]="sash.minSize" placeholder="Value in pixel or percent">
</sci-form-field>
</section>
<sci-form-field label="Min size">
<input [(ngModel)]="sash.minSize" placeholder="Value in pixel or percent">
</sci-form-field>
</section>

<section class="sashbox-styling">
Expand Down Expand Up @@ -79,4 +83,8 @@ <h1>sci-sashbox</h1>
<sci-form-field label="--sci-sashbox-splitter-opacity_hover" direction="column">
<input [formControl]="stylingFormControls['--sci-sashbox-splitter-opacity_hover']">
</sci-form-field>
</section>
</section>

<div class="glasspane" *ngIf="glasspaneVisible" tabindex="0">
<button class="close material-icons" (click)="onGlasspaneToggle()">close</button>
</div>
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
@mixin panel {
margin-top: 2em;
border: 1px solid var(--sci-color-P400);
border-radius: 5px;
padding: 1em;
Expand All @@ -12,15 +11,18 @@
}

:host {
display: flex;
flex-direction: column;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: max-content 1fr;
grid-auto-rows: max-content;
gap: 1em;

> h1 {
flex: none;
grid-column: 1/-1;
}

> sci-sashbox {
flex: auto;
grid-column: 1/-1;

section {
display: flex;
Expand All @@ -44,7 +46,7 @@
}

> section.sashbox-settings {
flex: none;
grid-column: 1/3;

@include panel();
display: grid;
Expand All @@ -53,30 +55,45 @@
gap: 1em 3em;
}

> section.sash-settings {
flex: none;
> section.testing {
grid-column: 3/4;
display: grid;
gap: 1em 3em;
grid-auto-rows: max-content;

@include panel();
}

> section.sash-settings {
@include panel();
display: grid;
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
grid-template-columns: 1fr;
grid-auto-rows: max-content;
gap: 1em;

> section {
@include panel();
display: grid;
grid-template-columns: 1fr;
grid-auto-rows: max-content;
gap: 1em 3em;
}
gap: 1em 3em;
}

> section.sashbox-styling {
flex: none;
grid-column: 1/-1;

@include panel();
display: grid;
grid-template-columns: repeat(auto-fill, 300px);
grid-auto-rows: max-content;
gap: 1em 3em;
}

> div.glasspane {
position: absolute;
inset: 0 0 0 0;
opacity: .75;
background-color: black;

> button.close {
position: absolute;
top: 0;
right: 0;
color: white;
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { Component, ElementRef, HostListener, OnInit, ViewChild } from '@angular/core';
import { FormControl } from '@angular/forms';
import { SciSashboxComponent } from '@scion/toolkit/sashbox';

Expand Down Expand Up @@ -38,6 +38,8 @@ export class SciSashboxPageComponent implements OnInit {
{visible: true, size: '250px', minSize: 75},
];

public glasspaneVisible = false;

@ViewChild(SciSashboxComponent, {static: true, read: ElementRef})
public sashBoxComponent: ElementRef<HTMLElement>;

Expand All @@ -48,6 +50,11 @@ export class SciSashboxPageComponent implements OnInit {
formControl.setValue(defaultValue);
});
}

@HostListener('keydown.escape')
public onGlasspaneToggle(): void {
this.glasspaneVisible = !this.glasspaneVisible;
}
}

export interface Sash {
Expand Down
23 changes: 22 additions & 1 deletion projects/scion/toolkit/sashbox/src/sashbox.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,25 @@
box-sizing: border-box;
overflow: hidden;

/*
* In the sashbox component (:host), we form a new stacking context to make nested stacking contexts local,
* preventing nested stacking contexts from leaking the sashbox element boundary.
*
* We use `0` as its z-index value. A z-index of `0` is like setting no z-index (auto) at all, except that it
* still forms a stacking context. Since it has the order `0`, it will not overlap subsequent positioned elements
* of the same parent stacking context.
*
* Our rationale for setting a z-index on the splitter:
* The splitter is a positioned element; thus, it implicitly forms a stacking context that overlaps the subsequent
* sash (as long as it is statically positioned). But sash content may also contain positioned elements or even
* have elements with a z-index set. To prevent them from overlapping the splitter, we form a stacking context on
* the sash <div> by setting its z-index to `0` and set the splitter’s z-index to `1`.
*
* Refer to https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context
* to learn more about stacking contexts.
*/
z-index: 0;

* {
box-sizing: border-box;
}
Expand All @@ -33,6 +52,8 @@
display: grid; // stretch the sash content
grid-auto-rows: 100%;
grid-auto-columns: 100%;
z-index: 0; // see the explanation above why we set this z-index

// flex properties are set programmatically.
}

Expand Down Expand Up @@ -72,12 +93,12 @@
border-radius: var(--sci-sashbox-splitter-border-radius);
place-items: center; // center the touch-target
align-self: center;
z-index: 1; // see the explanation above why we set this z-index

> div.touch-target {
position: absolute; // out of the document flow
display: grid; // stretch the handle
place-items: center; // center the handle
z-index: 1; // overlay the subsequent sash
pointer-events: auto; // ensure interactable

> div.handle {
Expand Down

0 comments on commit 6903256

Please sign in to comment.