Skip to content

fix(styles): extend icon map generation to support UI icons in post-icon mixin #5909

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

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/documentation/.storybook/styles/preview.scss
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ $monospace: SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier

// Mixin for toolbar button icons
@mixin toolbar-icon($icon-name) {
@include post.post-icon($name: $icon-name, $base: '/post-icons');
@include post.post-icon($name: $icon-name);
content: '';
margin-right: 0.5rem;
width: 1em;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ $resizer-max-width: 64px;
}

.my-1022-icon {
@include post.post-icon($name: '1022', $base: '/post-icons');
@include post.post-icon($name: '1022');
}

.my-accessibility-icon {
@include post.post-icon($name: 'accessibility', $base: '/post-icons');
@include post.post-icon($name: 'accessibility');
}

.my-handvictory-icon {
@include post.post-icon($name: 'handvictory', $base: '/post-icons', $color: #666666);
@include post.post-icon($name: 'handvictory');
}
147 changes: 146 additions & 1 deletion packages/styles/gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,151 @@ gulp.task('map-icons', done => {
done();
});

/**
* Create a SCSS icon map from UI icons with size detection
*/
gulp.task('map-ui-icons', done => {
const iconFiles = globSync('node_modules/@swisspost/design-system-icons/src/icons/ui/*');
const BATCH_SIZE = 400;
const allIconGroups = {};
const iconSizes = new Set();

// Group files into batches
const batches = [];
for (let i = 0; i < iconFiles.length; i += BATCH_SIZE) {
batches.push(iconFiles.slice(i, i + BATCH_SIZE));
}

// Process each batch
batches.forEach((batch, batchIndex) => {
batch.forEach(iconPath => {
const fileName = path.basename(iconPath);

if (fs.statSync(iconPath).isDirectory()) {
return;
}

const baseFileName = fileName.replace(/\.svg$/, '');
const sizeMatch = baseFileName.match(/_(\d+)$/);

if (sizeMatch) {
const size = sizeMatch[1];
let baseName = baseFileName.replace(`_${size}`, '');

// Transform the name
if (baseName.includes('_Solid_Shape')) {
baseName = baseName.replace(/_Solid_Shape$/, '').replace(/_/g, '').toLowerCase() + '-solid';
} else if (baseName.includes('_Solid')) {
baseName = baseName.replace(/_Solid$/, '').replace(/_/g, '').toLowerCase() + '-solid';
} else {
baseName = baseName.replace(/_Shape$/, '').replace(/_/g, '').toLowerCase();
}

iconSizes.add(size);

if (!allIconGroups[baseName]) {
allIconGroups[baseName] = {};
}

try {
const fileContent = fs.readFileSync(iconPath, 'utf8');

if (!fileContent.trim().startsWith('<?xml') && !fileContent.trim().startsWith('<svg')) {
return;
}

const iconSvg = fileContent
.split(/\r?\n/)
.map(line => line.trim())
.join('')
.replace(/"/g, "'")
.replace(/ fill='(none|currentColor)'/g, '')
.replace(/</g, '%3C')
.replace(/>/g, '%3E')
.replace(/#/g, '%23')
.replace(/\(/g, '%28')
.replace(/\)/g, '%29');

allIconGroups[baseName][size] = `"data:image/svg+xml,${iconSvg}"`;
} catch (error) {
// Skip problematic files
}
}
});

// Force garbage collection after each batch (if available)
if (global.gc) {
global.gc();
}
});

// Generate SCSS map
let scssContent = `$ui-icon-map: (\n`;

// Process icon groups in smaller chunks for final output
const iconNames = Object.keys(allIconGroups).sort();
const CHUNK_SIZE = 50;

for (let i = 0; i < iconNames.length; i += CHUNK_SIZE) {
const chunk = iconNames.slice(i, i + CHUNK_SIZE);

chunk.forEach(iconName => {
const sizes = allIconGroups[iconName];
scssContent += ` '${iconName}': (\n`;

Object.keys(sizes).sort((a, b) => Number(a) - Number(b)).forEach(size => {
scssContent += ` '${size}': ${sizes[size]},\n`;
});

scssContent += ` ),\n`;
});

// Force garbage collection every chunk
if (global.gc) {
global.gc();
}
}

scssContent += `);\n\n`;

// Add helper function
scssContent += `@function get-ui-icon($name, $size: '24') {\n`;
scssContent += ` @if map-has-key($ui-icon-map, $name) {\n`;
scssContent += ` $icon-sizes: map-get($ui-icon-map, $name);\n`;
scssContent += ` @if map-has-key($icon-sizes, $size) {\n`;
scssContent += ` @return map-get($icon-sizes, $size);\n`;
scssContent += ` } @else {\n`;
scssContent += ` $first-size: nth(map-keys($icon-sizes), 1);\n`;
scssContent += ` @return map-get($icon-sizes, $first-size);\n`;
scssContent += ` }\n`;
scssContent += ` } @else {\n`;
scssContent += ` @error "Icon '#{$name}' not found";\n`;
scssContent += ` }\n`;
scssContent += `}\n\n`;

// Add UI icon mixin
scssContent += `@mixin ui-icon($name, $width: 1em, $height: $width, $color: currentColor) {\n`;
scssContent += ` $size: '24';\n`;
scssContent += ` @if type-of($width) == number and unit($width) == 'px' {\n`;
scssContent += ` $size: '#{round($width / 1px)}';\n`;
scssContent += ` } @else if $width == 1rem or $width == 16px { $size: '16'; }\n`;
scssContent += ` @else if $width == 1.5rem or $width == 24px { $size: '24'; }\n`;
scssContent += ` @else if $width == 3rem or $width == 48px { $size: '48'; }\n`;
scssContent += ` display: inline-block;\n`;
scssContent += ` width: $width;\n`;
scssContent += ` height: $height;\n`;
scssContent += ` vertical-align: -0.15em;\n`;
scssContent += ` background-color: $color;\n`;
scssContent += ` $icon-url: get-ui-icon($name, $size);\n`;
scssContent += ` -webkit-mask: url('#{$icon-url}') center/contain no-repeat;\n`;
scssContent += ` mask: url('#{$icon-url}') center/contain no-repeat;\n`;
scssContent += `}\n`;

fs.writeFileSync(path.join('./src', '_ui-icon-map.scss'), scssContent, 'utf8');

done();
});

/**
* Transform `package.json` of the published subdirectory
*
Expand Down Expand Up @@ -193,7 +338,7 @@ exports.default = gulp.task(
gulp.series(
'prebuild-env-vars',
gulp.parallel(
gulp.series('map-icons', 'copy', 'autoprefixer', 'transform-package-json'),
gulp.series('map-icons', 'map-ui-icons', 'copy', 'autoprefixer', 'transform-package-json'),
gulp.series('temporarily-copy-token-files', 'sass'),
),
),
Expand Down
Loading
Loading