feat(docsearch): add DocSearch for Docusaurus (#39)
Co-authored-by: eunjae-lee <karis612@gmail.com> Co-authored-by: Shipow <shipowlata@gmail.com>
27
babel.config.js
Normal file
|
|
@ -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']],
|
||||
};
|
||||
};
|
||||
44
package.json
Normal file
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
20
rollup.config.js
Normal file
|
|
@ -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],
|
||||
};
|
||||
235
src/DocSearch.tsx
Normal file
|
|
@ -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<InternalDocSearchHit>
|
||||
>({
|
||||
query: '',
|
||||
suggestions: [],
|
||||
} as any);
|
||||
|
||||
const searchBoxRef = useRef<HTMLDivElement | null>(null);
|
||||
const dropdownRef = useRef<HTMLDivElement | null>(null);
|
||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||
const snipetLength = useRef<number>(10);
|
||||
|
||||
const searchClient = React.useMemo(() => createSearchClient(appId, apiKey), [
|
||||
appId,
|
||||
apiKey,
|
||||
]);
|
||||
|
||||
const {
|
||||
getEnvironmentProps,
|
||||
getRootProps,
|
||||
getFormProps,
|
||||
getLabelProps,
|
||||
getInputProps,
|
||||
getMenuProps,
|
||||
getItemProps,
|
||||
} = React.useMemo(
|
||||
() =>
|
||||
createAutocomplete<
|
||||
InternalDocSearchHit,
|
||||
React.FormEvent<HTMLFormElement>,
|
||||
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: '<mark>',
|
||||
highlightPostTag: '</mark>',
|
||||
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<DocSearchHit[]>(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 (
|
||||
<div
|
||||
className={[
|
||||
'DocSearch-Container',
|
||||
state.status === 'stalled' && 'DocSearch-Container--Stalled',
|
||||
state.status === 'error' && 'DocSearch-Container--Errored',
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
{...getRootProps({
|
||||
onClick(event: React.MouseEvent) {
|
||||
if (event.target === event.currentTarget) {
|
||||
onClose();
|
||||
}
|
||||
},
|
||||
})}
|
||||
>
|
||||
<div className="DocSearch-Modal">
|
||||
<header className="DocSearch-SearchBar" ref={searchBoxRef}>
|
||||
<SearchBox
|
||||
inputRef={inputRef}
|
||||
query={state.query}
|
||||
getFormProps={getFormProps}
|
||||
getLabelProps={getLabelProps}
|
||||
getInputProps={getInputProps}
|
||||
onClose={onClose}
|
||||
/>
|
||||
</header>
|
||||
|
||||
<div className="DocSearch-Dropdown" ref={dropdownRef}>
|
||||
<Dropdown
|
||||
state={state}
|
||||
getMenuProps={getMenuProps}
|
||||
getItemProps={getItemProps}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<footer className="DocSearch-Footer">
|
||||
<Footer />
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
38
src/Dropdown/Dropdown.tsx
Normal file
|
|
@ -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<InternalDocSearchHit>;
|
||||
getMenuProps: GetMenuProps;
|
||||
getItemProps: GetItemProps<InternalDocSearchHit, React.MouseEvent>;
|
||||
}
|
||||
|
||||
export function Dropdown(props: DropdownProps) {
|
||||
if (props.state.status === 'error') {
|
||||
return <Error />;
|
||||
}
|
||||
|
||||
if (
|
||||
props.state.status === 'idle' &&
|
||||
props.state.suggestions.every(source => source.items.length === 0)
|
||||
) {
|
||||
return <NoResults query={props.state.query} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Results
|
||||
suggestions={props.state.suggestions}
|
||||
getMenuProps={props.getMenuProps}
|
||||
getItemProps={props.getItemProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
1
src/Dropdown/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export * from './Dropdown';
|
||||
10
src/Error/Error.tsx
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import React from 'react';
|
||||
|
||||
export function Error() {
|
||||
return (
|
||||
<p>
|
||||
We‘re unable to fetch results. You might want to check your network
|
||||
connection.
|
||||
</p>
|
||||
);
|
||||
}
|
||||
1
src/Error/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export * from './Error';
|
||||
20
src/Footer/AlgoliaLogo.tsx
Normal file
64
src/Footer/Footer.tsx
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
import React from 'react';
|
||||
|
||||
import { AlgoliaLogo } from './AlgoliaLogo';
|
||||
|
||||
export function Footer() {
|
||||
return (
|
||||
<>
|
||||
<div className="DocSearch-Logo">
|
||||
<AlgoliaLogo />
|
||||
</div>
|
||||
<ul className="DocSearch-Commands">
|
||||
<li>
|
||||
<span className="DocSearch-Commands-Key">
|
||||
<CommandIcon>
|
||||
<path d="M12 3.53088v3c0 1-1 2-2 2H4M7 11.53088l-3-3 3-3" />
|
||||
</CommandIcon>
|
||||
</span>
|
||||
<span className="DocSearch-Label">to select</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="DocSearch-Commands-Key">
|
||||
<CommandIcon>
|
||||
<path d="M7.5 3.5v8M10.5 8.5l-3 3-3-3" />
|
||||
</CommandIcon>
|
||||
</span>
|
||||
<span className="DocSearch-Commands-Key">
|
||||
<CommandIcon>
|
||||
<path d="M7.5 11.5v-8M10.5 6.5l-3-3-3 3" />
|
||||
</CommandIcon>
|
||||
</span>
|
||||
<span className="DocSearch-Label">to navigate</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="DocSearch-Commands-Key">
|
||||
<CommandIcon>
|
||||
<path d="M13.6167 8.936c-.1065.3583-.6883.962-1.4875.962-.7993 0-1.653-.9165-1.653-2.1258v-.5678c0-1.2548.7896-2.1016 1.653-2.1016.8634 0 1.3601.4778 1.4875 1.0724M9 6c-.1352-.4735-.7506-.9219-1.46-.8972-.7092.0246-1.344.57-1.344 1.2166s.4198.8812 1.3445.9805C8.465 7.3992 8.968 7.9337 9 8.5c.032.5663-.454 1.398-1.4595 1.398C6.6593 9.898 6 9 5.963 8.4851m-1.4748.5368c-.2635.5941-.8099.876-1.5443.876s-1.7073-.6248-1.7073-2.204v-.4603c0-1.0416.721-2.131 1.7073-2.131.9864 0 1.6425 1.031 1.5443 2.2492h-2.956" />
|
||||
</CommandIcon>
|
||||
</span>
|
||||
<span className="DocSearch-Label">to close</span>
|
||||
</li>
|
||||
</ul>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
interface CommandIconProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
function CommandIcon(props: CommandIconProps) {
|
||||
return (
|
||||
<svg width="15" height="15">
|
||||
<g
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="1.2"
|
||||
>
|
||||
{props.children}
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
1
src/Footer/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export * from './Footer';
|
||||
1
src/Footer/index.tsx
Normal file
|
|
@ -0,0 +1 @@
|
|||
export * from './Footer';
|
||||
12
src/NoResults/NoResults.tsx
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import React from 'react';
|
||||
|
||||
interface NoResultsProps {
|
||||
query: string;
|
||||
}
|
||||
|
||||
export function NoResults(props: NoResultsProps) {
|
||||
return <div className="DocSearch-NoResults">
|
||||
<div className="Docsearch-Hit-title">No results for “{props.query}“.</div>
|
||||
<div className="DocSearch-Label">Try another search or if you believe this query should lead to actual results, please let us know with a <a href="">GitHub issue</a>.</div>
|
||||
</div>;
|
||||
}
|
||||
1
src/NoResults/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export * from './NoResults';
|
||||
31
src/Results/ActionIcon.tsx
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import React from 'react';
|
||||
|
||||
export function SelectIcon() {
|
||||
return (
|
||||
<svg width="20" height="20">
|
||||
<g
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
fill="none"
|
||||
fillRule="evenodd"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M16 2v5c0 2-1 4-4 4H4" />
|
||||
<path d="M8 16l-5-5 5-4" />
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function GoToExternal() {
|
||||
return (
|
||||
<svg width="20" height="20">
|
||||
<path
|
||||
d="M5 6v9h9v-3a1 1 0 112 0v4l-1 1H4l-1-1V5l1-1h4a1 1 0 110 2H5zm5 5a1 1 0 11-1-1l5-6h-3a1 1 0 110-2h6a1 1 0 011 1v6a1 1 0 11-2 0V6l-6 5z"
|
||||
fill="currentColor"
|
||||
fillRule="nonzero"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
147
src/Results/Results.tsx
Normal file
|
|
@ -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<AutocompleteSuggestion<InternalDocSearchHit>>;
|
||||
getMenuProps: GetMenuProps;
|
||||
getItemProps: GetItemProps<InternalDocSearchHit, React.MouseEvent>;
|
||||
}
|
||||
|
||||
export function Results(props: ResultsProps) {
|
||||
return (
|
||||
<div className="DocSearch-Dropdown-Container">
|
||||
{props.suggestions.map(({ source, items }) => {
|
||||
const title = items[0].hierarchy.lvl0;
|
||||
|
||||
return (
|
||||
<section key={title} className="DocSearch-Hits">
|
||||
<div className="DocSearch-Hit-source">{title}</div>
|
||||
|
||||
<ul {...props.getMenuProps()}>
|
||||
{items.map((item, index) => {
|
||||
return (
|
||||
<li
|
||||
key={item.objectID}
|
||||
className={[
|
||||
'DocSearch-Hit',
|
||||
item.__docsearch_parent && 'DocSearch-Hit--Child',
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
{...props.getItemProps({
|
||||
item,
|
||||
source,
|
||||
})}
|
||||
>
|
||||
{item.__docsearch_parent && (
|
||||
<svg className="DocSearch-Hit-Tree">
|
||||
<g
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
fill="none"
|
||||
fillRule="evenodd"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
{item.__docsearch_parent !==
|
||||
items[index + 1]?.__docsearch_parent ? (
|
||||
<path d="M8 8v22M26.5 30H8.3" />
|
||||
) : (
|
||||
<path d="M8 8v44M26.5 30H8.3" />
|
||||
)}
|
||||
</g>
|
||||
</svg>
|
||||
)}
|
||||
|
||||
<a href={item.url}>
|
||||
<div className="DocSearch-Hit-Container">
|
||||
<div className="DocSearch-Hit-icon">
|
||||
<SourceIcon type={item.type} />
|
||||
</div>
|
||||
|
||||
{item.hierarchy[item.type] && item.type === 'lvl1' && (
|
||||
<div className="DocSearch-Hit-content-wrapper">
|
||||
<span
|
||||
className="DocSearch-Hit-title"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html:
|
||||
item._snippetResult.hierarchy.lvl1.value,
|
||||
}}
|
||||
/>
|
||||
{item.content && (
|
||||
<span
|
||||
className="DocSearch-Hit-path"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: item._snippetResult.content.value,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{item.hierarchy[item.type] &&
|
||||
(item.type === 'lvl2' ||
|
||||
item.type === 'lvl3' ||
|
||||
item.type === 'lvl4' ||
|
||||
item.type === 'lvl5' ||
|
||||
item.type === 'lvl6') && (
|
||||
<div className="DocSearch-Hit-content-wrapper">
|
||||
<span
|
||||
className="DocSearch-Hit-title"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html:
|
||||
item._snippetResult.hierarchy[item.type]
|
||||
.value,
|
||||
}}
|
||||
/>
|
||||
<span
|
||||
className="DocSearch-Hit-path"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html:
|
||||
item._snippetResult.hierarchy.lvl1.value,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{item.type === 'content' && (
|
||||
<div className="DocSearch-Hit-content-wrapper">
|
||||
<span
|
||||
className="DocSearch-Hit-title"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: item._snippetResult.content.value,
|
||||
}}
|
||||
/>
|
||||
<span
|
||||
className="DocSearch-Hit-path"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html:
|
||||
item._snippetResult.hierarchy.lvl1.value,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="DocSearch-Hit-action">
|
||||
<SelectIcon />
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</section>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
58
src/Results/SourceIcon.tsx
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
import React from 'react';
|
||||
|
||||
export function SourceIcon(props: { type: string }) {
|
||||
switch (props.type) {
|
||||
case 'lvl1':
|
||||
return <LvlIcon />;
|
||||
case 'content':
|
||||
return <ContentIcon />;
|
||||
default:
|
||||
return <AnchorIcon />;
|
||||
}
|
||||
}
|
||||
|
||||
function LvlIcon() {
|
||||
return (
|
||||
<svg width="20" height="20">
|
||||
<path
|
||||
d="M17 6h-4-1V1l5 5v11l-1 2H4l-1-1V2l1-1h8l5 5z"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
fill="none"
|
||||
fillRule="evenodd"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function AnchorIcon() {
|
||||
return (
|
||||
<svg width="20" height="20">
|
||||
<path
|
||||
d="M13 13h4-4V8H7v5h6v4-4H7V8H3h4V3v5h6V3v5h4-4v5zm-6 0v4-4H3h4z"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
fill="none"
|
||||
fillRule="evenodd"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function ContentIcon() {
|
||||
return (
|
||||
<svg width="20" height="20">
|
||||
<path
|
||||
d="M17 5H3h14zm0 5H3h14zm0 5H3h14z"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
fill="none"
|
||||
fillRule="evenodd"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
1
src/Results/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export * from './Results';
|
||||
23
src/SearchBox/LoadingIcon.tsx
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import React from 'react';
|
||||
|
||||
export function LoadingIcon() {
|
||||
return (
|
||||
<svg viewBox="0 0 38 38" stroke="currentColor" strokeOpacity=".5">
|
||||
<g fill="none" fillRule="evenodd">
|
||||
<g transform="translate(1 1)" strokeWidth="2">
|
||||
<circle strokeOpacity=".3" 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>
|
||||
);
|
||||
}
|
||||
13
src/SearchBox/ResetIcon.tsx
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import React from 'react';
|
||||
|
||||
export function ResetIcon() {
|
||||
return (
|
||||
<svg viewBox="0 0 10 10">
|
||||
<path
|
||||
d="M5 4.12L8.93.18a.62.62 0 1 1 .89.89L5.88 5l3.94 3.93a.62.62 0 1 1-.89.89L5 5.88 1.07 9.82a.62.62 0 1 1-.89-.89L4.12 5 .18 1.07a.62.62 0 1 1 .89-.89L5 4.12z"
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
74
src/SearchBox/SearchBox.tsx
Normal file
|
|
@ -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<HTMLInputElement | null>;
|
||||
query: string;
|
||||
getFormProps: GetFormProps<React.FormEvent>;
|
||||
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 (
|
||||
<>
|
||||
<form
|
||||
action=""
|
||||
role="search"
|
||||
noValidate
|
||||
className="DocSearch-Form"
|
||||
onSubmit={onSubmit}
|
||||
onReset={onReset}
|
||||
>
|
||||
<label className="DocSearch-MagnifierLabel" {...props.getLabelProps()}>
|
||||
<SearchIcon />
|
||||
</label>
|
||||
|
||||
<div className="DocSearch-LoadingIndicator">
|
||||
<LoadingIcon />
|
||||
</div>
|
||||
|
||||
<input
|
||||
className="DocSearch-Input"
|
||||
ref={props.inputRef}
|
||||
{...props.getInputProps({
|
||||
inputElement: props.inputRef.current!,
|
||||
type: 'search',
|
||||
maxLength: '512',
|
||||
})}
|
||||
/>
|
||||
|
||||
<button
|
||||
type="reset"
|
||||
title="Clear the query"
|
||||
className="DocSearch-Reset"
|
||||
hidden={!props.query}
|
||||
onClick={onReset}
|
||||
>
|
||||
<ResetIcon />
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<button className="DocSearch-Cancel" onClick={props.onClose}>
|
||||
Cancel
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
17
src/SearchBox/SearchIcon.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import React from 'react';
|
||||
|
||||
export function SearchIcon() {
|
||||
return (
|
||||
<svg viewBox="0 0 18 18">
|
||||
<path
|
||||
d="M13.14 13.14L17 17l-3.86-3.86A7.11 7.11 0 1 1 3.08 3.08a7.11 7.11 0 0 1 10.06 10.06z"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.3"
|
||||
fill="none"
|
||||
fillRule="evenodd"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
1
src/SearchBox/index.tsx
Normal file
|
|
@ -0,0 +1 @@
|
|||
export * from './SearchBox';
|
||||
57
src/SearchButton/SearchButton.tsx
Normal file
|
|
@ -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 (
|
||||
<button
|
||||
type="button"
|
||||
className="DocSearch-SearchButton"
|
||||
onClick={props.onClick}
|
||||
>
|
||||
<svg
|
||||
className="DocSearch-SearchButton-Icon"
|
||||
width={18}
|
||||
viewBox="0 0 18 18"
|
||||
>
|
||||
<path
|
||||
d="M13.14 13.14L17 17l-3.86-3.86A7.11 7.11 0 1 1 3.08 3.08a7.11 7.11 0 0 1 10.06 10.06z"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.78"
|
||||
fill="none"
|
||||
fillRule="evenodd"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
</svg>
|
||||
|
||||
<span className="DocSearch-SearchButton-Placeholder">Search</span>
|
||||
|
||||
<span className="DocSearch-SearchButton-Key">{key}</span>
|
||||
<span className="DocSearch-SearchButton-Key">K</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
1
src/SearchButton/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export * from './SearchButton';
|
||||
2
src/index.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
export * from './DocSearch';
|
||||
export * from './SearchButton';
|
||||
543
src/style.css
Normal file
|
|
@ -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) {
|
||||
/* ... */
|
||||
}
|
||||
11
src/svg/icon_action_external.svg
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: sketchtool 55.2 (78181) - https://sketchapp.com -->
|
||||
<title>1c947b1e-eb2b-4ad3-ac34-75998ca8847a@1.00x</title>
|
||||
<desc>Created with sketchtool.</desc>
|
||||
<g id="autocomplete" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="icon/action/external" fill="#7E8BA1" fill-rule="nonzero">
|
||||
<path d="M4.82352941,6.05882353 L4.82352941,15.1764706 L13.9411765,15.1764706 L13.9411765,12.4411765 C13.9411765,11.9376227 14.3493874,11.5294118 14.8529412,11.5294118 C15.3564949,11.5294118 15.7647059,11.9376227 15.7647059,12.4411765 L15.7647059,16.0882353 C15.7647059,16.591789 15.3564949,17 14.8529412,17 L3.91176471,17 C3.40821096,17 3,16.591789 3,16.0882353 L3,5.14705882 C3,4.64350508 3.40821096,4.23529412 3.91176471,4.23529412 L7.55882353,4.23529412 C8.06237727,4.23529412 8.47058824,4.64350508 8.47058824,5.14705882 C8.47058824,5.65061257 8.06237727,6.05882353 7.55882353,6.05882353 L4.82352941,6.05882353 Z M10.0269706,11.2622647 C9.79804975,11.4992839 9.45905251,11.5943409 9.14027273,11.5109003 C8.82149296,11.4274598 8.5725402,11.178507 8.48909965,10.8597273 C8.4056591,10.5409475 8.50071606,10.2019503 8.73773529,9.97302941 L14.4818529,4.23073529 L11.2213824,4.23073529 C10.8895876,4.24124202 10.5783995,4.07022973 10.4093947,3.78451036 C10.2403899,3.49879099 10.2403899,3.14370901 10.4093947,2.85798964 C10.5783995,2.57227027 10.8895876,2.40125798 11.2213824,2.41176471 L16.6792059,2.41176471 C16.9206024,2.41130696 17.1522386,2.50701998 17.3228464,2.67779895 C17.4934543,2.84857791 17.5889636,3.08031002 17.5882353,3.32170588 L17.5882353,8.77952941 C17.598742,9.11132421 17.4277297,9.42251227 17.1420104,9.59151707 C16.856291,9.76052186 16.501209,9.76052186 16.2154896,9.59151707 C15.9297703,9.42251227 15.758758,9.11132421 15.7692647,8.77952941 L15.7692647,5.51723529 L10.0269706,11.2622647 Z" id="external"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2 KiB |
14
src/svg/icon_action_goto.svg
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: sketchtool 55.2 (78181) - https://sketchapp.com -->
|
||||
<title>1c20c2f5-d737-495d-a54b-64fd79363595@1.00x</title>
|
||||
<desc>Created with sketchtool.</desc>
|
||||
<g id="autocomplete" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
|
||||
<g id="icon/action/goto" stroke="#7E8BA1" stroke-width="2">
|
||||
<g id="Group" transform="translate(2.000000, 3.000000)">
|
||||
<path d="M16,0 C16,1.33333333 16,2.66666667 16,4 C16,6 14,8 12,8 C10.6666667,8 6.66666667,8 0,8" id="Path-2"></path>
|
||||
<polyline id="Path" points="6 14 2.27373675e-13 8 6 2"></polyline>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 894 B |
14
src/svg/icon_source_anchor.svg
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: sketchtool 55.2 (78181) - https://sketchapp.com -->
|
||||
<title>78adcbdb-966a-4cd6-9309-109b3e7780e1@1.00x</title>
|
||||
<desc>Created with sketchtool.</desc>
|
||||
<g id="autocomplete" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round">
|
||||
<g id="icon/source/anchor" stroke="#D4D9E1" stroke-width="2">
|
||||
<path d="M3,7 L17,7" id="Line-5-Copy-3"></path>
|
||||
<path d="M3,13 L17,13" id="Line-5-Copy-4"></path>
|
||||
<path d="M6,10 L20,10" id="Line-5-Copy-3" transform="translate(13.000000, 10.000000) rotate(-270.000000) translate(-13.000000, -10.000000) "></path>
|
||||
<path d="M6.75015599e-14,10 L14,10" id="Line-5-Copy-4" transform="translate(7.000000, 10.000000) rotate(-270.000000) translate(-7.000000, -10.000000) "></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1 KiB |
13
src/svg/icon_source_content.svg
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: sketchtool 55.2 (78181) - https://sketchapp.com -->
|
||||
<title>90955cc9-f7b3-4479-898c-19510cc4b6d4@1.00x</title>
|
||||
<desc>Created with sketchtool.</desc>
|
||||
<g id="autocomplete" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round">
|
||||
<g id="icon/source/content" stroke="#D4D9E1" stroke-width="2">
|
||||
<path d="M3,5 L17,5" id="Line-5"></path>
|
||||
<path d="M3,10 L17,10" id="Line-5-Copy"></path>
|
||||
<path d="M3,15 L17,15" id="Line-5-Copy-2"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 746 B |
11
src/svg/icon_source_page.svg
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: sketchtool 55.2 (78181) - https://sketchapp.com -->
|
||||
<title>1f96940f-ca50-4e02-84bd-79bc02579d53@1.00x</title>
|
||||
<desc>Created with sketchtool.</desc>
|
||||
<g id="autocomplete" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linejoin="round">
|
||||
<g id="icon/source/page" stroke="#D4D9E1" stroke-width="2">
|
||||
<path d="M16.9999972,6 C16.9999972,14 16.9999972,18 16.9999972,18 C16.9999972,18.5223389 16.7907986,19 15.9999972,19 C8.46227598,19 4.46227598,19 3.99999715,19 C3.30658461,19 2.99999715,18.6738281 2.99999715,18 C2.99999715,18 2.99999715,2 2.99999715,2 C2.99999715,1.44580078 3.42492676,1 3.99999715,1 L11.9999972,1 L16.9999972,6 L12.9999972,6 C12.4106445,6 11.9999972,5.60534668 11.9999972,5 L11.9999972,1 L16.9999972,6 Z" id="Path"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1,022 B |
16
src/svg/icon_source_recent.svg
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: sketchtool 55.2 (78181) - https://sketchapp.com -->
|
||||
<title>ee397e5b-5117-4da7-bdc8-0d42da41349d@1.00x</title>
|
||||
<desc>Created with sketchtool.</desc>
|
||||
<g id="autocomplete" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
|
||||
<g id="icon/source/recent" stroke="#D4D9E1" stroke-width="2">
|
||||
<g id="Group" transform="translate(0.096664, 2.499066)">
|
||||
<path d="M3.08304498,4.09716157 C3.43350031,3.49361228 3.86713807,2.92510915 4.38395826,2.40828896 C7.59501021,-0.802762985 12.801154,-0.802762985 16.012206,2.40828896 C19.2232579,5.6193409 19.2232579,10.8254847 16.012206,14.0365367 L16.012206,14.0365367 C13.3386797,16.7100629 9.28210578,17.1576076 6.14553896,15.3791705 C5.51491669,15.021607 4.92148395,14.5740624 4.38395826,14.0365367" id="Path"></path>
|
||||
<polyline id="Path-3" transform="translate(3.428951, 3.774856) rotate(-45.000000) translate(-3.428951, -3.774856) " points="4.80253786 6.52203073 2.05536332 3.77485619 4.80253786 1.02768166"></polyline>
|
||||
<path d="M10.3565991,3.50093368 L10.3565991,9.09953008" id="Line-3"></path>
|
||||
<path d="M10.3565991,9.10586193 L12.9033365,10.5009337" id="Line"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
12
src/svg/icon_tree_child.svg
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="30px" height="60px" viewBox="0 0 30 60" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: sketchtool 55.2 (78181) - https://sketchapp.com -->
|
||||
<title>8d4bc779-eae6-4075-8edb-f964556f9cd5@1.00x</title>
|
||||
<desc>Created with sketchtool.</desc>
|
||||
<g id="autocomplete" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
|
||||
<g id="icon/tree/child" stroke="#D4D9E1" stroke-width="2">
|
||||
<path d="M8,8 L8,52" id="Line"></path>
|
||||
<path d="M26.5,30 L8.3047619,30" id="Line"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 705 B |
12
src/svg/icon_tree_last-child.svg
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="30px" height="60px" viewBox="0 0 30 60" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: sketchtool 55.2 (78181) - https://sketchapp.com -->
|
||||
<title>658830a4-2f41-4e2b-b15f-517e3365664b@1.00x</title>
|
||||
<desc>Created with sketchtool.</desc>
|
||||
<g id="autocomplete" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
|
||||
<g id="icon/tree/last-child" stroke="#D4D9E1" stroke-width="2">
|
||||
<path d="M8,8 L8,30" id="Line"></path>
|
||||
<path d="M26.5,30 L8.3047619,30" id="Line"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 710 B |
12
src/svg/light_footer_key_arrow-down.svg
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="15px" height="15px" viewBox="0 0 15 15" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: sketchtool 55.2 (78181) - https://sketchapp.com -->
|
||||
<title>ba2c8f8b-aab6-4715-b010-a3c910a1a4ea@1.00x</title>
|
||||
<desc>Created with sketchtool.</desc>
|
||||
<g id="autocomplete" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
|
||||
<g id="light/footer/key/arrow-down" stroke="#7E8BA1">
|
||||
<path d="M11.5,7.5 L3.5,7.5" id="Path-2" transform="translate(7.500000, 7.500000) rotate(-90.000000) translate(-7.500000, -7.500000) "></path>
|
||||
<polyline id="Shape" transform="translate(7.500000, 10.000000) rotate(-90.000000) translate(-7.500000, -10.000000) " points="9 13 6 10 9 7"></polyline>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 905 B |
12
src/svg/light_footer_key_arrow-up.svg
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="15px" height="15px" viewBox="0 0 15 15" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: sketchtool 55.2 (78181) - https://sketchapp.com -->
|
||||
<title>67191d53-11cf-4c86-9c2e-8580c0fd5d08@1.00x</title>
|
||||
<desc>Created with sketchtool.</desc>
|
||||
<g id="autocomplete" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
|
||||
<g id="light/footer/key/arrow-up" stroke="#7E8BA1">
|
||||
<path d="M11.5,7.5 L3.5,7.5" id="Path-2-Copy" transform="translate(7.500000, 7.500000) scale(1, -1) rotate(-90.000000) translate(-7.500000, -7.500000) "></path>
|
||||
<polyline id="Shape-Copy" transform="translate(7.500000, 5.000000) scale(1, -1) rotate(-90.000000) translate(-7.500000, -5.000000) " points="9 8 6 5 9 2"></polyline>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 935 B |
14
src/svg/light_footer_key_enter.svg
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="15px" height="15px" viewBox="0 0 15 15" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: sketchtool 55.2 (78181) - https://sketchapp.com -->
|
||||
<title>f879c873-9cca-4202-b665-1ec6b93bb126@1.00x</title>
|
||||
<desc>Created with sketchtool.</desc>
|
||||
<g id="autocomplete" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
|
||||
<g id="light/footer/key/enter" stroke="#7E8BA1">
|
||||
<g id="Group-2" transform="translate(4.000000, 3.030884)">
|
||||
<path d="M8,0.5 C8,1.83333333 8,2.83333333 8,3.5 C8,4.5 7,5.5 6,5.5 C5.33333333,5.5 3.33333333,5.5 -2.27373675e-13,5.5" id="Path-2"></path>
|
||||
</g>
|
||||
<polyline id="Shape" transform="translate(5.500000, 8.530884) rotate(-360.000000) translate(-5.500000, -8.530884) " points="7 11.5308838 4 8.53088379 7 5.53088379"></polyline>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1,013 B |
11
src/svg/light_footer_key_esc.svg
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="15px" height="15px" viewBox="0 0 15 15" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: sketchtool 55.2 (78181) - https://sketchapp.com -->
|
||||
<title>67ddc381-274d-431d-a5d3-cf7d76b0c2cd@1.00x</title>
|
||||
<desc>Created with sketchtool.</desc>
|
||||
<g id="autocomplete" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
|
||||
<g id="light/footer/key/esc" fill-rule="nonzero" stroke="#7E8BA1">
|
||||
<path d="M13.6167337,8.93599037 C13.5102412,9.2942938 12.9284476,9.89794268 12.1291915,9.89794268 C11.3299354,9.89794268 10.476295,8.9815299 10.476295,7.77215533 L10.476295,7.20436816 C10.476295,5.94964204 11.2657983,5.10277517 12.1291915,5.10277517 C12.9925846,5.10277517 13.4893364,5.58059532 13.6167337,6.17518075 M9,6 C8.86478471,5.52650257 8.24941503,5.07810537 7.54008548,5.10277517 C6.83075593,5.12744496 6.19600545,5.67280617 6.19600545,6.31942015 C6.19600545,6.96603413 6.61582974,7.20056152 7.54045132,7.29986572 C8.4650729,7.39916992 8.96798545,7.9337231 9,8.5 C9.03201455,9.0662769 8.54603841,9.89794268 7.54045132,9.89794268 C6.65925929,9.89794268 6,9 5.9630371,8.48521892 M4.48822602,9.02197266 C4.22473145,9.6161499 3.67834884,9.89794268 2.94392628,9.89794268 C2.20950372,9.89794268 1.2365649,9.27317984 1.2365649,7.69401142 L1.2365649,7.23372021 C1.2365649,6.19205827 1.95759349,5.10277517 2.94392628,5.10277517 C3.93025907,5.10277517 4.58636475,6.13360596 4.48822602,7.35192871 L1.53226299,7.35192871" id="Shape"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
11
src/svg/logo-alogia.svg
Normal file
|
After Width: | Height: | Size: 12 KiB |
81
src/types/DocSearchHit.ts
Normal file
|
|
@ -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;
|
||||
}
|
||||
5
src/types/InternalDocSearchHit.tsx
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import { DocSearchHit } from './DocSearchHit';
|
||||
|
||||
export type InternalDocSearchHit = DocSearchHit & {
|
||||
__docsearch_parent: null | DocSearchHit;
|
||||
};
|
||||
2
src/types/index.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
export * from './DocSearchHit';
|
||||
export * from './InternalDocSearchHit';
|
||||
13
src/utils/createSearchClient.ts
Normal file
|
|
@ -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;
|
||||
}
|
||||
16
src/utils/groupBy.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
export function groupBy<TValue extends object>(
|
||||
values: TValue[],
|
||||
predicate: (value: TValue) => string
|
||||
): Record<string, TValue[]> {
|
||||
return values.reduce<Record<string, TValue[]>>((acc, item) => {
|
||||
const key = predicate(item);
|
||||
|
||||
if (!acc.hasOwnProperty(key)) {
|
||||
acc[key] = [];
|
||||
}
|
||||
|
||||
acc[key].push(item);
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
3
src/utils/index.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export * from './createSearchClient';
|
||||
export * from './groupBy';
|
||||
export * from './noop';
|
||||
1
src/utils/noop.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export function noop(..._args: any[]): void {}
|
||||
1
src/version.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export const version = '1.0.0-alpha.10';
|
||||
8
tsconfig.declaration.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"extends": "./tsconfig",
|
||||
"compilerOptions": {
|
||||
"noEmit": false,
|
||||
"declaration": true,
|
||||
"emitDeclarationOnly": true
|
||||
}
|
||||
}
|
||||
3
tsconfig.json
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"extends": "../../tsconfig"
|
||||
}
|
||||