@ndla/ui 34.6.2 → 34.6.3

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 (153) hide show
  1. package/es/Article/Article.js +11 -6
  2. package/es/Aside/Aside.js +5 -2
  3. package/es/CopyParagraphButton/CopyParagraphButtonV2.js +85 -0
  4. package/es/CopyParagraphButton/index.js +2 -1
  5. package/es/Embed/AudioEmbed.js +254 -0
  6. package/es/Embed/BrightcoveEmbed.js +250 -0
  7. package/es/Embed/ConceptEmbed.js +358 -0
  8. package/es/Embed/ConceptListEmbed.js +71 -0
  9. package/es/Embed/ContentLinkEmbed.js +42 -0
  10. package/es/Embed/ExternalEmbed.js +91 -0
  11. package/es/Embed/FootnoteEmbed.js +32 -0
  12. package/es/Embed/H5pEmbed.js +87 -0
  13. package/es/Embed/IframeEmbed.js +83 -0
  14. package/es/Embed/ImageEmbed.js +322 -0
  15. package/es/Embed/RelatedContentEmbed.js +58 -0
  16. package/es/Embed/UnknownEmbed.js +27 -0
  17. package/es/Embed/conceptComponents.js +282 -0
  18. package/es/Embed/index.js +21 -0
  19. package/es/FactBox/FactBoxV2.js +90 -0
  20. package/es/FactBox/index.js +1 -0
  21. package/es/Figure/Figure.js +8 -5
  22. package/es/Figure/FigureLicenseDialogContent.js +72 -0
  23. package/es/FileList/FileListV2.js +47 -0
  24. package/es/FileList/FileV2.js +34 -0
  25. package/es/FileList/PdfFile.js +25 -0
  26. package/es/FileList/index.js +3 -0
  27. package/es/Notion/Notion.js +5 -5
  28. package/es/Notion/NotionVisualElement.js +2 -2
  29. package/es/RelatedArticleList/RelatedArticleV2.js +101 -0
  30. package/es/RelatedArticleList/index.js +2 -1
  31. package/es/Table/Table.js +95 -8
  32. package/es/all.css +1 -1
  33. package/es/index.js +5 -4
  34. package/es/locale/messages-en.js +8 -1
  35. package/es/locale/messages-nb.js +8 -1
  36. package/es/locale/messages-nn.js +8 -1
  37. package/es/locale/messages-se.js +8 -1
  38. package/es/locale/messages-sma.js +8 -1
  39. package/lib/Article/Article.d.ts +2 -1
  40. package/lib/Article/Article.js +11 -6
  41. package/lib/Aside/Aside.d.ts +2 -1
  42. package/lib/Aside/Aside.js +5 -2
  43. package/lib/CopyParagraphButton/CopyParagraphButtonV2.d.ts +14 -0
  44. package/lib/CopyParagraphButton/CopyParagraphButtonV2.js +84 -0
  45. package/lib/CopyParagraphButton/index.d.ts +2 -1
  46. package/lib/CopyParagraphButton/index.js +7 -0
  47. package/lib/Embed/AudioEmbed.d.ts +20 -0
  48. package/lib/Embed/AudioEmbed.js +252 -0
  49. package/lib/Embed/BrightcoveEmbed.d.ts +16 -0
  50. package/lib/Embed/BrightcoveEmbed.js +250 -0
  51. package/lib/Embed/ConceptEmbed.d.ts +19 -0
  52. package/lib/Embed/ConceptEmbed.js +358 -0
  53. package/lib/Embed/ConceptListEmbed.d.ts +13 -0
  54. package/lib/Embed/ConceptListEmbed.js +70 -0
  55. package/lib/Embed/ContentLinkEmbed.d.ts +14 -0
  56. package/lib/Embed/ContentLinkEmbed.js +50 -0
  57. package/lib/Embed/ExternalEmbed.d.ts +14 -0
  58. package/lib/Embed/ExternalEmbed.js +90 -0
  59. package/lib/Embed/FootnoteEmbed.d.ts +13 -0
  60. package/lib/Embed/FootnoteEmbed.js +39 -0
  61. package/lib/Embed/H5pEmbed.d.ts +14 -0
  62. package/lib/Embed/H5pEmbed.js +86 -0
  63. package/lib/Embed/IframeEmbed.d.ts +14 -0
  64. package/lib/Embed/IframeEmbed.js +91 -0
  65. package/lib/Embed/ImageEmbed.d.ts +37 -0
  66. package/lib/Embed/ImageEmbed.js +326 -0
  67. package/lib/Embed/RelatedContentEmbed.d.ts +16 -0
  68. package/lib/Embed/RelatedContentEmbed.js +64 -0
  69. package/lib/Embed/UnknownEmbed.d.ts +13 -0
  70. package/lib/Embed/UnknownEmbed.js +35 -0
  71. package/lib/Embed/conceptComponents.d.ts +32 -0
  72. package/lib/Embed/conceptComponents.js +280 -0
  73. package/lib/Embed/index.d.ts +20 -0
  74. package/lib/Embed/index.js +97 -0
  75. package/lib/FactBox/FactBoxV2.d.ts +13 -0
  76. package/lib/FactBox/FactBoxV2.js +92 -0
  77. package/lib/FactBox/index.d.ts +1 -0
  78. package/lib/FactBox/index.js +7 -0
  79. package/lib/Figure/Figure.d.ts +5 -2
  80. package/lib/Figure/Figure.js +8 -5
  81. package/lib/Figure/FigureLicenseDialogContent.d.ts +22 -0
  82. package/lib/Figure/FigureLicenseDialogContent.js +71 -0
  83. package/lib/FileList/FileListV2.d.ts +13 -0
  84. package/lib/FileList/FileListV2.js +46 -0
  85. package/lib/FileList/FileV2.d.ts +16 -0
  86. package/lib/FileList/FileV2.js +42 -0
  87. package/lib/FileList/PdfFile.d.ts +13 -0
  88. package/lib/FileList/PdfFile.js +31 -0
  89. package/lib/FileList/index.d.ts +3 -0
  90. package/lib/FileList/index.js +21 -0
  91. package/lib/Notion/Notion.js +5 -5
  92. package/lib/Notion/NotionVisualElement.d.ts +1 -1
  93. package/lib/Notion/NotionVisualElement.js +2 -2
  94. package/lib/RelatedArticleList/RelatedArticleV2.d.ts +25 -0
  95. package/lib/RelatedArticleList/RelatedArticleV2.js +101 -0
  96. package/lib/RelatedArticleList/index.d.ts +2 -1
  97. package/lib/RelatedArticleList/index.js +7 -0
  98. package/lib/Table/Table.js +98 -8
  99. package/lib/all.css +1 -1
  100. package/lib/index.d.ts +5 -4
  101. package/lib/index.js +117 -2
  102. package/lib/locale/messages-en.d.ts +7 -0
  103. package/lib/locale/messages-en.js +8 -1
  104. package/lib/locale/messages-nb.d.ts +7 -0
  105. package/lib/locale/messages-nb.js +8 -1
  106. package/lib/locale/messages-nn.d.ts +7 -0
  107. package/lib/locale/messages-nn.js +8 -1
  108. package/lib/locale/messages-se.d.ts +7 -0
  109. package/lib/locale/messages-se.js +8 -1
  110. package/lib/locale/messages-sma.d.ts +7 -0
  111. package/lib/locale/messages-sma.js +8 -1
  112. package/lib/types.d.ts +1 -1
  113. package/package.json +16 -12
  114. package/src/Article/Article.tsx +8 -3
  115. package/src/Aside/Aside.tsx +9 -1
  116. package/src/Aside/component.aside.scss +3 -0
  117. package/src/CopyParagraphButton/CopyParagraphButtonV2.tsx +84 -0
  118. package/src/CopyParagraphButton/index.tsx +2 -1
  119. package/src/Embed/AudioEmbed.tsx +249 -0
  120. package/src/Embed/BrightcoveEmbed.tsx +203 -0
  121. package/src/Embed/ConceptEmbed.tsx +408 -0
  122. package/src/Embed/ConceptListEmbed.tsx +64 -0
  123. package/src/Embed/ContentLinkEmbed.tsx +41 -0
  124. package/src/Embed/ExternalEmbed.tsx +80 -0
  125. package/src/Embed/FootnoteEmbed.tsx +30 -0
  126. package/src/Embed/H5pEmbed.tsx +74 -0
  127. package/src/Embed/IframeEmbed.tsx +84 -0
  128. package/src/Embed/ImageEmbed.tsx +314 -0
  129. package/src/Embed/RelatedContentEmbed.tsx +62 -0
  130. package/src/Embed/UnknownEmbed.tsx +27 -0
  131. package/src/Embed/conceptComponents.tsx +393 -0
  132. package/src/Embed/index.ts +21 -0
  133. package/src/FactBox/FactBoxV2.tsx +56 -0
  134. package/src/FactBox/index.ts +2 -0
  135. package/src/Figure/Figure.tsx +28 -15
  136. package/src/Figure/FigureLicenseDialogContent.tsx +80 -0
  137. package/src/Figure/component.figure.scss +0 -1
  138. package/src/FileList/FileListV2.tsx +58 -0
  139. package/src/FileList/FileV2.tsx +35 -0
  140. package/src/FileList/PdfFile.tsx +25 -0
  141. package/src/FileList/index.ts +3 -0
  142. package/src/Notion/Notion.tsx +0 -1
  143. package/src/Notion/NotionVisualElement.tsx +1 -1
  144. package/src/RelatedArticleList/RelatedArticleV2.tsx +84 -0
  145. package/src/RelatedArticleList/index.ts +2 -1
  146. package/src/Table/Table.tsx +77 -4
  147. package/src/index.ts +19 -4
  148. package/src/locale/messages-en.ts +7 -0
  149. package/src/locale/messages-nb.ts +7 -0
  150. package/src/locale/messages-nn.ts +7 -0
  151. package/src/locale/messages-se.ts +7 -0
  152. package/src/locale/messages-sma.ts +7 -0
  153. package/src/types.ts +1 -1
@@ -0,0 +1,393 @@
1
+ /**
2
+ * Copyright (c) 2023-present, NDLA.
3
+ *
4
+ * This source code is licensed under the GPLv3 license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ import { forwardRef, ReactNode, RefAttributes, useEffect, useRef } from 'react';
10
+ import { ConceptVisualElementMeta } from '@ndla/types-embed';
11
+ import { useTranslation } from 'react-i18next';
12
+ import { Root, Item, Header, Trigger, Content } from '@radix-ui/react-accordion';
13
+ import { getGroupedContributorDescriptionList, getLicenseByAbbreviation, getLicenseCredits } from '@ndla/licenses';
14
+ import { css } from '@emotion/react';
15
+ import { ButtonV2 } from '@ndla/button';
16
+ import { breakpoints, colors, fonts, misc, mq, spacing } from '@ndla/core';
17
+ import { ChevronDown } from '@ndla/icons/common';
18
+ import { parseMarkdown } from '@ndla/util';
19
+ import styled from '@emotion/styled';
20
+ import { NotionDialogContent, NotionDialogLicenses, NotionDialogText } from '@ndla/notion';
21
+ import { Figure, FigureCaption } from '../Figure';
22
+ import { NotionVisualElementType } from '../Notion';
23
+ import { classLicenses, FigureLicenseByline, FigureLicenseCta } from '../Figure/FigureLicense';
24
+ import { Copyright } from '../types';
25
+ import { ImageLicenseButtons } from './ImageEmbed';
26
+
27
+ export interface ConceptNotionData {
28
+ title: string;
29
+ content?: string;
30
+ articlePath?: string;
31
+ metaImage?: {
32
+ url?: string;
33
+ alt?: string;
34
+ };
35
+ copyright?: Copyright;
36
+ source?: string;
37
+ visualElement?: ConceptVisualElementMeta;
38
+ }
39
+
40
+ interface ConceptNotionProps extends RefAttributes<HTMLDivElement>, ConceptNotionData {
41
+ className?: string;
42
+ closeButton?: ReactNode;
43
+ previewAlt?: boolean;
44
+ inPopover?: boolean;
45
+ tags?: string[];
46
+ subjects?: string[];
47
+ }
48
+
49
+ const getConceptVisualElement = (visualElement: ConceptVisualElementMeta): NotionVisualElementType | undefined => {
50
+ if (visualElement.status === 'error') {
51
+ return undefined;
52
+ }
53
+
54
+ if (visualElement.resource === 'image') {
55
+ return {
56
+ resource: visualElement.resource,
57
+ title: visualElement.data.title.title,
58
+ copyright: visualElement.data.copyright,
59
+ image: { src: visualElement.data.imageUrl, alt: visualElement.data.alttext.alttext },
60
+ url: visualElement.data.imageUrl,
61
+ };
62
+ } else if (visualElement.resource === 'brightcove') {
63
+ return {
64
+ resource: visualElement.resource,
65
+ title: visualElement.data.name,
66
+ url: `https://players.brightcove.net/${visualElement.embedData.account}/${visualElement.embedData.player}_default/index.html?videoId=${visualElement.embedData.videoid}`,
67
+ copyright: visualElement.data.copyright,
68
+ };
69
+ } else if (visualElement.resource === 'external' || visualElement.resource === 'iframe') {
70
+ return {
71
+ resource: visualElement.resource,
72
+ url: visualElement.embedData.url,
73
+ title: visualElement.embedData.url,
74
+ };
75
+ } else {
76
+ return {
77
+ resource: visualElement.resource,
78
+ url: visualElement.data.h5pUrl,
79
+ title: visualElement.embedData.title,
80
+ };
81
+ }
82
+ };
83
+
84
+ const notionContentCss = css`
85
+ @keyframes animateIn {
86
+ 0% {
87
+ opacity: 0;
88
+ transform: translate3d(0, -13px, 0);
89
+ }
90
+ 100% {
91
+ opacity: 1;
92
+ transform: translate3d(0, 0, 0);
93
+ }
94
+ }
95
+
96
+ animation-name: animateIn;
97
+ animation-duration: 300ms;
98
+ padding: ${spacing.normal};
99
+ background-color: white;
100
+ z-index: 1;
101
+ box-shadow: 0 0 30px rgba(0, 0, 0, 0.2);
102
+ ${mq.range({ from: breakpoints.tablet })} {
103
+ width: 500px;
104
+ }
105
+ ${mq.range({ from: breakpoints.desktop })} {
106
+ width: 720px;
107
+ }
108
+
109
+ ${mq.range({ until: breakpoints.tablet })} {
110
+ padding: ${spacing.small};
111
+ z-index: 9999;
112
+ height: 100%;
113
+ width: 100%;
114
+ overflow: auto;
115
+ }
116
+ `;
117
+
118
+ const StyledIframe = styled.iframe<{ type?: string }>`
119
+ height: auto;
120
+ min-height: ${(p) => p.type === 'video' && '400px'};
121
+ `;
122
+
123
+ const NotionHeader = styled.div`
124
+ display: flex;
125
+ align-items: center;
126
+ justify-content: flex-end;
127
+ border-bottom: 2px solid ${colors.brand.tertiary};
128
+ padding-bottom: ${spacing.small};
129
+ h1 {
130
+ flex-grow: 1;
131
+ margin: 0;
132
+ font-weight: ${fonts.weight.bold};
133
+ ${fonts.sizes('22px', 1.2)};
134
+ }
135
+ small {
136
+ padding-left: ${spacing.small};
137
+ margin-left: ${spacing.xsmall};
138
+ border-left: 1px solid ${colors.brand.greyLight};
139
+ ${fonts.sizes('20px', 1.2)};
140
+ font-weight: ${fonts.weight.normal};
141
+ }
142
+ `;
143
+
144
+ const StyledAccordionContent = styled(Content)`
145
+ background-color: ${colors.brand.lighter};
146
+ padding: ${spacing.small};
147
+ border-radius: ${misc.borderRadius};
148
+ overflow: hidden;
149
+ &[data-state='open'] {
150
+ animation: slideDown 300ms ease-out;
151
+ }
152
+ &[data-state='closed'] {
153
+ animation: slideUp 300ms ease-out;
154
+ }
155
+ @keyframes slideDown {
156
+ from {
157
+ height: 0;
158
+ }
159
+ to {
160
+ height: var(--radix-accordion-content-height);
161
+ }
162
+ }
163
+ @keyframes slideUp {
164
+ from {
165
+ height: var(--radix-accordion-content-height);
166
+ }
167
+ to {
168
+ height: 0;
169
+ }
170
+ }
171
+ `;
172
+
173
+ const StyledRoot = styled(Root)`
174
+ border-bottom: 1px solid ${colors.brand.greyLight};
175
+ `;
176
+
177
+ const StyledFigure = styled(Figure)`
178
+ && {
179
+ margin: ${spacing.normal} 0;
180
+ }
181
+ padding-bottom: 0;
182
+ `;
183
+
184
+ const StyledFigureCaption = styled(FigureCaption)`
185
+ border-bottom: 0;
186
+
187
+ h3 {
188
+ margin: 0;
189
+ }
190
+ `;
191
+
192
+ const StyledSpan = styled.span`
193
+ font-style: italic;
194
+ color: grey;
195
+ `;
196
+
197
+ const StyledAccordionTrigger = styled(ButtonV2)`
198
+ color: ${colors.brand.primary};
199
+ border-color: ${colors.brand.primary};
200
+ &[data-state='open'] {
201
+ svg {
202
+ transform: rotate(180deg);
203
+ }
204
+ }
205
+ svg {
206
+ transition: transform 300ms;
207
+ }
208
+ `;
209
+
210
+ const ListWrapper = styled.div`
211
+ display: flex;
212
+ gap: ${spacing.small};
213
+ align-items: center;
214
+ `;
215
+
216
+ const StyledList = styled.ul`
217
+ display: flex;
218
+ gap: ${spacing.small};
219
+ align-items: center;
220
+ list-style: none;
221
+ > li {
222
+ margin: 0;
223
+ font-family: ${fonts.sans};
224
+ font-weight: ${fonts.weight.semibold};
225
+ border-radius: ${misc.borderRadius};
226
+ background-color: ${colors.brand.greyLightest};
227
+ ${fonts.sizes('12px', 1.2)};
228
+ padding: ${spacing.xxsmall};
229
+ }
230
+ `;
231
+
232
+ export const ConceptNotionV2 = forwardRef<HTMLDivElement, ConceptNotionProps>(
233
+ (
234
+ {
235
+ visualElement,
236
+ articlePath,
237
+ title,
238
+ content,
239
+ source,
240
+ copyright,
241
+ closeButton,
242
+ inPopover,
243
+ previewAlt,
244
+ tags,
245
+ subjects,
246
+ ...rest
247
+ },
248
+ ref,
249
+ ) => {
250
+ const { t, i18n } = useTranslation();
251
+ const iframeRef = useRef<HTMLIFrameElement>(null);
252
+
253
+ useEffect(() => {
254
+ const iframe = iframeRef.current;
255
+ if (iframe) {
256
+ const [width, height] = [parseInt(iframe.width), parseInt(iframe.height)];
257
+ iframe.style.aspectRatio = `${width ? width : 16}/${height ? height : 9}`;
258
+ }
259
+ }, []);
260
+
261
+ const licenseCredits = getLicenseCredits(copyright);
262
+ const { creators, rightsholders, processors } = licenseCredits;
263
+ const authors = creators.length || rightsholders.length ? creators.concat(rightsholders) : processors;
264
+
265
+ const visualElementType =
266
+ visualElement?.embedData.resource === 'brightcove' ? 'video' : visualElement?.embedData.resource;
267
+
268
+ const notionVisualElement = visualElement ? getConceptVisualElement(visualElement) : undefined;
269
+ const visualElementLicense = getLicenseByAbbreviation(
270
+ notionVisualElement?.copyright?.license?.license ?? '',
271
+ i18n.language,
272
+ );
273
+ const visualElementLicenseCredits = getLicenseCredits(notionVisualElement?.copyright);
274
+ const visualElementAuthors =
275
+ visualElementLicenseCredits.creators.length || visualElementLicenseCredits.rightsholders.length
276
+ ? visualElementLicenseCredits.creators.concat(visualElementLicenseCredits.rightsholders)
277
+ : visualElementLicenseCredits.processors;
278
+ const visualElementGroupedAuthors = getGroupedContributorDescriptionList(
279
+ visualElementLicenseCredits,
280
+ i18n.language,
281
+ ).map((item) => ({
282
+ name: item.description,
283
+ type: item.label,
284
+ }));
285
+ return (
286
+ <div css={inPopover ? notionContentCss : undefined} {...rest} ref={ref}>
287
+ <NotionHeader>
288
+ <h1>
289
+ {title} {<small>{t('searchPage.resultType.notionsHeading')}</small>}
290
+ </h1>
291
+ {closeButton}
292
+ </NotionHeader>
293
+ <NotionDialogContent>
294
+ {notionVisualElement && (
295
+ <StyledFigure resizeIframe type={'full-column'}>
296
+ {notionVisualElement.image?.src ? (
297
+ <img src={notionVisualElement.image.src} alt={notionVisualElement.image.alt} />
298
+ ) : (
299
+ <StyledIframe
300
+ ref={iframeRef}
301
+ allowFullScreen
302
+ type={visualElementType}
303
+ src={notionVisualElement?.url}
304
+ title={notionVisualElement?.title}
305
+ />
306
+ )}
307
+ {previewAlt && visualElement?.resource === 'image' ? (
308
+ <StyledSpan>{`Alt: ${visualElement.embedData.alt}`}</StyledSpan>
309
+ ) : null}
310
+ {visualElementLicense && (
311
+ <StyledRoot type="single" collapsible>
312
+ <Item value="licenseInfo">
313
+ <StyledFigureCaption
314
+ figureId={''}
315
+ id={''}
316
+ modalButton={<></>}
317
+ reuseLabel={t('reuse')}
318
+ authors={visualElementAuthors}
319
+ licenseRights={visualElementLicense.rights}>
320
+ {visualElementLicense.abbreviation && (
321
+ <Header>
322
+ <Trigger asChild>
323
+ <StyledAccordionTrigger variant="outline" shape="pill" size="small" colorTheme="lighter">
324
+ {t('license.info')}
325
+ <ChevronDown />
326
+ </StyledAccordionTrigger>
327
+ </Trigger>
328
+ </Header>
329
+ )}
330
+ </StyledFigureCaption>
331
+ <StyledAccordionContent>
332
+ <div {...classLicenses()}>
333
+ <h1 {...classLicenses('title')}>{t(`license.${visualElementType}.rules`)}</h1>
334
+ <FigureLicenseByline
335
+ license={visualElementLicense}
336
+ messages={{
337
+ learnAboutLicenses: t('license.learnMore'),
338
+ }}
339
+ locale={i18n.language}
340
+ />
341
+ <FigureLicenseCta
342
+ authors={visualElementGroupedAuthors}
343
+ title={notionVisualElement?.title}
344
+ origin={notionVisualElement?.copyright?.origin}
345
+ messages={{ source: t('source'), title: t('title') }}>
346
+ {visualElementType === 'image' ? (
347
+ <ImageLicenseButtons
348
+ imageUrl={notionVisualElement.image?.src ?? ''}
349
+ title={notionVisualElement.title}
350
+ copyright={notionVisualElement.copyright}
351
+ articlePath={articlePath}
352
+ />
353
+ ) : null}
354
+ </FigureLicenseCta>
355
+ </div>
356
+ </StyledAccordionContent>
357
+ </Item>
358
+ </StyledRoot>
359
+ )}
360
+ </StyledFigure>
361
+ )}
362
+ <NotionDialogText>{parseMarkdown(content ?? '', 'body')}</NotionDialogText>
363
+ </NotionDialogContent>
364
+ {tags && (
365
+ <ListWrapper>
366
+ {`${t('notions.tags')}:`}
367
+ <StyledList>
368
+ {tags.map((tag, index) => (
369
+ <li key={index}>{tag}</li>
370
+ ))}
371
+ </StyledList>
372
+ </ListWrapper>
373
+ )}
374
+ {subjects && (
375
+ <ListWrapper>
376
+ {`${t('notions.usedIn')}:`}
377
+ <StyledList>
378
+ {subjects.map((subject, index) => (
379
+ <li key={index}>{subject}</li>
380
+ ))}
381
+ </StyledList>
382
+ </ListWrapper>
383
+ )}
384
+
385
+ <NotionDialogLicenses
386
+ authors={authors.map((a) => a.name)}
387
+ license={copyright?.license?.license ?? ''}
388
+ source={parseMarkdown(source ?? '', 'body')}
389
+ />
390
+ </div>
391
+ );
392
+ },
393
+ );
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Copyright (c) 2023-present, NDLA.
3
+ *
4
+ * This source code is licensed under the GPLv3 license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ export { default as ImageEmbed } from './ImageEmbed';
10
+ export { default as AudioEmbed } from './AudioEmbed';
11
+ export { default as H5pEmbed } from './H5pEmbed';
12
+ export { default as ExternalEmbed } from './ExternalEmbed';
13
+ export { default as IframeEmbed } from './IframeEmbed';
14
+ export { default as FootnoteEmbed } from './FootnoteEmbed';
15
+ export { default as BrightcoveEmbed } from './BrightcoveEmbed';
16
+ export { default as ContentLinkEmbed } from './ContentLinkEmbed';
17
+ export { default as RelatedContentEmbed } from './RelatedContentEmbed';
18
+ export { default as ConceptEmbed } from './ConceptEmbed';
19
+ export { ConceptNotionV2 } from './conceptComponents';
20
+ export { default as ConceptListEmbed } from './ConceptListEmbed';
21
+ export { default as UnknownEmbed } from './UnknownEmbed';
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Copyright (c) 2016-present, NDLA.
3
+ *
4
+ * This source code is licensed under the GPLv3 license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ import React, { ReactNode, useState } from 'react';
10
+ import BEMHelper from 'react-bem-helper';
11
+ import { useTranslation } from 'react-i18next';
12
+ import styled from '@emotion/styled';
13
+ import { IconButtonV2 } from '@ndla/button';
14
+ import { ChevronDown, ChevronUp } from '@ndla/icons/common';
15
+
16
+ const classes = new BEMHelper({
17
+ name: 'factbox',
18
+ prefix: 'c-',
19
+ });
20
+
21
+ interface Props {
22
+ children?: ReactNode;
23
+ }
24
+
25
+ const StyledAside = styled.aside`
26
+ display: flex;
27
+ flex-direction: column;
28
+ align-items: center;
29
+ `;
30
+
31
+ const StyledDiv = styled.div`
32
+ width: 100%;
33
+ `;
34
+
35
+ const StyledIconButton = styled(IconButtonV2)`
36
+ margin-top: -20px;
37
+ z-index: 1;
38
+ `;
39
+
40
+ const FactBox = ({ children }: Props) => {
41
+ const { t } = useTranslation();
42
+ const [isOpen, setIsOpen] = useState(false);
43
+
44
+ const additional = isOpen ? 'expanded' : '';
45
+
46
+ return (
47
+ <StyledAside {...classes(undefined, undefined, additional)}>
48
+ <StyledDiv {...classes('content')}>{children}</StyledDiv>
49
+ <StyledIconButton onClick={() => setIsOpen((p) => !p)} aria-label={t(`factbox.${isOpen ? 'close' : 'open'}`)}>
50
+ {isOpen ? <ChevronUp /> : <ChevronDown />}
51
+ </StyledIconButton>
52
+ </StyledAside>
53
+ );
54
+ };
55
+
56
+ export default FactBox;
@@ -1,3 +1,5 @@
1
1
  import FactBox from './FactBox';
2
2
 
3
+ export { default as FactBoxV2 } from './FactBoxV2';
4
+
3
5
  export default FactBox;
@@ -25,6 +25,7 @@ const classes = new BEMHelper({
25
25
  export const FigureCaption = ({
26
26
  figureId,
27
27
  id,
28
+ modalButton,
28
29
  children,
29
30
  caption,
30
31
  authors,
@@ -36,9 +37,11 @@ export const FigureCaption = ({
36
37
  hasLinkedVideo,
37
38
  hideIconsAndAuthors,
38
39
  linkedVideoMessages,
40
+ linkedVideoButton,
41
+ className,
39
42
  }: FigureCaptionProps) => {
40
43
  return (
41
- <figcaption {...classes('caption', hideFigcaption && !isMobile ? 'hidden-caption' : undefined)}>
44
+ <figcaption {...classes('caption', hideFigcaption && !isMobile ? 'hidden-caption' : undefined, className)}>
42
45
  {caption ? <div {...classes('info')}>{parseMarkdown(caption)}</div> : null}
43
46
  <footer {...classes('byline')}>
44
47
  <div {...classes('byline-licenselist')}>
@@ -48,26 +51,33 @@ export const FigureCaption = ({
48
51
  <span {...classes('byline-authors')}>{authors?.map((author) => author.name).join(', ')}</span>
49
52
  )}
50
53
  <div>
51
- <Button
52
- borderShape="rounded"
53
- outline
54
- size="small"
55
- type="button"
56
- data-dialog-trigger-id={id}
57
- data-dialog-source-id={figureId}>
58
- {reuseLabel}
59
- </Button>
60
- {hasLinkedVideo && (
54
+ {modalButton ? (
55
+ modalButton
56
+ ) : (
61
57
  <Button
62
58
  borderShape="rounded"
63
59
  outline
64
60
  size="small"
65
61
  type="button"
66
- {...classes('toggleAlternativeVideo')}>
67
- <span className="original">{linkedVideoMessages?.alternative}</span>
68
- <span className="alternative hidden">{linkedVideoMessages?.original}</span>
62
+ data-dialog-trigger-id={id}
63
+ data-dialog-source-id={figureId}>
64
+ {reuseLabel}
69
65
  </Button>
70
66
  )}
67
+ {hasLinkedVideo &&
68
+ (linkedVideoButton ? (
69
+ linkedVideoButton
70
+ ) : (
71
+ <Button
72
+ borderShape="rounded"
73
+ outline
74
+ size="small"
75
+ type="button"
76
+ {...classes('toggleAlternativeVideo')}>
77
+ <span className="original">{linkedVideoMessages?.alternative}</span>
78
+ <span className="alternative hidden">{linkedVideoMessages?.original}</span>
79
+ </Button>
80
+ ))}
71
81
  </div>
72
82
  {children}
73
83
  </div>
@@ -103,12 +113,15 @@ export interface FigureLicense {
103
113
  }
104
114
 
105
115
  interface FigureCaptionProps {
116
+ className?: string;
106
117
  figureId: string;
107
118
  id?: string;
108
119
  caption?: string;
109
- reuseLabel: string;
120
+ reuseLabel?: string;
110
121
  licenseRights: string[];
111
122
  children?: ReactNode;
123
+ modalButton?: ReactNode;
124
+ linkedVideoButton?: ReactNode;
112
125
  authors?: { name: string }[];
113
126
  link?: {
114
127
  url: string;
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Copyright (c) 2023-present, NDLA.
3
+ *
4
+ * This source code is licensed under the GPLv3 license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ import { useTranslation } from 'react-i18next';
10
+ import styled from '@emotion/styled';
11
+ import { colors, fonts, spacing } from '@ndla/core';
12
+ import { ButtonV2 } from '@ndla/button';
13
+ import { ModalBody } from '@ndla/modal';
14
+ import { ReactNode } from 'react';
15
+ import { Contributor } from '../types';
16
+ import { FigureLicense } from './Figure';
17
+ import { classLicenses, FigureLicenseByline, FigureLicenseCta } from './FigureLicense';
18
+
19
+ interface Props {
20
+ children?: ReactNode;
21
+ origin?: string;
22
+ authors?: Contributor[];
23
+ onClose: () => void;
24
+ type: 'image' | 'text' | 'audio' | 'podcast' | 'video' | 'h5p' | 'concept' | 'files';
25
+ title?: string;
26
+ license: FigureLicense;
27
+ locale: string;
28
+ }
29
+
30
+ const StyledModalBody = styled(ModalBody)`
31
+ background-color: ${colors.brand.light};
32
+ flex: 1;
33
+ `;
34
+
35
+ const StyledH1 = styled.h1`
36
+ color: ${colors.brand.primary};
37
+ ${fonts.sizes('22px', '22px')};
38
+ margin: 0;
39
+ flex-grow: 1;
40
+ `;
41
+
42
+ const StyledHeader = styled.div`
43
+ display: flex;
44
+ justify-content: flex-end;
45
+ padding-bottom: ${spacing.normal};
46
+ `;
47
+
48
+ export const FigureLicenseDialogContent = ({
49
+ children,
50
+ authors,
51
+ origin,
52
+ title,
53
+ locale,
54
+ license,
55
+ onClose,
56
+ type,
57
+ }: Props) => {
58
+ const { t } = useTranslation();
59
+ const messages = {
60
+ title: t('title'),
61
+ source: t('source'),
62
+ learnAboutLicenses: t('license.learnMore'),
63
+ };
64
+ return (
65
+ <StyledModalBody>
66
+ <div {...classLicenses()}>
67
+ <StyledHeader>
68
+ <StyledH1 id="license-dialog-rules-heading">{t(`license.${type}.rules`)}</StyledH1>
69
+ <ButtonV2 variant="link" onClick={onClose}>
70
+ {t('close')}
71
+ </ButtonV2>
72
+ </StyledHeader>
73
+ <FigureLicenseByline license={license} messages={messages} locale={locale} />
74
+ <FigureLicenseCta authors={authors} title={title} origin={origin} messages={messages}>
75
+ {children}
76
+ </FigureLicenseCta>
77
+ </div>
78
+ </StyledModalBody>
79
+ );
80
+ };
@@ -167,7 +167,6 @@
167
167
  .c-figure__fullscreen-btn {
168
168
  cursor: pointer;
169
169
  position: absolute;
170
- z-index: 1;
171
170
  padding: 0;
172
171
  bottom: 8px;
173
172
  right: 8px;