@ndla/image-search 11.0.102-alpha.0 → 11.0.104-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.
@@ -0,0 +1,18 @@
1
+ import { useMemo } from "react";
2
+ import { Text } from "@ndla/primitives";
3
+ import { humanFileSize } from "@ndla/util";
4
+ import { jsx } from "react/jsx-runtime";
5
+
6
+ //#region src/ImageMeta.tsx
7
+ const ImageMeta = ({ contentType, fileSize, imageDimensions, locale }) => {
8
+ const prettySize = useMemo(() => {
9
+ return humanFileSize(fileSize, locale);
10
+ }, [fileSize, locale]);
11
+ const dimensions = imageDimensions ? ` - ${imageDimensions.width}x${imageDimensions.height} px` : "";
12
+ return /* @__PURE__ */ jsx(Text, { children: `${contentType} - ${prettySize}${dimensions}` });
13
+ };
14
+ var ImageMeta_default = ImageMeta;
15
+
16
+ //#endregion
17
+ export { ImageMeta_default };
18
+ //# sourceMappingURL=ImageMeta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageMeta.js","names":[],"sources":["../src/ImageMeta.tsx"],"sourcesContent":["/**\n * Copyright (c) 2022-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 { Text } from \"@ndla/primitives\";\nimport type { IImageDimensionsDTO } from \"@ndla/types-backend/image-api\";\nimport { humanFileSize } from \"@ndla/util\";\n\ninterface Props {\n contentType: string;\n fileSize: number;\n imageDimensions?: IImageDimensionsDTO;\n locale: string;\n}\n\nconst ImageMeta = ({ contentType, fileSize, imageDimensions, locale }: Props) => {\n const prettySize = useMemo(() => {\n return humanFileSize(fileSize, locale);\n }, [fileSize, locale]);\n\n const dimensions = imageDimensions ? ` - ${imageDimensions.width}x${imageDimensions.height} px` : \"\";\n return <Text>{`${contentType} - ${prettySize}${dimensions}`}</Text>;\n};\n\nexport default ImageMeta;\n"],"mappings":";;;;;;AAoBA,MAAM,YAAY,CAAC,EAAE,aAAa,UAAU,iBAAiB,QAAe,KAAK;CAC/E,MAAM,aAAa,QAAQ,MAAM;AAC/B,SAAO,cAAc,UAAU,OAAO;CACvC,GAAE,CAAC,UAAU,MAAO,EAAC;CAEtB,MAAM,aAAa,mBAAmB,KAAK,gBAAgB,MAAM,GAAG,gBAAgB,OAAO,OAAO;AAClG,wBAAO,IAAC,mBAAO,EAAE,YAAY,KAAK,WAAW,EAAE,WAAW,IAAS;AACpE;AAED,wBAAe"}
@@ -0,0 +1,161 @@
1
+ import { ImageSearchResult } from "./ImageSearchResult.js";
2
+ import { useEffect, useState } from "react";
3
+ import { Button, IconButton, Input, PaginationContext, PaginationEllipsis, PaginationItem, PaginationNextTrigger, PaginationPrevTrigger, PaginationRoot, Text } from "@ndla/primitives";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ import { ArrowLeftShortLine, ArrowRightShortLine, SearchLine } from "@ndla/icons";
6
+ import { styled } from "@ndla/styled-system/jsx";
7
+
8
+ //#region src/ImageSearch.tsx
9
+ const ImageSearchWrapper = styled("div", { base: {
10
+ display: "flex",
11
+ flexDirection: "column",
12
+ gap: "small"
13
+ } });
14
+ const StyledSearchResults = styled("div", { base: {
15
+ display: "grid",
16
+ gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))",
17
+ gridGap: "xsmall",
18
+ gridAutoFlow: "dense"
19
+ } });
20
+ const InputWrapper = styled("div", { base: {
21
+ display: "flex",
22
+ gap: "xsmall"
23
+ } });
24
+ const StyledPaginationRoot = styled(PaginationRoot, { base: { flexWrap: "wrap" } });
25
+ const StyledButton = styled(Button, { base: { tabletWideDown: {
26
+ paddingInline: "xsmall",
27
+ "& span": { display: "none" }
28
+ } } });
29
+ const StyledPaginationItem = styled(PaginationItem, { base: { tabletWideDown: {
30
+ "&:nth-child(2)": { display: "none" },
31
+ "&:nth-last-child(2)": { display: "none" }
32
+ } } });
33
+ const ImageSearch = ({ onImageSelect, searchImages: search, onError, locale, noResults, checkboxAction, showCheckbox, translations }) => {
34
+ const [queryObject, setQueryObject] = useState({
35
+ query: void 0,
36
+ page: 1,
37
+ pageSize: 16
38
+ });
39
+ const [selectedImage, setSelectedImage] = useState();
40
+ const [searching, setSearching] = useState(false);
41
+ const [searchResult, setSearchResult] = useState();
42
+ const { page } = queryObject;
43
+ const noResultsFound = !searching && searchResult?.results.length === 0;
44
+ const onSelectImage = (image, saveAsMetaImage) => {
45
+ setSelectedImage(void 0);
46
+ if (!image) return;
47
+ onImageSelect(image);
48
+ if (saveAsMetaImage) checkboxAction?.(image);
49
+ };
50
+ const searchImages = (queryObject$1) => {
51
+ setSearching(true);
52
+ search(queryObject$1.query, queryObject$1.page).then((result) => {
53
+ setQueryObject({
54
+ query: queryObject$1.query,
55
+ pageSize: result.pageSize,
56
+ page: queryObject$1.page
57
+ });
58
+ setSearchResult(result);
59
+ setSearching(false);
60
+ }).catch((err) => {
61
+ onError?.(err);
62
+ setSearching(false);
63
+ });
64
+ };
65
+ const handleQueryChange = ({ target: { value } }) => {
66
+ setQueryObject((prevState) => ({
67
+ ...prevState,
68
+ query: value
69
+ }));
70
+ };
71
+ const onEnter = (e) => {
72
+ if (e.key === "Enter") searchImages(queryObject);
73
+ };
74
+ useEffect(() => {
75
+ searchImages(queryObject);
76
+ }, []);
77
+ return /* @__PURE__ */ jsxs(ImageSearchWrapper, { children: [
78
+ /* @__PURE__ */ jsxs(InputWrapper, {
79
+ role: "search",
80
+ children: [/* @__PURE__ */ jsx(Input, {
81
+ type: "search",
82
+ placeholder: translations.searchPlaceholder,
83
+ value: queryObject?.query ?? "",
84
+ onChange: handleQueryChange,
85
+ onKeyDown: onEnter
86
+ }), /* @__PURE__ */ jsx(IconButton, {
87
+ variant: "primary",
88
+ "aria-label": translations.searchButtonTitle,
89
+ title: translations.searchButtonTitle,
90
+ onKeyDown: onEnter,
91
+ onClick: () => searchImages(queryObject),
92
+ children: /* @__PURE__ */ jsx(SearchLine, {})
93
+ })]
94
+ }),
95
+ !!noResultsFound && noResults,
96
+ /* @__PURE__ */ jsx(StyledSearchResults, { children: searchResult?.results.map((image) => /* @__PURE__ */ jsx(ImageSearchResult, {
97
+ image,
98
+ onImageClick: (image$1) => setSelectedImage(image$1),
99
+ selectedImage,
100
+ onSelectImage,
101
+ showCheckbox: !!showCheckbox,
102
+ translations: translations.imagePreview,
103
+ locale
104
+ }, image.id)) }),
105
+ /* @__PURE__ */ jsxs(StyledPaginationRoot, {
106
+ page: page ?? 1,
107
+ onPageChange: (details) => searchImages({
108
+ ...queryObject,
109
+ page: details.page
110
+ }),
111
+ translations: translations.paginationTranslations,
112
+ count: searchResult?.totalCount ?? 0,
113
+ pageSize: searchResult?.pageSize,
114
+ hidden: noResultsFound,
115
+ children: [
116
+ /* @__PURE__ */ jsx(PaginationPrevTrigger, {
117
+ asChild: true,
118
+ children: /* @__PURE__ */ jsxs(StyledButton, {
119
+ variant: "tertiary",
120
+ "aria-label": translations.paginationTranslations?.prevTriggerLabel,
121
+ title: translations.paginationTranslations?.prevTriggerLabel,
122
+ children: [/* @__PURE__ */ jsx(ArrowLeftShortLine, {}), /* @__PURE__ */ jsx("span", { children: translations.paginationTranslations?.prevTriggerLabel })]
123
+ })
124
+ }),
125
+ /* @__PURE__ */ jsx(PaginationContext, { children: (pagination) => pagination.pages.map((page$1, index, full) => {
126
+ if (index === full.length - 1) return null;
127
+ return page$1.type === "page" ? /* @__PURE__ */ jsx(StyledPaginationItem, {
128
+ ...page$1,
129
+ asChild: true,
130
+ children: /* @__PURE__ */ jsx(Button, {
131
+ variant: page$1.value === pagination.page ? "primary" : "tertiary",
132
+ children: page$1.value
133
+ })
134
+ }, index) : /* @__PURE__ */ jsx(PaginationEllipsis, {
135
+ index,
136
+ asChild: true,
137
+ children: /* @__PURE__ */ jsx(Text, {
138
+ asChild: true,
139
+ consumeCss: true,
140
+ children: /* @__PURE__ */ jsx("div", { children: "…" })
141
+ })
142
+ }, index);
143
+ }) }),
144
+ /* @__PURE__ */ jsx(PaginationNextTrigger, {
145
+ asChild: true,
146
+ children: /* @__PURE__ */ jsxs(StyledButton, {
147
+ variant: "tertiary",
148
+ "aria-label": translations.paginationTranslations?.nextTriggerLabel,
149
+ title: translations.paginationTranslations?.nextTriggerLabel,
150
+ children: [/* @__PURE__ */ jsx("span", { children: translations.paginationTranslations?.nextTriggerLabel }), /* @__PURE__ */ jsx(ArrowRightShortLine, {})]
151
+ })
152
+ })
153
+ ]
154
+ })
155
+ ] });
156
+ };
157
+ var ImageSearch_default = ImageSearch;
158
+
159
+ //#endregion
160
+ export { ImageSearch_default };
161
+ //# sourceMappingURL=ImageSearch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageSearch.js","names":["image: IImageMetaInformationV3DTO | undefined","saveAsMetaImage?: boolean","queryObject: ISearchParamsDTO","queryObject","e: KeyboardEvent<HTMLInputElement | HTMLButtonElement>","image","page"],"sources":["../src/ImageSearch.tsx"],"sourcesContent":["/**\n * Copyright (c) 2017-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 ChangeEvent, type ReactNode, type KeyboardEvent, useEffect, useState } from \"react\";\nimport { ArrowLeftShortLine, ArrowRightShortLine, SearchLine } from \"@ndla/icons\";\nimport {\n Button,\n IconButton,\n Input,\n PaginationContext,\n PaginationEllipsis,\n PaginationItem,\n PaginationNextTrigger,\n PaginationPrevTrigger,\n PaginationRoot,\n Text,\n type PaginationRootProps,\n} from \"@ndla/primitives\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport type { IImageMetaInformationV3DTO, ISearchResultV3DTO, ISearchParamsDTO } from \"@ndla/types-backend/image-api\";\nimport ImageSearchResult from \"./ImageSearchResult\";\n\nconst ImageSearchWrapper = styled(\"div\", {\n base: {\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"small\",\n },\n});\n\nconst StyledSearchResults = styled(\"div\", {\n base: {\n display: \"grid\",\n gridTemplateColumns: \"repeat(auto-fit, minmax(200px, 1fr))\",\n gridGap: \"xsmall\",\n gridAutoFlow: \"dense\",\n },\n});\n\nconst InputWrapper = styled(\"div\", {\n base: {\n display: \"flex\",\n gap: \"xsmall\",\n },\n});\n\nconst StyledPaginationRoot = styled(PaginationRoot, {\n base: {\n flexWrap: \"wrap\",\n },\n});\n\nconst StyledButton = styled(Button, {\n base: {\n tabletWideDown: {\n paddingInline: \"xsmall\",\n \"& span\": {\n display: \"none\",\n },\n },\n },\n});\n\nconst StyledPaginationItem = styled(PaginationItem, {\n base: {\n tabletWideDown: {\n \"&:nth-child(2)\": {\n display: \"none\",\n },\n \"&:nth-last-child(2)\": {\n display: \"none\",\n },\n },\n },\n});\n\nexport interface PreviewTranslations {\n creatorsLabel: string;\n license: string;\n caption: string;\n altText: string;\n modelRelease: string;\n tags: string;\n close: string;\n checkboxLabel?: string;\n missingTitleFallback?: string;\n useImageTitle: string;\n}\n\nexport interface ImageSearchTranslations {\n searchPlaceholder: string;\n searchButtonTitle: string;\n imagePreview: PreviewTranslations;\n paginationTranslations: PaginationRootProps[\"translations\"];\n}\n\nexport interface ImageSearchProps {\n onImageSelect: (image: IImageMetaInformationV3DTO) => void;\n searchImages: (query: string | undefined, page: number | undefined) => Promise<ISearchResultV3DTO>;\n onError?: (err: any) => void;\n locale: string;\n noResults?: ReactNode;\n checkboxAction?: (image: IImageMetaInformationV3DTO) => void;\n showCheckbox?: boolean;\n translations: ImageSearchTranslations;\n}\n\nconst ImageSearch = ({\n onImageSelect,\n searchImages: search,\n onError,\n locale,\n noResults,\n checkboxAction,\n showCheckbox,\n translations,\n}: ImageSearchProps) => {\n const [queryObject, setQueryObject] = useState<ISearchParamsDTO>({\n query: undefined,\n page: 1,\n pageSize: 16,\n });\n const [selectedImage, setSelectedImage] = useState<IImageMetaInformationV3DTO | undefined>();\n const [searching, setSearching] = useState<boolean>(false);\n const [searchResult, setSearchResult] = useState<ISearchResultV3DTO | undefined>();\n\n const { page } = queryObject;\n const noResultsFound = !searching && searchResult?.results.length === 0;\n\n const onSelectImage = (image: IImageMetaInformationV3DTO | undefined, saveAsMetaImage?: boolean) => {\n setSelectedImage(undefined);\n if (!image) return;\n onImageSelect(image);\n if (saveAsMetaImage) {\n checkboxAction?.(image);\n }\n };\n\n const searchImages = (queryObject: ISearchParamsDTO) => {\n setSearching(true);\n search(queryObject.query, queryObject.page)\n .then((result) => {\n setQueryObject({\n query: queryObject.query,\n pageSize: result.pageSize,\n page: queryObject.page,\n });\n setSearchResult(result);\n setSearching(false);\n })\n .catch((err) => {\n onError?.(err);\n setSearching(false);\n });\n };\n\n const handleQueryChange = ({ target: { value } }: ChangeEvent<HTMLInputElement>) => {\n setQueryObject((prevState) => ({\n ...prevState,\n query: value,\n }));\n };\n\n const onEnter = (e: KeyboardEvent<HTMLInputElement | HTMLButtonElement>) => {\n if (e.key === \"Enter\") {\n searchImages(queryObject);\n }\n };\n\n useEffect(() => {\n searchImages(queryObject);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return (\n <ImageSearchWrapper>\n <InputWrapper role=\"search\">\n <Input\n type=\"search\"\n placeholder={translations.searchPlaceholder}\n value={queryObject?.query ?? \"\"}\n onChange={handleQueryChange}\n onKeyDown={onEnter}\n />\n <IconButton\n variant=\"primary\"\n aria-label={translations.searchButtonTitle}\n title={translations.searchButtonTitle}\n onKeyDown={onEnter}\n onClick={() => searchImages(queryObject)}\n >\n <SearchLine />\n </IconButton>\n </InputWrapper>\n {!!noResultsFound && noResults}\n <StyledSearchResults>\n {searchResult?.results.map((image) => (\n <ImageSearchResult\n key={image.id}\n image={image}\n onImageClick={(image) => setSelectedImage(image)}\n selectedImage={selectedImage}\n onSelectImage={onSelectImage}\n showCheckbox={!!showCheckbox}\n translations={translations.imagePreview}\n locale={locale}\n />\n ))}\n </StyledSearchResults>\n <StyledPaginationRoot\n page={page ?? 1}\n onPageChange={(details) => searchImages({ ...queryObject, page: details.page })}\n translations={translations.paginationTranslations}\n count={searchResult?.totalCount ?? 0}\n pageSize={searchResult?.pageSize}\n hidden={noResultsFound}\n >\n <PaginationPrevTrigger asChild>\n <StyledButton\n variant=\"tertiary\"\n aria-label={translations.paginationTranslations?.prevTriggerLabel}\n title={translations.paginationTranslations?.prevTriggerLabel}\n >\n <ArrowLeftShortLine />\n <span>{translations.paginationTranslations?.prevTriggerLabel}</span>\n </StyledButton>\n </PaginationPrevTrigger>\n <PaginationContext>\n {(pagination) =>\n pagination.pages.map((page, index, full) => {\n // Hide last page to not trigger RESULT_WINDOW_TOO_LARGE error\n if (index === full.length - 1) return null;\n return page.type === \"page\" ? (\n <StyledPaginationItem key={index} {...page} asChild>\n <Button variant={page.value === pagination.page ? \"primary\" : \"tertiary\"}>{page.value}</Button>\n </StyledPaginationItem>\n ) : (\n <PaginationEllipsis key={index} index={index} asChild>\n <Text asChild consumeCss>\n <div>&#8230;</div>\n </Text>\n </PaginationEllipsis>\n );\n })\n }\n </PaginationContext>\n <PaginationNextTrigger asChild>\n <StyledButton\n variant=\"tertiary\"\n aria-label={translations.paginationTranslations?.nextTriggerLabel}\n title={translations.paginationTranslations?.nextTriggerLabel}\n >\n <span>{translations.paginationTranslations?.nextTriggerLabel}</span>\n <ArrowRightShortLine />\n </StyledButton>\n </PaginationNextTrigger>\n </StyledPaginationRoot>\n </ImageSearchWrapper>\n );\n};\n\nexport default ImageSearch;\n"],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ import { getPreviewSrcSets } from "./util/imageUtil.js";
2
+ import { PreviewImage_default } from "./PreviewImage.js";
3
+ import { Button, Image, Text } from "@ndla/primitives";
4
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
+ import { styled } from "@ndla/styled-system/jsx";
6
+
7
+ //#region src/ImageSearchResult.tsx
8
+ const StyledButton = styled(Button, { base: {
9
+ display: "flex",
10
+ flexDirection: "column",
11
+ borderColor: "stroke.subtle"
12
+ } });
13
+ const StyledImage = styled(Image, { base: {
14
+ maxHeight: "135px",
15
+ width: "100%",
16
+ height: "100%"
17
+ } });
18
+ const StyledText = styled(Text, { base: { lineClamp: "3" } });
19
+ function ImageSearchResult({ image, onImageClick, selectedImage, onSelectImage, showCheckbox, translations, locale }) {
20
+ const isSelectedImage = selectedImage?.id === image.id;
21
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(StyledButton, {
22
+ variant: isSelectedImage ? "secondary" : "tertiary",
23
+ "data-testid": "select-image-from-list",
24
+ onClick: () => onImageClick(image),
25
+ "aria-expanded": isSelectedImage,
26
+ "aria-controls": `image-preview-${image.id}`,
27
+ children: [/* @__PURE__ */ jsx(StyledImage, {
28
+ alt: "",
29
+ srcSet: getPreviewSrcSets(image.image.imageUrl),
30
+ src: image.image.imageUrl,
31
+ variant: "rounded"
32
+ }), /* @__PURE__ */ jsx(StyledText, {
33
+ textStyle: "label.medium",
34
+ asChild: true,
35
+ consumeCss: true,
36
+ children: /* @__PURE__ */ jsx("span", { children: image.title.title.trim() ? image.title.title : translations.missingTitleFallback ?? `ID: ${image.id}` })
37
+ })]
38
+ }), isSelectedImage ? /* @__PURE__ */ jsx(PreviewImage_default, {
39
+ id: `image-preview-${image.id}`,
40
+ image: selectedImage,
41
+ onSelectImage,
42
+ translations,
43
+ showCheckbox,
44
+ locale
45
+ }) : null] });
46
+ }
47
+
48
+ //#endregion
49
+ export { ImageSearchResult };
50
+ //# sourceMappingURL=ImageSearchResult.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageSearchResult.js","names":["PreviewImage"],"sources":["../src/ImageSearchResult.tsx"],"sourcesContent":["/**\n * Copyright (c) 2017-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 { Text, Image, Button } from \"@ndla/primitives\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport type { IImageMetaInformationV3DTO } from \"@ndla/types-backend/image-api\";\nimport type { PreviewTranslations } from \"./ImageSearch\";\nimport PreviewImage from \"./PreviewImage\";\nimport { getPreviewSrcSets } from \"./util/imageUtil\";\n\nconst StyledButton = styled(Button, {\n base: {\n display: \"flex\",\n flexDirection: \"column\",\n borderColor: \"stroke.subtle\",\n },\n});\n\nconst StyledImage = styled(Image, {\n base: {\n maxHeight: \"135px\",\n width: \"100%\",\n height: \"100%\",\n },\n});\n\nconst StyledText = styled(Text, {\n base: {\n lineClamp: \"3\",\n },\n});\n\ninterface Props {\n image: IImageMetaInformationV3DTO;\n onImageClick: (image: IImageMetaInformationV3DTO) => void;\n selectedImage?: IImageMetaInformationV3DTO;\n onSelectImage: (image: IImageMetaInformationV3DTO | undefined, saveAsMetaImage?: boolean) => void;\n showCheckbox: boolean;\n translations: PreviewTranslations;\n locale: string;\n}\n\nexport default function ImageSearchResult({\n image,\n onImageClick,\n selectedImage,\n onSelectImage,\n showCheckbox,\n translations,\n locale,\n}: Props) {\n const isSelectedImage = selectedImage?.id === image.id;\n return (\n <>\n <StyledButton\n variant={isSelectedImage ? \"secondary\" : \"tertiary\"}\n data-testid=\"select-image-from-list\"\n onClick={() => onImageClick(image)}\n aria-expanded={isSelectedImage}\n aria-controls={`image-preview-${image.id}`}\n >\n <StyledImage\n alt=\"\"\n srcSet={getPreviewSrcSets(image.image.imageUrl)}\n src={image.image.imageUrl}\n variant=\"rounded\"\n />\n <StyledText textStyle=\"label.medium\" asChild consumeCss>\n <span>\n {image.title.title.trim() ? image.title.title : (translations.missingTitleFallback ?? `ID: ${image.id}`)}\n </span>\n </StyledText>\n </StyledButton>\n {isSelectedImage ? (\n <PreviewImage\n id={`image-preview-${image.id}`}\n image={selectedImage}\n onSelectImage={onSelectImage}\n translations={translations}\n showCheckbox={showCheckbox}\n locale={locale}\n />\n ) : null}\n </>\n );\n}\n"],"mappings":";;;;;;;AAeA,MAAM,eAAe,OAAO,QAAQ,EAClC,MAAM;CACJ,SAAS;CACT,eAAe;CACf,aAAa;AACd,EACF,EAAC;AAEF,MAAM,cAAc,OAAO,OAAO,EAChC,MAAM;CACJ,WAAW;CACX,OAAO;CACP,QAAQ;AACT,EACF,EAAC;AAEF,MAAM,aAAa,OAAO,MAAM,EAC9B,MAAM,EACJ,WAAW,IACZ,EACF,EAAC;AAYF,SAAwB,kBAAkB,EACxC,OACA,cACA,eACA,eACA,cACA,cACA,QACM,EAAE;CACR,MAAM,kBAAkB,eAAe,OAAO,MAAM;AACpD,wBACE,4CACE,KAAC;EACC,SAAS,kBAAkB,cAAc;EACzC,eAAY;EACZ,SAAS,MAAM,aAAa,MAAM;EAClC,iBAAe;EACf,kBAAgB,gBAAgB,MAAM,GAAG;6BAEzC,IAAC;GACC,KAAI;GACJ,QAAQ,kBAAkB,MAAM,MAAM,SAAS;GAC/C,KAAK,MAAM,MAAM;GACjB,SAAQ;IACR,kBACF,IAAC;GAAW,WAAU;GAAe;GAAQ;6BAC3C,IAAC,oBACE,MAAM,MAAM,MAAM,MAAM,GAAG,MAAM,MAAM,QAAS,aAAa,yBAAyB,MAAM,MAAM,GAAG,IACjG;IACI;GACA,EACd,kCACC,IAACA;EACC,KAAK,gBAAgB,MAAM,GAAG;EAC9B,OAAO;EACQ;EACD;EACA;EACN;GACR,GACA,QACH;AAEN"}
@@ -0,0 +1,125 @@
1
+ import { ImageMeta_default } from "./ImageMeta.js";
2
+ import { getSrcSets } from "./util/imageUtil.js";
3
+ import { useState } from "react";
4
+ import { Button, CheckboxControl, CheckboxHiddenInput, CheckboxIndicator, CheckboxLabel, CheckboxRoot, FieldRoot, IconButton, Image, Text } from "@ndla/primitives";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ import { CheckLine, CloseLine, HashTag } from "@ndla/icons";
7
+ import { styled } from "@ndla/styled-system/jsx";
8
+ import { getModelReleaseValue } from "@ndla/licenses";
9
+
10
+ //#region src/PreviewImage.tsx
11
+ const ImageContainer = styled("div", { base: { flexShrink: "0" } });
12
+ const StyledImage = styled(Image, { base: { maxHeight: "surface.xsmall" } });
13
+ const StyledImageMetadata = styled("div", { base: {
14
+ display: "flex",
15
+ flexDirection: "column",
16
+ gap: "xxsmall"
17
+ } });
18
+ const HashTagWrapper = styled("ul", { base: {
19
+ display: "flex",
20
+ gap: "xxsmall",
21
+ flexWrap: "wrap"
22
+ } });
23
+ const HashTagGroup = styled("div", { base: {
24
+ display: "flex",
25
+ gap: "xxsmall",
26
+ flexWrap: "wrap"
27
+ } });
28
+ const StyledTagItem = styled("li", { base: {
29
+ display: "flex",
30
+ alignItems: "center"
31
+ } });
32
+ const StyledPreview = styled("div", { base: {
33
+ display: "flex",
34
+ gridColumn: "1 / -1",
35
+ borderColor: "stroke.default",
36
+ border: "1px solid",
37
+ borderRadius: "xsmall",
38
+ gap: "small",
39
+ padding: "small",
40
+ flexWrap: "wrap",
41
+ overflow: "hidden"
42
+ } });
43
+ const StyledTopRow = styled("div", { base: {
44
+ display: "flex",
45
+ justifyContent: "space-between"
46
+ } });
47
+ const ContentWrapper = styled("div", { base: {
48
+ display: "flex",
49
+ flexDirection: "column",
50
+ gap: "medium",
51
+ flex: "2"
52
+ } });
53
+ const ActionsWrapper = styled("div", { base: {
54
+ display: "flex",
55
+ gap: "small",
56
+ marginBlockEnd: "medium"
57
+ } });
58
+ const StyledFieldRoot = styled(FieldRoot, { base: { alignSelf: "center" } });
59
+ const PreviewImage = ({ id, image, onSelectImage, showCheckbox, translations, locale }) => {
60
+ const [saveAsMetaImage, setSaveAsMetaImage] = useState(false);
61
+ return /* @__PURE__ */ jsxs(StyledPreview, {
62
+ id,
63
+ children: [/* @__PURE__ */ jsx(ImageContainer, { children: /* @__PURE__ */ jsx(StyledImage, {
64
+ alt: "",
65
+ srcSet: getSrcSets(image.image.imageUrl),
66
+ sizes: "(min-width: 800px) 360px, (min-width: 400px) 300px, 100vw",
67
+ src: image.image.imageUrl,
68
+ "aria-label": image.title.title,
69
+ variant: "rounded"
70
+ }) }), /* @__PURE__ */ jsxs(ContentWrapper, { children: [/* @__PURE__ */ jsxs(StyledImageMetadata, { children: [
71
+ /* @__PURE__ */ jsxs(StyledTopRow, { children: [/* @__PURE__ */ jsx(Text, {
72
+ textStyle: "title.medium",
73
+ children: image.title.title.trim() ? image.title.title : translations.missingTitleFallback ?? `ID: ${image.id}`
74
+ }), /* @__PURE__ */ jsx(IconButton, {
75
+ variant: "tertiary",
76
+ onClick: () => onSelectImage(void 0),
77
+ "aria-label": translations.close,
78
+ title: translations.close,
79
+ children: /* @__PURE__ */ jsx(CloseLine, {})
80
+ })] }),
81
+ !!image.copyright.creators.length && /* @__PURE__ */ jsxs(Text, { children: [/* @__PURE__ */ jsx("b", { children: `${translations.creatorsLabel}: ` }), image.copyright.creators.map((creator) => creator.name).join(", ")] }),
82
+ !!image.copyright.license.description?.trim() && /* @__PURE__ */ jsxs(Text, { children: [/* @__PURE__ */ jsx("b", { children: `${translations.license}: ` }), image.copyright.license.description] }),
83
+ !!image.caption.caption.trim() && /* @__PURE__ */ jsxs(Text, { children: [/* @__PURE__ */ jsx("b", { children: `${translations.caption}: ` }), image.caption.caption] }),
84
+ !!image.alttext.alttext.trim() && /* @__PURE__ */ jsxs(Text, { children: [
85
+ /* @__PURE__ */ jsx("b", { children: `${translations.altText}:` }),
86
+ " ",
87
+ image.alttext.alttext
88
+ ] }),
89
+ !!image.modelRelease.trim() && /* @__PURE__ */ jsxs(Text, { children: [/* @__PURE__ */ jsx("b", { children: `${translations.modelRelease}: ` }), getModelReleaseValue(image.modelRelease, locale)] }),
90
+ /* @__PURE__ */ jsx(ImageMeta_default, {
91
+ contentType: image.image.contentType,
92
+ fileSize: image.image.size,
93
+ imageDimensions: image.image.dimensions,
94
+ locale
95
+ }),
96
+ !!image.tags.tags.length && /* @__PURE__ */ jsxs(HashTagGroup, { children: [/* @__PURE__ */ jsx(Text, {
97
+ id: "tags-title",
98
+ children: /* @__PURE__ */ jsx("b", { children: `${translations.tags}: ` })
99
+ }), /* @__PURE__ */ jsx(HashTagWrapper, {
100
+ "aria-describedby": "tags-title",
101
+ children: image.tags.tags.map((tag) => /* @__PURE__ */ jsxs(StyledTagItem, { children: [/* @__PURE__ */ jsx(HashTag, { size: "small" }), tag] }, tag))
102
+ })] })
103
+ ] }), /* @__PURE__ */ jsxs(ActionsWrapper, { children: [/* @__PURE__ */ jsx(Button, {
104
+ "data-testid": "use-image",
105
+ onClick: () => onSelectImage(image, saveAsMetaImage),
106
+ children: translations.useImageTitle
107
+ }), !!showCheckbox && /* @__PURE__ */ jsx(StyledFieldRoot, { children: /* @__PURE__ */ jsxs(CheckboxRoot, {
108
+ checked: saveAsMetaImage,
109
+ onCheckedChange: () => setSaveAsMetaImage((prev) => !prev),
110
+ children: [
111
+ /* @__PURE__ */ jsx(CheckboxControl, { children: /* @__PURE__ */ jsx(CheckboxIndicator, {
112
+ asChild: true,
113
+ children: /* @__PURE__ */ jsx(CheckLine, {})
114
+ }) }),
115
+ /* @__PURE__ */ jsx(CheckboxLabel, { children: translations.checkboxLabel }),
116
+ /* @__PURE__ */ jsx(CheckboxHiddenInput, {})
117
+ ]
118
+ }) })] })] })]
119
+ });
120
+ };
121
+ var PreviewImage_default = PreviewImage;
122
+
123
+ //#endregion
124
+ export { PreviewImage_default };
125
+ //# sourceMappingURL=PreviewImage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PreviewImage.js","names":["ImageMeta"],"sources":["../src/PreviewImage.tsx"],"sourcesContent":["/**\n * Copyright (c) 2017-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 { useState } from \"react\";\nimport { CloseLine, HashTag, CheckLine } from \"@ndla/icons\";\nimport { getModelReleaseValue } from \"@ndla/licenses\";\nimport {\n Button,\n CheckboxControl,\n CheckboxHiddenInput,\n CheckboxIndicator,\n CheckboxLabel,\n CheckboxRoot,\n Text,\n Image,\n IconButton,\n FieldRoot,\n} from \"@ndla/primitives\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport type { IImageMetaInformationV3DTO } from \"@ndla/types-backend/image-api\";\nimport ImageMeta from \"./ImageMeta\";\nimport type { PreviewTranslations } from \"./ImageSearch\";\nimport { getSrcSets } from \"./util/imageUtil\";\n\nconst ImageContainer = styled(\"div\", {\n base: {\n flexShrink: \"0\",\n },\n});\n\nconst StyledImage = styled(Image, {\n base: {\n maxHeight: \"surface.xsmall\",\n },\n});\n\nconst StyledImageMetadata = styled(\"div\", {\n base: {\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"xxsmall\",\n },\n});\n\nconst HashTagWrapper = styled(\"ul\", {\n base: {\n display: \"flex\",\n gap: \"xxsmall\",\n flexWrap: \"wrap\",\n },\n});\n\nconst HashTagGroup = styled(\"div\", {\n base: {\n display: \"flex\",\n gap: \"xxsmall\",\n flexWrap: \"wrap\",\n },\n});\n\nconst StyledTagItem = styled(\"li\", {\n base: {\n display: \"flex\",\n alignItems: \"center\",\n },\n});\n\nconst StyledPreview = styled(\"div\", {\n base: {\n display: \"flex\",\n gridColumn: \"1 / -1\",\n borderColor: \"stroke.default\",\n border: \"1px solid\",\n borderRadius: \"xsmall\",\n gap: \"small\",\n padding: \"small\",\n flexWrap: \"wrap\",\n overflow: \"hidden\",\n },\n});\n\nconst StyledTopRow = styled(\"div\", {\n base: {\n display: \"flex\",\n justifyContent: \"space-between\",\n },\n});\n\nconst ContentWrapper = styled(\"div\", {\n base: {\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"medium\",\n flex: \"2\",\n },\n});\n\nconst ActionsWrapper = styled(\"div\", {\n base: {\n display: \"flex\",\n gap: \"small\",\n marginBlockEnd: \"medium\",\n },\n});\n\nconst StyledFieldRoot = styled(FieldRoot, {\n base: { alignSelf: \"center\" },\n});\n\ninterface Props {\n id: string;\n image: IImageMetaInformationV3DTO;\n onSelectImage: (image: IImageMetaInformationV3DTO | undefined, saveAsMetaImage?: boolean) => void;\n showCheckbox: boolean;\n translations: PreviewTranslations;\n locale: string;\n}\n\nconst PreviewImage = ({ id, image, onSelectImage, showCheckbox, translations, locale }: Props) => {\n const [saveAsMetaImage, setSaveAsMetaImage] = useState(false);\n\n return (\n <StyledPreview id={id}>\n <ImageContainer>\n <StyledImage\n alt=\"\"\n srcSet={getSrcSets(image.image.imageUrl)}\n sizes=\"(min-width: 800px) 360px, (min-width: 400px) 300px, 100vw\"\n src={image.image.imageUrl}\n aria-label={image.title.title}\n variant=\"rounded\"\n />\n </ImageContainer>\n <ContentWrapper>\n <StyledImageMetadata>\n <StyledTopRow>\n <Text textStyle=\"title.medium\">\n {image.title.title.trim() ? image.title.title : (translations.missingTitleFallback ?? `ID: ${image.id}`)}\n </Text>\n <IconButton\n variant=\"tertiary\"\n onClick={() => onSelectImage(undefined)}\n aria-label={translations.close}\n title={translations.close}\n >\n <CloseLine />\n </IconButton>\n </StyledTopRow>\n {!!image.copyright.creators.length && (\n <Text>\n <b>{`${translations.creatorsLabel}: `}</b>\n {image.copyright.creators.map((creator) => creator.name).join(\", \")}\n </Text>\n )}\n {!!image.copyright.license.description?.trim() && (\n <Text>\n <b>{`${translations.license}: `}</b>\n {image.copyright.license.description}\n </Text>\n )}\n {!!image.caption.caption.trim() && (\n <Text>\n <b>{`${translations.caption}: `}</b>\n {image.caption.caption}\n </Text>\n )}\n {!!image.alttext.alttext.trim() && (\n <Text>\n <b>{`${translations.altText}:`}</b> {image.alttext.alttext}\n </Text>\n )}\n {!!image.modelRelease.trim() && (\n <Text>\n <b>{`${translations.modelRelease}: `}</b>\n {getModelReleaseValue(image.modelRelease, locale)}\n </Text>\n )}\n <ImageMeta\n contentType={image.image.contentType}\n fileSize={image.image.size}\n imageDimensions={image.image.dimensions}\n locale={locale}\n />\n {!!image.tags.tags.length && (\n <HashTagGroup>\n <Text id=\"tags-title\">\n <b>{`${translations.tags}: `}</b>\n </Text>\n <HashTagWrapper aria-describedby=\"tags-title\">\n {image.tags.tags.map((tag) => (\n <StyledTagItem key={tag}>\n <HashTag size=\"small\" />\n {tag}\n </StyledTagItem>\n ))}\n </HashTagWrapper>\n </HashTagGroup>\n )}\n </StyledImageMetadata>\n <ActionsWrapper>\n <Button data-testid=\"use-image\" onClick={() => onSelectImage(image, saveAsMetaImage)}>\n {translations.useImageTitle}\n </Button>\n {!!showCheckbox && (\n <StyledFieldRoot>\n <CheckboxRoot checked={saveAsMetaImage} onCheckedChange={() => setSaveAsMetaImage((prev) => !prev)}>\n <CheckboxControl>\n <CheckboxIndicator asChild>\n <CheckLine />\n </CheckboxIndicator>\n </CheckboxControl>\n <CheckboxLabel>{translations.checkboxLabel}</CheckboxLabel>\n <CheckboxHiddenInput />\n </CheckboxRoot>\n </StyledFieldRoot>\n )}\n </ActionsWrapper>\n </ContentWrapper>\n </StyledPreview>\n );\n};\n\nexport default PreviewImage;\n"],"mappings":"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"}
package/es/index.js ADDED
@@ -0,0 +1,4 @@
1
+ import { ImageMeta_default } from "./ImageMeta.js";
2
+ import { ImageSearch_default } from "./ImageSearch.js";
3
+
4
+ export { ImageMeta_default as ImageMeta, ImageSearch_default as ImageSearch };
@@ -0,0 +1,27 @@
1
+ //#region src/util/imageUtil.ts
2
+ /**
3
+ * Copyright (c) 2017-present, NDLA.
4
+ *
5
+ * This source code is licensed under the GPLv3 license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ *
8
+ */
9
+ const getSrcSets = (imageUrl) => [
10
+ `${imageUrl}?width=1440 1440w`,
11
+ `${imageUrl}?width=1120 1120w`,
12
+ `${imageUrl}?width=1000 1000w`,
13
+ `${imageUrl}?width=960 960w`,
14
+ `${imageUrl}?width=800 800w`,
15
+ `${imageUrl}?width=640 640w`,
16
+ `${imageUrl}?width=480 480w`,
17
+ `${imageUrl}?width=320 320w`
18
+ ].join(", ");
19
+ const getPreviewSrcSets = (imageUrl) => [
20
+ `${imageUrl}?width=480 3x`,
21
+ `${imageUrl}?width=320 2x`,
22
+ `${imageUrl}?width=160 1x`
23
+ ].join(", ");
24
+
25
+ //#endregion
26
+ export { getPreviewSrcSets, getSrcSets };
27
+ //# sourceMappingURL=imageUtil.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imageUtil.js","names":["imageUrl: string"],"sources":["../../src/util/imageUtil.ts"],"sourcesContent":["/**\n * Copyright (c) 2017-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\n/* - Source sets for gallery search is retina-display optimized\n - Based on: https://www.smashingmagazine.com/2014/05/responsive-images-done-right-guide-picture-srcset/ */\n\nexport const getSrcSets = (imageUrl: string) =>\n [\n `${imageUrl}?width=1440 1440w`,\n `${imageUrl}?width=1120 1120w`,\n `${imageUrl}?width=1000 1000w`,\n `${imageUrl}?width=960 960w`,\n `${imageUrl}?width=800 800w`,\n `${imageUrl}?width=640 640w`,\n `${imageUrl}?width=480 480w`,\n `${imageUrl}?width=320 320w`,\n ].join(\", \");\n\nexport const getPreviewSrcSets = (imageUrl: string) =>\n [`${imageUrl}?width=480 3x`, `${imageUrl}?width=320 2x`, `${imageUrl}?width=160 1x`].join(\", \");\n"],"mappings":";;;;;;;;AAWA,MAAa,aAAa,CAACA,aACzB;EACG,EAAE,SAAS;EACX,EAAE,SAAS;EACX,EAAE,SAAS;EACX,EAAE,SAAS;EACX,EAAE,SAAS;EACX,EAAE,SAAS;EACX,EAAE,SAAS;EACX,EAAE,SAAS;AACb,EAAC,KAAK,KAAK;AAEd,MAAa,oBAAoB,CAACA,aAChC;EAAE,EAAE,SAAS;EAAiB,EAAE,SAAS;EAAiB,EAAE,SAAS;AAAe,EAAC,KAAK,KAAK"}
@@ -0,0 +1,19 @@
1
+ const require_rolldown_runtime = require('./_virtual/rolldown_runtime.js');
2
+ const react = require_rolldown_runtime.__toESM(require("react"));
3
+ const __ndla_primitives = require_rolldown_runtime.__toESM(require("@ndla/primitives"));
4
+ const __ndla_util = require_rolldown_runtime.__toESM(require("@ndla/util"));
5
+ const react_jsx_runtime = require_rolldown_runtime.__toESM(require("react/jsx-runtime"));
6
+
7
+ //#region src/ImageMeta.tsx
8
+ const ImageMeta = ({ contentType, fileSize, imageDimensions, locale }) => {
9
+ const prettySize = (0, react.useMemo)(() => {
10
+ return (0, __ndla_util.humanFileSize)(fileSize, locale);
11
+ }, [fileSize, locale]);
12
+ const dimensions = imageDimensions ? ` - ${imageDimensions.width}x${imageDimensions.height} px` : "";
13
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__ndla_primitives.Text, { children: `${contentType} - ${prettySize}${dimensions}` });
14
+ };
15
+ var ImageMeta_default = ImageMeta;
16
+
17
+ //#endregion
18
+ exports.ImageMeta_default = ImageMeta_default;
19
+ //# sourceMappingURL=ImageMeta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageMeta.js","names":["Text"],"sources":["../src/ImageMeta.tsx"],"sourcesContent":["/**\n * Copyright (c) 2022-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 { Text } from \"@ndla/primitives\";\nimport type { IImageDimensionsDTO } from \"@ndla/types-backend/image-api\";\nimport { humanFileSize } from \"@ndla/util\";\n\ninterface Props {\n contentType: string;\n fileSize: number;\n imageDimensions?: IImageDimensionsDTO;\n locale: string;\n}\n\nconst ImageMeta = ({ contentType, fileSize, imageDimensions, locale }: Props) => {\n const prettySize = useMemo(() => {\n return humanFileSize(fileSize, locale);\n }, [fileSize, locale]);\n\n const dimensions = imageDimensions ? ` - ${imageDimensions.width}x${imageDimensions.height} px` : \"\";\n return <Text>{`${contentType} - ${prettySize}${dimensions}`}</Text>;\n};\n\nexport default ImageMeta;\n"],"mappings":";;;;;;;AAoBA,MAAM,YAAY,CAAC,EAAE,aAAa,UAAU,iBAAiB,QAAe,KAAK;CAC/E,MAAM,aAAa,mBAAQ,MAAM;AAC/B,SAAO,+BAAc,UAAU,OAAO;CACvC,GAAE,CAAC,UAAU,MAAO,EAAC;CAEtB,MAAM,aAAa,mBAAmB,KAAK,gBAAgB,MAAM,GAAG,gBAAgB,OAAO,OAAO;AAClG,wBAAO,2BAACA,qCAAO,EAAE,YAAY,KAAK,WAAW,EAAE,WAAW,IAAS;AACpE;AAED,wBAAe"}
@@ -0,0 +1,162 @@
1
+ const require_rolldown_runtime = require('./_virtual/rolldown_runtime.js');
2
+ const require_ImageSearchResult = require('./ImageSearchResult.js');
3
+ const react = require_rolldown_runtime.__toESM(require("react"));
4
+ const __ndla_primitives = require_rolldown_runtime.__toESM(require("@ndla/primitives"));
5
+ const react_jsx_runtime = require_rolldown_runtime.__toESM(require("react/jsx-runtime"));
6
+ const __ndla_icons = require_rolldown_runtime.__toESM(require("@ndla/icons"));
7
+ const __ndla_styled_system_jsx = require_rolldown_runtime.__toESM(require("@ndla/styled-system/jsx"));
8
+
9
+ //#region src/ImageSearch.tsx
10
+ const ImageSearchWrapper = (0, __ndla_styled_system_jsx.styled)("div", { base: {
11
+ display: "flex",
12
+ flexDirection: "column",
13
+ gap: "small"
14
+ } });
15
+ const StyledSearchResults = (0, __ndla_styled_system_jsx.styled)("div", { base: {
16
+ display: "grid",
17
+ gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))",
18
+ gridGap: "xsmall",
19
+ gridAutoFlow: "dense"
20
+ } });
21
+ const InputWrapper = (0, __ndla_styled_system_jsx.styled)("div", { base: {
22
+ display: "flex",
23
+ gap: "xsmall"
24
+ } });
25
+ const StyledPaginationRoot = (0, __ndla_styled_system_jsx.styled)(__ndla_primitives.PaginationRoot, { base: { flexWrap: "wrap" } });
26
+ const StyledButton = (0, __ndla_styled_system_jsx.styled)(__ndla_primitives.Button, { base: { tabletWideDown: {
27
+ paddingInline: "xsmall",
28
+ "& span": { display: "none" }
29
+ } } });
30
+ const StyledPaginationItem = (0, __ndla_styled_system_jsx.styled)(__ndla_primitives.PaginationItem, { base: { tabletWideDown: {
31
+ "&:nth-child(2)": { display: "none" },
32
+ "&:nth-last-child(2)": { display: "none" }
33
+ } } });
34
+ const ImageSearch = ({ onImageSelect, searchImages: search, onError, locale, noResults, checkboxAction, showCheckbox, translations }) => {
35
+ const [queryObject, setQueryObject] = (0, react.useState)({
36
+ query: void 0,
37
+ page: 1,
38
+ pageSize: 16
39
+ });
40
+ const [selectedImage, setSelectedImage] = (0, react.useState)();
41
+ const [searching, setSearching] = (0, react.useState)(false);
42
+ const [searchResult, setSearchResult] = (0, react.useState)();
43
+ const { page } = queryObject;
44
+ const noResultsFound = !searching && searchResult?.results.length === 0;
45
+ const onSelectImage = (image, saveAsMetaImage) => {
46
+ setSelectedImage(void 0);
47
+ if (!image) return;
48
+ onImageSelect(image);
49
+ if (saveAsMetaImage) checkboxAction?.(image);
50
+ };
51
+ const searchImages = (queryObject$1) => {
52
+ setSearching(true);
53
+ search(queryObject$1.query, queryObject$1.page).then((result) => {
54
+ setQueryObject({
55
+ query: queryObject$1.query,
56
+ pageSize: result.pageSize,
57
+ page: queryObject$1.page
58
+ });
59
+ setSearchResult(result);
60
+ setSearching(false);
61
+ }).catch((err) => {
62
+ onError?.(err);
63
+ setSearching(false);
64
+ });
65
+ };
66
+ const handleQueryChange = ({ target: { value } }) => {
67
+ setQueryObject((prevState) => ({
68
+ ...prevState,
69
+ query: value
70
+ }));
71
+ };
72
+ const onEnter = (e) => {
73
+ if (e.key === "Enter") searchImages(queryObject);
74
+ };
75
+ (0, react.useEffect)(() => {
76
+ searchImages(queryObject);
77
+ }, []);
78
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ImageSearchWrapper, { children: [
79
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(InputWrapper, {
80
+ role: "search",
81
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__ndla_primitives.Input, {
82
+ type: "search",
83
+ placeholder: translations.searchPlaceholder,
84
+ value: queryObject?.query ?? "",
85
+ onChange: handleQueryChange,
86
+ onKeyDown: onEnter
87
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__ndla_primitives.IconButton, {
88
+ variant: "primary",
89
+ "aria-label": translations.searchButtonTitle,
90
+ title: translations.searchButtonTitle,
91
+ onKeyDown: onEnter,
92
+ onClick: () => searchImages(queryObject),
93
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__ndla_icons.SearchLine, {})
94
+ })]
95
+ }),
96
+ !!noResultsFound && noResults,
97
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StyledSearchResults, { children: searchResult?.results.map((image) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_ImageSearchResult.ImageSearchResult, {
98
+ image,
99
+ onImageClick: (image$1) => setSelectedImage(image$1),
100
+ selectedImage,
101
+ onSelectImage,
102
+ showCheckbox: !!showCheckbox,
103
+ translations: translations.imagePreview,
104
+ locale
105
+ }, image.id)) }),
106
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(StyledPaginationRoot, {
107
+ page: page ?? 1,
108
+ onPageChange: (details) => searchImages({
109
+ ...queryObject,
110
+ page: details.page
111
+ }),
112
+ translations: translations.paginationTranslations,
113
+ count: searchResult?.totalCount ?? 0,
114
+ pageSize: searchResult?.pageSize,
115
+ hidden: noResultsFound,
116
+ children: [
117
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__ndla_primitives.PaginationPrevTrigger, {
118
+ asChild: true,
119
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(StyledButton, {
120
+ variant: "tertiary",
121
+ "aria-label": translations.paginationTranslations?.prevTriggerLabel,
122
+ title: translations.paginationTranslations?.prevTriggerLabel,
123
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__ndla_icons.ArrowLeftShortLine, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: translations.paginationTranslations?.prevTriggerLabel })]
124
+ })
125
+ }),
126
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__ndla_primitives.PaginationContext, { children: (pagination) => pagination.pages.map((page$1, index, full) => {
127
+ if (index === full.length - 1) return null;
128
+ return page$1.type === "page" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StyledPaginationItem, {
129
+ ...page$1,
130
+ asChild: true,
131
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__ndla_primitives.Button, {
132
+ variant: page$1.value === pagination.page ? "primary" : "tertiary",
133
+ children: page$1.value
134
+ })
135
+ }, index) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__ndla_primitives.PaginationEllipsis, {
136
+ index,
137
+ asChild: true,
138
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__ndla_primitives.Text, {
139
+ asChild: true,
140
+ consumeCss: true,
141
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: "…" })
142
+ })
143
+ }, index);
144
+ }) }),
145
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__ndla_primitives.PaginationNextTrigger, {
146
+ asChild: true,
147
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(StyledButton, {
148
+ variant: "tertiary",
149
+ "aria-label": translations.paginationTranslations?.nextTriggerLabel,
150
+ title: translations.paginationTranslations?.nextTriggerLabel,
151
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: translations.paginationTranslations?.nextTriggerLabel }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__ndla_icons.ArrowRightShortLine, {})]
152
+ })
153
+ })
154
+ ]
155
+ })
156
+ ] });
157
+ };
158
+ var ImageSearch_default = ImageSearch;
159
+
160
+ //#endregion
161
+ exports.ImageSearch_default = ImageSearch_default;
162
+ //# sourceMappingURL=ImageSearch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageSearch.js","names":["PaginationRoot","Button","PaginationItem","image: IImageMetaInformationV3DTO | undefined","saveAsMetaImage?: boolean","queryObject: ISearchParamsDTO","queryObject","e: KeyboardEvent<HTMLInputElement | HTMLButtonElement>","Input","IconButton","SearchLine","ImageSearchResult","image","PaginationPrevTrigger","ArrowLeftShortLine","PaginationContext","page","PaginationEllipsis","Text","PaginationNextTrigger","ArrowRightShortLine"],"sources":["../src/ImageSearch.tsx"],"sourcesContent":["/**\n * Copyright (c) 2017-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 ChangeEvent, type ReactNode, type KeyboardEvent, useEffect, useState } from \"react\";\nimport { ArrowLeftShortLine, ArrowRightShortLine, SearchLine } from \"@ndla/icons\";\nimport {\n Button,\n IconButton,\n Input,\n PaginationContext,\n PaginationEllipsis,\n PaginationItem,\n PaginationNextTrigger,\n PaginationPrevTrigger,\n PaginationRoot,\n Text,\n type PaginationRootProps,\n} from \"@ndla/primitives\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport type { IImageMetaInformationV3DTO, ISearchResultV3DTO, ISearchParamsDTO } from \"@ndla/types-backend/image-api\";\nimport ImageSearchResult from \"./ImageSearchResult\";\n\nconst ImageSearchWrapper = styled(\"div\", {\n base: {\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"small\",\n },\n});\n\nconst StyledSearchResults = styled(\"div\", {\n base: {\n display: \"grid\",\n gridTemplateColumns: \"repeat(auto-fit, minmax(200px, 1fr))\",\n gridGap: \"xsmall\",\n gridAutoFlow: \"dense\",\n },\n});\n\nconst InputWrapper = styled(\"div\", {\n base: {\n display: \"flex\",\n gap: \"xsmall\",\n },\n});\n\nconst StyledPaginationRoot = styled(PaginationRoot, {\n base: {\n flexWrap: \"wrap\",\n },\n});\n\nconst StyledButton = styled(Button, {\n base: {\n tabletWideDown: {\n paddingInline: \"xsmall\",\n \"& span\": {\n display: \"none\",\n },\n },\n },\n});\n\nconst StyledPaginationItem = styled(PaginationItem, {\n base: {\n tabletWideDown: {\n \"&:nth-child(2)\": {\n display: \"none\",\n },\n \"&:nth-last-child(2)\": {\n display: \"none\",\n },\n },\n },\n});\n\nexport interface PreviewTranslations {\n creatorsLabel: string;\n license: string;\n caption: string;\n altText: string;\n modelRelease: string;\n tags: string;\n close: string;\n checkboxLabel?: string;\n missingTitleFallback?: string;\n useImageTitle: string;\n}\n\nexport interface ImageSearchTranslations {\n searchPlaceholder: string;\n searchButtonTitle: string;\n imagePreview: PreviewTranslations;\n paginationTranslations: PaginationRootProps[\"translations\"];\n}\n\nexport interface ImageSearchProps {\n onImageSelect: (image: IImageMetaInformationV3DTO) => void;\n searchImages: (query: string | undefined, page: number | undefined) => Promise<ISearchResultV3DTO>;\n onError?: (err: any) => void;\n locale: string;\n noResults?: ReactNode;\n checkboxAction?: (image: IImageMetaInformationV3DTO) => void;\n showCheckbox?: boolean;\n translations: ImageSearchTranslations;\n}\n\nconst ImageSearch = ({\n onImageSelect,\n searchImages: search,\n onError,\n locale,\n noResults,\n checkboxAction,\n showCheckbox,\n translations,\n}: ImageSearchProps) => {\n const [queryObject, setQueryObject] = useState<ISearchParamsDTO>({\n query: undefined,\n page: 1,\n pageSize: 16,\n });\n const [selectedImage, setSelectedImage] = useState<IImageMetaInformationV3DTO | undefined>();\n const [searching, setSearching] = useState<boolean>(false);\n const [searchResult, setSearchResult] = useState<ISearchResultV3DTO | undefined>();\n\n const { page } = queryObject;\n const noResultsFound = !searching && searchResult?.results.length === 0;\n\n const onSelectImage = (image: IImageMetaInformationV3DTO | undefined, saveAsMetaImage?: boolean) => {\n setSelectedImage(undefined);\n if (!image) return;\n onImageSelect(image);\n if (saveAsMetaImage) {\n checkboxAction?.(image);\n }\n };\n\n const searchImages = (queryObject: ISearchParamsDTO) => {\n setSearching(true);\n search(queryObject.query, queryObject.page)\n .then((result) => {\n setQueryObject({\n query: queryObject.query,\n pageSize: result.pageSize,\n page: queryObject.page,\n });\n setSearchResult(result);\n setSearching(false);\n })\n .catch((err) => {\n onError?.(err);\n setSearching(false);\n });\n };\n\n const handleQueryChange = ({ target: { value } }: ChangeEvent<HTMLInputElement>) => {\n setQueryObject((prevState) => ({\n ...prevState,\n query: value,\n }));\n };\n\n const onEnter = (e: KeyboardEvent<HTMLInputElement | HTMLButtonElement>) => {\n if (e.key === \"Enter\") {\n searchImages(queryObject);\n }\n };\n\n useEffect(() => {\n searchImages(queryObject);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return (\n <ImageSearchWrapper>\n <InputWrapper role=\"search\">\n <Input\n type=\"search\"\n placeholder={translations.searchPlaceholder}\n value={queryObject?.query ?? \"\"}\n onChange={handleQueryChange}\n onKeyDown={onEnter}\n />\n <IconButton\n variant=\"primary\"\n aria-label={translations.searchButtonTitle}\n title={translations.searchButtonTitle}\n onKeyDown={onEnter}\n onClick={() => searchImages(queryObject)}\n >\n <SearchLine />\n </IconButton>\n </InputWrapper>\n {!!noResultsFound && noResults}\n <StyledSearchResults>\n {searchResult?.results.map((image) => (\n <ImageSearchResult\n key={image.id}\n image={image}\n onImageClick={(image) => setSelectedImage(image)}\n selectedImage={selectedImage}\n onSelectImage={onSelectImage}\n showCheckbox={!!showCheckbox}\n translations={translations.imagePreview}\n locale={locale}\n />\n ))}\n </StyledSearchResults>\n <StyledPaginationRoot\n page={page ?? 1}\n onPageChange={(details) => searchImages({ ...queryObject, page: details.page })}\n translations={translations.paginationTranslations}\n count={searchResult?.totalCount ?? 0}\n pageSize={searchResult?.pageSize}\n hidden={noResultsFound}\n >\n <PaginationPrevTrigger asChild>\n <StyledButton\n variant=\"tertiary\"\n aria-label={translations.paginationTranslations?.prevTriggerLabel}\n title={translations.paginationTranslations?.prevTriggerLabel}\n >\n <ArrowLeftShortLine />\n <span>{translations.paginationTranslations?.prevTriggerLabel}</span>\n </StyledButton>\n </PaginationPrevTrigger>\n <PaginationContext>\n {(pagination) =>\n pagination.pages.map((page, index, full) => {\n // Hide last page to not trigger RESULT_WINDOW_TOO_LARGE error\n if (index === full.length - 1) return null;\n return page.type === \"page\" ? (\n <StyledPaginationItem key={index} {...page} asChild>\n <Button variant={page.value === pagination.page ? \"primary\" : \"tertiary\"}>{page.value}</Button>\n </StyledPaginationItem>\n ) : (\n <PaginationEllipsis key={index} index={index} asChild>\n <Text asChild consumeCss>\n <div>&#8230;</div>\n </Text>\n </PaginationEllipsis>\n );\n })\n }\n </PaginationContext>\n <PaginationNextTrigger asChild>\n <StyledButton\n variant=\"tertiary\"\n aria-label={translations.paginationTranslations?.nextTriggerLabel}\n title={translations.paginationTranslations?.nextTriggerLabel}\n >\n <span>{translations.paginationTranslations?.nextTriggerLabel}</span>\n <ArrowRightShortLine />\n </StyledButton>\n </PaginationNextTrigger>\n </StyledPaginationRoot>\n </ImageSearchWrapper>\n );\n};\n\nexport default ImageSearch;\n"],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ const require_rolldown_runtime = require('./_virtual/rolldown_runtime.js');
2
+ const require_imageUtil = require('./util/imageUtil.js');
3
+ const require_PreviewImage = require('./PreviewImage.js');
4
+ const __ndla_primitives = require_rolldown_runtime.__toESM(require("@ndla/primitives"));
5
+ const react_jsx_runtime = require_rolldown_runtime.__toESM(require("react/jsx-runtime"));
6
+ const __ndla_styled_system_jsx = require_rolldown_runtime.__toESM(require("@ndla/styled-system/jsx"));
7
+
8
+ //#region src/ImageSearchResult.tsx
9
+ const StyledButton = (0, __ndla_styled_system_jsx.styled)(__ndla_primitives.Button, { base: {
10
+ display: "flex",
11
+ flexDirection: "column",
12
+ borderColor: "stroke.subtle"
13
+ } });
14
+ const StyledImage = (0, __ndla_styled_system_jsx.styled)(__ndla_primitives.Image, { base: {
15
+ maxHeight: "135px",
16
+ width: "100%",
17
+ height: "100%"
18
+ } });
19
+ const StyledText = (0, __ndla_styled_system_jsx.styled)(__ndla_primitives.Text, { base: { lineClamp: "3" } });
20
+ function ImageSearchResult({ image, onImageClick, selectedImage, onSelectImage, showCheckbox, translations, locale }) {
21
+ const isSelectedImage = selectedImage?.id === image.id;
22
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(StyledButton, {
23
+ variant: isSelectedImage ? "secondary" : "tertiary",
24
+ "data-testid": "select-image-from-list",
25
+ onClick: () => onImageClick(image),
26
+ "aria-expanded": isSelectedImage,
27
+ "aria-controls": `image-preview-${image.id}`,
28
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StyledImage, {
29
+ alt: "",
30
+ srcSet: require_imageUtil.getPreviewSrcSets(image.image.imageUrl),
31
+ src: image.image.imageUrl,
32
+ variant: "rounded"
33
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StyledText, {
34
+ textStyle: "label.medium",
35
+ asChild: true,
36
+ consumeCss: true,
37
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: image.title.title.trim() ? image.title.title : translations.missingTitleFallback ?? `ID: ${image.id}` })
38
+ })]
39
+ }), isSelectedImage ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_PreviewImage.PreviewImage_default, {
40
+ id: `image-preview-${image.id}`,
41
+ image: selectedImage,
42
+ onSelectImage,
43
+ translations,
44
+ showCheckbox,
45
+ locale
46
+ }) : null] });
47
+ }
48
+
49
+ //#endregion
50
+ exports.ImageSearchResult = ImageSearchResult;
51
+ //# sourceMappingURL=ImageSearchResult.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageSearchResult.js","names":["Button","Image","Text","PreviewImage"],"sources":["../src/ImageSearchResult.tsx"],"sourcesContent":["/**\n * Copyright (c) 2017-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 { Text, Image, Button } from \"@ndla/primitives\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport type { IImageMetaInformationV3DTO } from \"@ndla/types-backend/image-api\";\nimport type { PreviewTranslations } from \"./ImageSearch\";\nimport PreviewImage from \"./PreviewImage\";\nimport { getPreviewSrcSets } from \"./util/imageUtil\";\n\nconst StyledButton = styled(Button, {\n base: {\n display: \"flex\",\n flexDirection: \"column\",\n borderColor: \"stroke.subtle\",\n },\n});\n\nconst StyledImage = styled(Image, {\n base: {\n maxHeight: \"135px\",\n width: \"100%\",\n height: \"100%\",\n },\n});\n\nconst StyledText = styled(Text, {\n base: {\n lineClamp: \"3\",\n },\n});\n\ninterface Props {\n image: IImageMetaInformationV3DTO;\n onImageClick: (image: IImageMetaInformationV3DTO) => void;\n selectedImage?: IImageMetaInformationV3DTO;\n onSelectImage: (image: IImageMetaInformationV3DTO | undefined, saveAsMetaImage?: boolean) => void;\n showCheckbox: boolean;\n translations: PreviewTranslations;\n locale: string;\n}\n\nexport default function ImageSearchResult({\n image,\n onImageClick,\n selectedImage,\n onSelectImage,\n showCheckbox,\n translations,\n locale,\n}: Props) {\n const isSelectedImage = selectedImage?.id === image.id;\n return (\n <>\n <StyledButton\n variant={isSelectedImage ? \"secondary\" : \"tertiary\"}\n data-testid=\"select-image-from-list\"\n onClick={() => onImageClick(image)}\n aria-expanded={isSelectedImage}\n aria-controls={`image-preview-${image.id}`}\n >\n <StyledImage\n alt=\"\"\n srcSet={getPreviewSrcSets(image.image.imageUrl)}\n src={image.image.imageUrl}\n variant=\"rounded\"\n />\n <StyledText textStyle=\"label.medium\" asChild consumeCss>\n <span>\n {image.title.title.trim() ? image.title.title : (translations.missingTitleFallback ?? `ID: ${image.id}`)}\n </span>\n </StyledText>\n </StyledButton>\n {isSelectedImage ? (\n <PreviewImage\n id={`image-preview-${image.id}`}\n image={selectedImage}\n onSelectImage={onSelectImage}\n translations={translations}\n showCheckbox={showCheckbox}\n locale={locale}\n />\n ) : null}\n </>\n );\n}\n"],"mappings":";;;;;;;;AAeA,MAAM,eAAe,qCAAOA,0BAAQ,EAClC,MAAM;CACJ,SAAS;CACT,eAAe;CACf,aAAa;AACd,EACF,EAAC;AAEF,MAAM,cAAc,qCAAOC,yBAAO,EAChC,MAAM;CACJ,WAAW;CACX,OAAO;CACP,QAAQ;AACT,EACF,EAAC;AAEF,MAAM,aAAa,qCAAOC,wBAAM,EAC9B,MAAM,EACJ,WAAW,IACZ,EACF,EAAC;AAYF,SAAwB,kBAAkB,EACxC,OACA,cACA,eACA,eACA,cACA,cACA,QACM,EAAE;CACR,MAAM,kBAAkB,eAAe,OAAO,MAAM;AACpD,wBACE,qFACE,4BAAC;EACC,SAAS,kBAAkB,cAAc;EACzC,eAAY;EACZ,SAAS,MAAM,aAAa,MAAM;EAClC,iBAAe;EACf,kBAAgB,gBAAgB,MAAM,GAAG;6BAEzC,2BAAC;GACC,KAAI;GACJ,QAAQ,oCAAkB,MAAM,MAAM,SAAS;GAC/C,KAAK,MAAM,MAAM;GACjB,SAAQ;IACR,kBACF,2BAAC;GAAW,WAAU;GAAe;GAAQ;6BAC3C,2BAAC,oBACE,MAAM,MAAM,MAAM,MAAM,GAAG,MAAM,MAAM,QAAS,aAAa,yBAAyB,MAAM,MAAM,GAAG,IACjG;IACI;GACA,EACd,kCACC,2BAACC;EACC,KAAK,gBAAgB,MAAM,GAAG;EAC9B,OAAO;EACQ;EACD;EACA;EACN;GACR,GACA,QACH;AAEN"}
@@ -0,0 +1,126 @@
1
+ const require_rolldown_runtime = require('./_virtual/rolldown_runtime.js');
2
+ const require_ImageMeta = require('./ImageMeta.js');
3
+ const require_imageUtil = require('./util/imageUtil.js');
4
+ const react = require_rolldown_runtime.__toESM(require("react"));
5
+ const __ndla_primitives = require_rolldown_runtime.__toESM(require("@ndla/primitives"));
6
+ const react_jsx_runtime = require_rolldown_runtime.__toESM(require("react/jsx-runtime"));
7
+ const __ndla_icons = require_rolldown_runtime.__toESM(require("@ndla/icons"));
8
+ const __ndla_styled_system_jsx = require_rolldown_runtime.__toESM(require("@ndla/styled-system/jsx"));
9
+ const __ndla_licenses = require_rolldown_runtime.__toESM(require("@ndla/licenses"));
10
+
11
+ //#region src/PreviewImage.tsx
12
+ const ImageContainer = (0, __ndla_styled_system_jsx.styled)("div", { base: { flexShrink: "0" } });
13
+ const StyledImage = (0, __ndla_styled_system_jsx.styled)(__ndla_primitives.Image, { base: { maxHeight: "surface.xsmall" } });
14
+ const StyledImageMetadata = (0, __ndla_styled_system_jsx.styled)("div", { base: {
15
+ display: "flex",
16
+ flexDirection: "column",
17
+ gap: "xxsmall"
18
+ } });
19
+ const HashTagWrapper = (0, __ndla_styled_system_jsx.styled)("ul", { base: {
20
+ display: "flex",
21
+ gap: "xxsmall",
22
+ flexWrap: "wrap"
23
+ } });
24
+ const HashTagGroup = (0, __ndla_styled_system_jsx.styled)("div", { base: {
25
+ display: "flex",
26
+ gap: "xxsmall",
27
+ flexWrap: "wrap"
28
+ } });
29
+ const StyledTagItem = (0, __ndla_styled_system_jsx.styled)("li", { base: {
30
+ display: "flex",
31
+ alignItems: "center"
32
+ } });
33
+ const StyledPreview = (0, __ndla_styled_system_jsx.styled)("div", { base: {
34
+ display: "flex",
35
+ gridColumn: "1 / -1",
36
+ borderColor: "stroke.default",
37
+ border: "1px solid",
38
+ borderRadius: "xsmall",
39
+ gap: "small",
40
+ padding: "small",
41
+ flexWrap: "wrap",
42
+ overflow: "hidden"
43
+ } });
44
+ const StyledTopRow = (0, __ndla_styled_system_jsx.styled)("div", { base: {
45
+ display: "flex",
46
+ justifyContent: "space-between"
47
+ } });
48
+ const ContentWrapper = (0, __ndla_styled_system_jsx.styled)("div", { base: {
49
+ display: "flex",
50
+ flexDirection: "column",
51
+ gap: "medium",
52
+ flex: "2"
53
+ } });
54
+ const ActionsWrapper = (0, __ndla_styled_system_jsx.styled)("div", { base: {
55
+ display: "flex",
56
+ gap: "small",
57
+ marginBlockEnd: "medium"
58
+ } });
59
+ const StyledFieldRoot = (0, __ndla_styled_system_jsx.styled)(__ndla_primitives.FieldRoot, { base: { alignSelf: "center" } });
60
+ const PreviewImage = ({ id, image, onSelectImage, showCheckbox, translations, locale }) => {
61
+ const [saveAsMetaImage, setSaveAsMetaImage] = (0, react.useState)(false);
62
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(StyledPreview, {
63
+ id,
64
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImageContainer, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StyledImage, {
65
+ alt: "",
66
+ srcSet: require_imageUtil.getSrcSets(image.image.imageUrl),
67
+ sizes: "(min-width: 800px) 360px, (min-width: 400px) 300px, 100vw",
68
+ src: image.image.imageUrl,
69
+ "aria-label": image.title.title,
70
+ variant: "rounded"
71
+ }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ContentWrapper, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(StyledImageMetadata, { children: [
72
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(StyledTopRow, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__ndla_primitives.Text, {
73
+ textStyle: "title.medium",
74
+ children: image.title.title.trim() ? image.title.title : translations.missingTitleFallback ?? `ID: ${image.id}`
75
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__ndla_primitives.IconButton, {
76
+ variant: "tertiary",
77
+ onClick: () => onSelectImage(void 0),
78
+ "aria-label": translations.close,
79
+ title: translations.close,
80
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__ndla_icons.CloseLine, {})
81
+ })] }),
82
+ !!image.copyright.creators.length && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__ndla_primitives.Text, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: `${translations.creatorsLabel}: ` }), image.copyright.creators.map((creator) => creator.name).join(", ")] }),
83
+ !!image.copyright.license.description?.trim() && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__ndla_primitives.Text, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: `${translations.license}: ` }), image.copyright.license.description] }),
84
+ !!image.caption.caption.trim() && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__ndla_primitives.Text, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: `${translations.caption}: ` }), image.caption.caption] }),
85
+ !!image.alttext.alttext.trim() && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__ndla_primitives.Text, { children: [
86
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: `${translations.altText}:` }),
87
+ " ",
88
+ image.alttext.alttext
89
+ ] }),
90
+ !!image.modelRelease.trim() && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__ndla_primitives.Text, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: `${translations.modelRelease}: ` }), (0, __ndla_licenses.getModelReleaseValue)(image.modelRelease, locale)] }),
91
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_ImageMeta.ImageMeta_default, {
92
+ contentType: image.image.contentType,
93
+ fileSize: image.image.size,
94
+ imageDimensions: image.image.dimensions,
95
+ locale
96
+ }),
97
+ !!image.tags.tags.length && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(HashTagGroup, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__ndla_primitives.Text, {
98
+ id: "tags-title",
99
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: `${translations.tags}: ` })
100
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HashTagWrapper, {
101
+ "aria-describedby": "tags-title",
102
+ children: image.tags.tags.map((tag) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(StyledTagItem, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__ndla_icons.HashTag, { size: "small" }), tag] }, tag))
103
+ })] })
104
+ ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ActionsWrapper, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__ndla_primitives.Button, {
105
+ "data-testid": "use-image",
106
+ onClick: () => onSelectImage(image, saveAsMetaImage),
107
+ children: translations.useImageTitle
108
+ }), !!showCheckbox && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StyledFieldRoot, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__ndla_primitives.CheckboxRoot, {
109
+ checked: saveAsMetaImage,
110
+ onCheckedChange: () => setSaveAsMetaImage((prev) => !prev),
111
+ children: [
112
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__ndla_primitives.CheckboxControl, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__ndla_primitives.CheckboxIndicator, {
113
+ asChild: true,
114
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__ndla_icons.CheckLine, {})
115
+ }) }),
116
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__ndla_primitives.CheckboxLabel, { children: translations.checkboxLabel }),
117
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__ndla_primitives.CheckboxHiddenInput, {})
118
+ ]
119
+ }) })] })] })]
120
+ });
121
+ };
122
+ var PreviewImage_default = PreviewImage;
123
+
124
+ //#endregion
125
+ exports.PreviewImage_default = PreviewImage_default;
126
+ //# sourceMappingURL=PreviewImage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PreviewImage.js","names":["Image","FieldRoot","Text","IconButton","CloseLine","ImageMeta","HashTag","Button","CheckboxRoot","CheckboxControl","CheckboxIndicator","CheckLine","CheckboxLabel","CheckboxHiddenInput"],"sources":["../src/PreviewImage.tsx"],"sourcesContent":["/**\n * Copyright (c) 2017-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 { useState } from \"react\";\nimport { CloseLine, HashTag, CheckLine } from \"@ndla/icons\";\nimport { getModelReleaseValue } from \"@ndla/licenses\";\nimport {\n Button,\n CheckboxControl,\n CheckboxHiddenInput,\n CheckboxIndicator,\n CheckboxLabel,\n CheckboxRoot,\n Text,\n Image,\n IconButton,\n FieldRoot,\n} from \"@ndla/primitives\";\nimport { styled } from \"@ndla/styled-system/jsx\";\nimport type { IImageMetaInformationV3DTO } from \"@ndla/types-backend/image-api\";\nimport ImageMeta from \"./ImageMeta\";\nimport type { PreviewTranslations } from \"./ImageSearch\";\nimport { getSrcSets } from \"./util/imageUtil\";\n\nconst ImageContainer = styled(\"div\", {\n base: {\n flexShrink: \"0\",\n },\n});\n\nconst StyledImage = styled(Image, {\n base: {\n maxHeight: \"surface.xsmall\",\n },\n});\n\nconst StyledImageMetadata = styled(\"div\", {\n base: {\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"xxsmall\",\n },\n});\n\nconst HashTagWrapper = styled(\"ul\", {\n base: {\n display: \"flex\",\n gap: \"xxsmall\",\n flexWrap: \"wrap\",\n },\n});\n\nconst HashTagGroup = styled(\"div\", {\n base: {\n display: \"flex\",\n gap: \"xxsmall\",\n flexWrap: \"wrap\",\n },\n});\n\nconst StyledTagItem = styled(\"li\", {\n base: {\n display: \"flex\",\n alignItems: \"center\",\n },\n});\n\nconst StyledPreview = styled(\"div\", {\n base: {\n display: \"flex\",\n gridColumn: \"1 / -1\",\n borderColor: \"stroke.default\",\n border: \"1px solid\",\n borderRadius: \"xsmall\",\n gap: \"small\",\n padding: \"small\",\n flexWrap: \"wrap\",\n overflow: \"hidden\",\n },\n});\n\nconst StyledTopRow = styled(\"div\", {\n base: {\n display: \"flex\",\n justifyContent: \"space-between\",\n },\n});\n\nconst ContentWrapper = styled(\"div\", {\n base: {\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"medium\",\n flex: \"2\",\n },\n});\n\nconst ActionsWrapper = styled(\"div\", {\n base: {\n display: \"flex\",\n gap: \"small\",\n marginBlockEnd: \"medium\",\n },\n});\n\nconst StyledFieldRoot = styled(FieldRoot, {\n base: { alignSelf: \"center\" },\n});\n\ninterface Props {\n id: string;\n image: IImageMetaInformationV3DTO;\n onSelectImage: (image: IImageMetaInformationV3DTO | undefined, saveAsMetaImage?: boolean) => void;\n showCheckbox: boolean;\n translations: PreviewTranslations;\n locale: string;\n}\n\nconst PreviewImage = ({ id, image, onSelectImage, showCheckbox, translations, locale }: Props) => {\n const [saveAsMetaImage, setSaveAsMetaImage] = useState(false);\n\n return (\n <StyledPreview id={id}>\n <ImageContainer>\n <StyledImage\n alt=\"\"\n srcSet={getSrcSets(image.image.imageUrl)}\n sizes=\"(min-width: 800px) 360px, (min-width: 400px) 300px, 100vw\"\n src={image.image.imageUrl}\n aria-label={image.title.title}\n variant=\"rounded\"\n />\n </ImageContainer>\n <ContentWrapper>\n <StyledImageMetadata>\n <StyledTopRow>\n <Text textStyle=\"title.medium\">\n {image.title.title.trim() ? image.title.title : (translations.missingTitleFallback ?? `ID: ${image.id}`)}\n </Text>\n <IconButton\n variant=\"tertiary\"\n onClick={() => onSelectImage(undefined)}\n aria-label={translations.close}\n title={translations.close}\n >\n <CloseLine />\n </IconButton>\n </StyledTopRow>\n {!!image.copyright.creators.length && (\n <Text>\n <b>{`${translations.creatorsLabel}: `}</b>\n {image.copyright.creators.map((creator) => creator.name).join(\", \")}\n </Text>\n )}\n {!!image.copyright.license.description?.trim() && (\n <Text>\n <b>{`${translations.license}: `}</b>\n {image.copyright.license.description}\n </Text>\n )}\n {!!image.caption.caption.trim() && (\n <Text>\n <b>{`${translations.caption}: `}</b>\n {image.caption.caption}\n </Text>\n )}\n {!!image.alttext.alttext.trim() && (\n <Text>\n <b>{`${translations.altText}:`}</b> {image.alttext.alttext}\n </Text>\n )}\n {!!image.modelRelease.trim() && (\n <Text>\n <b>{`${translations.modelRelease}: `}</b>\n {getModelReleaseValue(image.modelRelease, locale)}\n </Text>\n )}\n <ImageMeta\n contentType={image.image.contentType}\n fileSize={image.image.size}\n imageDimensions={image.image.dimensions}\n locale={locale}\n />\n {!!image.tags.tags.length && (\n <HashTagGroup>\n <Text id=\"tags-title\">\n <b>{`${translations.tags}: `}</b>\n </Text>\n <HashTagWrapper aria-describedby=\"tags-title\">\n {image.tags.tags.map((tag) => (\n <StyledTagItem key={tag}>\n <HashTag size=\"small\" />\n {tag}\n </StyledTagItem>\n ))}\n </HashTagWrapper>\n </HashTagGroup>\n )}\n </StyledImageMetadata>\n <ActionsWrapper>\n <Button data-testid=\"use-image\" onClick={() => onSelectImage(image, saveAsMetaImage)}>\n {translations.useImageTitle}\n </Button>\n {!!showCheckbox && (\n <StyledFieldRoot>\n <CheckboxRoot checked={saveAsMetaImage} onCheckedChange={() => setSaveAsMetaImage((prev) => !prev)}>\n <CheckboxControl>\n <CheckboxIndicator asChild>\n <CheckLine />\n </CheckboxIndicator>\n </CheckboxControl>\n <CheckboxLabel>{translations.checkboxLabel}</CheckboxLabel>\n <CheckboxHiddenInput />\n </CheckboxRoot>\n </StyledFieldRoot>\n )}\n </ActionsWrapper>\n </ContentWrapper>\n </StyledPreview>\n );\n};\n\nexport default PreviewImage;\n"],"mappings":";;;;;;;;;;;AA6BA,MAAM,iBAAiB,qCAAO,OAAO,EACnC,MAAM,EACJ,YAAY,IACb,EACF,EAAC;AAEF,MAAM,cAAc,qCAAOA,yBAAO,EAChC,MAAM,EACJ,WAAW,iBACZ,EACF,EAAC;AAEF,MAAM,sBAAsB,qCAAO,OAAO,EACxC,MAAM;CACJ,SAAS;CACT,eAAe;CACf,KAAK;AACN,EACF,EAAC;AAEF,MAAM,iBAAiB,qCAAO,MAAM,EAClC,MAAM;CACJ,SAAS;CACT,KAAK;CACL,UAAU;AACX,EACF,EAAC;AAEF,MAAM,eAAe,qCAAO,OAAO,EACjC,MAAM;CACJ,SAAS;CACT,KAAK;CACL,UAAU;AACX,EACF,EAAC;AAEF,MAAM,gBAAgB,qCAAO,MAAM,EACjC,MAAM;CACJ,SAAS;CACT,YAAY;AACb,EACF,EAAC;AAEF,MAAM,gBAAgB,qCAAO,OAAO,EAClC,MAAM;CACJ,SAAS;CACT,YAAY;CACZ,aAAa;CACb,QAAQ;CACR,cAAc;CACd,KAAK;CACL,SAAS;CACT,UAAU;CACV,UAAU;AACX,EACF,EAAC;AAEF,MAAM,eAAe,qCAAO,OAAO,EACjC,MAAM;CACJ,SAAS;CACT,gBAAgB;AACjB,EACF,EAAC;AAEF,MAAM,iBAAiB,qCAAO,OAAO,EACnC,MAAM;CACJ,SAAS;CACT,eAAe;CACf,KAAK;CACL,MAAM;AACP,EACF,EAAC;AAEF,MAAM,iBAAiB,qCAAO,OAAO,EACnC,MAAM;CACJ,SAAS;CACT,KAAK;CACL,gBAAgB;AACjB,EACF,EAAC;AAEF,MAAM,kBAAkB,qCAAOC,6BAAW,EACxC,MAAM,EAAE,WAAW,SAAU,EAC9B,EAAC;AAWF,MAAM,eAAe,CAAC,EAAE,IAAI,OAAO,eAAe,cAAc,cAAc,QAAe,KAAK;CAChG,MAAM,CAAC,iBAAiB,mBAAmB,GAAG,oBAAS,MAAM;AAE7D,wBACE,4BAAC;EAAkB;6BACjB,2BAAC,4CACC,2BAAC;GACC,KAAI;GACJ,QAAQ,6BAAW,MAAM,MAAM,SAAS;GACxC,OAAM;GACN,KAAK,MAAM,MAAM;GACjB,cAAY,MAAM,MAAM;GACxB,SAAQ;IACR,GACa,kBACjB,4BAAC,6CACC,4BAAC;mBACC,4BAAC,2CACC,2BAACC;IAAK,WAAU;cACb,MAAM,MAAM,MAAM,MAAM,GAAG,MAAM,MAAM,QAAS,aAAa,yBAAyB,MAAM,MAAM,GAAG;KACjG,kBACP,2BAACC;IACC,SAAQ;IACR,SAAS,MAAM,qBAAwB;IACvC,cAAY,aAAa;IACzB,OAAO,aAAa;8BAEpB,2BAACC,2BAAY;KACF,IACA;KACZ,MAAM,UAAU,SAAS,0BAC1B,4BAACF,qDACC,2BAAC,kBAAI,EAAE,aAAa,cAAc,MAAQ,EACzC,MAAM,UAAU,SAAS,IAAI,CAAC,YAAY,QAAQ,KAAK,CAAC,KAAK,KAAK,IAC9D;KAEN,MAAM,UAAU,QAAQ,aAAa,MAAM,oBAC5C,4BAACA,qDACC,2BAAC,kBAAI,EAAE,aAAa,QAAQ,MAAQ,EACnC,MAAM,UAAU,QAAQ,eACpB;KAEN,MAAM,QAAQ,QAAQ,MAAM,oBAC7B,4BAACA,qDACC,2BAAC,kBAAI,EAAE,aAAa,QAAQ,MAAQ,EACnC,MAAM,QAAQ,WACV;KAEN,MAAM,QAAQ,QAAQ,MAAM,oBAC7B,4BAACA;oBACC,2BAAC,kBAAI,EAAE,aAAa,QAAQ,KAAO;;IAAE,MAAM,QAAQ;OAC9C;KAEN,MAAM,aAAa,MAAM,oBAC1B,4BAACA,qDACC,2BAAC,kBAAI,EAAE,aAAa,aAAa,MAAQ,EACxC,0CAAqB,MAAM,cAAc,OAAO,IAC5C;mBAET,2BAACG;IACC,aAAa,MAAM,MAAM;IACzB,UAAU,MAAM,MAAM;IACtB,iBAAiB,MAAM,MAAM;IACrB;KACR;KACC,MAAM,KAAK,KAAK,0BACjB,4BAAC,2CACC,2BAACH;IAAK,IAAG;8BACP,2BAAC,kBAAI,EAAE,aAAa,KAAK,MAAQ;KAC5B,kBACP,2BAAC;IAAe,oBAAiB;cAC9B,MAAM,KAAK,KAAK,IAAI,CAAC,wBACpB,4BAAC,4CACC,2BAACI,wBAAQ,MAAK,UAAU,EACvB,QAFiB,IAGJ,CAChB;KACa,IACJ;MAEG,kBACtB,4BAAC,6CACC,2BAACC;GAAO,eAAY;GAAY,SAAS,MAAM,cAAc,OAAO,gBAAgB;aACjF,aAAa;IACP,IACN,gCACD,2BAAC,6CACC,4BAACC;GAAa,SAAS;GAAiB,iBAAiB,MAAM,mBAAmB,CAAC,UAAU,KAAK;;oBAChG,2BAACC,+DACC,2BAACC;KAAkB;+BACjB,2BAACC,2BAAY;MACK,GACJ;oBAClB,2BAACC,6CAAe,aAAa,gBAA8B;oBAC3D,2BAACC,0CAAsB;;IACV,GACC,IAEL,IACF;GACH;AAEnB;AAED,2BAAe"}
@@ -0,0 +1,30 @@
1
+ //#region rolldown:runtime
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
22
+
23
+ //#endregion
24
+
25
+ Object.defineProperty(exports, '__toESM', {
26
+ enumerable: true,
27
+ get: function () {
28
+ return __toESM;
29
+ }
30
+ });
package/lib/index.js ADDED
@@ -0,0 +1,5 @@
1
+ const require_ImageMeta = require('./ImageMeta.js');
2
+ const require_ImageSearch = require('./ImageSearch.js');
3
+
4
+ exports.ImageMeta = require_ImageMeta.ImageMeta_default;
5
+ exports.ImageSearch = require_ImageSearch.ImageSearch_default;
@@ -0,0 +1,29 @@
1
+
2
+ //#region src/util/imageUtil.ts
3
+ /**
4
+ * Copyright (c) 2017-present, NDLA.
5
+ *
6
+ * This source code is licensed under the GPLv3 license found in the
7
+ * LICENSE file in the root directory of this source tree.
8
+ *
9
+ */
10
+ const getSrcSets = (imageUrl) => [
11
+ `${imageUrl}?width=1440 1440w`,
12
+ `${imageUrl}?width=1120 1120w`,
13
+ `${imageUrl}?width=1000 1000w`,
14
+ `${imageUrl}?width=960 960w`,
15
+ `${imageUrl}?width=800 800w`,
16
+ `${imageUrl}?width=640 640w`,
17
+ `${imageUrl}?width=480 480w`,
18
+ `${imageUrl}?width=320 320w`
19
+ ].join(", ");
20
+ const getPreviewSrcSets = (imageUrl) => [
21
+ `${imageUrl}?width=480 3x`,
22
+ `${imageUrl}?width=320 2x`,
23
+ `${imageUrl}?width=160 1x`
24
+ ].join(", ");
25
+
26
+ //#endregion
27
+ exports.getPreviewSrcSets = getPreviewSrcSets;
28
+ exports.getSrcSets = getSrcSets;
29
+ //# sourceMappingURL=imageUtil.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imageUtil.js","names":["imageUrl: string"],"sources":["../../src/util/imageUtil.ts"],"sourcesContent":["/**\n * Copyright (c) 2017-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\n/* - Source sets for gallery search is retina-display optimized\n - Based on: https://www.smashingmagazine.com/2014/05/responsive-images-done-right-guide-picture-srcset/ */\n\nexport const getSrcSets = (imageUrl: string) =>\n [\n `${imageUrl}?width=1440 1440w`,\n `${imageUrl}?width=1120 1120w`,\n `${imageUrl}?width=1000 1000w`,\n `${imageUrl}?width=960 960w`,\n `${imageUrl}?width=800 800w`,\n `${imageUrl}?width=640 640w`,\n `${imageUrl}?width=480 480w`,\n `${imageUrl}?width=320 320w`,\n ].join(\", \");\n\nexport const getPreviewSrcSets = (imageUrl: string) =>\n [`${imageUrl}?width=480 3x`, `${imageUrl}?width=320 2x`, `${imageUrl}?width=160 1x`].join(\", \");\n"],"mappings":";;;;;;;;;AAWA,MAAa,aAAa,CAACA,aACzB;EACG,EAAE,SAAS;EACX,EAAE,SAAS;EACX,EAAE,SAAS;EACX,EAAE,SAAS;EACX,EAAE,SAAS;EACX,EAAE,SAAS;EACX,EAAE,SAAS;EACX,EAAE,SAAS;AACb,EAAC,KAAK,KAAK;AAEd,MAAa,oBAAoB,CAACA,aAChC;EAAE,EAAE,SAAS;EAAiB,EAAE,SAAS;EAAiB,EAAE,SAAS;AAAe,EAAC,KAAK,KAAK"}
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "@ndla/image-search",
3
- "version": "11.0.102-alpha.0",
3
+ "version": "11.0.104-alpha.0",
4
4
  "description": "A simple library for searching images from NDLA",
5
5
  "license": "GPL-3.0",
6
6
  "main": "lib/index.js",
7
7
  "module": "es/index.js",
8
8
  "types": "lib/index.d.ts",
9
9
  "scripts": {
10
- "build": "node ../../scripts/build.js package",
10
+ "build": "tsdown",
11
+ "dev": "tsdown --watch ./src",
11
12
  "build:types": "tsc -p tsconfig.build.json",
12
13
  "prepublish": "concurrently 'yarn build:types' 'mkdir -p dist' 'panda cssgen --minimal --outfile dist/styles.css' 'panda ship --outfile dist/panda.buildinfo.json'"
13
14
  },
@@ -26,14 +27,14 @@
26
27
  "es"
27
28
  ],
28
29
  "dependencies": {
29
- "@ndla/icons": "^8.0.58-alpha.0",
30
- "@ndla/licenses": "^9.0.1",
31
- "@ndla/primitives": "^1.0.88-alpha.0",
32
- "@ndla/styled-system": "^0.0.34",
33
- "@ndla/util": "^5.0.8-alpha.0"
30
+ "@ndla/icons": "^8.0.59-alpha.0",
31
+ "@ndla/licenses": "^9.0.2",
32
+ "@ndla/primitives": "^1.0.90-alpha.0",
33
+ "@ndla/styled-system": "^0.0.35",
34
+ "@ndla/util": "^5.0.9-alpha.0"
34
35
  },
35
36
  "devDependencies": {
36
- "@ndla/preset-panda": "^0.0.54",
37
+ "@ndla/preset-panda": "^0.0.55",
37
38
  "@ndla/types-backend": "^1.0.40",
38
39
  "@pandacss/dev": "^0.53.6"
39
40
  },
@@ -44,5 +45,5 @@
44
45
  "publishConfig": {
45
46
  "access": "public"
46
47
  },
47
- "gitHead": "52adafa4348c1c02ebc4da28317aa5270cbe2a80"
48
+ "gitHead": "70b2b4e3d374545b5245a63ea7644c3b9356c74f"
48
49
  }