├── .babelrc.js ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── docs ├── index.mdx ├── logo.svg ├── preview.png └── style.css ├── package-lock.json ├── package.json ├── postcss.config.js ├── src ├── css │ ├── colors.less │ ├── functions.less │ ├── style.less │ ├── utils.less │ └── vars.less ├── index.js └── js │ └── FileIcon.jsx └── webpack.config.js /.babelrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "presets": [ 3 | ["@babel/preset-env", { 4 | "useBuiltIns": "usage", 5 | "modules": false, 6 | }], 7 | ["@babel/preset-react"], 8 | ], 9 | }; 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | lib 4 | dist 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | src 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Drawbotics 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # File Icons 4 | ```bash 5 | npm install @drawbotics/file-icons 6 | ``` 7 | A simple library to display icons depending on the file type. The library can be used both by importing the stylesheet (useful if you have a simple project without React dependencies), and also exports a React component. 8 | 9 | **DISCLAIMER** 10 | In order to have a _single div_ usage, and given the complexity of the icon look, a lot of CSS features that may not be compatible with older browsers is used. Namely `linear-gradient`, `border-image` and `clip-path`. Nonetheless, all the styles are prefixed. 11 | 12 | ## Usage 13 | There are two ways to use the icons: with a simple `div` or with a React component. Either way, you should include the stylesheet to have the icons style: 14 | ``` 15 | @import '~@drawbotics/file-icons/dist/style.css'; 16 | ``` 17 | 18 | #### Pure CSS 19 | You can use `file-icons` just by importing the css styling and using a `div` as a base element. Add the class `file-icon` to your div and set the file type as a data attribute `data-file`: 20 | ```html 21 |
22 | ``` 23 | **Note**: The file extension needs to be in lower case. 24 | 25 | You can also pass different class names for different sizes: 26 | ```html 27 |
28 |
29 | ``` 30 | 31 | 32 | 33 | Example icons with default, medium and large sizing. 34 | 35 | #### React 36 | You can import the component anywhere in your code through: 37 | ```js 38 | import { FileIcon } from '@drawbotics/file-icons'; 39 | ``` 40 | If you use CSSinJS then don't forget to include the stylesheet if you haven't already 41 | ```js 42 | import '@drawbotics/file-icons/dist/style.css'; 43 | ``` 44 | 45 | And use it like such: 46 | ```js 47 | 48 | ``` 49 | 50 | Here are the different options you can pass: 51 | 52 | Prop | Type | Description | Possible values 53 | --- | --- | --- | --- 54 | `file` | String | File extension without the dot | `.doc` 55 | `size` | String | To toggle between the 3 sizes | `medium`, `large` 56 | `filename` | String | If you don't want to extract the extension yourself you can pass the filename directly | `my-document.pdf` 57 | 58 | When using the React component you don't need to worry if the extension/filename is lowercase, the component takes care of that. 59 | 60 | #### Supported file types 61 | Any file extension that does not match the ones included with the library will fallback to the default grey color. Each category has its own color. The supported extension is a subset of the provided value, meaning that `docx` is supported since we look for `doc`. 62 | 63 | Category | Possible values 64 | --- | --- 65 | Archives | `zip`, `rar`, `tar`, `dmg`, `jar` 66 | 3D Files | `3ds`, `dwg`, `obj`, `dae`, `skp`, `fbx` 67 | Text Documents | `doc`, `rtf`, `txt`, `odt`, `tex`, 68 | Vector graphics | `ai`, `svg` 69 | PDF | `pdf` 70 | Data | `xml`, `csv`, `xls` 71 | Images | `jpg`, `gif`, `png`, `jpeg`, `tif`, `psd`, `raw` 72 | Video | `webm`, `mkv`, `avi`, `mov`, `m4v`, `mpeg`, `mp4` 73 | Audio | `mp3`, `m4a`, `ogg`, `acc`, `flac` 74 | 75 | If you want to add support for an extension, or support a new category of files don't hesitate to submit a PR. More extensions and categories will be added through time. 76 | 77 | ## Develop 78 | ```bash 79 | npm install 80 | ``` 81 | To run examples 82 | ```bash 83 | npm run build:watch # for build step 84 | npm run example # to serve examples 85 | ``` 86 | 87 | To build for production 88 | ```bash 89 | npm run build 90 | ``` 91 | 92 | 93 | ## License 94 | MIT. See [LICENSE](LICENSE) for details. 95 | -------------------------------------------------------------------------------- /docs/index.mdx: -------------------------------------------------------------------------------- 1 | import { PlayGround, Knobs, PropsTable } from 'react-display-window/lib/components'; 2 | 3 | import './style.css'; 4 | 5 | import '../dist/style.css'; 6 | import { FileIcon } from '../dist/file-icons.js'; 7 | 8 | 9 | ## Pure CSS Example 10 | 11 | 12 |
13 |
14 |
15 |
16 |
17 | 18 | 19 | 20 | ## React Component 21 | 22 |
23 | 24 | 25 | 26 |
27 |
28 | 29 | 30 | # All types 31 | ### Archives 32 |
33 |
34 |
35 |
36 | 37 | 38 | ### 3D Files 39 |
40 |
41 |
42 |
43 | 44 | 45 | ### Text documents 46 |
47 |
48 |
49 |
50 |
51 | 52 | 53 | ### Vector graphics 54 |
55 |
56 |
57 |
58 | 59 | 60 | ### PDF 61 |
62 |
63 |
64 | 65 | 66 | ### Data 67 |
68 |
69 |
70 |
71 |
72 | 73 | 74 | ### Images 75 |
76 |
77 |
78 |
79 |
80 | 81 | 82 | ### Video 83 |
84 |
85 |
86 |
87 |
88 | 89 | 90 | ### Audio 91 |
92 |
93 |
94 |
95 |
96 | -------------------------------------------------------------------------------- /docs/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | Icons 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Drawbotics/file-icons/632d84de9c31a9535be177bf58caca19986974b1/docs/preview.png -------------------------------------------------------------------------------- /docs/style.css: -------------------------------------------------------------------------------- 1 | .container { 2 | width: 100%; 3 | padding: 50px; 4 | background: #DADDE3; 5 | display: flex; 6 | align-items: center; 7 | } 8 | 9 | 10 | .container > .file-icon { 11 | margin-right: 30px; 12 | } 13 | 14 | 15 | .playground { 16 | width: 100%; 17 | height: 500px; 18 | background: #DADDE3; 19 | display: flex; 20 | align-items: center; 21 | justify-content: space-around; 22 | } 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@drawbotics/file-icons", 3 | "version": "0.1.3", 4 | "description": "Small icon library for file type icons", 5 | "main": "dist/file-icons.js", 6 | "scripts": { 7 | "clean": "rimraf dist && rimraf lib", 8 | "build:umd": "NODE_ENV=production webpack --config webpack.config.js", 9 | "build": "npm run clean && npm run build:umd", 10 | "build:watch": "npm run clean && NODE_ENV=production webpack --watch --config webpack.config.js", 11 | "prepublishOnly": "npm run build", 12 | "example": "npx rdw serve --port 4001 docs/index.mdx" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/Drawbotics/file-icons" 17 | }, 18 | "publishConfig": { 19 | "registry": "https://npm.pkg.github.com/" 20 | }, 21 | "author": "Nick ", 22 | "license": "MIT", 23 | "peerDependencies": { 24 | "react": "16.x", 25 | "react-dom": "16.x" 26 | }, 27 | "devDependencies": { 28 | "@babel/core": "^7.2.2", 29 | "@babel/preset-env": "^7.2.0", 30 | "@babel/preset-react": "^7.0.0", 31 | "@drawbotics/check-env": "^1.0.0", 32 | "babel-loader": "^8.0.4", 33 | "better-webpack-progress": "^1.1.0", 34 | "css-loader": "^2.0.1", 35 | "emotion": "^10.0.5", 36 | "less": "^3.9.0", 37 | "less-loader": "^4.1.0", 38 | "mini-css-extract-plugin": "^0.5.0", 39 | "postcss-loader": "^3.0.0", 40 | "postcss-preset-env": "^6.5.0", 41 | "react": "^16.6.3", 42 | "react-display-window": "^2.1.2", 43 | "react-dom": "^16.6.3", 44 | "rimraf": "^2.6.2", 45 | "style-loader": "^0.23.1", 46 | "webpack": "^4.27.1", 47 | "webpack-cli": "^3.1.2" 48 | }, 49 | "dependencies": { 50 | "classnames": "^2.2.6" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | 'postcss-preset-env': {}, 4 | }, 5 | } 6 | -------------------------------------------------------------------------------- /src/css/colors.less: -------------------------------------------------------------------------------- 1 | // COLORS 2 | @border-color: #8C8C8C; 3 | @white: #FFFFFF; 4 | 5 | @blue: #1482C8; 6 | @dark-blue: #1C387A; 7 | @orange: #EC8F08; 8 | @purple: #5E43C6; 9 | @red: #D83433; 10 | @green: #47B188; 11 | @dark-green: #0C6845; 12 | @brown: #A19382; 13 | @pink: #FF4290; 14 | -------------------------------------------------------------------------------- /src/css/functions.less: -------------------------------------------------------------------------------- 1 | @import './vars'; 2 | 3 | 4 | .iconGradient(@color, @step) { 5 | @gradient: ~"45deg, @{color} calc(100% - @{step}), rgba(255, 255, 255, 0) calc(100% - @{step}), rgba(255, 255, 255, 0) 100%"; 6 | background: -ms-linear-gradient(@gradient); 7 | background: -o-linear-gradient(@gradient); 8 | background: -moz-linear-gradient(@gradient); 9 | background: -webkit-linear-gradient(@gradient); 10 | background: linear-gradient(@gradient); 11 | } 12 | 13 | 14 | .borderGradient(@color, @step) { 15 | @gradient: ~"45deg, @{color} calc(100% - @{step}), rgba(255, 255, 255, 0) calc(100% - @{step}), rgba(255, 255, 255, 0) 100%"; 16 | border-image: -ms-linear-gradient(@gradient); 17 | border-image: -o-linear-gradient(@gradient); 18 | border-image: -moz-linear-gradient(@gradient); 19 | border-image: -webkit-linear-gradient(@gradient); 20 | border-image: linear-gradient(@gradient); 21 | } 22 | 23 | 24 | .foldGradient(@fold-width) { 25 | @gradient: ~"45deg, @{white} calc(50% - @{fold-width}), @{border-color} calc(50% - @{fold-width}), @{border-color} calc(50% + @{fold-width}), transparent calc(50% + @{fold-width}), transparent 100%"; 26 | background: -ms-linear-gradient(@gradient); 27 | background: -o-linear-gradient(@gradient); 28 | background: -moz-linear-gradient(@gradient); 29 | background: -webkit-linear-gradient(@gradient); 30 | background: linear-gradient(@gradient); 31 | } 32 | 33 | 34 | .polygonPath(@a, @b) { 35 | clip-path: polygon(calc(100% - @a) 0, 100% @a, 100% 100%, @b 100%, @b 0); 36 | } 37 | -------------------------------------------------------------------------------- /src/css/style.less: -------------------------------------------------------------------------------- 1 | @import './vars'; 2 | @import './functions'; 3 | @import './utils'; 4 | 5 | 6 | .file-icon { 7 | font-family: Arial, sans-serif; 8 | display: inline-block; 9 | position: relative; 10 | width: @width; 11 | height: @height; 12 | border-width: 1px; 13 | -webkit-font-smoothing: antialiased; 14 | margin-left: @label-displacement - 1px; 15 | 16 | .iconGradient(@white, 5px); 17 | .borderGradient(@border-color, 6.5px); 18 | .polygonPath(9.5px, -@label-displacement); 19 | 20 | border-style: solid; 21 | border-image-slice: 1; 22 | 23 | &::before { 24 | content: ' '; 25 | position: absolute; 26 | top: -1px; 27 | right: -1px; 28 | height: @fold-size; 29 | width: @fold-size; 30 | box-shadow: -1px 1px 0px 0.5px fade(@border-color, 60); 31 | border-bottom-left-radius: 1px; 32 | 33 | .foldGradient(0.5px); 34 | } 35 | 36 | &::after { 37 | content: ' '; 38 | position: absolute; 39 | height: @label-height; 40 | width: @label-width; 41 | bottom: 4px; 42 | left: -@label-displacement; 43 | display: flex; 44 | align-items: center; 45 | justify-content: center; 46 | text-transform: uppercase; 47 | font-size: @font-size; 48 | color: @white; 49 | font-weight: 600; 50 | background: @border-color; 51 | letter-spacing: -1px; 52 | padding-top: 1px; 53 | } 54 | 55 | .contentFromType(); 56 | .backgroundFromType(); 57 | 58 | // MEDIUM SIZE 59 | &--medium { 60 | width: @width * @medium-multiplier; 61 | height: @height * @medium-multiplier; 62 | border-width: 3px; 63 | margin-left: @label-displacement * @medium-multiplier - 3px;; 64 | 65 | .iconGradient(@white, 15px); 66 | .borderGradient(@border-color, 18px); 67 | .polygonPath(26px, -(@label-displacement * @medium-multiplier)); 68 | 69 | border-style: solid; 70 | border-image-slice: 1; 71 | 72 | &::before { 73 | top: -2px; 74 | right: -2px; 75 | height: @fold-size * @medium-multiplier; 76 | width: @fold-size * @medium-multiplier; 77 | box-shadow: -1.5px 1.5px 0px 1.5px @border-color; 78 | border-bottom-left-radius: 5px; 79 | 80 | .foldGradient(2px); 81 | } 82 | 83 | &::after { 84 | height: @label-height * @medium-multiplier; 85 | width: @label-width * @medium-multiplier; 86 | font-size: @font-size * @medium-multiplier; 87 | bottom: 8px; 88 | left: -(@label-displacement * @medium-multiplier); 89 | } 90 | } 91 | 92 | // LARGE SIZE 93 | &--large { 94 | width: @width * @large-multiplier; 95 | height: @height * @large-multiplier; 96 | border-width: 4px; 97 | margin-left: @label-displacement * @large-multiplier - 4px; 98 | 99 | .iconGradient(@white, 35px); 100 | .borderGradient(@border-color, 38px); 101 | .polygonPath(48px, -(@label-displacement * @large-multiplier)); 102 | 103 | border-style: solid; 104 | border-image-slice: 1; 105 | 106 | &::before { 107 | top: -4px; 108 | right: -4px; 109 | height: @fold-size * @large-multiplier; 110 | width: @fold-size * @large-multiplier; 111 | box-shadow: -2px 2px 0px 2px @border-color; 112 | border-bottom-left-radius: 5px; 113 | 114 | .foldGradient(2px); 115 | } 116 | 117 | &::after { 118 | height: @label-height * @large-multiplier; 119 | width: @label-width * @large-multiplier; 120 | font-size: @font-size * @large-multiplier; 121 | bottom: 20px; 122 | left: -(@label-displacement * @large-multiplier); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/css/utils.less: -------------------------------------------------------------------------------- 1 | @import './colors'; 2 | 3 | 4 | .contentFromType() { 5 | &[data-file]::after { 6 | content: attr(data-file); 7 | } 8 | } 9 | 10 | .backgroundFromType() { 11 | &[data-file*=zip], 12 | &[data-file*=tar], 13 | &[data-file*=dmg], 14 | &[data-file*=jar], 15 | &[data-file*=rar] { 16 | &::after { 17 | background: @brown; 18 | } 19 | } 20 | 21 | &[data-file*=dwg], 22 | &[data-file*=obj], 23 | &[data-file*=dae], 24 | &[data-file*=skp], 25 | &[data-file*=fbx], 26 | &[data-file*='3ds'] { 27 | &::after { 28 | background: @purple; 29 | } 30 | } 31 | 32 | &[data-file*=doc], 33 | &[data-file*=rtf], 34 | &[data-file*=odt], 35 | &[data-file*=tex], 36 | &[data-file*=txt] { 37 | &::after { 38 | background: @blue; 39 | } 40 | } 41 | 42 | &[data-file*=svg], 43 | &[data-file*=ai] { 44 | &::after { 45 | background: @orange; 46 | } 47 | } 48 | 49 | &[data-file*=pdf] { 50 | &::after { 51 | background: @red; 52 | } 53 | } 54 | 55 | &[data-file*=xml], 56 | &[data-file*=csv], 57 | &[data-file*=xls] { 58 | &::after { 59 | background: @green; 60 | } 61 | } 62 | 63 | &[data-file*=jpg], 64 | &[data-file*=jpeg], 65 | &[data-file*=gif], 66 | &[data-file*=tif], 67 | &[data-file*=psd], 68 | &[data-file*=raw], 69 | &[data-file*=png] { 70 | &::after { 71 | background: @dark-blue; 72 | } 73 | } 74 | 75 | &[data-file*=webm], 76 | &[data-file*=mkv], 77 | &[data-file*=avi], 78 | &[data-file*=mov], 79 | &[data-file*=m4v], 80 | &[data-file*=mpeg], 81 | &[data-file*=mp4] { 82 | &::after { 83 | background: @pink; 84 | } 85 | } 86 | 87 | &[data-file*=mp3], 88 | &[data-file*=m4a], 89 | &[data-file*=ogg], 90 | &[data-file*=aac], 91 | &[data-file*=flac] { 92 | &::after { 93 | background: @dark-green; 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/css/vars.less: -------------------------------------------------------------------------------- 1 | @import './colors'; 2 | 3 | 4 | @width: 20px; 5 | @height: 28px; 6 | @fold-size: 10px; 7 | @label-width: 23px; 8 | @label-height: 10px; 9 | @label-displacement: 8px; 10 | @font-size: 8px; 11 | 12 | // SIZES 13 | @large-multiplier: 5; 14 | @medium-multiplier: 2.5; 15 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | export { default as FileIcon } from './js/FileIcon'; 2 | -------------------------------------------------------------------------------- /src/js/FileIcon.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import cn from 'classnames'; 3 | 4 | import '../css/style.less'; 5 | 6 | 7 | function getExtensionFromFilename(filename) { 8 | const sections = filename.split('.'); 9 | return sections[sections.length - 1]; 10 | } 11 | 12 | 13 | const className = (props) => cn('file-icon', { 14 | 'file-icon--medium': props.size === 'medium', 15 | 'file-icon--large': props.size === 'large', 16 | }); 17 | 18 | 19 | const Icon = ({ 20 | file, 21 | size, 22 | filename, 23 | }) => { 24 | const extension = filename && ! file ? getExtensionFromFilename(filename) : file; 25 | return ( 26 |
27 | ); 28 | }; 29 | 30 | 31 | export default Icon; 32 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | const ProgressPlugin = require('webpack/lib/ProgressPlugin'); 4 | const betterWebpackProgress = require('better-webpack-progress'); 5 | const checkEnv = require('@drawbotics/check-env'); 6 | const MiniCSSExtractPlugin = require('mini-css-extract-plugin'); 7 | 8 | 9 | checkEnv([ 'NODE_ENV' ]); 10 | 11 | 12 | const DEV_MODE = process.env.NODE_ENV === 'development'; 13 | 14 | 15 | module.exports = { 16 | mode: process.env.NODE_ENV, 17 | devtool: DEV_MODE ? 'cheap-module-source-map' : 'source-map', 18 | // stats: 'none', 19 | resolve: { 20 | extensions: [ '.js', '.jsx', '.less' ], 21 | }, 22 | entry: './src/index.js', 23 | output: { 24 | path: path.resolve(__dirname, 'dist'), 25 | filename: 'file-icons.js', 26 | sourceMapFilename: 'file-icons.js.map', 27 | library: 'file-icons', 28 | libraryTarget: 'umd', 29 | }, 30 | externals: [ 31 | 'react', 32 | 'react-dom', 33 | ], 34 | plugins: [ 35 | // new webpack.NamedModulesPlugin(), 36 | new webpack.EnvironmentPlugin({ 37 | NODE_ENV: process.env.NODE_ENV, 38 | }), 39 | new ProgressPlugin(betterWebpackProgress({ 40 | mode: 'compact', 41 | })), 42 | DEV_MODE ? {} :new MiniCSSExtractPlugin({ 43 | filename: 'style.css', 44 | }), 45 | ], 46 | module: { 47 | rules: [ 48 | { 49 | test: /\.jsx?/, 50 | include: [ path.resolve(__dirname, 'src') ], 51 | use: [ 'babel-loader' ], 52 | }, 53 | { 54 | test: /\.less?/, 55 | include: [ path.resolve(__dirname, 'src') ], 56 | use: [ 57 | DEV_MODE ? 'style-loader' : MiniCSSExtractPlugin.loader, 58 | 'css-loader', 59 | { 60 | loader: 'postcss-loader', 61 | options: { 62 | ident: 'postcss', 63 | plugins: [ 64 | require('autoprefixer')(), 65 | ], 66 | }, 67 | }, 68 | 'less-loader', 69 | ], 70 | }, 71 | ], 72 | }, 73 | }; 74 | --------------------------------------------------------------------------------