@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.
@@ -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?: string;\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"}
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"}
@@ -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: concept.title.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 : concept.title.title,
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={concept.title.title}\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 : concept.title.title}\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;AAEX,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,QAAQ,MAAM;EACrB,QAAQ,QAAQ;YAEf;GACa;AAIpB,wBACE,IAAC;EACa;EACZ,WAAW,QAAQ;EACJ;EACT;EACN,OAAO,kBAAkB,mBAAsB,QAAQ,MAAM;EAC7D,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"}
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: title.title
118
+ children: parsedTitle
117
119
  })
118
120
  }), !!filteredExamples.length && /* @__PURE__ */ jsx(AccordionItemTrigger$1, {
119
121
  asChild: true,
@@ -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: {\n title: string;\n language: string;\n };\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 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}>{title.title}</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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"}
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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"}
@@ -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?: string;
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?: string;\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"}
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: concept.title.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 : concept.title.title,
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={concept.title.title}\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 : concept.title.title}\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;AAEX,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,QAAQ,MAAM;EACrB,QAAQ,QAAQ;YAEf;GACa;AAIpB,wBACE,2BAAC;EACa;EACZ,WAAW,QAAQ;EACJ;EACT;EACN,OAAO,kBAAkB,mBAAsB,QAAQ,MAAM;EAC7D,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"}
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"}
@@ -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;
@@ -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: title.title
119
+ children: parsedTitle
118
120
  })
119
121
  }), !!filteredExamples.length && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__ark_ui_react.AccordionItemTrigger, {
120
122
  asChild: true,
@@ -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: {\n title: string;\n language: string;\n };\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 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}>{title.title}</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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"}
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.124-alpha.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.9.0",
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.2",
40
- "@ndla/primitives": "^1.0.90-alpha.0",
41
- "@ndla/safelink": "^7.0.92-alpha.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.40",
56
- "@ndla/types-embed": "^5.0.14-alpha.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": "70b2b4e3d374545b5245a63ea7644c3b9356c74f"
62
+ "gitHead": "0eac61669fb209d2ad3bf40122bc69d841265183"
63
63
  }
@@ -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?: string;
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={concept.title.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 : concept.title.title}
87
+ title={renderContext === "embed" ? undefined : parsedTitle}
83
88
  source={concept.source}
84
89
  >
85
90
  {parsedContent}
@@ -29,6 +29,7 @@ const glossMetaData: ConceptData["concept"] = {
29
29
  revision: 6,
30
30
  title: {
31
31
  title: "Ma Hong",
32
+ htmlTitle: "Ma Hong",
32
33
  language: "nb",
33
34
  },
34
35
  content: {
@@ -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: {
@@ -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}>{title.title}</span>
167
+ <span lang={title.language}>{parsedTitle}</span>
168
168
  </Text>
169
169
  {!!filteredExamples.length && (
170
170
  <AccordionItemTrigger asChild>