@create-figma-plugin/ui
Version:
Production-grade Preact components that replicate the Figma UI design
89 lines • 3.76 kB
JavaScript
import { INVALID_ID, ITEM_ID_DATA_ATTRIBUTE_NAME, VIEWPORT_MARGIN } from '../../../utilities/private/constants.js';
export function updateMenuElementLayout(rootElement, menuElement, selectedId) {
const rootElementBoundingClientRect = rootElement.getBoundingClientRect();
const rootWidth = rootElement.offsetWidth;
const rootHeight = rootElement.offsetHeight;
const rootLeft = rootElementBoundingClientRect.left;
const rootTop = rootElementBoundingClientRect.top;
menuElement.style.minWidth = `${rootWidth}px`;
const menuElementMaxWidth = window.innerWidth - 2 * VIEWPORT_MARGIN;
menuElement.style.maxWidth = `${menuElementMaxWidth}px`;
const menuElementMaxHeight = window.innerHeight - 2 * VIEWPORT_MARGIN;
menuElement.style.maxHeight = `${menuElementMaxHeight}px`;
const menuWidth = menuElement.offsetWidth;
const menuHeight = menuElement.offsetHeight;
const menuScrollHeight = menuElement.scrollHeight;
const menuPaddingTop = parseInt(window.getComputedStyle(menuElement).paddingTop, 10);
const labelElement = getSelectedLabelElement(menuElement, selectedId);
const left = computeLeft({
menuWidth,
rootLeft
});
menuElement.style.left = `${left}px`;
const top = computeTop({
menuHeight,
rootTop,
selectedTop: labelElement.offsetTop
});
menuElement.style.top = `${top}px`;
const isScrollable = menuScrollHeight > menuHeight;
if (isScrollable === false) {
return;
}
menuElement.scrollTop = computeScrollTop({
menuHeight,
menuPaddingTop,
menuScrollHeight,
rootHeight,
rootTop,
selectedTop: labelElement.offsetTop
});
}
function getSelectedLabelElement(menuElement, selectedId) {
const inputElement = menuElement.querySelector(selectedId === INVALID_ID
? `[${ITEM_ID_DATA_ATTRIBUTE_NAME}]`
: `[${ITEM_ID_DATA_ATTRIBUTE_NAME}='${selectedId}']`);
if (inputElement === null) {
throw new Error('`inputElement` is `null`');
}
const labelElement = inputElement.parentElement;
if (labelElement === null) {
throw new Error('`labelElement` is `null`');
}
return labelElement;
}
function computeLeft(options) {
const { menuWidth, rootLeft } = options;
if (rootLeft <= VIEWPORT_MARGIN) {
return VIEWPORT_MARGIN;
}
const viewportWidth = window.innerWidth;
if (rootLeft + menuWidth > viewportWidth - VIEWPORT_MARGIN) {
return viewportWidth - VIEWPORT_MARGIN - menuWidth;
}
return rootLeft;
}
function computeTop(options) {
const { menuHeight, rootTop, selectedTop } = options;
const viewportHeight = window.innerHeight;
if (rootTop <= VIEWPORT_MARGIN ||
menuHeight === viewportHeight - 2 * VIEWPORT_MARGIN) {
return VIEWPORT_MARGIN;
}
const top = rootTop - selectedTop;
const minimumTop = VIEWPORT_MARGIN;
const maximumTop = viewportHeight - VIEWPORT_MARGIN - menuHeight;
return restrictToRange(top, minimumTop, maximumTop);
}
function computeScrollTop(options) {
const { menuHeight, menuPaddingTop, menuScrollHeight, rootHeight, rootTop, selectedTop } = options;
const restrictedRootTop = restrictToRange(rootTop, VIEWPORT_MARGIN, window.innerHeight - VIEWPORT_MARGIN - rootHeight + menuPaddingTop / 2);
const scrollTop = selectedTop - (restrictedRootTop - VIEWPORT_MARGIN);
const minimumScrollTop = 0;
const maximumScrollTop = menuScrollHeight - menuHeight;
return restrictToRange(scrollTop, minimumScrollTop, maximumScrollTop);
}
function restrictToRange(number, minimum, maximum) {
return Math.min(Math.max(number, minimum), maximum);
}
//# sourceMappingURL=update-menu-element-layout.js.map