Started migration of attachment manager from vue

- Created new dropzone component.
- Added standard component event system using custom DOM events.
- Added tabs component.
- Added ajax-delete-row component.
This commit is contained in:
Dan Brown
2020-06-30 22:12:45 +01:00
parent 8dc9689c6d
commit 14b6cd1091
15 changed files with 315 additions and 72 deletions

View File

@ -0,0 +1,32 @@
/**
* AjaxDelete
* @extends {Component}
*/
import {onSelect} from "../services/dom";
class AjaxDeleteRow {
setup() {
this.row = this.$el;
this.url = this.$opts.url;
this.deleteButtons = this.$manyRefs.delete;
onSelect(this.deleteButtons, this.runDelete.bind(this));
}
runDelete() {
this.row.style.opacity = '0.7';
this.row.style.pointerEvents = 'none';
window.$http.delete(this.url).then(resp => {
if (typeof resp.data === 'object' && resp.data.message) {
window.$events.emit('success', resp.data.message);
}
this.row.remove();
}).catch(err => {
this.row.style.opacity = null;
this.row.style.pointerEvents = null;
});
}
}
export default AjaxDeleteRow;

View File

@ -0,0 +1,46 @@
/**
* Attachments
* @extends {Component}
*/
class Attachments {
setup() {
this.container = this.$el;
this.pageId = this.$opts.pageId;
this.editContainer = this.$refs.editContainer;
this.mainTabs = this.$refs.mainTabs;
this.list = this.$refs.list;
this.setupListeners();
}
setupListeners() {
this.container.addEventListener('dropzone-success', event => {
this.mainTabs.components.tabs.show('items');
window.$http.get(`/attachments/get/page/${this.pageId}`).then(resp => {
this.list.innerHTML = resp.data;
window.components.init(this.list);
})
});
this.container.addEventListener('sortable-list-sort', event => {
this.updateOrder(event.detail.ids);
});
this.editContainer.addEventListener('keypress', event => {
if (event.key === 'Enter') {
// TODO - Update editing file
}
})
}
updateOrder(idOrder) {
window.$http.put(`/attachments/sort/page/${this.pageId}`, {order: idOrder}).then(resp => {
window.$events.emit('success', resp.data.message);
});
}
}
export default Attachments;

View File

@ -0,0 +1,69 @@
import DropZoneLib from "dropzone";
import {fadeOut} from "../services/animations";
/**
* Dropzone
* @extends {Component}
*/
class Dropzone {
setup() {
this.container = this.$el;
this.url = this.$opts.url;
const _this = this;
this.dz = new DropZoneLib(this.container, {
addRemoveLinks: true,
dictRemoveFile: window.trans('components.image_upload_remove'),
timeout: Number(window.uploadTimeout) || 60000,
maxFilesize: Number(window.uploadLimit) || 256,
url: this.url,
withCredentials: true,
init() {
this.dz = this;
this.dz.on('sending', _this.onSending.bind(_this));
this.dz.on('success', _this.onSuccess.bind(_this));
this.dz.on('error', _this.onError.bind(_this));
}
});
}
onSending(file, xhr, data) {
const token = window.document.querySelector('meta[name=token]').getAttribute('content');
data.append('_token', token);
xhr.ontimeout = function (e) {
this.dz.emit('complete', file);
this.dz.emit('error', file, window.trans('errors.file_upload_timeout'));
}
}
onSuccess(file, data) {
this.container.dispatchEvent(new Event('dropzone'))
this.$emit('success', {file, data});
fadeOut(file.previewElement, 800, () => {
this.dz.removeFile(file);
});
}
onError(file, errorMessage, xhr) {
this.$emit('error', {file, errorMessage, xhr});
const setMessage = (message) => {
const messsageEl = file.previewElement.querySelector('[data-dz-errormessage]');
messsageEl.textContent = message;
}
if (xhr && xhr.status === 413) {
setMessage(window.trans('errors.server_upload_limit'))
} else if (errorMessage.file) {
setMessage(errorMessage.file);
}
}
removeAll() {
this.dz.removeAllFiles(true);
}
}
export default Dropzone;

View File

@ -40,6 +40,14 @@ function initComponent(name, element) {
instance.$refs = allRefs.refs;
instance.$manyRefs = allRefs.manyRefs;
instance.$opts = parseOpts(name, element);
instance.$emit = (eventName, data = {}) => {
data.from = instance;
const event = new CustomEvent(`${name}-${eventName}`, {
bubbles: true,
detail: data
});
instance.$el.dispatchEvent(event);
};
if (typeof instance.setup === 'function') {
instance.setup();
}
@ -158,4 +166,5 @@ export default initAll;
* @property {Object<String, HTMLElement>} $refs
* @property {Object<String, HTMLElement[]>} $manyRefs
* @property {Object<String, String>} $opts
* @property {function(string, Object)} $emit
*/

View File

@ -9,9 +9,12 @@ class SortableList {
this.container = this.$el;
this.handleSelector = this.$opts.handleSelector;
new Sortable(this.container, {
const sortable = new Sortable(this.container, {
handle: this.handleSelector,
animation: 150,
onSort: () => {
this.$emit('sort', {ids: sortable.toArray()});
}
});
}
}

View File

@ -0,0 +1,51 @@
/**
* Tabs
* Works by matching 'tabToggle<Key>' with 'tabContent<Key>' sections.
* @extends {Component}
*/
import {onSelect} from "../services/dom";
class Tabs {
setup() {
this.tabContentsByName = {};
this.tabButtonsByName = {};
this.allContents = [];
this.allButtons = [];
for (const [key, elems] of Object.entries(this.$manyRefs || {})) {
if (key.startsWith('toggle')) {
const cleanKey = key.replace('toggle', '').toLowerCase();
onSelect(elems, e => this.show(cleanKey));
this.allButtons.push(...elems);
this.tabButtonsByName[cleanKey] = elems;
}
if (key.startsWith('content')) {
const cleanKey = key.replace('content', '').toLowerCase();
this.tabContentsByName[cleanKey] = elems;
this.allContents.push(...elems);
}
}
}
show(key) {
this.allContents.forEach(c => {
c.classList.add('hidden');
c.classList.remove('selected');
});
this.allButtons.forEach(b => b.classList.remove('selected'));
const contents = this.tabContentsByName[key] || [];
const buttons = this.tabButtonsByName[key] || [];
if (contents.length > 0) {
contents.forEach(c => {
c.classList.remove('hidden')
c.classList.add('selected')
});
buttons.forEach(b => b.classList.add('selected'));
}
}
}
export default Tabs;