ToggleLayer

import { ToggleLayer } from 'react-laag';

ToggleLayer is the most important component of react-laag. As the name suggests, this component is used to toggle layers (between show and don't show). It assumes there are two key components at play:

  • a trigger, which can be any html-element
  • a layer, the element we want to toggle, can also be any html-element

position: relative;

react-laag expects you to style the scroll-parent where you want to contain your layer in position: relative (or absolute | fixed). If your layer does not need to be contained, because the trigger scrolls with the rest of the page for instance, you don't have to do anything, because react-laag looks at the document.body by default.

Render props

In order to give you as much control as possible, react-laag makes use of the render prop pattern; Instead of a normal react-element, react-laag expects you to pass in a function which in turn returns a react-element. To illustrate:

// Plain react-element...
<div />

// ...becomes
() => <div />

The cool thing about this is that react-laag provides you with tools, and you get to decide if and how to use them!

Controlled vs. uncontrolled

By default react-laag behaves uncontrolled. That means that some state is managed internally inside react-laag. Most of the time this works fine, but there are cases where more control is desirable. Fortunately, react-laag watches certain props and can tell if you want to manage parts of the state yourself.

const [isOpen, setOpen] = React.useState();

<ToggleLayer
  // by setting this prop, react-laag knows
  // you want control over this part of the state
  isOpen={isOpen}
/>

Props

fieldchildren
Required

Render prop that should render the trigger. Minimal example:

<ToggleLayer
  // rest of props go here
>
  {({ triggerRef }) => <div ref={triggerRef}>Trigger</div>}
</ToggleLayer>
Type
(props: ChildProps): ReactNode
ChildProps
details
triggerRef
Required
RefObject

In order to calculate the layer's position, react-laag needs access to the trigger's dom-element. Assign triggerRef to the ref prop when using a React-element or Component (make sure that you the ref is forwarded with React.forwardRef)

isOpen
boolean

Describes whether the layer is open or closed

open
(): void

Shows the layer

close
(): void

Hides the layer

toggle
(): void

Toggles between show/hide

layerSide
LayerSide | null

null when the layer is closed.
When the layer is open, layerSide describes on which side the layer is currently positioned relative to the trigger. When layerSide is "center", it means that the layer is anchored "CENTER".
LayerSide = "top" | "right" | "bottom" | "left" | "center"

fieldrenderLayer
Required

Render prop that should render the layer.

Type
(props: RenderLayerProps): ReactNode
RenderLayerProps
details
layerProps
Required
{ref: RefObject, style: CSSProperties}

In some cases you can spread these layerProps directly onto your component or element like so:

<div {...layerProps} />

But sometimes you want to add styles on top of the style-object react-laag provides. Such scenario's can be handled like this:

<div 
  ref={layerProps.ref}
  style={{ ...layerProps.style, backgroundColor: 'blue'}}
/>
isOpen
boolean

Determines whether we should render the layer or not. The reason for providing this prop, and not to ignore the renderLayer prop entirely when isOpen === false, is that you still have control over what to do when the layer is closed (ie. handling transitions / animations).

layerSide
LayerSide | null

null when the layer is closed.
When the layer is open, layerSide describes on which side the layer is currently positioned relative to the trigger. When layerSide is "center", it means that the layer is anchored "CENTER".
LayerSide = "top" | "right" | "bottom" | "left" | "center"

arrowStyle
CSSProperties

If you want to display an arrow-like element, which many tooltip-like components do, you can utilize the arrowStyle. This style object gives the position of the center of the trigger, relative to the layer. How you style your arrow further is up to you. react-laag provides an <Arrow /> component which allows you to add an arrow element quick and easy, but you're free to use your own svg-element for instance, and calculate its rotation based on the layerSide prop. Another possibility is to inject the positions into a styled-component and use a &::before{} selector.

triggerRect
ClientRect

Useful if you want to style your layer according to the trigger's width for instance.

close
(): void

Useful if you want to close the layer, from within the layer itself. For instance, when a menu-item was clicked.

A quick example to illustrate all provided RenderLayerProp's:

<ToggleLayer
  renderLayer={({ layerProps, isOpen, triggerRect, arrowStyle, close }) => {
    if (isOpen) {
      return (
        <div
          ref={layerProps.ref}
          className="layer"
          style={{
            ...layerProps.style,
            width: triggerRect.width
          }}
        >
          <div className="layer-arrow" style={arrowStyle} />

          <button onClick={close}>close</button>
        </div>
      );
    }

    return null;
  }}

// rest of props skipped for brevity
/>
fieldplacement

A object containing configuration regarding the placement of the layer.

Type
PlacementConfig
PlacementConfig
details
anchor
AnchorEnum
Default
"TOP_CENTER"

Tells react-laag which anchor (location of the layer) you prefer."CENTER" behaves a bit different compared to the rest of the anchors, in that it centers both horizontally and vertically, overlapping the trigger element.

Possible values
"TOP_LEFT" |"TOP_CENTER" |"TOP_RIGHT" |"BOTTOM_LEFT" |"BOTTOM_CENTER" |"BOTTOM_RIGHT" |"LEFT_TOP" |"LEFT_CENTER" |"LEFT_BOTTOM" |"RIGHT_TOP" |"RIGHT_CENTER" |"RIGHT_BOTTOM" |"CENTER"
possibleAnchors
AnchorEnum[]
Default
All anchors

possibleAnchors has only effect when autoAdjust is enabled. It describes which anchors should be considered when finding the best suitable anchor to fit on the screen.

autoAdjust
boolean
Default
false

Determines whether react-laag should find another anchor when the preferred one does not fit the current screen. When snapToAnchor is set to false, there will be a smooth 'sliding'-like effect from one anchor to the next.

snapToAnchor
boolean
Default
false

Determines whether the layer can place itself between two anchors, creating a 'sliding'-effect when scrolling the page/element. WithsnapToAnchor enabled, the layer will 'jump' from one anchor to the next one instantly.

preferX
"LEFT" | "RIGHT"
Default
"RIGHT"

Only has effect when autoAdjust is enabled. Determines which side is preferred when the layer fits on both the left and right side of the trigger.

preferY
"TOP" | "BOTTOM"
Default
"BOTTOM"

Only has effect when autoAdjust is enabled. Determines which side is preferred when the layer fits on both the top and bottom side of the trigger.

triggerOffset
number
Default
0

Determines the distance in pixels between the layer and the trigger.

scrollOffset
number
Default
10

Determines the minimum margin in pixels between the layer and the scroll-containers (incl. viewport)

arrowOffset
number
Default
0

Determines the minimum margin in pixels between the arrow and the layers edges. Useful when you need to respect the layers border-radius.

layerDimensions
{width: number, height: number} | (layerSide: LayerSide): {width: number, height: number}

Sometimes your layer needs a different width and / or height based on certain conditions, ie. on which side the layer is relative to the trigger-element. react-laag needs to anticipate these conditional dimensions on order to find the best suitable place to render the layer. Otherwise, react-laag will 'see' the layer's dimensions only after it has been re-positioned, which may result in an infinite loop of positional calculations.

{
  // rest of 'placement' here...

  layerDimensions: layerSide => ({
    width: layerSide === "bottom" ? 200 : 100,
    height: layerSide === "bottom" ? 100 : 200,
  })
}
fieldfixed
Type
boolean

By default react-laag tries to render the layer within the closest scroll-container. But in some cases you want the layer to show outside its parent scroll-container. This is especially true for tooltip-like components. It's called 'fixed'-mode, because instead of positioning the layer absolute, relative to its parent, the layer gets positioned fixed against the viewport.

Note
Only the viewport boundaries are taken into account when using placement.autoAdjust
fieldisOpen
Type
boolean

By setting the isOpen prop, you are controlling when the layer should show or not. This also means that trying to call the open(), close() andtoggle() functions from the children-props, will result in an error being thrown.

fieldonStyle
Type
(layerStyle: CSSProperties, arrowStyle: CSSProperties, layerSide: LayerSide): void

By passing onStyle you get control over how the relevant elements are styled. This is an advanced feature, and should generally only be used when necessary. A reason to control the styles yourself, is when you run into performance issues because your layer/trigger is expensive to render, and you want to update the styles without causing React to re-render.

Note
By using this prop you will receive no further style updates via the renderLayer prop when things like the layer's anchor has changed.
fieldcloseOnOutsideClick
Type
boolean

By setting this flag, react-laag will close the layer when a click has ocurred somewhere except the layer / trigger.

fieldonOutsideClick
Type
(): void

By using this prop, react-laag will notify you when a click has ocurred somewhere except the layer / trigger. Useful in combination with isOpen.

fieldcloseOnDisappear
Type
"partial" | "full"

The behavior depends on whether fixed is set to true or false:

In fixed mode, the layer will close when the trigger has fully / partially disappeared. In non-fixed mode, the layer will close when the layer has fully / partially disappeared.

fieldonDisappear
Type
(type: "partial" | "full"): void

By using this prop, react-laag will notify you when:
- the trigger has disappeared (fully / partially) in fixed mode
- the layer has disappeared (fully / partially) in non-fixed mode

fieldResizeObserver
Type
typeof ResizeObserver

Use this prop to inject a ResizeObserver polyfill for browsers that have no support out of the box.

fieldcontainer
Experimental
Type
HTMLElement | (): HTMLElement

Normally, react-laag renders the layers inside the closest scroll-parent. However, there might be use-cases where you want to control in which container the layers are rendered in. Since react-laag (in non-fixed mode) looks at the scrollTop and scrollLeft properties of the scroll-parent for positioning, it is advised to also put the container element inside the the same scroll-parent as the layer. It is possible to place the container element outside of the scroll-parent, but that will only work in 'fixed' mode.

Note
For elements that are part of the React render tree, it is recommended to pass a getter function which returns a reference to the element, because it could be that the element has not been mounted by React yet. For containers defined in an 'index.html' for example, a document.getElementById will suffice.
<div className="scroll-container">
  <div className="tooltip-container"></div>
  <p>
    Your content here...

    <ToggleLayer
      container={() => document.querySelector(".tooltip-container")}
    />
  </p>
</div>
fieldenvironment
Experimental
Type
Window
Default
window

This prop is only useful if you're rendering react-laag within a different window context from where your JavaScript is running; for example, an iframe or a shadow-root.