From ad630539c444417f414e0e8bcf74fd20f7cd73c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Chalifour?= Date: Tue, 31 Mar 2020 17:08:28 +0200 Subject: [PATCH] feat(docsearch): add DocSearch for Docusaurus (#39) Co-authored-by: eunjae-lee Co-authored-by: Shipow --- .eslintrc.js | 1 + package.json | 15 +- packages/autocomplete-core/src/onKeyDown.ts | 14 +- packages/autocomplete-core/src/propGetters.ts | 11 +- packages/docsearch-react/babel.config.js | 27 + packages/docsearch-react/package.json | 44 ++ packages/docsearch-react/rollup.config.js | 20 + packages/docsearch-react/src/DocSearch.tsx | 235 ++++++++ .../docsearch-react/src/Dropdown/Dropdown.tsx | 38 ++ .../docsearch-react/src/Dropdown/index.ts | 1 + packages/docsearch-react/src/Error/Error.tsx | 10 + packages/docsearch-react/src/Error/index.ts | 1 + .../src/Footer/AlgoliaLogo.tsx | 20 + .../docsearch-react/src/Footer/Footer.tsx | 64 +++ packages/docsearch-react/src/Footer/index.ts | 1 + packages/docsearch-react/src/Footer/index.tsx | 1 + .../src/NoResults/NoResults.tsx | 12 + .../docsearch-react/src/NoResults/index.ts | 1 + .../src/Results/ActionIcon.tsx | 31 + .../docsearch-react/src/Results/Results.tsx | 147 +++++ .../src/Results/SourceIcon.tsx | 58 ++ packages/docsearch-react/src/Results/index.ts | 1 + .../src/SearchBox/LoadingIcon.tsx | 23 + .../src/SearchBox/ResetIcon.tsx | 13 + .../src/SearchBox/SearchBox.tsx | 74 +++ .../src/SearchBox/SearchIcon.tsx | 17 + .../docsearch-react/src/SearchBox/index.tsx | 1 + .../src/SearchButton/SearchButton.tsx | 57 ++ .../docsearch-react/src/SearchButton/index.ts | 1 + packages/docsearch-react/src/index.ts | 2 + packages/docsearch-react/src/style.css | 543 ++++++++++++++++++ .../src/svg/icon_action_external.svg | 11 + .../src/svg/icon_action_goto.svg | 14 + .../src/svg/icon_source_anchor.svg | 14 + .../src/svg/icon_source_content.svg | 13 + .../src/svg/icon_source_page.svg | 11 + .../src/svg/icon_source_recent.svg | 16 + .../src/svg/icon_tree_child.svg | 12 + .../src/svg/icon_tree_last-child.svg | 12 + .../src/svg/light_footer_key_arrow-down.svg | 12 + .../src/svg/light_footer_key_arrow-up.svg | 12 + .../src/svg/light_footer_key_enter.svg | 14 + .../src/svg/light_footer_key_esc.svg | 11 + .../docsearch-react/src/svg/logo-alogia.svg | 11 + .../docsearch-react/src/types/DocSearchHit.ts | 81 +++ .../src/types/InternalDocSearchHit.tsx | 5 + packages/docsearch-react/src/types/index.ts | 2 + .../src/utils/createSearchClient.ts | 13 + packages/docsearch-react/src/utils/groupBy.ts | 16 + packages/docsearch-react/src/utils/index.ts | 3 + packages/docsearch-react/src/utils/noop.ts | 1 + packages/docsearch-react/src/version.ts | 1 + .../docsearch-react/tsconfig.declaration.json | 8 + packages/docsearch-react/tsconfig.json | 3 + packages/website/docusaurus.config.js | 4 + packages/website/package.json | 1 + packages/website/src/pages/index.js | 3 +- packages/website/src/theme/SearchBar/index.js | 93 +++ yarn.lock | 124 ++++ 59 files changed, 1990 insertions(+), 15 deletions(-) create mode 100644 packages/docsearch-react/babel.config.js create mode 100644 packages/docsearch-react/package.json create mode 100644 packages/docsearch-react/rollup.config.js create mode 100644 packages/docsearch-react/src/DocSearch.tsx create mode 100644 packages/docsearch-react/src/Dropdown/Dropdown.tsx create mode 100644 packages/docsearch-react/src/Dropdown/index.ts create mode 100644 packages/docsearch-react/src/Error/Error.tsx create mode 100644 packages/docsearch-react/src/Error/index.ts create mode 100644 packages/docsearch-react/src/Footer/AlgoliaLogo.tsx create mode 100644 packages/docsearch-react/src/Footer/Footer.tsx create mode 100644 packages/docsearch-react/src/Footer/index.ts create mode 100644 packages/docsearch-react/src/Footer/index.tsx create mode 100644 packages/docsearch-react/src/NoResults/NoResults.tsx create mode 100644 packages/docsearch-react/src/NoResults/index.ts create mode 100644 packages/docsearch-react/src/Results/ActionIcon.tsx create mode 100644 packages/docsearch-react/src/Results/Results.tsx create mode 100644 packages/docsearch-react/src/Results/SourceIcon.tsx create mode 100644 packages/docsearch-react/src/Results/index.ts create mode 100644 packages/docsearch-react/src/SearchBox/LoadingIcon.tsx create mode 100644 packages/docsearch-react/src/SearchBox/ResetIcon.tsx create mode 100644 packages/docsearch-react/src/SearchBox/SearchBox.tsx create mode 100644 packages/docsearch-react/src/SearchBox/SearchIcon.tsx create mode 100644 packages/docsearch-react/src/SearchBox/index.tsx create mode 100644 packages/docsearch-react/src/SearchButton/SearchButton.tsx create mode 100644 packages/docsearch-react/src/SearchButton/index.ts create mode 100644 packages/docsearch-react/src/index.ts create mode 100644 packages/docsearch-react/src/style.css create mode 100644 packages/docsearch-react/src/svg/icon_action_external.svg create mode 100644 packages/docsearch-react/src/svg/icon_action_goto.svg create mode 100644 packages/docsearch-react/src/svg/icon_source_anchor.svg create mode 100644 packages/docsearch-react/src/svg/icon_source_content.svg create mode 100644 packages/docsearch-react/src/svg/icon_source_page.svg create mode 100644 packages/docsearch-react/src/svg/icon_source_recent.svg create mode 100644 packages/docsearch-react/src/svg/icon_tree_child.svg create mode 100644 packages/docsearch-react/src/svg/icon_tree_last-child.svg create mode 100644 packages/docsearch-react/src/svg/light_footer_key_arrow-down.svg create mode 100644 packages/docsearch-react/src/svg/light_footer_key_arrow-up.svg create mode 100644 packages/docsearch-react/src/svg/light_footer_key_enter.svg create mode 100644 packages/docsearch-react/src/svg/light_footer_key_esc.svg create mode 100644 packages/docsearch-react/src/svg/logo-alogia.svg create mode 100644 packages/docsearch-react/src/types/DocSearchHit.ts create mode 100644 packages/docsearch-react/src/types/InternalDocSearchHit.tsx create mode 100644 packages/docsearch-react/src/types/index.ts create mode 100644 packages/docsearch-react/src/utils/createSearchClient.ts create mode 100644 packages/docsearch-react/src/utils/groupBy.ts create mode 100644 packages/docsearch-react/src/utils/index.ts create mode 100644 packages/docsearch-react/src/utils/noop.ts create mode 100644 packages/docsearch-react/src/version.ts create mode 100644 packages/docsearch-react/tsconfig.declaration.json create mode 100644 packages/docsearch-react/tsconfig.json create mode 100644 packages/website/src/theme/SearchBar/index.js diff --git a/.eslintrc.js b/.eslintrc.js index 03a278837..67805eb54 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -28,6 +28,7 @@ module.exports = { '@typescript-eslint/camelcase': ['error', { allow: ['__autocomplete_id'] }], // Useful to call functions like `nodeItem?.scrollIntoView()`. 'no-unused-expressions': 0, + complexity: 0, }, overrides: [ { diff --git a/package.json b/package.json index d28284c30..33acaf32e 100644 --- a/package.json +++ b/package.json @@ -2,8 +2,7 @@ "name": "@francoischalifour/autocomplete-monorepo", "private": true, "workspaces": [ - "packages/*", - "website" + "packages/*" ], "scripts": { "prepare": "yarn run build", @@ -13,12 +12,12 @@ "lint": "eslint --ext .js,.ts,.tsx .", "storybook": "start-storybook --quiet --port 6006 --ci --static-dir .storybook/static", "storybook:build": "build-storybook --quiet --output-dir packages/website/build/stories --static-dir .storybook/static", - "build:clean": "lerna run build:clean --scope @francoischalifour/autocomplete-*", - "build:umd": "lerna run build:umd --scope @francoischalifour/autocomplete-*", - "build:esm": "lerna run build:esm --scope @francoischalifour/autocomplete-*", - "build:types": "lerna run build:types --scope @francoischalifour/autocomplete-*", - "build": "lerna run build --scope @francoischalifour/autocomplete-*", - "watch": "lerna run watch --parallel --scope @francoischalifour/autocomplete-*", + "build:clean": "lerna run build:clean --scope @francoischalifour/autocomplete-* --scope docsearch-react", + "build:umd": "lerna run build:umd --scope @francoischalifour/autocomplete-* --scope docsearch-react", + "build:esm": "lerna run build:esm --scope @francoischalifour/autocomplete-* --scope docsearch-react", + "build:types": "lerna run build:types --scope @francoischalifour/autocomplete-* --scope docsearch-react", + "build": "lerna run build --scope @francoischalifour/autocomplete-* --scope docsearch-react", + "watch": "lerna run watch --parallel --scope @francoischalifour/autocomplete-* --scope docsearch-react", "dev": "yarn build && yarn watch-and-storybook", "watch-and-storybook": "concurrently \"yarn run watch\" \"yarn run storybook\"", "type-check": "tsc --noEmit", diff --git a/packages/autocomplete-core/src/onKeyDown.ts b/packages/autocomplete-core/src/onKeyDown.ts index eff81bcfb..b45ba4145 100644 --- a/packages/autocomplete-core/src/onKeyDown.ts +++ b/packages/autocomplete-core/src/onKeyDown.ts @@ -37,7 +37,12 @@ export function onKeyDown({ ); nodeItem?.scrollIntoView(false); - if (store.getState().highlightedIndex !== null) { + if ( + store.getState().highlightedIndex !== null && + store + .getState() + .suggestions.some(suggestion => suggestion.items.length > 0) + ) { const { item, itemValue, itemUrl, source } = getHighlightedItem({ state: store.getState(), }); @@ -94,7 +99,12 @@ export function onKeyDown({ } else if (event.key === 'Enter') { // No item is selected, so we let the browser handle the native `onSubmit` // form event. - if (store.getState().highlightedIndex === null) { + if ( + store.getState().highlightedIndex === null || + store + .getState() + .suggestions.every(suggestion => suggestion.items.length === 0) + ) { return; } diff --git a/packages/autocomplete-core/src/propGetters.ts b/packages/autocomplete-core/src/propGetters.ts index eebc534bc..5f3097239 100644 --- a/packages/autocomplete-core/src/propGetters.ts +++ b/packages/autocomplete-core/src/propGetters.ts @@ -39,7 +39,10 @@ export function getPropGetters({ // This ensures a working experience on mobile because we blur the input // on touch devices when the user starts scrolling (`touchmove`). onTouchStart(event) { - if (store.getState().isOpen === false) { + if ( + store.getState().isOpen === false || + event.target === getterProps.inputElement + ) { return; } @@ -65,10 +68,12 @@ export function getPropGetters({ // mimic the native platform behavior where the input is blurred to // hide the virtual keyboard. This gives more vertical space to // discover all the suggestions showing up in the dropdown. - onTouchMove() { + onTouchMove(event: TouchEvent) { if ( store.getState().isOpen === false || - getterProps.inputElement !== props.environment.document.activeElement + getterProps.inputElement !== + props.environment.document.activeElement || + event.target === getterProps.inputElement ) { return; } diff --git a/packages/docsearch-react/babel.config.js b/packages/docsearch-react/babel.config.js new file mode 100644 index 000000000..7d68e20c8 --- /dev/null +++ b/packages/docsearch-react/babel.config.js @@ -0,0 +1,27 @@ +/* eslint-disable import/no-commonjs */ + +module.exports = api => { + const isTest = api.env('test'); + const modules = isTest ? 'commonjs' : false; + const targets = {}; + + if (isTest) { + targets.node = true; + } else { + targets.browsers = ['last 2 versions', 'ie >= 9']; + } + + return { + presets: [ + '@babel/preset-typescript', + [ + '@babel/preset-env', + { + modules, + targets, + }, + ], + ], + plugins: [['@babel/plugin-transform-react-jsx']], + }; +}; diff --git a/packages/docsearch-react/package.json b/packages/docsearch-react/package.json new file mode 100644 index 000000000..d9e6bdc6b --- /dev/null +++ b/packages/docsearch-react/package.json @@ -0,0 +1,44 @@ +{ + "name": "docsearch-react", + "version": "1.0.0-alpha.10", + "license": "MIT", + "homepage": "https://github.com/algolia/autocomplete.js", + "repository": "algolia/autocomplete.js", + "author": { + "name": "Algolia, Inc.", + "url": "https://www.algolia.com" + }, + "sideEffects": false, + "files": [ + "dist/" + ], + "source": "src/index.ts", + "types": "dist/esm/index.d.ts", + "module": "dist/esm/index.js", + "main": "dist/umd/index.js", + "umd:main": "dist/umd/index.js", + "unpkg": "dist/umd/index.js", + "jsdelivr": "dist/umd/index.js", + "scripts": { + "build": "yarn build:clean && yarn build:umd && yarn build:esm && yarn build:css", + "": "// TODO: have a proper build:css, probably including autoprefixer?", + "build:css": "cp src/style.css dist/esm/ && cp src/style.css dist/umd/", + "build:css:watch": "chokidar src/style.css --command \"yarn build:css\"", + "build:esm": "babel src --root-mode upward --extensions '.ts,.tsx' --out-dir dist/esm", + "build:esm:watch": "yarn build:esm --watch", + "build:umd": "rollup --config", + "build:types": "tsc -p ./tsconfig.declaration.json --outDir ./dist/esm", + "build:types:watch": "chokidar \"**/*.ts\" \"**/*.tsx\" --command \"yarn build:types\" --ignore \"dist\"", + "build:clean": "rm -rf ./dist", + "watch": "concurrently \"yarn build:esm:watch\" \"yarn build:types:watch\" \"yarn build:css:watch\"" + }, + "dependencies": { + "@francoischalifour/autocomplete-core": "^1.0.0-alpha.10", + "@francoischalifour/autocomplete-preset-algolia": "^1.0.0-alpha.10", + "algoliasearch": "^4.0.0" + }, + "peerDependencies": { + "react": "^16.8.0", + "react-dom": "^16.8.0" + } +} diff --git a/packages/docsearch-react/rollup.config.js b/packages/docsearch-react/rollup.config.js new file mode 100644 index 000000000..a64b3b2b4 --- /dev/null +++ b/packages/docsearch-react/rollup.config.js @@ -0,0 +1,20 @@ +import json from '@rollup/plugin-json'; + +import { name } from './package.json'; +import { sharedPlugins } from '../autocomplete-core/rollup.config'; + +export default { + input: 'src/index.ts', + external: ['react', 'react-dom'], + output: { + file: 'dist/umd/index.js', + format: 'umd', + sourcemap: true, + name, + globals: { + react: 'React', + 'react-dom': 'ReactDOM', + }, + }, + plugins: [json(), ...sharedPlugins], +}; diff --git a/packages/docsearch-react/src/DocSearch.tsx b/packages/docsearch-react/src/DocSearch.tsx new file mode 100644 index 000000000..c98831227 --- /dev/null +++ b/packages/docsearch-react/src/DocSearch.tsx @@ -0,0 +1,235 @@ +import React, { useRef, useEffect } from 'react'; +import { + createAutocomplete, + AutocompleteState, +} from '@francoischalifour/autocomplete-core'; +import { getAlgoliaHits } from '@francoischalifour/autocomplete-preset-algolia'; + +import { DocSearchHit, InternalDocSearchHit } from './types'; +import { createSearchClient, groupBy, noop } from './utils'; +import { SearchBox } from './SearchBox'; +import { Dropdown } from './Dropdown'; +import { Footer } from './Footer'; + +interface DocSearchProps { + appId?: string; + apiKey: string; + indexName: string; + searchParameters: any; + onClose(): void; +} + +export function DocSearch({ + appId = 'BH4D9OD16A', + apiKey, + indexName, + searchParameters, + onClose = noop, +}: DocSearchProps) { + const [state, setState] = React.useState< + AutocompleteState + >({ + query: '', + suggestions: [], + } as any); + + const searchBoxRef = useRef(null); + const dropdownRef = useRef(null); + const inputRef = useRef(null); + const snipetLength = useRef(10); + + const searchClient = React.useMemo(() => createSearchClient(appId, apiKey), [ + appId, + apiKey, + ]); + + const { + getEnvironmentProps, + getRootProps, + getFormProps, + getLabelProps, + getInputProps, + getMenuProps, + getItemProps, + } = React.useMemo( + () => + createAutocomplete< + InternalDocSearchHit, + React.FormEvent, + React.MouseEvent, + React.KeyboardEvent + >({ + defaultHighlightedIndex: 0, + autoFocus: true, + placeholder: 'Search docs...', + openOnFocus: true, + onStateChange({ state }) { + setState(state as any); + }, + getSources({ query }) { + return getAlgoliaHits({ + searchClient, + queries: [ + { + indexName, + query, + params: { + attributesToRetrieve: [ + 'hierarchy.lvl0', + 'hierarchy.lvl1', + 'hierarchy.lvl2', + 'hierarchy.lvl3', + 'hierarchy.lvl4', + 'hierarchy.lvl5', + 'hierarchy.lvl6', + 'content', + 'type', + 'url', + ], + attributesToSnippet: [ + `hierarchy.lvl1:${snipetLength.current}`, + `hierarchy.lvl2:${snipetLength.current}`, + `hierarchy.lvl3:${snipetLength.current}`, + `hierarchy.lvl4:${snipetLength.current}`, + `hierarchy.lvl5:${snipetLength.current}`, + `hierarchy.lvl6:${snipetLength.current}`, + `content:${snipetLength.current}`, + ], + snippetEllipsisText: '…', + highlightPreTag: '', + highlightPostTag: '', + hitsPerPage: 20, + distinct: 4, + ...searchParameters, + }, + }, + ], + }).then((hits: DocSearchHit[]) => { + const formattedHits = hits.map(hit => { + const url = new URL(hit.url); + return { + ...hit, + url: hit.url + // @TODO: temporary convenience for development. + .replace(url.origin, '') + .replace('#__docusaurus', ''), + }; + }); + const sources = groupBy(formattedHits, hit => hit.hierarchy.lvl0); + + return Object.values(sources).map(items => { + return { + onSelect() { + onClose(); + }, + getSuggestionUrl({ suggestion }) { + return suggestion.url; + }, + getSuggestions() { + return Object.values( + groupBy(items, item => item.hierarchy.lvl1) + ) + .map(hits => + hits.map(item => { + return { + ...item, + // eslint-disable-next-line @typescript-eslint/camelcase + __docsearch_parent: + item.type !== 'lvl1' && + hits.find( + siblingItem => + siblingItem.type === 'lvl1' && + siblingItem.hierarchy.lvl1 === + item.hierarchy.lvl1 + ), + }; + }) + ) + .flat(); + }, + }; + }); + }); + }, + }), + [indexName, searchParameters, searchClient, onClose] + ); + + useEffect(() => { + const isMobileMediaQuery = window.matchMedia('(max-width: 750px)'); + + if (isMobileMediaQuery.matches) { + snipetLength.current = 5; + } + }, []); + + useEffect(() => { + if (dropdownRef.current) { + dropdownRef.current.scrollTop = 0; + } + }, [state.query]); + + useEffect(() => { + if (!(searchBoxRef.current && dropdownRef.current && inputRef.current)) { + return undefined; + } + + const { onTouchStart, onTouchMove } = getEnvironmentProps({ + searchBoxElement: searchBoxRef.current, + dropdownElement: dropdownRef.current, + inputElement: inputRef.current, + }); + + window.addEventListener('touchstart', onTouchStart); + window.addEventListener('touchmove', onTouchMove); + + return () => { + window.removeEventListener('touchstart', onTouchStart); + window.removeEventListener('touchmove', onTouchMove); + }; + }, [getEnvironmentProps, searchBoxRef, dropdownRef, inputRef]); + + return ( +
+
+
+ +
+ +
+ +
+ +
+
+
+
+
+ ); +} diff --git a/packages/docsearch-react/src/Dropdown/Dropdown.tsx b/packages/docsearch-react/src/Dropdown/Dropdown.tsx new file mode 100644 index 000000000..9aefaa8a7 --- /dev/null +++ b/packages/docsearch-react/src/Dropdown/Dropdown.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import { + AutocompleteState, + GetMenuProps, + GetItemProps, +} from '@francoischalifour/autocomplete-core'; + +import { InternalDocSearchHit } from '../types'; +import { Error } from '../Error'; +import { NoResults } from '../NoResults'; +import { Results } from '../Results'; + +interface DropdownProps { + state: AutocompleteState; + getMenuProps: GetMenuProps; + getItemProps: GetItemProps; +} + +export function Dropdown(props: DropdownProps) { + if (props.state.status === 'error') { + return ; + } + + if ( + props.state.status === 'idle' && + props.state.suggestions.every(source => source.items.length === 0) + ) { + return ; + } + + return ( + + ); +} diff --git a/packages/docsearch-react/src/Dropdown/index.ts b/packages/docsearch-react/src/Dropdown/index.ts new file mode 100644 index 000000000..2f29bad4e --- /dev/null +++ b/packages/docsearch-react/src/Dropdown/index.ts @@ -0,0 +1 @@ +export * from './Dropdown'; diff --git a/packages/docsearch-react/src/Error/Error.tsx b/packages/docsearch-react/src/Error/Error.tsx new file mode 100644 index 000000000..12d983b23 --- /dev/null +++ b/packages/docsearch-react/src/Error/Error.tsx @@ -0,0 +1,10 @@ +import React from 'react'; + +export function Error() { + return ( +

+ We‘re unable to fetch results. You might want to check your network + connection. +

+ ); +} diff --git a/packages/docsearch-react/src/Error/index.ts b/packages/docsearch-react/src/Error/index.ts new file mode 100644 index 000000000..ae6e95d01 --- /dev/null +++ b/packages/docsearch-react/src/Error/index.ts @@ -0,0 +1 @@ +export * from './Error'; diff --git a/packages/docsearch-react/src/Footer/AlgoliaLogo.tsx b/packages/docsearch-react/src/Footer/AlgoliaLogo.tsx new file mode 100644 index 000000000..7690b5f71 --- /dev/null +++ b/packages/docsearch-react/src/Footer/AlgoliaLogo.tsx @@ -0,0 +1,20 @@ +import React from 'react'; + +export function AlgoliaLogo() { + return ( + + Search by + + + + + ); +} diff --git a/packages/docsearch-react/src/Footer/Footer.tsx b/packages/docsearch-react/src/Footer/Footer.tsx new file mode 100644 index 000000000..7e098632f --- /dev/null +++ b/packages/docsearch-react/src/Footer/Footer.tsx @@ -0,0 +1,64 @@ +import React from 'react'; + +import { AlgoliaLogo } from './AlgoliaLogo'; + +export function Footer() { + return ( + <> +
+ +
+
    +
  • + + + + + + to select +
  • +
  • + + + + + + + + + + + to navigate +
  • +
  • + + + + + + to close +
  • +
+ + ); +} + +interface CommandIconProps { + children: React.ReactNode; +} + +function CommandIcon(props: CommandIconProps) { + return ( + + + {props.children} + + + ); +} diff --git a/packages/docsearch-react/src/Footer/index.ts b/packages/docsearch-react/src/Footer/index.ts new file mode 100644 index 000000000..ddcc5a9cd --- /dev/null +++ b/packages/docsearch-react/src/Footer/index.ts @@ -0,0 +1 @@ +export * from './Footer'; diff --git a/packages/docsearch-react/src/Footer/index.tsx b/packages/docsearch-react/src/Footer/index.tsx new file mode 100644 index 000000000..ddcc5a9cd --- /dev/null +++ b/packages/docsearch-react/src/Footer/index.tsx @@ -0,0 +1 @@ +export * from './Footer'; diff --git a/packages/docsearch-react/src/NoResults/NoResults.tsx b/packages/docsearch-react/src/NoResults/NoResults.tsx new file mode 100644 index 000000000..3c2793e36 --- /dev/null +++ b/packages/docsearch-react/src/NoResults/NoResults.tsx @@ -0,0 +1,12 @@ +import React from 'react'; + +interface NoResultsProps { + query: string; +} + +export function NoResults(props: NoResultsProps) { + return
+
No results for “{props.query}“.
+
Try another search or if you believe this query should lead to actual results, please let us know with a GitHub issue.
+
; +} diff --git a/packages/docsearch-react/src/NoResults/index.ts b/packages/docsearch-react/src/NoResults/index.ts new file mode 100644 index 000000000..20eae4db4 --- /dev/null +++ b/packages/docsearch-react/src/NoResults/index.ts @@ -0,0 +1 @@ +export * from './NoResults'; diff --git a/packages/docsearch-react/src/Results/ActionIcon.tsx b/packages/docsearch-react/src/Results/ActionIcon.tsx new file mode 100644 index 000000000..15e955e05 --- /dev/null +++ b/packages/docsearch-react/src/Results/ActionIcon.tsx @@ -0,0 +1,31 @@ +import React from 'react'; + +export function SelectIcon() { + return ( + + + + + + + ); +} + +export function GoToExternal() { + return ( + + + + ); +} diff --git a/packages/docsearch-react/src/Results/Results.tsx b/packages/docsearch-react/src/Results/Results.tsx new file mode 100644 index 000000000..def174b41 --- /dev/null +++ b/packages/docsearch-react/src/Results/Results.tsx @@ -0,0 +1,147 @@ +import React from 'react'; +import { + GetMenuProps, + GetItemProps, + AutocompleteSuggestion, +} from '@francoischalifour/autocomplete-core'; + +import { SourceIcon } from './SourceIcon'; +import { SelectIcon } from './ActionIcon'; +import { InternalDocSearchHit } from '../types'; + +interface ResultsProps { + suggestions: Array>; + getMenuProps: GetMenuProps; + getItemProps: GetItemProps; +} + +export function Results(props: ResultsProps) { + return ( +
+ {props.suggestions.map(({ source, items }) => { + const title = items[0].hierarchy.lvl0; + + return ( +
+
{title}
+ + +
+ ); + })} +
+ ); +} diff --git a/packages/docsearch-react/src/Results/SourceIcon.tsx b/packages/docsearch-react/src/Results/SourceIcon.tsx new file mode 100644 index 000000000..864826d38 --- /dev/null +++ b/packages/docsearch-react/src/Results/SourceIcon.tsx @@ -0,0 +1,58 @@ +import React from 'react'; + +export function SourceIcon(props: { type: string }) { + switch (props.type) { + case 'lvl1': + return ; + case 'content': + return ; + default: + return ; + } +} + +function LvlIcon() { + return ( + + + + ); +} + +function AnchorIcon() { + return ( + + + + ); +} + +function ContentIcon() { + return ( + + + + ); +} diff --git a/packages/docsearch-react/src/Results/index.ts b/packages/docsearch-react/src/Results/index.ts new file mode 100644 index 000000000..9646dd49e --- /dev/null +++ b/packages/docsearch-react/src/Results/index.ts @@ -0,0 +1 @@ +export * from './Results'; diff --git a/packages/docsearch-react/src/SearchBox/LoadingIcon.tsx b/packages/docsearch-react/src/SearchBox/LoadingIcon.tsx new file mode 100644 index 000000000..6963ca43d --- /dev/null +++ b/packages/docsearch-react/src/SearchBox/LoadingIcon.tsx @@ -0,0 +1,23 @@ +import React from 'react'; + +export function LoadingIcon() { + return ( + + + + + + + + + + + ); +} diff --git a/packages/docsearch-react/src/SearchBox/ResetIcon.tsx b/packages/docsearch-react/src/SearchBox/ResetIcon.tsx new file mode 100644 index 000000000..202c2cacb --- /dev/null +++ b/packages/docsearch-react/src/SearchBox/ResetIcon.tsx @@ -0,0 +1,13 @@ +import React from 'react'; + +export function ResetIcon() { + return ( + + + + ); +} diff --git a/packages/docsearch-react/src/SearchBox/SearchBox.tsx b/packages/docsearch-react/src/SearchBox/SearchBox.tsx new file mode 100644 index 000000000..8952d4ab4 --- /dev/null +++ b/packages/docsearch-react/src/SearchBox/SearchBox.tsx @@ -0,0 +1,74 @@ +import React, { MutableRefObject } from 'react'; +import { + GetFormProps, + GetLabelProps, + GetInputProps, +} from '@francoischalifour/autocomplete-core'; + +import { SearchIcon } from './SearchIcon'; +import { ResetIcon } from './ResetIcon'; +import { LoadingIcon } from './LoadingIcon'; + +interface SearchBoxProps { + inputRef: MutableRefObject; + query: string; + getFormProps: GetFormProps; + getLabelProps: GetLabelProps; + getInputProps: GetInputProps< + React.ChangeEvent, + React.MouseEvent, + React.KeyboardEvent + >; + onClose(): void; +} + +export function SearchBox(props: SearchBoxProps) { + const { onSubmit, onReset } = props.getFormProps({ + inputElement: props.inputRef.current, + }); + + return ( + <> +
+ + +
+ +
+ + + + +
+ + + + ); +} diff --git a/packages/docsearch-react/src/SearchBox/SearchIcon.tsx b/packages/docsearch-react/src/SearchBox/SearchIcon.tsx new file mode 100644 index 000000000..fab22ae79 --- /dev/null +++ b/packages/docsearch-react/src/SearchBox/SearchIcon.tsx @@ -0,0 +1,17 @@ +import React from 'react'; + +export function SearchIcon() { + return ( + + + + ); +} diff --git a/packages/docsearch-react/src/SearchBox/index.tsx b/packages/docsearch-react/src/SearchBox/index.tsx new file mode 100644 index 000000000..ffc46c999 --- /dev/null +++ b/packages/docsearch-react/src/SearchBox/index.tsx @@ -0,0 +1 @@ +export * from './SearchBox'; diff --git a/packages/docsearch-react/src/SearchButton/SearchButton.tsx b/packages/docsearch-react/src/SearchButton/SearchButton.tsx new file mode 100644 index 000000000..ddc8adf88 --- /dev/null +++ b/packages/docsearch-react/src/SearchButton/SearchButton.tsx @@ -0,0 +1,57 @@ +import React, { useState, useEffect } from 'react'; + +interface SearchButtonProps { + onClick(): void; +} + +const ACTION_KEY_DEFAULT = 'Ctrl'; +const ACTION_KEY_APPLE = '⌘'; + +function isAppleDevice() { + if (typeof navigator === 'undefined') { + return ACTION_KEY_DEFAULT; + } + + return /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform); +} + +export function SearchButton(props: SearchButtonProps) { + const [key, setKey] = useState(() => + isAppleDevice() ? ACTION_KEY_APPLE : ACTION_KEY_DEFAULT + ); + + useEffect(() => { + if (isAppleDevice()) { + setKey(ACTION_KEY_APPLE); + } + }, []); + + return ( + + ); +} diff --git a/packages/docsearch-react/src/SearchButton/index.ts b/packages/docsearch-react/src/SearchButton/index.ts new file mode 100644 index 000000000..6f4ca0984 --- /dev/null +++ b/packages/docsearch-react/src/SearchButton/index.ts @@ -0,0 +1 @@ +export * from './SearchButton'; diff --git a/packages/docsearch-react/src/index.ts b/packages/docsearch-react/src/index.ts new file mode 100644 index 000000000..c305abfc0 --- /dev/null +++ b/packages/docsearch-react/src/index.ts @@ -0,0 +1,2 @@ +export * from './DocSearch'; +export * from './SearchButton'; diff --git a/packages/docsearch-react/src/style.css b/packages/docsearch-react/src/style.css new file mode 100644 index 000000000..8ae3e3e89 --- /dev/null +++ b/packages/docsearch-react/src/style.css @@ -0,0 +1,543 @@ +/* Variables */ + +/* primary +secondary +bg +bg light +bg dark +text default +text muted +spacing +font size */ + +:root { + --docsearch-input-color: var(--ifm-color-emphasis-800); + --docsearch-highlight-color: var(--ifm-color-primary); + --docsearch-placeholder-color: rgb(150, 155, 175); + --docsearch-container-background: rgb(60, 65, 85, 0.8); + --docsearch-modal-background: var(--ifm-color-emphasis-100); + --docsearch-modal-shadow: inset 1px 1px 0px 0px rgba(255, 255, 255, 0.5), + 0px 3px 8px 0px rgba(85, 90, 100, 1); + --docsearch-searchbox-background: rgb(210, 215, 225); + --docsearch-searchbox-active-background: rgb(195, 200, 220); + --docsearch-searchbox-shadow: inset 1px 2px 6px 0px rgb(190, 195, 215), + inset 0px 1px 1px 0px rgb(134, 146, 221), 1px 1px 0px 0px white; + --docsearch-hit-color: var(--ifm-color-emphasis-800); + --docsearch-hit-active-color: white; + --docsearch-hit-background: white; + --docsearch-hit-shadow: 0px 2px 6px 0px rgb(212, 217, 225); + --docsearch-key-gradient: linear-gradient( + -225deg, + rgb(213, 219, 228) 0%, + rgb(248, 248, 248) 100% + ); + --docsearch-key-shadow: inset 0px -2px 0px 0px rgb(205, 205, 230), + inset 0px 0px 1px 1px white, 0px 1px 2px 1px rgba(30, 35, 90, 0.4); + --docsearch-footer-background: white; + --docsearch-footer-shadow: 0px -5px 7px 0px rgba(69, 98, 155, 0.12); + --docsearch-logo-color: #5468ff; + --docsearch-muted-color: rgb(190, 195, 215); + --docsearch-modal-width: 560px; + --docsearch-modal-height: 600px; + --docsearch-searchbox-height: 56px; + --docsearch-hit-height: 56px; + --docsearch-footer-height: 44px; + --docsearch-spacing: 12px; +} + +/* Darkmode */ + +html[data-theme='dark'] { + --docsearch-container-background: rgba(9, 10, 17, 0.8); + --docsearch-modal-background: rgb(21, 23, 42); + --docsearch-modal-shadow: inset 1px 1px 0px 0px rgb(44, 46, 64), + 0px 3px 8px 0px rgb(0, 3, 9); + --docsearch-searchbox-background: rgb(9, 10, 17); + --docsearch-searchbox-shadow: inset 0px 0px 0 2px var(--ifm-color-primary); + --docsearch-searchbox-active-background: rgb(9, 10, 17); + --docsearch-hit-color: var(--ifm-color-emphasis-500); + --docsearch-hit-shadow: none; + --docsearch-hit-background: rgb(9, 10, 17); + --docsearch-key-gradient: linear-gradient( + -26.56505117707799deg, + rgb(86, 88, 114) 0%, + rgb(49, 53, 91) 100% + ); + --docsearch-key-shadow: inset 0px -2px 0px 0px rgb(40, 45, 85), + inset 0px 0px 1px 1px rgb(81, 87, 125), 0px 2px 2px 0px rgb(3, 4, 9); + --docsearch-footer-background: rgb(30, 33, 54); + --docsearch-footer-shadow: inset 0px 1px 0px 0px rgba(73, 76, 106, 0.52), + 0px -4px 8px 0px rgba(0, 0, 0, 0.34); + --docsearch-logo-color: #fff; + --docsearch-muted-color: rgb(100, 105, 125); +} + +.DocSearch--active .main-wrapper { + filter: blur(2px); +} + +/* Search Button */ + +.DocSearch-SearchButton { + display: flex; + height: 40px; + margin: 0 0 0 20px; + padding: 0 12px; + margin-left: 2em; + border: none; + border-radius: 20px; + cursor: pointer; + align-items: center; + background: var(--docsearch-searchbox-background); + color: var(--ifm-color-emphasis-900); + transition: background-color 0.4s ease-out, box-shadow 0.4s ease-out; +} + +.DocSearch-SearchButton:hover, +.DocSearch-SearchButton:active, +.DocSearch-SearchButton:focus { + outline: none; + box-shadow: var(--docsearch-searchbox-shadow); + background: var(--docsearch-searchbox-active-background); +} + +.DocSearch-SearchButton-Icon { + color: var(--docsearch-highlight-color); +} + +.DocSearch-SearchButton-Placeholder { + margin: 0 8px; + font-size: 1rem; +} + +.DocSearch-SearchButton-Key { + display: flex; + align-items: center; + justify-content: center; + width: 20px; + height: 18px; + margin-right: 0.4em; + padding-bottom: 2px; + border-radius: 3px; + background: var(--docsearch-key-gradient); + box-shadow: var(--docsearch-key-shadow); + color: var(--ifm-color-emphasis-600); +} + +/* Body modifier */ + +.DocSearch--active { + /* + * We need to mark it as important because some websites override the + * `style` attribute (e.g. Docusaurus). + */ + overflow: hidden !important; +} + +/* Hide Docusaurus hamburger button when modal is opened */ +.DocSearch--active .menu__button { + display: none !important; +} + +/* Container & Modal */ + +.DocSearch-Container, +.DocSearch-Container * { + box-sizing: border-box; +} + +.DocSearch-Container { + height: 100vh; + width: 100vw; + position: fixed; + left: 0; + top: 0; + background-color: var(--docsearch-container-background); + animation: container 0.1s ease-in forwards; +} + +@keyframes container { + 0% { + opacity: 0.5; + } + 100% { + opacity: 1; + } +} + +.DocSearch-Container a { + text-decoration: none; +} + +.DocSearch-Modal { + position: relative; + flex-direction: column; + max-width: var(--docsearch-modal-width); + margin: 60px auto auto auto; + border-radius: 6px; + background: var(--docsearch-modal-background); + box-shadow: var(--docsearch-modal-shadow); +} + +/* Modal Searchbox */ + +.DocSearch-SearchBar { + display: flex; + padding: var(--docsearch-spacing) var(--docsearch-spacing) 0; +} + +.DocSearch-Form { + display: flex; + width: 100%; + position: relative; + height: var(--docsearch-searchbox-height); + padding: 0 var(--docsearch-spacing); + align-items: center; + border-radius: 4px; + background: var(--docsearch-searchbox-background); + box-shadow: var(--docsearch-searchbox-shadow); +} + +.DocSearch-Input { + height: 100%; + flex: 1; + width: 80%; + padding-left: 8px; + font: inherit; + font-size: 1.2em; + appearance: none; + border: none; + outline: none; + background: transparent; + color: var(--docsearch-input-color); +} + +.DocSearch-Input::placeholder { + color: var(--docsearch-placeholder-color); +} + +.DocSearch-Input::-webkit-search-cancel-button, +.DocSearch-Input::-webkit-search-decoration, +.DocSearch-Input::-webkit-search-results-button, +.DocSearch-Input::-webkit-search-results-decoration { + display: none; +} + +.DocSearch-LoadingIndicator, +.DocSearch-MagnifierLabel, +.DocSearch-Reset { + height: 20px; + width: 20px; + margin: 0; + padding: 0; +} + +.DocSearch-Container--Stalled .DocSearch-LoadingIndicator, +.DocSearch-MagnifierLabel, +.DocSearch-Reset { + display: flex; + align-items: center; + justify-content: center; +} + +.DocSearch-Container--Stalled .DocSearch-MagnifierLabel, +.DocSearch-LoadingIndicator { + display: none; +} + +.DocSearch-Reset { + appearance: none; + right: 0; + border: none; + border-radius: 50%; + background: none; + cursor: pointer; +} + +.DocSearch-Reset[hidden] { + display: none; +} + +.DocSearch-Reset:focus { + outline: none; +} + +.DocSearch-Reset svg { + height: 10px; + width: 10px; +} + +.DocSearch-LoadingIndicator svg, +.DocSearch-MagnifierLabel svg { + height: 24px; + width: 24px; +} + +.DocSearch-MagnifierLabel { + color: var(--docsearch-highlight-color); +} + +.DocSearch-Cancel { + display: none; +} + +/* Modal Dropdown */ + +.DocSearch-Dropdown { + height: calc( + var(--docsearch-modal-height) - var(--docsearch-searchbox-height) - + var(--docsearch-spacing) * 2 - var(--docsearch-footer-height) + ); + padding: 0 var(--docsearch-spacing) var(--docsearch-hit-height); + margin-bottom: var(--docsearch-footer-height); + overflow-y: scroll; + letter-spacing: 0.02em; +} + +.DocSearch-Dropdown ul { + margin: 0; + padding: 0; + list-style: none; +} + +.DocSearch-Dropdown-Container { + position: relative; +} + +/* Modal Footer */ + +.DocSearch-Footer { + position: absolute; + bottom: 0; + display: flex; + width: 100%; + height: var(--docsearch-footer-height); + padding: 0 var(--docsearch-spacing); + flex-direction: row-reverse; + flex-shrink: 0; + justify-content: space-between; + align-items: center; + border-radius: 0 0 8px 8px; + background: var(--docsearch-footer-background); + box-shadow: var(--docsearch-footer-shadow); + z-index: var(--ifm-z-index-fixed); +} + +.DocSearch-Commands { + display: flex; + margin: 0; + padding: 0; + list-style: none; +} + +.DocSearch-Commands li:not(:last-of-type) { + margin-right: 0.8em; +} + +.DocSearch-Commands { + display: flex; + color: var(--ifm-color-emphasis-600); +} + +.DocSearch-Commands li { + display: flex; + align-items: center; +} + +.DocSearch-Commands-Key { + display: flex; + width: 20px; + height: 18px; + margin-right: 0.4em; + padding-bottom: 2px; + align-items: center; + justify-content: center; + border-radius: 2px; + background: var(--docsearch-key-gradient); + box-shadow: var(--docsearch-key-shadow); +} + +.DocSearch-Logo a { + display: flex; +} + +.DocSearch-Logo svg { + margin-left: 8px; + color: var(--docsearch-logo-color); +} + +.DocSearch-Label { + font-size: 0.75em; + line-height: 1.8em; + color: var(--docsearch-muted-color); +} + +/* Hit */ + +.DocSearch-Hits mark { + background: none; + color: var(--docsearch-highlight-color); +} + +.DocSearch-Hit { + display: flex; + position: relative; + border-radius: 4px; + background: var(--docsearch-hit-background); + box-shadow: var(--docsearch-hit-shadow); + padding-left: var(--docsearch-spacing); + margin-bottom: 4px; +} + +.DocSearch-Hit-source { + margin: 0 calc(var(--docsearch-spacing) * -1); + padding: 8px var(--docsearch-spacing) 0; + line-height: 32px; + font-size: 0.85em; + font-weight: 600; + color: var(--docsearch-highlight-color); + letter-spacing: 0; + position: sticky; + top: 0; + z-index: var(--ifm-z-index-fixed); + background: var(--docsearch-modal-background); +} + +.DocSearch-Hit-Tree { + width: 24px; + height: var(--docsearch-hit-height); + margin-right: 4px; + color: var(--docsearch-muted-color); + opacity: 0.5; +} + +.DocSearch-Hit[aria-selected='true'] { + /* transition: background-color ease 0.1s; */ + background-color: var(--docsearch-highlight-color); +} + +.DocSearch-Hit[aria-selected='true'] .DocSearch-Hit-title, +.DocSearch-Hit[aria-selected='true'] mark, +.DocSearch-Hit[aria-selected='true'] .DocSearch-Hit-text, +.DocSearch-Hit[aria-selected='true'] .DocSearch-Hit-path, +.DocSearch-Hit[aria-selected='true'] .DocSearch-Hit-icon, +.DocSearch-Hit[aria-selected='true'] .DocSearch-Hit-action { + color: var(--docsearch-hit-active-color) !important; +} + +.DocSearch-Hit a { + display: block; + border-radius: 4px; + width: 100%; +} + +.DocSearch-Hit[aria-selected='true'] mark { + text-decoration: underline; +} + +.DocSearch-Hit-Container { + display: flex; + height: var(--docsearch-hit-height); + padding: 0 var(--docsearch-spacing) 0 0; + flex-direction: row; + align-items: center; + color: var(--docsearch-hit-color); +} + +.DocSearch-Hit-icon, +.DocSearch-Hit-action { + height: 20px; + width: 20px; + color: var(--docsearch-muted-color); +} + +.DocSearch-Hit-content-wrapper { + position: relative; + display: flex; + width: 80%; + margin: 0 8px; + line-height: 1.1em; + flex: 1 0 auto; + font-weight: 500; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + justify-content: center; + flex-direction: column; +} + +.DocSearch-Hit-title { + font-size: 0.9em; +} + +.DocSearch-Hit-path { + font-size: 0.7em; + color: var(--docsearch-muted-color); +} + +/* No Results */ + +.DocSearch-NoResults { + width: 80%; + margin: 0 auto; + text-align: center; + padding: var(--docsearch-spacing) 0; + margin-top: var(--docsearch-spacing); +} + +/* Responsive */ + +@media (max-width: 750px) { + :root { + /* quickfix https://stackoverflow.com/questions/37112218/css3-100vh-not-constant-in-mobile-browser */ + --docsearch-modal-height: calc(100vh - 110px); + --docsearch-spacing: 10px; + --docsearch-footer-height: 40px; + } + .DocSearch-SearchButton-KeySeparator, + .DocSearch-SearchButton-Key { + display: none; + } + .DocSearch-Modal { + border-radius: 0; + box-shadow: none; + margin: 0 auto; + } + .DocSearch-Cancel { + display: inline-block; + width: 0; + white-space: nowrap; + overflow: hidden; + margin-left: var(--docsearch-spacing); + padding: 0; + appearance: none; + border: 0; + cursor: pointer; + background: none; + flex: none; + font: inherit; + font-size: 1em; + font-weight: 500; + letter-spacing: 0.02em; + color: var(--docsearch-highlight-color); + animation: cancel-button ease-out 0.2s forwards; + } + + @keyframes cancel-button { + 100% { + width: 4em; + } + } + + .DocSearch-Commands { + display: none; + } + .DocSearch-Hit { + letter-spacing: 0; + } +} + +/* todo: hide keyboard shortcut on smartphones, touchscreens device - cross browser limitation with media q, maybe js is better https://codepen.io/Ferie/pen/vQOMmO?editors=1010 */ +@media (hover: none) and (pointer: coarse) { + /* ... */ +} diff --git a/packages/docsearch-react/src/svg/icon_action_external.svg b/packages/docsearch-react/src/svg/icon_action_external.svg new file mode 100644 index 000000000..24adf7f84 --- /dev/null +++ b/packages/docsearch-react/src/svg/icon_action_external.svg @@ -0,0 +1,11 @@ + + + + 1c947b1e-eb2b-4ad3-ac34-75998ca8847a@1.00x + Created with sketchtool. + + + + + + \ No newline at end of file diff --git a/packages/docsearch-react/src/svg/icon_action_goto.svg b/packages/docsearch-react/src/svg/icon_action_goto.svg new file mode 100644 index 000000000..5bcb44e5a --- /dev/null +++ b/packages/docsearch-react/src/svg/icon_action_goto.svg @@ -0,0 +1,14 @@ + + + + 1c20c2f5-d737-495d-a54b-64fd79363595@1.00x + Created with sketchtool. + + + + + + + + + \ No newline at end of file diff --git a/packages/docsearch-react/src/svg/icon_source_anchor.svg b/packages/docsearch-react/src/svg/icon_source_anchor.svg new file mode 100644 index 000000000..cf0ba8622 --- /dev/null +++ b/packages/docsearch-react/src/svg/icon_source_anchor.svg @@ -0,0 +1,14 @@ + + + + 78adcbdb-966a-4cd6-9309-109b3e7780e1@1.00x + Created with sketchtool. + + + + + + + + + \ No newline at end of file diff --git a/packages/docsearch-react/src/svg/icon_source_content.svg b/packages/docsearch-react/src/svg/icon_source_content.svg new file mode 100644 index 000000000..427474590 --- /dev/null +++ b/packages/docsearch-react/src/svg/icon_source_content.svg @@ -0,0 +1,13 @@ + + + + 90955cc9-f7b3-4479-898c-19510cc4b6d4@1.00x + Created with sketchtool. + + + + + + + + \ No newline at end of file diff --git a/packages/docsearch-react/src/svg/icon_source_page.svg b/packages/docsearch-react/src/svg/icon_source_page.svg new file mode 100644 index 000000000..dbec55d00 --- /dev/null +++ b/packages/docsearch-react/src/svg/icon_source_page.svg @@ -0,0 +1,11 @@ + + + + 1f96940f-ca50-4e02-84bd-79bc02579d53@1.00x + Created with sketchtool. + + + + + + \ No newline at end of file diff --git a/packages/docsearch-react/src/svg/icon_source_recent.svg b/packages/docsearch-react/src/svg/icon_source_recent.svg new file mode 100644 index 000000000..8e16b686b --- /dev/null +++ b/packages/docsearch-react/src/svg/icon_source_recent.svg @@ -0,0 +1,16 @@ + + + + ee397e5b-5117-4da7-bdc8-0d42da41349d@1.00x + Created with sketchtool. + + + + + + + + + + + \ No newline at end of file diff --git a/packages/docsearch-react/src/svg/icon_tree_child.svg b/packages/docsearch-react/src/svg/icon_tree_child.svg new file mode 100644 index 000000000..c6951fca9 --- /dev/null +++ b/packages/docsearch-react/src/svg/icon_tree_child.svg @@ -0,0 +1,12 @@ + + + + 8d4bc779-eae6-4075-8edb-f964556f9cd5@1.00x + Created with sketchtool. + + + + + + + \ No newline at end of file diff --git a/packages/docsearch-react/src/svg/icon_tree_last-child.svg b/packages/docsearch-react/src/svg/icon_tree_last-child.svg new file mode 100644 index 000000000..6f9a4b2f1 --- /dev/null +++ b/packages/docsearch-react/src/svg/icon_tree_last-child.svg @@ -0,0 +1,12 @@ + + + + 658830a4-2f41-4e2b-b15f-517e3365664b@1.00x + Created with sketchtool. + + + + + + + \ No newline at end of file diff --git a/packages/docsearch-react/src/svg/light_footer_key_arrow-down.svg b/packages/docsearch-react/src/svg/light_footer_key_arrow-down.svg new file mode 100644 index 000000000..7463f23d9 --- /dev/null +++ b/packages/docsearch-react/src/svg/light_footer_key_arrow-down.svg @@ -0,0 +1,12 @@ + + + + ba2c8f8b-aab6-4715-b010-a3c910a1a4ea@1.00x + Created with sketchtool. + + + + + + + \ No newline at end of file diff --git a/packages/docsearch-react/src/svg/light_footer_key_arrow-up.svg b/packages/docsearch-react/src/svg/light_footer_key_arrow-up.svg new file mode 100644 index 000000000..1c9310492 --- /dev/null +++ b/packages/docsearch-react/src/svg/light_footer_key_arrow-up.svg @@ -0,0 +1,12 @@ + + + + 67191d53-11cf-4c86-9c2e-8580c0fd5d08@1.00x + Created with sketchtool. + + + + + + + \ No newline at end of file diff --git a/packages/docsearch-react/src/svg/light_footer_key_enter.svg b/packages/docsearch-react/src/svg/light_footer_key_enter.svg new file mode 100644 index 000000000..85226e532 --- /dev/null +++ b/packages/docsearch-react/src/svg/light_footer_key_enter.svg @@ -0,0 +1,14 @@ + + + + f879c873-9cca-4202-b665-1ec6b93bb126@1.00x + Created with sketchtool. + + + + + + + + + \ No newline at end of file diff --git a/packages/docsearch-react/src/svg/light_footer_key_esc.svg b/packages/docsearch-react/src/svg/light_footer_key_esc.svg new file mode 100644 index 000000000..f693037c1 --- /dev/null +++ b/packages/docsearch-react/src/svg/light_footer_key_esc.svg @@ -0,0 +1,11 @@ + + + + 67ddc381-274d-431d-a5d3-cf7d76b0c2cd@1.00x + Created with sketchtool. + + + + + + \ No newline at end of file diff --git a/packages/docsearch-react/src/svg/logo-alogia.svg b/packages/docsearch-react/src/svg/logo-alogia.svg new file mode 100644 index 000000000..84502dc62 --- /dev/null +++ b/packages/docsearch-react/src/svg/logo-alogia.svg @@ -0,0 +1,11 @@ + + + + EE8C1461-B989-46C5-8B49-7EEB61DCD522@1.00x + Created with sketchtool. + + + + + + \ No newline at end of file diff --git a/packages/docsearch-react/src/types/DocSearchHit.ts b/packages/docsearch-react/src/types/DocSearchHit.ts new file mode 100644 index 000000000..71be3c930 --- /dev/null +++ b/packages/docsearch-react/src/types/DocSearchHit.ts @@ -0,0 +1,81 @@ +type ContentType = + | 'content' + | 'lvl0' + | 'lvl1' + | 'lvl2' + | 'lvl3' + | 'lvl4' + | 'lvl5' + | 'lvl6'; + +interface DocSearchHitAttributeHighlightResult { + value: string; + matchLevel: 'none' | 'partial' | 'full'; + matchedWords: string[]; + fullyHighlighted?: boolean; +} + +interface DocSearchHitHighlightResultHierarchy { + lvl0: DocSearchHitAttributeHighlightResult; + lvl1: DocSearchHitAttributeHighlightResult; + lvl2: DocSearchHitAttributeHighlightResult; + lvl3: DocSearchHitAttributeHighlightResult; + lvl4: DocSearchHitAttributeHighlightResult; + lvl5: DocSearchHitAttributeHighlightResult; + lvl6: DocSearchHitAttributeHighlightResult; +} + +interface DocSearchHitHighlightResult { + content: DocSearchHitAttributeHighlightResult; + hierarchy: DocSearchHitHighlightResultHierarchy; + hierarchy_camel: DocSearchHitHighlightResultHierarchy[]; +} + +interface DocSearchHitAttributeSnippetResult { + value: string; + matchLevel: 'none' | 'partial' | 'full'; +} + +interface DocSearchHitSnippetResult { + content: DocSearchHitAttributeSnippetResult; + hierarchy: DocSearchHitHighlightResultHierarchy; + hierarchy_camel: DocSearchHitHighlightResultHierarchy[]; +} + +export interface DocSearchHit { + objectID: string; + content: string | null; + url: string; + url_without_anchor: string; + type: ContentType; + anchor: string | null; + hierarchy: { + lvl0: string; + lvl1: string; + lvl2: string | null; + lvl3: string | null; + lvl4: string | null; + lvl5: string | null; + lvl6: string | null; + }; + _highlightResult: DocSearchHitHighlightResult; + _snippetResult: DocSearchHitSnippetResult; + _rankingInfo?: { + promoted: boolean; + nbTypos: number; + firstMatchedWord: number; + proximityDistance?: number; + geoDistance: number; + geoPrecision?: number; + nbExactWords: number; + words: number; + filters: number; + userScore: number; + matchedGeoLocation?: { + lat: number; + lng: number; + distance: number; + }; + }; + _distinctSeqID?: number; +} diff --git a/packages/docsearch-react/src/types/InternalDocSearchHit.tsx b/packages/docsearch-react/src/types/InternalDocSearchHit.tsx new file mode 100644 index 000000000..bf518ce91 --- /dev/null +++ b/packages/docsearch-react/src/types/InternalDocSearchHit.tsx @@ -0,0 +1,5 @@ +import { DocSearchHit } from './DocSearchHit'; + +export type InternalDocSearchHit = DocSearchHit & { + __docsearch_parent: null | DocSearchHit; +}; diff --git a/packages/docsearch-react/src/types/index.ts b/packages/docsearch-react/src/types/index.ts new file mode 100644 index 000000000..7ccf20336 --- /dev/null +++ b/packages/docsearch-react/src/types/index.ts @@ -0,0 +1,2 @@ +export * from './DocSearchHit'; +export * from './InternalDocSearchHit'; diff --git a/packages/docsearch-react/src/utils/createSearchClient.ts b/packages/docsearch-react/src/utils/createSearchClient.ts new file mode 100644 index 000000000..439f66b6b --- /dev/null +++ b/packages/docsearch-react/src/utils/createSearchClient.ts @@ -0,0 +1,13 @@ +import algoliasearch from 'algoliasearch/dist/algoliasearch-lite.esm.browser'; + +import { version } from '../version'; + +export function createSearchClient(appId: string, apiKey: string) { + const searchClient = algoliasearch(appId, apiKey); + + if (typeof searchClient.addAlgoliaAgent === 'function') { + searchClient.addAlgoliaAgent(`docsearch-react (${version})`); + } + + return searchClient; +} diff --git a/packages/docsearch-react/src/utils/groupBy.ts b/packages/docsearch-react/src/utils/groupBy.ts new file mode 100644 index 000000000..5ea2731ef --- /dev/null +++ b/packages/docsearch-react/src/utils/groupBy.ts @@ -0,0 +1,16 @@ +export function groupBy( + values: TValue[], + predicate: (value: TValue) => string +): Record { + return values.reduce>((acc, item) => { + const key = predicate(item); + + if (!acc.hasOwnProperty(key)) { + acc[key] = []; + } + + acc[key].push(item); + + return acc; + }, {}); +} diff --git a/packages/docsearch-react/src/utils/index.ts b/packages/docsearch-react/src/utils/index.ts new file mode 100644 index 000000000..58e751eff --- /dev/null +++ b/packages/docsearch-react/src/utils/index.ts @@ -0,0 +1,3 @@ +export * from './createSearchClient'; +export * from './groupBy'; +export * from './noop'; diff --git a/packages/docsearch-react/src/utils/noop.ts b/packages/docsearch-react/src/utils/noop.ts new file mode 100644 index 000000000..cadb2cd0f --- /dev/null +++ b/packages/docsearch-react/src/utils/noop.ts @@ -0,0 +1 @@ +export function noop(..._args: any[]): void {} diff --git a/packages/docsearch-react/src/version.ts b/packages/docsearch-react/src/version.ts new file mode 100644 index 000000000..03d930471 --- /dev/null +++ b/packages/docsearch-react/src/version.ts @@ -0,0 +1 @@ +export const version = '1.0.0-alpha.10'; diff --git a/packages/docsearch-react/tsconfig.declaration.json b/packages/docsearch-react/tsconfig.declaration.json new file mode 100644 index 000000000..9bd7baf23 --- /dev/null +++ b/packages/docsearch-react/tsconfig.declaration.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig", + "compilerOptions": { + "noEmit": false, + "declaration": true, + "emitDeclarationOnly": true + } +} diff --git a/packages/docsearch-react/tsconfig.json b/packages/docsearch-react/tsconfig.json new file mode 100644 index 000000000..41716a7dd --- /dev/null +++ b/packages/docsearch-react/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../tsconfig" +} diff --git a/packages/website/docusaurus.config.js b/packages/website/docusaurus.config.js index 27dea79bd..c2ee4d8b0 100644 --- a/packages/website/docusaurus.config.js +++ b/packages/website/docusaurus.config.js @@ -87,6 +87,10 @@ module.exports = { ], copyright: `Copyright © ${new Date().getFullYear()} • Designed and built by Algolia`, }, + algolia: { + indexName: 'autocomplete', + apiKey: 'a5c3ccfd361b8bcb9708e679c43ae0e5', + }, }, presets: [ [ diff --git a/packages/website/package.json b/packages/website/package.json index 5a16e73ad..0c90a471c 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -12,6 +12,7 @@ "@docusaurus/core": "2.0.0-alpha.43", "@docusaurus/preset-classic": "2.0.0-alpha.43", "classnames": "2.2.6", + "docsearch-react": "1.0.0-alpha.10", "react": "16.13.0", "react-dom": "16.13.0" }, diff --git a/packages/website/src/pages/index.js b/packages/website/src/pages/index.js index df93bf935..e4ea850df 100644 --- a/packages/website/src/pages/index.js +++ b/packages/website/src/pages/index.js @@ -51,7 +51,7 @@ function Home() { return ( -
+

{siteConfig.title}

{siteConfig.tagline}

@@ -68,7 +68,6 @@ function Home() {
-
{features && features.length && (
diff --git a/packages/website/src/theme/SearchBar/index.js b/packages/website/src/theme/SearchBar/index.js new file mode 100644 index 000000000..fea5226b2 --- /dev/null +++ b/packages/website/src/theme/SearchBar/index.js @@ -0,0 +1,93 @@ +/* eslint-disable import/no-unresolved */ + +import React, { useState, useEffect, useCallback } from 'react'; +import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +import { SearchButton } from 'docsearch-react'; + +let DocSearch = null; + +export default function SearchBar() { + const [isLoaded, setIsLoaded] = useState(false); + const [isShowing, setIsShowing] = useState(false); + const { siteConfig = {} } = useDocusaurusContext(); + + const { + indexName, + appId, + apiKey, + searchParameters, + } = siteConfig.themeConfig.algolia; + + const load = useCallback( + function load() { + if (isLoaded === true) { + return; + } + + Promise.all([ + import('docsearch-react'), + import('docsearch-react/dist/esm/style.css'), + ]).then(([{ DocSearch: DocSearchComp }]) => { + DocSearch = DocSearchComp; + setIsLoaded(true); + }); + }, + [isLoaded, setIsLoaded] + ); + + const onOpen = useCallback( + function onOpen() { + load(); + setIsShowing(true); + document.body.classList.add('DocSearch--active'); + }, + [load, setIsShowing] + ); + + const onClose = useCallback( + function onClose() { + setIsShowing(false); + document.body.classList.remove('DocSearch--active'); + }, + [setIsShowing] + ); + + useEffect(() => { + function onKeyDown(event) { + if ( + (event.key === 'Escape' && isShowing) || + (event.key === 'k' && (event.metaKey || event.ctrlKey)) + ) { + event.preventDefault(); + + if (isShowing) { + onClose(); + } else { + onOpen(); + } + } + } + + window.addEventListener('keydown', onKeyDown); + + return () => { + window.removeEventListener('keydown', onKeyDown); + }; + }, [isShowing, onOpen, onClose]); + + return ( + <> + + + {isLoaded && isShowing && ( + + )} + + ); +} diff --git a/yarn.lock b/yarn.lock index 23023cf32..4fb060de6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9,11 +9,23 @@ dependencies: "@algolia/cache-common" "4.0.2" +"@algolia/cache-browser-local-storage@4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.1.0.tgz#c4f1bfc57ea562248072b35831e3c4b646cc3921" + integrity sha512-r8BOgqZXVt+JPgP19PQNzZ+lYP+MP6eZKNQqfRYofFEx+K9oyfdtGCqmoWJsBUi3nNOzhbOcg2jfP2GJzJBZ5g== + dependencies: + "@algolia/cache-common" "4.1.0" + "@algolia/cache-common@4.0.2": version "4.0.2" resolved "https://registry.yarnpkg.com/@algolia/cache-common/-/cache-common-4.0.2.tgz#4ce81a3534ab6947570644df8803ff8f7d6b5ccf" integrity sha512-J0jGGTaYR0YrGjNRAzECYrY12qxIHpb3nxfCb4BZTP9h7Xs1GL52DVjX+RCuRJwZ71IeAn2lMNnAqYqrSWfWhA== +"@algolia/cache-common@4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@algolia/cache-common/-/cache-common-4.1.0.tgz#ab895f8049ff7064ca1bfea504a56f97fd5d4683" + integrity sha512-ZvvK40bs1BWLErchleZL4ctHT2uH56uLMnpZPCuIk+H2PKddeiIQc/z2JDu2BHr68u513XIAAoQ+C+LgKNugmw== + "@algolia/cache-in-memory@4.0.2": version "4.0.2" resolved "https://registry.yarnpkg.com/@algolia/cache-in-memory/-/cache-in-memory-4.0.2.tgz#f5ba60fb16c8602e71b310d751b62680800f0e70" @@ -21,6 +33,13 @@ dependencies: "@algolia/cache-common" "4.0.2" +"@algolia/cache-in-memory@4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@algolia/cache-in-memory/-/cache-in-memory-4.1.0.tgz#cb9b575df1ebe3befd198a50a444a7d181e50853" + integrity sha512-2382OXYFDeoPLA5vP9KP58ad15ows24ML5/io/T1N0xsZ0eVXDkT52qgaJw/esUfEkWScZ2R8kpesUa+qEP+kw== + dependencies: + "@algolia/cache-common" "4.1.0" + "@algolia/client-account@4.0.2": version "4.0.2" resolved "https://registry.yarnpkg.com/@algolia/client-account/-/client-account-4.0.2.tgz#b7194bc6096a22c07a527d42890cad6703f4c3c5" @@ -30,6 +49,15 @@ "@algolia/client-search" "4.0.2" "@algolia/transporter" "4.0.2" +"@algolia/client-account@4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@algolia/client-account/-/client-account-4.1.0.tgz#a31d26c22e6a56554ea4aa8552d153b1a1aa4363" + integrity sha512-GFINlsxAHM/GEeDBjoTx8+J1ra9SINQCuXi2C9QSLFClPKug2lzApm8niJJGXckhyZ2aDLb7drJ1qJ8bTspApw== + dependencies: + "@algolia/client-common" "4.1.0" + "@algolia/client-search" "4.1.0" + "@algolia/transporter" "4.1.0" + "@algolia/client-analytics@4.0.2": version "4.0.2" resolved "https://registry.yarnpkg.com/@algolia/client-analytics/-/client-analytics-4.0.2.tgz#ee8dfa64fb98c1391fb5f919848774455c8591c8" @@ -40,6 +68,16 @@ "@algolia/requester-common" "4.0.2" "@algolia/transporter" "4.0.2" +"@algolia/client-analytics@4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@algolia/client-analytics/-/client-analytics-4.1.0.tgz#eb05ccb636351b2d6494b2affb6034b791236998" + integrity sha512-JMyZ9vXGbTJWiO66fWEu9uJ7GSYfouUyaq8W/6esADPtBbelf+Nc0NRlicOwHHJGwiJvWdvELafxrhkR1+KR8A== + dependencies: + "@algolia/client-common" "4.1.0" + "@algolia/client-search" "4.1.0" + "@algolia/requester-common" "4.1.0" + "@algolia/transporter" "4.1.0" + "@algolia/client-common@4.0.2": version "4.0.2" resolved "https://registry.yarnpkg.com/@algolia/client-common/-/client-common-4.0.2.tgz#f4defbb9af56854b68a25d10d7d804a28ed5c764" @@ -48,6 +86,14 @@ "@algolia/requester-common" "4.0.2" "@algolia/transporter" "4.0.2" +"@algolia/client-common@4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@algolia/client-common/-/client-common-4.1.0.tgz#cd3a71cef1e0d87476252cbee20b0da938f6221c" + integrity sha512-fjSMKeG54vAyQAhf+uz039/birTiLun8nDuCNx4CUbzGl97M0g96Q8jpsiZa0cjSNgh0VakMzn2GnHbS55W9/Q== + dependencies: + "@algolia/requester-common" "4.1.0" + "@algolia/transporter" "4.1.0" + "@algolia/client-recommendation@4.0.2": version "4.0.2" resolved "https://registry.yarnpkg.com/@algolia/client-recommendation/-/client-recommendation-4.0.2.tgz#ca60ab1a4c50a1db4ce7f5b2c534f856c185904c" @@ -57,6 +103,15 @@ "@algolia/requester-common" "4.0.2" "@algolia/transporter" "4.0.2" +"@algolia/client-recommendation@4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@algolia/client-recommendation/-/client-recommendation-4.1.0.tgz#a0a26de4a6dd902d7ca55cf381cce3a7280d5b49" + integrity sha512-UEN/QgQwVtVH++yAs2uTuyZZQQ1p5Xs/7/FKT4Kh9/8NAyqDD49zuyq/giw8PRNhWc3C/9jiO7X4RKE8QrVWGw== + dependencies: + "@algolia/client-common" "4.1.0" + "@algolia/requester-common" "4.1.0" + "@algolia/transporter" "4.1.0" + "@algolia/client-search@4.0.2": version "4.0.2" resolved "https://registry.yarnpkg.com/@algolia/client-search/-/client-search-4.0.2.tgz#5daf37865e0cb357f2239e9800a32bd62eb8abb0" @@ -66,11 +121,25 @@ "@algolia/requester-common" "4.0.2" "@algolia/transporter" "4.0.2" +"@algolia/client-search@4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@algolia/client-search/-/client-search-4.1.0.tgz#07cc422af997e409968d3b74142e984aa71ae38c" + integrity sha512-bpCYMEXUdyiopEBSHHwnrRhNEwOLstIeb0Djz+/pVuTXEr3Xg3JUoAZ8xFsCVldcXaZQpbi1/T0y3ky6xUVzfw== + dependencies: + "@algolia/client-common" "4.1.0" + "@algolia/requester-common" "4.1.0" + "@algolia/transporter" "4.1.0" + "@algolia/logger-common@4.0.2": version "4.0.2" resolved "https://registry.yarnpkg.com/@algolia/logger-common/-/logger-common-4.0.2.tgz#f31731d0064faa8c270dc9c925bd9002c1a8e494" integrity sha512-yj3z/F1Rvk1wGB4N4FDhF86dqwJuiHcS/0Jb1cnXWfVDa/PoumaV7GU+khRlZrSeUbOP9npaMjb8ZtX7KafpaQ== +"@algolia/logger-common@4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@algolia/logger-common/-/logger-common-4.1.0.tgz#05608dee38dfa35bfe37874683760140d471bfdc" + integrity sha512-QrE4Srf1LB7ekLzl68bFqlTrv7Wk7+GpsaGfB4xFZ9Pfv89My9p7qTVqdLlA44hEFY3fZ9csJp1/PFVucgNB4w== + "@algolia/logger-console@4.0.2": version "4.0.2" resolved "https://registry.yarnpkg.com/@algolia/logger-console/-/logger-console-4.0.2.tgz#e42c6606000b03adb2b85af6300c9cb2e96c2bb1" @@ -78,6 +147,13 @@ dependencies: "@algolia/logger-common" "4.0.2" +"@algolia/logger-console@4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@algolia/logger-console/-/logger-console-4.1.0.tgz#099ee86716aea4c976345417397ddfa1338a5acc" + integrity sha512-sKELkiKIrj/tPRAdhOPNI0UxhK2uiIUXnGs/3ztAif6QX7vyE3lY19sj5pIVJctRvl8LW2UlzpBFGlcCDkho9Q== + dependencies: + "@algolia/logger-common" "4.1.0" + "@algolia/requester-browser-xhr@4.0.2": version "4.0.2" resolved "https://registry.yarnpkg.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.0.2.tgz#dc821f30497173de028b78f88548b8f2b0554032" @@ -85,11 +161,23 @@ dependencies: "@algolia/requester-common" "4.0.2" +"@algolia/requester-browser-xhr@4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.1.0.tgz#a7ab63f184f3d0aa8e85ac73ce39c528271c6d9b" + integrity sha512-bLMfIAkOLs1/vGA09yxU0N5+bE0fSSvEH2ySqVssfWLMP+KRAvby2Goxm8BgI9xLkOvLbhazfQ4Ov2448VvA1g== + dependencies: + "@algolia/requester-common" "4.1.0" + "@algolia/requester-common@4.0.2": version "4.0.2" resolved "https://registry.yarnpkg.com/@algolia/requester-common/-/requester-common-4.0.2.tgz#739cf210ab44778b05b63f7b8ca3f6299e84de17" integrity sha512-0r8AJfgABLQjYeO3BLjUxhmTvUw1p/xVitK3Vhgcnx3tDHPaNVcFyjrmQpKKJuztVxj2tM9DVvsGfrz07Jn8Tg== +"@algolia/requester-common@4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@algolia/requester-common/-/requester-common-4.1.0.tgz#91907e9963e455b11862d1cca02fc1d1d961dbce" + integrity sha512-Cy0ciOv5uIm6wF+uLc9DHhxgPJtYQuy1f//hwJcW5mlPX/prPgxWwLXzWyyA+Ca7uU3q+0Y3cIFvEWM5pDxMEg== + "@algolia/requester-node-http@4.0.2": version "4.0.2" resolved "https://registry.yarnpkg.com/@algolia/requester-node-http/-/requester-node-http-4.0.2.tgz#1dd4ad2d04fc28f78a642fd8fe4af1cec6230f07" @@ -97,6 +185,13 @@ dependencies: "@algolia/requester-common" "4.0.2" +"@algolia/requester-node-http@4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@algolia/requester-node-http/-/requester-node-http-4.1.0.tgz#db0a224538691f6fab18ced27c548cf3b4017689" + integrity sha512-tXp6Pjx9dFgM5ccW6YfEN6v2Zqq8uGwhS1pyq03/aRYRBK60LptjG5jo++vrOytrQDOnIjcZtQzBQch2GjCVmw== + dependencies: + "@algolia/requester-common" "4.1.0" + "@algolia/transporter@4.0.2": version "4.0.2" resolved "https://registry.yarnpkg.com/@algolia/transporter/-/transporter-4.0.2.tgz#25b1666f5cb4a2ff437a324e4b2705b21eaa0e3b" @@ -106,6 +201,15 @@ "@algolia/logger-common" "4.0.2" "@algolia/requester-common" "4.0.2" +"@algolia/transporter@4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@algolia/transporter/-/transporter-4.1.0.tgz#18cb8837ca4079a23572a3b7dbefece71fb6fff3" + integrity sha512-Z7PjHazSC+KFLDuCFOjvRNgLfh7XOE4tXi0a9O3gBRup4Sk3VQCfTw4ygCF3rRx6uYbq192efLu0nL1E9azxLA== + dependencies: + "@algolia/cache-common" "4.1.0" + "@algolia/logger-common" "4.1.0" + "@algolia/requester-common" "4.1.0" + "@babel/cli@7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.8.4.tgz#505fb053721a98777b2b175323ea4f090b7d3c1c" @@ -3724,6 +3828,26 @@ algoliasearch@^3.24.5: semver "^5.1.0" tunnel-agent "^0.6.0" +algoliasearch@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-4.1.0.tgz#d422ac0d115497021a6c96f4b9747dbaa63f164a" + integrity sha512-0lzjvqQZkJYPuv7LyQauMIMCFFzJWfUf3m9KuHjmFubwbnTDa87KCMXKouMJ0kWXXt6nTLNt0+2YRREOWx2PHw== + dependencies: + "@algolia/cache-browser-local-storage" "4.1.0" + "@algolia/cache-common" "4.1.0" + "@algolia/cache-in-memory" "4.1.0" + "@algolia/client-account" "4.1.0" + "@algolia/client-analytics" "4.1.0" + "@algolia/client-common" "4.1.0" + "@algolia/client-recommendation" "4.1.0" + "@algolia/client-search" "4.1.0" + "@algolia/logger-common" "4.1.0" + "@algolia/logger-console" "4.1.0" + "@algolia/requester-browser-xhr" "4.1.0" + "@algolia/requester-common" "4.1.0" + "@algolia/requester-node-http" "4.1.0" + "@algolia/transporter" "4.1.0" + alphanum-sort@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"