@ulu/frontend
Version:
A versatile SCSS and JavaScript component library offering configurable, accessible components and flexible integration into any project, with SCSS modules suitable for modern JS frameworks.
83 lines (75 loc) • 2.25 kB
JavaScript
/**
* @module utils/floating-ui
*/
import {
computePosition,
autoUpdate,
offset,
inline,
flip,
shift,
arrow,
} from "@floating-ui/dom";
export const defaults = {
strategy: "absolute",
placement: "bottom",
inline: false,
offset: {
mainAxis: 16
},
shift: true,
flip: true,
arrow: true, // Options for arrow (not element)
};
/**
*
* @param {Object} elements Elements (trigger, content, and optionally contentArrow)
* @param {*} options Configuration options for floatingUi
* @returns {Function} floating cleanup function call when no longer needed
*/
export function createFloatingUi(elements, config) {
const options = Object.assign({}, defaults, config);
const { placement, strategy } = options;
const { trigger, content, contentArrow } = elements;
return autoUpdate(trigger, content, () => {
computePosition(trigger, content, {
placement,
strategy,
middleware: [
...addPlugin(inline, options.inline),
...addPlugin(offset, options.offset),
...addPlugin(flip, options.flip),
...addPlugin(shift, options.shift),
...addPlugin(arrow, contentArrow && options.arrow, { element: contentArrow }),
]
}).then(data => {
const { x, y, middlewareData, placement } = data;
const arrowPos = middlewareData.arrow;
// Update computed styles for the content (popover container)
Object.assign(content.style, {
left: `${ x }px`,
top: `${ y }px`
});
// Update placement attribute (used by arrow for theming)
content.setAttribute("data-placement", placement);
// If arrow was enabled, add it's computed styles
if (arrowPos) {
Object.assign(contentArrow.style, {
// position: "absolute",
left: arrowPos?.x != null ? `${ arrowPos.x }px` : "",
top: arrowPos?.y != null ? `${ arrowPos.y }px` : "",
});
}
});
});
}
function addPlugin(plugin, option, overrides = {}) {
if (!option) {
return [];
// If object add it as options, else just enable without options
} else if (typeof option === "object") {
return [plugin({ ...option, ...overrides })];
} else {
return [plugin(overrides)];
}
}