├── .babelrc ├── .gitignore ├── .npmignore ├── .storybook └── config.js ├── .travis.yml ├── LICENSE.txt ├── NOTICE.txt ├── README.md ├── filenameMap.js ├── generate-module.js ├── package-lock.json ├── package.json ├── src └── util │ └── createIcon.js ├── stories └── index.js ├── test.js └── update.sh /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["@babel/plugin-transform-modules-commonjs"] 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package/ 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | icons 2 | generate-module.js -------------------------------------------------------------------------------- /.storybook/config.js: -------------------------------------------------------------------------------- 1 | import { configure } from '@storybook/react'; 2 | 3 | function loadStories() { 4 | require('../stories'); 5 | } 6 | 7 | configure(loadStories, module); 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "8" 4 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016-2021 Team Wertarbyte and contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | Material Design Icons license: 2 | 3 | Pictogrammers Free License 4 | -------------------------- 5 | 6 | This icon collection is released as free, open source, and GPL friendly by 7 | the [Pictogrammers](http://pictogrammers.com/) icon group. You may use it 8 | for commercial projects, open source projects, or anything really. 9 | 10 | # Icons: Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) 11 | Some of the icons are redistributed under the Apache 2.0 license. All other 12 | icons are either redistributed under their respective licenses or are 13 | distributed under the Apache 2.0 license. 14 | 15 | # Fonts: Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) 16 | All web and desktop fonts are distributed under the Apache 2.0 license. Web 17 | and desktop fonts contain some icons that are redistributed under the Apache 18 | 2.0 license. All other icons are either redistributed under their respective 19 | licenses or are distributed under the Apache 2.0 license. 20 | 21 | # Code: MIT (https://opensource.org/licenses/MIT) 22 | The MIT license applies to all non-font and non-icon files. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Material Design Icons for Material-UI 2 | 3 | [![Material Design Icons version](https://img.shields.io/badge/mdi-v7.4.47-blue.svg)](https://github.com/Templarian/MaterialDesign) 4 | [![Material Design Icons version](https://img.shields.io/badge/mdi--light-v0.2.63-blue.svg)](https://github.com/Templarian/MaterialDesignLight) 5 | 6 | This module provides [Material-UI][material-ui] `` components for all 7 | [Material Design Icons][md-icons]. This is pretty handy if you use React and Material-UI 8 | to build a web app and run out of icons. 9 | 10 | While this module contains wrappers for all icons, alias names are not included. For example, the _plus_ icon is aliased as _add_, but only the _plus_ icon 11 | is exported. 12 | 13 | [materialdesign-webfont-material-ui]: https://github.com/TeamWertarbyte/materialdesign-webfont-material-ui 14 | [material-ui]: http://www.material-ui.com/ 15 | [md-icons]: https://materialdesignicons.com/ 16 | 17 | ## Installation 18 | 19 | ```shell 20 | npm install mdi-material-ui --save 21 | ``` 22 | 23 | There are different major versions of this package, each one for different Material-UI releases. Note that this project does not follow semantic versioning. If Material Design Icons removes or renames icons, it will still be a minor version bump. 24 | 25 | | Material-UI | mdi-material-ui | npm tag | 26 | | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ------- | 27 | | ^5.0.0, ^6.0.0, ^7.0.0 | [![npm](https://img.shields.io/npm/v/mdi-material-ui.svg)](https://www.npmjs.com/package/mdi-material-ui) | latest | 28 | | ^4.0.0 | [![npm](https://img.shields.io/npm/v/mdi-material-ui/mui-v4.svg)](https://www.npmjs.com/package/mdi-material-ui/v/mui-v4) | mui-v4 | 29 | | ^1.0.0, ^3.0.0 | [![npm](https://img.shields.io/npm/v/mdi-material-ui/mui-v3.svg)](https://www.npmjs.com/package/mdi-material-ui/v/mui-v3) | mui-v3 | 30 | | 0.x | [![npm](https://img.shields.io/npm/v/mdi-material-ui/legacy.svg?color=yellow)](https://www.npmjs.com/package/mdi-material-ui/v/legacy) \* | legacy | 31 | 32 | \* mdi-material-ui v4 (for Material-UI v0) is not updated anymore 33 | 34 | ## Usage 35 | 36 | Every icon is exported with its original name in PascalCase. So `coffee` becomes `Coffee`, 37 | `cloud-print-outline` is exported as `CloudPrintOutline` and so on. 38 | 39 | The Material Design _Light_ icons are included in the `/light` subdirectory. 40 | 41 | ### With tree-shaking 42 | 43 | If your environment supports tree-shaking and you are sure that it works fine in your setup, you can simply import the icons as follows: 44 | 45 | ```js 46 | import { Coffee, Food } from 'mdi-material-ui' 47 | import { Camera, Settings } from 'mdi-material-ui/light' 48 | 49 | 50 | 51 | 52 | 53 | ``` 54 | 55 | ### Without tree-shaking 56 | 57 | If your environment doesn't support tree-shaking, you should only import the icons that you actually need in order to ensure that you don't end up bundling _all_ icons. 58 | 59 | ```js 60 | import Coffee from 'mdi-material-ui/Coffee' 61 | import Food from 'mdi-material-ui/Food' 62 | import Camera from 'mdi-material-ui/light/Camera' 63 | import Settings from 'mdi-material-ui/light/Settings' 64 | 65 | 66 | 67 | 68 | 69 | ``` 70 | 71 | ## License 72 | 73 | The scripts included in this repository are licensed under the MIT license. 74 | The icons are licensed under the MIT license (see [Material Design Icons](https://github.com/Templarian/MaterialDesign-SVG) and the [NOTICE][] file). 75 | 76 | [notice]: https://github.com/TeamWertarbyte/mdi-material-ui/blob/master/NOTICE 77 | -------------------------------------------------------------------------------- /filenameMap.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | License: "LicenseIcon", // rename License to LicenseIcon to avoid `yarn licenses generate-disclaimer` reading the icon file as license 3 | }; 4 | -------------------------------------------------------------------------------- /generate-module.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const fse = require('fs-extra') 3 | const path = require('path') 4 | const babel = require('@babel/core') 5 | const pick = require('lodash.pick') 6 | const filenameMap = require('./filenameMap') 7 | 8 | function checkNameClashes (icons) { 9 | const caseNameClashes = icons.filter( 10 | (icon) => 11 | icon.filename == null && 12 | icons.filter( 13 | (icon2) => 14 | icon2.filename == null && 15 | icon2.name.toLowerCase() === icon.name.toLowerCase() 16 | ).length > 1 17 | ) 18 | if (caseNameClashes.length > 0) { 19 | throw new Error( 20 | `The following icons have the same file name (case insensitive): ${caseNameClashes 21 | .map((icon) => icon.name) 22 | .join(', ')}` 23 | ) 24 | } 25 | } 26 | 27 | (async () => { 28 | const icons = Object.entries(require('@mdi/js')) 29 | .filter(([name]) => name.indexOf('mdi') === 0) 30 | .map(([name, path]) => ({ 31 | name: name.substr(3), // remove mdi prefix 32 | filename: filenameMap[name.substr(3)], 33 | svgPath: path 34 | })) 35 | checkNameClashes(icons) 36 | 37 | const lightIcons = Object.entries(require('@mdi/light-js')) 38 | .filter(([name]) => name.indexOf('mdil') === 0) 39 | .map(([name, path]) => ({ 40 | name: name.substr(4), // remove mdil prefix 41 | svgPath: path 42 | })) 43 | checkNameClashes(lightIcons) 44 | 45 | fse.removeSync(path.join(__dirname, 'package')) 46 | fse.mkdirpSync(path.join(__dirname, 'package', 'light')) 47 | fse.removeSync(path.join(__dirname, 'package', 'esm')) 48 | fse.mkdirpSync(path.join(__dirname, 'package', 'esm', 'light')) 49 | 50 | for (const { name, filename, svgPath } of icons) { 51 | const code = ` 52 | "use client"; 53 | import createIcon from './util/createIcon' 54 | export default createIcon('${svgPath}', '${name}') 55 | ` 56 | 57 | // es module 58 | fse.writeFileSync(path.join(__dirname, 'package', 'esm', `${filename || name}.js`), babel.transform(code, { 59 | presets: ['@babel/preset-react', ['@babel/preset-env', { modules: false }]], 60 | compact: process.env.NODE_ENV === 'production' 61 | }).code) 62 | 63 | // commonjs module 64 | fse.writeFileSync(path.join(__dirname, 'package', `${filename || name}.js`), babel.transform(code, { 65 | presets: ['@babel/preset-react', '@babel/preset-env'], 66 | compact: process.env.NODE_ENV === 'production' 67 | }).code) 68 | 69 | // typescript definition 70 | fse.writeFileSync(path.join(__dirname, 'package', `${filename || name}.d.ts`), `export { default } from '@mui/material/SvgIcon' 71 | `) 72 | } 73 | 74 | for (const { name, filename, svgPath } of lightIcons) { 75 | const code = ` 76 | "use client"; 77 | import createIcon from '../util/createIcon' 78 | export default createIcon('${svgPath}', '${name}') 79 | ` 80 | 81 | // es module 82 | fse.writeFileSync(path.join(__dirname, 'package', 'esm', 'light', `${filename || name}.js`), babel.transform(code, { 83 | presets: ['@babel/preset-react', ['@babel/preset-env', { modules: false }]], 84 | compact: process.env.NODE_ENV === 'production' 85 | }).code) 86 | 87 | // commonjs module 88 | fse.writeFileSync(path.join(__dirname, 'package', 'light', `${filename || name}.js`), babel.transform(code, { 89 | presets: ['@babel/preset-react', '@babel/preset-env'], 90 | compact: process.env.NODE_ENV === 'production' 91 | }).code) 92 | 93 | // typescript definition 94 | fse.writeFileSync(path.join(__dirname, 'package', 'light', `${filename || name}.d.ts`), `export { default } from '@mui/material/SvgIcon' 95 | `) 96 | } 97 | 98 | const generateIndexFiles = (destination, esmDestination, icons) => { 99 | // es module 100 | const allExports = icons.map(({ name, filename }) => `export { default as ${name} } from './${filename || name}'`).join('\n') 101 | fse.writeFileSync(path.join(esmDestination, 'index.js'), allExports) 102 | 103 | // typescript index definition (looks exactly the same) 104 | fse.writeFileSync(path.join(destination, 'index.d.ts'), allExports) 105 | 106 | // commonjs module 107 | fse.writeFileSync(path.join(destination, 'index.js'), babel.transform(allExports, { 108 | plugins: ['@babel/plugin-transform-modules-commonjs'], 109 | compact: process.env.NODE_ENV === 'production' 110 | }).code) 111 | } 112 | 113 | generateIndexFiles(path.join(__dirname, 'package'), path.join(__dirname, 'package', 'esm'), icons) 114 | generateIndexFiles(path.join(__dirname, 'package', 'light'), path.join(__dirname, 'package', 'esm', 'light'), lightIcons) 115 | 116 | // createIcon function 117 | fse.mkdirSync(path.join(__dirname, 'package', 'util')) 118 | fse.mkdirSync(path.join(__dirname, 'package', 'esm', 'util')) 119 | fse.writeFileSync( 120 | path.join(__dirname, 'package', 'util', 'createIcon.js'), 121 | babel.transform(fse.readFileSync(path.join(__dirname, 'src', 'util', 'createIcon.js')), { 122 | presets: [['@babel/preset-react', { runtime: "automatic" }], '@babel/preset-env'], 123 | compact: process.env.NODE_ENV === 'production' 124 | }).code 125 | ) 126 | fse.writeFileSync( 127 | path.join(__dirname, 'package', 'esm', 'util', 'createIcon.js'), 128 | babel.transform(fse.readFileSync(path.join(__dirname, 'src', 'util', 'createIcon.js')), { 129 | presets: [['@babel/preset-react', { runtime: "automatic" }], ['@babel/preset-env', { modules: false }]], 130 | compact: process.env.NODE_ENV === 'production' 131 | }).code 132 | ) 133 | 134 | // update readme 135 | const mdiVersion = require(path.join(require.resolve('@mdi/js'), '..', '..', 'package.json')).version 136 | const mdiLightVersion = require(path.join(require.resolve('@mdi/light-js'), '..', '..', 'package.json')).version 137 | let readme = await fse.readFile(path.join(__dirname, 'README.md'), 'utf-8') 138 | readme = readme.replace(/img\.shields\.io\/badge\/mdi-v(.+?)-blue\.svg/g, `img.shields.io/badge/mdi-v${mdiVersion}-blue.svg`) 139 | readme = readme.replace(/img\.shields\.io\/badge\/mdi--light-v(.+?)-blue\.svg/g, `img.shields.io/badge/mdi--light-v${mdiLightVersion}-blue.svg`) 140 | await fse.writeFile(path.join(__dirname, 'README.md'), readme, 'utf-8') 141 | 142 | // copy other files 143 | ;[ 144 | 'README.md', 145 | 'NOTICE.txt', 146 | 'LICENSE.txt', 147 | '.npmignore' 148 | ].forEach((file) => fse.copySync(path.join(__dirname, file), path.join(__dirname, 'package', file))) 149 | 150 | const packageJson = require('./package.json') 151 | packageJson.name = 'mdi-material-ui' 152 | fse.writeFileSync(path.join(__dirname, 'package', 'package.json'), JSON.stringify(pick(packageJson, [ 153 | 'name', 154 | 'version', 155 | 'description', 156 | 'main', 157 | 'module', 158 | 'jsnext:main', 159 | 'sideEffects', 160 | 'repository', 161 | 'keywords', 162 | 'author', 163 | 'license', 164 | 'bugs', 165 | 'homepage', 166 | 'peerDependencies' 167 | ]), null, 2), 'utf-8') 168 | })() 169 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mdi-material-ui-generator", 3 | "version": "0.0.0", 4 | "description": "Material-UI SvgIcon components for Material Design Icons.", 5 | "main": "./index.js", 6 | "module": "./esm/index.js", 7 | "jsnext:main": "./esm/index.js", 8 | "sideEffects": false, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/TeamWertarbyte/mdi-material-ui.git" 12 | }, 13 | "keywords": [ 14 | "material", 15 | "ui", 16 | "icons", 17 | "webfont", 18 | "font", 19 | "react" 20 | ], 21 | "author": "Wertarbyte", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/TeamWertarbyte/mdi-material-ui/issues" 25 | }, 26 | "homepage": "https://github.com/TeamWertarbyte/mdi-material-ui#readme", 27 | "devDependencies": { 28 | "@babel/core": "^7.24.5", 29 | "@babel/plugin-transform-modules-commonjs": "^7.24.1", 30 | "@babel/preset-env": "^7.24.5", 31 | "@babel/preset-react": "^7.24.1", 32 | "@emotion/react": "^11.1.5", 33 | "@emotion/styled": "^11.1.5", 34 | "@mdi/js": "^7.4.47", 35 | "@mdi/light-js": "^0.2.63", 36 | "@mui/material": "^7.0.0", 37 | "@storybook/react": "^6.5.6", 38 | "ava": "^3.15.0", 39 | "fs-extra": "^4.0.1", 40 | "lodash.pick": "^4.4.0", 41 | "react": "^18.1.0", 42 | "react-dom": "^18.1.0", 43 | "react-test-renderer": "^18.1.0" 44 | }, 45 | "peerDependencies": { 46 | "@mui/material": "^5.0.0 || ^6.0.0 || ^7.0.0", 47 | "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" 48 | }, 49 | "scripts": { 50 | "storybook": "start-storybook -p 6006", 51 | "build-storybook": "build-storybook", 52 | "build": "NODE_ENV=production node generate-module.js", 53 | "pretest": "node generate-module.js", 54 | "test": "ava" 55 | }, 56 | "private": true 57 | } 58 | -------------------------------------------------------------------------------- /src/util/createIcon.js: -------------------------------------------------------------------------------- 1 | import { createSvgIcon } from "@mui/material/utils"; 2 | 3 | export default (path, name) => createSvgIcon(, name); 4 | -------------------------------------------------------------------------------- /stories/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { storiesOf } from '@storybook/react' 3 | const icons = require('../package/index') 4 | const lightIcons = require('../package/light/index') 5 | 6 | function themed (children) { 7 | return ( 8 |
9 | {children} 10 |
11 | ) 12 | } 13 | 14 | const iconStories = storiesOf('Material Design Icons', module) 15 | iconStories.add(`all icons (${Object.keys(icons).length.toLocaleString()})`, () => themed( 16 |
17 | {Object.keys(icons).map((icon) => { 18 | const Icon = icons[icon] 19 | return 20 | })} 21 |
22 | )) 23 | 24 | Object.keys(icons).sort().forEach((icon) => { 25 | const Icon = icons[icon] 26 | iconStories.add(icon, () => themed()) 27 | }) 28 | 29 | const lightIconStories = storiesOf('Material Design Icons Light', module) 30 | lightIconStories.add(`all icons (${Object.keys(lightIcons).length.toLocaleString()})`, () => themed( 31 |
32 | {Object.keys(lightIcons).map((icon) => { 33 | const Icon = lightIcons[icon] 34 | return 35 | })} 36 |
37 | )) 38 | 39 | Object.keys(lightIcons).sort().forEach((icon) => { 40 | const Icon = lightIcons[icon] 41 | lightIconStories.add(icon, () => themed()) 42 | }) 43 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | const test = require("ava"); 2 | const React = require("react"); 3 | const renderer = require("react-test-renderer"); 4 | const fs = require("fs"); 5 | const filenameMap = require("./filenameMap"); 6 | const commonjsIcons = require("./package/index"); 7 | const commonjsIconsLight = require("./package/light/index"); 8 | 9 | test("the npm package", (t) => { 10 | // should set sideEffects to false to allow webpack to optimize re-exports 11 | const packageJson = require("./package/package.json"); 12 | t.false(packageJson.sideEffects); 13 | }); 14 | 15 | for (const iconName of Object.keys(commonjsIcons)) { 16 | test(`icons > ${iconName}`, (t) => { 17 | const renderedIcon = renderer 18 | .create(React.createElement(commonjsIcons[iconName])) 19 | .toJSON(); 20 | // t.is(renderedIcon[0].type, 'style') // generated by emotion 21 | t.is(renderedIcon[1].type, "svg"); 22 | t.is(renderedIcon[1].props["data-testid"], `${iconName}Icon`); 23 | }); 24 | } 25 | 26 | test("ES module index file", (t) => { 27 | const esmReExports = fs 28 | .readFileSync("./package/esm/index.js", "utf-8") 29 | .split("\n") 30 | .filter((line) => line.length > 0); 31 | t.is(esmReExports.length, Object.keys(commonjsIcons).length); 32 | 33 | for (const line of esmReExports) { 34 | const match = line.match( 35 | /^export \{ default as (.+?) \} from '\.\/(.+?)'$/ 36 | ); 37 | t.is(filenameMap[match[1]] || match[1], match[2]); 38 | t.truthy(commonjsIcons[match[1]]); 39 | } 40 | }); 41 | 42 | for (const iconName of Object.keys(commonjsIconsLight)) { 43 | test(`light icons > ${iconName}`, (t) => { 44 | const renderedIcon = renderer 45 | .create(React.createElement(commonjsIconsLight[iconName])) 46 | .toJSON(); 47 | // t.is(renderedIcon[0].type, 'style') // generated by emotion 48 | t.is(renderedIcon[1].type, "svg"); 49 | t.is(renderedIcon[1].props["data-testid"], `${iconName}Icon`); 50 | }); 51 | } 52 | 53 | test("mdi-light ES module index file", (t) => { 54 | const esmReExports = fs 55 | .readFileSync("./package/esm/light/index.js", "utf-8") 56 | .split("\n") 57 | .filter((line) => line.length > 0); 58 | t.is(esmReExports.length, Object.keys(commonjsIconsLight).length); 59 | 60 | for (const line of esmReExports) { 61 | const match = line.match( 62 | /^export \{ default as (.+?) \} from '\.\/(.+?)'$/ 63 | ); 64 | t.is(match[1], match[2]); 65 | t.truthy(commonjsIconsLight[match[1]]); 66 | } 67 | }); 68 | -------------------------------------------------------------------------------- /update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | npm i --save-dev @mdi/js@latest @mdi/light-js@latest 3 | ./generate-module.js 4 | npm test 5 | 6 | VERSION=`cat node_modules/@mdi/js/package.json | jq -r .version` 7 | git add --all 8 | git commit -m "Update to mdi $VERSION." 9 | git push origin master 10 | --------------------------------------------------------------------------------