Path: blob/main/src/resources/formats/html/bslib/components/scss/sidebar.scss
12924 views
$bslib-sidebar-bg: rgba(var(--bs-emphasis-color-rgb, 0,0,0), 0.05) !default;
$bslib-sidebar-fg: var(--bs-emphasis-color, black) !default;
$bslib-sidebar-toggle-bg: rgba(var(--bs-emphasis-color-rgb, 0,0,0), 0.1) !default;
$bslib-sidebar-border: var(--bs-card-border-width, #{$card-border-width}) solid var(--bs-card-border-color, #{$card-border-color}) !default;
$bslib-sidebar-column-sidebar: Min(calc(100% - var(--bslib-sidebar-icon-size)), var(--bslib-sidebar-width, 250px));
.bslib-sidebar-layout {
--bslib-sidebar-transition-duration: 500ms;
--bslib-sidebar-transition-easing-x: cubic-bezier(0.8, 0.78, 0.22, 1.07);
--bslib-sidebar-border: #{$bslib-sidebar-border};
--bslib-sidebar-border-radius: var(--bs-border-radius);
--bslib-sidebar-vert-border: #{$bslib-sidebar-border};
--bslib-sidebar-bg: #{$bslib-sidebar-bg};
--bslib-sidebar-fg: #{$bslib-sidebar-fg};
--bslib-sidebar-main-fg: var(--bs-card-color, var(--bs-body-color));
--bslib-sidebar-main-bg: var(--bs-card-bg, var(--bs-body-bg));
--bslib-sidebar-toggle-bg: #{$bslib-sidebar-toggle-bg};
--bslib-sidebar-padding: calc(var(--bslib-spacer) * 1.5);
--bslib-sidebar-icon-size: var(--bslib-spacer, 1rem);
--bslib-sidebar-icon-button-size: calc(var(--bslib-sidebar-icon-size, 1rem) * 2);
--bslib-sidebar-padding-icon: calc(var(--bslib-sidebar-icon-button-size, 2rem) * 1.5);
// We intentionally don't give a value here, but it could be set by someone else
// --bslib-collapse-toggle-border: ;
--bslib-collapse-toggle-border-radius: var(--bs-border-radius, #{$border-radius});
--bslib-collapse-toggle-transform: 0deg;
--bslib-sidebar-toggle-transition-easing: cubic-bezier(1, 0, 0, 1);
--bslib-collapse-toggle-right-transform: 180deg;
--bslib-sidebar-column-main: minmax(0, 1fr);
display: grid !important;
grid-template-columns: $bslib-sidebar-column-sidebar var(--bslib-sidebar-column-main);
position: relative;
@include transition(grid-template-columns ease-in-out var(--bslib-sidebar-transition-duration));
border: var(--bslib-sidebar-border);
border-radius: var(--bslib-sidebar-border-radius);
&[data-bslib-sidebar-border="false"] {
border: none;
}
&[data-bslib-sidebar-border-radius="false"] {
border-radius: initial;
}
> .main, > .sidebar {
grid-row: 1 / 2;
border-radius: inherit;
overflow: auto;
}
> .main {
grid-column: 2 / 3;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
padding: var(--bslib-sidebar-padding);
transition: padding var(--bslib-sidebar-transition-easing-x) var(--bslib-sidebar-transition-duration);
color: var(--bslib-sidebar-main-fg);
background-color: var(--bslib-sidebar-main-bg);
}
> .sidebar {
grid-column: 1 / 2;
width: 100%;
height: 100%;
border-right: var(--bslib-sidebar-vert-border);
border-top-right-radius: 0;
border-bottom-right-radius: 0;
color: var(--bslib-sidebar-fg);
background-color: var(--bslib-sidebar-bg);
backdrop-filter: blur(5px);
> .sidebar-content {
display: flex;
flex-direction: column;
gap: var(--bslib-spacer, 1rem);
padding: var(--bslib-sidebar-padding);
// Add space for the toggle button (removed if sidebar is always open)
padding-top: var(--bslib-sidebar-padding-icon);
> :last-child:not(.sidebar-title) {
// Remove margin-bottom from the last item because sidebar has padding.
// We make an exception for .sidebar-title because it might be common to
// have a title and bare text nodes (that don't count as children).
margin-bottom: 0;
}
> .accordion {
margin-left: calc(-1 * var(--bslib-sidebar-padding));
margin-right: calc(-1 * var(--bslib-sidebar-padding));
&:last-child {
margin-bottom: calc(-1 * var(--bslib-sidebar-padding));
}
&:not(:last-child) {
margin-bottom: $spacer;
}
.accordion-body {
display: flex;
flex-direction: column;
}
}
// Accordions in sidebars are made flush with `.accordion-flush`, which
// removes the top and bottom border of the first or last accordion item.
// But in our usage, the accordions might not be the first or last item in
// the sidebar. In that case, it's better to keep the top/bottom borders.
> .accordion:not(:first-child) .accordion-item:first-child {
border-top: var(--#{$prefix}accordion-border-width) solid var(--#{$prefix}accordion-border-color);
}
> .accordion:not(:last-child) .accordion-item:last-child {
border-bottom: var(--#{$prefix}accordion-border-width) solid var(--#{$prefix}accordion-border-color);
}
&.has-accordion > .sidebar-title {
border-bottom: none;
padding-bottom: 0;
}
}
.shiny-input-container {
width: 100%;
}
}
&[data-bslib-sidebar-open="always"] {
> .sidebar > .sidebar-content {
// Always-open sidebars don't have a toggle & can use normal top padding
padding-top: var(--bslib-sidebar-padding);
}
}
> .collapse-toggle {
grid-row: 1 / 2;
grid-column: 1 / 2;
display: inline-flex;
align-items: center;
position: absolute;
right: calc(var(--bslib-sidebar-icon-size));
top: calc(var(--bslib-sidebar-icon-size, 1rem) / 2);
border: none;
border-radius: var(--bslib-collapse-toggle-border-radius);
height: var(--bslib-sidebar-icon-button-size, 2rem);
width: var(--bslib-sidebar-icon-button-size, 2rem);
display: flex;
align-items: center;
justify-content: center;
padding: 0;
color: var(--bslib-sidebar-fg);
background-color: unset; // don't take `button` background color
transition:
color var(--bslib-sidebar-transition-easing-x) var(--bslib-sidebar-transition-duration),
top var(--bslib-sidebar-transition-easing-x) var(--bslib-sidebar-transition-duration),
right var(--bslib-sidebar-transition-easing-x) var(--bslib-sidebar-transition-duration),
left var(--bslib-sidebar-transition-easing-x) var(--bslib-sidebar-transition-duration);
&:hover {
background-color: var(--bslib-sidebar-toggle-bg);
}
> .collapse-icon {
opacity: 0.8;
width: var(--bslib-sidebar-icon-size);
height: var(--bslib-sidebar-icon-size);
transform: rotateY(var(--bslib-collapse-toggle-transform));
// N.B. since mobile view won't trigger a transition of grid-template-columns,
// we transition this toggle to ensure _some_ transition event always happens.
transition: transform var(--bslib-sidebar-toggle-transition-easing) var(--bslib-sidebar-transition-duration);
}
&:hover > .collapse-icon {
opacity: 1;
}
}
.sidebar-title {
font-size: $font-size-base * 1.25;
line-height: $line-height-sm;
margin-top: 0;
margin-bottom: $spacer;
padding-bottom: $spacer;
border-bottom: var(--bslib-sidebar-border);
}
&.sidebar-right {
grid-template-columns: var(--bslib-sidebar-column-main) $bslib-sidebar-column-sidebar;
> .main {
grid-column: 1 / 2;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-top-left-radius: inherit;
border-bottom-left-radius: inherit;
}
> .sidebar {
grid-column: 2 / 3;
border-right: none;
border-left: var(--bslib-sidebar-vert-border);
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
> .collapse-toggle {
grid-column: 2 / 3;
left: var(--bslib-sidebar-icon-size);
right: unset;
border: var(--bslib-collapse-toggle-border);
> .collapse-icon {
transform: rotateY(var(--bslib-collapse-toggle-right-transform));
}
}
}
&.sidebar-collapsed {
--bslib-collapse-toggle-transform: 180deg;
--bslib-collapse-toggle-right-transform: 0deg;
--bslib-sidebar-vert-border: none;
grid-template-columns: 0 minmax(0, 1fr);
&.sidebar-right {
grid-template-columns: minmax(0, 1fr) 0;
}
// Hide the sidebar contents after it's done transitioning so that
// those contents don't impact the overall layout (i.e., height)
&:not(.transitioning) {
// Putting `display:none` on .sidebar would change the number of columns
// in the grid, and I don't think we can transition between those states
> .sidebar > * {
display: none;
}
}
> .main {
border-radius: inherit;
}
&:not(.sidebar-right) > .main {
padding-left: var(--bslib-sidebar-padding-icon);
}
&.sidebar-right > .main {
padding-right: var(--bslib-sidebar-padding-icon);
}
> .collapse-toggle {
color: var(--bslib-sidebar-main-fg);
// The CSS variable (set via JS) is here to help avoid overlapping toggles
top: calc(
var(--bslib-sidebar-overlap-counter, 0) *
calc(var(--bslib-sidebar-icon-size) +
var(--bslib-sidebar-padding)
) + var(--bslib-sidebar-icon-size, 1rem) / 2);
right: calc(-2.5 * var(--bslib-sidebar-icon-size) - var(--bs-card-border-width, 1px));
}
&.sidebar-right > .collapse-toggle {
left: calc(-2.5 * var(--bslib-sidebar-icon-size) - var(--bs-card-border-width, 1px));
right: unset;
}
}
}
@include media-breakpoint-up(sm) {
// hide sidebar content while we transition the parent .sidebar on desktop
// (on mobile the reveal happens immediately)
.bslib-sidebar-layout.transitioning > .sidebar > .sidebar-content {
display: none;
}
}
@include media-breakpoint-down(sm) {
.bslib-sidebar-layout {
// Tell sidebar init js we're on mobile for `sidebar(open = "desktop")`
&[data-bslib-sidebar-open="desktop"] {
--bslib-sidebar-js-init-collapsed: true;
}
&, &.sidebar-right {
// Remove sidebar borders in mobile view (except always-open, added below)
> .sidebar { border: none; }
// Main area takes up entire layout area to avoid layout shift when
// sidebar is expanded as an overlay.
> .main {
grid-column: 1 / 3;
}
}
// Always open sidebars become "flow" layouts in mobile view
&[data-bslib-sidebar-open="always"] {
display: block !important;
> .sidebar {
max-height: var(--bslib-sidebar-max-height-mobile);
overflow-y: auto;
border-top: var(--bslib-sidebar-vert-border);
}
}
&:not([data-bslib-sidebar-open="always"]) {
// Sidebar layer has to be lifted up to cover other (nested) sidebars
&:not(.sidebar-collapsed) {
> .sidebar { z-index: 1; }
> .collapse-toggle { z-index: 1; }
}
// Either sidebar or main area take up entire layout depending on state
$full-closed: 100% 0;
$closed-full: 0 100%;
grid-template-columns: $full-closed;
&.sidebar-right {
grid-template-columns: $closed-full;
}
&.sidebar-collapsed {
grid-template-columns: $closed-full;
&.sidebar-right {
grid-template-columns: $full-closed;
}
}
// Keep padding on main contents when sidebar is expanded (avoid shifts)
&:not(.sidebar-right) > .main {
padding-left: var(--bslib-sidebar-padding-icon);
}
&.sidebar-right > .main {
padding-right: var(--bslib-sidebar-padding-icon);
}
// Make main contents transparent while sidebar is expanded
> .main {
opacity: 0;
transition: opacity var(--bslib-sidebar-transition-easing-x) var(--bslib-sidebar-transition-duration);
}
&.sidebar-collapsed > .main {
opacity: 1;
}
}
}
}