mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-05-31 13:05:47 +08:00
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:
32
resources/js/components/ajax-delete-row.js
Normal file
32
resources/js/components/ajax-delete-row.js
Normal 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;
|
46
resources/js/components/attachments.js
Normal file
46
resources/js/components/attachments.js
Normal 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;
|
69
resources/js/components/dropzone.js
Normal file
69
resources/js/components/dropzone.js
Normal 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;
|
@ -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
|
||||
*/
|
@ -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()});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
51
resources/js/components/tabs.js
Normal file
51
resources/js/components/tabs.js
Normal 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;
|
Reference in New Issue
Block a user