0

Is it possible to create dropdown where the layout has a sticky header and footer and the middle/content part is scrollable.

P.S. I was able to do it with setting the height, but that's not a feasable solution because it would stay always that same height even when the content is less.

I have created a stackblitz preview, please check out the example: https://stackblitz.com/edit/stackblitz-starters-qxjh28?file=src%2FApp.tsx

meightythree
  • 308
  • 2
  • 16

1 Answers1

1

Yes, it is possible to create a dropdown with a sticky header and footer, and a scrollable middle/content part. In the provided code using @floating-ui/react, you can achieve this behavior by making use of CSS and the position: sticky property.

To make the header and footer sticky, you can add the following CSS styles to the header and footer elements within the dropdown:

/* App.css or any other suitable CSS file */
.dropdown-container h3 {
  position: sticky;
  top: 0;
  background-color: #f1f1f1;
  padding: 10px;
}

.dropdown-container h3:last-child {
  position: sticky;
  bottom: 0;
  background-color: #f1f1f1;
  padding: 10px;
}

Here's the updated App component with the necessary CSS styles:

import * as React from 'react';
import { useState } from 'react';
import {
  autoUpdate,
  useFloating,
  useClick,
  useDismiss,
  offset,
  flip,
  size,
  useInteractions,
  FloatingPortal,
} from '@floating-ui/react';

import './style.css';

const OFFSET = 10;

export default function App() {
  const [isOpen, setIsOpen] = useState(false);

  const { refs, floatingStyles, context, strategy } = useFloating({
    placement: 'bottom-start',
    open: isOpen,
    onOpenChange: setIsOpen,
    whileElementMounted: autoUpdate,
    middleware: [
      offset(OFFSET),
      flip(),
      size({
        apply: ({ availableHeight, elements }) => {
          Object.assign(elements.floating.style, {
            maxHeight: `${Math.max(50, availableHeight - OFFSET)}px`,
          });
        },
      }),
    ],
  });

  const click = useClick(context);
  const dismiss = useDismiss(context);

  const { getReferenceProps, getFloatingProps } = useInteractions([
    click,
    dismiss,
  ]);

  return (
    <>
      <button {...getReferenceProps({ ref: refs.setReference })}>
        open dropdown
      </button>
      {isOpen && (
        <FloatingPortal>
          <div
            className="dropdown-container"
            {...getFloatingProps({
              ref: refs.setFloating,
              style: { ...floatingStyles, position: strategy },
            })}
          >
            <h3>Dropdown header (make me sticky)</h3>
            <ul>
              {Array.from({ length: 100 }).map((_, index) => (
                <li key={index}>{index}. list item</li>
              ))}
            </ul>
            <h3>Dropdown footer (make me sticky)</h3>
          </div>
        </FloatingPortal>
      )}
    </>
  );
}

With the above CSS styles, the <h3> elements inside the .dropdown-container will become sticky at the top and bottom respectively, creating the sticky header and footer effect. The middle/content part, represented by the <ul> element, will be scrollable when the content exceeds the available height.

Please note that position: sticky has some browser compatibility considerations, so ensure that it works as expected in your target browsers. The provided CSS styles are suitable for modern web browsers that support position: sticky.

snowden
  • 106
  • 5