@finos/legend-application-marketplace
Version:
Legend Marketplace application core
252 lines • 17.3 kB
JavaScript
import { createElement as _createElement } from "react";
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
/**
* Copyright (c) 2020-present, Goldman Sachs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useState, useMemo, useCallback, useEffect } from 'react';
import { Autocomplete, Box, CircularProgress, FormControlLabel, IconButton, InputAdornment, Menu, MenuItem, Switch, TextField, Typography, } from '@mui/material';
import { clsx, SearchIcon, TuneIcon } from '@finos/legend-art';
import { observer } from 'mobx-react-lite';
import { LegendMarketplaceInfoTooltip } from '../InfoTooltip/LegendMarketplaceInfoTooltip.js';
import { LegendMarketplaceTelemetryHelper } from '../../__lib__/LegendMarketplaceTelemetryHelper.js';
import { useLegendMarketplaceBaseStore } from '../../application/providers/LegendMarketplaceFrameworkProvider.js';
import { createDefaultSuggestions, createAutosuggestSuggestions, createSearchQuerySuggestion, createLoadingSuggestion, SearchSuggestionType, SEARCH_SUGGESTION_CONSTANTS, } from '../../utils/SearchSuggestions.js';
import { debounce, assertErrorThrown, LogEvent, } from '@finos/legend-shared';
import { APPLICATION_EVENT } from '@finos/legend-application';
import { generatePathForDataProductSearchResult, convertAutosuggestResultToSearchResult, } from '../../utils/SearchUtils.js';
export const LegendMarketplaceSearchBar = observer((props) => {
const { onSearch, stateSearchQuery, placeholder, onChange, className, showSettings, stateUseProducerSearch, stateUseFieldSearch, enableAutosuggest = true, } = props;
const legendMarketplaceBaseStore = useLegendMarketplaceBaseStore();
const applicationStore = legendMarketplaceBaseStore.applicationStore;
const [searchQuery, setSearchQuery] = useState(stateSearchQuery ?? '');
const [useProducerSearch, setUseProducerSearch] = useState(stateUseProducerSearch ?? false);
const [useFieldSearch, setUseFieldSearch] = useState(stateUseFieldSearch ?? false);
const [searchMenuAnchorEl, setSearchMenuAnchorEl] = useState();
const [suggestions, setSuggestions] = useState([]);
const [loadingSuggestions, setLoadingSuggestions] = useState(false);
const [isAutosuggestPopupOpen, setIsAutosuggestPopupOpen] = useState(false);
const searchMenuOpen = Boolean(searchMenuAnchorEl);
const defaultSuggestionsFromConfig = applicationStore.config.options.defaultSearchSuggestions;
const fetchAutosuggestions = useCallback(async (query, signal) => {
if (!enableAutosuggest) {
return;
}
try {
const response = await legendMarketplaceBaseStore.marketplaceServerClient.getAutosuggestions(query, legendMarketplaceBaseStore.envState.lakehouseEnvironment, SEARCH_SUGGESTION_CONSTANTS.AUTOSUGGEST_LIMIT, signal);
const autosuggestResults = response.results;
const userQuerySuggestion = createSearchQuerySuggestion(query);
if (autosuggestResults.length > 0) {
setSuggestions([
userQuerySuggestion,
...createAutosuggestSuggestions(autosuggestResults),
]);
}
else {
setSuggestions([userQuerySuggestion]);
}
}
catch (error) {
assertErrorThrown(error);
if (error.name === 'AbortError') {
return;
}
applicationStore.logService.error(LogEvent.create(APPLICATION_EVENT.GENERIC_FAILURE), error);
const fallbackQuerySuggestion = createSearchQuerySuggestion(query);
setSuggestions([fallbackQuerySuggestion]);
}
finally {
if (!signal?.aborted) {
setLoadingSuggestions(false);
}
}
}, [
enableAutosuggest,
legendMarketplaceBaseStore.marketplaceServerClient,
legendMarketplaceBaseStore.envState.lakehouseEnvironment,
applicationStore.logService,
]);
const debouncedFetchAutosuggestions = useMemo(() => debounce(fetchAutosuggestions, SEARCH_SUGGESTION_CONSTANTS.AUTOSUGGEST_DEBOUNCE_DELAY), [fetchAutosuggestions]);
// Cleanup debounced function on unmount
useEffect(() => {
return () => {
debouncedFetchAutosuggestions.cancel();
};
}, [debouncedFetchAutosuggestions]);
// Ensure component's state is in sync with external state
useEffect(() => {
setSearchQuery(stateSearchQuery ?? '');
}, [stateSearchQuery]);
useEffect(() => {
setUseProducerSearch(stateUseProducerSearch ?? false);
}, [stateUseProducerSearch]);
useEffect(() => {
setUseFieldSearch(stateUseFieldSearch ?? false);
}, [stateUseFieldSearch]);
useEffect(() => {
const abortController = new AbortController();
if (isAutosuggestPopupOpen) {
if (!searchQuery || searchQuery.trim().length === 0) {
setSuggestions(createDefaultSuggestions(defaultSuggestionsFromConfig));
setLoadingSuggestions(false);
}
else {
const userQuerySuggestion = createSearchQuerySuggestion(searchQuery);
const loadingIndicator = createLoadingSuggestion();
setSuggestions([userQuerySuggestion, loadingIndicator]);
setLoadingSuggestions(true);
// eslint-disable-next-line no-void
void debouncedFetchAutosuggestions(searchQuery, abortController.signal);
}
}
return () => {
abortController.abort();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [searchQuery, isAutosuggestPopupOpen, debouncedFetchAutosuggestions]);
const handleInputChange = (_event, newInputValue) => {
setSearchQuery(newInputValue);
onChange?.(newInputValue);
};
const handleSuggestionSelection = (_event, selectedSuggestion) => {
if (!selectedSuggestion) {
return;
}
if (typeof selectedSuggestion === 'string') {
setSearchQuery(selectedSuggestion);
return;
}
if (selectedSuggestion.type === SearchSuggestionType.LOADING) {
return;
}
const selectedQuery = selectedSuggestion.query;
setSearchQuery(selectedQuery);
if (selectedSuggestion.type === SearchSuggestionType.SEARCH_QUERY ||
selectedSuggestion.type === SearchSuggestionType.DEFAULT) {
onSearch?.(selectedQuery, useProducerSearch, useFieldSearch);
LegendMarketplaceTelemetryHelper.logEvent_SearchAutosuggestSelection(applicationStore.telemetryService, selectedQuery, selectedSuggestion.type);
}
else {
const autosuggestResult = selectedSuggestion.autosuggestResult;
if (autosuggestResult) {
const searchResult = convertAutosuggestResultToSearchResult(autosuggestResult);
const dataProductViewerPath = generatePathForDataProductSearchResult(searchResult);
if (dataProductViewerPath) {
applicationStore.navigationService.navigator.visitAddress(applicationStore.navigationService.navigator.generateAddress(dataProductViewerPath));
}
LegendMarketplaceTelemetryHelper.logEvent_SearchAutosuggestSelection(applicationStore.telemetryService, selectedQuery, selectedSuggestion.type);
}
}
};
const handleSubmit = (event) => {
event.preventDefault();
onSearch?.(searchQuery, useProducerSearch, useFieldSearch);
};
const getOptionLabel = (option) => typeof option === 'string' ? option : option.query;
const filterOptions = (options) => {
return options;
};
const getGroupLabel = (option) => {
if (typeof option === 'string') {
return '';
}
if (option.type === SearchSuggestionType.SEARCH_QUERY ||
option.type === SearchSuggestionType.LOADING) {
return '';
}
return option.type === SearchSuggestionType.DEFAULT
? SEARCH_SUGGESTION_CONSTANTS.GROUP_HEADER_SUGGESTED_SEARCHES
: SEARCH_SUGGESTION_CONSTANTS.GROUP_HEADER_DATA_PRODUCTS;
};
return (_jsxs("form", { className: clsx('legend-marketplace__search-bar', className), onSubmit: handleSubmit, children: [_jsx(Autocomplete, { freeSolo: true, fullWidth: true, open: enableAutosuggest ? isAutosuggestPopupOpen : false, onOpen: () => {
if (enableAutosuggest) {
setIsAutosuggestPopupOpen(true);
}
}, onClose: () => {
setIsAutosuggestPopupOpen(false);
}, value: null, inputValue: searchQuery, onInputChange: handleInputChange, onChange: handleSuggestionSelection, options: suggestions, filterOptions: filterOptions, getOptionLabel: getOptionLabel, groupBy: getGroupLabel, slotProps: {
popper: {
className: 'legend-marketplace__search-bar__dropdown',
modifiers: [
{
name: 'offset',
options: {
offset: [0, 4],
},
},
{
name: 'sameWidth',
enabled: true,
phase: 'beforeWrite',
requires: ['computeStyles'],
fn: ({ state }) => {
if (state.styles.popper) {
state.styles.popper.width = `${state.rects.reference.width}px`;
}
},
effect: ({ state }) => {
const referenceWidth = state.elements.reference.offsetWidth;
state.elements.popper.style.width = `${referenceWidth}px`;
},
},
],
placement: 'bottom-start',
},
}, renderGroup: (params) => (_jsxs(Box, { children: [params.group ===
SEARCH_SUGGESTION_CONSTANTS.GROUP_HEADER_DATA_PRODUCTS && (_jsx("div", { className: "legend-marketplace__search-bar__section-divider" })), params.group && (_jsx(Typography, { className: "legend-marketplace__search-bar__autocomplete-group-header", children: params.group })), params.children] }, params.key)), renderOption: (params, suggestionOption) => {
if (typeof suggestionOption === 'string') {
return (_createElement(Box, { component: "li", ...params, key: suggestionOption },
_jsx(Typography, { className: "legend-marketplace__search-bar__autocomplete-option__text", children: suggestionOption })));
}
if (suggestionOption.type === SearchSuggestionType.SEARCH_QUERY) {
return (_createElement(Box, { component: "li", ...params, key: suggestionOption.query, className: "legend-marketplace__search-bar__autocomplete-option" },
_jsxs("div", { className: "legend-marketplace__search-bar__autocomplete-option__search-query", children: [_jsx(SearchIcon, { className: "legend-marketplace__search-bar__autocomplete-option__search-icon" }), _jsx(Typography, { className: "legend-marketplace__search-bar__autocomplete-option__text", children: suggestionOption.query })] })));
}
if (suggestionOption.type === SearchSuggestionType.LOADING) {
return (_createElement(Box, { component: "li", ...params, key: SEARCH_SUGGESTION_CONSTANTS.LOADING_KEY, className: "legend-marketplace__search-bar__autocomplete-option legend-marketplace__search-bar__autocomplete-option--loading", style: { cursor: 'default' } },
_jsxs("div", { className: "legend-marketplace__search-bar__autocomplete-option__loading", children: [_jsx(CircularProgress, { size: 16, sx: { color: 'var(--marketplace-text-secondary)' } }), _jsx(Typography, { className: "legend-marketplace__search-bar__autocomplete-option__text", sx: { color: 'var(--marketplace-text-secondary)' }, children: suggestionOption.query })] })));
}
if (suggestionOption.type === SearchSuggestionType.DEFAULT) {
return (_createElement(Box, { component: "li", ...params, key: suggestionOption.query, className: "legend-marketplace__search-bar__autocomplete-option" },
_jsx(Typography, { className: "legend-marketplace__search-bar__autocomplete-option__text", children: suggestionOption.query })));
}
const autosuggestResult = suggestionOption.autosuggestResult;
if (!autosuggestResult) {
return null;
}
const dataProductName = autosuggestResult.dataProductName;
const dataProductDescription = autosuggestResult.dataProductDescription;
return (_createElement(Box, { component: "li", ...params, key: dataProductName, className: "legend-marketplace__search-bar__autocomplete-option" },
_jsxs("div", { className: "legend-marketplace__search-bar__autocomplete-option__data-product", children: [_jsx("div", { className: "legend-marketplace__search-bar__autocomplete-option__name", children: dataProductName }), dataProductDescription && (_jsx("div", { className: "legend-marketplace__search-bar__autocomplete-option__description", children: dataProductDescription }))] })));
}, renderInput: (params) => (_jsx(TextField, { ...params, className: "legend-marketplace__search-bar__text-field", placeholder: placeholder ?? SEARCH_SUGGESTION_CONSTANTS.DEFAULT_PLACEHOLDER, fullWidth: true, slotProps: {
input: {
...params.InputProps,
className: 'legend-marketplace__search-bar__input',
endAdornment: (_jsxs(_Fragment, { children: [loadingSuggestions && (_jsx(CircularProgress, { color: "inherit", size: 28 })), params.InputProps.endAdornment, _jsxs(InputAdornment, { position: "end", children: [showSettings && (_jsx(IconButton, { onClick: (event) => setSearchMenuAnchorEl(event.currentTarget), title: "Search settings", className: "legend-marketplace__search-bar__settings-icon", children: _jsx(TuneIcon, {}) })), _jsx(IconButton, { type: "submit", title: "Search", className: "legend-marketplace__search-bar__search-icon", children: _jsx(SearchIcon, {}) })] })] })),
},
} })) }), showSettings && (_jsxs(Menu, { anchorEl: searchMenuAnchorEl, open: searchMenuOpen, onClose: () => setSearchMenuAnchorEl(null), children: [_jsx(MenuItem, { children: _jsx(FormControlLabel, { control: _jsx(Switch, { checked: useProducerSearch, onChange: (event) => {
setUseProducerSearch(event.target.checked);
if (event.target.checked) {
setUseFieldSearch(false);
}
LegendMarketplaceTelemetryHelper.logEvent_ToggleProducerSearch(applicationStore.telemetryService, event.target.checked);
} }), label: _jsxs(_Fragment, { children: ["Producer Search", ' ', _jsx(LegendMarketplaceInfoTooltip, { title: "Use this search if you have just created a data product and would like to immediately see it" })] }) }) }), _jsx(MenuItem, { children: _jsx(FormControlLabel, { control: _jsx(Switch, { checked: useFieldSearch, onChange: (event) => {
setUseFieldSearch(event.target.checked);
if (event.target.checked) {
setUseProducerSearch(false);
}
LegendMarketplaceTelemetryHelper.logEvent_ToggleFieldSearch(applicationStore.telemetryService, event.target.checked);
} }), label: _jsxs(_Fragment, { children: ["Field Search", ' ', _jsx(LegendMarketplaceInfoTooltip, { title: "Use this search to discover data products and datasets that contain a specific field" })] }) }) })] }))] }));
});
//# sourceMappingURL=LegendMarketplaceSearchBar.js.map