@ndla/ui 36.0.2 → 37.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.
- package/es/Article/Article.js +7 -13
- package/es/Article/ArticleByline.js +79 -123
- package/es/Article/ArticleFootNotes.js +16 -11
- package/es/AudioPlayer/AudioPlayer.js +33 -35
- package/es/AudioPlayer/initAudioPlayers.js +6 -1
- package/es/Embed/AudioEmbed.js +44 -188
- package/es/Embed/BrightcoveEmbed.js +26 -122
- package/es/Embed/ConceptEmbed.js +53 -75
- package/es/Embed/EmbedErrorPlaceholder.js +41 -0
- package/es/Embed/ExternalEmbed.js +5 -12
- package/es/Embed/H5pEmbed.js +4 -14
- package/es/Embed/IframeEmbed.js +4 -4
- package/es/Embed/ImageEmbed.js +41 -153
- package/es/Embed/conceptComponents.js +62 -228
- package/es/Embed/types.js +1 -0
- package/es/KeyFigure/KeyFigure.js +57 -0
- package/es/{KeyPerformanceIndicator → KeyFigure}/index.js +1 -1
- package/es/LicenseByline/EmbedByline.js +33 -8
- package/es/LicenseByline/LicenseDescription.js +16 -14
- package/es/List/OrderedList.js +48 -0
- package/es/List/UnOrderedList.js +36 -0
- package/es/List/index.js +10 -0
- package/es/Navigation/NavigationBox.js +41 -48
- package/es/Navigation/NavigationHeading.js +18 -29
- package/es/Notion/Notion.js +5 -5
- package/es/Resource/resourceComponents.js +12 -11
- package/es/Typography/Heading.js +38 -0
- package/es/Typography/index.js +9 -0
- package/es/all.css +1 -1
- package/es/index.js +4 -2
- package/es/locale/messages-en.js +6 -2
- package/es/locale/messages-nb.js +6 -2
- package/es/locale/messages-nn.js +6 -2
- package/es/locale/messages-se.js +6 -2
- package/es/locale/messages-sma.js +6 -2
- package/es/model/ContentType.js +7 -1
- package/lib/Article/Article.d.ts +1 -3
- package/lib/Article/Article.js +7 -13
- package/lib/Article/ArticleByline.d.ts +3 -5
- package/lib/Article/ArticleByline.js +83 -126
- package/lib/Article/ArticleFootNotes.js +16 -11
- package/lib/AudioPlayer/AudioPlayer.d.ts +1 -2
- package/lib/AudioPlayer/AudioPlayer.js +33 -36
- package/lib/AudioPlayer/initAudioPlayers.d.ts +1 -0
- package/lib/AudioPlayer/initAudioPlayers.js +9 -3
- package/lib/Embed/AudioEmbed.d.ts +3 -2
- package/lib/Embed/AudioEmbed.js +53 -192
- package/lib/Embed/BrightcoveEmbed.d.ts +3 -1
- package/lib/Embed/BrightcoveEmbed.js +26 -121
- package/lib/Embed/ConceptEmbed.d.ts +7 -2
- package/lib/Embed/ConceptEmbed.js +51 -73
- package/lib/Embed/EmbedErrorPlaceholder.d.ts +17 -0
- package/lib/Embed/EmbedErrorPlaceholder.js +48 -0
- package/lib/Embed/ExternalEmbed.js +5 -11
- package/lib/Embed/H5pEmbed.js +4 -13
- package/lib/Embed/IframeEmbed.d.ts +2 -2
- package/lib/Embed/IframeEmbed.js +4 -4
- package/lib/Embed/ImageEmbed.d.ts +3 -10
- package/lib/Embed/ImageEmbed.js +48 -161
- package/lib/Embed/conceptComponents.d.ts +4 -2
- package/lib/Embed/conceptComponents.js +67 -231
- package/lib/Embed/index.d.ts +1 -0
- package/lib/Embed/types.d.ts +14 -0
- package/lib/Embed/types.js +5 -0
- package/lib/KeyFigure/KeyFigure.d.ts +10 -0
- package/lib/KeyFigure/KeyFigure.js +62 -0
- package/lib/KeyFigure/index.d.ts +1 -0
- package/lib/KeyFigure/index.js +13 -0
- package/lib/LicenseByline/EmbedByline.d.ts +10 -2
- package/lib/LicenseByline/EmbedByline.js +32 -7
- package/lib/LicenseByline/LicenseDescription.d.ts +3 -1
- package/lib/LicenseByline/LicenseDescription.js +14 -13
- package/lib/List/OrderedList.d.ts +15 -0
- package/lib/List/OrderedList.js +56 -0
- package/lib/List/UnOrderedList.d.ts +10 -0
- package/lib/List/UnOrderedList.js +43 -0
- package/lib/List/index.d.ts +9 -0
- package/lib/List/index.js +20 -0
- package/lib/Navigation/NavigationBox.js +40 -47
- package/lib/Navigation/NavigationHeading.js +17 -28
- package/lib/Notion/Notion.js +5 -5
- package/lib/Resource/resourceComponents.js +12 -11
- package/lib/Typography/Heading.d.ts +26 -0
- package/lib/Typography/Heading.js +45 -0
- package/lib/Typography/index.d.ts +8 -0
- package/lib/Typography/index.js +13 -0
- package/lib/all.css +1 -1
- package/lib/index.d.ts +4 -1
- package/lib/index.js +23 -3
- package/lib/locale/messages-en.d.ts +4 -0
- package/lib/locale/messages-en.js +6 -2
- package/lib/locale/messages-nb.d.ts +4 -0
- package/lib/locale/messages-nb.js +6 -2
- package/lib/locale/messages-nn.d.ts +4 -0
- package/lib/locale/messages-nn.js +6 -2
- package/lib/locale/messages-se.d.ts +4 -0
- package/lib/locale/messages-se.js +6 -2
- package/lib/locale/messages-sma.d.ts +4 -0
- package/lib/locale/messages-sma.js +6 -2
- package/lib/model/ContentType.d.ts +1 -0
- package/lib/model/ContentType.js +9 -2
- package/package.json +15 -15
- package/src/Article/Article.tsx +1 -8
- package/src/Article/ArticleByline.tsx +78 -127
- package/src/Article/ArticleFootNotes.tsx +33 -10
- package/src/Article/component.article.scss +1 -52
- package/src/Article/component.footnotes.scss +2 -2
- package/src/Aside/component.aside.scss +3 -3
- package/src/AudioPlayer/AudioPlayer.tsx +11 -24
- package/src/AudioPlayer/initAudioPlayers.tsx +7 -2
- package/src/Dialog/component.dialog.scss +4 -5
- package/src/Embed/AudioEmbed.stories.tsx +5 -3
- package/src/Embed/AudioEmbed.tsx +45 -192
- package/src/Embed/BrightcoveEmbed.stories.tsx +5 -1
- package/src/Embed/BrightcoveEmbed.tsx +19 -94
- package/src/Embed/ConceptEmbed.stories.tsx +5 -0
- package/src/Embed/ConceptEmbed.tsx +43 -54
- package/src/Embed/EmbedErrorPlaceholder.tsx +59 -0
- package/src/Embed/ExternalEmbed.stories.tsx +86 -0
- package/src/Embed/ExternalEmbed.tsx +3 -8
- package/src/Embed/H5pEmbed.stories.tsx +92 -0
- package/src/Embed/H5pEmbed.tsx +2 -10
- package/src/Embed/IframeEmbed.stories.tsx +130 -0
- package/src/Embed/IframeEmbed.tsx +3 -3
- package/src/Embed/ImageEmbed.stories.tsx +3 -1
- package/src/Embed/ImageEmbed.tsx +21 -116
- package/src/Embed/conceptComponents.tsx +67 -257
- package/src/Embed/index.ts +1 -0
- package/src/Embed/types.ts +12 -0
- package/src/FactBox/component.factbox.scss +3 -3
- package/src/Figure/component.figure-license.scss +4 -4
- package/src/Figure/component.figure.scss +1 -1
- package/src/KeyFigure/KeyFigure.stories.tsx +36 -0
- package/src/{KeyPerformanceIndicator/KeyPerformanceIndicator.tsx → KeyFigure/KeyFigure.tsx} +9 -7
- package/src/{KeyPerformanceIndicator → KeyFigure}/index.ts +1 -1
- package/src/LicenseByline/EmbedByline.stories.tsx +1 -0
- package/src/LicenseByline/EmbedByline.tsx +57 -9
- package/src/LicenseByline/LicenseDescription.tsx +9 -3
- package/src/List/OrderedList.tsx +115 -0
- package/src/List/UnOrderedList.tsx +49 -0
- package/src/List/index.ts +10 -0
- package/src/MediaList/component.medialist.scss +2 -2
- package/src/Navigation/NavigationBox.tsx +10 -14
- package/src/Navigation/NavigationHeading.tsx +15 -24
- package/src/Notion/Notion.tsx +1 -1
- package/src/RelatedArticleList/component.related-articles.scss +3 -13
- package/src/Resource/resourceComponents.tsx +4 -2
- package/src/Table/component.tables.scss +0 -46
- package/src/Translation/component.translation.scss +3 -5
- package/src/Typography/Heading.tsx +96 -0
- package/src/Typography/index.ts +9 -0
- package/src/index.ts +5 -1
- package/src/locale/messages-en.ts +4 -0
- package/src/locale/messages-nb.ts +4 -0
- package/src/locale/messages-nn.ts +4 -0
- package/src/locale/messages-se.ts +4 -0
- package/src/locale/messages-sma.ts +4 -0
- package/src/model/ContentType.ts +7 -0
- package/es/KeyPerformanceIndicator/KeyPerformanceIndicator.js +0 -57
- package/lib/KeyPerformanceIndicator/KeyPerformanceIndicator.d.ts +0 -8
- package/lib/KeyPerformanceIndicator/KeyPerformanceIndicator.js +0 -62
- package/lib/KeyPerformanceIndicator/index.d.ts +0 -1
- package/lib/KeyPerformanceIndicator/index.js +0 -13
- package/src/KeyPerformanceIndicator/KeyPerformanceIndicator.stories.tsx +0 -79
package/src/Embed/AudioEmbed.tsx
CHANGED
|
@@ -6,30 +6,21 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { AudioMetaData } from '@ndla/types-embed';
|
|
10
|
-
import { ICopyright } from '@ndla/types-backend/image-api';
|
|
11
|
-
import {
|
|
12
|
-
figureApa7CopyString,
|
|
13
|
-
getGroupedContributorDescriptionList,
|
|
14
|
-
getLicenseByAbbreviation,
|
|
15
|
-
getLicenseCredits,
|
|
16
|
-
} from '@ndla/licenses';
|
|
17
|
-
import { ModalV2 } from '@ndla/modal';
|
|
18
|
-
import { useState } from 'react';
|
|
19
|
-
import { useTranslation } from 'react-i18next';
|
|
9
|
+
import { AudioMetaData, ImageMetaData } from '@ndla/types-embed';
|
|
20
10
|
//@ts-ignore
|
|
21
11
|
import { Remarkable } from 'remarkable';
|
|
22
|
-
import { ButtonV2, CopyButton } from '@ndla/button';
|
|
23
|
-
import { SafeLinkButton } from '@ndla/safelink';
|
|
24
12
|
import AudioPlayer from '../AudioPlayer';
|
|
25
|
-
import { Figure
|
|
26
|
-
import { FigureLicenseDialogContent } from '../Figure/FigureLicenseDialogContent';
|
|
13
|
+
import { Figure } from '../Figure';
|
|
27
14
|
import { Author } from './ImageEmbed';
|
|
15
|
+
import { EmbedByline } from '../LicenseByline';
|
|
16
|
+
import EmbedErrorPlaceholder from './EmbedErrorPlaceholder';
|
|
17
|
+
import { HeartButtonType } from './types';
|
|
28
18
|
|
|
29
19
|
interface Props {
|
|
30
20
|
embed: AudioMetaData;
|
|
31
|
-
|
|
21
|
+
heartButton?: HeartButtonType;
|
|
32
22
|
}
|
|
23
|
+
|
|
33
24
|
export const getFirstNonEmptyLicenseCredits = (authors: {
|
|
34
25
|
creators: Author[];
|
|
35
26
|
rightsholders: Author[];
|
|
@@ -42,29 +33,25 @@ const renderMarkdown = (text: string) => {
|
|
|
42
33
|
return <span dangerouslySetInnerHTML={{ __html: rendered }} />;
|
|
43
34
|
};
|
|
44
35
|
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
36
|
+
const imageMetaToMockEmbed = (
|
|
37
|
+
imageMeta: Extract<AudioMetaData, { status: 'success' }>,
|
|
38
|
+
): Extract<ImageMetaData, { status: 'success' }> => ({
|
|
39
|
+
resource: 'image',
|
|
40
|
+
status: 'success',
|
|
41
|
+
// Make sure the seq is unused. It's rarely used, but it's nice to ensure uniqueness.
|
|
42
|
+
seq: imageMeta.seq + 0.1,
|
|
43
|
+
// We check that this exists where the function is used.
|
|
44
|
+
data: imageMeta.data.imageMeta!,
|
|
45
|
+
embedData: {
|
|
46
|
+
resource: 'image',
|
|
47
|
+
resourceId: imageMeta.data.imageMeta?.id?.toString() || '',
|
|
48
|
+
alt: imageMeta.data.imageMeta?.alttext.alttext ?? '',
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const AudioEmbed = ({ embed, heartButton: HeartButton }: Props) => {
|
|
48
53
|
if (embed.status === 'error') {
|
|
49
|
-
return
|
|
50
|
-
<Figure>
|
|
51
|
-
<svg
|
|
52
|
-
fill="#8A8888"
|
|
53
|
-
height="50"
|
|
54
|
-
viewBox="0 0 24 12"
|
|
55
|
-
width="100%"
|
|
56
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
57
|
-
style={{ backgroundColor: '#EFF0F2' }}
|
|
58
|
-
>
|
|
59
|
-
<path d="M0 0h24v24H0V0z" fill="none" />
|
|
60
|
-
<path
|
|
61
|
-
transform="scale(0.3) translate(28, 8.5)"
|
|
62
|
-
d="M11 15h2v2h-2zm0-8h2v6h-2zm.99-5C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"
|
|
63
|
-
/>
|
|
64
|
-
</svg>
|
|
65
|
-
<figcaption>{t('audio.error.caption')}</figcaption>
|
|
66
|
-
</Figure>
|
|
67
|
-
);
|
|
54
|
+
return <EmbedErrorPlaceholder type={embed.embedData.type === 'standard' ? 'audio' : 'podcast'} />;
|
|
68
55
|
}
|
|
69
56
|
|
|
70
57
|
const { data, embedData, seq } = embed;
|
|
@@ -75,181 +62,47 @@ const AudioEmbed = ({ embed, articlePath }: Props) => {
|
|
|
75
62
|
|
|
76
63
|
const subtitle = data.series ? { title: data.series.title.title, url: `/podkast/${data.series.id}` } : undefined;
|
|
77
64
|
|
|
78
|
-
const textVersion = data.manuscript
|
|
79
|
-
const description = renderMarkdown(data.podcastMeta?.introduction ?? '');
|
|
65
|
+
const textVersion = data.manuscript?.manuscript.length ? renderMarkdown(data.manuscript.manuscript) : undefined;
|
|
80
66
|
|
|
81
67
|
const coverPhoto = data.podcastMeta?.coverPhoto;
|
|
82
68
|
|
|
83
69
|
const img = coverPhoto && { url: coverPhoto.url, alt: coverPhoto.altText };
|
|
84
70
|
|
|
85
|
-
const authors = getLicenseCredits(data.copyright);
|
|
86
|
-
|
|
87
|
-
const license = getLicenseByAbbreviation(data.copyright.license.license, i18n.language);
|
|
88
|
-
|
|
89
|
-
const contributors = getGroupedContributorDescriptionList(data.copyright, i18n.language).map((item) => ({
|
|
90
|
-
name: item.description,
|
|
91
|
-
type: item.label,
|
|
92
|
-
}));
|
|
93
|
-
|
|
94
71
|
const figureId = `figure-${seq}-${data.id}`;
|
|
95
72
|
|
|
96
|
-
const copyString =
|
|
97
|
-
data.audioType === 'podcast'
|
|
98
|
-
? figureApa7CopyString(
|
|
99
|
-
data.title.title,
|
|
100
|
-
undefined,
|
|
101
|
-
data.audioFile.url,
|
|
102
|
-
articlePath,
|
|
103
|
-
data.copyright,
|
|
104
|
-
data.copyright.license.license,
|
|
105
|
-
'',
|
|
106
|
-
(id: string) => t(id),
|
|
107
|
-
i18n.language,
|
|
108
|
-
)
|
|
109
|
-
: undefined;
|
|
110
|
-
const captionAuthors = getFirstNonEmptyLicenseCredits(authors);
|
|
111
73
|
return (
|
|
112
74
|
<Figure id={figureId} type="full">
|
|
113
75
|
<AudioPlayer
|
|
114
|
-
description={
|
|
76
|
+
description={data.podcastMeta?.introduction ?? ''}
|
|
115
77
|
img={img}
|
|
116
78
|
src={data.audioFile.url}
|
|
117
79
|
textVersion={textVersion}
|
|
118
80
|
title={data.title.title}
|
|
119
81
|
subtitle={subtitle}
|
|
120
82
|
/>
|
|
121
|
-
<
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
authors={captionAuthors}
|
|
131
|
-
locale={i18n.language}
|
|
132
|
-
/>
|
|
133
|
-
<ModalV2 controlled isOpen={isOpen} onClose={() => setIsOpen(false)} labelledBy="license-dialog-rules-heading">
|
|
134
|
-
{(close) => (
|
|
135
|
-
<FigureLicenseDialogContent
|
|
136
|
-
onClose={close}
|
|
137
|
-
title={data.title.title}
|
|
138
|
-
license={license}
|
|
139
|
-
authors={contributors}
|
|
140
|
-
origin={data.copyright.origin}
|
|
141
|
-
locale={i18n.language}
|
|
142
|
-
type="audio"
|
|
143
|
-
>
|
|
144
|
-
{data.copyright.license.license !== 'COPYRIGHT' && (
|
|
145
|
-
<>
|
|
146
|
-
{copyString && (
|
|
147
|
-
<CopyButton
|
|
148
|
-
variant="outline"
|
|
149
|
-
copyNode={t('license.hasCopiedTitle')}
|
|
150
|
-
onClick={() => navigator.clipboard.writeText(copyString)}
|
|
151
|
-
>
|
|
152
|
-
{t('license.copyTitle')}
|
|
153
|
-
</CopyButton>
|
|
154
|
-
)}
|
|
155
|
-
<SafeLinkButton download to={data.audioFile.url} variant="outline">
|
|
156
|
-
{t('audio.download')}
|
|
157
|
-
</SafeLinkButton>
|
|
158
|
-
</>
|
|
159
|
-
)}
|
|
160
|
-
</FigureLicenseDialogContent>
|
|
161
|
-
)}
|
|
162
|
-
</ModalV2>
|
|
83
|
+
<EmbedByline
|
|
84
|
+
error={false}
|
|
85
|
+
type={embedData.type === 'standard' ? 'audio' : 'podcast'}
|
|
86
|
+
topRounded={false}
|
|
87
|
+
bottomRounded={!data.imageMeta}
|
|
88
|
+
copyright={embed.data.copyright}
|
|
89
|
+
>
|
|
90
|
+
{HeartButton && <HeartButton embed={embed} />}
|
|
91
|
+
</EmbedByline>
|
|
163
92
|
{data.imageMeta && (
|
|
164
|
-
<
|
|
165
|
-
|
|
166
|
-
|
|
93
|
+
<EmbedByline
|
|
94
|
+
error={false}
|
|
95
|
+
first={false}
|
|
96
|
+
type="image"
|
|
97
|
+
topRounded={false}
|
|
98
|
+
bottomRounded
|
|
167
99
|
copyright={data.imageMeta.copyright}
|
|
168
|
-
|
|
169
|
-
|
|
100
|
+
>
|
|
101
|
+
{HeartButton && <HeartButton embed={imageMetaToMockEmbed(embed)} />}
|
|
102
|
+
</EmbedByline>
|
|
170
103
|
)}
|
|
171
104
|
</Figure>
|
|
172
105
|
);
|
|
173
106
|
};
|
|
174
107
|
|
|
175
|
-
interface ImageLicenseProps {
|
|
176
|
-
title: string;
|
|
177
|
-
imageUrl: string;
|
|
178
|
-
copyright: ICopyright;
|
|
179
|
-
articlePath?: string;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const ImageLicense = ({ articlePath, title, imageUrl, copyright }: ImageLicenseProps) => {
|
|
183
|
-
const { t, i18n } = useTranslation();
|
|
184
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
185
|
-
const copyString = figureApa7CopyString(
|
|
186
|
-
title,
|
|
187
|
-
undefined,
|
|
188
|
-
imageUrl,
|
|
189
|
-
articlePath,
|
|
190
|
-
copyright,
|
|
191
|
-
copyright.license.license,
|
|
192
|
-
undefined,
|
|
193
|
-
(id: string) => t(id),
|
|
194
|
-
i18n.language,
|
|
195
|
-
);
|
|
196
|
-
const license = getLicenseByAbbreviation(copyright.license.license, i18n.language);
|
|
197
|
-
const authors = getLicenseCredits(copyright);
|
|
198
|
-
|
|
199
|
-
const contributors = getGroupedContributorDescriptionList(copyright, i18n.language).map((item) => ({
|
|
200
|
-
name: item.description,
|
|
201
|
-
type: item.label,
|
|
202
|
-
}));
|
|
203
|
-
|
|
204
|
-
const captionAuthors = getFirstNonEmptyLicenseCredits(authors);
|
|
205
|
-
|
|
206
|
-
return (
|
|
207
|
-
<>
|
|
208
|
-
<FigureCaption
|
|
209
|
-
figureId=""
|
|
210
|
-
id=""
|
|
211
|
-
licenseRights={license.rights}
|
|
212
|
-
modalButton={
|
|
213
|
-
<ButtonV2 variant="outline" shape="pill" size="small" onClick={() => setIsOpen(true)}>
|
|
214
|
-
{t('image.reuse')}
|
|
215
|
-
</ButtonV2>
|
|
216
|
-
}
|
|
217
|
-
authors={captionAuthors}
|
|
218
|
-
locale={i18n.language}
|
|
219
|
-
>
|
|
220
|
-
<ModalV2 controlled isOpen={isOpen} onClose={() => setIsOpen(false)}>
|
|
221
|
-
{(close) => (
|
|
222
|
-
<FigureLicenseDialogContent
|
|
223
|
-
onClose={close}
|
|
224
|
-
title={title}
|
|
225
|
-
license={license}
|
|
226
|
-
authors={contributors}
|
|
227
|
-
origin={copyright.origin}
|
|
228
|
-
locale={i18n.language}
|
|
229
|
-
type="image"
|
|
230
|
-
>
|
|
231
|
-
{copyright.license.license !== 'COPYRIGHTED' && (
|
|
232
|
-
<>
|
|
233
|
-
{copyString && (
|
|
234
|
-
<CopyButton
|
|
235
|
-
variant="outline"
|
|
236
|
-
copyNode={t('license.hasCopiedTitle')}
|
|
237
|
-
onClick={() => navigator.clipboard.writeText(copyString)}
|
|
238
|
-
>
|
|
239
|
-
{t('license.copyTitle')}
|
|
240
|
-
</CopyButton>
|
|
241
|
-
)}
|
|
242
|
-
<SafeLinkButton download to={imageUrl} variant="outline">
|
|
243
|
-
{t('image.download')}
|
|
244
|
-
</SafeLinkButton>
|
|
245
|
-
</>
|
|
246
|
-
)}
|
|
247
|
-
</FigureLicenseDialogContent>
|
|
248
|
-
)}
|
|
249
|
-
</ModalV2>
|
|
250
|
-
</FigureCaption>
|
|
251
|
-
</>
|
|
252
|
-
);
|
|
253
|
-
};
|
|
254
|
-
|
|
255
108
|
export default AudioEmbed;
|
|
@@ -8,9 +8,10 @@
|
|
|
8
8
|
|
|
9
9
|
import React from 'react';
|
|
10
10
|
import { Meta, StoryObj } from '@storybook/react';
|
|
11
|
-
import { BrightcoveData, BrightcoveEmbedData, BrightcoveMetaData } from '@ndla/types-embed';
|
|
11
|
+
import { BrightcoveData, BrightcoveEmbedData, BrightcoveMetaData, EmbedMetaData } from '@ndla/types-embed';
|
|
12
12
|
import BrightcoveEmbed from './BrightcoveEmbed';
|
|
13
13
|
import { defaultParameters } from '../../../../stories/defaults';
|
|
14
|
+
import StoryFavoriteButton from '../../../../stories/StoryFavoriteButton';
|
|
14
15
|
|
|
15
16
|
const embedData: BrightcoveEmbedData = {
|
|
16
17
|
resource: 'brightcove',
|
|
@@ -179,6 +180,7 @@ export default meta;
|
|
|
179
180
|
|
|
180
181
|
export const BrightcoveEmbedStory: StoryObj<typeof BrightcoveEmbed> = {
|
|
181
182
|
args: {
|
|
183
|
+
heartButton: StoryFavoriteButton,
|
|
182
184
|
embed: {
|
|
183
185
|
resource: 'brightcove',
|
|
184
186
|
status: 'success',
|
|
@@ -191,12 +193,14 @@ export const BrightcoveEmbedStory: StoryObj<typeof BrightcoveEmbed> = {
|
|
|
191
193
|
|
|
192
194
|
export const VisuallyInterpreted: StoryObj<typeof BrightcoveEmbed> = {
|
|
193
195
|
args: {
|
|
196
|
+
heartButton: StoryFavoriteButton,
|
|
194
197
|
embed: visuallyInterpretedEmbedMetaData,
|
|
195
198
|
},
|
|
196
199
|
};
|
|
197
200
|
|
|
198
201
|
export const BrightcoveFailed: StoryObj<typeof BrightcoveEmbed> = {
|
|
199
202
|
args: {
|
|
203
|
+
heartButton: StoryFavoriteButton,
|
|
200
204
|
embed: {
|
|
201
205
|
resource: 'brightcove',
|
|
202
206
|
status: 'error',
|
|
@@ -10,20 +10,19 @@ import sortBy from 'lodash/sortBy';
|
|
|
10
10
|
import isNumber from 'lodash/isNumber';
|
|
11
11
|
import styled from '@emotion/styled';
|
|
12
12
|
import { spacing } from '@ndla/core';
|
|
13
|
-
import { getGroupedContributorDescriptionList, getLicenseByAbbreviation } from '@ndla/licenses';
|
|
14
13
|
import { useEffect, useRef, useState } from 'react';
|
|
15
|
-
import { ModalV2 } from '@ndla/modal';
|
|
16
|
-
import { SafeLinkButton } from '@ndla/safelink';
|
|
17
14
|
import { BrightcoveEmbedData, BrightcoveMetaData, BrightcoveVideoSource } from '@ndla/types-embed';
|
|
18
15
|
import { useTranslation } from 'react-i18next';
|
|
19
|
-
import { ButtonV2
|
|
20
|
-
import { Figure
|
|
21
|
-
import {
|
|
22
|
-
import
|
|
16
|
+
import { ButtonV2 } from '@ndla/button';
|
|
17
|
+
import { Figure } from '../Figure';
|
|
18
|
+
import { EmbedByline } from '../LicenseByline';
|
|
19
|
+
import EmbedErrorPlaceholder from './EmbedErrorPlaceholder';
|
|
20
|
+
import { HeartButtonType } from './types';
|
|
23
21
|
|
|
24
22
|
interface Props {
|
|
25
23
|
embed: BrightcoveMetaData;
|
|
26
24
|
isConcept?: boolean;
|
|
25
|
+
heartButton?: HeartButtonType;
|
|
27
26
|
}
|
|
28
27
|
|
|
29
28
|
const LinkedVideoButton = styled(ButtonV2)`
|
|
@@ -57,10 +56,9 @@ const getIframeProps = (data: BrightcoveEmbedData, sources: BrightcoveVideoSourc
|
|
|
57
56
|
width: source?.width ?? '640',
|
|
58
57
|
};
|
|
59
58
|
};
|
|
60
|
-
const BrightcoveEmbed = ({ embed, isConcept }: Props) => {
|
|
61
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
59
|
+
const BrightcoveEmbed = ({ embed, isConcept, heartButton: HeartButton }: Props) => {
|
|
62
60
|
const [showOriginalVideo, setShowOriginalVideo] = useState(true);
|
|
63
|
-
const { t
|
|
61
|
+
const { t } = useTranslation();
|
|
64
62
|
const iframeRef = useRef<HTMLIFrameElement>(null);
|
|
65
63
|
const { embedData } = embed;
|
|
66
64
|
|
|
@@ -75,7 +73,7 @@ const BrightcoveEmbed = ({ embed, isConcept }: Props) => {
|
|
|
75
73
|
}, []);
|
|
76
74
|
if (embed.status === 'error') {
|
|
77
75
|
return (
|
|
78
|
-
<
|
|
76
|
+
<EmbedErrorPlaceholder type="video">
|
|
79
77
|
<BrightcoveIframe
|
|
80
78
|
ref={iframeRef}
|
|
81
79
|
title={`Video: ${embedData.videoid ?? ''}`}
|
|
@@ -84,35 +82,18 @@ const BrightcoveEmbed = ({ embed, isConcept }: Props) => {
|
|
|
84
82
|
{...getIframeProps(embedData, [])}
|
|
85
83
|
allowFullScreen
|
|
86
84
|
/>
|
|
87
|
-
|
|
88
|
-
</Figure>
|
|
85
|
+
</EmbedErrorPlaceholder>
|
|
89
86
|
);
|
|
90
87
|
}
|
|
91
88
|
const { data, seq } = embed;
|
|
92
89
|
|
|
93
90
|
const linkedVideoId = isNumeric(data.link?.text) ? data.link?.text : undefined;
|
|
94
91
|
|
|
95
|
-
const license = getLicenseByAbbreviation(data.copyright?.license.license ?? '', i18n.language);
|
|
96
|
-
const contributors = data.copyright
|
|
97
|
-
? getGroupedContributorDescriptionList(data.copyright, i18n.language).map((item) => ({
|
|
98
|
-
name: item.description,
|
|
99
|
-
type: item.label,
|
|
100
|
-
}))
|
|
101
|
-
: [];
|
|
102
|
-
|
|
103
|
-
const { rightsholders = [], creators = [], processors = [] } = data.copyright ?? {};
|
|
104
|
-
|
|
105
|
-
const download = sortBy(
|
|
106
|
-
data.sources.filter((src) => src.container === 'MP4' && src.src),
|
|
107
|
-
(src) => src.size,
|
|
108
|
-
)?.[0]?.src;
|
|
109
|
-
|
|
110
92
|
const figureId = `figure-${seq}-${data.id}`;
|
|
111
93
|
const originalVideoProps = getIframeProps(embedData, data.sources);
|
|
112
94
|
const alternativeVideoProps = linkedVideoId
|
|
113
95
|
? getIframeProps({ ...embedData, videoid: linkedVideoId }, data.sources)
|
|
114
96
|
: undefined;
|
|
115
|
-
const captionAuthors = getFirstNonEmptyLicenseCredits({ rightsholders, creators, processors });
|
|
116
97
|
|
|
117
98
|
return (
|
|
118
99
|
<Figure id={figureId} type={isConcept ? 'full-column' : 'full'} resizeIframe>
|
|
@@ -127,17 +108,13 @@ const BrightcoveEmbed = ({ embed, isConcept }: Props) => {
|
|
|
127
108
|
allowFullScreen
|
|
128
109
|
/>
|
|
129
110
|
</div>
|
|
130
|
-
<
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
{t('video.reuse')}
|
|
138
|
-
</ButtonV2>
|
|
139
|
-
}
|
|
140
|
-
linkedVideoButton={
|
|
111
|
+
<EmbedByline
|
|
112
|
+
type="video"
|
|
113
|
+
copyright={data.copyright!}
|
|
114
|
+
description={embedData.caption ?? data.description ?? ''}
|
|
115
|
+
bottomRounded
|
|
116
|
+
>
|
|
117
|
+
{!!linkedVideoId && (
|
|
141
118
|
<LinkedVideoButton
|
|
142
119
|
variant="outline"
|
|
143
120
|
shape="pill"
|
|
@@ -146,63 +123,11 @@ const BrightcoveEmbed = ({ embed, isConcept }: Props) => {
|
|
|
146
123
|
>
|
|
147
124
|
{t(`figure.button.${!showOriginalVideo ? 'original' : 'alternative'}`)}
|
|
148
125
|
</LinkedVideoButton>
|
|
149
|
-
}
|
|
150
|
-
licenseRights={license.rights}
|
|
151
|
-
authors={captionAuthors}
|
|
152
|
-
hasLinkedVideo={!!linkedVideoId}
|
|
153
|
-
/>
|
|
154
|
-
<ModalV2 controlled isOpen={isOpen} onClose={() => setIsOpen(false)} labelledBy="license-dialog-rules-heading">
|
|
155
|
-
{(close) => (
|
|
156
|
-
<FigureLicenseDialogContent
|
|
157
|
-
onClose={close}
|
|
158
|
-
title={data.name}
|
|
159
|
-
locale={i18n.language}
|
|
160
|
-
license={license}
|
|
161
|
-
authors={contributors}
|
|
162
|
-
type="video"
|
|
163
|
-
>
|
|
164
|
-
<VideoLicenseButtons
|
|
165
|
-
download={download}
|
|
166
|
-
licenseCode={data.copyright?.license.license}
|
|
167
|
-
src={originalVideoProps.src}
|
|
168
|
-
width={originalVideoProps.width}
|
|
169
|
-
height={originalVideoProps.height}
|
|
170
|
-
name={data.name}
|
|
171
|
-
/>
|
|
172
|
-
</FigureLicenseDialogContent>
|
|
173
126
|
)}
|
|
174
|
-
|
|
127
|
+
{HeartButton && <HeartButton embed={embed} />}
|
|
128
|
+
</EmbedByline>
|
|
175
129
|
</Figure>
|
|
176
130
|
);
|
|
177
131
|
};
|
|
178
132
|
|
|
179
|
-
interface VideoLicenseButtonsProps {
|
|
180
|
-
download: string;
|
|
181
|
-
licenseCode?: string;
|
|
182
|
-
src: string;
|
|
183
|
-
width: string | number;
|
|
184
|
-
height: string | number;
|
|
185
|
-
name?: string;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
const VideoLicenseButtons = ({ download, src, width, height, name, licenseCode }: VideoLicenseButtonsProps) => {
|
|
189
|
-
const { t } = useTranslation();
|
|
190
|
-
return (
|
|
191
|
-
<>
|
|
192
|
-
{licenseCode !== 'COPYRIGHTED' && (
|
|
193
|
-
<SafeLinkButton key="download" to={download} variant="outline" download>
|
|
194
|
-
{t('video.download')}
|
|
195
|
-
</SafeLinkButton>
|
|
196
|
-
)}
|
|
197
|
-
<CopyButton
|
|
198
|
-
variant="outline"
|
|
199
|
-
copyNode={t('license.hasCopiedTitle')}
|
|
200
|
-
onClick={() => navigator.clipboard.writeText(makeIframeString(src, width, height, name))}
|
|
201
|
-
>
|
|
202
|
-
{t('license.embed')}
|
|
203
|
-
</CopyButton>
|
|
204
|
-
</>
|
|
205
|
-
);
|
|
206
|
-
};
|
|
207
|
-
|
|
208
133
|
export default BrightcoveEmbed;
|
|
@@ -11,6 +11,7 @@ import { Meta, StoryObj } from '@storybook/react';
|
|
|
11
11
|
import { ConceptData, ConceptEmbedData } from '@ndla/types-embed';
|
|
12
12
|
import ConceptEmbed from './ConceptEmbed';
|
|
13
13
|
import { defaultParameters } from '../../../../stories/defaults';
|
|
14
|
+
import StoryFavoriteButton from '../../../../stories/StoryFavoriteButton';
|
|
14
15
|
|
|
15
16
|
const blockEmbedData: ConceptEmbedData = {
|
|
16
17
|
contentId: '35',
|
|
@@ -145,6 +146,7 @@ export default meta;
|
|
|
145
146
|
|
|
146
147
|
export const Block: StoryObj<typeof ConceptEmbed> = {
|
|
147
148
|
args: {
|
|
149
|
+
heartButton: StoryFavoriteButton,
|
|
148
150
|
embed: {
|
|
149
151
|
resource: 'concept',
|
|
150
152
|
status: 'success',
|
|
@@ -157,6 +159,7 @@ export const Block: StoryObj<typeof ConceptEmbed> = {
|
|
|
157
159
|
|
|
158
160
|
export const BlockFailed: StoryObj<typeof ConceptEmbed> = {
|
|
159
161
|
args: {
|
|
162
|
+
heartButton: StoryFavoriteButton,
|
|
160
163
|
embed: {
|
|
161
164
|
resource: 'concept',
|
|
162
165
|
status: 'error',
|
|
@@ -168,6 +171,7 @@ export const BlockFailed: StoryObj<typeof ConceptEmbed> = {
|
|
|
168
171
|
|
|
169
172
|
export const Inline: StoryObj<typeof ConceptEmbed> = {
|
|
170
173
|
args: {
|
|
174
|
+
heartButton: StoryFavoriteButton,
|
|
171
175
|
embed: {
|
|
172
176
|
resource: 'concept',
|
|
173
177
|
status: 'success',
|
|
@@ -180,6 +184,7 @@ export const Inline: StoryObj<typeof ConceptEmbed> = {
|
|
|
180
184
|
|
|
181
185
|
export const InlineFailed: StoryObj<typeof ConceptEmbed> = {
|
|
182
186
|
args: {
|
|
187
|
+
heartButton: StoryFavoriteButton,
|
|
183
188
|
embed: {
|
|
184
189
|
resource: 'concept',
|
|
185
190
|
status: 'error',
|