mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-05-09 18:40:06 +00:00
Converted image manager into vue component
This commit is contained in:
parent
a59d73de7b
commit
ab07f7df6c
11 changed files with 287 additions and 307 deletions
resources
|
@ -21,6 +21,8 @@ for (let i = 0, len = componentNames.length; i < len; i++) {
|
||||||
if (typeof window.components[name] === "undefined") window.components[name] = [];
|
if (typeof window.components[name] === "undefined") window.components[name] = [];
|
||||||
for (let j = 0, jLen = elems.length; j < jLen; j++) {
|
for (let j = 0, jLen = elems.length; j < jLen; j++) {
|
||||||
let instance = new component(elems[j]);
|
let instance = new component(elems[j]);
|
||||||
|
if (typeof elems[j].components === 'undefined') elems[j].components = {};
|
||||||
|
elems[j].components[name] = instance;
|
||||||
window.components[name].push(instance);
|
window.components[name].push(instance);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,14 +7,16 @@ class Notification {
|
||||||
this.textElem = elem.querySelector('span');
|
this.textElem = elem.querySelector('span');
|
||||||
this.autohide = this.elem.hasAttribute('data-autohide');
|
this.autohide = this.elem.hasAttribute('data-autohide');
|
||||||
window.Events.listen(this.type, text => {
|
window.Events.listen(this.type, text => {
|
||||||
console.log('show', text);
|
|
||||||
this.show(text);
|
this.show(text);
|
||||||
});
|
});
|
||||||
elem.addEventListener('click', this.hide.bind(this));
|
elem.addEventListener('click', this.hide.bind(this));
|
||||||
if (elem.hasAttribute('data-show')) this.show(this.textElem.textContent);
|
if (elem.hasAttribute('data-show')) this.show(this.textElem.textContent);
|
||||||
|
|
||||||
|
this.hideCleanup = this.hideCleanup.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
show(textToShow = '') {
|
show(textToShow = '') {
|
||||||
|
this.elem.removeEventListener('transitionend', this.hideCleanup);
|
||||||
this.textElem.textContent = textToShow;
|
this.textElem.textContent = textToShow;
|
||||||
this.elem.style.display = 'block';
|
this.elem.style.display = 'block';
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
@ -26,13 +28,12 @@ class Notification {
|
||||||
|
|
||||||
hide() {
|
hide() {
|
||||||
this.elem.classList.remove('showing');
|
this.elem.classList.remove('showing');
|
||||||
|
this.elem.addEventListener('transitionend', this.hideCleanup);
|
||||||
|
}
|
||||||
|
|
||||||
function transitionEnd() {
|
hideCleanup() {
|
||||||
this.elem.style.display = 'none';
|
this.elem.style.display = 'none';
|
||||||
this.elem.removeEventListener('transitionend', transitionEnd);
|
this.elem.removeEventListener('transitionend', this.hideCleanup);
|
||||||
}
|
|
||||||
|
|
||||||
this.elem.addEventListener('transitionend', transitionEnd.bind(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,256 +8,6 @@ moment.locale('en-gb');
|
||||||
|
|
||||||
module.exports = function (ngApp, events) {
|
module.exports = function (ngApp, events) {
|
||||||
|
|
||||||
ngApp.controller('ImageManagerController', ['$scope', '$attrs', '$http', '$timeout', 'imageManagerService',
|
|
||||||
function ($scope, $attrs, $http, $timeout, imageManagerService) {
|
|
||||||
|
|
||||||
$scope.images = [];
|
|
||||||
$scope.imageType = $attrs.imageType;
|
|
||||||
$scope.selectedImage = false;
|
|
||||||
$scope.dependantPages = false;
|
|
||||||
$scope.showing = false;
|
|
||||||
$scope.hasMore = false;
|
|
||||||
$scope.imageUpdateSuccess = false;
|
|
||||||
$scope.imageDeleteSuccess = false;
|
|
||||||
$scope.uploadedTo = $attrs.uploadedTo;
|
|
||||||
$scope.view = 'all';
|
|
||||||
|
|
||||||
$scope.searching = false;
|
|
||||||
$scope.searchTerm = '';
|
|
||||||
|
|
||||||
let page = 0;
|
|
||||||
let previousClickTime = 0;
|
|
||||||
let previousClickImage = 0;
|
|
||||||
let dataLoaded = false;
|
|
||||||
let callback = false;
|
|
||||||
|
|
||||||
let preSearchImages = [];
|
|
||||||
let preSearchHasMore = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used by dropzone to get the endpoint to upload to.
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
$scope.getUploadUrl = function () {
|
|
||||||
return window.baseUrl('/images/' + $scope.imageType + '/upload');
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancel the current search operation.
|
|
||||||
*/
|
|
||||||
function cancelSearch() {
|
|
||||||
$scope.searching = false;
|
|
||||||
$scope.searchTerm = '';
|
|
||||||
$scope.images = preSearchImages;
|
|
||||||
$scope.hasMore = preSearchHasMore;
|
|
||||||
}
|
|
||||||
$scope.cancelSearch = cancelSearch;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs on image upload, Adds an image to local list of images
|
|
||||||
* and shows a success message to the user.
|
|
||||||
* @param file
|
|
||||||
* @param data
|
|
||||||
*/
|
|
||||||
$scope.uploadSuccess = function (file, data) {
|
|
||||||
$scope.$apply(() => {
|
|
||||||
$scope.images.unshift(data);
|
|
||||||
});
|
|
||||||
events.emit('success', trans('components.image_upload_success'));
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs the callback and hides the image manager.
|
|
||||||
* @param returnData
|
|
||||||
*/
|
|
||||||
function callbackAndHide(returnData) {
|
|
||||||
if (callback) callback(returnData);
|
|
||||||
$scope.hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Image select action. Checks if a double-click was fired.
|
|
||||||
* @param image
|
|
||||||
*/
|
|
||||||
$scope.imageSelect = function (image) {
|
|
||||||
let dblClickTime = 300;
|
|
||||||
let currentTime = Date.now();
|
|
||||||
let timeDiff = currentTime - previousClickTime;
|
|
||||||
|
|
||||||
if (timeDiff < dblClickTime && image.id === previousClickImage) {
|
|
||||||
// If double click
|
|
||||||
callbackAndHide(image);
|
|
||||||
} else {
|
|
||||||
// If single
|
|
||||||
$scope.selectedImage = image;
|
|
||||||
$scope.dependantPages = false;
|
|
||||||
}
|
|
||||||
previousClickTime = currentTime;
|
|
||||||
previousClickImage = image.id;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Action that runs when the 'Select image' button is clicked.
|
|
||||||
* Runs the callback and hides the image manager.
|
|
||||||
*/
|
|
||||||
$scope.selectButtonClick = function () {
|
|
||||||
callbackAndHide($scope.selectedImage);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show the image manager.
|
|
||||||
* Takes a callback to execute later on.
|
|
||||||
* @param doneCallback
|
|
||||||
*/
|
|
||||||
function show(doneCallback) {
|
|
||||||
callback = doneCallback;
|
|
||||||
$scope.showing = true;
|
|
||||||
$('#image-manager').find('[overlay]').css('display', 'flex').hide().fadeIn(240);
|
|
||||||
// Get initial images if they have not yet been loaded in.
|
|
||||||
if (!dataLoaded) {
|
|
||||||
fetchData();
|
|
||||||
dataLoaded = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connects up the image manger so it can be used externally
|
|
||||||
// such as from TinyMCE.
|
|
||||||
imageManagerService.show = show;
|
|
||||||
imageManagerService.showExternal = function (doneCallback) {
|
|
||||||
$scope.$apply(() => {
|
|
||||||
show(doneCallback);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
window.ImageManager = imageManagerService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hide the image manager
|
|
||||||
*/
|
|
||||||
$scope.hide = function () {
|
|
||||||
$scope.showing = false;
|
|
||||||
$('#image-manager').find('[overlay]').fadeOut(240);
|
|
||||||
};
|
|
||||||
|
|
||||||
let baseUrl = window.baseUrl('/images/' + $scope.imageType + '/all/');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch the list image data from the server.
|
|
||||||
*/
|
|
||||||
function fetchData() {
|
|
||||||
let url = baseUrl + page + '?';
|
|
||||||
let components = {};
|
|
||||||
if ($scope.uploadedTo) components['page_id'] = $scope.uploadedTo;
|
|
||||||
if ($scope.searching) components['term'] = $scope.searchTerm;
|
|
||||||
|
|
||||||
|
|
||||||
url += Object.keys(components).map((key) => {
|
|
||||||
return key + '=' + encodeURIComponent(components[key]);
|
|
||||||
}).join('&');
|
|
||||||
|
|
||||||
$http.get(url).then((response) => {
|
|
||||||
$scope.images = $scope.images.concat(response.data.images);
|
|
||||||
$scope.hasMore = response.data.hasMore;
|
|
||||||
page++;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
$scope.fetchData = fetchData;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start a search operation
|
|
||||||
*/
|
|
||||||
$scope.searchImages = function() {
|
|
||||||
|
|
||||||
if ($scope.searchTerm === '') {
|
|
||||||
cancelSearch();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$scope.searching) {
|
|
||||||
preSearchImages = $scope.images;
|
|
||||||
preSearchHasMore = $scope.hasMore;
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.searching = true;
|
|
||||||
$scope.images = [];
|
|
||||||
$scope.hasMore = false;
|
|
||||||
page = 0;
|
|
||||||
baseUrl = window.baseUrl('/images/' + $scope.imageType + '/search/');
|
|
||||||
fetchData();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the current image listing view.
|
|
||||||
* @param viewName
|
|
||||||
*/
|
|
||||||
$scope.setView = function(viewName) {
|
|
||||||
cancelSearch();
|
|
||||||
$scope.images = [];
|
|
||||||
$scope.hasMore = false;
|
|
||||||
page = 0;
|
|
||||||
$scope.view = viewName;
|
|
||||||
baseUrl = window.baseUrl('/images/' + $scope.imageType + '/' + viewName + '/');
|
|
||||||
fetchData();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save the details of an image.
|
|
||||||
* @param event
|
|
||||||
*/
|
|
||||||
$scope.saveImageDetails = function (event) {
|
|
||||||
event.preventDefault();
|
|
||||||
let url = window.baseUrl('/images/update/' + $scope.selectedImage.id);
|
|
||||||
$http.put(url, this.selectedImage).then(response => {
|
|
||||||
events.emit('success', trans('components.image_update_success'));
|
|
||||||
}, (response) => {
|
|
||||||
if (response.status === 422) {
|
|
||||||
let errors = response.data;
|
|
||||||
let message = '';
|
|
||||||
Object.keys(errors).forEach((key) => {
|
|
||||||
message += errors[key].join('\n');
|
|
||||||
});
|
|
||||||
events.emit('error', message);
|
|
||||||
} else if (response.status === 403) {
|
|
||||||
events.emit('error', response.data.error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete an image from system and notify of success.
|
|
||||||
* Checks if it should force delete when an image
|
|
||||||
* has dependant pages.
|
|
||||||
* @param event
|
|
||||||
*/
|
|
||||||
$scope.deleteImage = function (event) {
|
|
||||||
event.preventDefault();
|
|
||||||
let force = $scope.dependantPages !== false;
|
|
||||||
let url = window.baseUrl('/images/' + $scope.selectedImage.id);
|
|
||||||
if (force) url += '?force=true';
|
|
||||||
$http.delete(url).then((response) => {
|
|
||||||
$scope.images.splice($scope.images.indexOf($scope.selectedImage), 1);
|
|
||||||
$scope.selectedImage = false;
|
|
||||||
events.emit('success', trans('components.image_delete_success'));
|
|
||||||
}, (response) => {
|
|
||||||
// Pages failure
|
|
||||||
if (response.status === 400) {
|
|
||||||
$scope.dependantPages = response.data;
|
|
||||||
} else if (response.status === 403) {
|
|
||||||
events.emit('error', response.data.error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple date creator used to properly format dates.
|
|
||||||
* @param stringDate
|
|
||||||
* @returns {Date}
|
|
||||||
*/
|
|
||||||
$scope.getDate = function (stringDate) {
|
|
||||||
return new Date(stringDate);
|
|
||||||
};
|
|
||||||
|
|
||||||
}]);
|
|
||||||
|
|
||||||
ngApp.controller('PageEditController', ['$scope', '$http', '$attrs', '$interval', '$timeout', '$sce',
|
ngApp.controller('PageEditController', ['$scope', '$http', '$attrs', '$interval', '$timeout', '$sce',
|
||||||
function ($scope, $http, $attrs, $interval, $timeout, $sce) {
|
function ($scope, $http, $attrs, $interval, $timeout, $sce) {
|
||||||
|
|
|
@ -383,7 +383,7 @@ module.exports = function (ngApp, events) {
|
||||||
// Show the image manager and handle image insertion
|
// Show the image manager and handle image insertion
|
||||||
function showImageManager() {
|
function showImageManager() {
|
||||||
let cursorPos = cm.getCursor('from');
|
let cursorPos = cm.getCursor('from');
|
||||||
window.ImageManager.showExternal(image => {
|
window.ImageManager.show(image => {
|
||||||
let selectedText = cm.getSelection();
|
let selectedText = cm.getSelection();
|
||||||
let newText = "";
|
let newText = "";
|
||||||
cm.focus();
|
cm.focus();
|
||||||
|
|
|
@ -67,10 +67,8 @@ require("./vues/vues");
|
||||||
require("./components");
|
require("./components");
|
||||||
|
|
||||||
// Load in angular specific items
|
// Load in angular specific items
|
||||||
const Services = require('./services');
|
|
||||||
const Directives = require('./directives');
|
const Directives = require('./directives');
|
||||||
const Controllers = require('./controllers');
|
const Controllers = require('./controllers');
|
||||||
Services(ngApp, window.Events);
|
|
||||||
Directives(ngApp, window.Events);
|
Directives(ngApp, window.Events);
|
||||||
Controllers(ngApp, window.Events);
|
Controllers(ngApp, window.Events);
|
||||||
|
|
||||||
|
|
|
@ -283,7 +283,7 @@ module.exports = function() {
|
||||||
|
|
||||||
if (type === 'image') {
|
if (type === 'image') {
|
||||||
// Show image manager
|
// Show image manager
|
||||||
window.ImageManager.showExternal(function (image) {
|
window.ImageManager.show(function (image) {
|
||||||
|
|
||||||
// Set popover link input to image url then fire change event
|
// Set popover link input to image url then fire change event
|
||||||
// to ensure the new value sticks
|
// to ensure the new value sticks
|
||||||
|
@ -365,7 +365,7 @@ module.exports = function() {
|
||||||
icon: 'image',
|
icon: 'image',
|
||||||
tooltip: 'Insert an image',
|
tooltip: 'Insert an image',
|
||||||
onclick: function () {
|
onclick: function () {
|
||||||
window.ImageManager.showExternal(function (image) {
|
window.ImageManager.show(function (image) {
|
||||||
let html = `<a href="${image.url}" target="_blank">`;
|
let html = `<a href="${image.url}" target="_blank">`;
|
||||||
html += `<img src="${image.thumbs.display}" alt="${image.name}">`;
|
html += `<img src="${image.thumbs.display}" alt="${image.name}">`;
|
||||||
html += '</a>';
|
html += '</a>';
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
"use strict";
|
|
||||||
|
|
||||||
module.exports = function(ngApp, events) {
|
|
||||||
|
|
||||||
ngApp.factory('imageManagerService', function() {
|
|
||||||
return {
|
|
||||||
show: false,
|
|
||||||
showExternal: false
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
};
|
|
60
resources/assets/js/vues/components/dropzone.js
Normal file
60
resources/assets/js/vues/components/dropzone.js
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
const DropZone = require("dropzone");
|
||||||
|
|
||||||
|
const template = `
|
||||||
|
<div class="dropzone-container">
|
||||||
|
<div class="dz-message">{{placeholder}}</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const props = ['placeholder', 'uploadUrl', 'uploadedTo'];
|
||||||
|
|
||||||
|
// TODO - Remove jQuery usage
|
||||||
|
function mounted() {
|
||||||
|
let container = this.$el;
|
||||||
|
let _this = this;
|
||||||
|
new DropZone(container, {
|
||||||
|
url: function() {
|
||||||
|
return _this.uploadUrl;
|
||||||
|
},
|
||||||
|
init: function () {
|
||||||
|
let dz = this;
|
||||||
|
|
||||||
|
dz.on('sending', function (file, xhr, data) {
|
||||||
|
let token = window.document.querySelector('meta[name=token]').getAttribute('content');
|
||||||
|
data.append('_token', token);
|
||||||
|
let uploadedTo = typeof _this.uploadedTo === 'undefined' ? 0 : _this.uploadedTo;
|
||||||
|
data.append('uploaded_to', uploadedTo);
|
||||||
|
});
|
||||||
|
|
||||||
|
dz.on('success', function (file, data) {
|
||||||
|
_this.$emit('success', {file, data});
|
||||||
|
$(file.previewElement).fadeOut(400, function () {
|
||||||
|
dz.removeFile(file);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
dz.on('error', function (file, errorMessage, xhr) {
|
||||||
|
_this.$emit('error', {file, errorMessage, xhr});
|
||||||
|
console.log(errorMessage);
|
||||||
|
console.log(xhr);
|
||||||
|
function setMessage(message) {
|
||||||
|
$(file.previewElement).find('[data-dz-errormessage]').text(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xhr.status === 413) setMessage(trans('errors.server_upload_limit'));
|
||||||
|
if (errorMessage.file) setMessage(errorMessage.file[0]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function data() {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
template,
|
||||||
|
props,
|
||||||
|
mounted,
|
||||||
|
data,
|
||||||
|
};
|
182
resources/assets/js/vues/image-manager.js
Normal file
182
resources/assets/js/vues/image-manager.js
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
const dropzone = require('./components/dropzone');
|
||||||
|
|
||||||
|
let page = 0;
|
||||||
|
let previousClickTime = 0;
|
||||||
|
let previousClickImage = 0;
|
||||||
|
let dataLoaded = false;
|
||||||
|
let callback = false;
|
||||||
|
let baseUrl = '';
|
||||||
|
|
||||||
|
let preSearchImages = [];
|
||||||
|
let preSearchHasMore = false;
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
images: [],
|
||||||
|
|
||||||
|
imageType: false,
|
||||||
|
uploadedTo: false,
|
||||||
|
|
||||||
|
selectedImage: false,
|
||||||
|
dependantPages: false,
|
||||||
|
showing: false,
|
||||||
|
view: 'all',
|
||||||
|
hasMore: false,
|
||||||
|
searching: false,
|
||||||
|
searchTerm: '',
|
||||||
|
|
||||||
|
imageUpdateSuccess: false,
|
||||||
|
imageDeleteSuccess: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const methods = {
|
||||||
|
|
||||||
|
show(providedCallback) {
|
||||||
|
callback = providedCallback;
|
||||||
|
this.showing = true;
|
||||||
|
this.$el.children[0].components.overlay.show();
|
||||||
|
|
||||||
|
// Get initial images if they have not yet been loaded in.
|
||||||
|
if (dataLoaded) return;
|
||||||
|
this.fetchData();
|
||||||
|
dataLoaded = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
hide() {
|
||||||
|
this.showing = false;
|
||||||
|
this.$el.children[0].components.overlay.hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
fetchData() {
|
||||||
|
let url = baseUrl + page;
|
||||||
|
let query = {};
|
||||||
|
if (this.uploadedTo !== false) query.page_id = this.uploadedTo;
|
||||||
|
if (this.searching) query.term = this.searchTerm;
|
||||||
|
|
||||||
|
this.$http.get(url, {params: query}).then(response => {
|
||||||
|
this.images = this.images.concat(response.data.images);
|
||||||
|
this.hasMore = response.data.hasMore;
|
||||||
|
page++;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setView(viewName) {
|
||||||
|
this.cancelSearch();
|
||||||
|
this.images = [];
|
||||||
|
this.hasMore = false;
|
||||||
|
page = 0;
|
||||||
|
this.view = viewName;
|
||||||
|
baseUrl = window.baseUrl(`/images/${this.imageType}/${viewName}/`);
|
||||||
|
this.fetchData();
|
||||||
|
},
|
||||||
|
|
||||||
|
searchImages() {
|
||||||
|
if (this.searchTerm === '') return this.cancelSearch();
|
||||||
|
|
||||||
|
// Cache current settings for later
|
||||||
|
if (!this.searching) {
|
||||||
|
preSearchImages = this.images;
|
||||||
|
preSearchHasMore = this.hasMore;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.searching = true;
|
||||||
|
this.images = [];
|
||||||
|
this.hasMore = false;
|
||||||
|
page = 0;
|
||||||
|
baseUrl = window.baseUrl(`/images/${this.imageType}/search/`);
|
||||||
|
this.fetchData();
|
||||||
|
},
|
||||||
|
|
||||||
|
cancelSearch() {
|
||||||
|
this.searching = false;
|
||||||
|
this.searchTerm = '';
|
||||||
|
this.images = preSearchImages;
|
||||||
|
this.hasMore = preSearchHasMore;
|
||||||
|
},
|
||||||
|
|
||||||
|
imageSelect(image) {
|
||||||
|
let dblClickTime = 300;
|
||||||
|
let currentTime = Date.now();
|
||||||
|
let timeDiff = currentTime - previousClickTime;
|
||||||
|
let isDblClick = timeDiff < dblClickTime && image.id === previousClickImage;
|
||||||
|
|
||||||
|
if (isDblClick) {
|
||||||
|
this.callbackAndHide(image);
|
||||||
|
} else {
|
||||||
|
this.selectedImage = image;
|
||||||
|
this.dependantPages = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
previousClickTime = currentTime;
|
||||||
|
previousClickImage = image.id;
|
||||||
|
},
|
||||||
|
|
||||||
|
callbackAndHide(imageResult) {
|
||||||
|
if (callback) callback(imageResult);
|
||||||
|
this.hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
saveImageDetails() {
|
||||||
|
let url = window.baseUrl(`/images/update/${this.selectedImage.id}`);
|
||||||
|
this.$http.put(url, this.selectedImage).then(response => {
|
||||||
|
this.$events.emit('success', trans('components.image_update_success'));
|
||||||
|
}).catch(error => {
|
||||||
|
if (error.response.status === 422) {
|
||||||
|
let errors = error.response.data;
|
||||||
|
let message = '';
|
||||||
|
Object.keys(errors).forEach((key) => {
|
||||||
|
message += errors[key].join('\n');
|
||||||
|
});
|
||||||
|
this.$events.emit('error', message);
|
||||||
|
} else if (error.response.status === 403) {
|
||||||
|
this.$events.emit('error', error.response.data.error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteImage() {
|
||||||
|
let force = this.dependantPages !== false;
|
||||||
|
let url = window.baseUrl('/images/' + this.selectedImage.id);
|
||||||
|
if (force) url += '?force=true';
|
||||||
|
this.$http.delete(url).then(response => {
|
||||||
|
this.images.splice(this.images.indexOf(this.selectedImage), 1);
|
||||||
|
this.selectedImage = false;
|
||||||
|
this.$events.emit('success', trans('components.image_delete_success'));
|
||||||
|
}).catch(error=> {
|
||||||
|
if (error.response.status === 400) {
|
||||||
|
this.dependantPages = error.response.data;
|
||||||
|
} else if (error.response.status === 403) {
|
||||||
|
this.$events.emit('error', error.response.data.error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getDate(stringDate) {
|
||||||
|
return new Date(stringDate);
|
||||||
|
},
|
||||||
|
|
||||||
|
uploadSuccess(event) {
|
||||||
|
this.images.unshift(event.data);
|
||||||
|
this.$events.emit('success', trans('components.image_upload_success'));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const computed = {
|
||||||
|
uploadUrl() {
|
||||||
|
return window.baseUrl(`/images/${this.imageType}/upload`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function mounted() {
|
||||||
|
window.ImageManager = this;
|
||||||
|
this.imageType = this.$el.getAttribute('image-type');
|
||||||
|
this.uploadedTo = this.$el.getAttribute('uploaded-to');
|
||||||
|
baseUrl = window.baseUrl('/images/' + this.imageType + '/all/')
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
mounted,
|
||||||
|
methods,
|
||||||
|
data,
|
||||||
|
computed,
|
||||||
|
components: {dropzone},
|
||||||
|
};
|
|
@ -7,7 +7,8 @@ function exists(id) {
|
||||||
let vueMapping = {
|
let vueMapping = {
|
||||||
'search-system': require('./search'),
|
'search-system': require('./search'),
|
||||||
'entity-dashboard': require('./entity-search'),
|
'entity-dashboard': require('./entity-search'),
|
||||||
'code-editor': require('./code-editor')
|
'code-editor': require('./code-editor'),
|
||||||
|
'image-manager': require('./image-manager'),
|
||||||
};
|
};
|
||||||
|
|
||||||
window.vues = {};
|
window.vues = {};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<div id="image-manager" image-type="{{ $imageType }}" ng-controller="ImageManagerController" uploaded-to="{{ $uploaded_to or 0 }}">
|
<div id="image-manager" image-type="{{ $imageType }}" uploaded-to="{{ $uploaded_to or 0 }}">
|
||||||
<div overlay ng-cloak ng-click="hide()">
|
<div overlay v-cloak>
|
||||||
<div class="popup-body" ng-click="$event.stopPropagation()">
|
<div class="popup-body" @click.stop="">
|
||||||
|
|
||||||
<div class="popup-header primary-background">
|
<div class="popup-header primary-background">
|
||||||
<div class="popup-title">{{ trans('components.image_select') }}</div>
|
<div class="popup-title">{{ trans('components.image_select') }}</div>
|
||||||
|
@ -10,82 +10,80 @@
|
||||||
<div class="flex-fill image-manager-body">
|
<div class="flex-fill image-manager-body">
|
||||||
|
|
||||||
<div class="image-manager-content">
|
<div class="image-manager-content">
|
||||||
<div ng-if="imageType === 'gallery'" class="container">
|
<div v-if="imageType === 'gallery'" class="container">
|
||||||
<div class="image-manager-header row faded-small nav-tabs">
|
<div class="image-manager-header row faded-small nav-tabs">
|
||||||
<div class="col-xs-4 tab-item" title="{{ trans('components.image_all_title') }}" ng-class="{selected: (view=='all')}" ng-click="setView('all')"><i class="zmdi zmdi-collection-image"></i> {{ trans('components.image_all') }}</div>
|
<div class="col-xs-4 tab-item" title="{{ trans('components.image_all_title') }}" :class="{selected: (view=='all')}" @click="setView('all')"><i class="zmdi zmdi-collection-image"></i> {{ trans('components.image_all') }}</div>
|
||||||
<div class="col-xs-4 tab-item" title="{{ trans('components.image_book_title') }}" ng-class="{selected: (view=='book')}" ng-click="setView('book')"><i class="zmdi zmdi-book text-book"></i> {{ trans('entities.book') }}</div>
|
<div class="col-xs-4 tab-item" title="{{ trans('components.image_book_title') }}" :class="{selected: (view=='book')}" @click="setView('book')"><i class="zmdi zmdi-book text-book"></i> {{ trans('entities.book') }}</div>
|
||||||
<div class="col-xs-4 tab-item" title="{{ trans('components.image_page_title') }}" ng-class="{selected: (view=='page')}" ng-click="setView('page')"><i class="zmdi zmdi-file-text text-page"></i> {{ trans('entities.page') }}</div>
|
<div class="col-xs-4 tab-item" title="{{ trans('components.image_page_title') }}" :class="{selected: (view=='page')}" @click="setView('page')"><i class="zmdi zmdi-file-text text-page"></i> {{ trans('entities.page') }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div ng-show="view === 'all'" >
|
<div v-show="view === 'all'" >
|
||||||
<form ng-submit="searchImages()" class="contained-search-box">
|
<form @submit="searchImages" class="contained-search-box">
|
||||||
<input type="text" placeholder="{{ trans('components.image_search_hint') }}" ng-model="searchTerm">
|
<input placeholder="{{ trans('components.image_search_hint') }}" v-model="searchTerm">
|
||||||
<button ng-class="{active: searching}" title="{{ trans('common.search_clear') }}" type="button" ng-click="cancelSearch()" class="text-button cancel"><i class="zmdi zmdi-close-circle-o"></i></button>
|
<button :class="{active: searching}" title="{{ trans('common.search_clear') }}" type="button" @click="cancelSearch()" class="text-button cancel"><i class="zmdi zmdi-close-circle-o"></i></button>
|
||||||
<button title="{{ trans('common.search') }}" class="text-button" type="submit"><i class="zmdi zmdi-search"></i></button>
|
<button title="{{ trans('common.search') }}" class="text-button"><i class="zmdi zmdi-search"></i></button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="image-manager-list">
|
<div class="image-manager-list">
|
||||||
<div ng-repeat="image in images">
|
<div v-if="images.length > 0" v-for="(image, idx) in images">
|
||||||
<div class="image anim fadeIn" ng-style="{animationDelay: ($index > 26) ? '160ms' : ($index * 25) + 'ms'}"
|
<div class="image anim fadeIn" :style="{animationDelay: (idx > 26) ? '160ms' : ((idx * 25) + 'ms')}"
|
||||||
ng-class="{selected: (image==selectedImage)}" ng-click="imageSelect(image)">
|
:class="{selected: (image==selectedImage)}" @click="imageSelect(image)">
|
||||||
<img ng-src="@{{image.thumbs.gallery}}" ng-attr-alt="@{{image.title}}" ng-attr-title="@{{image.name}}">
|
<img :src="image.thumbs.gallery" :alt="image.title" :title="image.name">
|
||||||
<div class="image-meta">
|
<div class="image-meta">
|
||||||
<span class="name" ng-bind="image.name"></span>
|
<span class="name" v-text="image.name"></span>
|
||||||
<span class="date">{{ trans('components.image_uploaded', ['uploadedDate' => "{{ getDate(image.created_at) }" . "}"]) }}</span>
|
<span class="date">{{ trans('components.image_uploaded', ['uploadedDate' => "{{ getDate(image.created_at) }" . "}"]) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="load-more" ng-show="hasMore" ng-click="fetchData()">{{ trans('components.image_load_more') }}</div>
|
<div class="load-more" v-show="hasMore" @click="fetchData">{{ trans('components.image_load_more') }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="image-manager-sidebar">
|
<div class="image-manager-sidebar">
|
||||||
<div class="inner">
|
<div class="inner">
|
||||||
|
|
||||||
<div class="image-manager-details anim fadeIn" ng-show="selectedImage">
|
<div class="image-manager-details anim fadeIn" v-if="selectedImage">
|
||||||
|
|
||||||
<form ng-submit="saveImageDetails($event)">
|
<form @submit.prevent="saveImageDetails">
|
||||||
<div>
|
<div>
|
||||||
<a ng-href="@{{selectedImage.url}}" target="_blank" style="display: block;">
|
<a :href="selectedImage.url" target="_blank" style="display: block;">
|
||||||
<img ng-src="@{{selectedImage.thumbs.gallery}}" ng-attr-alt="@{{selectedImage.title}}" ng-attr-title="@{{selectedImage.name}}">
|
<img :src="selectedImage.thumbs.gallery" :alt="selectedImage.title"
|
||||||
|
:title="selectedImage.name">
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="name">{{ trans('components.image_image_name') }}</label>
|
<label for="name">{{ trans('components.image_image_name') }}</label>
|
||||||
<input type="text" id="name" name="name" ng-model="selectedImage.name">
|
<input id="name" name="name" v-model="selectedImage.name">
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div ng-show="dependantPages">
|
<div v-show="dependantPages">
|
||||||
<p class="text-neg text-small">
|
<p class="text-neg text-small">
|
||||||
{{ trans('components.image_delete_confirm') }}
|
{{ trans('components.image_delete_confirm') }}
|
||||||
</p>
|
</p>
|
||||||
<ul class="text-neg">
|
<ul class="text-neg">
|
||||||
<li ng-repeat="page in dependantPages">
|
<li v-for="page in dependantPages">
|
||||||
<a ng-href="@{{ page.url }}" target="_blank" class="text-neg" ng-bind="page.name"></a>
|
<a :href="page.url" target="_blank" class="text-neg" v-text="page.name"></a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="clearfix">
|
<div class="clearfix">
|
||||||
<form class="float left" ng-submit="deleteImage($event)">
|
<form class="float left" @submit.prevent="deleteImage">
|
||||||
<button class="button icon neg"><i class="zmdi zmdi-delete"></i></button>
|
<button class="button icon neg"><i class="zmdi zmdi-delete"></i></button>
|
||||||
</form>
|
</form>
|
||||||
<button class="button pos anim fadeIn float right" ng-show="selectedImage" ng-click="selectButtonClick()">
|
<button class="button pos anim fadeIn float right" v-show="selectedImage" @click="callbackAndHide(selectedImage)">
|
||||||
<i class="zmdi zmdi-square-right"></i>{{ trans('components.image_select_image') }}
|
<i class="zmdi zmdi-square-right"></i>{{ trans('components.image_select_image') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<drop-zone message="{{ trans('components.image_dropzone') }}" upload-url="@{{getUploadUrl()}}" uploaded-to="@{{uploadedTo}}" event-success="uploadSuccess"></drop-zone>
|
<dropzone placeholder="{{ trans('components.image_dropzone') }}" :upload-url="uploadUrl" :uploaded-to="uploadedTo" @success="uploadSuccess"></dropzone>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue