reka-ui
Version:
Vue port for Radix UI Primitives.
1 lines • 15.1 kB
Source Map (JSON)
{"version":3,"file":"ListboxVirtualizer.cjs","sources":["../../src/Listbox/ListboxVirtualizer.vue"],"sourcesContent":["<script lang=\"ts\">\nexport interface ListboxVirtualizerProps<T extends AcceptableValue = AcceptableValue> {\n /** List of items */\n options: T[]\n /** Number of items rendered outside the visible area */\n overscan?: number\n /** Estimated size (in px) of each item */\n estimateSize?: number\n /** Text content for each item to achieve type-ahead feature */\n textContent?: (option: T) => string\n}\n</script>\n\n<script setup lang=\"ts\" generic=\"T extends AcceptableValue = AcceptableValue\">\nimport { type VirtualItem, type Virtualizer, useVirtualizer } from '@tanstack/vue-virtual'\nimport { Fragment, type Ref, type VNode, cloneVNode, computed, useSlots } from 'vue'\nimport { injectListboxRootContext } from './ListboxRoot.vue'\nimport { compare, queryCheckedElement } from './utils'\nimport { MAP_KEY_TO_FOCUS_INTENT } from '@/RovingFocus/utils'\nimport { refAutoReset } from '@vueuse/shared'\nimport { findValuesBetween, getActiveElement } from '@/shared'\nimport { getNextMatch } from '@/shared/useTypeahead'\nimport { useParentElement } from '@vueuse/core'\nimport { useCollection } from '@/Collection'\nimport type { AcceptableValue } from '@/shared/types'\n\nconst props = defineProps<ListboxVirtualizerProps<T>>()\n\ndefineSlots<{\n default: (props: {\n option: T\n virtualizer: Virtualizer<HTMLElement, Element>\n virtualItem: VirtualItem\n }) => any\n}>()\n\nconst slots = useSlots()\nconst rootContext = injectListboxRootContext()\nconst parentEl = useParentElement() as Ref<HTMLElement>\nconst { getItems } = useCollection<{ value: T }>()\n\n// set virtual true when this component mounted\nrootContext.isVirtual.value = true\n\nconst padding = computed(() => {\n const el = parentEl.value\n if (!el) {\n return { start: 0, end: 0 }\n }\n else {\n const styles = window.getComputedStyle(el)\n return {\n start: Number.parseFloat(styles.paddingBlockStart || styles.paddingTop),\n end: Number.parseFloat(styles.paddingBlockEnd || styles.paddingBottom),\n }\n }\n})\n\nconst virtualizer = useVirtualizer(\n {\n get scrollPaddingStart() { return padding.value.start },\n get scrollPaddingEnd() { return padding.value.end },\n get count() { return props.options.length },\n get horizontal() { return rootContext.orientation.value === 'horizontal' },\n estimateSize() {\n return props.estimateSize ?? 28\n },\n getScrollElement() { return parentEl.value },\n overscan: props.overscan ?? 12,\n },\n)\n\nconst virtualizedItems = computed(() => virtualizer.value.getVirtualItems().map((item) => {\n const defaultNode = slots.default!({\n option: props.options[item.index],\n virtualizer: virtualizer.value,\n virtualItem: item,\n })[0]\n\n const targetNode = defaultNode.type === Fragment && Array.isArray(defaultNode.children)\n ? defaultNode.children[0] as VNode\n : defaultNode\n\n return {\n item,\n is: cloneVNode(targetNode, {\n 'key': `${item.key}`,\n 'data-index': item.index,\n 'aria-setsize': props.options.length,\n 'aria-posinset': item.index + 1,\n 'style': {\n position: 'absolute',\n top: 0,\n left: 0,\n transform: `translateY(${item.start}px)`,\n overflowAnchor: 'none',\n },\n }),\n }\n}))\n\nrootContext.virtualFocusHook.on((event) => {\n const index = props.options.findIndex((option) => {\n if (Array.isArray(rootContext.modelValue.value))\n return compare(option, rootContext.modelValue.value[0], rootContext.by)\n else\n return compare(option, rootContext.modelValue.value!, rootContext.by)\n })\n if (index !== -1) {\n event?.preventDefault()\n\n virtualizer.value.scrollToIndex(index, { align: 'start' })\n requestAnimationFrame(() => {\n const item = queryCheckedElement(parentEl.value)\n if (item) {\n rootContext.changeHighlight(item)\n if (event)\n item?.focus()\n }\n })\n }\n else {\n rootContext.highlightFirstItem(event as InputEvent)\n }\n})\n\nrootContext.virtualHighlightHook.on((value) => {\n const index = props.options.findIndex((option) => {\n return compare(option, value, rootContext.by)\n })\n virtualizer.value.scrollToIndex(index, { align: 'start' })\n requestAnimationFrame(() => {\n const item = queryCheckedElement(parentEl.value)\n if (item)\n rootContext.changeHighlight(item)\n })\n})\n\n// Reset `search` 1 second after it was last updated\nconst search = refAutoReset('', 1000)\nconst optionsWithMetadata = computed(() => {\n const parseTextContent = (option: T) => {\n if (props.textContent)\n return props.textContent(option)\n else\n return option?.toString().toLowerCase()\n }\n\n return props.options.map((option, index) => ({\n index,\n textContent: parseTextContent(option),\n }))\n})\n\nfunction handleMultipleReplace(event: Event, intent: 'first' | 'last' | 'prev' | 'next') {\n if (!rootContext.firstValue?.value || !rootContext.multiple.value || !Array.isArray(rootContext.modelValue.value))\n return\n\n const collection = getItems().filter(i => i.ref.dataset.disabled !== '')\n const lastValue = collection.find(i => i.ref === rootContext.highlightedElement.value)?.value\n if (!lastValue)\n return\n\n let value: T[] | null = null\n switch (intent) {\n case 'prev':\n case 'next': {\n value = findValuesBetween(props.options, rootContext.firstValue.value as T, lastValue)\n break\n }\n case 'first': {\n value = findValuesBetween(props.options, rootContext.firstValue.value as T, props.options?.[0])\n break\n }\n case 'last': {\n value = findValuesBetween(props.options, rootContext.firstValue.value as T, props.options?.[props.options.length - 1])\n break\n }\n }\n rootContext.modelValue.value = value\n}\n\nrootContext.virtualKeydownHook.on((event) => {\n const isMetaKey = event.altKey || event.ctrlKey || event.metaKey\n const isTabKey = event.key === 'Tab' && !isMetaKey\n if (isTabKey)\n return\n\n let intent = MAP_KEY_TO_FOCUS_INTENT[event.key]\n\n // Meta + A, select all feature\n if (isMetaKey && event.key === 'a' && rootContext.multiple.value) {\n event.preventDefault()\n rootContext.modelValue.value = [...props.options]\n // purposely make the focus to last\n intent = 'last'\n }\n else if (event.shiftKey && intent) {\n handleMultipleReplace(event, intent)\n }\n\n if (['first', 'last'].includes(intent)) {\n event.preventDefault()\n\n const index = intent === 'first' ? 0 : props.options.length - 1\n virtualizer.value.scrollToIndex(index)\n requestAnimationFrame(() => {\n const items = getItems()\n const item = intent === 'first' ? items[0] : items[items.length - 1]\n if (item)\n rootContext.changeHighlight(item.ref)\n })\n }\n else if (!intent && !isMetaKey) {\n search.value += event.key\n const currentIndex = Number(getActiveElement()?.getAttribute('data-index'))\n const currentMatch = optionsWithMetadata.value[currentIndex].textContent\n const filteredOptions = optionsWithMetadata.value.map(i => i.textContent ?? '')\n const next = getNextMatch(filteredOptions, search.value, currentMatch)\n\n const nextMatch = optionsWithMetadata.value.find(option => option.textContent === next)\n if (nextMatch) {\n virtualizer.value.scrollToIndex(nextMatch.index, { align: 'start' })\n requestAnimationFrame(() => {\n const item = parentEl.value.querySelector(`[data-index=\"${nextMatch.index}\"]`)\n if (item instanceof HTMLElement)\n rootContext.changeHighlight(item)\n })\n }\n }\n})\n</script>\n\n<template>\n <div\n data-reka-virtualizer\n :style=\"{\n position: 'relative',\n width: '100%',\n height: `${virtualizer.getTotalSize()}px`,\n }\"\n >\n <component\n :is=\"is\"\n v-for=\"{ is, item } in virtualizedItems\"\n :key=\"item.index\"\n />\n </div>\n</template>\n"],"names":["useSlots","injectListboxRootContext","useParentElement","useCollection","computed","useVirtualizer","Fragment","cloneVNode","compare","queryCheckedElement","refAutoReset","findValuesBetween","MAP_KEY_TO_FOCUS_INTENT","getActiveElement","getNextMatch"],"mappings":"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"}