@wordpress/block-editor
Version:
122 lines (121 loc) • 3.88 kB
JavaScript
// packages/block-editor/src/components/block-tools/use-block-toolbar-popover-props.js
import { useRefEffect } from "@wordpress/compose";
import { useSelect } from "@wordpress/data";
import { getScrollContainer } from "@wordpress/dom";
import {
useCallback,
useLayoutEffect,
useMemo,
useState
} from "@wordpress/element";
import { store as blockEditorStore } from "../../store/index.mjs";
import { useBlockElement } from "../block-list/use-block-props/use-block-refs.mjs";
import { hasStickyOrFixedPositionValue } from "../../hooks/position.mjs";
import { getElementBounds } from "../../utils/dom.mjs";
var COMMON_PROPS = {
placement: "top-start"
};
var DEFAULT_PROPS = {
...COMMON_PROPS,
flip: false,
shift: true
};
var RESTRICTED_HEIGHT_PROPS = {
...COMMON_PROPS,
flip: true,
shift: false
};
function getProps(contentElement, selectedBlockElement, scrollContainer, toolbarHeight, isSticky) {
if (!contentElement || !selectedBlockElement) {
return DEFAULT_PROPS;
}
const scrollTop = scrollContainer?.scrollTop || 0;
const blockRect = getElementBounds(selectedBlockElement);
const contentRect = contentElement.getBoundingClientRect();
const topOfContentElementInViewport = scrollTop + contentRect.top;
const viewportHeight = contentElement.ownerDocument.documentElement.clientHeight;
const restrictedTopArea = topOfContentElementInViewport + toolbarHeight;
const hasSpaceForToolbarAbove = blockRect.top > restrictedTopArea;
const isBlockTallerThanViewport = blockRect.height > viewportHeight - toolbarHeight;
if (!isSticky && (hasSpaceForToolbarAbove || isBlockTallerThanViewport)) {
return DEFAULT_PROPS;
}
return RESTRICTED_HEIGHT_PROPS;
}
function useBlockToolbarPopoverProps({
contentElement,
clientId
}) {
const selectedBlockElement = useBlockElement(clientId);
const [toolbarHeight, setToolbarHeight] = useState(0);
const { blockIndex, isSticky } = useSelect(
(select) => {
const { getBlockIndex, getBlockAttributes } = select(blockEditorStore);
return {
blockIndex: getBlockIndex(clientId),
isSticky: hasStickyOrFixedPositionValue(
getBlockAttributes(clientId)
)
};
},
[clientId]
);
const scrollContainer = useMemo(() => {
if (!contentElement) {
return;
}
return getScrollContainer(contentElement);
}, [contentElement]);
const [props, setProps] = useState(
() => getProps(
contentElement,
selectedBlockElement,
scrollContainer,
toolbarHeight,
isSticky
)
);
const popoverRef = useRefEffect((popoverNode) => {
setToolbarHeight(popoverNode.offsetHeight);
}, []);
const updateProps = useCallback(
() => setProps(
getProps(
contentElement,
selectedBlockElement,
scrollContainer,
toolbarHeight,
isSticky
)
),
[contentElement, selectedBlockElement, scrollContainer, toolbarHeight]
);
useLayoutEffect(updateProps, [blockIndex, updateProps]);
useLayoutEffect(() => {
if (!contentElement || !selectedBlockElement) {
return;
}
const contentView = contentElement?.ownerDocument?.defaultView;
contentView?.addEventHandler?.("resize", updateProps);
let resizeObserver;
const blockView = selectedBlockElement?.ownerDocument?.defaultView;
if (blockView.ResizeObserver) {
resizeObserver = new blockView.ResizeObserver(updateProps);
resizeObserver.observe(selectedBlockElement);
}
return () => {
contentView?.removeEventHandler?.("resize", updateProps);
if (resizeObserver) {
resizeObserver.disconnect();
}
};
}, [updateProps, contentElement, selectedBlockElement]);
return {
...props,
ref: popoverRef
};
}
export {
useBlockToolbarPopoverProps as default
};
//# sourceMappingURL=use-block-toolbar-popover-props.mjs.map