-
Notifications
You must be signed in to change notification settings - Fork 331
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(core): introduce Reshape API (#647)
- Loading branch information
1 parent
0a70513
commit d6180d2
Showing
33 changed files
with
951 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
/** @jsx h */ | ||
import { autocomplete } from '@algolia/autocomplete-js'; | ||
import { createQuerySuggestionsPlugin } from '@algolia/autocomplete-plugin-query-suggestions'; | ||
import { createLocalStorageRecentSearchesPlugin } from '@algolia/autocomplete-plugin-recent-searches'; | ||
import { h, Fragment } from 'preact'; | ||
import { pipe } from 'ramda'; | ||
|
||
import '@algolia/autocomplete-theme-classic'; | ||
|
||
import { groupBy, limit, uniqBy } from './functions'; | ||
import { productsPlugin } from './productsPlugin'; | ||
import { searchClient } from './searchClient'; | ||
|
||
const recentSearchesPlugin = createLocalStorageRecentSearchesPlugin({ | ||
key: 'search', | ||
limit: 10, | ||
}); | ||
const querySuggestionsPlugin = createQuerySuggestionsPlugin({ | ||
searchClient, | ||
indexName: 'instant_search_demo_query_suggestions', | ||
getSearchParams() { | ||
return { | ||
hitsPerPage: 10, | ||
}; | ||
}, | ||
}); | ||
|
||
const dedupeAndLimitSuggestions = pipe( | ||
uniqBy(({ source, item }) => | ||
source.sourceId === 'querySuggestionsPlugin' ? item.query : item.label | ||
), | ||
limit(4) | ||
); | ||
const groupByCategory = groupBy((hit) => hit.categories[0], { | ||
getSource({ name, items }) { | ||
return { | ||
getItems() { | ||
return items.slice(0, 3); | ||
}, | ||
templates: { | ||
header() { | ||
return ( | ||
<Fragment> | ||
<span className="aa-SourceHeaderTitle">{name}</span> | ||
<div className="aa-SourceHeaderLine" /> | ||
</Fragment> | ||
); | ||
}, | ||
}, | ||
}; | ||
}, | ||
}); | ||
|
||
autocomplete({ | ||
container: '#autocomplete', | ||
placeholder: 'Search', | ||
debug: true, | ||
openOnFocus: true, | ||
plugins: [recentSearchesPlugin, querySuggestionsPlugin, productsPlugin], | ||
reshape({ sourcesBySourceId }) { | ||
const { | ||
recentSearchesPlugin, | ||
querySuggestionsPlugin, | ||
products, | ||
...rest | ||
} = sourcesBySourceId; | ||
|
||
return [ | ||
dedupeAndLimitSuggestions(recentSearchesPlugin, querySuggestionsPlugin), | ||
groupByCategory(products), | ||
Object.values(rest), | ||
]; | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import * as preact from 'preact'; | ||
|
||
// Parcel picks the `source` field of the monorepo packages and thus doesn't | ||
// apply the Babel config. We therefore need to manually override the constants | ||
// in the app, as well as the React pragmas. | ||
// See https://twitter.com/devongovett/status/1134231234605830144 | ||
(global as any).__DEV__ = process.env.NODE_ENV !== 'production'; | ||
(global as any).__TEST__ = false; | ||
(global as any).h = preact.h; | ||
(global as any).React = preact; |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { | ||
AutocompleteReshapeSource, | ||
BaseItem, | ||
} from '@algolia/autocomplete-core'; | ||
|
||
export type AutocompleteReshapeFunction<TParams = any> = < | ||
TItem extends BaseItem | ||
>( | ||
...params: TParams[] | ||
) => ( | ||
...expressions: Array<AutocompleteReshapeSource<TItem>> | ||
) => Array<AutocompleteReshapeSource<TItem>>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { BaseItem } from '@algolia/autocomplete-core'; | ||
import { AutocompleteSource } from '@algolia/autocomplete-js'; | ||
import { flatten } from '@algolia/autocomplete-shared'; | ||
|
||
import { AutocompleteReshapeFunction } from './AutocompleteReshapeFunction'; | ||
import { normalizeReshapeSources } from './normalizeReshapeSources'; | ||
|
||
export type GroupByOptions< | ||
TItem extends BaseItem, | ||
TSource extends AutocompleteSource<TItem> | ||
> = { | ||
getSource(params: { name: string; items: TItem[] }): Partial<TSource>; | ||
}; | ||
|
||
export const groupBy: AutocompleteReshapeFunction = < | ||
TItem extends BaseItem, | ||
TSource extends AutocompleteSource<TItem> = AutocompleteSource<TItem> | ||
>( | ||
predicate: (value: TItem) => string, | ||
options: GroupByOptions<TItem, TSource> | ||
) => { | ||
return function runGroupBy(...rawSources) { | ||
const sources = normalizeReshapeSources(rawSources); | ||
|
||
if (sources.length === 0) { | ||
return []; | ||
} | ||
|
||
// Since we create multiple sources from a single one, we take the first one | ||
// as reference to create the new sources from. | ||
const referenceSource = sources[0]; | ||
const items = flatten(sources.map((source) => source.getItems())); | ||
const groupedItems = items.reduce<Record<string, TItem[]>>((acc, item) => { | ||
const key = predicate(item as TItem); | ||
|
||
if (!acc.hasOwnProperty(key)) { | ||
acc[key] = []; | ||
} | ||
|
||
acc[key].push(item as TItem); | ||
|
||
return acc; | ||
}, {}); | ||
|
||
return Object.entries(groupedItems).map(([groupName, groupItems]) => { | ||
const userSource = options.getSource({ | ||
name: groupName, | ||
items: groupItems, | ||
}); | ||
|
||
return { | ||
...referenceSource, | ||
sourceId: groupName, | ||
getItems() { | ||
return groupItems; | ||
}, | ||
...userSource, | ||
templates: { | ||
...((referenceSource as any).templates as any), | ||
...(userSource as any).templates, | ||
}, | ||
}; | ||
}); | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export * from './groupBy'; | ||
export * from './limit'; | ||
export * from './uniqBy'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { AutocompleteReshapeFunction } from './AutocompleteReshapeFunction'; | ||
import { normalizeReshapeSources } from './normalizeReshapeSources'; | ||
|
||
export const limit: AutocompleteReshapeFunction<number> = (value) => { | ||
return function runLimit(...rawSources) { | ||
const sources = normalizeReshapeSources(rawSources); | ||
const limitPerSource = Math.ceil(value / sources.length); | ||
let sharedLimitRemaining = value; | ||
|
||
return sources.map((source, index) => { | ||
const isLastSource = index === sources.length - 1; | ||
const sourceLimit = isLastSource | ||
? sharedLimitRemaining | ||
: Math.min(limitPerSource, sharedLimitRemaining); | ||
const items = source.getItems().slice(0, sourceLimit); | ||
sharedLimitRemaining = Math.max(sharedLimitRemaining - items.length, 0); | ||
|
||
return { | ||
...source, | ||
getItems() { | ||
return items; | ||
}, | ||
}; | ||
}); | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { | ||
AutocompleteReshapeSource, | ||
BaseItem, | ||
} from '@algolia/autocomplete-core'; | ||
import { flatten } from '@algolia/autocomplete-shared'; | ||
|
||
// We filter out falsy values because dynamic sources may not exist at every render. | ||
// We flatten to support pipe operators from functional libraries like Ramda. | ||
export function normalizeReshapeSources<TItem extends BaseItem>( | ||
sources: Array<AutocompleteReshapeSource<TItem>> | ||
) { | ||
return flatten(sources).filter(Boolean); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { | ||
AutocompleteReshapeSource, | ||
BaseItem, | ||
} from '@algolia/autocomplete-core'; | ||
|
||
import { AutocompleteReshapeFunction } from './AutocompleteReshapeFunction'; | ||
import { normalizeReshapeSources } from './normalizeReshapeSources'; | ||
|
||
type UniqByPredicate<TItem extends BaseItem> = (params: { | ||
source: AutocompleteReshapeSource<TItem>; | ||
item: TItem; | ||
}) => TItem; | ||
|
||
export const uniqBy: AutocompleteReshapeFunction<UniqByPredicate<any>> = < | ||
TItem extends BaseItem | ||
>( | ||
predicate | ||
) => { | ||
return function runUniqBy(...rawSources) { | ||
const sources = normalizeReshapeSources(rawSources); | ||
const seen = new Set<TItem>(); | ||
|
||
return sources.map((source) => { | ||
const items = source.getItems().filter((item) => { | ||
const appliedItem = predicate({ source, item }); | ||
const hasSeen = seen.has(appliedItem); | ||
|
||
seen.add(appliedItem); | ||
|
||
return !hasSeen; | ||
}); | ||
|
||
return { | ||
...source, | ||
getItems() { | ||
return items; | ||
}, | ||
}; | ||
}); | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
|
||
<link rel="shortcut icon" href="favicon.png" type="image/x-icon" /> | ||
<link rel="stylesheet" href="style.css" /> | ||
|
||
<title>Reshape API | Autocomplete</title> | ||
</head> | ||
|
||
<body> | ||
<div class="container"> | ||
<div id="autocomplete"></div> | ||
</div> | ||
|
||
<script src="env.ts"></script> | ||
<script src="app.tsx"></script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
{ | ||
"name": "@algolia/autocomplete-example-reshape", | ||
"description": "Autocomplete example with the Reshape API", | ||
"version": "1.2.2", | ||
"private": true, | ||
"license": "MIT", | ||
"scripts": { | ||
"build": "parcel build index.html", | ||
"start": "parcel index.html" | ||
}, | ||
"dependencies": { | ||
"@algolia/autocomplete-js": "1.2.2", | ||
"@algolia/autocomplete-plugin-query-suggestions": "1.2.2", | ||
"@algolia/autocomplete-plugin-recent-searches": "1.2.2", | ||
"@algolia/autocomplete-preset-algolia": "1.2.2", | ||
"@algolia/autocomplete-shared": "1.2.2", | ||
"@algolia/autocomplete-theme-classic": "1.2.2", | ||
"@algolia/client-search": "4.9.1", | ||
"algoliasearch": "4.9.1", | ||
"preact": "10.5.13", | ||
"ramda": "0.27.1", | ||
"search-insights": "1.7.1" | ||
}, | ||
"devDependencies": { | ||
"@algolia/autocomplete-core": "1.2.2", | ||
"parcel": "2.0.0-beta.2" | ||
}, | ||
"keywords": [ | ||
"algolia", | ||
"autocomplete", | ||
"javascript" | ||
] | ||
} |
Oops, something went wrong.