From 78cb835861e4eb4af174421e7b97b2ea4e5b212e Mon Sep 17 00:00:00 2001 From: Julia Miocene Date: Fri, 4 Apr 2025 17:39:23 +0200 Subject: [PATCH 1/2] Update illustration sprite and previews --- build_scripts/index.js | 9 +- .../src/{svg_sprite.js => icon_sprite.js} | 7 +- build_scripts/src/illustration_sprite.js | 117 ++++++++++++++++++ build_scripts/src/illustrations.js | 2 +- svgpreviewer/components/svg_album.vue | 13 +- svgpreviewer/pages/illustrations.vue | 4 + 6 files changed, 138 insertions(+), 14 deletions(-) rename build_scripts/src/{svg_sprite.js => icon_sprite.js} (92%) create mode 100644 build_scripts/src/illustration_sprite.js diff --git a/build_scripts/index.js b/build_scripts/index.js index 5ae44c9a..c6f6e815 100644 --- a/build_scripts/index.js +++ b/build_scripts/index.js @@ -2,7 +2,8 @@ const path = require('path'); const rimraf = require('rimraf'); const { optimizeSVGs } = require('./src/svg_optimization'); -const { createSvgSprite } = require('./src/svg_sprite'); +const { createIconSprite } = require('./src/icon_sprite'); +const { createImageSprite } = require('./src/illustration_sprite'); const { copyFolderRecursive, copyFile } = require('./src/utils'); const { collectIllustrations } = require('./src/illustrations'); const { collectLogos } = require('./src/logos'); @@ -15,7 +16,7 @@ const STATIC_PATH = path.normalize(path.join(BASE_PATH, 'svgpreviewer', 'static' async function buildFiles() { console.log('Creating Icon Sprite...'); - await createSvgSprite( + await createIconSprite( BASE_PATH, DIST_PATH, path.join(BASE_PATH, 'sprite_icons', '*.svg'), @@ -24,7 +25,7 @@ async function buildFiles() { console.log('Created Icon Sprite'); console.log('Creating File Icon Sprite...'); - await createSvgSprite( + await createIconSprite( BASE_PATH, FILE_ICONS_DIST_PATH, path.join(BASE_PATH, 'file_icons', '*.svg'), @@ -33,7 +34,7 @@ async function buildFiles() { console.log('Created File Icon Sprite'); console.log('Creating Illustration Sprite...'); - await createSvgSprite( + await createImageSprite( BASE_PATH, DIST_PATH, path.join(BASE_PATH, 'illustrations', '*.svg'), diff --git a/build_scripts/src/svg_sprite.js b/build_scripts/src/icon_sprite.js similarity index 92% rename from build_scripts/src/svg_sprite.js rename to build_scripts/src/icon_sprite.js index 8b7a0fbe..a863743b 100644 --- a/build_scripts/src/svg_sprite.js +++ b/build_scripts/src/icon_sprite.js @@ -6,12 +6,9 @@ const SVGSpriter = require('svg-sprite'); const { getFilesizeInBytes } = require('./utils'); // eslint-disable-next-line max-params -const createSvgSprite = (BASE_PATH, destPath, globPattern, targetFile, additionalGlobPattern) => { +const createIconSprite = (BASE_PATH, destPath, globPattern, targetFile) => { const spriteFiles = glob.sync(globPattern); - if (additionalGlobPattern) - spriteFiles.push(...glob.sync(additionalGlobPattern)) - const spriter = new SVGSpriter({ dest: destPath, shape: { @@ -107,4 +104,4 @@ const createSvgSprite = (BASE_PATH, destPath, globPattern, targetFile, additiona }); }; -module.exports = { createSvgSprite }; +module.exports = { createIconSprite }; diff --git a/build_scripts/src/illustration_sprite.js b/build_scripts/src/illustration_sprite.js new file mode 100644 index 00000000..604b8615 --- /dev/null +++ b/build_scripts/src/illustration_sprite.js @@ -0,0 +1,117 @@ +const fs = require('fs'); +const path = require('path'); +const glob = require('glob'); +const mkdirp = require('mkdirp'); +const SVGSpriter = require('svg-sprite'); +const { getFilesizeInBytes } = require('./utils'); + +// eslint-disable-next-line max-params +const createImageSprite = (BASE_PATH, destPath, globPattern, targetFile, extraGlobPattern) => { + const spriteFiles = glob.sync(globPattern); + spriteFiles.push(...glob.sync(extraGlobPattern)); + + const spriter = new SVGSpriter({ + dest: destPath, + shape: { + dimension: { + maxWidth: 288, + maxHeight: 288, + attributes: true, + }, + transform: [ + { + svgo: { + /* + The following optimizations have been turned off because they apparently break our: + echarts icon rendering: + - https://github.com/apache/incubator-echarts/issues/11087 + - https://gitlab.com/gitlab-org/gitlab-svgs/issues/73 + */ + plugins: [ + { convertPathData: { noSpaceAfterFlags: false } }, + { mergePaths: { noSpaceAfterFlags: false } }, + ], + }, + }, + ], + }, + mode: { + inline: true, // Prepare for inline embedding + stack: { + example: false, + dest: '', + sprite: `${targetFile}.svg`, + }, + }, + svg: { + namespaceClassnames: false, + }, + }); + + const illustrations = []; + + function getIllustrationInfo(name) { + let size = 0; + + if (name.endsWith('-lg')) size = 288; + else if (name.endsWith('-md')) size = 144; + else if (name.endsWith('-sm')) size = 72; + + if (size !== 0) return { name, svg_size: size }; + return false; + } + + spriteFiles.forEach((file) => { + const filePath = path.resolve(file); + const illustrationInfo = getIllustrationInfo(path.basename(file, '.svg')); + if (illustrationInfo) { + spriter.add( + filePath, + null, + fs.readFileSync(filePath, { + encoding: 'utf-8', + }), + ); + illustrations.push(illustrationInfo); + } + }); + + // Compile the sprite + return new Promise((resolve, reject) => { + spriter.compile((error, result) => { + if (error) { + return reject(error); + } + + try { + Object.values(result).forEach((mode) => { + Object.values(mode).forEach((resource) => { + mkdirp.sync(path.dirname(resource.path)); + fs.writeFileSync(resource.path, resource.contents); + + console.log(`Compiled - Saving to ${resource.path}`); + }); + }); + + // Save the Illustrations in here to a json so we can then display a nice help sprite sheet in GitLab + const illustrationsInfo = { + iconCount: illustrations.length, + spriteSize: getFilesizeInBytes(path.join(destPath, `${targetFile}.svg`)), + illustrations, + }; + + fs.writeFileSync( + path.join(destPath, `${targetFile}.json`), + JSON.stringify(illustrationsInfo, null, 2), + 'utf8', + ); + } catch (e) { + return reject(e); + } + + return resolve(); + }); + }); +}; + +module.exports = { createImageSprite }; diff --git a/build_scripts/src/illustrations.js b/build_scripts/src/illustrations.js index 4172847d..13f0f103 100644 --- a/build_scripts/src/illustrations.js +++ b/build_scripts/src/illustrations.js @@ -5,7 +5,7 @@ const { optimizeSVGs } = require('./svg_optimization'); const { getIllustrationStats } = require('./utils'); const collectIllustrations = async (basePath, distPath) => { - const statsFile = path.join(distPath, 'illustrations.json'); + const statsFile = path.join(distPath, 'illustrations_individual.json'); console.log('Optimizing SVG illustrations...'); const childSVGs = await optimizeSVGs( diff --git a/svgpreviewer/components/svg_album.vue b/svgpreviewer/components/svg_album.vue index bb6695ac..fdf94aac 100644 --- a/svgpreviewer/components/svg_album.vue +++ b/svgpreviewer/components/svg_album.vue @@ -69,6 +69,11 @@ export default { required: false, default: 'Click entry to copy their name', }, + sizeInJson: { + type: Boolean, + required: false, + default: false, + }, }, data() { return { @@ -79,7 +84,7 @@ export default { computed: { ...mapQueryFieldsToComputed(queryFields), filteredItems() { - if (this.imageSprite) { + if (this.imageSprite && !this.sizeInJson) { if (this.searchString && this.searchString.startsWith('~')) { return this.items.filter((icon) => `~${icon}` === this.searchString); } @@ -165,11 +170,11 @@ export default { > -- GitLab From f5eed74cd7764180ed17a4e35951a45739c44acd Mon Sep 17 00:00:00 2001 From: Julia Miocene Date: Tue, 8 Apr 2025 14:15:21 +0200 Subject: [PATCH 2/2] Apply suggestions --- build_scripts/index.js | 47 +++---- build_scripts/src/illustration_sprite.js | 117 ------------------ .../src/{icon_sprite.js => svg_sprite.js} | 95 ++++++++++---- svgpreviewer/components/svg_album.vue | 26 ++-- svgpreviewer/pages/illustrations.vue | 3 +- 5 files changed, 104 insertions(+), 184 deletions(-) delete mode 100644 build_scripts/src/illustration_sprite.js rename build_scripts/src/{icon_sprite.js => svg_sprite.js} (65%) diff --git a/build_scripts/index.js b/build_scripts/index.js index c6f6e815..38c6ea7f 100644 --- a/build_scripts/index.js +++ b/build_scripts/index.js @@ -2,45 +2,48 @@ const path = require('path'); const rimraf = require('rimraf'); const { optimizeSVGs } = require('./src/svg_optimization'); -const { createIconSprite } = require('./src/icon_sprite'); -const { createImageSprite } = require('./src/illustration_sprite'); +const { createSvgSprite } = require('./src/svg_sprite'); const { copyFolderRecursive, copyFile } = require('./src/utils'); const { collectIllustrations } = require('./src/illustrations'); const { collectLogos } = require('./src/logos'); const BASE_PATH = path.join(__dirname, '..'); - const DIST_PATH = path.join(BASE_PATH, 'dist'); const FILE_ICONS_DIST_PATH = path.join(DIST_PATH, 'file_icons'); const STATIC_PATH = path.normalize(path.join(BASE_PATH, 'svgpreviewer', 'static')); async function buildFiles() { console.log('Creating Icon Sprite...'); - await createIconSprite( - BASE_PATH, - DIST_PATH, - path.join(BASE_PATH, 'sprite_icons', '*.svg'), - 'icons', - ); + await createSvgSprite({ + destPath: DIST_PATH, + globPatterns: [path.join(BASE_PATH, 'sprite_icons', '*.svg')], + targetFile: 'icons', + }); console.log('Created Icon Sprite'); console.log('Creating File Icon Sprite...'); - await createIconSprite( - BASE_PATH, - FILE_ICONS_DIST_PATH, - path.join(BASE_PATH, 'file_icons', '*.svg'), - 'file_icons', - ); + await createSvgSprite({ + destPath: FILE_ICONS_DIST_PATH, + globPatterns: [path.join(BASE_PATH, 'file_icons', '*.svg')], + targetFile: 'file_icons', + }); console.log('Created File Icon Sprite'); console.log('Creating Illustration Sprite...'); - await createImageSprite( - BASE_PATH, - DIST_PATH, - path.join(BASE_PATH, 'illustrations', '*.svg'), - 'illustrations', - path.join(BASE_PATH, 'illustrations/!(logos|third-party-logos)', '**', '**.svg'), - ); + await createSvgSprite({ + destPath: DIST_PATH, + globPatterns: [ + path.join(BASE_PATH, 'illustrations', '*.svg'), + path.join(BASE_PATH, 'illustrations/!(logos|third-party-logos)', '**', '**.svg'), + ], + targetFile: 'illustrations', + addDimension: true, + svgSizes: { + sm: 72, + md: 144, + lg: 288, + }, + }); console.log('Created Illustration Sprite'); console.log('Optimizing icons...'); diff --git a/build_scripts/src/illustration_sprite.js b/build_scripts/src/illustration_sprite.js deleted file mode 100644 index 604b8615..00000000 --- a/build_scripts/src/illustration_sprite.js +++ /dev/null @@ -1,117 +0,0 @@ -const fs = require('fs'); -const path = require('path'); -const glob = require('glob'); -const mkdirp = require('mkdirp'); -const SVGSpriter = require('svg-sprite'); -const { getFilesizeInBytes } = require('./utils'); - -// eslint-disable-next-line max-params -const createImageSprite = (BASE_PATH, destPath, globPattern, targetFile, extraGlobPattern) => { - const spriteFiles = glob.sync(globPattern); - spriteFiles.push(...glob.sync(extraGlobPattern)); - - const spriter = new SVGSpriter({ - dest: destPath, - shape: { - dimension: { - maxWidth: 288, - maxHeight: 288, - attributes: true, - }, - transform: [ - { - svgo: { - /* - The following optimizations have been turned off because they apparently break our: - echarts icon rendering: - - https://github.com/apache/incubator-echarts/issues/11087 - - https://gitlab.com/gitlab-org/gitlab-svgs/issues/73 - */ - plugins: [ - { convertPathData: { noSpaceAfterFlags: false } }, - { mergePaths: { noSpaceAfterFlags: false } }, - ], - }, - }, - ], - }, - mode: { - inline: true, // Prepare for inline embedding - stack: { - example: false, - dest: '', - sprite: `${targetFile}.svg`, - }, - }, - svg: { - namespaceClassnames: false, - }, - }); - - const illustrations = []; - - function getIllustrationInfo(name) { - let size = 0; - - if (name.endsWith('-lg')) size = 288; - else if (name.endsWith('-md')) size = 144; - else if (name.endsWith('-sm')) size = 72; - - if (size !== 0) return { name, svg_size: size }; - return false; - } - - spriteFiles.forEach((file) => { - const filePath = path.resolve(file); - const illustrationInfo = getIllustrationInfo(path.basename(file, '.svg')); - if (illustrationInfo) { - spriter.add( - filePath, - null, - fs.readFileSync(filePath, { - encoding: 'utf-8', - }), - ); - illustrations.push(illustrationInfo); - } - }); - - // Compile the sprite - return new Promise((resolve, reject) => { - spriter.compile((error, result) => { - if (error) { - return reject(error); - } - - try { - Object.values(result).forEach((mode) => { - Object.values(mode).forEach((resource) => { - mkdirp.sync(path.dirname(resource.path)); - fs.writeFileSync(resource.path, resource.contents); - - console.log(`Compiled - Saving to ${resource.path}`); - }); - }); - - // Save the Illustrations in here to a json so we can then display a nice help sprite sheet in GitLab - const illustrationsInfo = { - iconCount: illustrations.length, - spriteSize: getFilesizeInBytes(path.join(destPath, `${targetFile}.svg`)), - illustrations, - }; - - fs.writeFileSync( - path.join(destPath, `${targetFile}.json`), - JSON.stringify(illustrationsInfo, null, 2), - 'utf8', - ); - } catch (e) { - return reject(e); - } - - return resolve(); - }); - }); -}; - -module.exports = { createImageSprite }; diff --git a/build_scripts/src/icon_sprite.js b/build_scripts/src/svg_sprite.js similarity index 65% rename from build_scripts/src/icon_sprite.js rename to build_scripts/src/svg_sprite.js index a863743b..221f1794 100644 --- a/build_scripts/src/icon_sprite.js +++ b/build_scripts/src/svg_sprite.js @@ -6,16 +6,52 @@ const SVGSpriter = require('svg-sprite'); const { getFilesizeInBytes } = require('./utils'); // eslint-disable-next-line max-params -const createIconSprite = (BASE_PATH, destPath, globPattern, targetFile) => { - const spriteFiles = glob.sync(globPattern); +const createSvgSprite = ({ + destPath, + globPatterns, + targetFile, + addDimension = false, + svgSizes = {}, +} = {}) => { + const spriteFiles = []; + + globPatterns.forEach((pattern) => { + spriteFiles.push(...glob.sync(pattern)); + }); + + function getSpriteModes() { + if (addDimension) { + return { + inline: true, // Prepare for inline embedding + stack: { + example: false, + dest: '', + sprite: `${targetFile}.svg`, + }, + }; + } + return { + inline: true, // Prepare for inline embedding + symbol: { + example: false, + dest: '', + sprite: `${targetFile}.svg`, + }, + stack: { + example: false, + dest: '', + sprite: `${targetFile}-stacked.svg`, + }, + }; + } const spriter = new SVGSpriter({ dest: destPath, shape: { dimension: { - maxWidth: 200, - maxHeight: 200, - attributes: false, + maxWidth: 288, + maxHeight: 288, + attributes: addDimension, }, transform: [ { @@ -34,36 +70,41 @@ const createIconSprite = (BASE_PATH, destPath, globPattern, targetFile) => { }, ], }, - mode: { - inline: true, // Prepare for inline embedding - symbol: { - example: false, - dest: '', - sprite: `${targetFile}.svg`, - }, - stack: { - example: false, - dest: '', - sprite: `${targetFile}-stacked.svg`, - }, - }, + mode: getSpriteModes(), svg: { namespaceClassnames: false, }, }); + function getSvgNameAndSize(name) { + let size = 0; + + if (name.endsWith('-lg')) size = svgSizes.lg; + else if (name.endsWith('-md')) size = svgSizes.md; + else if (name.endsWith('-sm')) size = svgSizes.sm; + + if (size !== 0) return { name, svg_size: size }; + return false; + } + const icons = []; spriteFiles.forEach((file) => { const filePath = path.resolve(file); - spriter.add( - filePath, - null, - fs.readFileSync(filePath, { - encoding: 'utf-8', - }), - ); - icons.push(path.basename(file, '.svg')); + const svgInfo = addDimension + ? getSvgNameAndSize(path.basename(file, '.svg')) + : path.basename(file, '.svg'); + + if (svgInfo) { + spriter.add( + filePath, + null, + fs.readFileSync(filePath, { + encoding: 'utf-8', + }), + ); + icons.push(svgInfo); + } }); // Compile the sprite @@ -104,4 +145,4 @@ const createIconSprite = (BASE_PATH, destPath, globPattern, targetFile) => { }); }; -module.exports = { createIconSprite }; +module.exports = { createSvgSprite }; diff --git a/svgpreviewer/components/svg_album.vue b/svgpreviewer/components/svg_album.vue index fdf94aac..4c955686 100644 --- a/svgpreviewer/components/svg_album.vue +++ b/svgpreviewer/components/svg_album.vue @@ -69,11 +69,6 @@ export default { required: false, default: 'Click entry to copy their name', }, - sizeInJson: { - type: Boolean, - required: false, - default: false, - }, }, data() { return { @@ -84,16 +79,15 @@ export default { computed: { ...mapQueryFieldsToComputed(queryFields), filteredItems() { - if (this.imageSprite && !this.sizeInJson) { - if (this.searchString && this.searchString.startsWith('~')) { - return this.items.filter((icon) => `~${icon}` === this.searchString); - } - return this.items.filter((icon) => icon.includes(this.searchString)); - } if (this.searchString && this.searchString.startsWith('~')) { - return this.items.filter((icon) => `~${icon.name}` === this.searchString); + return this.items.filter( + (item) => `~${item.name ? item.name : item}` === this.searchString, + ); } - return this.items.filter((icon) => icon.name.includes(this.searchString)); + return this.items.filter((item) => { + const name = item.name ? item.name : item; + return name.includes(this.searchString); + }); }, copyStatusText() { switch (this.copyStatus) { @@ -170,11 +164,11 @@ export default { >