@ndla/primitives 1.0.112-alpha.0 → 1.0.115-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,5 +1,5 @@
1
1
  {
2
- "schemaVersion": "1.3.1",
2
+ "schemaVersion": "1.7.0",
3
3
  "styles": {
4
4
  "atomic": [
5
5
  "display]___[value:flex",
package/dist/styles.css CHANGED
@@ -2575,7 +2575,7 @@
2575
2575
  content: counter(level1, upper-alpha) '. ';
2576
2576
  }
2577
2577
 
2578
- .\[\&_li\]\:marker\:c_icon\.strong li:is(::marker, ::-webkit-details-marker) {
2578
+ .\[\&_li\]\:marker\:c_icon\.strong li::marker,.\[\&_li\]\:marker\:c_icon\.strong li::-webkit-details-marker {
2579
2579
  color: var(--colors-icon-strong);
2580
2580
  }
2581
2581
 
package/es/Image.mjs CHANGED
@@ -12,35 +12,59 @@ import { forwardRef } from "react";
12
12
  *
13
13
  */
14
14
  const makeSrcQueryString = ({ width, crop, focalPoint, imageLanguage }) => {
15
- return [
16
- width && `width=${width}`,
17
- crop && `cropStartX=${crop.startX}&cropEndX=${crop.endX}&cropStartY=${crop.startY}&cropEndY=${crop.endY}`,
18
- focalPoint && `focalX=${focalPoint.x}&focalY=${focalPoint.y}`,
19
- imageLanguage && `language=${imageLanguage}`
20
- ].filter((p) => p).join("&");
15
+ const params = [];
16
+ if (width) params.push(`width=${width}`);
17
+ if (crop) params.push(`cropStartX=${crop.startX}&cropEndX=${crop.endX}&cropStartY=${crop.startY}&cropEndY=${crop.endY}`);
18
+ if (focalPoint) params.push(`focalX=${focalPoint.x}&focalY=${focalPoint.y}`);
19
+ if (imageLanguage) params.push(`language=${imageLanguage}`);
20
+ return params.join("&");
21
+ };
22
+ const VAR_WIDTHS = {
23
+ icon: 240,
24
+ xsmall: 480,
25
+ small: 800,
26
+ medium: 1080,
27
+ large: 1440,
28
+ xlarge: 1920,
29
+ xxlarge: 2560
30
+ };
31
+ const IMAGE_WIDTHS = [
32
+ 2720,
33
+ 2080,
34
+ 1760,
35
+ 1440,
36
+ 1120,
37
+ 1e3,
38
+ 960,
39
+ 800,
40
+ 640,
41
+ 480,
42
+ 320,
43
+ 240,
44
+ 180
45
+ ];
46
+ const getVariantSrcSet = (variants) => {
47
+ return variants.map((variant) => {
48
+ return `${variant.variantUrl} ${VAR_WIDTHS[variant.size]}w`;
49
+ }).join(", ");
50
+ };
51
+ const getVariantSizes = (variants) => {
52
+ return variants.map((variant, i) => {
53
+ if (i === variants.length - 1) return `${VAR_WIDTHS[variant.size]}px`;
54
+ return `(max-width: ${VAR_WIDTHS[variant.size]}px) ${VAR_WIDTHS[variant.size]}px`;
55
+ }).join(", ");
21
56
  };
22
57
  const getSrcSet = ({ src, crop, focalPoint, imageLanguage }) => {
23
58
  if (!src) return void 0;
24
- return [
25
- 2720,
26
- 2080,
27
- 1760,
28
- 1440,
29
- 1120,
30
- 1e3,
31
- 960,
32
- 800,
33
- 640,
34
- 480,
35
- 320,
36
- 240,
37
- 180
38
- ].map((width) => `${src}?${makeSrcQueryString({
39
- width,
40
- crop,
41
- focalPoint,
42
- imageLanguage
43
- })} ${width}w`).join(", ");
59
+ return IMAGE_WIDTHS.map((width) => {
60
+ const queryString = makeSrcQueryString({
61
+ width,
62
+ crop,
63
+ focalPoint,
64
+ imageLanguage
65
+ });
66
+ return `${src}${queryString.length ? `?${queryString}` : ""} ${width}w`;
67
+ }).join(", ");
44
68
  };
45
69
  const FALLBACK_WIDTH = 1024;
46
70
  const FALLBACK_SIZES = "(min-width: 1024px) 1024px, 100vw";
@@ -80,19 +104,19 @@ const Img = forwardRef(({ fallbackWidth = FALLBACK_WIDTH, crop, focalPoint, imag
80
104
  focalPoint,
81
105
  imageLanguage
82
106
  });
107
+ const srcWithParms = queryString ? `${src}?${queryString}` : src;
83
108
  return /* @__PURE__ */ jsx(StyledImage, {
84
109
  alt,
85
- src: contentType === "image/gif" ? src : `${src}?${queryString}`,
110
+ src: contentType === "image/gif" ? src : srcWithParms,
86
111
  ...props,
87
112
  ref
88
113
  });
89
114
  });
90
- const Image = forwardRef(({ srcSet: srcSetProp, crop, focalPoint, src, contentType, imageLanguage, fallbackWidth = FALLBACK_WIDTH, sizes = FALLBACK_SIZES, alt, fallbackElement, ...props }, ref) => {
91
- const srcSet = srcSetProp ?? getSrcSet({
92
- src,
93
- crop,
94
- focalPoint,
95
- imageLanguage
115
+ const Image = forwardRef(({ srcSet: srcSetProp, crop, focalPoint, src, contentType, imageLanguage, fallbackWidth = FALLBACK_WIDTH, sizes: sizesProp, alt, fallbackElement, variants, ...props }, ref) => {
116
+ if (!src?.length && !variants?.length && fallbackElement) return /* @__PURE__ */ jsx(StyledFallbackElement, {
117
+ ...props,
118
+ ref,
119
+ children: fallbackElement
96
120
  });
97
121
  const queryString = makeSrcQueryString({
98
122
  width: fallbackWidth,
@@ -100,22 +124,30 @@ const Image = forwardRef(({ srcSet: srcSetProp, crop, focalPoint, src, contentTy
100
124
  focalPoint,
101
125
  imageLanguage
102
126
  });
103
- const fallbackSrc = src ? `${src}?${queryString}` : src;
104
- if ((!src || !src.length) && fallbackElement) return /* @__PURE__ */ jsx(StyledFallbackElement, {
105
- ...props,
106
- ref,
107
- children: fallbackElement
108
- });
109
- return /* @__PURE__ */ jsxs("picture", { children: [contentType !== "image/gif" && /* @__PURE__ */ jsx("source", {
110
- type: contentType,
111
- srcSet,
112
- sizes
113
- }), /* @__PURE__ */ jsx(StyledImage, {
114
- alt,
115
- src: contentType === "image/gif" ? src : fallbackSrc,
116
- ...props,
117
- ref
118
- })] });
127
+ const fallbackSrc = src && queryString ? `${src}?${queryString}` : src;
128
+ return /* @__PURE__ */ jsxs("picture", { children: [
129
+ !!variants?.length && !crop && !focalPoint && contentType !== "image/gif" && /* @__PURE__ */ jsx("source", {
130
+ type: "image/webp",
131
+ srcSet: getVariantSrcSet(variants),
132
+ sizes: sizesProp ?? getVariantSizes(variants)
133
+ }),
134
+ contentType !== "image/gif" && /* @__PURE__ */ jsx("source", {
135
+ type: contentType,
136
+ srcSet: getSrcSet({
137
+ src,
138
+ crop,
139
+ focalPoint,
140
+ imageLanguage
141
+ }),
142
+ sizes: sizesProp ?? FALLBACK_SIZES
143
+ }),
144
+ /* @__PURE__ */ jsx(StyledImage, {
145
+ alt,
146
+ src: contentType === "image/gif" ? src : fallbackSrc,
147
+ ...props,
148
+ ref
149
+ })
150
+ ] });
119
151
  });
120
152
 
121
153
  //#endregion
package/es/Image.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"Image.mjs","names":[],"sources":["../src/Image.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 { ark } from \"@ark-ui/react\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport type { StyledProps, StyledVariantProps } from \"@ndla/styled-system/types\";\n\nexport interface ImageCrop {\n startX: number;\n startY: number;\n endX: number;\n endY: number;\n}\n\nexport interface ImageFocalPoint {\n x: number;\n y: number;\n}\n\ninterface SrcQueryStringOptions {\n width: number | undefined;\n crop?: ImageCrop;\n focalPoint?: ImageFocalPoint;\n imageLanguage?: string;\n}\n\nexport const makeSrcQueryString = ({ width, crop, focalPoint, imageLanguage }: SrcQueryStringOptions) => {\n const widthParams = width && `width=${width}`;\n const cropParams =\n crop && `cropStartX=${crop.startX}&cropEndX=${crop.endX}&cropStartY=${crop.startY}&cropEndY=${crop.endY}`;\n const focalPointParams = focalPoint && `focalX=${focalPoint.x}&focalY=${focalPoint.y}`;\n const imageLanguageParams = imageLanguage && `language=${imageLanguage}`;\n const params = [widthParams, cropParams, focalPointParams, imageLanguageParams].filter((p) => p).join(\"&\");\n\n return params;\n};\n\ninterface SrcSetOptions {\n src?: string;\n crop?: ImageCrop;\n focalPoint?: ImageFocalPoint;\n imageLanguage?: string;\n}\n\nexport const getSrcSet = ({ src, crop, focalPoint, imageLanguage }: SrcSetOptions) => {\n if (!src) return undefined;\n const widths = [2720, 2080, 1760, 1440, 1120, 1000, 960, 800, 640, 480, 320, 240, 180];\n return widths\n .map((width) => `${src}?${makeSrcQueryString({ width, crop, focalPoint, imageLanguage })} ${width}w`)\n .join(\", \");\n};\n\nconst FALLBACK_WIDTH = 1024;\n\nconst FALLBACK_SIZES = \"(min-width: 1024px) 1024px, 100vw\";\n\nexport interface PictureProps extends StyledProps, ComponentPropsWithRef<\"picture\"> {\n src: string;\n sizes?: string;\n contentType?: string;\n srcSet?: string;\n crop?: ImageCrop;\n focalPoint?: ImageFocalPoint;\n imageLanguage?: string;\n}\n\nexport const Picture = forwardRef<HTMLPictureElement, PictureProps>(\n (\n {\n children,\n srcSet: srcSetProp,\n crop,\n focalPoint,\n src,\n imageLanguage,\n contentType,\n sizes = FALLBACK_SIZES,\n ...props\n },\n ref,\n ) => {\n const srcSet = srcSetProp ?? getSrcSet({ src, crop, focalPoint, imageLanguage });\n\n return (\n <styled.picture {...props} ref={ref}>\n {contentType !== \"image/gif\" && <source type={contentType} srcSet={srcSet} sizes={sizes} />}\n {children}\n </styled.picture>\n );\n },\n);\n\nconst StyledImage = styled(\"img\", {\n defaultVariants: { variant: \"regular\" },\n variants: {\n variant: {\n regular: {},\n rounded: {\n borderRadius: \"xsmall\",\n },\n },\n },\n});\n\nconst StyledFallbackElement = styled(\n ark.div,\n {\n base: {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n },\n },\n { baseComponent: true },\n);\n\ntype ImageVariantProps = StyledVariantProps<typeof StyledImage>;\n\nexport interface ImgProps extends StyledProps, ComponentPropsWithRef<\"img\">, ImageVariantProps {\n alt: string;\n src: string;\n fallbackWidth?: number;\n contentType?: string;\n crop?: ImageCrop;\n imageLanguage?: string;\n focalPoint?: ImageFocalPoint;\n}\n\nexport const Img = forwardRef<HTMLImageElement, ImgProps>(\n ({ fallbackWidth = FALLBACK_WIDTH, crop, focalPoint, imageLanguage, contentType, src, alt, ...props }, ref) => {\n const queryString = makeSrcQueryString({ width: fallbackWidth, crop, focalPoint, imageLanguage });\n return (\n <StyledImage alt={alt} src={contentType === \"image/gif\" ? src : `${src}?${queryString}`} {...props} ref={ref} />\n );\n },\n);\n\nexport interface ImageProps extends StyledProps, ComponentPropsWithRef<\"img\">, ImageVariantProps {\n alt: string;\n src?: string;\n sizes?: string;\n fallbackWidth?: number;\n contentType?: string;\n imageLanguage?: string;\n crop?: ImageCrop;\n fallbackElement?: ReactNode;\n focalPoint?: ImageFocalPoint;\n}\n\nexport const Image = forwardRef<HTMLImageElement, ImageProps>(\n (\n {\n srcSet: srcSetProp,\n crop,\n focalPoint,\n src,\n contentType,\n imageLanguage,\n fallbackWidth = FALLBACK_WIDTH,\n sizes = FALLBACK_SIZES,\n alt,\n fallbackElement,\n ...props\n },\n ref,\n ) => {\n const srcSet = srcSetProp ?? getSrcSet({ src, crop, focalPoint, imageLanguage });\n const queryString = makeSrcQueryString({ width: fallbackWidth, crop, focalPoint, imageLanguage });\n const fallbackSrc = src ? `${src}?${queryString}` : src;\n if ((!src || !src.length) && fallbackElement) {\n return (\n <StyledFallbackElement {...props} ref={ref}>\n {fallbackElement}\n </StyledFallbackElement>\n );\n }\n return (\n <picture>\n {contentType !== \"image/gif\" && <source type={contentType} srcSet={srcSet} sizes={sizes} />}\n <StyledImage alt={alt} src={contentType === \"image/gif\" ? src : fallbackSrc} {...props} ref={ref} />\n </picture>\n );\n },\n);\n"],"mappings":";;;;;;;;;;;;;AAgCA,MAAa,sBAAsB,EAAE,OAAO,MAAM,YAAY,oBAA2C;AAQvG,QAFe;EALK,SAAS,SAAS;EAEpC,QAAQ,cAAc,KAAK,OAAO,YAAY,KAAK,KAAK,cAAc,KAAK,OAAO,YAAY,KAAK;EAC5E,cAAc,UAAU,WAAW,EAAE,UAAU,WAAW;EACvD,iBAAiB,YAAY;EACsB,CAAC,QAAQ,MAAM,EAAE,CAAC,KAAK,IAAI;;AAY5G,MAAa,aAAa,EAAE,KAAK,MAAM,YAAY,oBAAmC;AACpF,KAAI,CAAC,IAAK,QAAO;AAEjB,QADe;EAAC;EAAM;EAAM;EAAM;EAAM;EAAM;EAAM;EAAK;EAAK;EAAK;EAAK;EAAK;EAAK;EAAI,CAEnF,KAAK,UAAU,GAAG,IAAI,GAAG,mBAAmB;EAAE;EAAO;EAAM;EAAY;EAAe,CAAC,CAAC,GAAG,MAAM,GAAG,CACpG,KAAK,KAAK;;AAGf,MAAM,iBAAiB;AAEvB,MAAM,iBAAiB;AAYvB,MAAa,UAAU,YAEnB,EACE,UACA,QAAQ,YACR,MACA,YACA,KACA,eACA,aACA,QAAQ,gBACR,GAAG,SAEL,QACG;CACH,MAAM,SAAS,cAAc,UAAU;EAAE;EAAK;EAAM;EAAY;EAAe,CAAC;AAEhF,QACE,qBAAC,OAAO;EAAQ,GAAI;EAAY;aAC7B,gBAAgB,eAAe,oBAAC;GAAO,MAAM;GAAqB;GAAe;IAAS,EAC1F;GACc;EAGtB;AAED,MAAM,cAAc,OAAO,OAAO;CAChC,iBAAiB,EAAE,SAAS,WAAW;CACvC,UAAU,EACR,SAAS;EACP,SAAS,EAAE;EACX,SAAS,EACP,cAAc,UACf;EACF,EACF;CACF,CAAC;AAEF,MAAM,wBAAwB,OAC5B,IAAI,KACJ,EACE,MAAM;CACJ,SAAS;CACT,YAAY;CACZ,gBAAgB;CACjB,EACF,EACD,EAAE,eAAe,MAAM,CACxB;AAcD,MAAa,MAAM,YAChB,EAAE,gBAAgB,gBAAgB,MAAM,YAAY,eAAe,aAAa,KAAK,KAAK,GAAG,SAAS,QAAQ;CAC7G,MAAM,cAAc,mBAAmB;EAAE,OAAO;EAAe;EAAM;EAAY;EAAe,CAAC;AACjG,QACE,oBAAC;EAAiB;EAAK,KAAK,gBAAgB,cAAc,MAAM,GAAG,IAAI,GAAG;EAAe,GAAI;EAAY;GAAO;EAGrH;AAcD,MAAa,QAAQ,YAEjB,EACE,QAAQ,YACR,MACA,YACA,KACA,aACA,eACA,gBAAgB,gBAChB,QAAQ,gBACR,KACA,iBACA,GAAG,SAEL,QACG;CACH,MAAM,SAAS,cAAc,UAAU;EAAE;EAAK;EAAM;EAAY;EAAe,CAAC;CAChF,MAAM,cAAc,mBAAmB;EAAE,OAAO;EAAe;EAAM;EAAY;EAAe,CAAC;CACjG,MAAM,cAAc,MAAM,GAAG,IAAI,GAAG,gBAAgB;AACpD,MAAK,CAAC,OAAO,CAAC,IAAI,WAAW,gBAC3B,QACE,oBAAC;EAAsB,GAAI;EAAY;YACpC;GACqB;AAG5B,QACE,qBAAC,wBACE,gBAAgB,eAAe,oBAAC;EAAO,MAAM;EAAqB;EAAe;GAAS,EAC3F,oBAAC;EAAiB;EAAK,KAAK,gBAAgB,cAAc,MAAM;EAAa,GAAI;EAAY;GAAO,IAC5F;EAGf"}
1
+ {"version":3,"file":"Image.mjs","names":["VAR_WIDTHS: Record<ImageVariantSize, number>"],"sources":["../src/Image.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 { ark } from \"@ark-ui/react\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport type { StyledProps, StyledVariantProps } from \"@ndla/styled-system/types\";\nimport type { ImageVariantDTO, ImageVariantSize } from \"@ndla/types-backend/image-api\";\n\nexport interface ImageCrop {\n startX: number;\n startY: number;\n endX: number;\n endY: number;\n}\n\nexport interface ImageFocalPoint {\n x: number;\n y: number;\n}\n\ninterface SrcQueryStringOptions {\n width?: number;\n crop?: ImageCrop;\n focalPoint?: ImageFocalPoint;\n imageLanguage?: string;\n}\n\nexport const makeSrcQueryString = ({ width, crop, focalPoint, imageLanguage }: SrcQueryStringOptions) => {\n const params = [];\n if (width) {\n params.push(`width=${width}`);\n }\n if (crop) {\n params.push(`cropStartX=${crop.startX}&cropEndX=${crop.endX}&cropStartY=${crop.startY}&cropEndY=${crop.endY}`);\n }\n if (focalPoint) {\n params.push(`focalX=${focalPoint.x}&focalY=${focalPoint.y}`);\n }\n if (imageLanguage) {\n params.push(`language=${imageLanguage}`);\n }\n return params.join(\"&\");\n};\n\ninterface SrcSetOptions {\n crop?: ImageCrop;\n focalPoint?: ImageFocalPoint;\n imageLanguage?: string;\n src?: string;\n}\n\nexport const VAR_WIDTHS: Record<ImageVariantSize, number> = {\n icon: 240,\n xsmall: 480,\n small: 800,\n medium: 1080,\n large: 1440,\n xlarge: 1920,\n xxlarge: 2560,\n};\n\nconst IMAGE_WIDTHS = [2720, 2080, 1760, 1440, 1120, 1000, 960, 800, 640, 480, 320, 240, 180];\n\nexport const getVariantSrcSet = (variants: ImageVariantDTO[]) => {\n return variants\n .map((variant) => {\n return `${variant.variantUrl} ${VAR_WIDTHS[variant.size]}w`;\n })\n .join(\", \");\n};\n\nexport const getVariantSizes = (variants: ImageVariantDTO[]) => {\n return variants\n .map((variant, i) => {\n if (i === variants.length - 1) {\n return `${VAR_WIDTHS[variant.size]}px`;\n }\n return `(max-width: ${VAR_WIDTHS[variant.size]}px) ${VAR_WIDTHS[variant.size]}px`;\n })\n .join(\", \");\n};\n\nexport const getSrcSet = ({ src, crop, focalPoint, imageLanguage }: SrcSetOptions) => {\n if (!src) return undefined;\n return IMAGE_WIDTHS.map((width) => {\n const queryString = makeSrcQueryString({ width, crop, focalPoint, imageLanguage });\n const query = queryString.length ? `?${queryString}` : \"\";\n return `${src}${query} ${width}w`;\n }).join(\", \");\n};\n\nconst FALLBACK_WIDTH = 1024;\n\nconst FALLBACK_SIZES = \"(min-width: 1024px) 1024px, 100vw\";\n\nexport interface PictureProps extends StyledProps, ComponentPropsWithRef<\"picture\"> {\n src: string;\n sizes?: string;\n contentType?: string;\n srcSet?: string;\n crop?: ImageCrop;\n focalPoint?: ImageFocalPoint;\n imageLanguage?: string;\n}\n\nexport const Picture = forwardRef<HTMLPictureElement, PictureProps>(\n (\n {\n children,\n srcSet: srcSetProp,\n crop,\n focalPoint,\n src,\n imageLanguage,\n contentType,\n sizes = FALLBACK_SIZES,\n ...props\n },\n ref,\n ) => {\n const srcSet = srcSetProp ?? getSrcSet({ src, crop, focalPoint, imageLanguage });\n\n return (\n <styled.picture {...props} ref={ref}>\n {contentType !== \"image/gif\" && <source type={contentType} srcSet={srcSet} sizes={sizes} />}\n {children}\n </styled.picture>\n );\n },\n);\n\nconst StyledImage = styled(\"img\", {\n defaultVariants: { variant: \"regular\" },\n variants: {\n variant: {\n regular: {},\n rounded: {\n borderRadius: \"xsmall\",\n },\n },\n },\n});\n\nconst StyledFallbackElement = styled(\n ark.div,\n {\n base: {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n },\n },\n { baseComponent: true },\n);\n\ntype ImageVariantProps = StyledVariantProps<typeof StyledImage>;\n\nexport interface ImgProps extends StyledProps, ComponentPropsWithRef<\"img\">, ImageVariantProps {\n alt: string;\n src: string;\n fallbackWidth?: number;\n contentType?: string;\n crop?: ImageCrop;\n imageLanguage?: string;\n focalPoint?: ImageFocalPoint;\n}\n\nexport const Img = forwardRef<HTMLImageElement, ImgProps>(\n ({ fallbackWidth = FALLBACK_WIDTH, crop, focalPoint, imageLanguage, contentType, src, alt, ...props }, ref) => {\n const queryString = makeSrcQueryString({ width: fallbackWidth, crop, focalPoint, imageLanguage });\n const srcWithParms = queryString ? `${src}?${queryString}` : src;\n return <StyledImage alt={alt} src={contentType === \"image/gif\" ? src : srcWithParms} {...props} ref={ref} />;\n },\n);\n\nexport interface ImageProps extends StyledProps, ComponentPropsWithRef<\"img\">, ImageVariantProps {\n alt: string;\n src?: string;\n sizes?: string;\n fallbackWidth?: number;\n contentType?: string;\n imageLanguage?: string;\n crop?: ImageCrop;\n fallbackElement?: ReactNode;\n focalPoint?: ImageFocalPoint;\n variants?: ImageVariantDTO[];\n}\n\nexport const Image = forwardRef<HTMLImageElement, ImageProps>(\n (\n {\n srcSet: srcSetProp,\n crop,\n focalPoint,\n src,\n contentType,\n imageLanguage,\n fallbackWidth = FALLBACK_WIDTH,\n sizes: sizesProp,\n alt,\n fallbackElement,\n variants,\n ...props\n },\n ref,\n ) => {\n if (!src?.length && !variants?.length && fallbackElement) {\n return (\n <StyledFallbackElement {...props} ref={ref}>\n {fallbackElement}\n </StyledFallbackElement>\n );\n }\n\n const queryString = makeSrcQueryString({ width: fallbackWidth, crop, focalPoint, imageLanguage });\n const fallbackSrc = src && queryString ? `${src}?${queryString}` : src;\n\n return (\n <picture>\n {!!variants?.length && !crop && !focalPoint && contentType !== \"image/gif\" && (\n <source\n type={\"image/webp\"}\n srcSet={getVariantSrcSet(variants)}\n sizes={sizesProp ?? getVariantSizes(variants)}\n />\n )}\n {contentType !== \"image/gif\" && (\n <source\n type={contentType}\n srcSet={getSrcSet({ src, crop, focalPoint, imageLanguage })}\n sizes={sizesProp ?? FALLBACK_SIZES}\n />\n )}\n <StyledImage alt={alt} src={contentType === \"image/gif\" ? src : fallbackSrc} {...props} ref={ref} />\n </picture>\n );\n },\n);\n"],"mappings":"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"}
package/lib/Image.d.ts CHANGED
@@ -7,6 +7,7 @@
7
7
  */
8
8
  import { type ComponentPropsWithRef, type ReactNode } from "react";
9
9
  import type { StyledProps, StyledVariantProps } from "@ndla/styled-system/types";
10
+ import type { ImageVariantDTO, ImageVariantSize } from "@ndla/types-backend/image-api";
10
11
  export interface ImageCrop {
11
12
  startX: number;
12
13
  startY: number;
@@ -18,18 +19,21 @@ export interface ImageFocalPoint {
18
19
  y: number;
19
20
  }
20
21
  interface SrcQueryStringOptions {
21
- width: number | undefined;
22
+ width?: number;
22
23
  crop?: ImageCrop;
23
24
  focalPoint?: ImageFocalPoint;
24
25
  imageLanguage?: string;
25
26
  }
26
27
  export declare const makeSrcQueryString: ({ width, crop, focalPoint, imageLanguage }: SrcQueryStringOptions) => string;
27
28
  interface SrcSetOptions {
28
- src?: string;
29
29
  crop?: ImageCrop;
30
30
  focalPoint?: ImageFocalPoint;
31
31
  imageLanguage?: string;
32
+ src?: string;
32
33
  }
34
+ export declare const VAR_WIDTHS: Record<ImageVariantSize, number>;
35
+ export declare const getVariantSrcSet: (variants: ImageVariantDTO[]) => string;
36
+ export declare const getVariantSizes: (variants: ImageVariantDTO[]) => string;
33
37
  export declare const getSrcSet: ({ src, crop, focalPoint, imageLanguage }: SrcSetOptions) => string | undefined;
34
38
  export interface PictureProps extends StyledProps, ComponentPropsWithRef<"picture"> {
35
39
  src: string;
@@ -65,6 +69,7 @@ export interface ImageProps extends StyledProps, ComponentPropsWithRef<"img">, I
65
69
  crop?: ImageCrop;
66
70
  fallbackElement?: ReactNode;
67
71
  focalPoint?: ImageFocalPoint;
72
+ variants?: ImageVariantDTO[];
68
73
  }
69
74
  export declare const Image: import("react").ForwardRefExoticComponent<Omit<ImageProps, "ref"> & import("react").RefAttributes<HTMLImageElement>>;
70
75
  export {};
package/lib/Image.js CHANGED
@@ -12,35 +12,59 @@ let react = require("react");
12
12
  *
13
13
  */
14
14
  const makeSrcQueryString = ({ width, crop, focalPoint, imageLanguage }) => {
15
- return [
16
- width && `width=${width}`,
17
- crop && `cropStartX=${crop.startX}&cropEndX=${crop.endX}&cropStartY=${crop.startY}&cropEndY=${crop.endY}`,
18
- focalPoint && `focalX=${focalPoint.x}&focalY=${focalPoint.y}`,
19
- imageLanguage && `language=${imageLanguage}`
20
- ].filter((p) => p).join("&");
15
+ const params = [];
16
+ if (width) params.push(`width=${width}`);
17
+ if (crop) params.push(`cropStartX=${crop.startX}&cropEndX=${crop.endX}&cropStartY=${crop.startY}&cropEndY=${crop.endY}`);
18
+ if (focalPoint) params.push(`focalX=${focalPoint.x}&focalY=${focalPoint.y}`);
19
+ if (imageLanguage) params.push(`language=${imageLanguage}`);
20
+ return params.join("&");
21
+ };
22
+ const VAR_WIDTHS = {
23
+ icon: 240,
24
+ xsmall: 480,
25
+ small: 800,
26
+ medium: 1080,
27
+ large: 1440,
28
+ xlarge: 1920,
29
+ xxlarge: 2560
30
+ };
31
+ const IMAGE_WIDTHS = [
32
+ 2720,
33
+ 2080,
34
+ 1760,
35
+ 1440,
36
+ 1120,
37
+ 1e3,
38
+ 960,
39
+ 800,
40
+ 640,
41
+ 480,
42
+ 320,
43
+ 240,
44
+ 180
45
+ ];
46
+ const getVariantSrcSet = (variants) => {
47
+ return variants.map((variant) => {
48
+ return `${variant.variantUrl} ${VAR_WIDTHS[variant.size]}w`;
49
+ }).join(", ");
50
+ };
51
+ const getVariantSizes = (variants) => {
52
+ return variants.map((variant, i) => {
53
+ if (i === variants.length - 1) return `${VAR_WIDTHS[variant.size]}px`;
54
+ return `(max-width: ${VAR_WIDTHS[variant.size]}px) ${VAR_WIDTHS[variant.size]}px`;
55
+ }).join(", ");
21
56
  };
22
57
  const getSrcSet = ({ src, crop, focalPoint, imageLanguage }) => {
23
58
  if (!src) return void 0;
24
- return [
25
- 2720,
26
- 2080,
27
- 1760,
28
- 1440,
29
- 1120,
30
- 1e3,
31
- 960,
32
- 800,
33
- 640,
34
- 480,
35
- 320,
36
- 240,
37
- 180
38
- ].map((width) => `${src}?${makeSrcQueryString({
39
- width,
40
- crop,
41
- focalPoint,
42
- imageLanguage
43
- })} ${width}w`).join(", ");
59
+ return IMAGE_WIDTHS.map((width) => {
60
+ const queryString = makeSrcQueryString({
61
+ width,
62
+ crop,
63
+ focalPoint,
64
+ imageLanguage
65
+ });
66
+ return `${src}${queryString.length ? `?${queryString}` : ""} ${width}w`;
67
+ }).join(", ");
44
68
  };
45
69
  const FALLBACK_WIDTH = 1024;
46
70
  const FALLBACK_SIZES = "(min-width: 1024px) 1024px, 100vw";
@@ -80,19 +104,19 @@ const Img = (0, react.forwardRef)(({ fallbackWidth = FALLBACK_WIDTH, crop, focal
80
104
  focalPoint,
81
105
  imageLanguage
82
106
  });
107
+ const srcWithParms = queryString ? `${src}?${queryString}` : src;
83
108
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StyledImage, {
84
109
  alt,
85
- src: contentType === "image/gif" ? src : `${src}?${queryString}`,
110
+ src: contentType === "image/gif" ? src : srcWithParms,
86
111
  ...props,
87
112
  ref
88
113
  });
89
114
  });
90
- const Image = (0, react.forwardRef)(({ srcSet: srcSetProp, crop, focalPoint, src, contentType, imageLanguage, fallbackWidth = FALLBACK_WIDTH, sizes = FALLBACK_SIZES, alt, fallbackElement, ...props }, ref) => {
91
- const srcSet = srcSetProp ?? getSrcSet({
92
- src,
93
- crop,
94
- focalPoint,
95
- imageLanguage
115
+ const Image = (0, react.forwardRef)(({ srcSet: srcSetProp, crop, focalPoint, src, contentType, imageLanguage, fallbackWidth = FALLBACK_WIDTH, sizes: sizesProp, alt, fallbackElement, variants, ...props }, ref) => {
116
+ if (!src?.length && !variants?.length && fallbackElement) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StyledFallbackElement, {
117
+ ...props,
118
+ ref,
119
+ children: fallbackElement
96
120
  });
97
121
  const queryString = makeSrcQueryString({
98
122
  width: fallbackWidth,
@@ -100,22 +124,30 @@ const Image = (0, react.forwardRef)(({ srcSet: srcSetProp, crop, focalPoint, src
100
124
  focalPoint,
101
125
  imageLanguage
102
126
  });
103
- const fallbackSrc = src ? `${src}?${queryString}` : src;
104
- if ((!src || !src.length) && fallbackElement) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StyledFallbackElement, {
105
- ...props,
106
- ref,
107
- children: fallbackElement
108
- });
109
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("picture", { children: [contentType !== "image/gif" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("source", {
110
- type: contentType,
111
- srcSet,
112
- sizes
113
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StyledImage, {
114
- alt,
115
- src: contentType === "image/gif" ? src : fallbackSrc,
116
- ...props,
117
- ref
118
- })] });
127
+ const fallbackSrc = src && queryString ? `${src}?${queryString}` : src;
128
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("picture", { children: [
129
+ !!variants?.length && !crop && !focalPoint && contentType !== "image/gif" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("source", {
130
+ type: "image/webp",
131
+ srcSet: getVariantSrcSet(variants),
132
+ sizes: sizesProp ?? getVariantSizes(variants)
133
+ }),
134
+ contentType !== "image/gif" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("source", {
135
+ type: contentType,
136
+ srcSet: getSrcSet({
137
+ src,
138
+ crop,
139
+ focalPoint,
140
+ imageLanguage
141
+ }),
142
+ sizes: sizesProp ?? FALLBACK_SIZES
143
+ }),
144
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StyledImage, {
145
+ alt,
146
+ src: contentType === "image/gif" ? src : fallbackSrc,
147
+ ...props,
148
+ ref
149
+ })
150
+ ] });
119
151
  });
120
152
 
121
153
  //#endregion
package/lib/Image.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"Image.js","names":["styled","ark"],"sources":["../src/Image.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 { ark } from \"@ark-ui/react\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport type { StyledProps, StyledVariantProps } from \"@ndla/styled-system/types\";\n\nexport interface ImageCrop {\n startX: number;\n startY: number;\n endX: number;\n endY: number;\n}\n\nexport interface ImageFocalPoint {\n x: number;\n y: number;\n}\n\ninterface SrcQueryStringOptions {\n width: number | undefined;\n crop?: ImageCrop;\n focalPoint?: ImageFocalPoint;\n imageLanguage?: string;\n}\n\nexport const makeSrcQueryString = ({ width, crop, focalPoint, imageLanguage }: SrcQueryStringOptions) => {\n const widthParams = width && `width=${width}`;\n const cropParams =\n crop && `cropStartX=${crop.startX}&cropEndX=${crop.endX}&cropStartY=${crop.startY}&cropEndY=${crop.endY}`;\n const focalPointParams = focalPoint && `focalX=${focalPoint.x}&focalY=${focalPoint.y}`;\n const imageLanguageParams = imageLanguage && `language=${imageLanguage}`;\n const params = [widthParams, cropParams, focalPointParams, imageLanguageParams].filter((p) => p).join(\"&\");\n\n return params;\n};\n\ninterface SrcSetOptions {\n src?: string;\n crop?: ImageCrop;\n focalPoint?: ImageFocalPoint;\n imageLanguage?: string;\n}\n\nexport const getSrcSet = ({ src, crop, focalPoint, imageLanguage }: SrcSetOptions) => {\n if (!src) return undefined;\n const widths = [2720, 2080, 1760, 1440, 1120, 1000, 960, 800, 640, 480, 320, 240, 180];\n return widths\n .map((width) => `${src}?${makeSrcQueryString({ width, crop, focalPoint, imageLanguage })} ${width}w`)\n .join(\", \");\n};\n\nconst FALLBACK_WIDTH = 1024;\n\nconst FALLBACK_SIZES = \"(min-width: 1024px) 1024px, 100vw\";\n\nexport interface PictureProps extends StyledProps, ComponentPropsWithRef<\"picture\"> {\n src: string;\n sizes?: string;\n contentType?: string;\n srcSet?: string;\n crop?: ImageCrop;\n focalPoint?: ImageFocalPoint;\n imageLanguage?: string;\n}\n\nexport const Picture = forwardRef<HTMLPictureElement, PictureProps>(\n (\n {\n children,\n srcSet: srcSetProp,\n crop,\n focalPoint,\n src,\n imageLanguage,\n contentType,\n sizes = FALLBACK_SIZES,\n ...props\n },\n ref,\n ) => {\n const srcSet = srcSetProp ?? getSrcSet({ src, crop, focalPoint, imageLanguage });\n\n return (\n <styled.picture {...props} ref={ref}>\n {contentType !== \"image/gif\" && <source type={contentType} srcSet={srcSet} sizes={sizes} />}\n {children}\n </styled.picture>\n );\n },\n);\n\nconst StyledImage = styled(\"img\", {\n defaultVariants: { variant: \"regular\" },\n variants: {\n variant: {\n regular: {},\n rounded: {\n borderRadius: \"xsmall\",\n },\n },\n },\n});\n\nconst StyledFallbackElement = styled(\n ark.div,\n {\n base: {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n },\n },\n { baseComponent: true },\n);\n\ntype ImageVariantProps = StyledVariantProps<typeof StyledImage>;\n\nexport interface ImgProps extends StyledProps, ComponentPropsWithRef<\"img\">, ImageVariantProps {\n alt: string;\n src: string;\n fallbackWidth?: number;\n contentType?: string;\n crop?: ImageCrop;\n imageLanguage?: string;\n focalPoint?: ImageFocalPoint;\n}\n\nexport const Img = forwardRef<HTMLImageElement, ImgProps>(\n ({ fallbackWidth = FALLBACK_WIDTH, crop, focalPoint, imageLanguage, contentType, src, alt, ...props }, ref) => {\n const queryString = makeSrcQueryString({ width: fallbackWidth, crop, focalPoint, imageLanguage });\n return (\n <StyledImage alt={alt} src={contentType === \"image/gif\" ? src : `${src}?${queryString}`} {...props} ref={ref} />\n );\n },\n);\n\nexport interface ImageProps extends StyledProps, ComponentPropsWithRef<\"img\">, ImageVariantProps {\n alt: string;\n src?: string;\n sizes?: string;\n fallbackWidth?: number;\n contentType?: string;\n imageLanguage?: string;\n crop?: ImageCrop;\n fallbackElement?: ReactNode;\n focalPoint?: ImageFocalPoint;\n}\n\nexport const Image = forwardRef<HTMLImageElement, ImageProps>(\n (\n {\n srcSet: srcSetProp,\n crop,\n focalPoint,\n src,\n contentType,\n imageLanguage,\n fallbackWidth = FALLBACK_WIDTH,\n sizes = FALLBACK_SIZES,\n alt,\n fallbackElement,\n ...props\n },\n ref,\n ) => {\n const srcSet = srcSetProp ?? getSrcSet({ src, crop, focalPoint, imageLanguage });\n const queryString = makeSrcQueryString({ width: fallbackWidth, crop, focalPoint, imageLanguage });\n const fallbackSrc = src ? `${src}?${queryString}` : src;\n if ((!src || !src.length) && fallbackElement) {\n return (\n <StyledFallbackElement {...props} ref={ref}>\n {fallbackElement}\n </StyledFallbackElement>\n );\n }\n return (\n <picture>\n {contentType !== \"image/gif\" && <source type={contentType} srcSet={srcSet} sizes={sizes} />}\n <StyledImage alt={alt} src={contentType === \"image/gif\" ? src : fallbackSrc} {...props} ref={ref} />\n </picture>\n );\n },\n);\n"],"mappings":";;;;;;;;;;;;;AAgCA,MAAa,sBAAsB,EAAE,OAAO,MAAM,YAAY,oBAA2C;AAQvG,QAFe;EALK,SAAS,SAAS;EAEpC,QAAQ,cAAc,KAAK,OAAO,YAAY,KAAK,KAAK,cAAc,KAAK,OAAO,YAAY,KAAK;EAC5E,cAAc,UAAU,WAAW,EAAE,UAAU,WAAW;EACvD,iBAAiB,YAAY;EACsB,CAAC,QAAQ,MAAM,EAAE,CAAC,KAAK,IAAI;;AAY5G,MAAa,aAAa,EAAE,KAAK,MAAM,YAAY,oBAAmC;AACpF,KAAI,CAAC,IAAK,QAAO;AAEjB,QADe;EAAC;EAAM;EAAM;EAAM;EAAM;EAAM;EAAM;EAAK;EAAK;EAAK;EAAK;EAAK;EAAK;EAAI,CAEnF,KAAK,UAAU,GAAG,IAAI,GAAG,mBAAmB;EAAE;EAAO;EAAM;EAAY;EAAe,CAAC,CAAC,GAAG,MAAM,GAAG,CACpG,KAAK,KAAK;;AAGf,MAAM,iBAAiB;AAEvB,MAAM,iBAAiB;AAYvB,MAAa,iCAET,EACE,UACA,QAAQ,YACR,MACA,YACA,KACA,eACA,aACA,QAAQ,gBACR,GAAG,SAEL,QACG;CACH,MAAM,SAAS,cAAc,UAAU;EAAE;EAAK;EAAM;EAAY;EAAe,CAAC;AAEhF,QACE,4CAACA,gCAAO;EAAQ,GAAI;EAAY;aAC7B,gBAAgB,eAAe,2CAAC;GAAO,MAAM;GAAqB;GAAe;IAAS,EAC1F;GACc;EAGtB;AAED,MAAM,mDAAqB,OAAO;CAChC,iBAAiB,EAAE,SAAS,WAAW;CACvC,UAAU,EACR,SAAS;EACP,SAAS,EAAE;EACX,SAAS,EACP,cAAc,UACf;EACF,EACF;CACF,CAAC;AAEF,MAAM,6DACJC,mBAAI,KACJ,EACE,MAAM;CACJ,SAAS;CACT,YAAY;CACZ,gBAAgB;CACjB,EACF,EACD,EAAE,eAAe,MAAM,CACxB;AAcD,MAAa,6BACV,EAAE,gBAAgB,gBAAgB,MAAM,YAAY,eAAe,aAAa,KAAK,KAAK,GAAG,SAAS,QAAQ;CAC7G,MAAM,cAAc,mBAAmB;EAAE,OAAO;EAAe;EAAM;EAAY;EAAe,CAAC;AACjG,QACE,2CAAC;EAAiB;EAAK,KAAK,gBAAgB,cAAc,MAAM,GAAG,IAAI,GAAG;EAAe,GAAI;EAAY;GAAO;EAGrH;AAcD,MAAa,+BAET,EACE,QAAQ,YACR,MACA,YACA,KACA,aACA,eACA,gBAAgB,gBAChB,QAAQ,gBACR,KACA,iBACA,GAAG,SAEL,QACG;CACH,MAAM,SAAS,cAAc,UAAU;EAAE;EAAK;EAAM;EAAY;EAAe,CAAC;CAChF,MAAM,cAAc,mBAAmB;EAAE,OAAO;EAAe;EAAM;EAAY;EAAe,CAAC;CACjG,MAAM,cAAc,MAAM,GAAG,IAAI,GAAG,gBAAgB;AACpD,MAAK,CAAC,OAAO,CAAC,IAAI,WAAW,gBAC3B,QACE,2CAAC;EAAsB,GAAI;EAAY;YACpC;GACqB;AAG5B,QACE,4CAAC,wBACE,gBAAgB,eAAe,2CAAC;EAAO,MAAM;EAAqB;EAAe;GAAS,EAC3F,2CAAC;EAAiB;EAAK,KAAK,gBAAgB,cAAc,MAAM;EAAa,GAAI;EAAY;GAAO,IAC5F;EAGf"}
1
+ {"version":3,"file":"Image.js","names":["VAR_WIDTHS: Record<ImageVariantSize, number>","styled","ark"],"sources":["../src/Image.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 { ark } from \"@ark-ui/react\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport type { StyledProps, StyledVariantProps } from \"@ndla/styled-system/types\";\nimport type { ImageVariantDTO, ImageVariantSize } from \"@ndla/types-backend/image-api\";\n\nexport interface ImageCrop {\n startX: number;\n startY: number;\n endX: number;\n endY: number;\n}\n\nexport interface ImageFocalPoint {\n x: number;\n y: number;\n}\n\ninterface SrcQueryStringOptions {\n width?: number;\n crop?: ImageCrop;\n focalPoint?: ImageFocalPoint;\n imageLanguage?: string;\n}\n\nexport const makeSrcQueryString = ({ width, crop, focalPoint, imageLanguage }: SrcQueryStringOptions) => {\n const params = [];\n if (width) {\n params.push(`width=${width}`);\n }\n if (crop) {\n params.push(`cropStartX=${crop.startX}&cropEndX=${crop.endX}&cropStartY=${crop.startY}&cropEndY=${crop.endY}`);\n }\n if (focalPoint) {\n params.push(`focalX=${focalPoint.x}&focalY=${focalPoint.y}`);\n }\n if (imageLanguage) {\n params.push(`language=${imageLanguage}`);\n }\n return params.join(\"&\");\n};\n\ninterface SrcSetOptions {\n crop?: ImageCrop;\n focalPoint?: ImageFocalPoint;\n imageLanguage?: string;\n src?: string;\n}\n\nexport const VAR_WIDTHS: Record<ImageVariantSize, number> = {\n icon: 240,\n xsmall: 480,\n small: 800,\n medium: 1080,\n large: 1440,\n xlarge: 1920,\n xxlarge: 2560,\n};\n\nconst IMAGE_WIDTHS = [2720, 2080, 1760, 1440, 1120, 1000, 960, 800, 640, 480, 320, 240, 180];\n\nexport const getVariantSrcSet = (variants: ImageVariantDTO[]) => {\n return variants\n .map((variant) => {\n return `${variant.variantUrl} ${VAR_WIDTHS[variant.size]}w`;\n })\n .join(\", \");\n};\n\nexport const getVariantSizes = (variants: ImageVariantDTO[]) => {\n return variants\n .map((variant, i) => {\n if (i === variants.length - 1) {\n return `${VAR_WIDTHS[variant.size]}px`;\n }\n return `(max-width: ${VAR_WIDTHS[variant.size]}px) ${VAR_WIDTHS[variant.size]}px`;\n })\n .join(\", \");\n};\n\nexport const getSrcSet = ({ src, crop, focalPoint, imageLanguage }: SrcSetOptions) => {\n if (!src) return undefined;\n return IMAGE_WIDTHS.map((width) => {\n const queryString = makeSrcQueryString({ width, crop, focalPoint, imageLanguage });\n const query = queryString.length ? `?${queryString}` : \"\";\n return `${src}${query} ${width}w`;\n }).join(\", \");\n};\n\nconst FALLBACK_WIDTH = 1024;\n\nconst FALLBACK_SIZES = \"(min-width: 1024px) 1024px, 100vw\";\n\nexport interface PictureProps extends StyledProps, ComponentPropsWithRef<\"picture\"> {\n src: string;\n sizes?: string;\n contentType?: string;\n srcSet?: string;\n crop?: ImageCrop;\n focalPoint?: ImageFocalPoint;\n imageLanguage?: string;\n}\n\nexport const Picture = forwardRef<HTMLPictureElement, PictureProps>(\n (\n {\n children,\n srcSet: srcSetProp,\n crop,\n focalPoint,\n src,\n imageLanguage,\n contentType,\n sizes = FALLBACK_SIZES,\n ...props\n },\n ref,\n ) => {\n const srcSet = srcSetProp ?? getSrcSet({ src, crop, focalPoint, imageLanguage });\n\n return (\n <styled.picture {...props} ref={ref}>\n {contentType !== \"image/gif\" && <source type={contentType} srcSet={srcSet} sizes={sizes} />}\n {children}\n </styled.picture>\n );\n },\n);\n\nconst StyledImage = styled(\"img\", {\n defaultVariants: { variant: \"regular\" },\n variants: {\n variant: {\n regular: {},\n rounded: {\n borderRadius: \"xsmall\",\n },\n },\n },\n});\n\nconst StyledFallbackElement = styled(\n ark.div,\n {\n base: {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n },\n },\n { baseComponent: true },\n);\n\ntype ImageVariantProps = StyledVariantProps<typeof StyledImage>;\n\nexport interface ImgProps extends StyledProps, ComponentPropsWithRef<\"img\">, ImageVariantProps {\n alt: string;\n src: string;\n fallbackWidth?: number;\n contentType?: string;\n crop?: ImageCrop;\n imageLanguage?: string;\n focalPoint?: ImageFocalPoint;\n}\n\nexport const Img = forwardRef<HTMLImageElement, ImgProps>(\n ({ fallbackWidth = FALLBACK_WIDTH, crop, focalPoint, imageLanguage, contentType, src, alt, ...props }, ref) => {\n const queryString = makeSrcQueryString({ width: fallbackWidth, crop, focalPoint, imageLanguage });\n const srcWithParms = queryString ? `${src}?${queryString}` : src;\n return <StyledImage alt={alt} src={contentType === \"image/gif\" ? src : srcWithParms} {...props} ref={ref} />;\n },\n);\n\nexport interface ImageProps extends StyledProps, ComponentPropsWithRef<\"img\">, ImageVariantProps {\n alt: string;\n src?: string;\n sizes?: string;\n fallbackWidth?: number;\n contentType?: string;\n imageLanguage?: string;\n crop?: ImageCrop;\n fallbackElement?: ReactNode;\n focalPoint?: ImageFocalPoint;\n variants?: ImageVariantDTO[];\n}\n\nexport const Image = forwardRef<HTMLImageElement, ImageProps>(\n (\n {\n srcSet: srcSetProp,\n crop,\n focalPoint,\n src,\n contentType,\n imageLanguage,\n fallbackWidth = FALLBACK_WIDTH,\n sizes: sizesProp,\n alt,\n fallbackElement,\n variants,\n ...props\n },\n ref,\n ) => {\n if (!src?.length && !variants?.length && fallbackElement) {\n return (\n <StyledFallbackElement {...props} ref={ref}>\n {fallbackElement}\n </StyledFallbackElement>\n );\n }\n\n const queryString = makeSrcQueryString({ width: fallbackWidth, crop, focalPoint, imageLanguage });\n const fallbackSrc = src && queryString ? `${src}?${queryString}` : src;\n\n return (\n <picture>\n {!!variants?.length && !crop && !focalPoint && contentType !== \"image/gif\" && (\n <source\n type={\"image/webp\"}\n srcSet={getVariantSrcSet(variants)}\n sizes={sizesProp ?? getVariantSizes(variants)}\n />\n )}\n {contentType !== \"image/gif\" && (\n <source\n type={contentType}\n srcSet={getSrcSet({ src, crop, focalPoint, imageLanguage })}\n sizes={sizesProp ?? FALLBACK_SIZES}\n />\n )}\n <StyledImage alt={alt} src={contentType === \"image/gif\" ? src : fallbackSrc} {...props} ref={ref} />\n </picture>\n );\n },\n);\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ndla/primitives",
3
3
  "type": "module",
4
- "version": "1.0.112-alpha.0",
4
+ "version": "1.0.115-alpha.0",
5
5
  "description": "Primitive components for NDLA.",
6
6
  "license": "GPL-3.0",
7
7
  "exports": {
@@ -33,12 +33,12 @@
33
33
  ],
34
34
  "dependencies": {
35
35
  "@ark-ui/react": "^5.25.0",
36
- "@ndla/styled-system": "^0.0.44",
36
+ "@ndla/styled-system": "^0.0.46",
37
37
  "@ndla/util": "^5.0.17-alpha.0"
38
38
  },
39
39
  "devDependencies": {
40
- "@ndla/preset-panda": "^0.0.66",
41
- "@pandacss/dev": "^1.3.1"
40
+ "@ndla/preset-panda": "^0.0.68",
41
+ "@pandacss/dev": "^1.7.0"
42
42
  },
43
43
  "peerDependencies": {
44
44
  "react": ">= 18",
@@ -47,5 +47,5 @@
47
47
  "publishConfig": {
48
48
  "access": "public"
49
49
  },
50
- "gitHead": "fbbf55cf6f16fd70d57f4a657acafcef02479efd"
50
+ "gitHead": "ce79c85e0a7fbc7909f4410fca6405536963f066"
51
51
  }