@ndla/ui 56.0.188-alpha.0 → 56.0.190-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 +4 -4
- package/dist/styles.css +17 -16
- package/es/AudioPlayer/AudioProgress.mjs +20 -17
- package/es/AudioPlayer/AudioProgress.mjs.map +1 -1
- package/es/Embed/BrightcoveEmbed.mjs.map +1 -1
- package/es/LicenseByline/EmbedByline.mjs +3 -2
- package/es/LicenseByline/EmbedByline.mjs.map +1 -1
- package/lib/AudioPlayer/AudioProgress.js +20 -17
- package/lib/AudioPlayer/AudioProgress.js.map +1 -1
- package/lib/Embed/BrightcoveEmbed.js.map +1 -1
- package/lib/LicenseByline/EmbedByline.js +2 -1
- package/lib/LicenseByline/EmbedByline.js.map +1 -1
- package/package.json +4 -4
- package/src/AudioPlayer/AudioProgress.tsx +11 -9
- package/src/Embed/AudioEmbed.stories.tsx +2 -0
- package/src/Embed/BrightcoveEmbed.tsx +1 -0
- package/src/Embed/ConceptEmbed.stories.tsx +32 -0
- package/src/Embed/ImageEmbed.stories.tsx +2 -2
- package/src/Embed/RelatedContentEmbed.stories.tsx +2 -0
- package/src/Grid/Grid.stories.tsx +32 -25
- package/src/LicenseByline/EmbedByline.tsx +3 -2
|
@@ -81,11 +81,10 @@
|
|
|
81
81
|
"direction]___[value:rtl]___[cond:& p:has(span[dir=\"rtl\"])",
|
|
82
82
|
"alignSelf]___[value:flex-start",
|
|
83
83
|
"marginInlineStart]___[value:3xsmall",
|
|
84
|
-
"
|
|
85
|
-
"
|
|
86
|
-
"height]___[value:4xsmall",
|
|
84
|
+
"marginBlockStart]___[value:-4xsmall",
|
|
85
|
+
"transitionProperty]___[value:background, border-radius, width, height",
|
|
87
86
|
"background]___[value:unset",
|
|
88
|
-
"
|
|
87
|
+
"marginBlockEnd]___[value:-xsmall",
|
|
89
88
|
"paddingBlockEnd]___[value:0",
|
|
90
89
|
"boxShadow]___[value:xsmall",
|
|
91
90
|
"background]___[value:surface.brand.1.subtle",
|
|
@@ -343,6 +342,7 @@
|
|
|
343
342
|
"outline]___[value:0px",
|
|
344
343
|
"boxShadow]___[value:none",
|
|
345
344
|
"aspectRatio]___[value:16/9",
|
|
345
|
+
"height]___[value:unset",
|
|
346
346
|
"gridTemplateColumns]___[value:repeat(2, 1fr)",
|
|
347
347
|
"gridTemplateColumns]___[value:1fr]___[cond:tabletDown",
|
|
348
348
|
"marginBlockStart]___[value:xsmall",
|
package/dist/styles.css
CHANGED
|
@@ -314,10 +314,6 @@
|
|
|
314
314
|
padding-inline: var(--spacing-xsmall);
|
|
315
315
|
}
|
|
316
316
|
|
|
317
|
-
.bdr_0 {
|
|
318
|
-
border-radius: 0;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
317
|
.px_medium {
|
|
322
318
|
padding-inline: var(--spacing-medium);
|
|
323
319
|
}
|
|
@@ -467,6 +463,19 @@
|
|
|
467
463
|
margin-inline-start: var(--spacing-3xsmall);
|
|
468
464
|
}
|
|
469
465
|
|
|
466
|
+
.mbs_-4xsmall {
|
|
467
|
+
margin-block-start: calc(var(--spacing-4xsmall) * -1);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
.trs-prop_background\,_border-radius\,_width\,_height {
|
|
471
|
+
--transition-prop: background, border-radius, width, height;
|
|
472
|
+
transition-property: background, border-radius, width, height;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
.mbe_-xsmall {
|
|
476
|
+
margin-block-end: calc(var(--spacing-xsmall) * -1);
|
|
477
|
+
}
|
|
478
|
+
|
|
470
479
|
.pbe_0 {
|
|
471
480
|
padding-block-end: 0;
|
|
472
481
|
}
|
|
@@ -750,18 +759,6 @@
|
|
|
750
759
|
max-width: var(--sizes-surface-xlarge);
|
|
751
760
|
}
|
|
752
761
|
|
|
753
|
-
.w_4xsmall {
|
|
754
|
-
width: var(--sizes-4xsmall);
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
.h_4xsmall {
|
|
758
|
-
height: var(--sizes-4xsmall);
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
.h_unset {
|
|
762
|
-
height: unset;
|
|
763
|
-
}
|
|
764
|
-
|
|
765
762
|
.min-w_4xlarge {
|
|
766
763
|
min-width: var(--sizes-4xlarge);
|
|
767
764
|
}
|
|
@@ -854,6 +851,10 @@
|
|
|
854
851
|
width: var(--sizes-surface-3xsmall);
|
|
855
852
|
}
|
|
856
853
|
|
|
854
|
+
.h_unset {
|
|
855
|
+
height: unset;
|
|
856
|
+
}
|
|
857
|
+
|
|
857
858
|
.before\:inset_0::before {
|
|
858
859
|
inset: 0;
|
|
859
860
|
}
|
|
@@ -7,27 +7,33 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
7
7
|
const StyledSliderThumb = styled(SliderThumb, { variants: { variant: {
|
|
8
8
|
standard: {},
|
|
9
9
|
simple: {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
height: "4xsmall"
|
|
10
|
+
marginBlockStart: "-4xsmall",
|
|
11
|
+
transitionProperty: "background, border-radius, width, height"
|
|
13
12
|
}
|
|
14
13
|
} } });
|
|
15
14
|
const StyledSliderTrack = styled(SliderTrack, { variants: { variant: {
|
|
16
15
|
standard: {},
|
|
17
|
-
simple: {
|
|
16
|
+
simple: {
|
|
17
|
+
marginBlockStart: "-4xsmall",
|
|
18
|
+
background: "unset"
|
|
19
|
+
}
|
|
18
20
|
} } });
|
|
19
|
-
const
|
|
21
|
+
const StyledSliderRoot = styled(SliderRoot, { variants: { variant: {
|
|
20
22
|
standard: {},
|
|
21
|
-
simple: {
|
|
23
|
+
simple: {
|
|
24
|
+
position: "relative",
|
|
25
|
+
marginBlockEnd: "-xsmall"
|
|
26
|
+
}
|
|
22
27
|
} } });
|
|
23
28
|
const AudioProgress = ({ currentTime, duration, onValueChange, variant }) => {
|
|
24
29
|
const { t } = useTranslation();
|
|
25
|
-
return /* @__PURE__ */ jsxs(
|
|
30
|
+
return /* @__PURE__ */ jsxs(StyledSliderRoot, {
|
|
26
31
|
value: [currentTime],
|
|
27
32
|
defaultValue: [0],
|
|
28
33
|
step: 1,
|
|
29
34
|
max: duration,
|
|
30
35
|
onValueChange,
|
|
36
|
+
variant,
|
|
31
37
|
getAriaValueText: (value) => t("audio.valueText", {
|
|
32
38
|
start: formatTime(Math.round(value.value)),
|
|
33
39
|
end: formatTime(Math.round(duration))
|
|
@@ -35,17 +41,14 @@ const AudioProgress = ({ currentTime, duration, onValueChange, variant }) => {
|
|
|
35
41
|
children: [/* @__PURE__ */ jsx(SliderLabel, {
|
|
36
42
|
srOnly: true,
|
|
37
43
|
children: t("audio.progressBar")
|
|
38
|
-
}), /* @__PURE__ */ jsxs(
|
|
44
|
+
}), /* @__PURE__ */ jsxs(SliderControl, { children: [/* @__PURE__ */ jsx(StyledSliderTrack, {
|
|
45
|
+
variant,
|
|
46
|
+
children: /* @__PURE__ */ jsx(SliderRange, {})
|
|
47
|
+
}), /* @__PURE__ */ jsx(StyledSliderThumb, {
|
|
48
|
+
index: 0,
|
|
39
49
|
variant,
|
|
40
|
-
children:
|
|
41
|
-
|
|
42
|
-
children: /* @__PURE__ */ jsx(SliderRange, {})
|
|
43
|
-
}), /* @__PURE__ */ jsx(StyledSliderThumb, {
|
|
44
|
-
index: 0,
|
|
45
|
-
variant,
|
|
46
|
-
children: /* @__PURE__ */ jsx(SliderHiddenInput, {})
|
|
47
|
-
})]
|
|
48
|
-
})]
|
|
50
|
+
children: /* @__PURE__ */ jsx(SliderHiddenInput, {})
|
|
51
|
+
})] })]
|
|
49
52
|
});
|
|
50
53
|
};
|
|
51
54
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AudioProgress.mjs","names":[],"sources":["../../src/AudioPlayer/AudioProgress.tsx"],"sourcesContent":["/**\n * Copyright (c) 2026-present, NDLA.\n *\n * This source code is licensed under the GPLv3 license found in the\n * LICENSE file in the root directory of this source tree.\n *\n */\n\nimport type { SliderValueChangeDetails } from \"@ark-ui/react\";\nimport {\n SliderRoot,\n SliderLabel,\n SliderControl,\n SliderTrack,\n SliderRange,\n SliderThumb,\n SliderHiddenInput,\n} from \"@ndla/primitives\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport { useTranslation } from \"react-i18next\";\nimport { formatTime } from \"./audioUtils\";\n\ninterface Props {\n currentTime: number;\n duration: number;\n onValueChange: (details: SliderValueChangeDetails) => void;\n variant?: \"simple\" | \"standard\";\n}\n\nconst StyledSliderThumb = styled(SliderThumb, {\n variants: {\n variant: {\n standard: {},\n simple: {\n
|
|
1
|
+
{"version":3,"file":"AudioProgress.mjs","names":[],"sources":["../../src/AudioPlayer/AudioProgress.tsx"],"sourcesContent":["/**\n * Copyright (c) 2026-present, NDLA.\n *\n * This source code is licensed under the GPLv3 license found in the\n * LICENSE file in the root directory of this source tree.\n *\n */\n\nimport type { SliderValueChangeDetails } from \"@ark-ui/react\";\nimport {\n SliderRoot,\n SliderLabel,\n SliderControl,\n SliderTrack,\n SliderRange,\n SliderThumb,\n SliderHiddenInput,\n} from \"@ndla/primitives\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport { useTranslation } from \"react-i18next\";\nimport { formatTime } from \"./audioUtils\";\n\ninterface Props {\n currentTime: number;\n duration: number;\n onValueChange: (details: SliderValueChangeDetails) => void;\n variant?: \"simple\" | \"standard\";\n}\n\nconst StyledSliderThumb = styled(SliderThumb, {\n variants: {\n variant: {\n standard: {},\n simple: {\n marginBlockStart: \"-4xsmall\",\n transitionProperty: \"background, border-radius, width, height\",\n },\n },\n },\n});\n\nconst StyledSliderTrack = styled(SliderTrack, {\n variants: {\n variant: {\n standard: {},\n simple: {\n marginBlockStart: \"-4xsmall\",\n background: \"unset\",\n },\n },\n },\n});\n\nconst StyledSliderRoot = styled(SliderRoot, {\n variants: {\n variant: {\n standard: {},\n simple: {\n position: \"relative\",\n marginBlockEnd: \"-xsmall\",\n },\n },\n },\n});\n\nexport const AudioProgress = ({ currentTime, duration, onValueChange, variant }: Props) => {\n const { t } = useTranslation();\n return (\n <StyledSliderRoot\n value={[currentTime]}\n defaultValue={[0]}\n step={1}\n max={duration}\n onValueChange={onValueChange}\n variant={variant}\n getAriaValueText={(value) =>\n t(\"audio.valueText\", {\n start: formatTime(Math.round(value.value)),\n end: formatTime(Math.round(duration)),\n })\n }\n >\n <SliderLabel srOnly>{t(\"audio.progressBar\")}</SliderLabel>\n <SliderControl>\n <StyledSliderTrack variant={variant}>\n <SliderRange />\n </StyledSliderTrack>\n <StyledSliderThumb index={0} variant={variant}>\n <SliderHiddenInput />\n </StyledSliderThumb>\n </SliderControl>\n </StyledSliderRoot>\n );\n};\n"],"mappings":";;;;;;AA6BA,MAAM,oBAAoB,OAAO,aAAa,EAC5C,UAAU,EACR,SAAS;CACP,UAAU,EAAE;CACZ,QAAQ;EACN,kBAAkB;EAClB,oBAAoB;EACrB;CACF,EACF,EACF,CAAC;AAEF,MAAM,oBAAoB,OAAO,aAAa,EAC5C,UAAU,EACR,SAAS;CACP,UAAU,EAAE;CACZ,QAAQ;EACN,kBAAkB;EAClB,YAAY;EACb;CACF,EACF,EACF,CAAC;AAEF,MAAM,mBAAmB,OAAO,YAAY,EAC1C,UAAU,EACR,SAAS;CACP,UAAU,EAAE;CACZ,QAAQ;EACN,UAAU;EACV,gBAAgB;EACjB;CACF,EACF,EACF,CAAC;AAEF,MAAa,iBAAiB,EAAE,aAAa,UAAU,eAAe,cAAqB;CACzF,MAAM,EAAE,MAAM,gBAAgB;AAC9B,QACE,qBAAC,kBAAD;EACE,OAAO,CAAC,YAAY;EACpB,cAAc,CAAC,EAAE;EACjB,MAAM;EACN,KAAK;EACU;EACN;EACT,mBAAmB,UACjB,EAAE,mBAAmB;GACnB,OAAO,WAAW,KAAK,MAAM,MAAM,MAAM,CAAC;GAC1C,KAAK,WAAW,KAAK,MAAM,SAAS,CAAC;GACtC,CAAC;YAXN,CAcE,oBAAC,aAAD;GAAa,QAAA;aAAQ,EAAE,oBAAoB;GAAe,CAAA,EAC1D,qBAAC,eAAD,EAAA,UAAA,CACE,oBAAC,mBAAD;GAA4B;aAC1B,oBAAC,aAAD,EAAe,CAAA;GACG,CAAA,EACpB,oBAAC,mBAAD;GAAmB,OAAO;GAAY;aACpC,oBAAC,mBAAD,EAAqB,CAAA;GACH,CAAA,CACN,EAAA,CAAA,CACC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BrightcoveEmbed.mjs","names":[],"sources":["../../src/Embed/BrightcoveEmbed.tsx"],"sourcesContent":["/**\n * Copyright (c) 2023-present, NDLA.\n *\n * This source code is licensed under the GPLv3 license found in the\n * LICENSE file in the root directory of this source tree.\n *\n */\n\nimport { Button, Figure } from \"@ndla/primitives\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport type { BrightcoveEmbedData, BrightcoveMetaData, BrightcoveVideoSource } from \"@ndla/types-embed\";\nimport parse from \"html-react-parser\";\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { EmbedByline } from \"../LicenseByline/EmbedByline\";\nimport { licenseAttributes } from \"../utils/licenseAttributes\";\nimport { EmbedErrorPlaceholder } from \"./EmbedErrorPlaceholder\";\nimport type { RenderContext } from \"./types\";\n\ninterface Props {\n embed: BrightcoveMetaData;\n renderContext?: RenderContext;\n lang?: string;\n}\n\nconst LinkedVideoButton = styled(Button, {\n base: {\n marginBlockStart: \"3xsmall\",\n },\n});\n\nconst BrightcoveIframe = styled(\"iframe\", {\n base: {\n border: 0,\n height: \"auto\",\n width: \"100%\",\n },\n});\n\nexport const makeIframeString = (url: string, width: string | number, height: string | number, title = \"\") => {\n const strippedWidth = typeof width === \"number\" ? width : width.replace(/\\s*px/, \"\");\n const strippedHeight = typeof height === \"number\" ? height : height.replace(/\\s*px/, \"\");\n const urlOrTitle = title || url;\n return `<iframe title=\"${urlOrTitle}\" aria-label=\"${urlOrTitle}\" src=\"${url}\" width=\"${strippedWidth}\" height=\"${strippedHeight}\" allowfullscreen scrolling=\"no\" style=\"border: none;\" loading=\"lazy\"></iframe>`;\n};\n\nexport const isNumeric = (value: any) => !Number.isNaN(value - Number.parseFloat(value));\n\nconst getIframeProps = (data: BrightcoveEmbedData, sources: BrightcoveVideoSource[]) => {\n const { account, videoid, player = \"default\" } = data;\n\n const source = sources.filter((s) => s.width && s.height).toSorted((a, b) => a!.height! - b.height!)[0];\n\n return {\n src: `https://players.brightcove.net/${account}/${player}_default/index.html?videoId=${videoid}`,\n height: source?.height ?? \"480\",\n width: source?.width ?? \"640\",\n };\n};\nexport const BrightcoveEmbed = ({ embed, renderContext = \"article\", lang }: Props) => {\n const [showOriginalVideo, setShowOriginalVideo] = useState(true);\n const { t } = useTranslation();\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const { embedData } = embed;\n const fallbackTitle = `${t(\"embed.type.video\")}: ${embedData.videoid}`;\n const parsedDescription = useMemo(() => {\n if (embed.embedData.caption || renderContext === \"article\") {\n return embed.embedData.caption ? parse(embed.embedData.caption) : undefined;\n } else if (embed.status === \"success\" && embed.data.description) {\n return parse(embed.data.description);\n }\n }, [embed, renderContext]);\n\n useEffect(() => {\n const iframe = iframeRef.current;\n if (iframe) {\n const [width, height] = [parseInt(iframe.width), parseInt(iframe.height)];\n iframe.style.aspectRatio = `${width}/${height}`;\n iframe.width = \"\";\n iframe.height = \"\";\n }\n }, []);\n if (embed.status === \"error\") {\n return (\n <EmbedErrorPlaceholder type=\"video\">\n <BrightcoveIframe\n ref={iframeRef}\n title={embedData.alt || fallbackTitle}\n aria-label={embedData.alt || fallbackTitle}\n {...getIframeProps(embedData, [])}\n allow=\"fullscreen; encrypted-media\"\n />\n </EmbedErrorPlaceholder>\n );\n }\n const { data } = embed;\n\n const linkedVideoId = isNumeric(data.link?.text) ? data.link?.text : undefined;\n\n const originalVideoProps = getIframeProps(embedData, data.sources);\n const alternativeVideoProps = linkedVideoId\n ? getIframeProps({ ...embedData, videoid: linkedVideoId }, data.sources)\n : undefined;\n\n const licenseProps = licenseAttributes(data?.copyright?.license.license, lang, embedData.pageUrl);\n\n const title = data.name?.trim() ? `${t(\"embed.type.video\")}: ${data.name}` : fallbackTitle;\n\n return (\n <Figure data-embed-type=\"brightcove\" {...licenseProps}>\n <div className=\"brightcove-video\">\n <BrightcoveIframe\n ref={iframeRef}\n className=\"original\"\n title={title}\n aria-label={title}\n {...(alternativeVideoProps && !showOriginalVideo ? alternativeVideoProps : originalVideoProps)}\n allow=\"fullscreen; encrypted-media\"\n />\n </div>\n <EmbedByline type=\"video\" copyright={data.copyright!} description={parsedDescription}>\n <div>\n {!!linkedVideoId && (\n <LinkedVideoButton size=\"small\" variant=\"secondary\" onClick={() => setShowOriginalVideo((p) => !p)}>\n {t(`figure.button.${!showOriginalVideo ? \"original\" : \"alternative\"}`)}\n </LinkedVideoButton>\n )}\n </div>\n </EmbedByline>\n </Figure>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;AAyBA,MAAM,oBAAoB,OAAO,QAAQ,EACvC,MAAM,EACJ,kBAAkB,WACnB,EACF,CAAC;AAEF,MAAM,mBAAmB,OAAO,UAAU,EACxC,MAAM;CACJ,QAAQ;CACR,QAAQ;CACR,OAAO;CACR,EACF,CAAC;AASF,MAAa,aAAa,UAAe,CAAC,OAAO,MAAM,QAAQ,OAAO,WAAW,MAAM,CAAC;AAExF,MAAM,kBAAkB,MAA2B,YAAqC;
|
|
1
|
+
{"version":3,"file":"BrightcoveEmbed.mjs","names":[],"sources":["../../src/Embed/BrightcoveEmbed.tsx"],"sourcesContent":["/**\n * Copyright (c) 2023-present, NDLA.\n *\n * This source code is licensed under the GPLv3 license found in the\n * LICENSE file in the root directory of this source tree.\n *\n */\n\nimport { Button, Figure } from \"@ndla/primitives\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport type { BrightcoveEmbedData, BrightcoveMetaData, BrightcoveVideoSource } from \"@ndla/types-embed\";\nimport parse from \"html-react-parser\";\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { EmbedByline } from \"../LicenseByline/EmbedByline\";\nimport { licenseAttributes } from \"../utils/licenseAttributes\";\nimport { EmbedErrorPlaceholder } from \"./EmbedErrorPlaceholder\";\nimport type { RenderContext } from \"./types\";\n\ninterface Props {\n embed: BrightcoveMetaData;\n renderContext?: RenderContext;\n lang?: string;\n}\n\nconst LinkedVideoButton = styled(Button, {\n base: {\n marginBlockStart: \"3xsmall\",\n },\n});\n\nconst BrightcoveIframe = styled(\"iframe\", {\n base: {\n border: 0,\n height: \"auto\",\n width: \"100%\",\n },\n});\n\nexport const makeIframeString = (url: string, width: string | number, height: string | number, title = \"\") => {\n const strippedWidth = typeof width === \"number\" ? width : width.replace(/\\s*px/, \"\");\n const strippedHeight = typeof height === \"number\" ? height : height.replace(/\\s*px/, \"\");\n const urlOrTitle = title || url;\n return `<iframe title=\"${urlOrTitle}\" aria-label=\"${urlOrTitle}\" src=\"${url}\" width=\"${strippedWidth}\" height=\"${strippedHeight}\" allowfullscreen scrolling=\"no\" style=\"border: none;\" loading=\"lazy\"></iframe>`;\n};\n\nexport const isNumeric = (value: any) => !Number.isNaN(value - Number.parseFloat(value));\n\nconst getIframeProps = (data: BrightcoveEmbedData, sources: BrightcoveVideoSource[]) => {\n // oxlint-disable-next-line typescript/no-useless-default-assignment\n const { account, videoid, player = \"default\" } = data;\n\n const source = sources.filter((s) => s.width && s.height).toSorted((a, b) => a!.height! - b.height!)[0];\n\n return {\n src: `https://players.brightcove.net/${account}/${player}_default/index.html?videoId=${videoid}`,\n height: source?.height ?? \"480\",\n width: source?.width ?? \"640\",\n };\n};\nexport const BrightcoveEmbed = ({ embed, renderContext = \"article\", lang }: Props) => {\n const [showOriginalVideo, setShowOriginalVideo] = useState(true);\n const { t } = useTranslation();\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const { embedData } = embed;\n const fallbackTitle = `${t(\"embed.type.video\")}: ${embedData.videoid}`;\n const parsedDescription = useMemo(() => {\n if (embed.embedData.caption || renderContext === \"article\") {\n return embed.embedData.caption ? parse(embed.embedData.caption) : undefined;\n } else if (embed.status === \"success\" && embed.data.description) {\n return parse(embed.data.description);\n }\n }, [embed, renderContext]);\n\n useEffect(() => {\n const iframe = iframeRef.current;\n if (iframe) {\n const [width, height] = [parseInt(iframe.width), parseInt(iframe.height)];\n iframe.style.aspectRatio = `${width}/${height}`;\n iframe.width = \"\";\n iframe.height = \"\";\n }\n }, []);\n if (embed.status === \"error\") {\n return (\n <EmbedErrorPlaceholder type=\"video\">\n <BrightcoveIframe\n ref={iframeRef}\n title={embedData.alt || fallbackTitle}\n aria-label={embedData.alt || fallbackTitle}\n {...getIframeProps(embedData, [])}\n allow=\"fullscreen; encrypted-media\"\n />\n </EmbedErrorPlaceholder>\n );\n }\n const { data } = embed;\n\n const linkedVideoId = isNumeric(data.link?.text) ? data.link?.text : undefined;\n\n const originalVideoProps = getIframeProps(embedData, data.sources);\n const alternativeVideoProps = linkedVideoId\n ? getIframeProps({ ...embedData, videoid: linkedVideoId }, data.sources)\n : undefined;\n\n const licenseProps = licenseAttributes(data?.copyright?.license.license, lang, embedData.pageUrl);\n\n const title = data.name?.trim() ? `${t(\"embed.type.video\")}: ${data.name}` : fallbackTitle;\n\n return (\n <Figure data-embed-type=\"brightcove\" {...licenseProps}>\n <div className=\"brightcove-video\">\n <BrightcoveIframe\n ref={iframeRef}\n className=\"original\"\n title={title}\n aria-label={title}\n {...(alternativeVideoProps && !showOriginalVideo ? alternativeVideoProps : originalVideoProps)}\n allow=\"fullscreen; encrypted-media\"\n />\n </div>\n <EmbedByline type=\"video\" copyright={data.copyright!} description={parsedDescription}>\n <div>\n {!!linkedVideoId && (\n <LinkedVideoButton size=\"small\" variant=\"secondary\" onClick={() => setShowOriginalVideo((p) => !p)}>\n {t(`figure.button.${!showOriginalVideo ? \"original\" : \"alternative\"}`)}\n </LinkedVideoButton>\n )}\n </div>\n </EmbedByline>\n </Figure>\n );\n};\n"],"mappings":"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"}
|
|
@@ -4,7 +4,7 @@ import { styled } from "@ndla/styled-system/jsx";
|
|
|
4
4
|
import { useState } from "react";
|
|
5
5
|
import { useTranslation } from "react-i18next";
|
|
6
6
|
import { AlertLine } from "@ndla/icons";
|
|
7
|
-
import { getLicenseByAbbreviation } from "@ndla/licenses";
|
|
7
|
+
import { getLicenseByAbbreviation, rights } from "@ndla/licenses";
|
|
8
8
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
9
9
|
//#region src/LicenseByline/EmbedByline.tsx
|
|
10
10
|
/**
|
|
@@ -110,6 +110,7 @@ const LicenseDescription = ({ children, isOpen, setIsOpen }) => {
|
|
|
110
110
|
const LicenseContainerContent = ({ children, copyright, type }) => {
|
|
111
111
|
const { t, i18n } = useTranslation();
|
|
112
112
|
const license = copyright ? getLicenseByAbbreviation(copyright.license?.license ?? "", i18n.language) : void 0;
|
|
113
|
+
const shouldShowLicense = !!license && !license.rights.includes(rights.NA);
|
|
113
114
|
const captionAuthors = [
|
|
114
115
|
copyright?.creators,
|
|
115
116
|
copyright?.rightsholders,
|
|
@@ -120,7 +121,7 @@ const LicenseContainerContent = ({ children, copyright, type }) => {
|
|
|
120
121
|
children,
|
|
121
122
|
` ${t(`embed.type.${type}`)}${captionAuthors.length ? ": " : ""}`,
|
|
122
123
|
/* @__PURE__ */ jsx("span", { children: captionAuthors.map((author) => author.name).join(", ") }),
|
|
123
|
-
|
|
124
|
+
shouldShowLicense ? /* @__PURE__ */ jsxs(Fragment$1, { children: [" / ", /* @__PURE__ */ jsx(LicenseLink, {
|
|
124
125
|
license,
|
|
125
126
|
hideLink: !isOpen && !!children
|
|
126
127
|
})] }) : null
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EmbedByline.mjs","names":[],"sources":["../../src/LicenseByline/EmbedByline.tsx"],"sourcesContent":["/**\n * Copyright (c) 2023-present, NDLA.\n *\n * This source code is licensed under the GPLv3 license found in the\n * LICENSE file in the root directory of this source tree.\n *\n */\n\nimport { AlertLine } from \"@ndla/icons\";\nimport { getLicenseByAbbreviation } from \"@ndla/licenses\";\nimport { Button, Text } from \"@ndla/primitives\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport type { CopyrightDTO as ArticleCopyright } from \"@ndla/types-backend/article-api\";\nimport type { CopyrightDTO as AudioCopyright } from \"@ndla/types-backend/audio-api\";\nimport type { DraftCopyrightDTO as ConceptCopyright } from \"@ndla/types-backend/concept-api\";\nimport type { CopyrightDTO as ImageCopyright } from \"@ndla/types-backend/image-api\";\nimport type { BrightcoveCopyright } from \"@ndla/types-embed\";\nimport { type Dispatch, type ReactNode, type SetStateAction, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { LicenseLink } from \"./LicenseLink\";\n\ninterface BaseProps {\n description?: ReactNode;\n children?: ReactNode;\n visibleAlt?: string;\n error?: true | false;\n hideDescription?: boolean;\n hideCopyright?: boolean;\n}\n\nexport interface EmbedBylineErrorProps extends BaseProps {\n type: EmbedBylineTypeProps[\"type\"] | \"h5p\" | \"external\" | \"code\";\n error: true;\n}\n\ninterface ImageProps extends BaseProps {\n type: \"image\";\n copyright: ImageCopyright | undefined;\n}\n\ninterface BrightcoveProps extends BaseProps {\n type: \"video\";\n copyright: BrightcoveCopyright | undefined;\n}\n\ninterface AudioProps extends BaseProps {\n type: \"audio\";\n copyright: AudioCopyright | undefined;\n}\n\ninterface PodcastProps extends BaseProps {\n type: \"podcast\";\n copyright: AudioCopyright | undefined;\n}\n\ninterface ConceptProps extends BaseProps {\n type: \"concept\" | \"gloss\";\n copyright: ConceptCopyright | undefined;\n}\n\ninterface CopyrightProps extends BaseProps {\n type: \"copyright\";\n copyright: ArticleCopyright | undefined;\n}\n\nexport type EmbedBylineTypeProps =\n | ImageProps\n | BrightcoveProps\n | AudioProps\n | PodcastProps\n | ConceptProps\n | CopyrightProps;\n\ntype Props = EmbedBylineTypeProps | EmbedBylineErrorProps;\n\nconst BylineWrapper = styled(\"figcaption\", {\n base: {\n display: \"flex\",\n flexDirection: \"column\",\n paddingBlock: \"xsmall\",\n textStyle: \"label.medium\",\n color: \"text.subtle\",\n },\n});\n\nconst ErrorBylineWrapper = styled(BylineWrapper, {\n base: {\n border: \"1px solid\",\n borderColor: \"stroke.error\",\n borderRadius: \"xsmall\",\n background: \"surface.dangerSubtle\",\n paddingInline: \"medium\",\n paddingBlock: \"medium\",\n },\n});\n\nconst StyledText = styled(Text, {\n base: {\n fontStyle: \"italic\",\n },\n});\n\nconst ContentWrapper = styled(\"div\", {\n base: {\n display: \"flex\",\n gap: \"xsmall\",\n alignItems: \"center\",\n textStyle: \"label.medium\",\n },\n});\n\nconst BaseDescription = styled(\"div\", {\n base: {\n display: \"inline-flex\",\n whiteSpace: \"pre-wrap\",\n },\n});\n\nexport const EmbedByline = ({ type, description, children, visibleAlt, hideCopyright, ...props }: Props) => {\n const { t } = useTranslation();\n\n if (props.error) {\n const typeString = type === \"h5p\" ? \"H5P\" : t(`embed.type.${type}`).toLowerCase();\n return (\n <ErrorBylineWrapper>\n <ContentWrapper>\n <AlertLine />\n <BaseDescription>\n <span>{t(\"embed.embedError\", { type: typeString })}</span>\n </BaseDescription>\n </ContentWrapper>\n </ErrorBylineWrapper>\n );\n }\n\n const { copyright } = props;\n const hideByline = hideCopyright && !description;\n\n return (\n <>\n {!hideByline && (\n <BylineWrapper>\n <div>\n {!!hideCopyright && description}\n {!hideCopyright && (\n <LicenseContainerContent type={type} copyright={copyright}>\n {description}\n </LicenseContainerContent>\n )}\n {children}\n </div>\n </BylineWrapper>\n )}\n {visibleAlt ? (\n <StyledText color=\"text.subtle\" textStyle=\"label.medium\" asChild consumeCss>\n <span>{`Alt: ${visibleAlt}`}</span>\n </StyledText>\n ) : null}\n </>\n );\n};\n\ninterface LicenseContainerProps {\n children?: ReactNode;\n copyright: EmbedBylineTypeProps[\"copyright\"];\n type: Props[\"type\"];\n}\n\nconst StyledDescription = styled(BaseDescription, {\n base: {\n mobileWideDown: {\n display: \"grid\",\n gridTemplateColumns: \"1fr auto\",\n alignItems: \"center\",\n overflow: \"hidden\",\n _open: {\n display: \"inline\",\n },\n },\n },\n});\n\nconst TextContent = styled(\"span\", {\n base: {\n mobileWideDown: {\n whiteSpace: \"nowrap\",\n maxHeight: \"large\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n transitionProperty: \"max-height\",\n transitionDuration: \"slow\",\n transitionTimingFunction: \"ease-in\",\n marginInlineEnd: \"4xsmall\",\n _open: {\n whiteSpace: \"pre-wrap\",\n maxHeight: \"none\",\n },\n },\n },\n});\n\nconst StyledButton = styled(Button, {\n base: {\n mobileWide: {\n display: \"none\",\n },\n _print: {\n display: \"none\",\n },\n },\n});\n\ninterface LicenseDescriptionProps {\n children?: ReactNode;\n isOpen: boolean;\n setIsOpen: Dispatch<SetStateAction<boolean>>;\n}\n\nconst LicenseDescription = ({ children, isOpen, setIsOpen }: LicenseDescriptionProps) => {\n const open = isOpen ? { \"data-open\": \"\" } : {};\n const { t } = useTranslation();\n\n const handleToggle = () => {\n setIsOpen(!isOpen);\n };\n\n return (\n <ContentWrapper>\n <StyledDescription {...open}>\n <TextContent {...open}>{children}</TextContent>\n <StyledButton variant=\"link\" size=\"small\" onClick={handleToggle}>\n {isOpen ? `${t(\"audio.readLessDescriptionLabel\")}` : `${t(\"audio.readMoreDescriptionLabel\")}`}\n </StyledButton>\n </StyledDescription>\n </ContentWrapper>\n );\n};\n\nexport const LicenseContainerContent = ({ children, copyright, type }: LicenseContainerProps) => {\n const { t, i18n } = useTranslation();\n const license = copyright ? getLicenseByAbbreviation(copyright.license?.license ?? \"\", i18n.language) : undefined;\n const captionAuthors =\n [copyright?.creators, copyright?.rightsholders, copyright?.processors].find((authors) => authors?.length) ?? [];\n const [isOpen, setIsOpen] = useState<boolean>(false);\n\n const content = (\n <>\n {children}\n {` ${t(`embed.type.${type}`)}${captionAuthors.length ? \": \" : \"\"}`}\n <span>{captionAuthors.map((author) => author.name).join(\", \")}</span>\n {license ? (\n <>\n {\" / \"}\n {<LicenseLink license={license} hideLink={!isOpen && !!children} />}\n </>\n ) : null}\n </>\n );\n\n if (children) {\n return (\n <LicenseDescription isOpen={isOpen} setIsOpen={setIsOpen}>\n {content}\n </LicenseDescription>\n );\n }\n\n return (\n <Text textStyle=\"label.medium\" asChild consumeCss>\n <span>{content}</span>\n </Text>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EmbedByline.mjs","names":[],"sources":["../../src/LicenseByline/EmbedByline.tsx"],"sourcesContent":["/**\n * Copyright (c) 2023-present, NDLA.\n *\n * This source code is licensed under the GPLv3 license found in the\n * LICENSE file in the root directory of this source tree.\n *\n */\n\nimport { AlertLine } from \"@ndla/icons\";\nimport { getLicenseByAbbreviation, rights } from \"@ndla/licenses\";\nimport { Button, Text } from \"@ndla/primitives\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport type { CopyrightDTO as ArticleCopyright } from \"@ndla/types-backend/article-api\";\nimport type { CopyrightDTO as AudioCopyright } from \"@ndla/types-backend/audio-api\";\nimport type { DraftCopyrightDTO as ConceptCopyright } from \"@ndla/types-backend/concept-api\";\nimport type { CopyrightDTO as ImageCopyright } from \"@ndla/types-backend/image-api\";\nimport type { BrightcoveCopyright } from \"@ndla/types-embed\";\nimport { type Dispatch, type ReactNode, type SetStateAction, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { LicenseLink } from \"./LicenseLink\";\n\ninterface BaseProps {\n description?: ReactNode;\n children?: ReactNode;\n visibleAlt?: string;\n error?: true | false;\n hideDescription?: boolean;\n hideCopyright?: boolean;\n}\n\nexport interface EmbedBylineErrorProps extends BaseProps {\n type: EmbedBylineTypeProps[\"type\"] | \"h5p\" | \"external\" | \"code\";\n error: true;\n}\n\ninterface ImageProps extends BaseProps {\n type: \"image\";\n copyright: ImageCopyright | undefined;\n}\n\ninterface BrightcoveProps extends BaseProps {\n type: \"video\";\n copyright: BrightcoveCopyright | undefined;\n}\n\ninterface AudioProps extends BaseProps {\n type: \"audio\";\n copyright: AudioCopyright | undefined;\n}\n\ninterface PodcastProps extends BaseProps {\n type: \"podcast\";\n copyright: AudioCopyright | undefined;\n}\n\ninterface ConceptProps extends BaseProps {\n type: \"concept\" | \"gloss\";\n copyright: ConceptCopyright | undefined;\n}\n\ninterface CopyrightProps extends BaseProps {\n type: \"copyright\";\n copyright: ArticleCopyright | undefined;\n}\n\nexport type EmbedBylineTypeProps =\n | ImageProps\n | BrightcoveProps\n | AudioProps\n | PodcastProps\n | ConceptProps\n | CopyrightProps;\n\ntype Props = EmbedBylineTypeProps | EmbedBylineErrorProps;\n\nconst BylineWrapper = styled(\"figcaption\", {\n base: {\n display: \"flex\",\n flexDirection: \"column\",\n paddingBlock: \"xsmall\",\n textStyle: \"label.medium\",\n color: \"text.subtle\",\n },\n});\n\nconst ErrorBylineWrapper = styled(BylineWrapper, {\n base: {\n border: \"1px solid\",\n borderColor: \"stroke.error\",\n borderRadius: \"xsmall\",\n background: \"surface.dangerSubtle\",\n paddingInline: \"medium\",\n paddingBlock: \"medium\",\n },\n});\n\nconst StyledText = styled(Text, {\n base: {\n fontStyle: \"italic\",\n },\n});\n\nconst ContentWrapper = styled(\"div\", {\n base: {\n display: \"flex\",\n gap: \"xsmall\",\n alignItems: \"center\",\n textStyle: \"label.medium\",\n },\n});\n\nconst BaseDescription = styled(\"div\", {\n base: {\n display: \"inline-flex\",\n whiteSpace: \"pre-wrap\",\n },\n});\n\nexport const EmbedByline = ({ type, description, children, visibleAlt, hideCopyright, ...props }: Props) => {\n const { t } = useTranslation();\n\n if (props.error) {\n const typeString = type === \"h5p\" ? \"H5P\" : t(`embed.type.${type}`).toLowerCase();\n return (\n <ErrorBylineWrapper>\n <ContentWrapper>\n <AlertLine />\n <BaseDescription>\n <span>{t(\"embed.embedError\", { type: typeString })}</span>\n </BaseDescription>\n </ContentWrapper>\n </ErrorBylineWrapper>\n );\n }\n\n const { copyright } = props;\n const hideByline = hideCopyright && !description;\n\n return (\n <>\n {!hideByline && (\n <BylineWrapper>\n <div>\n {!!hideCopyright && description}\n {!hideCopyright && (\n <LicenseContainerContent type={type} copyright={copyright}>\n {description}\n </LicenseContainerContent>\n )}\n {children}\n </div>\n </BylineWrapper>\n )}\n {visibleAlt ? (\n <StyledText color=\"text.subtle\" textStyle=\"label.medium\" asChild consumeCss>\n <span>{`Alt: ${visibleAlt}`}</span>\n </StyledText>\n ) : null}\n </>\n );\n};\n\ninterface LicenseContainerProps {\n children?: ReactNode;\n copyright: EmbedBylineTypeProps[\"copyright\"];\n type: Props[\"type\"];\n}\n\nconst StyledDescription = styled(BaseDescription, {\n base: {\n mobileWideDown: {\n display: \"grid\",\n gridTemplateColumns: \"1fr auto\",\n alignItems: \"center\",\n overflow: \"hidden\",\n _open: {\n display: \"inline\",\n },\n },\n },\n});\n\nconst TextContent = styled(\"span\", {\n base: {\n mobileWideDown: {\n whiteSpace: \"nowrap\",\n maxHeight: \"large\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n transitionProperty: \"max-height\",\n transitionDuration: \"slow\",\n transitionTimingFunction: \"ease-in\",\n marginInlineEnd: \"4xsmall\",\n _open: {\n whiteSpace: \"pre-wrap\",\n maxHeight: \"none\",\n },\n },\n },\n});\n\nconst StyledButton = styled(Button, {\n base: {\n mobileWide: {\n display: \"none\",\n },\n _print: {\n display: \"none\",\n },\n },\n});\n\ninterface LicenseDescriptionProps {\n children?: ReactNode;\n isOpen: boolean;\n setIsOpen: Dispatch<SetStateAction<boolean>>;\n}\n\nconst LicenseDescription = ({ children, isOpen, setIsOpen }: LicenseDescriptionProps) => {\n const open = isOpen ? { \"data-open\": \"\" } : {};\n const { t } = useTranslation();\n\n const handleToggle = () => {\n setIsOpen(!isOpen);\n };\n\n return (\n <ContentWrapper>\n <StyledDescription {...open}>\n <TextContent {...open}>{children}</TextContent>\n <StyledButton variant=\"link\" size=\"small\" onClick={handleToggle}>\n {isOpen ? `${t(\"audio.readLessDescriptionLabel\")}` : `${t(\"audio.readMoreDescriptionLabel\")}`}\n </StyledButton>\n </StyledDescription>\n </ContentWrapper>\n );\n};\n\nexport const LicenseContainerContent = ({ children, copyright, type }: LicenseContainerProps) => {\n const { t, i18n } = useTranslation();\n const license = copyright ? getLicenseByAbbreviation(copyright.license?.license ?? \"\", i18n.language) : undefined;\n const shouldShowLicense = !!license && !license.rights.includes(rights.NA);\n const captionAuthors =\n [copyright?.creators, copyright?.rightsholders, copyright?.processors].find((authors) => authors?.length) ?? [];\n const [isOpen, setIsOpen] = useState<boolean>(false);\n\n const content = (\n <>\n {children}\n {` ${t(`embed.type.${type}`)}${captionAuthors.length ? \": \" : \"\"}`}\n <span>{captionAuthors.map((author) => author.name).join(\", \")}</span>\n {shouldShowLicense ? (\n <>\n {\" / \"}\n {<LicenseLink license={license} hideLink={!isOpen && !!children} />}\n </>\n ) : null}\n </>\n );\n\n if (children) {\n return (\n <LicenseDescription isOpen={isOpen} setIsOpen={setIsOpen}>\n {content}\n </LicenseDescription>\n );\n }\n\n return (\n <Text textStyle=\"label.medium\" asChild consumeCss>\n <span>{content}</span>\n </Text>\n );\n};\n"],"mappings":"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"}
|
|
@@ -8,27 +8,33 @@ let react_jsx_runtime = require("react/jsx-runtime");
|
|
|
8
8
|
const StyledSliderThumb = (0, _ndla_styled_system_jsx.styled)(_ndla_primitives.SliderThumb, { variants: { variant: {
|
|
9
9
|
standard: {},
|
|
10
10
|
simple: {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
height: "4xsmall"
|
|
11
|
+
marginBlockStart: "-4xsmall",
|
|
12
|
+
transitionProperty: "background, border-radius, width, height"
|
|
14
13
|
}
|
|
15
14
|
} } });
|
|
16
15
|
const StyledSliderTrack = (0, _ndla_styled_system_jsx.styled)(_ndla_primitives.SliderTrack, { variants: { variant: {
|
|
17
16
|
standard: {},
|
|
18
|
-
simple: {
|
|
17
|
+
simple: {
|
|
18
|
+
marginBlockStart: "-4xsmall",
|
|
19
|
+
background: "unset"
|
|
20
|
+
}
|
|
19
21
|
} } });
|
|
20
|
-
const
|
|
22
|
+
const StyledSliderRoot = (0, _ndla_styled_system_jsx.styled)(_ndla_primitives.SliderRoot, { variants: { variant: {
|
|
21
23
|
standard: {},
|
|
22
|
-
simple: {
|
|
24
|
+
simple: {
|
|
25
|
+
position: "relative",
|
|
26
|
+
marginBlockEnd: "-xsmall"
|
|
27
|
+
}
|
|
23
28
|
} } });
|
|
24
29
|
const AudioProgress = ({ currentTime, duration, onValueChange, variant }) => {
|
|
25
30
|
const { t } = (0, react_i18next.useTranslation)();
|
|
26
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(
|
|
31
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(StyledSliderRoot, {
|
|
27
32
|
value: [currentTime],
|
|
28
33
|
defaultValue: [0],
|
|
29
34
|
step: 1,
|
|
30
35
|
max: duration,
|
|
31
36
|
onValueChange,
|
|
37
|
+
variant,
|
|
32
38
|
getAriaValueText: (value) => t("audio.valueText", {
|
|
33
39
|
start: require_audioUtils.formatTime(Math.round(value.value)),
|
|
34
40
|
end: require_audioUtils.formatTime(Math.round(duration))
|
|
@@ -36,17 +42,14 @@ const AudioProgress = ({ currentTime, duration, onValueChange, variant }) => {
|
|
|
36
42
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_ndla_primitives.SliderLabel, {
|
|
37
43
|
srOnly: true,
|
|
38
44
|
children: t("audio.progressBar")
|
|
39
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(
|
|
45
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_ndla_primitives.SliderControl, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StyledSliderTrack, {
|
|
46
|
+
variant,
|
|
47
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_ndla_primitives.SliderRange, {})
|
|
48
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StyledSliderThumb, {
|
|
49
|
+
index: 0,
|
|
40
50
|
variant,
|
|
41
|
-
children:
|
|
42
|
-
|
|
43
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_ndla_primitives.SliderRange, {})
|
|
44
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StyledSliderThumb, {
|
|
45
|
-
index: 0,
|
|
46
|
-
variant,
|
|
47
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_ndla_primitives.SliderHiddenInput, {})
|
|
48
|
-
})]
|
|
49
|
-
})]
|
|
51
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_ndla_primitives.SliderHiddenInput, {})
|
|
52
|
+
})] })]
|
|
50
53
|
});
|
|
51
54
|
};
|
|
52
55
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AudioProgress.js","names":["SliderThumb","SliderTrack","
|
|
1
|
+
{"version":3,"file":"AudioProgress.js","names":["SliderThumb","SliderTrack","SliderRoot","formatTime","SliderLabel","SliderControl","SliderRange","SliderHiddenInput"],"sources":["../../src/AudioPlayer/AudioProgress.tsx"],"sourcesContent":["/**\n * Copyright (c) 2026-present, NDLA.\n *\n * This source code is licensed under the GPLv3 license found in the\n * LICENSE file in the root directory of this source tree.\n *\n */\n\nimport type { SliderValueChangeDetails } from \"@ark-ui/react\";\nimport {\n SliderRoot,\n SliderLabel,\n SliderControl,\n SliderTrack,\n SliderRange,\n SliderThumb,\n SliderHiddenInput,\n} from \"@ndla/primitives\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport { useTranslation } from \"react-i18next\";\nimport { formatTime } from \"./audioUtils\";\n\ninterface Props {\n currentTime: number;\n duration: number;\n onValueChange: (details: SliderValueChangeDetails) => void;\n variant?: \"simple\" | \"standard\";\n}\n\nconst StyledSliderThumb = styled(SliderThumb, {\n variants: {\n variant: {\n standard: {},\n simple: {\n marginBlockStart: \"-4xsmall\",\n transitionProperty: \"background, border-radius, width, height\",\n },\n },\n },\n});\n\nconst StyledSliderTrack = styled(SliderTrack, {\n variants: {\n variant: {\n standard: {},\n simple: {\n marginBlockStart: \"-4xsmall\",\n background: \"unset\",\n },\n },\n },\n});\n\nconst StyledSliderRoot = styled(SliderRoot, {\n variants: {\n variant: {\n standard: {},\n simple: {\n position: \"relative\",\n marginBlockEnd: \"-xsmall\",\n },\n },\n },\n});\n\nexport const AudioProgress = ({ currentTime, duration, onValueChange, variant }: Props) => {\n const { t } = useTranslation();\n return (\n <StyledSliderRoot\n value={[currentTime]}\n defaultValue={[0]}\n step={1}\n max={duration}\n onValueChange={onValueChange}\n variant={variant}\n getAriaValueText={(value) =>\n t(\"audio.valueText\", {\n start: formatTime(Math.round(value.value)),\n end: formatTime(Math.round(duration)),\n })\n }\n >\n <SliderLabel srOnly>{t(\"audio.progressBar\")}</SliderLabel>\n <SliderControl>\n <StyledSliderTrack variant={variant}>\n <SliderRange />\n </StyledSliderTrack>\n <StyledSliderThumb index={0} variant={variant}>\n <SliderHiddenInput />\n </StyledSliderThumb>\n </SliderControl>\n </StyledSliderRoot>\n );\n};\n"],"mappings":";;;;;;;AA6BA,MAAM,qBAAA,GAAA,wBAAA,QAA2BA,iBAAAA,aAAa,EAC5C,UAAU,EACR,SAAS;CACP,UAAU,EAAE;CACZ,QAAQ;EACN,kBAAkB;EAClB,oBAAoB;EACrB;CACF,EACF,EACF,CAAC;AAEF,MAAM,qBAAA,GAAA,wBAAA,QAA2BC,iBAAAA,aAAa,EAC5C,UAAU,EACR,SAAS;CACP,UAAU,EAAE;CACZ,QAAQ;EACN,kBAAkB;EAClB,YAAY;EACb;CACF,EACF,EACF,CAAC;AAEF,MAAM,oBAAA,GAAA,wBAAA,QAA0BC,iBAAAA,YAAY,EAC1C,UAAU,EACR,SAAS;CACP,UAAU,EAAE;CACZ,QAAQ;EACN,UAAU;EACV,gBAAgB;EACjB;CACF,EACF,EACF,CAAC;AAEF,MAAa,iBAAiB,EAAE,aAAa,UAAU,eAAe,cAAqB;CACzF,MAAM,EAAE,OAAA,GAAA,cAAA,iBAAsB;AAC9B,QACE,iBAAA,GAAA,kBAAA,MAAC,kBAAD;EACE,OAAO,CAAC,YAAY;EACpB,cAAc,CAAC,EAAE;EACjB,MAAM;EACN,KAAK;EACU;EACN;EACT,mBAAmB,UACjB,EAAE,mBAAmB;GACnB,OAAOC,mBAAAA,WAAW,KAAK,MAAM,MAAM,MAAM,CAAC;GAC1C,KAAKA,mBAAAA,WAAW,KAAK,MAAM,SAAS,CAAC;GACtC,CAAC;YAXN,CAcE,iBAAA,GAAA,kBAAA,KAACC,iBAAAA,aAAD;GAAa,QAAA;aAAQ,EAAE,oBAAoB;GAAe,CAAA,EAC1D,iBAAA,GAAA,kBAAA,MAACC,iBAAAA,eAAD,EAAA,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAC,mBAAD;GAA4B;aAC1B,iBAAA,GAAA,kBAAA,KAACC,iBAAAA,aAAD,EAAe,CAAA;GACG,CAAA,EACpB,iBAAA,GAAA,kBAAA,KAAC,mBAAD;GAAmB,OAAO;GAAY;aACpC,iBAAA,GAAA,kBAAA,KAACC,iBAAAA,mBAAD,EAAqB,CAAA;GACH,CAAA,CACN,EAAA,CAAA,CACC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BrightcoveEmbed.js","names":["Button","EmbedErrorPlaceholder","licenseAttributes","Figure","EmbedByline"],"sources":["../../src/Embed/BrightcoveEmbed.tsx"],"sourcesContent":["/**\n * Copyright (c) 2023-present, NDLA.\n *\n * This source code is licensed under the GPLv3 license found in the\n * LICENSE file in the root directory of this source tree.\n *\n */\n\nimport { Button, Figure } from \"@ndla/primitives\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport type { BrightcoveEmbedData, BrightcoveMetaData, BrightcoveVideoSource } from \"@ndla/types-embed\";\nimport parse from \"html-react-parser\";\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { EmbedByline } from \"../LicenseByline/EmbedByline\";\nimport { licenseAttributes } from \"../utils/licenseAttributes\";\nimport { EmbedErrorPlaceholder } from \"./EmbedErrorPlaceholder\";\nimport type { RenderContext } from \"./types\";\n\ninterface Props {\n embed: BrightcoveMetaData;\n renderContext?: RenderContext;\n lang?: string;\n}\n\nconst LinkedVideoButton = styled(Button, {\n base: {\n marginBlockStart: \"3xsmall\",\n },\n});\n\nconst BrightcoveIframe = styled(\"iframe\", {\n base: {\n border: 0,\n height: \"auto\",\n width: \"100%\",\n },\n});\n\nexport const makeIframeString = (url: string, width: string | number, height: string | number, title = \"\") => {\n const strippedWidth = typeof width === \"number\" ? width : width.replace(/\\s*px/, \"\");\n const strippedHeight = typeof height === \"number\" ? height : height.replace(/\\s*px/, \"\");\n const urlOrTitle = title || url;\n return `<iframe title=\"${urlOrTitle}\" aria-label=\"${urlOrTitle}\" src=\"${url}\" width=\"${strippedWidth}\" height=\"${strippedHeight}\" allowfullscreen scrolling=\"no\" style=\"border: none;\" loading=\"lazy\"></iframe>`;\n};\n\nexport const isNumeric = (value: any) => !Number.isNaN(value - Number.parseFloat(value));\n\nconst getIframeProps = (data: BrightcoveEmbedData, sources: BrightcoveVideoSource[]) => {\n const { account, videoid, player = \"default\" } = data;\n\n const source = sources.filter((s) => s.width && s.height).toSorted((a, b) => a!.height! - b.height!)[0];\n\n return {\n src: `https://players.brightcove.net/${account}/${player}_default/index.html?videoId=${videoid}`,\n height: source?.height ?? \"480\",\n width: source?.width ?? \"640\",\n };\n};\nexport const BrightcoveEmbed = ({ embed, renderContext = \"article\", lang }: Props) => {\n const [showOriginalVideo, setShowOriginalVideo] = useState(true);\n const { t } = useTranslation();\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const { embedData } = embed;\n const fallbackTitle = `${t(\"embed.type.video\")}: ${embedData.videoid}`;\n const parsedDescription = useMemo(() => {\n if (embed.embedData.caption || renderContext === \"article\") {\n return embed.embedData.caption ? parse(embed.embedData.caption) : undefined;\n } else if (embed.status === \"success\" && embed.data.description) {\n return parse(embed.data.description);\n }\n }, [embed, renderContext]);\n\n useEffect(() => {\n const iframe = iframeRef.current;\n if (iframe) {\n const [width, height] = [parseInt(iframe.width), parseInt(iframe.height)];\n iframe.style.aspectRatio = `${width}/${height}`;\n iframe.width = \"\";\n iframe.height = \"\";\n }\n }, []);\n if (embed.status === \"error\") {\n return (\n <EmbedErrorPlaceholder type=\"video\">\n <BrightcoveIframe\n ref={iframeRef}\n title={embedData.alt || fallbackTitle}\n aria-label={embedData.alt || fallbackTitle}\n {...getIframeProps(embedData, [])}\n allow=\"fullscreen; encrypted-media\"\n />\n </EmbedErrorPlaceholder>\n );\n }\n const { data } = embed;\n\n const linkedVideoId = isNumeric(data.link?.text) ? data.link?.text : undefined;\n\n const originalVideoProps = getIframeProps(embedData, data.sources);\n const alternativeVideoProps = linkedVideoId\n ? getIframeProps({ ...embedData, videoid: linkedVideoId }, data.sources)\n : undefined;\n\n const licenseProps = licenseAttributes(data?.copyright?.license.license, lang, embedData.pageUrl);\n\n const title = data.name?.trim() ? `${t(\"embed.type.video\")}: ${data.name}` : fallbackTitle;\n\n return (\n <Figure data-embed-type=\"brightcove\" {...licenseProps}>\n <div className=\"brightcove-video\">\n <BrightcoveIframe\n ref={iframeRef}\n className=\"original\"\n title={title}\n aria-label={title}\n {...(alternativeVideoProps && !showOriginalVideo ? alternativeVideoProps : originalVideoProps)}\n allow=\"fullscreen; encrypted-media\"\n />\n </div>\n <EmbedByline type=\"video\" copyright={data.copyright!} description={parsedDescription}>\n <div>\n {!!linkedVideoId && (\n <LinkedVideoButton size=\"small\" variant=\"secondary\" onClick={() => setShowOriginalVideo((p) => !p)}>\n {t(`figure.button.${!showOriginalVideo ? \"original\" : \"alternative\"}`)}\n </LinkedVideoButton>\n )}\n </div>\n </EmbedByline>\n </Figure>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAyBA,MAAM,qBAAA,GAAA,wBAAA,QAA2BA,iBAAAA,QAAQ,EACvC,MAAM,EACJ,kBAAkB,WACnB,EACF,CAAC;AAEF,MAAM,oBAAA,GAAA,wBAAA,QAA0B,UAAU,EACxC,MAAM;CACJ,QAAQ;CACR,QAAQ;CACR,OAAO;CACR,EACF,CAAC;AASF,MAAa,aAAa,UAAe,CAAC,OAAO,MAAM,QAAQ,OAAO,WAAW,MAAM,CAAC;AAExF,MAAM,kBAAkB,MAA2B,YAAqC;
|
|
1
|
+
{"version":3,"file":"BrightcoveEmbed.js","names":["Button","EmbedErrorPlaceholder","licenseAttributes","Figure","EmbedByline"],"sources":["../../src/Embed/BrightcoveEmbed.tsx"],"sourcesContent":["/**\n * Copyright (c) 2023-present, NDLA.\n *\n * This source code is licensed under the GPLv3 license found in the\n * LICENSE file in the root directory of this source tree.\n *\n */\n\nimport { Button, Figure } from \"@ndla/primitives\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport type { BrightcoveEmbedData, BrightcoveMetaData, BrightcoveVideoSource } from \"@ndla/types-embed\";\nimport parse from \"html-react-parser\";\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { EmbedByline } from \"../LicenseByline/EmbedByline\";\nimport { licenseAttributes } from \"../utils/licenseAttributes\";\nimport { EmbedErrorPlaceholder } from \"./EmbedErrorPlaceholder\";\nimport type { RenderContext } from \"./types\";\n\ninterface Props {\n embed: BrightcoveMetaData;\n renderContext?: RenderContext;\n lang?: string;\n}\n\nconst LinkedVideoButton = styled(Button, {\n base: {\n marginBlockStart: \"3xsmall\",\n },\n});\n\nconst BrightcoveIframe = styled(\"iframe\", {\n base: {\n border: 0,\n height: \"auto\",\n width: \"100%\",\n },\n});\n\nexport const makeIframeString = (url: string, width: string | number, height: string | number, title = \"\") => {\n const strippedWidth = typeof width === \"number\" ? width : width.replace(/\\s*px/, \"\");\n const strippedHeight = typeof height === \"number\" ? height : height.replace(/\\s*px/, \"\");\n const urlOrTitle = title || url;\n return `<iframe title=\"${urlOrTitle}\" aria-label=\"${urlOrTitle}\" src=\"${url}\" width=\"${strippedWidth}\" height=\"${strippedHeight}\" allowfullscreen scrolling=\"no\" style=\"border: none;\" loading=\"lazy\"></iframe>`;\n};\n\nexport const isNumeric = (value: any) => !Number.isNaN(value - Number.parseFloat(value));\n\nconst getIframeProps = (data: BrightcoveEmbedData, sources: BrightcoveVideoSource[]) => {\n // oxlint-disable-next-line typescript/no-useless-default-assignment\n const { account, videoid, player = \"default\" } = data;\n\n const source = sources.filter((s) => s.width && s.height).toSorted((a, b) => a!.height! - b.height!)[0];\n\n return {\n src: `https://players.brightcove.net/${account}/${player}_default/index.html?videoId=${videoid}`,\n height: source?.height ?? \"480\",\n width: source?.width ?? \"640\",\n };\n};\nexport const BrightcoveEmbed = ({ embed, renderContext = \"article\", lang }: Props) => {\n const [showOriginalVideo, setShowOriginalVideo] = useState(true);\n const { t } = useTranslation();\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const { embedData } = embed;\n const fallbackTitle = `${t(\"embed.type.video\")}: ${embedData.videoid}`;\n const parsedDescription = useMemo(() => {\n if (embed.embedData.caption || renderContext === \"article\") {\n return embed.embedData.caption ? parse(embed.embedData.caption) : undefined;\n } else if (embed.status === \"success\" && embed.data.description) {\n return parse(embed.data.description);\n }\n }, [embed, renderContext]);\n\n useEffect(() => {\n const iframe = iframeRef.current;\n if (iframe) {\n const [width, height] = [parseInt(iframe.width), parseInt(iframe.height)];\n iframe.style.aspectRatio = `${width}/${height}`;\n iframe.width = \"\";\n iframe.height = \"\";\n }\n }, []);\n if (embed.status === \"error\") {\n return (\n <EmbedErrorPlaceholder type=\"video\">\n <BrightcoveIframe\n ref={iframeRef}\n title={embedData.alt || fallbackTitle}\n aria-label={embedData.alt || fallbackTitle}\n {...getIframeProps(embedData, [])}\n allow=\"fullscreen; encrypted-media\"\n />\n </EmbedErrorPlaceholder>\n );\n }\n const { data } = embed;\n\n const linkedVideoId = isNumeric(data.link?.text) ? data.link?.text : undefined;\n\n const originalVideoProps = getIframeProps(embedData, data.sources);\n const alternativeVideoProps = linkedVideoId\n ? getIframeProps({ ...embedData, videoid: linkedVideoId }, data.sources)\n : undefined;\n\n const licenseProps = licenseAttributes(data?.copyright?.license.license, lang, embedData.pageUrl);\n\n const title = data.name?.trim() ? `${t(\"embed.type.video\")}: ${data.name}` : fallbackTitle;\n\n return (\n <Figure data-embed-type=\"brightcove\" {...licenseProps}>\n <div className=\"brightcove-video\">\n <BrightcoveIframe\n ref={iframeRef}\n className=\"original\"\n title={title}\n aria-label={title}\n {...(alternativeVideoProps && !showOriginalVideo ? alternativeVideoProps : originalVideoProps)}\n allow=\"fullscreen; encrypted-media\"\n />\n </div>\n <EmbedByline type=\"video\" copyright={data.copyright!} description={parsedDescription}>\n <div>\n {!!linkedVideoId && (\n <LinkedVideoButton size=\"small\" variant=\"secondary\" onClick={() => setShowOriginalVideo((p) => !p)}>\n {t(`figure.button.${!showOriginalVideo ? \"original\" : \"alternative\"}`)}\n </LinkedVideoButton>\n )}\n </div>\n </EmbedByline>\n </Figure>\n );\n};\n"],"mappings":"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"}
|
|
@@ -111,6 +111,7 @@ const LicenseDescription = ({ children, isOpen, setIsOpen }) => {
|
|
|
111
111
|
const LicenseContainerContent = ({ children, copyright, type }) => {
|
|
112
112
|
const { t, i18n } = (0, react_i18next.useTranslation)();
|
|
113
113
|
const license = copyright ? (0, _ndla_licenses.getLicenseByAbbreviation)(copyright.license?.license ?? "", i18n.language) : void 0;
|
|
114
|
+
const shouldShowLicense = !!license && !license.rights.includes(_ndla_licenses.rights.NA);
|
|
114
115
|
const captionAuthors = [
|
|
115
116
|
copyright?.creators,
|
|
116
117
|
copyright?.rightsholders,
|
|
@@ -121,7 +122,7 @@ const LicenseContainerContent = ({ children, copyright, type }) => {
|
|
|
121
122
|
children,
|
|
122
123
|
` ${t(`embed.type.${type}`)}${captionAuthors.length ? ": " : ""}`,
|
|
123
124
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: captionAuthors.map((author) => author.name).join(", ") }),
|
|
124
|
-
|
|
125
|
+
shouldShowLicense ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [" / ", /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_LicenseLink.LicenseLink, {
|
|
125
126
|
license,
|
|
126
127
|
hideLink: !isOpen && !!children
|
|
127
128
|
})] }) : null
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EmbedByline.js","names":["Text","AlertLine","Button","LicenseLink"],"sources":["../../src/LicenseByline/EmbedByline.tsx"],"sourcesContent":["/**\n * Copyright (c) 2023-present, NDLA.\n *\n * This source code is licensed under the GPLv3 license found in the\n * LICENSE file in the root directory of this source tree.\n *\n */\n\nimport { AlertLine } from \"@ndla/icons\";\nimport { getLicenseByAbbreviation } from \"@ndla/licenses\";\nimport { Button, Text } from \"@ndla/primitives\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport type { CopyrightDTO as ArticleCopyright } from \"@ndla/types-backend/article-api\";\nimport type { CopyrightDTO as AudioCopyright } from \"@ndla/types-backend/audio-api\";\nimport type { DraftCopyrightDTO as ConceptCopyright } from \"@ndla/types-backend/concept-api\";\nimport type { CopyrightDTO as ImageCopyright } from \"@ndla/types-backend/image-api\";\nimport type { BrightcoveCopyright } from \"@ndla/types-embed\";\nimport { type Dispatch, type ReactNode, type SetStateAction, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { LicenseLink } from \"./LicenseLink\";\n\ninterface BaseProps {\n description?: ReactNode;\n children?: ReactNode;\n visibleAlt?: string;\n error?: true | false;\n hideDescription?: boolean;\n hideCopyright?: boolean;\n}\n\nexport interface EmbedBylineErrorProps extends BaseProps {\n type: EmbedBylineTypeProps[\"type\"] | \"h5p\" | \"external\" | \"code\";\n error: true;\n}\n\ninterface ImageProps extends BaseProps {\n type: \"image\";\n copyright: ImageCopyright | undefined;\n}\n\ninterface BrightcoveProps extends BaseProps {\n type: \"video\";\n copyright: BrightcoveCopyright | undefined;\n}\n\ninterface AudioProps extends BaseProps {\n type: \"audio\";\n copyright: AudioCopyright | undefined;\n}\n\ninterface PodcastProps extends BaseProps {\n type: \"podcast\";\n copyright: AudioCopyright | undefined;\n}\n\ninterface ConceptProps extends BaseProps {\n type: \"concept\" | \"gloss\";\n copyright: ConceptCopyright | undefined;\n}\n\ninterface CopyrightProps extends BaseProps {\n type: \"copyright\";\n copyright: ArticleCopyright | undefined;\n}\n\nexport type EmbedBylineTypeProps =\n | ImageProps\n | BrightcoveProps\n | AudioProps\n | PodcastProps\n | ConceptProps\n | CopyrightProps;\n\ntype Props = EmbedBylineTypeProps | EmbedBylineErrorProps;\n\nconst BylineWrapper = styled(\"figcaption\", {\n base: {\n display: \"flex\",\n flexDirection: \"column\",\n paddingBlock: \"xsmall\",\n textStyle: \"label.medium\",\n color: \"text.subtle\",\n },\n});\n\nconst ErrorBylineWrapper = styled(BylineWrapper, {\n base: {\n border: \"1px solid\",\n borderColor: \"stroke.error\",\n borderRadius: \"xsmall\",\n background: \"surface.dangerSubtle\",\n paddingInline: \"medium\",\n paddingBlock: \"medium\",\n },\n});\n\nconst StyledText = styled(Text, {\n base: {\n fontStyle: \"italic\",\n },\n});\n\nconst ContentWrapper = styled(\"div\", {\n base: {\n display: \"flex\",\n gap: \"xsmall\",\n alignItems: \"center\",\n textStyle: \"label.medium\",\n },\n});\n\nconst BaseDescription = styled(\"div\", {\n base: {\n display: \"inline-flex\",\n whiteSpace: \"pre-wrap\",\n },\n});\n\nexport const EmbedByline = ({ type, description, children, visibleAlt, hideCopyright, ...props }: Props) => {\n const { t } = useTranslation();\n\n if (props.error) {\n const typeString = type === \"h5p\" ? \"H5P\" : t(`embed.type.${type}`).toLowerCase();\n return (\n <ErrorBylineWrapper>\n <ContentWrapper>\n <AlertLine />\n <BaseDescription>\n <span>{t(\"embed.embedError\", { type: typeString })}</span>\n </BaseDescription>\n </ContentWrapper>\n </ErrorBylineWrapper>\n );\n }\n\n const { copyright } = props;\n const hideByline = hideCopyright && !description;\n\n return (\n <>\n {!hideByline && (\n <BylineWrapper>\n <div>\n {!!hideCopyright && description}\n {!hideCopyright && (\n <LicenseContainerContent type={type} copyright={copyright}>\n {description}\n </LicenseContainerContent>\n )}\n {children}\n </div>\n </BylineWrapper>\n )}\n {visibleAlt ? (\n <StyledText color=\"text.subtle\" textStyle=\"label.medium\" asChild consumeCss>\n <span>{`Alt: ${visibleAlt}`}</span>\n </StyledText>\n ) : null}\n </>\n );\n};\n\ninterface LicenseContainerProps {\n children?: ReactNode;\n copyright: EmbedBylineTypeProps[\"copyright\"];\n type: Props[\"type\"];\n}\n\nconst StyledDescription = styled(BaseDescription, {\n base: {\n mobileWideDown: {\n display: \"grid\",\n gridTemplateColumns: \"1fr auto\",\n alignItems: \"center\",\n overflow: \"hidden\",\n _open: {\n display: \"inline\",\n },\n },\n },\n});\n\nconst TextContent = styled(\"span\", {\n base: {\n mobileWideDown: {\n whiteSpace: \"nowrap\",\n maxHeight: \"large\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n transitionProperty: \"max-height\",\n transitionDuration: \"slow\",\n transitionTimingFunction: \"ease-in\",\n marginInlineEnd: \"4xsmall\",\n _open: {\n whiteSpace: \"pre-wrap\",\n maxHeight: \"none\",\n },\n },\n },\n});\n\nconst StyledButton = styled(Button, {\n base: {\n mobileWide: {\n display: \"none\",\n },\n _print: {\n display: \"none\",\n },\n },\n});\n\ninterface LicenseDescriptionProps {\n children?: ReactNode;\n isOpen: boolean;\n setIsOpen: Dispatch<SetStateAction<boolean>>;\n}\n\nconst LicenseDescription = ({ children, isOpen, setIsOpen }: LicenseDescriptionProps) => {\n const open = isOpen ? { \"data-open\": \"\" } : {};\n const { t } = useTranslation();\n\n const handleToggle = () => {\n setIsOpen(!isOpen);\n };\n\n return (\n <ContentWrapper>\n <StyledDescription {...open}>\n <TextContent {...open}>{children}</TextContent>\n <StyledButton variant=\"link\" size=\"small\" onClick={handleToggle}>\n {isOpen ? `${t(\"audio.readLessDescriptionLabel\")}` : `${t(\"audio.readMoreDescriptionLabel\")}`}\n </StyledButton>\n </StyledDescription>\n </ContentWrapper>\n );\n};\n\nexport const LicenseContainerContent = ({ children, copyright, type }: LicenseContainerProps) => {\n const { t, i18n } = useTranslation();\n const license = copyright ? getLicenseByAbbreviation(copyright.license?.license ?? \"\", i18n.language) : undefined;\n const captionAuthors =\n [copyright?.creators, copyright?.rightsholders, copyright?.processors].find((authors) => authors?.length) ?? [];\n const [isOpen, setIsOpen] = useState<boolean>(false);\n\n const content = (\n <>\n {children}\n {` ${t(`embed.type.${type}`)}${captionAuthors.length ? \": \" : \"\"}`}\n <span>{captionAuthors.map((author) => author.name).join(\", \")}</span>\n {license ? (\n <>\n {\" / \"}\n {<LicenseLink license={license} hideLink={!isOpen && !!children} />}\n </>\n ) : null}\n </>\n );\n\n if (children) {\n return (\n <LicenseDescription isOpen={isOpen} setIsOpen={setIsOpen}>\n {content}\n </LicenseDescription>\n );\n }\n\n return (\n <Text textStyle=\"label.medium\" asChild consumeCss>\n <span>{content}</span>\n </Text>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EmbedByline.js","names":["Text","AlertLine","Button","rights","LicenseLink"],"sources":["../../src/LicenseByline/EmbedByline.tsx"],"sourcesContent":["/**\n * Copyright (c) 2023-present, NDLA.\n *\n * This source code is licensed under the GPLv3 license found in the\n * LICENSE file in the root directory of this source tree.\n *\n */\n\nimport { AlertLine } from \"@ndla/icons\";\nimport { getLicenseByAbbreviation, rights } from \"@ndla/licenses\";\nimport { Button, Text } from \"@ndla/primitives\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport type { CopyrightDTO as ArticleCopyright } from \"@ndla/types-backend/article-api\";\nimport type { CopyrightDTO as AudioCopyright } from \"@ndla/types-backend/audio-api\";\nimport type { DraftCopyrightDTO as ConceptCopyright } from \"@ndla/types-backend/concept-api\";\nimport type { CopyrightDTO as ImageCopyright } from \"@ndla/types-backend/image-api\";\nimport type { BrightcoveCopyright } from \"@ndla/types-embed\";\nimport { type Dispatch, type ReactNode, type SetStateAction, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { LicenseLink } from \"./LicenseLink\";\n\ninterface BaseProps {\n description?: ReactNode;\n children?: ReactNode;\n visibleAlt?: string;\n error?: true | false;\n hideDescription?: boolean;\n hideCopyright?: boolean;\n}\n\nexport interface EmbedBylineErrorProps extends BaseProps {\n type: EmbedBylineTypeProps[\"type\"] | \"h5p\" | \"external\" | \"code\";\n error: true;\n}\n\ninterface ImageProps extends BaseProps {\n type: \"image\";\n copyright: ImageCopyright | undefined;\n}\n\ninterface BrightcoveProps extends BaseProps {\n type: \"video\";\n copyright: BrightcoveCopyright | undefined;\n}\n\ninterface AudioProps extends BaseProps {\n type: \"audio\";\n copyright: AudioCopyright | undefined;\n}\n\ninterface PodcastProps extends BaseProps {\n type: \"podcast\";\n copyright: AudioCopyright | undefined;\n}\n\ninterface ConceptProps extends BaseProps {\n type: \"concept\" | \"gloss\";\n copyright: ConceptCopyright | undefined;\n}\n\ninterface CopyrightProps extends BaseProps {\n type: \"copyright\";\n copyright: ArticleCopyright | undefined;\n}\n\nexport type EmbedBylineTypeProps =\n | ImageProps\n | BrightcoveProps\n | AudioProps\n | PodcastProps\n | ConceptProps\n | CopyrightProps;\n\ntype Props = EmbedBylineTypeProps | EmbedBylineErrorProps;\n\nconst BylineWrapper = styled(\"figcaption\", {\n base: {\n display: \"flex\",\n flexDirection: \"column\",\n paddingBlock: \"xsmall\",\n textStyle: \"label.medium\",\n color: \"text.subtle\",\n },\n});\n\nconst ErrorBylineWrapper = styled(BylineWrapper, {\n base: {\n border: \"1px solid\",\n borderColor: \"stroke.error\",\n borderRadius: \"xsmall\",\n background: \"surface.dangerSubtle\",\n paddingInline: \"medium\",\n paddingBlock: \"medium\",\n },\n});\n\nconst StyledText = styled(Text, {\n base: {\n fontStyle: \"italic\",\n },\n});\n\nconst ContentWrapper = styled(\"div\", {\n base: {\n display: \"flex\",\n gap: \"xsmall\",\n alignItems: \"center\",\n textStyle: \"label.medium\",\n },\n});\n\nconst BaseDescription = styled(\"div\", {\n base: {\n display: \"inline-flex\",\n whiteSpace: \"pre-wrap\",\n },\n});\n\nexport const EmbedByline = ({ type, description, children, visibleAlt, hideCopyright, ...props }: Props) => {\n const { t } = useTranslation();\n\n if (props.error) {\n const typeString = type === \"h5p\" ? \"H5P\" : t(`embed.type.${type}`).toLowerCase();\n return (\n <ErrorBylineWrapper>\n <ContentWrapper>\n <AlertLine />\n <BaseDescription>\n <span>{t(\"embed.embedError\", { type: typeString })}</span>\n </BaseDescription>\n </ContentWrapper>\n </ErrorBylineWrapper>\n );\n }\n\n const { copyright } = props;\n const hideByline = hideCopyright && !description;\n\n return (\n <>\n {!hideByline && (\n <BylineWrapper>\n <div>\n {!!hideCopyright && description}\n {!hideCopyright && (\n <LicenseContainerContent type={type} copyright={copyright}>\n {description}\n </LicenseContainerContent>\n )}\n {children}\n </div>\n </BylineWrapper>\n )}\n {visibleAlt ? (\n <StyledText color=\"text.subtle\" textStyle=\"label.medium\" asChild consumeCss>\n <span>{`Alt: ${visibleAlt}`}</span>\n </StyledText>\n ) : null}\n </>\n );\n};\n\ninterface LicenseContainerProps {\n children?: ReactNode;\n copyright: EmbedBylineTypeProps[\"copyright\"];\n type: Props[\"type\"];\n}\n\nconst StyledDescription = styled(BaseDescription, {\n base: {\n mobileWideDown: {\n display: \"grid\",\n gridTemplateColumns: \"1fr auto\",\n alignItems: \"center\",\n overflow: \"hidden\",\n _open: {\n display: \"inline\",\n },\n },\n },\n});\n\nconst TextContent = styled(\"span\", {\n base: {\n mobileWideDown: {\n whiteSpace: \"nowrap\",\n maxHeight: \"large\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n transitionProperty: \"max-height\",\n transitionDuration: \"slow\",\n transitionTimingFunction: \"ease-in\",\n marginInlineEnd: \"4xsmall\",\n _open: {\n whiteSpace: \"pre-wrap\",\n maxHeight: \"none\",\n },\n },\n },\n});\n\nconst StyledButton = styled(Button, {\n base: {\n mobileWide: {\n display: \"none\",\n },\n _print: {\n display: \"none\",\n },\n },\n});\n\ninterface LicenseDescriptionProps {\n children?: ReactNode;\n isOpen: boolean;\n setIsOpen: Dispatch<SetStateAction<boolean>>;\n}\n\nconst LicenseDescription = ({ children, isOpen, setIsOpen }: LicenseDescriptionProps) => {\n const open = isOpen ? { \"data-open\": \"\" } : {};\n const { t } = useTranslation();\n\n const handleToggle = () => {\n setIsOpen(!isOpen);\n };\n\n return (\n <ContentWrapper>\n <StyledDescription {...open}>\n <TextContent {...open}>{children}</TextContent>\n <StyledButton variant=\"link\" size=\"small\" onClick={handleToggle}>\n {isOpen ? `${t(\"audio.readLessDescriptionLabel\")}` : `${t(\"audio.readMoreDescriptionLabel\")}`}\n </StyledButton>\n </StyledDescription>\n </ContentWrapper>\n );\n};\n\nexport const LicenseContainerContent = ({ children, copyright, type }: LicenseContainerProps) => {\n const { t, i18n } = useTranslation();\n const license = copyright ? getLicenseByAbbreviation(copyright.license?.license ?? \"\", i18n.language) : undefined;\n const shouldShowLicense = !!license && !license.rights.includes(rights.NA);\n const captionAuthors =\n [copyright?.creators, copyright?.rightsholders, copyright?.processors].find((authors) => authors?.length) ?? [];\n const [isOpen, setIsOpen] = useState<boolean>(false);\n\n const content = (\n <>\n {children}\n {` ${t(`embed.type.${type}`)}${captionAuthors.length ? \": \" : \"\"}`}\n <span>{captionAuthors.map((author) => author.name).join(\", \")}</span>\n {shouldShowLicense ? (\n <>\n {\" / \"}\n {<LicenseLink license={license} hideLink={!isOpen && !!children} />}\n </>\n ) : null}\n </>\n );\n\n if (children) {\n return (\n <LicenseDescription isOpen={isOpen} setIsOpen={setIsOpen}>\n {content}\n </LicenseDescription>\n );\n }\n\n return (\n <Text textStyle=\"label.medium\" asChild consumeCss>\n <span>{content}</span>\n </Text>\n );\n};\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ndla/ui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "56.0.
|
|
4
|
+
"version": "56.0.190-alpha.0",
|
|
5
5
|
"description": "UI component library for NDLA",
|
|
6
6
|
"license": "GPL-3.0",
|
|
7
7
|
"exports": {
|
|
@@ -56,12 +56,12 @@
|
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@ndla/preset-panda": "^0.0.76",
|
|
59
|
-
"@ndla/types-backend": "^1.0.
|
|
60
|
-
"@ndla/types-embed": "^5.0.
|
|
59
|
+
"@ndla/types-backend": "^1.0.125",
|
|
60
|
+
"@ndla/types-embed": "^5.0.22-alpha.0",
|
|
61
61
|
"@pandacss/dev": "^1.10.0"
|
|
62
62
|
},
|
|
63
63
|
"publishConfig": {
|
|
64
64
|
"access": "public"
|
|
65
65
|
},
|
|
66
|
-
"gitHead": "
|
|
66
|
+
"gitHead": "e1b75ddb9016e416795b8264b6c53735ab18ec46"
|
|
67
67
|
}
|
|
@@ -32,9 +32,8 @@ const StyledSliderThumb = styled(SliderThumb, {
|
|
|
32
32
|
variant: {
|
|
33
33
|
standard: {},
|
|
34
34
|
simple: {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
height: "4xsmall",
|
|
35
|
+
marginBlockStart: "-4xsmall",
|
|
36
|
+
transitionProperty: "background, border-radius, width, height",
|
|
38
37
|
},
|
|
39
38
|
},
|
|
40
39
|
},
|
|
@@ -45,18 +44,20 @@ const StyledSliderTrack = styled(SliderTrack, {
|
|
|
45
44
|
variant: {
|
|
46
45
|
standard: {},
|
|
47
46
|
simple: {
|
|
47
|
+
marginBlockStart: "-4xsmall",
|
|
48
48
|
background: "unset",
|
|
49
49
|
},
|
|
50
50
|
},
|
|
51
51
|
},
|
|
52
52
|
});
|
|
53
53
|
|
|
54
|
-
const
|
|
54
|
+
const StyledSliderRoot = styled(SliderRoot, {
|
|
55
55
|
variants: {
|
|
56
56
|
variant: {
|
|
57
57
|
standard: {},
|
|
58
58
|
simple: {
|
|
59
|
-
|
|
59
|
+
position: "relative",
|
|
60
|
+
marginBlockEnd: "-xsmall",
|
|
60
61
|
},
|
|
61
62
|
},
|
|
62
63
|
},
|
|
@@ -65,12 +66,13 @@ const StyledSliderControl = styled(SliderControl, {
|
|
|
65
66
|
export const AudioProgress = ({ currentTime, duration, onValueChange, variant }: Props) => {
|
|
66
67
|
const { t } = useTranslation();
|
|
67
68
|
return (
|
|
68
|
-
<
|
|
69
|
+
<StyledSliderRoot
|
|
69
70
|
value={[currentTime]}
|
|
70
71
|
defaultValue={[0]}
|
|
71
72
|
step={1}
|
|
72
73
|
max={duration}
|
|
73
74
|
onValueChange={onValueChange}
|
|
75
|
+
variant={variant}
|
|
74
76
|
getAriaValueText={(value) =>
|
|
75
77
|
t("audio.valueText", {
|
|
76
78
|
start: formatTime(Math.round(value.value)),
|
|
@@ -79,14 +81,14 @@ export const AudioProgress = ({ currentTime, duration, onValueChange, variant }:
|
|
|
79
81
|
}
|
|
80
82
|
>
|
|
81
83
|
<SliderLabel srOnly>{t("audio.progressBar")}</SliderLabel>
|
|
82
|
-
<
|
|
84
|
+
<SliderControl>
|
|
83
85
|
<StyledSliderTrack variant={variant}>
|
|
84
86
|
<SliderRange />
|
|
85
87
|
</StyledSliderTrack>
|
|
86
88
|
<StyledSliderThumb index={0} variant={variant}>
|
|
87
89
|
<SliderHiddenInput />
|
|
88
90
|
</StyledSliderThumb>
|
|
89
|
-
</
|
|
90
|
-
</
|
|
91
|
+
</SliderControl>
|
|
92
|
+
</StyledSliderRoot>
|
|
91
93
|
);
|
|
92
94
|
};
|
|
@@ -50,6 +50,7 @@ const successData: AudioMeta = {
|
|
|
50
50
|
manuscript: { manuscript: "", language: "nb" },
|
|
51
51
|
created: "2022-02-28T17:09:28Z",
|
|
52
52
|
updated: "2022-02-28T17:09:28Z",
|
|
53
|
+
released: "2022-02-28T17:09:28Z",
|
|
53
54
|
};
|
|
54
55
|
|
|
55
56
|
const podcastEmbedData: AudioEmbedData = {
|
|
@@ -122,6 +123,7 @@ const podcastSuccessData: AudioMeta = {
|
|
|
122
123
|
},
|
|
123
124
|
created: "2021-06-25T08:55:31Z",
|
|
124
125
|
updated: "2022-03-26T07:49:09Z",
|
|
126
|
+
released: "2022-02-28T17:09:28Z",
|
|
125
127
|
imageMeta: {
|
|
126
128
|
id: "60913",
|
|
127
129
|
inactive: false,
|
|
@@ -47,6 +47,7 @@ export const makeIframeString = (url: string, width: string | number, height: st
|
|
|
47
47
|
export const isNumeric = (value: any) => !Number.isNaN(value - Number.parseFloat(value));
|
|
48
48
|
|
|
49
49
|
const getIframeProps = (data: BrightcoveEmbedData, sources: BrightcoveVideoSource[]) => {
|
|
50
|
+
// oxlint-disable-next-line typescript/no-useless-default-assignment
|
|
50
51
|
const { account, videoid, player = "default" } = data;
|
|
51
52
|
|
|
52
53
|
const source = sources.filter((s) => s.width && s.height).toSorted((a, b) => a!.height! - b.height!)[0];
|
|
@@ -201,3 +201,35 @@ export const InlineFailed: StoryObj<typeof ConceptEmbed> = {
|
|
|
201
201
|
children: "forklaring",
|
|
202
202
|
},
|
|
203
203
|
};
|
|
204
|
+
|
|
205
|
+
export const InlineNALicence: StoryObj<typeof ConceptEmbed> = {
|
|
206
|
+
args: {
|
|
207
|
+
embed: {
|
|
208
|
+
resource: "concept",
|
|
209
|
+
status: "success",
|
|
210
|
+
embedData: inlineEmbedData,
|
|
211
|
+
data: {
|
|
212
|
+
...blockMetaData,
|
|
213
|
+
concept: {
|
|
214
|
+
...blockMetaData.concept,
|
|
215
|
+
copyright: {
|
|
216
|
+
...blockMetaData.concept.copyright,
|
|
217
|
+
license: {
|
|
218
|
+
license: "N/A",
|
|
219
|
+
description: "N/A - ikke relevant",
|
|
220
|
+
url: "",
|
|
221
|
+
},
|
|
222
|
+
creators: [{ type: "writer", name: "Sissel Paaske" }],
|
|
223
|
+
processors: [
|
|
224
|
+
{ type: "processor", name: "Totaltekst" },
|
|
225
|
+
{ type: "correction", name: "Arbeidets art" },
|
|
226
|
+
],
|
|
227
|
+
rightsholders: [],
|
|
228
|
+
processed: false,
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
children: "forklaring",
|
|
234
|
+
},
|
|
235
|
+
};
|
|
@@ -360,7 +360,7 @@ export const FloatLeftExtraSmall: StoryObj<typeof ImageEmbed> = {
|
|
|
360
360
|
};
|
|
361
361
|
|
|
362
362
|
export const In2x2Grid: StoryFn<typeof ImageEmbed> = (args) => {
|
|
363
|
-
const items =
|
|
363
|
+
const items = Array.from<ReactNode>({ length: 4 }).fill(
|
|
364
364
|
<ImageEmbed
|
|
365
365
|
{...args}
|
|
366
366
|
embed={{
|
|
@@ -379,7 +379,7 @@ export const In2x2Grid: StoryFn<typeof ImageEmbed> = (args) => {
|
|
|
379
379
|
};
|
|
380
380
|
|
|
381
381
|
export const In4Grid: StoryFn<typeof ImageEmbed> = (args) => {
|
|
382
|
-
const items =
|
|
382
|
+
const items = Array.from<ReactNode>({ length: 4 }).fill(
|
|
383
383
|
<ImageEmbed
|
|
384
384
|
{...args}
|
|
385
385
|
embed={{
|
|
@@ -96,6 +96,7 @@ const filmResourceMeta: RelatedContentMetaData = {
|
|
|
96
96
|
updated: "2023-03-20T14:12:41.000Z",
|
|
97
97
|
updatedBy: "hd5ZL5Lm4kKkumWgN2gjy9wx",
|
|
98
98
|
published: "2021-01-12T13:33:00.000Z",
|
|
99
|
+
revised: "2021-01-12T13:33:00.000Z",
|
|
99
100
|
articleType: "standard",
|
|
100
101
|
supportedLanguages: ["nb", "nn"],
|
|
101
102
|
grepCodes: ["KE183", "KM2055", "KM3961", "KM6170", "TT1"],
|
|
@@ -266,6 +267,7 @@ const learningResourceMeta: RelatedContentMetaData = {
|
|
|
266
267
|
updated: "2023-02-13T12:57:57.000Z",
|
|
267
268
|
updatedBy: "LGsHWSsXKh520VNcOueMraSB",
|
|
268
269
|
published: "2020-03-25T10:21:31.000Z",
|
|
270
|
+
revised: "2021-01-12T13:33:00.000Z",
|
|
269
271
|
articleType: "standard",
|
|
270
272
|
supportedLanguages: ["nb", "nn"],
|
|
271
273
|
grepCodes: ["KM6126"],
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { PageContent } from "@ndla/primitives";
|
|
10
10
|
import { ArticleContent, ArticleWrapper } from "@ndla/ui";
|
|
11
11
|
import type { Meta, StoryFn } from "@storybook/react";
|
|
12
|
+
import type { ReactNode } from "react";
|
|
12
13
|
import { Plain } from "../KeyFigure/KeyFigure.stories";
|
|
13
14
|
import { Default as PitchStory } from "../Pitch/Pitch.stories";
|
|
14
15
|
import { Grid, GridItem } from "./Grid";
|
|
@@ -46,20 +47,22 @@ const keyFigureArgs = [
|
|
|
46
47
|
|
|
47
48
|
export const GridKeyPerformanceStory: StoryFn<typeof Grid> = ({ ...args }) => {
|
|
48
49
|
const columns = args.columns === "2x2" ? 4 : parseInt(args.columns);
|
|
49
|
-
const items =
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
50
|
+
const items = Array.from<ReactNode>({ length: columns })
|
|
51
|
+
.fill(0)
|
|
52
|
+
.map((_, idx) => {
|
|
53
|
+
const args = keyFigureArgs[idx % keyFigureArgs.length];
|
|
54
|
+
return (
|
|
55
|
+
<GridItem key={idx} data-type="grid-cell">
|
|
56
|
+
<Plain key={idx} {...args} />
|
|
57
|
+
</GridItem>
|
|
58
|
+
);
|
|
59
|
+
});
|
|
57
60
|
return <Grid {...args}>{items}</Grid>;
|
|
58
61
|
};
|
|
59
62
|
|
|
60
63
|
export const GridPitchStory: StoryFn<typeof Grid> = ({ ...args }) => {
|
|
61
64
|
const columns = args.columns === "2x2" ? 4 : parseInt(args.columns);
|
|
62
|
-
const items =
|
|
65
|
+
const items = Array.from<ReactNode>({ length: columns }).fill(
|
|
63
66
|
<GridItem data-type="grid-cell">
|
|
64
67
|
<PitchStory
|
|
65
68
|
// oxlint-disable-next-line typescript/no-non-null-asserted-optional-chain
|
|
@@ -77,27 +80,31 @@ export const GridPitchStory: StoryFn<typeof Grid> = ({ ...args }) => {
|
|
|
77
80
|
|
|
78
81
|
export const GridItemsWithBorders: StoryFn<typeof Grid> = ({ ...args }) => {
|
|
79
82
|
const columns = args.columns === "2x2" ? 4 : parseInt(args.columns);
|
|
80
|
-
const items =
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
83
|
+
const items = Array.from<ReactNode>({ length: columns })
|
|
84
|
+
.fill(0)
|
|
85
|
+
.map((_, idx) => {
|
|
86
|
+
const args = keyFigureArgs[idx % keyFigureArgs.length];
|
|
87
|
+
return (
|
|
88
|
+
<GridItem key={idx} data-type="grid-cell" border={idx % 2 === 0}>
|
|
89
|
+
<Plain key={idx} {...args} />
|
|
90
|
+
</GridItem>
|
|
91
|
+
);
|
|
92
|
+
});
|
|
88
93
|
return <Grid {...args}>{items}</Grid>;
|
|
89
94
|
};
|
|
90
95
|
|
|
91
96
|
export const GridItemsWithBordersInsideGridWithBorder: StoryFn<typeof Grid> = ({ ...args }) => {
|
|
92
97
|
const columns = args.columns === "2x2" ? 4 : parseInt(args.columns);
|
|
93
|
-
const items =
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
const items = Array.from<ReactNode>({ length: columns })
|
|
99
|
+
.fill(0)
|
|
100
|
+
.map((_, idx) => {
|
|
101
|
+
const args = keyFigureArgs[idx % keyFigureArgs.length];
|
|
102
|
+
return (
|
|
103
|
+
<GridItem key={idx} data-type="grid-cell" border={true}>
|
|
104
|
+
<Plain key={idx} {...args} />
|
|
105
|
+
</GridItem>
|
|
106
|
+
);
|
|
107
|
+
});
|
|
101
108
|
return (
|
|
102
109
|
<Grid {...args} border="lightBlue">
|
|
103
110
|
{items}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { AlertLine } from "@ndla/icons";
|
|
10
|
-
import { getLicenseByAbbreviation } from "@ndla/licenses";
|
|
10
|
+
import { getLicenseByAbbreviation, rights } from "@ndla/licenses";
|
|
11
11
|
import { Button, Text } from "@ndla/primitives";
|
|
12
12
|
import { styled } from "@ndla/styled-system/jsx";
|
|
13
13
|
import type { CopyrightDTO as ArticleCopyright } from "@ndla/types-backend/article-api";
|
|
@@ -239,6 +239,7 @@ const LicenseDescription = ({ children, isOpen, setIsOpen }: LicenseDescriptionP
|
|
|
239
239
|
export const LicenseContainerContent = ({ children, copyright, type }: LicenseContainerProps) => {
|
|
240
240
|
const { t, i18n } = useTranslation();
|
|
241
241
|
const license = copyright ? getLicenseByAbbreviation(copyright.license?.license ?? "", i18n.language) : undefined;
|
|
242
|
+
const shouldShowLicense = !!license && !license.rights.includes(rights.NA);
|
|
242
243
|
const captionAuthors =
|
|
243
244
|
[copyright?.creators, copyright?.rightsholders, copyright?.processors].find((authors) => authors?.length) ?? [];
|
|
244
245
|
const [isOpen, setIsOpen] = useState<boolean>(false);
|
|
@@ -248,7 +249,7 @@ export const LicenseContainerContent = ({ children, copyright, type }: LicenseCo
|
|
|
248
249
|
{children}
|
|
249
250
|
{` ${t(`embed.type.${type}`)}${captionAuthors.length ? ": " : ""}`}
|
|
250
251
|
<span>{captionAuthors.map((author) => author.name).join(", ")}</span>
|
|
251
|
-
{
|
|
252
|
+
{shouldShowLicense ? (
|
|
252
253
|
<>
|
|
253
254
|
{" / "}
|
|
254
255
|
{<LicenseLink license={license} hideLink={!isOpen && !!children} />}
|