reka-ui
Version:
Vue port for Radix UI Primitives.
1 lines • 5.29 kB
Source Map (JSON)
{"version":3,"file":"useTypeahead.cjs","sources":["../../src/shared/useTypeahead.ts"],"sourcesContent":["import { refAutoReset } from '@vueuse/shared'\nimport { getActiveElement } from './getActiveElement'\n\nexport function useTypeahead(callback?: (search: string) => void) {\n // Reset `search` 1 second after it was last updated\n const search = refAutoReset('', 1000)\n\n const handleTypeaheadSearch = (key: string, items: { ref: HTMLElement, value?: any }[]) => {\n search.value = search.value + key\n\n if (callback) {\n callback(key)\n }\n else {\n const currentItem = getActiveElement()\n const itemsWithTextValue = items.map(item => ({\n ...item,\n textValue: item.value?.textValue ?? item.ref.textContent?.trim() ?? '',\n }))\n const currentMatch = itemsWithTextValue.find(item => item.ref === currentItem)\n const values = itemsWithTextValue.map(item => item.textValue)\n const nextMatch = getNextMatch(values, search.value, currentMatch?.textValue)\n\n const newItem = itemsWithTextValue.find(item => item.textValue === nextMatch)\n\n if (newItem)\n (newItem.ref as HTMLElement).focus()\n return newItem?.ref\n }\n }\n\n const resetTypeahead = () => {\n search.value = ''\n }\n\n return {\n search,\n handleTypeaheadSearch,\n resetTypeahead,\n }\n}\n\n/**\n * Wraps an array around itself at a given start index\n * Example: `wrapArray(['a', 'b', 'c', 'd'], 2) === ['c', 'd', 'a', 'b']`\n */\nexport function wrapArray<T>(array: T[], startIndex: number) {\n return array.map((_, index) => array[(startIndex + index) % array.length])\n}\n\n/**\n * This is the \"meat\" of the typeahead matching logic. It takes in all the values,\n * the search and the current match, and returns the next match (or `undefined`).\n *\n * We normalize the search because if a user has repeatedly pressed a character,\n * we want the exact same behavior as if we only had that one character\n * (ie. cycle through options starting with that character)\n *\n * We also reorder the values by wrapping the array around the current match.\n * This is so we always look forward from the current match, and picking the first\n * match will always be the correct one.\n *\n * Finally, if the normalized search is exactly one character, we exclude the\n * current match from the values because otherwise it would be the first to match always\n * and focus would never move. This is as opposed to the regular case, where we\n * don't want focus to move if the current match still matches.\n */\nexport function getNextMatch(\n values: string[],\n search: string,\n currentMatch?: string,\n) {\n const isRepeated\n = search.length > 1 && Array.from(search).every(char => char === search[0])\n const normalizedSearch = isRepeated ? search[0] : search\n const currentMatchIndex = currentMatch ? values.indexOf(currentMatch) : -1\n let wrappedValues = wrapArray(values, Math.max(currentMatchIndex, 0))\n const excludeCurrentMatch = normalizedSearch.length === 1\n if (excludeCurrentMatch)\n wrappedValues = wrappedValues.filter(v => v !== currentMatch)\n const nextMatch = wrappedValues.find(value =>\n value.toLowerCase().startsWith(normalizedSearch.toLowerCase()),\n )\n return nextMatch !== currentMatch ? nextMatch : undefined\n}\n"],"names":["refAutoReset","getActiveElement"],"mappings":";;;;;AAGO,SAAS,aAAa,QAAqC,EAAA;AAEhE,EAAM,MAAA,MAAA,GAASA,mBAAa,CAAA,EAAA,EAAI,GAAI,CAAA;AAEpC,EAAM,MAAA,qBAAA,GAAwB,CAAC,GAAA,EAAa,KAA+C,KAAA;AACzF,IAAO,MAAA,CAAA,KAAA,GAAQ,OAAO,KAAQ,GAAA,GAAA;AAE9B,IAGK;AACH,MAAA,MAAM,cAAcC,wCAAiB,EAAA;AACrC,MAAM,MAAA,kBAAA,GAAqB,KAAM,CAAA,GAAA,CAAI,CAAS,IAAA,MAAA;AAAA,QAC5C,GAAG,IAAA;AAAA,QACH,SAAA,EAAW,KAAK,KAAO,EAAA,SAAA,IAAa,KAAK,GAAI,CAAA,WAAA,EAAa,MAAU,IAAA;AAAA,OACpE,CAAA,CAAA;AACF,MAAA,MAAM,eAAe,kBAAmB,CAAA,IAAA,CAAK,CAAQ,IAAA,KAAA,IAAA,CAAK,QAAQ,WAAW,CAAA;AAC7E,MAAA,MAAM,MAAS,GAAA,kBAAA,CAAmB,GAAI,CAAA,CAAA,IAAA,KAAQ,KAAK,SAAS,CAAA;AAC5D,MAAA,MAAM,YAAY,YAAa,CAAA,MAAA,EAAQ,MAAO,CAAA,KAAA,EAAO,cAAc,SAAS,CAAA;AAE5E,MAAA,MAAM,UAAU,kBAAmB,CAAA,IAAA,CAAK,CAAQ,IAAA,KAAA,IAAA,CAAK,cAAc,SAAS,CAAA;AAE5E,MAAI,IAAA,OAAA;AACF,QAAC,OAAA,CAAQ,IAAoB,KAAM,EAAA;AACrC,MAAA,OAAO,OAAS,EAAA,GAAA;AAAA;AAClB,GACF;AAEA,EAAA,MAAM,iBAAiB,MAAM;AAC3B,IAAA,MAAA,CAAO,KAAQ,GAAA,EAAA;AAAA,GACjB;AAEA,EAAO,OAAA;AAAA,IACL,MAAA;AAAA,IACA,qBAAA;AAAA,IACA;AAAA,GACF;AACF;AAMgB,SAAA,SAAA,CAAa,OAAY,UAAoB,EAAA;AAC3D,EAAO,OAAA,KAAA,CAAM,GAAI,CAAA,CAAC,CAAG,EAAA,KAAA,KAAU,OAAO,UAAa,GAAA,KAAA,IAAS,KAAM,CAAA,MAAM,CAAC,CAAA;AAC3E;AAmBgB,SAAA,YAAA,CACd,MACA,EAAA,MAAA,EACA,YACA,EAAA;AACA,EAAA,MAAM,UACF,GAAA,MAAA,CAAO,MAAS,GAAA,CAAA,IAAK,KAAM,CAAA,IAAA,CAAK,MAAM,CAAA,CAAE,KAAM,CAAA,CAAA,IAAA,KAAQ,IAAS,KAAA,MAAA,CAAO,CAAC,CAAC,CAAA;AAC5E,EAAA,MAAM,gBAAmB,GAAA,UAAA,GAAa,MAAO,CAAA,CAAC,CAAI,GAAA,MAAA;AAClD,EAAA,MAAM,iBAAoB,GAAA,YAAA,GAAe,MAAO,CAAA,OAAA,CAAQ,YAAY,CAAI,GAAA,EAAA;AACxE,EAAA,IAAI,gBAAgB,SAAU,CAAA,MAAA,EAAQ,KAAK,GAAI,CAAA,iBAAA,EAAmB,CAAC,CAAC,CAAA;AACpE,EAAM,MAAA,mBAAA,GAAsB,iBAAiB,MAAW,KAAA,CAAA;AACxD,EAAI,IAAA,mBAAA;AACF,IAAA,aAAA,GAAgB,aAAc,CAAA,MAAA,CAAO,CAAK,CAAA,KAAA,CAAA,KAAM,YAAY,CAAA;AAC9D,EAAA,MAAM,YAAY,aAAc,CAAA,IAAA;AAAA,IAAK,WACnC,KAAM,CAAA,WAAA,GAAc,UAAW,CAAA,gBAAA,CAAiB,aAAa;AAAA,GAC/D;AACA,EAAO,OAAA,SAAA,KAAc,eAAe,SAAY,GAAA,MAAA;AAClD;;;;;;"}