Class AppHeadroom

java.lang.Object
com.vaadin.flow.component.Component
org.vaadin.addons.joelpop.appheadroom.ui.component.AppHeadroom
All Implemented Interfaces:
com.vaadin.flow.component.AttachNotifier, com.vaadin.flow.component.DetachNotifier, com.vaadin.flow.component.HasElement, com.vaadin.flow.component.HasStyle, Serializable

@Tag("app-headroom") @JsModule("./app-headroom.ts") public class AppHeadroom extends com.vaadin.flow.component.Component
Hides/shows an AppLayout's top and bottom navigation bars on scroll.

This component only ever knows about AppLayout's own standard, public contract — specifically the navbar-top and navbar-bottom shadow-DOM parts every vaadin-app-layout instance exposes, regardless of subclass. It has no knowledge of, and no dependency on, any specific AppLayout extension (e.g. one that adds a persistent side rail): whatever a subclass does with those two standard parts, AppHeadroom finds and manages them the same way every time.

By default, a bar that's already pinned to the viewport (position: fixed) and shaped like a vertical rail rather than a horizontal bar (taller than wide) is left alone automatically — a plain, observable geometry fact, not something any extension has to declare. For cases where that inference isn't right (or an extension's own behavior can't be reliably inferred that way), setTopBarCollapsible(boolean) / setBottomBarCollapsible(boolean) let calling code state it explicitly instead. See those methods for how this is meant to be wired up from application code.

See Also:
  • Field Details

  • Method Details

    • applyTo

      public static AppHeadroom applyTo(com.vaadin.flow.component.applayout.AppLayout layout)
      Applies headroom scroll-hide/show behavior to the given AppLayout and returns the component for optional further configuration.

      The returned instance attaches itself as a peer under the current UI's root element, never as a light-DOM child of layout — so it never shows up in layout.getChildren(). This happens automatically whenever layout itself is attached (immediately, if it already is), and follows layout across any subsequent detach/re-attach. Call remove() to detach headroom behavior from layout without affecting the layout itself.

      The effect is active on every device by default; see setActivationPredicate(SerializableBiPredicate) to restrict it by device type/orientation.

    • remove

      public void remove()
      Detaches this instance, removing its scroll-hide/show behavior from the AppLayout it was applied to. Has no effect if already detached.
    • setTopOffset

      public AppHeadroom setTopOffset(int px)
      Sets the distance from the top of the page, in pixels, within which the chrome is always shown (default 100). Returns this for chaining.
      Throws:
      IllegalArgumentException - if px is negative
    • setHideTolerance

      public AppHeadroom setHideTolerance(int px)
      Sets how far, in pixels, the user must scroll down past the last shown-position high-water mark before the chrome hides (default 30). Returns this for chaining.
      Throws:
      IllegalArgumentException - if px is negative
    • setShowTolerance

      public AppHeadroom setShowTolerance(int px)
      Sets how far, in pixels, the user must scroll up past the last hidden-position low-water mark before the chrome is restored (default 30). Returns this for chaining.
      Throws:
      IllegalArgumentException - if px is negative
    • setTransitionDuration

      public AppHeadroom setTransitionDuration(int ms)
      Sets how long, in milliseconds, the show/hide slide and padding transitions take (default 600). Returns this for chaining.
      Throws:
      IllegalArgumentException - if ms is negative
    • getCondensedTop

      public AppHeadroom.CondensedBar getCondensedTop()
      The top bar's condensed-view accessor — a small alternate view that can cross-fade into place while the real top bar is scroll-hidden. Shape it with AppHeadroom.CondensedBar.asFloating() or AppHeadroom.CondensedBar.asRibbon() before supplying content; neither is configured by default, so nothing shows while hidden, same as before this feature existed.

      Fades on scroll position alone, the same as the real bar. The one thing that suppresses it: an explicit setTopBarCollapsible(boolean) (false) — a deliberate "never hide this" declaration from application code. The automatic pinned-rail geometry check that can similarly keep the real bar from ever hiding does not also suppress this — that check has no way to know whether a permanently-visible real bar and an app-supplied condensed view are related at all, so it only ever affects the real bar's own slide-away.

    • getCondensedBottom

      public AppHeadroom.CondensedBar getCondensedBottom()
      Same as getCondensedTop(), for the bottom bar.
    • setTopBarCollapsible

      public AppHeadroom setTopBarCollapsible(boolean collapsible)
      Explicitly overrides whether the top bar can ever collapse (hide) at all, regardless of the automatic position/shape inference. false means "never collapse this" — also suppresses getCondensedTop()'s condensed view, unlike the automatic inference, which only ever affects the real bar.

      Not called directly by any AppLayout extension — extensions have no reason to know AppHeadroom exists. Instead, this is meant to be wired up from application code that already explicitly combines an AppLayout extension with AppHeadroom (for example, code overriding an extension's own hook for observing layout-mode changes, if it has one), reacting to whatever that extension's own public API exposes about its current state and calling this method accordingly.

    • setBottomBarCollapsible

      public AppHeadroom setBottomBarCollapsible(boolean collapsible)
      Same as setTopBarCollapsible(boolean), for the bottom bar.
    • setTopBarPinned

      @Deprecated(since="25.1.1") public AppHeadroom setTopBarPinned(boolean pinned)
      Deprecated.
      Use setTopBarCollapsible(boolean) instead — note the inverted argument: setTopBarPinned(true) (never collapse) is equivalent to setTopBarCollapsible(false). Calling this delegates to setTopBarCollapsible(boolean).
    • setBottomBarPinned

      @Deprecated(since="25.1.1") public AppHeadroom setBottomBarPinned(boolean pinned)
      Deprecated.
      Use setBottomBarCollapsible(boolean) instead — see setTopBarPinned(boolean) for the argument-inversion note. Calling this delegates to setBottomBarCollapsible(boolean).
    • isCollapsed

      @Synchronize("pinned-changed") public boolean isCollapsed()
      Whether the chrome is currently collapsed/hidden (true) or expanded/shown (false, the initial and default state). Kept in sync with the client's own scroll-driven expand/collapse state; see addCollapseChangeListener(ComponentEventListener) to be notified of changes instead of polling this.
    • isPinned

      @Deprecated(since="25.1.1") public boolean isPinned()
      Deprecated.
      Use isCollapsed() instead — note the inverted return value: isPinned() (shown) is equivalent to !isCollapsed().
    • addCollapseChangeListener

      public com.vaadin.flow.shared.Registration addCollapseChangeListener(com.vaadin.flow.component.ComponentEventListener<AppHeadroom.CollapseChangeEvent> listener)
      Registers a listener to be notified whenever isCollapsed() changes.
    • addPinnedChangeListener

      @Deprecated(since="25.1.1") public com.vaadin.flow.shared.Registration addPinnedChangeListener(com.vaadin.flow.component.ComponentEventListener<AppHeadroom.PinnedChangeEvent> listener)
    • onDetach

      protected void onDetach(com.vaadin.flow.component.DetachEvent detachEvent)
      Resets the server-visible expanded state directly, rather than relying on the client's own pinned-changed event during teardown: when detachment is server-initiated (e.g. layout.remove(headroom)), Flow stops routing further client events for this component the moment removal begins, so the client-fired event from disconnectedCallback() never reaches here.
      Overrides:
      onDetach in class com.vaadin.flow.component.Component
    • setActivationPredicate

      public AppHeadroom setActivationPredicate(com.vaadin.flow.function.SerializableBiPredicate<AppHeadroom.DeviceType, AppHeadroom.Orientation> predicate)
      Sets the predicate that decides whether the headroom effect should be active, evaluated against this session's detected AppHeadroom.DeviceType and current AppHeadroom.Orientation. Re-evaluated automatically whenever either becomes known or changes (e.g. on device rotation) — there's nothing further to wire up.

      The default predicate always returns true: the effect is active on every device unless this is called. There's no separate plain on/off switch — force it fully on or off yourself with (deviceType, orientation) -> true / -> false, or restrict it by device, e.g. to restore the original phone-always/tablet-landscape-only behavior this had before being extracted into this library:

      AppHeadroom.applyTo(layout).setActivationPredicate((deviceType, orientation) ->
          deviceType == AppHeadroom.DeviceType.PHONE
              || (deviceType == AppHeadroom.DeviceType.TABLET && orientation == AppHeadroom.Orientation.LANDSCAPE));
      
      Returns:
      this, for chaining
    • setTabletMinShortSidePx

      public AppHeadroom setTabletMinShortSidePx(int px)
      Overrides the physical-screen-shorter-side threshold, in CSS pixels, used to distinguish AppHeadroom.DeviceType.TABLET from AppHeadroom.DeviceType.PHONE among touch devices (default 768, matching vaadin-flow-app-nav-layout's equivalent device detection).

      Must be called before the target AppLayout attaches to take effect — device type is detected once, at first attach, same as setActivationPredicate(SerializableBiPredicate)'s predicate is evaluated against whatever's known at the time.

      Returns:
      this, for chaining
      Throws:
      IllegalArgumentException - if px is negative
    • isActive

      public boolean isActive()
      Whether the headroom effect is currently active for this session, per the last evaluation of the activation predicate. Defaults to true until device type and orientation are both known (shortly after the target AppLayout attaches).