UNPKG

@floating-ui/vue

Version:
191 lines (183 loc) 6.08 kB
import { arrow as arrow$1, computePosition } from '@floating-ui/dom'; export { autoPlacement, autoUpdate, computePosition, detectOverflow, flip, getOverflowAncestors, hide, inline, limitShift, offset, platform, shift, size } from '@floating-ui/dom'; import { isNode, getNodeName } from '@floating-ui/utils/dom'; import { unref, computed, ref, shallowRef, watch, getCurrentScope, onScopeDispose, shallowReadonly } from 'vue-demi'; function isComponentPublicInstance(target) { return target != null && typeof target === 'object' && '$el' in target; } function unwrapElement(target) { if (isComponentPublicInstance(target)) { const element = target.$el; return isNode(element) && getNodeName(element) === '#comment' ? null : element; } return target; } function toValue(source) { return typeof source === 'function' ? source() : unref(source); } /** * Positions an inner element of the floating element such that it is centered to the reference element. * @param options The arrow options. * @see https://floating-ui.com/docs/arrow */ function arrow(options) { return { name: 'arrow', options, fn(args) { const element = unwrapElement(toValue(options.element)); if (element == null) { return {}; } return arrow$1({ element, padding: options.padding }).fn(args); } }; } function getDPR(element) { if (typeof window === 'undefined') { return 1; } const win = element.ownerDocument.defaultView || window; return win.devicePixelRatio || 1; } function roundByDPR(element, value) { const dpr = getDPR(element); return Math.round(value * dpr) / dpr; } /** * Computes the `x` and `y` coordinates that will place the floating element next to a reference element when it is given a certain CSS positioning strategy. * @param reference The reference template ref. * @param floating The floating template ref. * @param options The floating options. * @see https://floating-ui.com/docs/vue */ function useFloating(reference, floating, options) { if (options === void 0) { options = {}; } const whileElementsMountedOption = options.whileElementsMounted; const openOption = computed(() => { var _toValue; return (_toValue = toValue(options.open)) != null ? _toValue : true; }); const middlewareOption = computed(() => toValue(options.middleware)); const placementOption = computed(() => { var _toValue2; return (_toValue2 = toValue(options.placement)) != null ? _toValue2 : 'bottom'; }); const strategyOption = computed(() => { var _toValue3; return (_toValue3 = toValue(options.strategy)) != null ? _toValue3 : 'absolute'; }); const transformOption = computed(() => { var _toValue4; return (_toValue4 = toValue(options.transform)) != null ? _toValue4 : true; }); const referenceElement = computed(() => unwrapElement(reference.value)); const floatingElement = computed(() => unwrapElement(floating.value)); const x = ref(0); const y = ref(0); const strategy = ref(strategyOption.value); const placement = ref(placementOption.value); const middlewareData = shallowRef({}); const isPositioned = ref(false); const floatingStyles = computed(() => { const initialStyles = { position: strategy.value, left: '0', top: '0' }; if (!floatingElement.value) { return initialStyles; } const xVal = roundByDPR(floatingElement.value, x.value); const yVal = roundByDPR(floatingElement.value, y.value); if (transformOption.value) { return { ...initialStyles, transform: "translate(" + xVal + "px, " + yVal + "px)", ...(getDPR(floatingElement.value) >= 1.5 && { willChange: 'transform' }) }; } return { position: strategy.value, left: xVal + "px", top: yVal + "px" }; }); let whileElementsMountedCleanup; function update() { if (referenceElement.value == null || floatingElement.value == null) { return; } const open = openOption.value; computePosition(referenceElement.value, floatingElement.value, { middleware: middlewareOption.value, placement: placementOption.value, strategy: strategyOption.value }).then(position => { x.value = position.x; y.value = position.y; strategy.value = position.strategy; placement.value = position.placement; middlewareData.value = position.middlewareData; /** * The floating element's position may be recomputed while it's closed * but still mounted (such as when transitioning out). To ensure * `isPositioned` will be `false` initially on the next open, avoid * setting it to `true` when `open === false` (must be specified). */ isPositioned.value = open !== false; }); } function cleanup() { if (typeof whileElementsMountedCleanup === 'function') { whileElementsMountedCleanup(); whileElementsMountedCleanup = undefined; } } function attach() { cleanup(); if (whileElementsMountedOption === undefined) { update(); return; } if (referenceElement.value != null && floatingElement.value != null) { whileElementsMountedCleanup = whileElementsMountedOption(referenceElement.value, floatingElement.value, update); return; } } function reset() { if (!openOption.value) { isPositioned.value = false; } } watch([middlewareOption, placementOption, strategyOption, openOption], update, { flush: 'sync' }); watch([referenceElement, floatingElement], attach, { flush: 'sync' }); watch(openOption, reset, { flush: 'sync' }); if (getCurrentScope()) { onScopeDispose(cleanup); } return { x: shallowReadonly(x), y: shallowReadonly(y), strategy: shallowReadonly(strategy), placement: shallowReadonly(placement), middlewareData: shallowReadonly(middlewareData), isPositioned: shallowReadonly(isPositioned), floatingStyles, update }; } export { arrow, useFloating };