@ndla/ui 4.3.0 → 5.0.0

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 (177) hide show
  1. package/README.md +11 -11
  2. package/es/Article/Article.js +3 -7
  3. package/es/Article/ArticleByline.js +4 -4
  4. package/es/Article/ArticleNotions.js +37 -92
  5. package/es/ContentTypeBadge/ContentTypeBadge.js +10 -1
  6. package/es/FactBox/FactBox.js +36 -11
  7. package/es/Filter/FilterButtons.js +18 -17
  8. package/es/Filter/FilterCarousel.js +198 -0
  9. package/es/Masthead/Masthead.js +24 -2
  10. package/es/Notion/ConceptNotion.js +57 -0
  11. package/es/Notion/FigureNotion.js +82 -0
  12. package/es/Notion/Notion.js +122 -48
  13. package/es/Notion/NotionImage.js +47 -0
  14. package/es/Notion/NotionVisualElement.js +38 -0
  15. package/es/Notion/index.js +2 -1
  16. package/es/SearchTypeResult/ActiveFilterContent.js +17 -2
  17. package/es/SearchTypeResult/ActiveFilters.js +64 -50
  18. package/es/SearchTypeResult/PopupFilter.js +28 -125
  19. package/es/SearchTypeResult/ResultNavigation.js +41 -14
  20. package/es/SearchTypeResult/SearchFieldHeader.js +20 -41
  21. package/es/SearchTypeResult/SearchFilterContent.js +61 -0
  22. package/es/SearchTypeResult/SearchHeader.js +51 -28
  23. package/es/SearchTypeResult/SearchItem.js +64 -190
  24. package/es/SearchTypeResult/SearchItemList.js +132 -0
  25. package/es/SearchTypeResult/SearchItems.js +17 -13
  26. package/es/SearchTypeResult/SearchNotionItem.js +13 -13
  27. package/es/SearchTypeResult/SearchNotionsResult.js +16 -21
  28. package/es/SearchTypeResult/SearchTypeHeader.js +43 -26
  29. package/es/SearchTypeResult/SearchTypeResult.js +7 -5
  30. package/es/SearchTypeResult/SearchViewType.js +93 -0
  31. package/es/SearchTypeResult/components/ItemContexts.js +125 -0
  32. package/es/SearchTypeResult/components/ItemResourceHeader.js +87 -0
  33. package/es/SearchTypeResult/components/ItemTopicHeader.js +56 -0
  34. package/es/SearchTypeResult/components/SubjectFilters.js +177 -0
  35. package/es/SearchTypeResult/index.js +2 -1
  36. package/es/all.css +1 -1
  37. package/es/index-javascript.js +1 -2
  38. package/es/index.js +3 -2
  39. package/es/locale/messages-en.js +21 -6
  40. package/es/locale/messages-nb.js +21 -6
  41. package/es/locale/messages-nn.js +22 -7
  42. package/es/model/ContentType.js +2 -1
  43. package/es/shapes.js +1 -1
  44. package/lib/Article/Article.d.ts +4 -5
  45. package/lib/Article/Article.js +3 -7
  46. package/lib/Article/ArticleByline.js +4 -4
  47. package/lib/Article/ArticleNotions.d.ts +3 -8
  48. package/lib/Article/ArticleNotions.js +41 -90
  49. package/lib/ContentTypeBadge/ContentTypeBadge.d.ts +1 -0
  50. package/lib/ContentTypeBadge/ContentTypeBadge.js +14 -2
  51. package/lib/FactBox/FactBox.js +41 -8
  52. package/lib/Filter/FilterButtons.d.ts +3 -10
  53. package/lib/Filter/FilterButtons.js +19 -17
  54. package/lib/Filter/FilterCarousel.d.ts +13 -0
  55. package/lib/Filter/FilterCarousel.js +207 -0
  56. package/lib/Masthead/Masthead.js +30 -2
  57. package/lib/Notion/ConceptNotion.d.ts +25 -0
  58. package/lib/Notion/ConceptNotion.js +79 -0
  59. package/lib/Notion/FigureNotion.d.ts +23 -0
  60. package/lib/Notion/FigureNotion.js +97 -0
  61. package/lib/Notion/Notion.d.ts +24 -11
  62. package/lib/Notion/Notion.js +120 -48
  63. package/lib/Notion/NotionImage.d.ts +15 -0
  64. package/lib/Notion/NotionImage.js +63 -0
  65. package/lib/Notion/NotionVisualElement.d.ts +22 -0
  66. package/lib/Notion/NotionVisualElement.js +51 -0
  67. package/lib/Notion/index.d.ts +1 -0
  68. package/lib/Notion/index.js +8 -0
  69. package/lib/SearchTypeResult/ActiveFilterContent.js +16 -9
  70. package/lib/SearchTypeResult/ActiveFilters.d.ts +2 -1
  71. package/lib/SearchTypeResult/ActiveFilters.js +65 -50
  72. package/lib/SearchTypeResult/PopupFilter.d.ts +13 -19
  73. package/lib/SearchTypeResult/PopupFilter.js +27 -123
  74. package/lib/SearchTypeResult/ResultNavigation.d.ts +2 -2
  75. package/lib/SearchTypeResult/ResultNavigation.js +38 -14
  76. package/lib/SearchTypeResult/SearchFieldHeader.d.ts +3 -8
  77. package/lib/SearchTypeResult/SearchFieldHeader.js +18 -39
  78. package/lib/SearchTypeResult/SearchFilterContent.d.ts +16 -0
  79. package/lib/SearchTypeResult/SearchFilterContent.js +67 -0
  80. package/lib/SearchTypeResult/SearchHeader.d.ts +3 -7
  81. package/lib/SearchTypeResult/SearchHeader.js +59 -30
  82. package/lib/SearchTypeResult/SearchItem.d.ts +8 -12
  83. package/lib/SearchTypeResult/SearchItem.js +64 -187
  84. package/lib/SearchTypeResult/SearchItemList.d.ts +10 -0
  85. package/lib/SearchTypeResult/SearchItemList.js +139 -0
  86. package/lib/SearchTypeResult/SearchItems.d.ts +4 -3
  87. package/lib/SearchTypeResult/SearchItems.js +18 -13
  88. package/lib/SearchTypeResult/SearchNotionItem.js +13 -13
  89. package/lib/SearchTypeResult/SearchNotionsResult.d.ts +2 -4
  90. package/lib/SearchTypeResult/SearchNotionsResult.js +23 -23
  91. package/lib/SearchTypeResult/SearchTypeHeader.d.ts +2 -2
  92. package/lib/SearchTypeResult/SearchTypeHeader.js +40 -25
  93. package/lib/SearchTypeResult/SearchTypeResult.d.ts +7 -6
  94. package/lib/SearchTypeResult/SearchTypeResult.js +7 -5
  95. package/lib/SearchTypeResult/SearchViewType.d.ts +13 -0
  96. package/lib/SearchTypeResult/SearchViewType.js +99 -0
  97. package/lib/SearchTypeResult/components/ItemContexts.d.ts +19 -0
  98. package/lib/SearchTypeResult/components/ItemContexts.js +134 -0
  99. package/lib/SearchTypeResult/components/ItemResourceHeader.d.ts +16 -0
  100. package/lib/SearchTypeResult/components/ItemResourceHeader.js +98 -0
  101. package/lib/SearchTypeResult/components/ItemTopicHeader.d.ts +17 -0
  102. package/lib/SearchTypeResult/components/ItemTopicHeader.js +67 -0
  103. package/lib/SearchTypeResult/components/SubjectFilters.d.ts +32 -0
  104. package/lib/SearchTypeResult/components/SubjectFilters.js +192 -0
  105. package/lib/SearchTypeResult/index.d.ts +2 -1
  106. package/lib/SearchTypeResult/index.js +8 -0
  107. package/lib/all.css +1 -1
  108. package/lib/index-javascript.js +0 -20
  109. package/lib/index.d.ts +2 -1
  110. package/lib/index.js +24 -1
  111. package/lib/locale/messages-en.d.ts +16 -1
  112. package/lib/locale/messages-en.js +21 -6
  113. package/lib/locale/messages-nb.d.ts +15 -0
  114. package/lib/locale/messages-nb.js +21 -6
  115. package/lib/locale/messages-nn.d.ts +16 -1
  116. package/lib/locale/messages-nn.js +22 -7
  117. package/lib/model/ContentType.d.ts +1 -0
  118. package/lib/model/ContentType.js +4 -2
  119. package/lib/shapes.js +1 -1
  120. package/lib/types.d.ts +1 -0
  121. package/package.json +14 -15
  122. package/src/Article/Article.tsx +8 -15
  123. package/src/Article/ArticleByline.tsx +1 -1
  124. package/src/Article/ArticleNotions.tsx +13 -33
  125. package/src/ContentTypeBadge/ContentTypeBadge.tsx +8 -0
  126. package/src/ContentTypeBadge/component.content-type-badge.scss +5 -0
  127. package/src/FactBox/FactBox.tsx +22 -15
  128. package/src/Figure/component.figure.scss +1 -1
  129. package/src/Filter/FilterButtons.tsx +14 -15
  130. package/src/Filter/FilterCarousel.tsx +166 -0
  131. package/src/Masthead/Masthead.tsx +42 -18
  132. package/src/Notion/ConceptNotion.tsx +80 -0
  133. package/src/Notion/FigureNotion.tsx +86 -0
  134. package/src/Notion/Notion.tsx +205 -75
  135. package/src/Notion/NotionImage.tsx +51 -0
  136. package/src/Notion/NotionVisualElement.tsx +50 -0
  137. package/src/Notion/index.ts +1 -0
  138. package/src/SearchTypeResult/ActiveFilterContent.tsx +7 -2
  139. package/src/SearchTypeResult/ActiveFilters.tsx +72 -38
  140. package/src/SearchTypeResult/PopupFilter.tsx +73 -146
  141. package/src/SearchTypeResult/ResultNavigation.tsx +54 -16
  142. package/src/SearchTypeResult/SearchFieldHeader.tsx +15 -40
  143. package/src/SearchTypeResult/SearchFilterContent.tsx +63 -0
  144. package/src/SearchTypeResult/SearchHeader.tsx +31 -31
  145. package/src/SearchTypeResult/SearchItem.tsx +145 -233
  146. package/src/SearchTypeResult/SearchItemList.tsx +167 -0
  147. package/src/SearchTypeResult/SearchItems.tsx +26 -19
  148. package/src/SearchTypeResult/SearchNotionItem.tsx +5 -12
  149. package/src/SearchTypeResult/SearchNotionsResult.tsx +29 -22
  150. package/src/SearchTypeResult/SearchTypeHeader.tsx +51 -33
  151. package/src/SearchTypeResult/SearchTypeResult.tsx +13 -12
  152. package/src/SearchTypeResult/SearchViewType.tsx +109 -0
  153. package/src/SearchTypeResult/components/ItemContexts.tsx +138 -0
  154. package/src/SearchTypeResult/components/ItemResourceHeader.tsx +133 -0
  155. package/src/SearchTypeResult/components/ItemTopicHeader.tsx +95 -0
  156. package/src/SearchTypeResult/components/SubjectFilters.tsx +152 -0
  157. package/src/SearchTypeResult/index.ts +9 -1
  158. package/src/index-javascript.js +0 -2
  159. package/src/index.ts +3 -0
  160. package/src/locale/messages-en.ts +19 -4
  161. package/src/locale/messages-nb.ts +19 -4
  162. package/src/locale/messages-nn.ts +20 -5
  163. package/src/model/ContentType.ts +1 -0
  164. package/src/shapes.js +1 -0
  165. package/src/types.ts +1 -0
  166. package/es/Embedded/Facebook.js +0 -19
  167. package/es/Embedded/FacebookPage.js +0 -22
  168. package/es/Embedded/Twitter.js +0 -121
  169. package/es/Embedded/index.js +0 -3
  170. package/lib/Embedded/Facebook.js +0 -32
  171. package/lib/Embedded/FacebookPage.js +0 -35
  172. package/lib/Embedded/Twitter.js +0 -132
  173. package/lib/Embedded/index.js +0 -31
  174. package/src/Embedded/Facebook.jsx +0 -17
  175. package/src/Embedded/FacebookPage.jsx +0 -24
  176. package/src/Embedded/Twitter.jsx +0 -71
  177. package/src/Embedded/index.js +0 -3
@@ -5,19 +5,16 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
7
  */
8
-
9
- import React, { FormEvent } from 'react';
8
+ import React, { FormEvent, useEffect, useState } from 'react';
9
+ import { WithTranslation, withTranslation } from 'react-i18next';
10
10
  import styled from '@emotion/styled';
11
11
  import { breakpoints, fonts, mq, spacing } from '@ndla/core';
12
-
13
12
  import Button from '@ndla/button';
14
- import { WithTranslation, withTranslation } from 'react-i18next';
15
- import { FilterProps } from './ActiveFilterContent';
16
- import ActiveFilters from './ActiveFilters';
13
+
17
14
  import SearchFieldHeader from './SearchFieldHeader';
18
- import PopupFilter, { PopupFilterProps } from './PopupFilter';
19
15
  import { CompetenceGoalsItemType } from '../types';
20
16
  import CompetenceGoalItem from '../CompetenceGoalTab/CompetenceGoalItem';
17
+ import SubjectFilters, { SubjectFilterProps } from './components/SubjectFilters';
21
18
 
22
19
  const Wrapper = styled.div`
23
20
  margin-top: ${spacing.normal};
@@ -58,13 +55,6 @@ const PhraseText = styled.div`
58
55
  `;
59
56
  const PhraseSuggestionText = styled.div``;
60
57
 
61
- const HideOnDesktopWrapper = styled.div`
62
- display: none;
63
- ${mq.range({ until: breakpoints.desktop })} {
64
- display: block;
65
- }
66
- `;
67
-
68
58
  const GoalsLabel = styled.div`
69
59
  ${fonts.sizes('16px', '32px')};
70
60
  text-transform: uppercase;
@@ -75,11 +65,8 @@ type Props = {
75
65
  searchPhraseSuggestion?: string;
76
66
  searchPhraseSuggestionOnClick?: () => void;
77
67
  searchValue?: string;
78
- filters?: PopupFilterProps;
79
- activeFilters?: {
80
- filters: FilterProps[];
81
- onFilterRemove: (value: string, name: string) => void;
82
- };
68
+ filters: SubjectFilterProps['filters'];
69
+ activeFilters?: SubjectFilterProps['activeFilters'];
83
70
  competenceGoals?: CompetenceGoalsItemType[];
84
71
  onSearchValueChange: (value: string) => void;
85
72
  onSubmit: (event: FormEvent<HTMLFormElement>) => void;
@@ -99,9 +86,27 @@ const SearchHeader = ({
99
86
  noResults,
100
87
  t,
101
88
  }: Props & WithTranslation) => {
102
- const phraseText = noResults
103
- ? t('searchPage.noHitsShort', { query: searchPhrase })
104
- : `${t('searchPage.resultType.showingSearchPhrase')} ${searchPhrase}`;
89
+ const [isNarrowScreen, setIsNarrowScreen] = useState<boolean | undefined>();
90
+
91
+ useEffect(() => {
92
+ const isNarrowScreenMatch = window.matchMedia(`(max-width: ${breakpoints.desktop})`);
93
+ const handleChange = (e: MediaQueryListEvent | MediaQueryList) => {
94
+ setIsNarrowScreen(e.matches);
95
+ };
96
+ isNarrowScreenMatch.addEventListener('change', handleChange);
97
+ handleChange(isNarrowScreenMatch);
98
+ return () => {
99
+ isNarrowScreenMatch.removeEventListener('change', handleChange);
100
+ };
101
+ }, []);
102
+
103
+ const phraseText = noResults ? (
104
+ t('searchPage.noHitsShort', { query: searchPhrase })
105
+ ) : (
106
+ <>
107
+ {t('searchPage.resultType.showingSearchPhrase')} <b>&ldquo;{searchPhrase}&rdquo;</b>
108
+ </>
109
+ );
105
110
  const removeFilterSuggestion =
106
111
  noResults && activeFilters?.filters.length ? t('searchPage.removeFilterSuggestion') : undefined;
107
112
  return (
@@ -113,6 +118,7 @@ const SearchHeader = ({
113
118
  onSubmit={onSubmit}
114
119
  activeFilters={activeFilters}
115
120
  filters={filters}
121
+ isNarrowScreen={isNarrowScreen}
116
122
  />
117
123
  </SearchInputWrapper>
118
124
  <PhraseWrapper>
@@ -126,7 +132,7 @@ const SearchHeader = ({
126
132
  <PhraseSuggestionText>
127
133
  {t('searchPage.resultType.searchPhraseSuggestion')}{' '}
128
134
  <Button link onClick={searchPhraseSuggestionOnClick}>
129
- {searchPhraseSuggestion}
135
+ [{searchPhraseSuggestion}]
130
136
  </Button>
131
137
  </PhraseSuggestionText>
132
138
  )}
@@ -145,14 +151,8 @@ const SearchHeader = ({
145
151
  </CompetenceGoalsWrapper>
146
152
  )}
147
153
  </PhraseWrapper>
148
- {activeFilters && (
149
- <HideOnDesktopWrapper>
150
- <ActiveFilters
151
- {...activeFilters}
152
- showOnSmallScreen
153
- customElements={filters ? [<PopupFilter {...filters} />] : []}
154
- />
155
- </HideOnDesktopWrapper>
154
+ {isNarrowScreen && (
155
+ <SubjectFilters filters={filters} activeFilters={activeFilters} isNarrowScreen={isNarrowScreen} />
156
156
  )}
157
157
  </Wrapper>
158
158
  );
@@ -6,23 +6,22 @@
6
6
  *
7
7
  */
8
8
 
9
- import React, { ReactNode } from 'react';
9
+ import React from 'react';
10
10
  import styled from '@emotion/styled';
11
11
  import parse from 'html-react-parser';
12
- import { ChevronRight, Additional, Core } from '@ndla/icons/common';
12
+
13
13
  import SafeLink from '@ndla/safelink';
14
- import Button from '@ndla/button';
15
- import Modal, { ModalCloseButton } from '@ndla/modal';
14
+ import { animations, colors, fonts, spacing } from '@ndla/core';
16
15
 
17
- import { breakpoints, colors, fonts, mq, spacing } from '@ndla/core';
18
- import { useTranslation } from 'react-i18next';
19
16
  import { ContentType } from './SearchTypeResult';
20
17
  import constants from '../model';
21
- import ContentTypeBadge from '../ContentTypeBadge';
18
+ import ItemContexts, { ItemContextsType } from './components/ItemContexts';
19
+ import ItemTopicHeader from './components/ItemTopicHeader';
20
+ import ItemResourceHeader from './components/ItemResourceHeader';
22
21
 
23
22
  const { contentTypes } = constants;
24
23
 
25
- const resourceTypeColor = (type: string) => {
24
+ export const resourceTypeColor = (type: string) => {
26
25
  switch (type) {
27
26
  case contentTypes.SUBJECT_MATERIAL:
28
27
  return colors.subjectMaterial.light;
@@ -39,274 +38,187 @@ const resourceTypeColor = (type: string) => {
39
38
  case contentTypes.LEARNING_PATH:
40
39
  return colors.learningPath.light;
41
40
  default:
42
- return null;
41
+ return '';
43
42
  }
44
43
  };
45
44
 
46
45
  type ItemTypeProps = {
47
- type?: ContentType;
46
+ contentType?: ContentType;
47
+ isTopic?: boolean;
48
48
  };
49
49
 
50
- const ItemWrapper = styled.div`
51
- flex-direction: column;
52
- `;
53
-
54
- const ItemHead = styled.div<ItemTypeProps>`
55
- height: 200px;
56
- position: relative;
57
- a {
58
- box-shadow: none;
59
- }
60
- ${mq.range({ from: breakpoints.tablet })} {
61
- height: 160px;
62
- }
63
- ${mq.range({ from: breakpoints.desktop })} {
64
- height: 100px;
65
- }
66
- border: 1px solid ${(props) => props.type && `${resourceTypeColor(props.type)};`};
67
- border-bottom: 0;
68
- border-top-left-radius: 5px;
69
- border-top-right-radius: 5px;
70
- img {
71
- border-top-left-radius: 5px;
72
- border-top-right-radius: 5px;
73
- width: 100%;
74
- height: 100%;
75
- object-fit: cover;
76
- }
77
- `;
78
-
79
- const ItemIcon = styled.div<ItemTypeProps>`
80
- height: 100%;
81
- background: #ccc;
82
- border-top-left-radius: 5px;
83
- border-top-right-radius: 5px;
50
+ const Container = styled.div`
84
51
  display: flex;
85
- justify-content: center;
52
+ height: 400px;
86
53
  align-items: center;
87
- ${(props) => props.type && `background: ${resourceTypeColor(props.type)};`}
88
- `;
89
-
90
- const ItemContent = styled.div<ItemTypeProps>`
91
- border: 1px solid ${(props) => props.type && `${resourceTypeColor(props.type)};`};
92
- border-top: 0;
93
- border-bottom-left-radius: 5px;
94
- border-bottom-right-radius: 5px;
95
- padding: ${spacing.small};
54
+ justify-content: center;
96
55
  `;
97
56
 
98
- const ItemPillWrapper = styled.div`
99
- margin-top: 8px;
57
+ const ItemWrapper = styled.div`
58
+ flex-direction: column;
59
+ overflow: hidden;
100
60
  display: flex;
101
- flex-wrap: wrap;
102
- `;
103
- const ItemPill = styled.div`
104
- display: inline-block;
105
- background: ${colors.brand.greyLightest};
106
- padding: 2px 4px;
107
- border-radius: 2px;
108
- ${fonts.sizes('12px', '20px')};
109
- font-weight: ${fonts.weight.semibold};
110
- margin: 8px 4px 4px 0;
61
+ width: 100%;
62
+ height: 100%;
63
+ border: 1px solid ${colors.brand.neutral7};
64
+ border-radius: 5px;
65
+ transition: all ${animations.durations.fast} ease-in-out;
66
+ &:hover {
67
+ height: calc(100% + 4px);
68
+ width: calc(100% + 4px);
69
+ .topic-header-image {
70
+ height: calc(100% + 6px);
71
+ width: calc(100% + 6px);
72
+ }
73
+ .topic-label {
74
+ svg {
75
+ transform: scale(1.2);
76
+ }
77
+ }
78
+ .topic-label-list {
79
+ margin-left: 2px;
80
+ }
81
+ .resource-type-wrapper {
82
+ padding: 0 calc(${spacing.normal} + 2px);
83
+ .resource-icon-wrapper {
84
+ left: 19px;
85
+ svg {
86
+ transform: scale(1.2);
87
+ }
88
+ }
89
+ }
90
+ .resource-no-image {
91
+ background-color: ${colors.brand.greyLightest};
92
+ .c-content-type-badge {
93
+ width: 26px;
94
+ height: 26px;
95
+ left: 45px;
96
+ margin-left: 0;
97
+ top: 100%;
98
+ margin-top: -13px;
99
+ opacity: 1;
100
+ }
101
+ }
102
+ }
111
103
  `;
112
104
 
113
- const ItemTitle = styled.h3`
114
- font-size: 18px;
115
- line-height: 24px;
116
- margin-top: ${spacing.small};
117
- font-weight: 600;
118
- overflow-wrap: anywhere;
119
- `;
120
- const ItemText = styled.p`
121
- font-size: 15px;
122
- line-height: 20px;
123
- margin-bottom: ${spacing.small};
124
- word-break: break-word;
125
- overflow-wrap: anywhere;
126
- `;
127
- const BreadcrumbPath = styled.div`
128
- color: ${colors.text.light};
129
- font-size: 16px;
130
- line-height: 24px;
105
+ const ItemLink = styled(SafeLink)`
106
+ box-shadow: none;
107
+ color: unset;
108
+ text-decoration: none;
131
109
  display: flex;
132
- align-items: center;
133
- flex-wrap: wrap;
110
+ flex-direction: column;
111
+ position: relative;
112
+ min-height: 0;
113
+ flex: 1;
134
114
  `;
135
115
 
136
- const BreadcrumbItem = styled.span`
137
- display: inline-flex;
138
- align-items: center;
139
- flex-wrap: wrap;
140
- overflow-wrap: anywhere;
116
+ const TextWrapper = styled.div`
117
+ padding: 0 ${spacing.normal} ${spacing.small};
118
+ transition: all ${animations.durations.fast} ease-in-out;
119
+ ${ItemWrapper}:hover & {
120
+ padding-left: calc(${spacing.normal} + 2px);
121
+ padding-right: calc(${spacing.normal} + 2px);
122
+ }
141
123
  `;
142
124
 
143
- const ContextsWrapper = styled.div`
125
+ const ItemTitleWrapper = styled.div<ItemTypeProps>`
126
+ transition: all ${animations.durations.fast} ease-in-out;
144
127
  margin-top: ${spacing.small};
145
- button {
146
- ${fonts.sizes('16px', '24px')};
147
- box-shadow: none;
148
- &:hover {
149
- box-shadow: inset 0 -1px;
150
- }
151
- }
128
+ ${(props) =>
129
+ props.isTopic &&
130
+ `${ItemWrapper}:hover & {
131
+ padding-left:2px; padding-right: 2px;}`};
152
132
  `;
153
133
 
154
- const ModalHeader = styled.div`
155
- display: flex;
156
- justify-content: space-between;
157
- align-items: center;
158
- padding: ${spacing.normal} ${spacing.small};
159
- ${mq.range({ from: breakpoints.tablet })} {
160
- padding: ${spacing.normal} ${spacing.large};
134
+ const ItemTitle = styled.h3<ItemTypeProps>`
135
+ ${fonts.sizes('24px', '28px')};
136
+ color: ${colors.brand.primary};
137
+ ${(props) => props.isTopic && `margin-bottom: ${spacing.small};`};
138
+ font-weight: ${fonts.weight.semibold};
139
+ overflow-wrap: anywhere;
140
+ display: inline;
141
+ ${ItemWrapper}:hover & {
142
+ box-shadow: inset 0 -1px;
143
+ background-color: transparent;
161
144
  }
162
145
  `;
163
-
164
- const ModalHeading = styled.h2`
165
- margin: 0;
166
- ${fonts.sizes('16px', '20px')};
167
- font-weight: 600;
168
- `;
169
-
170
- const ModalContent = styled.div`
171
- padding: 0 ${spacing.small} ${spacing.normal};
172
- ${mq.range({ from: breakpoints.tablet })} {
173
- padding: 0 ${spacing.large} ${spacing.normal};
146
+ const ItemText = styled.div<ItemTypeProps>`
147
+ overflow: hidden;
148
+ ${fonts.sizes('16px', '24px')};
149
+ word-break: break-word;
150
+ overflow-wrap: anywhere;
151
+ transition: all ${animations.durations.fast} ease-in-out;
152
+ margin-top: ${spacing.small};
153
+ ${(props) =>
154
+ props.isTopic &&
155
+ `
156
+ ${fonts.sizes('18px', '28px')};
157
+ ${ItemWrapper}:hover & {
158
+ padding-left: 2px;
159
+ padding-right: 2px;
160
+ }`};
161
+ `;
162
+
163
+ const ContextWrapper = styled.div`
164
+ background: white;
165
+ z-index: 1;
166
+ padding: 0 ${spacing.normal} ${spacing.small};
167
+ transition: all ${animations.durations.fast} ease-in-out;
168
+ ${ItemWrapper}:hover & {
169
+ padding-left: calc(${spacing.normal} + 2px);
170
+ padding-right: calc(${spacing.normal} + 2px);
171
+ padding-bottom: calc(${spacing.small} + 2px);
174
172
  }
175
173
  `;
176
174
 
177
- const ContextList = styled.ul`
178
- margin: 0;
179
- padding: 0;
180
- list-style: none;
181
- `;
182
- const ContextListItem = styled.li`
183
- margin-bottom: ${spacing.normal};
184
- ${fonts.sizes('16px', '28px')};
185
- `;
186
-
187
- const IconWrapper = styled.div`
188
- margin-left: ${spacing.small};
189
- display: flex;
190
- align-items: center;
191
- `;
192
-
193
- type context = {
194
- breadcrumb: string[];
195
- url: string;
196
- isAdditional?: boolean;
197
- };
198
-
199
- export type SearchItemType = {
175
+ export type SearchItemProps = {
200
176
  id: string | number;
201
177
  title: string;
202
178
  url: string;
203
179
  ingress: string;
204
- contexts?: context[];
180
+ contexts?: ItemContextsType['contexts'];
205
181
  img?: { url: string; alt: string };
206
182
  labels?: string[];
207
- children?: ReactNode;
183
+ type?: ContentType;
208
184
  };
209
- type Props = {
210
- item: SearchItemType;
185
+ export type SearchItemType = {
186
+ item: SearchItemProps;
211
187
  type?: ContentType;
212
188
  };
213
- const SearchItem = ({ item, type }: Props) => {
214
- const { t } = useTranslation();
189
+ const SearchItem = ({ item, type }: SearchItemType) => {
215
190
  const { title, url, ingress, contexts, img = null, labels = [] } = item;
216
- const mainContext = contexts?.[0];
217
191
 
218
- const Breadcrumb = ({ breadcrumb, children }: { breadcrumb: string[]; children?: ReactNode }) => (
219
- <BreadcrumbPath>
220
- {breadcrumb.map((breadcrumbItem: string, i: number) => {
221
- return (
222
- <BreadcrumbItem key={`${breadcrumbItem}-${item.id}`}>
223
- <span>{breadcrumbItem}</span>
224
- {i !== breadcrumb.length - 1 && <ChevronRight />}
225
- </BreadcrumbItem>
226
- );
227
- })}
228
- {children}
229
- </BreadcrumbPath>
230
- );
192
+ const isTopic = type === contentTypes.TOPIC || type === contentTypes.MULTIDISCIPLINARY_TOPIC;
231
193
 
232
194
  return (
233
- <>
195
+ <Container>
234
196
  <ItemWrapper>
235
- <ItemHead type={type}>
236
- {img ? (
237
- <SafeLink to={url}>
238
- <img src={img.url} alt={img.alt} />
239
- </SafeLink>
197
+ <ItemLink to={url}>
198
+ {isTopic ? (
199
+ <ItemTopicHeader image={img} type={type}>
200
+ <ItemTitleWrapper isTopic>
201
+ <ItemTitle isTopic>{title}</ItemTitle>
202
+ </ItemTitleWrapper>
203
+ <ItemText isTopic>{parse(ingress)}</ItemText>
204
+ </ItemTopicHeader>
240
205
  ) : (
241
- <SafeLink to={url}>
242
- {type && (
243
- <ItemIcon type={type}>
244
- <ContentTypeBadge type={type} size="small" border={false} />
245
- </ItemIcon>
246
- )}
247
- </SafeLink>
248
- )}
249
- </ItemHead>
250
- <ItemContent type={type}>
251
- {labels.length > 0 && (
252
- <ItemPillWrapper>
253
- {labels.map((label) => (
254
- <ItemPill key={label}>{label}</ItemPill>
255
- ))}
256
- </ItemPillWrapper>
257
- )}
258
- <ItemTitle>
259
- <SafeLink to={url}>{title}</SafeLink>
260
- </ItemTitle>
261
- {item.children}
262
- <ItemText>{parse(ingress)}</ItemText>
263
- {mainContext && <Breadcrumb breadcrumb={mainContext.breadcrumb} />}
264
- {contexts && contexts.length > 1 && (
265
- <ContextsWrapper>
266
- <Modal
267
- activateButton={
268
- <Button link>
269
- {t('searchPage.contextModal.button', {
270
- count: contexts.length - 1,
271
- })}
272
- </Button>
273
- }
274
- animation="subtle"
275
- animationDuration={50}
276
- backgroundColor="white"
277
- size="medium">
278
- {(onClose: () => void) => (
279
- <>
280
- <ModalHeader>
281
- <ModalHeading>{t('searchPage.contextModal.heading')}</ModalHeading>
282
- <ModalCloseButton onClick={onClose} title={t('searchPage.close')} />
283
- </ModalHeader>
284
- <ModalContent>
285
- <ContextList>
286
- {contexts.map((context) => (
287
- <ContextListItem key={context.url}>
288
- <SafeLink to={context.url}>{title}</SafeLink>
289
- <Breadcrumb breadcrumb={context.breadcrumb}>
290
- <IconWrapper>
291
- {context.isAdditional ? (
292
- <Additional style={{ width: '22px', height: '22px' }} />
293
- ) : (
294
- <Core style={{ width: '22px', height: '22px' }} />
295
- )}
296
- </IconWrapper>
297
- </Breadcrumb>
298
- </ContextListItem>
299
- ))}
300
- </ContextList>
301
- </ModalContent>
302
- </>
303
- )}
304
- </Modal>
305
- </ContextsWrapper>
206
+ <>
207
+ <ItemResourceHeader labels={labels} img={img} type={type} />
208
+ <TextWrapper>
209
+ <ItemTitleWrapper>
210
+ <ItemTitle>{title}</ItemTitle>
211
+ </ItemTitleWrapper>
212
+ <ItemText>{parse(ingress)}</ItemText>
213
+ </TextWrapper>
214
+ </>
306
215
  )}
307
- </ItemContent>
216
+ <ContextWrapper>
217
+ {contexts && contexts.length > 0 && <ItemContexts contexts={contexts} id={item.id} title={item.title} />}
218
+ </ContextWrapper>
219
+ </ItemLink>
308
220
  </ItemWrapper>
309
- </>
221
+ </Container>
310
222
  );
311
223
  };
312
224