@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
package/src/Embed/ImageEmbed.tsx
CHANGED
|
@@ -6,26 +6,21 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
/** @jsxImportSource @emotion/react */
|
|
10
9
|
import parse from "html-react-parser";
|
|
11
|
-
import {
|
|
10
|
+
import { ReactNode, useMemo, useState } from "react";
|
|
12
11
|
import { useTranslation } from "react-i18next";
|
|
13
|
-
import styled from "@emotion/styled";
|
|
14
|
-
import { colors, misc, spacing } from "@ndla/core";
|
|
15
12
|
import { Plus } from "@ndla/icons/action";
|
|
16
13
|
import { ChevronDown, ChevronUp } from "@ndla/icons/common";
|
|
17
|
-
import {
|
|
14
|
+
import { Figure, FigureSize, FigureVariantProps, Image } from "@ndla/primitives";
|
|
15
|
+
import { styled } from "@ndla/styled-system/jsx";
|
|
18
16
|
import { ImageEmbedData, ImageMetaData } from "@ndla/types-embed";
|
|
19
17
|
import EmbedErrorPlaceholder from "./EmbedErrorPlaceholder";
|
|
20
18
|
import { RenderContext } from "./types";
|
|
21
|
-
import { Figure, FigureType } from "../Figure";
|
|
22
|
-
import Image from "../Image";
|
|
23
19
|
import { EmbedByline } from "../LicenseByline";
|
|
24
20
|
|
|
25
21
|
interface Props {
|
|
26
22
|
embed: ImageMetaData;
|
|
27
23
|
previewAlt?: boolean;
|
|
28
|
-
inGrid?: boolean;
|
|
29
24
|
lang?: string;
|
|
30
25
|
renderContext?: RenderContext;
|
|
31
26
|
children?: ReactNode;
|
|
@@ -49,21 +44,15 @@ export const getLicenseCredits = (copyright?: {
|
|
|
49
44
|
};
|
|
50
45
|
|
|
51
46
|
export const errorSvgSrc = `data:image/svg+xml;charset=UTF-8,%3Csvg fill='%238A8888' height='400' viewBox='0 0 24 12' width='100%25' xmlns='http://www.w3.org/2000/svg' style='background-color: %23EFF0F2'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath transform='scale(0.3) translate(28, 8.5)' 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'/%3E%3C/svg%3E`;
|
|
52
|
-
const isSmall = (size?: string): size is "xsmall" | "small" => size === "xsmall" || size === "small";
|
|
53
47
|
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
if (align && isAlign(align)) {
|
|
64
|
-
return align;
|
|
65
|
-
}
|
|
66
|
-
return "full";
|
|
48
|
+
const getFigureProps = (size?: string, float?: string): FigureVariantProps => {
|
|
49
|
+
const actualFloat = float === "left" ? "left" : float === "right" ? "right" : undefined;
|
|
50
|
+
const replacedSize: FigureSize = (size?.replace("-hide-byline", "") ?? "full") as FigureSize;
|
|
51
|
+
return {
|
|
52
|
+
float: actualFloat,
|
|
53
|
+
// Figure expects you to set a size when floating. If you don't, the figure will simply take up the available width. Fallback to medium in those cases.
|
|
54
|
+
size: replacedSize === "full" && float ? "medium" : replacedSize,
|
|
55
|
+
};
|
|
67
56
|
};
|
|
68
57
|
|
|
69
58
|
const getSizes = (size?: string, align?: string) => {
|
|
@@ -111,36 +100,127 @@ export const getCrop = (data: ImageEmbedData) => {
|
|
|
111
100
|
|
|
112
101
|
const expandedSizes = "(min-width: 1024px) 1024px, 100vw";
|
|
113
102
|
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
svg {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
103
|
+
const ImageWrapper = styled("div", {
|
|
104
|
+
base: {
|
|
105
|
+
overflow: "hidden",
|
|
106
|
+
position: "relative",
|
|
107
|
+
width: "100%",
|
|
108
|
+
"& img": {
|
|
109
|
+
width: "100%",
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
variants: {
|
|
113
|
+
svg: {
|
|
114
|
+
true: {
|
|
115
|
+
display: "flex",
|
|
116
|
+
justifyContent: "center",
|
|
117
|
+
},
|
|
118
|
+
false: {},
|
|
119
|
+
},
|
|
120
|
+
border: {
|
|
121
|
+
true: {
|
|
122
|
+
border: "1px solid",
|
|
123
|
+
// TODO: Not sure if we want this color.
|
|
124
|
+
borderColor: "surface.brand.1.strong",
|
|
125
|
+
borderBottom: "0",
|
|
126
|
+
borderRadius: "xsmall",
|
|
127
|
+
borderBottomLeftRadius: "0",
|
|
128
|
+
borderBottomRightRadius: "0",
|
|
129
|
+
},
|
|
130
|
+
false: {},
|
|
131
|
+
},
|
|
132
|
+
expandable: {
|
|
133
|
+
true: {
|
|
134
|
+
cursor: "pointer",
|
|
135
|
+
},
|
|
136
|
+
false: {},
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
});
|
|
135
140
|
|
|
136
|
-
const
|
|
141
|
+
const StyledFigure = styled(Figure, {
|
|
142
|
+
base: {
|
|
143
|
+
_hover: {
|
|
144
|
+
"& [data-byline-button]": {
|
|
145
|
+
background: "background.default",
|
|
146
|
+
},
|
|
147
|
+
"& button[data-expanded]": {
|
|
148
|
+
transform: "scale(1.2)",
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
"& button[data-expanded='true']": {
|
|
152
|
+
"& svg": {
|
|
153
|
+
transform: "rotate(-45deg)",
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// TODO: Ask about BylineButton styling. Not included in design
|
|
160
|
+
const BylineButton = styled(
|
|
161
|
+
"button",
|
|
162
|
+
{
|
|
163
|
+
base: {
|
|
164
|
+
cursor: "pointer",
|
|
165
|
+
position: "absolute",
|
|
166
|
+
zIndex: "base",
|
|
167
|
+
bottom: "0",
|
|
168
|
+
right: "0",
|
|
169
|
+
paddingBlock: "xsmall",
|
|
170
|
+
paddingInline: "xsmall",
|
|
171
|
+
transitionProperty: "transform, background-color, color",
|
|
172
|
+
transitionDuration: "normal",
|
|
173
|
+
transitionTimingFunction: "ease-out",
|
|
174
|
+
background: "background.default/20",
|
|
175
|
+
border: "0",
|
|
176
|
+
"& svg": {
|
|
177
|
+
transitionProperty: "transform",
|
|
178
|
+
transitionDuration: "normal",
|
|
179
|
+
transitionTimingFunction: "ease-out",
|
|
180
|
+
fill: "primary",
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
{ defaultProps: { type: "button" } },
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
const ExpandButton = styled(
|
|
188
|
+
"button",
|
|
189
|
+
{
|
|
190
|
+
base: {
|
|
191
|
+
display: "flex",
|
|
192
|
+
alignItems: "center",
|
|
193
|
+
justifyContent: "center",
|
|
194
|
+
cursor: "pointer",
|
|
195
|
+
position: "absolute",
|
|
196
|
+
padding: "0",
|
|
197
|
+
top: "xsmall",
|
|
198
|
+
right: "xsmall",
|
|
199
|
+
width: "medium",
|
|
200
|
+
height: "medium",
|
|
201
|
+
border: "2px solid",
|
|
202
|
+
borderColor: "background.default",
|
|
203
|
+
transitionProperty: "transform, background-color, color",
|
|
204
|
+
transitionDuration: "normal",
|
|
205
|
+
transitionTimingFunction: "ease-out",
|
|
206
|
+
color: "background.default",
|
|
207
|
+
backgroundColor: "surface.action",
|
|
208
|
+
borderRadius: "large",
|
|
209
|
+
"& svg": {
|
|
210
|
+
transitionProperty: "transform",
|
|
211
|
+
transitionDuration: "normal",
|
|
212
|
+
transitionTimingFunction: "ease-out",
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
{ defaultProps: { type: "button" } },
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
const ImageEmbed = ({ embed, previewAlt, lang, renderContext = "article", children }: Props) => {
|
|
137
220
|
const [isBylineHidden, setIsBylineHidden] = useState(hideByline(embed.embedData));
|
|
138
221
|
const [imageSizes, setImageSizes] = useState<string | undefined>(undefined);
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const [floatAttr, setFloatAttr] = useState<{ "data-float"?: string }>(() =>
|
|
142
|
-
inGrid && !embed.embedData.align ? {} : { "data-float": embed.embedData.align },
|
|
143
|
-
);
|
|
222
|
+
const figureProps = getFigureProps(embed.embedData.size, embed.embedData.align);
|
|
223
|
+
const { t } = useTranslation();
|
|
144
224
|
|
|
145
225
|
const parsedDescription = useMemo(() => {
|
|
146
226
|
if (embed.embedData.caption || renderContext === "article") {
|
|
@@ -152,60 +232,56 @@ const ImageEmbed = ({ embed, previewAlt, inGrid, lang, renderContext = "article"
|
|
|
152
232
|
}, [embed, renderContext]);
|
|
153
233
|
|
|
154
234
|
if (embed.status === "error") {
|
|
155
|
-
|
|
156
|
-
const figureType = getFigureType(size, align);
|
|
157
|
-
return <EmbedErrorPlaceholder type={"image"} figureType={figureType} />;
|
|
235
|
+
return <EmbedErrorPlaceholder type={"image"} figureType={figureProps?.size} float={figureProps?.float} />;
|
|
158
236
|
}
|
|
159
237
|
|
|
160
238
|
const { data, embedData } = embed;
|
|
161
239
|
|
|
162
240
|
const altText = embedData.alt || "";
|
|
163
241
|
|
|
164
|
-
const figureType = getFigureType(embedData.size, embedData.align);
|
|
165
242
|
const sizes = getSizes(embedData.size, embedData.align);
|
|
166
243
|
|
|
167
244
|
const focalPoint = getFocalPoint(embedData);
|
|
168
245
|
const crop = getCrop(embedData);
|
|
169
246
|
|
|
170
|
-
const isCopyrighted = data.copyright.license.license.toLowerCase() === COPYRIGHTED;
|
|
171
|
-
|
|
172
247
|
const toggleImageSize = () => {
|
|
173
|
-
|
|
174
|
-
setImageSizes(expandedSizes);
|
|
175
|
-
setTimeout(() => {
|
|
176
|
-
setFloatAttr({});
|
|
177
|
-
}, 400); //Removing the float parameter too quickly causes the image to be resized from left regardless
|
|
178
|
-
} else {
|
|
179
|
-
setImageSizes(undefined);
|
|
180
|
-
setFloatAttr({ "data-float": embedData.align });
|
|
181
|
-
}
|
|
248
|
+
setImageSizes((sizes) => (!sizes ? expandedSizes : undefined));
|
|
182
249
|
};
|
|
183
250
|
|
|
251
|
+
// TODO: Check how this works with `children`. Will only be important for ED
|
|
184
252
|
return (
|
|
185
|
-
<StyledFigure
|
|
253
|
+
<StyledFigure float={figureProps?.float} size={imageSizes ? "full" : figureProps?.size ?? "medium"}>
|
|
186
254
|
{children}
|
|
187
|
-
<
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
255
|
+
<ImageWrapper border={embedData.border === "true"} expandable={!!figureProps?.float}>
|
|
256
|
+
<Image
|
|
257
|
+
focalPoint={focalPoint}
|
|
258
|
+
contentType={data.image.contentType}
|
|
259
|
+
crop={crop}
|
|
260
|
+
sizes={imageSizes ?? sizes}
|
|
261
|
+
alt={altText}
|
|
262
|
+
src={data.image.imageUrl}
|
|
263
|
+
lang={lang}
|
|
264
|
+
onClick={figureProps?.float ? toggleImageSize : undefined}
|
|
265
|
+
/>
|
|
266
|
+
{!!embedData.align && (
|
|
198
267
|
<ExpandButton
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
268
|
+
aria-label={t(`license.images.itemImage.zoom${imageSizes ? "Out" : ""}ImageButtonLabel`)}
|
|
269
|
+
onClick={toggleImageSize}
|
|
270
|
+
data-expanded={!!imageSizes}
|
|
271
|
+
>
|
|
272
|
+
<Plus />
|
|
273
|
+
</ExpandButton>
|
|
274
|
+
)}
|
|
275
|
+
{(embedData.size?.endsWith("-hide-byline") || embedData.hideByline === "true") && (
|
|
276
|
+
<BylineButton
|
|
277
|
+
data-byline-button=""
|
|
278
|
+
aria-label={t(`license.images.itemImage.${isBylineHidden ? "expandByline" : "minimizeByline"}`)}
|
|
279
|
+
onClick={() => setIsBylineHidden((p) => !p)}
|
|
280
|
+
>
|
|
281
|
+
{isBylineHidden ? <ChevronDown /> : <ChevronUp />}
|
|
282
|
+
</BylineButton>
|
|
283
|
+
)}
|
|
284
|
+
</ImageWrapper>
|
|
209
285
|
{isBylineHidden ? null : (
|
|
210
286
|
<EmbedByline
|
|
211
287
|
type="image"
|
|
@@ -222,84 +298,4 @@ const hideByline = (embed: ImageEmbedData): boolean => {
|
|
|
222
298
|
return (!!embed.size && embed.size.endsWith("-hide-byline")) || embed.hideByline === "true";
|
|
223
299
|
};
|
|
224
300
|
|
|
225
|
-
interface ExpandButtonProps {
|
|
226
|
-
embedData: ImageEmbedData;
|
|
227
|
-
align?: string;
|
|
228
|
-
expanded: boolean;
|
|
229
|
-
bylineHidden: boolean;
|
|
230
|
-
onExpand: MouseEventHandler<HTMLButtonElement>;
|
|
231
|
-
onHideByline: MouseEventHandler<HTMLButtonElement>;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
const BylineButton = styled.button`
|
|
235
|
-
cursor: pointer;
|
|
236
|
-
position: absolute;
|
|
237
|
-
z-index: 1;
|
|
238
|
-
bottom: 0;
|
|
239
|
-
right: 0;
|
|
240
|
-
padding: ${spacing.small};
|
|
241
|
-
transition: all 0.3s ease-out;
|
|
242
|
-
background: ${colors.background.default}20;
|
|
243
|
-
border: 0;
|
|
244
|
-
|
|
245
|
-
svg {
|
|
246
|
-
transition: transform 0.4s ease-out;
|
|
247
|
-
width: ${spacing.normal};
|
|
248
|
-
height: ${spacing.normal};
|
|
249
|
-
fill: ${colors.brand.primary};
|
|
250
|
-
}
|
|
251
|
-
`;
|
|
252
|
-
|
|
253
|
-
const StyledButton = styled.button`
|
|
254
|
-
display: flex;
|
|
255
|
-
align-items: center;
|
|
256
|
-
justify-content: center;
|
|
257
|
-
cursor: pointer;
|
|
258
|
-
position: absolute;
|
|
259
|
-
padding: 0;
|
|
260
|
-
top: ${spacing.small};
|
|
261
|
-
right: ${spacing.small};
|
|
262
|
-
width: ${spacing.normal};
|
|
263
|
-
height: ${spacing.normal};
|
|
264
|
-
border: 2px solid ${colors.white};
|
|
265
|
-
transition: all 0.3s ease-out;
|
|
266
|
-
color: ${colors.white};
|
|
267
|
-
background-color: ${colors.brand.primary};
|
|
268
|
-
border-radius: ${misc.borderRadiusLarge};
|
|
269
|
-
svg {
|
|
270
|
-
transition: transform 0.4s ease-out;
|
|
271
|
-
height: ${spacing.nsmall};
|
|
272
|
-
width: ${spacing.nsmall};
|
|
273
|
-
}
|
|
274
|
-
`;
|
|
275
|
-
|
|
276
|
-
const ExpandButton = ({ align, embedData, expanded, bylineHidden, onExpand, onHideByline }: ExpandButtonProps) => {
|
|
277
|
-
const { t } = useTranslation();
|
|
278
|
-
if (isAlign(align)) {
|
|
279
|
-
return (
|
|
280
|
-
<StyledButton
|
|
281
|
-
type="button"
|
|
282
|
-
aria-label={t(`license.images.itemImage.zoom${expanded ? "Out" : ""}ImageButtonLabel`)}
|
|
283
|
-
onClick={onExpand}
|
|
284
|
-
data-expanded={expanded}
|
|
285
|
-
>
|
|
286
|
-
<Plus />
|
|
287
|
-
</StyledButton>
|
|
288
|
-
);
|
|
289
|
-
}
|
|
290
|
-
if (hideByline(embedData)) {
|
|
291
|
-
return (
|
|
292
|
-
<BylineButton
|
|
293
|
-
type="button"
|
|
294
|
-
data-byline-button=""
|
|
295
|
-
aria-label={t(`license.images.itemImage.${bylineHidden ? "expandByline" : "minimizeByline"}`)}
|
|
296
|
-
onClick={onHideByline}
|
|
297
|
-
>
|
|
298
|
-
{bylineHidden ? <ChevronDown /> : <ChevronUp />}
|
|
299
|
-
</BylineButton>
|
|
300
|
-
);
|
|
301
|
-
}
|
|
302
|
-
return null;
|
|
303
|
-
};
|
|
304
|
-
|
|
305
301
|
export default ImageEmbed;
|
|
@@ -7,13 +7,10 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
/** @jsxImportSource @emotion/react */
|
|
10
|
-
import { useTranslation } from "react-i18next";
|
|
11
10
|
import styled from "@emotion/styled";
|
|
12
|
-
import { animations, breakpoints, colors,
|
|
13
|
-
import {
|
|
14
|
-
import { Play } from "@ndla/icons/common";
|
|
11
|
+
import { animations, breakpoints, colors, mq, spacing } from "@ndla/core";
|
|
12
|
+
import { Image } from "@ndla/primitives";
|
|
15
13
|
import { Figure } from "../Figure";
|
|
16
|
-
import Image from "../Image";
|
|
17
14
|
|
|
18
15
|
const StyledImageWrapper = styled.div`
|
|
19
16
|
overflow: hidden;
|
|
@@ -41,7 +38,6 @@ const StyledImage = styled(Image)`
|
|
|
41
38
|
`;
|
|
42
39
|
|
|
43
40
|
interface Props {
|
|
44
|
-
type: "image" | "video" | "h5p" | "iframe" | "external" | "audio" | undefined;
|
|
45
41
|
src: string;
|
|
46
42
|
alt: string;
|
|
47
43
|
}
|
|
@@ -57,58 +53,12 @@ const StyledFigure = styled(Figure)`
|
|
|
57
53
|
}
|
|
58
54
|
`;
|
|
59
55
|
|
|
60
|
-
export const NotionImage = ({ src, alt
|
|
56
|
+
export const NotionImage = ({ src, alt }: Props) => {
|
|
61
57
|
return (
|
|
62
58
|
<StyledFigure type={"full-column"}>
|
|
63
59
|
<StyledImageWrapper>
|
|
64
|
-
<StyledImage alt={alt} src={src}
|
|
60
|
+
<StyledImage alt={alt} src={src} />
|
|
65
61
|
</StyledImageWrapper>
|
|
66
62
|
</StyledFigure>
|
|
67
63
|
);
|
|
68
64
|
};
|
|
69
|
-
|
|
70
|
-
interface OpenButtonProps {
|
|
71
|
-
type?: "image" | "video" | "h5p" | "iframe" | "external" | "audio";
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export const FigureActionIndicator = styled.div`
|
|
75
|
-
all: unset;
|
|
76
|
-
cursor: pointer;
|
|
77
|
-
position: absolute;
|
|
78
|
-
padding: 0;
|
|
79
|
-
bottom: 8px;
|
|
80
|
-
right: 8px;
|
|
81
|
-
width: 40px;
|
|
82
|
-
height: 40px;
|
|
83
|
-
display: flex;
|
|
84
|
-
justify-content: center;
|
|
85
|
-
align-items: center;
|
|
86
|
-
transition: all 0.3s ease-out;
|
|
87
|
-
// The 65 is added to alter the opacity.
|
|
88
|
-
background-color: ${colors.background.default}65;
|
|
89
|
-
border-radius: ${misc.borderRadiusLarge};
|
|
90
|
-
border: 0;
|
|
91
|
-
svg {
|
|
92
|
-
transition: transform 0.4s ease-out;
|
|
93
|
-
width: 18px;
|
|
94
|
-
height: 18px;
|
|
95
|
-
fill: ${colors.brand.primary};
|
|
96
|
-
color: ${colors.brand.primary};
|
|
97
|
-
}
|
|
98
|
-
${mq.range({ until: breakpoints.tablet })} {
|
|
99
|
-
display: none;
|
|
100
|
-
}
|
|
101
|
-
`;
|
|
102
|
-
|
|
103
|
-
export const OpenButton = ({ type }: OpenButtonProps) => {
|
|
104
|
-
const { t } = useTranslation();
|
|
105
|
-
return (
|
|
106
|
-
<FigureActionIndicator data-open-button="" aria-label={t("license.images.itemImage.zoomImageButtonLabel")}>
|
|
107
|
-
{type === "image" && <ExpandTwoArrows />}
|
|
108
|
-
{type === "h5p" && <CursorClick style={{ width: "24px", height: "24px" }} />}
|
|
109
|
-
{type === "iframe" && <CursorClick style={{ width: "24px", height: "24px" }} />}
|
|
110
|
-
{type === "external" && <CursorClick style={{ width: "24px", height: "24px" }} />}
|
|
111
|
-
{type === "video" && <Play style={{ width: "24px", height: "24px" }} />}
|
|
112
|
-
</FigureActionIndicator>
|
|
113
|
-
);
|
|
114
|
-
};
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
import styled from "@emotion/styled";
|
|
10
10
|
import { breakpoints, fonts, mq, colors, spacing } from "@ndla/core";
|
|
11
11
|
import { Launch } from "@ndla/icons/common";
|
|
12
|
+
import { Image } from "@ndla/primitives";
|
|
12
13
|
import { SafeLinkButton } from "@ndla/safelink";
|
|
13
|
-
import Image from "../Image";
|
|
14
14
|
|
|
15
15
|
const ResourceBoxContainer = styled.div`
|
|
16
16
|
display: flex;
|
|
@@ -52,18 +52,6 @@ const ContentWrapper = styled.div`
|
|
|
52
52
|
}
|
|
53
53
|
`;
|
|
54
54
|
|
|
55
|
-
const StyledButton = styled(SafeLinkButton)`
|
|
56
|
-
display: flex;
|
|
57
|
-
gap: ${spacing.xxsmall};
|
|
58
|
-
align-items: center;
|
|
59
|
-
border: 1px solid ${colors.brand.tertiary};
|
|
60
|
-
:hover {
|
|
61
|
-
background-color: ${colors.brand.primary};
|
|
62
|
-
border: 1px solid ${colors.brand.primary};
|
|
63
|
-
color: ${colors.white};
|
|
64
|
-
}
|
|
65
|
-
`;
|
|
66
|
-
|
|
67
55
|
const StyledImage = styled(Image)`
|
|
68
56
|
&& {
|
|
69
57
|
object-fit: cover;
|
|
@@ -98,10 +86,10 @@ export const ResourceBox = ({ image, title, caption, url, buttonText }: Props) =
|
|
|
98
86
|
<ContentWrapper>
|
|
99
87
|
<Title>{title}</Title>
|
|
100
88
|
<Caption>{caption}</Caption>
|
|
101
|
-
<
|
|
89
|
+
<SafeLinkButton to={url} target="_blank" variant="secondary">
|
|
102
90
|
{buttonText}
|
|
103
91
|
<Launch aria-hidden />
|
|
104
|
-
</
|
|
92
|
+
</SafeLinkButton>
|
|
105
93
|
</ContentWrapper>
|
|
106
94
|
</ResourceBoxContainer>
|
|
107
95
|
);
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
/** @jsxImportSource @emotion/react */
|
|
10
10
|
import { ReactElement, useEffect, useRef, useState } from "react";
|
|
11
11
|
import { useTranslation } from "react-i18next";
|
|
12
|
+
import styled from "@emotion/styled";
|
|
12
13
|
import { ButtonV2 } from "@ndla/button";
|
|
13
14
|
import { Additional, ChevronDown, ChevronUp } from "@ndla/icons/common";
|
|
14
15
|
import { SafeLink } from "@ndla/safelink";
|
|
@@ -64,6 +65,12 @@ type Props = {
|
|
|
64
65
|
unGrouped?: boolean;
|
|
65
66
|
};
|
|
66
67
|
|
|
68
|
+
const StyledSafeLink = styled(SafeLink)`
|
|
69
|
+
&[data-highlighted="true"] {
|
|
70
|
+
${highlightStyle}
|
|
71
|
+
}
|
|
72
|
+
`;
|
|
73
|
+
|
|
67
74
|
const ContentTypeResult = ({
|
|
68
75
|
contentTypeResult,
|
|
69
76
|
onNavigate,
|
|
@@ -139,8 +146,7 @@ const ContentTypeResult = ({
|
|
|
139
146
|
|
|
140
147
|
return (
|
|
141
148
|
<StyledListItem key={path} delayAnimation={delayAnimation}>
|
|
142
|
-
<
|
|
143
|
-
css={shouldHighlight && highlightStyle}
|
|
149
|
+
<StyledSafeLink
|
|
144
150
|
data-highlighted={shouldHighlight || false}
|
|
145
151
|
{...linkProps}
|
|
146
152
|
onClick={() => {
|
|
@@ -154,7 +160,7 @@ const ContentTypeResult = ({
|
|
|
154
160
|
)}
|
|
155
161
|
{linkContent}
|
|
156
162
|
{renderAdditionalIcon(t("resource.additionalTooltip"), additional)}
|
|
157
|
-
</
|
|
163
|
+
</StyledSafeLink>
|
|
158
164
|
</StyledListItem>
|
|
159
165
|
);
|
|
160
166
|
})}
|
|
@@ -79,6 +79,9 @@ const StyledSearchLink = styled(SafeLink)`
|
|
|
79
79
|
color: ${colors.text.light};
|
|
80
80
|
padding-left: ${spacing.xsmall};
|
|
81
81
|
}
|
|
82
|
+
&[data-highlighted="true"] {
|
|
83
|
+
${highlightStyle};
|
|
84
|
+
}
|
|
82
85
|
`;
|
|
83
86
|
|
|
84
87
|
type WrapperProps = {
|
|
@@ -304,7 +307,7 @@ const SearchResultSleeve = ({
|
|
|
304
307
|
<div>
|
|
305
308
|
<SearchLinkContainer>
|
|
306
309
|
<StyledSearchLink
|
|
307
|
-
|
|
310
|
+
data-highlighted={keyboardPathNavigation === GO_TO_SEARCHPAGE}
|
|
308
311
|
to={allResultUrl}
|
|
309
312
|
tabIndex={-1}
|
|
310
313
|
>
|
|
@@ -314,7 +317,7 @@ const SearchResultSleeve = ({
|
|
|
314
317
|
</StyledSearchLink>
|
|
315
318
|
{suggestion && suggestionUrl && (
|
|
316
319
|
<StyledSearchLink
|
|
317
|
-
|
|
320
|
+
data-highlighted={keyboardPathNavigation === GO_TO_SUGGESTION}
|
|
318
321
|
to={suggestionUrl}
|
|
319
322
|
tabIndex={-1}
|
|
320
323
|
>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2024-present, NDLA.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the GPLv3 license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { forwardRef, useState } from "react";
|
|
10
|
+
import { Button, ButtonProps } from "@ndla/primitives";
|
|
11
|
+
|
|
12
|
+
// TODO: Let's consider abandoning `disabled` on the button here. It should instead just open/close the widget based on its current state.
|
|
13
|
+
|
|
14
|
+
export interface ZendeskButtonProps extends ButtonProps {
|
|
15
|
+
widgetKey: string;
|
|
16
|
+
locale: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
declare global {
|
|
20
|
+
interface Window {
|
|
21
|
+
zE: (modifier: string, action: string, callback?: (() => void) | string) => void;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const ZendeskButton = forwardRef<HTMLButtonElement, ZendeskButtonProps>(
|
|
26
|
+
({ locale, variant = "secondary", widgetKey, children, ...rest }, ref) => {
|
|
27
|
+
const [loading, setLoading] = useState(false);
|
|
28
|
+
const handleClick = () => {
|
|
29
|
+
if (window && !window.zE) {
|
|
30
|
+
setLoading(true);
|
|
31
|
+
// Asynchronously load zendesk scripts for better performance
|
|
32
|
+
const script = document.createElement("script");
|
|
33
|
+
script.id = "ze-snippet";
|
|
34
|
+
script.type = "text/javascript";
|
|
35
|
+
script.async = true;
|
|
36
|
+
script.onload = () => {
|
|
37
|
+
if (window.zE) {
|
|
38
|
+
window.zE("webWidget", "setLocale", locale);
|
|
39
|
+
window.zE("webWidget:on", "close", () => {
|
|
40
|
+
setLoading(false);
|
|
41
|
+
});
|
|
42
|
+
window.zE("webWidget", "open");
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
script.src = `https://static.zdassets.com/ekr/snippet.js?key=${widgetKey}`;
|
|
46
|
+
document.body.appendChild(script);
|
|
47
|
+
} else if (window?.zE) {
|
|
48
|
+
window.zE("webWidget", "open");
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<Button onClick={handleClick} variant={variant} id="zendeskButton" disabled={loading} {...rest} ref={ref}>
|
|
54
|
+
{children}
|
|
55
|
+
</Button>
|
|
56
|
+
);
|
|
57
|
+
},
|
|
58
|
+
);
|
package/src/index.ts
CHANGED
|
@@ -172,3 +172,7 @@ export { LinkBlock, LinkBlockSection } from "./LinkBlock";
|
|
|
172
172
|
export type { Article as ArticleType } from "./types";
|
|
173
173
|
|
|
174
174
|
export { CodeBlock, codeLanguageOptions } from "./CodeBlock";
|
|
175
|
+
|
|
176
|
+
export { ZendeskButton } from "./ZendeskButton/ZendeskButton";
|
|
177
|
+
|
|
178
|
+
export type { ZendeskButtonProps } from "./ZendeskButton/ZendeskButton";
|
|
@@ -822,6 +822,7 @@ const messages = {
|
|
|
822
822
|
url: "Error loading the audio.",
|
|
823
823
|
caption: "Sorry, an error occurred while loading the audio.",
|
|
824
824
|
},
|
|
825
|
+
valueText: "{{start}} of {{end}}",
|
|
825
826
|
controls: {
|
|
826
827
|
forward15sec: "Forward 15 seconds",
|
|
827
828
|
rewind15sec: "Rewind 15 seconds",
|
|
@@ -822,6 +822,7 @@ const messages = {
|
|
|
822
822
|
url: "Feil ved lasting av lydfil.",
|
|
823
823
|
caption: "Beklager, en feil oppstod ved lasting av lydfil.",
|
|
824
824
|
},
|
|
825
|
+
valueText: "{{start}} av {{end}}",
|
|
825
826
|
controls: {
|
|
826
827
|
forward15sec: "Spol 15 sekunder fram",
|
|
827
828
|
rewind15sec: "Spol 15 sekunder tilbake",
|
|
@@ -822,6 +822,7 @@ const messages = {
|
|
|
822
822
|
url: "Feil ved lasting av lydfil.",
|
|
823
823
|
caption: "Orsak, ein feil oppstod ved lasting av lydfil.",
|
|
824
824
|
},
|
|
825
|
+
valueText: "{{start}} av {{end}}",
|
|
825
826
|
controls: {
|
|
826
827
|
forward15sec: "Spol 15 sekund fram",
|
|
827
828
|
rewind15sec: "Spol 15 sekund tilbake",
|