├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
├── dist
├── bfi.js
└── bfi.min.js
├── index.html
├── package-lock.json
├── package.json
└── src
└── css
├── bfi.css
└── bfi.min.css
/.gitattributes:
--------------------------------------------------------------------------------
1 | index.html linguist-documentation
2 | dist/* linguist-vendored=false
3 | src/** linguist-vendored=false
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (https://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # TypeScript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 |
60 | # parcel-bundler cache (https://parceljs.org/)
61 | .cache
62 |
63 | # next.js build output
64 | .next
65 |
66 | # nuxt.js build output
67 | .nuxt
68 |
69 | # vuepress build output
70 | .vuepress/dist
71 |
72 | # Serverless directories
73 | .serverless
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 nifte | Michael Lombardo
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 | # Better File Input
2 | A lightweight vanilla JavaScript library that converts HTML File Inputs into user-friendly, interactive elements with minimal setup required
3 |
4 | [](https://github.com/nifte/better-file-input/blob/master/LICENSE)
5 | [](https://www.npmjs.com/package/better-file-input)
6 |
7 | ## Demo
8 | View the live demo [here](https://nifte.github.io/better-file-input)
9 |
10 | ## Installation
11 | ### CDN
12 | Add the following to the `
` of your document:
13 | ```html
14 |
15 | ```
16 |
17 | ### npm
18 | 1. Install better-file-input with the following command:
19 | ```
20 | npm i better-file-input
21 | ```
22 | 2. Add the following to the `` of your document:
23 | ```html
24 |
25 | ```
26 |
27 | ### Manual
28 | 1. Download `dist/bfi.js` (or `dist/bfi.min.js`)
29 | 2. Add the following to the `` of your document:
30 | ```html
31 |
32 | ```
33 |
34 | ## Usage
35 | Simply add `class="bfi"` to your file inputs to automatically convert them to *better* file inputs:
36 | ```html
37 |
38 | ```
39 | **Note:** You can also add the `multiple` and `disabled` attributes to your better file inputs
40 |
41 | Dynamically-created file inputs will **not** be automatically converted - you need to call `bfi_init()` after creation to convert them:
42 | ```javascript
43 | bfi_init()
44 | ```
45 |
46 | You can call `bfi_clear()` to programmatically remove files from a converted file input:
47 | ```javascript
48 | bfi_clear() // Clear all better file inputs
49 | bfi_clear('#myFileInput') // Clear the better file input with the id 'myFileInput'
50 | ```
51 |
52 | ## Customization
53 | The `bfi_init()` function accepts one optional argument - an object containing pre-defined options to customize the look of your better file inputs:
54 | ```javascript
55 | bfi_init({
56 | 'containerColor': '#b8bfd8', // The color of the file container
57 | 'labelColor': 'rgb(77, 79, 86)', // The color of the file container label
58 | 'fileColor': 'linear-gradient(#84f189, #53b658)', // The color of the files
59 | 'fileNameColor': 'darkblue', // The color of the file names
60 | 'fileInfoColor': 'rgba(55, 55, 55, 0.75)', // The color of the file size info
61 | 'dragDropBorder': '3px dotted #374f6d' // The drag & drop border
62 | })
63 | ```
64 | To reset the look of your better file inputs, simply call the `bfi_reset()` function:
65 | ```javascript
66 | bfi_reset()
67 | ```
--------------------------------------------------------------------------------
/dist/bfi.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Better File Input 1.3.0 (https://github.com/nifte/better-file-input)
3 | * by nifte (https://github.com/nifte)
4 | * Licensed under MIT (https://github.com/nifte/better-file-input/blob/master/LICENSE)
5 | */
6 |
7 | // Define styles
8 | const style = '@-webkit-keyframes file_grow{0%{max-height:0;padding:0 10px}100%{max-height:100px}}@keyframes file_grow{0%{max-height:0;padding:0 10px}100%{max-height:100px}}@-webkit-keyframes shadow_grow{0%{-webkit-transform:scale(0);transform:scale(0)}100%{-webkit-transform:scale(1);transform:scale(1)}}@keyframes shadow_grow{0%{-webkit-transform:scale(0);transform:scale(0)}100%{-webkit-transform:scale(1);transform:scale(1)}}.bfi-container{display:block;position:relative;width:100%;height:unset;margin:0;padding:0;border-radius:5px;background:#f0f0f0;-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-transition:max-height 1s ease;transition:max-height 1s ease}.bfi-container.expanded{border:4px dashed gray}.bfi-container *{-webkit-box-sizing:border-box;box-sizing:border-box}.bfi-converted,.bfi-converted-multi{opacity:0;position:absolute;top:0;left:0;width:100%;height:100%}.bfi-container:not(.expanded) .bfi-converted,.bfi-container:not(.expanded) .bfi-converted-multi{z-index:-10}.bfi-container.expanded .bfi-converted,.bfi-container.expanded .bfi-converted-multi{z-index:20}.bfi-label,.bfi-label-selected{display:inline-block;width:100%;height:unset;margin:0;text-align:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;z-index:10}.bfi-container:not(.expanded) .bfi-label,.bfi-label-selected{padding:10px 20px}.bfi-container.expanded .bfi-label{padding:40px 20px}.bfi-label{-webkit-transition:padding .25s ease;transition:padding .25s ease}.bfi-clear,.bfi-label span{cursor:pointer;text-decoration:underline}.bfi-file{display:inline-block;width:-o-calc(100% - 20px);width:calc(100% - 20px);padding:6px 10px;background:#646464;background:-webkit-gradient(linear,left bottom,left top,from(rgba(90,90,90,1)),color-stop(75%,rgba(110,110,110,1)));background:linear-gradient(0deg,rgba(90,90,90,1) 0,rgba(110,110,110,1) 75%);color:#fff;border-radius:7px;z-index:10;line-height:1em;-webkit-animation:file_grow .7s ease;animation:file_grow .7s ease}.bfi-converted~.bfi-file{margin:10px}.bfi-converted-multi~.bfi-file{margin:0 10px 10px 10px}.bfi-file i{font-style:normal;font-size:.8em;color:#b4b4b4}.bfi-file .bfi-clear{position:absolute;right:25px;top:calc(50% - 2px);-webkit-transform:translateY(-50%);transform:translateY(-50%);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.bfi-shadow-container{position:absolute;display:none;margin:0;padding:0;left:0;right:0;top:0;bottom:0;clip:rect(0,auto,auto,0);z-index:15}.bfi-container.expanded .bfi-shadow-container{display:unset}.bfi-shadow{position:absolute;display:none;width:350px;height:350px;border-radius:50%;background:rgba(0,0,0,.06);-webkit-transition:left .1s ease,top .1s ease;transition:left .1s ease,top .1s ease}.bfi-container.hovering .bfi-shadow{display:unset;-webkit-animation:shadow_grow .5s ease;animation:shadow_grow .5s ease}';
9 |
10 | // Initialize function
11 | var bfi_counter = 0;
12 | function bfi_init(options = null) {
13 | if (document.querySelectorAll('.bfi-style').length < 1) document.body.insertAdjacentHTML('beforeend', ``);
14 | let bfi = document.querySelectorAll('input[type="file"].bfi');
15 | let total = bfi.length;
16 | for (let i = 0; i < total; i++) {
17 | bfi_counter++;
18 | let input = bfi[i];
19 | let container = document.createElement('div');
20 | let label = '';
21 | input.parentElement.insertBefore(container, input);
22 | container.appendChild(input);
23 | container.classList.add('bfi-container');
24 | if (input.hasAttribute('multiple')) {
25 | input.classList.add('bfi-converted-multi');
26 | container.insertAdjacentHTML('beforeend', '');
27 | label = 'Drag & Drop files here, or Browse';
28 | } else {
29 | input.classList.add('bfi-converted');
30 | label = 'Drag & Drop file here, or Browse';
31 | }
32 | if (!input.hasAttribute('id')) input.setAttribute('id', `bfi-${bfi_counter}`);
33 | let id = input.getAttribute('id');
34 | container.insertAdjacentHTML('afterbegin', ``);
35 | container.insertAdjacentHTML('beforeend', '');
36 | input.setAttribute('tabindex', -1);
37 | }
38 | document.querySelectorAll('input[type="file"].bfi').forEach(el => { el.classList.remove('bfi'); });
39 | if (options != null) {
40 | let style_override = '';
41 | if (options.hasOwnProperty('labelColor')) style_override += `.bfi-label, .bfi-label-selected { color: ${options.labelColor} }`;
42 | if (options.hasOwnProperty('containerColor')) style_override += `.bfi-container { background: ${options.containerColor} }`;
43 | if (options.hasOwnProperty('fileColor')) style_override += `.bfi-file { background: ${options.fileColor} }`;
44 | if (options.hasOwnProperty('fileNameColor')) style_override += `.bfi-file { color: ${options.fileNameColor} }`;
45 | if (options.hasOwnProperty('fileInfoColor')) style_override += `.bfi-file i { color: ${options.fileInfoColor} }`;
46 | if (options.hasOwnProperty('dragDropBorder')) style_override += `.bfi-container.expanded { border: ${options.dragDropBorder} }`;
47 | document.body.insertAdjacentHTML('beforeend', ``);
48 | }
49 | }
50 |
51 | // Reset style overrides
52 | function bfi_reset() {
53 | let styles = document.querySelectorAll('.bfi-style-override');
54 | if (styles.length) {
55 | styles.forEach(el => {
56 | el.remove();
57 | });
58 | }
59 | }
60 |
61 | // Initialize on DOM load
62 | document.addEventListener('DOMContentLoaded', () => {
63 | bfi_init();
64 | });
65 |
66 | // Drag files onto page
67 | var bfi_drag_timeout, bfi_hover_timeout;
68 | window.addEventListener('dragover', e => {
69 | let dt = e.dataTransfer;
70 | if (dt.types && (dt.types.indexOf ? dt.types.indexOf('Files') != -1 : dt.types.contains('Files'))) {
71 | document.querySelectorAll('.bfi-container').forEach(el => {
72 | if (el.querySelector('.bfi-label').style.display != 'none') el.classList.add('expanded');
73 | });
74 | clearTimeout(bfi_drag_timeout);
75 | bfi_drag_timeout = setTimeout(() => {
76 | document.querySelectorAll('.bfi-container').forEach(el => { el.classList.remove('expanded'); });
77 | }, 100);
78 | document.querySelectorAll('.bfi-shadow').forEach(el => {
79 | let container = el.closest('.bfi-shadow-container').getBoundingClientRect();
80 | let size = Number(container.width * 0.75).toFixed();
81 | if (size > 500) size = 500;
82 | el.style.width = size + 'px';
83 | el.style.height = size + 'px';
84 | el.style.left = Number(e.pageX - container.left - (size / 2)).toFixed() + 'px';
85 | el.style.top = Number(e.pageY - container.top - (size / 2)).toFixed() + 'px';
86 | });
87 | if (e.target.classList.contains('bfi-converted') || e.target.classList.contains('bfi-converted-multi')) {
88 | let container = e.target.closest('.bfi-container');
89 | container.classList.add('hovering');
90 | clearTimeout(bfi_hover_timeout);
91 | bfi_hover_timeout = setTimeout(() => {
92 | container.classList.remove('hovering');
93 | }, 100);
94 | }
95 | }
96 | });
97 |
98 | // Drag files out of container
99 | window.addEventListener('dragleave', e => {
100 | if (e.target.classList.contains('bfi-converted') || e.target.classList.contains('bfi-converted-multi')) {
101 | let container = e.target.closest('.bfi-container');
102 | container.classList.remove('hovering');
103 | }
104 | });
105 |
106 | // Prevent browser from opening any dragged files
107 | window.addEventListener('dragover', e => {
108 | if (!e.target.classList.contains('bfi-converted') && !e.target.classList.contains('bfi-converted-multi')) {
109 | e.preventDefault();
110 | e.dataTransfer.effectAllowed = 'none';
111 | e.dataTransfer.dropEffect = 'none';
112 | }
113 | });
114 |
115 | // Prevent browser from opening any dropped files
116 | window.addEventListener('drop', e => {
117 | if (!e.target.classList.contains('bfi-converted') && !e.target.classList.contains('bfi-converted-multi')) {
118 | e.preventDefault();
119 | e.dataTransfer.effectAllowed = 'none';
120 | e.dataTransfer.dropEffect = 'none';
121 | }
122 | });
123 |
124 | // Watch for file updates
125 | document.addEventListener('change', e => {
126 | if (e.target.classList.contains('bfi-converted')) {
127 | let container = e.target.closest('.bfi-container');
128 | if (e.target.files.length) {
129 | container.querySelector('.bfi-label').style.display = 'none';
130 | container.querySelectorAll('.bfi-file').forEach(el => { el.remove(); });
131 | let file = e.target.files[0].name;
132 | let size = Number(e.target.files[0].size / 1000).toFixed(1) + ' KB';
133 | container.insertAdjacentHTML('beforeend', `Undo${file}
${size}
`);
134 | } else {
135 | container.querySelector('.bfi-label').style.display = '';
136 | container.querySelectorAll('.bfi-file').forEach(el => { el.remove(); });
137 | }
138 | }
139 | if (e.target.classList.contains('bfi-converted-multi')) {
140 | let container = e.target.closest('.bfi-container');
141 | if (e.target.files.length) {
142 | container.querySelector('.bfi-label').style.display = 'none';
143 | container.querySelector('.bfi-label-selected').style.display = '';
144 | container.querySelectorAll('.bfi-file').forEach(el => { el.remove(); });
145 | let files = [];
146 | for (let i = 0; i < e.target.files.length; i++) {
147 | files.push({
148 | 'name': e.target.files[i].name,
149 | 'size': Number(e.target.files[i].size / 1000).toFixed(1) + ' KB'
150 | });
151 | }
152 | let fileCount = '1 file';
153 | if (files.length > 1) fileCount = `${files.length} files`;
154 | container.querySelector('.bfi-label-selected').innerHTML = `${fileCount} selected. Undo`;
155 | files.forEach(file => {
156 | container.insertAdjacentHTML('beforeend', `${file.name}
${file.size}
`);
157 | });
158 | } else {
159 | container.querySelector('.bfi-label').style.display = '';
160 | container.querySelector('.bfi-label-selected').style.display = 'none';
161 | container.querySelectorAll('.bfi-file').forEach(el => { el.remove(); });
162 | }
163 | }
164 | });
165 |
166 | // Simulate click on focused bfi element
167 | document.addEventListener('keyup', e => {
168 | if (e.keyCode == 32 || e.keyCode == 13) {
169 | if (document.activeElement.classList.contains('bfi-label')) document.activeElement.click();
170 | if (document.activeElement.classList.contains('bfi-clear')) document.activeElement.click();
171 | }
172 | });
173 |
174 | // Clear files on undo
175 | document.addEventListener('click', e => {
176 | if (e.target.classList.contains('bfi-clear')) {
177 | let container = e.target.closest('.bfi-container');
178 | let inputID = container.querySelector('.bfi-converted, .bfi-converted-multi').getAttribute('id');
179 | bfi_clear(`#${inputID}`);
180 | }
181 | });
182 |
183 | // Clear files from a bfi element
184 | function bfi_clear(query = null) {
185 | if (query == null) query = '.bfi-converted, .bfi-converted-multi';
186 | let inputs = document.querySelectorAll(query);
187 | if (inputs.length) {
188 | inputs.forEach(el => {
189 | el.value = '';
190 | el.dispatchEvent(new Event('change', { 'bubbles': true }));
191 | });
192 | }
193 | }
--------------------------------------------------------------------------------
/dist/bfi.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Better File Input 1.3.0 (https://github.com/nifte/better-file-input)
3 | * by nifte (https://github.com/nifte)
4 | * Licensed under MIT (https://github.com/nifte/better-file-input/blob/master/LICENSE)
5 | */
6 | const style="@-webkit-keyframes file_grow{0%{max-height:0;padding:0 10px}100%{max-height:100px}}@keyframes file_grow{0%{max-height:0;padding:0 10px}100%{max-height:100px}}@-webkit-keyframes shadow_grow{0%{-webkit-transform:scale(0);transform:scale(0)}100%{-webkit-transform:scale(1);transform:scale(1)}}@keyframes shadow_grow{0%{-webkit-transform:scale(0);transform:scale(0)}100%{-webkit-transform:scale(1);transform:scale(1)}}.bfi-container{display:block;position:relative;width:100%;height:unset;margin:0;padding:0;border-radius:5px;background:#f0f0f0;-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-transition:max-height 1s ease;transition:max-height 1s ease}.bfi-container.expanded{border:4px dashed gray}.bfi-container *{-webkit-box-sizing:border-box;box-sizing:border-box}.bfi-converted,.bfi-converted-multi{opacity:0;position:absolute;top:0;left:0;width:100%;height:100%}.bfi-container:not(.expanded) .bfi-converted,.bfi-container:not(.expanded) .bfi-converted-multi{z-index:-10}.bfi-container.expanded .bfi-converted,.bfi-container.expanded .bfi-converted-multi{z-index:20}.bfi-label,.bfi-label-selected{display:inline-block;width:100%;height:unset;margin:0;text-align:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;z-index:10}.bfi-container:not(.expanded) .bfi-label,.bfi-label-selected{padding:10px 20px}.bfi-container.expanded .bfi-label{padding:40px 20px}.bfi-label{-webkit-transition:padding .25s ease;transition:padding .25s ease}.bfi-clear,.bfi-label span{cursor:pointer;text-decoration:underline}.bfi-file{display:inline-block;width:-o-calc(100% - 20px);width:calc(100% - 20px);padding:6px 10px;background:#646464;background:-webkit-gradient(linear,left bottom,left top,from(rgba(90,90,90,1)),color-stop(75%,rgba(110,110,110,1)));background:linear-gradient(0deg,rgba(90,90,90,1) 0,rgba(110,110,110,1) 75%);color:#fff;border-radius:7px;z-index:10;line-height:1em;-webkit-animation:file_grow .7s ease;animation:file_grow .7s ease}.bfi-converted~.bfi-file{margin:10px}.bfi-converted-multi~.bfi-file{margin:0 10px 10px 10px}.bfi-file i{font-style:normal;font-size:.8em;color:#b4b4b4}.bfi-file .bfi-clear{position:absolute;right:25px;top:calc(50% - 2px);-webkit-transform:translateY(-50%);transform:translateY(-50%);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.bfi-shadow-container{position:absolute;display:none;margin:0;padding:0;left:0;right:0;top:0;bottom:0;clip:rect(0,auto,auto,0);z-index:15}.bfi-container.expanded .bfi-shadow-container{display:unset}.bfi-shadow{position:absolute;display:none;width:350px;height:350px;border-radius:50%;background:rgba(0,0,0,.06);-webkit-transition:left .1s ease,top .1s ease;transition:left .1s ease,top .1s ease}.bfi-container.hovering .bfi-shadow{display:unset;-webkit-animation:shadow_grow .5s ease;animation:shadow_grow .5s ease}";var bfi_drag_timeout,bfi_hover_timeout,bfi_counter=0;function bfi_init(e=null){document.querySelectorAll(".bfi-style").length<1&&document.body.insertAdjacentHTML("beforeend",``);let t=document.querySelectorAll('input[type="file"].bfi'),i=t.length;for(let e=0;e'),a="Drag & Drop files here, or Browse"):(i.classList.add("bfi-converted"),a="Drag & Drop file here, or Browse"),i.hasAttribute("id")||i.setAttribute("id",`bfi-${bfi_counter}`);let r=i.getAttribute("id");n.insertAdjacentHTML("afterbegin",``),n.insertAdjacentHTML("beforeend",''),i.setAttribute("tabindex",-1)}if(document.querySelectorAll('input[type="file"].bfi').forEach(e=>{e.classList.remove("bfi")}),null!=e){let t="";e.hasOwnProperty("labelColor")&&(t+=`.bfi-label, .bfi-label-selected { color: ${e.labelColor} }`),e.hasOwnProperty("containerColor")&&(t+=`.bfi-container { background: ${e.containerColor} }`),e.hasOwnProperty("fileColor")&&(t+=`.bfi-file { background: ${e.fileColor} }`),e.hasOwnProperty("fileNameColor")&&(t+=`.bfi-file { color: ${e.fileNameColor} }`),e.hasOwnProperty("fileInfoColor")&&(t+=`.bfi-file i { color: ${e.fileInfoColor} }`),e.hasOwnProperty("dragDropBorder")&&(t+=`.bfi-container.expanded { border: ${e.dragDropBorder} }`),document.body.insertAdjacentHTML("beforeend",``)}}function bfi_reset(){let e=document.querySelectorAll(".bfi-style-override");e.length&&e.forEach(e=>{e.remove()})}function bfi_clear(e=null){null==e&&(e=".bfi-converted, .bfi-converted-multi");let t=document.querySelectorAll(e);t.length&&t.forEach(e=>{e.value="",e.dispatchEvent(new Event("change",{bubbles:!0}))})}document.addEventListener("DOMContentLoaded",()=>{bfi_init()}),window.addEventListener("dragover",e=>{let t=e.dataTransfer;if(t.types&&(t.types.indexOf?-1!=t.types.indexOf("Files"):t.types.contains("Files"))&&(document.querySelectorAll(".bfi-container").forEach(e=>{"none"!=e.querySelector(".bfi-label").style.display&&e.classList.add("expanded")}),clearTimeout(bfi_drag_timeout),bfi_drag_timeout=setTimeout(()=>{document.querySelectorAll(".bfi-container").forEach(e=>{e.classList.remove("expanded")})},100),document.querySelectorAll(".bfi-shadow").forEach(t=>{let i=t.closest(".bfi-shadow-container").getBoundingClientRect(),n=Number(.75*i.width).toFixed();n>500&&(n=500),t.style.width=n+"px",t.style.height=n+"px",t.style.left=Number(e.pageX-i.left-n/2).toFixed()+"px",t.style.top=Number(e.pageY-i.top-n/2).toFixed()+"px"}),e.target.classList.contains("bfi-converted")||e.target.classList.contains("bfi-converted-multi"))){let t=e.target.closest(".bfi-container");t.classList.add("hovering"),clearTimeout(bfi_hover_timeout),bfi_hover_timeout=setTimeout(()=>{t.classList.remove("hovering")},100)}}),window.addEventListener("dragleave",e=>{if(e.target.classList.contains("bfi-converted")||e.target.classList.contains("bfi-converted-multi")){e.target.closest(".bfi-container").classList.remove("hovering")}}),window.addEventListener("dragover",e=>{e.target.classList.contains("bfi-converted")||e.target.classList.contains("bfi-converted-multi")||(e.preventDefault(),e.dataTransfer.effectAllowed="none",e.dataTransfer.dropEffect="none")}),window.addEventListener("drop",e=>{e.target.classList.contains("bfi-converted")||e.target.classList.contains("bfi-converted-multi")||(e.preventDefault(),e.dataTransfer.effectAllowed="none",e.dataTransfer.dropEffect="none")}),document.addEventListener("change",e=>{if(e.target.classList.contains("bfi-converted")){let t=e.target.closest(".bfi-container");if(e.target.files.length){t.querySelector(".bfi-label").style.display="none",t.querySelectorAll(".bfi-file").forEach(e=>{e.remove()});let i=e.target.files[0].name,n=Number(e.target.files[0].size/1e3).toFixed(1)+" KB";t.insertAdjacentHTML("beforeend",`Undo${i}
${n}
`)}else t.querySelector(".bfi-label").style.display="",t.querySelectorAll(".bfi-file").forEach(e=>{e.remove()})}if(e.target.classList.contains("bfi-converted-multi")){let t=e.target.closest(".bfi-container");if(e.target.files.length){t.querySelector(".bfi-label").style.display="none",t.querySelector(".bfi-label-selected").style.display="",t.querySelectorAll(".bfi-file").forEach(e=>{e.remove()});let i=[];for(let t=0;t1&&(n=`${i.length} files`),t.querySelector(".bfi-label-selected").innerHTML=`${n} selected. Undo`,i.forEach(e=>{t.insertAdjacentHTML("beforeend",`${e.name}
${e.size}
`)})}else t.querySelector(".bfi-label").style.display="",t.querySelector(".bfi-label-selected").style.display="none",t.querySelectorAll(".bfi-file").forEach(e=>{e.remove()})}}),document.addEventListener("keyup",e=>{32!=e.keyCode&&13!=e.keyCode||(document.activeElement.classList.contains("bfi-label")&&document.activeElement.click(),document.activeElement.classList.contains("bfi-clear")&&document.activeElement.click())}),document.addEventListener("click",e=>{if(e.target.classList.contains("bfi-clear")){bfi_clear(`#${e.target.closest(".bfi-container").querySelector(".bfi-converted, .bfi-converted-multi").getAttribute("id")}`)}});
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Better File Input
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
📁 Single file
13 |
14 |
15 |
16 |
17 |
18 | →
19 | ↓
20 |
21 |
22 |
23 |
24 |
25 |
🗃 Multiple files
26 |
27 |
28 |
29 |
30 |
31 | →
32 | ↓
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "better-file-input",
3 | "version": "1.3.2",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "balanced-match": {
8 | "version": "1.0.0",
9 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
10 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
11 | "dev": true
12 | },
13 | "brace-expansion": {
14 | "version": "1.1.11",
15 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
16 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
17 | "dev": true,
18 | "requires": {
19 | "balanced-match": "1.0.0",
20 | "concat-map": "0.0.1"
21 | }
22 | },
23 | "clean-css": {
24 | "version": "4.1.11",
25 | "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.1.11.tgz",
26 | "integrity": "sha1-Ls3xRaujj1R0DybO/Q/z4D4SXWo=",
27 | "dev": true,
28 | "requires": {
29 | "source-map": "0.5.7"
30 | },
31 | "dependencies": {
32 | "source-map": {
33 | "version": "0.5.7",
34 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
35 | "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
36 | "dev": true
37 | }
38 | }
39 | },
40 | "clean-css-cli": {
41 | "version": "4.1.11",
42 | "resolved": "https://registry.npmjs.org/clean-css-cli/-/clean-css-cli-4.1.11.tgz",
43 | "integrity": "sha1-AcVonwW8USojKF8d79veALwR2Gw=",
44 | "dev": true,
45 | "requires": {
46 | "clean-css": "4.1.11",
47 | "commander": "2.13.0",
48 | "glob": "7.1.2"
49 | }
50 | },
51 | "commander": {
52 | "version": "2.13.0",
53 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz",
54 | "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==",
55 | "dev": true
56 | },
57 | "concat-map": {
58 | "version": "0.0.1",
59 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
60 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
61 | "dev": true
62 | },
63 | "fs.realpath": {
64 | "version": "1.0.0",
65 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
66 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
67 | "dev": true
68 | },
69 | "glob": {
70 | "version": "7.1.2",
71 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
72 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
73 | "dev": true,
74 | "requires": {
75 | "fs.realpath": "1.0.0",
76 | "inflight": "1.0.6",
77 | "inherits": "2.0.3",
78 | "minimatch": "3.0.4",
79 | "once": "1.4.0",
80 | "path-is-absolute": "1.0.1"
81 | }
82 | },
83 | "inflight": {
84 | "version": "1.0.6",
85 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
86 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
87 | "dev": true,
88 | "requires": {
89 | "once": "1.4.0",
90 | "wrappy": "1.0.2"
91 | }
92 | },
93 | "inherits": {
94 | "version": "2.0.3",
95 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
96 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
97 | "dev": true
98 | },
99 | "minimatch": {
100 | "version": "3.0.4",
101 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
102 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
103 | "dev": true,
104 | "requires": {
105 | "brace-expansion": "1.1.11"
106 | }
107 | },
108 | "once": {
109 | "version": "1.4.0",
110 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
111 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
112 | "dev": true,
113 | "requires": {
114 | "wrappy": "1.0.2"
115 | }
116 | },
117 | "path-is-absolute": {
118 | "version": "1.0.1",
119 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
120 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
121 | "dev": true
122 | },
123 | "source-map": {
124 | "version": "0.6.1",
125 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
126 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
127 | "dev": true
128 | },
129 | "uglify-es": {
130 | "version": "3.3.9",
131 | "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz",
132 | "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==",
133 | "dev": true,
134 | "requires": {
135 | "commander": "2.13.0",
136 | "source-map": "0.6.1"
137 | }
138 | },
139 | "wrappy": {
140 | "version": "1.0.2",
141 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
142 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
143 | "dev": true
144 | }
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "better-file-input",
3 | "version": "1.3.2",
4 | "author": {
5 | "name": "nite",
6 | "email": "michael@nifte.io",
7 | "url": "https://nifte.io"
8 | },
9 | "description": "A JavaScript library for improving HTML File Inputs",
10 | "main": "dist/bfi.js",
11 | "scripts": {
12 | "minify-css": "cleancss -o src/css/bfi.min.css src/css/bfi.css",
13 | "minify-js": "uglifyjs dist/bfi.js -c -m -o dist/bfi.min.js"
14 | },
15 | "repository": {
16 | "type": "git",
17 | "url": "https://github.com/nifte/better-file-input"
18 | },
19 | "license": "MIT",
20 | "devDependencies": {
21 | "clean-css-cli": "^4.1.11",
22 | "uglify-es": "^3.3.9"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/css/bfi.css:
--------------------------------------------------------------------------------
1 | @-webkit-keyframes file_grow {
2 | 0% { max-height: 0px; padding: 0px 10px; }
3 | 100% { max-height: 100px; }
4 | }
5 |
6 | @keyframes file_grow {
7 | 0% { max-height: 0px; padding: 0px 10px; }
8 | 100% { max-height: 100px; }
9 | }
10 |
11 | @-webkit-keyframes shadow_grow {
12 | 0% { -webkit-transform: scale(0); transform: scale(0); }
13 | 100% { -webkit-transform: scale(1); transform: scale(1); }
14 | }
15 |
16 | @keyframes shadow_grow {
17 | 0% { -webkit-transform: scale(0); transform: scale(0); }
18 | 100% { -webkit-transform: scale(1); transform: scale(1); }
19 | }
20 |
21 | .bfi-container {
22 | display: block;
23 | position: relative;
24 | width: 100%;
25 | height: unset;
26 | margin: 0;
27 | padding: 0;
28 | border-radius: 5px;
29 | background: rgb(240, 240, 240);
30 | -webkit-box-sizing: border-box;
31 | box-sizing: border-box;
32 | -webkit-transition: max-height 1s ease;
33 | transition: max-height 1s ease;
34 | }
35 |
36 | .bfi-container.expanded {
37 | border: 4px dashed gray;
38 | }
39 |
40 | .bfi-container * {
41 | -webkit-box-sizing: border-box;
42 | box-sizing: border-box;
43 | }
44 |
45 | .bfi-converted,
46 | .bfi-converted-multi {
47 | opacity: 0;
48 | position: absolute;
49 | top: 0;
50 | left: 0;
51 | width: 100%;
52 | height: 100%;
53 | }
54 |
55 | .bfi-container:not(.expanded) .bfi-converted,
56 | .bfi-container:not(.expanded) .bfi-converted-multi {
57 | z-index: -10;
58 | }
59 |
60 | .bfi-container.expanded .bfi-converted,
61 | .bfi-container.expanded .bfi-converted-multi {
62 | z-index: 20;
63 | }
64 |
65 | .bfi-label,
66 | .bfi-label-selected {
67 | display: inline-block;
68 | width: 100%;
69 | height: unset;
70 | margin: 0;
71 | text-align: center;
72 | -webkit-user-select: none;
73 | -moz-user-select: none;
74 | -ms-user-select: none;
75 | user-select: none;
76 | z-index: 10;
77 | }
78 |
79 | .bfi-container:not(.expanded) .bfi-label,
80 | .bfi-label-selected {
81 | padding: 10px 20px;
82 | }
83 |
84 | .bfi-container.expanded .bfi-label {
85 | padding: 40px 20px;
86 | }
87 |
88 | .bfi-label {
89 | -webkit-transition: padding .25s ease;
90 | transition: padding .25s ease;
91 | }
92 |
93 | .bfi-clear,
94 | .bfi-label span {
95 | cursor: pointer;
96 | text-decoration: underline;
97 | }
98 |
99 | .bfi-file {
100 | display: inline-block;
101 | width: -o-calc(100% - 20px);
102 | width: calc(100% - 20px);
103 | padding: 6px 10px;
104 | background: rgb(100, 100, 100);
105 | background: -webkit-gradient(linear, left bottom, left top, from(rgba(90, 90, 90, 1)), color-stop(75%, rgba(110, 110, 110, 1)));
106 | background: linear-gradient(0deg, rgba(90, 90, 90, 1) 0%, rgba(110, 110, 110, 1) 75%);
107 | color: white;
108 | border-radius: 7px;
109 | z-index: 10;
110 | line-height: 1em;
111 | text-align: left;
112 | -webkit-animation: file_grow 0.7s ease;
113 | animation: file_grow 0.7s ease;
114 | }
115 |
116 | .bfi-converted ~ .bfi-file {
117 | margin: 10px;
118 | }
119 |
120 | .bfi-converted-multi ~ .bfi-file {
121 | margin: 0 10px 10px 10px;
122 | }
123 |
124 | .bfi-file i {
125 | font-style: normal;
126 | font-size: 0.8em;
127 | color: rgb(180, 180, 180);
128 | }
129 |
130 | .bfi-file .bfi-clear {
131 | position: absolute;
132 | right: 25px;
133 | top: calc(50% - 2px);
134 | -webkit-transform: translateY(-50%);
135 | transform: translateY(-50%);
136 | -webkit-user-select: none;
137 | -moz-user-select: none;
138 | -ms-user-select: none;
139 | user-select: none;
140 | }
141 |
142 | .bfi-shadow-container {
143 | position: absolute;
144 | display: none;
145 | margin: 0;
146 | padding: 0;
147 | left: 0;
148 | right: 0;
149 | top: 0;
150 | bottom: 0;
151 | clip: rect(0, auto, auto, 0);
152 | z-index: 15;
153 | }
154 |
155 | .bfi-container.expanded .bfi-shadow-container {
156 | display: unset;
157 | }
158 |
159 | .bfi-shadow {
160 | position: absolute;
161 | display: none;
162 | width: 350px;
163 | height: 350px;
164 | border-radius: 50%;
165 | background: rgba(0, 0, 0, 0.06);
166 | -webkit-transition: left 0.1s ease, top 0.1s ease;
167 | transition: left 0.1s ease, top 0.1s ease;
168 | }
169 |
170 | .bfi-container.hovering .bfi-shadow {
171 | display: unset;
172 | -webkit-animation: shadow_grow 0.5s ease;
173 | animation: shadow_grow 0.5s ease
174 | }
--------------------------------------------------------------------------------
/src/css/bfi.min.css:
--------------------------------------------------------------------------------
1 | @-webkit-keyframes file_grow{0%{max-height:0;padding:0 10px}100%{max-height:100px}}@keyframes file_grow{0%{max-height:0;padding:0 10px}100%{max-height:100px}}@-webkit-keyframes shadow_grow{0%{-webkit-transform:scale(0);transform:scale(0)}100%{-webkit-transform:scale(1);transform:scale(1)}}@keyframes shadow_grow{0%{-webkit-transform:scale(0);transform:scale(0)}100%{-webkit-transform:scale(1);transform:scale(1)}}.bfi-container{display:block;position:relative;width:100%;height:unset;margin:0;padding:0;border-radius:5px;background:#f0f0f0;-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-transition:max-height 1s ease;transition:max-height 1s ease}.bfi-container.expanded{border:4px dashed gray}.bfi-container *{-webkit-box-sizing:border-box;box-sizing:border-box}.bfi-converted,.bfi-converted-multi{opacity:0;position:absolute;top:0;left:0;width:100%;height:100%}.bfi-container:not(.expanded) .bfi-converted,.bfi-container:not(.expanded) .bfi-converted-multi{z-index:-10}.bfi-container.expanded .bfi-converted,.bfi-container.expanded .bfi-converted-multi{z-index:20}.bfi-label,.bfi-label-selected{display:inline-block;width:100%;height:unset;margin:0;text-align:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;z-index:10}.bfi-container:not(.expanded) .bfi-label,.bfi-label-selected{padding:10px 20px}.bfi-container.expanded .bfi-label{padding:40px 20px}.bfi-label{-webkit-transition:padding .25s ease;transition:padding .25s ease}.bfi-clear,.bfi-label span{cursor:pointer;text-decoration:underline}.bfi-file{display:inline-block;width:-o-calc(100% - 20px);width:calc(100% - 20px);padding:6px 10px;background:#646464;background:-webkit-gradient(linear,left bottom,left top,from(rgba(90,90,90,1)),color-stop(75%,rgba(110,110,110,1)));background:linear-gradient(0deg,rgba(90,90,90,1) 0,rgba(110,110,110,1) 75%);color:#fff;border-radius:7px;z-index:10;line-height:1em;text-align:left;-webkit-animation:file_grow .7s ease;animation:file_grow .7s ease}.bfi-converted~.bfi-file{margin:10px}.bfi-converted-multi~.bfi-file{margin:0 10px 10px 10px}.bfi-file i{font-style:normal;font-size:.8em;color:#b4b4b4}.bfi-file .bfi-clear{position:absolute;right:25px;top:calc(50% - 2px);-webkit-transform:translateY(-50%);transform:translateY(-50%);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.bfi-shadow-container{position:absolute;display:none;margin:0;padding:0;left:0;right:0;top:0;bottom:0;clip:rect(0,auto,auto,0);z-index:15}.bfi-container.expanded .bfi-shadow-container{display:unset}.bfi-shadow{position:absolute;display:none;width:350px;height:350px;border-radius:50%;background:rgba(0,0,0,.06);-webkit-transition:left .1s ease,top .1s ease;transition:left .1s ease,top .1s ease}.bfi-container.hovering .bfi-shadow{display:unset;-webkit-animation:shadow_grow .5s ease;animation:shadow_grow .5s ease}
--------------------------------------------------------------------------------