UNPKG

@grafana/ui

Version:
1 lines 9.79 kB
{"version":3,"file":"useOptions.mjs","sources":["../../../../src/components/Combobox/useOptions.ts"],"sourcesContent":["/* Spreading unbound arrays can be very slow or even crash the browser if used for arguments */\n/* eslint no-restricted-syntax: [\"error\", \"SpreadElement\"] */\n\nimport { debounce } from 'lodash';\nimport { useState, useCallback, useMemo } from 'react';\n\nimport { t } from '@grafana/i18n';\n\nimport { fuzzyFind, itemToString } from './filter';\nimport { ComboboxOption } from './types';\nimport { StaleResultError, useLatestAsyncCall } from './useLatestAsyncCall';\n\ntype AsyncOptions<T extends string | number> =\n | Array<ComboboxOption<T>>\n | ((inputValue: string) => Promise<Array<ComboboxOption<T>>>);\n\nconst asyncNoop = () => Promise.resolve([]);\n\nexport const DEBOUNCE_TIME_MS = 200;\n\n/**\n * Abstracts away sync/async options for combobox components.\n * It also filters options based on the user's input.\n *\n * Returns:\n * - options either filtered by user's input, or from async options fn\n * - function to call when user types (to filter, or call async fn)\n * - loading and error states\n */\nexport function useOptions<T extends string | number>(rawOptions: AsyncOptions<T>, createCustomValue: boolean) {\n const isAsync = typeof rawOptions === 'function';\n\n const loadOptions = useLatestAsyncCall(isAsync ? rawOptions : asyncNoop);\n\n const debouncedLoadOptions = useMemo(\n () =>\n debounce((searchTerm: string) => {\n return loadOptions(searchTerm)\n .then((options) => {\n setAsyncOptions(options);\n setAsyncLoading(false);\n setAsyncError(false);\n })\n .catch((error) => {\n if (!(error instanceof StaleResultError)) {\n setAsyncError(true);\n setAsyncLoading(false);\n\n if (error) {\n console.error('Error loading async options for Combobox', error);\n }\n }\n });\n }, DEBOUNCE_TIME_MS),\n [loadOptions]\n );\n\n const [asyncOptions, setAsyncOptions] = useState<Array<ComboboxOption<T>>>([]);\n const [asyncLoading, setAsyncLoading] = useState(false);\n const [asyncError, setAsyncError] = useState(false);\n\n // This hook keeps its own inputValue state (rather than accepting it as an arg) because it needs to be\n // told it for async options loading anyway.\n const [userTypedSearch, setUserTypedSearch] = useState('');\n\n const addCustomValue = useCallback(\n (opts: Array<ComboboxOption<T>>) => {\n let currentOptions: Array<ComboboxOption<T>> = opts;\n if (createCustomValue && userTypedSearch) {\n // Since the label of a normal option does not have to match its value and a custom option has the same value and label,\n // we just focus on the value to check if the option already exists\n const customValueExists = opts.some((opt) => opt.value === userTypedSearch);\n if (!customValueExists) {\n // Make sure to clone the array first to avoid mutating the original array!\n currentOptions = currentOptions.slice();\n currentOptions.unshift({\n label: userTypedSearch,\n value: userTypedSearch as T,\n description: t('combobox.custom-value.description', 'Use custom value'),\n });\n }\n }\n return currentOptions;\n },\n [createCustomValue, userTypedSearch]\n );\n\n const updateOptions = useCallback(\n (inputValue: string) => {\n setUserTypedSearch(inputValue);\n if (isAsync) {\n setAsyncLoading(true);\n debouncedLoadOptions(inputValue);\n }\n },\n [debouncedLoadOptions, isAsync]\n );\n\n const stringifiedOptions = useMemo(() => {\n return isAsync ? [] : rawOptions.map(itemToString);\n }, [isAsync, rawOptions]);\n\n // Create a list of options filtered by the current search.\n // If async, just returns the async options.\n const filteredOptions = useMemo(() => {\n if (isAsync) {\n return asyncOptions;\n }\n\n return fuzzyFind(rawOptions, stringifiedOptions, userTypedSearch);\n }, [asyncOptions, isAsync, rawOptions, stringifiedOptions, userTypedSearch]);\n\n const [finalOptions, groupStartIndices] = useMemo(() => {\n const { options, groupStartIndices } = sortByGroup(filteredOptions);\n\n return [addCustomValue(options), groupStartIndices];\n }, [filteredOptions, addCustomValue]);\n\n return { options: finalOptions, groupStartIndices, updateOptions, asyncLoading, asyncError };\n}\n\n/**\n * Sorts options by group and returns the sorted options and the starting index of each group\n */\nexport function sortByGroup<T extends string | number>(options: Array<ComboboxOption<T>>) {\n // Group options by their group\n const groupedOptions = new Map<string | undefined, Array<ComboboxOption<T>>>();\n const groupStartIndices = new Map<string | undefined, number>();\n\n for (const option of options) {\n const group = option.group;\n const existing = groupedOptions.get(group);\n if (existing) {\n existing.push(option);\n } else {\n groupedOptions.set(group, [option]);\n }\n }\n\n // If we only have one group (either the undefined group, or a single group), return the original array\n if (groupedOptions.size <= 1) {\n if (options[0]?.group) {\n groupStartIndices.set(options[0]?.group, 0);\n }\n\n return {\n options,\n groupStartIndices,\n };\n }\n\n // 'Preallocate' result array with same size as input - very minor optimization\n const result: Array<ComboboxOption<T>> = new Array(options.length);\n\n let currentIndex = 0;\n\n // Fill result array with grouped and undefined grouped options\n for (const [group, groupOptions] of groupedOptions) {\n if (group) {\n groupStartIndices.set(group, currentIndex);\n }\n for (const option of groupOptions) {\n result[currentIndex++] = option;\n }\n }\n\n return {\n options: result,\n groupStartIndices,\n };\n}\n"],"names":["groupStartIndices"],"mappings":";;;;;;;AAgBA,MAAM,SAAA,GAAY,MAAM,OAAA,CAAQ,OAAA,CAAQ,EAAE,CAAA;AAEnC,MAAM,gBAAA,GAAmB;AAWzB,SAAS,UAAA,CAAsC,YAA6B,iBAAA,EAA4B;AAC7G,EAAA,MAAM,OAAA,GAAU,OAAO,UAAA,KAAe,UAAA;AAEtC,EAAA,MAAM,WAAA,GAAc,kBAAA,CAAmB,OAAA,GAAU,UAAA,GAAa,SAAS,CAAA;AAEvE,EAAA,MAAM,oBAAA,GAAuB,OAAA;AAAA,IAC3B,MACE,QAAA,CAAS,CAAC,UAAA,KAAuB;AAC/B,MAAA,OAAO,WAAA,CAAY,UAAU,CAAA,CAC1B,IAAA,CAAK,CAAC,OAAA,KAAY;AACjB,QAAA,eAAA,CAAgB,OAAO,CAAA;AACvB,QAAA,eAAA,CAAgB,KAAK,CAAA;AACrB,QAAA,aAAA,CAAc,KAAK,CAAA;AAAA,MACrB,CAAC,CAAA,CACA,KAAA,CAAM,CAAC,KAAA,KAAU;AAChB,QAAA,IAAI,EAAE,iBAAiB,gBAAA,CAAA,EAAmB;AACxC,UAAA,aAAA,CAAc,IAAI,CAAA;AAClB,UAAA,eAAA,CAAgB,KAAK,CAAA;AAErB,UAAA,IAAI,KAAA,EAAO;AACT,YAAA,OAAA,CAAQ,KAAA,CAAM,4CAA4C,KAAK,CAAA;AAAA,UACjE;AAAA,QACF;AAAA,MACF,CAAC,CAAA;AAAA,IACL,GAAG,gBAAgB,CAAA;AAAA,IACrB,CAAC,WAAW;AAAA,GACd;AAEA,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,CAAA,GAAI,QAAA,CAAmC,EAAE,CAAA;AAC7E,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,CAAA,GAAI,SAAS,KAAK,CAAA;AACtD,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAI,SAAS,KAAK,CAAA;AAIlD,EAAA,MAAM,CAAC,eAAA,EAAiB,kBAAkB,CAAA,GAAI,SAAS,EAAE,CAAA;AAEzD,EAAA,MAAM,cAAA,GAAiB,WAAA;AAAA,IACrB,CAAC,IAAA,KAAmC;AAClC,MAAA,IAAI,cAAA,GAA2C,IAAA;AAC/C,MAAA,IAAI,qBAAqB,eAAA,EAAiB;AAGxC,QAAA,MAAM,oBAAoB,IAAA,CAAK,IAAA,CAAK,CAAC,GAAA,KAAQ,GAAA,CAAI,UAAU,eAAe,CAAA;AAC1E,QAAA,IAAI,CAAC,iBAAA,EAAmB;AAEtB,UAAA,cAAA,GAAiB,eAAe,KAAA,EAAM;AACtC,UAAA,cAAA,CAAe,OAAA,CAAQ;AAAA,YACrB,KAAA,EAAO,eAAA;AAAA,YACP,KAAA,EAAO,eAAA;AAAA,YACP,WAAA,EAAa,CAAA,CAAE,mCAAA,EAAqC,kBAAkB;AAAA,WACvE,CAAA;AAAA,QACH;AAAA,MACF;AACA,MAAA,OAAO,cAAA;AAAA,IACT,CAAA;AAAA,IACA,CAAC,mBAAmB,eAAe;AAAA,GACrC;AAEA,EAAA,MAAM,aAAA,GAAgB,WAAA;AAAA,IACpB,CAAC,UAAA,KAAuB;AACtB,MAAA,kBAAA,CAAmB,UAAU,CAAA;AAC7B,MAAA,IAAI,OAAA,EAAS;AACX,QAAA,eAAA,CAAgB,IAAI,CAAA;AACpB,QAAA,oBAAA,CAAqB,UAAU,CAAA;AAAA,MACjC;AAAA,IACF,CAAA;AAAA,IACA,CAAC,sBAAsB,OAAO;AAAA,GAChC;AAEA,EAAA,MAAM,kBAAA,GAAqB,QAAQ,MAAM;AACvC,IAAA,OAAO,OAAA,GAAU,EAAC,GAAI,UAAA,CAAW,IAAI,YAAY,CAAA;AAAA,EACnD,CAAA,EAAG,CAAC,OAAA,EAAS,UAAU,CAAC,CAAA;AAIxB,EAAA,MAAM,eAAA,GAAkB,QAAQ,MAAM;AACpC,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,OAAO,YAAA;AAAA,IACT;AAEA,IAAA,OAAO,SAAA,CAAU,UAAA,EAAY,kBAAA,EAAoB,eAAe,CAAA;AAAA,EAClE,GAAG,CAAC,YAAA,EAAc,SAAS,UAAA,EAAY,kBAAA,EAAoB,eAAe,CAAC,CAAA;AAE3E,EAAA,MAAM,CAAC,YAAA,EAAc,iBAAiB,CAAA,GAAI,QAAQ,MAAM;AACtD,IAAA,MAAM,EAAE,OAAA,EAAS,iBAAA,EAAAA,kBAAAA,EAAkB,GAAI,YAAY,eAAe,CAAA;AAElE,IAAA,OAAO,CAAC,cAAA,CAAe,OAAO,CAAA,EAAGA,kBAAiB,CAAA;AAAA,EACpD,CAAA,EAAG,CAAC,eAAA,EAAiB,cAAc,CAAC,CAAA;AAEpC,EAAA,OAAO,EAAE,OAAA,EAAS,YAAA,EAAc,iBAAA,EAAmB,aAAA,EAAe,cAAc,UAAA,EAAW;AAC7F;AAKO,SAAS,YAAuC,OAAA,EAAmC;AA5H1F,EAAA,IAAA,EAAA,EAAA,EAAA;AA8HE,EAAA,MAAM,cAAA,uBAAqB,GAAA,EAAkD;AAC7E,EAAA,MAAM,iBAAA,uBAAwB,GAAA,EAAgC;AAE9D,EAAA,KAAA,MAAW,UAAU,OAAA,EAAS;AAC5B,IAAA,MAAM,QAAQ,MAAA,CAAO,KAAA;AACrB,IAAA,MAAM,QAAA,GAAW,cAAA,CAAe,GAAA,CAAI,KAAK,CAAA;AACzC,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,QAAA,CAAS,KAAK,MAAM,CAAA;AAAA,IACtB,CAAA,MAAO;AACL,MAAA,cAAA,CAAe,GAAA,CAAI,KAAA,EAAO,CAAC,MAAM,CAAC,CAAA;AAAA,IACpC;AAAA,EACF;AAGA,EAAA,IAAI,cAAA,CAAe,QAAQ,CAAA,EAAG;AAC5B,IAAA,IAAA,CAAI,EAAA,GAAA,OAAA,CAAQ,CAAC,CAAA,KAAT,IAAA,GAAA,KAAA,CAAA,GAAA,EAAA,CAAY,KAAA,EAAO;AACrB,MAAA,iBAAA,CAAkB,KAAI,EAAA,GAAA,OAAA,CAAQ,CAAC,CAAA,KAAT,IAAA,GAAA,KAAA,CAAA,GAAA,EAAA,CAAY,OAAO,CAAC,CAAA;AAAA,IAC5C;AAEA,IAAA,OAAO;AAAA,MACL,OAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF;AAGA,EAAA,MAAM,MAAA,GAAmC,IAAI,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA;AAEjE,EAAA,IAAI,YAAA,GAAe,CAAA;AAGnB,EAAA,KAAA,MAAW,CAAC,KAAA,EAAO,YAAY,CAAA,IAAK,cAAA,EAAgB;AAClD,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,iBAAA,CAAkB,GAAA,CAAI,OAAO,YAAY,CAAA;AAAA,IAC3C;AACA,IAAA,KAAA,MAAW,UAAU,YAAA,EAAc;AACjC,MAAA,MAAA,CAAO,cAAc,CAAA,GAAI,MAAA;AAAA,IAC3B;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,OAAA,EAAS,MAAA;AAAA,IACT;AAAA,GACF;AACF;;;;"}