remix/ui/anchor · Demo

View Source

Anchor

Position a floating element against an anchor with the low-level anchor primitive.

Positioned surface
import { css, ref } from 'remix/ui'
import { anchor } from 'remix/ui/anchor'
import button from 'remix/ui/button'

export default function Example() {
  let anchorNode: HTMLElement | null = null
  let floatingNode: HTMLElement | null = null
  let cleanupAnchor = () => {}

  function positionFloating() {
    cleanupAnchor()

    if (anchorNode && floatingNode) {
      cleanupAnchor = anchor(floatingNode, anchorNode, {
        placement: 'bottom-start',
        offset: 8,
      })
    }
  }

  function clearFloating() {
    cleanupAnchor()
    cleanupAnchor = () => {}
  }

  return () => (
    <div mix={demoCss}>
      <button
        mix={[
          button(),
          ref((node, signal) => {
            if (!(node instanceof HTMLElement)) return

            anchorNode = node
            positionFloating()

            signal.addEventListener('abort', () => {
              if (anchorNode === node) {
                anchorNode = null
              }

              clearFloating()
            })
          }),
        ]}
      >
        Anchor target
      </button>

      <div
        mix={[
          floatingCss,
          ref((node, signal) => {
            if (!(node instanceof HTMLElement)) return

            floatingNode = node
            positionFloating()

            signal.addEventListener('abort', () => {
              if (floatingNode === node) {
                floatingNode = null
              }

              clearFloating()
            })
          }),
        ]}
      >
        Positioned surface
      </div>
    </div>
  )
}

const demoCss = css({
  display: 'grid',
  placeItems: 'center',
  minHeight: '9rem',
  width: 'min(100%, 24rem)',
})

const floatingCss = css({
  boxSizing: 'border-box',
  width: '12rem',
  border: '1px solid rgba(0, 0, 0, 0.12)',
  borderRadius: '8px',
  background: '#FFFFFF',
  boxShadow: '0 12px 32px rgba(0, 0, 0, 0.12)',
  color: '#101010',
  fontFamily: '"Inter Variable", Inter, ui-sans-serif, system-ui, sans-serif',
  fontSize: '13px',
  lineHeight: '18px',
  fontWeight: 500,
  letterSpacing: 0,
  padding: '10px 12px',
})