@boyernick/standard-ui-react 0.4.2-canary.53 → 0.4.2-canary.54

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/modal.tsx +25 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boyernick/standard-ui-react",
3
- "version": "0.4.2-canary.53",
3
+ "version": "0.4.2-canary.54",
4
4
  "description": "React components for StandardUI",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/modal.tsx CHANGED
@@ -174,15 +174,39 @@ const ModalImage = ({
174
174
  }: Pick<GalleryItem, "src" | "alt" | "imgProps"> & {
175
175
  fit?: "viewport" | "contain"
176
176
  }) => {
177
- const { className, ...props } = imgProps ?? {}
177
+ const { className, onLoad, onError, ...props } = imgProps ?? {}
178
+ // Held transparent until decoded, then faded in: a large file otherwise
179
+ // paints in top-down bands inside the lightbox. The ref catches an image the
180
+ // browser already has (a cached file, or the one the trigger just showed)
181
+ // before first paint, so it appears at once with nothing to fade.
182
+ const [loaded, setLoaded] = useState(false)
183
+ const catchLoaded = useCallback((image: HTMLImageElement | null) => {
184
+ if (image?.complete && image.naturalWidth > 0) setLoaded(true)
185
+ }, [])
178
186
 
179
187
  return (
180
188
  // eslint-disable-next-line @next/next/no-img-element
181
189
  <img
190
+ ref={catchLoaded}
182
191
  src={src}
183
192
  alt={alt}
184
193
  draggable={false}
194
+ onLoad={(event) => {
195
+ onLoad?.(event)
196
+ event.currentTarget
197
+ .decode()
198
+ .catch(() => {})
199
+ .then(() => setLoaded(true))
200
+ }}
201
+ onError={(event) => {
202
+ onError?.(event)
203
+ // Reveal the broken image rather than leave an invisible one.
204
+ setLoaded(true)
205
+ }}
185
206
  className={cn(
207
+ // Linear, like every fade in `motion` — see the note there.
208
+ "transition-opacity duration-[var(--duration-lg)] ease-linear motion-reduce:transition-none",
209
+ !loaded && "opacity-0",
186
210
  "rounded-lg object-contain shadow-lg select-none",
187
211
  fit === "contain"
188
212
  ? "max-h-full max-w-full"