@ndla/ui 55.0.12-alpha.0 → 55.0.13-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/all-aout.js +0 -0
- package/dist/all.css +1 -0
- package/dist/panda.buildinfo.json +170 -0
- package/dist/styles.css +686 -0
- package/es/Article/Article.js +3 -4
- package/es/Article/ArticleByline.js +9 -9
- package/es/Article/ArticleFootNotes.js +4 -4
- package/es/AudioPlayer/AudioPlayer.js +142 -163
- package/es/AudioPlayer/Controls.js +187 -203
- package/es/AudioPlayer/SpeechControl.js +13 -11
- package/es/BlogPost/BlogPost.js +85 -23
- package/es/CampaignBlock/CampaignBlock.js +3 -4
- package/es/CodeBlock/CodeBlock.js +88 -96
- package/es/ContactBlock/ContactBlock.js +54 -40
- package/es/ContentLoader/index.js +7 -7
- package/es/CopyParagraphButton/CopyParagraphButton.js +4 -4
- package/es/Embed/AudioEmbed.js +5 -9
- package/es/Embed/BrightcoveEmbed.js +12 -15
- package/es/Embed/CodeEmbed.js +58 -10
- package/es/Embed/ConceptEmbed.js +15 -20
- package/es/Embed/ContentLinkEmbed.js +1 -1
- package/es/Embed/EmbedErrorPlaceholder.js +32 -17
- package/es/Embed/ExternalEmbed.js +7 -10
- package/es/Embed/FootnoteEmbed.js +3 -3
- package/es/Embed/H5pEmbed.js +1 -2
- package/es/Embed/IframeEmbed.js +8 -9
- package/es/Embed/ImageEmbed.js +167 -122
- package/es/Embed/RelatedContentEmbed.js +8 -10
- package/es/Embed/UuDisclaimerEmbed.js +2 -2
- package/es/Embed/conceptComponents.js +9 -9
- package/es/ErrorMessage/ErrorMessage.js +1 -1
- package/es/FactBox/FactBox.js +2 -2
- package/es/FileList/File.js +1 -1
- package/es/FileList/Format.js +3 -3
- package/es/FrontpageArticle/FrontpageArticle.js +1 -1
- package/es/Gloss/Gloss.js +9 -11
- package/es/Gloss/GlossExample.js +3 -4
- package/es/Grid/Grid.js +1 -1
- package/es/Image/Image.js +7 -8
- package/es/Image/ImageLink.js +1 -1
- package/es/KeyFigure/KeyFigure.js +2 -2
- package/es/LanguageSelector/LanguageSelector.js +2 -2
- package/es/LetterFilter/LetterFilter.js +1 -1
- package/es/LicenseByline/EmbedByline.js +5 -6
- package/es/LicenseByline/LicenseDescription.js +1 -1
- package/es/LicenseByline/LicenseLink.js +1 -2
- package/es/Messages/MessageBox.js +1 -1
- package/es/Notion/Notion.js +2 -2
- package/es/Notion/NotionImage.js +12 -57
- package/es/RelatedArticleList/RelatedArticleList.js +3 -3
- package/es/ResourceBox/ResourceBox.js +12 -17
- package/es/Search/ActiveFilters.js +1 -1
- package/es/Search/ContentTypeResult.js +9 -6
- package/es/Search/ContentTypeResultStyles.js +1 -1
- package/es/Search/IsPathToHighlight.js +1 -1
- package/es/Search/SearchField.js +6 -8
- package/es/Search/SearchResult.js +14 -19
- package/es/Search/SearchResultSleeve.js +14 -16
- package/es/SnackBar/SnackbarProvider.js +8 -11
- package/es/TagSelector/TagSelector.js +1 -1
- package/es/TagSelector/ariaMessages.js +6 -6
- package/es/TreeStructure/AddFolderButton.js +4 -6
- package/es/TreeStructure/ComboboxButton.js +4 -7
- package/es/TreeStructure/FolderItem.js +12 -15
- package/es/TreeStructure/FolderItems.js +3 -3
- package/es/TreeStructure/TreeStructure.js +9 -12
- package/es/TreeStructure/helperFunctions.js +1 -1
- package/es/ZendeskButton/ZendeskButton.js +55 -0
- package/es/i18n/formatNestedMessages.js +1 -1
- package/es/index.js +2 -1
- package/es/locale/messages-en.js +9 -8
- package/es/locale/messages-nb.js +9 -8
- package/es/locale/messages-nn.js +9 -8
- package/es/locale/messages-se.js +9 -8
- package/es/locale/messages-sma.js +9 -8
- package/es/styles.css +686 -0
- package/es/utils/relativeUrl.js +3 -3
- package/lib/Article/Article.js +3 -4
- package/lib/Article/ArticleByline.js +9 -9
- package/lib/Article/ArticleFootNotes.js +4 -4
- package/lib/AudioPlayer/AudioPlayer.d.ts +1 -2
- package/lib/AudioPlayer/AudioPlayer.js +142 -162
- package/lib/AudioPlayer/Controls.js +190 -205
- package/lib/AudioPlayer/SpeechControl.js +13 -11
- package/lib/BlogPost/BlogPost.d.ts +2 -2
- package/lib/BlogPost/BlogPost.js +85 -24
- package/lib/CampaignBlock/CampaignBlock.js +3 -4
- package/lib/CodeBlock/CodeBlock.d.ts +5 -8
- package/lib/CodeBlock/CodeBlock.js +88 -96
- package/lib/ContactBlock/ContactBlock.js +55 -43
- package/lib/ContentLoader/index.js +7 -7
- package/lib/CopyParagraphButton/CopyParagraphButton.js +4 -4
- package/lib/Embed/AudioEmbed.js +5 -9
- package/lib/Embed/BrightcoveEmbed.js +12 -15
- package/lib/Embed/CodeEmbed.js +56 -8
- package/lib/Embed/ConceptEmbed.js +15 -20
- package/lib/Embed/ContentLinkEmbed.js +1 -1
- package/lib/Embed/EmbedErrorPlaceholder.d.ts +4 -3
- package/lib/Embed/EmbedErrorPlaceholder.js +32 -18
- package/lib/Embed/ExternalEmbed.js +7 -10
- package/lib/Embed/FootnoteEmbed.js +3 -3
- package/lib/Embed/H5pEmbed.js +1 -2
- package/lib/Embed/IframeEmbed.js +8 -9
- package/lib/Embed/ImageEmbed.d.ts +1 -2
- package/lib/Embed/ImageEmbed.js +167 -123
- package/lib/Embed/RelatedContentEmbed.js +8 -10
- package/lib/Embed/UuDisclaimerEmbed.js +2 -2
- package/lib/Embed/conceptComponents.js +9 -9
- package/lib/ErrorMessage/ErrorMessage.js +1 -1
- package/lib/FactBox/FactBox.js +2 -2
- package/lib/FileList/File.js +1 -1
- package/lib/FileList/Format.js +3 -3
- package/lib/FrontpageArticle/FrontpageArticle.js +1 -1
- package/lib/Gloss/Gloss.js +9 -11
- package/lib/Gloss/GlossExample.js +3 -4
- package/lib/Grid/Grid.js +1 -1
- package/lib/Image/Image.js +7 -8
- package/lib/Image/ImageLink.js +1 -1
- package/lib/KeyFigure/KeyFigure.js +2 -2
- package/lib/LanguageSelector/LanguageSelector.js +2 -2
- package/lib/LetterFilter/LetterFilter.js +1 -1
- package/lib/LicenseByline/EmbedByline.js +5 -6
- package/lib/LicenseByline/LicenseDescription.js +1 -1
- package/lib/LicenseByline/LicenseLink.js +1 -2
- package/lib/Messages/MessageBox.js +1 -1
- package/lib/Notion/Notion.js +2 -2
- package/lib/Notion/NotionImage.d.ts +1 -11
- package/lib/Notion/NotionImage.js +12 -59
- package/lib/RelatedArticleList/RelatedArticleList.js +3 -3
- package/lib/ResourceBox/ResourceBox.js +13 -18
- package/lib/Search/ActiveFilters.js +1 -1
- package/lib/Search/ContentTypeResult.js +9 -6
- package/lib/Search/ContentTypeResultStyles.js +1 -1
- package/lib/Search/IsPathToHighlight.js +1 -1
- package/lib/Search/SearchField.js +6 -8
- package/lib/Search/SearchResult.js +14 -19
- package/lib/Search/SearchResultSleeve.js +14 -16
- package/lib/SnackBar/SnackbarProvider.js +8 -11
- package/lib/TagSelector/TagSelector.js +1 -1
- package/lib/TagSelector/ariaMessages.js +6 -6
- package/lib/TreeStructure/AddFolderButton.js +4 -6
- package/lib/TreeStructure/ComboboxButton.js +4 -7
- package/lib/TreeStructure/FolderItem.js +12 -15
- package/lib/TreeStructure/FolderItems.js +3 -3
- package/lib/TreeStructure/TreeStructure.js +9 -12
- package/lib/TreeStructure/helperFunctions.js +1 -1
- package/lib/ZendeskButton/ZendeskButton.d.ts +19 -0
- package/lib/ZendeskButton/ZendeskButton.js +61 -0
- package/lib/i18n/formatNestedMessages.js +1 -1
- package/lib/index.d.ts +2 -0
- package/lib/index.js +7 -0
- package/lib/locale/messages-en.d.ts +1 -0
- package/lib/locale/messages-en.js +9 -8
- package/lib/locale/messages-nb.d.ts +1 -0
- package/lib/locale/messages-nb.js +9 -8
- package/lib/locale/messages-nn.d.ts +1 -0
- package/lib/locale/messages-nn.js +9 -8
- package/lib/locale/messages-se.d.ts +1 -0
- package/lib/locale/messages-se.js +9 -8
- package/lib/locale/messages-sma.d.ts +1 -0
- package/lib/locale/messages-sma.js +9 -8
- package/lib/styles.css +686 -0
- package/lib/types.d.ts +1 -0
- package/lib/utils/relativeUrl.js +3 -3
- package/package.json +17 -12
- package/src/AudioPlayer/AudioPlayer.tsx +139 -176
- package/src/AudioPlayer/Controls.tsx +210 -250
- package/src/AudioPlayer/SpeechControl.tsx +9 -7
- package/src/BlogPost/BlogPost.tsx +82 -58
- package/src/CodeBlock/CodeBlock.stories.tsx +0 -43
- package/src/CodeBlock/CodeBlock.tsx +91 -202
- package/src/ContactBlock/ContactBlock.tsx +10 -2
- package/src/Embed/CodeEmbed.stories.tsx +95 -0
- package/src/Embed/CodeEmbed.tsx +62 -7
- package/src/Embed/ConceptEmbed.tsx +1 -9
- package/src/Embed/EmbedErrorPlaceholder.tsx +31 -28
- package/src/Embed/ImageEmbed.stories.tsx +53 -11
- package/src/Embed/ImageEmbed.tsx +162 -166
- package/src/Notion/NotionImage.tsx +4 -54
- package/src/ResourceBox/ResourceBox.tsx +3 -15
- package/src/Search/ContentTypeResult.tsx +9 -3
- package/src/Search/SearchResultSleeve.tsx +5 -2
- package/src/ZendeskButton/ZendeskButton.tsx +58 -0
- package/src/index.ts +4 -0
- package/src/locale/messages-en.ts +1 -0
- package/src/locale/messages-nb.ts +1 -0
- package/src/locale/messages-nn.ts +1 -0
- package/src/locale/messages-se.ts +1 -0
- package/src/locale/messages-sma.ts +1 -0
- package/src/types.ts +2 -0
- package/src/Image/__tests__/Image-test.tsx +0 -66
- package/src/Image/__tests__/__snapshots__/Image-test.tsx.snap +0 -194
|
@@ -6,14 +6,12 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
/** @jsxImportSource @emotion/react */
|
|
10
9
|
import parse from "html-react-parser";
|
|
11
10
|
import { useTranslation } from "react-i18next";
|
|
12
|
-
import {
|
|
13
|
-
import styled from "@emotion/styled";
|
|
14
|
-
import { breakpoints, colors, fonts, misc, mq, spacing } from "@ndla/core";
|
|
11
|
+
import { Heading } from "@ndla/primitives";
|
|
15
12
|
import { SafeLink } from "@ndla/safelink";
|
|
16
|
-
import {
|
|
13
|
+
import { styled } from "@ndla/styled-system/jsx";
|
|
14
|
+
import { HeadingLevel } from "../types";
|
|
17
15
|
import { getPossiblyRelativeUrl } from "../utils/relativeUrl";
|
|
18
16
|
|
|
19
17
|
export interface Props {
|
|
@@ -29,68 +27,94 @@ export interface Props {
|
|
|
29
27
|
path?: string;
|
|
30
28
|
}
|
|
31
29
|
|
|
32
|
-
const Container = styled(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
30
|
+
const Container = styled(
|
|
31
|
+
SafeLink,
|
|
32
|
+
{
|
|
33
|
+
base: {
|
|
34
|
+
display: "flex",
|
|
35
|
+
flexDirection: "column",
|
|
36
|
+
color: "text.default",
|
|
37
|
+
backgroundColor: "background.default",
|
|
38
|
+
gap: "medium",
|
|
39
|
+
border: "1px solid",
|
|
40
|
+
borderColor: "stroke.subtle",
|
|
41
|
+
borderRadius: "xsmall",
|
|
42
|
+
transitionDuration: "fast",
|
|
43
|
+
transitionProperty: "background-color, border-color, max-width",
|
|
44
|
+
transitionTimingFunction: "default",
|
|
45
|
+
height: "100%",
|
|
46
|
+
"&:hover, &:focus-visible": {
|
|
47
|
+
borderColor: "stroke.hover",
|
|
48
|
+
backgroundColor: "surface.actionSubtle.hover",
|
|
49
|
+
"& h1, h2, h3, h4, h5, h6": {
|
|
50
|
+
textDecoration: "underline",
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
defaultVariants: {
|
|
55
|
+
size: "normal",
|
|
56
|
+
},
|
|
57
|
+
variants: {
|
|
58
|
+
// TODO: Reconsider these sizes. Maybe they should match up with surface?
|
|
59
|
+
size: {
|
|
60
|
+
normal: {
|
|
61
|
+
paddingBlock: "medium",
|
|
62
|
+
paddingInline: "medium",
|
|
63
|
+
tabletWide: {
|
|
64
|
+
maxWidth: "350px",
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
large: {
|
|
68
|
+
paddingBlock: "xlarge",
|
|
69
|
+
paddingInline: "xxlarge",
|
|
70
|
+
tabletWide: {
|
|
71
|
+
maxWidth: "532px",
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
// TODO: Reconsider this once we handle SafeLink
|
|
78
|
+
{ baseComponent: true },
|
|
79
|
+
);
|
|
57
80
|
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
81
|
+
const AuthorContainer = styled("div", {
|
|
82
|
+
base: {
|
|
83
|
+
display: "flex",
|
|
84
|
+
alignItems: "center",
|
|
85
|
+
gap: "xsmall",
|
|
86
|
+
textTransform: "uppercase",
|
|
87
|
+
textStyle: "body.large",
|
|
88
|
+
},
|
|
89
|
+
});
|
|
65
90
|
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
91
|
+
const StyledImg = styled("img", {
|
|
92
|
+
base: {
|
|
93
|
+
borderRadius: "xsmall",
|
|
94
|
+
flex: "1",
|
|
95
|
+
objectFit: "cover",
|
|
96
|
+
width: "100%",
|
|
97
|
+
height: "100%",
|
|
98
|
+
border: "0",
|
|
99
|
+
},
|
|
100
|
+
});
|
|
75
101
|
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
border: 0;
|
|
83
|
-
`;
|
|
102
|
+
const StyledHeading = styled(Heading, {
|
|
103
|
+
base: {
|
|
104
|
+
display: "inline-block",
|
|
105
|
+
width: "fit-content",
|
|
106
|
+
},
|
|
107
|
+
});
|
|
84
108
|
|
|
85
109
|
const BlogPost = ({ title, author, url, metaImage, headingLevel: Heading = "h3", size = "normal", path }: Props) => {
|
|
86
110
|
const { t } = useTranslation();
|
|
87
111
|
const href = getPossiblyRelativeUrl(url, path);
|
|
88
112
|
const imageWidth = size === "large" ? 532 : 350;
|
|
89
113
|
return (
|
|
90
|
-
<Container data-size={size} to={href}>
|
|
91
|
-
<
|
|
92
|
-
{parse(title)}
|
|
93
|
-
</
|
|
114
|
+
<Container data-size={size} to={href} size={size}>
|
|
115
|
+
<StyledHeading className="blog-title" asChild consumeCss textStyle="title.large">
|
|
116
|
+
<Heading>{parse(title)}</Heading>
|
|
117
|
+
</StyledHeading>
|
|
94
118
|
<StyledImg src={`${metaImage.url}?width=${imageWidth}`} alt={metaImage.alt} />
|
|
95
119
|
{!!author && <AuthorContainer aria-label={t("article.writtenBy", { authors: author })}>{author}</AuthorContainer>}
|
|
96
120
|
</Container>
|
|
@@ -7,8 +7,6 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { Meta, StoryFn, StoryObj } from "@storybook/react";
|
|
10
|
-
import { IconButtonV2 } from "@ndla/button";
|
|
11
|
-
import { DeleteForever } from "@ndla/icons/editor";
|
|
12
10
|
import CodeBlock from "./CodeBlock";
|
|
13
11
|
|
|
14
12
|
export default {
|
|
@@ -19,7 +17,6 @@ export default {
|
|
|
19
17
|
inlineStories: true,
|
|
20
18
|
},
|
|
21
19
|
args: {
|
|
22
|
-
title: "CodeBlock",
|
|
23
20
|
format: "html",
|
|
24
21
|
highlightedCode: `<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>demo-content<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
|
|
25
22
|
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>Lorem ipsum<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span>
|
|
@@ -36,37 +33,6 @@ export default {
|
|
|
36
33
|
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>is simply dummy text of the printing and typesetting industry<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
|
|
37
34
|
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span>
|
|
38
35
|
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>`,
|
|
39
|
-
code: `<div class="demo-content">
|
|
40
|
-
<h2>Lorem ipsum</h2>
|
|
41
|
-
<p>
|
|
42
|
-
<b>Lorem ipsum</b><br/>
|
|
43
|
-
<span>is simply dummy text of the printing and typesetting industry</span>
|
|
44
|
-
</p>
|
|
45
|
-
<p>
|
|
46
|
-
<b>Lorem ipsum</b><br/>
|
|
47
|
-
<span>is simply dummy text of the printing and typesetting industry</span>
|
|
48
|
-
</p>
|
|
49
|
-
<p>
|
|
50
|
-
<b>Lorem ipsum</b><br/>
|
|
51
|
-
<span>is simply dummy text of the printing and typesetting industry</span>
|
|
52
|
-
</p>
|
|
53
|
-
</div>`,
|
|
54
|
-
actionButton: (
|
|
55
|
-
<IconButtonV2 aria-label="Slett" variant="ghost" colorTheme="danger">
|
|
56
|
-
<DeleteForever />
|
|
57
|
-
</IconButtonV2>
|
|
58
|
-
),
|
|
59
|
-
showCopy: true,
|
|
60
|
-
},
|
|
61
|
-
argTypes: {
|
|
62
|
-
actionButton: {
|
|
63
|
-
table: {
|
|
64
|
-
disable: true,
|
|
65
|
-
type: {
|
|
66
|
-
detail: "Takes any ReactNode, but as the name implies: use a button component, preferably an icon-button",
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
36
|
},
|
|
71
37
|
} as Meta<typeof CodeBlock>;
|
|
72
38
|
|
|
@@ -81,11 +47,6 @@ export const CSS: StoryObj<typeof CodeBlock> = {
|
|
|
81
47
|
<span class="token property">margin</span><span class="token punctuation">:</span> <span class="token number">10</span><span class="token unit">px</span><span class="token punctuation">;</span>
|
|
82
48
|
<span class="token property">background</span><span class="token punctuation">:</span> <span class="token hexcode color">#ccc</span><span class="token punctuation">;</span>
|
|
83
49
|
<span class="token punctuation">}</span>`,
|
|
84
|
-
code: `body {
|
|
85
|
-
padding: 20px;
|
|
86
|
-
margin: 10px;
|
|
87
|
-
background: #ccc;
|
|
88
|
-
}`,
|
|
89
50
|
format: "css",
|
|
90
51
|
},
|
|
91
52
|
};
|
|
@@ -95,9 +56,6 @@ export const JS: StoryObj<typeof CodeBlock> = {
|
|
|
95
56
|
highlightedCode: `<span class="token keyword">const</span> arr <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"This"</span><span class="token punctuation">,</span> <span class="token string">"Little"</span><span class="token punctuation">,</span> <span class="token string">"Piggy"</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
|
|
96
57
|
<span class="token keyword">const</span> first <span class="token operator">=</span> arr<span class="token punctuation">.</span><span class="token method function property-access">shift</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
97
58
|
<span class="token console class-name">console</span><span class="token punctuation">.</span><span class="token method function property-access">log</span><span class="token punctuation">(</span>first<span class="token punctuation">)</span><span class="token punctuation">;</span>`,
|
|
98
|
-
code: `const arr = ["This", "Little", "Piggy"];
|
|
99
|
-
const first = arr.shift();
|
|
100
|
-
console.log(first);`,
|
|
101
59
|
format: "js",
|
|
102
60
|
},
|
|
103
61
|
};
|
|
@@ -105,7 +63,6 @@ console.log(first);`,
|
|
|
105
63
|
export const Text: StoryObj<typeof CodeBlock> = {
|
|
106
64
|
args: {
|
|
107
65
|
highlightedCode: `Pure text without highlighting and no title`,
|
|
108
|
-
code: `Pure text without highlighting and no title`,
|
|
109
66
|
format: "text",
|
|
110
67
|
},
|
|
111
68
|
};
|
|
@@ -6,176 +6,93 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import styled from "@
|
|
12
|
-
import {
|
|
13
|
-
import { colors, fonts, spacing } from "@ndla/core";
|
|
14
|
-
import { Copy } from "@ndla/icons/action";
|
|
15
|
-
import { Done } from "@ndla/icons/editor";
|
|
16
|
-
import { copyTextToClipboard } from "@ndla/util";
|
|
17
|
-
import { ICodeLangugeOption, codeLanguageOptions } from "./codeLanguageOptions";
|
|
9
|
+
import { type ComponentPropsWithRef, forwardRef, useMemo } from "react";
|
|
10
|
+
import { cx } from "@ndla/styled-system/css";
|
|
11
|
+
import { styled } from "@ndla/styled-system/jsx";
|
|
12
|
+
import { type JsxStyleProps } from "@ndla/styled-system/types";
|
|
18
13
|
|
|
19
|
-
|
|
20
|
-
margin: 15px 0;
|
|
21
|
-
`;
|
|
22
|
-
|
|
23
|
-
const TitleBar = styled.div`
|
|
24
|
-
display: flex;
|
|
25
|
-
justify-content: space-between;
|
|
26
|
-
width: 100%;
|
|
27
|
-
`;
|
|
28
|
-
|
|
29
|
-
const Title = styled.h3`
|
|
30
|
-
font-style: normal;
|
|
31
|
-
font-weight: normal;
|
|
32
|
-
font-size: 16px;
|
|
33
|
-
line-height: 32px;
|
|
34
|
-
letter-spacing: 1px;
|
|
35
|
-
text-transform: uppercase;
|
|
36
|
-
color: ${colors.text.primary};
|
|
37
|
-
margin: 5px 0;
|
|
38
|
-
`;
|
|
39
|
-
|
|
40
|
-
type Props = {
|
|
41
|
-
code: string;
|
|
14
|
+
interface Props extends JsxStyleProps, ComponentPropsWithRef<"pre"> {
|
|
42
15
|
highlightedCode: string;
|
|
43
16
|
format: string;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
.token.url,
|
|
124
|
-
.token.variable {
|
|
125
|
-
color: #a67f59;
|
|
126
|
-
background: rgba(255, 255, 255, 0.5);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
.token.atrule,
|
|
130
|
-
.token.attr-value,
|
|
131
|
-
.token.keyword,
|
|
132
|
-
.token.class-name {
|
|
133
|
-
color: #1990b8;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
.token.regex,
|
|
137
|
-
.token.important {
|
|
138
|
-
color: #e90;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
.language-css .token.string,
|
|
142
|
-
.style .token.string {
|
|
143
|
-
color: #a67f59;
|
|
144
|
-
background: rgba(255, 255, 255, 0.5);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
.token.important {
|
|
148
|
-
font-weight: normal;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
.token.bold {
|
|
152
|
-
font-weight: bold;
|
|
153
|
-
}
|
|
154
|
-
.token.italic {
|
|
155
|
-
font-style: italic;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
.token.entity {
|
|
159
|
-
cursor: help;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
.token.namespace {
|
|
163
|
-
opacity: 0.7;
|
|
164
|
-
}
|
|
165
|
-
`;
|
|
166
|
-
|
|
167
|
-
const getTitleFromFormat = (format: string) => {
|
|
168
|
-
const selectedLanguage = codeLanguageOptions.find((item: ICodeLangugeOption) => item.format === format);
|
|
169
|
-
if (selectedLanguage) {
|
|
170
|
-
return selectedLanguage.title;
|
|
171
|
-
}
|
|
172
|
-
return;
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
export const Codeblock = ({ actionButton, code, highlightedCode, format, showCopy = false, title }: Props) => {
|
|
176
|
-
const { t } = useTranslation();
|
|
177
|
-
const [isCopied, setIsCopied] = useState(false);
|
|
178
|
-
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const Pre = styled("pre", {
|
|
20
|
+
base: {
|
|
21
|
+
border: "1px solid",
|
|
22
|
+
borderColor: "stroke.subtle",
|
|
23
|
+
borderLeft: "4px solid",
|
|
24
|
+
borderLeftColor: "stroke.default",
|
|
25
|
+
borderRadius: "xsmall",
|
|
26
|
+
boxSizing: "border-box",
|
|
27
|
+
overflowX: "auto",
|
|
28
|
+
textStyle: "label.medium",
|
|
29
|
+
fontFamily: "code",
|
|
30
|
+
display: "block",
|
|
31
|
+
whiteSpace: "pre",
|
|
32
|
+
"& .linenumber": {
|
|
33
|
+
display: "inline-block",
|
|
34
|
+
paddingBlock: "0",
|
|
35
|
+
paddingInline: "small",
|
|
36
|
+
borderRight: "1px solid",
|
|
37
|
+
borderColor: "stroke.subtle",
|
|
38
|
+
width: "xxlarge",
|
|
39
|
+
textAlign: "right",
|
|
40
|
+
marginInlineEnd: "xsmall",
|
|
41
|
+
},
|
|
42
|
+
"& .linenumber[data-first]": {
|
|
43
|
+
paddingBlockStart: "xsmall",
|
|
44
|
+
},
|
|
45
|
+
"& .linenumber[data-last]": {
|
|
46
|
+
paddingBlockEnd: "xsmall",
|
|
47
|
+
},
|
|
48
|
+
// The remaining css is copied from the coy theme in prismjs. A lot of css is omitted due to styling clashes
|
|
49
|
+
"& .token.comment, .token.block-comment, .token.prolog, .token.doctype, .token.cdata": {
|
|
50
|
+
color: "#7d8b99",
|
|
51
|
+
},
|
|
52
|
+
"& .token.punctuation": {
|
|
53
|
+
color: "#5f6364",
|
|
54
|
+
},
|
|
55
|
+
"& .token.property, .token.tag, .token.boolean, .token.number, .token.function-name, .token.constant, .token.symbol, .token.deleted":
|
|
56
|
+
{
|
|
57
|
+
color: "#c92c2c",
|
|
58
|
+
},
|
|
59
|
+
"& .token.selector, .token.attr-name, .token.string, .token.char, .token.function, .token.builtin, .token.inserted":
|
|
60
|
+
{
|
|
61
|
+
color: "#2f9c0a",
|
|
62
|
+
},
|
|
63
|
+
"& .token.operator, .token.entity, .token.url, .token.variable": {
|
|
64
|
+
color: "#a67f59",
|
|
65
|
+
background: "rgba(255, 255, 255, 0.5)",
|
|
66
|
+
},
|
|
67
|
+
"& .token.atrule, .token.attr-value, .token.keyword, .token.class-name": {
|
|
68
|
+
color: "#1990b8",
|
|
69
|
+
},
|
|
70
|
+
"& .token.regex, .token.important": {
|
|
71
|
+
color: "#e90",
|
|
72
|
+
},
|
|
73
|
+
"& .language-css .token.string, .style .token.string": {
|
|
74
|
+
color: "#a67f59",
|
|
75
|
+
background: "rgba(255, 255, 255, 0.5)",
|
|
76
|
+
},
|
|
77
|
+
"& .token.important": {
|
|
78
|
+
fontWeight: "normal",
|
|
79
|
+
},
|
|
80
|
+
"& .token.bold": {
|
|
81
|
+
fontWeight: "bold",
|
|
82
|
+
},
|
|
83
|
+
"& .token.italic": {
|
|
84
|
+
fontStyle: "italic",
|
|
85
|
+
},
|
|
86
|
+
"& .token.entity": {
|
|
87
|
+
cursor: "help",
|
|
88
|
+
},
|
|
89
|
+
"& .token.namespace": {
|
|
90
|
+
opacity: "0.7",
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
export const Codeblock = forwardRef<HTMLPreElement, Props>(({ highlightedCode, format, className, ...props }, ref) => {
|
|
179
96
|
const codeWithLineNumbers = useMemo(() => {
|
|
180
97
|
return highlightedCode
|
|
181
98
|
.split("\n")
|
|
@@ -187,42 +104,14 @@ export const Codeblock = ({ actionButton, code, highlightedCode, format, showCop
|
|
|
187
104
|
.join("\n");
|
|
188
105
|
}, [highlightedCode]);
|
|
189
106
|
|
|
190
|
-
useEffect(() => {
|
|
191
|
-
if (isCopied) {
|
|
192
|
-
const timer = setInterval(() => setIsCopied(false), 3000);
|
|
193
|
-
// ensure interval is cleared - also if unmounted
|
|
194
|
-
return () => {
|
|
195
|
-
clearTimeout(timer);
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
}, [isCopied]);
|
|
199
|
-
|
|
200
107
|
return (
|
|
201
|
-
<
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
<code className={`language-${format}`} dangerouslySetInnerHTML={{ __html: codeWithLineNumbers }} />
|
|
208
|
-
</StyledPre>
|
|
209
|
-
{showCopy && (
|
|
210
|
-
<ButtonV2
|
|
211
|
-
title={t("codeBlock.copyButton")}
|
|
212
|
-
disabled={isCopied}
|
|
213
|
-
data-copied-title={t("codeBlock.copiedCode")}
|
|
214
|
-
data-copy-string={code}
|
|
215
|
-
onClick={() => {
|
|
216
|
-
copyTextToClipboard(code);
|
|
217
|
-
setIsCopied(true);
|
|
218
|
-
}}
|
|
219
|
-
>
|
|
220
|
-
{isCopied ? <Done aria-hidden="true" /> : <Copy aria-hidden="true" />}{" "}
|
|
221
|
-
{isCopied ? t("codeBlock.copiedCode") : t("codeBlock.copyCode")}
|
|
222
|
-
</ButtonV2>
|
|
223
|
-
)}
|
|
224
|
-
</Wrapper>
|
|
108
|
+
<Pre
|
|
109
|
+
className={cx(`language-${format}`, className)}
|
|
110
|
+
{...props}
|
|
111
|
+
dangerouslySetInnerHTML={{ __html: codeWithLineNumbers }}
|
|
112
|
+
ref={ref}
|
|
113
|
+
/>
|
|
225
114
|
);
|
|
226
|
-
};
|
|
115
|
+
});
|
|
227
116
|
|
|
228
117
|
export default Codeblock;
|
|
@@ -14,10 +14,10 @@ import styled from "@emotion/styled";
|
|
|
14
14
|
import { spacing, fonts, colors, mq, breakpoints, misc } from "@ndla/core";
|
|
15
15
|
import { BlobPointy as UnstyledBlobPointy, BlobRound as UnstyledBlobRound } from "@ndla/icons/common";
|
|
16
16
|
import { COPYRIGHTED, getLicenseByAbbreviation } from "@ndla/licenses";
|
|
17
|
+
import { Image } from "@ndla/primitives";
|
|
17
18
|
import { IAuthor, IImageMetaInformationV3 } from "@ndla/types-backend/image-api";
|
|
18
19
|
import { CanonicalUrlFuncs } from "../Embed";
|
|
19
20
|
import { errorSvgSrc } from "../Embed/ImageEmbed";
|
|
20
|
-
import Image, { ImageLink } from "../Image";
|
|
21
21
|
import LicenseLink from "../LicenseByline/LicenseLink";
|
|
22
22
|
|
|
23
23
|
const BLOB_WIDTH = 90;
|
|
@@ -150,9 +150,17 @@ interface LinkWrapperProps {
|
|
|
150
150
|
children: ReactNode;
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
+
const StyledLink = styled.a`
|
|
154
|
+
text-decoration: none;
|
|
155
|
+
`;
|
|
156
|
+
|
|
153
157
|
const LinkWrapper = ({ src, children }: LinkWrapperProps) => {
|
|
154
158
|
if (src) {
|
|
155
|
-
return
|
|
159
|
+
return (
|
|
160
|
+
<StyledLink target="_blank" href={src} rel="noopener noreferrer">
|
|
161
|
+
{children}
|
|
162
|
+
</StyledLink>
|
|
163
|
+
);
|
|
156
164
|
}
|
|
157
165
|
return children;
|
|
158
166
|
};
|