Setting the file. One moment.
Media · Wix Headless Templates · wix/skills · Skills Docs
ContentsBack to the top of the page Bundled file
blog/project/src/wix/ media.ts
TypeScript · 106 lines · 5 KB
13 | undefined
14 | { image ?: unknown ; url ?: string | null ; id ?: string | null };
15
16 /** A bare Wix media file id (`e6a89e_06cf…~mv2.png`): what REST-shaped media objects carry as `url` and `id`. */
17 const BARE_FILE_ID = / ^ [\w-] + ~mv2 \. [a-z0-9] +$ / i ;
18
19 function rawOf ( value : MediaLike ) : string {
20 if ( ! value) return "" ;
21 if ( typeof value === "string" ) return value;
22 if (value.image != null && typeof value.image === "object" ) return rawOf (value.image as MediaLike ); // { image: { id, url } }
23 return ( typeof value.image === "string" ? value.image : null ) ?? value.url ?? value.id ?? "" ;
24 }
25
26 /** Resolve any Wix media value to a browser-loadable URL ("" when absent). */
27 export function imgSrc ( value : MediaLike , width = 600 , height = 600 ) : string {
28 const v = rawOf (value);
29 if ( ! v) return "" ;
30 if (v. startsWith ( "wix:image://" )) {
31 return media. getScaledToFillImageUrl (v, width, height, {});
32 }
33 // A bare file id (what REST-shaped media objects carry) or an absolute Wix media URL (bare, or
34 // already carrying a /v1/fill/ segment at some other size): re-issue it through the scaler at the
35 // requested size, so every image path lands on one shape.
36 const file = BARE_FILE_ID . test (v) ? v : v. match ( / ^ https: \/\/ static \. wixstatic \. com \/ media \/ ( [ ^ /?#] + )/ )?.[ 1 ];
37 if (file) return `https://static.wixstatic.com/media/${ file }/v1/fill/w_${ width },h_${ height },al_c,q_90/${ file }` ;
38 return v;
39 }
40
41 /**
42 * Responsive candidates for `srcset`: the same image at several widths through Wix's scaler, so a
43 * card never downloads a hero-sized file. Pair with `sizes`:
44 * <img src={imgSrc(m, 640, 640)} srcSet={imgSrcSet(m)} sizes="(min-width: 1024px) 25vw, 50vw"
45 * width={640} height={640} loading="lazy" alt={…} />
46 * Accepts every form an image reaches you in: a raw `wix:image://` id, an ALREADY-RESOLVED Wix URL
47 * (what the DTOs carry — `imageUrl`, `gallery[]`; its `/v1/fill/w_…,h_…` segment is rewritten per
48 * width), or any other https URL (returned as the single candidate). `ratio` is height/width
49 * (1 = square). "" only when there is no image at all — still set `src` as well.
50 */
51 export function imgSrcSet ( value : MediaLike , widths : number [] = [ 320 , 480 , 640 , 960 ], ratio = 1 ) : string {
52 const raw = rawOf (value);
53 if ( ! raw) return "" ;
54 if (raw. startsWith ( "wix:image://" ) || BARE_FILE_ID . test (raw)) {
55 return widths. map (( w ) => `${ imgSrc ( value , w , Math . round ( w * ratio )) } ${ w }w` ). join ( ", " );
56 }
57 // A resolved Wix URL: …/v1/fill/w_800,h_800,al_c,… — swap the size for each width.
58 const FILL = / \/ v1 \/ fill \/ w_ \d + ,h_ \d + / ;
59 if ( /static \. wixstatic \. com \/ / . test (raw) && FILL . test (raw)) {
60 return widths. map (( w ) => `${ raw . replace ( FILL , `/v1/fill/w_${ w },h_${ Math . round ( w * ratio ) }` ) } ${ w }w` ). join ( ", " );
61 }
62 return raw; // one candidate, never an empty srcset for a real image
63 }
64
65 /**
66 * Everything an <img> needs for one Wix image, so a tile can never ship `srcSet` without `src`:
67 * <img {...imgAttrs(p.imageUrl, "(min-width: 1024px) 25vw, 50vw")} alt={p.name} />
68 * `sizes` is the width the image renders at (a CSS length or media-query list); `ratio` is
69 * height/width. Returns an empty object when there is no image — render your placeholder then.
70 */
71 export function imgAttrs ( value : MediaLike , sizes : string , ratio = 1 ) : { src : string ; srcSet : string ; sizes : string ; loading : "lazy" ; decoding : "async" } | Record < string , never > {
72 const src = imgSrc (value, 640 , Math. round ( 640 * ratio));
73 if ( ! src) return {};
74 return { src, srcSet: imgSrcSet (value, undefined , ratio), sizes, loading: "lazy" , decoding: "async" };
75 }
76
77 /**
78 * Height/width of a Wix image when the value carries its size: a raw
79 * `wix:image://…#originWidth=W&originHeight=H` id (the natural size) or a resolved `/v1/fill/w_W,h_H/`
80 * URL (the size it was scaled to). null when neither is present. Pass it as `ratio` to keep a
81 * srcset at the image's own aspect instead of the square default:
82 * <img {...imgAttrs(item.photo, "33vw", imgRatio(item.photo) ?? 1)} alt={…} />
83 */
84 export function imgRatio ( value : MediaLike ) : number | null {
85 const v = rawOf (value);
86 if ( typeof v !== "string" || ! v) return null ;
87 const size = v. startsWith ( "wix:image://" )
88 ? [v. match ( / [#&] originWidth=( \d + )/ )?.[ 1 ], v. match ( / [#&] originHeight=( \d + )/ )?.[ 1 ]]
89 : (v. match ( / \/ v1 \/ fill \/ w_( \d + ),h_( \d + )/ )?. slice ( 1 ) ?? []);
90 const w = Number (size[ 0 ]);
91 const h = Number (size[ 1 ]);
92 return w > 0 && h > 0 ? h / w : null ;
93 }
94
95 /**
96 * The identity of a media value BEFORE scaling — de-duplicate galleries on this, never on a
97 * resolved URL (two scaled URLs of one photo differ in their size parameters).
98 */
99 export function mediaKey ( value : MediaLike ) : string {
100 const v = rawOf (value);
101 if ( ! v) return "" ;
102 // The file id, whichever form the value takes — a raw id and a resolved URL of one photo share it.
103 if (v. startsWith ( "wix:image://" )) return v. slice ( "wix:image://v1/" . length ). split ( "/" )[ 0 ]. split ( "#" )[ 0 ];
104 const m = v. match ( / ^ https: \/\/ static \. wixstatic \. com \/ media \/ ( [ ^ /?#] + )/ );
105 return m ? m[ 1 ] : v;
106 }