@bloom-housing/ui-components 12.0.15 → 12.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/blocks/ImageCard.d.ts +2 -0
- package/dist/src/blocks/ImageCard.js +5 -3
- package/dist/src/blocks/ImageCard.js.map +1 -1
- package/dist/src/helpers/useFallbackImage.d.ts +6 -0
- package/dist/src/helpers/useFallbackImage.js +31 -0
- package/dist/src/helpers/useFallbackImage.js.map +1 -0
- package/package.json +1 -1
- package/src/blocks/ImageCard.tsx +14 -2
- package/src/helpers/useFallbackImage.ts +37 -0
|
@@ -33,6 +33,8 @@ export interface ImageCardProps {
|
|
|
33
33
|
imageUrl?: string;
|
|
34
34
|
/** Alternatively, a number of images can be passed in */
|
|
35
35
|
images?: ImageItem[];
|
|
36
|
+
/** A fallback image URL that will be displayed on error for all images */
|
|
37
|
+
fallbackImageUrl?: string;
|
|
36
38
|
/** A list of status indicators, an ApplicationStatus component is rendered for each item at the bottom of the card */
|
|
37
39
|
statuses?: StatusBarType[];
|
|
38
40
|
/** A list of image tags, a Tag component is rendered for each over the image */
|
|
@@ -20,6 +20,7 @@ import { Icon, IconFillColors } from "../icons/Icon";
|
|
|
20
20
|
import { Modal } from "../overlays/Modal";
|
|
21
21
|
import { Button } from "../actions/Button";
|
|
22
22
|
import { t } from "../helpers/translator";
|
|
23
|
+
import { useFallbackImage } from "../helpers/useFallbackImage";
|
|
23
24
|
/**
|
|
24
25
|
* @component ImageCard
|
|
25
26
|
*
|
|
@@ -28,6 +29,7 @@ import { t } from "../helpers/translator";
|
|
|
28
29
|
var ImageCard = function (props) {
|
|
29
30
|
var _a, _b, _c, _d, _e;
|
|
30
31
|
var _f = useState(false), showModal = _f[0], setShowModal = _f[1];
|
|
32
|
+
var _g = useFallbackImage(props === null || props === void 0 ? void 0 : props.fallbackImageUrl), imgParentRef = _g.imgParentRef, imgRefs = _g.imgRefs, onError = _g.onError;
|
|
31
33
|
var getStatuses = function () {
|
|
32
34
|
var _a;
|
|
33
35
|
var statuses = (_a = props.statuses) === null || _a === void 0 ? void 0 : _a.map(function (status, index) {
|
|
@@ -60,8 +62,8 @@ var ImageCard = function (props) {
|
|
|
60
62
|
};
|
|
61
63
|
var image = (React.createElement(React.Fragment, null,
|
|
62
64
|
React.createElement("div", { className: "image-card" },
|
|
63
|
-
React.createElement("figure", { className: innerClasses.join(" ") },
|
|
64
|
-
props.imageUrl ? (React.createElement("img", { src: props.imageUrl, alt: (_c = (_a = props.description) !== null && _a !== void 0 ? _a : (_b = props.strings) === null || _b === void 0 ? void 0 : _b.defaultImageAltText) !== null && _c !== void 0 ? _c : t("listings.buildingImageAltText") })) : props.images && displayedImages ? (displayedImages.map(function (image, index) { return (React.createElement("img", { key: index, src: image.thumbnailUrl || image.mobileUrl || image.url, alt: getAltText(index, displayedImages, image.description) })); })) : (React.createElement("div", { className: "image-card__placeholder" })),
|
|
65
|
+
React.createElement("figure", { className: innerClasses.join(" "), ref: imgParentRef },
|
|
66
|
+
props.imageUrl ? (React.createElement("img", { src: props.imageUrl, alt: (_c = (_a = props.description) !== null && _a !== void 0 ? _a : (_b = props.strings) === null || _b === void 0 ? void 0 : _b.defaultImageAltText) !== null && _c !== void 0 ? _c : t("listings.buildingImageAltText"), ref: function (el) { return (imgRefs.current[0] = el); }, onError: onError })) : props.images && displayedImages ? (displayedImages.map(function (image, index) { return (React.createElement("img", { key: index, src: image.thumbnailUrl || image.mobileUrl || image.url, alt: getAltText(index, displayedImages, image.description), ref: function (el) { return (imgRefs.current[index] = el); }, onError: onError })); })) : (React.createElement("div", { className: "image-card__placeholder" })),
|
|
65
67
|
props.images && props.images.length > 1 && (React.createElement(React.Fragment, null,
|
|
66
68
|
props.images && props.images.length > 3 && (React.createElement("div", { className: "image-card__more-images" },
|
|
67
69
|
React.createElement(Icon, { symbol: "plus", size: "xlarge" }),
|
|
@@ -90,7 +92,7 @@ var ImageCard = function (props) {
|
|
|
90
92
|
], actionsInContent: props.modalCloseInContent }, (_e = props.images) === null || _e === void 0 ? void 0 : _e.map(function (image, index) { return (React.createElement("p", { key: index, className: "md:mb-8" },
|
|
91
93
|
React.createElement("picture", null,
|
|
92
94
|
image.mobileUrl && React.createElement("source", { media: "(max-width: 767px)", srcSet: image.mobileUrl }),
|
|
93
|
-
React.createElement("img", { src: image.url, alt: getAltText(index, props.images, image.description) })))); })))));
|
|
95
|
+
React.createElement("img", { src: image.url, alt: getAltText(index, props.images, image.description), onError: onError })))); })))));
|
|
94
96
|
var card = image;
|
|
95
97
|
if (props.href) {
|
|
96
98
|
card = (React.createElement(LocalizedLink, { className: "block", href: props.href }, image));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ImageCard.js","sourceRoot":"","sources":["../../../src/blocks/ImageCard.tsx"],"names":[],"mappings":";;;;;;;;;;;AAAA,OAAO,KAAK,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAA;AACtE,OAAO,kBAAkB,CAAA;AACzB,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AACjC,OAAO,EAAgB,OAAO,EAAE,MAAM,WAAW,CAAA;AAEjD,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAA;AACnF,OAAO,EAAE,IAAI,EAAE,cAAc,EAAqB,MAAM,eAAe,CAAA;AACvE,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAC1C,OAAO,EAAE,CAAC,EAAE,MAAM,uBAAuB,CAAA;
|
|
1
|
+
{"version":3,"file":"ImageCard.js","sourceRoot":"","sources":["../../../src/blocks/ImageCard.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { SyntheticEvent } from "react";
|
|
2
|
+
export declare function useFallbackImage(fallbackSrc?: string): {
|
|
3
|
+
imgParentRef: import("react").RefObject<HTMLDivElement>;
|
|
4
|
+
imgRefs: import("react").MutableRefObject<(HTMLImageElement | null)[]>;
|
|
5
|
+
onError: (e: SyntheticEvent<HTMLImageElement>) => void;
|
|
6
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
2
|
+
export function useFallbackImage(fallbackSrc) {
|
|
3
|
+
var imgParentRef = useRef(null);
|
|
4
|
+
var imgRefs = useRef([]);
|
|
5
|
+
var onError = useCallback(function (e) {
|
|
6
|
+
if (fallbackSrc) {
|
|
7
|
+
e.currentTarget.src = fallbackSrc;
|
|
8
|
+
}
|
|
9
|
+
}, [fallbackSrc]);
|
|
10
|
+
useEffect(function () {
|
|
11
|
+
if (imgParentRef && imgParentRef.current && fallbackSrc) {
|
|
12
|
+
var numberOfImages = imgParentRef.current.children.length;
|
|
13
|
+
for (var i = 0; i < numberOfImages; i++) {
|
|
14
|
+
var imgRef = imgRefs.current[i];
|
|
15
|
+
if (imgRef) {
|
|
16
|
+
var complete = imgRef.complete, naturalHeight = imgRef.naturalHeight;
|
|
17
|
+
var errorLoadingImgBeforeHydration = complete && naturalHeight === 0;
|
|
18
|
+
if (errorLoadingImgBeforeHydration) {
|
|
19
|
+
imgRef.src = fallbackSrc;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}, [fallbackSrc]);
|
|
25
|
+
return {
|
|
26
|
+
imgParentRef: imgParentRef,
|
|
27
|
+
imgRefs: imgRefs,
|
|
28
|
+
onError: onError,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=useFallbackImage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useFallbackImage.js","sourceRoot":"","sources":["../../../src/helpers/useFallbackImage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AAEtE,MAAM,UAAU,gBAAgB,CAAC,WAAoB;IACnD,IAAM,YAAY,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAA;IACjD,IAAM,OAAO,GAAG,MAAM,CAA8B,EAAE,CAAC,CAAA;IAEvD,IAAM,OAAO,GAAG,WAAW,CACzB,UAAC,CAAmC;QAClC,IAAI,WAAW,EAAE;YACf,CAAC,CAAC,aAAa,CAAC,GAAG,GAAG,WAAW,CAAA;SAClC;IACH,CAAC,EACD,CAAC,WAAW,CAAC,CACd,CAAA;IAED,SAAS,CAAC;QACR,IAAI,YAAY,IAAI,YAAY,CAAC,OAAO,IAAI,WAAW,EAAE;YACvD,IAAM,cAAc,GAAG,YAAY,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAA;YAC3D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,cAAc,EAAE,CAAC,EAAE,EAAE;gBACvC,IAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;gBACjC,IAAI,MAAM,EAAE;oBACF,IAAA,QAAQ,GAAoB,MAAM,SAA1B,EAAE,aAAa,GAAK,MAAM,cAAX,CAAW;oBAC1C,IAAM,8BAA8B,GAAG,QAAQ,IAAI,aAAa,KAAK,CAAC,CAAA;oBACtE,IAAI,8BAA8B,EAAE;wBAClC,MAAM,CAAC,GAAG,GAAG,WAAW,CAAA;qBACzB;iBACF;aACF;SACF;IACH,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAA;IAEjB,OAAO;QACL,YAAY,cAAA;QACZ,OAAO,SAAA;QACP,OAAO,SAAA;KACR,CAAA;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bloom-housing/ui-components",
|
|
3
|
-
"version": "12.0.
|
|
3
|
+
"version": "12.0.16",
|
|
4
4
|
"author": "Sean Albert <sean.albert@exygy.com>",
|
|
5
5
|
"description": "Shared user interface components for Bloom affordable housing system",
|
|
6
6
|
"homepage": "https://github.com/bloom-housing/ui-components",
|
package/src/blocks/ImageCard.tsx
CHANGED
|
@@ -10,6 +10,7 @@ import { Icon, IconFillColors, UniversalIconType } from "../icons/Icon"
|
|
|
10
10
|
import { Modal } from "../overlays/Modal"
|
|
11
11
|
import { Button } from "../actions/Button"
|
|
12
12
|
import { t } from "../helpers/translator"
|
|
13
|
+
import { useFallbackImage } from "../helpers/useFallbackImage"
|
|
13
14
|
|
|
14
15
|
export interface StatusBarType {
|
|
15
16
|
status?: ApplicationStatusType
|
|
@@ -45,6 +46,8 @@ export interface ImageCardProps {
|
|
|
45
46
|
imageUrl?: string
|
|
46
47
|
/** Alternatively, a number of images can be passed in */
|
|
47
48
|
images?: ImageItem[]
|
|
49
|
+
/** A fallback image URL that will be displayed on error for all images */
|
|
50
|
+
fallbackImageUrl?: string
|
|
48
51
|
/** A list of status indicators, an ApplicationStatus component is rendered for each item at the bottom of the card */
|
|
49
52
|
statuses?: StatusBarType[]
|
|
50
53
|
/** A list of image tags, a Tag component is rendered for each over the image */
|
|
@@ -71,6 +74,7 @@ export interface ImageCardProps {
|
|
|
71
74
|
*/
|
|
72
75
|
const ImageCard = (props: ImageCardProps) => {
|
|
73
76
|
const [showModal, setShowModal] = useState(false)
|
|
77
|
+
const { imgParentRef, imgRefs, onError } = useFallbackImage(props?.fallbackImageUrl)
|
|
74
78
|
|
|
75
79
|
const getStatuses = () => {
|
|
76
80
|
const statuses = props.statuses?.map((status, index) => {
|
|
@@ -116,7 +120,7 @@ const ImageCard = (props: ImageCardProps) => {
|
|
|
116
120
|
const image = (
|
|
117
121
|
<>
|
|
118
122
|
<div className="image-card">
|
|
119
|
-
<figure className={innerClasses.join(" ")}>
|
|
123
|
+
<figure className={innerClasses.join(" ")} ref={imgParentRef}>
|
|
120
124
|
{props.imageUrl ? (
|
|
121
125
|
<img
|
|
122
126
|
src={props.imageUrl}
|
|
@@ -125,6 +129,8 @@ const ImageCard = (props: ImageCardProps) => {
|
|
|
125
129
|
props.strings?.defaultImageAltText ??
|
|
126
130
|
t("listings.buildingImageAltText")
|
|
127
131
|
}
|
|
132
|
+
ref={(el) => (imgRefs.current[0] = el)}
|
|
133
|
+
onError={onError}
|
|
128
134
|
/>
|
|
129
135
|
) : props.images && displayedImages ? (
|
|
130
136
|
displayedImages.map((image, index) => (
|
|
@@ -132,6 +138,8 @@ const ImageCard = (props: ImageCardProps) => {
|
|
|
132
138
|
key={index}
|
|
133
139
|
src={image.thumbnailUrl || image.mobileUrl || image.url}
|
|
134
140
|
alt={getAltText(index, displayedImages, image.description)}
|
|
141
|
+
ref={(el) => (imgRefs.current[index] = el)}
|
|
142
|
+
onError={onError}
|
|
135
143
|
/>
|
|
136
144
|
))
|
|
137
145
|
) : (
|
|
@@ -219,7 +227,11 @@ const ImageCard = (props: ImageCardProps) => {
|
|
|
219
227
|
<p key={index} className="md:mb-8">
|
|
220
228
|
<picture>
|
|
221
229
|
{image.mobileUrl && <source media="(max-width: 767px)" srcSet={image.mobileUrl} />}
|
|
222
|
-
<img
|
|
230
|
+
<img
|
|
231
|
+
src={image.url}
|
|
232
|
+
alt={getAltText(index, props.images, image.description)}
|
|
233
|
+
onError={onError}
|
|
234
|
+
/>
|
|
223
235
|
</picture>
|
|
224
236
|
</p>
|
|
225
237
|
))}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { SyntheticEvent, useCallback, useEffect, useRef } from "react"
|
|
2
|
+
|
|
3
|
+
export function useFallbackImage(fallbackSrc?: string) {
|
|
4
|
+
const imgParentRef = useRef<HTMLDivElement>(null)
|
|
5
|
+
const imgRefs = useRef<(HTMLImageElement | null)[]>([])
|
|
6
|
+
|
|
7
|
+
const onError = useCallback(
|
|
8
|
+
(e: SyntheticEvent<HTMLImageElement>) => {
|
|
9
|
+
if (fallbackSrc) {
|
|
10
|
+
e.currentTarget.src = fallbackSrc
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
[fallbackSrc]
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (imgParentRef && imgParentRef.current && fallbackSrc) {
|
|
18
|
+
const numberOfImages = imgParentRef.current.children.length
|
|
19
|
+
for (let i = 0; i < numberOfImages; i++) {
|
|
20
|
+
const imgRef = imgRefs.current[i]
|
|
21
|
+
if (imgRef) {
|
|
22
|
+
const { complete, naturalHeight } = imgRef
|
|
23
|
+
const errorLoadingImgBeforeHydration = complete && naturalHeight === 0
|
|
24
|
+
if (errorLoadingImgBeforeHydration) {
|
|
25
|
+
imgRef.src = fallbackSrc
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}, [fallbackSrc])
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
imgParentRef,
|
|
34
|
+
imgRefs,
|
|
35
|
+
onError,
|
|
36
|
+
}
|
|
37
|
+
}
|