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
245 changes: 173 additions & 72 deletions src/BlazorUI/Bit.BlazorUI.Extras/Components/NavPanel/BitNavPanel.razor

Large diffs are not rendered by default.

Large diffs are not rendered by default.

135 changes: 123 additions & 12 deletions src/BlazorUI/Bit.BlazorUI.Extras/Components/NavPanel/BitNavPanel.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@import "../../Styles/extra-variables.scss";
@import "../../Styles/extra-variables.scss";
@import "../../../Bit.BlazorUI/Styles/functions.scss";
@import "../../../Bit.BlazorUI/Styles/media-queries.scss";

Expand All @@ -7,24 +7,77 @@
position: sticky;
padding: spacing(2);
overflow: hidden auto;
// The panel scrolls its own overflow without drawing a bar for it: the bar would sit between the items
// and the edge of a surface that is already narrow, and the rail has no room for one at all.
scrollbar-width: none;
background-color: $clr-bg-pri;
height: $bit-env-height-available;
// The Width parameter is a custom property rather than a width of its own, so the state-dependent widths
// below (the rail, the small-screen drawer) keep deciding on their own and a panel that is given no width
// still takes the one its container hands it.
width: var(--bit-npn-w, auto);

&.bit-npn-tgl {
min-width: spacing(12);
max-width: spacing(12);
min-width: var(--bit-npn-tw, #{spacing(12)});
max-width: var(--bit-npn-tw, #{spacing(12)});

.bit-npn-cnt {
align-items: center;
}
}

// A rail that expands on hover widens in place, so the two widths are the two ends of a transition
// rather than a jump. Only on the screens the panel is a column of the page on: below the breakpoint it
// is an off-canvas drawer, and a transition declared here outranks the one the drawer slides in with
// (two classes against one) and would replace it with a widening the drawer never does.
&.bit-npn-eoh {
@include gt-sm {
transition: min-width 150ms $mot-easing, max-width 150ms $mot-easing;
}

// The expanded end of that transition needs a width of the same kind as the collapsed one: a rail
// that widens into an unbounded max-width has nothing to travel to and snaps open instead. A panel
// given no Width keeps both ends open (an invalid max-width is dropped) and stays as wide as its
// container makes it, and so do the two modes that size the panel themselves - the Width they are
// documented to ignore is not allowed back in as a bound.
&:not(.bit-npn-tgl):not(.bit-npn-fiw):not(.bit-npn-fuw) {
min-width: var(--bit-npn-w, auto);
max-width: var(--bit-npn-w, auto);
}
}

// The panel that pins its two ends stops being the scroller: the nav between them takes the overflow,
// so the header (with the search box in it) and the footer stay where they are however long the list is.
&.bit-npn-ste {
overflow: hidden;

.bit-npn-swp {
min-height: 0;
}

.bit-npn-cnt {
height: 100%;
min-height: 0;
}

.bit-nav {
flex: 1 1 auto;
min-height: 0;
overflow: hidden auto;
scrollbar-width: none;

&::-webkit-scrollbar {
width: 0;
}
}
}
Comment thread
msynk marked this conversation as resolved.

&.bit-npn-npd {
padding: 0;

&.bit-npn-tgl {
min-width: spacing(6);
max-width: spacing(6);
min-width: var(--bit-npn-tw, #{spacing(6)});
max-width: var(--bit-npn-tw, #{spacing(6)});
}

.bit-npn-cnt {
Expand All @@ -43,42 +96,67 @@

@include lt-md {
left: 0;
right: unset;
padding: 0;
opacity: 1;
position: fixed;
visibility: visible;
height: unset !important;
z-index: $zindex-callout;
bottom: $bit-env-inset-bottom;
top: $bit-env-inset-top !important;
transition: transform 150ms $mot-easing, opacity 100ms $mot-easing;

&.bit-rtl {
// The drawer is docked to the edge it comes from, which the Position expresses in the text direction:
// the End of a left-to-right layout and the Start of a right-to-left one are both the right of the
// screen, and a panel that is both is back where it started.
&.bit-rtl,
&.bit-npn-end {
right: 0;
left: unset;
}

&.bit-rtl.bit-npn-end {
left: 0;
right: unset;
}

&.bit-npn-cls {
opacity: 0;
transform: translateX(-100%);

&.bit-rtl {
// A panel that is only moved off screen keeps its links in the tab order and in the accessibility
// tree, so the closed one is hidden outright once it has finished sliding away.
visibility: hidden;
pointer-events: none;
transition: transform 150ms $mot-easing, opacity 100ms $mot-easing, visibility 0s linear 150ms;

&.bit-rtl,
&.bit-npn-end {
transform: translateX(100%);
}

&.bit-rtl.bit-npn-end {
transform: translateX(-100%);
}
}

&.bit-npn-tgl {
min-width: spacing(8);
max-width: spacing(8);
min-width: var(--bit-npn-tw, #{spacing(8)});
max-width: var(--bit-npn-tw, #{spacing(8)});
}

&.bit-npn-npd.bit-npn-tgl {
min-width: spacing(6);
max-width: spacing(6);
min-width: var(--bit-npn-tw, #{spacing(6)});
max-width: var(--bit-npn-tw, #{spacing(6)});
}

.bit-npn-tbn {
display: none;
}

.bit-npn-cbw {
display: contents;
}
}
}

Expand All @@ -88,6 +166,13 @@
.bit-npn-cnt {
height: -webkit-fill-available;
}

// A panel that pins its ends bounds its own flex column instead, which is what lets the nav between them
// take the overflow: a container sized to fill what is available would grow with the list rather than
// hand it a scrollbar.
&.bit-npn-ste .bit-npn-cnt {
height: 100%;
}
}

.bit-npn-fiw {
Expand All @@ -112,6 +197,13 @@
}
}

// The layer between the root element and the content: it is either the swipe trap of a panel that closes on
// a gesture, or a plain element of the same shape for one that does not.
.bit-npn-swp {
width: 100%;
height: 100%;
}

.bit-npn-cnt {
width: auto;
display: flex;
Expand All @@ -138,6 +230,25 @@
flex-grow: 1;
}

// The close button is the drawer's, so it is out of the layout entirely on the screens where the panel is a
// column of the page and the toggle button is the control that belongs there.
.bit-npn-cbw {
display: none;
}

// The live region of the search: read by a screen reader, invisible on the page.
.bit-npn-lvr {
width: 1px;
height: 1px;
padding: 0;
border: 0;
margin: -1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
clip-path: inset(50%);
}

.bit-nav-apri {
--bit-npn-clr-bg: #{$clr-pri};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ public class BitNavPanelClassStyles
/// </summary>
public string? ToggleButton { get; set; }

/// <summary>
/// Custom CSS classes/styles for the close button of the BitNavPanel.
/// </summary>
public string? CloseButton { get; set; }

/// <summary>
/// Custom CSS classes/styles for the search box of the BitNavPanel.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace Bit.BlazorUI;

/// <summary>
/// The edge of the viewport the off-canvas drawer of a <see cref="BitNavPanel{TItem}"/> comes from.
/// </summary>
/// <remarks>
/// Only the drawer of a small screen is docked to an edge: on a wide screen the panel is a column in the
/// normal flow of the page, where its place is decided by the layout that holds it.
/// </remarks>
public enum BitNavPanelPosition
{
/// <summary>
/// The drawer comes from the starting edge of the text direction: the left in a left-to-right layout,
/// the right in a right-to-left one.
/// </summary>
Start,

/// <summary>
/// The drawer comes from the ending edge of the text direction: the right in a left-to-right layout,
/// the left in a right-to-left one.
/// </summary>
End
}
Loading
Loading