From 83c653fc3225cab9e47053348edc84807b5d248f Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Wed, 12 Aug 2015 23:42:42 +0100 Subject: [PATCH] Got react image manager working --- .gitignore | 3 +- app/Http/Controllers/ImageController.php | 23 +-- gulpfile.js | 2 +- public/js/image-manager.js | 117 --------------- resources/assets/js/image-manager.js | 175 ++++++++++++++++------- resources/assets/sass/image-manager.scss | 136 ++++++++---------- resources/assets/sass/styles.scss | 6 +- resources/views/base.blade.php | 4 +- resources/views/pages/edit.blade.php | 5 +- resources/views/pages/form.blade.php | 2 +- 10 files changed, 212 insertions(+), 261 deletions(-) delete mode 100644 public/js/image-manager.js diff --git a/.gitignore b/.gitignore index a01bdcf71..26d89655c 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ Homestead.yaml .idea /public/plugins /public/css -/public/js/all* +/public/js +/public/uploads /public/bower /storage/images \ No newline at end of file diff --git a/app/Http/Controllers/ImageController.php b/app/Http/Controllers/ImageController.php index c8a13e48b..2ef5b1228 100644 --- a/app/Http/Controllers/ImageController.php +++ b/app/Http/Controllers/ImageController.php @@ -71,7 +71,7 @@ class ImageController extends Controller */ public function getAll($page = 0) { - $pageSize = 13; + $pageSize = 25; $images = DB::table('images')->orderBy('created_at', 'desc') ->skip($page*$pageSize)->take($pageSize)->get(); foreach($images as $image) { @@ -95,9 +95,9 @@ class ImageController extends Controller public function getThumbnail($image, $width = 220, $height = 220) { $explodedPath = explode('/', $image->url); - array_splice($explodedPath, 3, 0, ['thumbs-' . $width . '-' . $height]); + array_splice($explodedPath, 4, 0, ['thumbs-' . $width . '-' . $height]); $thumbPath = implode('/', $explodedPath); - $thumbFilePath = storage_path() . $thumbPath; + $thumbFilePath = public_path() . $thumbPath; // Return the thumbnail url path if already exists if(file_exists($thumbFilePath)) { @@ -105,7 +105,7 @@ class ImageController extends Controller } // Otherwise create the thumbnail - $thumb = ImageTool::make(storage_path() . $image->url); + $thumb = ImageTool::make(public_path() . $image->url); $thumb->fit($width, $height); // Create thumbnail folder if it does not exist @@ -127,17 +127,18 @@ class ImageController extends Controller { $imageUpload = $request->file('file'); $name = str_replace(' ', '-', $imageUpload->getClientOriginalName()); - $imagePath = '/images/' . Date('Y-m-M') . '/'; - $storagePath = storage_path(). $imagePath; - $fullPath = $storagePath . $name; + $storageName = substr(sha1(time()), 0, 10) . '-' . $name; + $imagePath = '/uploads/images/'.Date('Y-m-M').'/'; + $storagePath = public_path(). $imagePath; + $fullPath = $storagePath . $storageName; while(file_exists($fullPath)) { - $name = substr(sha1(rand()), 0, 3) . $name; - $fullPath = $storagePath . $name; + $storageName = substr(sha1(rand()), 0, 3) . $storageName; + $fullPath = $storagePath . $storageName; } - $imageUpload->move($storagePath, $name); + $imageUpload->move($storagePath, $storageName); // Create and save image object $this->image->name = $name; - $this->image->url = $imagePath . $name; + $this->image->url = $imagePath . $storageName; $this->image->created_by = Auth::user()->id; $this->image->updated_by = Auth::user()->id; $this->image->save(); diff --git a/gulpfile.js b/gulpfile.js index 654f78d51..6358835a7 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -13,5 +13,5 @@ var elixir = require('laravel-elixir'); elixir(function(mix) { mix.sass('styles.scss'); - mix.babel('image-manager.js'); + mix.babel('image-manager.js', 'public/js/image-manager.js'); }); diff --git a/public/js/image-manager.js b/public/js/image-manager.js deleted file mode 100644 index 45c26d8e1..000000000 --- a/public/js/image-manager.js +++ /dev/null @@ -1,117 +0,0 @@ - -// Dropzone config -Dropzone.options.imageUploadDropzone = { - uploadMultiple: false, - previewsContainer: '.image-manager-display .uploads', - init: function() { - this.on('success', function(event, image) { - $('.image-manager-display .uploads').empty(); - var newImage = $('').attr('data-image-id', image.id); - newImage.attr('title', image.name).attr('src', image.thumbnail); - newImage.data('imageData', image); - $('.image-manager-display .uploads').after(newImage); - }); - } -}; - -(function() { - - var isInit = false; - var elem; - var overlay; - var display; - var imageIndexUrl = '/images/all'; - var pageIndex = 0; - var hasMore = true; - var isGettingImages = true; - - var ImageManager = {}; - var action = false; - - ImageManager.show = function(selector, callback) { - if(isInit) { - showWindow(); - } else { - this.init(selector) - showWindow(); - } - - action = (typeof callback !== 'undefined') ? callback : false; - }; - - ImageManager.init = function(selector) { - elem = $(selector); - overlay = elem.closest('.overlay'); - display = elem.find('.image-manager-display').first(); - var uploads = display.find('.uploads'); - var images = display.find('images'); - var loadMore = display.find('.load-more'); - // Get recent images and show - $.getJSON(imageIndexUrl, showImages); - function showImages(data) { - var images = data.images; - hasMore = data.hasMore; - pageIndex++; - isGettingImages = false; - for(var i = 0; i < images.length; i++) { - var image = images[i]; - var newImage = $('').attr('data-image-id', image.id); - newImage.attr('title', image.name).attr('src', image.thumbnail); - loadMore.before(newImage); - newImage.data('imageData', image); - } - if(hasMore) loadMore.show(); - } - - loadMore.click(function() { - loadMore.hide(); - if(isGettingImages === false) { - isGettingImages = true; - $.getJSON(imageIndexUrl + '/' + pageIndex, showImages); - } - }); - - // Image grabbing on scroll - display.on('scroll', function() { - var displayBottom = display.scrollTop() + display.height(); - var elemTop = loadMore.offset().top; - if(elemTop < displayBottom && hasMore && isGettingImages === false) { - isGettingImages = true; - loadMore.hide(); - $.getJSON(imageIndexUrl + '/' + pageIndex, showImages); - } - }); - - elem.on('dblclick', '.image-manager-display img', function() { - var imageElem = $(this); - var imageData = imageElem.data('imageData'); - closeWindow(); - if(action) { - action(imageData); - } - }); - - elem.find('button[data-action="close"]').click(function() { - closeWindow(); - }); - - // Set up dropzone - elem.find('.image-manager-dropzone').first().dropzone({ - uploadMultiple: false - }); - - isInit = true; - }; - - function showWindow() { - overlay.closest('body').css('overflow', 'hidden'); - overlay.show(); - } - - function closeWindow() { - overlay.hide(); - overlay.closest('body').css('overflow', 'auto'); - } - - window.ImageManager = ImageManager; -})(); \ No newline at end of file diff --git a/resources/assets/js/image-manager.js b/resources/assets/js/image-manager.js index 0bece9894..2aaf02909 100644 --- a/resources/assets/js/image-manager.js +++ b/resources/assets/js/image-manager.js @@ -1,63 +1,140 @@ -class ImageList extends React.Component { +(function() { - constructor(props) { - super(props); - this.state = { - images: [], - hasMore: false, - page: 0 - }; - } - componentDidMount() { - $.getJSON('/images/all', data => { - this.setState({ - images: data.images, - hasMore: data.hasMore + class ImageManager extends React.Component { + + constructor(props) { + super(props); + this.state = { + images: [], + hasMore: false, + page: 0 + }; + } + + show(callback) { + $(React.findDOMNode(this)).show(); + this.callback = callback; + } + + hide() { + $(React.findDOMNode(this)).hide(); + } + + selectImage(image) { + if(this.callback) { + this.callback(image); + } + this.hide(); + } + + componentDidMount() { + var _this = this; + // Set initial images + $.getJSON('/images/all', data => { + this.setState({ + images: data.images, + hasMore: data.hasMore + }); }); - }); - } - - loadMore() { - this.state.page++; - $.getJSON('/images/all/' + this.state.page, data => { - this.setState({ - images: this.state.images.concat(data.images), - hasMore: data.hasMore + // Create dropzone + this.dropZone = new Dropzone(React.findDOMNode(this.refs.dropZone), { + url: '/upload/image', + init: function() { + var dz = this; + this.on("sending", function(file, xhr, data) { + data.append("_token", document.querySelector('meta[name=token]').getAttribute('content')); + }); + this.on("success", function(file, data) { + _this.state.images.unshift(data); + _this.setState({ + images: _this.state.images + }); + //$(file.previewElement).fadeOut(400, function() { + // dz.removeFile(file); + //}) + }); + } }); - }); - } + } - render() { - var images = this.state.images.map(function(image) { + loadMore() { + this.state.page++; + $.getJSON('/images/all/' + this.state.page, data => { + this.setState({ + images: this.state.images.concat(data.images), + hasMore: data.hasMore + }); + }); + } + + render() { + var loadMore = this.loadMore.bind(this); + var selectImage = this.selectImage.bind(this); return ( -
- +
+
+
+
+
Drop files or click here to upload
+
+ +
+
+

Images

+
+
); - }); - return ( -
- {images} -
Load More
-
+ } + + } + window.ImageManager = new ImageManager(); + + class ImageList extends React.Component { + + render() { + var selectImage = this.props.selectImage; + var images = this.props.data.map(function(image) { + return ( + + ); + }); + return ( +
+ {images} + { this.props.hasMore ?
Load More
: null } +
+ ); + } + + } + + class Image extends React.Component { + + setImage() { + this.props.selectImage(this.props.image); + } + + render() { + var setImage = this.setImage.bind(this); + return ( +
+ +
+ ); + } + + } + + if(document.getElementById('image-manager-container')) { + window.ImageManager = React.render( + , + document.getElementById('image-manager-container') ); } -} +})(); -class ImageManager extends React.Component { - render() { - return ( -
- -
- ); - } -} -React.render( - , - document.getElementById('container') -); \ No newline at end of file diff --git a/resources/assets/sass/image-manager.scss b/resources/assets/sass/image-manager.scss index 2bff88dd8..ffcad943f 100644 --- a/resources/assets/sass/image-manager.scss +++ b/resources/assets/sass/image-manager.scss @@ -9,46 +9,29 @@ border-radius: 4px; box-shadow: 0 0 15px 0 rgba(0, 0, 0, 0.3); overflow: hidden; - .image-list img { + .image-manager-list img { border-radius: 0; float: left; margin: 1px; cursor: pointer; } + position: fixed; + top: 0; + bottom: 0; + left: 0; + z-index: 999; + display: flex; } -#image-manager .dropzone { - display: table; - position: absolute; - top: 10px; - left: 300px; - width: 480px; - height: 60px; - border: 4px dashed $primary; - text-align: center; - z-index: 900; - .dz-message { - display: table-cell; - vertical-align: middle; - color: $primary; - font-size: 1.2em; - } - * { - pointer-events: none; - } +#image-manager .dropzone-container { + height: 100px; + position: relative; } -.image-manager-display-wrap { - height: 100%; - padding-top: 87px; - position: absolute; - top: 0;width: 100%; -} -.image-manager-display { - height: 100%; - width: 100%; - text-align: left; - overflow-y: scroll; + +#container { + height: 90vh; } + #image-manager .load-more { width: 150px; height: 150px; @@ -62,32 +45,54 @@ font-size: 20px; cursor: pointer; } -.image-manager-title { - font-size: 2em; - text-align: left; - margin: 0 $-m; - padding: $-xl $-m; - color: #666; - border-bottom: 1px solid #DDD; + +.image-manager-sidebar { + width: 300px; + height: 100%; + margin-left: 1px; + padding: 0 $-l; + border-left: 1px solid #DDD; } -.image-manager-dropzone { - background-color: lighten($primary, 40%); - height: 25%; - text-align: center; - font-size: 2em; - line-height: 2em; - padding-top: $-xl*1.2; - color: #666; - border-top: 2px solid $primary; +.image-manager-list { + overflow-y: scroll; + flex: 1; } +.image-manager-content { + display: flex; + flex-direction: column; + height: 100%; + flex: 1; +} + + + + // Dropzone /* * The MIT License * Copyright (c) 2012 Matias Meno */ - +.dz-message { + font-size: 1.6em; + font-style: italic; + color: #aaa; + text-align: center; + line-height: 90px; + cursor: pointer; + transition: all ease-in-out 120ms; + position: absolute; + top: 0; + left: 50%; + max-width: 400px; + width: 400px; + margin-left: -200px; +} +.dz-drag-hover .dz-message { + background-color: rgb(16, 126, 210); + color: #EEE; +} @keyframes passing-through { 0% { opacity: 0; @@ -128,30 +133,13 @@ .dropzone, .dropzone * { box-sizing: border-box; } -.dropzone { - background: white; - padding: 20px 20px; } -.dropzone.dz-clickable { - cursor: pointer; } -.dropzone.dz-clickable * { - cursor: default; } -.dropzone.dz-clickable .dz-message, .dropzone.dz-clickable .dz-message * { - cursor: pointer; } -.dropzone.dz-started .dz-message { - display: none; } -.dropzone.dz-drag-hover { - border-style: solid; } -.dropzone.dz-drag-hover .dz-message { - opacity: 0.5; } -.dropzone .dz-message { - text-align: center; - margin: 2em 0; } + .dz-preview { position: relative; display: inline-block; vertical-align: top; - margin: 16px; - min-height: 100px; } + margin: 12px; + min-height: 80px; } .dz-preview:hover { z-index: 1000; } .dz-preview:hover .dz-details { @@ -186,16 +174,16 @@ top: 0; left: 0; opacity: 0; - font-size: 13px; + font-size: 10px; min-width: 100%; max-width: 100%; - padding: 2em 1em; + padding: 6px 3px; text-align: center; color: rgba(0, 0, 0, 0.9); line-height: 150%; } .dz-preview .dz-details .dz-size { - margin-bottom: 1em; - font-size: 16px; } + margin-bottom: 0.5em; + font-size: 12px; } .dz-preview .dz-details .dz-filename { white-space: nowrap; } .dz-preview .dz-details .dz-filename:hover span { @@ -221,8 +209,8 @@ .dz-preview .dz-image { border-radius: 4px; overflow: hidden; - width: 120px; - height: 120px; + width: 80px; + height: 80px; position: relative; display: block; z-index: 10; } diff --git a/resources/assets/sass/styles.scss b/resources/assets/sass/styles.scss index 62f0a6919..7d23045bb 100644 --- a/resources/assets/sass/styles.scss +++ b/resources/assets/sass/styles.scss @@ -182,7 +182,7 @@ ul.menu { .overlay { background-color: rgba(0, 0, 0, 0.2); position: fixed; - display: block; + display: none; z-index: 95536; width: 100%; height: 100%; @@ -355,7 +355,8 @@ body.dragging, body.dragging * { width: 100%; height: 100%; z-index: -1; - .overlay { + &:after{ + content: ''; position: absolute; top: 0; left: 0; @@ -363,6 +364,7 @@ body.dragging, body.dragging * { height: 100%; z-index: -1; background-color: rgba(0,0,0,0.7); + display: block; } } diff --git a/resources/views/base.blade.php b/resources/views/base.blade.php index d1450f7cf..bf913c144 100644 --- a/resources/views/base.blade.php +++ b/resources/views/base.blade.php @@ -3,6 +3,7 @@ BookStack + {{----}} @@ -10,6 +11,7 @@ + diff --git a/resources/views/pages/edit.blade.php b/resources/views/pages/edit.blade.php index fee9782fd..9806c9232 100644 --- a/resources/views/pages/edit.blade.php +++ b/resources/views/pages/edit.blade.php @@ -2,8 +2,6 @@ @section('head') - - @stop @section('content') @@ -16,5 +14,6 @@ @stop @section('bottom') - @include('pages/image-manager') +
+ @stop \ No newline at end of file diff --git a/resources/views/pages/form.blade.php b/resources/views/pages/form.blade.php index c0a7b4379..83e6bbc0e 100644 --- a/resources/views/pages/form.blade.php +++ b/resources/views/pages/form.blade.php @@ -41,7 +41,7 @@ toolbar: "undo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | table image link | fontsizeselect fullscreen", content_style: "body {padding-left: 15px !important; padding-right: 15px !important; margin:0!important; margin-left:auto!important;margin-right:auto!important;}", file_browser_callback: function(field_name, url, type, win) { - ImageManager.show('#image-manager', function(image) { + ImageManager.show(function(image) { win.document.getElementById(field_name).value = image.url; if ("createEvent" in document) { var evt = document.createEvent("HTMLEvents");