-
Notifications
You must be signed in to change notification settings - Fork 83
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1743 from SpareBank1/fetch-svg-icons
feat(ffe-icons): legg til scripts for å hente svger fra material symbols
- Loading branch information
Showing
184 changed files
with
329 additions
and
248 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
name: update ffe-icons svgs | ||
|
||
on: | ||
workflow_dispatch: | ||
|
||
jobs: | ||
build: | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- name: Checkout repository | ||
uses: actions/checkout@v2 | ||
|
||
- name: Setup Node.js | ||
uses: actions/setup-node@v2 | ||
with: | ||
node-version: 18 | ||
|
||
- name: Install dependencies | ||
run: npm ci | ||
|
||
- name: Build icons | ||
run: npm run build:icons | ||
working-directory: packages/ffe-icons | ||
|
||
- name: Create Pull Request | ||
uses: peter-evans/create-pull-request@v3 | ||
with: | ||
token: ${{ secrets.GITHUB_TOKEN }} | ||
commit-message: 'fix(ffe-icons): oppdater ikon SVGer' | ||
title: 'feat(ffe-icons): oppdater ikon SVGer' | ||
body: "Oppdaterer SVG'ene i ffe-icons til å passe med de som er tilgjengelige i Material Symbols" | ||
branch: 'auto-update-icons' |
4 changes: 2 additions & 2 deletions
4
component-overview/examples/messages/message-box/InfoMessage-customIcon.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
import { InfoMessage } from '@sb1/ffe-message-box-react'; | ||
import { Paragraph } from '@sb1/ffe-core-react'; | ||
import { HandlevognIkon } from '@sb1/ffe-icons-react'; | ||
import Symbol from '@sb1/ffe-symbols-react'; | ||
|
||
<InfoMessage title="Handlevognen din er tom" icon={<HandlevognIkon title="Handlevogn, ikon" />}> | ||
<InfoMessage title="Handlevognen din er tom" icon={<Symbol ariaLabel="handlevogn" icon="shopping_cart" />}> | ||
<Paragraph>Du har ingenting i handlevognen din.</Paragraph> | ||
</InfoMessage> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,6 @@ | ||
import { LinkIcon } from '@sb1/ffe-core-react'; | ||
import { SnakkebobleIkon } from '@sb1/ffe-icons-react'; | ||
import Symbol from '@sb1/ffe-symbols-react'; | ||
|
||
<LinkIcon href="https://www.sparebank1.no"> | ||
<SnakkebobleIkon | ||
title="Snakk med oss" | ||
style={{ height: '80px' }} | ||
/> | ||
<Symbol ariaLabel="chat" icon="chat" /> | ||
</LinkIcon> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,60 +1,48 @@ | ||
#!/usr/bin/env node | ||
'use strict'; // eslint-disable-line strict | ||
|
||
const fs = require('fs'); | ||
const path = require('path'); | ||
const svgstore = require('svgstore'); | ||
const mkdirp = require('mkdirp'); | ||
|
||
const ICONS_PATH = path.join(__dirname, '..', 'icons'); | ||
|
||
// convenience to avoid having file extension in config | ||
const appendSvgExtension = icons => | ||
icons.map(name => (name.endsWith('.svg') ? name : `${name}.svg`)); | ||
|
||
const options = require('yargs') | ||
.config('opts') | ||
.options({ | ||
icons: { | ||
default: '**/*.svg', | ||
type: 'array', | ||
coerce: appendSvgExtension, | ||
}, | ||
projectIcons: { | ||
type: 'array', | ||
coerce: appendSvgExtension, | ||
}, | ||
dest: { | ||
default: 'dist', | ||
normalize: true, | ||
coerce: path.resolve, | ||
}, | ||
}).argv; | ||
|
||
const matchesIcon = icons => | ||
icons.includes('*.svg') || icons.includes('**/*.svg') | ||
? () => true | ||
: fileName => icons.includes(path.basename(fileName)); | ||
|
||
const sprite = svgstore(); | ||
|
||
fs.readdirSync(ICONS_PATH) | ||
.filter(fileName => fileName.match(/\.svg$/)) | ||
.filter(matchesIcon(options.icons)) | ||
.forEach(fileName => { | ||
const iconPath = path.join(ICONS_PATH, fileName); | ||
const iconName = path.basename(fileName, '.svg'); | ||
sprite.add(iconName, fs.readFileSync(iconPath), 'utf-8'); | ||
}); | ||
|
||
if (options.projectIcons) { | ||
options.projectIcons.forEach(fileName => { | ||
const iconPath = path.join(fileName); | ||
const iconName = path.basename(fileName, '.svg'); | ||
sprite.add(iconName, fs.readFileSync(iconPath), 'utf-8'); | ||
}); | ||
} | ||
|
||
mkdirp.sync(options.dest); | ||
|
||
fs.writeFileSync(path.join(options.dest, 'ffe-icons.svg'), sprite.toString()); | ||
const fs = require('fs'); | ||
const { makedirs } = require('./utils'); | ||
const { getIconNames } = require('./getIconNames'); | ||
const { getDownloads, downloadAll } = require('./downloadSvgs'); | ||
const { | ||
createListOfRemovedIcons, | ||
deleteRemovedIconsFiles, | ||
} = require('./deleteSvg'); | ||
|
||
(async () => { | ||
const weights = [300, 500]; | ||
const sizes = [ | ||
{ name: 'sm', opsz: 20 }, | ||
{ name: 'md', opsz: 24 }, | ||
{ name: 'lg', opsz: 40 }, | ||
{ name: 'xl', opsz: 48 }, | ||
]; | ||
const fill = [0, 1]; | ||
|
||
const iconNames = await getIconNames(); | ||
const listOfRemovedIcons = await createListOfRemovedIcons(iconNames); | ||
let downloads = []; | ||
|
||
for (const weight of weights) { | ||
for (const fillValue of fill) { | ||
const type = fillValue === 0 ? 'filled' : 'open'; | ||
for (const size of sizes) { | ||
const dirPath = path.resolve( | ||
__dirname, | ||
`../icons/${weight}/${type}/${size.name}`, | ||
); | ||
if (!fs.existsSync(dirPath)) { | ||
await makedirs(dirPath); | ||
} | ||
if (listOfRemovedIcons.length > 0) { | ||
await deleteRemovedIconsFiles(listOfRemovedIcons, dirPath); | ||
} | ||
downloads = downloads.concat( | ||
getDownloads(iconNames, weight, fillValue, size, dirPath), | ||
); | ||
} | ||
} | ||
} | ||
console.log('Downloading SVG files...'); | ||
await downloadAll(downloads); | ||
console.log('All done!'); | ||
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
const fs = require('fs/promises'); | ||
const path = require('path'); | ||
|
||
/* Function: createListOfRemovedIcons | ||
Creates and returns an array of all the filenames of svg-files that exist, | ||
but are no longer mentioned in the Material Symbols Codepoints. | ||
Since we know all the different subfolder / variations of the icons contain the same iconnames, | ||
we only need to check 1 folder. | ||
*/ | ||
const createListOfRemovedIcons = async iconNames => { | ||
const directory = path.resolve(__dirname, '../icons/300/filled/lg'); | ||
try { | ||
await fs.access(directory); | ||
const filesInDir = await fs.readdir(directory); | ||
const removedIcons = filesInDir.filter( | ||
fileName => !iconNames.includes(fileName.replace('.svg', '')), | ||
); | ||
return removedIcons; | ||
} catch (err) { | ||
console.log('Directory does not exist in check for removed icons'); | ||
return []; | ||
} | ||
}; | ||
|
||
/* Function: deleteSvgFile | ||
Does the actual deleting of the file | ||
*/ | ||
const deleteSvgFile = async fileName => { | ||
try { | ||
await fs.unlink(fileName); | ||
console.log(`Deleted file ${fileName}`); | ||
} catch (err) { | ||
console.error(`Failed to delete file ${fileName}: ${err}`); | ||
} | ||
}; | ||
|
||
/* Function: deleteRemovedIconsFiles | ||
Loop through the list of fileNames that should be deleted in a specific directory | ||
and call the delete function. | ||
*/ | ||
const deleteRemovedIconsFiles = async (listOfRemovedIcons, directory) => { | ||
for (const fileName of listOfRemovedIcons) { | ||
const filePath = path.join(directory, fileName); | ||
await deleteSvgFile(filePath); | ||
} | ||
}; | ||
|
||
module.exports = { createListOfRemovedIcons, deleteRemovedIconsFiles }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
const { apply, fileExists } = require('./utils'); | ||
const path = require('path'); | ||
const fs = require('fs/promises'); | ||
|
||
/* Function: generateDownloadUrl | ||
Takes the icon name, fill, weight and size, and generate the url to download the svg from. | ||
*/ | ||
const generateDownloadUrl = (iconName, fill, weight, size) => { | ||
let style = `wght${weight}${fill === 0 ? '' : 'fill1'}`; | ||
if (fill === 0 && weight === 400) { | ||
style = 'default'; // Can technically be removed since we're not supporting the standard value of 400 font weight. But keeping it in to make sure nothing break if we decide to add it later | ||
} | ||
return `https://fonts.gstatic.com/s/i/short-term/release/materialsymbolsrounded/${iconName}/${style}/${size}px.svg`; | ||
}; | ||
|
||
/* Function: getDownloads | ||
Returns an array for each variation of the icons with the download url, filename and filepath. | ||
The array is later used to know what to download and where to save the files. | ||
*/ | ||
const getDownloads = (iconNames, weight, fill, size, dirPath) => { | ||
const downloads = []; | ||
if (!iconNames || !weight || !size || fill === undefined) { | ||
throw new Error('iconNames, weight, fill or size is not provided'); | ||
} | ||
for (const icon of iconNames) { | ||
const safeIconName = icon; // Fix for icons that has a number as the first character - which is not valid const name | ||
downloads.push({ | ||
url: generateDownloadUrl(safeIconName, fill, weight, size.opsz), | ||
fileName: `${icon}.svg`, | ||
filePath: dirPath, | ||
}); | ||
} | ||
return downloads; | ||
}; | ||
|
||
/* Function: download | ||
Does the actual downloading of the file | ||
*/ | ||
const download = async downloadElement => { | ||
const { url, fileName, filePath } = downloadElement; | ||
const fileLocation = path.resolve(__dirname, `${filePath}/${fileName}`); | ||
try { | ||
console.log(`Downloading ${fileLocation}`); | ||
const response = await fetch(url); | ||
if (!response.ok) { | ||
throw new Error(`HTTP error! status: ${response.status}`); | ||
} | ||
const data = await response.text(); | ||
await fs.writeFile(fileLocation, data); | ||
} catch (error) { | ||
console.error( | ||
`Failed to download file from ${url} to ${fileLocation}. Error: ${error.message}`, | ||
); | ||
} | ||
}; | ||
|
||
/* Function: downloadAll | ||
Takes all the downloads, and apply the download function to each of them. | ||
Also let you set if you want to ignore existing files, this is on by default. | ||
*/ | ||
const downloadAll = async (downloads, { ignoreExisting = true } = {}) => { | ||
let allDownloads = []; | ||
if (ignoreExisting) { | ||
for (const file of downloads) { | ||
const fileAlreadyExists = await fileExists( | ||
`${file.filePath}/${file.fileName}`, | ||
); | ||
if (!fileAlreadyExists) { | ||
allDownloads.push(file); | ||
} | ||
} | ||
} else { | ||
allDownloads = downloads; | ||
} | ||
await apply(download, allDownloads); | ||
}; | ||
|
||
module.exports = { getDownloads, downloadAll }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/* Fetches all the icon names by using the codepoints in the font */ | ||
const getIconNames = async () => { | ||
let iconNames = []; | ||
const url = | ||
'https://raw.githubusercontent.com/google/material-design-icons/master/variablefont/MaterialSymbolsRounded%5BFILL%2CGRAD%2Copsz%2Cwght%5D.codepoints'; | ||
const response = await fetch(url); | ||
const data = await response.text(); | ||
|
||
const lines = data.split('\n'); | ||
const names = lines | ||
.filter(line => line.trim() !== '') | ||
.map(line => `${line.split(' ')[0]}`); | ||
iconNames = [...iconNames, ...names]; | ||
|
||
return iconNames; | ||
}; | ||
|
||
module.exports = { getIconNames }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
const fs = require('fs/promises'); | ||
|
||
/* Create directory */ | ||
const makedirs = async dir => { | ||
try { | ||
await fs.mkdir(dir, { recursive: true }); | ||
console.log(`Created ${dir}`); | ||
} catch (err) { | ||
throw new Error(`Failed to create directory ${dir}: ${err}`); | ||
} | ||
}; | ||
|
||
/* Function: apply | ||
Utility function to run an async function on an array of arguments in parallel with a concurrency limit. | ||
Used to download multiple files at once, but with a concurrency limit to avoid overloading the server. | ||
Arguments: | ||
- func: async function to run (download) | ||
- args: array of arguments to pass to the function (array of download objects) | ||
*/ | ||
const apply = async (func, args) => { | ||
const concurrency = 8; | ||
const results = []; | ||
let i = 0; | ||
const next = async () => { | ||
const j = i++; | ||
if (j >= args.length) { | ||
return; | ||
} | ||
try { | ||
results[j] = await func(args[j]); | ||
} catch (error) { | ||
console.error(error); | ||
} | ||
await next(); | ||
}; | ||
await Promise.all(Array.from({ length: concurrency }, next)); | ||
return results; | ||
}; | ||
|
||
/* Function: fileExists | ||
Checks if a file exists at the given path | ||
*/ | ||
const fileExists = async filePath => { | ||
try { | ||
await fs.access(filePath); | ||
return true; | ||
} catch (error) { | ||
// The file doesn't exist or there was an error accessing it | ||
return false; | ||
} | ||
}; | ||
|
||
module.exports = { makedirs, apply, fileExists }; |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.