From 7d1992b081c62a069ce4385174a7cc391fb8ed23 Mon Sep 17 00:00:00 2001 From: Muah Date: Tue, 16 Jan 2018 09:30:57 +0200 Subject: [PATCH] v2.5.0 - image editor https://github.com/ctf0/Laravel-Media-Manager/wiki/Optimize-Edited-Images-On-Save - update readme - update wiki - update resources --- README.md | 45 +- logs/v2.4.1.txt | 5 - logs/v2.5.0.txt | 9 + src/Controllers/MediaController.php | 99 ++++- src/MediaRoutes.php | 3 + .../js/components/ImageEditor/caman.vue | 84 ++++ .../js/components/ImageEditor/cropper.vue | 397 ++++++++++++++++++ src/resources/assets/js/components/media.vue | 51 ++- .../assets/js/components/modules/cache.js | 6 +- .../assets/js/components/modules/form.js | 79 +++- .../assets/js/components/modules/selected.js | 8 +- .../assets/js/components/modules/utils.js | 14 +- .../assets/js/components/modules/watch.js | 8 +- src/resources/assets/js/icons.js | 60 +++ src/resources/assets/js/manager.js | 34 +- src/resources/assets/sass/media.scss | 149 ++++++- .../assets/sass/packages/switcher.scss | 2 +- src/resources/lang/en/messages.php | 15 +- src/resources/views/_manager.blade.php | 167 ++++++-- src/resources/views/cards/vertical.blade.php | 8 +- 20 files changed, 1089 insertions(+), 154 deletions(-) delete mode 100644 logs/v2.4.1.txt create mode 100644 logs/v2.5.0.txt create mode 100644 src/resources/assets/js/components/ImageEditor/caman.vue create mode 100644 src/resources/assets/js/components/ImageEditor/cropper.vue create mode 100644 src/resources/assets/js/icons.js diff --git a/README.md b/README.md index 683adae..db678c8 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ The only media manager with this number of features & flexibility.

- +

- to optimize uploaded files on the fly try [approached](https://github.com/approached/laravel-image-optimizer) or [spatie](https://github.com/spatie/laravel-image-optimizer) @@ -31,15 +31,15 @@ The only media manager with this number of features & flexibility. `php artisan vendor:publish --provider="ctf0\MediaManager\MediaManagerServiceProvider"` - after installation, package will auto-add - + package routes to `routes/web.php` - + package assets compiling to `webpack.mix.js` + + package routes to `routes/web.php` + + package assets compiling to `webpack.mix.js` - install dependencies ```bash -yarn add vue vue-ls vue-multi-ref vue-tippy@v1 vue2-filters vue-bounty vue-notif vue-clipboard2 vue-awesome vue-touch@next axios dropzone keycode babel-preset-es2015-node6 babel-preset-stage-2 +yarn add vue vue-ls vue-multi-ref vue-tippy@v1 vue2-filters vue-bounty vue-notif vue-clipboard2 vue-awesome vue-touch@next axios dropzone cropperjs keycode babel-preset-es2015-node6 babel-preset-stage-2 # or -npm install vue vue-ls vue-multi-ref vue-tippy@v1 vue2-filters vue-bounty vue-notif vue-clipboard2 vue-awesome vue-touch@next axios dropzone keycode babel-preset-es2015-node6 babel-preset-stage-2 --save +npm install vue vue-ls vue-multi-ref vue-tippy@v1 vue2-filters vue-bounty vue-notif vue-clipboard2 vue-awesome vue-touch@next axios dropzone cropperjs keycode babel-preset-es2015-node6 babel-preset-stage-2 --save ``` - add this one liner to your main js file and run `npm run watch` to compile your `js/css` files. @@ -57,12 +57,13 @@ new Vue({ ## Features +- [image editor](https://github.com/ctf0/Laravel-Media-Manager/wiki/Image-Editor) - multi + upload + move/copy + delete +- upload an image from a url - bulk selection -- restrict access to [folders](https://github.com/ctf0/Laravel-Media-Manager/wiki/Folder-Restriction) - dynamically hide [files](https://github.com/ctf0/Laravel-Media-Manager/wiki/Hide-Files-With-Extension) - dynamically hide [folders](https://github.com/ctf0/Laravel-Media-Manager/wiki/Hide-Folders) - toggle between `random names` & `original names` for uploaded files @@ -100,8 +101,9 @@ new Vue({ | navigation | button | keyboard | click / tap | touch | |----------------|--------------------------------------------|---------------|--------------------------|-------------------------| | | upload *(toolbar)* | u | * | | - | | refresh *(toolbar)* | r | * / hold *"clear cache"* | | + | | refresh *(toolbar)* | r | * / hold *(clear cache)* | | | | move *(toolbar)* | m | * | swipe up | + | | editor *(toolbar)* | e | * | | | | delete *(toolbar)* | d/del | * | swipe down | | | lock/unlock *(toolbar)* | l | * | | | | (reset) bulk select *(toolbar)* | b | * | | @@ -120,6 +122,7 @@ new Vue({ | |   | | | | | | limit bulk select *(files container)* | shift + click | | | | | preview image/pdf/text *(files container)* | space | ** | | + | | image editor *(files container)* | | hold | | | | hide *(preview)* | space/esc | * | | | select next | | right / down | * | swipe left *(preview)* | | select prev | | left / up | * | swipe right *(preview)* | @@ -130,17 +133,23 @@ new Vue({ - events - | type | event-name | description | - |---------|---------------------------------------|------------------------------------------| - | [JS](https://github.com/gocanto/vuemit) | - | | modal-show | when modal is showen | - | | modal-hide | when modal is hidden | - | | file_selected *(when inside modal)* | get selected file url | - | [Laravel](https://laravel.com/docs/5.5/events#manually-registering-events) | - | | MMFileUploaded($file_path) | get uploaded file full path | - | | MMFileDeleted($file_path, $is_folder) | get deleted file/folder full path | - | | MMFileRenamed($old_path, $new_path) | get renamed file/folder "old & new" path | - | | MMFileMoved($old_path, $new_path) | get moved file/folder "old & new" path | + | type | event-name | description | + |-----------------|---------------------------------------|--------------------------------------------------| + | [JS][js] | | | + | | modal-show | when modal is showen | + | | modal-hide | when modal is hidden | + | | file_selected *(when inside modal)* | get selected file url | + | [Laravel][lara] | | | + | | MMFileUploaded($file_path) | get uploaded file full [path][path] | + | | [MMFileSaved][event]($file_path) | get saved(edited/link) image full [path][path] | + | | MMFileDeleted($file_path, $is_folder) | get deleted file/folder full [path][path] | + | | MMFileRenamed($old_path, $new_path) | get renamed file/folder "old & new" [path][path] | + | | MMFileMoved($old_path, $new_path) | get moved file/folder "old & new" [path][path] | + +[js]: https://github.com/gocanto/vuemit +[lara]: https://laravel.com/docs/5.5/events#manually-registering-events +[event]: https://github.com/ctf0/Laravel-Media-Manager/wiki/Image-Editor#optimize-edited-images-on-save +[path]: https://gist.github.com/ctf0/9fa6013954654384052d2e2e809b9bf6
diff --git a/logs/v2.4.1.txt b/logs/v2.4.1.txt deleted file mode 100644 index 3243ed1..0000000 --- a/logs/v2.4.1.txt +++ /dev/null @@ -1,5 +0,0 @@ -- limit swipe “up & down” to the file it self instead of the whole container, so now you can swipe as usual without getting the modal showing up. -- add press & hold for refresh button so you can clear the manager browser storage/cache. -- disable file preview from sidebar in bulk selection -- update rdme -- update resources \ No newline at end of file diff --git a/logs/v2.5.0.txt b/logs/v2.5.0.txt new file mode 100644 index 0000000..a12764d --- /dev/null +++ b/logs/v2.5.0.txt @@ -0,0 +1,9 @@ +- added full image editor https://github.com/ctf0/Laravel-Media-Manager/wiki/Image-Editor +- you can now save images from url too, it follows the same flow as the normal upload (random names, upload to current folder, events) +- added new event "MMFileSaved" for both of the above features. +- fix setting selected file twice +- fix clearing cache twice + +- update readme +- update wiki +- update resources \ No newline at end of file diff --git a/src/Controllers/MediaController.php b/src/Controllers/MediaController.php index f892ba7..723d942 100755 --- a/src/Controllers/MediaController.php +++ b/src/Controllers/MediaController.php @@ -127,7 +127,7 @@ public function upload(Request $request) $original = $one->getClientOriginalName(); $name_only = pathinfo($original, PATHINFO_FILENAME); $ext_only = pathinfo($original, PATHINFO_EXTENSION); - $file_name = $random_name ? uniqid() . ".$ext_only" : $this->cleanName($name_only, null) . ".$ext_only"; + $file_name = $random_name ? $this->sanitizedText . ".$ext_only" : $this->cleanName($name_only, null) . ".$ext_only"; $file_type = $one->getMimeType(); $destination = "$upload_path/$file_name"; @@ -149,13 +149,11 @@ public function upload(Request $request) event('MMFileUploaded', $this->getFilePath($saved_name)); $result[] = [ - 'path' => preg_replace('/^public\//', '', $saved_name), 'success' => true, 'message' => $file_name, ]; } catch (Exception $e) { $result[] = [ - 'path' => '', 'success' => false, 'message' => "\"$file_name\" " . $e->getMessage(), ]; @@ -165,6 +163,101 @@ public function upload(Request $request) return response()->json(['data'=>$result]); } + /** + * save cropped image. + * + * @param Request $request [description] + * + * @return [type] [description] + */ + public function uploadCropped(Request $request) + { + $path = $request->path; + $data = explode(',', $request->data)[1]; + $original = $request->name; + + $name_only = pathinfo($original, PATHINFO_FILENAME) . '_' . $this->sanitizedText; + $ext_only = pathinfo($original, PATHINFO_EXTENSION); + $file_name = "$name_only.$ext_only"; + $destination = "$path/$file_name"; + + try { + // check existence + if ($this->storageDisk->exists($destination)) { + throw new Exception(trans('MediaManager::messages.error_already_exists')); + } + + // save file + $this->storageDisk->put($destination, base64_decode($data)); + + // fire event + event('MMFileSaved', $this->getFilePath($destination)); + + $result = [ + 'success' => true, + 'message' => $file_name, + ]; + } catch (Exception $e) { + $result = [ + 'success' => false, + 'message' => "\"$file_name\" " . $e->getMessage(), + ]; + } + + return response()->json($result); + } + + /** + * save image from link. + * + * @param Request $request [description] + * + * @return [type] [description] + */ + public function uploadLink(Request $request) + { + $url = $request->url; + $path = $request->path; + $random_name = $request->random_names; + + $original = substr($url, strrpos($url, '/') + 1); + $name_only = pathinfo($original, PATHINFO_FILENAME); + $ext_only = pathinfo($original, PATHINFO_EXTENSION); + $file_name = $random_name ? $this->sanitizedText . ".$ext_only" : $this->cleanName($name_only, null) . ".$ext_only"; + $destination = "$path/$file_name"; + $file_type = image_type_to_mime_type(exif_imagetype($url)); + + try { + // check for mime type + if (str_contains($file_type, $this->unallowedMimes)) { + throw new Exception(trans('MediaManager::messages.not_allowed_file_ext', ['attr'=>$file_type])); + } + + // check existence + if ($this->storageDisk->exists($destination)) { + throw new Exception(trans('MediaManager::messages.error_already_exists')); + } + + // save file + $this->storageDisk->put($destination, file_get_contents($url)); + + // fire event + event('MMFileSaved', $this->getFilePath($destination)); + + $result = [ + 'success' => true, + 'message' => $file_name, + ]; + } catch (Exception $e) { + $result = [ + 'success' => false, + 'message' => "\"$file_name\" " . $e->getMessage(), + ]; + } + + return response()->json($result); + } + /** * create new folder. * diff --git a/src/MediaRoutes.php b/src/MediaRoutes.php index ee1fcc5..d1bd56c 100644 --- a/src/MediaRoutes.php +++ b/src/MediaRoutes.php @@ -16,6 +16,9 @@ public static function routes() ], function () use ($controller) { Route::get('/', ['uses' => "$controller@index", 'as' => 'index']); Route::post('upload', ['uses' => "$controller@upload", 'as' => 'upload']); + Route::post('upload-cropped', ['uses' => "$controller@uploadCropped", 'as' => 'uploadCropped']); + Route::post('upload-link', ['uses' => "$controller@uploadLink", 'as' => 'uploadLink']); + Route::post('files', ['uses' => "$controller@get_files", 'as' => 'files']); Route::post('directories', ['uses' => "$controller@get_dirs", 'as' => 'directories']); Route::post('new_folder', ['uses' => "$controller@new_folder", 'as' => 'new_folder']); diff --git a/src/resources/assets/js/components/ImageEditor/caman.vue b/src/resources/assets/js/components/ImageEditor/caman.vue new file mode 100644 index 0000000..4edad40 --- /dev/null +++ b/src/resources/assets/js/components/ImageEditor/caman.vue @@ -0,0 +1,84 @@ + + + diff --git a/src/resources/assets/js/components/ImageEditor/cropper.vue b/src/resources/assets/js/components/ImageEditor/cropper.vue new file mode 100644 index 0000000..3f0e7fd --- /dev/null +++ b/src/resources/assets/js/components/ImageEditor/cropper.vue @@ -0,0 +1,397 @@ + + + + + diff --git a/src/resources/assets/js/components/media.vue b/src/resources/assets/js/components/media.vue index 8fbf801..ecc20f9 100644 --- a/src/resources/assets/js/components/media.vue +++ b/src/resources/assets/js/components/media.vue @@ -12,9 +12,10 @@ import Watchers from './modules/watch' import Computed from './modules/computed' import Bounty from 'vue-bounty' +import Cropper from './ImageEditor/cropper.vue' export default { - components: {Bounty}, + components: {Bounty, Cropper}, name: 'media-manager', mixins: [ Utilities, @@ -45,11 +46,11 @@ export default { ], data() { return { - isLoading: false, no_files: false, loading_files: false, no_search: false, ajax_error: false, + isLoading: false, toggleInfo: true, toggleUploadArea: false, showProgress: false, @@ -63,6 +64,7 @@ export default { randomNames: false, useCopy: false, toolBar: true, + imageWasEdited: false, files: [], folders: [], @@ -78,11 +80,12 @@ export default { currentFilterName: null, searchItemsCount: null, searchFor: null, - new_folder_name: null, - new_filename: null, - active_modal: null, + urlToUpload: null, + newFolderName: null, + newFilename: null, + activeModal: null, - navDirection: null, + imageSlideDirection: null, uploadPanelGradients: [ 'linear-gradient(141deg, #009e6c 0%, #00d1b2 71%, #00e7eb 100%)', 'linear-gradient(141deg, #04a6d7 0%, #209cee 71%, #3287f5 100%)', @@ -106,6 +109,11 @@ export default { }, mounted() { this.fileUpload() + + // check if image was edited + EventHub.listen('image-edited', () => { + this.imageWasEdited = true + }) }, updated() { this.autoPlay() @@ -133,7 +141,7 @@ export default { shortCuts(e) { if (!(this.isLoading || e.altKey || e.ctrlKey || e.metaKey)) { // when modal isnt visible - if (!this.active_modal) { + if (!this.activeModal) { // when search is not focused if (document.activeElement.dataset.search == undefined) { // when no bulk selecting @@ -181,6 +189,11 @@ export default { this.toggleModal('preview_modal') } } + + // image editor + if (keycode(e) == 'e') { + this.imageEditor() + } } // end of when there are files @@ -285,7 +298,20 @@ export default { /* end of short cuts */ refresh() { - this.getFiles(this.folders, null, this.selectedFile.name) + this.getFiles(this.folders, null, this.selectedFile ? this.selectedFile.name : null) + }, + moveItem() { + if (this.$refs.move[0].disabled) { + return + } + + this.toggleModal('move_file_modal') + }, + renameItem() { + this.toggleModal('rename_file_modal') + }, + imageEditor() { + return this.$refs['image_editor'].click() }, deleteItem() { if (this.$refs.delete[0].disabled) { @@ -308,16 +334,7 @@ export default { this.toggleModal('confirm_delete_modal') }, - moveItem() { - if (this.$refs.move[0].disabled) { - return - } - this.toggleModal('move_file_modal') - }, - renameItem() { - this.toggleModal('rename_file_modal') - }, blkSlct() { this.bulkSelect = !this.bulkSelect this.bulkSelectAll = false diff --git a/src/resources/assets/js/components/modules/cache.js b/src/resources/assets/js/components/modules/cache.js index 8a4196a..37a8715 100644 --- a/src/resources/assets/js/components/modules/cache.js +++ b/src/resources/assets/js/components/modules/cache.js @@ -32,11 +32,13 @@ export default { : cacheName == 'root_' ? `/${destination}` : `${cacheName}${destination}` } - let items = destination ? [extra, cacheName] : [cacheName] + let items = destination + ? extra == cacheName ? [cacheName] : [extra, cacheName] + : [cacheName] items.forEach((one) => { return localforage.removeItem(one).then(() => { - console.log(`${one} is cleared!`) + console.log(`${one} cache is cleared!`) }).catch((err) => { console.warn('localforage.removeItem', err) }) diff --git a/src/resources/assets/js/components/modules/form.js b/src/resources/assets/js/components/modules/form.js index 093db42..2b4041e 100644 --- a/src/resources/assets/js/components/modules/form.js +++ b/src/resources/assets/js/components/modules/form.js @@ -62,10 +62,55 @@ export default { }) }, + // upload image from link + saveLinkForm(event) { + let url = this.urlToUpload + + if (!url) { + return this.showNotif(this.trans('no_val'), 'warning') + } + + this.toggleUploadArea = false + this.toggleLoading() + this.loadingFiles('show') + + axios.post(event.target.action, { + path: this.files.path, + url: url, + random_names: this.randomNames + }).then(({data}) => { + this.toggleLoading() + this.loadingFiles('hide') + + if (!data.success) { + return this.showNotif(data.message, 'danger') + } + + this.resetInput('urlToUpload') + this.$nextTick(() => { + this.$refs.save_link_modal_input.focus() + }) + + this.showNotif(`${this.trans('save_success')} "${data.message}"`) + this.removeCachedResponse('../') + this.getFiles(this.folders, null, data.message) + + }).catch((err) => { + console.error(err) + this.toggleLoading() + this.toggleModal() + this.loadingFiles('hide') + this.resetInput('urlToUpload') + + this.ajaxError() + }) + }, + /* Main */ getFiles(folders = '/', prev_folder = null, prev_file = null) { this.resetInput(['searchFor', 'sortBy', 'currentFilterName', 'selectedFile', 'currentFileIndex']) + this.noFiles('hide') if (!this.loading_files) { this.toggleInfo = false @@ -149,11 +194,6 @@ export default { }) } - // we have files - if (this.allItemsCount) { - this.selectFirst() - } - this.$nextTick(() => { // check for prev opened folder if (prev_folder) { @@ -172,6 +212,11 @@ export default { } }) } + + // if prevs not found + if (!this.selectedFile) { + this.selectFirst() + } }) // we have files @@ -226,7 +271,7 @@ export default { /* Tool-Bar */ NewFolderForm(event) { - let folder_name = this.new_folder_name + let folder_name = this.newFolderName if (!folder_name) { return this.showNotif(this.trans('no_val'), 'warning') @@ -240,19 +285,19 @@ export default { axios.post(event.target.action, { current_path: this.files.path, - new_folder_name: folder_name + newFolderName: folder_name }).then(({data}) => { this.toggleLoading() - this.resetInput('new_folder_name') + this.resetInput('newFolderName') this.toggleModal() if (!data.success) { return this.showNotif(data.message, 'danger') } - this.showNotif(`${this.trans('create_success')} "${data.new_folder_name}" at "${data.full_path}"`) - this.removeCachedResponse('../') - this.getFiles(this.folders, data.new_folder_name) + this.showNotif(`${this.trans('create_success')} "${data.newFolderName}" at "${data.full_path}"`) + this.removeCachedResponse() + this.getFiles(this.folders, data.newFolderName) }).catch((err) => { console.error(err) @@ -262,7 +307,7 @@ export default { // rename RenameFileForm(event) { - let changed = this.new_filename + let changed = this.newFilename if (!changed) { return this.showNotif(this.trans('no_val'), 'warning') @@ -276,12 +321,12 @@ export default { let filename = this.selectedFile.name let ext = this.getExtension(filename) - let new_filename = ext == null ? changed : `${changed}.${ext}` + let newFilename = ext == null ? changed : `${changed}.${ext}` axios.post(event.target.action, { folder_location: this.files.path, filename: filename, - new_filename: new_filename + newFilename: newFilename }).then(({data}) => { this.toggleLoading() this.toggleModal() @@ -290,8 +335,8 @@ export default { return this.showNotif(data.message, 'danger') } - this.showNotif(`${this.trans('rename_success')} "${filename}" to "${data.new_filename}"`) - this.updateItemName(this.selectedFile, filename, data.new_filename) + this.showNotif(`${this.trans('rename_success')} "${filename}" to "${data.newFilename}"`) + this.updateItemName(this.selectedFile, filename, data.newFilename) this.removeCachedResponse() if (this.selectedFileIs('folder')) { @@ -401,7 +446,7 @@ export default { }) this.$refs['success-audio'].play() - this.removeCachedResponse() + this.removeCachedResponse('../') this.toggleModal() this.isBulkSelecting() ? this.blkSlct() : this.selectFirst() diff --git a/src/resources/assets/js/components/modules/selected.js b/src/resources/assets/js/components/modules/selected.js index f4cd35e..2ca9120 100644 --- a/src/resources/assets/js/components/modules/selected.js +++ b/src/resources/assets/js/components/modules/selected.js @@ -131,7 +131,7 @@ export default { // go to last item if (keycode(e) == 'end') { e.preventDefault() - this.navDirection = 'next' + this.imageSlideDirection = 'next' let last = this.filesList.length - 1 this.scrollToFile(this.$refs[`file_${last}`]) @@ -140,7 +140,7 @@ export default { // go to first item if (keycode(e) == 'home') { e.preventDefault() - this.navDirection = 'prev' + this.imageSlideDirection = 'prev' this.scrollToFile(this.$refs.file_0) } @@ -160,7 +160,7 @@ export default { let curSelectedIndex = this.currentFileIndex if (curSelectedIndex !== 0) { - this.navDirection = 'prev' + this.imageSlideDirection = 'prev' let newSelected = curSelectedIndex - 1 this.scrollToFile(this.$refs[`file_${newSelected}`]) } @@ -169,7 +169,7 @@ export default { let curSelectedIndex = this.currentFileIndex if (curSelectedIndex < this.allItemsCount - 1) { - this.navDirection = 'next' + this.imageSlideDirection = 'next' let newSelected = curSelectedIndex + 1 this.scrollToFile(this.$refs[`file_${newSelected}`]) } diff --git a/src/resources/assets/js/components/modules/utils.js b/src/resources/assets/js/components/modules/utils.js index f17e9b4..5f79d12 100644 --- a/src/resources/assets/js/components/modules/utils.js +++ b/src/resources/assets/js/components/modules/utils.js @@ -4,7 +4,7 @@ export default { return item == list[list.length - 1] }, isActiveModal(el) { - return this.active_modal == el + return this.activeModal == el }, showNotif(msg, s = 'success') { if (msg) { @@ -50,7 +50,7 @@ export default { }, /* Buttons */ - mv_dl() { + item_ops() { if (this.isBulkSelecting()) { return this.bulkListFilter.length == 0 } @@ -103,13 +103,19 @@ export default { /* Toggle */ toggleModal(selector = null) { if (!selector) { + // refresh if an image was edited + if (this.activeModal == 'imageEditor_modal' && this.imageWasEdited) { + this.imageWasEdited = false + this.refresh() + } + this.noScroll('remove') - this.resetInput('active_modal') + this.resetInput('activeModal') return EventHub.fire('modal-hide') } this.noScroll('add') - this.active_modal = selector + this.activeModal = selector EventHub.fire('modal-show') }, toggleLoading() { diff --git a/src/resources/assets/js/components/modules/watch.js b/src/resources/assets/js/components/modules/watch.js index 3e29618..51a26c6 100644 --- a/src/resources/assets/js/components/modules/watch.js +++ b/src/resources/assets/js/components/modules/watch.js @@ -37,7 +37,7 @@ export default { this.bulkSelectAll = true } }, - active_modal(val) { + activeModal(val) { if (val == 'new_folder_modal') { this.$nextTick(() => { return this.$refs.new_folder_modal_input.focus() @@ -61,6 +61,12 @@ export default { return this.$refs.confirm_delete_modal_submit.focus() }) } + + if (val == 'save_link_modal') { + this.$nextTick(() => { + return this.$refs.save_link_modal_input.focus() + }) + } }, showProgress(val) { if (val) { diff --git a/src/resources/assets/js/icons.js b/src/resources/assets/js/icons.js new file mode 100644 index 0000000..0368518 --- /dev/null +++ b/src/resources/assets/js/icons.js @@ -0,0 +1,60 @@ +import 'vue-awesome/icons/shopping-basket' +import 'vue-awesome/icons/folder' +import 'vue-awesome/icons/refresh' +import 'vue-awesome/icons/share' +import 'vue-awesome/icons/terminal' +import 'vue-awesome/icons/trash' +import 'vue-awesome/icons/minus' +import 'vue-awesome/icons/plus' +import 'vue-awesome/icons/puzzle-piece' +import 'vue-awesome/icons/image' +import 'vue-awesome/icons/video-camera' +import 'vue-awesome/icons/music' +import 'vue-awesome/icons/times' +import 'vue-awesome/icons/bell-o' +import 'vue-awesome/icons/search' +import 'vue-awesome/icons/angle-double-right' +import 'vue-awesome/icons/angle-double-left' +import 'vue-awesome/icons/bars' +import 'vue-awesome/icons/clone' +import 'vue-awesome/icons/file-pdf-o' +import 'vue-awesome/icons/power-off' +import 'vue-awesome/icons/file-text-o' +import 'vue-awesome/icons/download' +import 'vue-awesome/icons/warning' +import 'vue-awesome/icons/archive' +import 'vue-awesome/icons/unlock' +import 'vue-awesome/icons/lock' +import 'vue-awesome/icons/eye' +import 'vue-awesome/icons/film' +import 'vue-awesome/icons/object-ungroup' +import 'vue-awesome/icons/circle' +import 'vue-awesome/icons/link' +import 'vue-awesome/icons/spinner' +// cropperjs +import 'vue-awesome/icons/crop' +import 'vue-awesome/icons/ban' +import 'vue-awesome/icons/check' +import 'vue-awesome/icons/arrows' +import 'vue-awesome/icons/search-plus' +import 'vue-awesome/icons/search-minus' +import 'vue-awesome/icons/rotate-left' +import 'vue-awesome/icons/rotate-right' +import 'vue-awesome/icons/arrows-h' +import 'vue-awesome/icons/arrows-v' +// camanjs +import 'vue-awesome/icons/sun-o' +import 'vue-awesome/icons/adjust' +import 'vue-awesome/icons/eye-slash' +import 'vue-awesome/icons/flash' +import 'vue-awesome/icons/thermometer-half' +import 'vue-awesome/icons/eyedropper' +import 'vue-awesome/icons/lemon-o' +import 'vue-awesome/icons/flask' +import 'vue-awesome/icons/dot-circle-o' +import 'vue-awesome/icons/scissors' +import 'vue-awesome/icons/diamond' +import 'vue-awesome/icons/filter' +import 'vue-awesome/icons/cube' +import 'vue-awesome/icons/shield' +Vue.component('icon', require('vue-awesome/components/Icon')) diff --git a/src/resources/assets/js/manager.js b/src/resources/assets/js/manager.js index 9af4b34..2bbfcbc 100644 --- a/src/resources/assets/js/manager.js +++ b/src/resources/assets/js/manager.js @@ -3,6 +3,9 @@ window.Vue = require('vue') window.EventHub = require('vuemit') window.keycode = require('keycode') window.Dropzone = require('dropzone') +window.Cropper = require('cropperjs') +import 'cropperjs/dist/cropper.css' + Vue.use(require('vue2-filters')) Vue.use(require('vue-clipboard2')) Vue.use(require('vue-ls')) @@ -58,36 +61,7 @@ localforage.config({ }) // vue-awesome -import 'vue-awesome/icons/shopping-basket' -import 'vue-awesome/icons/folder' -import 'vue-awesome/icons/refresh' -import 'vue-awesome/icons/share' -import 'vue-awesome/icons/terminal' -import 'vue-awesome/icons/trash' -import 'vue-awesome/icons/minus' -import 'vue-awesome/icons/plus' -import 'vue-awesome/icons/puzzle-piece' -import 'vue-awesome/icons/image' -import 'vue-awesome/icons/video-camera' -import 'vue-awesome/icons/music' -import 'vue-awesome/icons/times' -import 'vue-awesome/icons/bell-o' -import 'vue-awesome/icons/search' -import 'vue-awesome/icons/angle-double-right' -import 'vue-awesome/icons/angle-double-left' -import 'vue-awesome/icons/bars' -import 'vue-awesome/icons/clone' -import 'vue-awesome/icons/file-pdf-o' -import 'vue-awesome/icons/power-off' -import 'vue-awesome/icons/file-text-o' -import 'vue-awesome/icons/download' -import 'vue-awesome/icons/warning' -import 'vue-awesome/icons/archive' -import 'vue-awesome/icons/unlock' -import 'vue-awesome/icons/lock' -import 'vue-awesome/icons/eye' -import 'vue-awesome/icons/film' -Vue.component('icon', require('vue-awesome/components/Icon')) +require('./icons') /* Components */ Vue.component('MediaManager', require('./components/media.vue')) diff --git a/src/resources/assets/sass/media.scss b/src/resources/assets/sass/media.scss index 095f759..870dbb7 100644 --- a/src/resources/assets/sass/media.scss +++ b/src/resources/assets/sass/media.scss @@ -76,12 +76,45 @@ color: $white; } - .form-switcher { + .form-switcher, + .save_link { position: absolute; z-index: 1; + } + + .form-switcher { bottom: 0.5rem; left: 0.5rem; } + + .save_link { + right: 0.5rem; + bottom: 0; + filter: drop-shadow(0 0 0.1rem rgba($black, 0.3)); + + .circle, + .anchor { + transition: all 0.3s; + } + + .circle { + color: $active_theme; + } + + .anchor { + color: darken($active_theme, 50%); + } + + &:hover { + .anchor { + color: $active_theme; + } + + .circle { + color: darken($active_theme, 50%); + } + } + } } @include media('max', 1023) { @@ -343,8 +376,13 @@ right: 3px; width: 0.5rem; height: 0.5rem; - cursor: default; + padding: 0; + border: none; border-radius: 100vw; + + &:disabled { + cursor: not-allowed; + } } .__box-copy-link { @@ -464,7 +502,6 @@ background-color: $active_theme; .__sidebar-preview { - overflow: hidden; flex: 0 0 auto; text-align: center; color: darken($active_theme, 50%); @@ -587,8 +624,6 @@ /* modal */ .__modal-preview { - padding: 0.7rem 0.7rem 0; - * { color: $black; } @@ -633,6 +668,93 @@ margin-top: 15px; } +.__modal-content-wrapper { + align-self: center; +} + +/* Editor */ +$editorBtns: rgba(darken($active_theme, 70%), 0.8); + +.__modal-editor { + .__cropper { + min-width: unset; + background: transparent; + box-shadow: none; + + .__cropper-top-toolbar, + .__cropper-side-toolbar, + .__cropper-bottom-toolbar { + display: flex; + align-self: flex-start; + background-color: $editorBtns; + } + + .top, + .mid, + .bottom { + display: flex; + } + + .mid { + .__cropper-side-toolbar { + flex-flow: column nowrap; + } + + .card-image { + margin: 1rem 0 1rem 1rem; + + .image { + max-width: 50vw; + max-height: 50vh; + } + + img { + max-width: 100%; + } + } + } + + .bottom { + justify-content: flex-end; + } + } + + .__caman { + position: relative; + + .__caman-controls { + position: absolute; + z-index: 1; + top: -82px; + left: 0; + background-color: $editorBtns; + } + } + + .btn-plain { + padding: 0.7rem; + transition: all 0.3s; + color: white; + + &:hover, + &.is-active { + background-color: $blue; + } + + &:disabled { + background-color: rgba(darken($active_theme, 80%), 0.8); + } + } +} + +@include media('max', 1023) { + .__modal-editor { + .image { + max-width: 75vw !important; + } + } +} + /* Options */ /* manager in modal */ @@ -730,8 +852,8 @@ } } - .count-enter, - .count-leave-to { + .counter-enter, + .counter-leave-to { transform: translateX(-0.7rem); opacity: 0; } @@ -842,8 +964,13 @@ a[target='_blank'] { background-color: $green; } + &.is-black { + background-color: darken($active_theme, 70%); + } + &.is-danger, &.is-link, + &.is-black, &.is-success { * { color: $white; @@ -861,7 +988,11 @@ a[target='_blank'] { .modal-card { margin: 0; - padding: 1rem; + padding-top: 3rem; + } + + .modal-card-title { + max-width: calc(100% - 20px); } .modal-content { @@ -875,7 +1006,7 @@ a[target='_blank'] { &.is-active { margin-bottom: 2%; - padding-top: 4%; + padding: 0.7rem 0.7rem 0; } } diff --git a/src/resources/assets/sass/packages/switcher.scss b/src/resources/assets/sass/packages/switcher.scss index 824c9ec..d747575 100755 --- a/src/resources/assets/sass/packages/switcher.scss +++ b/src/resources/assets/sass/packages/switcher.scss @@ -78,7 +78,7 @@ transform: translateX(0); border-radius: 100vw; background-color: $white; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4); + box-shadow: 0 1px 3px rgba($black, 0.4); } &:hover { diff --git a/src/resources/lang/en/messages.php b/src/resources/lang/en/messages.php index 1fc1c55..a740bf1 100644 --- a/src/resources/lang/en/messages.php +++ b/src/resources/lang/en/messages.php @@ -3,6 +3,7 @@ return array ( 'add_folder' => 'Add Folder', 'add_new_folder' => 'Add New Folder', + 'add_url' => 'Url ...', 'ajax_error' => 'Oh NO! Something Went Wrong.', 'are_you_sure_delete' => 'Are you sure you want to delete ?', 'audio_support' => 'Your browser does not support the audio element.', @@ -18,6 +19,15 @@ 'copy_to_cp' => 'Copy Link To Clipboard', 'create_new_folder' => 'Create New Folder', 'create_success' => 'Successfully Created', + 'crop' => 'Crop', + 'crop_apply' => 'Apply Changes', + 'crop_flip_horizontal' => 'Flip Horizontal', + 'crop_flip_vertical' => 'Flip Vertical', + 'crop_reset' => 'Reset', + 'crop_rotate_left' => 'Rotate Left', + 'crop_rotate_right' => 'Rotate Right', + 'crop_zoom_in' => 'Zoom In', + 'crop_zoom_out' => 'Zoom Out', 'delete' => 'Delete', 'delete_confirm' => 'Yes, Delete it !', 'delete_folder' => 'Deleting a folder will remove all files and folders contained inside.', @@ -26,6 +36,7 @@ 'download_file' => 'Download File', 'download_folder' => 'Download Folder', 'downloaded' => 'Downloaded', + 'editor' => 'Editor', 'error_already_exists' => 'A File/Folder already exists with that name.', 'error_creating_dir' => 'Something gone wrong while creating the directory, please check your permissions,', 'error_deleting_file' => 'Something  gone wrong while deleting this file / folder, please check your permissions,', @@ -58,6 +69,7 @@ 'rename' => 'Rename', 'rename_file_folder' => 'Rename File/Folder', 'rename_success' => 'Successfully Renamed', + 'save_link' => 'Upload Image From Url', 'select_all' => 'Select All', 'select_non' => 'Select Non', 'selected' => 'Selected', @@ -66,6 +78,7 @@ 'size' => 'Size', 'sort_by' => 'Sort By', 'stand_by' => 'Please Stand By ...', + 'stream_error' => 'Couldn\'t open ":attr" for streaming.', 'title' => 'Title', 'too_many_files' => 'Items Will Be Deleted !', 'total' => 'Total', @@ -76,5 +89,5 @@ 'upload_text' => 'Drag & Drop Files
Or
Click To Upload', 'use_random_names' => 'Use Random Names ?', 'video_support' => 'Your browser does not support the video tag.', - 'stream_error' => 'Couldn\'t open ":attr" for streaming.', + 'save_success' => 'Successfully Saved As', ); \ No newline at end of file diff --git a/src/resources/views/_manager.blade.php b/src/resources/views/_manager.blade.php index 4e4c3fb..27732de 100644 --- a/src/resources/views/_manager.blade.php +++ b/src/resources/views/_manager.blade.php @@ -24,6 +24,7 @@ 'move_success' => trans('MediaManager::messages.move_success'), 'delete_success' => trans('MediaManager::messages.delete_success'), 'copy_success' => trans('MediaManager::messages.copy_success'), + 'save_success' => trans('MediaManager::messages.save_success'), ]) }}" :upload-panel-img-list="{{ $patterns }}" files-route="{{ route('media.files') }}" @@ -52,7 +53,7 @@ :disabled="isLoading" @click="toggleUploadPanel()" v-tippy title="u"> - + {{ trans('MediaManager::messages.upload') }} @@ -62,7 +63,7 @@ @@ -78,7 +79,7 @@ v-tippy title="r" @tap="refresh()" @hold="clearCache()"> - + @@ -91,33 +92,44 @@
{{-- rename --}} -
+
+ {{-- editor --}} +
+ +
+ {{-- delete --}}
@@ -132,7 +144,7 @@ :disabled="lock()" v-tippy title="l" @click="pushToLockedList()"> - + @@ -156,11 +168,11 @@ class="button" :disabled="searchItemsCount == 0 || isLoading" v-tippy title="a"> @@ -174,7 +186,7 @@ class="button" :class="{'is-danger' : bulkSelect}" :disabled="searchItemsCount == 0 || !allItemsCount || isLoading" v-tippy title="b"> - + {{ trans('MediaManager::messages.bulk_select') }}
@@ -192,7 +204,7 @@ class="button" class="button" :class="{'is-link': filterNameIs('image')}" :disabled="!btnFilter('image') || isLoading"> - +
@@ -201,7 +213,7 @@ class="button" class="button" :class="{'is-link': filterNameIs('video')}" :disabled="!btnFilter('video') || isLoading"> - +
@@ -210,7 +222,7 @@ class="button" class="button" :class="{'is-link': filterNameIs('audio')}" :disabled="!btnFilter('audio') || isLoading"> - +
@@ -219,7 +231,7 @@ class="button" class="button" :class="{'is-link': filterNameIs('folder')}" :disabled="!btnFilter('folder') || isLoading"> - +
@@ -228,7 +240,7 @@ class="button" class="button" :class="{'is-link': filterNameIs('text')}" :disabled="!btnFilter('text') || isLoading"> - +
@@ -237,7 +249,7 @@ class="button" class="button" :class="{'is-danger': btnFilter('all')}" :disabled="!btnFilter('all') || isLoading"> - +
@@ -255,7 +267,7 @@ class="button" -
+
@@ -272,7 +284,7 @@ class="button" v-model="searchFor" data-search placeholder="{{ trans('MediaManager::messages.find') }}"> - +

@@ -280,7 +292,7 @@ class="button"

@@ -296,15 +308,30 @@ class="button"
- {{ csrf_field() }} + + + {{-- text --}}
{!! trans('MediaManager::messages.upload_text') !!}
-
+ {{-- randomNames --}} +
- - + {{-- urlToUpload --}} +
@@ -382,15 +409,17 @@ class="button" :class="{'bulk-selected': IsInBulkList(file), 'selected' : selectedFile == file}" :ref="'file_' + index" @swipeup="moveItem()" - @swipedown="deleteItem()"> + @swipedown="deleteItem()" + @hold="imageEditor()"> {{-- lock file --}} -
-
+ {{-- copy file link --}}
-
- +
+ + {{-- image_editor --}} + + + {{-- save_link --}} + + {{-- new_folder_modal --}} @@ -950,4 +1040,5 @@ class="button is-danger" {{-- scripts --}} + diff --git a/src/resources/views/cards/vertical.blade.php b/src/resources/views/cards/vertical.blade.php index 25d9eca..48ac5c9 100644 --- a/src/resources/views/cards/vertical.blade.php +++ b/src/resources/views/cards/vertical.blade.php @@ -5,7 +5,7 @@ @@ -70,7 +70,7 @@ - +