@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.
Files changed (39) hide show
  1. package/out/components/header/full-search/chapters/chapters.js +59 -0
  2. package/out/components/header/full-search/chapters/chapters.module.pcss.js +11 -0
  3. package/out/components/header/full-search/empty/empty.js +22 -0
  4. package/out/components/header/full-search/empty/empty.module.pcss.js +6 -0
  5. package/out/components/header/full-search/empty/full-search-empty.svg.js +1539 -0
  6. package/out/components/header/full-search/full-search.js +105 -0
  7. package/out/components/header/full-search/full-search.module.pcss.js +10 -0
  8. package/out/components/header/full-search/hit-list/get-extended-hits.js +55 -0
  9. package/out/components/header/full-search/hit-list/hit-list.js +47 -0
  10. package/out/components/header/full-search/hit-list/hit-list.module.pcss.js +5 -0
  11. package/out/components/header/full-search/loading/loading.js +13 -0
  12. package/out/components/header/full-search/loading/loading.module.pcss.js +4 -0
  13. package/out/components/header/full-search/results-list/results-list.js +25 -0
  14. package/out/components/header/header.js +62 -7
  15. package/out/components/header/index.css +381 -0
  16. package/out/components/header/is-macos.js +5 -0
  17. package/out/components/header/key-codes.js +3 -0
  18. package/out/components/header/quick-search/empty/empty.js +17 -0
  19. package/out/components/header/quick-search/empty/empty.module.pcss.js +5 -0
  20. package/out/components/header/quick-search/list/list.js +33 -0
  21. package/out/components/header/quick-search/list/list.module.pcss.js +7 -0
  22. package/out/components/header/quick-search/loading/loading.js +14 -0
  23. package/out/components/header/quick-search/loading/loading.module.pcss.js +4 -0
  24. package/out/components/header/quick-search/quick-search.js +41 -0
  25. package/out/components/header/quick-search/quick-search.module.pcss.js +4 -0
  26. package/out/components/header/quick-search/result/result.js +30 -0
  27. package/out/components/header/quick-search/result/result.module.pcss.js +6 -0
  28. package/out/components/header/search-box/search-box.js +73 -0
  29. package/out/components/header/search-box/search-box.module.pcss.js +7 -0
  30. package/out/components/header/search-wrapper/init-search.js +60 -0
  31. package/out/components/header/search-wrapper/search-const.js +13 -0
  32. package/out/components/header/search-wrapper/search-context.js +18 -0
  33. package/out/components/header/search-wrapper/search-with-algolia.js +58 -0
  34. package/out/components/header/search-wrapper/search-wrapper.js +40 -0
  35. package/out/components/header/search-wrapper/use-search.js +85 -0
  36. package/out/components/top-menu/dropdown-menu/dropdown-menu.js +1 -3
  37. package/out/components/top-menu/dropdown-menu/dropdown-menu.module.pcss.js +0 -1
  38. package/out/components/top-menu/index.css +0 -11
  39. 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.1",
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/icons": "^0.2.0",
49
- "@rescui/input": "^0.1.2",
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.0",
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",