eugeneilyin / mdi-norm

Fixed, normalized, minifed, and deduplicated Material Design system SVG icons for React
MIT License
3 stars 0 forks source link

How does this compare to @material-ui/icons package #1

Open gcloeval opened 5 years ago

gcloeval commented 5 years ago

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/DeleteFilled

then 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

eugeneilyin commented 5 years ago

Hi @gcloeval,

Dynamical theming

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"/>
  }

Dynamical icons

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).

eugeneilyin commented 5 years ago

@gcloeval to compare with material-ui the mdi-norm:

Some illustrations

If you use material-ui library with the next icons across your project:

  1. Filled › navigate_next
  2. Filled › chevron_right
  3. Filled › keyboard_arrow_right
  4. Outline › navigate_next
  5. Outline › keyboard_arrow_right
  6. Outline › chevron_right
  7. Sharp › navigate_next
  8. Sharp › keyboard_arrow_right
  9. Sharp › chevron_right
  10. TwoTone › navigate_next
  11. TwoTone › keyboard_arrow_right
  12. TwoTone › chevron_right

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.

gcloeval commented 5 years ago

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

eugeneilyin commented 5 years ago

@gcloeval migration to mdi-norm as minimum: