svelte-ux
Version:
- Increment version in `package.json` and commit as `Version bump to x.y.z` - `npm run publish`
62 lines (61 loc) • 3.4 kB
JavaScript
import { computePosition, autoUpdate, flip, offset, shift, autoPlacement, size, } from '@floating-ui/dom';
import portal from './portal';
export function popover(node, options) {
var _a;
const popoverEl = node;
const anchorEl = (_a = options === null || options === void 0 ? void 0 : options.anchorEl) !== null && _a !== void 0 ? _a : node.parentElement;
const cleanup = autoUpdate(anchorEl, popoverEl, () => {
// Only allow autoPlacement to swap sides (ex. top/bottom) and not also axises (ex. left/right). Mathces flip behavor
const allowedPlacements = (options === null || options === void 0 ? void 0 : options.autoPlacement) && (options === null || options === void 0 ? void 0 : options.placement)
? [options === null || options === void 0 ? void 0 : options.placement, getOppositePlacement(options === null || options === void 0 ? void 0 : options.placement)]
: undefined;
const positionOptions = {
placement: options === null || options === void 0 ? void 0 : options.placement,
middleware: [
offset(options === null || options === void 0 ? void 0 : options.offset),
(options === null || options === void 0 ? void 0 : options.autoPlacement) ? autoPlacement({ allowedPlacements }) : flip(),
(options === null || options === void 0 ? void 0 : options.resize) &&
size({
padding: options === null || options === void 0 ? void 0 : options.padding,
apply({ availableWidth, availableHeight, elements }) {
Object.assign(elements.floating.style, {
maxWidth: `${availableWidth}px`,
maxHeight: `${availableHeight}px`,
});
},
}),
shift({ padding: options === null || options === void 0 ? void 0 : options.padding }),
],
};
computePosition(anchorEl, popoverEl, positionOptions).then(({ x, y }) => {
Object.assign(popoverEl.style, {
left: `${x}px`,
top: `${y}px`,
...((options === null || options === void 0 ? void 0 : options.matchWidth) && {
width: `${anchorEl.offsetWidth}px`,
}),
});
});
});
function onClickOutside(e) {
if (!anchorEl.contains(e.target) && !popoverEl.contains(e.target)) {
node.dispatchEvent(new CustomEvent('clickOutside', node));
}
}
document.addEventListener('click', onClickOutside);
const portalResult = portal(node);
return {
...portalResult,
destroy() {
var _a;
cleanup();
(_a = portalResult === null || portalResult === void 0 ? void 0 : portalResult.destroy) === null || _a === void 0 ? void 0 : _a.call(portalResult);
document.removeEventListener('click', onClickOutside);
},
};
}
// See: https://github.com/floating-ui/floating-ui/blob/master/packages/core/src/utils/getOppositePlacement.ts (not exported)
const hash = { left: 'right', right: 'left', bottom: 'top', top: 'bottom' };
export function getOppositePlacement(placement) {
return placement.replace(/left|right|bottom|top/g, (matched) => hash[matched]);
}