@ndla/ui 56.0.18-alpha.0 → 56.0.19-alpha.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/dist/panda.buildinfo.json +10 -8
- package/dist/styles.css +35 -29
- package/es/Article/Article.js +3 -3
- package/es/Article/ArticleByline.js +5 -4
- package/es/Article/index.js +1 -2
- package/es/AudioPlayer/AudioPlayer.js +1 -1
- package/es/CampaignBlock/CampaignBlock.js +25 -13
- package/es/Concept/Concept.js +4 -0
- package/es/ContactBlock/ContactBlock.js +0 -1
- package/es/ContentTypeBadge/ContentTypeBadgeNew.js +0 -3
- package/es/ContentTypeHero/ContentTypeHero.js +2 -8
- package/es/Embed/AudioEmbed.js +3 -0
- package/es/Embed/BrightcoveEmbed.js +5 -1
- package/es/Embed/ConceptEmbed.js +19 -10
- package/es/Embed/ConceptListEmbed.js +2 -1
- package/es/Embed/ImageEmbed.js +3 -0
- package/es/Embed/index.js +1 -0
- package/es/FileList/FileList.js +5 -1
- package/es/FileList/PdfFile.js +6 -1
- package/es/FileList/index.js +1 -1
- package/es/Grid/Grid.js +0 -1
- package/es/LicenseByline/EmbedByline.js +4 -1
- package/es/TreeStructure/TreeStructure.js +0 -2
- package/es/i18n/index.js +1 -1
- package/es/i18n/useComponentTranslations.js +61 -0
- package/es/index.js +7 -5
- package/es/locale/messages-en.js +23 -1
- package/es/locale/messages-nb.js +23 -1
- package/es/locale/messages-nn.js +71 -49
- package/es/locale/messages-se.js +23 -1
- package/es/locale/messages-sma.js +23 -1
- package/es/styles.css +35 -29
- package/es/utils/licenseAttributes.js +18 -0
- package/lib/Article/Article.js +3 -3
- package/lib/Article/ArticleByline.js +5 -4
- package/lib/Article/index.d.ts +0 -1
- package/lib/Article/index.js +1 -8
- package/lib/AudioPlayer/AudioPlayer.js +1 -1
- package/lib/CampaignBlock/CampaignBlock.js +25 -13
- package/lib/Concept/Concept.d.ts +1 -0
- package/lib/Concept/Concept.js +4 -0
- package/lib/ContactBlock/ContactBlock.js +0 -1
- package/lib/ContentTypeBadge/ContentTypeBadgeNew.js +0 -3
- package/lib/ContentTypeHero/ContentTypeHero.js +2 -7
- package/lib/Embed/AudioEmbed.js +3 -0
- package/lib/Embed/BrightcoveEmbed.d.ts +2 -1
- package/lib/Embed/BrightcoveEmbed.js +5 -1
- package/lib/Embed/ConceptEmbed.d.ts +1 -0
- package/lib/Embed/ConceptEmbed.js +19 -10
- package/lib/Embed/ConceptListEmbed.js +2 -1
- package/lib/Embed/ImageEmbed.js +3 -0
- package/lib/Embed/index.d.ts +1 -0
- package/lib/Embed/index.js +7 -0
- package/lib/FileList/FileList.d.ts +3 -1
- package/lib/FileList/FileList.js +5 -1
- package/lib/FileList/PdfFile.js +6 -1
- package/lib/FileList/index.d.ts +1 -1
- package/lib/FileList/index.js +6 -0
- package/lib/Grid/Grid.js +0 -1
- package/lib/LicenseByline/EmbedByline.js +4 -1
- package/lib/TreeStructure/TreeStructure.js +0 -2
- package/lib/i18n/index.d.ts +1 -1
- package/lib/i18n/index.js +12 -0
- package/lib/i18n/useComponentTranslations.d.ts +31 -0
- package/lib/i18n/useComponentTranslations.js +64 -2
- package/lib/index.d.ts +6 -4
- package/lib/index.js +38 -6
- package/lib/locale/messages-en.d.ts +22 -0
- package/lib/locale/messages-en.js +23 -1
- package/lib/locale/messages-nb.d.ts +22 -0
- package/lib/locale/messages-nb.js +23 -1
- package/lib/locale/messages-nn.d.ts +22 -0
- package/lib/locale/messages-nn.js +71 -49
- package/lib/locale/messages-se.d.ts +22 -0
- package/lib/locale/messages-se.js +23 -1
- package/lib/locale/messages-sma.d.ts +22 -0
- package/lib/locale/messages-sma.js +23 -1
- package/lib/styles.css +35 -29
- package/lib/utils/licenseAttributes.d.ts +16 -0
- package/lib/utils/licenseAttributes.js +25 -0
- package/package.json +8 -8
- package/src/Article/Article.tsx +7 -6
- package/src/Article/ArticleByline.tsx +2 -3
- package/src/Article/index.ts +0 -2
- package/src/AudioPlayer/AudioPlayer.tsx +5 -3
- package/src/CampaignBlock/CampaignBlock.tsx +23 -12
- package/src/Concept/Concept.tsx +6 -2
- package/src/ContactBlock/ContactBlock.tsx +0 -1
- package/src/ContentTypeBadge/ContentTypeBadgeNew.tsx +0 -3
- package/src/ContentTypeHero/ContentTypeHero.tsx +2 -7
- package/src/Embed/AudioEmbed.tsx +4 -1
- package/src/Embed/BrightcoveEmbed.tsx +6 -3
- package/src/Embed/ConceptEmbed.tsx +17 -10
- package/src/Embed/ConceptListEmbed.tsx +1 -0
- package/src/Embed/ImageEmbed.tsx +4 -0
- package/src/Embed/index.ts +1 -0
- package/src/FileList/FileList.tsx +16 -10
- package/src/FileList/PdfFile.tsx +8 -2
- package/src/FileList/index.ts +1 -1
- package/src/Grid/Grid.tsx +0 -1
- package/src/LicenseByline/EmbedByline.tsx +2 -1
- package/src/TreeStructure/TreeStructure.tsx +0 -1
- package/src/i18n/index.ts +2 -0
- package/src/i18n/useComponentTranslations.ts +78 -0
- package/src/index.ts +8 -2
- package/src/locale/messages-en.ts +24 -1
- package/src/locale/messages-nb.ts +24 -1
- package/src/locale/messages-nn.ts +72 -49
- package/src/locale/messages-se.ts +24 -1
- package/src/locale/messages-sma.ts +24 -1
- package/src/utils/licenseAttributes.ts +23 -0
- package/es/Article/ArticleParagraph.js +0 -23
- package/lib/Article/ArticleParagraph.d.ts +0 -13
- package/lib/Article/ArticleParagraph.js +0 -30
- package/src/Article/ArticleParagraph.tsx +0 -27
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ndla/ui",
|
|
3
|
-
"version": "56.0.
|
|
3
|
+
"version": "56.0.19-alpha.0",
|
|
4
4
|
"description": "UI component library for NDLA",
|
|
5
5
|
"license": "GPL-3.0",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -34,11 +34,11 @@
|
|
|
34
34
|
],
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@ndla/core": "^5.0.2",
|
|
37
|
-
"@ndla/icons": "^8.0.
|
|
38
|
-
"@ndla/licenses": "^8.0.
|
|
39
|
-
"@ndla/primitives": "^1.0.
|
|
40
|
-
"@ndla/safelink": "^7.0.
|
|
41
|
-
"@ndla/styled-system": "^0.0.
|
|
37
|
+
"@ndla/icons": "^8.0.13-alpha.0",
|
|
38
|
+
"@ndla/licenses": "^8.0.2-alpha.0",
|
|
39
|
+
"@ndla/primitives": "^1.0.17-alpha.0",
|
|
40
|
+
"@ndla/safelink": "^7.0.17-alpha.0",
|
|
41
|
+
"@ndla/styled-system": "^0.0.15",
|
|
42
42
|
"@ndla/util": "^4.1.0",
|
|
43
43
|
"html-react-parser": "^5.1.8",
|
|
44
44
|
"i18next-browser-languagedetector": "^7.1.0"
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"react-router-dom": "> 6.0.0"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"@ndla/preset-panda": "^0.0.
|
|
56
|
+
"@ndla/preset-panda": "^0.0.23",
|
|
57
57
|
"@ndla/types-backend": "^0.2.86",
|
|
58
58
|
"@ndla/types-embed": "^5.0.1-alpha.0",
|
|
59
59
|
"@pandacss/dev": "^0.45.2",
|
|
@@ -66,5 +66,5 @@
|
|
|
66
66
|
"publishConfig": {
|
|
67
67
|
"access": "public"
|
|
68
68
|
},
|
|
69
|
-
"gitHead": "
|
|
69
|
+
"gitHead": "58687255a5be26e49b6a21d830cf0af5be37b9fa"
|
|
70
70
|
}
|
package/src/Article/Article.tsx
CHANGED
|
@@ -32,7 +32,6 @@ const StyledArticleWrapper = styled(
|
|
|
32
32
|
background: "background.default",
|
|
33
33
|
display: "flex",
|
|
34
34
|
flexDirection: "column",
|
|
35
|
-
gap: "xxlarge",
|
|
36
35
|
color: "text.default",
|
|
37
36
|
alignItems: "center",
|
|
38
37
|
width: "100%",
|
|
@@ -131,11 +130,13 @@ export const ArticleTitle = ({
|
|
|
131
130
|
return (
|
|
132
131
|
<ArticleHeader>
|
|
133
132
|
<ArticleHGroup>
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
133
|
+
{(!!contentType || !!heartButton) && (
|
|
134
|
+
<StyledStack justify="space-between" align="center" direction="row" gap="small">
|
|
135
|
+
{!!contentType && <ContentTypeBadgeNew contentType={contentType} />}
|
|
136
|
+
{heartButton}
|
|
137
|
+
</StyledStack>
|
|
138
|
+
)}
|
|
139
|
+
<Heading textStyle="heading.medium" id={id} lang={lang} property="dct:title">
|
|
139
140
|
{title}
|
|
140
141
|
</Heading>
|
|
141
142
|
</ArticleHGroup>
|
|
@@ -36,8 +36,6 @@ const Wrapper = styled("div", {
|
|
|
36
36
|
},
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
-
// TODO: This is designed with 24px of inline padding. If you do this, most bylines will break into two lines.
|
|
40
|
-
// Should reconsider.
|
|
41
39
|
const TextWrapper = styled("div", {
|
|
42
40
|
base: {
|
|
43
41
|
display: "flex",
|
|
@@ -169,7 +167,8 @@ export const ArticleByline = ({
|
|
|
169
167
|
<LicenseWrapper>
|
|
170
168
|
{license && <LicenseLink license={license} />}
|
|
171
169
|
{showPrimaryContributors && (
|
|
172
|
-
|
|
170
|
+
//eslint-disable-next-line react/no-unknown-property
|
|
171
|
+
<span property="cc:attributionName">
|
|
173
172
|
{authors.length > 0 &&
|
|
174
173
|
`${t("article.authorsLabel", {
|
|
175
174
|
names: renderContributors(authors, t),
|
package/src/Article/index.ts
CHANGED
|
@@ -190,9 +190,11 @@ const AudioPlayer = ({ src, title, subtitle, speech, description, img, textVersi
|
|
|
190
190
|
{showFullDescription || description.length < DESCRIPTION_MAX_LENGTH
|
|
191
191
|
? description
|
|
192
192
|
: `${truncatedDescription}...`}
|
|
193
|
-
|
|
194
|
-
{
|
|
195
|
-
|
|
193
|
+
{description.length > DESCRIPTION_MAX_LENGTH && (
|
|
194
|
+
<Button variant="link" onClick={() => setShowFullDescription((p) => !p)}>
|
|
195
|
+
{t(`audio.${showFullDescription ? "readLessDescriptionLabel" : "readMoreDescriptionLabel"}`)}
|
|
196
|
+
</Button>
|
|
197
|
+
)}
|
|
196
198
|
</Text>
|
|
197
199
|
)}
|
|
198
200
|
{!!textVersion && !!img && textVersionButton}
|
|
@@ -36,21 +36,33 @@ interface Props {
|
|
|
36
36
|
|
|
37
37
|
const Container = styled("div", {
|
|
38
38
|
base: {
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
display: "grid",
|
|
40
|
+
gridTemplateColumns: "1fr",
|
|
41
41
|
gap: "medium",
|
|
42
|
-
flexDirection: "column",
|
|
43
42
|
border: "1px solid",
|
|
44
43
|
borderColor: "stroke.default",
|
|
45
44
|
backgroundColor: "background.default",
|
|
46
45
|
borderRadius: "xsmall",
|
|
47
46
|
boxShadow: "full",
|
|
48
|
-
marginBlockEnd: "4xsmall",
|
|
49
47
|
overflow: "hidden",
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
},
|
|
49
|
+
variants: {
|
|
50
|
+
imageSide: {
|
|
51
|
+
left: {
|
|
52
|
+
tabletWide: {
|
|
53
|
+
gridTemplateColumns: "minmax(230px, 455px) auto", //required for campaign block in myNdla
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
right: {
|
|
57
|
+
tabletWide: {
|
|
58
|
+
gridTemplateColumns: "auto minmax(230px, 455px)", //required for campaign block in myNdla
|
|
59
|
+
},
|
|
60
|
+
},
|
|
52
61
|
},
|
|
53
62
|
},
|
|
63
|
+
defaultVariants: {
|
|
64
|
+
imageSide: "left",
|
|
65
|
+
},
|
|
54
66
|
});
|
|
55
67
|
|
|
56
68
|
const LinkText = styled(Text, {
|
|
@@ -78,15 +90,14 @@ const LinkHeader = styled(Text, {
|
|
|
78
90
|
|
|
79
91
|
const StyledImg = styled("img", {
|
|
80
92
|
base: {
|
|
81
|
-
alignSelf: "center",
|
|
82
93
|
objectFit: "cover",
|
|
83
94
|
width: "100%",
|
|
84
95
|
height: "215px",
|
|
85
|
-
|
|
86
|
-
height: "
|
|
96
|
+
tablet: {
|
|
97
|
+
height: "265px",
|
|
87
98
|
},
|
|
88
99
|
tabletWide: {
|
|
89
|
-
|
|
100
|
+
height: "340px",
|
|
90
101
|
},
|
|
91
102
|
},
|
|
92
103
|
});
|
|
@@ -94,7 +105,6 @@ const StyledImg = styled("img", {
|
|
|
94
105
|
const ContentWrapper = styled("div", {
|
|
95
106
|
base: {
|
|
96
107
|
width: "100%",
|
|
97
|
-
position: "relative",
|
|
98
108
|
display: "flex",
|
|
99
109
|
flexDirection: "column",
|
|
100
110
|
gap: "medium",
|
|
@@ -102,6 +112,7 @@ const ContentWrapper = styled("div", {
|
|
|
102
112
|
justifyContent: "center",
|
|
103
113
|
paddingBlock: "medium",
|
|
104
114
|
paddingInline: "medium",
|
|
115
|
+
minWidth: "270px", //required for campaign block in myNdla
|
|
105
116
|
},
|
|
106
117
|
});
|
|
107
118
|
|
|
@@ -147,7 +158,7 @@ const CampaignBlock = ({
|
|
|
147
158
|
const imageComponent = image && <StyledImg src={`${image.src}?width=455`} height={340} width={455} alt={image.alt} />;
|
|
148
159
|
const HeaderComponent = url?.url ? LinkHeader : Text;
|
|
149
160
|
return (
|
|
150
|
-
<Container className={className} data-embed-type="campaign-block">
|
|
161
|
+
<Container className={className} data-embed-type="campaign-block" imageSide={imageSide}>
|
|
151
162
|
{imageSide === "left" && imageComponent}
|
|
152
163
|
<ContentWrapper>
|
|
153
164
|
<MaybeLinkText url={url?.url} path={path}>
|
package/src/Concept/Concept.tsx
CHANGED
|
@@ -13,6 +13,7 @@ import { IDraftCopyright as ConceptCopyright } from "@ndla/types-backend/concept
|
|
|
13
13
|
import { ConceptVisualElementMeta } from "@ndla/types-embed";
|
|
14
14
|
import { BrightcoveEmbed, ExternalEmbed, H5pEmbed, IframeEmbed, ImageEmbed } from "../Embed";
|
|
15
15
|
import { EmbedByline } from "../LicenseByline/EmbedByline";
|
|
16
|
+
import { licenseAttributes } from "../utils/licenseAttributes";
|
|
16
17
|
|
|
17
18
|
export interface ConceptProps extends ComponentPropsWithRef<"figure"> {
|
|
18
19
|
copyright?: ConceptCopyright;
|
|
@@ -20,6 +21,7 @@ export interface ConceptProps extends ComponentPropsWithRef<"figure"> {
|
|
|
20
21
|
lang?: string;
|
|
21
22
|
title?: string;
|
|
22
23
|
children?: ReactNode;
|
|
24
|
+
source?: string;
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
const StyledFigure = styled(Figure, {
|
|
@@ -43,9 +45,11 @@ const ContentWrapper = styled("div", {
|
|
|
43
45
|
// TODO: Figure out if we need to support headerButtons.
|
|
44
46
|
|
|
45
47
|
export const Concept = forwardRef<HTMLElement, ConceptProps>(
|
|
46
|
-
({ copyright, visualElement, lang, children, title, ...rest }, ref) => {
|
|
48
|
+
({ copyright, visualElement, lang, children, title, source, ...rest }, ref) => {
|
|
49
|
+
const licenseProps = licenseAttributes(copyright?.license?.license, lang, source);
|
|
50
|
+
|
|
47
51
|
return (
|
|
48
|
-
<StyledFigure ref={ref} {...rest}>
|
|
52
|
+
<StyledFigure ref={ref} {...rest} {...licenseProps}>
|
|
49
53
|
<ContentWrapper lang={lang}>
|
|
50
54
|
{!!title && (
|
|
51
55
|
<>
|
|
@@ -42,12 +42,9 @@ export const contentTypeToBadgeVariantMap: Record<ContentType, BadgeVariant> = {
|
|
|
42
42
|
[contentTypes.SOURCE_MATERIAL]: "brand1",
|
|
43
43
|
[contentTypes.LEARNING_PATH]: "brand3",
|
|
44
44
|
[contentTypes.TOPIC]: "neutral",
|
|
45
|
-
// TODO: Verify this color
|
|
46
45
|
[contentTypes.MULTIDISCIPLINARY]: "neutral",
|
|
47
46
|
[contentTypes.CONCEPT]: "brand1",
|
|
48
|
-
// TODO: Verify this color
|
|
49
47
|
[contentTypes.EXTERNAL]: "brand2",
|
|
50
|
-
// TODO: Verify resourceEmbedTypeMapping colors
|
|
51
48
|
[contentTypes.IMAGE]: "brand1",
|
|
52
49
|
[contentTypes.AUDIO]: "brand1",
|
|
53
50
|
[contentTypes.PODCAST]: "brand1",
|
|
@@ -11,23 +11,18 @@ import { Hero, HeroProps, HeroVariant } from "@ndla/primitives";
|
|
|
11
11
|
import { ContentType } from "../ContentTypeBadge/ContentTypeBadgeNew";
|
|
12
12
|
import * as contentTypes from "../model/ContentType";
|
|
13
13
|
|
|
14
|
-
// TODO: Figure out what to do with frontpage articles. If anything...
|
|
15
|
-
// Also, verify all of these colors.
|
|
16
14
|
export const contentTypeToHeroMap: Record<ContentType, HeroVariant> = {
|
|
17
15
|
[contentTypes.SUBJECT_MATERIAL]: "primary",
|
|
18
|
-
[contentTypes.TASKS_AND_ACTIVITIES]: "
|
|
16
|
+
[contentTypes.TASKS_AND_ACTIVITIES]: "brand2Bold",
|
|
19
17
|
[contentTypes.ASSESSMENT_RESOURCES]: "brand2",
|
|
20
18
|
// This will never happen
|
|
21
19
|
[contentTypes.SUBJECT]: "primary",
|
|
22
20
|
[contentTypes.SOURCE_MATERIAL]: "brand1",
|
|
23
21
|
// This will never happen
|
|
24
22
|
[contentTypes.LEARNING_PATH]: "primary",
|
|
25
|
-
// TODO: This needs a color
|
|
26
23
|
[contentTypes.TOPIC]: "neutral",
|
|
27
|
-
|
|
28
|
-
[contentTypes.MULTIDISCIPLINARY]: "brand4",
|
|
24
|
+
[contentTypes.MULTIDISCIPLINARY]: "primary",
|
|
29
25
|
[contentTypes.CONCEPT]: "brand1Subtle",
|
|
30
|
-
// TODO: No clue what this'll be. Maybe unused?
|
|
31
26
|
[contentTypes.EXTERNAL]: "primary",
|
|
32
27
|
[contentTypes.IMAGE]: "primary",
|
|
33
28
|
[contentTypes.AUDIO]: "primary",
|
package/src/Embed/AudioEmbed.tsx
CHANGED
|
@@ -12,6 +12,7 @@ import EmbedErrorPlaceholder from "./EmbedErrorPlaceholder";
|
|
|
12
12
|
import { Author } from "./ImageEmbed";
|
|
13
13
|
import AudioPlayer from "../AudioPlayer";
|
|
14
14
|
import { EmbedByline } from "../LicenseByline";
|
|
15
|
+
import { licenseAttributes } from "../utils/licenseAttributes";
|
|
15
16
|
|
|
16
17
|
interface Props {
|
|
17
18
|
embed: AudioMetaData;
|
|
@@ -42,8 +43,10 @@ const AudioEmbed = ({ embed, lang }: Props) => {
|
|
|
42
43
|
|
|
43
44
|
const img = coverPhoto && { url: coverPhoto.url, alt: coverPhoto.altText };
|
|
44
45
|
|
|
46
|
+
const licenseProps = licenseAttributes(data.copyright.license.license, lang, embedData.url);
|
|
47
|
+
|
|
45
48
|
return (
|
|
46
|
-
<Figure lang={lang} data-embed-type={type}>
|
|
49
|
+
<Figure lang={lang} data-embed-type={type} {...licenseProps}>
|
|
47
50
|
<AudioPlayer
|
|
48
51
|
description={data.podcastMeta?.introduction ?? ""}
|
|
49
52
|
img={img}
|
|
@@ -15,10 +15,12 @@ import { BrightcoveEmbedData, BrightcoveMetaData, BrightcoveVideoSource } from "
|
|
|
15
15
|
import EmbedErrorPlaceholder from "./EmbedErrorPlaceholder";
|
|
16
16
|
import { RenderContext } from "./types";
|
|
17
17
|
import { EmbedByline } from "../LicenseByline";
|
|
18
|
+
import { licenseAttributes } from "../utils/licenseAttributes";
|
|
18
19
|
|
|
19
20
|
interface Props {
|
|
20
21
|
embed: BrightcoveMetaData;
|
|
21
22
|
renderContext?: RenderContext;
|
|
23
|
+
lang?: string;
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
const LinkedVideoButton = styled(Button, {
|
|
@@ -54,7 +56,7 @@ const getIframeProps = (data: BrightcoveEmbedData, sources: BrightcoveVideoSourc
|
|
|
54
56
|
width: source?.width ?? "640",
|
|
55
57
|
};
|
|
56
58
|
};
|
|
57
|
-
const BrightcoveEmbed = ({ embed, renderContext = "article" }: Props) => {
|
|
59
|
+
const BrightcoveEmbed = ({ embed, renderContext = "article", lang }: Props) => {
|
|
58
60
|
const [showOriginalVideo, setShowOriginalVideo] = useState(true);
|
|
59
61
|
const { t } = useTranslation();
|
|
60
62
|
const iframeRef = useRef<HTMLIFrameElement>(null);
|
|
@@ -100,8 +102,10 @@ const BrightcoveEmbed = ({ embed, renderContext = "article" }: Props) => {
|
|
|
100
102
|
? getIframeProps({ ...embedData, videoid: linkedVideoId }, data.sources)
|
|
101
103
|
: undefined;
|
|
102
104
|
|
|
105
|
+
const licenseProps = licenseAttributes(data?.copyright?.license.license, lang, embedData.pageUrl);
|
|
106
|
+
|
|
103
107
|
return (
|
|
104
|
-
<Figure data-embed-type="brightcove">
|
|
108
|
+
<Figure data-embed-type="brightcove" {...licenseProps}>
|
|
105
109
|
<div className="brightcove-video">
|
|
106
110
|
<BrightcoveIframe
|
|
107
111
|
ref={iframeRef}
|
|
@@ -114,7 +118,6 @@ const BrightcoveEmbed = ({ embed, renderContext = "article" }: Props) => {
|
|
|
114
118
|
/>
|
|
115
119
|
</div>
|
|
116
120
|
<EmbedByline type="video" copyright={data.copyright!} description={parsedDescription}>
|
|
117
|
-
{/* TODO: Figure out if this button should still be here. If yes, figure out what it should look like. */}
|
|
118
121
|
{!!linkedVideoId && (
|
|
119
122
|
<LinkedVideoButton size="small" variant="secondary" onClick={() => setShowOriginalVideo((p) => !p)}>
|
|
120
123
|
{t(`figure.button.${!showOriginalVideo ? "original" : "alternative"}`)}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import parse from "html-react-parser";
|
|
10
10
|
import { forwardRef, useMemo } from "react";
|
|
11
|
+
import { Portal } from "@ark-ui/react";
|
|
11
12
|
import { PopoverContent, PopoverRoot, PopoverTrigger } from "@ndla/primitives";
|
|
12
13
|
import { styled } from "@ndla/styled-system/jsx";
|
|
13
14
|
import { ConceptMetaData } from "@ndla/types-embed";
|
|
@@ -29,6 +30,8 @@ interface Props extends BaseProps {
|
|
|
29
30
|
const StyledPopoverContent = styled(PopoverContent, {
|
|
30
31
|
base: {
|
|
31
32
|
width: "surface.xlarge",
|
|
33
|
+
maxHeight: "50vh",
|
|
34
|
+
overflowY: "auto",
|
|
32
35
|
},
|
|
33
36
|
});
|
|
34
37
|
|
|
@@ -60,6 +63,7 @@ export const ConceptEmbed = ({ embed, renderContext, lang }: Props) => {
|
|
|
60
63
|
visualElement={visualElement}
|
|
61
64
|
lang={lang}
|
|
62
65
|
title={concept.title.title}
|
|
66
|
+
source={concept.source}
|
|
63
67
|
>
|
|
64
68
|
{parsedContent}
|
|
65
69
|
</InlineConcept>
|
|
@@ -72,6 +76,7 @@ export const ConceptEmbed = ({ embed, renderContext, lang }: Props) => {
|
|
|
72
76
|
visualElement={visualElement}
|
|
73
77
|
lang={lang}
|
|
74
78
|
title={renderContext === "embed" ? undefined : concept.title.title}
|
|
79
|
+
source={concept.source}
|
|
75
80
|
>
|
|
76
81
|
{parsedContent}
|
|
77
82
|
</BlockConcept>
|
|
@@ -80,21 +85,23 @@ export const ConceptEmbed = ({ embed, renderContext, lang }: Props) => {
|
|
|
80
85
|
|
|
81
86
|
export interface InlineConceptProps extends ConceptProps, BaseProps {
|
|
82
87
|
linkText?: string;
|
|
88
|
+
source?: string;
|
|
83
89
|
}
|
|
84
90
|
|
|
85
91
|
export const InlineConcept = forwardRef<HTMLSpanElement, InlineConceptProps>(
|
|
86
|
-
({ linkText, copyright, visualElement, lang, children, title, ...rest }, ref) => (
|
|
92
|
+
({ linkText, copyright, visualElement, lang, children, title, source, ...rest }, ref) => (
|
|
87
93
|
<PopoverRoot>
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
</InlineTriggerButton>
|
|
94
|
+
{/* @ts-expect-error placing ref and rest on popover trigger somehow removes a bug where the popover target becomes a bit bigger */}
|
|
95
|
+
<PopoverTrigger asChild ref={ref} {...rest}>
|
|
96
|
+
<InlineTriggerButton>{linkText}</InlineTriggerButton>
|
|
92
97
|
</PopoverTrigger>
|
|
93
|
-
<
|
|
94
|
-
<
|
|
95
|
-
{
|
|
96
|
-
|
|
97
|
-
|
|
98
|
+
<Portal>
|
|
99
|
+
<StyledPopoverContent>
|
|
100
|
+
<Concept copyright={copyright} visualElement={visualElement} lang={lang} title={title} source={source}>
|
|
101
|
+
{children}
|
|
102
|
+
</Concept>
|
|
103
|
+
</StyledPopoverContent>
|
|
104
|
+
</Portal>
|
|
98
105
|
</PopoverRoot>
|
|
99
106
|
),
|
|
100
107
|
);
|
package/src/Embed/ImageEmbed.tsx
CHANGED
|
@@ -17,6 +17,7 @@ import { ImageEmbedData, ImageMetaData } from "@ndla/types-embed";
|
|
|
17
17
|
import EmbedErrorPlaceholder from "./EmbedErrorPlaceholder";
|
|
18
18
|
import { RenderContext } from "./types";
|
|
19
19
|
import { EmbedByline } from "../LicenseByline";
|
|
20
|
+
import { licenseAttributes } from "../utils/licenseAttributes";
|
|
20
21
|
|
|
21
22
|
interface Props {
|
|
22
23
|
embed: ImageMetaData;
|
|
@@ -247,12 +248,15 @@ const ImageEmbed = ({ embed, previewAlt, lang, renderContext = "article", childr
|
|
|
247
248
|
setImageSizes((sizes) => (!sizes ? expandedSizes : undefined));
|
|
248
249
|
};
|
|
249
250
|
|
|
251
|
+
const licenseProps = licenseAttributes(data.copyright.license.license, lang, embedData.url);
|
|
252
|
+
|
|
250
253
|
// TODO: Check how this works with `children`. Will only be important for ED
|
|
251
254
|
return (
|
|
252
255
|
<StyledFigure
|
|
253
256
|
float={figureProps?.float}
|
|
254
257
|
size={imageSizes ? "full" : figureProps?.size ?? "medium"}
|
|
255
258
|
data-embed-type="image"
|
|
259
|
+
{...licenseProps}
|
|
256
260
|
>
|
|
257
261
|
{children}
|
|
258
262
|
<ImageWrapper border={embedData.border === "true"} expandable={!!figureProps?.float}>
|
package/src/Embed/index.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
export { default as ImageEmbed, getCrop, getFocalPoint } from "./ImageEmbed";
|
|
10
|
+
export { InlineTriggerButton } from "./InlineTriggerButton";
|
|
10
11
|
export { default as AudioEmbed } from "./AudioEmbed";
|
|
11
12
|
export { default as H5pEmbed } from "./H5pEmbed";
|
|
12
13
|
export { default as ExternalEmbed } from "./ExternalEmbed";
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import { ComponentPropsWithoutRef } from "react";
|
|
10
10
|
import { useTranslation } from "react-i18next";
|
|
11
|
+
import { ark } from "@ark-ui/react";
|
|
11
12
|
import { Heading } from "@ndla/primitives";
|
|
12
13
|
import { styled } from "@ndla/styled-system/jsx";
|
|
13
14
|
|
|
@@ -21,19 +22,24 @@ export const FileListWrapper = styled("div", {
|
|
|
21
22
|
},
|
|
22
23
|
});
|
|
23
24
|
|
|
24
|
-
export const FileListItem = styled(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
export const FileListItem = styled(
|
|
26
|
+
ark.li,
|
|
27
|
+
{
|
|
28
|
+
base: {
|
|
29
|
+
listStyle: "none",
|
|
30
|
+
background: "surface.infoSubtle",
|
|
31
|
+
borderBlockEnd: "1px solid",
|
|
32
|
+
borderColor: "stroke.default",
|
|
33
|
+
display: "flex",
|
|
34
|
+
justifyContent: "space-between",
|
|
31
35
|
|
|
32
|
-
|
|
33
|
-
|
|
36
|
+
_hover: {
|
|
37
|
+
backgroundColor: "surface.infoSubtle.hover",
|
|
38
|
+
},
|
|
34
39
|
},
|
|
35
40
|
},
|
|
36
|
-
}
|
|
41
|
+
{ baseComponent: true },
|
|
42
|
+
);
|
|
37
43
|
|
|
38
44
|
export const FileListEmbed = ({ children, ...rest }: Props) => {
|
|
39
45
|
const { t } = useTranslation();
|
package/src/FileList/PdfFile.tsx
CHANGED
|
@@ -20,6 +20,12 @@ const StyledIframe = styled("iframe", {
|
|
|
20
20
|
},
|
|
21
21
|
});
|
|
22
22
|
|
|
23
|
+
const StyledListElement = styled("li", {
|
|
24
|
+
base: {
|
|
25
|
+
listStyle: "none",
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
|
|
23
29
|
const StyledFigure = styled(Figure, {
|
|
24
30
|
base: {
|
|
25
31
|
display: "flex",
|
|
@@ -30,13 +36,13 @@ const StyledFigure = styled(Figure, {
|
|
|
30
36
|
|
|
31
37
|
export const PdfFile = ({ title, url }: Props) => {
|
|
32
38
|
return (
|
|
33
|
-
<
|
|
39
|
+
<StyledListElement>
|
|
34
40
|
<StyledFigure>
|
|
35
41
|
<Heading asChild consumeCss textStyle="title.medium">
|
|
36
42
|
<h4>{title}</h4>
|
|
37
43
|
</Heading>
|
|
38
44
|
<StyledIframe title={title} height="1050" src={url} />
|
|
39
45
|
</StyledFigure>
|
|
40
|
-
</
|
|
46
|
+
</StyledListElement>
|
|
41
47
|
);
|
|
42
48
|
};
|
package/src/FileList/index.ts
CHANGED
package/src/Grid/Grid.tsx
CHANGED
|
@@ -233,7 +233,8 @@ export const LicenseContainerContent = ({ children, copyright, type }: LicenseCo
|
|
|
233
233
|
<>
|
|
234
234
|
{children}
|
|
235
235
|
{` ${t(`embed.type.${type}`)}${captionAuthors.length ? ": " : ""}`}
|
|
236
|
-
{
|
|
236
|
+
{/*eslint-disable-next-line react/no-unknown-property */}
|
|
237
|
+
<span property="cc:attributionName">{captionAuthors.map((author) => author.name).join(", ")}</span>
|
|
237
238
|
{license ? (
|
|
238
239
|
<>
|
|
239
240
|
{" / "}
|
|
@@ -293,7 +293,6 @@ const TreeStructureItem = ({ folder, targetResource }: TreeStructureItemProps) =
|
|
|
293
293
|
|
|
294
294
|
const FolderIcon = folder.status === "shared" ? StyledFolderUserLine : StyledFolderLine;
|
|
295
295
|
|
|
296
|
-
// TODO: Pressing enter selects the item and closes the popover immediately. Do we actually want this? Old behavior.
|
|
297
296
|
const onKeyDown = useCallback(
|
|
298
297
|
(e: KeyboardEvent<HTMLElement>) => {
|
|
299
298
|
if (e.key === "Enter") {
|
package/src/i18n/index.ts
CHANGED
|
@@ -11,6 +11,10 @@ import type { ComboboxCollectionItem } from "@ark-ui/react";
|
|
|
11
11
|
import type { ComboboxRootProps, PaginationRootProps, TagsInputRootProps } from "@ndla/primitives";
|
|
12
12
|
import { TagSelectorRootProps } from "../TagSelector/TagSelector";
|
|
13
13
|
|
|
14
|
+
type DeepPartial<T> = {
|
|
15
|
+
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
|
|
16
|
+
};
|
|
17
|
+
|
|
14
18
|
export const useTagsInputTranslations = (
|
|
15
19
|
translations?: Partial<TagsInputRootProps["translations"]>,
|
|
16
20
|
): TagsInputRootProps["translations"] => {
|
|
@@ -70,3 +74,77 @@ export const usePaginationTranslations = (
|
|
|
70
74
|
...translations,
|
|
71
75
|
};
|
|
72
76
|
};
|
|
77
|
+
|
|
78
|
+
// TODO: Deduplicate this and place it somewhere smart. Maybe core?
|
|
79
|
+
interface AudioSearchTranslations {
|
|
80
|
+
searchPlaceholder: string;
|
|
81
|
+
searchButtonTitle: string;
|
|
82
|
+
useAudio: string;
|
|
83
|
+
noResults: string;
|
|
84
|
+
paginationTranslations: PaginationRootProps["translations"];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
interface MetadataTranslations {
|
|
88
|
+
creatorsLabel: string;
|
|
89
|
+
license: string;
|
|
90
|
+
caption: string;
|
|
91
|
+
altText: string;
|
|
92
|
+
modelRelease: string;
|
|
93
|
+
tags: string;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
interface ImageSearchTranslations {
|
|
97
|
+
searchPlaceholder: string;
|
|
98
|
+
searchButtonTitle: string;
|
|
99
|
+
useImageTitle: string;
|
|
100
|
+
close: string;
|
|
101
|
+
imageMetadata: MetadataTranslations;
|
|
102
|
+
paginationTranslations: PaginationRootProps["translations"];
|
|
103
|
+
missingTitleFallback?: string;
|
|
104
|
+
checkboxLabel?: string;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export const useImageSearchTranslations = (
|
|
108
|
+
translations: DeepPartial<ImageSearchTranslations> = {},
|
|
109
|
+
): ImageSearchTranslations => {
|
|
110
|
+
const { t } = useTranslation("translation", { keyPrefix: "component.imageSearch" });
|
|
111
|
+
const paginationTranslations = usePaginationTranslations();
|
|
112
|
+
|
|
113
|
+
const { imageMetadata, paginationTranslations: fallbackPaginationTranslations, ...remaining } = translations;
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
close: t("close"),
|
|
117
|
+
searchPlaceholder: t("searchPlaceholder"),
|
|
118
|
+
searchButtonTitle: t("searchButtonTitle"),
|
|
119
|
+
useImageTitle: t("useImageTitle"),
|
|
120
|
+
imageMetadata: {
|
|
121
|
+
creatorsLabel: t("imageMetadata.creatorsLabel"),
|
|
122
|
+
license: t("imageMetadata.license"),
|
|
123
|
+
caption: t("imageMetadata.caption"),
|
|
124
|
+
altText: t("imageMetadata.altText"),
|
|
125
|
+
modelRelease: t("imageMetadata.modelRelease"),
|
|
126
|
+
tags: t("imageMetadata.tags"),
|
|
127
|
+
...imageMetadata,
|
|
128
|
+
},
|
|
129
|
+
paginationTranslations: { ...paginationTranslations, ...fallbackPaginationTranslations },
|
|
130
|
+
...remaining,
|
|
131
|
+
};
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export const useAudioSearchTranslations = (
|
|
135
|
+
translations: DeepPartial<AudioSearchTranslations> = {},
|
|
136
|
+
): AudioSearchTranslations => {
|
|
137
|
+
const { t } = useTranslation("translation", { keyPrefix: "component.audioSearch" });
|
|
138
|
+
const paginationTranslations = usePaginationTranslations();
|
|
139
|
+
|
|
140
|
+
const { paginationTranslations: fallbackPaginationTranslations, ...remaining } = translations;
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
searchPlaceholder: t("searchPlaceholder"),
|
|
144
|
+
searchButtonTitle: t("searchButtonTitle"),
|
|
145
|
+
useAudio: t("useAudio"),
|
|
146
|
+
noResults: t("noResults"),
|
|
147
|
+
paginationTranslations: { ...paginationTranslations, ...fallbackPaginationTranslations },
|
|
148
|
+
...remaining,
|
|
149
|
+
};
|
|
150
|
+
};
|