@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,105 @@
|
|
|
1
|
+
import React__default, { useState, useContext, useRef, useCallback, useLayoutEffect } from 'react';
|
|
2
|
+
import useScrollbarSize from 'react-scrollbar-size';
|
|
3
|
+
import { ThemeProvider } from '@rescui/ui-contexts';
|
|
4
|
+
import Input from '@rescui/input';
|
|
5
|
+
import Button from '@rescui/button';
|
|
6
|
+
import Switcher from '@rescui/switcher';
|
|
7
|
+
import { CloseIcon, ErrorIcon } from '@rescui/icons';
|
|
8
|
+
import { useSearch } from '../search-wrapper/use-search.js';
|
|
9
|
+
import { ResultsList } from './results-list/results-list.js';
|
|
10
|
+
import SearchContext from '../search-wrapper/search-context.js';
|
|
11
|
+
import styles from './full-search.module.pcss.js';
|
|
12
|
+
|
|
13
|
+
const FullSearch = ({
|
|
14
|
+
searchString,
|
|
15
|
+
killSearch
|
|
16
|
+
}) => {
|
|
17
|
+
const [isScrollbarCompensated, setIsScrollbarCompensated] = useState(false);
|
|
18
|
+
const [inputValue, setInputValue] = useState(searchString);
|
|
19
|
+
const {
|
|
20
|
+
hits,
|
|
21
|
+
placeholder
|
|
22
|
+
} = useSearch(inputValue, 75);
|
|
23
|
+
const {
|
|
24
|
+
matching,
|
|
25
|
+
matchingOptions,
|
|
26
|
+
setMatching
|
|
27
|
+
} = useContext(SearchContext);
|
|
28
|
+
const ref = useRef(null);
|
|
29
|
+
const headerRef = useRef(null);
|
|
30
|
+
const listRef = useRef(null);
|
|
31
|
+
const isResultsVisible = hits.length > 0 && inputValue;
|
|
32
|
+
const handleSwitcherChange = useCallback(value => {
|
|
33
|
+
setMatching(value);
|
|
34
|
+
}, []);
|
|
35
|
+
const clearSearch = useCallback(() => {
|
|
36
|
+
setInputValue('');
|
|
37
|
+
}, []);
|
|
38
|
+
const handleInputChange = useCallback(e => {
|
|
39
|
+
setInputValue(e.target.value);
|
|
40
|
+
}, [inputValue]); // Scrollbar compensation
|
|
41
|
+
|
|
42
|
+
const {
|
|
43
|
+
width
|
|
44
|
+
} = useScrollbarSize();
|
|
45
|
+
useLayoutEffect(() => {
|
|
46
|
+
if (headerRef.current && listRef.current) {
|
|
47
|
+
const countedHeight = headerRef.current.offsetHeight + listRef.current.offsetHeight;
|
|
48
|
+
|
|
49
|
+
if (isResultsVisible) {
|
|
50
|
+
setIsScrollbarCompensated(countedHeight < window.innerWidth);
|
|
51
|
+
} else {
|
|
52
|
+
setIsScrollbarCompensated(true);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}, [hits, inputValue]);
|
|
56
|
+
return React__default.createElement(ThemeProvider, {
|
|
57
|
+
theme: 'light'
|
|
58
|
+
}, React__default.createElement("div", {
|
|
59
|
+
className: styles.fullSearch,
|
|
60
|
+
style: {
|
|
61
|
+
paddingRight: isScrollbarCompensated ? `${width}px` : 0
|
|
62
|
+
},
|
|
63
|
+
tabIndex: 1000,
|
|
64
|
+
ref: ref
|
|
65
|
+
}, React__default.createElement("div", {
|
|
66
|
+
className: styles.header,
|
|
67
|
+
ref: headerRef
|
|
68
|
+
}, React__default.createElement("div", {
|
|
69
|
+
className: styles.closeSearch
|
|
70
|
+
}, React__default.createElement(Button, {
|
|
71
|
+
mode: 'clear',
|
|
72
|
+
size: 'l',
|
|
73
|
+
icon: React__default.createElement(CloseIcon, null),
|
|
74
|
+
onClick: killSearch
|
|
75
|
+
})), React__default.createElement("div", {
|
|
76
|
+
className: styles.wrapper
|
|
77
|
+
}, React__default.createElement(Input, {
|
|
78
|
+
placeholder: 'Search',
|
|
79
|
+
value: inputValue,
|
|
80
|
+
onChange: handleInputChange,
|
|
81
|
+
onClear: clearSearch,
|
|
82
|
+
clearIcon: React__default.createElement("button", {
|
|
83
|
+
className: styles.fullSearchClearButton
|
|
84
|
+
}, React__default.createElement(ErrorIcon, {
|
|
85
|
+
size: 'l'
|
|
86
|
+
})),
|
|
87
|
+
size: 'l',
|
|
88
|
+
autoFocus: true
|
|
89
|
+
}), isResultsVisible && React__default.createElement("div", {
|
|
90
|
+
className: styles.switcher
|
|
91
|
+
}, React__default.createElement(Switcher, {
|
|
92
|
+
value: matching,
|
|
93
|
+
onChange: handleSwitcherChange,
|
|
94
|
+
options: matchingOptions,
|
|
95
|
+
size: 'xs'
|
|
96
|
+
})))), React__default.createElement("div", {
|
|
97
|
+
className: styles.wrapper,
|
|
98
|
+
ref: listRef
|
|
99
|
+
}, inputValue && React__default.createElement(ResultsList, {
|
|
100
|
+
placeholder: placeholder,
|
|
101
|
+
hits: hits
|
|
102
|
+
}))));
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export { FullSearch };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
var styles = {
|
|
2
|
+
"fullSearch": "ktl-full-search-module_fullSearch_MTU8t",
|
|
3
|
+
"closeSearch": "ktl-full-search-module_closeSearch_5vYDG",
|
|
4
|
+
"fullSearchClearButton": "ktl-full-search-module_fullSearchClearButton_DGS6T",
|
|
5
|
+
"wrapper": "ktl-full-search-module_wrapper_9rxXb",
|
|
6
|
+
"header": "ktl-full-search-module_header_Wltw0",
|
|
7
|
+
"results": "ktl-full-search-module_results_svcSE",
|
|
8
|
+
"switcher": "ktl-full-search-module_switcher_o1RgM"
|
|
9
|
+
};
|
|
10
|
+
export { styles as default };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const groupHitsByPageId = ungroupedHits => ungroupedHits.reduce((groups, currentHit) => {
|
|
2
|
+
const currentGroup = groups.find(group => group.name === currentHit.mainTitle);
|
|
3
|
+
|
|
4
|
+
if (currentGroup) {
|
|
5
|
+
currentGroup.hits.push(currentHit);
|
|
6
|
+
return groups;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const newGroup = {
|
|
10
|
+
name: currentHit.mainTitle,
|
|
11
|
+
hits: [currentHit]
|
|
12
|
+
};
|
|
13
|
+
return [...groups, newGroup];
|
|
14
|
+
}, []);
|
|
15
|
+
|
|
16
|
+
const getChapterName = fullName => fullName.split(':')[1] || fullName;
|
|
17
|
+
|
|
18
|
+
const highlightChaptersTitles = chapters => chapters.map(chapter => ({ ...chapter,
|
|
19
|
+
highlightedTitle: getChapterName(chapter.highlightedTitle)
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
const getExtendedHits = hits => {
|
|
23
|
+
try {
|
|
24
|
+
const groupedHits = groupHitsByPageId(hits);
|
|
25
|
+
return groupedHits.map(group => {
|
|
26
|
+
const mainHit = group.hits.find(hit => hit.title === group.name);
|
|
27
|
+
|
|
28
|
+
if (mainHit) {
|
|
29
|
+
const chapters = group.hits.filter(hit => hit.title !== group.name);
|
|
30
|
+
return { ...mainHit,
|
|
31
|
+
title: mainHit.highlightedTitle,
|
|
32
|
+
chapters: highlightChaptersTitles(chapters)
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const {
|
|
37
|
+
url,
|
|
38
|
+
breadcrumb,
|
|
39
|
+
...restHitProps
|
|
40
|
+
} = group.hits[0];
|
|
41
|
+
return { ...restHitProps,
|
|
42
|
+
title: group.name,
|
|
43
|
+
url: url.split('#')[0],
|
|
44
|
+
breadcrumb,
|
|
45
|
+
chapters: highlightChaptersTitles(group.hits)
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
} catch (error) {
|
|
49
|
+
// reportError(error, { func: 'getExtendedHits' });
|
|
50
|
+
console.log('error');
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export { getExtendedHits as default };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React__default from 'react';
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
import { useTextStyles } from '@rescui/typography';
|
|
4
|
+
import getExtendedHits from './get-extended-hits.js';
|
|
5
|
+
import { Chapters } from '../chapters/chapters.js';
|
|
6
|
+
import styles from './hit-list.module.pcss.js';
|
|
7
|
+
|
|
8
|
+
const HitList = ({
|
|
9
|
+
hits
|
|
10
|
+
}) => {
|
|
11
|
+
const textCn = useTextStyles();
|
|
12
|
+
const extendedHits = getExtendedHits(hits);
|
|
13
|
+
return React__default.createElement(React__default.Fragment, null, extendedHits.map(({
|
|
14
|
+
title,
|
|
15
|
+
snippet,
|
|
16
|
+
chapters = [],
|
|
17
|
+
url,
|
|
18
|
+
breadcrumb
|
|
19
|
+
}, pageIndex) => {
|
|
20
|
+
const isHitWithChapters = chapters.length > 0;
|
|
21
|
+
return React__default.createElement("div", {
|
|
22
|
+
className: styles.hitList,
|
|
23
|
+
key: pageIndex
|
|
24
|
+
}, React__default.createElement("h3", {
|
|
25
|
+
className: textCn('rs-h3')
|
|
26
|
+
}, React__default.createElement("a", {
|
|
27
|
+
className: classNames(styles.titleLink, textCn('rs-link', {
|
|
28
|
+
mode: 'clear',
|
|
29
|
+
hardness: 'hard'
|
|
30
|
+
})),
|
|
31
|
+
href: url,
|
|
32
|
+
dangerouslySetInnerHTML: {
|
|
33
|
+
__html: title
|
|
34
|
+
}
|
|
35
|
+
})), isHitWithChapters ? React__default.createElement(Chapters, {
|
|
36
|
+
chapters: chapters,
|
|
37
|
+
hitIndex: pageIndex
|
|
38
|
+
}) : React__default.createElement("div", {
|
|
39
|
+
className: classNames(textCn('rs-text-2')),
|
|
40
|
+
dangerouslySetInnerHTML: {
|
|
41
|
+
__html: snippet
|
|
42
|
+
}
|
|
43
|
+
}));
|
|
44
|
+
}));
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export { HitList };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React__default from 'react';
|
|
2
|
+
import { LoadingIcon } from '@rescui/icons';
|
|
3
|
+
import styles from './loading.module.pcss.js';
|
|
4
|
+
|
|
5
|
+
const FullSearchLoader = () => {
|
|
6
|
+
return React__default.createElement("div", {
|
|
7
|
+
className: styles.loader
|
|
8
|
+
}, React__default.createElement(LoadingIcon, {
|
|
9
|
+
size: 'l'
|
|
10
|
+
}));
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export { FullSearchLoader };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React__default from 'react';
|
|
2
|
+
import { FullSearchLoader } from '../loading/loading.js';
|
|
3
|
+
import { FullSearchEmpty } from '../empty/empty.js';
|
|
4
|
+
import { HitList } from '../hit-list/hit-list.js';
|
|
5
|
+
|
|
6
|
+
const ResultsList = ({
|
|
7
|
+
placeholder,
|
|
8
|
+
hits
|
|
9
|
+
}) => {
|
|
10
|
+
if (placeholder) {
|
|
11
|
+
return React__default.createElement(FullSearchEmpty, {
|
|
12
|
+
placeholder: placeholder
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (hits.length) {
|
|
17
|
+
return React__default.createElement(HitList, {
|
|
18
|
+
hits: hits
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return React__default.createElement(FullSearchLoader, null);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export { ResultsList };
|
|
@@ -1,19 +1,24 @@
|
|
|
1
|
-
import React__default, { forwardRef, useState, useMemo, useRef, useImperativeHandle, useLayoutEffect } from 'react';
|
|
1
|
+
import React__default, { forwardRef, useState, useMemo, useRef, useImperativeHandle, useLayoutEffect, useCallback, useEffect } from 'react';
|
|
2
2
|
import { ThemeProvider } from '@rescui/ui-contexts';
|
|
3
|
+
import FocusManager from '@rescui/focus-manager';
|
|
3
4
|
import useResizeObserver from '@react-hook/resize-observer';
|
|
5
|
+
import OutsideClickHandler from 'react-outside-click-handler';
|
|
4
6
|
import { LogoLarge } from './logo-large/logo-large.js';
|
|
5
7
|
import styles from './header.module.pcss.js';
|
|
6
8
|
import { getNavScheme } from './nav-scheme.js';
|
|
7
9
|
import { HorizontalMenu } from './horizontal-menu/horizontal-menu.js';
|
|
8
10
|
import { SearchButton } from './search-button/search-button.js';
|
|
11
|
+
import { SearchBox } from './search-box/search-box.js';
|
|
9
12
|
import { MenuPopup } from './menu-popup/menu-popup.js';
|
|
10
13
|
import { LogoSmall } from './logo-small/logo-small.js';
|
|
11
14
|
import classNames from 'classnames';
|
|
15
|
+
import { SearchWrapper } from './search-wrapper/search-wrapper.js';
|
|
16
|
+
import { isMacOs } from './is-macos.js';
|
|
17
|
+
import { KEY_K_CODE } from './key-codes.js';
|
|
12
18
|
const MENU_POPUP_BREAKPOINT = 640;
|
|
13
19
|
const Header = forwardRef(({
|
|
14
20
|
productWebUrl,
|
|
15
21
|
hasSearch = false,
|
|
16
|
-
onSearchClick,
|
|
17
22
|
currentUrl,
|
|
18
23
|
currentTitle,
|
|
19
24
|
className,
|
|
@@ -21,10 +26,14 @@ const Header = forwardRef(({
|
|
|
21
26
|
dropdownTheme = 'dark',
|
|
22
27
|
linkHandler,
|
|
23
28
|
isPlayground,
|
|
24
|
-
isUrlActive
|
|
29
|
+
isUrlActive,
|
|
30
|
+
searchConfig,
|
|
31
|
+
noScrollClassName
|
|
25
32
|
}, forwardedRef) => {
|
|
26
33
|
const [menuPopupExpanded, setMenuPopupExpanded] = useState(false);
|
|
27
34
|
const [isMenuPopupVisible, setIsMenuPopupVisible] = useState(false);
|
|
35
|
+
const [isSearchBoxVisible, setSearchBoxVisible] = useState(false);
|
|
36
|
+
const [fullSearchActive, setFullSearchActive] = useState(false);
|
|
28
37
|
const items = useMemo(() => getNavScheme(isUrlActive, currentUrl, isPlayground), [isUrlActive, currentUrl, isPlayground]);
|
|
29
38
|
const headerRef = useRef(null);
|
|
30
39
|
useImperativeHandle(forwardedRef, () => headerRef.current);
|
|
@@ -32,8 +41,47 @@ const Header = forwardRef(({
|
|
|
32
41
|
setIsMenuPopupVisible((headerRef.current?.getBoundingClientRect?.().width ?? 0) <= MENU_POPUP_BREAKPOINT);
|
|
33
42
|
}, [headerRef]);
|
|
34
43
|
useResizeObserver(headerRef, entry => setIsMenuPopupVisible(entry.contentRect.width <= MENU_POPUP_BREAKPOINT));
|
|
44
|
+
const handleSearchButtonClick = useCallback(() => {
|
|
45
|
+
setSearchBoxVisible(!isSearchBoxVisible);
|
|
46
|
+
}, []);
|
|
47
|
+
const handleSearchClose = useCallback(() => {
|
|
48
|
+
setSearchBoxVisible(false);
|
|
49
|
+
setFullSearchActive(false);
|
|
50
|
+
}, []);
|
|
51
|
+
const fullSearchKeyHandler = useCallback(event => {
|
|
52
|
+
const isMacOsHotkeyPressed = isMacOs() && event.keyCode === KEY_K_CODE && event.metaKey;
|
|
53
|
+
const isOtherPlatformsHotkeyPressed = !isMacOs() && event.keyCode === KEY_K_CODE && event.ctrlKey;
|
|
54
|
+
|
|
55
|
+
if (isMacOsHotkeyPressed || isOtherPlatformsHotkeyPressed) {
|
|
56
|
+
event.preventDefault();
|
|
57
|
+
event.stopPropagation();
|
|
58
|
+
setSearchBoxVisible(true);
|
|
59
|
+
setFullSearchActive(true);
|
|
60
|
+
}
|
|
61
|
+
}, []);
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
if (typeof document !== `undefined`) {
|
|
64
|
+
document.addEventListener('keydown', fullSearchKeyHandler);
|
|
65
|
+
const focusManager = new FocusManager();
|
|
66
|
+
return () => {
|
|
67
|
+
document.removeEventListener('keydown', fullSearchKeyHandler);
|
|
68
|
+
focusManager.destroy();
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}, [fullSearchKeyHandler]);
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
if (typeof document !== `undefined` && noScrollClassName) {
|
|
74
|
+
if (fullSearchActive) {
|
|
75
|
+
document.body.classList.add(noScrollClassName);
|
|
76
|
+
} else {
|
|
77
|
+
document.body.classList.remove(noScrollClassName);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}, [fullSearchActive]);
|
|
35
81
|
return React__default.createElement(ThemeProvider, {
|
|
36
82
|
theme: 'dark'
|
|
83
|
+
}, React__default.createElement(SearchWrapper, {
|
|
84
|
+
searchConfig: searchConfig
|
|
37
85
|
}, React__default.createElement("header", {
|
|
38
86
|
ref: headerRef,
|
|
39
87
|
className: classNames(styles.headerMenu, className, {
|
|
@@ -45,19 +93,26 @@ const Header = forwardRef(({
|
|
|
45
93
|
homeUrl: currentUrl
|
|
46
94
|
}) : React__default.createElement(LogoLarge, {
|
|
47
95
|
productWebUrl: productWebUrl
|
|
48
|
-
}),
|
|
96
|
+
}), isSearchBoxVisible ? React__default.createElement(OutsideClickHandler, {
|
|
97
|
+
onOutsideClick: handleSearchClose
|
|
98
|
+
}, React__default.createElement(SearchBox, {
|
|
99
|
+
closeHandler: handleSearchClose,
|
|
100
|
+
isMobile: isMenuPopupVisible,
|
|
101
|
+
fullSearchActive: fullSearchActive,
|
|
102
|
+
setFullSearchActive: setFullSearchActive
|
|
103
|
+
})) : React__default.createElement(React__default.Fragment, null, !isMenuPopupVisible && React__default.createElement(HorizontalMenu, {
|
|
49
104
|
items: items,
|
|
50
105
|
theme: dropdownTheme,
|
|
51
106
|
linkHandler: linkHandler
|
|
52
107
|
}), React__default.createElement(SearchButton, {
|
|
53
|
-
onClick:
|
|
54
|
-
isActive: hasSearch
|
|
108
|
+
onClick: handleSearchButtonClick,
|
|
109
|
+
isActive: hasSearch
|
|
55
110
|
}), isMenuPopupVisible && React__default.createElement(MenuPopup, {
|
|
56
111
|
items: items,
|
|
57
112
|
setExpand: setMenuPopupExpanded,
|
|
58
113
|
isExpanded: menuPopupExpanded,
|
|
59
114
|
headerRef: headerRef,
|
|
60
115
|
linkHandler: linkHandler
|
|
61
|
-
})));
|
|
116
|
+
})))));
|
|
62
117
|
});
|
|
63
118
|
export { Header };
|