-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Responsive Images #124
base: main
Are you sure you want to change the base?
Responsive Images #124
Changes from all commits
43cfd43
0f45d87
a7c63fb
f801b97
fcdbdfe
2aa6bfc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
/** Customizable Configuration for the Generator */ | ||
export const config = { | ||
/** Responsive image size bounds (max width/ max height in px) */ | ||
imageSizes: [400, 600, 800, 1000], | ||
} as const; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,8 @@ | |
"files": [ | ||
"docs/", | ||
"generated/*", | ||
"src/" | ||
"src/", | ||
"./generator-config.ts" | ||
Comment on lines
+9
to
+10
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. publish config |
||
], | ||
"types": "generated/design-tokens.types.ts", | ||
"repository": { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ import axios from 'axios'; | |
import { parseData } from './lib/token-parser'; | ||
import { TokenLayer, TokenLayers } from './lib/token-parser.types'; | ||
import { isVariableReference } from './lib/token-parser.utils'; | ||
import { config } from '../generator-config'; | ||
|
||
const MEDIA_FILES_PATH = './generated'; | ||
|
||
|
@@ -37,18 +38,37 @@ const findAssetSizeAndDimensions = async (url: string) => { | |
console.warn(`Error downloading ${url}`); | ||
throw e; | ||
}) | ||
.then((response) => { | ||
.then(async (response) => { | ||
console.info('downloaded. converting to webp'); | ||
return sharp(Buffer.from(response.data)) | ||
.webp() | ||
.toBuffer({ resolveWithObject: true }) | ||
.catch((e) => { | ||
console.warn(`Error parsing ${url}`); | ||
throw e; | ||
}); | ||
}) | ||
.then(({ data, info }) => { | ||
return { data, info }; | ||
const image = sharp(Buffer.from(response.data)); | ||
|
||
return Promise.all( | ||
[...config.imageSizes, 'full' as const].map((width) => | ||
image | ||
.resize( | ||
width === 'full' // full res version | ||
? undefined | ||
: { | ||
// resize to max-size, this might sometimes create duplicates, | ||
// if the source image is small, but this makes the | ||
width: width, | ||
height: width, | ||
fit: 'inside', | ||
withoutEnlargement: true, | ||
} | ||
) | ||
Comment on lines
+48
to
+59
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. resize based on config and combine with full-res version |
||
.webp() | ||
.toBuffer({ resolveWithObject: true }) | ||
.then((v) => ({ | ||
...v, | ||
width, | ||
})) | ||
.catch((e) => { | ||
console.warn(`Error parsing ${url}`); | ||
throw e; | ||
}) | ||
) | ||
); | ||
}); | ||
}; | ||
|
||
|
@@ -64,6 +84,12 @@ const filterUnique = | |
return acc; | ||
}; | ||
|
||
// Promise to delay items in batches, e.g. to avoid upstream rate-limiting | ||
const batchDelay = (requestIndex: number, batchSize = 20) => | ||
new Promise<void>((resolve) => { | ||
setTimeout(() => resolve(), Math.floor(requestIndex / batchSize) * 20); | ||
}); | ||
|
||
loadLayersFile(snapshotFileName) | ||
.then(({ layers, order }) => { | ||
return order | ||
|
@@ -85,13 +111,17 @@ loadLayersFile(snapshotFileName) | |
.then((assets) => | ||
// for every asset, download it and convert it to webp | ||
Promise.all( | ||
assets.map(({ value, ...rest }) => | ||
findAssetSizeAndDimensions(value).then((metadata) => ({ | ||
...rest, | ||
...metadata, | ||
})) | ||
assets.flatMap(({ value, ...rest }, i) => | ||
batchDelay(i) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
.then(() => findAssetSizeAndDimensions(value)) | ||
.then((variations) => | ||
variations.map((variation) => ({ | ||
...rest, | ||
...variation, | ||
})) | ||
) | ||
) | ||
) | ||
).then((v) => v.flat()) | ||
) | ||
.then((downloadedAssets) => { | ||
// find all the directories we need to create | ||
|
@@ -133,13 +163,16 @@ const saveFiles = < | |
data: Buffer; | ||
name: string; | ||
path: string; | ||
width?: number | string; | ||
} | ||
>( | ||
downloadedAssets: T[] | ||
): Promise<void[]> => | ||
Promise.all( | ||
downloadedAssets.map(({ name, data, path }) => { | ||
const fileName = `${assetSavePath(path)}/${name}.webp`; | ||
downloadedAssets.map(({ name, data, path, width }) => { | ||
const fileName = `${assetSavePath(path)}/${name}${ | ||
width && width !== 'full' ? `_w${width}` : '' | ||
}.webp`; | ||
console.info(`SAVING ${fileName}`); | ||
return promises.writeFile(fileName, data); | ||
}) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { config } from '../../generator-config'; | ||
|
||
/** expands `src` to `srcSet` using the configured image sized */ | ||
export const srcToSrcSet = (src: string) => { | ||
const isLocalImage = src.startsWith('/') || src.startsWith('.'); | ||
if (!isLocalImage) { | ||
return { | ||
src, | ||
}; | ||
} | ||
const withoutExtension = src.replace('.webp', ''); | ||
const srcSet = config.imageSizes | ||
.map((s) => `${withoutExtension}_w${s}.webp ${s}w`) | ||
.join(', '); | ||
|
||
return { | ||
srcSet, | ||
src, | ||
}; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
import React, { forwardRef } from 'react'; | ||
import { cx } from '@emotion/css'; | ||
import { radiusTokens } from '@rangle/radius-foundations/generated/design-tokens.constants'; | ||
import { srcToSrcSet } from '@rangle/radius-foundations/src/helpers/src-to-src-set'; | ||
import { | ||
RadiusAutoLayout, | ||
Typography, | ||
|
@@ -75,7 +76,7 @@ export const RadiusImageTextItem = forwardRef< | |
> | ||
<RadiusAutoLayout | ||
as="img" | ||
src={src} | ||
{...srcToSrcSet(src)} | ||
Comment on lines
-78
to
+79
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use helper to auto-generate sourceset |
||
alt={alt} | ||
width="fill-parent" | ||
height="fill-parent" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This config can be reused to configure the generator based on project needs, e.g. as an alternative to special tokens