@ndla/ui 56.0.124-alpha.0 → 56.0.126-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/es/Concept/Concept.js.map +1 -1
- package/es/Embed/ConceptEmbed.js +3 -2
- package/es/Embed/ConceptEmbed.js.map +1 -1
- package/es/Gloss/Gloss.js +3 -1
- package/es/Gloss/Gloss.js.map +1 -1
- package/lib/Concept/Concept.d.ts +2 -2
- package/lib/Concept/Concept.js.map +1 -1
- package/lib/Embed/ConceptEmbed.js +3 -2
- package/lib/Embed/ConceptEmbed.js.map +1 -1
- package/lib/Gloss/Gloss.d.ts +2 -5
- package/lib/Gloss/Gloss.js +3 -1
- package/lib/Gloss/Gloss.js.map +1 -1
- package/package.json +8 -8
- package/src/Concept/Concept.tsx +2 -2
- package/src/Embed/ConceptEmbed.stories.tsx +1 -1
- package/src/Embed/ConceptEmbed.tsx +7 -2
- package/src/Embed/GlossEmbed.stories.tsx +1 -0
- package/src/Gloss/Gloss.stories.tsx +4 -0
- package/src/Gloss/Gloss.tsx +6 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Concept.js","names":["ImageEmbed","BrightcoveEmbed","H5pEmbed","IframeEmbed","ExternalEmbed"],"sources":["../../src/Concept/Concept.tsx"],"sourcesContent":["/**\n * Copyright (c) 2024-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 ComponentPropsWithRef, type ReactNode, forwardRef } from \"react\";\nimport { Figure } from \"@ndla/primitives\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport type { IDraftCopyrightDTO as ConceptCopyright } from \"@ndla/types-backend/concept-api\";\nimport type { ConceptVisualElementMeta } from \"@ndla/types-embed\";\nimport { BrightcoveEmbed, ExternalEmbed, H5pEmbed, IframeEmbed, ImageEmbed } from \"../Embed\";\nimport { EmbedByline } from \"../LicenseByline/EmbedByline\";\nimport { licenseAttributes } from \"../utils/licenseAttributes\";\n\nexport interface ConceptProps extends ComponentPropsWithRef<\"figure\"> {\n copyright?: ConceptCopyright;\n visualElement?: ConceptVisualElementMeta;\n lang?: string;\n title?:
|
|
1
|
+
{"version":3,"file":"Concept.js","names":["ImageEmbed","BrightcoveEmbed","H5pEmbed","IframeEmbed","ExternalEmbed"],"sources":["../../src/Concept/Concept.tsx"],"sourcesContent":["/**\n * Copyright (c) 2024-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 ComponentPropsWithRef, type ReactNode, forwardRef } from \"react\";\nimport { Figure } from \"@ndla/primitives\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport type { IDraftCopyrightDTO as ConceptCopyright } from \"@ndla/types-backend/concept-api\";\nimport type { ConceptVisualElementMeta } from \"@ndla/types-embed\";\nimport { BrightcoveEmbed, ExternalEmbed, H5pEmbed, IframeEmbed, ImageEmbed } from \"../Embed\";\nimport { EmbedByline } from \"../LicenseByline/EmbedByline\";\nimport { licenseAttributes } from \"../utils/licenseAttributes\";\n\nexport interface ConceptProps extends Omit<ComponentPropsWithRef<\"figure\">, \"title\"> {\n copyright?: ConceptCopyright;\n visualElement?: ConceptVisualElementMeta;\n lang?: string;\n title?: ReactNode;\n children?: ReactNode;\n source?: string;\n previewAlt?: boolean;\n}\n\nconst StyledFigure = styled(Figure, {\n base: {\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"medium\",\n },\n});\n\nconst ContentWrapper = styled(\"div\", {\n base: {\n textStyle: \"body.large\",\n display: \"inline\",\n \"& p\": {\n display: \"inline\",\n },\n },\n});\n\nexport const Concept = forwardRef<HTMLElement, ConceptProps>(\n ({ copyright, visualElement, lang, children, title, source, previewAlt, ...rest }, ref) => {\n const licenseProps = licenseAttributes(copyright?.license?.license, lang, source);\n\n return (\n <StyledFigure ref={ref} {...rest} {...licenseProps}>\n <ContentWrapper lang={lang}>\n {!!title && (\n <>\n <b>{title}</b>\n {` – `}\n </>\n )}\n {children}\n </ContentWrapper>\n {visualElement?.resource === \"image\" ? (\n <ImageEmbed embed={visualElement} lang={lang} previewAlt={previewAlt} />\n ) : visualElement?.resource === \"brightcove\" ? (\n <BrightcoveEmbed embed={visualElement} />\n ) : visualElement?.resource === \"h5p\" ? (\n <H5pEmbed embed={visualElement} />\n ) : visualElement?.resource === \"iframe\" ? (\n <IframeEmbed embed={visualElement} />\n ) : visualElement?.resource === \"external\" ? (\n <ExternalEmbed embed={visualElement} />\n ) : null}\n {!!copyright && <EmbedByline copyright={copyright} type=\"concept\" />}\n </StyledFigure>\n );\n },\n);\n"],"mappings":";;;;;;;;;;;;;AA2BA,MAAM,eAAe,OAAO,QAAQ,EAClC,MAAM;CACJ,SAAS;CACT,eAAe;CACf,KAAK;AACN,EACF,EAAC;AAEF,MAAM,iBAAiB,OAAO,OAAO,EACnC,MAAM;CACJ,WAAW;CACX,SAAS;CACT,OAAO,EACL,SAAS,SACV;AACF,EACF,EAAC;AAEF,MAAa,UAAU,WACrB,CAAC,EAAE,WAAW,eAAe,MAAM,UAAU,OAAO,QAAQ,WAAY,GAAG,MAAM,EAAE,QAAQ;CACzF,MAAM,eAAe,kBAAkB,WAAW,SAAS,SAAS,MAAM,OAAO;AAEjF,wBACE,KAAC;EAAkB;EAAK,GAAI;EAAM,GAAI;;mBACpC,KAAC;IAAqB;iBACjB,yBACD,8CACE,IAAC,iBAAG,QAAU,GACZ,QACD,EAEJ;KACc;GAChB,eAAe,aAAa,0BAC3B,IAACA;IAAW,OAAO;IAAqB;IAAkB;KAAc,GACtE,eAAe,aAAa,+BAC9B,IAACC,2BAAgB,OAAO,gBAAiB,GACvC,eAAe,aAAa,wBAC9B,IAACC,oBAAS,OAAO,gBAAiB,GAChC,eAAe,aAAa,2BAC9B,IAACC,uBAAY,OAAO,gBAAiB,GACnC,eAAe,aAAa,6BAC9B,IAACC,yBAAc,OAAO,gBAAiB,GACrC;KACD,6BAAa,IAAC;IAAuB;IAAW,MAAK;KAAY;;GACvD;AAElB,EACF"}
|
package/es/Embed/ConceptEmbed.js
CHANGED
|
@@ -20,6 +20,7 @@ const ConceptEmbed = ({ embed, renderContext, lang, previewAlt }) => {
|
|
|
20
20
|
if (embed.status === "error" || !embed.data.concept.content) return void 0;
|
|
21
21
|
return parse(embed.data.concept.content.htmlContent);
|
|
22
22
|
}, [embed]);
|
|
23
|
+
const parsedTitle = useMemo(() => embed.status === "success" ? parse(embed.data.concept.title.htmlTitle) : void 0, [embed]);
|
|
23
24
|
if (embed.status === "error" && embed.embedData.type === "inline") return /* @__PURE__ */ jsx("span", { children: embed.embedData.linkText });
|
|
24
25
|
if (embed.status === "error") return /* @__PURE__ */ jsx(EmbedErrorPlaceholder_default, { type: "gloss" });
|
|
25
26
|
const { concept, visualElement } = embed.data;
|
|
@@ -30,7 +31,7 @@ const ConceptEmbed = ({ embed, renderContext, lang, previewAlt }) => {
|
|
|
30
31
|
copyright: concept.copyright,
|
|
31
32
|
visualElement,
|
|
32
33
|
lang,
|
|
33
|
-
title:
|
|
34
|
+
title: parsedTitle,
|
|
34
35
|
source: concept.source,
|
|
35
36
|
children: parsedContent
|
|
36
37
|
});
|
|
@@ -39,7 +40,7 @@ const ConceptEmbed = ({ embed, renderContext, lang, previewAlt }) => {
|
|
|
39
40
|
copyright: concept.copyright,
|
|
40
41
|
visualElement,
|
|
41
42
|
lang,
|
|
42
|
-
title: renderContext === "embed" ? void 0 :
|
|
43
|
+
title: renderContext === "embed" ? void 0 : parsedTitle,
|
|
43
44
|
source: concept.source,
|
|
44
45
|
children: parsedContent
|
|
45
46
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConceptEmbed.js","names":["EmbedErrorPlaceholder"],"sources":["../../src/Embed/ConceptEmbed.tsx"],"sourcesContent":["/**\n * Copyright (c) 2024-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 parse from \"html-react-parser\";\nimport { forwardRef, useMemo, useRef } from \"react\";\nimport { Portal } from \"@ark-ui/react\";\nimport { PopoverContent, PopoverRoot, PopoverTrigger } from \"@ndla/primitives\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport type { ConceptMetaData } from \"@ndla/types-embed\";\nimport { ConceptInlineTriggerButton } from \"./ConceptInlineTriggerButton\";\nimport EmbedErrorPlaceholder from \"./EmbedErrorPlaceholder\";\nimport { GlossEmbed } from \"./GlossEmbed\";\nimport type { RenderContext } from \"./types\";\nimport { Concept, type ConceptProps } from \"../Concept/Concept\";\n\ninterface BaseProps {\n renderContext?: RenderContext;\n lang?: string;\n previewAlt?: boolean;\n}\n\ninterface Props extends BaseProps {\n embed: ConceptMetaData;\n}\n\nconst StyledPopoverContent = styled(PopoverContent, {\n base: {\n width: \"surface.xlarge\",\n maxHeight: \"50vh\",\n overflowY: \"auto\",\n },\n});\n\nexport const ConceptEmbed = ({ embed, renderContext, lang, previewAlt }: Props) => {\n const parsedContent = useMemo(() => {\n if (embed.status === \"error\" || !embed.data.concept.content) return undefined;\n return parse(embed.data.concept.content.htmlContent);\n }, [embed]);\n\n if (embed.status === \"error\" && embed.embedData.type === \"inline\") {\n return <span>{embed.embedData.linkText}</span>;\n }\n if (embed.status === \"error\") {\n // TODO: This could be either concept or gloss. We don't know if it errors out. :)\n return <EmbedErrorPlaceholder type=\"gloss\" />;\n }\n\n const { concept, visualElement } = embed.data;\n\n // TODO: Consider whether we should do this in article-converter instead.\n if (embed.data.concept.glossData) {\n return <GlossEmbed embed={embed} />;\n }\n\n if (embed.embedData.type === \"inline\") {\n return (\n <InlineConcept\n previewAlt={previewAlt}\n linkText={embed.embedData.linkText}\n copyright={concept.copyright}\n visualElement={visualElement}\n lang={lang}\n title={
|
|
1
|
+
{"version":3,"file":"ConceptEmbed.js","names":["EmbedErrorPlaceholder"],"sources":["../../src/Embed/ConceptEmbed.tsx"],"sourcesContent":["/**\n * Copyright (c) 2024-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 parse from \"html-react-parser\";\nimport { forwardRef, useMemo, useRef } from \"react\";\nimport { Portal } from \"@ark-ui/react\";\nimport { PopoverContent, PopoverRoot, PopoverTrigger } from \"@ndla/primitives\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport type { ConceptMetaData } from \"@ndla/types-embed\";\nimport { ConceptInlineTriggerButton } from \"./ConceptInlineTriggerButton\";\nimport EmbedErrorPlaceholder from \"./EmbedErrorPlaceholder\";\nimport { GlossEmbed } from \"./GlossEmbed\";\nimport type { RenderContext } from \"./types\";\nimport { Concept, type ConceptProps } from \"../Concept/Concept\";\n\ninterface BaseProps {\n renderContext?: RenderContext;\n lang?: string;\n previewAlt?: boolean;\n}\n\ninterface Props extends BaseProps {\n embed: ConceptMetaData;\n}\n\nconst StyledPopoverContent = styled(PopoverContent, {\n base: {\n width: \"surface.xlarge\",\n maxHeight: \"50vh\",\n overflowY: \"auto\",\n },\n});\n\nexport const ConceptEmbed = ({ embed, renderContext, lang, previewAlt }: Props) => {\n const parsedContent = useMemo(() => {\n if (embed.status === \"error\" || !embed.data.concept.content) return undefined;\n return parse(embed.data.concept.content.htmlContent);\n }, [embed]);\n\n const parsedTitle = useMemo(\n () => (embed.status === \"success\" ? parse(embed.data.concept.title.htmlTitle) : undefined),\n [embed],\n );\n\n if (embed.status === \"error\" && embed.embedData.type === \"inline\") {\n return <span>{embed.embedData.linkText}</span>;\n }\n if (embed.status === \"error\") {\n // TODO: This could be either concept or gloss. We don't know if it errors out. :)\n return <EmbedErrorPlaceholder type=\"gloss\" />;\n }\n\n const { concept, visualElement } = embed.data;\n\n // TODO: Consider whether we should do this in article-converter instead.\n if (embed.data.concept.glossData) {\n return <GlossEmbed embed={embed} />;\n }\n\n if (embed.embedData.type === \"inline\") {\n return (\n <InlineConcept\n previewAlt={previewAlt}\n linkText={embed.embedData.linkText}\n copyright={concept.copyright}\n visualElement={visualElement}\n lang={lang}\n title={parsedTitle}\n source={concept.source}\n >\n {parsedContent}\n </InlineConcept>\n );\n }\n\n return (\n <BlockConcept\n previewAlt={previewAlt}\n copyright={concept.copyright}\n visualElement={visualElement}\n lang={lang}\n title={renderContext === \"embed\" ? undefined : parsedTitle}\n source={concept.source}\n >\n {parsedContent}\n </BlockConcept>\n );\n};\n\nexport interface InlineConceptProps extends ConceptProps, BaseProps {\n linkText?: string;\n source?: string;\n}\n\nexport const InlineConcept = forwardRef<HTMLSpanElement, InlineConceptProps>(\n ({ linkText, copyright, visualElement, previewAlt, lang, children, title, source, ...rest }, ref) => {\n const contentRef = useRef<HTMLDivElement>(null);\n return (\n <PopoverRoot initialFocusEl={() => contentRef.current}>\n {/* @ts-expect-error placing ref and rest on popover trigger somehow removes a bug where the popover target becomes a bit bigger */}\n <PopoverTrigger asChild ref={ref} {...rest}>\n <ConceptInlineTriggerButton>{linkText}</ConceptInlineTriggerButton>\n </PopoverTrigger>\n <Portal>\n <StyledPopoverContent ref={contentRef}>\n <Concept\n copyright={copyright}\n visualElement={visualElement}\n title={title}\n lang={lang}\n source={source}\n previewAlt={previewAlt}\n >\n {children}\n </Concept>\n </StyledPopoverContent>\n </Portal>\n </PopoverRoot>\n );\n },\n);\n\nexport interface BlockConceptProps extends ConceptProps {}\n\nexport const BlockConcept = forwardRef<HTMLElement, BlockConceptProps>((props, ref) => (\n <Concept {...props} data-embed-type=\"concept\" ref={ref} />\n));\n"],"mappings":";;;;;;;;;;;;AA8BA,MAAM,uBAAuB,OAAO,gBAAgB,EAClD,MAAM;CACJ,OAAO;CACP,WAAW;CACX,WAAW;AACZ,EACF,EAAC;AAEF,MAAa,eAAe,CAAC,EAAE,OAAO,eAAe,MAAM,YAAmB,KAAK;CACjF,MAAM,gBAAgB,QAAQ,MAAM;AAClC,MAAI,MAAM,WAAW,YAAY,MAAM,KAAK,QAAQ,QAAS;AAC7D,SAAO,MAAM,MAAM,KAAK,QAAQ,QAAQ,YAAY;CACrD,GAAE,CAAC,KAAM,EAAC;CAEX,MAAM,cAAc,QAClB,MAAO,MAAM,WAAW,YAAY,MAAM,MAAM,KAAK,QAAQ,MAAM,UAAU,WAC7E,CAAC,KAAM,EACR;AAED,KAAI,MAAM,WAAW,WAAW,MAAM,UAAU,SAAS,SACvD,wBAAO,IAAC,oBAAM,MAAM,UAAU,WAAgB;AAEhD,KAAI,MAAM,WAAW,QAEnB,wBAAO,IAACA,iCAAsB,MAAK,UAAU;CAG/C,MAAM,EAAE,SAAS,eAAe,GAAG,MAAM;AAGzC,KAAI,MAAM,KAAK,QAAQ,UACrB,wBAAO,IAAC,cAAkB,QAAS;AAGrC,KAAI,MAAM,UAAU,SAAS,SAC3B,wBACE,IAAC;EACa;EACZ,UAAU,MAAM,UAAU;EAC1B,WAAW,QAAQ;EACJ;EACT;EACN,OAAO;EACP,QAAQ,QAAQ;YAEf;GACa;AAIpB,wBACE,IAAC;EACa;EACZ,WAAW,QAAQ;EACJ;EACT;EACN,OAAO,kBAAkB,mBAAsB;EAC/C,QAAQ,QAAQ;YAEf;GACY;AAElB;AAOD,MAAa,gBAAgB,WAC3B,CAAC,EAAE,UAAU,WAAW,eAAe,YAAY,MAAM,UAAU,OAAO,OAAQ,GAAG,MAAM,EAAE,QAAQ;CACnG,MAAM,aAAa,OAAuB,KAAK;AAC/C,wBACE,KAAC;EAAY,gBAAgB,MAAM,WAAW;6BAE5C,IAAC;GAAe;GAAa;GAAK,GAAI;6BACpC,IAAC,wCAA4B,WAAsC;IACpD,kBACjB,IAAC,oCACC,IAAC;GAAqB,KAAK;6BACzB,IAAC;IACY;IACI;IACR;IACD;IACE;IACI;IAEX;KACO;IACW,GAChB;GACG;AAEjB,EACF;AAID,MAAa,eAAe,WAA2C,CAAC,OAAO,wBAC7E,IAAC;CAAQ,GAAI;CAAO,mBAAgB;CAAe;EAAO,CAC1D"}
|
package/es/Gloss/Gloss.js
CHANGED
|
@@ -3,6 +3,7 @@ import { GlossExample_default } from "./GlossExample.js";
|
|
|
3
3
|
import { useMemo } from "react";
|
|
4
4
|
import { AccordionItem, AccordionItemContent, AccordionItemIndicator, AccordionRoot, IconButton, Text } from "@ndla/primitives";
|
|
5
5
|
import { styled } from "@ndla/styled-system/jsx";
|
|
6
|
+
import parse from "html-react-parser";
|
|
6
7
|
import { useTranslation } from "react-i18next";
|
|
7
8
|
import { ArrowDownShortLine } from "@ndla/icons";
|
|
8
9
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -50,6 +51,7 @@ const StyledAccordionItem = styled(AccordionItem, {
|
|
|
50
51
|
});
|
|
51
52
|
const Gloss = ({ title, glossData, audio, exampleIds, exampleLangs, variant }) => {
|
|
52
53
|
const { t } = useTranslation();
|
|
54
|
+
const parsedTitle = useMemo(() => parse(title.htmlTitle), [title.htmlTitle]);
|
|
53
55
|
const filteredExamples = useMemo(() => getFilteredExamples(glossData, exampleIds, exampleLangs), [
|
|
54
56
|
exampleIds,
|
|
55
57
|
exampleLangs,
|
|
@@ -113,7 +115,7 @@ const Gloss = ({ title, glossData, audio, exampleIds, exampleLangs, variant }) =
|
|
|
113
115
|
consumeCss: true,
|
|
114
116
|
children: /* @__PURE__ */ jsx("span", {
|
|
115
117
|
lang: title.language,
|
|
116
|
-
children:
|
|
118
|
+
children: parsedTitle
|
|
117
119
|
})
|
|
118
120
|
}), !!filteredExamples.length && /* @__PURE__ */ jsx(AccordionItemTrigger$1, {
|
|
119
121
|
asChild: true,
|
package/es/Gloss/Gloss.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Gloss.js","names":["glossData: IGlossDataDTO | undefined","exampleIds: string | undefined","exampleLangs: string | undefined","SpeechControl","AccordionItemTrigger","GlossExample"],"sources":["../../src/Gloss/Gloss.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 { useMemo } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { AccordionItemTrigger } from \"@ark-ui/react\";\nimport { ArrowDownShortLine } from \"@ndla/icons\";\nimport {\n AccordionItem,\n AccordionItemContent,\n AccordionItemIndicator,\n AccordionRoot,\n IconButton,\n Text,\n} from \"@ndla/primitives\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport type { StyledVariantProps } from \"@ndla/styled-system/types\";\nimport type { IGlossDataDTO, IGlossExampleDTO } from \"@ndla/types-backend/concept-api\";\nimport GlossExample from \"./GlossExample\";\nimport SpeechControl from \"../AudioPlayer/SpeechControl\";\n\n// TODO: Figure out padding between bordered and simple variant.\n// The design says that the content above the accordion content should have enough padding to align with the accordion content.\n// When a gloss is bordered there's way too much padding.\n\nconst getFilteredExamples = (\n glossData: IGlossDataDTO | undefined,\n exampleIds: string | undefined,\n exampleLangs: string | undefined,\n): IGlossExampleDTO[][] => {\n if (exampleIds !== undefined || exampleLangs !== undefined) {\n const exampleIdsList = exampleIds?.toString()?.split(\",\") ?? [];\n const exampleLangsList = exampleLangs?.split(\",\") ?? [];\n\n const filteredExamples =\n glossData?.examples?.map((examples, i) => {\n if (exampleIdsList.includes(i.toString())) {\n return examples.filter((e) => exampleLangsList.includes(e.language));\n }\n return [];\n }) ?? [];\n const examplesWithoutEmpty = filteredExamples.filter((el) => !!el.length);\n return examplesWithoutEmpty;\n }\n return glossData?.examples ?? [];\n};\n\nconst Container = styled(\"div\", {\n base: {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n },\n});\n\nconst TextWrapper = styled(\"div\", {\n base: {\n display: \"flex\",\n gap: \"small\",\n },\n});\n\nconst StyledAccordionItemContent = styled(AccordionItemContent, {\n base: {\n paddingInline: \"0\",\n },\n});\n\nconst StyledContainer = styled(Container, {\n base: {\n marginBlockStart: \"3xsmall\",\n },\n});\n\nconst StyledAccordionItem = styled(AccordionItem, {\n base: {\n paddingBlock: \"small\",\n paddingInline: \"medium\",\n },\n defaultVariants: {\n variant: \"simple\",\n },\n variants: {\n variant: {\n simple: {},\n bordered: {\n border: \"1px solid\",\n borderColor: \"stroke.subtle\",\n borderRadius: \"xsmall\",\n },\n },\n },\n});\n\ntype GlossVariantProps = StyledVariantProps<typeof StyledAccordionItem>;\n\nexport interface Props {\n title:
|
|
1
|
+
{"version":3,"file":"Gloss.js","names":["glossData: IGlossDataDTO | undefined","exampleIds: string | undefined","exampleLangs: string | undefined","SpeechControl","AccordionItemTrigger","GlossExample"],"sources":["../../src/Gloss/Gloss.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 parse from \"html-react-parser\";\nimport { useMemo } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { AccordionItemTrigger } from \"@ark-ui/react\";\nimport { ArrowDownShortLine } from \"@ndla/icons\";\nimport {\n AccordionItem,\n AccordionItemContent,\n AccordionItemIndicator,\n AccordionRoot,\n IconButton,\n Text,\n} from \"@ndla/primitives\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport type { StyledVariantProps } from \"@ndla/styled-system/types\";\nimport type { ConceptTitleDTO, IGlossDataDTO, IGlossExampleDTO } from \"@ndla/types-backend/concept-api\";\nimport GlossExample from \"./GlossExample\";\nimport SpeechControl from \"../AudioPlayer/SpeechControl\";\n\n// TODO: Figure out padding between bordered and simple variant.\n// The design says that the content above the accordion content should have enough padding to align with the accordion content.\n// When a gloss is bordered there's way too much padding.\n\nconst getFilteredExamples = (\n glossData: IGlossDataDTO | undefined,\n exampleIds: string | undefined,\n exampleLangs: string | undefined,\n): IGlossExampleDTO[][] => {\n if (exampleIds !== undefined || exampleLangs !== undefined) {\n const exampleIdsList = exampleIds?.toString()?.split(\",\") ?? [];\n const exampleLangsList = exampleLangs?.split(\",\") ?? [];\n\n const filteredExamples =\n glossData?.examples?.map((examples, i) => {\n if (exampleIdsList.includes(i.toString())) {\n return examples.filter((e) => exampleLangsList.includes(e.language));\n }\n return [];\n }) ?? [];\n const examplesWithoutEmpty = filteredExamples.filter((el) => !!el.length);\n return examplesWithoutEmpty;\n }\n return glossData?.examples ?? [];\n};\n\nconst Container = styled(\"div\", {\n base: {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n },\n});\n\nconst TextWrapper = styled(\"div\", {\n base: {\n display: \"flex\",\n gap: \"small\",\n },\n});\n\nconst StyledAccordionItemContent = styled(AccordionItemContent, {\n base: {\n paddingInline: \"0\",\n },\n});\n\nconst StyledContainer = styled(Container, {\n base: {\n marginBlockStart: \"3xsmall\",\n },\n});\n\nconst StyledAccordionItem = styled(AccordionItem, {\n base: {\n paddingBlock: \"small\",\n paddingInline: \"medium\",\n },\n defaultVariants: {\n variant: \"simple\",\n },\n variants: {\n variant: {\n simple: {},\n bordered: {\n border: \"1px solid\",\n borderColor: \"stroke.subtle\",\n borderRadius: \"xsmall\",\n },\n },\n },\n});\n\ntype GlossVariantProps = StyledVariantProps<typeof StyledAccordionItem>;\n\nexport interface Props {\n title: ConceptTitleDTO;\n glossData?: IGlossDataDTO;\n audio?: {\n title: string;\n src?: string;\n };\n exampleIds?: string;\n exampleLangs?: string;\n}\n\nconst Gloss = ({ title, glossData, audio, exampleIds, exampleLangs, variant }: Props & GlossVariantProps) => {\n const { t } = useTranslation();\n\n const parsedTitle = useMemo(() => parse(title.htmlTitle), [title.htmlTitle]);\n\n const filteredExamples = useMemo(\n () => getFilteredExamples(glossData, exampleIds, exampleLangs),\n [exampleIds, exampleLangs, glossData],\n );\n\n if (!glossData) return null;\n\n return (\n <AccordionRoot multiple variant=\"clean\">\n <StyledAccordionItem value=\"gloss\" variant={variant}>\n <Container>\n <TextWrapper>\n <Text textStyle=\"label.medium\" fontWeight=\"bold\" asChild consumeCss lang={glossData.originalLanguage}>\n <span>{glossData.gloss}</span>\n </Text>\n {!!glossData.transcriptions.traditional && (\n <Text textStyle=\"label.medium\" asChild consumeCss>\n <span\n key={t(\"gloss.transcriptions.traditional\")}\n aria-label={t(\"gloss.transcriptions.traditional\")}\n lang={glossData.originalLanguage}\n >\n {glossData.transcriptions.traditional}\n </span>\n </Text>\n )}\n {!!glossData.transcriptions.pinyin && (\n <Text textStyle=\"label.medium\" asChild consumeCss>\n <span\n data-pinyin=\"\"\n key={t(\"gloss.transcriptions.pinyin\")}\n aria-label={t(\"gloss.transcriptions.pinyin\")}\n lang={glossData.originalLanguage}\n >\n {glossData.transcriptions.pinyin}\n </span>\n </Text>\n )}\n {!!glossData.wordClass && (\n <Text textStyle=\"label.medium\" asChild consumeCss>\n <span aria-label={t(\"gloss.wordClass\")}>{t(`wordClass.${glossData.wordClass}`).toLowerCase()}</span>\n </Text>\n )}\n </TextWrapper>\n {!!audio?.src && <SpeechControl src={audio.src} title={audio.title} type=\"gloss\" />}\n </Container>\n <StyledContainer>\n <Text textStyle=\"label.medium\" asChild consumeCss>\n <span lang={title.language}>{parsedTitle}</span>\n </Text>\n {!!filteredExamples.length && (\n <AccordionItemTrigger asChild>\n <IconButton variant=\"tertiary\" aria-label={t(\"gloss.showExamples\")} title={t(\"gloss.showExamples\")}>\n <AccordionItemIndicator asChild>\n <ArrowDownShortLine size=\"medium\" />\n </AccordionItemIndicator>\n </IconButton>\n </AccordionItemTrigger>\n )}\n </StyledContainer>\n <StyledAccordionItemContent>\n {filteredExamples.map((examples, index) => (\n <GlossExample\n key={`gloss-example-${index}`}\n examples={examples}\n originalLanguage={glossData.originalLanguage}\n />\n ))}\n </StyledAccordionItemContent>\n </StyledAccordionItem>\n </AccordionRoot>\n );\n};\n\nexport default Gloss;\n"],"mappings":"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"}
|
package/lib/Concept/Concept.d.ts
CHANGED
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
import { type ComponentPropsWithRef, type ReactNode } from "react";
|
|
9
9
|
import type { IDraftCopyrightDTO as ConceptCopyright } from "@ndla/types-backend/concept-api";
|
|
10
10
|
import type { ConceptVisualElementMeta } from "@ndla/types-embed";
|
|
11
|
-
export interface ConceptProps extends ComponentPropsWithRef<"figure"> {
|
|
11
|
+
export interface ConceptProps extends Omit<ComponentPropsWithRef<"figure">, "title"> {
|
|
12
12
|
copyright?: ConceptCopyright;
|
|
13
13
|
visualElement?: ConceptVisualElementMeta;
|
|
14
14
|
lang?: string;
|
|
15
|
-
title?:
|
|
15
|
+
title?: ReactNode;
|
|
16
16
|
children?: ReactNode;
|
|
17
17
|
source?: string;
|
|
18
18
|
previewAlt?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Concept.js","names":["Figure","ImageEmbed","BrightcoveEmbed","H5pEmbed","IframeEmbed","ExternalEmbed","EmbedByline"],"sources":["../../src/Concept/Concept.tsx"],"sourcesContent":["/**\n * Copyright (c) 2024-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 ComponentPropsWithRef, type ReactNode, forwardRef } from \"react\";\nimport { Figure } from \"@ndla/primitives\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport type { IDraftCopyrightDTO as ConceptCopyright } from \"@ndla/types-backend/concept-api\";\nimport type { ConceptVisualElementMeta } from \"@ndla/types-embed\";\nimport { BrightcoveEmbed, ExternalEmbed, H5pEmbed, IframeEmbed, ImageEmbed } from \"../Embed\";\nimport { EmbedByline } from \"../LicenseByline/EmbedByline\";\nimport { licenseAttributes } from \"../utils/licenseAttributes\";\n\nexport interface ConceptProps extends ComponentPropsWithRef<\"figure\"> {\n copyright?: ConceptCopyright;\n visualElement?: ConceptVisualElementMeta;\n lang?: string;\n title?:
|
|
1
|
+
{"version":3,"file":"Concept.js","names":["Figure","ImageEmbed","BrightcoveEmbed","H5pEmbed","IframeEmbed","ExternalEmbed","EmbedByline"],"sources":["../../src/Concept/Concept.tsx"],"sourcesContent":["/**\n * Copyright (c) 2024-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 ComponentPropsWithRef, type ReactNode, forwardRef } from \"react\";\nimport { Figure } from \"@ndla/primitives\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport type { IDraftCopyrightDTO as ConceptCopyright } from \"@ndla/types-backend/concept-api\";\nimport type { ConceptVisualElementMeta } from \"@ndla/types-embed\";\nimport { BrightcoveEmbed, ExternalEmbed, H5pEmbed, IframeEmbed, ImageEmbed } from \"../Embed\";\nimport { EmbedByline } from \"../LicenseByline/EmbedByline\";\nimport { licenseAttributes } from \"../utils/licenseAttributes\";\n\nexport interface ConceptProps extends Omit<ComponentPropsWithRef<\"figure\">, \"title\"> {\n copyright?: ConceptCopyright;\n visualElement?: ConceptVisualElementMeta;\n lang?: string;\n title?: ReactNode;\n children?: ReactNode;\n source?: string;\n previewAlt?: boolean;\n}\n\nconst StyledFigure = styled(Figure, {\n base: {\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"medium\",\n },\n});\n\nconst ContentWrapper = styled(\"div\", {\n base: {\n textStyle: \"body.large\",\n display: \"inline\",\n \"& p\": {\n display: \"inline\",\n },\n },\n});\n\nexport const Concept = forwardRef<HTMLElement, ConceptProps>(\n ({ copyright, visualElement, lang, children, title, source, previewAlt, ...rest }, ref) => {\n const licenseProps = licenseAttributes(copyright?.license?.license, lang, source);\n\n return (\n <StyledFigure ref={ref} {...rest} {...licenseProps}>\n <ContentWrapper lang={lang}>\n {!!title && (\n <>\n <b>{title}</b>\n {` – `}\n </>\n )}\n {children}\n </ContentWrapper>\n {visualElement?.resource === \"image\" ? (\n <ImageEmbed embed={visualElement} lang={lang} previewAlt={previewAlt} />\n ) : visualElement?.resource === \"brightcove\" ? (\n <BrightcoveEmbed embed={visualElement} />\n ) : visualElement?.resource === \"h5p\" ? (\n <H5pEmbed embed={visualElement} />\n ) : visualElement?.resource === \"iframe\" ? (\n <IframeEmbed embed={visualElement} />\n ) : visualElement?.resource === \"external\" ? (\n <ExternalEmbed embed={visualElement} />\n ) : null}\n {!!copyright && <EmbedByline copyright={copyright} type=\"concept\" />}\n </StyledFigure>\n );\n },\n);\n"],"mappings":";;;;;;;;;;;;;;AA2BA,MAAM,eAAe,qCAAOA,0BAAQ,EAClC,MAAM;CACJ,SAAS;CACT,eAAe;CACf,KAAK;AACN,EACF,EAAC;AAEF,MAAM,iBAAiB,qCAAO,OAAO,EACnC,MAAM;CACJ,WAAW;CACX,SAAS;CACT,OAAO,EACL,SAAS,SACV;AACF,EACF,EAAC;AAEF,MAAa,UAAU,sBACrB,CAAC,EAAE,WAAW,eAAe,MAAM,UAAU,OAAO,QAAQ,WAAY,GAAG,MAAM,EAAE,QAAQ;CACzF,MAAM,eAAe,4CAAkB,WAAW,SAAS,SAAS,MAAM,OAAO;AAEjF,wBACE,4BAAC;EAAkB;EAAK,GAAI;EAAM,GAAI;;mBACpC,4BAAC;IAAqB;iBACjB,yBACD,qFACE,2BAAC,iBAAG,QAAU,GACZ,QACD,EAEJ;KACc;GAChB,eAAe,aAAa,0BAC3B,2BAACC;IAAW,OAAO;IAAqB;IAAkB;KAAc,GACtE,eAAe,aAAa,+BAC9B,2BAACC,mDAAgB,OAAO,gBAAiB,GACvC,eAAe,aAAa,wBAC9B,2BAACC,qCAAS,OAAO,gBAAiB,GAChC,eAAe,aAAa,2BAC9B,2BAACC,2CAAY,OAAO,gBAAiB,GACnC,eAAe,aAAa,6BAC9B,2BAACC,+CAAc,OAAO,gBAAiB,GACrC;KACD,6BAAa,2BAACC;IAAuB;IAAW,MAAK;KAAY;;GACvD;AAElB,EACF"}
|
|
@@ -21,6 +21,7 @@ const ConceptEmbed = ({ embed, renderContext, lang, previewAlt }) => {
|
|
|
21
21
|
if (embed.status === "error" || !embed.data.concept.content) return void 0;
|
|
22
22
|
return (0, html_react_parser.default)(embed.data.concept.content.htmlContent);
|
|
23
23
|
}, [embed]);
|
|
24
|
+
const parsedTitle = (0, react.useMemo)(() => embed.status === "success" ? (0, html_react_parser.default)(embed.data.concept.title.htmlTitle) : void 0, [embed]);
|
|
24
25
|
if (embed.status === "error" && embed.embedData.type === "inline") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: embed.embedData.linkText });
|
|
25
26
|
if (embed.status === "error") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_EmbedErrorPlaceholder.EmbedErrorPlaceholder_default, { type: "gloss" });
|
|
26
27
|
const { concept, visualElement } = embed.data;
|
|
@@ -31,7 +32,7 @@ const ConceptEmbed = ({ embed, renderContext, lang, previewAlt }) => {
|
|
|
31
32
|
copyright: concept.copyright,
|
|
32
33
|
visualElement,
|
|
33
34
|
lang,
|
|
34
|
-
title:
|
|
35
|
+
title: parsedTitle,
|
|
35
36
|
source: concept.source,
|
|
36
37
|
children: parsedContent
|
|
37
38
|
});
|
|
@@ -40,7 +41,7 @@ const ConceptEmbed = ({ embed, renderContext, lang, previewAlt }) => {
|
|
|
40
41
|
copyright: concept.copyright,
|
|
41
42
|
visualElement,
|
|
42
43
|
lang,
|
|
43
|
-
title: renderContext === "embed" ? void 0 :
|
|
44
|
+
title: renderContext === "embed" ? void 0 : parsedTitle,
|
|
44
45
|
source: concept.source,
|
|
45
46
|
children: parsedContent
|
|
46
47
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConceptEmbed.js","names":["PopoverContent","EmbedErrorPlaceholder","GlossEmbed","PopoverRoot","PopoverTrigger","ConceptInlineTriggerButton","Portal","Concept"],"sources":["../../src/Embed/ConceptEmbed.tsx"],"sourcesContent":["/**\n * Copyright (c) 2024-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 parse from \"html-react-parser\";\nimport { forwardRef, useMemo, useRef } from \"react\";\nimport { Portal } from \"@ark-ui/react\";\nimport { PopoverContent, PopoverRoot, PopoverTrigger } from \"@ndla/primitives\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport type { ConceptMetaData } from \"@ndla/types-embed\";\nimport { ConceptInlineTriggerButton } from \"./ConceptInlineTriggerButton\";\nimport EmbedErrorPlaceholder from \"./EmbedErrorPlaceholder\";\nimport { GlossEmbed } from \"./GlossEmbed\";\nimport type { RenderContext } from \"./types\";\nimport { Concept, type ConceptProps } from \"../Concept/Concept\";\n\ninterface BaseProps {\n renderContext?: RenderContext;\n lang?: string;\n previewAlt?: boolean;\n}\n\ninterface Props extends BaseProps {\n embed: ConceptMetaData;\n}\n\nconst StyledPopoverContent = styled(PopoverContent, {\n base: {\n width: \"surface.xlarge\",\n maxHeight: \"50vh\",\n overflowY: \"auto\",\n },\n});\n\nexport const ConceptEmbed = ({ embed, renderContext, lang, previewAlt }: Props) => {\n const parsedContent = useMemo(() => {\n if (embed.status === \"error\" || !embed.data.concept.content) return undefined;\n return parse(embed.data.concept.content.htmlContent);\n }, [embed]);\n\n if (embed.status === \"error\" && embed.embedData.type === \"inline\") {\n return <span>{embed.embedData.linkText}</span>;\n }\n if (embed.status === \"error\") {\n // TODO: This could be either concept or gloss. We don't know if it errors out. :)\n return <EmbedErrorPlaceholder type=\"gloss\" />;\n }\n\n const { concept, visualElement } = embed.data;\n\n // TODO: Consider whether we should do this in article-converter instead.\n if (embed.data.concept.glossData) {\n return <GlossEmbed embed={embed} />;\n }\n\n if (embed.embedData.type === \"inline\") {\n return (\n <InlineConcept\n previewAlt={previewAlt}\n linkText={embed.embedData.linkText}\n copyright={concept.copyright}\n visualElement={visualElement}\n lang={lang}\n title={
|
|
1
|
+
{"version":3,"file":"ConceptEmbed.js","names":["PopoverContent","EmbedErrorPlaceholder","GlossEmbed","PopoverRoot","PopoverTrigger","ConceptInlineTriggerButton","Portal","Concept"],"sources":["../../src/Embed/ConceptEmbed.tsx"],"sourcesContent":["/**\n * Copyright (c) 2024-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 parse from \"html-react-parser\";\nimport { forwardRef, useMemo, useRef } from \"react\";\nimport { Portal } from \"@ark-ui/react\";\nimport { PopoverContent, PopoverRoot, PopoverTrigger } from \"@ndla/primitives\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport type { ConceptMetaData } from \"@ndla/types-embed\";\nimport { ConceptInlineTriggerButton } from \"./ConceptInlineTriggerButton\";\nimport EmbedErrorPlaceholder from \"./EmbedErrorPlaceholder\";\nimport { GlossEmbed } from \"./GlossEmbed\";\nimport type { RenderContext } from \"./types\";\nimport { Concept, type ConceptProps } from \"../Concept/Concept\";\n\ninterface BaseProps {\n renderContext?: RenderContext;\n lang?: string;\n previewAlt?: boolean;\n}\n\ninterface Props extends BaseProps {\n embed: ConceptMetaData;\n}\n\nconst StyledPopoverContent = styled(PopoverContent, {\n base: {\n width: \"surface.xlarge\",\n maxHeight: \"50vh\",\n overflowY: \"auto\",\n },\n});\n\nexport const ConceptEmbed = ({ embed, renderContext, lang, previewAlt }: Props) => {\n const parsedContent = useMemo(() => {\n if (embed.status === \"error\" || !embed.data.concept.content) return undefined;\n return parse(embed.data.concept.content.htmlContent);\n }, [embed]);\n\n const parsedTitle = useMemo(\n () => (embed.status === \"success\" ? parse(embed.data.concept.title.htmlTitle) : undefined),\n [embed],\n );\n\n if (embed.status === \"error\" && embed.embedData.type === \"inline\") {\n return <span>{embed.embedData.linkText}</span>;\n }\n if (embed.status === \"error\") {\n // TODO: This could be either concept or gloss. We don't know if it errors out. :)\n return <EmbedErrorPlaceholder type=\"gloss\" />;\n }\n\n const { concept, visualElement } = embed.data;\n\n // TODO: Consider whether we should do this in article-converter instead.\n if (embed.data.concept.glossData) {\n return <GlossEmbed embed={embed} />;\n }\n\n if (embed.embedData.type === \"inline\") {\n return (\n <InlineConcept\n previewAlt={previewAlt}\n linkText={embed.embedData.linkText}\n copyright={concept.copyright}\n visualElement={visualElement}\n lang={lang}\n title={parsedTitle}\n source={concept.source}\n >\n {parsedContent}\n </InlineConcept>\n );\n }\n\n return (\n <BlockConcept\n previewAlt={previewAlt}\n copyright={concept.copyright}\n visualElement={visualElement}\n lang={lang}\n title={renderContext === \"embed\" ? undefined : parsedTitle}\n source={concept.source}\n >\n {parsedContent}\n </BlockConcept>\n );\n};\n\nexport interface InlineConceptProps extends ConceptProps, BaseProps {\n linkText?: string;\n source?: string;\n}\n\nexport const InlineConcept = forwardRef<HTMLSpanElement, InlineConceptProps>(\n ({ linkText, copyright, visualElement, previewAlt, lang, children, title, source, ...rest }, ref) => {\n const contentRef = useRef<HTMLDivElement>(null);\n return (\n <PopoverRoot initialFocusEl={() => contentRef.current}>\n {/* @ts-expect-error placing ref and rest on popover trigger somehow removes a bug where the popover target becomes a bit bigger */}\n <PopoverTrigger asChild ref={ref} {...rest}>\n <ConceptInlineTriggerButton>{linkText}</ConceptInlineTriggerButton>\n </PopoverTrigger>\n <Portal>\n <StyledPopoverContent ref={contentRef}>\n <Concept\n copyright={copyright}\n visualElement={visualElement}\n title={title}\n lang={lang}\n source={source}\n previewAlt={previewAlt}\n >\n {children}\n </Concept>\n </StyledPopoverContent>\n </Portal>\n </PopoverRoot>\n );\n },\n);\n\nexport interface BlockConceptProps extends ConceptProps {}\n\nexport const BlockConcept = forwardRef<HTMLElement, BlockConceptProps>((props, ref) => (\n <Concept {...props} data-embed-type=\"concept\" ref={ref} />\n));\n"],"mappings":";;;;;;;;;;;;;AA8BA,MAAM,uBAAuB,qCAAOA,kCAAgB,EAClD,MAAM;CACJ,OAAO;CACP,WAAW;CACX,WAAW;AACZ,EACF,EAAC;AAEF,MAAa,eAAe,CAAC,EAAE,OAAO,eAAe,MAAM,YAAmB,KAAK;CACjF,MAAM,gBAAgB,mBAAQ,MAAM;AAClC,MAAI,MAAM,WAAW,YAAY,MAAM,KAAK,QAAQ,QAAS;AAC7D,SAAO,+BAAM,MAAM,KAAK,QAAQ,QAAQ,YAAY;CACrD,GAAE,CAAC,KAAM,EAAC;CAEX,MAAM,cAAc,mBAClB,MAAO,MAAM,WAAW,YAAY,+BAAM,MAAM,KAAK,QAAQ,MAAM,UAAU,WAC7E,CAAC,KAAM,EACR;AAED,KAAI,MAAM,WAAW,WAAW,MAAM,UAAU,SAAS,SACvD,wBAAO,2BAAC,oBAAM,MAAM,UAAU,WAAgB;AAEhD,KAAI,MAAM,WAAW,QAEnB,wBAAO,2BAACC,+DAAsB,MAAK,UAAU;CAG/C,MAAM,EAAE,SAAS,eAAe,GAAG,MAAM;AAGzC,KAAI,MAAM,KAAK,QAAQ,UACrB,wBAAO,2BAACC,iCAAkB,QAAS;AAGrC,KAAI,MAAM,UAAU,SAAS,SAC3B,wBACE,2BAAC;EACa;EACZ,UAAU,MAAM,UAAU;EAC1B,WAAW,QAAQ;EACJ;EACT;EACN,OAAO;EACP,QAAQ,QAAQ;YAEf;GACa;AAIpB,wBACE,2BAAC;EACa;EACZ,WAAW,QAAQ;EACJ;EACT;EACN,OAAO,kBAAkB,mBAAsB;EAC/C,QAAQ,QAAQ;YAEf;GACY;AAElB;AAOD,MAAa,gBAAgB,sBAC3B,CAAC,EAAE,UAAU,WAAW,eAAe,YAAY,MAAM,UAAU,OAAO,OAAQ,GAAG,MAAM,EAAE,QAAQ;CACnG,MAAM,aAAa,kBAAuB,KAAK;AAC/C,wBACE,4BAACC;EAAY,gBAAgB,MAAM,WAAW;6BAE5C,2BAACC;GAAe;GAAa;GAAK,GAAI;6BACpC,2BAACC,2EAA4B,WAAsC;IACpD,kBACjB,2BAACC,mDACC,2BAAC;GAAqB,KAAK;6BACzB,2BAACC;IACY;IACI;IACR;IACD;IACE;IACI;IAEX;KACO;IACW,GAChB;GACG;AAEjB,EACF;AAID,MAAa,eAAe,sBAA2C,CAAC,OAAO,wBAC7E,2BAACA;CAAQ,GAAI;CAAO,mBAAgB;CAAe;EAAO,CAC1D"}
|
package/lib/Gloss/Gloss.d.ts
CHANGED
|
@@ -6,16 +6,13 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
import type { StyledVariantProps } from "@ndla/styled-system/types";
|
|
9
|
-
import type { IGlossDataDTO } from "@ndla/types-backend/concept-api";
|
|
9
|
+
import type { ConceptTitleDTO, IGlossDataDTO } from "@ndla/types-backend/concept-api";
|
|
10
10
|
declare const StyledAccordionItem: import("@ndla/styled-system/types").StyledComponent<import("react").ForwardRefExoticComponent<import("@ndla/primitives").AccordionItemProps & import("react").RefAttributes<HTMLDivElement>>, {
|
|
11
11
|
variant?: "simple" | "bordered" | undefined;
|
|
12
12
|
}>;
|
|
13
13
|
type GlossVariantProps = StyledVariantProps<typeof StyledAccordionItem>;
|
|
14
14
|
export interface Props {
|
|
15
|
-
title:
|
|
16
|
-
title: string;
|
|
17
|
-
language: string;
|
|
18
|
-
};
|
|
15
|
+
title: ConceptTitleDTO;
|
|
19
16
|
glossData?: IGlossDataDTO;
|
|
20
17
|
audio?: {
|
|
21
18
|
title: string;
|
package/lib/Gloss/Gloss.js
CHANGED
|
@@ -4,6 +4,7 @@ const require_GlossExample = require('./GlossExample.js');
|
|
|
4
4
|
const react = require_rolldown_runtime.__toESM(require("react"));
|
|
5
5
|
const __ndla_primitives = require_rolldown_runtime.__toESM(require("@ndla/primitives"));
|
|
6
6
|
const __ndla_styled_system_jsx = require_rolldown_runtime.__toESM(require("@ndla/styled-system/jsx"));
|
|
7
|
+
const html_react_parser = require_rolldown_runtime.__toESM(require("html-react-parser"));
|
|
7
8
|
const react_i18next = require_rolldown_runtime.__toESM(require("react-i18next"));
|
|
8
9
|
const __ndla_icons = require_rolldown_runtime.__toESM(require("@ndla/icons"));
|
|
9
10
|
const react_jsx_runtime = require_rolldown_runtime.__toESM(require("react/jsx-runtime"));
|
|
@@ -51,6 +52,7 @@ const StyledAccordionItem = (0, __ndla_styled_system_jsx.styled)(__ndla_primitiv
|
|
|
51
52
|
});
|
|
52
53
|
const Gloss = ({ title, glossData, audio, exampleIds, exampleLangs, variant }) => {
|
|
53
54
|
const { t } = (0, react_i18next.useTranslation)();
|
|
55
|
+
const parsedTitle = (0, react.useMemo)(() => (0, html_react_parser.default)(title.htmlTitle), [title.htmlTitle]);
|
|
54
56
|
const filteredExamples = (0, react.useMemo)(() => getFilteredExamples(glossData, exampleIds, exampleLangs), [
|
|
55
57
|
exampleIds,
|
|
56
58
|
exampleLangs,
|
|
@@ -114,7 +116,7 @@ const Gloss = ({ title, glossData, audio, exampleIds, exampleLangs, variant }) =
|
|
|
114
116
|
consumeCss: true,
|
|
115
117
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
116
118
|
lang: title.language,
|
|
117
|
-
children:
|
|
119
|
+
children: parsedTitle
|
|
118
120
|
})
|
|
119
121
|
}), !!filteredExamples.length && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__ark_ui_react.AccordionItemTrigger, {
|
|
120
122
|
asChild: true,
|
package/lib/Gloss/Gloss.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Gloss.js","names":["glossData: IGlossDataDTO | undefined","exampleIds: string | undefined","exampleLangs: string | undefined","AccordionItemContent","AccordionItem","AccordionRoot","Text","SpeechControl","AccordionItemTrigger","IconButton","AccordionItemIndicator","ArrowDownShortLine","GlossExample"],"sources":["../../src/Gloss/Gloss.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 { useMemo } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { AccordionItemTrigger } from \"@ark-ui/react\";\nimport { ArrowDownShortLine } from \"@ndla/icons\";\nimport {\n AccordionItem,\n AccordionItemContent,\n AccordionItemIndicator,\n AccordionRoot,\n IconButton,\n Text,\n} from \"@ndla/primitives\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport type { StyledVariantProps } from \"@ndla/styled-system/types\";\nimport type { IGlossDataDTO, IGlossExampleDTO } from \"@ndla/types-backend/concept-api\";\nimport GlossExample from \"./GlossExample\";\nimport SpeechControl from \"../AudioPlayer/SpeechControl\";\n\n// TODO: Figure out padding between bordered and simple variant.\n// The design says that the content above the accordion content should have enough padding to align with the accordion content.\n// When a gloss is bordered there's way too much padding.\n\nconst getFilteredExamples = (\n glossData: IGlossDataDTO | undefined,\n exampleIds: string | undefined,\n exampleLangs: string | undefined,\n): IGlossExampleDTO[][] => {\n if (exampleIds !== undefined || exampleLangs !== undefined) {\n const exampleIdsList = exampleIds?.toString()?.split(\",\") ?? [];\n const exampleLangsList = exampleLangs?.split(\",\") ?? [];\n\n const filteredExamples =\n glossData?.examples?.map((examples, i) => {\n if (exampleIdsList.includes(i.toString())) {\n return examples.filter((e) => exampleLangsList.includes(e.language));\n }\n return [];\n }) ?? [];\n const examplesWithoutEmpty = filteredExamples.filter((el) => !!el.length);\n return examplesWithoutEmpty;\n }\n return glossData?.examples ?? [];\n};\n\nconst Container = styled(\"div\", {\n base: {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n },\n});\n\nconst TextWrapper = styled(\"div\", {\n base: {\n display: \"flex\",\n gap: \"small\",\n },\n});\n\nconst StyledAccordionItemContent = styled(AccordionItemContent, {\n base: {\n paddingInline: \"0\",\n },\n});\n\nconst StyledContainer = styled(Container, {\n base: {\n marginBlockStart: \"3xsmall\",\n },\n});\n\nconst StyledAccordionItem = styled(AccordionItem, {\n base: {\n paddingBlock: \"small\",\n paddingInline: \"medium\",\n },\n defaultVariants: {\n variant: \"simple\",\n },\n variants: {\n variant: {\n simple: {},\n bordered: {\n border: \"1px solid\",\n borderColor: \"stroke.subtle\",\n borderRadius: \"xsmall\",\n },\n },\n },\n});\n\ntype GlossVariantProps = StyledVariantProps<typeof StyledAccordionItem>;\n\nexport interface Props {\n title:
|
|
1
|
+
{"version":3,"file":"Gloss.js","names":["glossData: IGlossDataDTO | undefined","exampleIds: string | undefined","exampleLangs: string | undefined","AccordionItemContent","AccordionItem","AccordionRoot","Text","SpeechControl","AccordionItemTrigger","IconButton","AccordionItemIndicator","ArrowDownShortLine","GlossExample"],"sources":["../../src/Gloss/Gloss.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 parse from \"html-react-parser\";\nimport { useMemo } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { AccordionItemTrigger } from \"@ark-ui/react\";\nimport { ArrowDownShortLine } from \"@ndla/icons\";\nimport {\n AccordionItem,\n AccordionItemContent,\n AccordionItemIndicator,\n AccordionRoot,\n IconButton,\n Text,\n} from \"@ndla/primitives\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport type { StyledVariantProps } from \"@ndla/styled-system/types\";\nimport type { ConceptTitleDTO, IGlossDataDTO, IGlossExampleDTO } from \"@ndla/types-backend/concept-api\";\nimport GlossExample from \"./GlossExample\";\nimport SpeechControl from \"../AudioPlayer/SpeechControl\";\n\n// TODO: Figure out padding between bordered and simple variant.\n// The design says that the content above the accordion content should have enough padding to align with the accordion content.\n// When a gloss is bordered there's way too much padding.\n\nconst getFilteredExamples = (\n glossData: IGlossDataDTO | undefined,\n exampleIds: string | undefined,\n exampleLangs: string | undefined,\n): IGlossExampleDTO[][] => {\n if (exampleIds !== undefined || exampleLangs !== undefined) {\n const exampleIdsList = exampleIds?.toString()?.split(\",\") ?? [];\n const exampleLangsList = exampleLangs?.split(\",\") ?? [];\n\n const filteredExamples =\n glossData?.examples?.map((examples, i) => {\n if (exampleIdsList.includes(i.toString())) {\n return examples.filter((e) => exampleLangsList.includes(e.language));\n }\n return [];\n }) ?? [];\n const examplesWithoutEmpty = filteredExamples.filter((el) => !!el.length);\n return examplesWithoutEmpty;\n }\n return glossData?.examples ?? [];\n};\n\nconst Container = styled(\"div\", {\n base: {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n },\n});\n\nconst TextWrapper = styled(\"div\", {\n base: {\n display: \"flex\",\n gap: \"small\",\n },\n});\n\nconst StyledAccordionItemContent = styled(AccordionItemContent, {\n base: {\n paddingInline: \"0\",\n },\n});\n\nconst StyledContainer = styled(Container, {\n base: {\n marginBlockStart: \"3xsmall\",\n },\n});\n\nconst StyledAccordionItem = styled(AccordionItem, {\n base: {\n paddingBlock: \"small\",\n paddingInline: \"medium\",\n },\n defaultVariants: {\n variant: \"simple\",\n },\n variants: {\n variant: {\n simple: {},\n bordered: {\n border: \"1px solid\",\n borderColor: \"stroke.subtle\",\n borderRadius: \"xsmall\",\n },\n },\n },\n});\n\ntype GlossVariantProps = StyledVariantProps<typeof StyledAccordionItem>;\n\nexport interface Props {\n title: ConceptTitleDTO;\n glossData?: IGlossDataDTO;\n audio?: {\n title: string;\n src?: string;\n };\n exampleIds?: string;\n exampleLangs?: string;\n}\n\nconst Gloss = ({ title, glossData, audio, exampleIds, exampleLangs, variant }: Props & GlossVariantProps) => {\n const { t } = useTranslation();\n\n const parsedTitle = useMemo(() => parse(title.htmlTitle), [title.htmlTitle]);\n\n const filteredExamples = useMemo(\n () => getFilteredExamples(glossData, exampleIds, exampleLangs),\n [exampleIds, exampleLangs, glossData],\n );\n\n if (!glossData) return null;\n\n return (\n <AccordionRoot multiple variant=\"clean\">\n <StyledAccordionItem value=\"gloss\" variant={variant}>\n <Container>\n <TextWrapper>\n <Text textStyle=\"label.medium\" fontWeight=\"bold\" asChild consumeCss lang={glossData.originalLanguage}>\n <span>{glossData.gloss}</span>\n </Text>\n {!!glossData.transcriptions.traditional && (\n <Text textStyle=\"label.medium\" asChild consumeCss>\n <span\n key={t(\"gloss.transcriptions.traditional\")}\n aria-label={t(\"gloss.transcriptions.traditional\")}\n lang={glossData.originalLanguage}\n >\n {glossData.transcriptions.traditional}\n </span>\n </Text>\n )}\n {!!glossData.transcriptions.pinyin && (\n <Text textStyle=\"label.medium\" asChild consumeCss>\n <span\n data-pinyin=\"\"\n key={t(\"gloss.transcriptions.pinyin\")}\n aria-label={t(\"gloss.transcriptions.pinyin\")}\n lang={glossData.originalLanguage}\n >\n {glossData.transcriptions.pinyin}\n </span>\n </Text>\n )}\n {!!glossData.wordClass && (\n <Text textStyle=\"label.medium\" asChild consumeCss>\n <span aria-label={t(\"gloss.wordClass\")}>{t(`wordClass.${glossData.wordClass}`).toLowerCase()}</span>\n </Text>\n )}\n </TextWrapper>\n {!!audio?.src && <SpeechControl src={audio.src} title={audio.title} type=\"gloss\" />}\n </Container>\n <StyledContainer>\n <Text textStyle=\"label.medium\" asChild consumeCss>\n <span lang={title.language}>{parsedTitle}</span>\n </Text>\n {!!filteredExamples.length && (\n <AccordionItemTrigger asChild>\n <IconButton variant=\"tertiary\" aria-label={t(\"gloss.showExamples\")} title={t(\"gloss.showExamples\")}>\n <AccordionItemIndicator asChild>\n <ArrowDownShortLine size=\"medium\" />\n </AccordionItemIndicator>\n </IconButton>\n </AccordionItemTrigger>\n )}\n </StyledContainer>\n <StyledAccordionItemContent>\n {filteredExamples.map((examples, index) => (\n <GlossExample\n key={`gloss-example-${index}`}\n examples={examples}\n originalLanguage={glossData.originalLanguage}\n />\n ))}\n </StyledAccordionItemContent>\n </StyledAccordionItem>\n </AccordionRoot>\n );\n};\n\nexport default Gloss;\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ndla/ui",
|
|
3
|
-
"version": "56.0.
|
|
3
|
+
"version": "56.0.126-alpha.0",
|
|
4
4
|
"description": "UI component library for NDLA",
|
|
5
5
|
"license": "GPL-3.0",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -33,12 +33,12 @@
|
|
|
33
33
|
"types"
|
|
34
34
|
],
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@ark-ui/react": "^5.
|
|
36
|
+
"@ark-ui/react": "^5.14.1",
|
|
37
37
|
"@ndla/core": "^6.0.1-alpha.0",
|
|
38
38
|
"@ndla/icons": "^8.0.59-alpha.0",
|
|
39
|
-
"@ndla/licenses": "^9.0.
|
|
40
|
-
"@ndla/primitives": "^1.0.
|
|
41
|
-
"@ndla/safelink": "^7.0.
|
|
39
|
+
"@ndla/licenses": "^9.0.3",
|
|
40
|
+
"@ndla/primitives": "^1.0.91-alpha.0",
|
|
41
|
+
"@ndla/safelink": "^7.0.93-alpha.0",
|
|
42
42
|
"@ndla/styled-system": "^0.0.35",
|
|
43
43
|
"@ndla/util": "^5.0.9-alpha.0",
|
|
44
44
|
"html-react-parser": "^5.1.19"
|
|
@@ -52,12 +52,12 @@
|
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@ndla/preset-panda": "^0.0.55",
|
|
55
|
-
"@ndla/types-backend": "^1.0.
|
|
56
|
-
"@ndla/types-embed": "^5.0.
|
|
55
|
+
"@ndla/types-backend": "^1.0.50",
|
|
56
|
+
"@ndla/types-embed": "^5.0.15-alpha.0",
|
|
57
57
|
"@pandacss/dev": "^0.53.6"
|
|
58
58
|
},
|
|
59
59
|
"publishConfig": {
|
|
60
60
|
"access": "public"
|
|
61
61
|
},
|
|
62
|
-
"gitHead": "
|
|
62
|
+
"gitHead": "0eac61669fb209d2ad3bf40122bc69d841265183"
|
|
63
63
|
}
|
package/src/Concept/Concept.tsx
CHANGED
|
@@ -15,11 +15,11 @@ import { BrightcoveEmbed, ExternalEmbed, H5pEmbed, IframeEmbed, ImageEmbed } fro
|
|
|
15
15
|
import { EmbedByline } from "../LicenseByline/EmbedByline";
|
|
16
16
|
import { licenseAttributes } from "../utils/licenseAttributes";
|
|
17
17
|
|
|
18
|
-
export interface ConceptProps extends ComponentPropsWithRef<"figure"> {
|
|
18
|
+
export interface ConceptProps extends Omit<ComponentPropsWithRef<"figure">, "title"> {
|
|
19
19
|
copyright?: ConceptCopyright;
|
|
20
20
|
visualElement?: ConceptVisualElementMeta;
|
|
21
21
|
lang?: string;
|
|
22
|
-
title?:
|
|
22
|
+
title?: ReactNode;
|
|
23
23
|
children?: ReactNode;
|
|
24
24
|
source?: string;
|
|
25
25
|
previewAlt?: boolean;
|
|
@@ -29,7 +29,7 @@ const inlineEmbedData: ConceptEmbedData = {
|
|
|
29
29
|
const conceptMetaData: ConceptData["concept"] = {
|
|
30
30
|
id: 110,
|
|
31
31
|
revision: 16,
|
|
32
|
-
title: { title: "skin – formasjonsskade", language: "nb" },
|
|
32
|
+
title: { title: "skin – formasjonsskade", htmlTitle: "skin – formasjonsskade", language: "nb" },
|
|
33
33
|
content: {
|
|
34
34
|
content:
|
|
35
35
|
"<p>Ordet «skin» er engelsk og brukes om formasjonsskade som oppstår i boreprosessen i området som grenser inn til brønnen. Skaden er størst i området nærmest hullet, men den kan bre seg utover et stykke fra brønnen. Skin forteller om bergartens permeabilitet i reservoarsonen.</p> <p>Hullveggen skades både av borekronen, små partikler og væsken som brukes i brønnen.</p> <p>Skaden i bergarten gir dårligere forhold for oljen som skal strømme til brønnen. Gangene i bergarten plugges, og det oppstår et trykkfall som reduserer produksjonstrykket i brønnen.</p> <p>Det er viktig å redusere omfanget av skaden ved å velge væsker som passer godt til bergartsegenskapene, og å bore med en borekrone som skader minst mulig.</p> <p>Skader som er dannet av borevæske, kan repareres ved å syrebehandle hullets overflate.</p>",
|
|
@@ -42,6 +42,11 @@ export const ConceptEmbed = ({ embed, renderContext, lang, previewAlt }: Props)
|
|
|
42
42
|
return parse(embed.data.concept.content.htmlContent);
|
|
43
43
|
}, [embed]);
|
|
44
44
|
|
|
45
|
+
const parsedTitle = useMemo(
|
|
46
|
+
() => (embed.status === "success" ? parse(embed.data.concept.title.htmlTitle) : undefined),
|
|
47
|
+
[embed],
|
|
48
|
+
);
|
|
49
|
+
|
|
45
50
|
if (embed.status === "error" && embed.embedData.type === "inline") {
|
|
46
51
|
return <span>{embed.embedData.linkText}</span>;
|
|
47
52
|
}
|
|
@@ -65,7 +70,7 @@ export const ConceptEmbed = ({ embed, renderContext, lang, previewAlt }: Props)
|
|
|
65
70
|
copyright={concept.copyright}
|
|
66
71
|
visualElement={visualElement}
|
|
67
72
|
lang={lang}
|
|
68
|
-
title={
|
|
73
|
+
title={parsedTitle}
|
|
69
74
|
source={concept.source}
|
|
70
75
|
>
|
|
71
76
|
{parsedContent}
|
|
@@ -79,7 +84,7 @@ export const ConceptEmbed = ({ embed, renderContext, lang, previewAlt }: Props)
|
|
|
79
84
|
copyright={concept.copyright}
|
|
80
85
|
visualElement={visualElement}
|
|
81
86
|
lang={lang}
|
|
82
|
-
title={renderContext === "embed" ? undefined :
|
|
87
|
+
title={renderContext === "embed" ? undefined : parsedTitle}
|
|
83
88
|
source={concept.source}
|
|
84
89
|
>
|
|
85
90
|
{parsedContent}
|
|
@@ -18,6 +18,7 @@ const meta: Meta<typeof Gloss> = {
|
|
|
18
18
|
args: {
|
|
19
19
|
title: {
|
|
20
20
|
title: "Å angripe",
|
|
21
|
+
htmlTitle: "Å angripe",
|
|
21
22
|
language: "nb",
|
|
22
23
|
},
|
|
23
24
|
glossData: {
|
|
@@ -75,6 +76,7 @@ export const NoExamples: StoryObj<typeof Gloss> = {
|
|
|
75
76
|
args: {
|
|
76
77
|
title: {
|
|
77
78
|
title: "Å angripe",
|
|
79
|
+
htmlTitle: "Å angripe",
|
|
78
80
|
language: "nb",
|
|
79
81
|
},
|
|
80
82
|
glossData: {
|
|
@@ -95,6 +97,7 @@ export const GlossChineseStory: StoryObj<typeof Gloss> = {
|
|
|
95
97
|
args: {
|
|
96
98
|
title: {
|
|
97
99
|
title: "Ma Hong",
|
|
100
|
+
htmlTitle: "Ma Hong",
|
|
98
101
|
language: "nb",
|
|
99
102
|
},
|
|
100
103
|
glossData: {
|
|
@@ -133,6 +136,7 @@ export const BigExample: StoryObj<typeof Gloss> = {
|
|
|
133
136
|
args: {
|
|
134
137
|
title: {
|
|
135
138
|
title: "(spørsmålspartikkel); hva med…?",
|
|
139
|
+
htmlTitle: "(spørsmålspartikkel); hva med…?",
|
|
136
140
|
language: "nb",
|
|
137
141
|
},
|
|
138
142
|
audio: {
|
package/src/Gloss/Gloss.tsx
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import parse from "html-react-parser";
|
|
9
10
|
import { useMemo } from "react";
|
|
10
11
|
import { useTranslation } from "react-i18next";
|
|
11
12
|
import { AccordionItemTrigger } from "@ark-ui/react";
|
|
@@ -20,7 +21,7 @@ import {
|
|
|
20
21
|
} from "@ndla/primitives";
|
|
21
22
|
import { styled } from "@ndla/styled-system/jsx";
|
|
22
23
|
import type { StyledVariantProps } from "@ndla/styled-system/types";
|
|
23
|
-
import type { IGlossDataDTO, IGlossExampleDTO } from "@ndla/types-backend/concept-api";
|
|
24
|
+
import type { ConceptTitleDTO, IGlossDataDTO, IGlossExampleDTO } from "@ndla/types-backend/concept-api";
|
|
24
25
|
import GlossExample from "./GlossExample";
|
|
25
26
|
import SpeechControl from "../AudioPlayer/SpeechControl";
|
|
26
27
|
|
|
@@ -100,10 +101,7 @@ const StyledAccordionItem = styled(AccordionItem, {
|
|
|
100
101
|
type GlossVariantProps = StyledVariantProps<typeof StyledAccordionItem>;
|
|
101
102
|
|
|
102
103
|
export interface Props {
|
|
103
|
-
title:
|
|
104
|
-
title: string;
|
|
105
|
-
language: string;
|
|
106
|
-
};
|
|
104
|
+
title: ConceptTitleDTO;
|
|
107
105
|
glossData?: IGlossDataDTO;
|
|
108
106
|
audio?: {
|
|
109
107
|
title: string;
|
|
@@ -116,6 +114,8 @@ export interface Props {
|
|
|
116
114
|
const Gloss = ({ title, glossData, audio, exampleIds, exampleLangs, variant }: Props & GlossVariantProps) => {
|
|
117
115
|
const { t } = useTranslation();
|
|
118
116
|
|
|
117
|
+
const parsedTitle = useMemo(() => parse(title.htmlTitle), [title.htmlTitle]);
|
|
118
|
+
|
|
119
119
|
const filteredExamples = useMemo(
|
|
120
120
|
() => getFilteredExamples(glossData, exampleIds, exampleLangs),
|
|
121
121
|
[exampleIds, exampleLangs, glossData],
|
|
@@ -164,7 +164,7 @@ const Gloss = ({ title, glossData, audio, exampleIds, exampleLangs, variant }: P
|
|
|
164
164
|
</Container>
|
|
165
165
|
<StyledContainer>
|
|
166
166
|
<Text textStyle="label.medium" asChild consumeCss>
|
|
167
|
-
<span lang={title.language}>{
|
|
167
|
+
<span lang={title.language}>{parsedTitle}</span>
|
|
168
168
|
</Text>
|
|
169
169
|
{!!filteredExamples.length && (
|
|
170
170
|
<AccordionItemTrigger asChild>
|