@snowcone-app/ui 0.2.0 → 0.2.1
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/CHANGELOG.md +6 -0
- package/dist/index.cjs +5 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/composed/HeroProductImage.tsx +13 -5
package/package.json
CHANGED
|
@@ -878,6 +878,10 @@ export const HeroProductImage = memo(function HeroProductImage({
|
|
|
878
878
|
// useEffect runs and overlays prevUrl (showing OLD on top), then prevUrl
|
|
879
879
|
// fades out to reveal NEW again — i.e. the new → old → new flash.
|
|
880
880
|
const [renderedUrl, setRenderedUrl] = useState<string | null>(displayUrl);
|
|
881
|
+
// `renderedUrl` is set as soon as a URL exists, but the <img> paints nothing
|
|
882
|
+
// until the render is actually fetched (seconds, on a cold render). Keep the
|
|
883
|
+
// pulsing skeleton up until the first image has painted.
|
|
884
|
+
const [firstImageLoaded, setFirstImageLoaded] = useState(false);
|
|
881
885
|
const prevDisplayUrlRef = useRef<string | null>(displayUrl);
|
|
882
886
|
// Fire the L3 "you forgot signMockupUrl" dev hint at most once per component.
|
|
883
887
|
const signHintShownRef = useRef(false);
|
|
@@ -972,6 +976,13 @@ export const HeroProductImage = memo(function HeroProductImage({
|
|
|
972
976
|
style={style}
|
|
973
977
|
data-hero-image="true"
|
|
974
978
|
>
|
|
979
|
+
{/* Loading skeleton — painted UNDER the <img> so the image covers it the
|
|
980
|
+
instant it renders, with no state-flush flash. Removed once the first
|
|
981
|
+
image has loaded; later URL changes crossfade over a visible image. */}
|
|
982
|
+
{!firstImageLoaded && (
|
|
983
|
+
<div className="absolute inset-0 bg-muted-foreground/20 animate-pulse" />
|
|
984
|
+
)}
|
|
985
|
+
|
|
975
986
|
{/* Current image — shows `renderedUrl`, which the crossfade effect only
|
|
976
987
|
advances after the next image has finished preloading. This prevents
|
|
977
988
|
the new → old → new flash that happened when displayUrl was bound
|
|
@@ -990,6 +1001,7 @@ export const HeroProductImage = memo(function HeroProductImage({
|
|
|
990
1001
|
fetchPriority="high"
|
|
991
1002
|
onClick={onClick}
|
|
992
1003
|
onLoad={() => {
|
|
1004
|
+
setFirstImageLoaded(true);
|
|
993
1005
|
if (!onLoadCalledRef.current && onLoad) {
|
|
994
1006
|
onLoadCalledRef.current = true;
|
|
995
1007
|
onLoad();
|
|
@@ -997,6 +1009,7 @@ export const HeroProductImage = memo(function HeroProductImage({
|
|
|
997
1009
|
onUrlGeneratedRef.current?.(renderedUrl);
|
|
998
1010
|
}}
|
|
999
1011
|
onError={() => {
|
|
1012
|
+
setFirstImageLoaded(true);
|
|
1000
1013
|
onError?.();
|
|
1001
1014
|
// Dev-only guidance for the most common signed-shop trap: a mockup
|
|
1002
1015
|
// <img> that loads an UNSIGNED resolver URL gets a 403 "Missing
|
|
@@ -1047,11 +1060,6 @@ export const HeroProductImage = memo(function HeroProductImage({
|
|
|
1047
1060
|
/>
|
|
1048
1061
|
)}
|
|
1049
1062
|
|
|
1050
|
-
{/* Simple placeholder - shown until we have a URL to render */}
|
|
1051
|
-
{!renderedUrl && (
|
|
1052
|
-
<div className="absolute inset-0 bg-zinc-200" />
|
|
1053
|
-
)}
|
|
1054
|
-
|
|
1055
1063
|
{/* SAFARI FIX: Disabled shimmer overlay - it depends on layers state which causes re-renders */}
|
|
1056
1064
|
{/* {showShimmer && layers.length > 0 && (
|
|
1057
1065
|
<div
|