UNPKG

reka-ui

Version:

Vue port for Radix UI Primitives.

1 lines 15.9 kB
{"version":3,"file":"SelectItemAlignedPosition.cjs","sources":["../../src/Select/SelectItemAlignedPosition.vue"],"sourcesContent":["<script lang=\"ts\">\nimport type { Ref } from 'vue'\nimport type { PrimitiveProps } from '@/Primitive'\nimport { clamp, createContext, useForwardExpose } from '@/shared'\nimport { useCollection } from '@/Collection'\nimport { useResizeObserver } from '@vueuse/core'\n\ninterface SelectItemAlignedPositionContext {\n contentWrapper?: Ref<HTMLElement | undefined>\n shouldExpandOnScrollRef?: Ref<boolean>\n onScrollButtonChange: (node: HTMLElement | undefined) => void\n}\n\nexport interface SelectItemAlignedPositionProps extends PrimitiveProps {}\n\nexport const [injectSelectItemAlignedPositionContext, provideSelectItemAlignedPositionContext]\n = createContext<SelectItemAlignedPositionContext>('SelectItemAlignedPosition')\n</script>\n\n<script setup lang=\"ts\">\nimport { nextTick, onMounted, ref } from 'vue'\nimport { injectSelectRootContext } from './SelectRoot.vue'\nimport { injectSelectContentContext } from './SelectContentImpl.vue'\nimport { CONTENT_MARGIN } from './utils'\nimport { Primitive } from '@/Primitive'\n\ndefineOptions({\n inheritAttrs: false,\n})\n\nconst props = defineProps<SelectItemAlignedPositionProps>()\nconst emits = defineEmits<{\n placed: []\n}>()\n\nconst { getItems } = useCollection()\nconst rootContext = injectSelectRootContext()\nconst contentContext = injectSelectContentContext()\n\nconst shouldExpandOnScrollRef = ref(false)\nconst shouldRepositionRef = ref(true)\n\nconst contentWrapperElement = ref<HTMLElement>()\nconst { forwardRef, currentElement: contentElement } = useForwardExpose()\n\nconst { viewport, selectedItem, selectedItemText, focusSelectedItem }\n = contentContext!\n\nfunction position() {\n if (\n rootContext.triggerElement.value\n && rootContext.valueElement.value\n && contentWrapperElement.value\n && contentElement.value\n && viewport?.value\n && selectedItem?.value\n && selectedItemText?.value\n ) {\n const triggerRect = rootContext.triggerElement.value.getBoundingClientRect()\n\n // -----------------------------------------------------------------------------------------\n // Horizontal positioning\n // -----------------------------------------------------------------------------------------\n const contentRect = contentElement.value.getBoundingClientRect()\n const valueNodeRect = rootContext.valueElement.value.getBoundingClientRect()\n const itemTextRect = selectedItemText.value.getBoundingClientRect()\n\n if (rootContext.dir.value !== 'rtl') {\n const itemTextOffset = itemTextRect.left - contentRect.left\n const left = valueNodeRect.left - itemTextOffset\n const leftDelta = triggerRect.left - left\n const minContentWidth = triggerRect.width + leftDelta\n const contentWidth = Math.max(minContentWidth, contentRect.width)\n const rightEdge = window.innerWidth - CONTENT_MARGIN\n const clampedLeft = clamp(left, CONTENT_MARGIN, Math.max(CONTENT_MARGIN, rightEdge - contentWidth))\n\n contentWrapperElement.value.style.minWidth = `${minContentWidth}px`\n contentWrapperElement.value.style.left = `${clampedLeft}px`\n }\n else {\n const itemTextOffset = contentRect.right - itemTextRect.right\n const right = window.innerWidth - valueNodeRect.right - itemTextOffset\n const rightDelta = window.innerWidth - triggerRect.right - right\n const minContentWidth = triggerRect.width + rightDelta\n const contentWidth = Math.max(minContentWidth, contentRect.width)\n const leftEdge = window.innerWidth - CONTENT_MARGIN\n const clampedRight = clamp(\n right,\n CONTENT_MARGIN,\n Math.max(CONTENT_MARGIN, leftEdge - contentWidth),\n )\n\n contentWrapperElement.value.style.minWidth = `${minContentWidth}px`\n contentWrapperElement.value.style.right = `${clampedRight}px`\n }\n\n // -----------------------------------------------------------------------------------------\n // Vertical positioning\n // -----------------------------------------------------------------------------------------\n const items = getItems().map(i => i.ref)\n const availableHeight = window.innerHeight - CONTENT_MARGIN * 2\n const itemsHeight = viewport.value.scrollHeight\n\n const contentStyles = window.getComputedStyle(contentElement.value)\n const contentBorderTopWidth = Number.parseInt(\n contentStyles.borderTopWidth,\n 10,\n )\n const contentPaddingTop = Number.parseInt(contentStyles.paddingTop, 10)\n const contentBorderBottomWidth = Number.parseInt(\n contentStyles.borderBottomWidth,\n 10,\n )\n const contentPaddingBottom = Number.parseInt(\n contentStyles.paddingBottom,\n 10,\n )\n\n const fullContentHeight = contentBorderTopWidth + contentPaddingTop + itemsHeight + contentPaddingBottom + contentBorderBottomWidth\n const minContentHeight = Math.min(\n selectedItem.value.offsetHeight * 5,\n fullContentHeight,\n )\n\n const viewportStyles = window.getComputedStyle(viewport.value)\n const viewportPaddingTop = Number.parseInt(viewportStyles.paddingTop, 10)\n const viewportPaddingBottom = Number.parseInt(\n viewportStyles.paddingBottom,\n 10,\n )\n\n const topEdgeToTriggerMiddle\n = triggerRect.top + triggerRect.height / 2 - CONTENT_MARGIN\n const triggerMiddleToBottomEdge = availableHeight - topEdgeToTriggerMiddle\n\n const selectedItemHalfHeight = selectedItem.value.offsetHeight / 2\n const itemOffsetMiddle\n = selectedItem.value.offsetTop + selectedItemHalfHeight\n const contentTopToItemMiddle\n = contentBorderTopWidth + contentPaddingTop + itemOffsetMiddle\n const itemMiddleToContentBottom\n = fullContentHeight - contentTopToItemMiddle\n\n const willAlignWithoutTopOverflow\n = contentTopToItemMiddle <= topEdgeToTriggerMiddle\n\n if (willAlignWithoutTopOverflow) {\n const isLastItem = selectedItem.value === items[items.length - 1]\n contentWrapperElement.value.style.bottom = `${0}px`\n const viewportOffsetBottom\n = contentElement.value.clientHeight\n - viewport.value.offsetTop\n - viewport.value.offsetHeight\n const clampedTriggerMiddleToBottomEdge = Math.max(\n triggerMiddleToBottomEdge,\n selectedItemHalfHeight\n // viewport might have padding bottom, include it to avoid a scrollable viewport\n + (isLastItem ? viewportPaddingBottom : 0)\n + viewportOffsetBottom\n + contentBorderBottomWidth,\n )\n const height = contentTopToItemMiddle + clampedTriggerMiddleToBottomEdge\n contentWrapperElement.value.style.height = `${height}px`\n }\n else {\n const isFirstItem = selectedItem.value === items[0]\n contentWrapperElement.value.style.top = `${0}px`\n const clampedTopEdgeToTriggerMiddle = Math.max(\n topEdgeToTriggerMiddle,\n contentBorderTopWidth\n + viewport.value.offsetTop\n // viewport might have padding top, include it to avoid a scrollable viewport\n + (isFirstItem ? viewportPaddingTop : 0)\n + selectedItemHalfHeight,\n )\n const height = clampedTopEdgeToTriggerMiddle + itemMiddleToContentBottom\n contentWrapperElement.value.style.height = `${height}px`\n viewport.value.scrollTop\n = contentTopToItemMiddle\n - topEdgeToTriggerMiddle\n + viewport.value.offsetTop\n }\n\n contentWrapperElement.value.style.margin = `${CONTENT_MARGIN}px 0`\n contentWrapperElement.value.style.minHeight = `${minContentHeight}px`\n contentWrapperElement.value.style.maxHeight = `${availableHeight}px`\n // -----------------------------------------------------------------------------------------\n\n emits('placed')\n\n // we don't want the initial scroll position adjustment to trigger \"expand on scroll\"\n // so we explicitly turn it on only after they've registered.\n requestAnimationFrame(() => (shouldExpandOnScrollRef.value = true))\n }\n}\n\n// copy z-index from content to wrapper\nconst contentZIndex = ref('')\n\nonMounted(async () => {\n await nextTick()\n position()\n if (contentElement.value)\n contentZIndex.value = window.getComputedStyle(contentElement.value).zIndex\n})\n\n// When the viewport becomes scrollable at the top, the scroll up button will mount.\n// Because it is part of the normal flow, it will push down the viewport, thus throwing our\n// trigger => selectedItem alignment off by the amount the viewport was pushed down.\n// We wait for this to happen and then re-run the positining logic one more time to account for it.\nfunction handleScrollButtonChange(node: HTMLElement | undefined) {\n if (node && shouldRepositionRef.value === true) {\n position()\n focusSelectedItem?.()\n shouldRepositionRef.value = false\n }\n}\n\n// Resize and position when trigger element changes\nuseResizeObserver(rootContext.triggerElement, () => {\n position()\n})\n\nprovideSelectItemAlignedPositionContext({\n contentWrapper: contentWrapperElement,\n shouldExpandOnScrollRef,\n onScrollButtonChange: handleScrollButtonChange,\n})\n</script>\n\n<template>\n <div\n ref=\"contentWrapperElement\"\n :style=\"{\n display: 'flex',\n flexDirection: 'column',\n position: 'fixed',\n zIndex: contentZIndex,\n }\"\n >\n <Primitive\n :ref=\"forwardRef\"\n :style=\"{\n // When we get the height of the content, it includes borders. If we were to set\n // the height without having `boxSizing: 'border-box'` it would be too big.\n boxSizing: 'border-box',\n // We need to ensure the content doesn't get taller than the wrapper\n maxHeight: '100%',\n }\"\n v-bind=\"{ ...$attrs, ...props }\"\n >\n <slot />\n </Primitive>\n </div>\n</template>\n"],"names":["createContext","useCollection","injectSelectRootContext","injectSelectContentContext","ref","useForwardExpose","CONTENT_MARGIN","clamp","onMounted","nextTick","useResizeObserver"],"mappings":"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"}