@nosto/autocomplete
Version:
Library designed to simplify the implementation of search autocomplete functionality
259 lines (253 loc) • 6.53 kB
TypeScript
import { SearchQuery, InputSearchQuery, SearchResult, SearchOptions } from '@nosto/nosto-js/client';
import * as _nosto_search_js from '@nosto/search-js';
import { HitDecorator, SearchOptions as SearchOptions$1 } from '@nosto/search-js';
export { priceDecorator } from '@nosto/search-js/currencies';
/**
* @group Autocomplete
* @category Core
*/
interface GoogleAnalyticsConfig {
/**
* Path of search page
* @default "/search"
*/
serpPath?: string;
/**
* Search query url parameter name
* @default "query"
*/
queryParamName?: string;
/**
* Enable Google Analytics
* @default true
*/
enabled?: boolean;
}
type Selector = string | Element;
/**
* @group Autocomplete
* @category Core
*/
interface AutocompleteConfig<State> {
/**
* The input element to attach the autocomplete to
*/
inputSelector: Selector;
/**
* The dropdown element to attach the autocomplete to
*/
dropdownSelector: Selector | ((input: HTMLInputElement) => Selector);
/**
* The function to use to render the dropdown
*/
render: (container: HTMLElement, state: State) => void | PromiseLike<void>;
/**
* Minimum length of the query before searching
*/
minQueryLength?: number;
/**
* The function to use to fetch the search state
*/
fetch: SearchQuery | ((input: string) => PromiseLike<State>);
/**
* The function to use to submit the search
*/
submit?: (query: string, config: AutocompleteConfig<State>, options?: SearchAutocompleteOptions) => void;
/**
* Enable history
*/
historyEnabled?: boolean;
/**
* Max number of history items to show
*/
historySize?: number;
/**
* Enable Nosto Analytics
*/
nostoAnalytics?: boolean;
/**
* Google Analytics configuration. Set to `false` to disable.
*/
googleAnalytics?: GoogleAnalyticsConfig | boolean;
/**
* Decorate each search hit before rendering
*
* @example
* ```ts
* import { priceDecorator } from "@nosto/autocomplete"
*
* autocomplete({
* hitDecorators: [
* priceDecorator({ defaultCurrency: "USD" })
* ]
* })
* ```
*/
hitDecorators?: HitDecorator[];
/**
* Whether to submit the form natively or not
*/
nativeSubmit?: boolean;
/**
* A function to call when the user clicks on a search hit to use custom routing
* @example
* ```ts
* autocomplete({
* routingHandler: (url) => {
* location.href = url
* }
* })
* ```
*/
routingHandler?: (url: string) => void;
}
/**
* @group Autocomplete
* @category Core
*/
interface DefaultState {
/**
* The current search query object.
*/
query?: InputSearchQuery;
/**
* The current search response.
*/
response?: SearchResult;
/**
* The history items.
*/
history?: {
item: string;
}[];
}
type AutocompleteInstance = {
/**
* Open the dropdown.
*/
open(): void;
/**
* Close the dropdown.
*/
close(): void;
/**
* Destroy the autocomplete instance.
*/
destroy(): void;
};
type SearchAutocompleteOptions = Pick<SearchOptions, "isKeyword" | "redirect">;
/**
* @param config Autocomplete configuration.
* @returns Autocomplete instance.
* @group Autocomplete
* @category Core
* @example
* ```js
* import { autocomplete } from '@nosto/autocomplete';
*
* // Basic usage
* autocomplete({
* inputSelector: '#search',
* dropdownSelector: '#dropdown',
* render: (container, state) => {
* container.innerHTML = `
* <div>
* <h1>${state.query}</h1>
* <ul>
* ${state.response.products
* .map((product) => `<li>${product.name}</li>`)
* .join('')}
* </ul>
* </div>
* `;
* },
* fetch: {
* products: {
* fields: ['name', 'url', 'imageUrl', 'price', 'listPrice', 'brand'],
* size: 5
* },
* keywords: {
* size: 5,
* fields: ['keyword', '_highlight.keyword'],
* highlight: {
* preTag: `<strong>`,
* postTag: '</strong>'
* }
* }
* }
* });
*
* // Liquid template.
* import { autocomplete, fromRemoteLiquidTemplate, fromLiquidTemplate, defaultLiquidTemplate } from '@nosto/autocomplete/liquid';
*
* autocomplete({
* inputSelector: '#search',
* dropdownSelector: '#dropdown',
* render: fromRemoteLiquidTemplate('autocomplete.liquid'),
* // Or:
* render: window.nostoAutocomplete.fromLiquidTemplate(defaultLiquidTemplate),
* ...
* });
*
* // Mustache template.
* import { autocomplete, fromRemoteMustacheTemplate, fromMustacheTemplate, defaultMustacheTemplate } from '@nosto/autocomplete/mustache';
*
* autocomplete({
* inputSelector: '#search',
* dropdownSelector: '#dropdown',
* render: fromRemoteMustacheTemplate('autocomplete.mustache'),
* // Or:
* render: fromMustacheTemplate(defaultMustacheTemplate),
* ...
* });
*
* // React example.
* import { autocomplete, Autocomplete } from '@nosto/autocomplete/react';
* import ReactDOM from 'react-dom';
*
* let reactRoot;
*
* autocomplete({
* inputSelector: '#search',
* dropdownSelector: '#dropdown',
* render: function (container, state) {
* if (!reactRoot) {
* reactRoot = ReactDOM.createRoot(container);
* }
* reactRoot.render(<Autocomplete {...state} />);
* },
* ...
* });
* ```
*/
declare function autocomplete<State = DefaultState>(config: AutocompleteConfig<State>): AutocompleteInstance;
/**
*
* @param query Query object.
* @param options Options object.
* @returns Promise of search response.
* @group Autocomplete
* @category Core
* @example
* ```js
* import { search } from "@nosto/autocomplete"
*
* search({
* query: "shoes",
* products: {
* fields: ["name", "price"],
* facets: ["brand", "category"],
* size: 10,
* from: 0,
* }
* }).then((state) => {
* console.log(state.response)
* })
* ```
*/
declare function search(query: SearchQuery, options?: SearchOptions$1): Promise<{
query: SearchQuery;
response: _nosto_search_js.DecoratedResult<readonly _nosto_search_js.HitDecorator[]>;
}>;
export { autocomplete, search };
export type { AutocompleteConfig, AutocompleteInstance, DefaultState, GoogleAnalyticsConfig, SearchAutocompleteOptions, Selector };