@grafana/ui
Version:
Grafana Components Library
1 lines • 9.59 kB
Source Map (JSON)
{"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\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 }, 200),\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":"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"}