@fiduswriter/image-manager 0.1.4 → 0.1.6

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.
Files changed (65) hide show
  1. package/README.md +53 -3
  2. package/dist/copyright_dialog/index.d.ts +10 -13
  3. package/dist/copyright_dialog/index.d.ts.map +1 -1
  4. package/dist/copyright_dialog/index.js +32 -14
  5. package/dist/copyright_dialog/index.js.map +1 -1
  6. package/dist/copyright_dialog/templates.d.ts +16 -12
  7. package/dist/copyright_dialog/templates.d.ts.map +1 -1
  8. package/dist/copyright_dialog/templates.js.map +1 -1
  9. package/dist/database.d.ts +7 -6
  10. package/dist/database.d.ts.map +1 -1
  11. package/dist/database.js +17 -11
  12. package/dist/database.js.map +1 -1
  13. package/dist/edit_dialog/index.d.ts +22 -19
  14. package/dist/edit_dialog/index.d.ts.map +1 -1
  15. package/dist/edit_dialog/index.js +65 -33
  16. package/dist/edit_dialog/index.js.map +1 -1
  17. package/dist/edit_dialog/model.d.ts +2 -19
  18. package/dist/edit_dialog/model.d.ts.map +1 -1
  19. package/dist/edit_dialog/model.js +51 -31
  20. package/dist/edit_dialog/model.js.map +1 -1
  21. package/dist/edit_dialog/templates.d.ts +7 -4
  22. package/dist/edit_dialog/templates.d.ts.map +1 -1
  23. package/dist/edit_dialog/templates.js.map +1 -1
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js.map +1 -1
  26. package/dist/overview/categories.d.ts +7 -5
  27. package/dist/overview/categories.d.ts.map +1 -1
  28. package/dist/overview/categories.js +18 -13
  29. package/dist/overview/categories.js.map +1 -1
  30. package/dist/overview/index.d.ts +31 -27
  31. package/dist/overview/index.d.ts.map +1 -1
  32. package/dist/overview/index.js +64 -33
  33. package/dist/overview/index.js.map +1 -1
  34. package/dist/overview/menu.d.ts +4 -47
  35. package/dist/overview/menu.d.ts.map +1 -1
  36. package/dist/overview/menu.js +13 -8
  37. package/dist/overview/menu.js.map +1 -1
  38. package/dist/overview/templates.d.ts +7 -3
  39. package/dist/overview/templates.d.ts.map +1 -1
  40. package/dist/overview/templates.js.map +1 -1
  41. package/dist/selection_dialog/index.d.ts +17 -15
  42. package/dist/selection_dialog/index.d.ts.map +1 -1
  43. package/dist/selection_dialog/index.js +18 -8
  44. package/dist/selection_dialog/index.js.map +1 -1
  45. package/dist/types.d.ts +118 -0
  46. package/dist/types.d.ts.map +1 -0
  47. package/dist/types.js +2 -0
  48. package/dist/types.js.map +1 -0
  49. package/package.json +94 -78
  50. package/src/copyright_dialog/{index.js → index.ts} +70 -38
  51. package/src/copyright_dialog/{templates.js → templates.ts} +19 -3
  52. package/src/database.ts +83 -0
  53. package/src/edit_dialog/{index.js → index.ts} +110 -45
  54. package/src/edit_dialog/{model.js → model.ts} +95 -46
  55. package/src/edit_dialog/{templates.js → templates.ts} +13 -2
  56. package/src/global.d.ts +37 -0
  57. package/src/overview/{categories.js → categories.ts} +34 -21
  58. package/src/overview/{index.js → index.ts} +157 -75
  59. package/src/overview/menu.ts +85 -0
  60. package/src/overview/{templates.js → templates.ts} +16 -2
  61. package/src/selection_dialog/{index.js → index.ts} +85 -49
  62. package/src/types.ts +133 -0
  63. package/src/database.js +0 -57
  64. package/src/overview/menu.js +0 -75
  65. /package/src/{index.js → index.ts} +0 -0
@@ -6,39 +6,51 @@ import {
6
6
  gettext,
7
7
  postJson
8
8
  } from "fwtoolkit"
9
+ import type {OverviewMenuDropdownItem} from "fwtoolkit/overview_menu"
10
+
9
11
  import {usermediaEditcategoriesTemplate} from "./templates.js"
12
+ import type {ImageOverview} from "./index.js"
13
+ import type {
14
+ ImageCategory,
15
+ SaveCategoriesRequest,
16
+ SaveCategoriesResponse
17
+ } from "../types.js"
10
18
 
11
19
  export class ImageOverviewCategories {
12
- constructor(imageOverview) {
20
+ imageOverview: ImageOverview
21
+
22
+ constructor(imageOverview: ImageOverview) {
13
23
  this.imageOverview = imageOverview
14
24
  imageOverview.mod.categories = this
15
25
  }
16
26
 
17
27
  //save changes or create a new category
18
- saveCategories(cats) {
28
+ saveCategories(cats: SaveCategoriesRequest): void {
19
29
  activateWait()
20
30
 
21
- postJson("/api/usermedia/save_category/", {
22
- ids: cats.ids,
23
- titles: cats.titles
24
- })
31
+ postJson("/api/usermedia/save_category/", cats as unknown as Record<string, unknown>)
25
32
  .catch(error => {
26
33
  addAlert("error", gettext("Could not update categories"))
27
34
  deactivateWait()
28
35
  throw error
29
36
  })
30
37
  .then(({json}) => {
31
- this.imageOverview.app.imageDB.cats = json.entries
32
- this.setImageCategoryList(json.entries)
38
+ const response = json as SaveCategoriesResponse
39
+ this.imageOverview.app.imageDB.cats = response.entries
40
+ this.setImageCategoryList(response.entries)
33
41
  addAlert("success", gettext("The categories have been updated"))
34
42
  deactivateWait()
35
43
  })
36
44
  }
37
45
 
38
- setImageCategoryList(imageCategories) {
46
+ setImageCategoryList(imageCategories: ImageCategory[]): void {
39
47
  const catSelector = this.imageOverview.menu.model.content.find(
40
- menuItem => menuItem.id === "cat_selector"
48
+ (menuItem): menuItem is OverviewMenuDropdownItem =>
49
+ menuItem.id === "cat_selector"
41
50
  )
51
+ if (!catSelector) {
52
+ return
53
+ }
42
54
  catSelector.content = catSelector.content.filter(
43
55
  cat => cat.type !== "category"
44
56
  )
@@ -46,19 +58,19 @@ export class ImageOverviewCategories {
46
58
  imageCategories.map(cat => ({
47
59
  type: "category",
48
60
  title: cat.category_title,
49
- action: _overview => {
61
+ action: (_overview: unknown) => {
50
62
  const trs = document.querySelectorAll(
51
63
  "#imagelist > tbody > tr"
52
64
  )
53
65
  trs.forEach(tr => {
66
+ const imageCell = tr.querySelector(".fw-usermedia-image")
54
67
  if (
55
- tr
56
- .querySelector(".fw-usermedia-image")
57
- .classList.contains(`cat_${cat.id}`)
68
+ imageCell &&
69
+ imageCell.classList.contains(`cat_${cat.id}`)
58
70
  ) {
59
- tr.style.display = ""
71
+ ;(tr as HTMLElement).style.display = ""
60
72
  } else {
61
- tr.style.display = "none"
73
+ ;(tr as HTMLElement).style.display = "none"
62
74
  }
63
75
  })
64
76
  }
@@ -68,21 +80,22 @@ export class ImageOverviewCategories {
68
80
  }
69
81
 
70
82
  //open a dialog for editing categories
71
- editCategoryDialog() {
83
+ editCategoryDialog(): void {
72
84
  const buttons = [
73
85
  {
74
86
  text: gettext("Submit"),
75
87
  classes: "fw-dark",
76
88
  click: () => {
77
- const cats = {
89
+ const cats: SaveCategoriesRequest = {
78
90
  ids: [],
79
91
  titles: []
80
92
  }
81
93
  document
82
94
  .querySelectorAll("#edit-categories .category-form")
83
95
  .forEach(el => {
84
- const thisVal = el.value.trim()
85
- let thisId = el.dataset.id
96
+ const input = el as HTMLInputElement
97
+ const thisVal = input.value.trim()
98
+ let thisId: string | number = input.dataset.id || "0"
86
99
  if ("undefined" == typeof thisId) {
87
100
  thisId = 0
88
101
  }
@@ -96,7 +109,7 @@ export class ImageOverviewCategories {
96
109
  }
97
110
  },
98
111
  {
99
- type: "cancel"
112
+ type: "cancel" as const
100
113
  }
101
114
  ]
102
115
 
@@ -19,12 +19,67 @@ import {
19
19
  staticUrl,
20
20
  whenReady
21
21
  } from "fwtoolkit"
22
+ import type {DataTable} from "simple-datatables"
23
+
22
24
  import {ImageOverviewCategories} from "./categories.js"
23
25
  import {bulkMenuModel, menuModel} from "./menu.js"
26
+ import type {
27
+ ImageManagerApp,
28
+ ImageManagerPage,
29
+ ImageOverviewPlugin,
30
+ ImageOverviewPluginConstructor,
31
+ ImageTableRow
32
+ } from "../types.js"
33
+
34
+ interface DataTableRow {
35
+ cells: {data: unknown; text?: string}[]
36
+ }
37
+
38
+ interface VirtualNode {
39
+ nodeName: string
40
+ attributes?: Record<string, string | boolean>
41
+ childNodes?: VirtualNode[]
42
+ }
43
+
24
44
  /** Helper functions for user added images/SVGs.*/
25
45
 
26
46
  export class ImageOverview {
27
- constructor({app, user, plugins = []}) {
47
+ app: ImageManagerApp
48
+
49
+ user: unknown
50
+
51
+ plugins: [string, Record<string, ImageOverviewPluginConstructor>][]
52
+
53
+ mod: {
54
+ categories?: ImageOverviewCategories
55
+ }
56
+
57
+ lastSort: {
58
+ column: number
59
+ dir: "asc" | "desc"
60
+ }
61
+
62
+ pluginsActivated = false
63
+
64
+ dom!: HTMLBodyElement
65
+
66
+ menu!: OverviewMenuView
67
+
68
+ overviewTable: OverviewDataTable | null = null
69
+
70
+ table: DataTable | null = null
71
+
72
+ dtBulk: import("fwtoolkit").DatatableBulk | null = null
73
+
74
+ constructor({
75
+ app,
76
+ user,
77
+ plugins = []
78
+ }: {
79
+ app: ImageManagerApp
80
+ user: unknown
81
+ plugins?: [string, Record<string, ImageOverviewPluginConstructor>][]
82
+ }) {
28
83
  this.app = app
29
84
  this.user = user
30
85
  this.plugins = plugins
@@ -33,7 +88,7 @@ export class ImageOverview {
33
88
  this.lastSort = {column: 0, dir: "asc"}
34
89
  }
35
90
 
36
- init() {
91
+ init(): Promise<void> {
37
92
  ensureCSS([
38
93
  staticUrl("css/dialog_usermedia.css"),
39
94
  staticUrl("css/dot_menu.css")
@@ -48,7 +103,7 @@ export class ImageOverview {
48
103
  this.menu.init()
49
104
  this.activatePlugins()
50
105
  this.bindEvents()
51
- this.mod.categories.setImageCategoryList(this.app.imageDB.cats)
106
+ this.mod.categories!.setImageCategoryList(this.app.imageDB.cats)
52
107
  this.initTable(Object.keys(this.app.imageDB.db))
53
108
  // Reset scroll position to top to prevent Safari from auto-scrolling
54
109
  // to the focused table element, which would hide the header/menu
@@ -56,7 +111,7 @@ export class ImageOverview {
56
111
  })
57
112
  }
58
113
 
59
- render() {
114
+ render(): void {
60
115
  this.dom = document.createElement("body")
61
116
  this.dom.innerHTML = baseBodyTemplate({
62
117
  contents: "",
@@ -71,14 +126,14 @@ export class ImageOverview {
71
126
  feedbackTab.init()
72
127
  }
73
128
 
74
- activatePlugins() {
129
+ activatePlugins(): Promise<void> {
75
130
  if (this.pluginsActivated) {
76
131
  // Plugins have been activated already
77
- return
132
+ return Promise.resolve()
78
133
  }
79
134
  this.pluginsActivated = true
80
135
  // Add plugins.
81
- const pluginInstances = {}
136
+ const pluginInstances: Record<string, ImageOverviewPlugin> = {}
82
137
 
83
138
  return Promise.all(
84
139
  this.plugins.map(([app, plugin]) => {
@@ -88,11 +143,10 @@ export class ImageOverview {
88
143
  return Promise.all(
89
144
  Object.values(plugin).map(pluginExport => {
90
145
  if (typeof pluginExport === "function") {
91
- pluginInstances[pluginExport.name] = new pluginExport(
92
- this
93
- )
146
+ const Plugin = pluginExport as ImageOverviewPluginConstructor
147
+ pluginInstances[Plugin.name] = new Plugin(this)
94
148
  return (
95
- pluginInstances[pluginExport.name].init() ||
149
+ pluginInstances[Plugin.name].init?.() ||
96
150
  Promise.resolve()
97
151
  )
98
152
  }
@@ -100,12 +154,12 @@ export class ImageOverview {
100
154
  })
101
155
  )
102
156
  })
103
- )
157
+ ).then(() => undefined)
104
158
  }
105
159
 
106
160
  //delete image
107
- deleteImage(ids) {
108
- ids = ids.map(id => Number.parseInt(id))
161
+ deleteImage(ids: (string | number)[]): void {
162
+ const numericIds = ids.map(id => Number.parseInt(String(id)))
109
163
  if (this.app.isOffline()) {
110
164
  addAlert(
111
165
  "error",
@@ -116,7 +170,7 @@ export class ImageOverview {
116
170
  return
117
171
  }
118
172
  activateWait()
119
- post("/api/usermedia/delete/", {ids})
173
+ post("/api/usermedia/delete/", {ids: numericIds})
120
174
  .catch(error => {
121
175
  addAlert("error", gettext("The image(s) could not be deleted"))
122
176
  deactivateWait()
@@ -132,14 +186,14 @@ export class ImageOverview {
132
186
  }
133
187
  })
134
188
  .then(() => {
135
- ids.forEach(id => delete this.app.imageDB.db[id])
136
- this.removeTableRows(ids)
189
+ numericIds.forEach(id => delete this.app.imageDB.db[id])
190
+ this.removeTableRows(numericIds)
137
191
  addAlert("success", gettext("The image(s) have been deleted"))
138
192
  })
139
193
  .then(() => deactivateWait())
140
194
  }
141
195
 
142
- deleteImageDialog(ids) {
196
+ deleteImageDialog(ids: (string | number)[]): void {
143
197
  const buttons = [
144
198
  {
145
199
  text: gettext("Delete"),
@@ -150,7 +204,7 @@ export class ImageOverview {
150
204
  }
151
205
  },
152
206
  {
153
- type: "cancel"
207
+ type: "cancel" as const
154
208
  }
155
209
  ]
156
210
  const dialog = new Dialog({
@@ -163,24 +217,29 @@ export class ImageOverview {
163
217
  dialog.open()
164
218
  }
165
219
 
166
- updateTable(ids) {
220
+ updateTable(ids: (string | number)[]): void {
167
221
  // Remove items that already exist
168
222
  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)
223
+ if (this.table) {
224
+ this.table.insert({
225
+ data: ids.map(id => this.createTableRow(Number(id)))
226
+ })
227
+ // Redo last sort
228
+ this.table.columns.sort(this.lastSort.column, this.lastSort.dir)
229
+ }
172
230
  }
173
231
 
174
- createTableRow(id) {
232
+ createTableRow(id: number): ImageTableRow {
175
233
  const image = this.app.imageDB.db[id]
176
234
  const cats = image.cats.map(cat => `cat_${cat}`)
177
235
 
178
- let fileType = image.file_type.split("/")
236
+ const fileTypeParts = image.file_type.split("/")
179
237
 
180
- if (1 < fileType.length) {
181
- fileType = fileType[1].toUpperCase()
238
+ let fileType: string
239
+ if (1 < fileTypeParts.length) {
240
+ fileType = fileTypeParts[1].toUpperCase()
182
241
  } else {
183
- fileType = fileType[0].toUpperCase()
242
+ fileType = fileTypeParts[0].toUpperCase()
184
243
  }
185
244
 
186
245
  return [
@@ -203,26 +262,32 @@ export class ImageOverview {
203
262
  ]
204
263
  }
205
264
 
206
- removeTableRows(ids) {
207
- ids = ids.map(id => Number.parseInt(id))
265
+ removeTableRows(ids: (string | number)[]): void {
266
+ const numericIds = ids.map(id => Number.parseInt(String(id)))
267
+
268
+ if (!this.table) {
269
+ return
270
+ }
208
271
 
209
272
  const existingRows = this.table.data.data
210
- .map((row, index) => {
211
- const id = row.cells[0].data
212
- if (ids.includes(id)) {
273
+ .map((row: DataTableRow, index: number) => {
274
+ const id = Number(row.cells[0].data)
275
+ if (numericIds.includes(id)) {
213
276
  return index
214
277
  } else {
215
278
  return false
216
279
  }
217
280
  })
218
- .filter(rowIndex => rowIndex !== false)
281
+ .filter(
282
+ (rowIndex): rowIndex is number => rowIndex !== false
283
+ )
219
284
 
220
285
  if (existingRows.length) {
221
286
  this.table.rows.remove(existingRows)
222
287
  }
223
288
  }
224
289
 
225
- onResize() {
290
+ onResize(): void {
226
291
  if (!this.table) {
227
292
  return
228
293
  }
@@ -230,7 +295,7 @@ export class ImageOverview {
230
295
  }
231
296
 
232
297
  /* Initialize the overview table */
233
- initTable(ids) {
298
+ initTable(ids: string[]): void {
234
299
  if (this.overviewTable) {
235
300
  this.overviewTable.destroy()
236
301
  this.overviewTable = null
@@ -238,10 +303,13 @@ export class ImageOverview {
238
303
  this.table = null
239
304
  this.dtBulk = null
240
305
 
241
- const contentsEl = this.dom.querySelector(".fw-contents")
306
+ const contentsEl = this.dom.querySelector(".fw-contents") as HTMLElement | null
307
+ if (!contentsEl) {
308
+ return
309
+ }
242
310
  contentsEl.innerHTML = ""
243
311
 
244
- const hiddenCols = [0]
312
+ const hiddenCols: number[] = [0]
245
313
 
246
314
  if (window.innerWidth < 500) {
247
315
  hiddenCols.push(1)
@@ -272,11 +340,11 @@ export class ImageOverview {
272
340
  sort: this.lastSort.dir
273
341
  }
274
342
  ],
275
- data: ids.map(id => this.createTableRow(id)),
343
+ data: ids.map(id => this.createTableRow(Number.parseInt(id))),
276
344
  idColumn: 0,
277
345
  checkboxColumn: 1,
278
346
  bulkMenu: bulkMenuModel(),
279
- bulkMenuPage: this,
347
+ bulkMenuPage: this as Record<string, unknown>,
280
348
  searchable: true,
281
349
  scrollY: `${Math.max(window.innerHeight - 360, 100)}px`,
282
350
  tabIndex: 1,
@@ -292,7 +360,7 @@ export class ImageOverview {
292
360
  gettext("Added"),
293
361
  ""
294
362
  ],
295
- template: (options, _dom) =>
363
+ template: (options: {classes: Record<string, string>; scrollY: string; paging?: boolean}, _dom) =>
296
364
  `<div class='${options.classes.container}'${options.scrollY.length ? ` style='height: ${options.scrollY}; overflow-Y: auto;'` : ""}></div>
297
365
  <div class='${options.classes.bottom}'>
298
366
  ${
@@ -303,20 +371,23 @@ export class ImageOverview {
303
371
  <nav class='${options.classes.pagination}'></nav>
304
372
  </div>`,
305
373
  rowRender: (row, tr, _index) => {
306
- const id = row.cells[0].data
307
- const inputNode = {
374
+ const id = row.cells[0].data as number
375
+ const inputNode: VirtualNode = {
308
376
  nodeName: "input",
309
377
  attributes: {
310
378
  type: "checkbox",
311
379
  class: "entry-select fw-check",
312
- "data-id": id,
380
+ "data-id": String(id),
313
381
  id: `doc-img-${id}`
314
382
  }
315
383
  }
316
384
  if (row.cells[1].data) {
317
- inputNode.attributes.checked = true
385
+ inputNode.attributes!.checked = "checked"
318
386
  }
319
- tr.childNodes[0].childNodes = [
387
+ const trNode = tr as {
388
+ childNodes: {childNodes: VirtualNode[]}[]
389
+ }
390
+ trNode.childNodes[0].childNodes = [
320
391
  inputNode,
321
392
  {
322
393
  nodeName: "label",
@@ -330,39 +401,46 @@ export class ImageOverview {
330
401
  if (this.getSelected().length > 0) {
331
402
  return
332
403
  }
333
- const rowIndex = this.table.data.data.indexOf(row)
404
+ if (!this.table) {
405
+ return
406
+ }
407
+ const rowIndex = this.table.data.data.findIndex(
408
+ dataRow =>
409
+ (dataRow as unknown as DataTableRow).cells[0].data ===
410
+ row.cells[0].data
411
+ )
334
412
  const button = this.table.dom.querySelector(
335
413
  `tr[data-index="${rowIndex}"] span.edit-image`
336
414
  )
337
415
  if (button) {
338
- button.click()
416
+ ;(button as HTMLElement).click()
339
417
  }
340
418
  },
341
419
  onDelete: row => {
342
- const imageId = row.cells[0].data
420
+ const imageId = row.cells[0].data as number
343
421
  this.deleteImageDialog([imageId])
344
422
  }
345
423
  })
346
424
  this.overviewTable.init()
347
- this.table = this.overviewTable.table
348
- this.table.id = "imagelist"
349
- this.dtBulk = this.overviewTable.dtBulk
425
+ this.table = this.overviewTable.table!
426
+ ;(this.table as unknown as {id: string}).id = "imagelist"
427
+ this.dtBulk = this.overviewTable.dtBulk || null
350
428
 
351
429
  this.table.on("datatable.sort", (column, dir) => {
352
- this.lastSort = {column, dir}
430
+ this.lastSort = {column: column as number, dir: dir as "asc" | "desc"}
353
431
  })
354
432
 
355
433
  this.table.dom.focus()
356
434
  }
357
435
 
358
436
  // get IDs of selected bib entries
359
- getSelected() {
437
+ getSelected(): number[] {
360
438
  return Array.from(
361
439
  this.dom.querySelectorAll(".entry-select:checked:not(:disabled)")
362
- ).map(el => Number.parseInt(el.getAttribute("data-id")))
440
+ ).map(el => Number.parseInt(el.getAttribute("data-id") || "0"))
363
441
  }
364
442
 
365
- bindEvents() {
443
+ bindEvents(): void {
366
444
  this.dom.addEventListener("click", event =>
367
445
  this.handleActivation(event)
368
446
  )
@@ -371,33 +449,37 @@ export class ImageOverview {
371
449
  )
372
450
  }
373
451
 
374
- handleActivation(event) {
452
+ handleActivation(event: Event): void {
375
453
  if (!isActivationEvent(event)) {
376
454
  return
377
455
  }
378
- const el = {}
456
+ const el: {target?: Element | null} = {}
379
457
  switch (true) {
380
458
  case findTarget(event, ".delete-image", el): {
381
- const imageId = el.target.dataset.id
382
- this.deleteImageDialog([imageId])
459
+ const imageId = (el.target as HTMLElement | null)?.dataset.id
460
+ this.deleteImageDialog([imageId || ""])
383
461
  break
384
462
  }
385
463
  case findTarget(event, ".edit-image", el): {
386
- const imageId = el.target.dataset.id
387
- import("../edit_dialog/index.js").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
- })
464
+ const imageId = (el.target as HTMLElement | null)?.dataset.id
465
+ import("../edit_dialog/index.js").then(
466
+ ({ImageEditDialog}) => {
467
+ const dialog = new ImageEditDialog(
468
+ this.app.imageDB,
469
+ imageId ? Number.parseInt(imageId) : false,
470
+ this as unknown as ImageManagerPage
471
+ )
472
+ dialog.init().then(() => {
473
+ if (imageId) {
474
+ this.updateTable([Number.parseInt(imageId)])
475
+ }
476
+ })
477
+ }
478
+ )
397
479
  break
398
480
  }
399
481
  case findTarget(event, ".fw-add-input", el): {
400
- const itemEl = el.target.closest(".fw-list-input")
482
+ const itemEl = (el.target as HTMLElement | null)?.closest(".fw-list-input") as HTMLElement
401
483
  if (!itemEl.nextElementSibling) {
402
484
  itemEl.insertAdjacentHTML(
403
485
  "afterend",
@@ -409,7 +491,7 @@ export class ImageOverview {
409
491
  </tr>`
410
492
  )
411
493
  } else {
412
- itemEl.parentElement.removeChild(itemEl)
494
+ itemEl.parentElement!.removeChild(itemEl)
413
495
  }
414
496
  break
415
497
  }
@@ -418,7 +500,7 @@ export class ImageOverview {
418
500
  }
419
501
  }
420
502
 
421
- close() {
503
+ close(): void {
422
504
  if (this.table) {
423
505
  this.table.destroy()
424
506
  this.table = null
@@ -429,7 +511,7 @@ export class ImageOverview {
429
511
  }
430
512
  if (this.menu) {
431
513
  this.menu.destroy()
432
- this.menu = null
514
+ this.menu = null as unknown as OverviewMenuView
433
515
  }
434
516
  }
435
517
  }
@@ -0,0 +1,85 @@
1
+ import {gettext} from "fwtoolkit"
2
+ import type {ContentMenuInit} from "fwtoolkit/content_menu"
3
+ import type {OverviewMenuModel} from "fwtoolkit/overview_menu"
4
+
5
+ import type {ImageManagerPage} from "../types.js"
6
+ import type {ImageOverview} from "./index.js"
7
+
8
+ export const bulkMenuModel = (): ContentMenuInit => ({
9
+ content: [
10
+ {
11
+ title: gettext("Delete selected"),
12
+ tooltip: gettext("Delete selected images."),
13
+ action: (overview: unknown) => {
14
+ const ids = (overview as ImageOverview).getSelected()
15
+ if (ids.length) {
16
+ ;(overview as ImageOverview).deleteImageDialog(ids)
17
+ }
18
+ },
19
+ disabled: (overview: unknown) =>
20
+ !(overview as ImageOverview).getSelected().length ||
21
+ (overview as ImageOverview).app.isOffline()
22
+ }
23
+ ]
24
+ })
25
+
26
+ export const menuModel = (): OverviewMenuModel => ({
27
+ content: [
28
+ {
29
+ type: "dropdown",
30
+ id: "cat_selector",
31
+ keys: "Alt-c",
32
+ content: [
33
+ {
34
+ title: gettext("All categories"),
35
+ action: (_overview: unknown) => {
36
+ const trs = document.querySelectorAll(
37
+ "#imagelist > tbody > tr"
38
+ )
39
+ trs.forEach(tr => ((tr as HTMLElement).style.display = ""))
40
+ }
41
+ }
42
+ ],
43
+ order: 1
44
+ },
45
+ {
46
+ type: "text",
47
+ title: gettext("Edit categories"),
48
+ keys: "Alt-e",
49
+ action: (overview: unknown) =>
50
+ (overview as ImageOverview).mod.categories!.editCategoryDialog(),
51
+ order: 2,
52
+ disabled: (overview: unknown) => (overview as ImageOverview).app.isOffline()
53
+ },
54
+ {
55
+ type: "text",
56
+ title: gettext("Upload new image"),
57
+ keys: "Alt-u",
58
+ action: (overview: unknown) => {
59
+ import("../edit_dialog/index.js").then(({ImageEditDialog}) => {
60
+ const imageUpload = new ImageEditDialog(
61
+ (overview as ImageOverview).app.imageDB,
62
+ false,
63
+ overview as ImageManagerPage
64
+ )
65
+ imageUpload.init().then(imageId => {
66
+ if (imageId) {
67
+ ;(overview as ImageOverview).updateTable([imageId])
68
+ }
69
+ })
70
+ })
71
+ },
72
+ order: 3,
73
+ disabled: (overview: unknown) => (overview as ImageOverview).app.isOffline()
74
+ },
75
+ {
76
+ type: "search",
77
+ icon: "search",
78
+ title: gettext("Search images"),
79
+ keys: "Alt-s",
80
+ input: (overview: unknown, text: string) =>
81
+ (overview as ImageOverview).table!.search(text),
82
+ order: 4
83
+ }
84
+ ]
85
+ })