]> BookStack Code Mirror - bookstack/blob - resources/assets/js/components/image-manager.vue
Revamped image system to use driver-agnotstic storage and be more efficent
[bookstack] / resources / assets / js / components / image-manager.vue
1 <template>
2     <div id="image-manager">
3         <div class="overlay" v-el:overlay @click="overlayClick">
4             <div class="image-manager-body">
5                 <div class="image-manager-content">
6                     <div class="image-manager-list">
7                         <div v-for="image in images">
8                             <img class="anim fadeIn"
9                                  :class="{selected: (image==selectedImage)}"
10                                  :src="image.thumbs.gallery" :alt="image.title" :title="image.name"
11                                  @click="imageClick(image)"
12                                  :style="{animationDelay: ($index > 26) ? '160ms' : ($index * 25) + 'ms'}">
13                         </div>
14                         <div class="load-more" v-show="hasMore" @click="fetchData">Load More</div>
15                     </div>
16                 </div>
17                 <button class="neg button image-manager-close" @click="hide">x</button>
18                 <div class="image-manager-sidebar">
19                     <h2 v-el:image-title>Images</h2>
20                     <hr class="even">
21                     <div class="dropzone-container" v-el:drop-zone>
22                         <div class="dz-message">Drop files or click here to upload</div>
23                     </div>
24                     <div class="image-manager-details anim fadeIn" v-show="selectedImage">
25                         <hr class="even">
26                         <form @submit="saveImageDetails" v-el:image-form>
27                             <div class="form-group">
28                                 <label for="name">Image Name</label>
29                                 <input type="text" id="name" name="name" v-model="selectedImage.name">
30                             </div>
31                         </form>
32                         <hr class="even">
33                         <div v-show="dependantPages">
34                             <p class="text-neg text-small">
35                                 This image is used in the pages below, Click delete again to confirm you want to delete
36                                 this image.
37                             </p>
38                             <ul class="text-neg">
39                                 <li v-for="page in dependantPages">
40                                     <a :href="page.url" target="_blank" class="text-neg">{{ page.name }}</a>
41                                 </li>
42                             </ul>
43                         </div>
44
45                         <form @submit="deleteImage" v-el:image-delete-form>
46                             <button class="button neg"><i class="zmdi zmdi-delete"></i>Delete Image</button>
47                         </form>
48                     </div>
49                     <div class="image-manager-bottom">
50                         <button class="button pos anim fadeIn" v-show="selectedImage" @click="selectButtonClick"><i
51                                 class="zmdi zmdi-square-right"></i>Select Image
52                         </button>
53                     </div>
54                 </div>
55             </div>
56         </div>
57     </div>
58 </template>
59
60 <script>
61
62     var Dropzone = require('dropzone');
63
64     module.exports = {
65         data: function () {
66             return {
67                 images: [],
68                 hasMore: false,
69                 page: 0,
70                 cClickTime: 0,
71                 selectedImage: false,
72                 dependantPages: false,
73                 deleteForm: {},
74                 token: document.querySelector('meta[name=token]').getAttribute('content'),
75                 dataLoaded: false
76             }
77         },
78
79         created: function () {
80             window.ImageManager = this;
81         },
82
83         ready: function () {
84             // Create dropzone
85             this.setupDropZone();
86         },
87
88         methods: {
89             fetchData: function () {
90                 var _this = this;
91                 this.$http.get('/images/gallery/all/' + _this.page, function (data) {
92                     _this.images = _this.images.concat(data.images);
93                     _this.hasMore = data.hasMore;
94                     _this.page++;
95                 });
96             },
97
98             setupDropZone: function () {
99                 var _this = this;
100                 var dropZone = new Dropzone(_this.$els.dropZone, {
101                     url: '/images/gallery/upload',
102                     init: function () {
103                         var dz = this;
104                         this.on("sending", function (file, xhr, data) {
105                             data.append("_token", _this.token);
106                         });
107                         this.on("success", function (file, data) {
108                             _this.images.unshift(data);
109                             $(file.previewElement).fadeOut(400, function () {
110                                 dz.removeFile(file);
111                             });
112                         });
113                         this.on('error', function (file, errorMessage, xhr) {
114                             if (errorMessage.file) {
115                                 $(file.previewElement).find('[data-dz-errormessage]').text(errorMessage.file[0]);
116                             }
117                             console.log(errorMessage);
118                         });
119                     }
120                 });
121             },
122
123             imageClick: function (image) {
124                 var dblClickTime = 380;
125                 var cTime = (new Date()).getTime();
126                 var timeDiff = cTime - this.cClickTime;
127                 if (this.cClickTime !== 0 && timeDiff < dblClickTime && this.selectedImage === image) {
128                     // DoubleClick
129                     if (this.callback) {
130                         this.callback(image);
131                     }
132                     this.hide();
133                 } else {
134                     this.selectedImage = (this.selectedImage === image) ? false : image;
135                     this.dependantPages = false;
136                 }
137                 this.cClickTime = cTime;
138             },
139
140             selectButtonClick: function () {
141                 if (this.callback) {
142                     this.callback(this.selectedImage);
143                 }
144                 this.hide();
145             },
146
147             show: function (callback) {
148                 this.callback = callback;
149                 this.$els.overlay.style.display = 'block';
150                 // Get initial images if they have not yet been loaded in.
151                 if (!this.dataLoaded) {
152                     this.fetchData(this.page);
153                     this.dataLoaded = true;
154                 }
155             },
156
157             overlayClick: function (e) {
158                 if (e.target.className === 'overlay') {
159                     this.hide();
160                 }
161             },
162
163             hide: function () {
164                 this.$els.overlay.style.display = 'none';
165             },
166
167             saveImageDetails: function (e) {
168                 e.preventDefault();
169                 var _this = this;
170                 _this.selectedImage._token = _this.token;
171                 var form = $(_this.$els.imageForm);
172                 $.ajax('/images/update/' + _this.selectedImage.id, {
173                     method: 'PUT',
174                     data: _this.selectedImage
175                 }).done(function () {
176                     form.showSuccess('Image name updated');
177                 }).fail(function (jqXHR) {
178                     form.showFailure(jqXHR.responseJSON);
179                 })
180             },
181
182             deleteImage: function (e) {
183                 e.preventDefault();
184                 var _this = this;
185                 _this.deleteForm.force = _this.dependantPages !== false;
186                 _this.deleteForm._token = _this.token;
187                 $.ajax('/images/' + _this.selectedImage.id, {
188                     method: 'DELETE',
189                     data: _this.deleteForm
190                 }).done(function () {
191                     _this.images.splice(_this.images.indexOf(_this.selectedImage), 1);
192                     _this.selectedImage = false;
193                     $(_this.$els.imageTitle).showSuccess('Image Deleted');
194                 }).fail(function (jqXHR, textStatus) {
195                     // Pages failure
196                     if (jqXHR.status === 400) {
197                         _this.dependantPages = jqXHR.responseJSON;
198                     }
199                 });
200             }
201
202         }
203
204     };
205 </script>