Skip to content

Instantly share code, notes, and snippets.

@ivanbanov
Last active September 13, 2018 11:50
Show Gist options
  • Save ivanbanov/a7c18f0ccf14ed3e1f5a3a19a3887dc7 to your computer and use it in GitHub Desktop.
Save ivanbanov/a7c18f0ccf14ed3e1f5a3a19a3887dc7 to your computer and use it in GitHub Desktop.
ELM + SVG Store + SVGO + HTML injection
(() => {
const icons = document.createElement('div');
icons.innerHTML = require('images/icons.svg').match(/<svg.*<\/svg>/, '')[0];
icons.style.display = 'none';
document.body.appendChild(icons);
})();
module Icon exposing (view)
import Html
import Html.Attributes
import Svg exposing (..)
import Svg.Attributes exposing (..)
import Icons
view : List (Html.Attribute msg) -> (Icons.Icons -> String) -> Html.Html msg
view attributes icon =
Html.span
(attributes ++ [ Html.class "icon" ] )
[ svg []
[ use
[ xlinkHref <| "#" ++ icon Icons.icons ]
[]
]
]
const fs = require('fs');
const svgstore = require('svgstore');
var SVGO = require('svgo');
const ICONS_PATH = 'images/icons'; // svg icons folder
const ICON_MODULE_PATH = 'elm'; // export path for the elm file
const SRC_IMG_PATH = 'images'; // export path for the icons sprite
// Get all the icons
// -----------------------------------------------------------------------------
const iconsFileName = fs.readdirSync(ICONS_PATH, 'utf-8').filter(file => /\.svg/.test(file));
const icons = iconsFileName.map(icon => {
const file = fs.readFileSync(`${ICONS_PATH}/${icon}`, 'utf8');
return {
name: icon.split('.')[0],
file,
};
});
// Generated files banner
// -----------------------------------------------------------------------------
const warningBanner =`
██╗ ██╗ █████╗ ██████╗ ███╗ ██╗██╗███╗ ██╗ ██████╗
██║ ██║██╔══██╗██╔══██╗████╗ ██║██║████╗ ██║██╔════╝
██║ █╗ ██║███████║██████╔╝██╔██╗ ██║██║██╔██╗ ██║██║ ███╗
██║███╗██║██╔══██║██╔══██╗██║╚██╗██║██║██║╚██╗██║██║ ██║
╚███╔███╔╝██║ ██║██║ ██║██║ ╚████║██║██║ ╚████║╚██████╔╝
╚══╝╚══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝╚═╝ ╚═══╝ ╚═════╝
This file is generated in runtime, any change shall be done in the script
webpack/scripts/icons-build.js
`;
// Read and optimize all icons and then add to the store with the icon name
// -----------------------------------------------------------------------------
const svgo = new SVGO({
plugins: [
{ removeAttrs: { attrs: 'fill|stroke' } },
{ removeViewBox: false },
],
});
let store = svgstore();
const optimizedIcons = icons.map(icon => {
svgo
.optimize(icon.file)
.then(result => {
store.add(icon.name, result.data);
});
});
// String helpers
// -----------------------------------------------------------------------------
function toCamelCase(str) {
return str.replace(
/[_.-](\w|$)/g,
(_, x) => x.toUpperCase()
);
}
// Create needed files and folders if it don't exist
// -----------------------------------------------------------------------------
for (let dir of [paths.dist, SRC_IMG_PATH, ICON_MODULE_PATH]) {
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}
}
fs.writeFileSync(`${SRC_IMG_PATH}/icons.svg`, '<svg></svg>');
// create Elm module UI.Icon.Icons
// -----------------------------------------------------------------------------
const elmModuleFile = fs.readFileSync(`${paths.webpack}/scripts/ui-icons-elm-template.elm`, 'utf8');
const elmIconsRecord = icons.reduce((record, icon) => {
record[toCamelCase(icon.name)] = icon.name;
return record;
}, {});
const elmIconsTypeRecord =
JSON.stringify(elmIconsRecord)
.replace(/"/g, '')
.replace('{', ' ')
.replace(/,/g, '\n , ')
.replace(/:.*"?/g, ' : String')
+ '\n ';
const elmIcons =
JSON.stringify(elmIconsRecord)
.replace(/"/g, '')
.replace('{', ' ')
.replace(/,/g, '"\n , ')
.replace(/:/g, ' = "')
.replace('}', '"')
+ '\n ';
fs.writeFileSync(
`${ICON_MODULE_PATH}/Icons.elm`, `{-${warningBanner}-}\n\n\n` + (
elmModuleFile
.replace('{- ICONS_TYPE_RECORD_PLACEHOLDER -}', elmIconsTypeRecord)
.replace('{- ICONS_PLACEHOLDER -}', elmIcons)
)
);
// Create the icons sprite
// -----------------------------------------------------------------------------
Promise
.all(optimizedIcons)
.then(() => fs.writeFileSync(
`${SRC_IMG_PATH}/icons.svg`,
store.toString().replace(/(^<\?xml.*\?>)/, `$1\n<!--${warningBanner}-->\n\n\n`)
));
.icon {
vertical-align: baseline;
fill: currentColor;
position: relative;
display: inline-flex;
}
.icon svg {
display: block;
overflow: visible;
transform: translateZ(0);
width: 1em;
height: 1em;
}
module Icons exposing (Icons)
type alias Icons =
{{- ICONS_TYPE_RECORD_PLACEHOLDER -}}
icon : Icons
icon =
{{- ICONS_RECORD_PLACEHOLDER -}}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment