diff --git a/examples/js/app.ts b/examples/js/app.ts index af30a0dee..9b48408a3 100644 --- a/examples/js/app.ts +++ b/examples/js/app.ts @@ -6,6 +6,8 @@ import { getAlgoliaHits, reverseHighlightItem, } from '@algolia/autocomplete-js'; +import { createRecentSearchesPlugin } from '@algolia/autocomplete-plugin-recent-searches'; +import '@algolia/autocomplete-plugin-recent-searches/style'; const searchClient = algoliasearch( 'latency', @@ -14,10 +16,14 @@ const searchClient = algoliasearch( type QuerySuggestionHit = { query: string }; +const recentSearches = createRecentSearchesPlugin({ key: 'recent' }); + autocomplete>({ container: '#autocomplete', debug: true, + openOnFocus: true, // dropdownPlacement: 'start', + plugins: [recentSearches], getSources({ query }) { return getAlgoliaHits({ searchClient, @@ -27,6 +33,7 @@ autocomplete>({ query, params: { hitsPerPage: 10, + facetFilters: [...recentSearches.data.getFacetFilters()], }, }, ], diff --git a/examples/js/package.json b/examples/js/package.json index eb1874df7..aa40b8208 100644 --- a/examples/js/package.json +++ b/examples/js/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "@algolia/autocomplete-js": "^1.0.0-alpha.32", + "@algolia/autocomplete-plugin-recent-searches": "^1.0.0-alpha.32", "@algolia/client-search": "4.5.1", "algoliasearch": "4.5.1" }, diff --git a/packages/autocomplete-core/src/createAutocomplete.ts b/packages/autocomplete-core/src/createAutocomplete.ts index 71f77eddd..b4db7df5c 100644 --- a/packages/autocomplete-core/src/createAutocomplete.ts +++ b/packages/autocomplete-core/src/createAutocomplete.ts @@ -43,6 +43,7 @@ export function createAutocomplete< setIsOpen, setStatus, setContext, + refresh, }); function refresh() { @@ -57,6 +58,7 @@ export function createAutocomplete< setIsOpen, setStatus, setContext, + refresh, }); } diff --git a/packages/autocomplete-core/src/getPropGetters.ts b/packages/autocomplete-core/src/getPropGetters.ts index 2c9ab0de8..912d2a09d 100644 --- a/packages/autocomplete-core/src/getPropGetters.ts +++ b/packages/autocomplete-core/src/getPropGetters.ts @@ -4,6 +4,7 @@ import { InternalAutocompleteOptions, AutocompleteSetters, AutocompleteStore, + AutocompleteRefresh, GetDropdownProps, GetEnvironmentProps, GetFormProps, @@ -18,6 +19,7 @@ import { getHighlightedItem, isOrContainsNode } from './utils'; interface GetPropGettersOptions extends AutocompleteSetters { store: AutocompleteStore; props: InternalAutocompleteOptions; + refresh: AutocompleteRefresh; } export function getPropGetters({ @@ -29,6 +31,7 @@ export function getPropGetters({ setIsOpen, setStatus, setContext, + refresh, }: GetPropGettersOptions) { const getEnvironmentProps: GetEnvironmentProps = (getterProps) => { return { @@ -135,6 +138,7 @@ export function getPropGetters({ setIsOpen, setStatus, setContext, + refresh, }); } store.send('reset', null); @@ -165,6 +169,7 @@ export function getPropGetters({ setIsOpen, setStatus, setContext, + refresh, }); } @@ -205,6 +210,7 @@ export function getPropGetters({ setIsOpen, setStatus, setContext, + refresh, }); }, onKeyDown: (event) => { @@ -218,6 +224,7 @@ export function getPropGetters({ setIsOpen, setStatus, setContext, + refresh, }); }, onFocus, @@ -351,6 +358,7 @@ export function getPropGetters({ setIsOpen, setStatus, setContext, + refresh, nextState: { isOpen: false, }, diff --git a/packages/autocomplete-core/src/onInput.ts b/packages/autocomplete-core/src/onInput.ts index 3570e287d..b7eae731f 100644 --- a/packages/autocomplete-core/src/onInput.ts +++ b/packages/autocomplete-core/src/onInput.ts @@ -3,6 +3,7 @@ import { AutocompleteSetters, AutocompleteState, AutocompleteStore, + AutocompleteRefresh, } from './types'; import { getHighlightedItem } from './utils'; @@ -21,6 +22,7 @@ interface OnInputParams extends AutocompleteSetters { * but we want to close the dropdown in that case. */ nextState?: Partial>; + refresh: AutocompleteRefresh; } export function onInput({ @@ -35,6 +37,7 @@ export function onInput({ setStatus, setContext, nextState = {}, + refresh, }: OnInputParams): Promise { if (props.onInput) { return Promise.resolve( @@ -47,6 +50,7 @@ export function onInput({ setIsOpen, setStatus, setContext, + refresh, }) ); } @@ -89,6 +93,7 @@ export function onInput({ setIsOpen, setStatus, setContext, + refresh, }) .then((sources) => { setStatus('loading'); @@ -106,6 +111,7 @@ export function onInput({ setIsOpen, setStatus, setContext, + refresh, }) ).then((items) => { return { diff --git a/packages/autocomplete-core/src/onKeyDown.ts b/packages/autocomplete-core/src/onKeyDown.ts index bceda1491..74bef2078 100644 --- a/packages/autocomplete-core/src/onKeyDown.ts +++ b/packages/autocomplete-core/src/onKeyDown.ts @@ -4,6 +4,7 @@ import { InternalAutocompleteOptions, AutocompleteSetters, AutocompleteStore, + AutocompleteRefresh, } from './types'; import { getHighlightedItem } from './utils'; @@ -11,6 +12,7 @@ interface OnKeyDownOptions extends AutocompleteSetters { event: KeyboardEvent; store: AutocompleteStore; props: InternalAutocompleteOptions; + refresh: AutocompleteRefresh; } export function onKeyDown({ @@ -23,6 +25,7 @@ export function onKeyDown({ setIsOpen, setStatus, setContext, + refresh, }: OnKeyDownOptions): void { if (event.key === 'ArrowUp' || event.key === 'ArrowDown') { // Default browser behavior changes the caret placement on ArrowUp and @@ -91,6 +94,7 @@ export function onKeyDown({ setIsOpen, setStatus, setContext, + refresh, }); } } else if (event.key === 'Escape') { @@ -205,6 +209,7 @@ export function onKeyDown({ nextState: { isOpen: false, }, + refresh, }).then(() => { source.onSelect({ suggestion: item, diff --git a/packages/autocomplete-core/src/types/api.ts b/packages/autocomplete-core/src/types/api.ts index 4dbf86804..2b2a8e872 100644 --- a/packages/autocomplete-core/src/types/api.ts +++ b/packages/autocomplete-core/src/types/api.ts @@ -17,9 +17,11 @@ export interface AutocompleteApi< /** * Triggers a search to refresh the state. */ - refresh(): Promise; + refresh: AutocompleteRefresh; } +export type AutocompleteRefresh = () => Promise; + export interface AutocompleteSuggestion { source: InternalAutocompleteSource; items: TItem[]; @@ -28,6 +30,7 @@ export interface AutocompleteSuggestion { export interface GetSourcesParams extends AutocompleteSetters { query: string; state: AutocompleteState; + refresh: AutocompleteRefresh; } interface ItemParams { @@ -58,6 +61,7 @@ interface OnSubmitParams extends AutocompleteSetters { interface OnInputParams extends AutocompleteSetters { query: string; state: AutocompleteState; + refresh: AutocompleteRefresh; } export interface AutocompleteSource { diff --git a/packages/autocomplete-plugin-recent-searches/package.json b/packages/autocomplete-plugin-recent-searches/package.json index 18117c157..bc3d271d7 100644 --- a/packages/autocomplete-plugin-recent-searches/package.json +++ b/packages/autocomplete-plugin-recent-searches/package.json @@ -1,7 +1,7 @@ { "name": "@algolia/autocomplete-plugin-recent-searches", "description": "A plugin to add recent searches to Algolia Autocomplete.", - "version": "1.0.0-alpha.30", + "version": "1.0.0-alpha.32", "license": "MIT", "source": "src/index.ts", "types": "dist/esm/index.d.ts", @@ -15,9 +15,9 @@ }, "scripts": { "build:clean": "rm -rf ./dist", - "build:esm": "babel src --root-mode upward --extensions '.ts,.tsx' --out-dir dist/esm --ignore '**/*/__tests__/'", + "build:esm": "babel src --root-mode upward --extensions '.ts,.tsx' --out-dir dist/esm --ignore '**/*/__tests__/' && cp src/style.css ./dist/esm/", "build:types": "tsc -p ./tsconfig.declaration.json --outDir ./dist/esm", - "build:umd": "rollup --config", + "build:umd": "rollup --config && cp src/style.css ./dist/umd/", "build": "rm -rf ./dist && yarn build:umd && yarn build:esm && yarn build:types", "on:change": "concurrently \"yarn build:esm\" \"yarn build:types\"", "prepare": "yarn run build:esm", @@ -31,7 +31,8 @@ }, "sideEffects": false, "files": [ - "dist/" + "dist/", + "style/" ], "dependencies": { "@algolia/autocomplete-core": "^1.0.0-alpha.31" diff --git a/packages/autocomplete-plugin-recent-searches/src/createRecentSearchesPlugin.ts b/packages/autocomplete-plugin-recent-searches/src/createRecentSearchesPlugin.ts index 5aa93c873..1547ab5c6 100644 --- a/packages/autocomplete-plugin-recent-searches/src/createRecentSearchesPlugin.ts +++ b/packages/autocomplete-plugin-recent-searches/src/createRecentSearchesPlugin.ts @@ -1,6 +1,8 @@ import { AutocompletePlugin } from '@algolia/autocomplete-core'; import { createRecentSearchesStore } from './createRecentSearchesStore'; +import { recentIcon } from './recentIcon'; +import { resetIcon } from './resetIcon'; type PluginOptions = { /** @@ -38,7 +40,7 @@ export function createRecentSearchesPlugin({ }); return { - getSources: ({ query }) => { + getSources: ({ query, refresh }) => { if (query) { return []; } @@ -50,19 +52,36 @@ export function createRecentSearchesPlugin({ return store.getAll(); }, templates: { - item({ item }) { - return ` -
-
- - - - + item({ item, root }) { + const container = document.createElement('div'); + container.className = 'aa-RecentSearchesItem'; - ${item.query} -
-
- `; + const leftItems = document.createElement('div'); + leftItems.className = 'leftItems'; + const icon = document.createElement('div'); + icon.className = 'item-icon icon'; + icon.innerHTML = recentIcon; + const title = document.createElement('div'); + title.className = 'title'; + title.innerText = item.query; + leftItems.appendChild(icon); + leftItems.appendChild(title); + + const removeButton = document.createElement('button'); + removeButton.className = 'item-icon removeButton'; + removeButton.type = 'button'; + removeButton.innerHTML = resetIcon; + removeButton.title = 'Remove'; + + container.appendChild(leftItems); + container.appendChild(removeButton); + root.appendChild(container); + + removeButton.addEventListener('click', (event) => { + event.stopPropagation(); + store.remove(item); + refresh(); + }); }, }, }, diff --git a/packages/autocomplete-plugin-recent-searches/src/recentIcon.ts b/packages/autocomplete-plugin-recent-searches/src/recentIcon.ts new file mode 100644 index 000000000..a7b27f4f6 --- /dev/null +++ b/packages/autocomplete-plugin-recent-searches/src/recentIcon.ts @@ -0,0 +1,4 @@ +export const recentIcon = ` + + +`; diff --git a/packages/autocomplete-plugin-recent-searches/src/resetIcon.ts b/packages/autocomplete-plugin-recent-searches/src/resetIcon.ts new file mode 100644 index 000000000..1dd6aa7e6 --- /dev/null +++ b/packages/autocomplete-plugin-recent-searches/src/resetIcon.ts @@ -0,0 +1,11 @@ +// copied from autocomplete-js +export const resetIcon = ` + +`; diff --git a/packages/autocomplete-plugin-recent-searches/src/style.css b/packages/autocomplete-plugin-recent-searches/src/style.css new file mode 100644 index 000000000..5df365480 --- /dev/null +++ b/packages/autocomplete-plugin-recent-searches/src/style.css @@ -0,0 +1,21 @@ +.aa-RecentSearchesItem { + display: flex; + flex-grow: 1; + justify-content: space-between; +} + +.aa-RecentSearchesItem .leftItems { + display: flex; +} + +.aa-RecentSearchesItem .removeButton { + border: 0; + background-color: transparent; + cursor: pointer; + opacity: 0.8; +} + +.aa-RecentSearchesItem .removeButton:hover { + opacity: 1; + color: #000; +} diff --git a/packages/autocomplete-plugin-recent-searches/style/index.js b/packages/autocomplete-plugin-recent-searches/style/index.js new file mode 100644 index 000000000..722a95168 --- /dev/null +++ b/packages/autocomplete-plugin-recent-searches/style/index.js @@ -0,0 +1 @@ +export * from '../dist/esm/style.css';