@jetbrains/kotlin-web-site-ui 4.0.1 → 4.1.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/components/header/full-search/chapters/chapters.js +59 -0
- package/out/components/header/full-search/chapters/chapters.module.pcss.js +11 -0
- package/out/components/header/full-search/empty/empty.js +22 -0
- package/out/components/header/full-search/empty/empty.module.pcss.js +6 -0
- package/out/components/header/full-search/empty/full-search-empty.svg.js +1539 -0
- package/out/components/header/full-search/full-search.js +105 -0
- package/out/components/header/full-search/full-search.module.pcss.js +10 -0
- package/out/components/header/full-search/hit-list/get-extended-hits.js +55 -0
- package/out/components/header/full-search/hit-list/hit-list.js +47 -0
- package/out/components/header/full-search/hit-list/hit-list.module.pcss.js +5 -0
- package/out/components/header/full-search/loading/loading.js +13 -0
- package/out/components/header/full-search/loading/loading.module.pcss.js +4 -0
- package/out/components/header/full-search/results-list/results-list.js +25 -0
- package/out/components/header/header.js +62 -7
- package/out/components/header/index.css +381 -0
- package/out/components/header/is-macos.js +5 -0
- package/out/components/header/key-codes.js +3 -0
- package/out/components/header/quick-search/empty/empty.js +17 -0
- package/out/components/header/quick-search/empty/empty.module.pcss.js +5 -0
- package/out/components/header/quick-search/list/list.js +33 -0
- package/out/components/header/quick-search/list/list.module.pcss.js +7 -0
- package/out/components/header/quick-search/loading/loading.js +14 -0
- package/out/components/header/quick-search/loading/loading.module.pcss.js +4 -0
- package/out/components/header/quick-search/quick-search.js +41 -0
- package/out/components/header/quick-search/quick-search.module.pcss.js +4 -0
- package/out/components/header/quick-search/result/result.js +30 -0
- package/out/components/header/quick-search/result/result.module.pcss.js +6 -0
- package/out/components/header/search-box/search-box.js +73 -0
- package/out/components/header/search-box/search-box.module.pcss.js +7 -0
- package/out/components/header/search-wrapper/init-search.js +60 -0
- package/out/components/header/search-wrapper/search-const.js +13 -0
- package/out/components/header/search-wrapper/search-context.js +18 -0
- package/out/components/header/search-wrapper/search-with-algolia.js +58 -0
- package/out/components/header/search-wrapper/search-wrapper.js +40 -0
- package/out/components/header/search-wrapper/use-search.js +85 -0
- package/out/components/top-menu/dropdown-menu/dropdown-menu.js +1 -3
- package/out/components/top-menu/dropdown-menu/dropdown-menu.module.pcss.js +0 -1
- package/out/components/top-menu/index.css +0 -11
- package/package.json +14 -4
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import algoliasearch from 'algoliasearch/lite';
|
|
2
|
+
import { EXACT_MATCH } from './search-const.js';
|
|
3
|
+
import { searchWithAlgolia } from './search-with-algolia.js';
|
|
4
|
+
import { useState, useEffect, useMemo } from 'react';
|
|
5
|
+
const attributesToRetrieve = ['pageTitle', 'url', 'breadcrumbs', 'mainTitle'];
|
|
6
|
+
const attributesToSnippet = ['content:25'];
|
|
7
|
+
|
|
8
|
+
function initSearch({
|
|
9
|
+
searchAlgoliaId,
|
|
10
|
+
searchAlgoliaApiKey,
|
|
11
|
+
searchAlgoliaIndexName
|
|
12
|
+
}) {
|
|
13
|
+
const [searchIndex, setSearchIndex] = useState(null);
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (searchAlgoliaId && searchAlgoliaApiKey && searchAlgoliaIndexName) {
|
|
16
|
+
const algoliaClient = algoliasearch(searchAlgoliaId, searchAlgoliaApiKey);
|
|
17
|
+
setSearchIndex(algoliaClient.initIndex(searchAlgoliaIndexName));
|
|
18
|
+
}
|
|
19
|
+
}, []);
|
|
20
|
+
const search = useMemo(() => async (query, matching, maxHits) => {
|
|
21
|
+
try {
|
|
22
|
+
const isExactSearch = matching === EXACT_MATCH.value;
|
|
23
|
+
const searchArgs = {
|
|
24
|
+
attributesToRetrieve,
|
|
25
|
+
attributesToSnippet,
|
|
26
|
+
isExactSearch,
|
|
27
|
+
maxHits,
|
|
28
|
+
query
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
if (!searchIndex) {
|
|
32
|
+
throw new Error('ConfigData is empty');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const searchData = await searchWithAlgolia({ ...searchArgs,
|
|
36
|
+
searchIndex
|
|
37
|
+
});
|
|
38
|
+
const {
|
|
39
|
+
hits,
|
|
40
|
+
totalHits
|
|
41
|
+
} = searchData;
|
|
42
|
+
return {
|
|
43
|
+
hits,
|
|
44
|
+
totalHits
|
|
45
|
+
};
|
|
46
|
+
} catch (error) {
|
|
47
|
+
//reportError(error, { component: 'search' });
|
|
48
|
+
return {
|
|
49
|
+
hits: [],
|
|
50
|
+
totalHits: 0
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}, [searchIndex]);
|
|
54
|
+
return {
|
|
55
|
+
searchIndex,
|
|
56
|
+
search
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export { initSearch };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const FULL_SEARCH = 'full';
|
|
2
|
+
const QUICK_SEARCH = 'quick';
|
|
3
|
+
const DEBOUNCE_SEARCH = 500;
|
|
4
|
+
const DEBOUNCE_QUERY_UPDATE = 100;
|
|
5
|
+
const ALL_RESULTS = {
|
|
6
|
+
label: 'All results',
|
|
7
|
+
value: 'All results'
|
|
8
|
+
};
|
|
9
|
+
const EXACT_MATCH = {
|
|
10
|
+
label: 'Exact match',
|
|
11
|
+
value: 'Exact match'
|
|
12
|
+
};
|
|
13
|
+
export { ALL_RESULTS, DEBOUNCE_QUERY_UPDATE, DEBOUNCE_SEARCH, EXACT_MATCH, FULL_SEARCH, QUICK_SEARCH };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React__default from 'react';
|
|
2
|
+
const searchParamsInitValue = {
|
|
3
|
+
query: '',
|
|
4
|
+
type: '',
|
|
5
|
+
setType: () => null,
|
|
6
|
+
search: () => new Promise(() => ({
|
|
7
|
+
hits: [],
|
|
8
|
+
totalHits: 0
|
|
9
|
+
})),
|
|
10
|
+
matching: '',
|
|
11
|
+
matchingOptions: [],
|
|
12
|
+
setMatching: () => null,
|
|
13
|
+
updateQueryString: () => null,
|
|
14
|
+
searchQueryState: '',
|
|
15
|
+
setSearchQueryState: () => null
|
|
16
|
+
};
|
|
17
|
+
const SearchContext = React__default.createContext(searchParamsInitValue);
|
|
18
|
+
export { SearchContext as default };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const snippetEllipsisText = '…';
|
|
2
|
+
|
|
3
|
+
const searchWithAlgolia = async ({
|
|
4
|
+
attributesToRetrieve = [],
|
|
5
|
+
attributesToSnippet = [],
|
|
6
|
+
isExactSearch = false,
|
|
7
|
+
maxHits = 0,
|
|
8
|
+
query = '',
|
|
9
|
+
searchIndex
|
|
10
|
+
}) => {
|
|
11
|
+
try {
|
|
12
|
+
if (searchIndex?.indexName) {
|
|
13
|
+
const searchResponse = await searchIndex.search(query, {
|
|
14
|
+
attributesToRetrieve,
|
|
15
|
+
attributesToSnippet,
|
|
16
|
+
clickAnalytics: true,
|
|
17
|
+
hitsPerPage: maxHits,
|
|
18
|
+
query,
|
|
19
|
+
snippetEllipsisText,
|
|
20
|
+
typoTolerance: !isExactSearch
|
|
21
|
+
});
|
|
22
|
+
return {
|
|
23
|
+
hits: mapResponseToHits(searchResponse.hits),
|
|
24
|
+
totalHits: searchResponse.nbHits
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
throw new Error(`Algolia client can't be initialized`);
|
|
29
|
+
} catch (error) {
|
|
30
|
+
return {
|
|
31
|
+
totalHits: 0,
|
|
32
|
+
hits: []
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
function mapResponseToHits(results) {
|
|
38
|
+
return results.map(({
|
|
39
|
+
_highlightResult,
|
|
40
|
+
_snippetResult,
|
|
41
|
+
breadcrumbs,
|
|
42
|
+
mainTitle,
|
|
43
|
+
objectID,
|
|
44
|
+
pageTitle,
|
|
45
|
+
url
|
|
46
|
+
}) => ({
|
|
47
|
+
breadcrumb: breadcrumbs,
|
|
48
|
+
highlightedTitle: _highlightResult.pageTitle.value,
|
|
49
|
+
hitId: objectID,
|
|
50
|
+
mainTitle,
|
|
51
|
+
pageId: _highlightResult.parent.value,
|
|
52
|
+
snippet: _snippetResult.content.value,
|
|
53
|
+
title: pageTitle,
|
|
54
|
+
url
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export { searchWithAlgolia };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React__default, { useState } from 'react';
|
|
2
|
+
import { initSearch } from './init-search.js';
|
|
3
|
+
import SearchContext from './search-context.js';
|
|
4
|
+
import { QUICK_SEARCH, ALL_RESULTS, EXACT_MATCH } from './search-const.js';
|
|
5
|
+
const matchingOptions = [{
|
|
6
|
+
label: ALL_RESULTS.label,
|
|
7
|
+
value: ALL_RESULTS.value
|
|
8
|
+
}, {
|
|
9
|
+
label: EXACT_MATCH.label,
|
|
10
|
+
value: EXACT_MATCH.value
|
|
11
|
+
}];
|
|
12
|
+
|
|
13
|
+
const SearchWrapper = ({
|
|
14
|
+
children,
|
|
15
|
+
searchConfig
|
|
16
|
+
}) => {
|
|
17
|
+
const {
|
|
18
|
+
search
|
|
19
|
+
} = initSearch(searchConfig);
|
|
20
|
+
const [type, setType] = useState(QUICK_SEARCH);
|
|
21
|
+
const [matching, setMatching] = useState(matchingOptions[0].value);
|
|
22
|
+
const [searchQueryState, setSearchQueryState] = useState(''); // router
|
|
23
|
+
|
|
24
|
+
return React__default.createElement(SearchContext.Provider, {
|
|
25
|
+
value: {
|
|
26
|
+
query: '',
|
|
27
|
+
type,
|
|
28
|
+
setType,
|
|
29
|
+
search,
|
|
30
|
+
matching,
|
|
31
|
+
matchingOptions,
|
|
32
|
+
setMatching,
|
|
33
|
+
updateQueryString: () => {},
|
|
34
|
+
searchQueryState,
|
|
35
|
+
setSearchQueryState
|
|
36
|
+
}
|
|
37
|
+
}, children);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export { SearchWrapper };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { useState, useContext, useEffect } from 'react';
|
|
2
|
+
import SearchContext from './search-context.js';
|
|
3
|
+
import { DEBOUNCE_QUERY_UPDATE, DEBOUNCE_SEARCH, FULL_SEARCH, QUICK_SEARCH } from './search-const.js';
|
|
4
|
+
let updateQueryTimeout = 0;
|
|
5
|
+
let searchTimeout = 0;
|
|
6
|
+
|
|
7
|
+
const useSearch = (inputValue = '', maxHits = 25) => {
|
|
8
|
+
const [hits, setHits] = useState([]);
|
|
9
|
+
const [totalHits, setTotalHits] = useState(0);
|
|
10
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
11
|
+
const [queryId, setQueryId] = useState('');
|
|
12
|
+
const [placeholder, setPlaceholder] = useState('');
|
|
13
|
+
const {
|
|
14
|
+
matching,
|
|
15
|
+
updateQueryString,
|
|
16
|
+
type,
|
|
17
|
+
search,
|
|
18
|
+
setSearchQueryState
|
|
19
|
+
} = useContext(SearchContext);
|
|
20
|
+
const isFullSearch = type === FULL_SEARCH;
|
|
21
|
+
const isQuickSearch = type === QUICK_SEARCH;
|
|
22
|
+
|
|
23
|
+
const debouncedSearch = () => {
|
|
24
|
+
if (updateQueryTimeout) {
|
|
25
|
+
clearTimeout(updateQueryTimeout);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (searchTimeout) {
|
|
29
|
+
clearTimeout(searchTimeout);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
updateQueryTimeout = window.setTimeout(() => {
|
|
33
|
+
if (isQuickSearch) {
|
|
34
|
+
setSearchQueryState(inputValue);
|
|
35
|
+
} else if (isFullSearch) {
|
|
36
|
+
updateQueryString(inputValue, type);
|
|
37
|
+
}
|
|
38
|
+
}, DEBOUNCE_QUERY_UPDATE);
|
|
39
|
+
searchTimeout = window.setTimeout(async () => {
|
|
40
|
+
if (inputValue) {
|
|
41
|
+
setIsLoading(true);
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const results = await search(inputValue, matching, maxHits);
|
|
45
|
+
|
|
46
|
+
if (results.hits.length === 0) {
|
|
47
|
+
setPlaceholder(`We’re sorry! We could’t find results for «${inputValue}»`);
|
|
48
|
+
} else {
|
|
49
|
+
setPlaceholder('');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
setHits(results.hits);
|
|
53
|
+
setTotalHits(results.totalHits);
|
|
54
|
+
|
|
55
|
+
if (results.queryId) {
|
|
56
|
+
setQueryId(results.queryId);
|
|
57
|
+
}
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error(error);
|
|
60
|
+
} finally {
|
|
61
|
+
setIsLoading(false);
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
// Clear hits and placeholder when search string is empty.
|
|
65
|
+
// It prevents show previous negative result when searching from scratch
|
|
66
|
+
setPlaceholder('');
|
|
67
|
+
setHits([]);
|
|
68
|
+
}
|
|
69
|
+
}, DEBOUNCE_SEARCH);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
debouncedSearch();
|
|
74
|
+
return () => clearTimeout(updateQueryTimeout);
|
|
75
|
+
}, [inputValue, matching]);
|
|
76
|
+
return {
|
|
77
|
+
hits,
|
|
78
|
+
totalHits,
|
|
79
|
+
isLoading,
|
|
80
|
+
queryId,
|
|
81
|
+
placeholder
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export { useSearch };
|
|
@@ -44,9 +44,7 @@ const DropdownMenu = ({
|
|
|
44
44
|
}, React__default.createElement("button", {
|
|
45
45
|
className: classNames(styles.button, textCn('rs-text-2')),
|
|
46
46
|
"aria-haspopup": "true"
|
|
47
|
-
}, React__default.createElement("span", {
|
|
48
|
-
className: styles.buttonText
|
|
49
|
-
}, activeItem.title), React__default.createElement(SvgArrowDropdownIcon, {
|
|
47
|
+
}, React__default.createElement("span", null, activeItem.title), React__default.createElement(SvgArrowDropdownIcon, {
|
|
50
48
|
className: styles.icon
|
|
51
49
|
})), React__default.createElement("nav", {
|
|
52
50
|
className: classNames(styles.dropdownList, {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
var styles = {
|
|
2
2
|
"dropdownMenu": "ktl-dropdown-menu-module_dropdown-menu_tq2uU",
|
|
3
3
|
"button": "ktl-dropdown-menu-module_button_OYsuv",
|
|
4
|
-
"buttonText": "ktl-dropdown-menu-module_button-text_SJmh-",
|
|
5
4
|
"icon": "ktl-dropdown-menu-module_icon_GGhMI",
|
|
6
5
|
"dropdownList": "ktl-dropdown-menu-module_dropdown-list_Ylkvt",
|
|
7
6
|
"fadein": "ktl-dropdown-menu-module_fadein_MySnq",
|
|
@@ -51,7 +51,6 @@
|
|
|
51
51
|
height: 48px;
|
|
52
52
|
padding: 0 16px;
|
|
53
53
|
grid-gap: 16px;
|
|
54
|
-
grid-template-columns: 1fr auto;
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
.ktl-top-menu-module_logo_CNH2W {
|
|
@@ -106,15 +105,12 @@
|
|
|
106
105
|
height: 100%;
|
|
107
106
|
justify-self: flex-start;
|
|
108
107
|
white-space: nowrap;
|
|
109
|
-
min-width: 0;
|
|
110
|
-
max-width: 100%;
|
|
111
108
|
}
|
|
112
109
|
|
|
113
110
|
.ktl-dropdown-menu-module_button_OYsuv {
|
|
114
111
|
border: none;
|
|
115
112
|
background: none;
|
|
116
113
|
height: 100%;
|
|
117
|
-
max-width: 100%;
|
|
118
114
|
padding: 0;
|
|
119
115
|
margin: 0;
|
|
120
116
|
display: flex;
|
|
@@ -123,18 +119,11 @@
|
|
|
123
119
|
cursor: pointer;
|
|
124
120
|
}
|
|
125
121
|
|
|
126
|
-
.ktl-dropdown-menu-module_button-text_SJmh- {
|
|
127
|
-
overflow: hidden;
|
|
128
|
-
text-overflow: ellipsis;
|
|
129
|
-
flex: 0 1 auto;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
122
|
.ktl-dropdown-menu-module_icon_GGhMI {
|
|
133
123
|
width: 12px;
|
|
134
124
|
height: 6px;
|
|
135
125
|
margin-left: 6px;
|
|
136
126
|
transition: transform ease-in-out var(--ktl-transition-xfast);
|
|
137
|
-
flex: 0 0 auto;
|
|
138
127
|
}
|
|
139
128
|
|
|
140
129
|
.ktl-dropdown-menu-module_dropdown-list_Ylkvt {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jetbrains/kotlin-web-site-ui",
|
|
3
3
|
"description": "UI components for Kotlin web sites development",
|
|
4
|
-
"version": "4.0.
|
|
4
|
+
"version": "4.1.0-alpha.10",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "JetBrains",
|
|
7
7
|
"files": [
|
|
@@ -24,14 +24,18 @@
|
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
26
|
"@rescui/checkbox": "0.x",
|
|
27
|
+
"@rescui/focus-manager": "0.x",
|
|
27
28
|
"@rescui/icons": "0.x",
|
|
28
29
|
"@rescui/input": "0.x",
|
|
29
30
|
"@rescui/tooltip": "0.x",
|
|
30
31
|
"@rescui/typography": "0.x",
|
|
31
32
|
"@rescui/ui-contexts": "0.x",
|
|
33
|
+
"algoliasearch": "4.x",
|
|
32
34
|
"formik": "2.x",
|
|
33
35
|
"react": ">= 16.8.6 < 18",
|
|
34
36
|
"react-dom": ">= 16.8.6 < 18",
|
|
37
|
+
"react-outside-click-handler": "1.x",
|
|
38
|
+
"react-scrollbar-size": "5.x",
|
|
35
39
|
"react-swipeable-views": "0.x",
|
|
36
40
|
"sha.js": "2.x"
|
|
37
41
|
},
|
|
@@ -45,10 +49,12 @@
|
|
|
45
49
|
"@react-hook/resize-observer": "^1.2.5",
|
|
46
50
|
"@rescui/button": "^0.2.1",
|
|
47
51
|
"@rescui/checkbox": "^0.1.0",
|
|
48
|
-
"@rescui/
|
|
49
|
-
"@rescui/
|
|
52
|
+
"@rescui/focus-manager": "^0.1.1",
|
|
53
|
+
"@rescui/icons": "^0.8.2",
|
|
54
|
+
"@rescui/input": "^0.4.6",
|
|
55
|
+
"@rescui/switcher": "^0.2.2",
|
|
50
56
|
"@rescui/tooltip": "^0.1.2",
|
|
51
|
-
"@rescui/typography": "^0.3
|
|
57
|
+
"@rescui/typography": "^0.7.3",
|
|
52
58
|
"@rescui/ui-contexts": "^0.1.3",
|
|
53
59
|
"@rollup/plugin-babel": "^5.3.1",
|
|
54
60
|
"@rollup/plugin-json": "^4.1.0",
|
|
@@ -65,11 +71,13 @@
|
|
|
65
71
|
"@types/react": "^17.0.0",
|
|
66
72
|
"@types/react-dom": "^17.0.0",
|
|
67
73
|
"@types/react-modal": "^3.13.1",
|
|
74
|
+
"@types/react-outside-click-handler": "^1.3.1",
|
|
68
75
|
"@types/react-swipeable-views": "^0.13.1",
|
|
69
76
|
"@types/sha.js": "^2.4.0",
|
|
70
77
|
"@types/youtube": "^0.0.45",
|
|
71
78
|
"@typescript-eslint/eslint-plugin": "^4.22.0",
|
|
72
79
|
"@typescript-eslint/parser": "^4.22.0",
|
|
80
|
+
"algoliasearch": "^4.13.1",
|
|
73
81
|
"autoprefixer": "^10.2.5",
|
|
74
82
|
"babel-loader": "^8.2.2",
|
|
75
83
|
"babel-plugin-module-resolver": "^4.1.0",
|
|
@@ -103,7 +111,9 @@
|
|
|
103
111
|
"react": "^16.8.6",
|
|
104
112
|
"react-dom": "^16.8.6",
|
|
105
113
|
"react-modal": "^3.14.4",
|
|
114
|
+
"react-outside-click-handler": "^1.3.0",
|
|
106
115
|
"react-remove-scroll-bar": "^2.2.0",
|
|
116
|
+
"react-scrollbar-size": "^5.0.0",
|
|
107
117
|
"react-swipeable-views": "^0.14.0",
|
|
108
118
|
"rollup": "^2.70.1",
|
|
109
119
|
"rollup-plugin-node-externals": "^4.0.0",
|