// User-facing variables, use for theming
$bslib-sidebar-bg: null !default;
$bslib-sidebar-fg: null !default;

@if $bslib-sidebar-bg == null {
  // sidebar bg not set by the user
  $bslib-sidebar-bg: rgba(var(--bs-emphasis-color-rgb, 0,0,0), 0.05);
} @else if $bslib-sidebar-fg == null and type-of($bslib-sidebar-bg) == "color" {
  // sidebar bg set, fg unset
  $bslib-sidebar-fg: color-contrast($bslib-sidebar-bg);
}
@if $bslib-sidebar-fg == null {
  // sidebar bg and fg were unset
  $bslib-sidebar-fg: var(--_main-fg);
}

$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;

// Internal variables, don't use for theming!
$bslib-sidebar-column-sidebar: Min(calc(100% - var(--_padding-icon)), var(--_sidebar-width));

.bslib-sidebar-layout {
  // We use container queries to conditionally apply the `--bs-card-color`
  // variable only when it has a non-space value, otherwise the empty
  // `--bs-card-color` (from Bootstrap's Sass) unsets `--_main-fg`.
  container-type: style;

  --_transition-duration: 0; // Transitions are enabled by .transitioning class
  --_transition-easing-x: var(--bslib-sidebar-transition-easing-x, cubic-bezier(0.8, 0.78, 0.22, 1.07));
  --_border: var(--bslib-sidebar-border, #{$bslib-sidebar-border});
  --_border-radius: var(--bslib-sidebar-border-radius, var(--bs-border-radius));
  --_vert-border: var(--bslib-sidebar-vert-border, var(--_border));
  --_sidebar-width: var(--bslib-sidebar-width, 250px);
  --_sidebar-bg: var(--bslib-sidebar-bg, #{$bslib-sidebar-bg});
  --_sidebar-fg: var(--bslib-sidebar-fg, #{$bslib-sidebar-fg});
  --_main-fg: var(--bslib-sidebar-main-fg, var(--bs-body-color));
  @container style(--bs-card-color: not " ") {
    --_main-fg: var(--bslib-sidebar-main-fg, var(--bs-card-color, var(--bs-body-color)));
  }
  --_main-bg: var(--bslib-sidebar-main-bg, transparent);
  --_toggle-bg: var(--bslib-sidebar-toggle-bg, #{$bslib-sidebar-toggle-bg});
  --_padding: var(--bslib-sidebar-padding, var(--bslib-spacer, 1.5rem));
  --_icon-size: var(--bslib-sidebar-icon-size, 1rem);
  --_icon-button-size: var(--bslib-sidebar-icon-button-size, calc(var(--_icon-size, 1rem) * 2));
  --_padding-icon: calc(var(--_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: ;
  --_toggle-border-radius: var(--bslib-collapse-toggle-border-radius, var(--bs-border-radius, #{$border-radius}));
  --_toggle-transform: var(--bslib-collapse-toggle-transform, 0deg);
  --_toggle-transition-easing: var(--bslib-sidebar-toggle-transition-easing, cubic-bezier(1, 0, 0, 1));
  --_toggle-right-transform: var(--bslib-collapse-toggle-right-transform, 180deg);
  --_toggle-position-y: calc(var(--_js-toggle-count-this-side, 0) * calc(var(--_icon-size) + var(--_padding)) + var(--_icon-size, 1rem) / 2);
  --_toggle-position-x: calc(-2.5 * var(--_icon-size) - var(--bs-card-border-width, 1px));
  --_mobile-max-height: var(--bslib-sidebar-mobile-max-height, var(--bslib-sidebar-max-height-mobile));
  --_sidebar-mobile-opacity: var(--bslib-sidebar-mobile-opacity);
  --_main-mobile-expanded-opacity: var(--bslib-sidebar-main-mobile-expanded-opacity, 0);
  --_sidebar-mobile-max-width: var(--bslib-sidebar-mobile-max-width);
  --_sidebar-mobile-box-shadow: var(--bslib-sidebar-mobile-box-shadow);
  --_column-main: minmax(0, 1fr);
  // Calculate the height of all toggle buttons in nested sidebar layouts
  --_toggle-collective-height: calc(calc(var(--_icon-button-size) + 0.5em) * var(--_js-toggle-count-max-side, 1));

  // Resize handle variables
  --_resize-handle-width: var(--bslib-sidebar-resize-handle-width, 8px);

  @media (any-pointer: coarse) {
    --_resize-handle-width: var(--bslib-sidebar-resize-handle-width, 26px);
  }
  --_resize-indicator-color: var(--_sidebar-fg, var(--bs-emphasis-color, black));
  --_resize-indicator-color-active: var(--bslib-sidebar-resize-indicator-color-active, var(--bs-primary, #0d6efd));

  &.transitioning {
    --_transition-duration: max(var(--bslib-sidebar-transition-duration, 300ms), 5ms);
  }

  display: grid !important;
  grid-template-columns: $bslib-sidebar-column-sidebar var(--_column-main);
  position: relative;

  @include transition(
    grid-template-columns ease-in-out var(--_transition-duration),
    background-color linear var(--_transition-duration)
  );

  border: var(--_border);
  border-radius: var(--_border-radius);

  // Ensure that the layout is tall enough to accommodate all toggle buttons.
  // We also have to beat `min-height: 0` on fill items in fillable containers.
  &, .html-fill-container > &.html-fill-item {
    min-height: var(--_toggle-collective-height);
  }

  &[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(--_padding);
    transition: padding var(--_transition-easing-x) var(--_transition-duration);
    color: var(--_main-fg);
    background-color: var(--_main-bg);
  }

  > .sidebar {
    grid-column: 1 / 2;
    width: 100%;
    border-right: var(--_vert-border);
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
    color: var(--_sidebar-fg);
    background-color: var(--_sidebar-bg);
    position: relative; // For resize handle positioning

    > .sidebar-content {
      display: flex;
      flex-direction: column;
      gap: var(--bslib-spacer, 1rem);
      padding: var(--_padding);
      // Add space for the toggle button (removed if sidebar is always open)
      padding-top: var(--_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(--_padding));
        margin-right: calc(-1 * var(--_padding));
        &:last-child {
          margin-bottom: calc(-1 * var(--_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%;
    }
  }

  > .collapse-toggle {
    grid-row: 1 / 2;
    grid-column: 1 / 2;
    z-index: $zindex-dropdown;
    display: inline-flex;
    align-items: center;
    position: absolute;
    right: calc(var(--_icon-size));
    top: calc(var(--_icon-size, 1rem) / 2);
    border: none;
    border-radius: var(--_toggle-border-radius);
    height: var(--_icon-button-size, 2rem);
    width: var(--_icon-button-size, 2rem);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    color: var(--_sidebar-fg);
    background-color: unset; // don't take `button` background color
    transition:
      color var(--_transition-easing-x) var(--_transition-duration),
      top var(--_transition-easing-x) var(--_transition-duration),
      right var(--_transition-easing-x) var(--_transition-duration),
      left var(--_transition-easing-x) var(--_transition-duration);

    &:hover {
      background-color: var(--_toggle-bg);
    }

    > .collapse-icon {
      opacity: 0.8;
      width: var(--_icon-size);
      height: var(--_icon-size);
      transform: rotateY(var(--_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(--_toggle-transition-easing) var(--_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(--_border);
  }

  &.sidebar-right {
    grid-template-columns: var(--_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(--_vert-border);
      border-top-left-radius: 0;
      border-bottom-left-radius: 0;
    }

    > .collapse-toggle {
      grid-column: 2 / 3;
      left: var(--_icon-size);
      right: unset;
      border: var(--bslib-collapse-toggle-border);
      > .collapse-icon {
        transform: rotateY(var(--_toggle-right-transform));
      }
    }
  }

  // hide sidebar content while we transition the parent .sidebar
  &.transitioning > .sidebar > .sidebar-content {
    display: none;
  }

  &.sidebar-collapsed {
    --_toggle-transform: 180deg;
    --_toggle-right-transform: 0deg;
    --_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;
      padding-left: var(--_padding-icon);
      padding-right: var(--_padding-icon);
    }

    > .collapse-toggle {
      color: var(--_main-fg);
      // The CSS variable (set via JS) is here to help avoid overlapping toggles
      top: var(--_toggle-position-y);
      right: var(--_toggle-position-x);
    }

    &.sidebar-right > .collapse-toggle {
      left: var(--_toggle-position-x);
      right: unset;
    }
  }

  // Resize handle styles
  .bslib-sidebar-resize-handle {
    position: absolute;
    top: 0;
    bottom: 0;
    width: var(--_resize-handle-width);
    left: calc(calc(-1 * var(--_resize-handle-width)) - 2px);
    grid-column: 2;
    pointer-events: none;
    user-select: none;
    z-index: calc(#{$zindex-dropdown} + 1); // Above toggle button

    // Trip area: a narrow strip at the sidebar outer edge that detects the
    // mouse crossing the sidebar boundary. Positioned past the handle so
    // it doesn't overlap the sidebar scrollbar beneath the handle.
    // Expands when .handle-active is added to prevent accidental deactivation.
    &::before {
      content: "";
      position: absolute;
      top: 0;
      bottom: 0;
      left: 100%;
      width: 5px;
      pointer-events: auto;
    }

    // When the mouse crosses the sidebar edge, JS adds .handle-active
    // to show the resize cursor and expand the trip area.
    &.handle-active {
      cursor: ew-resize;

      &::before {
        left: calc(100% - 10px);
        width: 24px;
      }
    }

    // Touch users get the full handle + extended hit area
    @media (any-pointer: coarse) {
      pointer-events: auto;
      cursor: ew-resize;

      &::before {
        left: 0;
        width: auto;
        right: calc(-1 * var(--_resize-handle-width) - 2px);
      }
    }

    .resize-indicator {
      position: absolute;
      top: 50%;
      right: 2px;
      width: 2px;
      height: 30px;
      transform: translate(-50%, -50%);
      background-color: var(--_resize-indicator-color);
      opacity: 0.1;
      border-radius: 1px;
      transition: all 0.15s ease;
    }

    &:hover, &:focus, &:active, &.handle-active {
      .resize-indicator {
        opacity: 1;
      }
    }

    &:hover .resize-indicator,
    &:focus .resize-indicator,
    &.handle-active .resize-indicator {
      width: 3px;
      height: 40px;
    }

    &:active .resize-indicator {
      background-color: var(--_resize-indicator-color-active);
      width: 4px;
      height: 50px;
    }

    // Focus styles for accessibility
    &:focus {
      outline: none;

      .resize-indicator {
        outline: 2px solid var(--bs-focus-ring-color, rgba(13, 110, 253, 0.25));
        outline-offset: 2px;
      }
    }
  }

  // Right sidebar resize handle positioning
  &.sidebar-right > .bslib-sidebar-resize-handle {
    left: 2px;

    &::before {
      left: auto;
      right: 100%;
    }

    &.handle-active::before {
      right: calc(100% - 10px);
    }
  }

  // Hide resize handle when sidebar is not resizable
  > .sidebar:not([data-resizable]) ~ .bslib-sidebar-resize-handle {
    display: none;
  }

  // Hide resize handle during transitions and when collapsed
  &.transitioning,
  &.sidebar-collapsed {
    > .bslib-sidebar-resize-handle {
      display: none;
    }
  }

  // Visual feedback during resize
  &.sidebar-resizing {
    // Disable text selection during resize
    user-select: none;

    > .bslib-sidebar-resize-handle .resize-indicator {
      background-color: var(--_resize-indicator-color-active);
      width: 4px;
      height: 50px;
    }
  }
}

.bslib-sidebar-layout {
  // Sidebar init js uses this variable to know which device size we're on
  --bslib-sidebar-js-window-size: desktop;
}

@include media-breakpoint-down(sm) {
  .bslib-sidebar-layout {
    --bslib-sidebar-js-window-size: mobile;

    // Hide resize handle on mobile devices
    .bslib-sidebar-resize-handle {
      display: none !important;
    }
  }
}

@include media-breakpoint-up(sm) {
  .bslib-sidebar-layout[data-collapsible-desktop="false"] {
    // Always-open sidebars don't have a toggle & can use normal padding
    --_padding-icon: var(--_padding);

    > .collapse-toggle {
      display: none;
    }

    > .sidebar[hidden] {
      display: block !important;
      > .sidebar-content {
        display: flex !important;
      }
    }
  }
}

@include media-breakpoint-down(sm) {
  .bslib-sidebar-layout {
    &, &.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;
      }
    }

    &[data-collapsible-mobile="true"] {
      // On mobile, when the sidebar is collapsible, we add an additional row
      // for the collapse toggle. This avoid overlapping the toggle and the
      // main content when the sidebar is collapsed. When the sidebar expands,
      // it takes up the entire layout area.
      grid-template-rows: calc(var(--_icon-button-size) + var(--_padding)) 1fr;
      > .collapse-toggle {
        grid-row: 1 / 2; // top row
      }
      > .main {
        grid-row: 2 / 3; // bottom row
      }
      > .sidebar {
        grid-row: 1 / 3; // whole layout
      }
      // Sidebar layer has to be lifted up to cover other (nested) sidebars
      &:not(.sidebar-collapsed),
      &.transitioning {
        > .sidebar {
          z-index: $zindex-offcanvas;
        }
        > .collapse-toggle {
          z-index: $zindex-offcanvas;
        }
      }

      > .collapse-toggle {
        top: unset;
        position: relative;
        align-self: center;
      }

      // Keep toggle on sidebar side when expanded on mobile
      &:not(.sidebar-right) > .collapse-toggle {
        left: var(--_icon-size);
        right: unset;
        justify-self: left;
      }

      &.sidebar-right > .collapse-toggle {
        right: var(--_icon-size);
        left: unset;
        justify-self: right;
      }

      > .sidebar {
        opacity: var(--_sidebar-mobile-opacity, 1);
        max-width: var(--_sidebar-mobile-max-width, 100%);
        box-shadow: var(--_sidebar-mobile-box-shadow);
      }

      // Move scrollable region to .sidebar-content to avoid scrolling sidebar
      // elements underneath the collapse toggle
      > .sidebar {
        margin: 0;
        padding-top: var(--_padding-icon);
      }
      > .sidebar > .sidebar-content {
        padding-top: 0;
        height: 100%;
        overflow-y: auto;
      }

      // When `max-width` is less than 100%, push it to the appropriate side
      &:not(.sidebar-right) > .sidebar {
        margin-right: auto;
      }
      &.sidebar-right > .sidebar {
        margin-left: auto;
      }

      // 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;
        }
      }

      // Padding appears in collapse toggle grid region
      > .main {
        padding-top: 1px;
        padding-left: var(--_padding);
        padding-right: var(--_padding);
      }

      // Hide (or soften) main area when sidebar is expanded over content
      > .main {
        opacity: var(--_main-mobile-expanded-opacity);
        transition: opacity var(--_transition-easing-x) var(--_transition-duration);
      }
      &.sidebar-collapsed > .main {
        opacity: 1;
      }

      // Move main bg to layout container so that the collapse toggle grid
      // region has the same background as the main area
      > .main {
        background-color: none;
      }
      &, &.sidebar-collapsed {
        background-color: var(--_main-bg);
      }
    }
  }
}

@include media-breakpoint-down(sm) {
  .bslib-sidebar-layout[data-collapsible-mobile="false"] {
    // Always open sidebars become "flow" layouts
    display: block !important;
    --_padding-icon: var(--_padding); // use normal padding all around
    --_vert-border: var(--_border);   // and activate top border

    > .sidebar[hidden] {
      display: block !important;
      > .sidebar-content {
        display: flex !important;
      }
    }

    & > .sidebar {
      max-height: var(--_mobile-max-height);
      overflow-y: auto;
    }

    &[data-open-mobile="always"] > .sidebar {
      border-top: var(--_vert-border);
    }

    &[data-open-mobile="always-above"] > .sidebar {
      border-bottom: var(--_vert-border);
    }

    > .collapse-toggle {
      display: none;
    }
  }
}

// During resizing, set cursor and don't allow selection
html[data-bslib-sidebar-resizing="true"] {
  cursor: ew-resize !important;
  user-select: none !important;
}
