@grafana/ui
Version:
Grafana Components Library
118 lines (115 loc) • 3.97 kB
JavaScript
import { debounce } from 'lodash';
import { useMemo, useState, useCallback } from 'react';
import { t } from '@grafana/i18n';
import { itemToString, fuzzyFind } from './filter.mjs';
import { useLatestAsyncCall, StaleResultError } from './useLatestAsyncCall.mjs';
;
const asyncNoop = () => Promise.resolve([]);
const DEBOUNCE_TIME_MS = 200;
function useOptions(rawOptions, createCustomValue) {
const isAsync = typeof rawOptions === "function";
const loadOptions = useLatestAsyncCall(isAsync ? rawOptions : asyncNoop);
const debouncedLoadOptions = useMemo(
() => debounce((searchTerm) => {
return loadOptions(searchTerm).then((options) => {
setAsyncOptions(options);
setAsyncLoading(false);
setAsyncError(false);
}).catch((error) => {
if (!(error instanceof StaleResultError)) {
setAsyncError(true);
setAsyncLoading(false);
if (error) {
console.error("Error loading async options for Combobox", error);
}
}
});
}, DEBOUNCE_TIME_MS),
[loadOptions]
);
const [asyncOptions, setAsyncOptions] = useState([]);
const [asyncLoading, setAsyncLoading] = useState(false);
const [asyncError, setAsyncError] = useState(false);
const [userTypedSearch, setUserTypedSearch] = useState("");
const addCustomValue = useCallback(
(opts) => {
let currentOptions = opts;
if (createCustomValue && userTypedSearch) {
const customValueExists = opts.some((opt) => opt.value === userTypedSearch);
if (!customValueExists) {
currentOptions = currentOptions.slice();
currentOptions.unshift({
label: userTypedSearch,
value: userTypedSearch,
description: t("combobox.custom-value.description", "Use custom value")
});
}
}
return currentOptions;
},
[createCustomValue, userTypedSearch]
);
const updateOptions = useCallback(
(inputValue) => {
setUserTypedSearch(inputValue);
if (isAsync) {
setAsyncLoading(true);
debouncedLoadOptions(inputValue);
}
},
[debouncedLoadOptions, isAsync]
);
const stringifiedOptions = useMemo(() => {
return isAsync ? [] : rawOptions.map(itemToString);
}, [isAsync, rawOptions]);
const filteredOptions = useMemo(() => {
if (isAsync) {
return asyncOptions;
}
return fuzzyFind(rawOptions, stringifiedOptions, userTypedSearch);
}, [asyncOptions, isAsync, rawOptions, stringifiedOptions, userTypedSearch]);
const [finalOptions, groupStartIndices] = useMemo(() => {
const { options, groupStartIndices: groupStartIndices2 } = sortByGroup(filteredOptions);
return [addCustomValue(options), groupStartIndices2];
}, [filteredOptions, addCustomValue]);
return { options: finalOptions, groupStartIndices, updateOptions, asyncLoading, asyncError };
}
function sortByGroup(options) {
var _a, _b;
const groupedOptions = /* @__PURE__ */ new Map();
const groupStartIndices = /* @__PURE__ */ new Map();
for (const option of options) {
const group = option.group;
const existing = groupedOptions.get(group);
if (existing) {
existing.push(option);
} else {
groupedOptions.set(group, [option]);
}
}
if (groupedOptions.size <= 1) {
if ((_a = options[0]) == null ? void 0 : _a.group) {
groupStartIndices.set((_b = options[0]) == null ? void 0 : _b.group, 0);
}
return {
options,
groupStartIndices
};
}
const result = new Array(options.length);
let currentIndex = 0;
for (const [group, groupOptions] of groupedOptions) {
if (group) {
groupStartIndices.set(group, currentIndex);
}
for (const option of groupOptions) {
result[currentIndex++] = option;
}
}
return {
options: result,
groupStartIndices
};
}
export { DEBOUNCE_TIME_MS, sortByGroup, useOptions };
//# sourceMappingURL=useOptions.mjs.map