UNPKG

react-instantsearch

Version:
482 lines (479 loc) 23.8 kB
import { _ as _$1 } from '@swc/helpers/cjs/_object_spread.cjs'; import { _ as _$4 } from '@swc/helpers/cjs/_object_spread_props.cjs'; import { _ } from '@swc/helpers/cjs/_object_without_properties.cjs'; import { _ as _$5 } from '@swc/helpers/cjs/_sliced_to_array.cjs'; import { _ as _$2 } from '@swc/helpers/cjs/_to_consumable_array.cjs'; import { _ as _$3 } from '@swc/helpers/cjs/_type_of.cjs'; import { createAutocompleteComponent, createAutocompletePanelComponent, createAutocompleteIndexComponent, createAutocompleteSuggestionComponent, createAutocompleteRecentSearchComponent, createAutocompleteDetachedContainerComponent, createAutocompleteDetachedOverlayComponent, createAutocompleteDetachedFormContainerComponent, createAutocompleteDetachedSearchButtonComponent, createAutocompletePropGetters, createAutocompleteStorage, cx } from 'instantsearch-ui-components'; import React, { createElement, Fragment, useEffect, useId, useMemo, useRef, useState } from 'react'; import { useInstantSearch, useSearchBox, Index, Configure, useAutocomplete } from 'react-instantsearch-core'; import { AutocompleteSearch } from '../components/AutocompleteSearch.js'; import { ReverseHighlight } from './ReverseHighlight.js'; var Autocomplete = createAutocompleteComponent({ createElement: createElement, Fragment: Fragment }); var AutocompletePanel = createAutocompletePanelComponent({ createElement: createElement, Fragment: Fragment }); var AutocompleteIndex = createAutocompleteIndexComponent({ createElement: createElement, Fragment: Fragment }); var AutocompleteSuggestion = createAutocompleteSuggestionComponent({ createElement: createElement, Fragment: Fragment }); var AutocompleteRecentSearch = createAutocompleteRecentSearchComponent({ createElement: createElement, Fragment: Fragment }); var AutocompleteDetachedContainer = createAutocompleteDetachedContainerComponent({ createElement: createElement, Fragment: Fragment }); var AutocompleteDetachedOverlay = createAutocompleteDetachedOverlayComponent({ createElement: createElement, Fragment: Fragment }); var AutocompleteDetachedFormContainer = createAutocompleteDetachedFormContainerComponent({ createElement: createElement, Fragment: Fragment }); var AutocompleteDetachedSearchButton = createAutocompleteDetachedSearchButtonComponent({ createElement: createElement, Fragment: Fragment }); var usePropGetters = createAutocompletePropGetters({ useEffect: useEffect, useId: useId, useMemo: useMemo, useRef: useRef, useState: useState }); var useStorage = createAutocompleteStorage({ useEffect: useEffect, useMemo: useMemo, useState: useState }); var DEFAULT_DETACHED_MEDIA_QUERY = '(max-width: 680px)'; var DEFAULT_DETACHED_MODAL_MEDIA_QUERY = '(min-width: 680px)'; var DETACHED_MEDIA_QUERY_CSS_VAR = '--ais-autocomplete-detached-media-query'; var DETACHED_MODAL_MEDIA_QUERY_CSS_VAR = '--ais-autocomplete-detached-modal-media-query'; var DEFAULT_TRANSLATIONS = { detachedCancelButtonText: 'Cancel', detachedSearchButtonTitle: 'Search', detachedClearButtonTitle: 'Clear' }; function getCssMediaQueryValue(name) { if (typeof window === 'undefined' || typeof document === 'undefined') { return ''; } return getComputedStyle(document.documentElement).getPropertyValue(name).trim(); } function resolveMediaQuery(value, cssVarName, fallback) { if (value === '') { return ''; } if (value) { return value; } return getCssMediaQueryValue(cssVarName) || fallback; } function getMediaQueryList(mediaQuery) { if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') { return null; } return window.matchMedia(mediaQuery); } /** * Hook to manage detached (mobile) mode state */ function useDetachedMode(mediaQuery) { var resolvedMediaQuery = useMemo(function() { return resolveMediaQuery(mediaQuery, DETACHED_MEDIA_QUERY_CSS_VAR, DEFAULT_DETACHED_MEDIA_QUERY); }, [ mediaQuery ]); var _useState = _$5(useState(function() { var _getMediaQueryList; return resolvedMediaQuery ? Boolean((_getMediaQueryList = getMediaQueryList(resolvedMediaQuery)) === null || _getMediaQueryList === void 0 ? void 0 : _getMediaQueryList.matches) : false; }), 2), isDetached = _useState[0], setIsDetached = _useState[1]; var _useState1 = _$5(useState(false), 2), isModalOpen = _useState1[0], setIsModalOpen = _useState1[1]; var _useState2 = _$5(useState(false), 2), isModalDetached = _useState2[0], setIsModalDetached = _useState2[1]; useEffect(function() { if (!resolvedMediaQuery) { setIsDetached(false); return function() {}; } var mql = getMediaQueryList(resolvedMediaQuery); if (!mql) { setIsDetached(false); return function() {}; } var handler = function handler(event) { var wasDetached = isDetached; setIsDetached(event.matches); // Close modal if switching from detached to non-detached if (wasDetached && !event.matches) { setIsModalOpen(false); } }; mql.addEventListener('change', handler); return function() { return mql.removeEventListener('change', handler); }; }, [ resolvedMediaQuery, isDetached ]); useEffect(function() { if (!isDetached) { setIsModalDetached(false); return function() {}; } var modalMediaQuery = resolveMediaQuery(undefined, DETACHED_MODAL_MEDIA_QUERY_CSS_VAR, DEFAULT_DETACHED_MODAL_MEDIA_QUERY); if (!modalMediaQuery) { setIsModalDetached(false); return function() {}; } var mql = getMediaQueryList(modalMediaQuery); if (!mql) { setIsModalDetached(false); return function() {}; } var handler = function handler(event) { setIsModalDetached(event.matches); }; setIsModalDetached(mql.matches); mql.addEventListener('change', handler); return function() { return mql.removeEventListener('change', handler); }; }, [ isDetached ]); useEffect(function() { if (typeof document === 'undefined') return function() {}; if (isModalOpen) { var scrollY = window.scrollY; document.body.style.top = "-".concat(scrollY, "px"); document.body.classList.add('ais-Autocomplete--detached'); return function() { document.body.classList.remove('ais-Autocomplete--detached'); document.body.style.top = ''; window.scrollTo(0, scrollY); }; } return function() {}; }, [ isModalOpen ]); return { isDetached: isDetached, isModalDetached: isModalDetached, isModalOpen: isModalOpen, setIsModalOpen: setIsModalOpen }; } function EXPERIMENTAL_Autocomplete(_0) { var _0_indices = _0.indices, indices = _0_indices === void 0 ? [] : _0_indices, showSuggestions = _0.showSuggestions, showRecent = _0.showRecent, userSearchParameters = _0.searchParameters, detachedMediaQuery = _0.detachedMediaQuery, tmp = _0.translations, userTranslations = tmp === void 0 ? {} : tmp, props = _(_0, [ "indices", "showSuggestions", "showRecent", "searchParameters", "detachedMediaQuery", "translations" ]); var _showRecent_classNames, _showRecent_classNames1, _showRecent_classNames2, _showRecent_classNames3; var translations = _$1({}, DEFAULT_TRANSLATIONS, userTranslations); var _useInstantSearch = useInstantSearch(), indexUiState = _useInstantSearch.indexUiState, indexRenderState = _useInstantSearch.indexRenderState, status = _useInstantSearch.status; var refine = useSearchBox({}, { $$type: 'ais.autocomplete', $$widgetType: 'ais.autocomplete' }).refine; var isSearchStalled = status === 'stalled'; var searchParameters = _$1({ hitsPerPage: 5 }, userSearchParameters); var indicesConfig = _$2(indices); if (showSuggestions === null || showSuggestions === void 0 ? void 0 : showSuggestions.indexName) { var _showSuggestions_classNames, _showSuggestions_classNames1, _showSuggestions_classNames2, _showSuggestions_classNames3; indicesConfig.unshift({ indexName: showSuggestions.indexName, headerComponent: showSuggestions.headerComponent, itemComponent: showSuggestions.itemComponent || function(param) { var item = param.item, onSelect = param.onSelect, onApply = param.onApply; return /*#__PURE__*/ React.createElement(AutocompleteSuggestion, { item: item, onSelect: onSelect, onApply: onApply }, /*#__PURE__*/ React.createElement(ConditionalReverseHighlight, { item: item })); }, classNames: { root: cx('ais-AutocompleteSuggestions', showSuggestions === null || showSuggestions === void 0 ? void 0 : (_showSuggestions_classNames = showSuggestions.classNames) === null || _showSuggestions_classNames === void 0 ? void 0 : _showSuggestions_classNames.root), list: cx('ais-AutocompleteSuggestionsList', showSuggestions === null || showSuggestions === void 0 ? void 0 : (_showSuggestions_classNames1 = showSuggestions.classNames) === null || _showSuggestions_classNames1 === void 0 ? void 0 : _showSuggestions_classNames1.list), header: cx('ais-AutocompleteSuggestionsHeader', showSuggestions === null || showSuggestions === void 0 ? void 0 : (_showSuggestions_classNames2 = showSuggestions.classNames) === null || _showSuggestions_classNames2 === void 0 ? void 0 : _showSuggestions_classNames2.header), item: cx('ais-AutocompleteSuggestionsItem', showSuggestions === null || showSuggestions === void 0 ? void 0 : (_showSuggestions_classNames3 = showSuggestions.classNames) === null || _showSuggestions_classNames3 === void 0 ? void 0 : _showSuggestions_classNames3.item) }, getQuery: function getQuery(item) { return item.query; }, getURL: showSuggestions.getURL }); } var recentSearchConfig = showRecent ? { headerComponent: (typeof showRecent === "undefined" ? "undefined" : _$3(showRecent)) === 'object' ? showRecent.headerComponent : undefined, itemComponent: (typeof showRecent === "undefined" ? "undefined" : _$3(showRecent)) === 'object' && showRecent.itemComponent ? showRecent.itemComponent : AutocompleteRecentSearch, classNames: { root: cx('ais-AutocompleteRecentSearches', (typeof showRecent === "undefined" ? "undefined" : _$3(showRecent)) === 'object' ? (_showRecent_classNames = showRecent.classNames) === null || _showRecent_classNames === void 0 ? void 0 : _showRecent_classNames.root : undefined), list: cx('ais-AutocompleteRecentSearchesList', (typeof showRecent === "undefined" ? "undefined" : _$3(showRecent)) === 'object' ? (_showRecent_classNames1 = showRecent.classNames) === null || _showRecent_classNames1 === void 0 ? void 0 : _showRecent_classNames1.list : undefined), header: cx('ais-AutocompleteRecentSearchesHeader', (typeof showRecent === "undefined" ? "undefined" : _$3(showRecent)) === 'object' ? (_showRecent_classNames2 = showRecent.classNames) === null || _showRecent_classNames2 === void 0 ? void 0 : _showRecent_classNames2.header : undefined), item: cx('ais-AutocompleteRecentSearchesItem', (typeof showRecent === "undefined" ? "undefined" : _$3(showRecent)) === 'object' ? (_showRecent_classNames3 = showRecent.classNames) === null || _showRecent_classNames3 === void 0 ? void 0 : _showRecent_classNames3.item : undefined) } } : undefined; var isSearchPage = useMemo(function() { return typeof indexRenderState.hits !== 'undefined' || typeof indexRenderState.infiniteHits !== 'undefined'; }, [ indexRenderState ]); return /*#__PURE__*/ React.createElement(Fragment, null, /*#__PURE__*/ React.createElement(Index, { EXPERIMENTAL_isolated: true }, /*#__PURE__*/ React.createElement(Configure, searchParameters), indicesConfig.map(function(index) { return /*#__PURE__*/ React.createElement(Index, { key: index.indexName, indexName: index.indexName }, /*#__PURE__*/ React.createElement(Configure, index.searchParameters)); }), /*#__PURE__*/ React.createElement(InnerAutocomplete, _$4(_$1({}, props), { indicesConfig: indicesConfig, refineSearchBox: refine, isSearchStalled: isSearchStalled, indexUiState: indexUiState, isSearchPage: isSearchPage, showRecent: showRecent, recentSearchConfig: recentSearchConfig, showSuggestions: showSuggestions, detachedMediaQuery: detachedMediaQuery, translations: translations })))); } function InnerAutocomplete(_0) { var indicesConfig = _0.indicesConfig, refineSearchBox = _0.refineSearchBox, isSearchStalled = _0.isSearchStalled, getSearchPageURL = _0.getSearchPageURL, userOnSelect = _0.onSelect, indexUiState = _0.indexUiState, isSearchPage = _0.isSearchPage, PanelComponent = _0.panelComponent, showRecent = _0.showRecent, recentSearchConfig = _0.recentSearchConfig, showSuggestions = _0.showSuggestions, transformItems = _0.transformItems, placeholder = _0.placeholder, _0_detachedMediaQuery = _0.detachedMediaQuery, detachedMediaQuery = _0_detachedMediaQuery === void 0 ? DEFAULT_DETACHED_MEDIA_QUERY : _0_detachedMediaQuery, translations = _0.translations, classNames = _0.classNames, props = _(_0, [ "indicesConfig", "refineSearchBox", "isSearchStalled", "getSearchPageURL", "onSelect", "indexUiState", "isSearchPage", "panelComponent", "showRecent", "recentSearchConfig", "showSuggestions", "transformItems", "placeholder", "detachedMediaQuery", "translations", "classNames" ]); var _useAutocomplete = useAutocomplete({ transformItems: transformItems }), indices = _useAutocomplete.indices, refineAutocomplete = _useAutocomplete.refine, currentRefinement = _useAutocomplete.currentRefinement; var _useDetachedMode = useDetachedMode(detachedMediaQuery), isDetached = _useDetachedMode.isDetached, isModalDetached = _useDetachedMode.isModalDetached, isModalOpen = _useDetachedMode.isModalOpen, setIsModalOpen = _useDetachedMode.setIsModalOpen; var previousIsDetachedRef = useRef(isDetached); var _useStorage = useStorage({ showRecent: showRecent, query: currentRefinement, indices: indices, indicesConfig: indicesConfig, suggestionsIndexName: showSuggestions === null || showSuggestions === void 0 ? void 0 : showSuggestions.indexName }), storage = _useStorage.storage, storageHits = _useStorage.storageHits, indicesForPropGetters = _useStorage.indicesForPropGetters, indicesConfigForPropGetters = _useStorage.indicesConfigForPropGetters; var _usePropGetters = usePropGetters({ indices: indicesForPropGetters, indicesConfig: indicesConfigForPropGetters, onRefine: function onRefine(query) { refineAutocomplete(query); refineSearchBox(query); storage.onAdd(query); }, onApply: function onApply(query) { refineAutocomplete(query); }, onSelect: userOnSelect !== null && userOnSelect !== void 0 ? userOnSelect : function(param) { var query = param.query, setQuery = param.setQuery, url = param.url; if (url) { window.location.href = url; return; } if (!isSearchPage && typeof getSearchPageURL !== 'undefined') { window.location.href = getSearchPageURL(_$4(_$1({}, indexUiState), { query: query })); return; } setQuery(query); }, onSubmit: function onSubmit() { // Close the detached modal when form is submitted if (isDetached) { setIsModalOpen(false); } }, placeholder: placeholder, isDetached: isDetached }), getInputProps = _usePropGetters.getInputProps, getItemProps = _usePropGetters.getItemProps, getPanelProps = _usePropGetters.getPanelProps, getRootProps = _usePropGetters.getRootProps, isOpen = _usePropGetters.isOpen, setIsOpen = _usePropGetters.setIsOpen, focusInput = _usePropGetters.focusInput; // Open panel and focus input when modal opens useEffect(function() { if (isDetached && isModalOpen) { setIsOpen(true); // Focus input to show the keyboard on mobile focusInput(); } }, [ isDetached, isModalOpen, setIsOpen, focusInput ]); // Keep the modal open if the panel was open before switching to detached useEffect(function() { var wasDetached = previousIsDetachedRef.current; var switchedToDetached = !wasDetached && isDetached; if (switchedToDetached && isOpen) { setIsModalOpen(true); } previousIsDetachedRef.current = isDetached; }, [ isDetached, isOpen, setIsModalOpen ]); var elements = {}; if (showRecent && recentSearchConfig) { var RecentSearchItemComponent = recentSearchConfig.itemComponent; elements.recent = /*#__PURE__*/ React.createElement(AutocompleteIndex, { HeaderComponent: recentSearchConfig.headerComponent, // @ts-ignore - there seems to be problems with React.ComponentType and this, but it's actually correct ItemComponent: function ItemComponent(param) { var item = param.item, onSelect = param.onSelect, onApply = param.onApply; return /*#__PURE__*/ React.createElement(RecentSearchItemComponent, { item: item, onSelect: onSelect, onRemoveRecentSearch: function onRemoveRecentSearch() { return storage.onRemove(item.query); }, onApply: onApply }, /*#__PURE__*/ React.createElement(ConditionalReverseHighlight, { item: item })); }, classNames: recentSearchConfig.classNames, items: storageHits, getItemProps: getItemProps, key: "recentSearches" }); } indices.forEach(function(param) { var indexId = param.indexId, indexName = param.indexName, hits = param.hits; var elementId = indexName === (showSuggestions === null || showSuggestions === void 0 ? void 0 : showSuggestions.indexName) ? 'suggestions' : indexName; var filteredHits = elementId === 'suggestions' && showRecent ? hits.filter(function(suggestionHit) { return !storageHits.find(function(storageHit) { return storageHit.query === suggestionHit.query; }); }) : hits; var currentIndexConfig = indicesConfig.find(function(config) { return config.indexName === indexName; }); if (!currentIndexConfig) { return; } elements[elementId] = /*#__PURE__*/ React.createElement(AutocompleteIndex, { key: indexId, // @ts-expect-error - there seems to be problems with React.ComponentType and this, but it's actually correct HeaderComponent: currentIndexConfig.headerComponent, // @ts-expect-error - there seems to be problems with React.ComponentType and this, but it's actually correct ItemComponent: currentIndexConfig.itemComponent, items: filteredHits.map(function(item) { return _$4(_$1({}, item), { __indexName: indexId }); }), getItemProps: getItemProps, classNames: currentIndexConfig.classNames }); }); var searchBoxContent = /*#__PURE__*/ React.createElement(AutocompleteSearch, { inputProps: getInputProps(), clearQuery: function clearQuery() { refineSearchBox(''); refineAutocomplete(''); }, onQueryChange: function onQueryChange(query) { refineAutocomplete(query); }, query: currentRefinement || indexUiState.query || '', refine: refineSearchBox, isSearchStalled: isSearchStalled }); var panelContent = /*#__PURE__*/ React.createElement(AutocompletePanel, getPanelProps(), PanelComponent ? /*#__PURE__*/ React.createElement(PanelComponent, { elements: elements, indices: indices }) : Object.keys(elements).map(function(elementId) { return elements[elementId]; })); var detachedContainerClassNames = isModalDetached ? _$4(_$1({}, classNames), { detachedContainer: cx('ais-AutocompleteDetachedContainer--modal', classNames === null || classNames === void 0 ? void 0 : classNames.detachedContainer) }) : classNames; if (isDetached) { return /*#__PURE__*/ React.createElement(Autocomplete, _$4(_$1({}, props, getRootProps()), { classNames: classNames }), /*#__PURE__*/ React.createElement(AutocompleteDetachedSearchButton, { query: currentRefinement || indexUiState.query || '', placeholder: placeholder, classNames: classNames, onClick: function onClick() { setIsModalOpen(true); setIsOpen(true); }, onClear: function onClear() { refineSearchBox(''); refineAutocomplete(''); }, translations: translations }), isModalOpen && /*#__PURE__*/ React.createElement(AutocompleteDetachedOverlay, { classNames: classNames, onClose: function onClose() { setIsModalOpen(false); setIsOpen(false); } }, /*#__PURE__*/ React.createElement(AutocompleteDetachedContainer, { classNames: detachedContainerClassNames }, /*#__PURE__*/ React.createElement(AutocompleteDetachedFormContainer, { classNames: classNames, onCancel: function onCancel() { setIsModalOpen(false); setIsOpen(false); }, translations: translations }, searchBoxContent), panelContent))); } // Normal (non-detached) rendering return /*#__PURE__*/ React.createElement(Autocomplete, _$4(_$1({}, props, getRootProps()), { classNames: classNames }), searchBoxContent, panelContent); } function ConditionalReverseHighlight(param) { var item = param.item; var _item__highlightResult; if (!((_item__highlightResult = item._highlightResult) === null || _item__highlightResult === void 0 ? void 0 : _item__highlightResult.query) || // @ts-expect-error - we should not have matchLevel as arrays here item._highlightResult.query.matchLevel === 'none') { return item.query; } return /*#__PURE__*/ React.createElement(ReverseHighlight, { attribute: "query", hit: item }); } export { EXPERIMENTAL_Autocomplete };