@docsearch/react 5.0.4 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist/esm/{DocSearch-2gNxdJcC.d.ts → DocSearch-BFLV-F2L.d.ts} +3 -2
  2. package/dist/esm/{DocSearchAI-DKtAqje2.d.ts → DocSearchAI-f-C2lV8l.d.ts} +2 -2
  3. package/dist/esm/{DocSearchAskAiModal-BzQZxvTY.js → DocSearchAskAiModal-CZm9AVU0.js} +3 -3
  4. package/dist/esm/{DocSearchAskAiModal-BzQZxvTY.js.map → DocSearchAskAiModal-CZm9AVU0.js.map} +1 -1
  5. package/dist/esm/{DocSearchModal-Cgdi4eHY.js → DocSearchModal-CLcbYBea.js} +2 -2
  6. package/dist/esm/{DocSearchModal-Cgdi4eHY.js.map → DocSearchModal-CLcbYBea.js.map} +1 -1
  7. package/dist/esm/askaiModal.d.ts +1 -1
  8. package/dist/esm/askaiModal.js +1 -1
  9. package/dist/esm/docsearch.d.ts +1 -1
  10. package/dist/esm/docsearch.js +1 -1
  11. package/dist/esm/docsearchAi.d.ts +1 -1
  12. package/dist/esm/docsearchAi.js +1 -1
  13. package/dist/esm/index.d.ts +2 -2
  14. package/dist/esm/index.js +2 -2
  15. package/dist/esm/modal.d.ts +1 -1
  16. package/dist/esm/modal.js +1 -1
  17. package/dist/esm/{normalizeDocSearchIndexes-CFHzf-sx.js → normalizeDocSearchIndexes-B_bI_ytC.js} +170 -52
  18. package/dist/esm/normalizeDocSearchIndexes-B_bI_ytC.js.map +1 -0
  19. package/dist/esm/sidepanel.d.ts +2 -2
  20. package/dist/esm/sidepanel.js +1 -1
  21. package/dist/esm/{useSuggestedQuestions-DitQZbae.js → useSuggestedQuestions-DkGTPSdx.js} +3 -3
  22. package/dist/esm/useSuggestedQuestions-DkGTPSdx.js.map +1 -0
  23. package/dist/esm/version.d.ts +1 -1
  24. package/dist/esm/version.js +1 -1
  25. package/dist/esm/version.js.map +1 -1
  26. package/dist/umd/index.js +45 -45
  27. package/dist/umd/index.js.map +1 -1
  28. package/package.json +6 -6
  29. package/dist/esm/normalizeDocSearchIndexes-CFHzf-sx.js.map +0 -1
  30. package/dist/esm/useSuggestedQuestions-DitQZbae.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"DocSearchModal-Cgdi4eHY.js","names":[],"sources":["../../src/components/KeywordSearchBox.tsx","../../src/components/KeywordStartScreen.tsx","../../src/ScreenState.tsx","../../src/DocSearchModal.tsx"],"sourcesContent":["import type {\n AutocompleteApi,\n AutocompleteState,\n} from '@algolia/autocomplete-core';\nimport React, { type JSX, type RefObject } from 'react';\n\nimport type { InternalDocSearchHit } from '../types';\n\nimport { SearchBoxForm } from './ui/SearchBoxForm';\n\nexport type KeywordSearchBoxTranslations = Partial<{\n clearButtonTitle: string;\n clearButtonAriaLabel: string;\n closeButtonText: string;\n closeButtonAriaLabel: string;\n placeholderText: string;\n enterKeyHint: string;\n searchInputLabel: string;\n}>;\n\ninterface KeywordSearchBoxProps extends AutocompleteApi<\n InternalDocSearchHit,\n React.FormEvent,\n React.MouseEvent,\n React.KeyboardEvent\n> {\n state: AutocompleteState<InternalDocSearchHit>;\n autoFocus: boolean;\n inputRef: RefObject<HTMLInputElement | null>;\n onClose: () => void;\n placeholder: string;\n isFromSelection: boolean;\n translations?: KeywordSearchBoxTranslations;\n}\n\nexport function KeywordSearchBox({\n translations = {},\n ...props\n}: KeywordSearchBoxProps): JSX.Element {\n const {\n clearButtonTitle = 'Clear',\n clearButtonAriaLabel = 'Clear the query',\n closeButtonText = 'Close',\n closeButtonAriaLabel = 'Close',\n searchInputLabel = 'Search',\n } = translations;\n\n const actionsBeforeClose = (\n <>\n <button\n className=\"DocSearch-Clear\"\n type=\"reset\"\n aria-label={clearButtonAriaLabel}\n hidden={!props.state.query}\n tabIndex={props.state.query ? 0 : -1}\n aria-hidden={!props.state.query ? 'true' : 'false'}\n >\n {clearButtonTitle}\n </button>\n\n {props.state.query && <div className=\"DocSearch-Divider\" />}\n </>\n );\n\n return (\n <SearchBoxForm\n {...props}\n clearButtonTitle={clearButtonTitle}\n clearButtonAriaLabel={clearButtonAriaLabel}\n closeButtonText={closeButtonText}\n closeButtonAriaLabel={closeButtonAriaLabel}\n searchInputLabel={searchInputLabel}\n inputProps={{\n enterKeyHint: 'search',\n }}\n actionsBeforeClose={actionsBeforeClose}\n />\n );\n}\n","import React, { type JSX } from 'react';\n\nimport type { ScreenStateProps } from '../ScreenState';\nimport type { InternalDocSearchHit } from '../types';\n\nimport type { StoredSearchesSectionsTranslations } from './ui/StoredSearchesSections';\nimport { StoredSearchesSections } from './ui/StoredSearchesSections';\n\nexport type KeywordStartScreenTranslations = StoredSearchesSectionsTranslations;\n\ntype KeywordStartScreenProps = Omit<\n ScreenStateProps<InternalDocSearchHit>,\n 'translations'\n> & {\n hasCollections: boolean;\n translations?: KeywordStartScreenTranslations;\n};\n\nexport function KeywordStartScreen({\n translations = {},\n ...props\n}: KeywordStartScreenProps): JSX.Element | null {\n return (\n <div className=\"DocSearch-Dropdown-Container\">\n <StoredSearchesSections {...props} translations={translations} />\n </div>\n );\n}\n","import type {\n AutocompleteApi,\n AutocompleteState,\n BaseItem,\n} from '@algolia/autocomplete-core';\nimport React from 'react';\n\nimport type { KeywordStartScreenTranslations } from './components/KeywordStartScreen';\nimport { KeywordStartScreen } from './components/KeywordStartScreen';\nimport type { DocSearchProps } from './DocSearch';\nimport type { ErrorScreenTranslations } from './ErrorScreen';\nimport { ErrorScreen } from './ErrorScreen';\nimport type { NoResultsScreenTranslations } from './NoResultsScreen';\nimport { NoResultsScreen } from './NoResultsScreen';\nimport type { ResultsScreenTranslations } from './ResultsScreen';\nimport { ResultsScreen } from './ResultsScreen';\nimport type { StoredSearchPlugin } from './stored-searches';\nimport type { InternalDocSearchHit, StoredDocSearchHit } from './types';\nimport { isQueryEmpty } from './utils';\n\nexport type ScreenStateTranslations = Partial<{\n errorScreen: ErrorScreenTranslations;\n startScreen: KeywordStartScreenTranslations;\n noResultsScreen: NoResultsScreenTranslations;\n resultsScreen: ResultsScreenTranslations;\n}>;\n\nexport interface ScreenStateProps<\n TItem extends BaseItem,\n> extends AutocompleteApi<\n TItem,\n React.FormEvent,\n React.MouseEvent,\n React.KeyboardEvent\n> {\n state: AutocompleteState<TItem>;\n recentSearches: StoredSearchPlugin<StoredDocSearchHit>;\n favoriteSearches: StoredSearchPlugin<StoredDocSearchHit>;\n onItemClick: (\n item: InternalDocSearchHit,\n event: KeyboardEvent | MouseEvent\n ) => void;\n inputRef: React.MutableRefObject<HTMLInputElement | null>;\n hitComponent: DocSearchProps['hitComponent'];\n indexName: string;\n disableUserPersonalization: boolean;\n resultsFooterComponent: DocSearchProps['resultsFooterComponent'];\n translations: ScreenStateTranslations;\n getMissingResultsUrl?: DocSearchProps['getMissingResultsUrl'];\n hasCollections: boolean;\n resultBadgeKey?: string;\n}\n\nexport const ScreenState = React.memo(\n ({ translations = {}, ...props }: ScreenStateProps<InternalDocSearchHit>) => {\n if (props.state?.status === 'error') {\n return <ErrorScreen translations={translations?.errorScreen} />;\n }\n\n if (isQueryEmpty(props.state.query)) {\n return (\n <KeywordStartScreen\n {...props}\n hasCollections={props.hasCollections}\n translations={translations?.startScreen}\n />\n );\n }\n\n if (!props.hasCollections) {\n return (\n <NoResultsScreen\n {...props}\n translations={translations?.noResultsScreen}\n />\n );\n }\n\n return (\n <ResultsScreen {...props} translations={translations?.resultsScreen} />\n );\n },\n function areEqual(prevProps, nextProps) {\n const isLoading =\n nextProps.state.status === 'loading' ||\n nextProps.state.status === 'stalled';\n\n const isWaitingForCollections =\n nextProps.state.status === 'idle' &&\n !isQueryEmpty(nextProps.state.query) &&\n prevProps.state.query !== nextProps.state.query &&\n prevProps.state.collections === nextProps.state.collections;\n\n return isLoading || isWaitingForCollections;\n }\n);\n","import { createAutocomplete } from '@algolia/autocomplete-core';\nimport React, { type JSX } from 'react';\n\nimport type { FacetBarTranslations } from './components/FacetBar';\nimport { FacetBar } from './components/FacetBar';\nimport type { KeywordSearchBoxTranslations } from './components/KeywordSearchBox';\nimport { KeywordSearchBox } from './components/KeywordSearchBox';\nimport { ModalShell } from './components/ui/ModalShell';\nimport type { DocSearchProps } from './DocSearch';\nimport type { FooterTranslations } from './Footer';\nimport { Footer } from './Footer';\nimport { Hit } from './Hit';\nimport { useDocSearchFacets } from './hooks/useDocSearchFacets';\nimport { useSendItemClickEvent } from './hooks/useDocSearchInsights';\nimport { useInitialModalQuery } from './hooks/useInitialModalQuery';\nimport { useModalEnvironment } from './hooks/useModalEnvironment';\nimport { useModalRefs } from './hooks/useModalRefs';\nimport { useRefreshOnInitialQuery } from './hooks/useRefreshOnInitialQuery';\nimport { useSaveRecentSearch } from './hooks/useSaveRecentSearch';\nimport { useStoredDocSearches } from './hooks/useStoredDocSearches';\nimport type { ScreenStateTranslations } from './ScreenState';\nimport { ScreenState } from './ScreenState';\nimport type { DocSearchState, InternalDocSearchHit } from './types';\nimport { useSearchClient } from './useSearchClient';\nimport {\n identity,\n isModifierEvent,\n isQueryEmpty,\n noop,\n scrollTo as scrollToUtils,\n} from './utils';\nimport {\n buildNoQuerySources,\n buildQuerySources,\n type BuildQuerySourcesState,\n} from './utils/createDocSearchSources';\nimport { normalizeDocSearchIndexes } from './utils/normalizeDocSearchIndexes';\n\nexport type ModalTranslations = Partial<{\n searchBox: KeywordSearchBoxTranslations;\n footer: FooterTranslations;\n facets: FacetBarTranslations;\n}> &\n ScreenStateTranslations;\n\nexport type DocSearchModalProps = DocSearchProps & {\n initialScrollY: number;\n onClose?: () => void;\n translations?: ModalTranslations;\n};\n\nexport function DocSearchModal({\n appId,\n apiKey,\n maxResultsPerGroup,\n theme,\n onClose = noop,\n transformItems = identity,\n hitComponent = Hit,\n resultsFooterComponent = (): JSX.Element | null => null,\n navigator,\n initialScrollY = 0,\n transformSearchClient = identity,\n disableUserPersonalization = false,\n initialQuery: initialQueryFromProp = '',\n translations = {},\n getMissingResultsUrl,\n insights = false,\n recentSearchesLimit = 7,\n recentSearchesWithFavoritesLimit = 4,\n indices,\n facets,\n footerAction,\n ...props\n}: DocSearchModalProps): JSX.Element {\n const {\n footer: footerTranslations,\n searchBox: searchBoxTranslations,\n facets: facetBarTranslations,\n ...screenStateTranslations\n } = translations;\n const [state, setState] = React.useState<\n DocSearchState<InternalDocSearchHit>\n >({\n query: '',\n collections: [],\n completion: null,\n context: {},\n isOpen: false,\n activeItemId: null,\n status: 'idle',\n });\n\n const placeholder =\n translations?.searchBox?.placeholderText ||\n props.placeholder ||\n 'Search docs';\n\n const { containerRef, modalRef, formElementRef, dropdownRef, inputRef } =\n useModalRefs();\n const { initialQuery, initialQueryFromSelection } =\n useInitialModalQuery(initialQueryFromProp);\n\n const searchClient = useSearchClient(appId, apiKey, transformSearchClient);\n\n const indexes = React.useMemo(\n () =>\n normalizeDocSearchIndexes({\n indices,\n }),\n [indices]\n );\n const defaultIndexName = indexes[0].name;\n\n const autocompleteRef =\n React.useRef<\n ReturnType<\n typeof createAutocomplete<\n InternalDocSearchHit,\n React.FormEvent<HTMLFormElement>,\n React.MouseEvent,\n React.KeyboardEvent\n >\n >\n >(undefined);\n\n const {\n visibleFacets,\n facetSelections,\n facetSelectionsRef,\n handleFacetSelectionChange,\n clearFacetSelections,\n } = useDocSearchFacets({\n facets,\n indexes,\n searchClient,\n onSelectionsChange: () => autocompleteRef.current?.refresh(),\n });\n\n const { favoriteSearches, recentSearches } = useStoredDocSearches({\n defaultIndexName,\n recentSearchesLimit,\n recentSearchesWithFavoritesLimit,\n });\n const saveRecentSearch = useSaveRecentSearch({\n favoriteSearches,\n recentSearches,\n disableUserPersonalization,\n });\n const sendItemClickEvent = useSendItemClickEvent(state);\n\n if (!autocompleteRef.current) {\n autocompleteRef.current = createAutocomplete({\n id: 'docsearch',\n defaultActiveItemId: 0,\n openOnFocus: true,\n initialState: {\n query: initialQuery,\n context: {\n searchSuggestions: [],\n },\n },\n insights: Boolean(insights),\n navigator,\n onStateChange(changes) {\n setState(changes.state);\n },\n getSources({ query, state: sourcesState, setContext, setStatus }) {\n if (isQueryEmpty(query)) {\n const noQuerySources = buildNoQuerySources({\n recentSearches,\n favoriteSearches,\n saveRecentSearch,\n onClose,\n disableUserPersonalization,\n });\n return noQuerySources;\n }\n\n const querySourcesState: BuildQuerySourcesState = {\n context: sourcesState.context,\n };\n\n const algoliaSourcesPromise = buildQuerySources({\n query,\n state: querySourcesState,\n setContext,\n setStatus,\n searchClient,\n indexes,\n insights: Boolean(insights),\n appId,\n apiKey,\n maxResultsPerGroup,\n transformItems,\n saveRecentSearch,\n onClose,\n facetSelections: facetSelectionsRef,\n });\n\n return algoliaSourcesPromise;\n },\n });\n }\n\n const autocomplete = autocompleteRef.current;\n\n const { getEnvironmentProps, getRootProps, refresh } = autocomplete;\n\n useModalEnvironment({\n getEnvironmentProps,\n containerRef,\n dropdownRef,\n formElementRef,\n inputRef,\n initialScrollY,\n modalRef,\n theme,\n });\n\n React.useEffect(() => {\n if (dropdownRef.current) {\n scrollToUtils(dropdownRef.current);\n }\n }, [state.query, dropdownRef]);\n\n useRefreshOnInitialQuery({ initialQuery, inputRef, refresh });\n\n // hide the dropdown on idle and no collections\n let showDocsearchDropdown = true;\n const hasCollections = state.collections.some(\n (collection) => collection.items.length > 0\n );\n if (\n state.status === 'idle' &&\n hasCollections === false &&\n isQueryEmpty(state.query)\n ) {\n showDocsearchDropdown = false;\n }\n\n return (\n <ModalShell\n state={state}\n containerRef={containerRef}\n modalRef={modalRef}\n formElementRef={formElementRef}\n dropdownRef={dropdownRef}\n getRootProps={getRootProps}\n showDropdown={showDocsearchDropdown}\n searchBox={\n <KeywordSearchBox\n {...autocomplete}\n state={state}\n placeholder={placeholder || 'Search docs'}\n autoFocus={isQueryEmpty(initialQuery)}\n inputRef={inputRef}\n isFromSelection={\n Boolean(initialQuery) && initialQuery === initialQueryFromSelection\n }\n translations={searchBoxTranslations}\n onClose={onClose}\n />\n }\n filterBar={\n !isQueryEmpty(state.query) ? (\n <FacetBar\n facets={visibleFacets}\n selections={facetSelections}\n translations={facetBarTranslations}\n clearSelections={clearFacetSelections}\n onSelectionChange={handleFacetSelectionChange}\n />\n ) : null\n }\n screenState={\n <ScreenState\n {...autocomplete}\n indexName={defaultIndexName}\n state={state}\n hitComponent={hitComponent}\n resultsFooterComponent={resultsFooterComponent}\n disableUserPersonalization={disableUserPersonalization}\n recentSearches={recentSearches}\n favoriteSearches={favoriteSearches}\n inputRef={inputRef}\n translations={screenStateTranslations}\n getMissingResultsUrl={getMissingResultsUrl}\n hasCollections={hasCollections}\n resultBadgeKey={props.resultBadgeKey}\n onItemClick={(item, event) => {\n sendItemClickEvent(item);\n saveRecentSearch(item);\n if (!isModifierEvent(event)) {\n onClose();\n }\n }}\n />\n }\n footer={\n <Footer translations={footerTranslations} footerAction={footerAction} />\n }\n onClose={onClose}\n />\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"DocSearchModal-CLcbYBea.js","names":[],"sources":["../../src/components/KeywordSearchBox.tsx","../../src/components/KeywordStartScreen.tsx","../../src/ScreenState.tsx","../../src/DocSearchModal.tsx"],"sourcesContent":["import type {\n AutocompleteApi,\n AutocompleteState,\n} from '@algolia/autocomplete-core';\nimport React, { type JSX, type RefObject } from 'react';\n\nimport type { InternalDocSearchHit } from '../types';\n\nimport { SearchBoxForm } from './ui/SearchBoxForm';\n\nexport type KeywordSearchBoxTranslations = Partial<{\n clearButtonTitle: string;\n clearButtonAriaLabel: string;\n closeButtonText: string;\n closeButtonAriaLabel: string;\n placeholderText: string;\n enterKeyHint: string;\n searchInputLabel: string;\n}>;\n\ninterface KeywordSearchBoxProps extends AutocompleteApi<\n InternalDocSearchHit,\n React.FormEvent,\n React.MouseEvent,\n React.KeyboardEvent\n> {\n state: AutocompleteState<InternalDocSearchHit>;\n autoFocus: boolean;\n inputRef: RefObject<HTMLInputElement | null>;\n onClose: () => void;\n placeholder: string;\n isFromSelection: boolean;\n translations?: KeywordSearchBoxTranslations;\n}\n\nexport function KeywordSearchBox({\n translations = {},\n ...props\n}: KeywordSearchBoxProps): JSX.Element {\n const {\n clearButtonTitle = 'Clear',\n clearButtonAriaLabel = 'Clear the query',\n closeButtonText = 'Close',\n closeButtonAriaLabel = 'Close',\n searchInputLabel = 'Search',\n } = translations;\n\n const actionsBeforeClose = (\n <>\n <button\n className=\"DocSearch-Clear\"\n type=\"reset\"\n aria-label={clearButtonAriaLabel}\n hidden={!props.state.query}\n tabIndex={props.state.query ? 0 : -1}\n aria-hidden={!props.state.query ? 'true' : 'false'}\n >\n {clearButtonTitle}\n </button>\n\n {props.state.query && <div className=\"DocSearch-Divider\" />}\n </>\n );\n\n return (\n <SearchBoxForm\n {...props}\n clearButtonTitle={clearButtonTitle}\n clearButtonAriaLabel={clearButtonAriaLabel}\n closeButtonText={closeButtonText}\n closeButtonAriaLabel={closeButtonAriaLabel}\n searchInputLabel={searchInputLabel}\n inputProps={{\n enterKeyHint: 'search',\n }}\n actionsBeforeClose={actionsBeforeClose}\n />\n );\n}\n","import React, { type JSX } from 'react';\n\nimport type { ScreenStateProps } from '../ScreenState';\nimport type { InternalDocSearchHit } from '../types';\n\nimport type { StoredSearchesSectionsTranslations } from './ui/StoredSearchesSections';\nimport { StoredSearchesSections } from './ui/StoredSearchesSections';\n\nexport type KeywordStartScreenTranslations = StoredSearchesSectionsTranslations;\n\ntype KeywordStartScreenProps = Omit<\n ScreenStateProps<InternalDocSearchHit>,\n 'translations'\n> & {\n hasCollections: boolean;\n translations?: KeywordStartScreenTranslations;\n};\n\nexport function KeywordStartScreen({\n translations = {},\n ...props\n}: KeywordStartScreenProps): JSX.Element | null {\n return (\n <div className=\"DocSearch-Dropdown-Container\">\n <StoredSearchesSections {...props} translations={translations} />\n </div>\n );\n}\n","import type {\n AutocompleteApi,\n AutocompleteState,\n BaseItem,\n} from '@algolia/autocomplete-core';\nimport React from 'react';\n\nimport type { KeywordStartScreenTranslations } from './components/KeywordStartScreen';\nimport { KeywordStartScreen } from './components/KeywordStartScreen';\nimport type { DocSearchProps } from './DocSearch';\nimport type { ErrorScreenTranslations } from './ErrorScreen';\nimport { ErrorScreen } from './ErrorScreen';\nimport type { NoResultsScreenTranslations } from './NoResultsScreen';\nimport { NoResultsScreen } from './NoResultsScreen';\nimport type { ResultsScreenTranslations } from './ResultsScreen';\nimport { ResultsScreen } from './ResultsScreen';\nimport type { StoredSearchPlugin } from './stored-searches';\nimport type { InternalDocSearchHit, StoredDocSearchHit } from './types';\nimport { isQueryEmpty } from './utils';\n\nexport type ScreenStateTranslations = Partial<{\n errorScreen: ErrorScreenTranslations;\n startScreen: KeywordStartScreenTranslations;\n noResultsScreen: NoResultsScreenTranslations;\n resultsScreen: ResultsScreenTranslations;\n}>;\n\nexport interface ScreenStateProps<\n TItem extends BaseItem,\n> extends AutocompleteApi<\n TItem,\n React.FormEvent,\n React.MouseEvent,\n React.KeyboardEvent\n> {\n state: AutocompleteState<TItem>;\n recentSearches: StoredSearchPlugin<StoredDocSearchHit>;\n favoriteSearches: StoredSearchPlugin<StoredDocSearchHit>;\n onItemClick: (\n item: InternalDocSearchHit,\n event: KeyboardEvent | MouseEvent\n ) => void;\n inputRef: React.MutableRefObject<HTMLInputElement | null>;\n hitComponent: DocSearchProps['hitComponent'];\n indexName: string;\n disableUserPersonalization: boolean;\n resultsFooterComponent: DocSearchProps['resultsFooterComponent'];\n translations: ScreenStateTranslations;\n getMissingResultsUrl?: DocSearchProps['getMissingResultsUrl'];\n hasCollections: boolean;\n resultBadgeKey?: string;\n}\n\nexport const ScreenState = React.memo(\n ({ translations = {}, ...props }: ScreenStateProps<InternalDocSearchHit>) => {\n if (props.state?.status === 'error') {\n return <ErrorScreen translations={translations?.errorScreen} />;\n }\n\n if (isQueryEmpty(props.state.query)) {\n return (\n <KeywordStartScreen\n {...props}\n hasCollections={props.hasCollections}\n translations={translations?.startScreen}\n />\n );\n }\n\n if (!props.hasCollections) {\n return (\n <NoResultsScreen\n {...props}\n translations={translations?.noResultsScreen}\n />\n );\n }\n\n return (\n <ResultsScreen {...props} translations={translations?.resultsScreen} />\n );\n },\n function areEqual(prevProps, nextProps) {\n const isLoading =\n nextProps.state.status === 'loading' ||\n nextProps.state.status === 'stalled';\n\n const isWaitingForCollections =\n nextProps.state.status === 'idle' &&\n !isQueryEmpty(nextProps.state.query) &&\n prevProps.state.query !== nextProps.state.query &&\n prevProps.state.collections === nextProps.state.collections;\n\n return isLoading || isWaitingForCollections;\n }\n);\n","import { createAutocomplete } from '@algolia/autocomplete-core';\nimport React, { type JSX } from 'react';\n\nimport type { FacetBarTranslations } from './components/FacetBar';\nimport { FacetBar } from './components/FacetBar';\nimport type { KeywordSearchBoxTranslations } from './components/KeywordSearchBox';\nimport { KeywordSearchBox } from './components/KeywordSearchBox';\nimport { ModalShell } from './components/ui/ModalShell';\nimport type { DocSearchProps } from './DocSearch';\nimport type { FooterTranslations } from './Footer';\nimport { Footer } from './Footer';\nimport { Hit } from './Hit';\nimport { useDocSearchFacets } from './hooks/useDocSearchFacets';\nimport { useSendItemClickEvent } from './hooks/useDocSearchInsights';\nimport { useInitialModalQuery } from './hooks/useInitialModalQuery';\nimport { useModalEnvironment } from './hooks/useModalEnvironment';\nimport { useModalRefs } from './hooks/useModalRefs';\nimport { useRefreshOnInitialQuery } from './hooks/useRefreshOnInitialQuery';\nimport { useSaveRecentSearch } from './hooks/useSaveRecentSearch';\nimport { useStoredDocSearches } from './hooks/useStoredDocSearches';\nimport type { ScreenStateTranslations } from './ScreenState';\nimport { ScreenState } from './ScreenState';\nimport type { DocSearchState, InternalDocSearchHit } from './types';\nimport { useSearchClient } from './useSearchClient';\nimport {\n identity,\n isModifierEvent,\n isQueryEmpty,\n noop,\n scrollTo as scrollToUtils,\n} from './utils';\nimport {\n buildNoQuerySources,\n buildQuerySources,\n type BuildQuerySourcesState,\n} from './utils/createDocSearchSources';\nimport { normalizeDocSearchIndexes } from './utils/normalizeDocSearchIndexes';\n\nexport type ModalTranslations = Partial<{\n searchBox: KeywordSearchBoxTranslations;\n footer: FooterTranslations;\n facets: FacetBarTranslations;\n}> &\n ScreenStateTranslations;\n\nexport type DocSearchModalProps = DocSearchProps & {\n initialScrollY: number;\n onClose?: () => void;\n translations?: ModalTranslations;\n};\n\nexport function DocSearchModal({\n appId,\n apiKey,\n maxResultsPerGroup,\n theme,\n onClose = noop,\n transformItems = identity,\n hitComponent = Hit,\n resultsFooterComponent = (): JSX.Element | null => null,\n navigator,\n initialScrollY = 0,\n transformSearchClient = identity,\n disableUserPersonalization = false,\n initialQuery: initialQueryFromProp = '',\n translations = {},\n getMissingResultsUrl,\n insights = false,\n recentSearchesLimit = 7,\n recentSearchesWithFavoritesLimit = 4,\n indices,\n facets,\n footerAction,\n ...props\n}: DocSearchModalProps): JSX.Element {\n const {\n footer: footerTranslations,\n searchBox: searchBoxTranslations,\n facets: facetBarTranslations,\n ...screenStateTranslations\n } = translations;\n const [state, setState] = React.useState<\n DocSearchState<InternalDocSearchHit>\n >({\n query: '',\n collections: [],\n completion: null,\n context: {},\n isOpen: false,\n activeItemId: null,\n status: 'idle',\n });\n\n const placeholder =\n translations?.searchBox?.placeholderText ||\n props.placeholder ||\n 'Search docs';\n\n const { containerRef, modalRef, formElementRef, dropdownRef, inputRef } =\n useModalRefs();\n const { initialQuery, initialQueryFromSelection } =\n useInitialModalQuery(initialQueryFromProp);\n\n const searchClient = useSearchClient(appId, apiKey, transformSearchClient);\n\n const indexes = React.useMemo(\n () =>\n normalizeDocSearchIndexes({\n indices,\n }),\n [indices]\n );\n const defaultIndexName = indexes[0].name;\n\n const autocompleteRef =\n React.useRef<\n ReturnType<\n typeof createAutocomplete<\n InternalDocSearchHit,\n React.FormEvent<HTMLFormElement>,\n React.MouseEvent,\n React.KeyboardEvent\n >\n >\n >(undefined);\n\n const {\n visibleFacets,\n facetSelections,\n facetSelectionsRef,\n handleFacetSelectionChange,\n clearFacetSelections,\n } = useDocSearchFacets({\n facets,\n indexes,\n searchClient,\n onSelectionsChange: () => autocompleteRef.current?.refresh(),\n });\n\n const { favoriteSearches, recentSearches } = useStoredDocSearches({\n defaultIndexName,\n recentSearchesLimit,\n recentSearchesWithFavoritesLimit,\n });\n const saveRecentSearch = useSaveRecentSearch({\n favoriteSearches,\n recentSearches,\n disableUserPersonalization,\n });\n const sendItemClickEvent = useSendItemClickEvent(state);\n\n if (!autocompleteRef.current) {\n autocompleteRef.current = createAutocomplete({\n id: 'docsearch',\n defaultActiveItemId: 0,\n openOnFocus: true,\n initialState: {\n query: initialQuery,\n context: {\n searchSuggestions: [],\n },\n },\n insights: Boolean(insights),\n navigator,\n onStateChange(changes) {\n setState(changes.state);\n },\n getSources({ query, state: sourcesState, setContext, setStatus }) {\n if (isQueryEmpty(query)) {\n const noQuerySources = buildNoQuerySources({\n recentSearches,\n favoriteSearches,\n saveRecentSearch,\n onClose,\n disableUserPersonalization,\n });\n return noQuerySources;\n }\n\n const querySourcesState: BuildQuerySourcesState = {\n context: sourcesState.context,\n };\n\n const algoliaSourcesPromise = buildQuerySources({\n query,\n state: querySourcesState,\n setContext,\n setStatus,\n searchClient,\n indexes,\n insights: Boolean(insights),\n appId,\n apiKey,\n maxResultsPerGroup,\n transformItems,\n saveRecentSearch,\n onClose,\n facetSelections: facetSelectionsRef,\n });\n\n return algoliaSourcesPromise;\n },\n });\n }\n\n const autocomplete = autocompleteRef.current;\n\n const { getEnvironmentProps, getRootProps, refresh } = autocomplete;\n\n useModalEnvironment({\n getEnvironmentProps,\n containerRef,\n dropdownRef,\n formElementRef,\n inputRef,\n initialScrollY,\n modalRef,\n theme,\n });\n\n React.useEffect(() => {\n if (dropdownRef.current) {\n scrollToUtils(dropdownRef.current);\n }\n }, [state.query, dropdownRef]);\n\n useRefreshOnInitialQuery({ initialQuery, inputRef, refresh });\n\n // hide the dropdown on idle and no collections\n let showDocsearchDropdown = true;\n const hasCollections = state.collections.some(\n (collection) => collection.items.length > 0\n );\n if (\n state.status === 'idle' &&\n hasCollections === false &&\n isQueryEmpty(state.query)\n ) {\n showDocsearchDropdown = false;\n }\n\n return (\n <ModalShell\n state={state}\n containerRef={containerRef}\n modalRef={modalRef}\n formElementRef={formElementRef}\n dropdownRef={dropdownRef}\n getRootProps={getRootProps}\n showDropdown={showDocsearchDropdown}\n searchBox={\n <KeywordSearchBox\n {...autocomplete}\n state={state}\n placeholder={placeholder || 'Search docs'}\n autoFocus={isQueryEmpty(initialQuery)}\n inputRef={inputRef}\n isFromSelection={\n Boolean(initialQuery) && initialQuery === initialQueryFromSelection\n }\n translations={searchBoxTranslations}\n onClose={onClose}\n />\n }\n filterBar={\n !isQueryEmpty(state.query) ? (\n <FacetBar\n facets={visibleFacets}\n selections={facetSelections}\n translations={facetBarTranslations}\n clearSelections={clearFacetSelections}\n onSelectionChange={handleFacetSelectionChange}\n />\n ) : null\n }\n screenState={\n <ScreenState\n {...autocomplete}\n indexName={defaultIndexName}\n state={state}\n hitComponent={hitComponent}\n resultsFooterComponent={resultsFooterComponent}\n disableUserPersonalization={disableUserPersonalization}\n recentSearches={recentSearches}\n favoriteSearches={favoriteSearches}\n inputRef={inputRef}\n translations={screenStateTranslations}\n getMissingResultsUrl={getMissingResultsUrl}\n hasCollections={hasCollections}\n resultBadgeKey={props.resultBadgeKey}\n onItemClick={(item, event) => {\n sendItemClickEvent(item);\n saveRecentSearch(item);\n if (!isModifierEvent(event)) {\n onClose();\n }\n }}\n />\n }\n footer={\n <Footer translations={footerTranslations} footerAction={footerAction} />\n }\n onClose={onClose}\n />\n );\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import { d as DocSearchAskAiModalProps, f as DocSearchAskAiModalTranslations, u as DocSearchAskAiModal } from "./DocSearchAI-DKtAqje2.js";
1
+ import { d as DocSearchAskAiModalProps, f as DocSearchAskAiModalTranslations, u as DocSearchAskAiModal } from "./DocSearchAI-f-C2lV8l.js";
2
2
  export { DocSearchAskAiModal, DocSearchAskAiModalProps, DocSearchAskAiModalTranslations };
@@ -1,3 +1,3 @@
1
- import { t as DocSearchAskAiModal } from "./DocSearchAskAiModal-BzQZxvTY.js";
1
+ import { t as DocSearchAskAiModal } from "./DocSearchAskAiModal-CZm9AVU0.js";
2
2
 
3
3
  export { DocSearchAskAiModal };
@@ -1,2 +1,2 @@
1
- import { a as DocSearchProps, c as DocSearchTranslations, i as DocSearchInner, l as HitComponentProps, n as DocSearchFacet, o as DocSearchRef, r as DocSearchIndex, s as DocSearchTransformClient, t as DocSearch, u as ResultsFooterComponentProps } from "./DocSearch-2gNxdJcC.js";
1
+ import { a as DocSearchProps, c as DocSearchTranslations, i as DocSearchInner, l as HitComponentProps, n as DocSearchFacet, o as DocSearchRef, r as DocSearchIndex, s as DocSearchTransformClient, t as DocSearch, u as ResultsFooterComponentProps } from "./DocSearch-BFLV-F2L.js";
2
2
  export { DocSearch, DocSearchFacet, DocSearchIndex, DocSearchInner, DocSearchProps, type DocSearchRef, DocSearchTransformClient, DocSearchTranslations, HitComponentProps, ResultsFooterComponentProps };
@@ -1,7 +1,7 @@
1
1
  import { t as _objectSpread2 } from "./objectSpread2-BNmiQYyW.js";
2
2
  import { t as DocSearchButton } from "./DocSearchButton-CaxxOzQU.js";
3
3
  import { t as _objectWithoutProperties } from "./objectWithoutProperties-phqYxDS0.js";
4
- import { t as DocSearchModal } from "./DocSearchModal-Cgdi4eHY.js";
4
+ import { t as DocSearchModal } from "./DocSearchModal-CLcbYBea.js";
5
5
  import { DocSearch as DocSearch$1, useDocSearch } from "@docsearch/core";
6
6
  import React from "react";
7
7
  import { createPortal } from "react-dom";
@@ -1,3 +1,3 @@
1
1
  import { o as ToolCalls } from "./index-BijiN4hK.js";
2
- import { a as DocSearchAIProps, c as Memory, i as DocSearchAIInner, l as PromptSuggestions, n as AskAiSearchParameters, o as DocSearchAITranslations, r as DocSearchAI, s as DocSearchAskAi, t as AgentStudioSearchParameters } from "./DocSearchAI-DKtAqje2.js";
2
+ import { a as DocSearchAIProps, c as Memory, i as DocSearchAIInner, l as PromptSuggestions, n as AskAiSearchParameters, o as DocSearchAITranslations, r as DocSearchAI, s as DocSearchAskAi, t as AgentStudioSearchParameters } from "./DocSearchAI-f-C2lV8l.js";
3
3
  export { AgentStudioSearchParameters, AskAiSearchParameters, DocSearchAI, DocSearchAIInner, DocSearchAIProps, DocSearchAITranslations, DocSearchAskAi, Memory, PromptSuggestions, type ToolCalls };
@@ -1,7 +1,7 @@
1
1
  import { t as _objectSpread2 } from "./objectSpread2-BNmiQYyW.js";
2
2
  import { t as DocSearchButton } from "./DocSearchButton-CaxxOzQU.js";
3
3
  import { t as _objectWithoutProperties } from "./objectWithoutProperties-phqYxDS0.js";
4
- import { t as DocSearchAskAiModal } from "./DocSearchAskAiModal-BzQZxvTY.js";
4
+ import { t as DocSearchAskAiModal } from "./DocSearchAskAiModal-CZm9AVU0.js";
5
5
  import { DocSearch, useDocSearch } from "@docsearch/core";
6
6
  import React from "react";
7
7
  import { createPortal } from "react-dom";
@@ -1,8 +1,8 @@
1
1
  import { a as StoredDocSearchHit, c as AskAiFeedbackReason, d as DocSearchHit, i as StoredAskAiState, l as OnAskAiFeedback, n as SuggestedQuestionHit, o as ToolCalls, r as StoredAskAiMessage, s as AskAiFeedbackPayload, t as SuggestedQuestion, u as DocSearchState } from "./index-BijiN4hK.js";
2
2
  import { t as DocSearchTheme } from "./DocSearchTheme-DvL4mf0D.js";
3
- import { a as DocSearchProps, b as InternalDocSearchHit, c as DocSearchTranslations, d as DocSearchModal, f as DocSearchModalProps, i as DocSearchInner, l as HitComponentProps, n as DocSearchFacet, o as DocSearchRef, p as ModalTranslations, r as DocSearchIndex, s as DocSearchTransformClient, t as DocSearch, u as ResultsFooterComponentProps } from "./DocSearch-2gNxdJcC.js";
3
+ import { a as DocSearchProps, b as InternalDocSearchHit, c as DocSearchTranslations, d as DocSearchModal, f as DocSearchModalProps, i as DocSearchInner, l as HitComponentProps, n as DocSearchFacet, o as DocSearchRef, p as ModalTranslations, r as DocSearchIndex, s as DocSearchTransformClient, t as DocSearch, u as ResultsFooterComponentProps } from "./DocSearch-BFLV-F2L.js";
4
4
  import { n as useDocSearchKeyboardEvents, r as KeyboardShortcuts, t as UseDocSearchKeyboardEventsProps } from "./useDocSearchKeyboardEvents-B7PKBkuX.js";
5
5
  import { n as DocSearchButton, r as DocSearchButtonProps, t as ButtonTranslations } from "./DocSearchButton-DyJd_QGM.js";
6
- import { a as DocSearchAIProps, c as Memory, d as DocSearchAskAiModalProps, f as DocSearchAskAiModalTranslations, i as DocSearchAIInner, l as PromptSuggestions, n as AskAiSearchParameters, o as DocSearchAITranslations, r as DocSearchAI, s as DocSearchAskAi, t as AgentStudioSearchParameters, u as DocSearchAskAiModal } from "./DocSearchAI-DKtAqje2.js";
6
+ import { a as DocSearchAIProps, c as Memory, d as DocSearchAskAiModalProps, f as DocSearchAskAiModalTranslations, i as DocSearchAIInner, l as PromptSuggestions, n as AskAiSearchParameters, o as DocSearchAITranslations, r as DocSearchAI, s as DocSearchAskAi, t as AgentStudioSearchParameters, u as DocSearchAskAiModal } from "./DocSearchAI-f-C2lV8l.js";
7
7
  import { version } from "./version.js";
8
8
  export { AgentStudioSearchParameters, AskAiFeedbackPayload, AskAiFeedbackReason, AskAiSearchParameters, ButtonTranslations, DocSearch, DocSearchAI, DocSearchAIInner, DocSearchAIProps, DocSearchAITranslations, DocSearchAskAi, DocSearchAskAiModal, DocSearchAskAiModalProps, DocSearchAskAiModalTranslations, DocSearchButton, DocSearchButtonProps, DocSearchFacet, DocSearchHit, DocSearchIndex, DocSearchInner, DocSearchModal, DocSearchModalProps, DocSearchProps, type DocSearchRef, DocSearchState, DocSearchTheme, DocSearchTransformClient, DocSearchTranslations, HitComponentProps, InternalDocSearchHit, KeyboardShortcuts, Memory, ModalTranslations, OnAskAiFeedback, PromptSuggestions, ResultsFooterComponentProps, StoredAskAiMessage, StoredAskAiState, StoredDocSearchHit, SuggestedQuestion, SuggestedQuestionHit, type ToolCalls, UseDocSearchKeyboardEventsProps, useDocSearchKeyboardEvents, version };
package/dist/esm/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { t as DocSearchButton } from "./DocSearchButton-CaxxOzQU.js";
2
- import { t as DocSearchAskAiModal } from "./DocSearchAskAiModal-BzQZxvTY.js";
3
- import { t as DocSearchModal } from "./DocSearchModal-Cgdi4eHY.js";
2
+ import { t as DocSearchAskAiModal } from "./DocSearchAskAiModal-CZm9AVU0.js";
3
+ import { t as DocSearchModal } from "./DocSearchModal-CLcbYBea.js";
4
4
  import { version } from "./version.js";
5
5
  import { DocSearch, DocSearchInner } from "./docsearch.js";
6
6
  import { DocSearchAI, DocSearchAIInner } from "./docsearchAi.js";
@@ -1,2 +1,2 @@
1
- import { d as DocSearchModal, f as DocSearchModalProps, p as ModalTranslations } from "./DocSearch-2gNxdJcC.js";
1
+ import { d as DocSearchModal, f as DocSearchModalProps, p as ModalTranslations } from "./DocSearch-BFLV-F2L.js";
2
2
  export { DocSearchModal, DocSearchModalProps, ModalTranslations };
package/dist/esm/modal.js CHANGED
@@ -1,3 +1,3 @@
1
- import { t as DocSearchModal } from "./DocSearchModal-Cgdi4eHY.js";
1
+ import { t as DocSearchModal } from "./DocSearchModal-CLcbYBea.js";
2
2
 
3
3
  export { DocSearchModal };
@@ -239,18 +239,58 @@ function getFacetLabel(facet) {
239
239
  if (facet.label) return facet.label;
240
240
  return facet.key.replace(/[._-]+/g, " ").replace(/\b\w/g, (letter) => letter.toUpperCase());
241
241
  }
242
+ /**
243
+ * Extracts the facet key from an Algolia `facetFilters` string entry such as
244
+ * `language:en`. Returns `null` when the entry doesn't have a `key:value`
245
+ * shape.
246
+ */
247
+ function getFacetFilterKey(facetFilter) {
248
+ const separatorIndex = facetFilter.indexOf(":");
249
+ return separatorIndex <= 0 ? null : facetFilter.slice(0, separatorIndex);
250
+ }
251
+ function parseFacetFilter(facetFilter) {
252
+ const key = getFacetFilterKey(facetFilter);
253
+ if (key === null) return null;
254
+ const value = facetFilter.slice(key.length + 1);
255
+ return value ? {
256
+ key,
257
+ value
258
+ } : null;
259
+ }
260
+ /**
261
+ * Reads the initial facet selections from the configured `facetFilters` of each
262
+ * index. Later indices override earlier ones for the same facet key.
263
+ *
264
+ * - A `key:value` string selects a single value.
265
+ * - A nested OR group whose entries all share the same key (for example
266
+ * `['docusaurus_tag:default', 'docusaurus_tag:docs-default-current']`)
267
+ * selects every value in the group.
268
+ * - OR groups that mix facet keys can't map to a single facet and are ignored.
269
+ */
242
270
  function deriveDefaultSelectedFacetsFromIndex(indices) {
243
271
  const defaultFacets = {};
244
272
  for (const index of indices) {
245
273
  var _index$searchParamete;
246
274
  const facetFilters = (_index$searchParamete = index.searchParameters) === null || _index$searchParamete === void 0 ? void 0 : _index$searchParamete.facetFilters;
247
275
  for (const facetFilter of Array.isArray(facetFilters) ? facetFilters : [facetFilters]) {
248
- if (typeof facetFilter !== "string") continue;
249
- const separatorIndex = facetFilter.indexOf(":");
250
- if (separatorIndex <= 0) continue;
251
- const key = facetFilter.slice(0, separatorIndex);
252
- const value = facetFilter.slice(separatorIndex + 1);
253
- if (key && value) defaultFacets[key] = value;
276
+ if (typeof facetFilter === "string") {
277
+ const parsed = parseFacetFilter(facetFilter);
278
+ if (parsed) defaultFacets[parsed.key] = [parsed.value];
279
+ continue;
280
+ }
281
+ if (!Array.isArray(facetFilter) || facetFilter.length === 0) continue;
282
+ let groupKey = null;
283
+ const groupValues = [];
284
+ for (const entry of facetFilter) {
285
+ const parsed = typeof entry === "string" ? parseFacetFilter(entry) : null;
286
+ if (!parsed || groupKey !== null && parsed.key !== groupKey) {
287
+ groupKey = null;
288
+ break;
289
+ }
290
+ groupKey = parsed.key;
291
+ groupValues.push(parsed.value);
292
+ }
293
+ if (groupKey !== null) defaultFacets[groupKey] = groupValues;
254
294
  }
255
295
  }
256
296
  return defaultFacets;
@@ -284,8 +324,10 @@ const _excluded2 = [
284
324
  "collisionBoundary",
285
325
  "container"
286
326
  ];
287
- const _excluded3 = ["className", "children"];
288
- const _excluded4 = ["className"];
327
+ const _excluded3 = ["className"];
328
+ const _excluded4 = ["className", "children"];
329
+ const _excluded5 = ["className", "children"];
330
+ const _excluded6 = ["className"];
289
331
  function Menu$1(_ref) {
290
332
  let props = _extends({}, (_objectDestructuringEmpty(_ref), _ref));
291
333
  return /* @__PURE__ */ React.createElement(Menu.Root, props);
@@ -310,13 +352,19 @@ function MenuPopup(_ref3) {
310
352
  }, /* @__PURE__ */ React.createElement(Menu.Popup, _objectSpread2({ className: "DocSearch-Menu-Popup" }, props))));
311
353
  }
312
354
  Menu$1.Popup = MenuPopup;
313
- function MenuRadioGroup(_ref4) {
314
- let props = _extends({}, (_objectDestructuringEmpty(_ref4), _ref4));
355
+ function MenuItem(_ref4) {
356
+ let { className } = _ref4, props = _objectWithoutProperties(_ref4, _excluded3);
357
+ const cn = `DocSearch-Menu-Item${className ? ` ${className}` : ""}`;
358
+ return /* @__PURE__ */ React.createElement(Menu.Item, _objectSpread2({ className: cn }, props));
359
+ }
360
+ Menu$1.Item = MenuItem;
361
+ function MenuRadioGroup(_ref5) {
362
+ let props = _extends({}, (_objectDestructuringEmpty(_ref5), _ref5));
315
363
  return /* @__PURE__ */ React.createElement(Menu.RadioGroup, props);
316
364
  }
317
365
  Menu$1.RadioGroup = MenuRadioGroup;
318
- function MenuRadioItem(_ref5) {
319
- let { className, children } = _ref5, props = _objectWithoutProperties(_ref5, _excluded3);
366
+ function MenuRadioItem(_ref6) {
367
+ let { className, children } = _ref6, props = _objectWithoutProperties(_ref6, _excluded4);
320
368
  const cn = `DocSearch-Menu-Item DocSearch-Menu-RadioItem${className ? ` ${className}` : ""}`;
321
369
  return /* @__PURE__ */ React.createElement(Menu.RadioItem, _objectSpread2({
322
370
  className: cn,
@@ -324,8 +372,14 @@ function MenuRadioItem(_ref5) {
324
372
  }, props), /* @__PURE__ */ React.createElement("span", { className: "DocSearch-Menu-RadioItem-Indicator" }, /* @__PURE__ */ React.createElement(Menu.RadioItemIndicator, null, /* @__PURE__ */ React.createElement(CheckIcon, null))), children);
325
373
  }
326
374
  Menu$1.RadioItem = MenuRadioItem;
327
- function MenuGroupLabel(_ref6) {
328
- let { className } = _ref6, props = _objectWithoutProperties(_ref6, _excluded4);
375
+ function MenuCheckboxItem(_ref7) {
376
+ let { className, children } = _ref7, props = _objectWithoutProperties(_ref7, _excluded5);
377
+ const cn = `DocSearch-Menu-Item DocSearch-Menu-CheckboxItem${className ? ` ${className}` : ""}`;
378
+ return /* @__PURE__ */ React.createElement(Menu.CheckboxItem, _objectSpread2({ className: cn }, props), /* @__PURE__ */ React.createElement("span", { className: "DocSearch-Menu-CheckboxItem-Indicator" }, /* @__PURE__ */ React.createElement(Menu.CheckboxItemIndicator, null, /* @__PURE__ */ React.createElement(CheckIcon, null))), children);
379
+ }
380
+ Menu$1.CheckboxItem = MenuCheckboxItem;
381
+ function MenuGroupLabel(_ref8) {
382
+ let { className } = _ref8, props = _objectWithoutProperties(_ref8, _excluded6);
329
383
  const cn = `DocSearch-Menu-GroupLabel${className ? ` ${className}` : ""}`;
330
384
  return /* @__PURE__ */ React.createElement(Menu.GroupLabel, _objectSpread2({ className: cn }, props));
331
385
  }
@@ -333,34 +387,64 @@ Menu$1.GroupLabel = MenuGroupLabel;
333
387
 
334
388
  //#endregion
335
389
  //#region src/components/FacetBar.tsx
336
- const FacetMenu = React.memo(function FacetMenu({ facet, selectedValue, defaultValueLabel, onSelectionChange, registerTrigger, menuTriggerSelectedAriaLabel }) {
390
+ const EMPTY_SELECTION$1 = [];
391
+ const FacetValueItem = React.memo(function FacetValueItem({ facetKey, value, checked, onToggle }) {
392
+ const handleCheckedChange = React.useCallback(() => {
393
+ onToggle(facetKey, value);
394
+ }, [
395
+ facetKey,
396
+ onToggle,
397
+ value
398
+ ]);
399
+ return /* @__PURE__ */ React.createElement(Menu$1.CheckboxItem, {
400
+ checked,
401
+ label: value,
402
+ onCheckedChange: handleCheckedChange
403
+ }, capitalize(value));
404
+ });
405
+ function toggleValue(value) {
406
+ return (currentValues) => currentValues.includes(value) ? currentValues.filter((selected) => selected !== value) : [...currentValues, value];
407
+ }
408
+ function removeValue(value) {
409
+ return (currentValues) => currentValues.filter((selected) => selected !== value);
410
+ }
411
+ const FacetMenu = React.memo(function FacetMenu({ facet, selectedValues, defaultValueLabel, onSelectionChange, registerTrigger, menuTriggerSelectedAriaLabel }) {
337
412
  const label = getFacetLabel(facet);
338
- const handleValueChange = React.useCallback((value) => {
339
- onSelectionChange(facet.key, value);
413
+ const hasSelection = selectedValues.length > 0;
414
+ const selectedSet = React.useMemo(() => new Set(selectedValues), [selectedValues]);
415
+ const handleToggleValue = React.useCallback((facetKey, value) => {
416
+ onSelectionChange(facetKey, toggleValue(value));
417
+ }, [onSelectionChange]);
418
+ const handleSelectAll = React.useCallback(() => {
419
+ onSelectionChange(facet.key, EMPTY_SELECTION$1);
340
420
  }, [facet.key, onSelectionChange]);
341
421
  const triggerRef = React.useCallback((el) => {
342
422
  registerTrigger(facet.key, el);
343
423
  }, [facet.key, registerTrigger]);
344
424
  return /* @__PURE__ */ React.createElement(Menu$1, null, /* @__PURE__ */ React.createElement(Menu$1.Trigger, {
345
425
  ref: triggerRef,
346
- "data-has-selection": Boolean(selectedValue),
347
- "aria-label": selectedValue ? `${label}, ${selectedValue} ${menuTriggerSelectedAriaLabel}` : label
348
- }, /* @__PURE__ */ React.createElement("span", null, capitalize(label)), /* @__PURE__ */ React.createElement(ChevronIcon, null)), /* @__PURE__ */ React.createElement(Menu$1.Popup, null, /* @__PURE__ */ React.createElement(Menu$1.RadioGroup, {
349
- value: selectedValue,
350
- onValueChange: handleValueChange
351
- }, /* @__PURE__ */ React.createElement(Menu$1.RadioItem, {
352
- value: "",
353
- label: `${defaultValueLabel} ${label}`
354
- }, defaultValueLabel), facet.values.map((value) => /* @__PURE__ */ React.createElement(Menu$1.RadioItem, {
426
+ "data-has-selection": hasSelection,
427
+ "aria-label": hasSelection ? `${label}, ${selectedValues.join(", ")} ${menuTriggerSelectedAriaLabel}` : label
428
+ }, /* @__PURE__ */ React.createElement("span", null, capitalize(label)), /* @__PURE__ */ React.createElement(ChevronIcon, null)), /* @__PURE__ */ React.createElement(Menu$1.Popup, null, /* @__PURE__ */ React.createElement(Menu$1.Item, {
429
+ className: "DocSearch-Menu-ResetItem",
430
+ label: `${defaultValueLabel} ${label}`,
431
+ onClick: handleSelectAll
432
+ }, defaultValueLabel), facet.values.map((value) => /* @__PURE__ */ React.createElement(FacetValueItem, {
355
433
  key: value,
434
+ facetKey: facet.key,
356
435
  value,
357
- label: value
358
- }, capitalize(value))))));
436
+ checked: selectedSet.has(value),
437
+ onToggle: handleToggleValue
438
+ }))));
359
439
  });
360
440
  const SelectedFacetChip = React.memo(function SelectedFacetChip({ facetKey, facetLabel, value, dismissAriaLabel, onDismiss }) {
361
441
  const handleDismissFacet = React.useCallback((e) => {
362
- onDismiss(facetKey, e);
363
- }, [facetKey, onDismiss]);
442
+ onDismiss(facetKey, value, e);
443
+ }, [
444
+ facetKey,
445
+ onDismiss,
446
+ value
447
+ ]);
364
448
  return /* @__PURE__ */ React.createElement(Chip, null, capitalize(value), /* @__PURE__ */ React.createElement(Chip.Dismiss, {
365
449
  "aria-label": `${dismissAriaLabel} ${capitalize(value)} (${facetLabel})`,
366
450
  onClick: handleDismissFacet
@@ -370,14 +454,19 @@ const FacetBar = React.memo(function FacetBar({ facets, selections, onSelectionC
370
454
  const { defaultValueLabel = "All", facetMenuTriggerAriaLabel = "selected", clearAllLabel = "Clear all", facetsAriaLabel = "Search filters", selectedFacetsAriaLabel = "Selected search filters", clearFacetAriaLabel = "Clear filter:" } = translations;
371
455
  const visibleFacetKeys = React.useMemo(() => new Set(facets.map((f) => f.key)), [facets]);
372
456
  const selectionsToDisplay = React.useMemo(() => {
373
- return Object.entries(selections).filter(([key, value]) => Boolean(value) && visibleFacetKeys.has(key));
457
+ const entries = [];
458
+ for (const [key, values] of Object.entries(selections)) {
459
+ if (!visibleFacetKeys.has(key)) continue;
460
+ for (const value of values) entries.push([key, value]);
461
+ }
462
+ return entries;
374
463
  }, [selections, visibleFacetKeys]);
375
464
  const triggerRefs = React.useRef(/* @__PURE__ */ new Map());
376
465
  const registerTrigger = React.useCallback((facetKey, el) => {
377
466
  if (el) triggerRefs.current.set(facetKey, el);
378
467
  else triggerRefs.current.delete(facetKey);
379
468
  }, []);
380
- const handleDismissFacet = React.useCallback((facetKey, ev) => {
469
+ const handleDismissFacet = React.useCallback((facetKey, value, ev) => {
381
470
  const dismissButton = ev.currentTarget;
382
471
  const selectionBar = dismissButton.closest(".DocSearch-FacetSelectionBar");
383
472
  if (selectionBar) {
@@ -387,7 +476,7 @@ const FacetBar = React.memo(function FacetBar({ facets, selections, onSelectionC
387
476
  const target = (_ref = (_dismissButtons = dismissButtons[index + 1]) !== null && _dismissButtons !== void 0 ? _dismissButtons : dismissButtons[index - 1]) !== null && _ref !== void 0 ? _ref : triggerRefs.current.get(facetKey);
388
477
  target === null || target === void 0 || target.focus();
389
478
  }
390
- onSelectionChange(facetKey, "");
479
+ onSelectionChange(facetKey, removeValue(value));
391
480
  }, [onSelectionChange]);
392
481
  const handleClearAll = React.useCallback(() => {
393
482
  var _triggerRefs$current$;
@@ -402,11 +491,10 @@ const FacetBar = React.memo(function FacetBar({ facets, selections, onSelectionC
402
491
  "aria-label": facetsAriaLabel
403
492
  }, facets.map((facet) => {
404
493
  var _selections$facet$key;
405
- const selectedValue = (_selections$facet$key = selections[facet.key]) !== null && _selections$facet$key !== void 0 ? _selections$facet$key : "";
406
494
  return /* @__PURE__ */ React.createElement(FacetMenu, {
407
495
  key: facet.key,
408
496
  facet,
409
- selectedValue,
497
+ selectedValues: (_selections$facet$key = selections[facet.key]) !== null && _selections$facet$key !== void 0 ? _selections$facet$key : EMPTY_SELECTION$1,
410
498
  defaultValueLabel,
411
499
  registerTrigger,
412
500
  menuTriggerSelectedAriaLabel: facetMenuTriggerAriaLabel,
@@ -419,7 +507,7 @@ const FacetBar = React.memo(function FacetBar({ facets, selections, onSelectionC
419
507
  }, selectionsToDisplay.map(([key, value]) => {
420
508
  var _facetLabels$get;
421
509
  return /* @__PURE__ */ React.createElement(SelectedFacetChip, {
422
- key,
510
+ key: `${key}:${value}`,
423
511
  facetKey: key,
424
512
  facetLabel: (_facetLabels$get = facetLabels.get(key)) !== null && _facetLabels$get !== void 0 ? _facetLabels$get : key,
425
513
  value,
@@ -598,6 +686,19 @@ function useFacetValues({ facets, indexes, searchClient }) {
598
686
 
599
687
  //#endregion
600
688
  //#region src/hooks/useDocSearchFacets.ts
689
+ const EMPTY_SELECTION = [];
690
+ /**
691
+ * Compares two selections for a facet. A missing entry and an empty array are
692
+ * both "nothing selected" so clearing an untouched facet doesn't trigger a
693
+ * refresh.
694
+ */
695
+ function areSelectionsEqual(a, b) {
696
+ const left = a !== null && a !== void 0 ? a : EMPTY_SELECTION;
697
+ const right = b !== null && b !== void 0 ? b : EMPTY_SELECTION;
698
+ if (left === right) return true;
699
+ if (left.length !== right.length) return false;
700
+ return left.every((value, index) => value === right[index]);
701
+ }
601
702
  function useDocSearchFacets({ facets, indexes, searchClient, onSelectionsChange }) {
602
703
  const normalizedFacets = React.useMemo(() => normalizeFacets(facets), [facets]);
603
704
  const normalizedFacetsRef = React.useRef(normalizedFacets);
@@ -627,16 +728,23 @@ function useDocSearchFacets({ facets, indexes, searchClient, onSelectionsChange
627
728
  visibleFacets,
628
729
  facetSelections,
629
730
  facetSelectionsRef,
630
- handleFacetSelectionChange: React.useCallback((facet, value) => {
631
- if (facetSelectionsRef.current[facet] === value) return;
632
- const next = _objectSpread2({}, facetSelectionsRef.current);
633
- next[facet] = value;
634
- applySelections(next);
731
+ handleFacetSelectionChange: React.useCallback((facet, update) => {
732
+ const existing = facetSelectionsRef.current[facet];
733
+ const values = typeof update === "function" ? update(existing !== null && existing !== void 0 ? existing : EMPTY_SELECTION) : update;
734
+ if (areSelectionsEqual(existing, values)) return;
735
+ applySelections(_objectSpread2(_objectSpread2({}, facetSelectionsRef.current), {}, { [facet]: values }));
635
736
  }, [applySelections]),
636
737
  clearFacetSelections: React.useCallback(() => {
637
- if (Object.keys(facetSelectionsRef.current).length === 0) return;
738
+ const current = facetSelectionsRef.current;
638
739
  const next = {};
639
- for (const facet of normalizedFacetsRef.current) if (facetSelectionsRef.current[facet.key]) next[facet.key] = "";
740
+ let hasSelectedValues = false;
741
+ for (const facet of normalizedFacetsRef.current) {
742
+ const values = current[facet.key];
743
+ if (values === void 0) continue;
744
+ if (values.length > 0) hasSelectedValues = true;
745
+ next[facet.key] = EMPTY_SELECTION;
746
+ }
747
+ if (!hasSelectedValues) return;
640
748
  applySelections(next);
641
749
  }, [applySelections])
642
750
  };
@@ -1130,21 +1238,31 @@ function ResultsScreen(_ref) {
1130
1238
 
1131
1239
  //#endregion
1132
1240
  //#region src/utils/createDocSearchSources.ts
1241
+ function isFacetFilterOverridden(facetFilter, selectedFacets) {
1242
+ if (typeof facetFilter === "string") {
1243
+ const key = getFacetFilterKey(facetFilter);
1244
+ return key !== null && selectedFacets.has(key);
1245
+ }
1246
+ return facetFilter.length > 0 && facetFilter.every((entry) => isFacetFilterOverridden(entry, selectedFacets));
1247
+ }
1248
+ /**
1249
+ * Merges configured `facetFilters` with the user's facet selections.
1250
+ *
1251
+ * Multiple values selected for the same facet are combined into an OR group
1252
+ * (nested array). Different facets are combined with AND (top-level entries),
1253
+ * following Algolia `facetFilters` semantics.
1254
+ */
1133
1255
  function createFacetFilters(searchParametersFacetFilters, facetSelections) {
1134
1256
  const selections = Object.entries(facetSelections);
1257
+ if (selections.length === 0) return searchParametersFacetFilters;
1135
1258
  const selectedFacets = new Set(selections.map(([facet]) => facet));
1136
1259
  const dynamicFacetFilters = [];
1137
- for (const [facet, selection] of selections) if (selection !== "") dynamicFacetFilters.push(`${facet}:${selection}`);
1138
- if (selectedFacets.size === 0) return searchParametersFacetFilters;
1260
+ for (const [facet, values] of selections) if (values.length === 1) dynamicFacetFilters.push(`${facet}:${values[0]}`);
1261
+ else if (values.length > 1) dynamicFacetFilters.push(values.map((value) => `${facet}:${value}`));
1139
1262
  let configuredFacetFilters = [];
1140
1263
  if (searchParametersFacetFilters && Array.isArray(searchParametersFacetFilters)) configuredFacetFilters = searchParametersFacetFilters;
1141
1264
  else if (searchParametersFacetFilters) configuredFacetFilters = [searchParametersFacetFilters];
1142
- return [...configuredFacetFilters.filter((facetFilter) => {
1143
- if (typeof facetFilter !== "string") return true;
1144
- const separatorIndex = facetFilter.indexOf(":");
1145
- const facet = facetFilter.slice(0, separatorIndex);
1146
- return separatorIndex <= 0 || !selectedFacets.has(facet);
1147
- }), ...dynamicFacetFilters];
1265
+ return [...configuredFacetFilters.filter((facetFilter) => !isFacetFilterOverridden(facetFilter, selectedFacets)), ...dynamicFacetFilters];
1148
1266
  }
1149
1267
  function buildNoQuerySources({ recentSearches, favoriteSearches, saveRecentSearch, onClose, disableUserPersonalization }) {
1150
1268
  if (disableUserPersonalization) return [];
@@ -1268,4 +1386,4 @@ function normalizeDocSearchIndexes({ indices }) {
1268
1386
 
1269
1387
  //#endregion
1270
1388
  export { noop as C, SOURCE_IDS as D, identity as E, getCollection as O, scrollTo as S, isModifierEvent as T, Hit as _, NoResultsScreen as a, SearchBoxForm as b, Results as c, useRefreshOnInitialQuery as d, useModalRefs as f, useDocSearchFacets as g, useSendItemClickEvent as h, ResultsScreen as i, useStoredDocSearches as l, useInitialModalQuery as m, buildNoQuerySources as n, ErrorScreen as o, useModalEnvironment as p, buildQuerySources as r, StoredSearchesSections as s, normalizeDocSearchIndexes as t, useSaveRecentSearch as u, Footer as v, isQueryEmpty as w, FacetBar as x, ModalShell as y };
1271
- //# sourceMappingURL=normalizeDocSearchIndexes-CFHzf-sx.js.map
1389
+ //# sourceMappingURL=normalizeDocSearchIndexes-B_bI_ytC.js.map