Inner
You may want to position the floating element such that an inner element inside of it is anchored to the reference element. The most common use case for this is a macOS-style select menu, where the selected item pops up directly over the target button.
Despite being related to positioning, the intricate UX details involved requires the modules to be available only for the interactions package.
Usage
There are two modules: inner()
inner()
and
useInnerOffset()
useInnerOffset()
.
-
inner()
inner()
is a middleware that positions the floating element such that an inner element inside of it is anchored to the reference element. -
useInnerOffset()
useInnerOffset()
is an interaction hook that offsets the anchoring upon awheel
wheel
event. This allows the floating element’s height to expand, revealing more list items. This is not strictly required but is necessary to match the UX of the macOS-style select menu.
import {inner, useInnerOffset} from '@floating-ui/react';
// ...
const {context} = useFloating({
middleware: [inner()],
});
const {getReferenceProps, getFloatingProps} = useInteractions([
useInnerOffset(context),
]);
import {inner, useInnerOffset} from '@floating-ui/react';
// ...
const {context} = useFloating({
middleware: [inner()],
});
const {getReferenceProps, getFloatingProps} = useInteractions([
useInnerOffset(context),
]);
Examples
inner Props
interface Props extends DetectOverflowOptions {
listRef: React.MutableRefObject<Array<HTMLElement | null>>;
index: number;
offset?: number;
overflowRef?: React.MutableRefObject<SideObject | null>;
scrollRef?: React.MutableRefObject<HTMLElement | null>;
referenceOverflowThreshold?: number;
minItemsVisible?: number;
onFallbackChange?: (fallback: boolean) => void;
}
interface Props extends DetectOverflowOptions {
listRef: React.MutableRefObject<Array<HTMLElement | null>>;
index: number;
offset?: number;
overflowRef?: React.MutableRefObject<SideObject | null>;
scrollRef?: React.MutableRefObject<HTMLElement | null>;
referenceOverflowThreshold?: number;
minItemsVisible?: number;
onFallbackChange?: (fallback: boolean) => void;
}
listRef
Required
default: []
[]
An ref containing an array of list items inside the floating element.
const listRef = useRef([]);
inner({
listRef,
});
const listRef = useRef([]);
inner({
listRef,
});
index
Required
default: 0
0
The index of the list item that should be anchored to the reference element.
const [index, setIndex] = useState(0);
inner({
index,
});
const [index, setIndex] = useState(0);
inner({
index,
});
overflowRef
default: undefined
undefined
A ref containing a SideObject
SideObject
. This is used to
determine the overflow of the floating element and is required
when using the useInnerOffset()
useInnerOffset()
interaction hook.
const overflowRef = useRef({
top: 0,
bottom: 0,
left: 0,
right: 0,
});
inner({
overflowRef,
});
const overflowRef = useRef({
top: 0,
bottom: 0,
left: 0,
right: 0,
});
inner({
overflowRef,
});
scrollRef
default: undefined
undefined
An optional ref containing an HTMLElement
HTMLElement
. This may be
used as the scrolling container instead of the floating element —
for instance, to position inner elements as direct children
without being interfered by scrolling layout.
referenceOverflowThreshold
default: 0
0
This determines the distance in pixels of the reference element
from the edges of the boundary. If the reference element is too
close to the edges of its clipping boundary,
onFallbackChange
onFallbackChange
will be invoked to use fallback
positioning.
inner({
referenceOverflowThreshold: 20,
});
inner({
referenceOverflowThreshold: 20,
});
minItemsVisible
default: 4
4
Specifies the minimum number of items that should be visible
before fallback positioning is used.
onFallbackChange
onFallbackChange
will be invoked if there are less
than the number specified visible.
inner({
minItemsVisible: 10,
});
inner({
minItemsVisible: 10,
});
offset
default: 0
0
Determines the offset of the anchoring.
inner({
offset: 10,
});
inner({
offset: 10,
});
onFallbackChange
default: no-op
A callback that is invoked with a boolean when positioning should
enter fallback mode. You should fallback to middleware that
create standard anchor positioning when true
true
.
const [fallback, setFallback] = useState(false);
inner({
onFallbackChange: setFallback,
});
const [fallback, setFallback] = useState(false);
inner({
onFallbackChange: setFallback,
});
useInnerOffset Props
interface Props extends DetectOverflowOptions {
enabled?: boolean;
overflowRef: React.MutableRefObject<SideObject | null>;
scrollRef?: React.MutableRefObject<HTMLElement | null>;
onChange: (
offset: number | ((offset: number) => number)
) => void;
}
interface Props extends DetectOverflowOptions {
enabled?: boolean;
overflowRef: React.MutableRefObject<SideObject | null>;
scrollRef?: React.MutableRefObject<HTMLElement | null>;
onChange: (
offset: number | ((offset: number) => number)
) => void;
}
enabled
default: true
true
Conditionally enable/disable the hook.
useInnerOffset(context, {
enabled: false,
});
useInnerOffset(context, {
enabled: false,
});
overflowRef
Required
default: undefined
undefined
A ref containing a SideObject
SideObject
. This is used to
determine the overflow of the floating element.
const overflowRef = useRef({
top: 0,
bottom: 0,
left: 0,
right: 0,
});
useInnerOffset(context, {
overflowRef,
});
const overflowRef = useRef({
top: 0,
bottom: 0,
left: 0,
right: 0,
});
useInnerOffset(context, {
overflowRef,
});
scrollRef
default: undefined
undefined
An optional ref containing an HTMLElement
HTMLElement
. This may be
used as the scrolling container instead of the floating element —
for instance, to position inner elements as direct children
without being interfered by scrolling layout.
onChange
Required
default: no-op
A callback that is invoked with a new offset upon the
wheel
wheel
event to offset the anchoring, and thus expand
the floating element’s height.
const [offset, setOffset] = useState(0);
const {context} = useFloating({
middleware: [
inner({
// ...
offset,
}),
],
});
// ...
useInnerOffset(context, {
onChange: setOffset,
});
const [offset, setOffset] = useState(0);
const {context} = useFloating({
middleware: [
inner({
// ...
offset,
}),
],
});
// ...
useInnerOffset(context, {
onChange: setOffset,
});