@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
|
@@ -6,36 +6,36 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import React, { ReactNode } from 'react';
|
|
9
|
+
import React, { ReactNode, useCallback, useEffect, useState } from 'react';
|
|
10
10
|
import styled from '@emotion/styled';
|
|
11
|
-
import
|
|
12
|
-
import { CopyButton, ButtonV2 } from '@ndla/button';
|
|
13
|
-
import { colors, fonts, spacing } from '@ndla/core';
|
|
14
|
-
import { copyTextToClipboard, printPage } from '@ndla/util';
|
|
11
|
+
import { breakpoints, colors, fonts, mq, spacing } from '@ndla/core';
|
|
15
12
|
import { useTranslation } from 'react-i18next';
|
|
16
13
|
import { getLicenseByAbbreviation } from '@ndla/licenses';
|
|
17
|
-
import {
|
|
14
|
+
import { AccordionRoot, AccordionHeader, AccordionContent, AccordionItem } from '@ndla/accordion';
|
|
18
15
|
import { TFunction } from 'i18next';
|
|
16
|
+
import { FootNote } from '../types';
|
|
17
|
+
import ArticleFootNotes from './ArticleFootNotes';
|
|
18
|
+
import LicenseLink from '../LicenseByline/LicenseLink';
|
|
19
19
|
|
|
20
20
|
const Wrapper = styled.div`
|
|
21
21
|
margin-top: ${spacing.normal};
|
|
22
22
|
padding-top: ${spacing.normal};
|
|
23
23
|
padding-bottom: ${spacing.xsmall};
|
|
24
24
|
border-top: 1px solid ${colors.brand.greyLight};
|
|
25
|
-
${fonts.sizes('
|
|
25
|
+
${fonts.sizes('16px', '24px')};
|
|
26
26
|
font-family: ${fonts.sans};
|
|
27
27
|
color: ${colors.brand.greyDark};
|
|
28
28
|
`;
|
|
29
29
|
|
|
30
30
|
const TextWrapper = styled.div`
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
31
|
+
display: flex;
|
|
32
|
+
justify-content: space-between;
|
|
33
|
+
width: 100%;
|
|
34
|
+
padding-bottom: ${spacing.mediumlarge};
|
|
35
|
+
${mq.range({ until: breakpoints.tabletWide })} {
|
|
36
|
+
flex-direction: column;
|
|
37
|
+
flex-direction: column-reverse;
|
|
38
|
+
gap: ${spacing.xsmall};
|
|
39
39
|
}
|
|
40
40
|
`;
|
|
41
41
|
|
|
@@ -57,11 +57,8 @@ type Props = {
|
|
|
57
57
|
published: string;
|
|
58
58
|
license: string;
|
|
59
59
|
licenseBox?: ReactNode;
|
|
60
|
-
copyPageUrlLink?: string;
|
|
61
|
-
printUrl?: string;
|
|
62
60
|
locale?: string;
|
|
63
|
-
|
|
64
|
-
copySourceReference?: string;
|
|
61
|
+
footnotes?: FootNote[];
|
|
65
62
|
};
|
|
66
63
|
|
|
67
64
|
const renderContributors = (contributors: SupplierProps[] | AuthorProps[], t: TFunction) => {
|
|
@@ -85,133 +82,87 @@ const getSuppliersText = (suppliers: SupplierProps[], t: TFunction) => {
|
|
|
85
82
|
: t('article.supplierLabel', { name: renderContributors(suppliers, t), interpolation: { escapeValue: false } });
|
|
86
83
|
};
|
|
87
84
|
|
|
85
|
+
const LicenseWrapper = styled.div`
|
|
86
|
+
display: flex;
|
|
87
|
+
gap: ${spacing.small};
|
|
88
|
+
`;
|
|
89
|
+
|
|
90
|
+
const refRegexp = /note\d/;
|
|
91
|
+
const referencesAccordionId = 'references';
|
|
92
|
+
|
|
88
93
|
const ArticleByline = ({
|
|
89
94
|
authors = [],
|
|
90
95
|
suppliers = [],
|
|
91
|
-
|
|
96
|
+
footnotes,
|
|
97
|
+
license: licenseString,
|
|
92
98
|
licenseBox,
|
|
93
99
|
published,
|
|
94
|
-
copyPageUrlLink,
|
|
95
|
-
printUrl,
|
|
96
100
|
locale,
|
|
97
|
-
copyEmbedLink,
|
|
98
|
-
copySourceReference,
|
|
99
101
|
}: Props) => {
|
|
100
102
|
const { t } = useTranslation();
|
|
103
|
+
const [openAccordions, setOpenAccordions] = useState<string[]>([]);
|
|
104
|
+
|
|
105
|
+
const onHashChange = useCallback(
|
|
106
|
+
(e: HashChangeEvent) => {
|
|
107
|
+
const hash = e.newURL.split('#')[1];
|
|
108
|
+
if (hash.match(refRegexp) && !openAccordions.includes(referencesAccordionId)) {
|
|
109
|
+
setOpenAccordions([...openAccordions, referencesAccordionId]);
|
|
110
|
+
const el = document.getElementById(`#${hash}`);
|
|
111
|
+
el?.click();
|
|
112
|
+
el?.focus();
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
[openAccordions],
|
|
116
|
+
);
|
|
101
117
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
const copyLicense = () => {
|
|
110
|
-
if (copySourceReference) {
|
|
111
|
-
copyTextToClipboard(copySourceReference);
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
const copyEmbededLink = () => {
|
|
115
|
-
if (copyEmbedLink) {
|
|
116
|
-
copyTextToClipboard(copyEmbedLink);
|
|
117
|
-
}
|
|
118
|
-
};
|
|
118
|
+
useEffect(() => {
|
|
119
|
+
window.addEventListener('hashchange', onHashChange);
|
|
120
|
+
return () => window.removeEventListener('hashchange', onHashChange);
|
|
121
|
+
}, [onHashChange]);
|
|
122
|
+
|
|
123
|
+
const license = getLicenseByAbbreviation(licenseString, locale);
|
|
119
124
|
|
|
120
125
|
const showPrimaryContributors = suppliers.length > 0 || authors.length > 0;
|
|
121
126
|
const showSecondaryContributors = suppliers.length > 0 && authors.length > 0;
|
|
122
127
|
|
|
123
|
-
const buttonId = 'popupUseContent';
|
|
124
|
-
|
|
125
128
|
return (
|
|
126
129
|
<Wrapper>
|
|
127
|
-
<
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
)}
|
|
130
|
+
<TextWrapper>
|
|
131
|
+
<LicenseWrapper>
|
|
132
|
+
<LicenseLink license={license} />
|
|
133
|
+
{showPrimaryContributors && (
|
|
134
|
+
<PrimaryContributorsWrapper>
|
|
135
|
+
{authors.length > 0
|
|
136
|
+
? t('article.authorsLabel', {
|
|
137
|
+
names: renderContributors(authors, t),
|
|
138
|
+
interpolation: { escapeValue: false },
|
|
139
|
+
})
|
|
140
|
+
: getSuppliersText(suppliers, t)}
|
|
141
|
+
</PrimaryContributorsWrapper>
|
|
142
|
+
)}
|
|
143
|
+
</LicenseWrapper>
|
|
144
|
+
<div>
|
|
145
|
+
{t('article.lastUpdated')} {published}
|
|
146
|
+
</div>
|
|
147
|
+
</TextWrapper>
|
|
146
148
|
{showSecondaryContributors && <TextWrapper>{getSuppliersText(suppliers, t)}</TextWrapper>}
|
|
147
|
-
<
|
|
149
|
+
<AccordionRoot type="multiple" onValueChange={setOpenAccordions} value={openAccordions}>
|
|
148
150
|
{licenseBox && (
|
|
149
|
-
<
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
{t('article.useContent')}
|
|
154
|
-
</ButtonV2>
|
|
155
|
-
}
|
|
156
|
-
backgroundColor="white"
|
|
157
|
-
position="top"
|
|
158
|
-
size="medium"
|
|
159
|
-
>
|
|
160
|
-
{(onClose: () => void) => (
|
|
161
|
-
<>
|
|
162
|
-
<ModalHeader modifier="no-bottom-padding">
|
|
163
|
-
<ModalCloseButton onClick={onClose} title="Lukk" />
|
|
164
|
-
</ModalHeader>
|
|
165
|
-
<ModalBody>{licenseBox}</ModalBody>
|
|
166
|
-
</>
|
|
167
|
-
)}
|
|
168
|
-
</Modal>
|
|
169
|
-
)}
|
|
170
|
-
{copySourceReference && (
|
|
171
|
-
<CopyButton
|
|
172
|
-
size="small"
|
|
173
|
-
shape="pill"
|
|
174
|
-
variant="outline"
|
|
175
|
-
aria-live="assertive"
|
|
176
|
-
copyNode={t('license.hasCopiedTitle')}
|
|
177
|
-
data-copy-string={copySourceReference}
|
|
178
|
-
onClick={copyLicense}
|
|
179
|
-
>
|
|
180
|
-
{`${t('license.copy')} ${t('license.copyTitle').toLowerCase()}`}
|
|
181
|
-
</CopyButton>
|
|
151
|
+
<AccordionItem value="rulesForUse">
|
|
152
|
+
<AccordionHeader>{t('article.useContent')}</AccordionHeader>
|
|
153
|
+
<AccordionContent>{licenseBox}</AccordionContent>
|
|
154
|
+
</AccordionItem>
|
|
182
155
|
)}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
copyNode={t('article.copyPageLinkCopied')}
|
|
192
|
-
>
|
|
193
|
-
{t('article.copyPageLink')}
|
|
194
|
-
</CopyButton>
|
|
195
|
-
)}
|
|
196
|
-
{copyEmbedLink && (
|
|
197
|
-
<CopyButton
|
|
198
|
-
size="small"
|
|
199
|
-
shape="pill"
|
|
200
|
-
variant="outline"
|
|
201
|
-
aria-live="assertive"
|
|
202
|
-
copyNode={t('license.hasCopiedTitle')}
|
|
203
|
-
data-copy-string={copyEmbedLink}
|
|
204
|
-
onClick={copyEmbededLink}
|
|
205
|
-
>
|
|
206
|
-
{`${t('license.copy')} ${t('license.tabs.embedlink').toLowerCase()}`}
|
|
207
|
-
</CopyButton>
|
|
208
|
-
)}
|
|
209
|
-
{printUrl && (
|
|
210
|
-
<ButtonV2 size="small" shape="pill" variant="outline" onClick={() => printPage(printUrl)}>
|
|
211
|
-
{t('article.printPage')}
|
|
212
|
-
</ButtonV2>
|
|
156
|
+
|
|
157
|
+
{!!footnotes?.length && (
|
|
158
|
+
<AccordionItem value={referencesAccordionId}>
|
|
159
|
+
<AccordionHeader>Referanser</AccordionHeader>
|
|
160
|
+
<AccordionContent forceMount>
|
|
161
|
+
<ArticleFootNotes footNotes={footnotes} />
|
|
162
|
+
</AccordionContent>
|
|
163
|
+
</AccordionItem>
|
|
213
164
|
)}
|
|
214
|
-
</
|
|
165
|
+
</AccordionRoot>
|
|
215
166
|
</Wrapper>
|
|
216
167
|
);
|
|
217
168
|
};
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import React from 'react';
|
|
10
|
+
import styled from '@emotion/styled';
|
|
11
|
+
import { colors, spacing } from '@ndla/core';
|
|
10
12
|
import { FootNote as FootNoteType } from '../types';
|
|
11
13
|
|
|
12
14
|
const citeDetailString = (description: string | undefined) => (description ? `${description}. ` : '');
|
|
@@ -15,14 +17,26 @@ type FootNoteProps = {
|
|
|
15
17
|
footNote: FootNoteType;
|
|
16
18
|
};
|
|
17
19
|
|
|
20
|
+
const Cite = styled.cite`
|
|
21
|
+
display: flex;
|
|
22
|
+
gap: ${spacing.small};
|
|
23
|
+
a {
|
|
24
|
+
box-shadow: none;
|
|
25
|
+
text-decoration: underline;
|
|
26
|
+
text-underline-offset: ${spacing.xxsmall};
|
|
27
|
+
&:hover,
|
|
28
|
+
&:focus-visible {
|
|
29
|
+
text-decoration: none;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
`;
|
|
33
|
+
|
|
18
34
|
const FootNote = ({ footNote }: FootNoteProps) => (
|
|
19
|
-
<li
|
|
20
|
-
<
|
|
21
|
-
<
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
</a>
|
|
25
|
-
</sup>
|
|
35
|
+
<li>
|
|
36
|
+
<Cite id={`note${footNote.ref}`}>
|
|
37
|
+
<a href={`#ref${footNote.ref}`} target="_self">
|
|
38
|
+
{footNote.ref}
|
|
39
|
+
</a>
|
|
26
40
|
{`«${footNote.title}». ${footNote.authors.join(' ')}. ${citeDetailString(footNote.edition)}${citeDetailString(
|
|
27
41
|
footNote.publisher,
|
|
28
42
|
)}${footNote.year}. `}
|
|
@@ -32,7 +46,7 @@ const FootNote = ({ footNote }: FootNoteProps) => (
|
|
|
32
46
|
{'.'}
|
|
33
47
|
</a>
|
|
34
48
|
) : null}
|
|
35
|
-
</
|
|
49
|
+
</Cite>
|
|
36
50
|
</li>
|
|
37
51
|
);
|
|
38
52
|
|
|
@@ -40,12 +54,21 @@ type ArticleFootNotesProps = {
|
|
|
40
54
|
footNotes: Array<FootNoteType>;
|
|
41
55
|
};
|
|
42
56
|
|
|
57
|
+
const FootnoteList = styled.ol`
|
|
58
|
+
margin: 0;
|
|
59
|
+
display: flex;
|
|
60
|
+
flex-direction: column;
|
|
61
|
+
list-style: none;
|
|
62
|
+
padding: ${spacing.small};
|
|
63
|
+
color: ${colors.text.light};
|
|
64
|
+
`;
|
|
65
|
+
|
|
43
66
|
const ArticleFootNotes = ({ footNotes }: ArticleFootNotesProps) => (
|
|
44
|
-
<
|
|
67
|
+
<FootnoteList>
|
|
45
68
|
{footNotes.map((footNote) => (
|
|
46
69
|
<FootNote key={footNote.ref} footNote={footNote} />
|
|
47
70
|
))}
|
|
48
|
-
</
|
|
71
|
+
</FootnoteList>
|
|
49
72
|
);
|
|
50
73
|
|
|
51
74
|
export default ArticleFootNotes;
|
|
@@ -4,15 +4,6 @@
|
|
|
4
4
|
** Title has icon when article is a resource type
|
|
5
5
|
**/
|
|
6
6
|
|
|
7
|
-
@mixin contentList() {
|
|
8
|
-
ul:not([class]),
|
|
9
|
-
ul.o-list--two-columns,
|
|
10
|
-
ul.o-list--bullets,
|
|
11
|
-
ol {
|
|
12
|
-
@content;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
7
|
.c-article {
|
|
17
8
|
font-family: $font-serif;
|
|
18
9
|
background: $white;
|
|
@@ -33,58 +24,16 @@
|
|
|
33
24
|
margin-bottom: 29px;
|
|
34
25
|
}
|
|
35
26
|
}
|
|
36
|
-
p {
|
|
37
|
-
@include chinese() {
|
|
38
|
-
@include font-size(20px, 35px);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
@include contentList() {
|
|
43
|
-
@include chinese() {
|
|
44
|
-
@include font-size(20px, 35px);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
@include chinese() {
|
|
49
|
-
@include contentList() {
|
|
50
|
-
@include font-size(22px, 35px);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
p {
|
|
54
|
-
@include font-size(22px, 35px);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
27
|
|
|
58
28
|
@include mq(tablet) {
|
|
59
29
|
@include font-size(20px, 35px);
|
|
60
30
|
|
|
61
|
-
p {
|
|
62
|
-
@include chinese() {
|
|
63
|
-
@include font-size(22px, 35px);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
31
|
> section > p {
|
|
68
32
|
&:not([class]) {
|
|
69
33
|
margin-bottom: 35px;
|
|
70
34
|
}
|
|
71
35
|
}
|
|
72
36
|
|
|
73
|
-
@include contentList() {
|
|
74
|
-
@include chinese() {
|
|
75
|
-
@include font-size(22px, 35px);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
@include chinese() {
|
|
80
|
-
@include contentList() {
|
|
81
|
-
@include font-size(22px, 35px);
|
|
82
|
-
}
|
|
83
|
-
p {
|
|
84
|
-
@include font-size(22px, 35px);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
37
|
padding: 0 $spacing $spacing;
|
|
89
38
|
margin-bottom: $spacing--large;
|
|
90
39
|
margin-top: -$spacing * 6;
|
|
@@ -178,7 +127,7 @@
|
|
|
178
127
|
}
|
|
179
128
|
|
|
180
129
|
p {
|
|
181
|
-
@include
|
|
130
|
+
@include font-size(16px, 20px);
|
|
182
131
|
color: $text-light-color;
|
|
183
132
|
text-transform: uppercase;
|
|
184
133
|
margin-bottom: 0;
|
|
@@ -17,7 +17,7 @@ $highlight-color: $brand-grey--lighter;
|
|
|
17
17
|
display: block;
|
|
18
18
|
border-top: 2px solid $brand-grey--lighter;
|
|
19
19
|
color: gray;
|
|
20
|
-
@include
|
|
20
|
+
@include font-size(15px, 1.6);
|
|
21
21
|
}
|
|
22
22
|
.c-footnotes__item {
|
|
23
23
|
margin-bottom: $spacing--small;
|
|
@@ -37,7 +37,7 @@ $highlight-color: $brand-grey--lighter;
|
|
|
37
37
|
padding: 10px 15px;
|
|
38
38
|
box-shadow: none;
|
|
39
39
|
text-decoration: underline;
|
|
40
|
-
@include
|
|
40
|
+
@include font-size(15px, 1.6);
|
|
41
41
|
|
|
42
42
|
&:hover,
|
|
43
43
|
&:active,
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
.c-aside {
|
|
7
7
|
position: relative;
|
|
8
8
|
margin: $spacing--large 0;
|
|
9
|
-
@include
|
|
9
|
+
@include font-size(16px, 1.5);
|
|
10
10
|
z-index: 1;
|
|
11
11
|
|
|
12
12
|
@include mq(tablet) {
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
.c-aside h1 {
|
|
60
60
|
margin-top: 0;
|
|
61
61
|
margin-bottom: $spacing;
|
|
62
|
-
@include
|
|
62
|
+
@include font-size(22px, 34px);
|
|
63
63
|
font-weight: $font-weight-bold;
|
|
64
64
|
position: relative;
|
|
65
65
|
z-index: 2;
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
.c-aside h4,
|
|
71
71
|
.c-aside h5 {
|
|
72
72
|
display: block;
|
|
73
|
-
@include
|
|
73
|
+
@include font-size(16px, 1.5);
|
|
74
74
|
letter-spacing: 0.1em;
|
|
75
75
|
margin-top: $spacing;
|
|
76
76
|
margin-bottom: $spacing--small;
|
|
@@ -6,14 +6,13 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import React, { ReactNode,
|
|
9
|
+
import React, { ReactNode, useMemo, useState } from 'react';
|
|
10
10
|
import styled from '@emotion/styled';
|
|
11
11
|
import { breakpoints, colors, fonts, mq, spacing } from '@ndla/core';
|
|
12
12
|
import { ButtonV2 } from '@ndla/button';
|
|
13
13
|
import { Cross as CrossIcon } from '@ndla/icons/action';
|
|
14
14
|
import { useTranslation } from 'react-i18next';
|
|
15
15
|
import SafeLink from '@ndla/safelink';
|
|
16
|
-
import shave from 'shave';
|
|
17
16
|
import Controls from './Controls';
|
|
18
17
|
import SpeechControl from './SpeechControl';
|
|
19
18
|
|
|
@@ -163,11 +162,7 @@ const TextVersionText = styled.div`
|
|
|
163
162
|
max-width: 670px;
|
|
164
163
|
`;
|
|
165
164
|
|
|
166
|
-
|
|
167
|
-
shave(el, 90, {
|
|
168
|
-
character: `... <a href="#" onclick="(function(e){e.preventDefault(); const parentNode = e.target.parentNode; parentNode.nextSibling.style.display = 'inline'; parentNode.remove();return false;})(arguments[0]);return false;">${readMoreLabel}</a>`,
|
|
169
|
-
});
|
|
170
|
-
};
|
|
165
|
+
const DESCRIPTION_MAX_LENGTH = 200;
|
|
171
166
|
|
|
172
167
|
type Props = {
|
|
173
168
|
src: string;
|
|
@@ -177,7 +172,7 @@ type Props = {
|
|
|
177
172
|
url?: string;
|
|
178
173
|
};
|
|
179
174
|
speech?: boolean;
|
|
180
|
-
description?:
|
|
175
|
+
description?: string;
|
|
181
176
|
textVersion?: ReactNode;
|
|
182
177
|
img?: {
|
|
183
178
|
url: string;
|
|
@@ -189,15 +184,8 @@ type Props = {
|
|
|
189
184
|
const AudioPlayer = ({ src, title, subtitle, speech, description, img, textVersion, staticRenderId }: Props) => {
|
|
190
185
|
const { t } = useTranslation();
|
|
191
186
|
const [showTextVersion, setShowTextVersion] = useState(false);
|
|
192
|
-
|
|
193
|
-
const
|
|
194
|
-
const readMoreDescriptionLabel = t('audio.readMoreDescriptionLabel');
|
|
195
|
-
|
|
196
|
-
useEffect(() => {
|
|
197
|
-
if (descriptionRef?.current) {
|
|
198
|
-
truncateDescription(descriptionRef.current, readMoreDescriptionLabel);
|
|
199
|
-
}
|
|
200
|
-
}, [readMoreDescriptionLabel]);
|
|
187
|
+
const [showFullDescription, setShowFullDescription] = useState(false);
|
|
188
|
+
const truncatedDescription = useMemo(() => description?.slice(0, DESCRIPTION_MAX_LENGTH), [description]);
|
|
201
189
|
|
|
202
190
|
if (speech) {
|
|
203
191
|
return (
|
|
@@ -244,13 +232,12 @@ const AudioPlayer = ({ src, title, subtitle, speech, description, img, textVersi
|
|
|
244
232
|
</TitleWrapper>
|
|
245
233
|
{description && (
|
|
246
234
|
<StyledDescription>
|
|
247
|
-
<
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
</div>
|
|
235
|
+
{showFullDescription || description.length < DESCRIPTION_MAX_LENGTH
|
|
236
|
+
? description
|
|
237
|
+
: `${truncatedDescription}...`}
|
|
238
|
+
<ButtonV2 variant="link" onClick={() => setShowFullDescription((p) => !p)}>
|
|
239
|
+
{t(`audio.${showFullDescription ? 'readLessDescriptionLabel' : 'readMoreDescriptionLabel'}`)}
|
|
240
|
+
</ButtonV2>
|
|
254
241
|
</StyledDescription>
|
|
255
242
|
)}
|
|
256
243
|
{textVersion && img && <TextVersionComponent />}
|
|
@@ -7,11 +7,16 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import React from 'react';
|
|
9
9
|
import ReactDOM from 'react-dom';
|
|
10
|
-
|
|
10
|
+
import shave from 'shave';
|
|
11
11
|
import Controls from './Controls';
|
|
12
12
|
import SpeechControl from './SpeechControl';
|
|
13
13
|
import { Locale } from '../types';
|
|
14
|
-
|
|
14
|
+
|
|
15
|
+
export const truncateDescription = (el: HTMLElement, readMoreLabel: string | null) => {
|
|
16
|
+
shave(el, 90, {
|
|
17
|
+
character: `... <a href="#" onclick="(function(e){e.preventDefault(); const parentNode = e.target.parentNode; parentNode.nextSibling.style.display = 'inline'; parentNode.remove();return false;})(arguments[0]);return false;">${readMoreLabel}</a>`,
|
|
18
|
+
});
|
|
19
|
+
};
|
|
15
20
|
|
|
16
21
|
const forEachElement = (selector: string, callback: Function) => {
|
|
17
22
|
const nodeList = document.querySelectorAll(selector);
|
|
@@ -17,12 +17,12 @@
|
|
|
17
17
|
}
|
|
18
18
|
&:not(&--large, &--fullscreen) {
|
|
19
19
|
h1 {
|
|
20
|
-
@include
|
|
20
|
+
@include font-size(22px, 26px);
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
&--small-heading {
|
|
24
24
|
h1 {
|
|
25
|
-
@include
|
|
25
|
+
@include font-size(22px, 26px);
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
@include mq(tablet) {
|
|
46
46
|
min-width: 20rem;
|
|
47
47
|
}
|
|
48
|
-
@include font-size(18px);
|
|
48
|
+
@include font-size(18px, 1.33);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
&--active &__content {
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
background: transparent;
|
|
91
91
|
border: none;
|
|
92
92
|
color: $brand-color;
|
|
93
|
-
@include font-size(
|
|
93
|
+
@include font-size(18px, 1.33);
|
|
94
94
|
font-weight: $font-weight-normal;
|
|
95
95
|
padding: 0;
|
|
96
96
|
box-shadow: $link;
|
|
@@ -124,7 +124,6 @@
|
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
|
|
128
127
|
&--fullscreen &__content {
|
|
129
128
|
@include mq(tablet) {
|
|
130
129
|
height: 100vh;
|
|
@@ -11,6 +11,7 @@ import { Meta, StoryObj } from '@storybook/react';
|
|
|
11
11
|
import { AudioEmbedData, AudioMeta } from '@ndla/types-embed';
|
|
12
12
|
import AudioEmbed from './AudioEmbed';
|
|
13
13
|
import { defaultParameters } from '../../../../stories/defaults';
|
|
14
|
+
import StoryFavoriteButton from '../../../../stories/StoryFavoriteButton';
|
|
14
15
|
|
|
15
16
|
const embedData: AudioEmbedData = {
|
|
16
17
|
resource: 'audio',
|
|
@@ -156,9 +157,6 @@ const meta: Meta<typeof AudioEmbed> = {
|
|
|
156
157
|
title: 'Enkle komponenter/Embeds/AudioEmbed',
|
|
157
158
|
component: AudioEmbed,
|
|
158
159
|
tags: ['autodocs'],
|
|
159
|
-
args: {
|
|
160
|
-
articlePath: undefined,
|
|
161
|
-
},
|
|
162
160
|
decorators: [
|
|
163
161
|
(Story) => (
|
|
164
162
|
<div className="o-wrapper">
|
|
@@ -179,6 +177,7 @@ export default meta;
|
|
|
179
177
|
|
|
180
178
|
export const AudioEmbedStory: StoryObj<typeof AudioEmbed> = {
|
|
181
179
|
args: {
|
|
180
|
+
heartButton: StoryFavoriteButton,
|
|
182
181
|
embed: {
|
|
183
182
|
resource: 'audio',
|
|
184
183
|
status: 'success',
|
|
@@ -191,6 +190,7 @@ export const AudioEmbedStory: StoryObj<typeof AudioEmbed> = {
|
|
|
191
190
|
|
|
192
191
|
export const AudioEmbedFailed: StoryObj<typeof AudioEmbed> = {
|
|
193
192
|
args: {
|
|
193
|
+
heartButton: StoryFavoriteButton,
|
|
194
194
|
embed: {
|
|
195
195
|
resource: 'audio',
|
|
196
196
|
status: 'error',
|
|
@@ -202,6 +202,7 @@ export const AudioEmbedFailed: StoryObj<typeof AudioEmbed> = {
|
|
|
202
202
|
|
|
203
203
|
export const Podcast: StoryObj<typeof AudioEmbed> = {
|
|
204
204
|
args: {
|
|
205
|
+
heartButton: StoryFavoriteButton,
|
|
205
206
|
embed: {
|
|
206
207
|
resource: 'audio',
|
|
207
208
|
status: 'success',
|
|
@@ -214,6 +215,7 @@ export const Podcast: StoryObj<typeof AudioEmbed> = {
|
|
|
214
215
|
|
|
215
216
|
export const PodcastFailed: StoryObj<typeof AudioEmbed> = {
|
|
216
217
|
args: {
|
|
218
|
+
heartButton: StoryFavoriteButton,
|
|
217
219
|
embed: {
|
|
218
220
|
resource: 'audio',
|
|
219
221
|
status: 'error',
|