]> BookStack Code Mirror - bookstack/blob - resources/js/components/image-manager.js
Ran eslint fix on existing codebase
[bookstack] / resources / js / components / image-manager.js
1 import {
2     onChildEvent, onSelect, removeLoading, showLoading,
3 } from '../services/dom';
4 import {Component} from './component';
5
6 export class ImageManager extends Component {
7
8     setup() {
9         // Options
10         this.uploadedTo = this.$opts.uploadedTo;
11
12         // Element References
13         this.container = this.$el;
14         this.popupEl = this.$refs.popup;
15         this.searchForm = this.$refs.searchForm;
16         this.searchInput = this.$refs.searchInput;
17         this.cancelSearch = this.$refs.cancelSearch;
18         this.listContainer = this.$refs.listContainer;
19         this.filterTabs = this.$manyRefs.filterTabs;
20         this.selectButton = this.$refs.selectButton;
21         this.formContainer = this.$refs.formContainer;
22         this.dropzoneContainer = this.$refs.dropzoneContainer;
23
24         // Instance data
25         this.type = 'gallery';
26         this.lastSelected = {};
27         this.lastSelectedTime = 0;
28         this.callback = null;
29         this.resetState = () => {
30             this.hasData = false;
31             this.page = 1;
32             this.filter = 'all';
33         };
34         this.resetState();
35
36         this.setupListeners();
37     }
38
39     setupListeners() {
40         onSelect(this.filterTabs, e => {
41             this.resetAll();
42             this.filter = e.target.dataset.filter;
43             this.setActiveFilterTab(this.filter);
44             this.loadGallery();
45         });
46
47         this.searchForm.addEventListener('submit', event => {
48             this.resetListView();
49             this.loadGallery();
50             event.preventDefault();
51         });
52
53         onSelect(this.cancelSearch, event => {
54             this.resetListView();
55             this.resetSearchView();
56             this.loadGallery();
57             this.cancelSearch.classList.remove('active');
58         });
59
60         this.searchInput.addEventListener('input', event => {
61             this.cancelSearch.classList.toggle('active', this.searchInput.value.trim());
62         });
63
64         onChildEvent(this.listContainer, '.load-more', 'click', async event => {
65             showLoading(event.target);
66             this.page++;
67             await this.loadGallery();
68             event.target.remove();
69         });
70
71         this.listContainer.addEventListener('event-emit-select-image', this.onImageSelectEvent.bind(this));
72
73         this.listContainer.addEventListener('error', event => {
74             event.target.src = baseUrl('loading_error.png');
75         }, true);
76
77         onSelect(this.selectButton, () => {
78             if (this.callback) {
79                 this.callback(this.lastSelected);
80             }
81             this.hide();
82         });
83
84         onChildEvent(this.formContainer, '#image-manager-delete', 'click', event => {
85             if (this.lastSelected) {
86                 this.loadImageEditForm(this.lastSelected.id, true);
87             }
88         });
89
90         this.formContainer.addEventListener('ajax-form-success', this.refreshGallery.bind(this));
91         this.container.addEventListener('dropzone-success', this.refreshGallery.bind(this));
92     }
93
94     show(callback, type = 'gallery') {
95         this.resetAll();
96
97         this.callback = callback;
98         this.type = type;
99         this.getPopup().show();
100         this.dropzoneContainer.classList.toggle('hidden', type !== 'gallery');
101
102         if (!this.hasData) {
103             this.loadGallery();
104             this.hasData = true;
105         }
106     }
107
108     hide() {
109         this.getPopup().hide();
110     }
111
112     /**
113      * @returns {Popup}
114      */
115     getPopup() {
116         return window.$components.firstOnElement(this.popupEl, 'popup');
117     }
118
119     async loadGallery() {
120         const params = {
121             page: this.page,
122             search: this.searchInput.value || null,
123             uploaded_to: this.uploadedTo,
124             filter_type: this.filter === 'all' ? null : this.filter,
125         };
126
127         const {data: html} = await window.$http.get(`images/${this.type}`, params);
128         if (params.page === 1) {
129             this.listContainer.innerHTML = '';
130         }
131         this.addReturnedHtmlElementsToList(html);
132         removeLoading(this.listContainer);
133     }
134
135     addReturnedHtmlElementsToList(html) {
136         const el = document.createElement('div');
137         el.innerHTML = html;
138         window.$components.init(el);
139         for (const child of [...el.children]) {
140             this.listContainer.appendChild(child);
141         }
142     }
143
144     setActiveFilterTab(filterName) {
145         for (const tab of this.filterTabs) {
146             const selected = tab.dataset.filter === filterName;
147             tab.setAttribute('aria-selected', selected ? 'true' : 'false');
148         }
149     }
150
151     resetAll() {
152         this.resetState();
153         this.resetListView();
154         this.resetSearchView();
155         this.resetEditForm();
156         this.setActiveFilterTab('all');
157         this.selectButton.classList.add('hidden');
158     }
159
160     resetSearchView() {
161         this.searchInput.value = '';
162     }
163
164     resetEditForm() {
165         this.formContainer.innerHTML = '';
166     }
167
168     resetListView() {
169         showLoading(this.listContainer);
170         this.page = 1;
171     }
172
173     refreshGallery() {
174         this.resetListView();
175         this.loadGallery();
176     }
177
178     onImageSelectEvent(event) {
179         const image = JSON.parse(event.detail.data);
180         const isDblClick = ((image && image.id === this.lastSelected.id)
181             && Date.now() - this.lastSelectedTime < 400);
182         const alreadySelected = event.target.classList.contains('selected');
183         [...this.listContainer.querySelectorAll('.selected')].forEach(el => {
184             el.classList.remove('selected');
185         });
186
187         if (!alreadySelected) {
188             event.target.classList.add('selected');
189             this.loadImageEditForm(image.id);
190         } else {
191             this.resetEditForm();
192         }
193         this.selectButton.classList.toggle('hidden', alreadySelected);
194
195         if (isDblClick && this.callback) {
196             this.callback(image);
197             this.hide();
198         }
199
200         this.lastSelected = image;
201         this.lastSelectedTime = Date.now();
202     }
203
204     async loadImageEditForm(imageId, requestDelete = false) {
205         if (!requestDelete) {
206             this.formContainer.innerHTML = '';
207         }
208
209         const params = requestDelete ? {delete: true} : {};
210         const {data: formHtml} = await window.$http.get(`/images/edit/${imageId}`, params);
211         this.formContainer.innerHTML = formHtml;
212         window.$components.init(this.formContainer);
213     }
214
215 }