import { defineComponent, ref, onMounted, nextTick, createElementBlock, openBlock, normalizeStyle, createVNode, unref, mergeProps, withCtx, renderSlot } from 'vue'; import { C as CONTENT_MARGIN } from './utils.js'; import { useResizeObserver } from '@vueuse/core'; import { u as useCollection } from '../Collection/Collection.js'; import { i as injectSelectRootContext } from './SelectRoot.js'; import { i as injectSelectContentContext } from './SelectContentImpl.js'; import { u as useForwardExpose } from '../shared/useForwardExpose.js'; import { c as createContext } from '../shared/createContext.js'; import { P as Primitive } from '../Primitive/Primitive.js'; import { c as clamp } from '../shared/clamp.js'; const [injectSelectItemAlignedPositionContext, provideSelectItemAlignedPositionContext] = createContext("SelectItemAlignedPosition"); const _sfc_main = /* @__PURE__ */ defineComponent({ ...{ inheritAttrs: false }, __name: "SelectItemAlignedPosition", props: { asChild: { type: Boolean }, as: {} }, emits: ["placed"], setup(__props, { emit: __emit }) { const props = __props; const emits = __emit; const { getItems } = useCollection(); const rootContext = injectSelectRootContext(); const contentContext = injectSelectContentContext(); const shouldExpandOnScrollRef = ref(false); const shouldRepositionRef = ref(true); const contentWrapperElement = ref(); const { forwardRef, currentElement: contentElement } = useForwardExpose(); const { viewport, selectedItem, selectedItemText, focusSelectedItem } = contentContext; function position() { if (rootContext.triggerElement.value && rootContext.valueElement.value && contentWrapperElement.value && contentElement.value && viewport?.value && selectedItem?.value && selectedItemText?.value) { const triggerRect = rootContext.triggerElement.value.getBoundingClientRect(); const contentRect = contentElement.value.getBoundingClientRect(); const valueNodeRect = rootContext.valueElement.value.getBoundingClientRect(); const itemTextRect = selectedItemText.value.getBoundingClientRect(); if (rootContext.dir.value !== "rtl") { const itemTextOffset = itemTextRect.left - contentRect.left; const left = valueNodeRect.left - itemTextOffset; const leftDelta = triggerRect.left - left; const minContentWidth = triggerRect.width + leftDelta; const contentWidth = Math.max(minContentWidth, contentRect.width); const rightEdge = window.innerWidth - CONTENT_MARGIN; const clampedLeft = clamp(left, CONTENT_MARGIN, Math.max(CONTENT_MARGIN, rightEdge - contentWidth)); contentWrapperElement.value.style.minWidth = `${minContentWidth}px`; contentWrapperElement.value.style.left = `${clampedLeft}px`; } else { const itemTextOffset = contentRect.right - itemTextRect.right; const right = window.innerWidth - valueNodeRect.right - itemTextOffset; const rightDelta = window.innerWidth - triggerRect.right - right; const minContentWidth = triggerRect.width + rightDelta; const contentWidth = Math.max(minContentWidth, contentRect.width); const leftEdge = window.innerWidth - CONTENT_MARGIN; const clampedRight = clamp( right, CONTENT_MARGIN, Math.max(CONTENT_MARGIN, leftEdge - contentWidth) ); contentWrapperElement.value.style.minWidth = `${minContentWidth}px`; contentWrapperElement.value.style.right = `${clampedRight}px`; } const items = getItems().map((i) => i.ref); const availableHeight = window.innerHeight - CONTENT_MARGIN * 2; const itemsHeight = viewport.value.scrollHeight; const contentStyles = window.getComputedStyle(contentElement.value); const contentBorderTopWidth = Number.parseInt( contentStyles.borderTopWidth, 10 ); const contentPaddingTop = Number.parseInt(contentStyles.paddingTop, 10); const contentBorderBottomWidth = Number.parseInt( contentStyles.borderBottomWidth, 10 ); const contentPaddingBottom = Number.parseInt( contentStyles.paddingBottom, 10 ); const fullContentHeight = contentBorderTopWidth + contentPaddingTop + itemsHeight + contentPaddingBottom + contentBorderBottomWidth; const minContentHeight = Math.min( selectedItem.value.offsetHeight * 5, fullContentHeight ); const viewportStyles = window.getComputedStyle(viewport.value); const viewportPaddingTop = Number.parseInt(viewportStyles.paddingTop, 10); const viewportPaddingBottom = Number.parseInt( viewportStyles.paddingBottom, 10 ); const topEdgeToTriggerMiddle = triggerRect.top + triggerRect.height / 2 - CONTENT_MARGIN; const triggerMiddleToBottomEdge = availableHeight - topEdgeToTriggerMiddle; const selectedItemHalfHeight = selectedItem.value.offsetHeight / 2; const itemOffsetMiddle = selectedItem.value.offsetTop + selectedItemHalfHeight; const contentTopToItemMiddle = contentBorderTopWidth + contentPaddingTop + itemOffsetMiddle; const itemMiddleToContentBottom = fullContentHeight - contentTopToItemMiddle; const willAlignWithoutTopOverflow = contentTopToItemMiddle <= topEdgeToTriggerMiddle; if (willAlignWithoutTopOverflow) { const isLastItem = selectedItem.value === items[items.length - 1]; contentWrapperElement.value.style.bottom = `${0}px`; const viewportOffsetBottom = contentElement.value.clientHeight - viewport.value.offsetTop - viewport.value.offsetHeight; const clampedTriggerMiddleToBottomEdge = Math.max( triggerMiddleToBottomEdge, selectedItemHalfHeight + (isLastItem ? viewportPaddingBottom : 0) + viewportOffsetBottom + contentBorderBottomWidth ); const height = contentTopToItemMiddle + clampedTriggerMiddleToBottomEdge; contentWrapperElement.value.style.height = `${height}px`; } else { const isFirstItem = selectedItem.value === items[0]; contentWrapperElement.value.style.top = `${0}px`; const clampedTopEdgeToTriggerMiddle = Math.max( topEdgeToTriggerMiddle, contentBorderTopWidth + viewport.value.offsetTop + (isFirstItem ? viewportPaddingTop : 0) + selectedItemHalfHeight ); const height = clampedTopEdgeToTriggerMiddle + itemMiddleToContentBottom; contentWrapperElement.value.style.height = `${height}px`; viewport.value.scrollTop = contentTopToItemMiddle - topEdgeToTriggerMiddle + viewport.value.offsetTop; } contentWrapperElement.value.style.margin = `${CONTENT_MARGIN}px 0`; contentWrapperElement.value.style.minHeight = `${minContentHeight}px`; contentWrapperElement.value.style.maxHeight = `${availableHeight}px`; emits("placed"); requestAnimationFrame(() => shouldExpandOnScrollRef.value = true); } } const contentZIndex = ref(""); onMounted(async () => { await nextTick(); position(); if (contentElement.value) contentZIndex.value = window.getComputedStyle(contentElement.value).zIndex; }); function handleScrollButtonChange(node) { if (node && shouldRepositionRef.value === true) { position(); focusSelectedItem?.(); shouldRepositionRef.value = false; } } useResizeObserver(rootContext.triggerElement, () => { position(); }); provideSelectItemAlignedPositionContext({ contentWrapper: contentWrapperElement, shouldExpandOnScrollRef, onScrollButtonChange: handleScrollButtonChange }); return (_ctx, _cache) => { return openBlock(), createElementBlock("div", { ref_key: "contentWrapperElement", ref: contentWrapperElement, style: normalizeStyle({ display: "flex", flexDirection: "column", position: "fixed", zIndex: contentZIndex.value }) }, [ createVNode(unref(Primitive), mergeProps({ ref: unref(forwardRef), style: { // When we get the height of the content, it includes borders. If we were to set // the height without having `boxSizing: 'border-box'` it would be too big. boxSizing: "border-box", // We need to ensure the content doesn't get taller than the wrapper maxHeight: "100%" } }, { ..._ctx.$attrs, ...props }), { default: withCtx(() => [ renderSlot(_ctx.$slots, "default") ]), _: 3 }, 16) ], 4); }; } }); export { _sfc_main as _, injectSelectItemAlignedPositionContext as i }; //# sourceMappingURL=SelectItemAlignedPosition.js.map