@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,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,5 @@
1
+ var styles = {
2
+ "hitList": "ktl-hit-list-module_hitList_1MP6m",
3
+ "titleLink": "ktl-hit-list-module_titleLink_rdJ6u"
4
+ };
5
+ export { styles as default };
@@ -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,4 @@
1
+ var styles = {
2
+ "loader": "ktl-loading-module_loader_B2IQl"
3
+ };
4
+ export { styles as default };
@@ -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
- }), !isMenuPopupVisible && React__default.createElement(HorizontalMenu, {
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: onSearchClick,
54
- isActive: hasSearch && !!onSearchClick
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 };