@fiduswriter/image-manager 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/README.md +5 -0
- package/dist/copyright_dialog/index.d.ts +17 -0
- package/dist/copyright_dialog/index.d.ts.map +1 -0
- package/dist/copyright_dialog/index.js +148 -0
- package/dist/copyright_dialog/index.js.map +1 -0
- package/dist/copyright_dialog/templates.d.ts +13 -0
- package/dist/copyright_dialog/templates.d.ts.map +1 -0
- package/dist/copyright_dialog/templates.js +48 -0
- package/dist/copyright_dialog/templates.js.map +1 -0
- package/dist/database.d.ts +9 -0
- package/dist/database.d.ts.map +1 -0
- package/dist/database.js +51 -0
- package/dist/database.js.map +1 -0
- package/dist/edit_dialog/index.d.ts +24 -0
- package/dist/edit_dialog/index.d.ts.map +1 -0
- package/dist/edit_dialog/index.js +186 -0
- package/dist/edit_dialog/index.js.map +1 -0
- package/dist/edit_dialog/model.d.ts +20 -0
- package/dist/edit_dialog/model.d.ts.map +1 -0
- package/dist/edit_dialog/model.js +153 -0
- package/dist/edit_dialog/model.js.map +1 -0
- package/dist/edit_dialog/templates.d.ts +5 -0
- package/dist/edit_dialog/templates.d.ts.map +1 -0
- package/dist/edit_dialog/templates.js +33 -0
- package/dist/edit_dialog/templates.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/overview/categories.d.ts +8 -0
- package/dist/overview/categories.d.ts.map +1 -0
- package/dist/overview/categories.js +94 -0
- package/dist/overview/categories.js.map +1 -0
- package/dist/overview/index.d.ts +39 -0
- package/dist/overview/index.d.ts.map +1 -0
- package/dist/overview/index.js +358 -0
- package/dist/overview/index.js.map +1 -0
- package/dist/overview/menu.d.ts +48 -0
- package/dist/overview/menu.d.ts.map +1 -0
- package/dist/overview/menu.js +67 -0
- package/dist/overview/menu.js.map +1 -0
- package/dist/overview/templates.d.ts +4 -0
- package/dist/overview/templates.d.ts.map +1 -0
- package/dist/overview/templates.js +24 -0
- package/dist/overview/templates.js.map +1 -0
- package/dist/selection_dialog/index.d.ts +19 -0
- package/dist/selection_dialog/index.d.ts.map +1 -0
- package/dist/selection_dialog/index.js +149 -0
- package/dist/selection_dialog/index.js.map +1 -0
- package/package.json +53 -0
- package/src/copyright_dialog/index.js +179 -0
- package/src/copyright_dialog/templates.js +56 -0
- package/src/database.js +57 -0
- package/src/edit_dialog/index.js +222 -0
- package/src/edit_dialog/model.js +184 -0
- package/src/edit_dialog/templates.js +41 -0
- package/src/index.js +4 -0
- package/src/overview/categories.js +115 -0
- package/src/overview/index.js +435 -0
- package/src/overview/menu.js +75 -0
- package/src/overview/templates.js +30 -0
- package/src/selection_dialog/index.js +176 -0
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
import {baseBodyTemplate} from "@fiduswriter/common/common"
|
|
2
|
+
import {FeedbackTab} from "@fiduswriter/common/feedback"
|
|
3
|
+
import {SiteMenu} from "@fiduswriter/common/menu"
|
|
4
|
+
import {
|
|
5
|
+
Dialog,
|
|
6
|
+
OverviewDataTable,
|
|
7
|
+
OverviewMenuView,
|
|
8
|
+
activateWait,
|
|
9
|
+
addAlert,
|
|
10
|
+
deactivateWait,
|
|
11
|
+
ensureCSS,
|
|
12
|
+
escapeText,
|
|
13
|
+
findTarget,
|
|
14
|
+
gettext,
|
|
15
|
+
isActivationEvent,
|
|
16
|
+
localizeDate,
|
|
17
|
+
post,
|
|
18
|
+
setDocTitle,
|
|
19
|
+
staticUrl,
|
|
20
|
+
whenReady
|
|
21
|
+
} from "fwtoolkit"
|
|
22
|
+
import {ImageOverviewCategories} from "./categories"
|
|
23
|
+
import {bulkMenuModel, menuModel} from "./menu"
|
|
24
|
+
/** Helper functions for user added images/SVGs.*/
|
|
25
|
+
|
|
26
|
+
export class ImageOverview {
|
|
27
|
+
constructor({app, user, plugins = []}) {
|
|
28
|
+
this.app = app
|
|
29
|
+
this.user = user
|
|
30
|
+
this.plugins = plugins
|
|
31
|
+
this.mod = {}
|
|
32
|
+
|
|
33
|
+
this.lastSort = {column: 0, dir: "asc"}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
init() {
|
|
37
|
+
ensureCSS([
|
|
38
|
+
staticUrl("css/dialog_usermedia.css"),
|
|
39
|
+
staticUrl("css/dot_menu.css")
|
|
40
|
+
])
|
|
41
|
+
|
|
42
|
+
return whenReady().then(() => {
|
|
43
|
+
this.render()
|
|
44
|
+
new ImageOverviewCategories(this)
|
|
45
|
+
const smenu = new SiteMenu(this.app, "images")
|
|
46
|
+
smenu.init()
|
|
47
|
+
this.menu = new OverviewMenuView(this, menuModel)
|
|
48
|
+
this.menu.init()
|
|
49
|
+
this.activatePlugins()
|
|
50
|
+
this.bindEvents()
|
|
51
|
+
this.mod.categories.setImageCategoryList(this.app.imageDB.cats)
|
|
52
|
+
this.initTable(Object.keys(this.app.imageDB.db))
|
|
53
|
+
// Reset scroll position to top to prevent Safari from auto-scrolling
|
|
54
|
+
// to the focused table element, which would hide the header/menu
|
|
55
|
+
window.scrollTo(0, 0)
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
render() {
|
|
60
|
+
this.dom = document.createElement("body")
|
|
61
|
+
this.dom.innerHTML = baseBodyTemplate({
|
|
62
|
+
contents: "",
|
|
63
|
+
user: this.user,
|
|
64
|
+
hasOverview: true,
|
|
65
|
+
app: this.app
|
|
66
|
+
})
|
|
67
|
+
document.body = this.dom
|
|
68
|
+
ensureCSS([staticUrl("css/cropper.min.css")])
|
|
69
|
+
setDocTitle(gettext("Media Manager"), this.app)
|
|
70
|
+
const feedbackTab = new FeedbackTab()
|
|
71
|
+
feedbackTab.init()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
activatePlugins() {
|
|
75
|
+
if (this.pluginsActivated) {
|
|
76
|
+
// Plugins have been activated already
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
this.pluginsActivated = true
|
|
80
|
+
// Add plugins.
|
|
81
|
+
this.plugins = {}
|
|
82
|
+
|
|
83
|
+
return Promise.all(
|
|
84
|
+
this.plugins.map(([app, plugin]) => {
|
|
85
|
+
if (!this.app.settings.APPS.includes(app)) {
|
|
86
|
+
return Promise.resolve()
|
|
87
|
+
}
|
|
88
|
+
return Promise.all(
|
|
89
|
+
Object.values(plugin).map(pluginExport => {
|
|
90
|
+
if (typeof pluginExport === "function") {
|
|
91
|
+
this.plugins[pluginExport.name] = new pluginExport(
|
|
92
|
+
this
|
|
93
|
+
)
|
|
94
|
+
return (
|
|
95
|
+
this.plugins[pluginExport.name].init() ||
|
|
96
|
+
Promise.resolve()
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
return Promise.resolve()
|
|
100
|
+
})
|
|
101
|
+
)
|
|
102
|
+
})
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
//delete image
|
|
107
|
+
deleteImage(ids) {
|
|
108
|
+
ids = ids.map(id => Number.parseInt(id))
|
|
109
|
+
if (this.app.isOffline()) {
|
|
110
|
+
addAlert(
|
|
111
|
+
"error",
|
|
112
|
+
gettext(
|
|
113
|
+
"You are currently offline. Please try again when you are back online."
|
|
114
|
+
)
|
|
115
|
+
)
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
activateWait()
|
|
119
|
+
post("/api/usermedia/delete/", {ids})
|
|
120
|
+
.catch(error => {
|
|
121
|
+
addAlert("error", gettext("The image(s) could not be deleted"))
|
|
122
|
+
deactivateWait()
|
|
123
|
+
if (this.app.isOffline()) {
|
|
124
|
+
addAlert(
|
|
125
|
+
"error",
|
|
126
|
+
gettext(
|
|
127
|
+
"You are currently offline. Please try again when you are back online."
|
|
128
|
+
)
|
|
129
|
+
)
|
|
130
|
+
} else {
|
|
131
|
+
throw error
|
|
132
|
+
}
|
|
133
|
+
})
|
|
134
|
+
.then(() => {
|
|
135
|
+
ids.forEach(id => delete this.app.imageDB.db[id])
|
|
136
|
+
this.removeTableRows(ids)
|
|
137
|
+
addAlert("success", gettext("The image(s) have been deleted"))
|
|
138
|
+
})
|
|
139
|
+
.then(() => deactivateWait())
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
deleteImageDialog(ids) {
|
|
143
|
+
const buttons = [
|
|
144
|
+
{
|
|
145
|
+
text: gettext("Delete"),
|
|
146
|
+
classes: "fw-dark",
|
|
147
|
+
click: () => {
|
|
148
|
+
this.deleteImage(ids)
|
|
149
|
+
dialog.close()
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
type: "cancel"
|
|
154
|
+
}
|
|
155
|
+
]
|
|
156
|
+
const dialog = new Dialog({
|
|
157
|
+
id: "confirmdeletion",
|
|
158
|
+
icon: "exclamation-triangle",
|
|
159
|
+
title: gettext("Confirm deletion"),
|
|
160
|
+
body: `<p>${gettext("Delete the image(s)")}?</p>`,
|
|
161
|
+
buttons
|
|
162
|
+
})
|
|
163
|
+
dialog.open()
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
updateTable(ids) {
|
|
167
|
+
// Remove items that already exist
|
|
168
|
+
this.removeTableRows(ids)
|
|
169
|
+
this.table.insert({data: ids.map(id => this.createTableRow(id))})
|
|
170
|
+
// Redo last sort
|
|
171
|
+
this.table.columns.sort(this.lastSort.column, this.lastSort.dir)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
createTableRow(id) {
|
|
175
|
+
const image = this.app.imageDB.db[id]
|
|
176
|
+
const cats = image.cats.map(cat => `cat_${cat}`)
|
|
177
|
+
|
|
178
|
+
let fileType = image.file_type.split("/")
|
|
179
|
+
|
|
180
|
+
if (1 < fileType.length) {
|
|
181
|
+
fileType = fileType[1].toUpperCase()
|
|
182
|
+
} else {
|
|
183
|
+
fileType = fileType[0].toUpperCase()
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return [
|
|
187
|
+
id,
|
|
188
|
+
false, // checkbox
|
|
189
|
+
`<span class="fw-usermedia-image ${cats.join(" ")}">
|
|
190
|
+
<img src="${image.thumbnail ? image.thumbnail : image.image}">
|
|
191
|
+
</span>
|
|
192
|
+
<span class="fw-usermedia-title">
|
|
193
|
+
<span class="edit-image fw-link-text fw-searchable" data-id="${id}">
|
|
194
|
+
${image.title.length ? escapeText(image.title) : gettext("Untitled")}
|
|
195
|
+
</span>
|
|
196
|
+
<span class="fw-usermedia-type">${fileType}</span>
|
|
197
|
+
</span>`,
|
|
198
|
+
`<span>${image.width} x ${image.height}</span>`,
|
|
199
|
+
`<span class="fw-date">${localizeDate(image.added, "sortable-date")}</span>`,
|
|
200
|
+
`<span class="delete-image fw-link-text" data-id="${id}">
|
|
201
|
+
<i class="fa fa-trash-alt"></i>
|
|
202
|
+
</span>`
|
|
203
|
+
]
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
removeTableRows(ids) {
|
|
207
|
+
ids = ids.map(id => Number.parseInt(id))
|
|
208
|
+
|
|
209
|
+
const existingRows = this.table.data.data
|
|
210
|
+
.map((row, index) => {
|
|
211
|
+
const id = row.cells[0].data
|
|
212
|
+
if (ids.includes(id)) {
|
|
213
|
+
return index
|
|
214
|
+
} else {
|
|
215
|
+
return false
|
|
216
|
+
}
|
|
217
|
+
})
|
|
218
|
+
.filter(rowIndex => rowIndex !== false)
|
|
219
|
+
|
|
220
|
+
if (existingRows.length) {
|
|
221
|
+
this.table.rows.remove(existingRows)
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
onResize() {
|
|
226
|
+
if (!this.table) {
|
|
227
|
+
return
|
|
228
|
+
}
|
|
229
|
+
this.initTable(Object.keys(this.app.imageDB.db))
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/* Initialize the overview table */
|
|
233
|
+
initTable(ids) {
|
|
234
|
+
if (this.overviewTable) {
|
|
235
|
+
this.overviewTable.destroy()
|
|
236
|
+
this.overviewTable = null
|
|
237
|
+
}
|
|
238
|
+
this.table = null
|
|
239
|
+
this.dtBulk = null
|
|
240
|
+
|
|
241
|
+
const contentsEl = this.dom.querySelector(".fw-contents")
|
|
242
|
+
contentsEl.innerHTML = ""
|
|
243
|
+
|
|
244
|
+
const hiddenCols = [0]
|
|
245
|
+
|
|
246
|
+
if (window.innerWidth < 500) {
|
|
247
|
+
hiddenCols.push(1)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
this.overviewTable = new OverviewDataTable({
|
|
251
|
+
dom: contentsEl,
|
|
252
|
+
classes: ["fw-data-table", "fw-large"],
|
|
253
|
+
columns: [
|
|
254
|
+
{
|
|
255
|
+
select: 0,
|
|
256
|
+
type: "number"
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
select: 1,
|
|
260
|
+
type: "boolean"
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
select: hiddenCols,
|
|
264
|
+
hidden: true
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
select: [1, 3, 5],
|
|
268
|
+
sortable: false
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
select: [this.lastSort.column],
|
|
272
|
+
sort: this.lastSort.dir
|
|
273
|
+
}
|
|
274
|
+
],
|
|
275
|
+
data: ids.map(id => this.createTableRow(id)),
|
|
276
|
+
idColumn: 0,
|
|
277
|
+
checkboxColumn: 1,
|
|
278
|
+
bulkMenu: bulkMenuModel(),
|
|
279
|
+
bulkMenuPage: this,
|
|
280
|
+
searchable: true,
|
|
281
|
+
scrollY: `${Math.max(window.innerHeight - 360, 100)}px`,
|
|
282
|
+
tabIndex: 1,
|
|
283
|
+
labels: {
|
|
284
|
+
noRows: gettext("No images available"), // Message shown when there are no images
|
|
285
|
+
noResults: gettext("No images found") // Message shown when no images are found after search
|
|
286
|
+
},
|
|
287
|
+
headings: [
|
|
288
|
+
"",
|
|
289
|
+
"",
|
|
290
|
+
gettext("File"),
|
|
291
|
+
gettext("Size (px)"),
|
|
292
|
+
gettext("Added"),
|
|
293
|
+
""
|
|
294
|
+
],
|
|
295
|
+
template: (options, _dom) =>
|
|
296
|
+
`<div class='${options.classes.container}'${options.scrollY.length ? ` style='height: ${options.scrollY}; overflow-Y: auto;'` : ""}></div>
|
|
297
|
+
<div class='${options.classes.bottom}'>
|
|
298
|
+
${
|
|
299
|
+
options.paging
|
|
300
|
+
? `<div class='${options.classes.info}'></div>`
|
|
301
|
+
: ""
|
|
302
|
+
}
|
|
303
|
+
<nav class='${options.classes.pagination}'></nav>
|
|
304
|
+
</div>`,
|
|
305
|
+
rowRender: (row, tr, _index) => {
|
|
306
|
+
const id = row.cells[0].data
|
|
307
|
+
const inputNode = {
|
|
308
|
+
nodeName: "input",
|
|
309
|
+
attributes: {
|
|
310
|
+
type: "checkbox",
|
|
311
|
+
class: "entry-select fw-check",
|
|
312
|
+
"data-id": id,
|
|
313
|
+
id: `doc-img-${id}`
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
if (row.cells[1].data) {
|
|
317
|
+
inputNode.attributes.checked = true
|
|
318
|
+
}
|
|
319
|
+
tr.childNodes[0].childNodes = [
|
|
320
|
+
inputNode,
|
|
321
|
+
{
|
|
322
|
+
nodeName: "label",
|
|
323
|
+
attributes: {
|
|
324
|
+
for: `doc-img-${id}`
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
]
|
|
328
|
+
},
|
|
329
|
+
onEnter: (row, _event) => {
|
|
330
|
+
if (this.getSelected().length > 0) {
|
|
331
|
+
return
|
|
332
|
+
}
|
|
333
|
+
const rowIndex = this.table.data.data.indexOf(row)
|
|
334
|
+
const button = this.table.dom.querySelector(
|
|
335
|
+
`tr[data-index="${rowIndex}"] span.edit-image`
|
|
336
|
+
)
|
|
337
|
+
if (button) {
|
|
338
|
+
button.click()
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
onDelete: row => {
|
|
342
|
+
const imageId = row.cells[0].data
|
|
343
|
+
this.deleteImageDialog([imageId])
|
|
344
|
+
}
|
|
345
|
+
})
|
|
346
|
+
this.overviewTable.init()
|
|
347
|
+
this.table = this.overviewTable.table
|
|
348
|
+
this.table.id = "imagelist"
|
|
349
|
+
this.dtBulk = this.overviewTable.dtBulk
|
|
350
|
+
|
|
351
|
+
this.table.on("datatable.sort", (column, dir) => {
|
|
352
|
+
this.lastSort = {column, dir}
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
this.table.dom.focus()
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// get IDs of selected bib entries
|
|
359
|
+
getSelected() {
|
|
360
|
+
return Array.from(
|
|
361
|
+
this.dom.querySelectorAll(".entry-select:checked:not(:disabled)")
|
|
362
|
+
).map(el => Number.parseInt(el.getAttribute("data-id")))
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
bindEvents() {
|
|
366
|
+
this.dom.addEventListener("click", event =>
|
|
367
|
+
this.handleActivation(event)
|
|
368
|
+
)
|
|
369
|
+
this.dom.addEventListener("keydown", event =>
|
|
370
|
+
this.handleActivation(event)
|
|
371
|
+
)
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
handleActivation(event) {
|
|
375
|
+
if (!isActivationEvent(event)) {
|
|
376
|
+
return
|
|
377
|
+
}
|
|
378
|
+
const el = {}
|
|
379
|
+
switch (true) {
|
|
380
|
+
case findTarget(event, ".delete-image", el): {
|
|
381
|
+
const imageId = el.target.dataset.id
|
|
382
|
+
this.deleteImageDialog([imageId])
|
|
383
|
+
break
|
|
384
|
+
}
|
|
385
|
+
case findTarget(event, ".edit-image", el): {
|
|
386
|
+
const imageId = el.target.dataset.id
|
|
387
|
+
import("../edit_dialog").then(({ImageEditDialog}) => {
|
|
388
|
+
const dialog = new ImageEditDialog(
|
|
389
|
+
this.app.imageDB,
|
|
390
|
+
imageId,
|
|
391
|
+
this
|
|
392
|
+
)
|
|
393
|
+
dialog.init().then(() => {
|
|
394
|
+
this.updateTable([imageId])
|
|
395
|
+
})
|
|
396
|
+
})
|
|
397
|
+
break
|
|
398
|
+
}
|
|
399
|
+
case findTarget(event, ".fw-add-input", el): {
|
|
400
|
+
const itemEl = el.target.closest(".fw-list-input")
|
|
401
|
+
if (!itemEl.nextElementSibling) {
|
|
402
|
+
itemEl.insertAdjacentHTML(
|
|
403
|
+
"afterend",
|
|
404
|
+
`<tr class="fw-list-input">
|
|
405
|
+
<td>
|
|
406
|
+
<input type="text" class="category-form">
|
|
407
|
+
<span class="fw-add-input icon-addremove" tabindex="0"></span>
|
|
408
|
+
</td>
|
|
409
|
+
</tr>`
|
|
410
|
+
)
|
|
411
|
+
} else {
|
|
412
|
+
itemEl.parentElement.removeChild(itemEl)
|
|
413
|
+
}
|
|
414
|
+
break
|
|
415
|
+
}
|
|
416
|
+
default:
|
|
417
|
+
break
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
close() {
|
|
422
|
+
if (this.table) {
|
|
423
|
+
this.table.destroy()
|
|
424
|
+
this.table = null
|
|
425
|
+
}
|
|
426
|
+
if (this.dtBulk) {
|
|
427
|
+
this.dtBulk.destroy()
|
|
428
|
+
this.dtBulk = null
|
|
429
|
+
}
|
|
430
|
+
if (this.menu) {
|
|
431
|
+
this.menu.destroy()
|
|
432
|
+
this.menu = null
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import {gettext} from "fwtoolkit"
|
|
2
|
+
|
|
3
|
+
export const bulkMenuModel = () => ({
|
|
4
|
+
content: [
|
|
5
|
+
{
|
|
6
|
+
title: gettext("Delete selected"),
|
|
7
|
+
tooltip: gettext("Delete selected images."),
|
|
8
|
+
action: overview => {
|
|
9
|
+
const ids = overview.getSelected()
|
|
10
|
+
if (ids.length) {
|
|
11
|
+
overview.deleteImageDialog(ids)
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
disabled: overview =>
|
|
15
|
+
!overview.getSelected().length || overview.app.isOffline()
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
export const menuModel = () => ({
|
|
21
|
+
content: [
|
|
22
|
+
{
|
|
23
|
+
type: "dropdown",
|
|
24
|
+
id: "cat_selector",
|
|
25
|
+
keys: "Alt-c",
|
|
26
|
+
content: [
|
|
27
|
+
{
|
|
28
|
+
title: gettext("All categories"),
|
|
29
|
+
action: _overview => {
|
|
30
|
+
const trs = document.querySelectorAll(
|
|
31
|
+
"#imagelist > tbody > tr"
|
|
32
|
+
)
|
|
33
|
+
trs.forEach(tr => (tr.style.display = ""))
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
order: 1
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
type: "text",
|
|
41
|
+
title: gettext("Edit categories"),
|
|
42
|
+
keys: "Alt-e",
|
|
43
|
+
action: overview => overview.mod.categories.editCategoryDialog(),
|
|
44
|
+
order: 2,
|
|
45
|
+
disabled: overview => overview.app.isOffline()
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
type: "text",
|
|
49
|
+
title: gettext("Upload new image"),
|
|
50
|
+
keys: "Alt-u",
|
|
51
|
+
action: overview => {
|
|
52
|
+
import("../edit_dialog").then(({ImageEditDialog}) => {
|
|
53
|
+
const imageUpload = new ImageEditDialog(
|
|
54
|
+
overview.app.imageDB,
|
|
55
|
+
false,
|
|
56
|
+
overview
|
|
57
|
+
)
|
|
58
|
+
imageUpload.init().then(imageId => {
|
|
59
|
+
overview.updateTable([imageId])
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
},
|
|
63
|
+
order: 3,
|
|
64
|
+
disabled: overview => overview.app.isOffline()
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
type: "search",
|
|
68
|
+
icon: "search",
|
|
69
|
+
title: gettext("Search images"),
|
|
70
|
+
keys: "Alt-s",
|
|
71
|
+
input: (overview, text) => overview.table.search(text),
|
|
72
|
+
order: 4
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
})
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import {escapeText} from "fwtoolkit"
|
|
2
|
+
|
|
3
|
+
/** A template for the image category edit form. */
|
|
4
|
+
const usermediaCategoryformsTemplate = ({categories}) =>
|
|
5
|
+
`${categories
|
|
6
|
+
.map(
|
|
7
|
+
cat =>
|
|
8
|
+
`<tr id="categoryTr_${cat.id}" class="fw-list-input">
|
|
9
|
+
<td>
|
|
10
|
+
<input type="text" class="category-form" id="categoryTitle_${cat.id}"
|
|
11
|
+
value="${escapeText(cat.category_title)}" data-id="${cat.id}" />
|
|
12
|
+
<span class="fw-add-input icon-addremove" tabindex="0"></span>
|
|
13
|
+
</td>
|
|
14
|
+
</tr>`
|
|
15
|
+
)
|
|
16
|
+
.join("")}
|
|
17
|
+
<tr class="fw-list-input">
|
|
18
|
+
<td>
|
|
19
|
+
<input type="text" class="category-form" />
|
|
20
|
+
<span class="fw-add-input icon-addremove" tabindex="0"></span>
|
|
21
|
+
</td>
|
|
22
|
+
</tr>`
|
|
23
|
+
|
|
24
|
+
/** A template to edit image categories. */
|
|
25
|
+
export const usermediaEditcategoriesTemplate = ({categories}) =>
|
|
26
|
+
`<table id="editCategoryList" class="fw-dialog-table">
|
|
27
|
+
<tbody>
|
|
28
|
+
${usermediaCategoryformsTemplate({categories})}
|
|
29
|
+
</tbody>
|
|
30
|
+
</table>`
|