Open gcloeval opened 5 years ago
Hi @gcloeval,
You can dynamically change icons themes, if you are using generic camelCased icon names (without concrete theme prefixes). Such React components supports theme
property. And you can pass Icons as parameters with dynamic themes support.
Here is a small example of custom component with Icon passed as parameter and alternative CSS customization instead of supported Icon component parameters:
export const MyButton = ({icon: Icon, theme: {iconsTheme} = {}, caption}) =>
<button>
<Icon theme={iconsTheme} className="icon"/>
{caption}
</button>;
button svg {
width: 16px;
height: 16px;
margin-right: 8px;
}
button .icon {
color: red;
opacity: .54;
}
button:hover .icon, button:focus .icon {
opacity: .87;
}
Then use it:
import { AlarmOn } from 'mdi-norm/es/AlarmOn'
...
render() {
return <MyButton icon={AlarmOn} theme={{iconsTheme: 'two-tone'}} caption="Setup Alarm"/>
}
Like any other macros the mdi-norm/macro
works on compilations stage. It's literally transforms your JS-code before it goes to compilation and gives you useful syntactic sugar (insert imports for you, translate icons name to camel case, insert props to JSX literal, etc.). So, it's not available on production when your JS code is executed on browser or server.
Usually you don't need all 5,220 SVG icons from all 5 themes in your JS bundle on client site. If you have to choose dynamically from concrete set of icons you can put them into array or object and use icons later:
import i from 'mdi-norm/macro'
const icons = {
info: i`two-tone-info`,
warning: <i className="material-icons">Outline Pan_Tool</i>,
stop: i('Not interested', {size: 48}),
};
// translated by macros to
// const icons = {
// info: <TwoToneInfo/>,
// warning: <OutlinePanTool className="material-icons"/>,
// stop: <NotInterested size={48}/>,
//};
...
render() {
const listItemIcon = 'info';
return <>
<div>{icons.info} Info</div>
<div>{icons.warning} Warning</div>
<div>{icons.stop} Stop</div>
<div>{icons[listItemIcon]} Info</div>
</>;
}
But if you have to include all 5220 SVG icons (can't imagine such case) into your bundle and ready to have huge js chunk (~1.5Mb raw js or ~300Kb gzipped), then you can include everyone (c) icon into the project and use them later:
import * as Icons from 'mdi-norm'
...
render() {
const myIconName = this.props.icon || 'Face';
const Icon = Icons[myIconName];
return <><Icon size={240} style={{color: 'green'}}/></>;
}
Of course you can segregate such 'icons' chunk to separate js file (like /icons.js
).
@gcloeval to compare with material-ui the mdi-norm
:
@babel/runtime
<Icon />
component overhead (less then 2Kb, ~0.7Kb gzipped)<path>
draw commands optimizationsIf you use material-ui
library with the next icons across your project:
The twelve different SVG code contents will be embedded into your production JS bundle, but with mdi-norm
the single and most compact SVG code serves all these icons at ones: <path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/>
Besides duplicates removal the command-by-command draw optimizations applied to reduce the SVG code even more:
Let's compare swap_vert
icon of the Outline theme:
In material-ui
library SwapVertOutlined.js:
<React.Fragment>
<path fill="none" d="M0 0h24v24H0V0z" />
<g>
<path d="M16 17.01V10h-2v7.01h-3L15 21l4-3.99h-3zM9 3L5 6.99h3V14h2V6.99h3L9 3z" />
<path d="M16 17.01V10h-2v7.01h-3L15 21l4-3.99h-3zM9 3L5 6.99h3V14h2V6.99h3L9 3z" />
</g>
</React.Fragment>
In this mdi-norm
library the OutlineSwapVert.js is a full duplicate of TwoToneImportExport.js:
<path d="M5 6.99h3V14h2V6.99h3L9 3zM14 10v7.01h-3L15 21l4-3.99h-3V10z"/>
Both libraries produces the same rendering result, but mdi-norm
library based on 70% more compact SVG code (73 bytes vs 246 bytes) for this icon.
That all sounds fantastic! Thanks so much. My project is a bit all over the place right now, once it stabilizes, I will record the bundle size (with material ui icons), and then branch it to try with this version - I will report back to you
@gcloeval migration to mdi-norm
as minimum:
<i className="material-icons">local_bar</i>
then just add import i from 'mdi-norm/macro'
into your import section and all such code will be transpiled for you by the macros (this will include <LocalBar />
component with all 5 SVG variants of this icon for each theme inside. But you can decrease size even more, if you specify concrete theme in icon name as prefix like filled_local_bar
for example).
Hi, I am just curious if you can provide a bit more clarification how does this compare to https://material-ui.com/style/icons/ and SvgIcon component (the prebuilt ones)
I believe this library may solve my gripe with material-ui icons but I just want to clarify. I dont want to use their generic
<Icon>
because it requires additional URL to download Typical case for me is using an icon to "toggling" something, so I have to: import @material-ui/icons/DeleteOutlined import @material-ui/icons/DeleteFilledthen based on some state, return one of the two plus some color modifications.
I also sometimes dynamically determine which icon to return - which is easy to do dynamically when using their Icon jsx component but that one requires that i include a link to href="https://fonts.googleapis.com/icon?family=Material+Icons"
which i am trying to avoid.
So it seems like this would be a great fix, esp. with the fact that you already have a babel.macro!
So, if I do
import i from 'mdi-norm/macro'
then missed_video_call
can i dynamically change theme and missed_video_call values above to get the different output during rendering - i am not sure when these macros are processed?
i.e. can it work in onRender method: {state.iconName}
thanks