diff --git a/demos/fileinput/index.html b/demos/fileinput/index.html new file mode 100644 index 00000000..00ef02fc --- /dev/null +++ b/demos/fileinput/index.html @@ -0,0 +1,11 @@ + + + + OxJS FileInput Demo + + + + + + \ No newline at end of file diff --git a/demos/fileinput/js/fileinput.js b/demos/fileinput/js/fileinput.js new file mode 100644 index 00000000..02bc0242 --- /dev/null +++ b/demos/fileinput/js/fileinput.js @@ -0,0 +1,16 @@ +Ox.load('UI', function() { + + //Ox.Theme('modern'); + + Ox.FileInput({ + //maxFiles: 1, + width: 256 + }) + .css({ + position: 'absolute', + left: '16px', + top: '16px' + }) + .appendTo(Ox.UI.$body); + +}); \ No newline at end of file diff --git a/source/Ox.UI/css/Ox.UI.css b/source/Ox.UI/css/Ox.UI.css index a4e50e5c..e600e2f3 100644 --- a/source/Ox.UI/css/Ox.UI.css +++ b/source/Ox.UI/css/Ox.UI.css @@ -733,6 +733,28 @@ input.OxCheckbox { } /* -------------------------------------------------------------------------------- +OxFileInput +-------------------------------------------------------------------------------- +*/ +.OxFileInput > .OxBar { + border-width: 1px; + border-style: solid; + border-radius: 8px; +} +.OxFileInput > .OxFiles { + position: absolute; + top: 15px; + border-width: 1px; + border-style: solid; + border-bottom-left-radius: 8px; + border-bottom-right-radius: 8px; +} +.OxFileInput > .OxFiles .OxItem:last-child { + border-bottom-left-radius: 8px; + border-bottom-right-radius: 8px; +} +/* +-------------------------------------------------------------------------------- OxForm -------------------------------------------------------------------------------- */ diff --git a/source/Ox.UI/js/Form/Ox.FileInput.js b/source/Ox.UI/js/Form/Ox.FileInput.js new file mode 100644 index 00000000..41af59b1 --- /dev/null +++ b/source/Ox.UI/js/Form/Ox.FileInput.js @@ -0,0 +1,270 @@ +'use strict'; + +Ox.FileInput = function(options, self) { + + self = self || {}; + var that = Ox.Element({}, self) + .defaults({ + maxFiles: -1, + maxLines: -1, + maxSize: -1, + width: 256 + }) + .options(options || {}) + .addClass('OxFileInput') + .css({width: self.options.width + 'px'}); + + self.multiple = self.options.maxFiles != 1; + self.files = []; + self.size = 0; + + self.$bar = Ox.Bar({size: 14}) + .css({width: self.options.width - 2 + 'px'}) + .appendTo(that); + + self.$text = $('
') + .css({ + float: 'left', + width: self.options.width - 102 + 'px', + paddingLeft: '4px', + overflow: 'hidden', + textOverflow: 'ellipsis' + }) + .html(getText()) + .appendTo(self.$bar); + + self.$size = $('
') + .css({ + float: 'left', + width: '64px', + height: '14px', + paddingRight: '16px', + textAlign: 'right' + }) + .appendTo(self.$bar); + + self.$button = Ox.Button({ + style: 'symbol', + title: 'add', + type: 'image' + }) + .css({ + float: 'left', + marginTop: '-1px' + }) + .bindEvent({ + click: clearFile + }) + .appendTo(self.$bar); + + self.$input = Ox.Element({ + element: '' + //tooltip: self.multiple ? 'Add Files' : 'Select File' + }) + .attr( + Ox.extend({ + title: self.multiple ? 'Add Files' : 'Select File', + type: 'file' + }, self.multiple ? { + multiple: true + } : {}) + ) + .css({ + float: 'left', + width: '16px', + height: '14px', + margin: '-1px -7px 0 -16px', + opacity: 0 + }) + .bind({ + change: addFiles + }) + .appendTo(self.$bar); + + if (self.multiple) { + self.$files = $('
') + .addClass('OxFiles') + .css({ + width: self.options.width - 2 + 'px', + height: getHeight() + }) + .hide() + .appendTo(that); + self.$list = Ox.TextList({ + columns: [ + { + id: 'name', + visible: true, + width: self.options.width - 94 + }, + { + align: 'right', + format: function(value) { + return Ox.formatValue(value, 'B'); + }, + id: 'size', + visible: true, + width: 64 + }, + { + align: 'right', + format: function(value, data) { + return Ox.Button({ + style: 'symbol', + title: 'close', + type: 'image' + }) + .attr({title: 'Remove File'}) + .css({margin: '-1px -4px 0 0'}) + .bindEvent({ + click: function() { + self.$list.options({selected: [value]}); + removeFiles([value]); + /* + setTimeout(function() { + self.$list.options({selected: []}); + }, 25); + */ + } + }); + }, + id: 'id', + unique: true, + visible: true, + width: 28 + } + ], + items: [], + sort: [{key: 'name', operator: '+'}] + }) + .css({ + left: 0, + top: 0, + width: self.options.width - 2 + 'px', + height: '64px', + }) + .bindEvent({ + 'delete': function(data) { + removeFiles(data.ids); + } + }) + .appendTo(self.$files); + } + + function addFiles(e) { + var filelist = e.target.files, + files = []; + Ox.loop(filelist.length, function(i) { + files.push(filelist.item(i)); + }); + if (self.options.maxSize != -1) { + files.sort(function(a, b) { + return a.size - b.size; + }); + } + files.sort(self.options.maxSize == -1 ? function(a, b) { + return a.name < b.name ? -1 : a.name > b.name ? 1 : 0; + } : function(a, b) { + return a.size - b.size; + }).forEach(function(file) { + if (!exists(file) && ( + self.options.maxFiles == -1 + || self.files.length < self.options.maxFiles + )) { + if ( + self.options.maxSize == -1 + || self.size + file.size < self.options.maxSize + ) { + self.files.push(file); + self.size += file.size; + } + } + }); + Ox.print('FILES:::', files); + self.$text.html(getText()); + self.$size.html(getSize()); + if (self.multiple) { + self.$bar.css({ + borderBottomLeftRadius: 0, + borderBottomRightRadius: 1 + }); + self.$files.css({height: getHeight()}).show(); + self.$list.options({items: getItems()}); + if ( + self.files.length == self.options.maxFiles + || self.size == self.options.maxSize + ) { + self.$button.options({disabled: true}); + } + } else { + self.$button.options({title: 'close'}).attr({title: 'Clear'}); + self.$input.hide(); + } + } + + function clearFile() { + self.files = []; + self.size = 0; + self.$text.html(getText()); + self.$size.html(getSize()); + self.$button.options({title: 'add'}).attr({title: ''}); + self.$input.show(); + } + + function exists(file) { + return self.files.some(function(f) { + return f.name == file.name + && f.size == file.size + && Ox.isEqual(f.lastModifiedDate, file.lastModifiedDate); + }); + } + + function getHeight() { + return ( + self.options.maxLines == -1 + ? self.files.length + : Math.min(self.files.length, self.options.maxLines) + ) * 16 + 'px'; + } + + function getItems() { + return self.files.map(function(file, i) { + return {name: file.name, size: file.size, id: i}; + }); + } + + function getSize() { + return self.size ? Ox.formatValue(self.size, 'B') : ''; + } + + function getText() { + var length = self.files.length + return length == 0 + ? 'No file' + (self.multiple ? 's' : '') + ' selected' + : self.multiple + ? length + ' file' + (length == 1 ? '' : 's') + ' selected' + : self.files[0].name; + } + + function removeFiles(ids) { + self.files = self.files.filter(function(v, i) { + return ids.indexOf(i) == -1; + }); + self.size = self.files.reduce(function(prev, curr) { + return prev + curr.size; + }, 0); + if (self.files.length == 0) { + self.$bar.css({ + borderBottomLeftRadius: '8px', + borderBottomRightRadius: '8px' + }); + self.$files.hide(); + } + self.$text.html(getText()); + self.$size.html(getSize()); + self.$list.options({items: getItems(), selected: []}); + self.$files.css({height: getHeight()}); + } + + return that; +}; diff --git a/source/Ox.UI/themes/classic/css/classic.css b/source/Ox.UI/themes/classic/css/classic.css index f16ef3ae..d83b70d8 100644 --- a/source/Ox.UI/themes/classic/css/classic.css +++ b/source/Ox.UI/themes/classic/css/classic.css @@ -369,6 +369,11 @@ Forms background: -webkit-linear-gradient(top, rgb(160, 160, 160), rgb(192, 192, 192)); } +.OxThemeClassic .OxFileInput > .OxBar, +.OxThemeClassic .OxFileInput > .OxFiles { + border-color: rgb(176, 176, 176); +} + .OxThemeClassic .OxArrayEditable.OxArrayEditableTextarea .OxEditableElement { border-top-color: rgb(208, 208, 208); } diff --git a/source/Ox.UI/themes/modern/css/modern.css b/source/Ox.UI/themes/modern/css/modern.css index 08c7919f..8bf6dcc5 100644 --- a/source/Ox.UI/themes/modern/css/modern.css +++ b/source/Ox.UI/themes/modern/css/modern.css @@ -356,6 +356,11 @@ Forms background: -webkit-linear-gradient(top, rgb(0, 0, 0), rgb(32, 32, 32) 10%, rgb(64, 64, 64)); } +.OxThemeModern .OxFileInput > .OxBar, +.OxThemeModern .OxFileInput > .OxFiles { + border-color: rgb(48, 48, 48); +} + .OxThemeModern .OxArrayEditable.OxArrayEditableTextarea .OxEditableElement { border-top-color: rgb(48, 48, 48); }