angular-instantsearch
Version:
Lightning-fast search for Angular apps, by Algolia.
2,956 lines • 96.5 kB
JavaScript
import { Input, EventEmitter, isDevMode, VERSION as VERSION$1, Component, Inject, PLATFORM_ID, Output, SkipSelf, forwardRef, Optional, NgModule, ChangeDetectionStrategy, ContentChild, TemplateRef, ViewChild, KeyValueDiffers, NgZone } from '@angular/core';
import { isPlatformBrowser, CommonModule, DOCUMENT } from '@angular/common';
import { connectBreadcrumb, connectClearRefinements, connectCurrentRefinements, connectHierarchicalMenu, connectHitsPerPage, connectHitsWithInsights, connectInfiniteHitsWithInsights, connectMenu, connectNumericMenu, connectPagination, connectRange, connectRefinementList, connectSearchBox, connectSortBy, connectRatingMenu, connectStats, connectToggleRefinement, connectConfigure, EXPERIMENTAL_connectConfigureRelatedItems, connectQueryRules, connectVoiceSearch } from 'instantsearch.js/es/connectors';
import * as algoliasearchProxy from 'algoliasearch/lite';
import instantsearch from 'instantsearch.js/es';
import indexWidget from 'instantsearch.js/es/widgets/index/index';
import { highlight, reverseHighlight, snippet, reverseSnippet } from 'instantsearch.js/es/helpers';
import { getPropertyByPath as getPropertyByPath$1 } from 'instantsearch.js/es/lib/utils';
import * as noUiSlider from 'nouislider';
import * as encodeProxy from 'querystring-es3/encode';
function bem(widgetName) {
const cx = function (element, subElement) {
let cssClass = `ais-${widgetName}`;
if (element) {
cssClass += `-${element}`;
}
if (subElement) {
cssClass += `--${subElement}`;
}
return cssClass;
};
return cx;
}
function parseNumberInput(input) {
return typeof input === 'string' ? parseInt(input, 10) : input;
}
function noop(...args) { }
function capitalize(s) {
return s.charAt(0).toUpperCase() + s.slice(1);
}
// See https://github.com/algolia/instantsearch.js/blob/9296022fecadfbf82f15e837c215a1356eac4bc5/src/lib/utils/range.ts
function range({ start = 0, end, step = 1, }) {
// We can't divide by 0 so we re-assign the step to 1 if it happens.
const limitStep = step === 0 ? 1 : step;
// In some cases the array to create has a decimal length.
// We therefore need to round the value.
// Example:
// { start: 1, end: 5000, step: 500 }
// => Array length = (5000 - 1) / 500 = 9.998
const arrayLength = Math.round((end - start) / limitStep);
return [...Array(arrayLength)].map((_, current) => start + current * limitStep);
}
// See https://github.com/algolia/react-instantsearch/blob/86dfe8674d566124af55a8f044051d0062786c1a/packages/react-instantsearch-core/src/core/utils.ts#L138-L142
function getPropertyByPath(object, path) {
return path
.replace(/\[(\d+)]/g, '.$1')
.split('.')
.reduce((current, key) => (current ? current[key] : undefined), object);
}
class TypedBaseWidget {
constructor(widgetName) {
this.updateState = (state, isFirstRendering) => {
if (isFirstRendering) {
Promise.resolve().then(() => {
this.state = state;
});
}
else {
this.state = state;
}
};
this.cx = bem(widgetName);
}
get parent() {
if (this.parentIndex) {
return this.parentIndex;
}
return this.instantSearchInstance;
}
createWidget(connector, options, additionalWidgetProperties = {}) {
this.widget = Object.assign(Object.assign({}, connector(this.updateState, noop)(options)), additionalWidgetProperties);
}
ngOnInit() {
this.parent.addWidgets([this.widget]);
}
ngOnDestroy() {
if (isPlatformBrowser(this.instantSearchInstance.platformId)) {
this.parent.removeWidgets([this.widget]);
}
}
/**
* Helper to generate class names for an item
* @param item element to generate a class name for
*/
getItemClass(item) {
const className = this.cx('item');
if (item.isRefined) {
return `${className} ${this.cx('item', 'selected')}`;
}
return className;
}
}
TypedBaseWidget.propDecorators = {
autoHideContainer: [{ type: Input }]
};
const VERSION = '4.4.3';
// this is needed for different webpack/typescript configurations
const algoliasearch$1 = algoliasearchProxy.default || algoliasearchProxy;
class NgAisInstantSearch {
constructor(platformId) {
this.platformId = platformId;
this.instanceName = 'default';
this.change = new EventEmitter();
this.onRender = () => {
this.change.emit({
results: this.instantSearchInstance.helper.lastResults,
state: this.instantSearchInstance.helper.state,
});
};
}
ngOnInit() {
if (isDevMode()) {
console.warn(`We are deprecating Angular InstantSearch.
For more information and alternative solutions, go to https://alg.li/angular-deprecation.`);
}
if (typeof this.config.searchClient.addAlgoliaAgent === 'function') {
this.config.searchClient.addAlgoliaAgent(`angular (${VERSION$1.full})`);
this.config.searchClient.addAlgoliaAgent(`angular-instantsearch (${VERSION})`);
}
this.instantSearchInstance = instantsearch(this.config);
this.instantSearchInstance.on('render', this.onRender);
}
ngAfterViewInit() {
this.instantSearchInstance.start();
}
ngOnDestroy() {
if (this.instantSearchInstance) {
this.instantSearchInstance.removeListener('render', this.onRender);
this.instantSearchInstance.dispose();
}
}
addWidgets(widgets) {
this.instantSearchInstance.addWidgets(widgets);
}
removeWidgets(widgets) {
this.instantSearchInstance.removeWidgets(widgets);
}
refresh() {
this.instantSearchInstance.refresh();
}
}
NgAisInstantSearch.decorators = [
{ type: Component, args: [{
selector: 'ais-instantsearch',
template: '<ng-content></ng-content>'
},] }
];
NgAisInstantSearch.ctorParameters = () => [
{ type: Object, decorators: [{ type: Inject, args: [PLATFORM_ID,] }] }
];
NgAisInstantSearch.propDecorators = {
config: [{ type: Input }],
instanceName: [{ type: Input }],
change: [{ type: Output }]
};
class NgAisIndex {
constructor(
// public API does not include SkipSelf, but the index widget should accept parents, avoiding itself.
parentIndex, instantSearchInstance) {
this.parentIndex = parentIndex;
this.instantSearchInstance = instantSearchInstance;
}
get parent() {
if (this.parentIndex) {
return this.parentIndex;
}
return this.instantSearchInstance;
}
createWidget() {
this.widget = Object.assign(Object.assign({}, indexWidget({
indexName: this.indexName,
indexId: this.indexId,
})), { $$widgetType: 'ais.index' });
}
addWidgets(widgets) {
this.widget.addWidgets(widgets);
}
removeWidgets(widgets) {
this.widget.removeWidgets(widgets);
}
ngOnInit() {
this.createWidget();
this.parent.addWidgets([this.widget]);
}
ngOnDestroy() {
if (isPlatformBrowser(this.instantSearchInstance.platformId)) {
this.parent.removeWidgets([this.widget]);
}
}
}
NgAisIndex.decorators = [
{ type: Component, args: [{
selector: 'ais-index',
template: `<ng-content></ng-content>`
},] }
];
NgAisIndex.ctorParameters = () => [
{ type: NgAisIndex, decorators: [{ type: SkipSelf }, { type: Inject, args: [forwardRef(() => NgAisIndex),] }, { type: Optional }] },
{ type: NgAisInstantSearch, decorators: [{ type: Inject, args: [forwardRef(() => NgAisInstantSearch),] }] }
];
NgAisIndex.propDecorators = {
indexName: [{ type: Input }],
indexId: [{ type: Input }]
};
class NgAisBreadcrumb extends TypedBaseWidget {
constructor(parentIndex, instantSearchInstance) {
super('Breadcrumb');
this.parentIndex = parentIndex;
this.instantSearchInstance = instantSearchInstance;
this.state = {
createURL: () => '#',
items: [],
refine: noop,
canRefine: false,
};
}
get isHidden() {
return this.state.items.length === 0 && this.autoHideContainer;
}
get items() {
return this.state.items.map((item, idx) => (Object.assign(Object.assign({}, item), { separator: idx !== 0, isLast: idx === this.state.items.length - 1 })));
}
ngOnInit() {
this.createWidget(connectBreadcrumb, {
attributes: this.attributes,
rootPath: this.rootPath,
separator: this.separator,
transformItems: this.transformItems,
}, {
$$widgetType: 'ais.breadcrumb',
});
super.ngOnInit();
}
handleClick(event, item) {
event.preventDefault();
event.stopPropagation();
if (item.value) {
this.state.refine(item.value);
}
}
}
NgAisBreadcrumb.decorators = [
{ type: Component, args: [{
selector: 'ais-breadcrumb',
template: `
<div
[class]="cx()"
*ngIf="!isHidden"
>
<ul [class]="cx('list')">
<li
*ngFor="let item of items"
[ngClass]="[cx('item'), item.isLast ? cx('item', 'selected') : '']"
(click)="handleClick($event, item)"
>
<span
*ngIf="item.separator"
[class]="cx('separator')"
aria-hidden="true"
>
>
</span>
<a
[class]="cx('link')"
href="{{state.createURL(item.value)}}"
*ngIf="!item.isLast"
(click)="handleClick($event, item)"
>
{{item.label}}
</a>
<span *ngIf="item.isLast">
{{item.label}}
</span>
</li>
</ul>
</div>
`
},] }
];
NgAisBreadcrumb.ctorParameters = () => [
{ type: NgAisIndex, decorators: [{ type: Inject, args: [forwardRef(() => NgAisIndex),] }, { type: Optional }] },
{ type: NgAisInstantSearch, decorators: [{ type: Inject, args: [forwardRef(() => NgAisInstantSearch),] }] }
];
NgAisBreadcrumb.propDecorators = {
attributes: [{ type: Input }],
rootPath: [{ type: Input }],
separator: [{ type: Input }],
transformItems: [{ type: Input }]
};
class NgAisBreadcrumbModule {
}
NgAisBreadcrumbModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgAisBreadcrumb],
entryComponents: [NgAisBreadcrumb],
exports: [NgAisBreadcrumb],
imports: [CommonModule],
},] }
];
class NgAisClearRefinements extends TypedBaseWidget {
constructor(parentIndex, instantSearchInstance) {
super('ClearRefinements');
this.parentIndex = parentIndex;
this.instantSearchInstance = instantSearchInstance;
// rendering options
this.resetLabel = 'Clear refinements';
this.state = {
hasRefinements: false,
canRefine: false,
refine: noop,
createURL: () => '#',
};
}
get isHidden() {
return !this.state.hasRefinements && this.autoHideContainer;
}
ngOnInit() {
this.createWidget(connectClearRefinements, {
includedAttributes: this.includedAttributes,
excludedAttributes: this.excludedAttributes,
transformItems: this.transformItems,
}, {
$$widgetType: 'ais.clearRefinements',
});
super.ngOnInit();
}
handleClick(event) {
event.preventDefault();
if (this.state.hasRefinements) {
this.state.refine();
}
}
}
NgAisClearRefinements.decorators = [
{ type: Component, args: [{
selector: 'ais-clear-refinements',
template: `
<div
[class]="cx()"
*ngIf="!isHidden"
>
<button
[class]="cx('button') + (!state.hasRefinements ? (' ' + cx('button', 'disabled')) : '')"
(click)="handleClick($event)"
[disabled]="!state.hasRefinements"
>
{{resetLabel}}
</button>
</div>
`
},] }
];
NgAisClearRefinements.ctorParameters = () => [
{ type: NgAisIndex, decorators: [{ type: Inject, args: [forwardRef(() => NgAisIndex),] }, { type: Optional }] },
{ type: NgAisInstantSearch, decorators: [{ type: Inject, args: [forwardRef(() => NgAisInstantSearch),] }] }
];
NgAisClearRefinements.propDecorators = {
resetLabel: [{ type: Input }],
includedAttributes: [{ type: Input }],
excludedAttributes: [{ type: Input }],
transformItems: [{ type: Input }]
};
class NgAisClearRefinementsModule {
}
NgAisClearRefinementsModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgAisClearRefinements],
entryComponents: [NgAisClearRefinements],
exports: [NgAisClearRefinements],
imports: [CommonModule],
},] }
];
class NgAisCurrentRefinements extends TypedBaseWidget {
constructor(parentIndex, instantSearchInstance) {
super('CurrentRefinements');
this.parentIndex = parentIndex;
this.instantSearchInstance = instantSearchInstance;
this.state = {
createURL: () => '#',
refine: noop,
items: [],
canRefine: false,
};
}
get isHidden() {
return this.state.items.length === 0 && this.autoHideContainer;
}
ngOnInit() {
this.createWidget(connectCurrentRefinements, {
includedAttributes: this.includedAttributes,
excludedAttributes: this.excludedAttributes,
transformItems: this.transformItems,
}, {
$$widgetType: 'ais.currentRefinements',
});
super.ngOnInit();
}
handleClick(event, refinement) {
event.preventDefault();
this.state.refine(refinement);
}
}
NgAisCurrentRefinements.decorators = [
{ type: Component, args: [{
selector: 'ais-current-refinements',
template: `
<div
[class]="cx()"
*ngIf="!isHidden"
>
<ul
[class]="cx('list')"
*ngFor="let item of state.items"
>
<li [class]="cx('item')">
<span [class]="cx('label')">{{item.label | titlecase}}:</span>
<span
[class]="cx('category')"
*ngFor="let refinement of item.refinements"
>
<span [class]="cx('categoryLabel')">{{refinement.label}}</span>
<button [class]="cx('delete')" (click)="handleClick($event, refinement)">✕</button>
</span>
</li>
</ul>
</div>
`
},] }
];
NgAisCurrentRefinements.ctorParameters = () => [
{ type: NgAisIndex, decorators: [{ type: Inject, args: [forwardRef(() => NgAisIndex),] }, { type: Optional }] },
{ type: NgAisInstantSearch, decorators: [{ type: Inject, args: [forwardRef(() => NgAisInstantSearch),] }] }
];
NgAisCurrentRefinements.propDecorators = {
includedAttributes: [{ type: Input }],
excludedAttributes: [{ type: Input }],
transformItems: [{ type: Input }]
};
class NgAisCurrentRefinementsModule {
}
NgAisCurrentRefinementsModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgAisCurrentRefinements],
entryComponents: [NgAisCurrentRefinements],
exports: [NgAisCurrentRefinements],
imports: [CommonModule],
},] }
];
class NgAisHierarchicalMenu extends TypedBaseWidget {
constructor(parentIndex, instantSearchInstance) {
super('HierarchicalMenu');
this.parentIndex = parentIndex;
this.instantSearchInstance = instantSearchInstance;
this.state = {
createURL: () => '#',
items: [],
refine: noop,
canRefine: false,
isShowingMore: false,
toggleShowMore: noop,
canToggleShowMore: false,
sendEvent: noop,
};
}
get isHidden() {
return this.state.items.length === 0 && this.autoHideContainer;
}
ngOnInit() {
this.createWidget(connectHierarchicalMenu, {
limit: parseNumberInput(this.limit),
attributes: this.attributes,
rootPath: this.rootPath,
separator: this.separator,
showParentLevel: this.showParentLevel,
sortBy: this.sortBy,
transformItems: this.transformItems,
}, {
$$widgetType: 'ais.hierarchicalMenu',
});
super.ngOnInit();
}
}
NgAisHierarchicalMenu.decorators = [
{ type: Component, args: [{
selector: 'ais-hierarchical-menu',
template: `
<div
[class]="cx()"
*ngIf="!isHidden"
>
<ul [class]="cx('list') + ' ' + cx('list', 'lvl0')">
<ais-hierarchical-menu-item
*ngFor="let item of state.items"
[item]="item"
[createURL]="state.createURL"
[refine]="state.refine"
>
</ais-hierarchical-menu-item>
</ul>
</div>
`
},] }
];
NgAisHierarchicalMenu.ctorParameters = () => [
{ type: NgAisIndex, decorators: [{ type: Inject, args: [forwardRef(() => NgAisIndex),] }, { type: Optional }] },
{ type: NgAisInstantSearch, decorators: [{ type: Inject, args: [forwardRef(() => NgAisInstantSearch),] }] }
];
NgAisHierarchicalMenu.propDecorators = {
attributes: [{ type: Input }],
separator: [{ type: Input }],
rootPath: [{ type: Input }],
showParentLevel: [{ type: Input }],
limit: [{ type: Input }],
sortBy: [{ type: Input }],
transformItems: [{ type: Input }]
};
class NgAisHierarchicalMenuItem {
constructor() {
this.lvl = 1;
this.cx = bem('HierarchicalMenu');
}
getItemClass(item) {
let className = this.cx('item');
if (item.isRefined) {
className = `${className} ${this.cx('item', 'selected')}`;
}
if (this.isArray(item.data) && item.data.length > 0) {
className = `${className} ${this.cx('item', 'parent')}`;
}
return className;
}
getListClass() {
return `${this.cx('list')} ${this.cx('list', 'child')} ${this.cx('list', `lvl${this.lvl}`)}`;
}
isArray(potentialArray) {
return Array.isArray(potentialArray);
}
handleClick(event, item) {
event.preventDefault();
event.stopPropagation();
this.refine(item.value);
}
}
NgAisHierarchicalMenuItem.decorators = [
{ type: Component, args: [{
selector: 'ais-hierarchical-menu-item',
template: `
<li
[class]="getItemClass(item)"
(click)="handleClick($event, item)"
>
<a
[class]="cx('link')"
href="{{createURL(item.value)}}"
(click)="handleClick($event, item)"
>
<span [class]="cx('label')">{{item.label}}</span>
<span [class]="cx('count')">{{item.count}}</span>
</a>
<ul
[class]="getListClass()"
*ngIf="item.isRefined && isArray(item.data) && item.data.length > 0"
>
<ais-hierarchical-menu-item
*ngFor="let child of item.data"
[item]="child"
[createURL]="createURL"
[refine]="refine"
[lvl]="lvl + 1"
>
</ais-hierarchical-menu-item>
</ul>
</li>
`
},] }
];
NgAisHierarchicalMenuItem.propDecorators = {
lvl: [{ type: Input }],
refine: [{ type: Input }],
createURL: [{ type: Input }],
item: [{ type: Input }]
};
class NgAisHierarchicalMenuModule {
}
NgAisHierarchicalMenuModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgAisHierarchicalMenu, NgAisHierarchicalMenuItem],
entryComponents: [NgAisHierarchicalMenu],
exports: [NgAisHierarchicalMenu],
imports: [CommonModule],
},] }
];
class NgAisHitsPerPage extends TypedBaseWidget {
constructor(parentIndex, instantSearchInstance) {
super('HitsPerPage');
this.parentIndex = parentIndex;
this.instantSearchInstance = instantSearchInstance;
this.state = {
items: [],
refine: noop,
hasNoResults: true,
canRefine: false,
};
}
get isHidden() {
return this.state.items.length === 0 && this.autoHideContainer;
}
ngOnInit() {
this.createWidget(connectHitsPerPage, {
items: this.items,
transformItems: this.transformItems,
}, {
$$widgetType: 'ais.hitsPerPage',
});
super.ngOnInit();
}
}
NgAisHitsPerPage.decorators = [
{ type: Component, args: [{
selector: 'ais-hits-per-page',
template: `
<div
[class]="cx()"
*ngIf="!isHidden"
>
<select
[class]="cx('select')"
(change)="state.refine($event.target.value)"
>
<option
[class]="cx('option')"
*ngFor="let item of state.items"
[value]="item.value"
[selected]="item.isRefined"
>
{{item.label}}
</option>
</select>
</div>
`
},] }
];
NgAisHitsPerPage.ctorParameters = () => [
{ type: NgAisIndex, decorators: [{ type: Inject, args: [forwardRef(() => NgAisIndex),] }, { type: Optional }] },
{ type: NgAisInstantSearch, decorators: [{ type: Inject, args: [forwardRef(() => NgAisInstantSearch),] }] }
];
NgAisHitsPerPage.propDecorators = {
items: [{ type: Input }],
transformItems: [{ type: Input }]
};
class NgAisHitsPerPageModule {
}
NgAisHitsPerPageModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgAisHitsPerPage],
entryComponents: [NgAisHitsPerPage],
exports: [NgAisHitsPerPage],
imports: [CommonModule],
},] }
];
class NgAisHighlight {
constructor() {
this.tagName = 'mark';
}
get content() {
const highlightAttributeResult = getPropertyByPath$1(this.hit._highlightResult, this.attribute);
const fallback = getPropertyByPath$1(this.hit, this.attribute);
// @MAJOR drop this custom fallback once it is implemented directly in instantsearch.js v5
if (!highlightAttributeResult && fallback) {
return fallback;
}
return highlight({
attribute: this.attribute,
highlightedTagName: this.tagName,
hit: this.hit,
});
}
}
NgAisHighlight.decorators = [
{ type: Component, args: [{
selector: 'ais-highlight',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `<span class="ais-Highlight" [innerHtml]="content"></span>`
},] }
];
NgAisHighlight.propDecorators = {
attribute: [{ type: Input }],
hit: [{ type: Input }],
tagName: [{ type: Input }]
};
class NgAisHighlightModule {
}
NgAisHighlightModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgAisHighlight],
entryComponents: [NgAisHighlight],
exports: [NgAisHighlight],
imports: [CommonModule],
},] }
];
class NgAisHits extends TypedBaseWidget {
constructor(parentIndex, instantSearchInstance) {
super('Hits');
this.parentIndex = parentIndex;
this.instantSearchInstance = instantSearchInstance;
this.state = {
hits: [],
results: undefined,
bindEvent: undefined,
sendEvent: undefined,
};
this.updateState = (state, isFirstRendering) => {
if (isFirstRendering)
return;
this.state = state;
};
}
ngOnInit() {
this.createWidget(connectHitsWithInsights, {
escapeHTML: this.escapeHTML,
transformItems: this.transformItems,
}, {
$$widgetType: 'ais.hits',
});
super.ngOnInit();
}
}
NgAisHits.decorators = [
{ type: Component, args: [{
selector: 'ais-hits',
template: `
<div [class]="cx()">
<ng-container *ngTemplateOutlet="template; context: state"></ng-container>
<!-- default rendering if no template specified -->
<div *ngIf="!template">
<ul [class]="cx('list')">
<li
[class]="cx('item')"
*ngFor="let hit of state.hits"
>
<ais-highlight attribute="name" [hit]="hit">
</ais-highlight>
</li>
</ul>
</div>
</div>
`
},] }
];
NgAisHits.ctorParameters = () => [
{ type: NgAisIndex, decorators: [{ type: Inject, args: [forwardRef(() => NgAisIndex),] }, { type: Optional }] },
{ type: NgAisInstantSearch, decorators: [{ type: Inject, args: [forwardRef(() => NgAisInstantSearch),] }] }
];
NgAisHits.propDecorators = {
template: [{ type: ContentChild, args: [TemplateRef, { static: false },] }],
escapeHTML: [{ type: Input }],
transformItems: [{ type: Input }]
};
class NgAisHitsModule {
}
NgAisHitsModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgAisHits],
entryComponents: [NgAisHits],
exports: [NgAisHits],
imports: [CommonModule, NgAisHighlightModule],
},] }
];
class NgAisIndexModule {
static forRoot() {
return {
ngModule: NgAisIndexModule,
providers: [],
};
}
}
NgAisIndexModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgAisIndex],
entryComponents: [NgAisIndex],
exports: [NgAisIndex],
imports: [CommonModule],
},] }
];
class NgAisInfiniteHits extends TypedBaseWidget {
constructor(parentIndex, instantSearchInstance) {
super('InfiniteHits');
this.parentIndex = parentIndex;
this.instantSearchInstance = instantSearchInstance;
this.showPrevious = false;
this.showPreviousLabel = 'Show previous results';
this.showMoreLabel = 'Show more results';
this.state = {
hits: [],
results: undefined,
currentPageHits: [],
isFirstPage: false,
isLastPage: false,
showMore: noop,
showPrevious: noop,
sendEvent: noop,
bindEvent: () => '',
};
this.updateState = (state, isFirstRendering) => {
if (isFirstRendering)
return;
this.state = state;
};
}
ngOnInit() {
this.createWidget(connectInfiniteHitsWithInsights, {
escapeHTML: this.escapeHTML,
transformItems: this.transformItems,
}, {
$$widgetType: 'ais.infiniteHits',
});
super.ngOnInit();
}
showMoreHandler(event) {
event.preventDefault();
this.state.showMore();
}
showPreviousHandler(event) {
event.preventDefault();
this.state.showPrevious();
}
}
NgAisInfiniteHits.decorators = [
{ type: Component, args: [{
selector: 'ais-infinite-hits',
template: `
<div [class]="cx()">
<ng-container *ngTemplateOutlet="template; context: state"></ng-container>
<!-- default rendering if no template specified -->
<button
[ngClass]="[cx('loadPrevious'), this.state.isFirstPage ? cx('loadPrevious', 'disabled') : '']"
(click)="showPreviousHandler($event)"
[disabled]="state.isFirstPage"
*ngIf="showPrevious && !template"
>
{{showPreviousLabel}}
</button>
<div *ngIf="!template">
<ul [class]="cx('list')">
<li
[class]="cx('item')"
*ngFor="let hit of state.hits"
>
<ais-highlight attribute="name" [hit]="hit">
</ais-highlight>
</li>
</ul>
</div>
<button
[ngClass]="[cx('loadMore'), this.state.isLastPage ? cx('loadMore', 'disabled') : '']"
(click)="showMoreHandler($event)"
[disabled]="state.isLastPage"
*ngIf="!template"
>
{{showMoreLabel}}
</button>
</div>
`
},] }
];
NgAisInfiniteHits.ctorParameters = () => [
{ type: NgAisIndex, decorators: [{ type: Inject, args: [forwardRef(() => NgAisIndex),] }, { type: Optional }] },
{ type: NgAisInstantSearch, decorators: [{ type: Inject, args: [forwardRef(() => NgAisInstantSearch),] }] }
];
NgAisInfiniteHits.propDecorators = {
template: [{ type: ContentChild, args: [TemplateRef, { static: false },] }],
escapeHTML: [{ type: Input }],
showPrevious: [{ type: Input }],
showPreviousLabel: [{ type: Input }],
showMoreLabel: [{ type: Input }],
transformItems: [{ type: Input }]
};
class NgAisInfiniteHitsModule {
}
NgAisInfiniteHitsModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgAisInfiniteHits],
entryComponents: [NgAisInfiniteHits],
exports: [NgAisInfiniteHits],
imports: [CommonModule, NgAisHighlightModule],
},] }
];
class NgAisInstantSearchModule {
static forRoot() {
return {
ngModule: NgAisInstantSearchModule,
providers: [],
};
}
}
NgAisInstantSearchModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgAisInstantSearch],
entryComponents: [NgAisInstantSearch],
exports: [NgAisInstantSearch],
imports: [CommonModule],
},] }
];
class NgAisMenu extends TypedBaseWidget {
constructor(parentIndex, instantSearchInstance) {
super('Menu');
this.parentIndex = parentIndex;
this.instantSearchInstance = instantSearchInstance;
// rendering options
this.showMoreLabel = 'Show more';
this.showLessLabel = 'Show less';
this.state = {
items: [],
refine: noop,
createURL: () => '#',
canRefine: false,
isShowingMore: false,
canToggleShowMore: false,
toggleShowMore: noop,
sendEvent: noop,
};
}
get isHidden() {
return this.state.items.length === 0 && this.autoHideContainer;
}
get showMoreClass() {
let className = this.cx('showMore');
if (!this.state.canToggleShowMore) {
className = `${className} ${this.cx('showMore', 'disabled')}`;
}
return className;
}
ngOnInit() {
this.createWidget(connectMenu, {
attribute: this.attribute,
showMore: this.showMore,
limit: this.limit,
showMoreLimit: this.showMoreLimit,
sortBy: this.sortBy,
transformItems: this.transformItems,
}, {
$$widgetType: 'ais.menu',
});
super.ngOnInit();
}
handleClick(event, value) {
event.preventDefault();
event.stopPropagation();
this.state.refine(value);
}
}
NgAisMenu.decorators = [
{ type: Component, args: [{
selector: 'ais-menu',
template: `
<div
[class]="cx()"
*ngIf="!isHidden"
>
<ul [class]="cx('list')">
<li
[class]="getItemClass(item)"
*ngFor="let item of state.items"
(click)="handleClick($event, item.value)"
>
<a
href="{{state.createURL(item.value)}}"
[class]="cx('link')"
(click)="handleClick($event, item.value)"
>
<span [class]="cx('label')">{{item.label}}</span>
<span [class]="cx('count')">{{item.count}}</span>
</a>
</li>
</ul>
<button
*ngIf="showMore"
(click)="state.toggleShowMore()"
[class]="showMoreClass"
[disabled]="!state.canToggleShowMore"
>
{{state.isShowingMore ? showLessLabel : showMoreLabel}}
</button>
</div>
`
},] }
];
NgAisMenu.ctorParameters = () => [
{ type: NgAisIndex, decorators: [{ type: Inject, args: [forwardRef(() => NgAisIndex),] }, { type: Optional }] },
{ type: NgAisInstantSearch, decorators: [{ type: Inject, args: [forwardRef(() => NgAisInstantSearch),] }] }
];
NgAisMenu.propDecorators = {
showMoreLabel: [{ type: Input }],
showLessLabel: [{ type: Input }],
attribute: [{ type: Input }],
showMore: [{ type: Input }],
limit: [{ type: Input }],
showMoreLimit: [{ type: Input }],
sortBy: [{ type: Input }],
transformItems: [{ type: Input }]
};
class NgAisMenuModule {
}
NgAisMenuModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgAisMenu],
entryComponents: [NgAisMenu],
exports: [NgAisMenu],
imports: [CommonModule],
},] }
];
class NgAisNumericMenu extends TypedBaseWidget {
constructor(parentIndex, instantSearchInstance) {
super('NumericMenu');
this.parentIndex = parentIndex;
this.instantSearchInstance = instantSearchInstance;
this.state = {
items: [],
refine: noop,
createURL: () => '#',
hasNoResults: true,
sendEvent: noop,
canRefine: false,
};
}
get isHidden() {
return this.state.items.length === 0 && this.autoHideContainer;
}
ngOnInit() {
this.createWidget(connectNumericMenu, {
attribute: this.attribute,
items: this.items,
transformItems: this.transformItems,
}, {
$$widgetType: 'ais.numericMenu',
});
super.ngOnInit();
}
refine(event, item) {
event.preventDefault();
event.stopPropagation();
this.state.refine(item.value);
}
}
NgAisNumericMenu.decorators = [
{ type: Component, args: [{
selector: 'ais-numeric-menu',
template: `
<div
[class]="cx()"
*ngIf="!isHidden"
>
<ul [class]="cx('list')">
<li
[class]="getItemClass(item)"
*ngFor="let item of state.items"
>
<label [class]="cx('label')">
<input
[class]="cx('radio')"
type="radio"
name="NumericMenu"
[checked]="item.isRefined"
(change)="refine($event, item)"
/>
<span [class]="cx('labelText')">{{item.label}}</span>
</label>
</li>
</ul>
</div>
`
},] }
];
NgAisNumericMenu.ctorParameters = () => [
{ type: NgAisIndex, decorators: [{ type: Inject, args: [forwardRef(() => NgAisIndex),] }, { type: Optional }] },
{ type: NgAisInstantSearch, decorators: [{ type: Inject, args: [forwardRef(() => NgAisInstantSearch),] }] }
];
NgAisNumericMenu.propDecorators = {
attribute: [{ type: Input }],
items: [{ type: Input }],
transformItems: [{ type: Input }]
};
class NgAisNumericMenuModule {
}
NgAisNumericMenuModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgAisNumericMenu],
entryComponents: [NgAisNumericMenu],
exports: [NgAisNumericMenu],
imports: [CommonModule],
},] }
];
class NgAisPagination extends TypedBaseWidget {
constructor(parentIndex, instantSearchInstance) {
super('Pagination');
this.parentIndex = parentIndex;
this.instantSearchInstance = instantSearchInstance;
// rendering options
this.showFirst = true;
this.showLast = true;
this.showPrevious = true;
this.showNext = true;
// TODO: check if this works, padding and totalPages are most likely strings when passed to the template
this.state = {
createURL: () => '#',
currentRefinement: 0,
nbHits: 0,
nbPages: 0,
refine: noop,
pages: [],
canRefine: false,
isFirstPage: false,
isLastPage: false,
};
}
ngOnInit() {
this.createWidget(connectPagination, {
padding: parseNumberInput(this.padding),
totalPages: parseNumberInput(this.totalPages),
}, {
$$widgetType: 'ais.pagination',
});
super.ngOnInit();
}
refine(event, page) {
event.stopPropagation();
event.preventDefault();
if (page < 0 ||
page === this.state.currentRefinement ||
page >= this.state.nbPages) {
return;
}
this.state.refine(page);
}
}
NgAisPagination.decorators = [
{ type: Component, args: [{
selector: 'ais-pagination',
template: `
<div [ngClass]="[cx(), state.nbPages <= 1 ? cx('', 'noRefinement') : '']">
<ul [class]="cx('list')">
<li
*ngIf="showFirst"
(click)="refine($event, 0)"
[class]="
cx('item') +
' ' +
cx('item', 'firstPage') +
(state.currentRefinement === 0 ? ' ' + cx('item', 'disabled') : '')
"
>
<a
[href]="state.createURL(0)"
[class]="cx('link')"
>
‹‹
</a>
</li>
<li
*ngIf="showPrevious"
(click)="refine($event, state.currentRefinement - 1)"
[class]="
cx('item') +
' ' +
cx('item', 'previousPage') +
(state.currentRefinement === 0 ? ' ' + cx('item', 'disabled') : '')
"
>
<a
[href]="state.createURL(state.currentRefinement - 1)"
[class]="cx('link')"
>
‹
</a>
</li>
<li
[class]="
cx('item') +
' ' +
cx('item', 'page') +
(state.currentRefinement === page ? ' ' + cx('item', 'selected') : '')
"
*ngFor="let page of state.pages"
(click)="refine($event, page)"
>
<a
[class]="cx('link')"
[href]="state.createURL(page)"
>
{{page + 1}}
</a>
</li>
<li
*ngIf="showNext"
(click)="refine($event, state.currentRefinement + 1)"
[class]="
cx('item') +
' ' +
cx('item', 'nextPage') +
(state.currentRefinement + 1 === state.nbPages ? ' ' + cx('item', 'disabled') : '')
"
>
<a
[href]="state.createURL(state.currentRefinement + 1)"
[class]="cx('link')"
>
›
</a>
</li>
<li
*ngIf="showLast"
(click)="refine($event, state.nbPages - 1)"
[class]="
cx('item') +
' ' +
cx('item', 'lastPage') +
(state.currentRefinement + 1 === state.nbPages ? ' ' + cx('item', 'disabled') : '')
"
>
<a
[href]="state.createURL(state.nbPages - 1)"
[class]="cx('link')"
>
››
</a>
</li>
</ul>
</div>
`
},] }
];
NgAisPagination.ctorParameters = () => [
{ type: NgAisIndex, decorators: [{ type: Inject, args: [forwardRef(() => NgAisIndex),] }, { type: Optional }] },
{ type: NgAisInstantSearch, decorators: [{ type: Inject, args: [forwardRef(() => NgAisInstantSearch),] }] }
];
NgAisPagination.propDecorators = {
showFirst: [{ type: Input }],
showLast: [{ type: Input }],
showPrevious: [{ type: Input }],
showNext: [{ type: Input }],
padding: [{ type: Input }],
totalPages: [{ type: Input }]
};
class NgAisPaginationModule {
}
NgAisPaginationModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgAisPagination],
entryComponents: [NgAisPagination],
exports: [NgAisPagination],
imports: [CommonModule],
},] }
];
class NgAisRangeSlider extends TypedBaseWidget {
constructor(parentIndex, instantSearchInstance) {
super('RangeSlider');
this.parentIndex = parentIndex;
this.instantSearchInstance = instantSearchInstance;
// rendering options
this.pips = true;
this.tooltips = true;
this.state = {
canRefine: false,
format: {
from: () => '',
to: () => '',
},
range: { min: 0, max: 1 },
refine: noop,
start: [0, 1],
sendEvent: noop,
};
this.updateState = (state, isFirstRendering) => {
if (isFirstRendering) {
// create slider
const config = {
animate: false,
behaviour: 'snap',
connect: true,
range: { min: 0, max: 1 },
start: [0, 1],
step: this.step,
tooltips: this.tooltips && [
{ to: this.formatTooltip },
{ to: this.formatTooltip },
],
};
// tslint:disable-next-line: no-boolean-literal-compare (pips is @Input, so could be not a boolean)
if (this.pips === true || typeof this.pips === 'undefined') {
Object.assign(config, {
pips: {
density: 3,
mode: 'positions',
stepped: true,
values: [0, 50, 100],
},
});
}
else if (this.pips !== undefined) {
Object.assign(config, { pips: this.pips });
}
this.slider = noUiSlider.create(this.sliderContainer.nativeElement, config);
// register listen events
this.sliderContainer.nativeElement.noUiSlider.on('change', this.handleChange);
}
// update component inner state
this.state = state;
// update the slider state
const { range: { min, max }, start, } = state;
const disabled = min === max;
const range = disabled ? { min, max: max + 0.0001 } : { min, max };
// TODO: test this as we're nolonger passing disable
// it seems the API has changed: slider.setAttribute('disabled', true) / slider.removeAttribute('disabled');
// see: https://refreshless.com/nouislider/more/#section-disable
this.slider.updateOptions({ range, start });
};
this.handleChange = (values) => {
this.state.refine(values);
};
this.formatTooltip = (value) => {
return value.toFixed(parseNumberInput(this.precision));
};
}
get step() {
// compute step from the precision value
const precision = parseNumberInput(this.precision) || 2;
return 1 / Math.pow(10, precision);
}
ngOnInit() {
this.createWidget(connectRange, {
attribute: this.attribute,
max: parseNumberInput(this.max),
min: parseNumberInput(this.min),
precision: parseNumberInput(this.precision),
}, {
$$widgetType: 'ais.rangeSlider',
});
super.ngOnInit();
}
}
NgAisRangeSlider.decorators = [
{ type: Component, args: [{
selector: 'ais-range-slider',
template: `
<div [class]="cx()">
<div [class]="cx('body')">
<div #sliderContainer></div>
</div>
</div>
`
},] }
];
NgAisRangeSlider.ctorParameters = () => [
{ type: NgAisIndex, decorators: [{ type: Inject, args: [forwardRef(() => NgAisIndex),] }, { type: Optional }] },
{ type: NgAisInstantSearch, decorators: [{ type: Inject, args: [forwardRef(() => NgAisInstantSearch),] }] }
];
NgAisRangeSlider.propDecorators = {
sliderContainer: [{ type: ViewChild, args: ['sliderContainer', { static: false },] }],
pips: [{ type: Input }],
tooltips: [{ type: Input }],
attribute: [{ type: Input }],
min: [{ type: Input }],
max: [{ type: Input }],
precision: [{ type: Input }]
};
class NgAisRangeSliderModule {
}
NgAisRangeSliderModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgAisRangeSlider],
entryComponents: [NgAisRangeSlider],
exports: [NgAisRangeSlider],
imports: [CommonModule],
},] }
];
class NgAisRefinementList extends TypedBaseWidget {
constructor(parentIndex, instantSearchInstance) {
super('RefinementList');
this.parentIndex = parentIndex;
this.instantSearchInstance = instantSearchInstance;
// rendering options
this.showMoreLabel = 'Show more';
this.showLessLabel = 'Show less';
this.searchPlaceholder = 'Search here...';
this.state = {
canRefine: false,
canToggleShowMore: false,
createURL: () => '',
isShowingMore: false,
items: [],
refine: noop,
toggleShowMore: noop,
searchForItems: noop,
isFromSearch: false,
hasExhaustiveItems: false,
sendEvent: noop,
};
}
get isHidden() {
return this.state.items.length === 0 && this.autoHideContainer;
}
ngOnInit() {
this.createWidget(connectRefinementList, {
showMore: this.showMore,
limit: parseNumberInput(this.limit),
showMoreLimit: parseNumberInput(this.showMoreLimit),
attribute: this.attribute,
operator: this.operator,
sortBy: this.sortBy,
escapeFacetValues: true,
transformItems: this.transformItems,
}, {
$$widgetType: 'ais.refinementList',
});
super.ngOnInit();
}
refine(event, item) {
event.preventDefault();
event.stopPropagation();
if (this.state.canRefine) {
// update UI directly, it will update the checkbox state
item.isRefined = !item.isRefined;
// refine through Algolia API
this.state.refine(item.value);
}
}
}
NgAisRefinementList.decorators = [
{ type: Component, args: [{
selector: 'ais-refinement-list',
template: `
<div
[class]="cx()"
*ngIf="!isHidden"
>
<div
*ngIf="searchable"
[class]="cx('searchBox')"
>
<ais-facets-search
[search]="state.searchForItems"
[searchPlaceholder]="searchPlaceholder"
>
</ais-facets-search>
</div>
<ul [class]="cx('list')">
<li
[class]="getItemClass(item)"
*ngFor="let item of state.items"
(click)="refine($event, item)"
>
<label [class]="cx('label')">
<input
[class]="cx('checkbox')"
type="checkbox"
value="{{item.value}}"
[checked]="item.isRefined"
/>
<span [class]="cx('labelText')">
<ais-highlight attribute="highlighted" [hit]="item"></ais-highlight>
</span>
<span [class]="cx('count')">{{item.count}}</span>
</label>
</li>
</ul>
<button
[class]="cx('showMore')"
*ngIf="showMore"
(click)="state.toggleShowMore()"
[disabled]="!state.canToggleShowMore"
>
{{state.isShowingMore ? showLessLabel : showMoreLabel}}
</button>
</div>
`
},] }
];
NgAisRefinementList.ctorParameters = () => [
{ type: NgAisIndex, decorators: [{ type: Inject, args: [forwardRef(() => NgAisIndex),] }, { type: Optional }] },
{ type: NgAisInstantSearch, decorators: [{ type: Inject, args: [forwardRef(() => NgAisInstantSearch),] }] }
];
NgAisRefinementList.propDecorators = {
showMoreLabel: [{ type: Input }],
showLessLabel: [{ type: Input }],
searchable: [{ type: Input }],
searchPlaceholder: [{ type: Input }],
attribute: [{ type: Input }],
operator: [{ type: Input }],
limit: [{ type: Input }],
showMore: [{ type: Input }],
showMoreLimit: [{ type: Input }],
sortBy: [{ type: Input }],
transformItems: [{ type: Input }]
};
class NgAisFacetsSearch {
constructor() {
this.cx = bem('SearchBox');
this.searchQuery = '';
}
handleChange(value) {
this.searchQuery = value;
this.search(value);
}
handleSubmit(event) {
event.preventDefault();
this.search(this.searchQuery);
}
}
NgAisFacetsSearch.decorators = [
{ type: Component, args: [{
selector: 'ais-facets-search',
template: `
<div [class]="cx()">
<form
[class]="cx('form')"
(submit)="handleSubmit($event)"
novalidate
>
<input
[class]="cx('input')"
autocapitalize="off"
autocorrect="off"
placeholder="{{searchPlaceholder}}"
role="textbox"
spellcheck="false"
type="text"
[value]="searchQuery"
(input)="handleChange($event.target.value)"
/>
<button
[class]="cx('submit')"
title="Submit the search query."
type="submit"
>
<svg
[ngClass]="cx('submitIcon')"
viewBox="0 0 40 40"
width="10"
height="10"
>
<path d="M26.804 29.01c-2.832 2.34-6.465 3.746-10.426 3.746C7.333 32.756 0 25.424 0 16.378 0 7.333 7.333 0 16.378 0c9.046 0 16.378 7.333 16.378 16.378 0 3.96-1.406 7.594-3.746 10.426l10.534 10.534c.607.607.61 1.59-.004 2.202-.61.61-1.597.61-2.202.004L26.804 29.01zm-10.426.627c7.323 0 13.26-5.936 13.26-13.26 0-7.32-5.937-13.257-13.26-13.257C9.056 3.12 3.12 9.056 3.12 16.378c0 7.323 5.936 13.26 13.258 13.26z"></path>
</svg>
</button>
<button
[class]="cx('reset')"
type="reset"
title="Clear the search query."
hidden
>
<svg
[ngClass]="cx('resetIcon')"
viewBox="0 0 20 20"
width="10"
height="10"
>
<path d="M8.114 10L.944 2.83 0 1.885 1.886 0l.943.943L10 8.113l7.17-7.17.944-.943L20 1.886l-.943.943-7.17 7.17 7.17 7.17.943.944L18.114 20l-.943-.943-7.17-7.17-7.17 7.17-.944.943L0 18.114l.943-.943L8.113 10z"></path>
</svg>
</button>
</form>
</div>
`
},] }
];
NgAisFacetsSearch.propDecorators = {
searchPlaceholder: [{ type: Input }],
search: [{ type: Input }]
};
class NgAisRefinementListModule {
}
NgAisRefinementListModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgAisRefinementList, NgAisFacetsSearch],
entryComponents: [NgAisRefinementList],
exports: [NgAisRefinementList],
imports: [CommonModule, NgAisHighlightModule],
},] }
];
class NgAisSearchBox extends TypedBaseWidget {
constructor(parentIndex, instantSearchInstance, injectedDocument) {
super('SearchBox');
this.parentIndex = parentIndex;
this.instantSearchInstance = instantSearchInstance;
this.injectedDocument = injectedDocument;
this.placeholder = 'Search';
this.submitTitle = 'Submit';
this.resetTitle = 'Reset';
this.searchAsYouType = true;
this.autofocus = false;
this.showLoadingIndicator = true;
// Output events
// form
this.submit = new EventEmitter();
this.reset = new EventEmitter();
// input
this.change = new EventEmitter();
this.focus = new EventEmitter();
this.blur = new EventEmitter();
this.query = '';
this.state = {
query: '',
refine: noop,
clear: noop,
isSearchStalled: false,
};
this.createWidget(connectSearchBox, {}, {
$$widgetType: 'ais.searchBox',
});
this.document = injectedDocument;
}
ngAfterViewInit() {
if (this.autofocus) {
this.searchBox.nativeElement.focus();
}
}
ngDoCheck() {
// We bypass the state update if the input is focused to avoid concurrent
// updates when typing.
if (this.query !== this.state.query &&
this.searchBox &&
this.searchBox.nativeElement &&
this.document.activeElement !== this.searchBox.nativeElement) {
this.query = this.state.query;
}
}
handleChange(query) {
this.change.emit(query);
if (this.searchAsYouType) {
this.state.refine(query);
}
}
handleSubmit(event) {
// send submit event to parent component
this.submit.emit(event);
event.preventDefault();
if (!this.searchAsYouType) {
this.state.refine(this.searchBox.nativeElement.value);
}
}
handleReset(event) {
// send reset event to parent component
this.reset.emit(event);
// reset search
this.state.refine('');
}
}
NgAisSearchBox.decorators = [
{ type: Component, args: [{
selector: 'ais-search-box',
template: `
<div [class]="cx()">
<form
[class]="cx('form')"
novalidate
(submit)="handleSubmit($event)"
>
<input
[class]="cx('input')"
autocapitalize="off"
autocorrect="off"
placeholder="{{placeholder}}"
role="textbox"
spellcheck="false"
type="text"
[value]="query"
(input)="handleChange($event.target.value)"
(focus)="focus.emit($event)"
(blur)="blur.emit($event)"
#searchBox
/>
<button
[class]="cx('submit')"
type="submit"
title="{{submitTitle}}"
>
<svg
[ngClass]="cx('submitIcon')"
viewBox="0 0 40 40"
width="40"
height="40"
>
<path d="M26.804 29.01c-2.832 2.34-6.465 3.746-10.426 3.746C7.333 32.756 0 25.424 0 16.378 0 7.333 7.333 0 16.378 0c9.046 0 16.378 7.333 16.378 16.378 0 3.96-1.406 7.594-3.746 10.426l10.534 10.534c.607.607.61 1.59-.004 2.202-.61.61-1.597.61-2.202.004L26.804 29.01zm-10.426.627c7.323 0 13.26-5.936 13.26-13.26 0-7.32-5.937-13.257-13.26-13.257C9.056 3.12 3.12 9.056 3.12 16.378c0 7.323 5.936 13.26 13.258 13.26z"></path>
</svg>
</button>
<button
[class]="cx('reset')"
type="reset"
title="{{resetTitle}}"
(click)="handleReset($event)"
[hidden]="!state.query || (state.query && !state.query.trim()) || (state.isSearchStalled && showLoadingIndicator)">
<svg
[ngClass]="cx('resetIcon')"
viewBox="0 0 20 20"
width="20"
height="20"
>
<path d="M8.114 10L.944 2.83 0 1.885 1.886 0l.943.943L10 8.113l7.17-7.17.944-.943L20 1.886l-.943.943-7.17 7.17 7.17 7.17.943.944L18.114 20l-.943-.943-7.17-7.17-7.17 7.17-.944.943L0 18.114l.943-.943L8.113 10z"></path>
</svg>
</button>
<span
[class]="cx('loadingIndicator')"
[hidden]="!showLoadingIndicator || !state.isSearchStalled"
>
<svg
width="16"
height="16"
viewBox="0 0 38 38"
stroke="#444"
[ngClass]="cx('loadingIcon')"
>
<g fill="none" fillRule="evenodd">
<g transform="translate(1 1)" strokeWidth="2">
<circle strokeOpacity=".5" cx="18" cy="18" r="18" />
<path d="M36 18c0-9.94-8.06-18-18-18">
<animateTransform
attributeName="transform"
type="rotate"
from="0 18 18"
to="360 18 18"
dur="1s"
repeatCount="indefinite"
/>
</path>
</g>
</g>
</svg>
</span>
</form>
</div>
`
},] }
];
NgAisSearchBox.ctorParameters = () => [
{ type: NgAisIndex, decorators: [{ type: Inject, args: [forwardRef(() => NgAisIndex),] }, { type: Optional }] },
{ type: NgAisInstantSearch, decorators: [{ type: Inject, args: [forwardRef(() => NgAisInstantSearch),] }] },
{ type: Document, decorators: [{ type: Inject, args: [DOCUMENT,] }] }
];
NgAisSearchBox.propDecorators = {
searchBox: [{ type: ViewChild, args: ['searchBox', { static: false },] }],
placeholder: [{ type: Input }],
submitTitle: [{ type: Input }],
resetTitle: [{ type: Input }],
searchAsYouType: [{ type: Input }],
autofocus: [{ type: Input }],
showLoadingIndicator: [{ type: Input }],
submit: [{ type: Output }],
reset: [{ type: Output }],
change: [{ type: Output }],
focus: [{ type: Output }],
blur: [{ type: Output }]
};
class NgAisSearchBoxModule {
}
NgAisSearchBoxModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgAisSearchBox],
entryComponents: [NgAisSearchBox],
exports: [NgAisSearchBox],
imports: [CommonModule],
},] }
];
class NgAisSortBy extends TypedBaseWidget {
constructor(parentIndex, instantSearchInstance) {
super('SortBy');
this.parentIndex = parentIndex;
this.instantSearchInstance = instantSearchInstance;
this.state = {
currentRefinement: null,
options: [],
refine: noop,
hasNoResults: false,
canRefine: false,
};
}
ngOnInit() {
this.createWidget(connectSortBy, {
items: this.items,
transformItems: this.transformItems,
}, {
$$widgetType: 'ais.sortBy',
});
super.ngOnInit();
}
}
NgAisSortBy.decorators = [
{ type: Component, args: [{
selector: 'ais-sort-by',
template: `
<div [class]="cx()">
<select
[class]="cx('select')"
(change)="state.refine($event.target.value)"
>
<option
[class]="cx('option')"
*ngFor="let item of state.options"
[value]="item.value"
[selected]="item.value === state.currentRefinement"
>
{{item.label}}
</option>
</select>
</div>
`
},] }
];
NgAisSortBy.ctorParameters = () => [
{ type: NgAisIndex, decorators: [{ type: Inject, args: [forwardRef(() => NgAisIndex),] }, { type: Optional }] },
{ type: NgAisInstantSearch, decorators: [{ type: Inject, args: [forwardRef(() => NgAisInstantSearch),] }] }
];
NgAisSortBy.propDecorators = {
items: [{ type: Input }],
transformItems: [{ type: Input }]
};
class NgAisSortByModule {
}
NgAisSortByModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgAisSortBy],
entryComponents: [NgAisSortBy],
exports: [NgAisSortBy],
imports: [CommonModule],
},] }
];
class NgAisRatingMenu extends TypedBaseWidget {
constructor(parentIndex, instantSearchInstance) {
super('RatingMenu');
this.parentIndex = parentIndex;
this.instantSearchInstance = instantSearchInstance;
// rendering options
this.andUpLabel = '& Up';
this.state = {
createURL: () => '#',
hasNoResults: false,
items: [],
refine: noop,
canRefine: false,
sendEvent: noop,
};
}
get isHidden() {
return this.state.items.length === 0 && this.autoHideContainer;
}
ngOnInit() {
this.createWidget(connectRatingMenu, {
attribute: this.attribute,
max: parseNumberInput(this.max),
}, {
$$widgetType: 'ais.ratingMenu',
});
super.ngOnInit();
}
handleClick(event, value) {
event.preventDefault();
event.stopPropagation();
this.state.refine(value);
}
}
NgAisRatingMenu.decorators = [
{ type: Component, args: [{
selector: 'ais-rating-menu',
template: `
<div
[ngClass]="[
cx(),
state.items.length === 0 ? cx('', 'noRefinement') : ''
]"
*ngIf="!isHidden"
>
<svg style="display:none;">
<symbol
id="ais-StarRating-starSymbol"
viewBox="0 0 24 24"
>
<path d="M12 .288l2.833 8.718h9.167l-7.417 5.389 2.833 8.718-7.416-5.388-7.417 5.388 2.833-8.718-7.416-5.389h9.167z"/>
</symbol>
<symbol
id="ais-StarRating-starEmptySymbol"
viewBox="0 0 24 24"
>
<path d="M12 6.76l1.379 4.246h4.465l-3.612 2.625 1.379 4.246-3.611-2.625-3.612 2.625 1.379-4.246-3.612-2.625h4.465l1.38-4.246zm0-6.472l-2.833 8.718h-9.167l7.416 5.389-2.833 8.718 7.417-5.388 7.416 5.388-2.833-8.718 7.417-5.389h-9.167l-2.833-8.718z"/>
</symbol>
</svg>
<ul [class]="cx('list')">
<li
*ngFor="let item of state.items"
[class]="getItemClass(item)"
(click)="handleClick($event, item.value)"
>
<a
href="{{state.createURL(item.value)}}"
[class]="cx('link')"
(click)="handleClick($event, item.value)"
>
<svg
width="24"
height="24"
*ngFor="let star of item.stars"
[ngClass]="cx('starIcon') + ' ' + (star ? cx('starIcon', 'full') : cx('starIcon', 'empty'))"
aria-hidden="true"
>
<use
*ngIf="star"
xlink:href="#ais-StarRating-starSymbol"
>
</use>
<use
*ngIf="!star"
xlink:href="#ais-StarRating-starEmptySymbol"
>
</use>
</svg>
<span [class]="cx('label')" aria-hidden="true">{{andUpLabel}}</span>
<span [class]="cx('count')">{{item.count}}</span>
</a>
</li>
</ul>
</div>
`
},] }
];
NgAisRatingMenu.ctorParameters = () => [
{ type: NgAisIndex, decorators: [{ type: Inject, args: [forwardRef(() => NgAisIndex),] }, { type: Optional }] },
{ type: NgAisInstantSearch, decorators: [{ type: Inject, args: [forwardRef(() => NgAisInstantSearch),] }] }
];
NgAisRatingMenu.propDecorators = {
andUpLabel: [{ type: Input }],
attribute: [{ type: Input }],
max: [{ type: Input }]
};
class NgAisRatingMenuModule {
}
NgAisRatingMenuModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgAisRatingMenu],
entryComponents: [NgAisRatingMenu],
exports: [NgAisRatingMenu],
imports: [CommonModule],
},] }
];
class NgAisStats extends TypedBaseWidget {
constructor(parentIndex, instantSearchInstance) {
super('Stats');
this.parentIndex = parentIndex;
this.instantSearchInstance = instantSearchInstance;
this.state = {
nbHits: 0,
nbPages: 0,
page: 0,
processingTimeMS: 0,
query: '',
areHitsSorted: false,
};
this.createWidget(connectStats, {}, {
$$widgetType: 'ais.stats',
});
}
get templateContext() {
return { state: this.state };
}
}
NgAisStats.decorators = [
{ type: Component, args: [{
selector: 'ais-stats',
template: `
<div [class]="cx()">
<ng-container *ngTemplateOutlet="template; context: templateContext">
</ng-container>
<span *ngIf="!template" [class]="cx('text')">
{{state.nbHits}} results found in {{state.processingTimeMS}}ms.
</span>
</div>
`
},] }
];
NgAisStats.ctorParameters = () => [
{ type: NgAisIndex, decorators: [{ type: Inject, args: [forwardRef(() => NgAisIndex),] }, { type: Optional }] },
{ type: NgAisInstantSearch, decorators: [{ type: Inject, args: [forwardRef(() => NgAisInstantSearch),] }] }
];
NgAisStats.propDecorators = {
template: [{ type: ContentChild, args: [TemplateRef, { static: false },] }]
};
class NgAisStatsModule {
}
NgAisStatsModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgAisStats],
entryComponents: [NgAisStats],
exports: [NgAisStats],
imports: [CommonModule],
},] }
];
class NgAisToggle extends TypedBaseWidget {
constructor(parentIndex, instantSearchInstance) {
super('ToggleRefinement');
this.parentIndex = parentIndex;
this.instantSearchInstance = instantSearchInstance;
this.state = {
canRefine: false,
sendEvent: undefined,
value: {
count: undefined,
isRefined: false,
name: '',
offFacetValue: undefined,
onFacetValue: undefined,
},
createURL: () => '#',
refine: noop,
};
}
ngOnInit() {
this.createWidget(connectToggleRefinement, {
attribute: this.attribute,
on: this.on,
off: this.off,
}, {
$$widgetType: 'ais.toggleRefinement',
});
super.ngOnInit();
}
handleChange(event) {
event.preventDefault();
event.stopPropagation();
this.state.refine(this.state.value);
}
}
NgAisToggle.decorators = [
{ type: Component, args: [{
selector: 'ais-toggle',
template: `
<div [class]='cx()'>
<label [class]="cx('label')">
<input
[class]="cx('checkbox')"
type='checkbox'
value='{{state.value.name}}'
[checked]='state.value.isRefined'
(change)='handleChange($event)'
/>
<span [class]="cx('labelText')">
{{label || state.value.name}}
</span>
<span [class]="cx('count')">{{state.value.count}}</span>
</label>
</div>
`
},] }
];
NgAisToggle.ctorParameters = () => [
{ type: NgAisIndex, decorators: [{ type: Inject, args: [forwardRef(() => NgAisIndex),] }, { type: Optional }] },
{ type: NgAisInstantSearch, decorators: [{ type: Inject, args: [forwardRef(() => NgAisInstantSearch),] }] }
];
NgAisToggle.propDecorators = {
label: [{ type: Input }],
attribute: [{ type: Input }],
on: [{ type: Input }],
off: [{ type: Input }]
};
class NgAisToggleModule {
}
NgAisToggleModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgAisToggle],
entryComponents: [NgAisToggle],
exports: [NgAisToggle],
imports: [CommonModule],
},] }
];
class NgAisReverseHighlight {
constructor() {
this.highlightedTagName = 'mark';
}
get content() {
return reverseHighlight({
attribute: this.attribute,
hit: this.hit,
highlightedTagName: this.highlightedTagName,
});
}
}
NgAisReverseHighlight.decorators = [
{ type: Component, args: [{
selector: 'ais-reverse-highlight',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `<span class="ais-ReverseHighlight" [innerHtml]="content"></span>`
},] }
];
NgAisReverseHighlight.propDecorators = {
attribute: [{ type: Input }],
hit: [{ type: Input }],
highlightedTagName: [{ type: Input }]
};
class NgAisReverseHighlightModule {
}
NgAisReverseHighlightModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgAisReverseHighlight],
exports: [NgAisReverseHighlight],
},] }
];
class NgAisSnippet {
constructor() {
this.highlightedTagName = 'mark';
}
get content() {
return snippet({
attribute: this.attribute,
hit: this.hit,
highlightedTagName: this.highlightedTagName,
});
}
}
NgAisSnippet.decorators = [
{ type: Component, args: [{
selector: 'ais-snippet',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `<span class="ais-Snippet" [innerHtml]="content"></span>`
},] }
];
NgAisSnippet.propDecorators = {
attribute: [{ type: Input }],
hit: [{ type: Input }],
highlightedTagName: [{ type: Input }]
};
class NgAisSnippetModule {
}
NgAisSnippetModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgAisSnippet],
exports: [NgAisSnippet],
},] }
];
class NgAisReverseSnippet {
constructor() {
this.highlightedTagName = 'mark';
}
get content() {
return reverseSnippet({
attribute: this.attribute,
hit: this.hit,
highlightedTagName: this.highlightedTagName,
});
}
}
NgAisReverseSnippet.decorators = [
{ type: Component, args: [{
selector: 'ais-reverse-snippet',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `<span class="ais-ReverseSnippet" [innerHtml]="content"></span>`
},] }
];
NgAisReverseSnippet.propDecorators = {
attribute: [{ type: Input }],
hit: [{ type: Input }],
highlightedTagName: [{ type: Input }]
};
class NgAisReverseSnippetModule {
}
NgAisReverseSnippetModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgAisReverseSnippet],
exports: [NgAisReverseSnippet],
},] }
];
class NgAisRangeInput extends TypedBaseWidget {
constructor(parentIndex, instantSearchInstance) {
super('RangeInput');
this.parentIndex = parentIndex;
this.instantSearchInstance = instantSearchInstance;
// rendering options
this.currency = '$';
this.separator = 'to';
this.submitLabel = 'Go';
this.precision = 0;
this.state = {
range: { min: undefined, max: undefined },
refine: noop,
start: [0, 0],
// TODO: use canRefine & format
canRefine: false,
format: {
from: () => '',
to: () => '',
},
sendEvent: undefined,
};
}
get step() {
const precision = parseNumberInput(this.precision);
return 1 / Math.pow(10, precision);
}
get canRefine() {
return this.state.range.min !== this.state.range.max;
}
ngOnInit() {
this.createWidget(connectRange, {
attribute: this.attribute,
max: parseNumberInput(this.max),
min: parseNumberInput(this.min),
precision: parseNumberInput(this.precision),
}, {
$$widgetType: 'ais.rangeInput',
});
super.ngOnInit();
}
handleChange(event, type) {
const value = parseNumberInput(event.target.value);
if (type === 'min') {
this.minInputValue = value;
}
else {
this.maxInputValue = value;
}
}
handleSubmit(event) {
event.preventDefault();
this.state.refine([this.minInputValue, this.maxInputValue]);
}
}
NgAisRangeInput.decorators = [
{ type: Component, args: [{
selector: 'ais-range-input',
template: `
<div [ngClass]="[
cx(),
!canRefine ? cx('', 'noRefinement') : ''
]">
<form
[class]="cx('form')"
(submit)="handleSubmit($event)"
novalidate
>
<label [class]="cx('label')">
<span [class]="cx('currency')">{{currency}}</span>
<input
[ngClass]="[
cx('input'),
cx('input', 'min')
]"
type="number"
[min]="state.range.min"
[max]="state.range.max"
[placeholder]="state.range.min"
[value]="minInputValue"
[step]="step"
(change)="handleChange($event, 'min')"
/>
</label>
<span [class]="cx('separator')">{{separator}}</span>
<label [class]="cx('label')">
<span [class]="cx('currency')">{{currency}}</span>
<input
[ngClass]="[
cx('input'),
cx('input', 'max')
]"
type="number"
[min]="state.range.min"
[max]="state.range.max"
[placeholder]="state.range.max"
[value]="maxInputValue"
[step]="step"
(change)="handleChange($event, 'max')"
/>
</label>
<button
[class]="cx('submit')"
(click)="handleSubmit($event)"
>
{{submitLabel}}
</button>
</form>
</div>
`
},] }
];
NgAisRangeInput.ctorParameters = () => [
{ type: NgAisIndex, decorators: [{ type: Inject, args: [forwardRef(() => NgAisIndex),] }, { type: Optional }] },
{ type: NgAisInstantSearch, decorators: [{ type: Inject, args: [forwardRef(() => NgAisInstantSearch),] }] }
];
NgAisRangeInput.propDecorators = {
currency: [{ type: Input }],
separator: [{ type: Input }],
submitLabel: [{ type: Input }],
attribute: [{ type: Input }],
min: [{ type: Input }],
max: [{ type: Input }],
precision: [{ type: Input }]
};
class NgAisRangeInputModule {
}
NgAisRangeInputModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgAisRangeInput],
entryComponents: [NgAisRangeInput],
exports: [NgAisRangeInput],
imports: [CommonModule],
},] }
];
class NgAisPanel {
}
NgAisPanel.decorators = [
{ type: Component, args: [{
selector: 'ais-panel',
template: `
<div class="ais-Panel">
<div *ngIf="header" class="ais-Panel-header">
{{header}}
</div>
<div class="ais-Panel-body">
<ng-content></ng-content>
</div>
<div *ngIf="footer" class="ais-Panel-footer">
{{footer}}
</div>
</div>
`
},] }
];
NgAisPanel.propDecorators = {
header: [{ type: Input }],
footer: [{ type: Input }]
};
class NgAisPanelModule {
}
NgAisPanelModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgAisPanel],
entryComponents: [NgAisPanel],
exports: [NgAisPanel],
imports: [CommonModule],
},] }
];
class NgAisConfigure extends TypedBaseWidget {
constructor(differs, parentIndex, instantSearchInstance) {
super('Configure');
this.differs = differs;
this.parentIndex = parentIndex;
this.instantSearchInstance = instantSearchInstance;
this.state = {
refine: noop,
};
}
set searchParameters(values) {
this.internalSearchParameters = values;
if (!this.differ && values) {
this.differ = this.differs.find(values).create();
}
}
ngOnInit() {
this.createWidget(connectConfigure, {
searchParameters: this.internalSearchParameters,
}, {
$$widgetType: 'ais.configure',
});
super.ngOnInit();
}
ngDoCheck() {
if (this.differ) {
const changes = this.differ.diff(this.internalSearchParameters);
if (changes) {
this.state.refine(this.internalSearchParameters);
}
}
}
}
NgAisConfigure.decorators = [
{ type: Component, args: [{
selector: 'ais-configure',
template: ''
},] }
];
NgAisConfigure.ctorParameters = () => [
{ type: KeyValueDiffers },
{ type: NgAisIndex, decorators: [{ type: Inject, args: [forwardRef(() => NgAisIndex),] }, { type: Optional }] },
{ type: NgAisInstantSearch, decorators: [{ type: Inject, args: [forwardRef(() => NgAisInstantSearch),] }] }
];
NgAisConfigure.propDecorators = {
searchParameters: [{ type: Input }]
};
class NgAisConfigureModule {
}
NgAisConfigureModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgAisConfigure],
entryComponents: [NgAisConfigure],
exports: [NgAisConfigure],
imports: [CommonModule],
},] }
];
class NgAisConfigureRelatedItems extends TypedBaseWidget {
constructor(parentIndex, instantSearchInstance) {
super('ExperimentalConfigureRelatedItems');
this.parentIndex = parentIndex;
this.instantSearchInstance = instantSearchInstance;
}
ngOnInit() {
this.createWidget(EXPERIMENTAL_connectConfigureRelatedItems, {
hit: this.hit,
matchingPatterns: this.matchingPatterns,
transformSearchParameters: this.transformSearchParameters,
}, {
$$widgetType: 'ais.configureRelatedItems',
});
super.ngOnInit();
}
}
NgAisConfigureRelatedItems.decorators = [
{ type: Component, args: [{
selector: 'ais-experimental-configure-related-items',
template: ''
},] }
];
NgAisConfigureRelatedItems.ctorParameters = () => [
{ type: NgAisIndex, decorators: [{ type: Inject, args: [forwardRef(() => NgAisIndex),] }, { type: Optional }] },
{ type: NgAisInstantSearch, decorators: [{ type: Inject, args: [forwardRef(() => NgAisInstantSearch),] }] }
];
NgAisConfigureRelatedItems.propDecorators = {
hit: [{ type: Input }],
matchingPatterns: [{ type: Input }],
transformSearchParameters: [{ type: Input }]
};
class NgAisConfigureRelatedItemsModule {
}
NgAisConfigureRelatedItemsModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgAisConfigureRelatedItems],
entryComponents: [NgAisConfigureRelatedItems],
exports: [NgAisConfigureRelatedItems],
imports: [CommonModule],
},] }
];
class NgAisQueryRuleCustomData extends TypedBaseWidget {
constructor(parentIndex, instantSearchInstance) {
super('QueryRuleCustomData');
this.parentIndex = parentIndex;
this.instantSearchInstance = instantSearchInstance;
this.state = {
items: [],
};
}
get templateContext() {
return {
items: this.state.items,
};
}
ngOnInit() {
this.createWidget(connectQueryRules, {
transformItems: this.transformItems,
}, {
$$widgetType: 'ais.queryRuleCustomData',
});
super.ngOnInit();
}
}
NgAisQueryRuleCustomData.decorators = [
{ type: Component, args: [{
selector: 'ais-query-rule-custom-data',
template: `
<div [class]='cx()'>
<ng-container *ngTemplateOutlet='template; context: templateContext'>
</ng-container>
<div *ngIf='!template'>
<div *ngFor='let item of state.items'>
<pre>{{ item | json }}</pre>
</div>
</div>
</div>
`
},] }
];
NgAisQueryRuleCustomData.ctorParameters = () => [
{ type: NgAisIndex, decorators: [{ type: Inject, args: [forwardRef(() => NgAisIndex),] }, { type: Optional }] },
{ type: NgAisInstantSearch, decorators: [{ type: Inject, args: [forwardRef(() => NgAisInstantSearch),] }] }
];
NgAisQueryRuleCustomData.propDecorators = {
template: [{ type: ContentChild, args: [TemplateRef, { static: false },] }],
transformItems: [{ type: Input }]
};
class NgAisQueryRuleCustomDataModule {
}
NgAisQueryRuleCustomDataModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgAisQueryRuleCustomData],
entryComponents: [NgAisQueryRuleCustomData],
exports: [NgAisQueryRuleCustomData],
imports: [CommonModule],
},] }
];
class NgAisQueryRuleContext extends TypedBaseWidget {
constructor(parentIndex, instantSearchInstance) {
super('QueryRuleContext');
this.parentIndex = parentIndex;
this.instantSearchInstance = instantSearchInstance;
}
ngOnInit() {
this.createWidget(connectQueryRules, {
trackedFilters: this.trackedFilters,
transformRuleContexts: this.transformRuleContexts,
}, {
$$widgetType: 'ais.queryRuleContext',
});
super.ngOnInit();
}
}
NgAisQueryRuleContext.decorators = [
{ type: Component, args: [{
selector: 'ais-query-rule-context',
template: ''
},] }
];
NgAisQueryRuleContext.ctorParameters = () => [
{ type: NgAisIndex, decorators: [{ type: Inject, args: [forwardRef(() => NgAisIndex),] }, { type: Optional }] },
{ type: NgAisInstantSearch, decorators: [{ type: Inject, args: [forwardRef(() => NgAisInstantSearch),] }] }
];
NgAisQueryRuleContext.propDecorators = {
trackedFilters: [{ type: Input }],
transformRuleContexts: [{ type: Input }]
};
class NgAisQueryRuleContextModule {
}
NgAisQueryRuleContextModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgAisQueryRuleContext],
entryComponents: [NgAisQueryRuleContext],
exports: [NgAisQueryRuleContext],
imports: [CommonModule],
},] }
];
class NgAisVoiceSearch extends TypedBaseWidget {
constructor(parentIndex, instantSearchInstance, zone) {
super('VoiceSearch');
this.parentIndex = parentIndex;
this.instantSearchInstance = instantSearchInstance;
this.zone = zone;
// rendering options
this.buttonTitle = 'Search by voice';
this.disabledButtonTitle = 'Search by voice (not supported on this browser)';
this.state = {
isBrowserSupported: undefined,
isListening: undefined,
toggleListening: noop,
voiceListeningState: {
status: 'initial',
transcript: '',
isSpeechFinal: false,
errorCode: undefined,
},
};
this.templateContext = {
status: 'initial',
errorCode: undefined,
transcript: '',
isSpeechFinal: false,
isListening: false,
isBrowserSupported: false,
};
this.handleClick = (event) => {
event.currentTarget.blur();
this.state.toggleListening();
};
this.isNotAllowedError = () => this.state.voiceListeningState.status === 'error' &&
this.state.voiceListeningState.errorCode === 'not-allowed';
this.updateState = (state) => {
this.zone.run(() => {
this.templateContext = {
status: state.voiceListeningState.status,
errorCode: state.voiceListeningState.errorCode,
transcript: state.voiceListeningState.transcript,
isSpeechFinal: state.voiceListeningState.isSpeechFinal,
isListening: state.isListening,
isBrowserSupported: state.isBrowserSupported,
};
this.state = state;
});
};
}
ngOnInit() {
this.createWidget(connectVoiceSearch, {
searchAsYouSpeak: this.searchAsYouSpeak,
}, {
$$widgetType: 'ais.voiceSearch',
});
super.ngOnInit();
}
}
NgAisVoiceSearch.decorators = [
{ type: Component, args: [{
selector: 'ais-voice-search',
template: `
<div [class]="cx()">
<button
type="button"
[class]="cx('button')"
[title]="state.isBrowserSupported ? buttonTitle : disabledButtonTitle"
[disabled]="!state.isBrowserSupported"
(click)="handleClick($event)"
>
<ng-container *ngTemplateOutlet="button ? button : defaultButton; context: templateContext"></ng-container>
</button>
<div [class]="cx('status')">
<ng-container *ngTemplateOutlet="status ? status : defaultStatus; context: templateContext"></ng-container>
</div>
</div>
<ng-template #defaultButton let-status="status" let-errorCode="errorCode" let-isListening="isListening">
<svg
xmlns='http://www.w3.org/2000/svg'
width='16'
height='16'
viewBox='0 0 24 24'
fill='none'
stroke='currentColor'
strokeWidth='2'
strokeLinecap='round'
strokeLinejoin='round'
>
<ng-container *ngIf="isNotAllowedError(); then errorSvgContent else normalSvgContent">
</ng-container>
<ng-template #errorSvgContent>
<line x1="1" y1="1" x2="23" y2="23"></line>
<path d="M9 9v3a3 3 0 0 0 5.12 2.12M15 9.34V4a3 3 0 0 0-5.94-.6"></path>
<path d="M17 16.95A7 7 0 0 1 5 12v-2m14 0v2a7 7 0 0 1-.11 1.23"></path>
<line x1="12" y1="19" x2="12" y2="23"></line>
<line x1="8" y1="23" x2="16" y2="23"></line>
</ng-template>
<ng-template #normalSvgContent>
<path
d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"
[attr.fill]="isListening ? 'currentColor' : 'none'"
></path>
<path d="M19 10v2a7 7 0 0 1-14 0v-2"></path>
<line x1="12" y1="19" x2="12" y2="23"></line>
<line x1="8" y1="23" x2="16" y2="23"></line>
</ng-template>
</svg>
</ng-template>
<ng-template #defaultStatus let-transcript="transcript">
<p>{{transcript}}</p>
</ng-template>
`
},] }
];
NgAisVoiceSearch.ctorParameters = () => [
{ type: NgAisIndex, decorators: [{ type: Inject, args: [forwardRef(() => NgAisIndex),] }, { type: Optional }] },
{ type: NgAisInstantSearch, decorators: [{ type: Inject, args: [forwardRef(() => NgAisInstantSearch),] }] },
{ type: NgZone }
];
NgAisVoiceSearch.propDecorators = {
button: [{ type: ContentChild, args: ['button', { static: false },] }],
status: [{ type: ContentChild, args: ['status', { static: false },] }],
buttonTitle: [{ type: Input }],
disabledButtonTitle: [{ type: Input }],
searchAsYouSpeak: [{ type: Input }]
};
class NgAisVoiceSearchModule {
}
NgAisVoiceSearchModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgAisVoiceSearch],
entryComponents: [NgAisVoiceSearch],
exports: [NgAisVoiceSearch],
imports: [CommonModule],
},] }
];
// compatibility with different typescript settings:
// - esModuleInterop
// - allowSyntheticDefaultImports
const algoliasearch = (typeof algoliasearchProxy.default === 'function'
? algoliasearchProxy.default
: algoliasearchProxy);
const encode = encodeProxy.default || encodeProxy;
function createSSRSearchClient({ appId, apiKey, httpClient, HttpHeaders, transferState, makeStateKey, options = {}, }) {
// A custom network request needs to be done, using TransferState of Angular.
// This is done to make sure the request done backend for SSR doesn't get
// made again frontend during hydration.
// For compatibility with both v3 and v4 of algoliasearch, we are overriding the
// network request function in two places:
// v4: custom "requester"
// v3: override "_request" on the prototype
// since neither v3 uses the requester argument, and v4 use the _request, we
// can safely do this without checking the version
const searchClient = algoliasearch(appId, apiKey, Object.assign(Object.assign({}, options), { requester: {
send({ headers, method, url, data }) {
const transferStateKey = makeStateKey(`ngais(${data})`);
if (transferState.hasKey(transferStateKey)) {
const response = JSON.parse(transferState.get(transferStateKey, JSON.stringify({})));
return Promise.resolve({
status: response.status,
content: JSON.stringify(response.body),
isTimedOut: false,
});
}
return new Promise((resolve, reject) => {
httpClient
.request(method, url, {
headers,
body: data,
observe: 'response',
})
.subscribe(response => {
transferState.set(transferStateKey, JSON.stringify(response));
resolve({
status: response.status,
content: JSON.stringify(response.body),
isTimedOut: false,
});
}, response => reject({
status: response.status,
body: response.body,
}));
});
},
} }));
searchClient.addAlgoliaAgent(`angular (${VERSION$1.full})`);
searchClient.addAlgoliaAgent(`angular-instantsearch (${VERSION})`);
searchClient.addAlgoliaAgent(`angular-instantsearch-server (${VERSION})`);
searchClient._request = (rawUrl, options) => {
let headers = new HttpHeaders();
headers = headers.set('content-type', options.method === 'POST'
? 'application/x-www-form-urlencoded'
: 'application/json');
headers = headers.set('accept', 'application/json');
const url = rawUrl + (rawUrl.includes('?') ? '&' : '?') + encode(options.headers);
const transferStateKey = makeStateKey(`ngais(${options.body})`);
if (transferState.hasKey(transferStateKey)) {
const response = JSON.parse(transferState.get(transferStateKey, JSON.stringify({})));
return Promise.resolve({
statusCode: response.status,
body: response.body,
headers: response.headers,
});
}
return new Promise((resolve, reject) => {
httpClient
.request(options.method, url, {
headers,
body: options.body,
observe: 'response',
})
.subscribe(response => {
transferState.set(transferStateKey, JSON.stringify(response));
resolve({
statusCode: response.status,
body: response.body,
headers: response.headers,
});
}, response => reject({
statusCode: response.status,
body: response.body,
headers: response.headers,
}));
});
};
return searchClient;
}
class BaseWidget {
constructor(widgetName) {
this.state = {};
this.updateState = (state, isFirstRendering) => {
if (isFirstRendering) {
return Promise.resolve().then(() => {
this.state = state;
});
}
this.state = state;
};
this.cx = bem(widgetName);
}
get parent() {
if (this.parentIndex) {
return this.parentIndex;
}
return this.instantSearchInstance;
}
createWidget(connector, options = {}, additionalWidgetProperties = {}) {
this.widget = Object.assign(Object.assign({}, connector(this.updateState, noop)(options)), additionalWidgetProperties);
}
ngOnInit() {
this.parent.addWidgets([this.widget]);
}
ngOnDestroy() {
if (isPlatformBrowser(this.instantSearchInstance.platformId)) {
this.parent.removeWidgets([this.widget]);
}
}
/**
* Helper to generate class names for an item
* @param item element to generate a class name for
*/
getItemClass(item) {
const className = this.cx('item');
if (item.isRefined) {
return `${className} ${this.cx('item', 'selected')}`;
}
return className;
}
}
BaseWidget.propDecorators = {
autoHideContainer: [{ type: Input }]
};
const NGIS_MODULES = [
NgAisInstantSearchModule,
NgAisIndexModule,
NgAisHitsModule,
NgAisSearchBoxModule,
NgAisClearRefinementsModule,
NgAisMenuModule,
NgAisPaginationModule,
NgAisRefinementListModule,
NgAisHitsPerPageModule,
NgAisSortByModule,
NgAisNumericMenuModule,
NgAisStatsModule,
NgAisToggleModule,
NgAisInfiniteHitsModule,
NgAisCurrentRefinementsModule,
NgAisHierarchicalMenuModule,
NgAisRatingMenuModule,
NgAisRangeSliderModule,
NgAisBreadcrumbModule,
NgAisHighlightModule,
NgAisReverseHighlightModule,
NgAisSnippetModule,
NgAisReverseSnippetModule,
NgAisRangeInputModule,
NgAisPanelModule,
NgAisConfigureModule,
NgAisConfigureRelatedItemsModule,
NgAisQueryRuleCustomDataModule,
NgAisQueryRuleContextModule,
NgAisVoiceSearchModule,
];
class NgAisRootModule {
}
NgAisRootModule.decorators = [
{ type: NgModule, args: [{
exports: NGIS_MODULES,
imports: [NgAisInstantSearchModule.forRoot()],
},] }
];
class NgAisModule {
static forRoot() {
return { ngModule: NgAisRootModule };
}
}
NgAisModule.decorators = [
{ type: NgModule, args: [{ imports: NGIS_MODULES, exports: NGIS_MODULES },] }
];
/**
* Generated bundle index. Do not edit.
*/
export { BaseWidget, NgAisBreadcrumbModule, NgAisClearRefinementsModule, NgAisConfigureModule, NgAisConfigureRelatedItemsModule, NgAisCurrentRefinementsModule, NgAisHierarchicalMenuModule, NgAisHighlightModule, NgAisHitsModule, NgAisHitsPerPageModule, NgAisIndex, NgAisIndexModule, NgAisInfiniteHitsModule, NgAisInstantSearch, NgAisInstantSearchModule, NgAisMenuModule, NgAisModule, NgAisNumericMenuModule, NgAisPaginationModule, NgAisPanelModule, NgAisQueryRuleContextModule, NgAisQueryRuleCustomDataModule, NgAisRangeInputModule, NgAisRangeSliderModule, NgAisRatingMenuModule, NgAisRefinementListModule, NgAisReverseHighlightModule, NgAisReverseSnippetModule, NgAisRootModule, NgAisSearchBoxModule, NgAisSnippetModule, NgAisSortByModule, NgAisStatsModule, NgAisToggleModule, NgAisVoiceSearchModule, TypedBaseWidget, createSSRSearchClient, NgAisPanel as ɵa, NgAisBreadcrumb as ɵb, NgAisConfigureRelatedItems as ɵba, NgAisQueryRuleCustomData as ɵbb, NgAisQueryRuleContext as ɵbc, NgAisVoiceSearch as ɵbd, NgAisClearRefinements as ɵc, NgAisCurrentRefinements as ɵd, NgAisHierarchicalMenu as ɵe, NgAisHierarchicalMenuItem as ɵf, NgAisHitsPerPage as ɵg, NgAisHits as ɵh, NgAisHighlight as ɵi, NgAisInfiniteHits as ɵj, NgAisMenu as ɵk, NgAisNumericMenu as ɵl, NgAisPagination as ɵm, NgAisRangeSlider as ɵn, NgAisRefinementList as ɵo, NgAisFacetsSearch as ɵp, NgAisSearchBox as ɵq, NgAisSortBy as ɵr, NgAisRatingMenu as ɵs, NgAisStats as ɵt, NgAisToggle as ɵu, NgAisReverseHighlight as ɵv, NgAisSnippet as ɵw, NgAisReverseSnippet as ɵx, NgAisRangeInput as ɵy, NgAisConfigure as ɵz };
//# sourceMappingURL=angular-instantsearch.js.map