Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@
style="@StyleBuilder.Value"
class="@ClassBuilder.Value"
dir="@Dir?.ToString().ToLower()">
<div style="@Styles?.Top" class="bit-ash-top @Classes?.Top" />
<div style="@Styles?.Top" class="bit-ash-top @Classes?.Top"></div>
@* The direction is repeated here on purpose, and the pair of it and the LOGICAL inset variables the
two side bars are sized from is what keeps each safe area on the physical edge it belongs to. This
row lays its three children out in the reading direction, so the first of them is the physically
right one in a right-to-left shell - and the inline-start inset it reads is, under the same
direction, the inset of the physical right. Take either half away and the two stop cancelling out:
the left of the screen would be inset by whatever the right of it needed. *@
<div style="@Styles?.Center"
class="bit-ash-center @Classes?.Center"
dir="@Dir?.ToString().ToLower()">
<div style="@Styles?.Left" class="bit-ash-left @Classes?.Left"></div>
<div id="BitAppShell-container" @ref="_containerRef" style="@Styles?.Main" class="bit-ash-main @Classes?.Main">
<div id="@MainContainerId" @ref="_containerRef" style="@_MainStyle" class="@_MainClass" data-bit-ash-main>
<CascadingValue Name="@BitAppShell.Container" Value="_containerRef">
<BitCascadingValueProvider Values="Values" ValueList="ValueList">
@ChildContent
Expand Down

Large diffs are not rendered by default.

114 changes: 103 additions & 11 deletions src/BlazorUI/Bit.BlazorUI.Extras/Components/AppShell/BitAppShell.scss
Original file line number Diff line number Diff line change
@@ -1,54 +1,146 @@
@import '../../Styles/extra-variables.scss';
@import '../../../Bit.BlazorUI/Styles/functions.scss';

// Deliberately outside the library's z-index scale (which tops out at the snackbar's 1400). The four
// bars mask the physical chrome of the device - the notch, the rounded corners, the home indicator - so
// there is nothing an application can render that is allowed to paint over them.
$bit-ash-inset-zindex: 999999;

// The four bars around the main container are sized from custom properties of their own rather than
// straight off the env() values, so that NoInsets has one place to take all four back to zero and a page
// can override a single edge without having to know how the bar is built.
// The two side ones read the LOGICAL insets: the center is laid out in the reading direction, so its
// first child is the physically right one in a right-to-left app shell - and the physical right edge is
// the one whose inset it has to be sized by.
.bit-ash {
--bit-ash-inset-top: #{$bit-env-inset-top};
--bit-ash-inset-bottom: #{$bit-env-inset-bottom};
--bit-ash-inset-start: #{$bit-env-inset-inline-start};
--bit-ash-inset-end: #{$bit-env-inset-inline-end};
// How much of the shell the on-screen keyboard is covering. It stays 0 unless AvoidKeyboard is set,
// which is what puts the measured height here from the browser side; a page can read it to place
// chrome of its own against the same number.
--bit-ash-keyboard-inset: 0px;
// How much of the bottom inset is still worth keeping while the keyboard is open. The inset is there
// to keep content off the home indicator, and the keyboard is already covering everything that far up
// the screen - so a bar of the shell's background left below the content and above the keyboard is
// room taken from the content for nothing at all. What the keyboard has taken over is taken off here,
// and the middle below is sized from this rather than from the inset itself.
--bit-ash-inset-bottom-visible: max(0px, calc(var(--bit-ash-inset-bottom) - var(--bit-ash-keyboard-inset)));

width: 100%;
height: 100%;
display: flex;
flex-direction: column;
background-color: $clr-bg-pri;
}

// The insets a browser reports change while its own retractable UI slides in and out - the bottom bar of
// an edge-to-edge Chrome on Android does it on every scroll - which moves the two bars sized from them on
// every frame of that slide. Asked for stable insets, the shell is sized from the STATIC maximums of the
// same four instead: the layout is laid out once, for the room left when nothing is retracted, and the
// browser slides its UI over the background of a bar rather than over the content.
.bit-ash-sin {
--bit-ash-inset-top: #{$bit-env-max-inset-top};
--bit-ash-inset-bottom: #{$bit-env-max-inset-bottom};
--bit-ash-inset-start: #{$bit-env-max-inset-inline-start};
--bit-ash-inset-end: #{$bit-env-max-inset-inline-end};
}

.bit-ash-nin {
--bit-ash-inset-top: 0px;
--bit-ash-inset-bottom: 0px;
--bit-ash-inset-start: 0px;
--bit-ash-inset-end: 0px;
}

// And one edge at a time, for the application that insets that edge itself. These come after both rules
// above, so an edge taken away stays away whether the shell was asked for stable insets or for none: a
// shell asking for both has asked for one edge to be gone, and the other three to be sized either way.
.bit-ash-nit {
--bit-ash-inset-top: 0px;
}

.bit-ash-nib {
--bit-ash-inset-bottom: 0px;
}

.bit-ash-nis {
--bit-ash-inset-start: 0px;
}

.bit-ash-nie {
--bit-ash-inset-end: 0px;
}

// The shell of an application owns the window: nothing is laid out around it and nothing is scrolled
// past it, so it can be positioned against the viewport rather than sized by whatever room the page gives
// it - which is what saves the host page from having to carry a height of its own down through html and
// body. The height above resolves against the viewport here, so both ways of asking agree.
.bit-ash-fsc {
inset: 0;
position: fixed;
}

.bit-ash-top {
width: 100%;
z-index: 999999;
height: $bit-env-inset-top;
z-index: $bit-ash-inset-zindex;
height: var(--bit-ash-inset-top);
background-color: $clr-bg-pri;
}

.bit-ash-bottom {
width: 100%;
z-index: 999999;
height: $bit-env-inset-bottom;
z-index: $bit-ash-inset-zindex;
height: var(--bit-ash-inset-bottom-visible);
background-color: $clr-bg-pri;
}

.bit-ash-center {
width: 100%;
display: flex;
height: calc(100% - $bit-env-inset-top - $bit-env-inset-bottom);
height: calc(100% - var(--bit-ash-inset-top) - var(--bit-ash-inset-bottom-visible) - var(--bit-ash-keyboard-inset));
}

.bit-ash-main {
height: 100%;
display: flex;
overflow: auto;
position: relative;
scroll-behavior: smooth;
overscroll-behavior: none;
width: calc(100% - $bit-env-inset-left - $bit-env-inset-right);
width: calc(100% - var(--bit-ash-inset-start) - var(--bit-ash-inset-end));
}

// Applies to every move the main container is not dragged to by the reader: the scrolling API of the
// component, a fragment navigation into it, and the browser bringing a focused element into view.
.bit-ash-smt {
scroll-behavior: smooth;
}

// The force-animation class opts a whole SUBTREE out of the preference, so a shell carrying it keeps its
// smooth scrolling - which is what every other animated component in the library does with it.
@media (prefers-reduced-motion: reduce) {
.bit-ash-smt:not(.bit-fam):not(.bit-fam *) {
scroll-behavior: auto;
}
}

// Only the reader's own gestures are stopped. The element stays scrollable through the scrolling API,
// which is what the public methods of the component move it with.
.bit-ash-nsc {
overflow: hidden;
}

.bit-ash-left {
height: 100%;
z-index: 999999;
width: $bit-env-inset-left;
z-index: $bit-ash-inset-zindex;
width: var(--bit-ash-inset-start);
background-color: $clr-bg-pri;
}

.bit-ash-right {
height: 100%;
z-index: 999999;
width: $bit-env-inset-right;
z-index: $bit-ash-inset-zindex;
width: var(--bit-ash-inset-end);
background-color: $clr-bg-pri;
}
Loading
Loading