@redocly/theme 0.18.3-patch.2 → 0.18.3-patch.6

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 (89) hide show
  1. package/lib/I18n/LanguagePicker.js +17 -10
  2. package/lib/I18n/styledVariables.js +1 -0
  3. package/lib/components/Breadcrumbs/Breadcrumb.js +0 -4
  4. package/lib/components/Breadcrumbs/Breadcrumbs.js +5 -1
  5. package/lib/components/Breadcrumbs/styledVariables.js +2 -1
  6. package/lib/components/Catalog/Catalog.d.ts +5 -0
  7. package/lib/components/Catalog/Catalog.js +18 -16
  8. package/lib/components/Catalog/CatalogCard.js +22 -5
  9. package/lib/components/CodeBlock/styledVariables.js +2 -2
  10. package/lib/components/Filter/Filter.d.ts +2 -1
  11. package/lib/components/Filter/Filter.js +15 -14
  12. package/lib/components/Filter/FilterContent.d.ts +2 -1
  13. package/lib/components/Filter/FilterContent.js +5 -4
  14. package/lib/components/Filter/FilterPopover.d.ts +3 -1
  15. package/lib/components/Filter/FilterPopover.js +4 -4
  16. package/lib/components/Filter/styledVariables.js +1 -1
  17. package/lib/components/Footer/Footer.js +2 -1
  18. package/lib/components/Footer/FooterColumn.js +2 -0
  19. package/lib/components/Footer/styledVariables.js +1 -1
  20. package/lib/components/LastUpdated/LastUpdated.js +1 -0
  21. package/lib/components/Markdown/Admonition.js +12 -9
  22. package/lib/components/Markdown/Mermaid.js +3 -0
  23. package/lib/components/Markdown/styledVariables.d.ts +1 -0
  24. package/lib/components/Markdown/styledVariables.js +17 -7
  25. package/lib/components/Panel/PanelHeader.js +1 -0
  26. package/lib/components/Panel/styledVariables.js +1 -1
  27. package/lib/components/Product/ProductPicker.js +17 -5
  28. package/lib/components/Select/Select.d.ts +13 -6
  29. package/lib/components/Select/Select.js +20 -15
  30. package/lib/components/Select/styledVariables.js +2 -1
  31. package/lib/components/Separator/SeparatorItem.js +1 -0
  32. package/lib/components/Sidebar/HeaderWrapper.js +2 -2
  33. package/lib/components/Sidebar/VersionPicker.d.ts +3 -3
  34. package/lib/components/Sidebar/VersionPicker.js +5 -4
  35. package/lib/components/Sidebar/styledVariables.js +5 -4
  36. package/lib/components/Tag/styledVariables.js +10 -9
  37. package/lib/components/Tiles/TileHeader.js +1 -1
  38. package/lib/config.d.ts +25 -2
  39. package/lib/config.js +12 -3
  40. package/lib/globalStyle.js +35 -33
  41. package/lib/hooks/useMobileMenu.js +5 -6
  42. package/lib/hooks/useModalScrollLock.d.ts +1 -0
  43. package/lib/hooks/useModalScrollLock.js +16 -0
  44. package/lib/icons/AlertIcon/AlertIcon.js +0 -5
  45. package/lib/types/portal/src/shared/types/catalog.d.ts +5 -1
  46. package/lib/ui/Highlight.d.ts +1 -1
  47. package/lib/ui/Highlight.js +1 -1
  48. package/lib/ui/darkColors.js +26 -26
  49. package/lib/utils/css-variables.js +1 -1
  50. package/package.json +1 -1
  51. package/src/I18n/LanguagePicker.tsx +23 -21
  52. package/src/I18n/styledVariables.ts +1 -0
  53. package/src/components/Breadcrumbs/Breadcrumb.tsx +0 -4
  54. package/src/components/Breadcrumbs/Breadcrumbs.tsx +5 -1
  55. package/src/components/Breadcrumbs/styledVariables.ts +2 -1
  56. package/src/components/Catalog/Catalog.tsx +17 -10
  57. package/src/components/Catalog/CatalogCard.tsx +26 -3
  58. package/src/components/CodeBlock/styledVariables.ts +2 -2
  59. package/src/components/Filter/Filter.tsx +28 -26
  60. package/src/components/Filter/FilterContent.tsx +7 -4
  61. package/src/components/Filter/FilterPopover.tsx +13 -3
  62. package/src/components/Filter/styledVariables.ts +1 -1
  63. package/src/components/Footer/Footer.tsx +1 -1
  64. package/src/components/Footer/FooterColumn.tsx +2 -0
  65. package/src/components/Footer/styledVariables.ts +1 -1
  66. package/src/components/LastUpdated/LastUpdated.tsx +1 -2
  67. package/src/components/Markdown/Admonition.tsx +13 -8
  68. package/src/components/Markdown/Mermaid.tsx +3 -0
  69. package/src/components/Markdown/styledVariables.ts +18 -7
  70. package/src/components/Panel/PanelHeader.ts +1 -0
  71. package/src/components/Panel/styledVariables.ts +1 -1
  72. package/src/components/Product/ProductPicker.tsx +18 -13
  73. package/src/components/Select/Select.tsx +47 -26
  74. package/src/components/Select/styledVariables.ts +2 -1
  75. package/src/components/Separator/SeparatorItem.tsx +1 -0
  76. package/src/components/Sidebar/HeaderWrapper.tsx +2 -2
  77. package/src/components/Sidebar/VersionPicker.tsx +5 -4
  78. package/src/components/Sidebar/styledVariables.ts +5 -4
  79. package/src/components/Tag/styledVariables.ts +10 -9
  80. package/src/components/Tiles/TileHeader.ts +1 -1
  81. package/src/config.ts +11 -1
  82. package/src/globalStyle.ts +36 -34
  83. package/src/hooks/useMobileMenu.ts +3 -4
  84. package/src/hooks/useModalScrollLock.ts +12 -0
  85. package/src/icons/AlertIcon/AlertIcon.tsx +0 -5
  86. package/src/types/portal/src/shared/types/catalog.ts +7 -1
  87. package/src/ui/Highlight.tsx +2 -2
  88. package/src/ui/darkColors.tsx +26 -26
  89. package/src/utils/css-variables.ts +4 -2
@@ -2,19 +2,20 @@ import React from 'react';
2
2
  import styled from 'styled-components';
3
3
  import { DatePicker } from 'react-date-picker';
4
4
 
5
- import type { ReactNode } from 'react';
5
+ // import type { ReactNode } from 'react';
6
6
 
7
7
  import type { ResolvedFilter } from '@theme/types/portal/src/shared/types/catalog';
8
8
  import { useTranslate } from '@portal/hooks';
9
9
  import { CheckboxIcon } from '@theme/icons';
10
10
  import { Select } from '@theme/components/Select';
11
+ import type { ThemeConfig } from '@theme/config';
11
12
 
12
13
  export function Filter({
13
14
  filter,
14
15
  filterValuesCasing,
15
16
  }: {
16
17
  filter: ResolvedFilter & { selectedOptions: any };
17
- filterValuesCasing?: 'sentence';
18
+ filterValuesCasing?: NonNullable<ThemeConfig['catalog']>[string]['filterValuesCasing'];
18
19
  }) {
19
20
  const { translate } = useTranslate();
20
21
  const translationKeys = {
@@ -24,7 +25,7 @@ export function Filter({
24
25
 
25
26
  if (!filter.parentUsed) return null;
26
27
 
27
- let selectOptions: { value: string; component: React.ReactNode }[] = [];
28
+ let selectOptions: { value: string; element: React.ReactNode }[] = [];
28
29
  if (filter.type === 'select') {
29
30
  const defaultOptionCount = filter.filteredOptions.reduce(
30
31
  (acc, option) => acc + option.count,
@@ -33,8 +34,8 @@ export function Filter({
33
34
  selectOptions = [
34
35
  {
35
36
  value: '',
36
- component: (
37
- <FilterOption key="all" role="link" onClick={() => filter.selectOption('')}>
37
+ element: (
38
+ <FilterOption key="all" onClick={() => filter.selectOption('')}>
38
39
  <FilterOptionLabel>{translate(translationKeys.selectAll, 'All')}</FilterOptionLabel>
39
40
  <FilterOptionCount>{defaultOptionCount}</FilterOptionCount>
40
41
  </FilterOption>
@@ -44,12 +45,8 @@ export function Filter({
44
45
  selectOptions.push(
45
46
  ...filter.filteredOptions.map((option) => ({
46
47
  value: option.value,
47
- component: (
48
- <FilterOption
49
- key={option.value}
50
- role="link"
51
- onClick={() => filter.selectOption(option.value)}
52
- >
48
+ element: (
49
+ <FilterOption key={option.value}>
53
50
  <FilterOptionLabel>
54
51
  {changeCasing(translate(option.value), filterValuesCasing)}
55
52
  </FilterOptionLabel>
@@ -60,22 +57,14 @@ export function Filter({
60
57
  );
61
58
  }
62
59
 
63
- let selectedOptionComponent: ReactNode;
64
- if (filter.type === 'select') {
65
- const selectedOption = (filter.selectedOptions as Set<any>).values().next()?.value;
66
- selectedOptionComponent =
67
- selectOptions.find((option) => option.value === selectedOption)?.component ||
68
- selectOptions[0].component;
69
- }
70
-
71
60
  return (
72
61
  <FilterGroup key={filter.property + filter.title}>
73
62
  <FilterTitle>{translate(filter.titleTranslationKey, filter.title)} </FilterTitle>
74
63
  {filter.type === 'select' ? (
75
64
  <StyledSelect
76
- withArrow={true}
77
- selected={selectedOptionComponent}
78
- options={selectOptions.map((option) => option.component)}
65
+ value={(filter.selectedOptions as Set<any>).values().next()?.value}
66
+ onChange={(value) => filter.selectOption(value)}
67
+ options={selectOptions}
79
68
  />
80
69
  ) : filter.type === 'date-range' ? (
81
70
  <>
@@ -140,11 +129,24 @@ export function Filter({
140
129
  );
141
130
  }
142
131
 
143
- function changeCasing(str: string, casing?: 'sentence') {
144
- if (casing !== 'sentence') return str;
132
+ function changeCasing(
133
+ str: string,
134
+ casing?: NonNullable<ThemeConfig['catalog']>[string]['filterValuesCasing'],
135
+ ) {
136
+ if (!casing || casing === 'original' || !str) return str;
137
+
138
+ if (casing === 'lowercase') {
139
+ return str.toLowerCase();
140
+ }
141
+
142
+ if (casing === 'uppercase') {
143
+ return str.toUpperCase();
144
+ }
145
145
 
146
- const words = str.split(/[\s-_]+/);
147
- return words.map((word) => word[0].toUpperCase() + word.slice(1).toLowerCase()).join(' ');
146
+ if (casing === 'sentence') {
147
+ const words = str.split(/[\s-_]+/);
148
+ return words.map((word) => word[0].toUpperCase() + word.slice(1).toLowerCase()).join(' ');
149
+ }
148
150
  }
149
151
 
150
152
  const FilterGroup = styled.div`
@@ -2,9 +2,12 @@ import React from 'react';
2
2
  import styled from 'styled-components';
3
3
  import { Button } from '@theme';
4
4
 
5
+ import type { ThemeConfig } from '@theme';
6
+
5
7
  import { useTranslate } from '@portal/hooks';
6
8
  import { FilterControls } from '@theme/components/Catalog';
7
- import { Filter } from '@theme/components/Filter/index';
9
+ import { Filter } from '@theme/components/Filter';
10
+ import { Sidebar } from '@theme/components/Sidebar';
8
11
  import type { ResolvedFilter } from '@theme/types/portal/src/shared/types/catalog';
9
12
  import { StyledInput } from '@theme/components/Filter/FilterPopover';
10
13
 
@@ -13,7 +16,7 @@ interface FilterContentProps {
13
16
  filters: ResolvedFilter[];
14
17
  filterTerm: string;
15
18
  isMobile: boolean;
16
- filterValuesCasing?: 'sentence';
19
+ filterValuesCasing?: NonNullable<ThemeConfig['catalog']>[string]['filterValuesCasing'];
17
20
  }
18
21
 
19
22
  export function FilterContent({
@@ -68,7 +71,7 @@ export function FilterContent({
68
71
  );
69
72
  }
70
73
 
71
- const FilterContentWrapper = styled.div<{ isMobile?: boolean }>`
74
+ const FilterContentWrapper = styled(Sidebar)<{ isMobile?: boolean }>`
72
75
  width: var(--sidebar-width);
73
76
  display: none;
74
77
 
@@ -77,7 +80,7 @@ const FilterContentWrapper = styled.div<{ isMobile?: boolean }>`
77
80
  margin: var(--filter-content-clear-button-margin);
78
81
  }
79
82
 
80
- ${({ theme, isMobile }) => !isMobile && theme.mediaQueries.small} {
83
+ ${({ theme, isMobile }) => !isMobile && theme.mediaQueries.medium} {
81
84
  display: block;
82
85
  border-right: 1px solid var(--filter-content-border-color);
83
86
 
@@ -2,6 +2,8 @@ import React from 'react';
2
2
  import styled from 'styled-components';
3
3
  import { Filter, FilterControls } from '@theme';
4
4
 
5
+ import type { ThemeConfig } from '@theme';
6
+
5
7
  import { FilterContent, FilterItems } from '@theme/components/Filter/FilterContent';
6
8
  import { useTranslate } from '@portal/hooks';
7
9
  import type { ResolvedFilter } from '@theme/types/portal/src/shared/types/catalog';
@@ -9,13 +11,20 @@ import type { ResolvedFilter } from '@theme/types/portal/src/shared/types/catalo
9
11
  interface FilterPopoverProps {
10
12
  setIsAddingFilter: (value: boolean) => void;
11
13
  filters: ResolvedFilter[];
14
+ filterValuesCasing?: NonNullable<ThemeConfig['catalog']>[string]['filterValuesCasing'];
12
15
  }
13
16
 
14
- export function FilterPopover({ setIsAddingFilter, filters }: FilterPopoverProps) {
17
+ export function FilterPopover({
18
+ setIsAddingFilter,
19
+ filters,
20
+ filterValuesCasing,
21
+ }: FilterPopoverProps) {
15
22
  const [filterTerm, setFilterTerm] = React.useState('');
16
23
  const filteredFilters = filters
17
24
  .map((filter) => {
18
- const options = filter.options.filter((option) => option.value.includes(filterTerm));
25
+ const options = filter.options.filter((option) =>
26
+ option.value.toLowerCase().includes(filterTerm.toLowerCase()),
27
+ );
19
28
  return options.length
20
29
  ? {
21
30
  ...filter,
@@ -43,6 +52,7 @@ export function FilterPopover({ setIsAddingFilter, filters }: FilterPopoverProps
43
52
  setFilterTerm={setFilterTerm}
44
53
  filters={filters}
45
54
  filterTerm={filterTerm}
55
+ filterValuesCasing={filterValuesCasing}
46
56
  isMobile
47
57
  />
48
58
  <FilterControls>
@@ -72,7 +82,7 @@ const FilterPopoverWrapper = styled.aside<{ isActiveInMobileMode?: boolean }>`
72
82
  background-color: var(--filter-popover-background-color);
73
83
  display: block;
74
84
 
75
- ${({ theme }) => theme.mediaQueries.small} {
85
+ ${({ theme }) => theme.mediaQueries.medium} {
76
86
  display: none;
77
87
  }
78
88
  `;
@@ -30,7 +30,7 @@ export const filter = css`
30
30
  --filter-option-count-background-color: var(--bg-raised);
31
31
 
32
32
  --filter-content-border-color: var(--border-secondary);
33
- --filter-content-items-padding: 0 var(--mobile-catalog-filter-padding-horizontal);
33
+ --filter-content-items-padding: 0 var(--spacing-lg);
34
34
  --filter-content-clear-button-margin: var(--spacing-base) auto 0 auto;
35
35
  --filter-content-clear-button-width: calc(var(--spacing-unit) * 40);
36
36
  --filter-controls-background-color: var(--bg-base);
@@ -21,7 +21,7 @@ export function Footer(): JSX.Element | null {
21
21
  <FooterPresentationalComponent
22
22
  items={items as ResolvedNavItem[]}
23
23
  copyrightText={copyrightText}
24
- logo={logo}
24
+ logo={footer?.logo?.hide ? undefined : logo}
25
25
  />
26
26
  );
27
27
  }
@@ -83,6 +83,8 @@ const FooterColumnContainer = styled.div`
83
83
  flex: 1;
84
84
  margin: 0 0 var(--mobile-footer-column-margin-bottom) 0;
85
85
  min-width: var(--footer-column-min-width);
86
+ align-content: center;
87
+ flex-wrap: wrap;
86
88
 
87
89
  ${({ theme }) => theme.mediaQueries.small} {
88
90
  text-align: left;
@@ -42,7 +42,7 @@ export const footer = css`
42
42
  --footer-copyright-text-align: start;
43
43
 
44
44
  --footer-logo-display: block;
45
- --footer-logo-margin-right: 86px; // @presenter Spacing
45
+ --footer-logo-margin-right: 0; // @presenter Spacing
46
46
  --footer-logo-margin-bottom: var(--spacing-xl); // @presenter Spacing
47
47
 
48
48
  --footer-language-picker-display: block;
@@ -51,8 +51,7 @@ export function LastUpdated(props: LastUpdatedProps): JSX.Element | null {
51
51
  data-print-datetime={isoDate}
52
52
  data-translation-key={translationKey}
53
53
  >
54
- {text}
55
- {/* TODO: fix issue with snapshot tests - they should not depend on current date */}
54
+ {text} {/* TODO: fix issue with snapshot tests - they should not depend on current date */}
56
55
  <time dateTime={isoDate}>{lastUpdatedString}</time>
57
56
  </Wrapper>
58
57
  );
@@ -31,18 +31,20 @@ export function Admonition({
31
31
  data-hash={dataHash}
32
32
  >
33
33
  <AlertIcon type={type} />
34
- {name ? <Heading type={type}>{name}</Heading> : null}
35
- <Content>{children}</Content>
34
+ <TextContainer>
35
+ {name ? <Heading type={type}>{name}</Heading> : null}
36
+ <Content>{children}</Content>
37
+ </TextContainer>
36
38
  </Wrapper>
37
39
  );
38
40
  }
39
41
 
40
42
  const Wrapper = styled.div<AdmonitionTypeProps>`
41
- position: relative;
42
- align-items: center;
43
+ display: flex;
44
+ flex-direction: row;
45
+ gap: var(--spacing-base);
43
46
  margin: var(--admonition-margin-vertical) var(--admonition-margin-horizontal);
44
47
  padding: var(--admonition-padding-vertical) var(--admonition-padding-horizontal);
45
- padding-left: calc(var(--admonition-padding-horizontal) * 2 + var(--admonition-icon-size));
46
48
  border-radius: var(--admonition-border-radius);
47
49
  font-size: var(--admonition-font-size);
48
50
  font-weight: var(--admonition-font-weight);
@@ -58,10 +60,13 @@ const Wrapper = styled.div<AdmonitionTypeProps>`
58
60
  `}
59
61
  `;
60
62
 
61
- const Heading = styled.h5<AdmonitionTypeProps>`
62
- display: block;
63
- margin: 0 0 10px;
63
+ const TextContainer = styled.div`
64
+ display: flex;
65
+ flex-direction: column;
66
+ gap: var(--spacing-unit);
67
+ `;
64
68
 
69
+ const Heading = styled.div<AdmonitionTypeProps>`
65
70
  letter-spacing: var(--admonition-heading-letter-spacing);
66
71
  color: ${({ type }) => `var(--admonition-${type}-heading-text-color)`};
67
72
 
@@ -28,6 +28,9 @@ export function Mermaid({
28
28
  }
29
29
 
30
30
  const Wrapper = styled.div`
31
+ background-color: var(--mermaid-background-color);
32
+ border-radius: var(--mermaid-border-radius);
33
+
31
34
  .mermaid > svg {
32
35
  font-size: var(--font-size-base) !important;
33
36
  max-width: 100%;
@@ -7,11 +7,11 @@ export const admonition = css`
7
7
  * @tokens Admonition typography
8
8
  */
9
9
 
10
- --admonition-font-size: var(--font-size-base); // @presenter FontSize
10
+ --admonition-font-size: var(--font-size-sm); // @presenter FontSize
11
11
  --admonition-font-weight: var(--font-weight-regular); // @presenter FontWeight
12
12
  --admonition-line-height: var(--line-height-base); // @presenter LineHeight
13
13
  --admonition-heading-font-size: var(--font-size-base); // @presenter FontSize
14
- --admonition-heading-font-weight: var(--font-weight-bold); // @presenter FontWeight
14
+ --admonition-heading-font-weight: var(--font-weight-regular); // @presenter FontWeight
15
15
  --admonition-heading-letter-spacing: 0.3px; // @presenter LetterSpacing
16
16
  --admonition-heading-transform: uppercase;
17
17
 
@@ -21,16 +21,16 @@ export const admonition = css`
21
21
  */
22
22
 
23
23
  --admonition-margin-horizontal: 0;
24
- --admonition-margin-vertical: var(--spacing-xs);
24
+ --admonition-margin-vertical: var(--spacing-lg);
25
25
  --admonition-padding-horizontal: var(--spacing-base);
26
- --admonition-padding-vertical: var(--spacing-base);
26
+ --admonition-padding-vertical: var(--spacing-md);
27
27
  --admonition-icon-size: 25px;
28
28
 
29
29
  /**
30
30
  * @tokens Admonition border
31
31
  */
32
32
 
33
- --admonition-border-radius: var(--border-radius-lg); // @presenter BorderRadius
33
+ --admonition-border-radius: 0; // @presenter BorderRadius
34
34
  --admonition-border-style: var(--border-style);
35
35
  --admonition-border-width: var(--border-width);
36
36
 
@@ -148,7 +148,7 @@ export const markdown = css`
148
148
  * @tokens Markdown Table
149
149
  */
150
150
 
151
- --md-table-font-size: var(--font-size-base); // @presenter FontSize
151
+ --md-table-font-size: var(--font-size-sm); // @presenter FontSize
152
152
  --md-table-margin-vertical: 20px; // @presenter Spacing
153
153
  --md-table-background-color: transparent; // @presenter Color
154
154
 
@@ -216,4 +216,15 @@ export const markdown = css`
216
216
  --md-tabs-hover-tab-border-color: var(--border-secondary); // @presenter Color
217
217
 
218
218
  // @tokens End
219
- `;
219
+ `;
220
+
221
+ export const mermaid = css`
222
+ /**
223
+ * @tokens Mermaid
224
+ */
225
+
226
+ --mermaid-background-color: var(--bg-raised-light); // @presenter Color
227
+ --mermaid-border-radius: var(--border-radius-lg); // @presenter BorderRadius
228
+
229
+ // @tokens End
230
+ `;
@@ -18,6 +18,7 @@ export const PanelHeader = styled.div.attrs<{ className?: string }>(({ className
18
18
  justify-content: space-between;
19
19
  min-height: 40px;
20
20
  white-space: var(--panel-heading-white-space-local);
21
+ color: var(--panel-heading-text-color);
21
22
 
22
23
  cursor: pointer;
23
24
 
@@ -421,7 +421,7 @@ export const apiReferencePanels = css`
421
421
  * @tokens Panel try-it tabs
422
422
  */
423
423
 
424
- --panel-try-it-tabs-font-size: 12px; // @presenter FontSize
424
+ --panel-try-it-tabs-font-size: var(--font-size-sm);
425
425
  --panel-try-it-tabs-font-family: var(--panel-font-family); // @presenter FontFamily
426
426
  --panel-try-it-tabs-font-weight: var(--panel-font-weight); // @presenter FontWeight
427
427
 
@@ -4,7 +4,8 @@ import styled from 'styled-components';
4
4
  import type { ThemeUIConfig } from '../../config';
5
5
 
6
6
  import { useCurrentProduct, useGlobalData } from '@portal/hooks';
7
- import { Link } from '@portal/Link';
7
+ import { usePreloadHistory } from '@portal/usePreloadHistory';
8
+ // import { Link } from '@portal/Link';
8
9
  import { ArrowIcon } from '@theme/icons';
9
10
 
10
11
  import { Select, SelectInput, SelectList, SelectListItem } from '../Select';
@@ -13,26 +14,30 @@ import { Product } from './Product';
13
14
  export const ProductPicker = () => {
14
15
  const globalData = useGlobalData() as { theme?: ThemeUIConfig };
15
16
  const currentProduct = useCurrentProduct();
16
- const currentProductComponent = currentProduct ? (
17
- <Product product={currentProduct} />
18
- ) : (
19
- 'Products'
20
- );
17
+ // const currentProductComponent = currentProduct ? (
18
+ // <Product product={currentProduct} />
19
+ // ) : (
20
+ // 'Products'
21
+ // );
22
+ const { push } = usePreloadHistory();
21
23
 
22
24
  const products = Object.values(globalData?.theme?.products || {});
23
25
 
24
- const productComponents = products.map((product) => (
25
- <Link key={product.slug} to={product.link}>
26
- <Product product={product} />
27
- </Link>
28
- ));
26
+ const productComponents = products.map((product) => ({
27
+ element: <Product product={product} />,
28
+ value: product,
29
+ }));
29
30
 
30
31
  return products.length ? (
31
32
  <ProductSelect
32
- selected={currentProductComponent}
33
+ value={currentProduct}
34
+ placeholder="Products"
33
35
  options={productComponents}
34
- withArrow={true}
35
36
  triggerEvent="hover"
37
+ onChange={(product: typeof currentProduct) => {
38
+ if (!product) return;
39
+ push(product.link);
40
+ }}
36
41
  />
37
42
  ) : null;
38
43
  };
@@ -1,40 +1,46 @@
1
- import React, { useEffect, useRef, useState } from 'react';
1
+ import React, { useRef, useState } from 'react';
2
2
  import styled from 'styled-components';
3
3
 
4
4
  import { ArrowIcon } from '@theme/icons';
5
5
  import { useOutsideClick } from '@theme/hooks';
6
6
 
7
- export interface SelectProps {
8
- selected: React.ReactNode | string;
9
- options: React.ReactNode[] | string[];
7
+ export interface SelectProps<T = any> {
8
+ value: T;
9
+ options: {
10
+ element: React.ReactNode | string;
11
+ value: T;
12
+ label?: string;
13
+ }[];
10
14
  dataAttributes?: Record<string, string>;
11
15
  className?: string;
12
16
  withArrow?: boolean;
13
17
  triggerEvent?: 'click' | 'hover';
14
- onChange?: (value: React.ReactNode | string) => void;
18
+ onChange?: (value: any) => void;
15
19
  placement?: 'top' | 'bottom';
16
20
  alignment?: 'start' | 'end';
17
21
  icon?: React.ReactNode;
18
22
  onlyIcon?: boolean;
23
+ placeholder?: string;
19
24
  }
20
25
 
21
- export const Select = ({
26
+ export function Select<T>({
22
27
  className,
23
- selected,
28
+ value,
24
29
  options,
25
30
  dataAttributes,
26
- withArrow,
31
+ withArrow = true,
27
32
  triggerEvent = 'click',
28
33
  onChange,
29
34
  placement,
30
35
  alignment,
31
36
  icon,
32
37
  onlyIcon,
33
- }: SelectProps) => {
34
- const selectRef = useRef<HTMLDivElement | null>(null);
38
+ placeholder,
39
+ }: SelectProps<T>) {
40
+ const containerRef = useRef<HTMLDivElement | null>(null);
35
41
 
36
42
  const [isOpen, setIsOpen] = useState<boolean>(false);
37
- const [_selected, setSelected] = useState<React.ReactNode | string>(selected);
43
+ // const [selectedIdx, setSelectedIdx] = useState<React.ReactNode | string>(selected);
38
44
 
39
45
  const handleOpen = () => {
40
46
  setIsOpen(true);
@@ -48,46 +54,56 @@ export const Select = ({
48
54
  setIsOpen(!isOpen);
49
55
  };
50
56
 
51
- const handleSelect = (value: React.ReactNode | string) => {
52
- setSelected(value);
57
+ const handleSelect = (value: T) => {
58
+ // setSelectedIdx(options.findIndex(o => o.value === value));
53
59
  setIsOpen(false);
54
60
  onChange?.(value);
55
61
  };
56
62
 
57
- useOutsideClick(selectRef, handleClose);
63
+ useOutsideClick(containerRef, handleClose);
58
64
 
59
- useEffect(() => {
60
- handleSelect(selected);
61
- // eslint-disable-next-line react-hooks/exhaustive-deps
62
- }, [selected]);
65
+ // useEffect(() => {
66
+ // handleSelect(selected);
67
+ // // eslint-disable-next-line react-hooks/exhaustive-deps
68
+ // }, [selected]);
69
+
70
+ const selectedOption = options.find((option) => option.value === value);
63
71
 
64
72
  return (
65
73
  <SelectContainer
66
74
  {...dataAttributes}
67
75
  data-testid="select"
68
76
  className={className}
69
- ref={selectRef}
77
+ ref={containerRef}
70
78
  onPointerEnter={triggerEvent === 'hover' ? handleOpen : undefined}
71
79
  onPointerLeave={triggerEvent === 'hover' ? handleClose : undefined}
72
80
  onClick={triggerEvent === 'click' ? handleToggle : undefined}
73
81
  >
74
82
  <SelectInput>
75
- {!onlyIcon && <SelectInputValue>{_selected}</SelectInputValue>}
83
+ {!onlyIcon && (
84
+ <SelectInputValue>
85
+ {selectedOption?.label || selectedOption?.element || placeholder}
86
+ </SelectInputValue>
87
+ )}
76
88
  {icon}
77
89
  {withArrow ? isOpen ? <ArrowIcon direction="up" /> : <ArrowIcon direction="down" /> : null}
78
90
  </SelectInput>
79
91
  {isOpen && (
80
92
  <SelectList placement={placement} alignment={alignment}>
81
93
  {options.map((option, index) => (
82
- <SelectListItem key={index} onClick={() => handleSelect(option)}>
83
- {option}
94
+ <SelectListItem
95
+ key={index}
96
+ onClick={() => handleSelect(option.value)}
97
+ selected={option.value === value}
98
+ >
99
+ {typeof option.element === 'string' ? <div>{option.element}</div> : option.element}
84
100
  </SelectListItem>
85
101
  ))}
86
102
  </SelectList>
87
103
  )}
88
104
  </SelectContainer>
89
105
  );
90
- };
106
+ }
91
107
 
92
108
  export const SelectContainer = styled.div`
93
109
  position: relative;
@@ -129,7 +145,6 @@ export const SelectList = styled.ul<{ placement?: string; alignment?: string }>`
129
145
  min-width: var(--select-list-min-width);
130
146
  width: 100%;
131
147
  max-width: var(--select-list-max-width);
132
- ${({ placement }) => (!placement || placement === 'bottom' ? 'margin-top: 2px' : '')};
133
148
  padding: var(--select-list-padding);
134
149
  background-color: var(--select-list-background-color);
135
150
  border-radius: var(--select-list-border-radius);
@@ -141,7 +156,7 @@ export const SelectList = styled.ul<{ placement?: string; alignment?: string }>`
141
156
  z-index: 1;
142
157
  `;
143
158
 
144
- export const SelectListItem = styled.li`
159
+ export const SelectListItem = styled.li<{ selected: boolean }>`
145
160
  border-radius: var(--select-list-item-border-radius);
146
161
 
147
162
  & > * {
@@ -149,6 +164,12 @@ export const SelectListItem = styled.li`
149
164
  }
150
165
 
151
166
  :hover {
152
- background-color: var(--select-list-item-active-background-color);
167
+ background-color: var(--select-list-item-hover-background-color);
153
168
  }
169
+
170
+ ${({ selected }) =>
171
+ selected &&
172
+ `
173
+ &, &:hover { background-color: var(--select-list-item-active-background-color); }
174
+ `}
154
175
  `;
@@ -24,7 +24,8 @@ export const select = css`
24
24
  --select-list-item-horizontal-padding: var(--spacing-base); // @presenter Spacing
25
25
  --select-list-item-vertical-padding: var(--spacing-xs); // @presenter Spacing
26
26
  --select-list-item-border-radius: var(--border-radius); // @presenter BorderRadius
27
- --select-list-item-active-background-color: var(--bg-raised); // @presenter Color
27
+ --select-list-item-active-background-color: var(--color-primary-bg); // @presenter Color
28
+ --select-list-item-hover-background-color: var(--bg-raised); // @presenter Color
28
29
 
29
30
  // @tokens End
30
31
  `;
@@ -23,5 +23,6 @@ export const SeparatorItem = styled(MenuItemLabel).attrs<{ className?: string }>
23
23
 
24
24
  :hover {
25
25
  color: var(--sidebar-item-separator-text-color);
26
+ background: none;
26
27
  }
27
28
  `;
@@ -4,7 +4,7 @@ export const HeaderWrapper = styled.div.attrs<{ className?: string }>(({ classNa
4
4
  'data-component-name': 'Sidebar/HeaderWrapper',
5
5
  className,
6
6
  }))`
7
- margin: var(--sidebar-offset-top) 0 0 var(--sidebar-offset-left);
8
- padding-bottom: var(--sidebar-header-padding-bottom);
7
+ margin: var(--sidebar-offset-top) 0 0 0;
8
+ padding: 0 0 var(--sidebar-header-padding-bottom) var(--sidebar-offset-left);
9
9
  border-bottom: solid 1px var(--border-secondary);
10
10
  `;
@@ -9,6 +9,11 @@ export const VersionPicker = styled(Select).attrs(() => ({
9
9
  'data-component-name': 'Sidebar/VersionPicker',
10
10
  },
11
11
  }))<SelectProps>`
12
+ --select-list-item-active-background-color: var(
13
+ --version-picker-list-item-active-background-color
14
+ );
15
+ --select-list-item-hover-background-color: var(--version-picker-list-item-hover-background-color);
16
+
12
17
  font-size: var(--version-picker-font-size);
13
18
  font-weight: var(--version-picker-font-weight);
14
19
  border-radius: var(--version-picker-border-radius);
@@ -40,9 +45,5 @@ export const VersionPicker = styled(Select).attrs(() => ({
40
45
  padding: var(--version-picker-list-item-vertical-padding)
41
46
  var(--version-picker-list-item-horizontal-padding);
42
47
  }
43
-
44
- :hover {
45
- background-color: var(--version-picker-list-item-active-background-color);
46
- }
47
48
  }
48
49
  `;